diff src/device.c @ 428:3ecd8885ac67 r21-2-22

Import from CVS: tag r21-2-22
author cvs
date Mon, 13 Aug 2007 11:28:15 +0200
parents
children 8de8e3f6228a
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/device.c	Mon Aug 13 11:28:15 2007 +0200
@@ -0,0 +1,1332 @@
+ /* Generic device functions.
+   Copyright (C) 1994, 1995 Board of Trustees, University of Illinois.
+   Copyright (C) 1994, 1995 Free Software Foundation, Inc.
+   Copyright (C) 1995, 1996 Ben Wing
+
+This file is part of XEmacs.
+
+XEmacs is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+XEmacs is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with XEmacs; see the file COPYING.  If not, write to
+the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* Synched up with: Not in FSF. */
+
+/* Original version by Chuck Thompson;
+   rewritten and expanded by Ben Wing. */
+
+#include <config.h>
+#include "lisp.h"
+
+#include "buffer.h"
+#include "console.h"
+#include "device.h"
+#include "elhash.h"
+#include "events.h"
+#include "faces.h"
+#include "frame.h"
+#include "keymap.h"
+#include "redisplay.h"
+#include "specifier.h"
+#include "sysdep.h"
+#include "window.h"
+
+#ifdef HAVE_SCROLLBARS
+#include "scrollbar.h"
+#endif
+
+#include "syssignal.h"
+
+/* Vdefault_device is the firstly-created non-stream device that's still
+   around.  We don't really use it anywhere currently, but it might
+   be used for resourcing at some point.  (Currently we use
+   Vdefault_x_device.) */
+Lisp_Object Vdefault_device;
+
+Lisp_Object Vcreate_device_hook, Vdelete_device_hook;
+
+/* Device classes */
+/* Qcolor defined in general.c */
+Lisp_Object Qgrayscale, Qmono;
+
+/* Device metrics symbols */
+Lisp_Object
+  Qcolor_default, Qcolor_select, Qcolor_balloon, Qcolor_3d_face,
+  Qcolor_3d_light, Qcolor_3d_dark, Qcolor_menu, Qcolor_menu_highlight,
+  Qcolor_menu_button, Qcolor_menu_disabled, Qcolor_toolbar,
+  Qcolor_scrollbar, Qcolor_desktop, Qcolor_workspace, Qfont_default,
+  Qfont_menubar, Qfont_dialog, Qsize_cursor, Qsize_scrollbar,
+  Qsize_menu, Qsize_toolbar, Qsize_toolbar_button,
+  Qsize_toolbar_border, Qsize_icon, Qsize_icon_small, Qsize_device,
+  Qsize_workspace, Qsize_device_mm, Qdevice_dpi, Qnum_bit_planes,
+  Qnum_color_cells, Qmouse_buttons, Qswap_buttons, Qshow_sounds,
+  Qslow_device, Qsecurity;
+
+Lisp_Object Qdevicep, Qdevice_live_p;
+Lisp_Object Qcreate_device_hook;
+Lisp_Object Qdelete_device_hook;
+Lisp_Object Vdevice_class_list;
+
+
+static Lisp_Object
+mark_device (Lisp_Object obj)
+{
+  struct device *d = XDEVICE (obj);
+
+  mark_object (d->name);
+  mark_object (d->connection);
+  mark_object (d->canon_connection);
+  mark_object (d->console);
+  mark_object (d->selected_frame);
+  mark_object (d->frame_with_focus_real);
+  mark_object (d->frame_with_focus_for_hooks);
+  mark_object (d->frame_that_ought_to_have_focus);
+  mark_object (d->device_class);
+  mark_object (d->user_defined_tags);
+  mark_object (d->pixel_to_glyph_cache.obj1);
+  mark_object (d->pixel_to_glyph_cache.obj2);
+
+  mark_object (d->color_instance_cache);
+  mark_object (d->font_instance_cache);
+#ifdef MULE
+  mark_object (d->charset_font_cache);
+#endif
+  mark_object (d->image_instance_cache);
+
+  if (d->devmeths)
+    {
+      mark_object (d->devmeths->symbol);
+      MAYBE_DEVMETH (d, mark_device, (d));
+    }
+
+  return (d->frame_list);
+}
+
+static void
+print_device (Lisp_Object obj, Lisp_Object printcharfun, int escapeflag)
+{
+  struct device *d = XDEVICE (obj);
+  char buf[256];
+
+  if (print_readably)
+    error ("printing unreadable object #<device %s 0x%x>",
+	   XSTRING_DATA (d->name), d->header.uid);
+
+  sprintf (buf, "#<%s-device", !DEVICE_LIVE_P (d) ? "dead" :
+	   DEVICE_TYPE_NAME (d));
+  write_c_string (buf, printcharfun);
+  if (DEVICE_LIVE_P (d))
+    {
+      write_c_string (" on ", printcharfun);
+      print_internal (DEVICE_CONNECTION (d), printcharfun, 1);
+    }
+  sprintf (buf, " 0x%x>", d->header.uid);
+  write_c_string (buf, printcharfun);
+}
+
+DEFINE_LRECORD_IMPLEMENTATION ("device", device,
+			       mark_device, print_device, 0, 0, 0, 0,
+			       struct device);
+
+int
+valid_device_class_p (Lisp_Object class)
+{
+  return !NILP (memq_no_quit (class, Vdevice_class_list));
+}
+
+DEFUN ("valid-device-class-p", Fvalid_device_class_p, 1, 1, 0, /*
+Given a DEVICE-CLASS, return t if it is valid.
+Valid classes are 'color, 'grayscale, and 'mono.
+*/
+       (device_class))
+{
+  return valid_device_class_p (device_class) ? Qt : Qnil;
+}
+
+DEFUN ("device-class-list", Fdevice_class_list, 0, 0, 0, /*
+Return a list of valid device classes.
+*/
+       ())
+{
+  return Fcopy_sequence (Vdevice_class_list);
+}
+
+static struct device *
+allocate_device (Lisp_Object console)
+{
+  Lisp_Object device;
+  struct device *d = alloc_lcrecord_type (struct device, &lrecord_device);
+  struct gcpro gcpro1;
+
+  zero_lcrecord (d);
+
+  XSETDEVICE (device, d);
+  GCPRO1 (device);
+
+  d->name = Qnil;
+  d->console = console;
+  d->connection = Qnil;
+  d->canon_connection = Qnil;
+  d->frame_list = Qnil;
+  d->selected_frame = Qnil;
+  d->frame_with_focus_real = Qnil;
+  d->frame_with_focus_for_hooks = Qnil;
+  d->frame_that_ought_to_have_focus = Qnil;
+  d->device_class = Qnil;
+  d->user_defined_tags = Qnil;
+  d->pixel_to_glyph_cache.obj1 = Qnil;
+  d->pixel_to_glyph_cache.obj2 = Qnil;
+
+  d->infd = d->outfd = -1;
+
+  /* #### is 20 reasonable? */
+  d->color_instance_cache =
+    make_lisp_hash_table (20, HASH_TABLE_KEY_WEAK, HASH_TABLE_EQUAL);
+  d->font_instance_cache =
+    make_lisp_hash_table (20, HASH_TABLE_KEY_WEAK, HASH_TABLE_EQUAL);
+#ifdef MULE
+  /* Note that the following table is bi-level. */
+  d->charset_font_cache =
+    make_lisp_hash_table (20, HASH_TABLE_NON_WEAK, HASH_TABLE_EQ);
+#endif
+  /*
+     Note that the image instance cache is actually bi-level.
+     See device.h.  We use a low number here because most of the
+     time there aren't very many different masks that will be used.
+     */
+  d->image_instance_cache =
+    make_lisp_hash_table (5, HASH_TABLE_NON_WEAK, HASH_TABLE_EQ);
+
+  UNGCPRO;
+  return d;
+}
+
+struct device *
+decode_device (Lisp_Object device)
+{
+  if (NILP (device))
+    device = Fselected_device (Qnil);
+  /* quietly accept frames for the device arg */
+  else if (FRAMEP (device))
+    device = FRAME_DEVICE (decode_frame (device));
+  CHECK_LIVE_DEVICE (device);
+  return XDEVICE (device);
+}
+
+DEFUN ("dfw-device", Fdfw_device, 1, 1, 0, /*
+Given a device, frame, or window, return the associated device.
+Return nil otherwise.
+*/
+       (obj))
+{
+  return DFW_DEVICE (obj);
+}
+
+
+DEFUN ("selected-device", Fselected_device, 0, 1, 0, /*
+Return the device which is currently active.
+If optional CONSOLE is non-nil, return the device that would be currently
+active if CONSOLE were the selected console.
+*/
+       (console))
+{
+  if (NILP (console) && NILP (Vselected_console))
+    return Qnil; /* happens early in temacs */
+  return CONSOLE_SELECTED_DEVICE (decode_console (console));
+}
+
+/* Called from selected_frame_1(), called from Fselect_window() */
+void
+select_device_1 (Lisp_Object device)
+{
+  struct device *dev = XDEVICE (device);
+  Lisp_Object old_selected_device = Fselected_device (Qnil);
+
+  if (EQ (device, old_selected_device))
+    return;
+
+  /* now select the device's console */
+  CONSOLE_SELECTED_DEVICE (XCONSOLE (DEVICE_CONSOLE (dev))) = device;
+  select_console_1 (DEVICE_CONSOLE (dev));
+}
+
+DEFUN ("select-device", Fselect_device, 1, 1, 0, /*
+Select the device DEVICE.
+Subsequent editing commands apply to its console, selected frame,
+and selected window.
+The selection of DEVICE lasts until the next time the user does
+something to select a different device, or until the next time this
+function is called.
+*/
+       (device))
+{
+  CHECK_LIVE_DEVICE (device);
+
+  /* select the device's selected frame's selected window.  This will call
+     selected_frame_1()->selected_device_1()->selected_console_1(). */
+  if (!NILP (DEVICE_SELECTED_FRAME (XDEVICE (device))))
+    Fselect_window (FRAME_SELECTED_WINDOW
+		    (XFRAME (DEVICE_SELECTED_FRAME (XDEVICE (device)))),
+                    Qnil);
+  else
+    error ("Can't select a device with no frames");
+  return Qnil;
+}
+
+void
+set_device_selected_frame (struct device *d, Lisp_Object frame)
+{
+  if (!NILP (frame) && !FRAME_MINIBUF_ONLY_P (XFRAME (frame)))
+    set_console_last_nonminibuf_frame (XCONSOLE (DEVICE_CONSOLE (d)), frame);
+  d->selected_frame = frame;
+}
+
+DEFUN ("set-device-selected-frame", Fset_device_selected_frame, 2, 2, 0, /*
+Set the selected frame of device object DEVICE to FRAME.
+If DEVICE is nil, the selected device is used.
+If DEVICE is the selected device, this makes FRAME the selected frame.
+*/
+       (device, frame))
+{
+  XSETDEVICE (device, decode_device (device));
+  CHECK_LIVE_FRAME (frame);
+
+  if (! EQ (device, FRAME_DEVICE (XFRAME (frame))))
+    error ("In `set-device-selected-frame', FRAME is not on DEVICE");
+
+  if (EQ (device, Fselected_device (Qnil)))
+    return Fselect_frame (frame);
+
+  set_device_selected_frame (XDEVICE (device), frame);
+  return frame;
+}
+
+DEFUN ("devicep", Fdevicep, 1, 1, 0, /*
+Return non-nil if OBJECT is a device.
+*/
+       (object))
+{
+  return DEVICEP (object) ? Qt : Qnil;
+}
+
+DEFUN ("device-live-p", Fdevice_live_p, 1, 1, 0, /*
+Return non-nil if OBJECT is a device that has not been deleted.
+*/
+       (object))
+{
+  return DEVICEP (object) && DEVICE_LIVE_P (XDEVICE (object)) ? Qt : Qnil;
+}
+
+DEFUN ("device-name", Fdevice_name, 0, 1, 0, /*
+Return the name of the specified device.
+DEVICE defaults to the selected device if omitted.
+*/
+       (device))
+{
+  return DEVICE_NAME (decode_device (device));
+}
+
+DEFUN ("device-connection", Fdevice_connection, 0, 1, 0, /*
+Return the connection of the specified device.
+DEVICE defaults to the selected device if omitted.
+*/
+       (device))
+{
+  return DEVICE_CONNECTION (decode_device (device));
+}
+
+DEFUN ("device-console", Fdevice_console, 0, 1, 0, /*
+Return the console of the specified device.
+DEVICE defaults to the selected device if omitted.
+*/
+       (device))
+{
+  return DEVICE_CONSOLE (decode_device (device));
+}
+
+#ifdef HAVE_WINDOW_SYSTEM
+
+static void
+init_global_resources (struct device *d)
+{
+  init_global_faces (d);
+#ifdef HAVE_SCROLLBARS
+  init_global_scrollbars (d);
+#endif
+#ifdef HAVE_TOOLBARS
+  init_global_toolbars (d);
+#endif
+}
+
+#endif
+
+static void
+init_device_resources (struct device *d)
+{
+  init_device_faces (d);
+#ifdef HAVE_SCROLLBARS
+  init_device_scrollbars (d);
+#endif
+#ifdef HAVE_TOOLBARS
+  init_device_toolbars (d);
+#endif
+}
+
+static Lisp_Object
+semi_canonicalize_device_connection (struct console_methods *meths,
+				     Lisp_Object name, Error_behavior errb)
+{
+  return CONTYPE_METH_OR_GIVEN (meths, semi_canonicalize_device_connection,
+				(name, errb), name);
+}
+
+static Lisp_Object
+canonicalize_device_connection (struct console_methods *meths,
+				Lisp_Object name, Error_behavior errb)
+{
+  return CONTYPE_METH_OR_GIVEN (meths, canonicalize_device_connection,
+				(name, errb), name);
+}
+
+static Lisp_Object
+find_device_of_type (struct console_methods *meths, Lisp_Object canon)
+{
+  Lisp_Object devcons, concons;
+
+  DEVICE_LOOP_NO_BREAK (devcons, concons)
+    {
+      Lisp_Object device = XCAR (devcons);
+
+      if (EQ (CONMETH_TYPE (meths), DEVICE_TYPE (XDEVICE (device)))
+	  && internal_equal (DEVICE_CANON_CONNECTION (XDEVICE (device)),
+			     canon, 0))
+	return device;
+    }
+
+  return Qnil;
+}
+
+DEFUN ("find-device", Ffind_device, 1, 2, 0, /*
+Look for an existing device attached to connection CONNECTION.
+Return the device if found; otherwise, return nil.
+
+If TYPE is specified, only return devices of that type; otherwise,
+return devices of any type. (It is possible, although unlikely,
+that two devices of different types could have the same connection
+name; in such a case, the first device found is returned.)
+*/
+       (connection, type))
+{
+  Lisp_Object canon = Qnil;
+  struct gcpro gcpro1;
+
+  GCPRO1 (canon);
+
+  if (!NILP (type))
+    {
+      struct console_methods *conmeths = decode_console_type (type, ERROR_ME);
+      canon = canonicalize_device_connection (conmeths, connection,
+					      ERROR_ME_NOT);
+      if (UNBOUNDP (canon))
+	RETURN_UNGCPRO (Qnil);
+
+      RETURN_UNGCPRO (find_device_of_type (conmeths, canon));
+    }
+  else
+    {
+      int i;
+
+      for (i = 0; i < Dynarr_length (the_console_type_entry_dynarr); i++)
+	{
+	  struct console_methods *conmeths =
+	    Dynarr_at (the_console_type_entry_dynarr, i).meths;
+	  canon = canonicalize_device_connection (conmeths, connection,
+						  ERROR_ME_NOT);
+	  if (!UNBOUNDP (canon))
+	    {
+	      Lisp_Object device = find_device_of_type (conmeths, canon);
+	      if (!NILP (device))
+		RETURN_UNGCPRO (device);
+	    }
+	}
+
+      RETURN_UNGCPRO (Qnil);
+    }
+}
+
+DEFUN ("get-device", Fget_device, 1, 2, 0, /*
+Look for an existing device attached to connection CONNECTION.
+Return the device if found; otherwise, signal an error.
+
+If TYPE is specified, only return devices of that type; otherwise,
+return devices of any type. (It is possible, although unlikely,
+that two devices of different types could have the same connection
+name; in such a case, the first device found is returned.)
+*/
+       (connection, type))
+{
+  Lisp_Object device = Ffind_device (connection, type);
+  if (NILP (device))
+    {
+      if (NILP (type))
+	signal_simple_error ("No such device", connection);
+      else
+	signal_simple_error_2 ("No such device", type, connection);
+    }
+  return device;
+}
+
+static Lisp_Object
+delete_deviceless_console (Lisp_Object console)
+{
+  if (NILP (XCONSOLE (console)->device_list))
+    Fdelete_console (console, Qnil);
+  return Qnil;
+}
+
+DEFUN ("make-device", Fmake_device, 2, 3, 0, /*
+Return a new device of type TYPE, attached to connection CONNECTION.
+
+The valid values for CONNECTION are device-specific; however,
+CONNECTION is generally a string. (Specifically, for X devices,
+CONNECTION should be a display specification such as "foo:0", and
+for TTY devices, CONNECTION should be the filename of a TTY device
+file, such as "/dev/ttyp4", or nil to refer to XEmacs' standard
+input/output.)
+
+PROPS, if specified, should be a plist of properties controlling
+device creation.
+
+If CONNECTION specifies an already-existing device connection, that
+device is simply returned; no new device is created, and PROPS
+have no effect.
+*/
+       (type, connection, props))
+{
+  /* This function can GC */
+  struct device *d;
+  struct console *con;
+  Lisp_Object device = Qnil;
+  Lisp_Object console = Qnil;
+  Lisp_Object name = Qnil;
+  struct console_methods *conmeths;
+  int speccount = specpdl_depth();
+
+  struct gcpro gcpro1, gcpro2, gcpro3;
+#ifdef HAVE_X_WINDOWS
+  /* #### icky-poo.  If this is the first X device we are creating,
+     then retrieve the global face resources.  We have to do it
+     here, at the same time as (or just before) the device face
+     resources are retrieved; specifically, it needs to be done
+     after the device has been created but before any frames have
+     been popped up or much anything else has been done.  It's
+     possible for other devices to specify different global
+     resources (there's a property on each X server's root window
+     that holds some resources); tough luck for the moment.
+
+     This is a nasty violation of device independence, but
+     there's not a whole lot I can figure out to do about it.
+     The real problem is that the concept of resources is not
+     generalized away from X.  Similar resource-related
+     device-independence violations occur in faces.el. */
+  int first_x_device = NILP (Vdefault_x_device) && EQ (type, Qx);
+#endif
+
+  GCPRO3 (device, console, name);
+
+  conmeths = decode_console_type (type, ERROR_ME_NOT);
+  if (!conmeths)
+    signal_simple_error ("Invalid device type", type);
+
+  device = Ffind_device (connection, type);
+  if (!NILP (device))
+    RETURN_UNGCPRO (device);
+
+  name = Fplist_get (props, Qname, Qnil);
+
+  {
+    Lisp_Object conconnect =
+      (HAS_CONTYPE_METH_P (conmeths, device_to_console_connection)) ?
+      CONTYPE_METH (conmeths, device_to_console_connection,
+		    (connection, ERROR_ME)) :
+      connection;
+    console = create_console (name, type, conconnect, props);
+  }
+
+  record_unwind_protect(delete_deviceless_console, console);
+
+  con = XCONSOLE (console);
+  d = allocate_device (console);
+  XSETDEVICE (device, d);
+
+  d->devmeths = con->conmeths;
+
+  DEVICE_NAME (d) = name;
+  DEVICE_CONNECTION (d) =
+    semi_canonicalize_device_connection (conmeths, connection, ERROR_ME);
+  DEVICE_CANON_CONNECTION (d) =
+    canonicalize_device_connection (conmeths, connection, ERROR_ME);
+
+  MAYBE_DEVMETH (d, init_device, (d, props));
+
+  /* Do it this way so that the device list is in order of creation */
+  con->device_list = nconc2 (con->device_list, Fcons (device, Qnil));
+  RESET_CHANGED_SET_FLAGS;
+  if (NILP (Vdefault_device) || DEVICE_STREAM_P (XDEVICE (Vdefault_device)))
+    Vdefault_device = device;
+
+  init_device_sound (d);
+#ifdef HAVE_X_WINDOWS
+  if (first_x_device)
+    init_global_resources (d);
+#endif
+  init_device_resources (d);
+
+  MAYBE_DEVMETH (d, finish_init_device, (d, props));
+
+  /* If this is the first device on the console, make it the selected one. */
+  if (NILP (CONSOLE_SELECTED_DEVICE (con)))
+    CONSOLE_SELECTED_DEVICE (con) = device;
+
+  /* #### the following should trap errors. */
+  setup_device_initial_specifier_tags (d);
+
+  UNGCPRO;
+  unbind_to(speccount, Qnil);
+  return device;
+}
+
+/* find a device other than the selected one.  Prefer non-stream
+   devices over stream devices.  Maybe stay on the same console. */
+
+static Lisp_Object
+find_other_device (Lisp_Object device, int on_same_console)
+{
+  Lisp_Object devcons = Qnil, concons;
+  Lisp_Object console = DEVICE_CONSOLE (XDEVICE (device));
+
+  /* look for a non-stream device */
+  DEVICE_LOOP_NO_BREAK (devcons, concons)
+    {
+      Lisp_Object dev = XCAR (devcons);
+      if (on_same_console && !EQ (console, DEVICE_CONSOLE (XDEVICE (dev))))
+	continue;
+      if (!DEVICE_STREAM_P (XDEVICE (dev)) && !EQ (dev, device) &&
+	  !NILP (DEVICE_SELECTED_FRAME (XDEVICE (dev))))
+	goto double_break_1;
+    }
+
+ double_break_1:
+  if (!NILP (devcons))
+    return XCAR (devcons);
+
+  /* OK, now look for a stream device */
+  DEVICE_LOOP_NO_BREAK (devcons, concons)
+    {
+      Lisp_Object dev = XCAR (devcons);
+      if (on_same_console && !EQ (console, DEVICE_CONSOLE (XDEVICE (dev))))
+	continue;
+      if (!EQ (dev, device) && !NILP (DEVICE_SELECTED_FRAME (XDEVICE (dev))))
+	goto double_break_2;
+    }
+ double_break_2:
+  if (!NILP (devcons))
+    return XCAR (devcons);
+
+  /* Sorry, there ain't none */
+  return Qnil;
+}
+
+static int
+find_nonminibuffer_frame_not_on_device_predicate (Lisp_Object frame,
+						  void *closure)
+{
+  Lisp_Object device;
+
+  VOID_TO_LISP (device, closure);
+  if (FRAME_MINIBUF_ONLY_P (XFRAME (frame)))
+    return 0;
+  if (EQ (device, FRAME_DEVICE (XFRAME (frame))))
+    return 0;
+  return 1;
+}
+
+Lisp_Object
+find_nonminibuffer_frame_not_on_device (Lisp_Object device)
+{
+  return find_some_frame (find_nonminibuffer_frame_not_on_device_predicate,
+			  LISP_TO_VOID (device));
+}
+
+
+/* Delete device D.
+
+   If FORCE is non-zero, allow deletion of the only frame.
+
+   If CALLED_FROM_DELETE_CONSOLE is non-zero, then, if
+   deleting the last device on a console, just delete it,
+   instead of calling `delete-console'.
+
+   If FROM_IO_ERROR is non-zero, then the device is gone due
+   to an I/O error.  This affects what happens if we exit
+   (we do an emergency exit instead of `save-buffers-kill-emacs'.)
+*/
+
+void
+delete_device_internal (struct device *d, int force,
+			int called_from_delete_console,
+			int from_io_error)
+{
+  /* This function can GC */
+  struct console *c;
+  Lisp_Object device;
+  struct gcpro gcpro1;
+
+  /* OK to delete an already-deleted device. */
+  if (!DEVICE_LIVE_P (d))
+    return;
+
+  XSETDEVICE (device, d);
+  GCPRO1 (device);
+
+  c = XCONSOLE (DEVICE_CONSOLE (d));
+
+  if (!called_from_delete_console)
+    {
+      int delete_console = 0;
+      /* If we're deleting the only device on the console,
+	 delete the console. */
+      if ((XINT (Flength (CONSOLE_DEVICE_LIST (c))) == 1)
+	  /* if we just created the device, it might not be listed,
+	     or something ... */
+	  && !NILP (memq_no_quit (device, CONSOLE_DEVICE_LIST (c))))
+	delete_console = 1;
+      /* Or if there aren't any nonminibuffer frames that would be
+	 left, delete the console (this will make XEmacs exit). */
+      else if (NILP (find_nonminibuffer_frame_not_on_device (device)))
+	delete_console = 1;
+
+      if (delete_console)
+	{
+	  delete_console_internal (c, force, 0, from_io_error);
+	  UNGCPRO;
+	  return;
+	}
+    }
+
+  reset_one_device (d);
+
+  {
+    Lisp_Object frmcons;
+
+    /* First delete all frames without their own minibuffers,
+       to avoid errors coming from attempting to delete a frame
+       that is a surrogate for another frame. */
+    DEVICE_FRAME_LOOP (frmcons, d)
+      {
+	struct frame *f = XFRAME (XCAR (frmcons));
+	/* delete_frame_internal() might do anything such as run hooks,
+	   so be defensive. */
+	if (FRAME_LIVE_P (f) && !FRAME_HAS_MINIBUF_P (f))
+	  delete_frame_internal (f, 1, 1, from_io_error);
+
+	if (!DEVICE_LIVE_P (d)) /* make sure the delete-*-hook didn't
+				   go ahead and delete anything */
+	  {
+	    UNGCPRO;
+	    return;
+	  }
+      }
+
+    /* #### This should probably be a device method but it is time for
+       19.14 to go out the door. */
+#ifdef HAVE_X_WINDOWS
+    /* Next delete all frames which have the popup property to avoid
+       deleting a child after its parent. */
+    DEVICE_FRAME_LOOP (frmcons, d)
+      {
+	struct frame *f = XFRAME (XCAR (frmcons));
+
+	if (FRAME_LIVE_P (f))
+	  {
+	    Lisp_Object popup = Fframe_property (XCAR (frmcons), Qpopup, Qnil);
+	    if (!NILP (popup))
+	      delete_frame_internal (f, 1, 1, from_io_error);
+
+	    if (!DEVICE_LIVE_P (d)) /* make sure the delete-*-hook didn't
+				       go ahead and delete anything */
+	      {
+		UNGCPRO;
+		return;
+	      }
+	  }
+      }
+#endif /* HAVE_X_WINDOWS */
+
+    DEVICE_FRAME_LOOP (frmcons, d)
+      {
+	struct frame *f = XFRAME (XCAR (frmcons));
+	/* delete_frame_internal() might do anything such as run hooks,
+	   so be defensive. */
+	if (FRAME_LIVE_P (f))
+	  delete_frame_internal (f, 1, 1, from_io_error);
+
+	if (!DEVICE_LIVE_P (d)) /* make sure the delete-*-hook didn't
+				   go ahead and delete anything */
+	  {
+	    UNGCPRO;
+	    return;
+	  }
+      }
+  }
+
+  set_device_selected_frame (d, Qnil);
+
+  /* try to select another device */
+
+  if (EQ (device, Fselected_device (DEVICE_CONSOLE (d))))
+    {
+      Lisp_Object other_dev = find_other_device (device, 1);
+      if (!NILP (other_dev))
+	Fselect_device (other_dev);
+    }
+
+  if (EQ (device, Vdefault_device))
+    Vdefault_device = find_other_device (device, 0);
+
+  MAYBE_DEVMETH (d, delete_device, (d));
+
+  CONSOLE_DEVICE_LIST (c) = delq_no_quit (device, CONSOLE_DEVICE_LIST (c));
+  RESET_CHANGED_SET_FLAGS;
+  d->devmeths = dead_console_methods;
+  UNGCPRO;
+}
+
+/* delete a device as a result of an I/O error.  Called from
+   an enqueued magic-eval event. */
+
+void
+io_error_delete_device (Lisp_Object device)
+{
+  /* Note: it's the console that should get deleted, but
+     delete_device_internal() contains a hack that also deletes the
+     console when called from this function.  */
+  delete_device_internal (XDEVICE (device), 1, 0, 1);
+}
+
+DEFUN ("delete-device", Fdelete_device, 1, 2, 0, /*
+Delete DEVICE, permanently eliminating it from use.
+Normally, you cannot delete the last non-minibuffer-only frame (you must
+use `save-buffers-kill-emacs' or `kill-emacs').  However, if optional
+second argument FORCE is non-nil, you can delete the last frame. (This
+will automatically call `save-buffers-kill-emacs'.)
+*/
+       (device, force))
+{
+  CHECK_DEVICE (device);
+  delete_device_internal (XDEVICE (device), !NILP (force), 0, 0);
+  return Qnil;
+}
+
+DEFUN ("device-frame-list", Fdevice_frame_list, 0, 1, 0, /*
+Return a list of all frames on DEVICE.
+If DEVICE is nil, the selected device will be used.
+*/
+       (device))
+{
+  return Fcopy_sequence (DEVICE_FRAME_LIST (decode_device (device)));
+}
+
+DEFUN ("device-class", Fdevice_class, 0, 1, 0, /*
+Return the class (color behavior) of DEVICE.
+This will be one of 'color, 'grayscale, or 'mono.
+*/
+       (device))
+{
+  return DEVICE_CLASS (decode_device (device));
+}
+
+DEFUN ("set-device-class", Fset_device_class, 2, 2, 0, /*
+Set the class (color behavior) of DEVICE.
+CLASS should be one of 'color, 'grayscale, or 'mono.
+This is only allowed on device such as TTY devices, where the color
+behavior cannot necessarily be determined automatically.
+*/
+       (device, class))
+{
+  struct device *d = decode_device (device);
+  XSETDEVICE (device, d);
+  if (!DEVICE_TTY_P (d))
+    signal_simple_error ("Cannot change the class of this device", device);
+  if (!EQ (class, Qcolor) && !EQ (class, Qmono) && !EQ (class, Qgrayscale))
+    signal_simple_error ("Must be color, mono, or grayscale", class);
+  if (! EQ (DEVICE_CLASS (d), class))
+    {
+      Lisp_Object frmcons;
+      DEVICE_CLASS (d) = class;
+      DEVICE_FRAME_LOOP (frmcons, d)
+	{
+	  struct frame *f = XFRAME (XCAR (frmcons));
+
+	  recompute_all_cached_specifiers_in_frame (f);
+	  MARK_FRAME_FACES_CHANGED (f);
+	  MARK_FRAME_GLYPHS_CHANGED (f);
+	  MARK_FRAME_SUBWINDOWS_CHANGED (f);
+	  MARK_FRAME_TOOLBARS_CHANGED (f);
+	  f->menubar_changed = 1;
+	}
+    }
+  return Qnil;
+}
+
+DEFUN ("set-device-baud-rate", Fset_device_baud_rate, 2, 2, 0, /*
+Set the output baud rate of DEVICE to RATE.
+On most systems, changing this value will affect the amount of padding
+and other strategic decisions made during redisplay.
+*/
+       (device, rate))
+{
+  CHECK_INT (rate);
+
+  DEVICE_BAUD_RATE (decode_device (device)) = XINT (rate);
+
+  return rate;
+}
+
+DEFUN ("device-baud-rate", Fdevice_baud_rate, 0, 1, 0, /*
+Return the output baud rate of DEVICE.
+*/
+       (device))
+{
+  return make_int (DEVICE_BAUD_RATE (decode_device (device)));
+}
+
+DEFUN ("device-system-metric", Fdevice_system_metric, 1, 3, 0, /*
+Get a metric for DEVICE as provided by the system.
+
+METRIC must be a symbol specifying requested metric.  Note that the metrics
+returned are these provided by the system internally, not read from resources,
+so obtained from the most internal level.
+
+If a metric is not provided by the system, then DEFAULT is returned.
+
+When DEVICE is nil, selected device is assumed
+
+Metrics, by group, are:
+
+COLORS.  Colors are returned as valid color instantiators.  No other assumption
+on the returned value should be made (i.e. it can be a string on one system but
+a color instance on another).  For colors, returned value is a cons of
+foreground and background colors.  Note that if the system provides only one
+color of the pair, the second one may be nil.
+
+color-default         Standard window text foreground and background.
+color-select          Selection highlight text and background colors.
+color-balloon         Balloon popup text and background colors.
+color-3d-face         3-D object (button, modeline) text and surface colors.
+color-3d-light        Fore and back colors for 3-D edges facing light source.
+color-3d-dark         Fore and back colors for 3-D edges facing away from
+                      light source.
+color-menu            Text and background for menus
+color-menu-highlight  Selected menu item colors
+color-menu-button     Menu button colors
+color-menu-disabled   Unselectable menu item colors
+color-toolbar         Toolbar foreground and background colors
+color-scrollbar       Scrollbar foreground and background colors
+color-desktop         Desktop window colors
+color-workspace       Workspace window colors
+
+FONTS. Fonts are returned as valid font instantiators.  No other assumption on
+the returned value should be made (i.e. it can be a string on one system but
+font instance on another).
+
+font-default          Default fixed width font.
+font-menubar          Menubar font
+font-dialog           Dialog boxes font
+
+GEOMETRY. These metrics are returned as conses of (X . Y).  As with colors,
+either car or cdr of the cons may be nil if the system does not provide one
+of the corresponding dimensions.
+
+size-cursor           Mouse cursor size.
+size-scrollbar        Scrollbars (WIDTH . HEIGHT)
+size-menu             Menubar height, as (nil . HEIGHT)
+size-toolbar          Toolbar width and height.
+size-toolbar-button   Toolbar button size.
+size-toolbar-border   Toolbar border width and height.
+size-icon             Icon dimensions.
+size-icon-small       Small icon dimensions.
+size-device           Device screen size in pixels.
+size-workspace        Workspace size in pixels. This can be less than the
+                      above if window manager has decorations which
+                      effectively shrink the area remaining for application
+                      windows.
+size-device-mm        Device screen size in millimeters.
+device-dpi            Device resolution, in dots per inch.
+num-bit-planes        Integer, number of device bit planes.
+num-color-cells       Integer, number of device color cells.
+
+FEATURES.  This group reports various device features.  If a feature is
+present, integer 1 (one) is returned, if it is not present, then integer
+0 (zero) is returned.  If the system is unaware of the feature, then
+DEFAULT is returned.
+
+mouse-buttons         Integer, number of mouse buttons, or zero if no mouse.
+swap-buttons          Non-zero if left and right mouse buttons are swapped.
+show-sounds           User preference for visual over audible bell.
+slow-device           Device is slow, avoid animation.
+security              Non-zero if user environment is secure.
+*/
+       (device, metric, default_))
+{
+  struct device *d = decode_device (device);
+  enum device_metrics m;
+  Lisp_Object res;
+
+  /* Decode metric */
+#define FROB(met)				\
+  else if (EQ (metric, Q##met))			\
+    m = DM_##met
+
+  if (0)
+    ;
+  FROB (color_default);
+  FROB (color_select);
+  FROB (color_balloon);
+  FROB (color_3d_face);
+  FROB (color_3d_light);
+  FROB (color_3d_dark);
+  FROB (color_menu);
+  FROB (color_menu_highlight);
+  FROB (color_menu_button);
+  FROB (color_menu_disabled);
+  FROB (color_toolbar);
+  FROB (color_scrollbar);
+  FROB (color_desktop);
+  FROB (color_workspace);
+  FROB (font_default);
+  FROB (font_menubar);
+  FROB (font_dialog);
+  FROB (size_cursor);
+  FROB (size_scrollbar);
+  FROB (size_menu);
+  FROB (size_toolbar);
+  FROB (size_toolbar_button);
+  FROB (size_toolbar_border);
+  FROB (size_icon);
+  FROB (size_icon_small);
+  FROB (size_device);
+  FROB (size_workspace);
+  FROB (size_device_mm);
+  FROB (device_dpi);
+  FROB (num_bit_planes);
+  FROB (num_color_cells);
+  FROB (mouse_buttons);
+  FROB (swap_buttons);
+  FROB (show_sounds);
+  FROB (slow_device);
+  FROB (security);
+  else
+    signal_simple_error ("Invalid device metric symbol", metric);
+
+  res = DEVMETH_OR_GIVEN (d, device_system_metrics, (d, m), Qunbound);
+  return UNBOUNDP(res) ? default_ : res;
+
+#undef FROB
+}
+
+DEFUN ("device-system-metrics", Fdevice_system_metrics, 0, 1, 0, /*
+Get a property list of device metric for DEVICE.
+
+See `device-system-metric' for the description of available metrics.
+DEVICE defaults to selected device when omitted.
+*/
+       (device))
+{
+  struct device *d = decode_device (device);
+  Lisp_Object plist = Qnil, one_metric;
+
+#define FROB(m)								\
+  if (!UNBOUNDP ((one_metric =						\
+		  DEVMETH_OR_GIVEN (d, device_system_metrics,     	\
+				    (d, DM_##m), Qunbound))))		\
+    plist = Fcons (Q##m, Fcons (one_metric, plist));
+
+  FROB (color_default);
+  FROB (color_select);
+  FROB (color_balloon);
+  FROB (color_3d_face);
+  FROB (color_3d_light);
+  FROB (color_3d_dark);
+  FROB (color_menu);
+  FROB (color_menu_highlight);
+  FROB (color_menu_button);
+  FROB (color_menu_disabled);
+  FROB (color_toolbar);
+  FROB (color_scrollbar);
+  FROB (color_desktop);
+  FROB (color_workspace);
+  FROB (font_default);
+  FROB (font_menubar);
+  FROB (font_dialog);
+  FROB (size_cursor);
+  FROB (size_scrollbar);
+  FROB (size_menu);
+  FROB (size_toolbar);
+  FROB (size_toolbar_button);
+  FROB (size_toolbar_border);
+  FROB (size_icon);
+  FROB (size_icon_small);
+  FROB (size_device);
+  FROB (size_workspace);
+  FROB (size_device_mm);
+  FROB (device_dpi);
+  FROB (num_bit_planes);
+  FROB (num_color_cells);
+  FROB (mouse_buttons);
+  FROB (swap_buttons);
+  FROB (show_sounds);
+  FROB (slow_device);
+  FROB (security);
+
+  return plist;
+
+#undef FROB
+}
+
+Lisp_Object
+domain_device_type (Lisp_Object domain)
+{
+  /* This cannot GC */
+  assert (WINDOWP (domain) || FRAMEP (domain)
+	  || DEVICEP (domain) || CONSOLEP (domain));
+
+  if (WINDOWP (domain))
+    {
+      if (!WINDOW_LIVE_P (XWINDOW (domain)))
+	return Qdead;
+      domain = WINDOW_FRAME (XWINDOW (domain));
+    }
+  if (FRAMEP (domain))
+    {
+      if (!FRAME_LIVE_P (XFRAME (domain)))
+	return Qdead;
+      domain = FRAME_DEVICE (XFRAME (domain));
+    }
+  if (DEVICEP (domain))
+    {
+      if (!DEVICE_LIVE_P (XDEVICE (domain)))
+	return Qdead;
+      domain = DEVICE_CONSOLE (XDEVICE (domain));
+    }
+  return CONSOLE_TYPE (XCONSOLE (domain));
+}
+
+/*
+ * Determine whether window system bases window geometry on character
+ * or pixel counts.
+ * Return non-zero for pixel-based geometry, zero for character-based.
+ */
+int
+window_system_pixelated_geometry (Lisp_Object domain)
+{
+  /* This cannot GC */
+  Lisp_Object winsy = domain_device_type (domain);
+  struct console_methods *meth = decode_console_type (winsy, ERROR_ME_NOT);
+  assert (meth);
+  return (MAYBE_INT_CONTYPE_METH (meth, device_implementation_flags, ())
+	  & XDEVIMPF_PIXEL_GEOMETRY);
+}
+
+DEFUN ("domain-device-type", Fdomain_device_type, 0, 1, 0, /*
+Return the device type symbol for a DOMAIN, e.g. 'x or 'tty.
+DOMAIN can be either a window, frame, device or console.
+*/
+       (domain))
+{
+  if (!WINDOWP (domain) && !FRAMEP (domain)
+      && !DEVICEP (domain) && !CONSOLEP (domain))
+    signal_simple_error
+      ("Domain must be either a window, frame, device or console", domain);
+
+  return domain_device_type (domain);
+}
+
+void
+handle_asynch_device_change (void)
+{
+  int i;
+  int old_asynch_device_change_pending = asynch_device_change_pending;
+  for (i = 0; i < Dynarr_length (the_console_type_entry_dynarr); i++)
+    {
+      if (Dynarr_at (the_console_type_entry_dynarr, i).meths->
+	  asynch_device_change_method)
+	(Dynarr_at (the_console_type_entry_dynarr, i).meths->
+	 asynch_device_change_method) ();
+    }
+  /* reset the flag to 0 unless another notification occurred while
+     we were processing this one.  Block SIGWINCH during this
+     check to prevent a possible race condition. */
+#ifndef WINDOWSNT
+  EMACS_BLOCK_SIGNAL (SIGWINCH);
+#endif
+  if (old_asynch_device_change_pending == asynch_device_change_pending)
+    asynch_device_change_pending = 0;
+#ifndef WINDOWSNT
+  EMACS_UNBLOCK_SIGNAL (SIGWINCH);
+#endif
+}
+
+void
+call_critical_lisp_code (struct device *d, Lisp_Object function,
+			 Lisp_Object object)
+{
+  int old_gc_currently_forbidden = gc_currently_forbidden;
+  Lisp_Object old_inhibit_quit = Vinhibit_quit;
+
+  /* There's no reason to bother doing specbinds here, because if
+     initialize-*-faces signals an error, emacs is going to crash
+     immediately.
+     */
+  gc_currently_forbidden = 1;
+  Vinhibit_quit = Qt;
+  LOCK_DEVICE (d);
+
+  /* But it's useful to have an error handler; otherwise an infinite
+     loop may result. */
+  if (!NILP (object))
+    call1_with_handler (Qreally_early_error_handler, function, object);
+  else
+    call0_with_handler (Qreally_early_error_handler, function);
+
+  UNLOCK_DEVICE (d);
+  Vinhibit_quit = old_inhibit_quit;
+  gc_currently_forbidden = old_gc_currently_forbidden;
+}
+
+
+/************************************************************************/
+/*                            initialization                            */
+/************************************************************************/
+
+void
+syms_of_device (void)
+{
+  DEFSUBR (Fvalid_device_class_p);
+  DEFSUBR (Fdevice_class_list);
+
+  DEFSUBR (Fdfw_device);
+  DEFSUBR (Fselected_device);
+  DEFSUBR (Fselect_device);
+  DEFSUBR (Fset_device_selected_frame);
+  DEFSUBR (Fdevicep);
+  DEFSUBR (Fdevice_live_p);
+  DEFSUBR (Fdevice_name);
+  DEFSUBR (Fdevice_connection);
+  DEFSUBR (Fdevice_console);
+  DEFSUBR (Ffind_device);
+  DEFSUBR (Fget_device);
+  DEFSUBR (Fmake_device);
+  DEFSUBR (Fdelete_device);
+  DEFSUBR (Fdevice_frame_list);
+  DEFSUBR (Fdevice_class);
+  DEFSUBR (Fset_device_class);
+  DEFSUBR (Fdevice_system_metrics);
+  DEFSUBR (Fdevice_system_metric);
+  DEFSUBR (Fset_device_baud_rate);
+  DEFSUBR (Fdevice_baud_rate);
+  DEFSUBR (Fdomain_device_type);
+
+  defsymbol (&Qdevicep, "devicep");
+  defsymbol (&Qdevice_live_p, "device-live-p");
+
+  defsymbol (&Qcreate_device_hook, "create-device-hook");
+  defsymbol (&Qdelete_device_hook, "delete-device-hook");
+
+  /* Qcolor defined in general.c */
+  defsymbol (&Qgrayscale, "grayscale");
+  defsymbol (&Qmono, "mono");
+
+  /* Device metrics symbols */
+  defsymbol (&Qcolor_default, "color-default");
+  defsymbol (&Qcolor_select, "color-select");
+  defsymbol (&Qcolor_balloon, "color-balloon");
+  defsymbol (&Qcolor_3d_face, "color-3d-face");
+  defsymbol (&Qcolor_3d_light, "color-3d-light");
+  defsymbol (&Qcolor_3d_dark, "color-3d-dark");
+  defsymbol (&Qcolor_menu, "color-menu");
+  defsymbol (&Qcolor_menu_highlight, "color-menu-highlight");
+  defsymbol (&Qcolor_menu_button, "color-menu-button");
+  defsymbol (&Qcolor_menu_disabled, "color-menu-disabled");
+  defsymbol (&Qcolor_toolbar, "color-toolbar");
+  defsymbol (&Qcolor_scrollbar, "color-scrollbar");
+  defsymbol (&Qcolor_desktop, "color-desktop");
+  defsymbol (&Qcolor_workspace, "color-workspace");
+  defsymbol (&Qfont_default, "font-default");
+  defsymbol (&Qfont_menubar, "font-menubar");
+  defsymbol (&Qfont_dialog, "font-dialog");
+  defsymbol (&Qsize_cursor, "size-cursor");
+  defsymbol (&Qsize_scrollbar, "size-scrollbar");
+  defsymbol (&Qsize_menu, "size-menu");
+  defsymbol (&Qsize_toolbar, "size-toolbar");
+  defsymbol (&Qsize_toolbar_button, "size-toolbar-button");
+  defsymbol (&Qsize_toolbar_border, "size-toolbar-border");
+  defsymbol (&Qsize_icon, "size-icon");
+  defsymbol (&Qsize_icon_small, "size-icon-small");
+  defsymbol (&Qsize_device, "size-device");
+  defsymbol (&Qsize_workspace, "size-workspace");
+  defsymbol (&Qsize_device_mm, "size-device-mm");
+  defsymbol (&Qnum_bit_planes, "num-bit-planes");
+  defsymbol (&Qnum_color_cells, "num-color-cells");
+  defsymbol (&Qdevice_dpi, "device-dpi");
+  defsymbol (&Qmouse_buttons, "mouse-buttons");
+  defsymbol (&Qswap_buttons, "swap-buttons");
+  defsymbol (&Qshow_sounds, "show-sounds");
+  defsymbol (&Qslow_device, "slow-device");
+  defsymbol (&Qsecurity, "security");
+}
+
+void
+reinit_vars_of_device (void)
+{
+  staticpro_nodump (&Vdefault_device);
+  Vdefault_device = Qnil;
+  asynch_device_change_pending = 0;
+}
+
+void
+vars_of_device (void)
+{
+  reinit_vars_of_device ();
+
+  DEFVAR_LISP ("create-device-hook", &Vcreate_device_hook /*
+Function or functions to call when a device is created.
+One argument, the newly-created device.
+This is called after the first frame has been created, but before
+  calling the `create-frame-hook'.
+Note that in general the device will not be selected.
+*/ );
+  Vcreate_device_hook = Qnil;
+
+  DEFVAR_LISP ("delete-device-hook", &Vdelete_device_hook /*
+Function or functions to call when a device is deleted.
+One argument, the to-be-deleted device.
+*/ );
+  Vdelete_device_hook = Qnil;
+
+  Vdevice_class_list = list3 (Qcolor, Qgrayscale, Qmono);
+  staticpro (&Vdevice_class_list);
+
+  /* Death to devices.el !!! */
+  Fprovide(intern("devices"));
+}