Removed %T in mode-line-format. Trivial documentation changes.
[bpt/emacs.git] / src / keyboard.c
index 069c605..b6f1dfa 100644 (file)
@@ -1,5 +1,5 @@
 /* Keyboard and mouse input; editor command loop.
-   Copyright (C) 1985,86,87,88,89,93,94,95,96,97,99,2000,01,02,03
+   Copyright (C) 1985,86,87,88,89,93,94,95,96,97,99,2000,01,02,03,04
      Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
@@ -23,13 +23,12 @@ Boston, MA 02111-1307, USA.  */
 #include <signal.h>
 #include <stdio.h>
 #include "lisp.h"
-#include "systty.h" /* This must be included befor termchar.h. */
 #include "termchar.h"
 #include "termopts.h"
+#include "frame.h"
 #include "termhooks.h"
 #include "macros.h"
 #include "keyboard.h"
-#include "frame.h"
 #include "window.h"
 #include "commands.h"
 #include "buffer.h"
@@ -461,11 +460,6 @@ FILE *dribble;
 /* Nonzero if input is available.  */
 int input_pending;
 
-/* 1 if should obey 0200 bit in input chars as "Meta", 2 if should
-   keep 0200 bit in input chars.  0 to ignore the 0200 bit.  */
-
-int meta_key;
-
 /* Non-zero means force key bindings update in parse_menu_item.  */
 
 int update_menu_bindings;
@@ -476,36 +470,6 @@ extern char *pending_malloc_warning;
 
 static struct input_event kbd_buffer[KBD_BUFFER_SIZE];
 
-/* Vector to GCPRO the Lisp objects referenced from kbd_buffer.
-
-   The interrupt-level event handlers will never enqueue an event on a
-   frame which is not in Vframe_list, and once an event is dequeued,
-   internal_last_event_frame or the event itself points to the frame.
-   So that's all fine.
-
-   But while the event is sitting in the queue, it's completely
-   unprotected.  Suppose the user types one command which will run for
-   a while and then delete a frame, and then types another event at
-   the frame that will be deleted, before the command gets around to
-   it.  Suppose there are no references to this frame elsewhere in
-   Emacs, and a GC occurs before the second event is dequeued.  Now we
-   have an event referring to a freed frame, which will crash Emacs
-   when it is dequeued.
-
-   Similar things happen when an event on a scroll bar is enqueued; the
-   window may be deleted while the event is in the queue.
-
-   So, we use this vector to protect the Lisp_Objects in the event
-   queue.  That way, they'll be dequeued as dead frames or windows,
-   but still valid Lisp objects.
-
-   If kbd_buffer[i].kind != NO_EVENT, then
-
-   AREF (kbd_buffer_gcpro, 2 * i) == kbd_buffer[i].frame_or_window.
-   AREF (kbd_buffer_gcpro, 2 * i + 1) == kbd_buffer[i].arg.  */
-
-static Lisp_Object kbd_buffer_gcpro;
-
 /* Pointer to next available character in kbd_buffer.
    If kbd_fetch_ptr == kbd_store_ptr, the buffer is empty.
    This may be kbd_buffer + KBD_BUFFER_SIZE, meaning that the
@@ -584,7 +548,7 @@ Lisp_Object Qvertical_scroll_bar;
 Lisp_Object Qmenu_bar;
 extern Lisp_Object Qleft_margin, Qright_margin;
 extern Lisp_Object Qleft_fringe, Qright_fringe;
-extern Lisp_Object Qimage;
+extern Lisp_Object QCmap;
 
 Lisp_Object recursive_edit_unwind (), command_loop ();
 Lisp_Object Fthis_command_keys ();
@@ -622,9 +586,6 @@ int interrupt_input;
 /* Nonzero while interrupts are temporarily deferred during redisplay.  */
 int interrupts_deferred;
 
-/* Nonzero means use ^S/^Q for flow control.  */
-int flow_control;
-
 /* Allow m- file to inhibit use of FIONREAD.  */
 #ifdef BROKEN_FIONREAD
 #undef FIONREAD
@@ -701,6 +662,7 @@ static Lisp_Object apply_modifiers P_ ((int, Lisp_Object));
 static void clear_event P_ ((struct input_event *));
 static void any_kboard_state P_ ((void));
 static SIGTYPE interrupt_signal P_ ((int signalnum));
+static void handle_interrupt P_ ((void));
 
 /* Nonzero means don't try to suspend even if the operating system seems
    to support it.  */
