diff src/device.c @ 0:376386a54a3c r19-14

Import from CVS: tag r19-14
author cvs
date Mon, 13 Aug 2007 08:45:50 +0200
parents
children 9ee227acff29
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/device.c	Mon Aug 13 08:45:50 2007 +0200
@@ -0,0 +1,1140 @@
+/* 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 "scrollbar.h"
+#include "specifier.h"
+#include "sysdep.h"
+#include "window.h"
+
+#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;
+
+Lisp_Object Qdevicep, Qdevice_live_p;
+Lisp_Object Qdelete_device;
+Lisp_Object Qcreate_device_hook;
+Lisp_Object Qdelete_device_hook;
+
+Lisp_Object Vdevice_class_list;
+
+MAC_DEFINE (struct device *, MTdevice_data)
+
+
+static Lisp_Object mark_device (Lisp_Object, void (*) (Lisp_Object));
+static void print_device (Lisp_Object, Lisp_Object, int);
+DEFINE_LRECORD_IMPLEMENTATION ("device", device,
+			       mark_device, print_device, 0, 0, 0,
+			       struct device);
+
+static Lisp_Object
+mark_device (Lisp_Object obj, void (*markobj) (Lisp_Object))
+{
+  struct device *d = XDEVICE (obj);
+
+  ((markobj) (d->name));
+  ((markobj) (d->connection));
+  ((markobj) (d->canon_connection));
+  ((markobj) (d->console));
+  ((markobj) (d->_selected_frame));
+  ((markobj) (d->frame_with_focus_real));
+  ((markobj) (d->frame_with_focus_for_hooks));
+  ((markobj) (d->frame_that_ought_to_have_focus));
+  ((markobj) (d->device_class));
+  ((markobj) (d->user_defined_tags));
+  ((markobj) (d->pixel_to_glyph_cache.obj1));
+  ((markobj) (d->pixel_to_glyph_cache.obj2));
+
+  ((markobj) (d->color_instance_cache));
+  ((markobj) (d->font_instance_cache));
+  ((markobj) (d->image_instance_cache));
+
+  if (d->devmeths)
+    {
+      ((markobj) (d->devmeths->symbol));
+      MAYBE_DEVMETH (d, mark_device, (d, markobj));
+    }
+
+  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>",
+	   string_data (XSTRING (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);
+}
+
+
+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, Svalid_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)
+     Lisp_Object device_class;
+{
+  if (valid_device_class_p (device_class))
+    return Qt;
+  else
+    return Qnil;
+}
+
+DEFUN ("device-class-list", Fdevice_class_list, Sdevice_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 = Qnil;
+  struct device *d = alloc_lcrecord (sizeof (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_hashtable (20, HASHTABLE_KEY_WEAK,
+						 HASHTABLE_EQUAL);
+  d->font_instance_cache = make_lisp_hashtable (20, HASHTABLE_KEY_WEAK,
+						HASHTABLE_EQUAL);
+  /*
+     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 diferent masks that will be used.
+     */
+  d->image_instance_cache = make_lisp_hashtable (5, HASHTABLE_NONWEAK,
+						 HASHTABLE_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 */
+  if (FRAMEP (device))
+    device = FRAME_DEVICE (decode_frame (device));
+  CHECK_LIVE_DEVICE (device);
+  return XDEVICE (device);
+}
+
+Lisp_Object
+make_device (struct device *d)
+{
+  Lisp_Object device = Qnil;
+  XSETDEVICE (device, d);
+  return device;
+}
+
+DEFUN ("dfw-device", Fdfw_device, Sdfw_device, 1, 1, 0 /*
+Given a device, frame, or window, return the associated device.
+Return nil otherwise.
+*/ )
+     (obj)
+     Lisp_Object obj;
+{
+  return DFW_DEVICE (obj);
+}
+
+
+DEFUN ("selected-device", Fselected_device, Sselected_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)
+     Lisp_Object 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, Sselect_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)
+     Lisp_Object 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)))));
+  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,
+       Sset_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)
+     Lisp_Object 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, Sdevicep, 1, 1, 0 /*
+Return non-nil if OBJECT is a device.
+*/ )
+     (object)
+     Lisp_Object object;
+{
+  if (!DEVICEP (object))
+    return Qnil;
+  return Qt;
+}
+
+DEFUN ("device-live-p", Fdevice_live_p, Sdevice_live_p, 1, 1, 0 /*
+Return non-nil if OBJECT is a device that has not been deleted.
+*/ )
+     (object)
+     Lisp_Object object;
+{
+  if (!DEVICEP (object) || !DEVICE_LIVE_P (XDEVICE (object)))
+    return Qnil;
+  return Qt;
+}
+
+DEFUN ("device-name", Fdevice_name, Sdevice_name, 0, 1, 0 /*
+Return the name of the specified device.
+DEVICE defaults to the selected device if omitted.
+*/ )
+     (device)
+     Lisp_Object device;
+{
+  return DEVICE_NAME (decode_device (device));
+}
+
+DEFUN ("device-connection", Fdevice_connection, Sdevice_connection, 0, 1, 0 /*
+Return the connection of the specified device.
+DEVICE defaults to the selected device if omitted.
+*/ )
+     (device)
+     Lisp_Object device;
+{
+  return DEVICE_CONNECTION (decode_device (device));
+}
+
+DEFUN ("device-console", Fdevice_console, Sdevice_console, 0, 1, 0 /*
+Return the console of the specified device.
+DEVICE defaults to the selected device if omitted.
+*/ )
+     (device)
+     Lisp_Object device;
+{
+  return DEVICE_CONSOLE (decode_device (device));
+}
+
+#ifdef HAVE_X_WINDOWS
+extern Lisp_Object Vdefault_x_device;
+#endif
+#ifdef HAVE_NEXTSTEP
+extern Lisp_Object Vdefault_ns_device;
+#endif
+
+#ifdef HAVE_X_WINDOWS
+
+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)))
+	  && !NILP (Fequal (DEVICE_CANON_CONNECTION (XDEVICE (device)),
+			    canon)))
+	return device;
+    }
+
+  return Qnil;
+}
+
+DEFUN ("find-device", Ffind_device, Sfind_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 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, Sget_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 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;
+}
+
+DEFUN ("make-device", Fmake_device, Smake_device, 2, 3, 0 /*
+Create 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)
+     Lisp_Object 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;
+
+  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 =
+      CONTYPE_METH_OR_GIVEN (conmeths,
+			     device_to_console_connection,
+			     (connection, ERROR_ME),
+			     connection);
+    console = create_console (name, type, conconnect, props);
+  }
+
+  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;
+  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 = Qnil;
+  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 = Qnil;
+  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)
+{
+  delete_device_internal (XDEVICE (device), 1, 0, 1);
+}
+
+DEFUN ("delete-device", Fdelete_device, Sdelete_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)
+     Lisp_Object device, force;
+{
+  CHECK_DEVICE (device);
+  delete_device_internal (XDEVICE (device), !NILP (force), 0, 0);
+  return Qnil;
+}
+
+DEFUN ("device-frame-list", Fdevice_frame_list, Sdevice_frame_list,
+       0, 1, 0 /*
+Return a list of all frames on DEVICE.
+If DEVICE is nil, the selected device will be used.
+*/ )
+  (device)
+     Lisp_Object device;
+{
+  return Fcopy_sequence (DEVICE_FRAME_LIST (decode_device (device)));
+}
+
+DEFUN ("device-class", Fdevice_class, Sdevice_class,
+       0, 1, 0 /*
+Return the class (color behavior) of DEVICE.
+This will be one of 'color, 'grayscale, or 'mono.
+*/ )
+     (device)
+     Lisp_Object device;
+{
+  return DEVICE_CLASS (decode_device (device));
+}
+
+DEFUN ("set-device-class", Fset_device_class, Sset_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)
+     Lisp_Object 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);
+  DEVICE_CLASS (d) = class;
+  return Qnil;
+}
+
+DEFUN ("device-pixel-width", Fdevice_pixel_width, Sdevice_pixel_width,
+       0, 1, 0 /*
+Return the width in pixels of DEVICE, or nil if unknown.
+*/ )
+     (device)
+  Lisp_Object device;
+{
+  struct device *d = decode_device (device);
+  int retval;
+
+  retval = DEVMETH_OR_GIVEN (d, device_pixel_width, (d), 0);
+  if (retval <= 0)
+    return Qnil;
+
+  return make_int (retval);
+}
+
+DEFUN ("device-pixel-height", Fdevice_pixel_height, Sdevice_pixel_height,
+       0, 1, 0 /*
+Return the height in pixels of DEVICE, or nil if unknown.
+*/ )
+     (device)
+  Lisp_Object device;
+{
+  struct device *d = decode_device (device);
+  int retval;
+
+  retval = DEVMETH_OR_GIVEN (d, device_pixel_height, (d), 0);
+  if (retval <= 0)
+    return Qnil;
+
+  return make_int (retval);
+}
+
+DEFUN ("device-mm-width", Fdevice_mm_width, Sdevice_mm_width,
+       0, 1, 0 /*
+Return the width in millimeters of DEVICE, or nil if unknown.
+*/ )
+     (device)
+  Lisp_Object device;
+{
+  struct device *d = decode_device (device);
+  int retval;
+
+  retval = DEVMETH_OR_GIVEN (d, device_mm_width, (d), 0);
+  if (retval <= 0)
+    return Qnil;
+
+  return make_int (retval);
+}
+
+DEFUN ("device-mm-height", Fdevice_mm_height, Sdevice_mm_height,
+       0, 1, 0 /*
+Return the height in millimeters of DEVICE, or nil if unknown.
+*/ )
+     (device)
+  Lisp_Object device;
+{
+  struct device *d = decode_device (device);
+  int retval;
+
+  retval = DEVMETH_OR_GIVEN (d, device_mm_height, (d), 0);
+  if (retval <= 0)
+    return Qnil;
+
+  return make_int (retval);
+}
+
+DEFUN ("device-bitplanes", Fdevice_bitplanes, Sdevice_bitplanes,
+       0, 1, 0 /*
+Return the number of bitplanes of DEVICE, or nil if unknown.
+*/ )
+     (device)
+  Lisp_Object device;
+{
+  struct device *d = decode_device (device);
+  int retval;
+
+  retval = DEVMETH_OR_GIVEN (d, device_bitplanes, (d), 0);
+  if (retval <= 0)
+    return Qnil;
+
+  return make_int (retval);
+}
+
+DEFUN ("device-color-cells", Fdevice_color_cells, Sdevice_color_cells,
+       0, 1, 0 /*
+Return the number of color cells of DEVICE, or nil if unknown.
+*/ )
+     (device)
+  Lisp_Object device;
+{
+  struct device *d = decode_device (device);
+  int retval;
+
+  retval = DEVMETH_OR_GIVEN (d, device_color_cells, (d), 0);
+  if (retval <= 0)
+    return Qnil;
+
+  return make_int (retval);
+}
+
+DEFUN ("set-device-baud-rate", Fset_device_baud_rate, Sset_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)
+     Lisp_Object device, rate;
+{
+  CHECK_INT (rate);
+
+  DEVICE_BAUD_RATE (decode_device (device)) = XINT (rate);
+
+  return rate;
+}
+
+DEFUN ("device-baud-rate", Fdevice_baud_rate, Sdevice_baud_rate,
+       0, 1, 0 /*
+Return the output baud rate of DEVICE.
+*/ )
+     (device)
+     Lisp_Object device;
+{
+  return make_int (DEVICE_BAUD_RATE (decode_device (device)));
+}
+
+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. */
+  EMACS_BLOCK_SIGNAL (SIGWINCH);
+  if (old_asynch_device_change_pending == asynch_device_change_pending)
+    asynch_device_change_pending = 0;
+  EMACS_UNBLOCK_SIGNAL (SIGWINCH);
+}
+
+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 (&Svalid_device_class_p);
+  defsubr (&Sdevice_class_list);
+
+  defsubr (&Sdfw_device);
+  defsubr (&Sselected_device);
+  defsubr (&Sselect_device);
+  defsubr (&Sset_device_selected_frame);
+  defsubr (&Sdevicep);
+  defsubr (&Sdevice_live_p);
+  defsubr (&Sdevice_name);
+  defsubr (&Sdevice_connection);
+  defsubr (&Sdevice_console);
+  defsubr (&Sfind_device);
+  defsubr (&Sget_device);
+  defsubr (&Smake_device);
+  defsubr (&Sdelete_device);
+  defsubr (&Sdevice_frame_list);
+  defsubr (&Sdevice_class);
+  defsubr (&Sset_device_class);
+  defsubr (&Sdevice_pixel_width);
+  defsubr (&Sdevice_pixel_height);
+  defsubr (&Sdevice_mm_width);
+  defsubr (&Sdevice_mm_height);
+  defsubr (&Sdevice_bitplanes);
+  defsubr (&Sdevice_color_cells);
+  defsubr (&Sset_device_baud_rate);
+  defsubr (&Sdevice_baud_rate);
+
+  defsymbol (&Qdevicep, "devicep");
+  defsymbol (&Qdevice_live_p, "device-live-p");
+  defsymbol (&Qdelete_device, "delete-device");
+
+  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");
+}
+
+void
+vars_of_device (void)
+{
+  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;
+
+  staticpro (&Vdefault_device);
+  Vdefault_device = Qnil;
+
+  asynch_device_change_pending = 0;
+
+  Vdevice_class_list = list3 (Qcolor, Qgrayscale, Qmono);
+  staticpro (&Vdevice_class_list);
+}