(main): Don't call setpgrp if !initialized.
[bpt/emacs.git] / src / keyboard.c
index c942e16..7dd56e9 100644 (file)
@@ -20,7 +20,7 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 /* Allow config.h to undefine symbols found here.  */
 #include <signal.h>
 
-#include "config.h"
+#include <config.h>
 #include <stdio.h>
 #undef NULL
 #include "termchar.h"
@@ -36,6 +36,7 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #include "dispextern.h"
 #include "keyboard.h"
 #include "intervals.h"
+#include "blockinput.h"
 #include <setjmp.h>
 #include <errno.h>
 
@@ -49,6 +50,16 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 
 extern int errno;
 
+/* Variables for blockinput.h: */
+
+/* Non-zero if interrupt input is blocked right now.  */
+int interrupt_input_blocked;
+
+/* Nonzero means an input interrupt has arrived
+   during the current critical section.  */
+int interrupt_input_pending;
+
+
 #ifdef HAVE_X_WINDOWS
 extern Lisp_Object Vmouse_grabbed;
 
@@ -73,7 +84,7 @@ struct backtrace
 
 /* Non-nil disable property on a command means
    do not execute it; call disabled-command-hook's value instead.  */
-Lisp_Object Qdisabled, Vdisabled_command_hook;
+Lisp_Object Qdisabled, Qdisabled_command_hook;
 
 #define NUM_RECENT_KEYS (100)
 int recent_keys_index; /* Index for storing next element into recent_keys */
@@ -111,11 +122,17 @@ static int echoing;
 int immediate_quit;
 
 /* Character to recognize as the help char.  */
-Lisp_Object help_char;
+Lisp_Object Vhelp_char;
 
 /* Form to execute when help char is typed.  */
 Lisp_Object Vhelp_form;
 
+/* Command to run when the help character follows a prefix key.  */
+Lisp_Object Vprefix_help_command;
+
+/* List of items that should move to the end of the menu bar.  */
+Lisp_Object Vmenu_bar_final_items;
+
 /* Character that causes a quit.  Normally C-g.
 
    If we are running on an ordinary terminal, this must be an ordinary
@@ -228,6 +245,7 @@ unsigned long last_event_timestamp;
 Lisp_Object Qself_insert_command;
 Lisp_Object Qforward_char;
 Lisp_Object Qbackward_char;
+Lisp_Object Qundefined;
 
 /* read_key_sequence stores here the command definition of the
    key sequence that it reads.  */
@@ -242,9 +260,17 @@ Lisp_Object Vkeyboard_translate_table;
 /* Keymap mapping ASCII function key sequences onto their preferred forms.  */
 extern Lisp_Object Vfunction_key_map;
 
+/* Keymap mapping ASCII function key sequences onto their preferred forms.  */
+Lisp_Object Vkey_translation_map;
+
 /* Non-nil means deactivate the mark at end of this command.  */
 Lisp_Object Vdeactivate_mark;
 
+/* Menu bar specified in Lucid Emacs fashion.  */
+
+Lisp_Object Vlucid_menu_bar_dirty_flag;
+Lisp_Object Qrecompute_lucid_menubar, Qactivate_menubar_hook;
+
 /* Hooks to run before and after each command.  */
 Lisp_Object Qpre_command_hook, Qpost_command_hook;
 Lisp_Object Vpre_command_hook, Vpost_command_hook;
@@ -255,7 +281,9 @@ FILE *dribble;
 /* Nonzero if input is available.  */
 int input_pending;
 
-/* Nonzero if should obey 0200 bit in input chars as "Meta".  */
+/* 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;
 
 extern char *pending_malloc_warning;
@@ -314,10 +342,10 @@ static struct input_event *kbd_store_ptr;
    dequeuing functions?  Such a flag could be screwed up by interrupts
    at inopportune times.  */
 
-/* If this flag is non-zero, we will check mouse_moved to see when the
+/* If this flag is non-zero, we check mouse_moved to see when the
    mouse moves, and motion events will appear in the input stream.  If
-   it is zero, mouse motion will be ignored.  */
-int do_mouse_tracking;
+   it is zero, mouse motion is ignored.  */
+static int do_mouse_tracking;
 
 /* The window system handling code should set this if the mouse has
    moved since the last call to the mouse_position_hook.  Calling that
@@ -338,7 +366,6 @@ int mouse_moved;
 /* Symbols to head events.  */
 Lisp_Object Qmouse_movement;
 Lisp_Object Qscroll_bar_movement;
-
 Lisp_Object Qswitch_frame;
 
 /* Symbols to denote kinds of events.  */
@@ -350,6 +377,8 @@ Lisp_Object Qmouse_click;
 Lisp_Object Qevent_kind;
 Lisp_Object Qevent_symbol_elements;
 
+Lisp_Object Qmenu_enable;
+
 /* An event header symbol HEAD may have a property named
    Qevent_symbol_element_mask, which is of the form (BASE MODIFIERS);
    BASE is the base, unmodified version of HEAD, and MODIFIERS is the
@@ -373,6 +402,7 @@ extern Lisp_Object Qmenu_enable;
 
 Lisp_Object recursive_edit_unwind (), command_loop ();
 Lisp_Object Fthis_command_keys ();
+Lisp_Object Qextended_command_history;
 
 /* Address (if not 0) of EMACS_TIME to zero out if a SIGIO interrupt
    happens.  */
@@ -401,13 +431,11 @@ int flow_control;
 #endif
 #endif
 
-/* If we support X Windows, and won't get an interrupt when input
-   arrives from the server, poll periodically so we can detect C-g.  */
+/* If we support X Windows, turn on the code to poll periodically
+   to detect C-g.  It isn't actually used when doing interrupt input.  */
 #ifdef HAVE_X_WINDOWS
-#ifndef SIGIO
 #define POLL_FOR_INPUT
 #endif
-#endif
 \f
 /* Global variable declarations.  */
 
@@ -416,6 +444,12 @@ void (*keyboard_init_hook) ();
 
 static int read_avail_input ();
 static void get_input_pending ();
+static int readable_events ();
+static Lisp_Object read_char_menu_prompt ();
+static Lisp_Object make_lispy_event ();
+static Lisp_Object make_lispy_movement ();
+static Lisp_Object modify_event_symbol ();
+static Lisp_Object make_lispy_switch_frame ();
 
 /* > 0 if we are to echo keystrokes.  */
 static int echo_keystrokes;
@@ -485,7 +519,7 @@ echo_char (c)
          ptr += name->size;
        }
 