@@ -1211,9 +1173,7 @@ cmd_error_internal (data, context)
   if (!sf->glyphs_initialized_p
       /* This is the case of the frame dumped with Emacs, when we're
         running under a window system.  */
-      || (!NILP (Vwindow_system)
-         && !inhibit_window_system
-         && FRAME_TERMCAP_P (sf))
+      || FRAME_INITIAL_P (sf)
       || noninteractive)
     {
       stream = Qexternal_debugging_output;
@@ -2072,7 +2032,10 @@ void
 start_polling ()
 {
 #ifdef POLL_FOR_INPUT
-  if (read_socket_hook && !interrupt_input)
+  /* XXX This condition was (read_socket_hook && !interrupt_input),
+     but read_socket_hook is not global anymore.  Let's pretend that
+     it's always set. */
+  if (!interrupt_input)
     {
       /* Turn alarm handling on unconditionally.  It might have
         been turned off in process.c.  */
@@ -2106,7 +2069,10 @@ int
 input_polling_used ()
 {
 #ifdef POLL_FOR_INPUT
-  return read_socket_hook && !interrupt_input;
+  /* XXX This condition was (read_socket_hook && !interrupt_input),
+     but read_socket_hook is not global anymore.  Let's pretend that
+     it's always set. */
+  return !interrupt_input;
 #else
   return 0;
 #endif
@@ -2118,7 +2084,10 @@ void
 stop_polling ()
 {
 #ifdef POLL_FOR_INPUT
-  if (read_socket_hook && !interrupt_input)
+  /* XXX This condition was (read_socket_hook && !interrupt_input),
+     but read_socket_hook is not global anymore.  Let's pretend that
+     it's always set. */
+  if (!interrupt_input)
     ++poll_suppress_count;
 #endif
 }
@@ -2940,13 +2909,13 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu)
     {
       Lisp_Object posn;
 
-      posn = POSN_BUFFER_POSN (EVENT_START (c));
+      posn = POSN_POSN (EVENT_START (c));
       /* Handle menu-bar events:
         insert the dummy prefix event `menu-bar'.  */
       if (EQ (posn, Qmenu_bar) || EQ (posn, Qtool_bar))
        {
          /* Change menu-bar to (menu-bar) as the event "position".  */
-         POSN_BUFFER_SET_POSN (EVENT_START (c), Fcons (posn, Qnil));
+         POSN_SET_POSN (EVENT_START (c), Fcons (posn, Qnil));
 
          also_record = c;
          Vunread_command_events = Fcons (c, Vunread_command_events);
@@ -3605,7 +3574,7 @@ kbd_buffer_store_event (event)
          }
 
          last_event_timestamp = event->timestamp;
-         interrupt_signal (0 /* dummy */);
+         handle_interrupt ();
          return;
        }
 
@@ -3631,7 +3600,6 @@ kbd_buffer_store_event (event)
      Discard the event if it would fill the last slot.  */
   if (kbd_fetch_ptr - 1 != kbd_store_ptr)
     {
-      int idx;
 
 #if 0 /* The SELECTION_REQUEST_EVENT case looks bogus, and it's error
         prone to assign individual members for other events, in case
@@ -3661,9 +3629,6 @@ kbd_buffer_store_event (event)
       *kbd_store_ptr = *event;
 #endif
 
-      idx = 2 * (kbd_store_ptr - kbd_buffer);
-      ASET (kbd_buffer_gcpro, idx, event->frame_or_window);
-      ASET (kbd_buffer_gcpro, idx + 1, event->arg);
       ++kbd_store_ptr;
     }
 }
@@ -3779,9 +3744,6 @@ static INLINE void
 clear_event (event)
      struct input_event *event;
 {
-  int idx = 2 * (event - kbd_buffer);
-  ASET (kbd_buffer_gcpro, idx, Qnil);
-  ASET (kbd_buffer_gcpro, idx + 1, Qnil);
   event->kind = NO_EVENT;
 }
 
@@ -4088,7 +4050,8 @@ kbd_buffer_get_event (kbp, used_mouse_menu)
         If there is no valid info, it does not store anything
         so x remains nil.  */
       x = Qnil;
-      (*mouse_position_hook) (&f, 0, &bar_window, &part, &x, &y, &time);
+      if (f && FRAME_DISPLAY (f)->mouse_position_hook) /* XXX Can f or mouse_position_hook be NULL here? */
+        (*FRAME_DISPLAY (f)->mouse_position_hook) (&f, 0, &bar_window, &part, &x, &y, &time);
 
       obj = Qnil;
 
@@ -4979,9 +4942,11 @@ make_lispy_position (f, x, y, time)
     {
       /* It's a click in window window at frame coordinates (x,y)  */
       struct window *w = XWINDOW (window);
-      Lisp_Object object = Qnil;
+      Lisp_Object string_info = Qnil;
       int textpos = -1, rx = -1, ry = -1;
       int dx = -1, dy = -1;
+      int width = -1, height = -1;
+      Lisp_Object object = Qnil;
 
       /* Set event coordinates to window-relative coordinates
         for constructing the Lisp event below.  */
@@ -4997,9 +4962,10 @@ make_lispy_position (f, x, y, time)
 
          posn = part == ON_MODE_LINE ? Qmode_line : Qheader_line;
          rx = wx, ry = wy;
-         string = mode_line_string (w, &rx, &ry, &dx, &dy, part, &charpos);
+         string = mode_line_string (w, part, &rx, &ry, &charpos,
+                                    &object, &dx, &dy, &width, &height);
          if (STRINGP (string))
-           object = Fcons (string, make_number (charpos));
+           string_info = Fcons (string, make_number (charpos));
          if (w == XWINDOW (selected_window))
            textpos = PT;
          else
@@ -5010,6 +4976,7 @@ make_lispy_position (f, x, y, time)
          posn = Qvertical_line;
          wx = -1;
          dx = 0;
+         width = 1;
        }
       else if (part == ON_LEFT_MARGIN || part == ON_RIGHT_MARGIN)
        {
@@ -5018,9 +4985,10 @@ make_lispy_position (f, x, y, time)
          
          posn = (part == ON_LEFT_MARGIN) ? Qleft_margin : Qright_margin;
          rx = wx, ry = wy;
-         string = marginal_area_string (w, &rx, &ry, &dx, &dy, part, &charpos);
+         string = marginal_area_string (w, part, &rx, &ry, &charpos,
+                                        &object, &dx, &dy, &width, &height);
          if (STRINGP (string))
-           object = Fcons (string, make_number (charpos));
+           string_info = Fcons (string, make_number (charpos));
        }
       else if (part == ON_LEFT_FRINGE || part == ON_RIGHT_FRINGE)
        {
@@ -5039,35 +5007,60 @@ make_lispy_position (f, x, y, time)
 
       if (textpos < 0)
        {
-         Lisp_Object string;
+         Lisp_Object string2, object2 = Qnil;
          struct display_pos p;
          int dx2, dy2;
+         int width2, height2;
          wx = max (WINDOW_LEFT_MARGIN_WIDTH (w), wx);
-         buffer_posn_from_coords (w, &wx, &wy, &dx2, &dy2, &string, &p);
+         string2 = buffer_posn_from_coords (w, &wx, &wy, &p,
+                                            &object2, &dx2, &dy2,
+                                            &width2, &height2);
          textpos = CHARPOS (p.pos);
          if (rx < 0) rx = wx;
          if (ry < 0) ry = wy;
          if (dx < 0) dx = dx2;
          if (dy < 0) dy = dy2;
+         if (width < 0) width = width2;
+         if (height < 0) height = height2;
 
          if (NILP (posn))
            {
              posn = make_number (textpos);
-             if (STRINGP (string))
-               object = Fcons (string,
-                               make_number (CHARPOS (p.string_pos)));
-             else if (CONSP (string) && EQ (XCAR (string), Qimage))
-               object = string;
+             if (STRINGP (string2))
+               string_info = Fcons (string2,
+                                    make_number (CHARPOS (p.string_pos)));
            }
+         if (NILP (object))
+           object = object2;
        }
 
+#ifdef HAVE_WINDOW_SYSTEM
+      if (IMAGEP (object))
+       {
+         Lisp_Object image_map, hotspot;
+         if ((image_map = Fplist_get (XCDR (object), QCmap),
+              !NILP (image_map))
+             && (hotspot = find_hot_spot (image_map, dx, dy),
+                 CONSP (hotspot))
+             && (hotspot = XCDR (hotspot), CONSP (hotspot)))
+           posn = XCAR (hotspot);
+       }
+#endif
+
+      /* Object info */
       extra_info = Fcons (object,
+                         Fcons (Fcons (make_number (dx),
+                                       make_number (dy)),
+                                Fcons (Fcons (make_number (width),
+                                              make_number (height)),
+                                       Qnil)));
+
+      /* String info */
+      extra_info = Fcons (string_info,
                          Fcons (make_number (textpos),
                                 Fcons (Fcons (make_number (rx),
                                               make_number (ry)),
-                                       Fcons (Fcons (make_number (dx),
-                                                     make_number (dy)),
-                                              Qnil))));
+                                       extra_info)));
     }
   else if (f != 0)
     {
@@ -6460,7 +6453,7 @@ get_filtered_input_pending (addr, do_timers_now, filter_events)
   /* If input is being read as it arrives, and we have none, there is none.  */
   if (*addr > 0 || (interrupt_input && ! interrupts_deferred))
     return;
-
+  
   /* Try to read some input and see how much we get.  */
   gobble_input (0);
   *addr = (!NILP (Vquit_flag)
@@ -6499,7 +6492,10 @@ gobble_input (expected)
     }
   else
 #ifdef POLL_FOR_INPUT
-  if (read_socket_hook && !interrupt_input && poll_suppress_count == 0)
+  /* XXX This condition was (read_socket_hook && !interrupt_input),
+     but read_socket_hook is not global anymore.  Let's pretend that
+     it's always set. */
+  if (!interrupt_input && poll_suppress_count == 0)
     {
       SIGMASKTYPE mask;
       mask = sigblock (sigmask (SIGALRM));
@@ -6578,174 +6574,227 @@ read_avail_input (expected)
 {
   struct input_event buf[KBD_BUFFER_SIZE];
   register int i;
-  int nread;
-
+  struct display *d;
+  int nread = 0;
+  
   for (i = 0; i < KBD_BUFFER_SIZE; i++)
     EVENT_INIT (buf[i]);
 
-  if (read_socket_hook)
-    /* No need for FIONREAD or fcntl; just say don't wait.  */
-    nread = (*read_socket_hook) (buf, KBD_BUFFER_SIZE, expected);
-  else
+  d = display_list;
+  while (d)
+    {
+      struct display *next = d->next_display;
+      
+      if (d->read_socket_hook)
+        /* No need for FIONREAD or fcntl; just say don't wait.  */
+        nread = (*d->read_socket_hook) (d, buf, KBD_BUFFER_SIZE, expected);
+
+      if (nread == -2)
+        {
+          /* The display device terminated; it should be closed. */
+
+          /* Kill Emacs if this was our last display. */
+          if (! display_list->next_display)
+            kill (getpid (), SIGHUP);
+
+          /* XXX Is calling delete_display safe here?  It calls Fdelete_frame. */
+          if (d->delete_display_hook)
+            (*d->delete_display_hook) (d);
+          else
+            delete_display (d);
+        }
+      else if (nread > 0)
+        {
+          /* We've got input. */
+          break;
+        }
+
+      d = next;
+    }
+
+  /* Scan the chars for C-g and store them in kbd_buffer.  */
+  for (i = 0; i < nread; i++)
     {
-      /* Using KBD_BUFFER_SIZE - 1 here avoids reading more than
-        the kbd_buffer can really hold.  That may prevent loss
-        of characters on some systems when input is stuffed at us.  */
-      unsigned char cbuf[KBD_BUFFER_SIZE - 1];
-      int n_to_read;
+      kbd_buffer_store_event (&buf[i]);
+      /* Don't look at input that follows a C-g too closely.
+        This reduces lossage due to autorepeat on C-g.  */
+      if (buf[i].kind == ASCII_KEYSTROKE_EVENT
+         && buf[i].code == quit_char)
+       break;
+    }
+
+  return nread;
+}
+
+/* This is the tty way of reading available input.
 
+   Note that each terminal device has its own `struct display' object,
+   and so this function is called once for each individual termcap
+   display.  The first parameter indicates which device to read from.  */
+
+int
+tty_read_avail_input (struct display *display,
+                      struct input_event *buf,
+                      int numchars, int expected)
+{
+  /* Using KBD_BUFFER_SIZE - 1 here avoids reading more than
+     the kbd_buffer can really hold.  That may prevent loss
+     of characters on some systems when input is stuffed at us.  */
+  unsigned char cbuf[KBD_BUFFER_SIZE - 1];
+  int n_to_read, i;
+  struct tty_display_info *tty = display->display_info.tty;
+  Lisp_Object frame;
+  int nread = 0;
+  
+  if (display->type != output_termcap)
+    abort ();
+  
+  /* XXX I think the following code should be moved to separate
+     functions in system-dependent files. */
 #ifdef WINDOWSNT
-      return 0;
+  return 0;
 #else /* not WINDOWSNT */
 #ifdef MSDOS
-      n_to_read = dos_keysns ();
-      if (n_to_read == 0)
-       return 0;
-
-      cbuf[0] = dos_keyread ();
-      nread = 1;
-
+  n_to_read = dos_keysns ();
+  if (n_to_read == 0)
+    return 0;
+  
+  cbuf[0] = dos_keyread ();
+  nread = 1;
+  
 #else /* not MSDOS */
 
-      struct tty_output *tty;
-      nread = 0;
-
-      /* Try to read from each available tty, until one succeeds. */
-      for (tty = tty_list; tty && !nread; tty = tty->next) {
+  if (! tty->term_initted)
+    return 0;
         
-        /* Determine how many characters we should *try* to read.  */
+  /* Determine how many characters we should *try* to read.  */
 #ifdef FIONREAD
-        /* Find out how much input is available.  */
-        if (ioctl (fileno (TTY_INPUT (tty)), FIONREAD, &n_to_read) < 0)
-          {
-            /* Formerly simply reported no input, but that sometimes led to
-               a failure of Emacs to terminate.
-               SIGHUP seems appropriate if we can't reach the terminal.  */
-            /* ??? Is it really right to send the signal just to this process
-               rather than to the whole process group?
-               Perhaps on systems with FIONREAD Emacs is alone in its group.  */
-            if (! noninteractive)
-              {
-                if (! tty_list->next)
-                  kill (getpid (), SIGHUP); /* This was the last terminal. */
-                else
-                  n_to_read = 0; /* XXX tty should be closed here. */
-              }
-            else
-              {
-                n_to_read = 0;
-              }
-          }
-        if (n_to_read == 0)
-          continue;
-        if (n_to_read > sizeof cbuf)
-          n_to_read = sizeof cbuf;
+  /* Find out how much input is available.  */
+  if (ioctl (fileno (TTY_INPUT (tty)), FIONREAD, &n_to_read) < 0)
+    {
+      if (! noninteractive)
+        return -2;          /* Close this display. */
+      else
+        n_to_read = 0;
+    }
+  if (n_to_read == 0)
+    return 0;
+  if (n_to_read > sizeof cbuf)
+    n_to_read = sizeof cbuf;
 #else /* no FIONREAD */
 #if defined (USG) || defined (DGUX) || defined(CYGWIN)
-        /* Read some input if available, but don't wait.  */
-        n_to_read = sizeof cbuf;
-        fcntl (fileno (TTY_INPUT (tty)), F_SETFL, O_NDELAY);
+  /* Read some input if available, but don't wait.  */
+  n_to_read = sizeof cbuf;
+  fcntl (fileno (TTY_INPUT (tty)), F_SETFL, O_NDELAY);
 #else
-        you lose;
+  you lose;
 #endif
 #endif
-
-        /* Now read; for one reason or another, this will not block.
-           NREAD is set to the number of chars read.  */
-        do
-          {
-            nread = emacs_read (fileno (TTY_INPUT (tty)), cbuf, n_to_read);
-            /* POSIX infers that processes which are not in the session leader's
-               process group won't get SIGHUP's at logout time.  BSDI adheres to
-               this part standard and returns -1 from read (0) with errno==EIO
-               when the control tty is taken away.
-               Jeffrey Honig <jch@bsdi.com> says this is generally safe.  */
-            if (nread == -1 && errno == EIO)
-              {
-                if (! tty_list->next)
-                  kill (0, SIGHUP); /* This was the last terminal. */
-                else
-                  ;             /* XXX tty should be closed here. */
-              }
+  
+  /* Now read; for one reason or another, this will not block.
+     NREAD is set to the number of chars read.  */
+  do
+    {
+      nread = emacs_read (fileno (TTY_INPUT (tty)), cbuf, n_to_read);
+      /* POSIX infers that processes which are not in the session leader's
+         process group won't get SIGHUP's at logout time.  BSDI adheres to
+         this part standard and returns -1 from read (0) with errno==EIO
+         when the control tty is taken away.
+         Jeffrey Honig <jch@bsdi.com> says this is generally safe.  */
+      if (nread == -1 && errno == EIO)
+        {
+          return -2;          /* Close this display. */
+        }
 #if defined (AIX) && (! defined (aix386) && defined (_BSD))
-            /* The kernel sometimes fails to deliver SIGHUP for ptys.
-               This looks incorrect, but it isn't, because _BSD causes
-               O_NDELAY to be defined in fcntl.h as O_NONBLOCK,
-               and that causes a value other than 0 when there is no input.  */
-            if (nread == 0)
-              {
-                if (! tty_list->next)
-                  kill (0, SIGHUP); /* This was the last terminal. */
-                else
-                  ;             /* XXX tty should be closed here. */
-              }
+      /* The kernel sometimes fails to deliver SIGHUP for ptys.
+         This looks incorrect, but it isn't, because _BSD causes
+         O_NDELAY to be defined in fcntl.h as O_NONBLOCK,
+         and that causes a value other than 0 when there is no input.  */
+      if (nread == 0)
+        {
+          return -2;          /* Close this display. */
+        }
 #endif
-          }
-        while (
-               /* We used to retry the read if it was interrupted.
-                  But this does the wrong thing when O_NDELAY causes
-                  an EAGAIN error.  Does anybody know of a situation
-                  where a retry is actually needed?  */
+    }
+  while (
+         /* We used to retry the read if it was interrupted.
+            But this does the wrong thing when O_NDELAY causes
+            an EAGAIN error.  Does anybody know of a situation
+            where a retry is actually needed?  */
 #if 0
-               nread < 0 && (errno == EAGAIN
+         nread < 0 && (errno == EAGAIN
 #ifdef EFAULT
-                             || errno == EFAULT
+                       || errno == EFAULT
 #endif
 #ifdef EBADSLT
-                             || errno == EBADSLT
+                       || errno == EBADSLT
 #endif
-                             )
+                       )
 #else
-               0
+         0
 #endif
-               );
-        
+         );
+  
 #ifndef FIONREAD
 #if defined (USG) || defined (DGUX) || defined (CYGWIN)
-        fcntl (fileno (TTY_INPUT (tty)), F_SETFL, 0);
+  fcntl (fileno (TTY_INPUT (tty)), F_SETFL, 0);
 #endif /* USG or DGUX or CYGWIN */
 #endif /* no FIONREAD */
-
-      } /* for each tty */
-      
-      if (! nread)
-        return 0;
-      
+  
+  if (nread <= 0)
+    return nread;
+  
 #endif /* not MSDOS */
 #endif /* not WINDOWSNT */
-
-      /* XXX Select frame corresponding to the tty. */
-      
-      for (i = 0; i < nread; i++)
-       {
-         buf[i].kind = ASCII_KEYSTROKE_EVENT;
-         buf[i].modifiers = 0;
-         if (meta_key == 1 && (cbuf[i] & 0x80))
-           buf[i].modifiers = meta_modifier;
-         if (meta_key != 2)
-           cbuf[i] &= ~0x80;
-
-         buf[i].code = cbuf[i];
-         buf[i].frame_or_window = selected_frame;
-         buf[i].arg = Qnil;
-       }
-    }
-
-  /* Scan the chars for C-g and store them in kbd_buffer.  */
+  
+  /* Select the frame corresponding to the active tty.  Note that the
+     value of selected_frame is not reliable here, redisplay tends to
+     temporarily change it. */
+  frame = tty->top_frame;
+  
   for (i = 0; i < nread; i++)
     {
-      kbd_buffer_store_event (&buf[i]);
-      /* Don't look at input that follows a C-g too closely.
-        This reduces lossage due to autorepeat on C-g.  */
-      if (buf[i].kind == ASCII_KEYSTROKE_EVENT
-         && buf[i].code == quit_char)
-       break;
+      buf[i].kind = ASCII_KEYSTROKE_EVENT;
+      buf[i].modifiers = 0;
+      if (tty->meta_key == 1 && (cbuf[i] & 0x80))
+        buf[i].modifiers = meta_modifier;
+      if (tty->meta_key != 2)
+        cbuf[i] &= ~0x80;
+      
+      buf[i].code = cbuf[i];
+      buf[i].frame_or_window = frame;
+      buf[i].arg = Qnil;
     }
 
   return nread;
 }
+
 #endif /* not VMS */
 \f
+void
+handle_async_input ()
+{
+#ifdef BSD4_1
+  extern int select_alarmed;
+#endif
+  interrupt_input_pending = 0;
+
+  while (1)
+    {
+      int nread;
+      nread = read_avail_input (1);
+      /* -1 means it's not ok to read the input now.
+        UNBLOCK_INPUT will read it later; now, avoid infinite loop.
+        0 means there was no keyboard input available.  */
+      if (nread <= 0)
+       break;
+
+#ifdef BSD4_1
+      select_alarmed = 1;  /* Force the select emulator back to life */
+#endif
+    }
+}
+
 #ifdef SIGIO   /* for entire page */
 /* Note SIGIO has been undef'd if FIONREAD is missing.  */
 
@@ -6755,9 +6804,6 @@ input_available_signal (signo)
 {
   /* Must preserve main program's value of errno.  */
   int old_errno = errno;
-#ifdef BSD4_1
-  extern int select_alarmed;
-#endif
 
 #if defined (USG) && !defined (POSIX_SIGNALS)
   /* USG systems forget handlers when they are used;
@@ -6772,20 +6818,11 @@ input_available_signal (signo)
   if (input_available_clear_time)
     EMACS_SET_SECS_USECS (*input_available_clear_time, 0, 0);
 
-  while (1)
-    {
-      int nread;
-      nread = read_avail_input (1);
-      /* -1 means it's not ok to read the input now.
-        UNBLOCK_INPUT will read it later; now, avoid infinite loop.
-        0 means there was no keyboard input available.  */
-      if (nread <= 0)
-       break;
-
-#ifdef BSD4_1
-      select_alarmed = 1;  /* Force the select emulator back to life */
+#ifdef SYNC_INPUT
+  interrupt_input_pending = 1;
+#else
+  handle_async_input ();
 #endif
-    }
 
 #ifdef BSD4_1
   sigfree ();
@@ -6804,7 +6841,7 @@ void
 reinvoke_input_signal ()
 {
 #ifdef SIGIO
-  kill (getpid (), SIGIO);
+  handle_async_input ();
 #endif
 }
 
@@ -8867,7 +8904,7 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last,
              Lisp_Object window, posn;
 
              window = POSN_WINDOW      (EVENT_START (key));
-             posn   = POSN_BUFFER_POSN (EVENT_START (key));
+             posn   = POSN_POSN (EVENT_START (key));
 
              if (CONSP (posn)
                  || (!NILP (fake_prefixed_keys)
@@ -8925,7 +8962,7 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last,
                  localized_local_map = 1;
                  start = EVENT_START (key);
 
-                 if (CONSP (start) && CONSP (XCDR (start)))
+                 if (CONSP (start) && POSN_INBUFFER_P (start))
                    {
                      pos = POSN_BUFFER_POSN (start);
                      if (INTEGERP (pos)
@@ -9035,7 +9072,7 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last,
            {
              Lisp_Object posn;
 
-             posn = POSN_BUFFER_POSN (EVENT_START (key));
+             posn = POSN_POSN (EVENT_START (key));
              /* Handle menu-bar events:
                 insert the dummy prefix event `menu-bar'.  */
              if (EQ (posn, Qmenu_bar) || EQ (posn, Qtool_bar))
@@ -9047,8 +9084,8 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last,
 
                  /* Zap the position in key, so we know that we've
                     expanded it, and don't try to do so again.  */
-                 POSN_BUFFER_SET_POSN (EVENT_START (key),
-                                       Fcons (posn, Qnil));
+                 POSN_SET_POSN (EVENT_START (key),
+                                Fcons (posn, Qnil));
 
                  mock_input = t + 2;
                  goto replay_sequence;
@@ -9843,8 +9880,12 @@ detect_input_pending_run_timers (do_display)
         from an idle timer function.  The symptom of the bug is that
         the cursor sometimes doesn't become visible until the next X
         event is processed.  --gerd.  */
-      if (rif)
-       rif->flush_display (NULL);
+      {
+        Lisp_Object tail, frame;
+        FOR_EACH_FRAME (tail, frame)
+          if (FRAME_RIF (XFRAME (frame)))
+            FRAME_RIF (XFRAME (frame))->flush_display (XFRAME (frame));
+      }
     }
 
   return input_pending;
@@ -10060,7 +10101,6 @@ Also end any kbd macro being defined.  */)
   discard_tty_input ();
 
   kbd_fetch_ptr =  kbd_store_ptr;
-  Ffillarray (kbd_buffer_gcpro, Qnil);
   input_pending = 0;
 
   return Qnil;
@@ -10098,12 +10138,12 @@ On such systems, Emacs starts a subshell instead of suspending.  */)
     call1 (Vrun_hooks, intern ("suspend-hook"));
 
   GCPRO1 (stuffstring);
-  get_tty_size (CURTTY (), &old_width, &old_height);
+  get_tty_size (fileno (TTY_INPUT (CURTTY ())), &old_width, &old_height);
   reset_all_sys_modes ();
   /* sys_suspend can get an error if it tries to fork a subshell
      and the system resources aren't available for that.  */
   record_unwind_protect ((Lisp_Object (*) P_ ((Lisp_Object))) init_sys_modes,
-                        Qnil);
+                        (Lisp_Object)CURTTY()); /* XXX */
   stuff_buffered_input (stuffstring);
   if (cannot_suspend)
     sys_subshell ();
@@ -10114,7 +10154,7 @@ On such systems, Emacs starts a subshell instead of suspending.  */)
   /* Check if terminal/window size has changed.
      Note that this is not useful when we are running directly
      with a window system; but suspend should be disabled in that case.  */
-  get_tty_size (CURTTY (), &width, &height);
+  get_tty_size (fileno (TTY_INPUT (CURTTY ())), &width, &height);
   if (width != old_width || height != old_height)
     change_frame_size (SELECTED_FRAME (), height, width, 0, 0, 0);
 
@@ -10154,17 +10194,13 @@ stuff_buffered_input (stuffstring)
      Should we ignore anything that was typed in at the "wrong" kboard?  */
   for (; kbd_fetch_ptr != kbd_store_ptr; kbd_fetch_ptr++)
     {
-      int idx;
 
       if (kbd_fetch_ptr == kbd_buffer + KBD_BUFFER_SIZE)
        kbd_fetch_ptr = kbd_buffer;
       if (kbd_fetch_ptr->kind == ASCII_KEYSTROKE_EVENT)
        stuff_char (kbd_fetch_ptr->code);
 
-      kbd_fetch_ptr->kind = NO_EVENT;
-      idx = 2 * (kbd_fetch_ptr - kbd_buffer);
-      ASET (kbd_buffer_gcpro, idx, Qnil);
-      ASET (kbd_buffer_gcpro, idx + 1, Qnil);
+      clear_event (kbd_fetch_ptr);
     }
 
   input_pending = 0;
@@ -10178,10 +10214,10 @@ set_waiting_for_input (time_to_clear)
 {
   input_available_clear_time = time_to_clear;
 
-  /* Tell interrupt_signal to throw back to read_char,  */
+  /* Tell handle_interrupt to throw back to read_char,  */
   waiting_for_input = 1;
 
-  /* If interrupt_signal was called before and buffered a C-g,
+  /* If handle_interrupt was called before and buffered a C-g,
      make it run again now, to avoid timing error. */
   if (!NILP (Vquit_flag))
     quit_throw_to_read_char ();
@@ -10190,45 +10226,76 @@ set_waiting_for_input (time_to_clear)
 void
 clear_waiting_for_input ()
 {
-  /* Tell interrupt_signal not to throw back to read_char,  */
+  /* Tell handle_interrupt not to throw back to read_char,  */
   waiting_for_input = 0;
   input_available_clear_time = 0;
 }
 
-/* This routine is called at interrupt level in response to C-g.
-
-   If interrupt_input, this is the handler for SIGINT.  Otherwise, it
-   is called from kbd_buffer_store_event, in handling SIGIO or
-   SIGTINT.
+/* The SIGINT handler.
 
-   If `waiting_for_input' is non zero, then unless `echoing' is
-   nonzero, immediately throw back to read_char.
-
-   Otherwise it sets the Lisp variable quit-flag not-nil.  This causes
-   eval to throw, when it gets a chance.  If quit-flag is already
-   non-nil, it stops the job right away.  */
+   If we have a frame on the controlling tty, the SIGINT was generated
+   by C-g, so we call handle_interrupt.  Otherwise, the handler kills
+   Emacs.  */
 
 static SIGTYPE
 interrupt_signal (signalnum)   /* If we don't have an argument, */
      int signalnum;            /* some compilers complain in signal calls. */
 {
-  char c;
   /* Must preserve main program's value of errno.  */
   int old_errno = errno;
-  struct frame *sf = SELECTED_FRAME ();
+  struct display *display;
 
 #if defined (USG) && !defined (POSIX_SIGNALS)
-  if (!read_socket_hook && NILP (Vwindow_system))
+  /* USG systems forget handlers when they are used;
+     must reestablish each time */
+  signal (SIGINT, interrupt_signal);
+  signal (SIGQUIT, interrupt_signal);
+#endif /* USG */
+
+  /* See if we have a display on our controlling terminal. */
+  display = get_named_tty_display (NULL);
+  if (!display)
     {
-      /* USG systems forget handlers when they are used;
-        must reestablish each time */
-      signal (SIGINT, interrupt_signal);
-      signal (SIGQUIT, interrupt_signal);
+      /* If there are no frames there, let's pretend that we are a
+         well-behaving UN*X program and quit. */
+      Fkill_emacs (Qnil);
     }
-#endif /* USG */
+  else
+    {
+      /* Otherwise, the SIGINT was probably generated by C-g.  */
+      
+      /* Set internal_last_event_frame to the top frame of the
+         controlling tty, if we have a frame there.  We disable the
+         interrupt key on secondary ttys, so the SIGINT must have come
+         from the controlling tty.  */
+      internal_last_event_frame = display->display_info.tty->top_frame;
+
+      handle_interrupt ();
+    }
+
+  errno = old_errno;
+}
+
+/* This routine is called at interrupt level in response to C-g.
+   
+   It is called from the SIGINT handler or kbd_buffer_store_event.
+
+   If `waiting_for_input' is non zero, then unless `echoing' is
+   nonzero, immediately throw back to read_char.
+
+   Otherwise it sets the Lisp variable quit-flag not-nil.  This causes
+   eval to throw, when it gets a chance.  If quit-flag is already
+   non-nil, it stops the job right away. */
+
+static void
+handle_interrupt ()
+{
+  char c; 
+  struct frame *sf = SELECTED_FRAME ();
 
   cancel_echoing ();
 
+  /* XXX This code needs to be revised for multi-tty support. */
   if (!NILP (Vquit_flag)
       && (FRAME_TERMCAP_P (sf) || FRAME_MSDOS_P (sf)))
     {
@@ -10345,9 +10412,7 @@ interrupt_signal (signalnum)    /* If we don't have an argument, */
     }
 
   if (waiting_for_input && !echoing)
-    quit_throw_to_read_char ();
-
-  errno = old_errno;
+      quit_throw_to_read_char ();
 }
 
 /* Handle a C-g by making read_char return C-g.  */
@@ -10394,6 +10459,11 @@ See also `current-input-mode'.  */)
      (interrupt, flow, meta, quit)
      Lisp_Object interrupt, flow, meta, quit;
 {
+  /* XXX This function needs to be revised for multi-device support.
+     Currently it compiles fine, but its semantics are wrong.  It sets
+     global parameters (e.g. interrupt_input) based on only the
+     current frame's device. */
+  
   if (!NILP (quit)
       && (!INTEGERP (quit) || XINT (quit) < 0 || XINT (quit) > 0400))
     error ("set-input-mode: QUIT must be an ASCII character");
@@ -10409,7 +10479,7 @@ See also `current-input-mode'.  */)
 
 #ifdef SIGIO
 /* Note SIGIO has been undef'd if FIONREAD is missing.  */
-  if (read_socket_hook)
+  if (FRAME_DISPLAY (SELECTED_FRAME ())->read_socket_hook)
     {
       /* When using X, don't give the user a real choice,
         because we haven't implemented the mechanisms to support it.  */
@@ -10430,16 +10500,21 @@ See also `current-input-mode'.  */)
   interrupt_input = 1;
 #endif
 
-  flow_control = !NILP (flow);
-  if (NILP (meta))
-    meta_key = 0;
-  else if (EQ (meta, Qt))
-    meta_key = 1;
-  else
-    meta_key = 2;
+  if (FRAME_TERMCAP_P (XFRAME (selected_frame)))
+    {
+      struct tty_display_info *tty = CURTTY ();
+      tty->flow_control = !NILP (flow);
+      if (NILP (meta))
+        tty->meta_key = 0;
+      else if (EQ (meta, Qt))
+        tty->meta_key = 1;
+      else
+        tty->meta_key = 2;
+    }
+  
   if (!NILP (quit))
     /* Don't let this value be out of range.  */
-    quit_char = XINT (quit) & (meta_key ? 0377 : 0177);
+    quit_char = XINT (quit) & (NILP (meta) ? 0177 : 0377);
 
 #ifndef DOS_NT
   init_all_sys_modes ();
@@ -10469,10 +10544,21 @@ The elements of this list correspond to the arguments of
      ()
 {
   Lisp_Object val[4];
-
+  struct frame *sf = XFRAME (selected_frame);
+  
   val[0] = interrupt_input ? Qt : Qnil;
-  val[1] = flow_control ? Qt : Qnil;
-  val[2] = meta_key == 2 ? make_number (0) : meta_key == 1 ? Qt : Qnil;
+  if (FRAME_TERMCAP_P (sf))
+    {
+      val[1] = FRAME_TTY (sf)->flow_control ? Qt : Qnil;
+      val[2] = FRAME_TTY (sf)->meta_key == 2
+        ? make_number (0)
+        : CURTTY ()->meta_key == 1 ? Qt : Qnil;
+    }
+  else
+    {
+      val[1] = Qnil;
+      val[2] = Qt;
+    }
   XSETFASTINT (val[3], quit_char);
 
   return Flist (sizeof (val) / sizeof (val[0]), val);
@@ -10564,7 +10650,6 @@ init_keyboard ()
   recent_keys_index = 0;
   kbd_fetch_ptr = kbd_buffer;
   kbd_store_ptr = kbd_buffer;
-  kbd_buffer_gcpro = Fmake_vector (make_number (2 * KBD_BUFFER_SIZE), Qnil);
 #ifdef HAVE_MOUSE
   do_mouse_tracking = Qnil;
 #endif
@@ -10581,8 +10666,14 @@ init_keyboard ()
   wipe_kboard (current_kboard);
   init_kboard (current_kboard);
 
-  if (!noninteractive && !read_socket_hook && NILP (Vwindow_system))
+  if (!noninteractive)
     {
+      /* Before multi-tty support, these handlers used to be installed
+         only if the current session was a tty session.  Now an Emacs
+         session may have multiple display types, so we always handle
+         SIGINT.  There is special code in interrupt_signal to exit
+         Emacs on SIGINT when there are no termcap frames on the
+         controlling terminal. */
       signal (SIGINT, interrupt_signal);
 #if defined (HAVE_TERMIO) || defined (HAVE_TERMIOS)
       /* For systems with SysV TERMIO, C-g is set up for both SIGINT and
@@ -10855,9 +10946,6 @@ syms_of_keyboard ()
   Fset (Qextended_command_history, Qnil);
   staticpro (&Qextended_command_history);
 
-  kbd_buffer_gcpro = Fmake_vector (make_number (2 * KBD_BUFFER_SIZE), Qnil);
-  staticpro (&kbd_buffer_gcpro);
-
   accent_key_syms = Qnil;
   staticpro (&accent_key_syms);