-      if (echoptr == echobuf && EQ (c, help_char))
+      if (echoptr == echobuf && EQ (c, Vhelp_char))
        {
          strcpy (ptr, " (Type ? for further options)");
          ptr += strlen (ptr);
@@ -505,6 +539,9 @@ echo_dash ()
 {
   if (!immediate_echo && echoptr == echobuf)
     return;
+  /* Do nothing if not echoing at all.  */
+  if (echoptr == 0)
+    return;
 
   /* Put a dash at the end of the buffer temporarily,
      but make it go away when the next character is added.  */
@@ -835,7 +872,7 @@ static int read_key_sequence ();
 Lisp_Object
 command_loop_1 ()
 {
-  Lisp_Object cmd;
+  Lisp_Object cmd, tem;
   int lose;
   int nonundocount;
   Lisp_Object keybuf[30];
@@ -858,6 +895,11 @@ command_loop_1 ()
   no_redisplay = 0;
   this_command_key_count = 0;
 
+  /* Make sure this hook runs after commands that get errors and
+     throw to top level.  */
+  if (!NILP (Vpost_command_hook))
+    call1 (Vrun_hooks, Qpost_command_hook);
+
   while (1)
     {
       /* Install chars successfully executed in kbd macro.  */
@@ -880,7 +922,7 @@ command_loop_1 ()
       Vdeactivate_mark = Qnil;
 
       /* If minibuffer on and echo area in use,
-        wait 2 sec and redraw minibufer.  */
+        wait 2 sec and redraw minibuffer.  */
 
       if (minibuf_level && echo_area_glyphs)
        {
@@ -916,9 +958,43 @@ command_loop_1 ()
        Fselect_frame (internal_last_event_frame, Qnil);
 #endif
 #endif
+      /* If it has changed current-menubar from previous value,
+        really recompute the menubar from the value.  */
+      if (! NILP (Vlucid_menu_bar_dirty_flag)
+         && !NILP (Ffboundp (Qrecompute_lucid_menubar)))
+       call0 (Qrecompute_lucid_menubar);
+
+#if 0 /* This is done in xdisp.c now.  */
+#ifdef MULTI_FRAME
+      for (tem = Vframe_list; CONSP (tem); tem = XCONS (tem)->cdr)
+       {
+         struct frame *f = XFRAME (XCONS (tem)->car);
+         struct window *w = XWINDOW (FRAME_SELECTED_WINDOW (f));
+
+         /* If the user has switched buffers or windows, we need to
+            recompute to reflect the new bindings.  But we'll
+            recompute when update_mode_lines is set too; that means
+            that people can use force-mode-line-update to request
+            that the menu bar be recomputed.  The adverse effect on
+            the rest of the redisplay algorithm is about the same as
+            windows_or_buffers_changed anyway.  */
+         if (windows_or_buffers_changed
+             || update_mode_lines
+             || (XFASTINT (w->last_modified) < MODIFF
+                 && (XFASTINT (w->last_modified)
+                     <= XBUFFER (w->buffer)->save_modified)))
+           {
+             struct buffer *prev = current_buffer;
+             current_buffer = XBUFFER (w->buffer);
+             FRAME_MENU_BAR_ITEMS (f) = menu_bar_items ();
+             current_buffer = prev;
+           }
+       }
+#endif /* MULTI_FRAME */
+#endif /* 0 */
 
       /* Read next key sequence; i gets its length.  */
-      i = read_key_sequence (keybuf, (sizeof keybuf / sizeof (keybuf[0])), 0);
+      i = read_key_sequence (keybuf, (sizeof keybuf / sizeof (keybuf[0])), Qnil);
 
       ++num_input_keys;
 
@@ -927,9 +1003,22 @@ command_loop_1 ()
 
       if (i == 0)              /* End of file -- happens only in */
        return Qnil;            /* a kbd macro, at the end.  */
+      /* -1 means read_key_sequence got a menu that was rejected.
+        Just loop around and read another command.  */
+      if (i == -1)
+       {
+         cancel_echoing ();
+         this_command_key_count = 0;
+         continue;
+       }
 
       last_command_char = keybuf[i - 1];
 
+      /* If the previous command tried to force a specific window-start,
+        forget about that, in case this command moves point far away
+        from that position.  */
+      XWINDOW (selected_window)->force_start = Qnil;
+
       cmd = read_key_sequence_cmd;
       if (!NILP (Vexecuting_macro))
        {
@@ -954,7 +1043,7 @@ command_loop_1 ()
       if (!NILP (Vpre_command_hook))
        call1 (Vrun_hooks, Qpre_command_hook);
 
-      if (NILP (cmd))
+      if (NILP (this_command))
        {
          /* nil means key is undefined.  */
          bitch_at_user ();
@@ -969,14 +1058,14 @@ command_loop_1 ()
            {
              /* Recognize some common commands in common situations and
                 do them directly.  */
-             if (EQ (cmd, Qforward_char) && point < ZV)
+             if (EQ (this_command, Qforward_char) && point < ZV)
                {
                   struct Lisp_Vector *dp
                    = window_display_table (XWINDOW (selected_window));
                  lose = FETCH_CHAR (point);
                  SET_PT (point + 1);
                  if ((dp
-                      ? (XTYPE (DISP_CHAR_VECTOR (dp, lose)) != Lisp_Vector
+                      ? (VECTORP (DISP_CHAR_VECTOR (dp, lose))
                          && XVECTOR (DISP_CHAR_VECTOR (dp, lose))->size == 1)
                       : (lose >= 0x20 && lose < 0x7f))
                      && (XFASTINT (XWINDOW (selected_window)->last_modified)
@@ -990,7 +1079,7 @@ command_loop_1 ()
                    no_redisplay = direct_output_forward_char (1);
                  goto directly_done;
                }
-             else if (EQ (cmd, Qbackward_char) && point > BEGV)
+             else if (EQ (this_command, Qbackward_char) && point > BEGV)
                {
                   struct Lisp_Vector *dp
                    = window_display_table (XWINDOW (selected_window));
@@ -1011,7 +1100,7 @@ command_loop_1 ()
                    no_redisplay = direct_output_forward_char (-1);
                  goto directly_done;
                }
-             else if (EQ (cmd, Qself_insert_command)
+             else if (EQ (this_command, Qself_insert_command)
                       /* Try this optimization only on ascii keystrokes.  */
                       && XTYPE (last_command_char) == Lisp_Int)
                {
@@ -1036,7 +1125,7 @@ command_loop_1 ()
                    || !EQ (current_buffer->selective_display, Qnil)
                    || detect_input_pending ()
                    || !NILP (Vexecuting_macro);
-                 if (internal_self_insert (XINT (c), 0))
+                 if (internal_self_insert (c, 0))
                    {
                      lose = 1;
                      nonundocount = 0;
@@ -1046,7 +1135,7 @@ command_loop_1 ()
                    {
                      struct Lisp_Vector *dp
                        = window_display_table (XWINDOW (selected_window));
-                     int lose = XINT (c);
+                     int lose = c;
 
                      if (dp)
                        {
@@ -1074,7 +1163,7 @@ command_loop_1 ()
          nonundocount = 0;
          if (NILP (Vprefix_arg))
            Fundo_boundary ();
-         Fcommand_execute (cmd, Qnil);
+         Fcommand_execute (this_command, Qnil);
 
        }
     directly_done: ;
@@ -1114,7 +1203,7 @@ command_loop_1 ()
 /* Number of seconds between polling for input.  */
 int polling_period;
 
-/* Nonzero means polling for input is temporarily suppresed.  */
+/* Nonzero means polling for input is temporarily suppressed.  */
 int poll_suppress_count;
 
 #ifdef POLL_FOR_INPUT
@@ -1126,12 +1215,9 @@ int polling_for_input;
 SIGTYPE
 input_poll_signal ()
 {
-#ifdef HAVE_X_WINDOWS
-  extern int x_input_blocked;
-  if (x_input_blocked == 0)
-#endif
-    if (!waiting_for_input)
-      read_avail_input (0);
+  if (interrupt_input_blocked == 0
+      && !waiting_for_input)
+    read_avail_input (0);
   signal (SIGALRM, input_poll_signal);
   alarm (polling_period);
 }
@@ -1144,7 +1230,7 @@ input_poll_signal ()
 start_polling ()
 {
 #ifdef POLL_FOR_INPUT
-  if (read_socket_hook)
+  if (read_socket_hook && !interrupt_input)
     {
       poll_suppress_count--;
       if (poll_suppress_count == 0)
@@ -1162,7 +1248,7 @@ start_polling ()
 stop_polling ()
 {
 #ifdef POLL_FOR_INPUT
-  if (read_socket_hook)
+  if (read_socket_hook && !interrupt_input)
     {
       if (poll_suppress_count == 0)
        {
@@ -1173,6 +1259,66 @@ stop_polling ()
     }
 #endif
 }
+
+/* Set the value of poll_suppress_count to COUNT
+   and start or stop polling accordingly.  */
+
+void
+set_poll_suppress_count (count)
+     int count;
+{
+#ifdef POLL_FOR_INPUT
+  if (count == 0 && poll_suppress_count != 0)
+    {
+      poll_suppress_count = 1;
+      start_polling ();
+    }
+  else if (count != 0 && poll_suppress_count == 0)
+    {
+      stop_polling ();
+    }
+  poll_suppress_count = count;
+#endif
+}
+\f
+/* Applying the control modifier to CHARACTER.  */
+int
+make_ctrl_char (c)
+     int c;
+{
+  /* Save the upper bits here.  */
+  int upper = c & ~0177;
+
+  c &= 0177;
+
+  /* Everything in the columns containing the upper-case letters
+     denotes a control character.  */
+  if (c >= 0100 && c < 0140)
+    {
+      int oc = c;
+      c &= ~0140;
+      /* Set the shift modifier for a control char
+        made from a shifted letter.  But only for letters!  */
+      if (oc >= 'A' && oc <= 'Z')
+       c |= shift_modifier;
+    }
+
+  /* The lower-case letters denote control characters too.  */
+  else if (c >= 'a' && c <= 'z')
+    c &= ~0140;
+
+  /* Include the bits for control and shift
+     only if the basic ASCII code can't indicate them.  */
+  else if (c >= ' ')
+    c |= ctrl_modifier;
+
+  /* Replace the high bits.  */
+  c |= (upper & ~ctrl_modifier);
+
+  return c;
+}
+
+
 \f
 /* Input of single characters from keyboard */
 
@@ -1192,7 +1338,9 @@ static Lisp_Object kbd_buffer_get_event ();
 
    If USED_MOUSE_MENU is non-zero, then we set *USED_MOUSE_MENU to 1
    if we used a mouse menu to read the input, or zero otherwise.  If
-   USED_MOUSE_MENU is zero, *USED_MOUSE_MENU is left alone.  */
+   USED_MOUSE_MENU is zero, *USED_MOUSE_MENU is left alone.
+
+   Value is t if we showed a menu and the user rejected it.  */
 
 Lisp_Object
 read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu)
@@ -1236,7 +1384,7 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu)
         remain unchanged.
 
          Since this event came from a macro, it would be misleading to
-        leave internal_last_event_frame set to whereever the last
+        leave internal_last_event_frame set to wherever the last
         real event came from.  Normally, a switch-frame event selects
         internal_last_event_frame after each command is read, but
         events read from a macro should never cause a new frame to be
@@ -1244,7 +1392,11 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu)
       Vlast_event_frame = internal_last_event_frame = Qmacro;
 #endif
 
-      if (executing_macro_index >= XFASTINT (Flength (Vexecuting_macro)))
+      /* Exit the macro if we are at the end.
+        Also, some things replace the macro with t
+        to force an early exit.  */
+      if (EQ (Vexecuting_macro, Qt)
+         || executing_macro_index >= XFASTINT (Flength (Vexecuting_macro)))
        {
          XSET (c, Lisp_Int, -1);
          return c;
@@ -1270,6 +1422,9 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu)
       goto reread_first;
     }
 
+  if (commandflag >= 0 && !input_pending && !detect_input_pending ())
+    prepare_menu_bars ();
+
   /* Save outer setjmp data, in case called recursively.  */
   save_getcjmp (save_jump);
 
@@ -1285,6 +1440,10 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu)
       XSET (internal_last_event_frame, Lisp_Frame, selected_frame);
       Vlast_event_frame = internal_last_event_frame;
 #endif
+      /* If we report the quit char as an event,
+        don't do so more than once.  */
+      if (!NILP (Vinhibit_quit))
+       Vquit_flag = Qnil;
 
       goto non_reread;
     }
@@ -1299,6 +1458,7 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu)
   /* If in middle of key sequence and minibuffer not active,
      start echoing if enough time elapses.  */
   if (minibuf_level == 0 && !immediate_echo && this_command_key_count > 0
+      && ! noninteractive
       && echo_keystrokes > 0
       && (echo_area_glyphs == 0 || *echo_area_glyphs == 0))
     {
@@ -1340,7 +1500,7 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu)
 
   /* Slow down auto saves logarithmically in size of current buffer,
      and garbage collect while we're at it.  */
-  if (NILP (c))
+  if (INTERACTIVE && NILP (c))
     {
       int delay_level, buffer_size;
 
@@ -1375,27 +1535,43 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu)
                 consing going on to make it worthwhile.  */
              if (!detect_input_pending ()
                  && consing_since_gc > gc_cons_threshold / 2)
-               Fgarbage_collect ();
+               {
+                 Fgarbage_collect ();
+                 /* prepare_menu_bars isn't safe here, but it should
+                    also be unnecessary.  */
+                 redisplay ();
+               }
            }
        }
     }
 
   /* Actually read a character, waiting if necessary.  */
-  if (NILP (c))
-    c = kbd_buffer_get_event ();
-
-  if (NILP (c))
-    abort ();                  /* Don't think this can happen. */
+  while (NILP (c))
+    {
+      c = kbd_buffer_get_event ();
+      if (!NILP (c))
+       break;
+      if (commandflag >= 0 && !input_pending && !detect_input_pending ())
+       redisplay ();
+    }
 
   /* Terminate Emacs in batch mode if at eof.  */
   if (noninteractive && XTYPE (c) == Lisp_Int && XINT (c) < 0)
     Fkill_emacs (make_number (1));
 
-  /* Test for ControlMask and Mod1Mask.  */
-  if (extra_keyboard_modifiers & 4)
-    c &= ~0140;
-  if (extra_keyboard_modifiers & 8)
-    c |= 0200;
+  if (XTYPE (c) == Lisp_Int)
+    {
+      /* Add in any extra modifiers, where appropriate.  */
+      if ((extra_keyboard_modifiers & CHAR_CTL)
+         || ((extra_keyboard_modifiers & 0177) < ' '
+             && (extra_keyboard_modifiers & 0177) != 0))
+       XSETINT (c, make_ctrl_char (XINT (c)));
+
+      /* Transfer any other modifier bits directly from
+        extra_keyboard_modifiers to c.  Ignore the actual character code
+        in the low 16 bits of extra_keyboard_modifiers.  */
+      XSETINT (c, XINT (c) | (extra_keyboard_modifiers & ~0xff7f & ~CHAR_CTL));
+    }
 
  non_reread:
 
@@ -1471,7 +1647,7 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu)
   num_input_chars++;
 
   /* Process the help character specially if enabled */
-  if (EQ (c, help_char) && !NILP (Vhelp_form))
+  if (EQ (c, Vhelp_char) && !NILP (Vhelp_form))
     {
       Lisp_Object tem0;
       count = specpdl_ptr - specpdl;
@@ -1487,6 +1663,7 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu)
       c = read_char (0, 0, 0, Qnil, 0);
       /* Remove the help from the frame */
       unbind_to (count, Qnil);
+      prepare_menu_bars ();
       redisplay ();
       if (EQ (c, make_number (040)))
        {
@@ -1524,26 +1701,6 @@ restore_getcjmp (temp)
 }
 
 \f
-/* Low level keyboard/mouse input.
-   kbd_buffer_store_event places events in kbd_buffer, and
-   kbd_buffer_get_event retrieves them.
-   mouse_moved indicates when the mouse has moved again, and
-   *mouse_position_hook provides the mouse position.  */
-
-/* Set this for debugging, to have a way to get out */
-int stop_character;
-
-extern int frame_garbaged;
-
-/* Return true iff there are any events in the queue that read-char
-   would return.  If this returns false, a read-char would block.  */
-static int
-readable_events ()
-{
-  return ! EVENT_QUEUES_EMPTY;
-}
-
-
 /* Restore mouse tracking enablement.  See Ftrack_mouse for the only use
    of this function.  */
 static Lisp_Object
@@ -1561,6 +1718,7 @@ tracking_off (old_value)
         redisplay.  */
       if (!readable_events ())
        {
+         prepare_menu_bars ();
          redisplay_preserve_echo_area ();
          get_input_pending (&input_pending);
        }
@@ -1586,6 +1744,23 @@ Normally, mouse motion is ignored.")
   val = Fprogn (args);
   return unbind_to (count, val);
 }
+\f
+/* Low level keyboard/mouse input.
+   kbd_buffer_store_event places events in kbd_buffer, and
+   kbd_buffer_get_event retrieves them.
+   mouse_moved indicates when the mouse has moved again, and
+   *mouse_position_hook provides the mouse position.  */
+
+/* Return true iff there are any events in the queue that read-char
+   would return.  If this returns false, a read-char would block.  */
+static int
+readable_events ()
+{
+  return ! EVENT_QUEUES_EMPTY;
+}
+
+/* Set this for debugging, to have a way to get out */
+int stop_character;
 
 /* Store an event obtained at interrupt level into kbd_buffer, fifo */
 
@@ -1598,7 +1773,14 @@ kbd_buffer_store_event (event)
 
   if (event->kind == ascii_keystroke)
     {
-      register int c = XFASTINT (event->code) & 0377;
+      register int c = event->code & 0377;
+
+      if (event->modifiers & ctrl_modifier)
+       c = make_ctrl_char (c);
+
+      c |= (event->modifiers
+           & (meta_modifier | alt_modifier
+              | hyper_modifier | super_modifier));
 
       if (c == quit_char)
        {
@@ -1610,8 +1792,8 @@ kbd_buffer_store_event (event)
             get returned to Emacs as an event, the next event read
             will set Vlast_event_frame again, so this is safe to do.  */
          {
-           Lisp_Object focus =
-             FRAME_FOCUS_FRAME (XFRAME (event->frame_or_window));
+           Lisp_Object focus
+             FRAME_FOCUS_FRAME (XFRAME (event->frame_or_window));
 
            if (NILP (focus))
              internal_last_event_frame = event->frame_or_window;
@@ -1631,8 +1813,6 @@ kbd_buffer_store_event (event)
          sys_suspend ();
          return;
        }
-
-      XSET (event->code, Lisp_Int, c);
     }
 
   if (kbd_store_ptr - kbd_buffer == KBD_BUFFER_SIZE)
@@ -1645,13 +1825,23 @@ kbd_buffer_store_event (event)
   if (kbd_fetch_ptr - 1 != kbd_store_ptr)
     {
       kbd_store_ptr->kind = event->kind;
-      kbd_store_ptr->code = event->code;
-      kbd_store_ptr->part = event->part;
-      kbd_store_ptr->frame_or_window = event->frame_or_window;
-      kbd_store_ptr->modifiers = event->modifiers;
-      kbd_store_ptr->x = event->x;
-      kbd_store_ptr->y = event->y;
-      kbd_store_ptr->timestamp = event->timestamp;
+      if (event->kind == selection_request_event)
+       {
+         /* We must not use the ordinary copying code for this case,
+            since `part' is an enum and copying it might not copy enough
+            in this case.  */
+         bcopy (event, kbd_store_ptr, sizeof (*event));
+       }
+      else
+       {
+         kbd_store_ptr->code = event->code;
+         kbd_store_ptr->part = event->part;
+         kbd_store_ptr->frame_or_window = event->frame_or_window;
+         kbd_store_ptr->modifiers = event->modifiers;
+         kbd_store_ptr->x = event->x;
+         kbd_store_ptr->y = event->y;
+         kbd_store_ptr->timestamp = event->timestamp;
+       }
       (XVECTOR (kbd_buffer_frame_or_window)->contents[kbd_store_ptr
                                                      - kbd_buffer]
        = event->frame_or_window);
@@ -1659,11 +1849,12 @@ kbd_buffer_store_event (event)
       kbd_store_ptr++;
     }
 }
-
-static Lisp_Object make_lispy_event ();
-static Lisp_Object make_lispy_movement ();
-static Lisp_Object modify_event_symbol ();
-static Lisp_Object make_lispy_switch_frame ();
+\f
+/* Read one event from the event buffer, waiting if necessary.
+   The value is a Lisp object representing the event.
+   The value is nil for an event that should be ignored,
+   or that was handled here.
+   We always read and discard one event.  */
 
 static Lisp_Object
 kbd_buffer_get_event ()
@@ -1729,45 +1920,104 @@ kbd_buffer_get_event ()
 
       obj = Qnil;
 
-#ifdef MULTI_FRAME
+      /* These two kinds of events get special handling
+        and don't actually appear to the command loop.
+        We return nil for them.  */
+      if (event->kind == selection_request_event)
+       {
+#ifdef HAVE_X11
+         x_handle_selection_request (event);
+         kbd_fetch_ptr = event + 1;
+#else
+         /* We're getting selection request events, but we don't have
+             a window system.  */
+         abort ();
+#endif
+       }
+
+      else if (event->kind == selection_clear_event)
+       {
+#ifdef HAVE_X11
+         x_handle_selection_clear (event);
+         kbd_fetch_ptr = event + 1;
+#else
+         /* We're getting selection request events, but we don't have
+             a window system.  */
+         abort ();
+#endif
+       }
+#ifdef HAVE_X11
+      else if (event->kind == delete_window_event)
+       {
+         Lisp_Object tail, frame;
+         struct frame *f;
+       
+         /* If the user destroys the only frame, Emacs should exit.
+            Count visible frames and iconified frames.  */
+         for (tail = Vframe_list; CONSP (tail); tail = XCONS (tail)->cdr)
+           {
+             frame = XCONS (tail)->car;
+             if (XTYPE (frame) != Lisp_Frame || EQ (frame, event->frame_or_window))
+               continue;
+             f = XFRAME (frame);
+             if (FRAME_VISIBLE_P (f) || FRAME_ICONIFIED_P (f))
+               break;
+           }
+
+         if (! CONSP (tail))
+           kill (getpid (), SIGHUP);
+
+         Fdelete_frame (event->frame_or_window, Qt);
+         kbd_fetch_ptr = event + 1;
+       }
+#endif
+      /* Just discard these, by returning nil.
+        (They shouldn't be found in the buffer,
+        but on some machines it appears they do show up.)  */
+      else if (event->kind == no_event)
+       kbd_fetch_ptr = event + 1;
+
       /* If this event is on a different frame, return a switch-frame this
         time, and leave the event in the queue for next time.  */
-      {
-       Lisp_Object frame = event->frame_or_window;
-       Lisp_Object focus;
+      else
+       {
+#ifdef MULTI_FRAME
+         Lisp_Object frame = event->frame_or_window;
+         Lisp_Object focus;
 
-       if (XTYPE (frame) == Lisp_Window)
-         frame = WINDOW_FRAME (XWINDOW (frame));
+         if (XTYPE (frame) == Lisp_Window)
+           frame = WINDOW_FRAME (XWINDOW (frame));
 
-       focus = FRAME_FOCUS_FRAME (XFRAME (frame));
-       if (! NILP (focus))
-         frame = focus;
+         focus = FRAME_FOCUS_FRAME (XFRAME (frame));
+         if (! NILP (focus))
+           frame = focus;
 
-       if (! EQ (frame, internal_last_event_frame))
-         {
-           internal_last_event_frame = frame;
+         if (! EQ (frame, internal_last_event_frame)
+             && XFRAME (frame) != selected_frame)
            obj = make_lispy_switch_frame (frame);
-         }
-      }
-#endif
+         internal_last_event_frame = frame;
+#endif /* MULTI_FRAME */
 
-      /* If we didn't decide to make a switch-frame event, go ahead
-        and build a real event from the queue entry.  */
-      if (NILP (obj))
-       {
-         obj = make_lispy_event (event);
-      
-         /* Wipe out this event, to catch bugs.  */
-         event->kind = no_event;
-         (XVECTOR (kbd_buffer_frame_or_window)->contents[event - kbd_buffer]
-          = Qnil);
+         /* If we didn't decide to make a switch-frame event, go ahead
+            and build a real event from the queue entry.  */
 
-         kbd_fetch_ptr = event + 1;
+         if (NILP (obj))
+           {
+             obj = make_lispy_event (event);
+
+             /* Wipe out this event, to catch bugs.  */
+             event->kind = no_event;
+             (XVECTOR (kbd_buffer_frame_or_window)->contents[event - kbd_buffer]
+              = Qnil);
+
+             kbd_fetch_ptr = event + 1;
+           }
        }
     }
+  /* Try generating a mouse motion event.  */
   else if (do_mouse_tracking && mouse_moved)
     {
-      FRAME_PTR f;
+      FRAME_PTR f = 0;
       Lisp_Object bar_window;
       enum scroll_bar_part part;
       Lisp_Object x, y;
@@ -1788,11 +2038,10 @@ kbd_buffer_get_event ()
          if (NILP (frame))
            XSET (frame, Lisp_Frame, f);
 
-         if (! EQ (frame, internal_last_event_frame))
-           {
-             XSET (internal_last_event_frame, Lisp_Frame, frame);
-             obj = make_lispy_switch_frame (internal_last_event_frame);
-           }
+         if (! EQ (frame, internal_last_event_frame)
+             && XFRAME (frame) != selected_frame)
+           obj = make_lispy_switch_frame (frame);
+         internal_last_event_frame = frame;
        }
 #endif
 
@@ -1814,19 +2063,159 @@ kbd_buffer_get_event ()
 
   return (obj);
 }
+\f
+/* Process any events that are not user-visible,
+   then return, without reading any user-visible events.  */
 
+void
+swallow_events ()
+{
+  while (kbd_fetch_ptr != kbd_store_ptr)
+    {
+      struct input_event *event;
+
+      event = ((kbd_fetch_ptr < kbd_buffer + KBD_BUFFER_SIZE)
+              ? kbd_fetch_ptr
+              : kbd_buffer);
 
+      last_event_timestamp = event->timestamp;
+
+      /* These two kinds of events get special handling
+        and don't actually appear to the command loop.  */
+      if (event->kind == selection_request_event)
+       {
+#ifdef HAVE_X11
+         x_handle_selection_request (event);
+         kbd_fetch_ptr = event + 1;
+#else
+         /* We're getting selection request events, but we don't have
+             a window system.  */
+         abort ();
+#endif
+       }
+
+      else if (event->kind == selection_clear_event)
+       {
+#ifdef HAVE_X11
+         x_handle_selection_clear (event);
+         kbd_fetch_ptr = event + 1;
+#else
+         /* We're getting selection request events, but we don't have
+             a window system.  */
+         abort ();
+#endif
+       }
+      else
+       break;
+    }
+
+  get_input_pending (&input_pending);
+}
+\f
 /* Caches for modify_event_symbol.  */
+static Lisp_Object accent_key_syms;
 static Lisp_Object func_key_syms;
 static Lisp_Object mouse_syms;
 
+/* This is a list of keysym codes for special "accent" characters.
+   It parallels lispy_accent_keys.  */
+
+static int lispy_accent_codes[] =
+{
+#ifdef XK_dead_circumflex
+  XK_dead_circumflex,
+#else
+  0,
+#endif
+#ifdef XK_dead_grave
+  XK_dead_grave,
+#else
+  0,
+#endif
+#ifdef XK_dead_tilde
+  XK_dead_tilde,
+#else
+  0,
+#endif
+#ifdef XK_dead_diaeresis
+  XK_dead_diaeresis,
+#else
+  0,
+#endif
+#ifdef XK_dead_macron
+  XK_dead_macron,
+#else
+  0,
+#endif
+#ifdef XK_dead_degree
+  XK_dead_degree,
+#else
+  0,
+#endif
+#ifdef XK_dead_acute
+  XK_dead_acute,
+#else
+  0,
+#endif
+#ifdef XK_dead_cedilla
+  XK_dead_cedilla,
+#else
+  0,
+#endif
+#ifdef XK_dead_breve
+  XK_dead_breve,
+#else
+  0,
+#endif
+#ifdef XK_dead_ogonek
+  XK_dead_ogonek,
+#else
+  0,
+#endif
+#ifdef XK_dead_caron
+  XK_dead_caron,
+#else
+  0,
+#endif
+#ifdef XK_dead_doubleacute
+  XK_dead_doubleacute,
+#else
+  0,
+#endif
+#ifdef XK_dead_abovedot
+  XK_dead_abovedot,
+#else
+  0,
+#endif
+};
+
+/* This is a list of Lisp names for special "accent" characters.
+   It parallels lispy_accent_codes.  */
+
+static char *lispy_accent_keys[] =
+{
+  "dead-circumflex",
+  "dead-grave",
+  "dead-tilde",
+  "dead-diaeresis",
+  "dead-macron",
+  "dead-degree",
+  "dead-acute",
+  "dead-cedilla",
+  "dead-breve",
+  "dead-ogonek",
+  "dead-caron",
+  "dead-doubleacute",
+  "dead-abovedot",
+};
+
 /* You'll notice that this table is arranged to be conveniently
    indexed by X Windows keysym values.  */
 static char *lispy_function_keys[] =
   {
     /* X Keysym value */
 
-    0, 0, 0, 0, 0, 0, 0, 0,    /* 0xff00 */
+    "remove", 0, 0, 0, 0, 0, 0, 0,     /* 0xff00 */
     "backspace",
     "tab",
     "linefeed",
@@ -1879,9 +2268,9 @@ static char *lispy_function_keys[] =
     "insertchar",
     "deletechar",
     "backtab",
-    "kp_backtab",              /* 0x1000ff75 */
+    "kp-backtab",              /* 0x1000ff75 */
     0,                         /* 0xff76 */
-    0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xff7f */
+    0, 0, 0, 0, 0, 0, 0, 0, "kp-numlock",      /* 0xff7f */
     "kp-space",                        /* 0xff80 */    /* IsKeypadKey */
     0, 0, 0, 0, 0, 0, 0, 0,
     "kp-tab",                  /* 0xff89 */
@@ -1944,6 +2333,23 @@ Lisp_Object *scroll_bar_parts[] = {
 
 static Lisp_Object button_down_location;
 
+/* Information about the most recent up-going button event:  Which
+   button, what location, and what time. */
+
+static int last_mouse_button;
+static int last_mouse_x;
+static int last_mouse_y;
+static unsigned long button_down_time;
+
+/* The maximum time between clicks to make a double-click,
+   or Qnil to disable double-click detection,
+   or Qt for no time limit.  */
+Lisp_Object Vdouble_click_time;
+
+/* The number of clicks in this multiple-click. */
+
+int double_click_count;
+
 /* Given a struct input_event, build the lisp event which represents
    it.  If EVENT is 0, build a mouse movement event from the mouse
    movement buffer, which should have a movement event in it.
@@ -1956,6 +2362,8 @@ static Lisp_Object
 make_lispy_event (event)
      struct input_event *event;
 {
+  int i;
+
 #ifdef SWITCH_ENUM_BUG
   switch ((int) event->kind)
 #else
@@ -1965,25 +2373,38 @@ make_lispy_event (event)
       /* A simple keystroke.  */
     case ascii_keystroke:
       {
-       int c = XFASTINT (event->code);
-       /* Include the bits for control and shift
-          only if the basic ASCII code can't indicate them.  */
-       if ((event->modifiers & ctrl_modifier)
-           && c >= 040)
-         c |= ctrl_modifier;
-       if (XFASTINT (event->code) < 040
-           && (event->modifiers & shift_modifier))
-         c |= shift_modifier;
+       int c = event->code & 0377;
+       /* Turn ASCII characters into control characters
+          when proper.  */
+       if (event->modifiers & ctrl_modifier)
+         c = make_ctrl_char (c);
+
+       /* Add in the other modifier bits.  We took care of ctrl_modifier
+          just above, and the shift key was taken care of by the X code,
+          and applied to control characters by make_ctrl_char.  */
        c |= (event->modifiers
              & (meta_modifier | alt_modifier
                 | hyper_modifier | super_modifier));
+       button_down_time = 0;
        return c;
       }
 
       /* A function key.  The symbol may need to have modifier prefixes
         tacked onto it.  */
     case non_ascii_keystroke:
-      return modify_event_symbol (XFASTINT (event->code), event->modifiers,
+      button_down_time = 0;
+
+      for (i = 0; i < sizeof (lispy_accent_codes) / sizeof (int); i++)
+       if (event->code == lispy_accent_codes[i])
+         return modify_event_symbol (i,
+                                     event->modifiers,
+                                     Qfunction_key,
+                                     lispy_accent_keys, &accent_key_syms,
+                                     (sizeof (lispy_accent_keys)
+                                      / sizeof (lispy_accent_keys[0])));
+
+      return modify_event_symbol (event->code - 0xff00,
+                                 event->modifiers,
                                  Qfunction_key,
                                  lispy_function_keys, &func_key_syms,
                                  (sizeof (lispy_function_keys)
@@ -1995,7 +2416,8 @@ make_lispy_event (event)
     case mouse_click:
     case scroll_bar_click:
       {
-       int button = XFASTINT (event->code);
+       int button = event->code;
+       int is_double;
        Lisp_Object position;
        Lisp_Object *start_pos_ptr;
        Lisp_Object start_pos;
@@ -2007,7 +2429,7 @@ make_lispy_event (event)
        if (event->kind == mouse_click)
          {
            int part;
-           struct frame *f = XFRAME (event->frame_or_window);
+           FRAME_PTR f = XFRAME (event->frame_or_window);
            Lisp_Object window
              = window_from_coordinates (f, XINT (event->x), XINT (event->y),
                                         &part);
@@ -2023,8 +2445,8 @@ make_lispy_event (event)
                    Lisp_Object pos, string;
                    pos = Fcdr (Fcdr (Fcar (items)));
                    string = Fcar (Fcdr (Fcar (items)));
-                   if (XINT (event->x) > XINT (pos)
-                       && XINT (event->x) <= XINT (pos) + XSTRING (string)->size)
+                   if (XINT (event->x) >= XINT (pos)
+                       && XINT (event->x) < XINT (pos) + XSTRING (string)->size)
                      break;
                  }
                position
@@ -2087,37 +2509,75 @@ make_lispy_event (event)
        start_pos = *start_pos_ptr;
        *start_pos_ptr = Qnil;
 
+       is_double = (button == last_mouse_button
+                    && XINT (event->x) == last_mouse_x
+                    && XINT (event->y) == last_mouse_y
+                    && button_down_time != 0
+                    && (EQ (Vdouble_click_time, Qt)
+                        || (INTEGERP (Vdouble_click_time)
+                            && ((int)(event->timestamp - button_down_time)
+                                < XINT (Vdouble_click_time)))));
+       last_mouse_button = button;
+       last_mouse_x = XINT (event->x);
+       last_mouse_y = XINT (event->y);
+
        /* If this is a button press, squirrel away the location, so
            we can decide later whether it was a click or a drag.  */
        if (event->modifiers & down_modifier)
-         *start_pos_ptr = Fcopy_alist (position);
+         {
+           if (is_double)
+             {
+               double_click_count++;
+               event->modifiers |= ((double_click_count > 2)
+                                    ? triple_modifier
+                                    : double_modifier);
+             }
+           else
+             double_click_count = 1;
+           button_down_time = event->timestamp;
+           *start_pos_ptr = Fcopy_alist (position);
+         }
 
        /* Now we're releasing a button - check the co-ordinates to
            see if this was a click or a drag.  */
        else if (event->modifiers & up_modifier)
          {
-           /* Is there a start position stored at all for this
-              button?
-
-              It would be nice if we could assume that if we're
-              getting a button release, we must therefore have gotten
-              a button press.  Unfortunately, the X menu code thwarts
-              this assumption, so we'll have to be more robust.  We
-              treat a button release with no stored start position as
-              a click.  */
+           /* If we did not see a down before this up,
+              ignore the up.  Probably this happened because
+              the down event chose a menu item.
+              It would be an annoyance to treat the release
+              of the button that chose the menu item
+              as a separate event.  */
+
+           if (XTYPE (start_pos) != Lisp_Cons)
+             return Qnil;
+
            event->modifiers &= ~up_modifier;
+#if 0 /* Formerly we treated an up with no down as a click event.  */
            if (XTYPE (start_pos) != Lisp_Cons)
              event->modifiers |= click_modifier;
            else
+#endif
              {
                /* The third element of every position should be the (x,y)
                   pair.  */
                Lisp_Object down = Fnth (make_number (2), start_pos);
 
-               event->modifiers |= ((EQ (event->x, XCONS (down)->car)
-                                     && EQ (event->y, XCONS (down)->cdr))
-                                    ? click_modifier
-                                    : drag_modifier);
+               if (EQ (event->x, XCONS (down)->car)
+                   && EQ (event->y, XCONS (down)->cdr))
+                 {
+                   if (is_double && double_click_count > 1)
+                     event->modifiers |= ((double_click_count > 2)
+                                          ? triple_modifier
+                                          : double_modifier);
+                   else
+                     event->modifiers |= click_modifier;
+                 }
+               else
+                 {
+                   button_down_time = 0;
+                   event->modifiers |= drag_modifier;
+                 }
              }
          }
        else
@@ -2140,6 +2600,11 @@ make_lispy_event (event)
                          Fcons (start_pos,
                                 Fcons (position,
                                        Qnil)));
+         else if (event->modifiers & (double_modifier | triple_modifier))
+           return Fcons (head,
+                         Fcons (position,
+                                Fcons (make_number (double_click_count),
+                                       Qnil)));
          else
            return Fcons (head,
                          Fcons (position,
@@ -2149,7 +2614,7 @@ make_lispy_event (event)
 
       /* The 'kind' field of the event is something we don't recognize.  */
     default:
-      abort();
+      abort ();
     }
 }
 
@@ -2200,10 +2665,17 @@ make_lispy_movement (frame, bar_window, part, x, y, time)
                  buffer_posn_from_coords (XWINDOW (window),
                                           XINT (x), XINT (y)));
        }
+      else if (frame != 0)
+       {
+         XSET (window, Lisp_Frame, frame);
+         posn = Qnil;
+       }
       else
        {
          window = Qnil;
          posn = Qnil;
+         XFASTINT (x) = 0;
+         XFASTINT (y) = 0;
        }
 
       return Fcons (Qmouse_movement,
@@ -2294,6 +2766,24 @@ parse_modifiers_uncached (symbol, modifier_end)
            modifiers |= down_modifier;
            i += 5;
          }
+       else if (i + 7 <= name->size
+                && ! strncmp (name->data + i, "double-", 7))
+         {
+           modifiers |= double_modifier;
+           i += 7;
+         }
+       else
+         goto no_more_modifiers;
+       break;
+
+      case 't':
+       if (i + 7 > name->size)
+         goto no_more_modifiers;
+       if (! strncmp (name->data + i, "triple-", 7))
+         {
+           modifiers |= triple_modifier;
+           i += 7;
+         }
        else
          goto no_more_modifiers;
        break;
@@ -2306,7 +2796,8 @@ parse_modifiers_uncached (symbol, modifier_end)
  no_more_modifiers:
 
   /* Should we include the `click' modifier?  */
-  if (! (modifiers & (down_modifier | drag_modifier))
+  if (! (modifiers & (down_modifier | drag_modifier
+                     | double_modifier | triple_modifier))
       && i + 7 == name->size
       && strncmp (name->data + i, "mouse-", 6) == 0
       && ('0' <= name->data[i + 6] && name->data[i + 6] <= '9'))
@@ -2332,7 +2823,7 @@ apply_modifiers_uncached (modifiers, base, base_len)
      to use Fintern, which expects a genuine Lisp_String, and keeps a
      reference to it.  */
   char *new_mods =
-    (char *) alloca (sizeof ("A-C-H-M-S-s-down-drag-"));
+    (char *) alloca (sizeof ("A-C-H-M-S-s-down-drag-double-triple-"));
   int mod_len;
 
   {
@@ -2349,6 +2840,8 @@ apply_modifiers_uncached (modifiers, base, base_len)
     if (modifiers & meta_modifier)  { *p++ = 'M'; *p++ = '-'; }
     if (modifiers & shift_modifier) { *p++ = 'S'; *p++ = '-'; }
     if (modifiers & super_modifier) { *p++ = 's'; *p++ = '-'; }
+    if (modifiers & double_modifier)  { strcpy (p, "double-");  p += 7; }
+    if (modifiers & triple_modifier)  { strcpy (p, "triple-");  p += 7; }
     if (modifiers & down_modifier)  { strcpy (p, "down-");  p += 5; }
     if (modifiers & drag_modifier)  { strcpy (p, "drag-");  p += 5; }
     /* The click modifier is denoted by the absence of other modifiers.  */
@@ -2371,10 +2864,11 @@ apply_modifiers_uncached (modifiers, base, base_len)
 
 static char *modifier_names[] =
 {
-  "up", 0, 0, 0, 0, 0, 0, "down",
-  "drag", "click", 0, 0, 0, 0, 0, 0,
+  "up", "down", "drag", "click", "double", "triple", 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0,
   0, 0, "alt", "super", "hyper", "shift", "control", "meta"
 };
+#define NUM_MOD_NAMES (sizeof (modifier_names) / sizeof (modifier_names[0]))
 
 static Lisp_Object modifier_symbols;
 
@@ -2387,14 +2881,10 @@ lispy_modifier_list (modifiers)
   int i;
 
   modifier_list = Qnil;
-  for (i = 0; (1<<i) <= modifiers; i++)
+  for (i = 0; (1<<i) <= modifiers && i < NUM_MOD_NAMES; i++)
     if (modifiers & (1<<i))
-      {
-       if (i >= XVECTOR (modifier_symbols)->size)
-         abort ();
-       modifier_list = Fcons (XVECTOR (modifier_symbols)->contents[i],
-                              modifier_list);
-      }
+      modifier_list = Fcons (XVECTOR (modifier_symbols)->contents[i],
+                            modifier_list);
 
   return modifier_list;
 }
@@ -2424,6 +2914,8 @@ parse_modifiers (symbol)
                   Qnil);
       Lisp_Object mask;
 
+      if (modifiers & ~((1<<VALBITS) - 1))
+       abort ();
       XFASTINT (mask) = modifiers;
       elements = Fcons (unmodified, Fcons (mask, Qnil));
 
@@ -2458,6 +2950,9 @@ apply_modifiers (modifiers, base)
 {
   Lisp_Object cache, index, entry, new_symbol;
 
+  /* Mask out upper bits.  We don't know where this value's been.  */
+  modifiers &= (1<<VALBITS) - 1;
+
   /* The click modifier never figures into cache indices.  */
   cache = Fget (base, Qmodifier_cache);
   XFASTINT (index) = (modifiers & ~click_modifier);
@@ -2489,8 +2984,8 @@ apply_modifiers (modifiers, base)
      You'd think we could just set this once and for all when we
      intern the symbol above, but reorder_modifiers may call us when
      BASE's property isn't set right; we can't assume that just
-     because we found something in the cache it must have its kind set
-     right.  */
+     because it has a Qmodifier_cache property it must have its
+     Qevent_kind set right as well.  */
   if (NILP (Fget (new_symbol, Qevent_kind)))
     {
       Lisp_Object kind = Fget (base, Qevent_kind);
@@ -2547,7 +3042,7 @@ reorder_modifiers (symbol)
    the returned symbol. 
 
    The symbols we create are supposed to have an
-   `event-symbol-elements' propery, which lists the modifiers present
+   `event-symbol-elements' property, which lists the modifiers present
    in the symbol's name.  */
 
 static Lisp_Object
@@ -2585,7 +3080,14 @@ modify_event_symbol (symbol_num, modifiers, symbol_kind, name_table,
   if (NILP (*slot))
     {
       /* No; let's create it.  */
-      *slot = intern (name_table[symbol_num]);
+      if (name_table[symbol_num])
+       *slot = intern (name_table[symbol_num]);
+      else
+       {
+         char buf[20];
+         sprintf (buf, "key-%d", symbol_num);
+         *slot = intern (buf);
+       }
 
       /* Fill in the cache entries for this symbol; this also  
         builds the Qevent_symbol_elements property, which the user
@@ -2668,7 +3170,10 @@ read_avail_input (expected)
     nread = (*read_socket_hook) (0, buf, KBD_BUFFER_SIZE, expected, expected);
   else
     {
-      unsigned char cbuf[KBD_BUFFER_SIZE];
+      /* 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];
 
 #ifdef FIONREAD
       /* Find out how much input is available.  */
@@ -2685,7 +3190,7 @@ read_avail_input (expected)
       if (nread > sizeof cbuf)
        nread = sizeof cbuf;
 #else /* no FIONREAD */
-#ifdef USG
+#if defined(USG) || defined(DGUX)
       /* Read some input if available, but don't wait.  */
       nread = sizeof cbuf;
       fcntl (fileno (stdin), F_SETFL, O_NDELAY);
@@ -2706,6 +3211,8 @@ read_avail_input (expected)
          if (nread == 0)
            kill (SIGHUP, 0);
 #endif
+         /* This code is wrong, but at least it gets the right results.
+            Fix it for 19.23.  */
          /* Retry the read if it is interrupted.  */
          if (nread >= 0
              || ! (errno == EAGAIN || errno == EFAULT
@@ -2717,17 +3224,18 @@ read_avail_input (expected)
        }
 
 #ifndef FIONREAD
-#ifdef USG
+#if defined (USG) || defined (DGUX)
       fcntl (fileno (stdin), F_SETFL, 0);
-#endif /* USG */
+#endif /* USG or DGUX */
 #endif /* no FIONREAD */
       for (i = 0; i < nread; i++)
        {
          buf[i].kind = ascii_keystroke;
          buf[i].modifiers = 0;
-         if (meta_key && (cbuf[i] & 0x80))
+         if (meta_key == 1 && (cbuf[i] & 0x80))
            buf[i].modifiers = meta_modifier;
-         cbuf[i] &= ~0x80;
+         if (meta_key != 2)
+           cbuf[i] &= ~0x80;
            
          XSET (buf[i].code,            Lisp_Int,   cbuf[i]);
 #ifdef MULTI_FRAME
@@ -2800,6 +3308,22 @@ input_available_signal (signo)
   errno = old_errno;
 }
 #endif /* SIGIO */
+
+/* Send ourselves a SIGIO.
+
+   This function exists so that the UNBLOCK_INPUT macro in
+   blockinput.h can have some way to take care of input we put off
+   dealing with, without assuming that every file which uses
+   UNBLOCK_INPUT also has #included the files necessary to get SIGIO. */
+void
+reinvoke_input_signal ()
+{
+#ifdef SIGIO  
+  kill (0, SIGIO);
+#endif
+}
+
+
 \f
 /* Return the prompt-string of a sparse keymap.
    This is the first element which is a string.
@@ -2838,11 +3362,21 @@ menu_bar_items ()
      in the current keymaps, or nil where it is not a prefix.  */
   Lisp_Object *maps;
 
-  Lisp_Object def, tem;
+  Lisp_Object def, tem, tail;
 
   Lisp_Object result;
 
   int mapno;
+  Lisp_Object oquit;
+
+  /* In order to build the menus, we need to call the keymap
+     accessors.  They all call QUIT.  But this function is called
+     during redisplay, during which a quit is fatal.  So inhibit
+     quitting while building the menus.
+     We do this instead of specbind because (1) errors will clear it anyway
+     and (2) this avoids risk of specpdl overflow.  */
+  oquit = Vinhibit_quit;
+  Vinhibit_quit = Qt; 
 
   /* Build our list of keymaps.
      If we recognize a function key and replace its escape sequence in
@@ -2858,19 +3392,19 @@ menu_bar_items ()
 #ifdef USE_TEXT_PROPERTIES
     maps[nmaps-2] = get_local_map (PT, current_buffer);
 #else
-    maps[nmaps-2] = current_buffer->local_map;
+    maps[nmaps-2] = current_buffer->keymap;
 #endif
-    maps[nmaps-1] = global_map;
+    maps[nmaps-1] = current_global_map;
   }
 
   /* Look up in each map the dummy prefix key `menu-bar'.  */
 
   result = Qnil;
 
-  for (mapno = 0; mapno < nmaps; mapno++)
+  for (mapno = nmaps - 1; mapno >= 0; mapno--)
     {
       if (! NILP (maps[mapno]))
-       def = get_keyelt (access_keymap (maps[mapno], Qmenu_bar, 1));
+       def = get_keyelt (access_keymap (maps[mapno], Qmenu_bar, 1, 0));
       else
        def = Qnil;
 
@@ -2879,6 +3413,17 @@ menu_bar_items ()
        result = menu_bar_one_keymap (def, result);
     }
 
+  for (tail = Vmenu_bar_final_items; CONSP (tail); tail = XCONS (tail)->cdr)
+    {
+      Lisp_Object elt;
+
+      elt = Fassq (XCONS (tail)->car, result);
+      if (!NILP (elt))
+       result = Fcons (elt, Fdelq (elt, result));
+    }
+
+  result = Fnreverse (result);
+  Vinhibit_quit = oquit;
   return result;
 }
 \f
@@ -2906,6 +3451,9 @@ menu_bar_one_keymap (keymap, result)
                result = menu_bar_item (key, item_string,
                                        Fcdr (binding), result);
            }
+         else if (EQ (binding, Qundefined))
+           result = menu_bar_item (key, item_string,
+                                   binding, result);
        }
       else if (XTYPE (item) == Lisp_Vector)
        {
@@ -2924,6 +3472,9 @@ menu_bar_one_keymap (keymap, result)
                    result = menu_bar_item (key, item_string,
                                            Fcdr (binding), result);
                }
+             else if (EQ (binding, Qundefined))
+               result = menu_bar_item (key, item_string,
+                                       binding, result);
            }
        }
     }
@@ -2935,9 +3486,17 @@ static Lisp_Object
 menu_bar_item (key, item_string, def, result)
      Lisp_Object key, item_string, def, result;
 {
-  Lisp_Object tem, elt;
+  Lisp_Object tem;
   Lisp_Object enabled;
 
+  if (EQ (def, Qundefined))
+    {
+      /* If a map has an explicit nil as definition,
+        discard any previously made menu bar item.  */
+      tem = Fassq (key, result);
+      return Fdelq (tem, result);
+    }
+
   /* See if this entry is enabled.  */
   enabled = Qt;
 
@@ -2952,20 +3511,16 @@ menu_bar_item (key, item_string, def, result)
 
   /* Add an entry for this key and string
      if there is none yet.  */
-  elt = Fassq (key, result);
-  if (!NILP (enabled) && NILP (elt))
+  tem = Fassq (key, result);
+  if (!NILP (enabled) && NILP (tem))
     result = Fcons (Fcons (key, Fcons (item_string, Qnil)), result);
 
   return result;
 }
 \f
-static int echo_flag;
-static int echo_now;
-
-/* Read a character like read_char but optionally prompt based on maps
-   in the array MAPS.  NMAPS is the length of MAPS.  Return nil if we
-   decided not to read a character, because there are no menu items in
-   MAPS.
+/* Read a character using menus based on maps in the array MAPS.
+   NMAPS is the length of MAPS.  Return nil if there are no menus in the maps.
+   Return t if we displayed a menu but the user rejected it.
 
    PREV_EVENT is the previous input event, or nil if we are reading
    the first event of a key sequence.
@@ -2977,7 +3532,7 @@ static int echo_now;
    The prompting is done based on the prompt-string of the map
    and the strings associated with various map elements.  */
 
-Lisp_Object
+static Lisp_Object
 read_char_menu_prompt (nmaps, maps, prev_event, used_mouse_menu)
      int nmaps;
      Lisp_Object *maps;
@@ -3030,8 +3585,17 @@ read_char_menu_prompt (nmaps, maps, prev_event, used_mouse_menu)
          realmaps[nmaps1++] = maps[mapno];
 
       value = Fx_popup_menu (prev_event, Flist (nmaps1, realmaps));
+      if (CONSP (value))
+       {
+         /* If we got more than one event, put all but the first
+            onto this list to be read later.
+            Return just the first event now.  */
+         unread_command_events
+           = nconc2 (XCONS (value)->cdr, unread_command_events);
+         value = XCONS (value)->car;
+       }
       if (NILP (value))
-       XSET (value, Lisp_Int, quit_char);
+       value = Qt;
       if (used_mouse_menu)
        *used_mouse_menu = 1;
       return value;
@@ -3172,6 +3736,8 @@ read_char_menu_prompt (nmaps, maps, prev_event, used_mouse_menu)
    When KEY is not defined in any of the keymaps, if it is an upper
    case letter and there are bindings for the corresponding lower-case
    letter, return the bindings for the lower-case letter.
+   We store 1 in *CASE_CONVERTED in this case.
+   Otherwise, we don't change *CASE_CONVERTED.
 
    If KEY has no bindings in any of the CURRENT maps, NEXT is left
    unmodified.
@@ -3179,10 +3745,11 @@ read_char_menu_prompt (nmaps, maps, prev_event, used_mouse_menu)
    NEXT may == CURRENT.  */
 
 static int
-follow_key (key, nmaps, current, defs, next)
+follow_key (key, nmaps, current, defs, next, case_converted)
      Lisp_Object key;
      Lisp_Object *current, *defs, *next;
      int nmaps;
+     int *case_converted;
 {
   int i, first_binding;
 
@@ -3194,7 +3761,7 @@ follow_key (key, nmaps, current, defs, next)
        if (! NILP (current[i]))
          {
            next[i] =
-             get_keyelt (access_keymap (current[i], meta_prefix_char, 1));
+             get_keyelt (access_keymap (current[i], meta_prefix_char, 1, 0));
 
            /* Note that since we pass the resulting bindings through
               get_keymap_1, non-prefix bindings for meta-prefix-char
@@ -3213,7 +3780,7 @@ follow_key (key, nmaps, current, defs, next)
     {
       if (! NILP (current[i]))
        {
-         defs[i] = get_keyelt (access_keymap (current[i], key, 1));
+         defs[i] = get_keyelt (access_keymap (current[i], key, 1, 0));
          if (! NILP (defs[i]))
            first_binding = i;
        }
@@ -3226,26 +3793,31 @@ follow_key (key, nmaps, current, defs, next)
      lower-case letter, return the bindings for the lower-case letter.  */
   if (first_binding == nmaps
       && XTYPE (key) == Lisp_Int
-      && (UPPERCASEP (XINT (key) & 0x3ffff)
+      && ((((XINT (key) & 0x3ffff)
+           < XSTRING (current_buffer->downcase_table)->size)
+          && UPPERCASEP (XINT (key) & 0x3ffff))
          || (XINT (key) & shift_modifier)))
     {
       if (XINT (key) & shift_modifier)
        XSETINT (key, XINT (key) & ~shift_modifier);
       else
-       XSETINT (key, DOWNCASE (XINT (key)));
+       XSETINT (key, (DOWNCASE (XINT (key) & 0x3ffff)
+                      | (XINT (key) & ~0x3ffff)));
 
       first_binding = nmaps;
       for (i = nmaps - 1; i >= 0; i--)
        {
          if (! NILP (current[i]))
            {
-             defs[i] = get_keyelt (access_keymap (current[i], key, 1));
+             defs[i] = get_keyelt (access_keymap (current[i], key, 1, 0));
              if (! NILP (defs[i]))
                first_binding = i;
            }
          else
            defs[i] = Qnil;
        }
+      if (first_binding  != nmaps)
+       *case_converted = 1;
     }
 
   /* Given the set of bindings we've found, produce the next set of maps.  */
@@ -3260,6 +3832,7 @@ follow_key (key, nmaps, current, defs, next)
    storing it in KEYBUF, a buffer of size BUFSIZE.
    Prompt with PROMPT.
    Return the length of the key sequence stored.
+   Return -1 if the user rejected a command menu.
 
    Echo starting immediately unless `prompt' is 0.
 
@@ -3272,8 +3845,10 @@ follow_key (key, nmaps, current, defs, next)
    function key's sequence.  If so, we try to read the whole function
    key, and substitute its symbolic name into the key sequence.
 
-   We ignore unbound `down-' mouse clicks.  We turn unbound `drag-'
-   events into similar click events, if that would make them bound.
+   We ignore unbound `down-' mouse clicks.  We turn unbound `drag-' and
+   `double-' events into similar click events, if that would make them
+   bound.  We try to turn `triple-' events first into `double-' events,
+   then into clicks.
 
    If we get a mouse click in a mode line, vertical divider, or other
    non-text area, we treat the click as if it were prefixed by the
@@ -3286,11 +3861,12 @@ follow_key (key, nmaps, current, defs, next)
    If the user switches frames in the midst of a key sequence, we put
    off the switch-frame event until later; the next call to
    read_char will return it.  */
+
 static int
 read_key_sequence (keybuf, bufsize, prompt)
      Lisp_Object *keybuf;
      int bufsize;
-     char *prompt;
+     Lisp_Object prompt;
 {
   int count = specpdl_ptr - specpdl;
 
@@ -3344,31 +3920,51 @@ read_key_sequence (keybuf, bufsize, prompt)
      recognized a function key, to avoid searching for the function
      key's again in Vfunction_key_map.  */
   int fkey_start = 0, fkey_end = 0;
-  Lisp_Object fkey_map = Vfunction_key_map;
+  Lisp_Object fkey_map;
+
+  /* Likewise, for key_translation_map.  */
+  int keytran_start = 0, keytran_end = 0;
+  Lisp_Object keytran_map;
 
   /* If we receive a ``switch-frame'' event in the middle of a key sequence,
      we put it off for later.  While we're reading, we keep the event here.  */
-  Lisp_Object delayed_switch_frame = Qnil;
+  Lisp_Object delayed_switch_frame;
+
+  /* See the comment below... */
+#if defined (GOBBLE_FIRST_EVENT)
+  Lisp_Object first_event;
+#endif
+
+  struct buffer *starting_buffer;
+
+  /* Nonzero if we found the binding for one of the chars
+     in this key sequence by downcasing it.  */
+  int case_converted = 0;
 
+  /* Nonzero if we seem to have got the beginning of a binding
+     in function_key_map.  */
+  int function_key_possible = 0;
 
-  /* If there is no function key map, turn off function key scanning.  */
+  int junk;
+
+  last_nonmenu_event = Qnil;
+
+  delayed_switch_frame = Qnil;
+  fkey_map = Vfunction_key_map;
+  keytran_map = Vkey_translation_map;
+
+  /* If there is no function-key-map, turn off function key scanning.  */
   if (NILP (Fkeymapp (Vfunction_key_map)))
     fkey_start = fkey_end = bufsize + 1;
 
-  /* We need to save the current buffer in case we switch buffers to
-     find the right binding for a mouse click.  Note that we can't use
-     save_excursion_{save,restore} here, because they save point as
-     well as the current buffer; we don't want to save point, because
-     redisplay may change it, to accomodate a Fset_window_start or
-     something.  */
-  record_unwind_protect (Fset_buffer, Fcurrent_buffer ());
-                        
-  last_nonmenu_event = Qnil;
+  /* If there is no key-translation-map, turn off scanning.  */
+  if (NILP (Fkeymapp (Vkey_translation_map)))
+    keytran_start = keytran_end = bufsize + 1;
 
   if (INTERACTIVE)
     {
-      if (prompt)
-       echo_prompt (prompt);
+      if (!NILP (prompt))
+       echo_prompt (XSTRING (prompt)->data);
       else if (cursor_in_echo_area)
        /* This doesn't put in a dash if the echo buffer is empty, so
           you don't always see a dash hanging out in the minibuffer.  */
@@ -3381,11 +3977,26 @@ read_key_sequence (keybuf, bufsize, prompt)
     echo_start = echo_length ();
   keys_start = this_command_key_count;
 
+#if defined (GOBBLE_FIRST_EVENT)
+  /* This doesn't quite work, because some of the things that read_char
+     does cannot safely be bypassed.  It seems too risky to try to make
+     this work right.  */ 
+
+  /* Read the first char of the sequence specially, before setting
+     up any keymaps, in case a filter runs and switches buffers on us.  */
+  first_event = read_char (NILP (prompt), 0, submaps, last_nonmenu_event,
+                          &junk);
+#endif /* GOBBLE_FIRST_EVENT */
+
   /* We jump here when the key sequence has been thoroughly changed, and
      we need to rescan it starting from the beginning.  When we jump here,
      keybuf[0..mock_input] holds the sequence we should reread.  */
  replay_sequence:
 
+  starting_buffer = current_buffer;
+  case_converted = 0;
+  function_key_possible = 0;
+
   /* Build our list of keymaps.
      If we recognize a function key and replace its escape sequence in
      keybuf with its symbol, or if the sequence starts with a mouse
@@ -3405,9 +4016,9 @@ read_key_sequence (keybuf, bufsize, prompt)
 #ifdef USE_TEXT_PROPERTIES
     submaps[nmaps-2] = get_local_map (PT, current_buffer);
 #else
-    submaps[nmaps-2] = current_buffer->local_map;
+    submaps[nmaps-2] = current_buffer->keymap;
 #endif
-    submaps[nmaps-1] = global_map;
+    submaps[nmaps-1] = current_global_map;
   }
 
   /* Find an accurate initial value for first_binding.  */
@@ -3415,9 +4026,7 @@ read_key_sequence (keybuf, bufsize, prompt)
     if (! NILP (submaps[first_binding]))
       break;
 
-  /* We jump here when a function key substitution has forced us to
-     reprocess the current key sequence.  keybuf[0..mock_input] is the
-     sequence we want to reread.  */
+  /* Start from the beginning in keybuf.  */
   t = 0;
 
   /* These are no-ops the first time through, but if we restart, they
@@ -3426,11 +4035,24 @@ read_key_sequence (keybuf, bufsize, prompt)
   if (INTERACTIVE)
     echo_truncate (echo_start);
 
-  /* If the best binding for the current key sequence is a keymap,
-     or we may be looking at a function key's escape sequence, keep
-     on reading.  */
+  /* If the best binding for the current key sequence is a keymap, or
+     we may be looking at a function key's escape sequence, keep on
+     reading.  */
   while ((first_binding < nmaps && ! NILP (submaps[first_binding]))
-        || (first_binding >= nmaps && fkey_start < t))
+        || (first_binding >= nmaps
+            && fkey_start < t
+            /* mock input is never part of a function key's sequence.  */
+            && mock_input <= fkey_start)
+        || (first_binding >= nmaps
+            && keytran_start < t
+            /* mock input is never part of a function key's sequence.  */
+            && mock_input <= keytran_start)
+        /* Don't return in the middle of a possible function key sequence,
+           if the only bindings we found were via case conversion.
+           Thus, if ESC O a has a function-key-map translation
+           and ESC o has a binding, don't return after ESC O,
+           so that we can translate ESC O plus the next character.  */
+        || (function_key_possible && case_converted))
     {
       Lisp_Object key;
       int used_mouse_menu = 0;
@@ -3466,6 +4088,9 @@ read_key_sequence (keybuf, bufsize, prompt)
       this_command_key_count = keys_local_start;
       first_binding = local_first_binding;
 
+      /* By default, assume each event is "real".  */
+      last_real_key_start = t;
+
       /* Does mock_input indicate that we are re-reading a key sequence?  */
       if (t < mock_input)
        {
@@ -3477,11 +4102,16 @@ read_key_sequence (keybuf, bufsize, prompt)
       /* If not, we should actually read a character.  */
       else
        {
-         last_real_key_start = t;
+         struct buffer *buf = current_buffer;
 
-         key = read_char (!prompt, nmaps, submaps, last_nonmenu_event,
+         key = read_char (NILP (prompt), nmaps, submaps, last_nonmenu_event,
                           &used_mouse_menu);
 
+         /* read_char returns t when it shows a menu and the user rejects it.
+            Just return -1.  */
+         if (EQ (key, Qt))
+           return -1;
+
          /* read_char returns -1 at the end of a macro.
             Emacs 18 handles this by returning immediately with a
             zero, so that's what we'll do.  */
@@ -3491,88 +4121,142 @@ read_key_sequence (keybuf, bufsize, prompt)
              goto done;
            }
          
+         /* If we have a quit that was typed in another frame, and
+            quit_throw_to_read_char switched buffers,
+            replay to get the right keymap.  */
+         if (EQ (key, quit_char) && current_buffer != starting_buffer)
+           {
+             keybuf[t++] = key;
+             mock_input = t;
+             Vquit_flag = Qnil;
+             goto replay_sequence;
+           }
+           
          Vquit_flag = Qnil;
+       }
 
-         /* Clicks in non-text areas get prefixed by the symbol 
-            in their CHAR-ADDRESS field.  For example, a click on
-            the mode line is prefixed by the symbol `mode-line'.
+      /* Clicks in non-text areas get prefixed by the symbol 
+        in their CHAR-ADDRESS field.  For example, a click on
+        the mode line is prefixed by the symbol `mode-line'.
+
+        Furthermore, key sequences beginning with mouse clicks
+        are read using the keymaps of the buffer clicked on, not
+        the current buffer.  So we may have to switch the buffer
+        here.
+
+        When we turn one event into two events, we must make sure
+        that neither of the two looks like the original--so that,
+        if we replay the events, they won't be expanded again.
+        If not for this, such reexpansion could happen either here
+        or when user programs play with this-command-keys.  */
+      if (EVENT_HAS_PARAMETERS (key))
+       {
+         Lisp_Object kind = EVENT_HEAD_KIND (EVENT_HEAD (key));
 
-            Furthermore, key sequences beginning with mouse clicks
-            are read using the keymaps of the buffer clicked on, not
-            the current buffer.  So we may have to switch the buffer
-            here.  */
-         if (EVENT_HAS_PARAMETERS (key))
+         if (EQ (kind, Qmouse_click))
            {
-             Lisp_Object kind = EVENT_HEAD_KIND (EVENT_HEAD (key));
+             Lisp_Object window = POSN_WINDOW      (EVENT_START (key));
+             Lisp_Object posn   = POSN_BUFFER_POSN (EVENT_START (key));
 
-             if (EQ (kind, Qmouse_click))
+             if (XTYPE (posn) == Lisp_Cons)
                {
-                 Lisp_Object window = POSN_WINDOW      (EVENT_START (key));
-                 Lisp_Object posn   = POSN_BUFFER_POSN (EVENT_START (key));
-
-                 /* Key sequences beginning with mouse clicks are
-                    read using the keymaps in the buffer clicked on,
-                    not the current buffer.  If we're at the
-                    beginning of a key sequence, switch buffers.  */
-                 if (t == 0
-                     && XTYPE (window) == Lisp_Window
-                     && XTYPE (XWINDOW (window)->buffer) == Lisp_Buffer
-                     && XBUFFER (XWINDOW (window)->buffer) != current_buffer)
-                   {
-                     if (XTYPE (posn) == Lisp_Symbol)
-                       {
-                         if (t + 1 >= bufsize)
-                           error ("key sequence too long");
-                         keybuf[t] = posn;
-                         keybuf[t+1] = key;
-                         mock_input = t + 2;
-                       }
-                     else
-                       {
-                         keybuf[t] = key;
-                         mock_input = t + 1;
-                       }
-
-                     set_buffer_internal (XBUFFER (XWINDOW (window)->buffer));
-                     goto replay_sequence;
-                   }
-                 else if (XTYPE (posn) == Lisp_Symbol)
-                   {
-                     if (t + 1 >= bufsize)
-                       error ("key sequence too long");
-                     keybuf[t] = posn;
-                     keybuf[t+1] = key;
-                     mock_input = t + 2;
+                 /* We're looking at the second event of a
+                    sequence which we expanded before.  Set
+                    last_real_key_start appropriately.  */
+                 if (t > 0)
+                   last_real_key_start = t - 1;
+               }
 
-                     goto replay_key;
-                   }
+             /* Key sequences beginning with mouse clicks are
+                read using the keymaps in the buffer clicked on,
+                not the current buffer.  If we're at the
+                beginning of a key sequence, switch buffers.  */
+             if (last_real_key_start == 0
+                 && XTYPE (window) == Lisp_Window
+                 && XTYPE (XWINDOW (window)->buffer) == Lisp_Buffer
+                 && XBUFFER (XWINDOW (window)->buffer) != current_buffer)
+               {
+                 keybuf[t] = key;
+                 mock_input = t + 1;
+
+                 /* Arrange to go back to the original buffer once we're
+                    done reading the key sequence.  Note that we can't
+                    use save_excursion_{save,restore} here, because they
+                    save point as well as the current buffer; we don't
+                    want to save point, because redisplay may change it,
+                    to accommodate a Fset_window_start or something.  We
+                    don't want to do this at the top of the function,
+                    because we may get input from a subprocess which
+                    wants to change the selected window and stuff (say,
+                    emacsclient).  */
+                 record_unwind_protect (Fset_buffer, Fcurrent_buffer ());
+
+                 set_buffer_internal (XBUFFER (XWINDOW (window)->buffer));
+                 goto replay_sequence;
                }
-             else if (EQ (kind, Qswitch_frame))
+             else if (XTYPE (posn) == Lisp_Symbol)
                {
-                 /* If we're at the beginning of a key sequence, go
-                    ahead and return this event.  If we're in the
-                    midst of a key sequence, delay it until the end. */
-                 if (t > 0)
-                   {
-                     delayed_switch_frame = key;
-                     goto replay_key;
-                   }
+                 /* Expand mode-line and scroll-bar events into two events:
+                    use posn as a fake prefix key.  */
+
+                 if (t + 1 >= bufsize)
+                   error ("key sequence too long");
+                 keybuf[t] = posn;
+                 keybuf[t+1] = key;
+                 mock_input = t + 2;
+
+                 /* Zap the position in key, so we know that we've
+                    expanded it, and don't try to do so again.  */
+                 POSN_BUFFER_POSN (EVENT_START (key))
+                   = Fcons (posn, Qnil);
+                 goto replay_key;
                }
-             else
+           }
+         else if (EQ (kind, Qswitch_frame))
+           {
+             /* If we're at the beginning of a key sequence, go
+                ahead and return this event.  If we're in the
+                midst of a key sequence, delay it until the end. */
+             if (t > 0)
                {
-                 Lisp_Object posn   = POSN_BUFFER_POSN (EVENT_START (key));
+                 delayed_switch_frame = key;
+                 goto replay_key;
+               }
+           }
+         else
+           {
+             Lisp_Object posn   = POSN_BUFFER_POSN (EVENT_START (key));
 
-                 /* Handle menu-bar events:
-                    insert the dummy prefix char `menu-bar'.  */
-                 if (EQ (posn, Qmenu_bar))
-                   {
-                     if (t + 1 >= bufsize)
-                       error ("key sequence too long");
-                     keybuf[t] = posn;
-                     keybuf[t+1] = key;
-                     mock_input = t + 2;
-                     goto replay_sequence;
-                   }
+             /* Handle menu-bar events:
+                insert the dummy prefix event `menu-bar'.  */
+             if (EQ (posn, Qmenu_bar))
+               {
+                 if (t + 1 >= bufsize)
+                   error ("key sequence too long");
+                 /* Run the Lucid hook.  */
+                 call1 (Vrun_hooks, Qactivate_menubar_hook);
+                 /* If it has changed current-menubar from previous value,
+                    really recompute the menubar from the value.  */
+                 if (! NILP (Vlucid_menu_bar_dirty_flag))
+                   call0 (Qrecompute_lucid_menubar);
+                 keybuf[t] = posn;
+                 keybuf[t+1] = key;
+
+                 /* Zap the position in key, so we know that we've
+                    expanded it, and don't try to do so again.  */
+                 POSN_BUFFER_POSN (EVENT_START (key))
+                   = Fcons (posn, Qnil);
+
+                 mock_input = t + 2;
+                 goto replay_sequence;
+               }
+             else if (XTYPE (posn) == Lisp_Cons)
+               {
+                 /* We're looking at the second event of a
+                    sequence which we expanded before.  Set
+                    last_real_key_start appropriately.  */
+                 if (last_real_key_start == t && t > 0)
+                   last_real_key_start = t - 1;
                }
            }
        }
@@ -3583,7 +4267,8 @@ read_key_sequence (keybuf, bufsize, prompt)
                                   nmaps   - first_binding,
                                   submaps + first_binding,
                                   defs    + first_binding,
-                                  submaps + first_binding)
+                                  submaps + first_binding,
+                                  &case_converted)
                       + first_binding);
 
       /* If KEY wasn't bound, we'll try some fallbacks.  */
@@ -3591,83 +4276,109 @@ read_key_sequence (keybuf, bufsize, prompt)
        {
          Lisp_Object head = EVENT_HEAD (key);
 
+         if (EQ (head, Vhelp_char))
+           {
+             read_key_sequence_cmd = Vprefix_help_command;
+             keybuf[t++] = key;
+             last_nonmenu_event = key;
+             goto done;
+           }
+
          if (XTYPE (head) == Lisp_Symbol)
            {
              Lisp_Object breakdown = parse_modifiers (head);
-             Lisp_Object modifiers =
-               XINT (XCONS (XCONS (breakdown)->cdr)->car);
-
-             /* We drop unbound `down-' events altogether.  */
-             if (modifiers & down_modifier)
+             int modifiers = XINT (XCONS (XCONS (breakdown)->cdr)->car);
+
+             /* Attempt to reduce an unbound mouse event to a simpler
+                event that is bound:
+                  Drags reduce to clicks.
+                  Double-clicks reduce to clicks.
+                  Triple-clicks reduce to double-clicks, then to clicks.
+                  Down-clicks are eliminated.
+                  Double-downs reduce to downs, then are eliminated.
+                  Triple-downs reduce to double-downs, then to downs,
+                    then are eliminated. */
+             if (modifiers & (down_modifier | drag_modifier
+                              | double_modifier | triple_modifier))
                {
-                 /* Dispose of this event by simply jumping back to
-                    replay_key, to get another event.
-
-                    Note that if this event came from mock input,
-                    then just jumping back to replay_key will just
-                    hand it to us again.  So we have to wipe out any
-                    mock input.
-
-                    We could delete keybuf[t] and shift everything
-                    after that to the left by one spot, but we'd also
-                    have to fix up any variable that points into
-                    keybuf, and shifting isn't really necessary
-                    anyway.
-
-                    Adding prefixes for non-textual mouse clicks
-                    creates two characters of mock input, and both
-                    must be thrown away.  If we're only looking at
-                    the prefix now, we can just jump back to
-                    replay_key.  On the other hand, if we've already
-                    processed the prefix, and now the actual click
-                    itself is giving us trouble, then we've lost the
-                    state of the keymaps we want to backtrack to, and
-                    we need to replay the whole sequence to rebuild
-                    it.
-
-                    Beyond that, only function key expansion could
-                    create more than two keys, but that should never
-                    generate mouse events, so it's okay to zero
-                    mock_input in that case too.
-
-                    Isn't this just the most wonderful code ever?  */
-                 if (t == last_real_key_start)
+                 while (modifiers & (down_modifier | drag_modifier
+                                     | double_modifier | triple_modifier))
                    {
-                     mock_input = 0;
-                     goto replay_key;
-                   }
-                 else
-                   {
-                     mock_input = last_real_key_start;
-                     goto replay_sequence;
-                   }
-               }
+                     Lisp_Object new_head, new_click;
+                     if (modifiers & triple_modifier)
+                       modifiers ^= (double_modifier | triple_modifier);
+                     else if (modifiers & (drag_modifier | double_modifier))
+                       modifiers &= ~(drag_modifier | double_modifier);
+                     else
+                       {
+                         /* Dispose of this `down' event by simply jumping
+                            back to replay_key, to get another event.
+
+                            Note that if this event came from mock input,
+                            then just jumping back to replay_key will just
+                            hand it to us again.  So we have to wipe out any
+                            mock input.
+
+                            We could delete keybuf[t] and shift everything
+                            after that to the left by one spot, but we'd also
+                            have to fix up any variable that points into
+                            keybuf, and shifting isn't really necessary
+                            anyway.
+
+                            Adding prefixes for non-textual mouse clicks
+                            creates two characters of mock input, and both
+                            must be thrown away.  If we're only looking at
+                            the prefix now, we can just jump back to
+                            replay_key.  On the other hand, if we've already
+                            processed the prefix, and now the actual click
+                            itself is giving us trouble, then we've lost the
+                            state of the keymaps we want to backtrack to, and
+                            we need to replay the whole sequence to rebuild
+                            it.
+
+                            Beyond that, only function key expansion could
+                            create more than two keys, but that should never
+                            generate mouse events, so it's okay to zero
+                            mock_input in that case too.
+
+                            Isn't this just the most wonderful code ever?  */
+                         if (t == last_real_key_start)
+                           {
+                             mock_input = 0;
+                             goto replay_key;
+                           }
+                         else
+                           {
+                             mock_input = last_real_key_start;
+                             goto replay_sequence;
+                           }
+                       }
 
-             /* We turn unbound `drag-' events into `click-'
-                events, if the click would be bound.  */
-             else if (modifiers & drag_modifier)
-               {
-                 Lisp_Object new_head =
-                   apply_modifiers (modifiers & ~drag_modifier,
-                                    XCONS (breakdown)->car);
-                 Lisp_Object new_click =
-                   Fcons (new_head, Fcons (EVENT_START (key), Qnil));
-
-                 /* Look for a binding for this new key.  follow_key
-                    promises that it didn't munge submaps the
-                    last time we called it, since key was unbound.  */
-                 first_binding =
-                   (follow_key (new_click,
-                                nmaps   - local_first_binding,
-                                submaps + local_first_binding,
-                                defs    + local_first_binding,
-                                submaps + local_first_binding)
-                    + local_first_binding);
-
-                 /* If that click is bound, go for it.  */
-                 if (first_binding < nmaps)
-                   key = new_click;
-                 /* Otherwise, we'll leave key set to the drag event.  */
+                     new_head
+                       = apply_modifiers (modifiers, XCONS (breakdown)->car);
+                     new_click
+                       = Fcons (new_head, Fcons (EVENT_START (key), Qnil));
+
+                     /* Look for a binding for this new key.  follow_key
+                        promises that it didn't munge submaps the
+                        last time we called it, since key was unbound.  */
+                     first_binding
+                       = (follow_key (new_click,
+                                      nmaps   - local_first_binding,
+                                      submaps + local_first_binding,
+                                      defs    + local_first_binding,
+                                      submaps + local_first_binding,
+                                      &case_converted)
+                          + local_first_binding);
+
+                     /* If that click is bound, go for it.  */
+                     if (first_binding < nmaps)
+                       {
+                         key = new_click;
+                         break;
+                       }
+                     /* Otherwise, we'll leave key set to the drag event.  */
+                   }
                }
            }
        }
@@ -3684,12 +4395,14 @@ read_key_sequence (keybuf, bufsize, prompt)
         off the end of it.  We only want to scan real keyboard input
         for function key sequences, so if mock_input says that we're
         re-reading old events, don't examine it.  */
-      if (first_binding >= nmaps
+      if ((first_binding >= nmaps || case_converted)
          && t >= mock_input)
        {
          Lisp_Object fkey_next;
 
-         /* Scan from fkey_end until we find a bound suffix.  */
+         /* Continue scan from fkey_end until we find a bound suffix.
+            If we fail, increment fkey_start
+            and start fkey_end from there.  */
          while (fkey_end < t)
            {
              Lisp_Object key;
@@ -3699,37 +4412,71 @@ read_key_sequence (keybuf, bufsize, prompt)
                 with meta_prefix_char.  I hate this.  */
              if (XTYPE (key) == Lisp_Int && XINT (key) & meta_modifier)
                {
-                 fkey_next =
-                   get_keymap_1
+                 fkey_next
+                   get_keymap_1
                      (get_keyelt
-                      (access_keymap
-                       (fkey_map, meta_prefix_char, 1)),
+                      (access_keymap (fkey_map, meta_prefix_char, 1, 0)),
                       0, 1);
                  XFASTINT (key) = XFASTINT (key) & ~meta_modifier;
                }
              else
                fkey_next = fkey_map;
 
-             fkey_next =
-               get_keyelt (access_keymap (fkey_next, key, 1));
+             fkey_next
+               = get_keyelt (access_keymap (fkey_next, key, 1, 0));
+
+             /* If the function key map gives a function, not an
+                array, then call the function with no args and use
+                its value instead.  */
+             if (SYMBOLP (fkey_next) && ! NILP (Ffboundp (fkey_next))
+                 && fkey_end == t)
+               {
+                 struct gcpro gcpro1, gcpro2, gcpro3;
+                 Lisp_Object tem;
+                 tem = fkey_next;
+
+                 GCPRO3 (fkey_map, keytran_map, delayed_switch_frame);
+                 fkey_next = call1 (fkey_next, prompt);
+                 UNGCPRO;
+                 /* If the function returned something invalid,
+                    barf--don't ignore it.
+                    (To ignore it safely, we would need to gcpro a bunch of 
+                    other variables.)  */
+                 if (! (VECTORP (fkey_next) || STRINGP (fkey_next)))
+                   error ("Function in function-key-map returns invalid key sequence");
+               }
+
+             function_key_possible = ! NILP (fkey_next);
 
              /* If keybuf[fkey_start..fkey_end] is bound in the
                 function key map and it's a suffix of the current
                 sequence (i.e. fkey_end == t), replace it with
                 the binding and restart with fkey_start at the end. */
-             if (XTYPE (fkey_next) == Lisp_Vector
+             if ((VECTORP (fkey_next) || STRINGP (fkey_next))
                  && fkey_end == t)
                {
-                 t = fkey_start + XVECTOR (fkey_next)->size;
+                 int len = XFASTINT (Flength (fkey_next));
+
+                 t = fkey_start + len;
                  if (t >= bufsize)
                    error ("key sequence too long");
 
-                 bcopy (XVECTOR (fkey_next)->contents,
-                        keybuf + fkey_start,
-                        (t - fkey_start) * sizeof (keybuf[0]));
+                 if (VECTORP (fkey_next))
+                   bcopy (XVECTOR (fkey_next)->contents,
+                          keybuf + fkey_start,
+                          (t - fkey_start) * sizeof (keybuf[0]));
+                 else if (STRINGP (fkey_next))
+                   {
+                     int i;
+
+                     for (i = 0; i < len; i++)
+                       XFASTINT (keybuf[fkey_start + i])
+                         = XSTRING (fkey_next)->data[i];
+                   }
                  
                  mock_input = t;
                  fkey_start = fkey_end = t;
+                 fkey_map = Vfunction_key_map;
 
                  goto replay_sequence;
                }
@@ -3742,9 +4489,103 @@ read_key_sequence (keybuf, bufsize, prompt)
                {
                  fkey_end = ++fkey_start;
                  fkey_map = Vfunction_key_map;
+                 function_key_possible = 0;
                }
            }
        }
+
+      /* Look for this sequence in key-translation-map.  */
+      {
+       Lisp_Object keytran_next;
+
+       /* Scan from keytran_end until we find a bound suffix.  */
+       while (keytran_end < t)
+         {
+           Lisp_Object key;
+
+           key = keybuf[keytran_end++];
+           /* Look up meta-characters by prefixing them
+              with meta_prefix_char.  I hate this.  */
+           if (XTYPE (key) == Lisp_Int && XINT (key) & meta_modifier)
+             {
+               keytran_next
+                 = get_keymap_1
+                   (get_keyelt
+                    (access_keymap (keytran_map, meta_prefix_char, 1, 0)),
+                    0, 1);
+               XFASTINT (key) = XFASTINT (key) & ~meta_modifier;
+             }
+           else
+             keytran_next = keytran_map;
+
+           keytran_next
+             = get_keyelt (access_keymap (keytran_next, key, 1, 0));
+
+           /* If the key translation map gives a function, not an
+              array, then call the function with no args and use
+              its value instead.  */
+           if (SYMBOLP (keytran_next) && ! NILP (Ffboundp (keytran_next))
+               && keytran_end == t)
+             {
+               struct gcpro gcpro1, gcpro2, gcpro3;
+               Lisp_Object tem;
+               tem = keytran_next;
+
+               GCPRO3 (keytran_map, keytran_map, delayed_switch_frame);
+               keytran_next = call1 (keytran_next, prompt);
+               UNGCPRO;
+               /* If the function returned something invalid,
+                  barf--don't ignore it.
+                  (To ignore it safely, we would need to gcpro a bunch of 
+                  other variables.)  */
+               if (! (VECTORP (keytran_next) || STRINGP (keytran_next)))
+                 error ("Function in function-key-map returns invalid key sequence");
+             }
+
+           /* If keybuf[keytran_start..keytran_end] is bound in the
+              key translation map and it's a suffix of the current
+              sequence (i.e. keytran_end == t), replace it with
+              the binding and restart with keytran_start at the end. */
+           if ((VECTORP (keytran_next) || STRINGP (keytran_next))
+               && keytran_end == t)
+             {
+               int len = XFASTINT (Flength (keytran_next));
+
+               t = keytran_start + len;
+               if (t >= bufsize)
+                 error ("key sequence too long");
+
+               if (VECTORP (keytran_next))
+                 bcopy (XVECTOR (keytran_next)->contents,
+                        keybuf + keytran_start,
+                        (t - keytran_start) * sizeof (keybuf[0]));
+               else if (STRINGP (keytran_next))
+                 {
+                   int i;
+
+                   for (i = 0; i < len; i++)
+                     XFASTINT (keybuf[keytran_start + i])
+                       = XSTRING (keytran_next)->data[i];
+                 }
+
+               mock_input = t;
+               keytran_start = keytran_end = t;
+               keytran_map = Vkey_translation_map;
+
+               goto replay_sequence;
+             }
+
+           keytran_map = get_keymap_1 (keytran_next, 0, 1);
+
+           /* If we no longer have a bound suffix, try a new positions for 
+              keytran_start.  */
+           if (NILP (keytran_map))
+             {
+               keytran_end = ++keytran_start;
+               keytran_map = Vkey_translation_map;
+             }
+         }
+      }
     }
 
   read_key_sequence_cmd = (first_binding < nmaps
@@ -3754,9 +4595,27 @@ read_key_sequence (keybuf, bufsize, prompt)
  done:
   unread_switch_frame = delayed_switch_frame;
   unbind_to (count, Qnil);
+
+  /* Occasionally we fabricate events, perhaps by expanding something
+     according to function-key-map, or by adding a prefix symbol to a
+     mouse click in the scroll bar or modeline.  In this cases, return
+     the entire generated key sequence, even if we hit an unbound
+     prefix or a definition before the end.  This means that you will
+     be able to push back the event properly, and also means that
+     read-key-sequence will always return a logical unit.
+
+     Better ideas?  */
+  for (; t < mock_input; t++)
+    {
+      echo_char (keybuf[t]);
+      add_command_key (keybuf[t]);
+    }
+
   return t;
 }
 
+#if 0 /* This doc string is too long for some compilers.
+        This commented-out definition serves for DOC.  */
 DEFUN ("read-key-sequence", Fread_key_sequence, Sread_key_sequence, 1, 2, 0,
   "Read a sequence of keystrokes and return as a string or vector.\n\
 The sequence is sufficient to specify a non-prefix command in the\n\
@@ -3772,13 +4631,13 @@ and `quit-flag' is not set.\n\
 If the key sequence starts with a mouse click, then the sequence is read\n\
 using the keymaps of the buffer of the window clicked in, not the buffer\n\
 of the selected window as normal.\n\
-\n\
+""\n\
 `read-key-sequence' drops unbound button-down events, since you normally\n\
 only care about the click or drag events which follow them.  If a drag\n\
-event is unbound, but the corresponding click event would be bound,\n\
-`read-key-sequence' turns the drag event into a click event at the\n\
+or multi-click event is unbound, but the corresponding click event would\n\
+be bound, `read-key-sequence' turns the event into a click event at the\n\
 drag's starting position.  This means that you don't have to distinguish\n\
-between click and drag events unless you want to.\n\
+between click and drag, double, or triple events unless you want to.\n\
 \n\
 `read-key-sequence' prefixes mouse events on mode lines, the vertical\n\
 lines separating windows, and scroll bars with imaginary keys\n\
@@ -3790,6 +4649,11 @@ frame-switch event is put off until after the current key sequence.\n\
 `read-key-sequence' checks `function-key-map' for function key\n\
 sequences, where they wouldn't conflict with ordinary bindings.  See\n\
 `function-key-map' for more details.")
+  (prompt, continue_echo)
+#endif
+
+DEFUN ("read-key-sequence", Fread_key_sequence, Sread_key_sequence, 1, 2, 0,
+  0)
   (prompt, continue_echo)
      Lisp_Object prompt, continue_echo;
 {
@@ -3808,9 +4672,13 @@ sequences, where they wouldn't conflict with ordinary bindings.  See\n\
   if (NILP (continue_echo))
     this_command_key_count = 0;
 
-  i = read_key_sequence (keybuf, (sizeof keybuf/sizeof (keybuf[0])),
-                        NILP (prompt) ? 0 : XSTRING (prompt)->data);
+  i = read_key_sequence (keybuf, (sizeof keybuf/sizeof (keybuf[0])), prompt);
 
+  if (i == -1)
+    {
+      Vquit_flag = Qt;
+      QUIT;
+    }
   UNGCPRO;
   return make_event_array (i, keybuf);
 }
@@ -3838,7 +4706,7 @@ Otherwise, that is done only if an arg is read using the minibuffer.")
     {
       tem = Fget (cmd, Qdisabled);
       if (!NILP (tem))
-       return call1 (Vrun_hooks, Vdisabled_command_hook);
+       return call1 (Vrun_hooks, Qdisabled_command_hook);
     }
 
   while (1)
@@ -3914,11 +4782,11 @@ DEFUN ("execute-extended-command", Fexecute_extended_command, Sexecute_extended_
 
   /* Prompt with buf, and then read a string, completing from and
      restricting to the set of all defined commands.  Don't provide
-     any initial input.  The last Qnil says not to perform a 
-     peculiar hack on the initial input.  */
+     any initial input.  Save the command read on the extended-command
+     history list. */
   function = Fcompleting_read (build_string (buf),
                               Vobarray, Qcommandp,
-                              Qt, Qnil, Qnil);
+                              Qt, Qnil, Qextended_command_history);
 
   /* Set this_command_keys to the concatenation of saved_keys and
      function, followed by a RET.  */
@@ -4007,7 +4875,8 @@ DEFUN ("recent-keys", Frecent_keys, Srecent_keys, 0, 0, 0,
 }
 
 DEFUN ("this-command-keys", Fthis_command_keys, Sthis_command_keys, 0, 0, 0,
-  "Return string of the keystrokes that invoked this command.")
+  "Return the key sequence that invoked this command.\n\
+The value is a string or a vector.")
   ()
 {
   return make_event_array (this_command_key_count,
@@ -4072,7 +4941,7 @@ On systems that don't have job control, run a subshell instead.\n\n\
 If optional arg STUFFSTRING is non-nil, its characters are stuffed\n\
 to be read as terminal input by Emacs's parent, after suspension.\n\
 \n\
-Before suspending, call the functions in `suspend-hooks' with no args.\n\
+Before suspending, call the functions in `suspend-hook' with no args.\n\
 If any of them returns nil, don't call the rest and don't suspend.\n\
 Otherwise, suspend normally and after resumption run the normal hook\n\
 `suspend-resume-hook' if that is bound and non-nil.\n\
@@ -4092,17 +4961,9 @@ On such systems, Emacs starts a subshell instead of suspending.")
   if (!NILP (stuffstring))
     CHECK_STRING (stuffstring, 0);
 
-  /* Run the functions in suspend-hooks.  */
-  tem = Fsymbol_value (intern ("suspend-hooks"));
-  while (CONSP (tem))
-    {
-      Lisp_Object val;
-      GCPRO2 (stuffstring, tem);
-      val = call0 (Fcar (tem));
-      UNGCPRO;
-      tem = Fcdr (tem);
-      if (!EQ (val, Qnil)) return Qnil;
-    }
+  /* Run the functions in suspend-hook.  */
+  if (!NILP (Vrun_hooks))
+    call1 (Vrun_hooks, intern ("suspend-hook"));
 
   GCPRO1 (stuffstring);
   get_frame_size (&old_width, &old_height);
@@ -4119,10 +4980,9 @@ On such systems, Emacs starts a subshell instead of suspending.")
      with a window system; but suspend should be disabled in that case.  */
   get_frame_size (&width, &height);
   if (width != old_width || height != old_height)
-    change_frame_size (0, height, width, 0, 0);
+    change_frame_size (selected_frame, height, width, 0, 0);
 
-  /* Call value of suspend-resume-hook
-     if it is bound and value is non-nil.  */
+  /* Run suspend-resume-hook.  */
   if (!NILP (Vrun_hooks))
     call1 (Vrun_hooks, intern ("suspend-resume-hook"));
   
@@ -4131,7 +4991,7 @@ On such systems, Emacs starts a subshell instead of suspending.")
 }
 
 /* If STUFFSTRING is a string, stuff its contents as pending terminal input.
-   Then in any case stuff anthing Emacs has read ahead and not used.  */
+   Then in any case stuff anything Emacs has read ahead and not used.  */
 
 stuff_buffered_input (stuffstring)
      Lisp_Object stuffstring;
@@ -4157,7 +5017,7 @@ stuff_buffered_input (stuffstring)
       if (kbd_fetch_ptr == kbd_buffer + KBD_BUFFER_SIZE)
        kbd_fetch_ptr = kbd_buffer;
       if (kbd_fetch_ptr->kind == ascii_keystroke)
-       stuff_char (XINT (kbd_fetch_ptr->code));
+       stuff_char (kbd_fetch_ptr->code);
       kbd_fetch_ptr->kind = no_event;
       (XVECTOR (kbd_buffer_frame_or_window)->contents[kbd_fetch_ptr
                                                     - kbd_buffer]
@@ -4181,13 +5041,6 @@ set_waiting_for_input (time_to_clear)
      make it run again now, to avoid timing error. */
   if (!NILP (Vquit_flag))
     quit_throw_to_read_char ();
-
-  /* If alarm has gone off already, echo now.  */
-  if (echo_flag)
-    {
-      echo ();
-      echo_flag = 0;
-    }
 }
 
 clear_waiting_for_input ()
@@ -4215,7 +5068,6 @@ interrupt_signal ()
   char c;
   /* Must preserve main program's value of errno.  */
   int old_errno = errno;
-  extern Lisp_Object Vwindow_system;
 
 #ifdef USG
   /* USG systems forget handlers when they are used;
@@ -4258,7 +5110,10 @@ interrupt_signal ()
       printf ("Auto-save? (y or n) ");
       fflush (stdout);
       if (((c = getchar ()) & ~040) == 'Y')
-       Fdo_auto_save (Qnil, Qnil);
+       {
+         Fdo_auto_save (Qt, Qnil);
+         printf ("Auto-save done\n");
+       }
       while (c != '\n') c = getchar ();
 #ifdef VMS
       printf ("Abort (and enter debugger)? (y or n) ");
@@ -4308,6 +5163,17 @@ quit_throw_to_read_char ()
   unread_command_events = Qnil;
   unread_command_char = -1;
 
+#ifdef POLL_FOR_INPUT
+  /* May be > 1 if in recursive minibuffer.  */
+  if (poll_suppress_count == 0)
+    abort ();
+#endif
+#ifdef MULTI_FRAME
+  if (XTYPE (internal_last_event_frame) == Lisp_Frame
+      && XFRAME (internal_last_event_frame) != selected_frame)
+    Fhandle_switch_frame (make_lispy_switch_frame (internal_last_event_frame));
+#endif
+
   _longjmp (getcjmp, 1);
 }
 \f
@@ -4317,16 +5183,22 @@ First arg INTERRUPT non-nil means use input interrupts;\n\
  nil means use CBREAK mode.\n\
 Second arg FLOW non-nil means use ^S/^Q flow control for output to terminal\n\
  (no effect except in CBREAK mode).\n\
-Third arg META non-nil means accept 8-bit input (for a Meta key).\n\
- Otherwise, the top bit is ignored, on the assumption it is parity.\n\
-Optional fourth arg QUIT if non-nil specifies character to use for quitting.")
+Third arg META t means accept 8-bit input (for a Meta key).\n\
+ META nil means ignore the top bit, on the assumption it is parity.\n\
+ Otherwise, accept 8-bit input and don't use the top bit for Meta.\n\
+Optional fourth arg QUIT if non-nil specifies character to use for quitting.\n\
+See also `current-input-mode'.")
   (interrupt, flow, meta, quit)
      Lisp_Object interrupt, flow, meta, quit;
 {
   if (!NILP (quit)
       && (XTYPE (quit) != Lisp_Int
          || XINT (quit) < 0 || XINT (quit) > 0400))
-    error ("set-input-mode: QUIT must be an ASCII character.");
+    error ("set-input-mode: QUIT must be an ASCII character");
+
+#ifdef POLL_FOR_INPUT
+  stop_polling ();
+#endif
 
   reset_sys_modes ();
 #ifdef SIGIO
@@ -4345,14 +5217,51 @@ Optional fourth arg QUIT if non-nil specifies character to use for quitting.")
   interrupt_input = 1;
 #endif
   flow_control = !NILP (flow);
-  meta_key = !NILP (meta);
+  if (NILP (meta))
+    meta_key = 0;
+  else if (EQ (meta, Qt))
+    meta_key = 1;
+  else
+    meta_key = 2;
   if (!NILP (quit))
     /* Don't let this value be out of range.  */
     quit_char = XINT (quit) & (meta_key ? 0377 : 0177);
 
   init_sys_modes ();
+
+#ifdef POLL_FOR_INPUT
+  poll_suppress_count = 1;
+  start_polling ();
+#endif
   return Qnil;
 }
+
+DEFUN ("current-input-mode", Fcurrent_input_mode, Scurrent_input_mode, 0, 0, 0,
+  "Return information about the way Emacs currently reads keyboard input.\n\
+The value is a list of the form (INTERRUPT FLOW META QUIT), where\n\
+  INTERRUPT is non-nil if Emacs is using interrupt-driven input; if\n\
+    nil, Emacs is using CBREAK mode.\n\
+  FLOW is non-nil if Emacs uses ^S/^Q flow control for output to the\n\
+    terminal; this does not apply if Emacs uses interrupt-driven input.\n\
+  META is t if accepting 8-bit input with 8th bit as Meta flag.\n\
+    META nil means ignoring the top bit, on the assumption it is parity.\n\
+    META is neither t nor nil if accepting 8-bit input and using\n\
+    all 8 bits as the character code.\n\
+  QUIT is the character Emacs currently uses to quit.\n\
+The elements of this list correspond to the arguments of\n\
+`set-input-mode'.")
+  ()
+{
+  Lisp_Object val[4];
+
+  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;
+  XFASTINT (val[3]) = quit_char;
+
+  return Flist (sizeof (val) / sizeof (val[0]), val);
+}
+
 \f
 init_keyboard ()
 {
@@ -4441,6 +5350,9 @@ struct event_head head_table[] = {
 
 syms_of_keyboard ()
 {
+  Qdisabled_command_hook = intern ("disabled-command-hook");
+  staticpro (&Qdisabled_command_hook);
+
   Qself_insert_command = intern ("self-insert-command");
   staticpro (&Qself_insert_command);
 
@@ -4453,6 +5365,9 @@ syms_of_keyboard ()
   Qdisabled = intern ("disabled");
   staticpro (&Qdisabled);
 
+  Qundefined = intern ("undefined");
+  staticpro (&Qundefined);
+
   Qpre_command_hook = intern ("pre-command-hook");
   staticpro (&Qpre_command_hook);
 
@@ -4464,6 +5379,9 @@ syms_of_keyboard ()
   Qmouse_click = intern ("mouse-click");
   staticpro (&Qmouse_click);
 
+  Qmenu_enable = intern ("menu-enable");
+  staticpro (&Qmenu_enable);
+
   Qmode_line = intern ("mode-line");
   staticpro (&Qmode_line);
   Qvertical_line = intern ("vertical-line");
@@ -4489,6 +5407,11 @@ syms_of_keyboard ()
   Qmodifier_cache = intern ("modifier-cache");
   staticpro (&Qmodifier_cache);
 
+  Qrecompute_lucid_menubar = intern ("recompute-lucid-menubar");
+  staticpro (&Qrecompute_lucid_menubar);
+  Qactivate_menubar_hook = intern ("activate-menubar-hook");
+  staticpro (&Qactivate_menubar_hook);
+
   {
     struct event_head *p;
 
@@ -4523,6 +5446,10 @@ syms_of_keyboard ()
   this_command_keys = Fmake_vector (make_number (40), Qnil);
   staticpro (&this_command_keys);
 
+  Qextended_command_history = intern ("extended-command-history");
+  Fset (Qextended_command_history, Qnil);
+  staticpro (&Qextended_command_history);
+
   kbd_buffer_frame_or_window
     = Fmake_vector (make_number (KBD_BUFFER_SIZE), Qnil);
   staticpro (&kbd_buffer_frame_or_window);
@@ -4551,12 +5478,9 @@ syms_of_keyboard ()
   defsubr (&Sdiscard_input);
   defsubr (&Sopen_dribble_file);
   defsubr (&Sset_input_mode);
+  defsubr (&Scurrent_input_mode);
   defsubr (&Sexecute_extended_command);
 
-  DEFVAR_LISP ("disabled-command-hook", &Vdisabled_command_hook,
-    "Value is called instead of any command that is disabled\n\
-\(has a non-nil `disabled' property).");
-
   DEFVAR_LISP ("last-command-char", &last_command_char,
     "Last input event that was part of a command.");
 
@@ -4621,6 +5545,13 @@ Polling is needed only when using X windows and SIGIO does not work.\n\
 Polling is automatically disabled in all other cases.");
   polling_period = 2;
   
+  DEFVAR_LISP ("double-click-time", &Vdouble_click_time,
+    "*Maximum time between mouse clicks to make a double-click.\n\
+Measured in milliseconds.  nil means disable double-click recognition;\n\
+t means double-clicks have no time limit and are detected\n\
+by position only.");
+  Vdouble_click_time = make_number (500);
+
   DEFVAR_INT ("num-input-keys", &num_input_keys,
     "*Number of complete keys read from the keyboard so far.");
   num_input_keys = 0;
@@ -4632,18 +5563,24 @@ If the last event came from a keyboard macro, this is set to `macro'.");
   Vlast_event_frame = Qnil;
 #endif
 
-  DEFVAR_LISP ("help-char", &help_char,
+  DEFVAR_LISP ("help-char", &Vhelp_char,
     "Character to recognize as meaning Help.\n\
 When it is read, do `(eval help-form)', and display result if it's a string.\n\
 If the value of `help-form' is nil, this char can be read normally.");
-  XSET (help_char, Lisp_Int, Ctl ('H'));
+  XSET (Vhelp_char, Lisp_Int, Ctl ('H'));
 
   DEFVAR_LISP ("help-form", &Vhelp_form,
-    "Form to execute when character help-char is read.\n\
+    "Form to execute when character `help-char' is read.\n\
 If the form returns a string, that string is displayed.\n\
 If `help-form' is nil, the help char is not recognized.");
   Vhelp_form = Qnil;
 
+  DEFVAR_LISP ("prefix-help-command", &Vprefix_help_command,
+    "Command to run when `help-char' character follows a prefix key.\n\
+This command is used only when there is no actual binding\n\
+for that character after that prefix key.");
+  Vprefix_help_command = Qnil;
+
   DEFVAR_LISP ("top-level", &Vtop_level,
     "Form to evaluate when Emacs starts up.\n\
 Useful to set before you dump a modified Emacs.");
@@ -4655,6 +5592,12 @@ Each character is looked up in this string and the contents used instead.\n\
 If string is of length N, character codes N and up are untranslated.");
   Vkeyboard_translate_table = Qnil;
 
+  DEFVAR_LISP ("key-translation-map", &Vkey_translation_map,
+    "Keymap of key translations that can override keymaps.\n\
+This keymap works like `function-key-map', but comes after that,\n\
+and applies even for keys that have ordinary bindings.");
+  Vkey_translation_map = Qnil;
+
   DEFVAR_BOOL ("menu-prompting", &menu_prompting,
     "Non-nil means prompt with menus when appropriate.\n\
 This is done when reading from a keymap that has a prompt string,\n\
@@ -4672,10 +5615,15 @@ Type this character while in a menu prompt to rotate around the lines of it.");
 
   DEFVAR_INT ("extra-keyboard-modifiers", &extra_keyboard_modifiers,
     "A mask of additional modifier keys to use with every keyboard character.\n\
-These bits follow the convention for X windows,\n\
-but the control and meta bits work even when you are not using X:\n\
-  1 -- shift bit      2 -- lock bit\n\
-  4 -- control bit    8 -- meta bit.");
+Emacs applies the modifiers of the character stored here to each keyboard\n\
+character it reads.  For example, after evaluating the expression\n\
+    (setq extra-keyboard-modifiers ?\C-x)\n\
+all input characters will have the control modifier applied to them.\n\
+\n\
+Note that the character ?\C-@, equivalent to the integer zero, does\n\
+not count as a control character; rather, it counts as a character\n\
+with no modifiers; thus, setting `extra-keyboard-modifiers' to zero\n\
+cancels any modification.");
   extra_keyboard_modifiers = 0;
 
   DEFVAR_LISP ("deactivate-mark", &Vdeactivate_mark,
@@ -4690,8 +5638,20 @@ Buffer modification stores t in this variable.");
   Vpre_command_hook = Qnil;
 
   DEFVAR_LISP ("post-command-hook", &Vpost_command_hook,
-    "Normal hook run before each command is executed.");
+    "Normal hook run after each command is executed.");
   Vpost_command_hook = Qnil;
+
+  DEFVAR_LISP ("lucid-menu-bar-dirty-flag", &Vlucid_menu_bar_dirty_flag,
+    "t means menu bar, specified Lucid style, needs to be recomputed.");
+  Vlucid_menu_bar_dirty_flag = Qnil;
+
+  DEFVAR_LISP ("menu-bar-final-items", &Vmenu_bar_final_items,
+    "List of menu bar items to move to the end of the menu bar.\n\
+The elements of the list are event types that may have menu bar bindings.");
+  Vmenu_bar_final_items = Qnil;
+
+  DEFVAR_BOOL ("track-mouse", &do_mouse_tracking,
+              "*Non-nil means generate motion events for mouse motion.");
 }
 
 keys_of_keyboard ()