*** empty log message ***
[bpt/emacs.git] / src / keyboard.c
index 05efd69..6c25c3d 100644 (file)
@@ -1,5 +1,6 @@
 /* Keyboard and mouse input; editor command loop.
-   Copyright (C) 1985,86,87,88,89,93,94,95,96,97,99 Free Software Foundation, Inc.
+   Copyright (C) 1985,86,87,88,89,93,94,95,96,97,99, 2000
+     Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -26,6 +27,7 @@ Boston, MA 02111-1307, USA.  */
 #include "lisp.h"
 #include "termhooks.h"
 #include "macros.h"
+#include "keyboard.h"
 #include "frame.h"
 #include "window.h"
 #include "commands.h"
@@ -33,11 +35,12 @@ Boston, MA 02111-1307, USA.  */
 #include "charset.h"
 #include "disptab.h"
 #include "dispextern.h"
-#include "keyboard.h"
 #include "syntax.h"
 #include "intervals.h"
 #include "blockinput.h"
 #include "puresize.h"
+#include "systime.h"
+#include "atimer.h"
 #include <setjmp.h>
 #include <errno.h>
 
@@ -70,7 +73,9 @@ Boston, MA 02111-1307, USA.  */
 /* Include systime.h after xterm.h to avoid double inclusion of time.h. */
 #include "systime.h"
 
+#ifndef USE_CRT_DLL
 extern int errno;
+#endif
 
 /* Variables for blockinput.h: */
 
@@ -200,13 +205,18 @@ static int echoing;
 
 static struct kboard *ok_to_echo_at_next_pause;
 
-/* The kboard currently echoing, or null for none.  Set in echo_now to
-   the kboard echoing.  Reset to 0 in cancel_echoing.  If non-null,
-   and a current echo area message exists, we know that it comes from
-   echoing.  */
+/* The kboard last echoing, or null for none.  Reset to 0 in
+   cancel_echoing.  If non-null, and a current echo area message
+   exists, and echo_message_buffer is eq to the current message
+   buffer, we know that the message comes from echo_kboard.  */
 
 static struct kboard *echo_kboard;
 
+/* The buffer used for echoing.  Set in echo_now, reset in
+   cancel_echoing.  */
+
+static Lisp_Object echo_message_buffer;
+
 /* Nonzero means disregard local maps for the menu bar.  */
 static int inhibit_local_menu_bar_menus;
 
@@ -376,6 +386,9 @@ Lisp_Object Qtimer_event_handler;
    key sequence that it reads.  */
 Lisp_Object read_key_sequence_cmd;
 
+/* Echo unfinished commands after this many seconds of pause.  */
+Lisp_Object Vecho_keystrokes;
+
 /* Form to evaluate (if non-nil) when Emacs is started.  */
 Lisp_Object Vtop_level;
 
@@ -442,9 +455,10 @@ int meta_key;
 extern char *pending_malloc_warning;
 
 /* Circular buffer for pre-read keyboard input.  */
+
 static struct input_event kbd_buffer[KBD_BUFFER_SIZE];
 
-/* Vector to GCPRO the frames and windows mentioned in kbd_buffer.
+/* Vector to GCPRO the Lisp objects referenced from kbd_buffer.
 
    The interrupt-level event handlers will never enqueue an event on a
    frame which is not in Vframe_list, and once an event is dequeued,
@@ -463,14 +477,16 @@ static struct input_event kbd_buffer[KBD_BUFFER_SIZE];
    Similar things happen when an event on a scroll bar is enqueued; the
    window may be deleted while the event is in the queue.
 
-   So, we use this vector to protect the frame_or_window field in the
-   event queue.  That way, they'll be dequeued as dead frames or
-   windows, but still valid lisp objects.
+   So, we use this vector to protect the Lisp_Objects in the event
+   queue.  That way, they'll be dequeued as dead frames or windows,
+   but still valid Lisp objects.
 
    If kbd_buffer[i].kind != no_event, then
-     (XVECTOR (kbd_buffer_frame_or_window)->contents[i]
-      == kbd_buffer[i].frame_or_window.  */
-static Lisp_Object kbd_buffer_frame_or_window;
+
+   AREF (kbd_buffer_gcpro, 2 * i) == kbd_buffer[i].frame_or_window.
+   AREF (kbd_buffer_gcpro, 2 * i + 1) == kbd_buffer[i].arg.  */
+
+static Lisp_Object kbd_buffer_gcpro;
 
 /* Pointer to next available character in kbd_buffer.
    If kbd_fetch_ptr == kbd_store_ptr, the buffer is empty.
@@ -603,30 +619,51 @@ int flow_control;
 #ifdef HAVE_WINDOW_SYSTEM
 #define POLL_FOR_INPUT
 #endif
+
+/* After a command is executed, if point is moved into a region that
+   has specific properties (e.g. composition, display), we adjust
+   point to the boundary of the region.  But, if a command sets this
+   valiable to non-nil, we suppress this point adjustment.  This
+   variable is set to nil before reading a command.  */
+
+Lisp_Object Vdisable_point_adjustment;
+
+/* If non-nil, always disable point adjustment.  */
+
+Lisp_Object Vglobal_disable_point_adjustment;
+
 \f
 /* Global variable declarations.  */
 
 /* Function for init_keyboard to call with no args (if nonzero).  */
 void (*keyboard_init_hook) ();
 
-static int read_avail_input ();
-static void get_input_pending ();
-static int readable_events ();
+static int read_avail_input P_ ((int));
+static void get_input_pending P_ ((int *, int));
+static int readable_events P_ ((int));
+static Lisp_Object read_char_x_menu_prompt P_ ((int, Lisp_Object *,
+                                               Lisp_Object, int *));
 static Lisp_Object read_char_x_menu_prompt ();
-static Lisp_Object read_char_minibuf_menu_prompt ();
-static Lisp_Object make_lispy_event ();
+static Lisp_Object read_char_minibuf_menu_prompt P_ ((int, int,
+                                                     Lisp_Object *));
+static Lisp_Object make_lispy_event P_ ((struct input_event *));
 #ifdef HAVE_MOUSE
-static Lisp_Object make_lispy_movement ();
+static Lisp_Object make_lispy_movement P_ ((struct frame *, Lisp_Object,
+                                           enum scroll_bar_part,
+                                           Lisp_Object, Lisp_Object,
+                                           unsigned long));
 #endif
-static Lisp_Object modify_event_symbol ();
-static Lisp_Object make_lispy_switch_frame ();
+static Lisp_Object modify_event_symbol P_ ((int, unsigned, Lisp_Object,
+                                           Lisp_Object, char **,
+                                           Lisp_Object *, unsigned));
+static Lisp_Object make_lispy_switch_frame P_ ((Lisp_Object));
+static int parse_solitary_modifier P_ ((Lisp_Object));
 static int parse_solitary_modifier ();
+static void save_getcjmp P_ ((jmp_buf));
 static void save_getcjmp ();
-static void restore_getcjmp ();
+static void restore_getcjmp P_ ((jmp_buf));
 static Lisp_Object apply_modifiers P_ ((int, Lisp_Object));
-
-/* > 0 if we are to echo keystrokes.  */
-static int echo_keystrokes;
+static void clear_event P_ ((struct input_event *));
 
 /* Nonzero means don't try to suspend even if the operating system seems
    to support it.  */
@@ -756,11 +793,14 @@ echo_now ()
     }
 
   echoing = 1;
-  echo_kboard = current_kboard;
   message2_nolog (current_kboard->echobuf, strlen (current_kboard->echobuf),
                  ! NILP (current_buffer->enable_multibyte_characters));
   echoing = 0;
 
+  /* Record in what buffer we echoed, and from which kboard.  */
+  echo_message_buffer = echo_area_buffer[0];
+  echo_kboard = current_kboard;
+
   if (waiting_for_input && !NILP (Vquit_flag))
     quit_throw_to_read_char ();
 }
@@ -773,8 +813,9 @@ cancel_echoing ()
   current_kboard->immediate_echo = 0;
   current_kboard->echoptr = current_kboard->echobuf;
   current_kboard->echo_after_prompt = -1;
-  ok_to_echo_at_next_pause = 0;
-  echo_kboard = 0;
+  ok_to_echo_at_next_pause = NULL;
+  echo_kboard = NULL;
+  echo_message_buffer = Qnil;
 }
 
 /* Return the length of the current echo string.  */
@@ -844,6 +885,14 @@ recursive_edit_1 ()
       specbind (Qstandard_input, Qt);
     }
 
+#ifdef HAVE_X_WINDOWS
+  /* The command loop has started a busy-cursor timer, so we have to
+     cancel it here, otherwise it will fire because the recursive edit
+     can take some time.  */
+  if (display_busy_cursor_p)
+    cancel_busy_cursor ();
+#endif
+
   val = command_loop ();
   if (EQ (val, Qt))
     Fsignal (Qquit, Qnil);
@@ -1153,6 +1202,10 @@ DEFUN ("top-level", Ftop_level, Stop_level, 0, 0, "",
   "Exit all recursive editing levels.")
   ()
 {
+#ifdef HAVE_X_WINDOWS
+  if (display_busy_cursor_p)
+    cancel_busy_cursor ();
+#endif
   Fthrow (Qtop_level, Qnil);
 }
 
@@ -1182,6 +1235,7 @@ DEFUN ("abort-recursive-edit", Fabort_recursive_edit, Sabort_recursive_edit, 0,
 Lisp_Object Fcommand_execute ();
 static int read_key_sequence ();
 void safe_run_hooks ();
+static void adjust_point_for_property ();
 
 Lisp_Object
 command_loop_1 ()
@@ -1369,6 +1423,12 @@ command_loop_1 ()
       last_point_position = PT;
       XSETBUFFER (last_point_position_buffer, prev_buffer);
 
+      /* By default, we adjust point to a boundary of a region that
+         has such a property that should be treated intangible
+         (e.g. composition, display).  But, some commands will set
+         this variable differently.  */
+      Vdisable_point_adjustment = Qnil;
+
       /* Execute the command.  */
 
       Vthis_command = cmd;
@@ -1498,18 +1558,18 @@ command_loop_1 ()
 
 #ifdef HAVE_X_WINDOWS
          if (display_busy_cursor_p)
-           {
-             if (inhibit_busy_cursor != 2)
-               inhibit_busy_cursor = 0;
-             if (!inhibit_busy_cursor)
-               Fx_show_busy_cursor ();
-           }
+           start_busy_cursor ();
 #endif
 
          nonundocount = 0;
          if (NILP (current_kboard->Vprefix_arg))
            Fundo_boundary ();
          Fcommand_execute (Vthis_command, Qnil, Qnil, Qnil);
+
+#ifdef HAVE_X_WINDOWS
+         if (display_busy_cursor_p)
+           cancel_busy_cursor ();
+#endif
        }
     directly_done: ;
       current_kboard->Vlast_prefix_arg = Vcurrent_prefix_arg;
@@ -1571,6 +1631,13 @@ command_loop_1 ()
        }
 
     finalize:
+
+      if (current_buffer == prev_buffer
+         && last_point_position != PT
+         && NILP (Vdisable_point_adjustment)
+         && NILP (Vglobal_disable_point_adjustment))
+       adjust_point_for_property (last_point_position);
+
       /* Install chars successfully executed in kbd macro.  */
 
       if (!NILP (current_kboard->defining_kbd_macro)
@@ -1584,6 +1651,54 @@ command_loop_1 ()
     }
 }
 
+extern Lisp_Object Qcomposition, Qdisplay;
+
+/* Adjust point to a boundary of a region that has such a property
+   that should be treated intangible.  For the moment, we check
+   `composition' and `display' property.  LAST_PT is the last position
+   of point.  */
+
+static void
+adjust_point_for_property (last_pt)
+     int last_pt;
+{
+  int start, end;
+  Lisp_Object val;
+  int check_composition = 1, check_display = 1;
+
+  while (check_composition || check_display)
+    {
+      if (check_composition
+         && PT > BEGV && PT < ZV
+         && get_property_and_range (PT, Qcomposition, &val, &start, &end, Qnil)
+         && COMPOSITION_VALID_P (start, end, val)
+         && start < PT && end > PT
+         && (last_pt <= start || last_pt >= end))
+       {
+         if (PT < last_pt)
+           SET_PT (start);
+         else
+           SET_PT (end);
+         check_display = 1;
+       }
+      check_composition = 0;
+      if (check_display
+         && PT > BEGV && PT < ZV
+         && get_property_and_range (PT, Qdisplay, &val, &start, &end, Qnil)
+         && display_prop_intangible_p (val)
+         && start < PT && end > PT
+         && (last_pt <= start || last_pt >= end))
+       {
+         if (PT < last_pt)
+           SET_PT (start);
+         else
+           SET_PT (end);
+         check_composition = 1;
+       }
+      check_display = 0;
+    }
+}
+
 /* Subroutine for safe_run_hooks: run the hook HOOK.  */
 
 static Lisp_Object
@@ -1599,7 +1714,7 @@ static Lisp_Object
 safe_run_hooks_error (data)
      Lisp_Object data;
 {
-  Fset (Vinhibit_quit, Qnil);
+  return Fset (Vinhibit_quit, Qnil);
 }
 
 /* If we get an error while running the hook, cause the hook variable
@@ -1617,37 +1732,48 @@ safe_run_hooks (hook)
 
   unbind_to (count, Qnil);
 }
+
 \f
-/* Number of seconds between polling for input.  */
+/* Number of seconds between polling for input.  This is a Lisp
+   variable that can be bound.  */
+
 int polling_period;
 
 /* Nonzero means polling for input is temporarily suppressed.  */
+
 int poll_suppress_count;
 
-/* Nonzero if polling_for_input is actually being used.  */
-int polling_for_input;
+/* Asynchronous timer for polling.  */
+
+struct atimer *poll_timer;
+
 
 #ifdef POLL_FOR_INPUT
 
-/* Handle an alarm once each second and read pending input
-   so as to handle a C-g if it comces in.  */
+/* Poll for input, so what we catch a C-g if it comes in.  This
+   function is called from x_make_frame_visible, see comment
+   there.  */
 
-SIGTYPE
-input_poll_signal (signalnum)  /* If we don't have an argument, */
-     int signalnum;            /* some compilers complain in signal calls. */
+void
+poll_for_input_1 ()
 {
-  /* This causes the call to start_polling at the end
-     to do its job.  It also arranges for a quit or error
-     from within read_avail_input to resume polling.  */
-  poll_suppress_count++;
   if (interrupt_input_blocked == 0
       && !waiting_for_input)
     read_avail_input (0);
-  /* Turn on the SIGALRM handler and request another alarm.  */
-  start_polling ();
 }
 
-#endif
+/* Timer callback function for poll_timer.  TIMER is equal to
+   poll_timer.  */
+
+void
+poll_for_input (timer)
+     struct atimer *timer;
+{
+  if (poll_suppress_count == 0)
+    poll_for_input_1 ();
+}
+
+#endif /* POLL_FOR_INPUT */
 
 /* Begin signals to poll for input, if they are appropriate.
    This function is called unconditionally from various places.  */
@@ -1658,13 +1784,28 @@ start_polling ()
 #ifdef POLL_FOR_INPUT
   if (read_socket_hook && !interrupt_input)
     {
-      poll_suppress_count--;
-      if (poll_suppress_count == 0)
+      /* Turn alarm handling on unconditionally.  It might have
+        been turned off in process.c.  */
+      turn_on_atimers (1);
+      
+      /* If poll timer doesn't exist, are we need one with
+        a different interval, start a new one.  */
+      if (poll_timer == NULL
+         || EMACS_SECS (poll_timer->interval) != polling_period)
        {
-         signal (SIGALRM, input_poll_signal);
-         polling_for_input = 1;
-         alarm (polling_period);
+         EMACS_TIME interval;
+
+         if (poll_timer)
+           cancel_atimer (poll_timer);
+      
+         EMACS_SET_SECS_USECS (interval, polling_period, 0);
+         poll_timer = start_atimer (ATIMER_CONTINUOUS, interval,
+                                    poll_for_input, NULL);
        }
+
+      /* Let the timer's callback function poll for input
+        if this becomes zero.  */
+      --poll_suppress_count;
     }
 #endif
 }
@@ -1688,14 +1829,7 @@ stop_polling ()
 {
 #ifdef POLL_FOR_INPUT
   if (read_socket_hook && !interrupt_input)
-    {
-      if (poll_suppress_count == 0)
-       {
-         polling_for_input = 0;
-         alarm (0);
-       }
-      poll_suppress_count++;
-    }
+    ++poll_suppress_count;
 #endif
 }
 
@@ -1733,6 +1867,7 @@ bind_polling_period (n)
   if (n > new)
     new = n;
 
+  stop_other_atimers (poll_timer);
   stop_polling ();
   specbind (Qpolling_period, make_number (new));
   /* Start a new alarm with the new period.  */
@@ -1778,6 +1913,82 @@ make_ctrl_char (c)
   return c;
 }
 
+/* Display help echo in the echo area.
+
+   HELP a string means display that string, HELP nil means clear the
+   help echo.  If HELP is a function, call it with OBJECT and POS as
+   arguments; the function should return a help string or nil for
+   none.  For all other types of HELP evaluate it to obtain a string.
+
+   WINDOW is the window in which the help was generated, if any.
+   It is nil if not in a window.
+
+   If OBJECT is a buffer, POS is the position in the buffer where the
+   `help-echo' text property was found.
+
+   If OBJECT is an overlay, that overlay has a `help-echo' property,
+   and POS is the position in the overlay's buffer under the mouse.
+
+   If OBJECT is a string (an overlay string or a string displayed with
+   the `display' property).  POS is the position in that string under
+   the mouse.
+
+   OK_TO_IVERWRITE_KEYSTROKE_ECHO non-zero means it's okay if the help
+   echo overwrites a keystroke echo currently displayed in the echo
+   area.
+
+   Note: this function may only be called with HELP nil or a string
+   from X code running asynchronously.  */
+
+void
+show_help_echo (help, window, object, pos, ok_to_overwrite_keystroke_echo)
+     Lisp_Object help, window, object, pos;
+     int ok_to_overwrite_keystroke_echo;
+{
+  if (!NILP (help) && !STRINGP (help))
+    {
+      if (FUNCTIONP (help))
+       {
+         Lisp_Object args[4];
+         args[0] = help;
+         args[1] = window;
+         args[2] = object;
+         args[3] = pos;
+         help = call_function (4, args);
+       }
+      else
+       help = eval_form (help);
+      
+      if (!STRINGP (help))
+       return;
+    }
+
+  if (STRINGP (help) || NILP (help))
+    {
+      if (!NILP (Vshow_help_function))
+       call1 (Vshow_help_function, help);
+      else if (/* Don't overwrite minibuffer contents.  */
+              !MINI_WINDOW_P (XWINDOW (selected_window))
+              /* Don't overwrite a keystroke echo.  */
+              && (NILP (echo_message_buffer)
+                  || ok_to_overwrite_keystroke_echo)
+              /* Don't overwrite a prompt.  */
+              && !cursor_in_echo_area)
+       {
+         if (STRINGP (help))
+           {
+             int count = specpdl_ptr - specpdl;
+             specbind (Qmessage_truncate_lines, Qt);
+             message3_nolog (help, XSTRING (help)->size,
+                             STRING_MULTIBYTE (help));
+             unbind_to (count, Qnil);
+           }
+         else
+           message (0);
+       }
+    }
+}
+
 
 \f
 /* Input of single characters from keyboard */
@@ -1971,19 +2182,43 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu)
        }
     }
 
-  /* Message turns off echoing unless more keystrokes turn it on again. */
-  if (/* There is a current message.  */
+  /* Message turns off echoing unless more keystrokes turn it on again.
+     
+     The code in 20.x for the condition was
+
+     1. echo_area_glyphs && *echo_area_glyphs
+     2. && echo_area_glyphs != current_kboard->echobuf
+     3. && ok_to_echo_at_next_pause != echo_area_glyphs
+
+     (1) means there's a current message displayed
+     
+     (2) means it's not the message from echoing from the current
+     kboard.
+     
+     (3) There's only one place in 20.x where ok_to_echo_at_next_pause
+     is set to a non-null value.  This is done in read_char and it is
+     set to echo_area_glyphs after a call to echo_char.  That means
+     ok_to_echo_at_next_pause is either null or
+     current_kboard->echobuf with the appropriate current_kboard at
+     that time.
+
+     So, condition (3) means in clear text ok_to_echo_at_next_pause
+     must be either null, or the current message isn't from echoing at
+     all, or it's from echoing from a different kboard than the
+     current one.  */
+  
+  if (/* There currently something in the echo area  */
       !NILP (echo_area_buffer[0])
-      /* And we're not echoing from this kboard.  */
-      && echo_kboard != current_kboard
-      /* And it's either not ok to echo (ok_to_echo == NULL), or the
-        last char echoed was from a different kboard.  */
-      && ok_to_echo_at_next_pause != echo_kboard)
+      && (/* And it's either not from echoing.  */
+         !EQ (echo_area_buffer[0], echo_message_buffer)
+         /* Or it's an echo from a different kboard.  */
+         || echo_kboard != current_kboard
+         /* Or we explicitly allow overwriting whatever there is.  */
+         || ok_to_echo_at_next_pause == NULL))
     cancel_echoing ();
   else
-    /* If already echoing, continue.  */
     echo_dash ();
-
+      
   /* Try reading a character via menu prompting in the minibuf.
      Try this before the sit-for, because the sit-for
      would do the wrong thing if we are supposed to do
@@ -2056,7 +2291,8 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu)
       && !current_kboard->immediate_echo
       && this_command_key_count > 0
       && ! noninteractive
-      && echo_keystrokes > 0
+      && (FLOATP (Vecho_keystrokes) || INTEGERP (Vecho_keystrokes))
+      && NILP (Fzerop (Vecho_keystrokes))
       && (/* No message.  */
          NILP (echo_area_buffer[0])
          /* Or empty message.  */
@@ -2076,9 +2312,13 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu)
        echo_now ();
       else
        {
+         int sec, usec;
+         double duration = extract_float (Vecho_keystrokes);
+         sec = (int) duration;
+         usec = (duration - sec) * 1000000;
          save_getcjmp (save_jump);
          restore_getcjmp (local_getcjmp);
-         tem0 = sit_for (echo_keystrokes, 0, 1, 1, 0);
+         tem0 = sit_for (sec, usec, 1, 1, 0);
          restore_getcjmp (save_jump);
          if (EQ (tem0, Qt)
              && ! CONSP (Vunread_command_events))
@@ -2383,16 +2623,21 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu)
   if (INTEGERP (c)
       && ! NILP (Vinput_method_function)
       && (unsigned) XINT (c) >= ' '
-      && (unsigned) XINT (c) < 127)
+      && (unsigned) XINT (c) != 127
+      && (unsigned) XINT (c) < 256)
     {
       previous_echo_area_message = Fcurrent_message ();
       Vinput_method_previous_message = previous_echo_area_message;
     }
 
-  /* Now wipe the echo area.  */
-  if (!NILP (echo_area_buffer[0]))
-    safe_run_hooks (Qecho_area_clear_hook);
-  clear_message (1, 0);
+  /* Now wipe the echo area, except for help events which do their
+     own stuff with the echo area.  */
+  if (!CONSP (c) || !(EQ (Qhelp_echo, XCAR (c))))
+    {
+      if (!NILP (echo_area_buffer[0]))
+       safe_run_hooks (Qecho_area_clear_hook);
+      clear_message (1, 0);
+    }
 
  reread_for_input_method:
  from_macro:
@@ -2403,7 +2648,8 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu)
         after the first event of the key sequence.  */
       && NILP (prev_event)
       && (unsigned) XINT (c) >= ' '
-      && (unsigned) XINT (c) < 127)
+      && (unsigned) XINT (c) != 127
+      && (unsigned) XINT (c) < 256)
     {
       Lisp_Object keys; 
       int key_count;
@@ -2485,21 +2731,15 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu)
  reread_first:
 
   /* Display help if not echoing.  */
-  if (CONSP (c)
-      && EQ (XCAR (c), Qhelp_echo))
-    {
-      Lisp_Object msg = XCDR (XCDR (c));
-
-      if (!NILP (Vshow_help_function))
-       call1 (Vshow_help_function, msg);
-      else if (!echoing && !MINI_WINDOW_P (XWINDOW (selected_window)))
-       {
-         if (STRINGP (msg))
-           message3_nolog (msg, XSTRING (msg)->size, STRING_MULTIBYTE (msg));
-         else
-           message (0);
-       }
-      
+  if (CONSP (c) && EQ (XCAR (c), Qhelp_echo))
+    {
+      /* (help-echo FRAME HELP WINDOW OBJECT POS).  */
+      Lisp_Object help, object, position, window;
+      help = Fnth (make_number (2), c);
+      window = Fnth (make_number (3), c);
+      object = Fnth (make_number (4), c);
+      position = Fnth (make_number (5), c);
+      show_help_echo (help, window, object, position, 0);
       goto retry;
     }
   
@@ -2509,7 +2749,8 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu)
       before_command_echo_length = echo_length ();
 
       /* Don't echo mouse motion events.  */
-      if (echo_keystrokes
+      if ((FLOATP (Vecho_keystrokes) || INTEGERP (Vecho_keystrokes))
+         && NILP (Fzerop (Vecho_keystrokes))
          && ! (EVENT_HAS_PARAMETERS (c)
                && EQ (EVENT_HEAD_KIND (EVENT_HEAD (c)), Qmouse_movement)))
        {
@@ -2579,7 +2820,8 @@ record_menu_key (c)
   before_command_echo_length = echo_length ();
 
   /* Don't echo mouse motion events.  */
-  if (echo_keystrokes)
+  if ((FLOATP (Vecho_keystrokes) || INTEGERP (Vecho_keystrokes))
+      && NILP (Fzerop (Vecho_keystrokes)))
     {
       echo_char (c);
 
@@ -2714,6 +2956,7 @@ tracking_off (old_value)
          get_input_pending (&input_pending, 1);
        }
     }
+  return Qnil;
 }
 
 DEFUN ("track-mouse", Ftrack_mouse, Strack_mouse, 0, UNEVALLED, 0,
@@ -2857,6 +3100,7 @@ kbd_buffer_store_event (event)
                    {
                      sp->kind = no_event;
                      sp->frame_or_window = Qnil;
+                     sp->arg = Qnil;
                    }
                }
              return;
@@ -2904,6 +3148,11 @@ kbd_buffer_store_event (event)
      Discard the event if it would fill the last slot.  */
   if (kbd_fetch_ptr - 1 != kbd_store_ptr)
     {
+      int idx;
+      
+#if 0 /* The selection_request_event case looks bogus, and it's error
+        prone to assign individual members for other events, in case
+        the input_event structure is changed.  --2000-07-13, gerd.  */
       struct input_event *sp = kbd_store_ptr;
       sp->kind = event->kind;
       if (event->kind == selection_request_event)
@@ -2914,22 +3163,94 @@ kbd_buffer_store_event (event)
          bcopy (event, (char *) sp, sizeof (*event));
        }
       else
+
        {
          sp->code = event->code;
          sp->part = event->part;
          sp->frame_or_window = event->frame_or_window;
+         sp->arg = event->arg;
          sp->modifiers = event->modifiers;
          sp->x = event->x;
          sp->y = event->y;
          sp->timestamp = event->timestamp;
        }
-      (XVECTOR (kbd_buffer_frame_or_window)->contents[kbd_store_ptr
-                                                     - kbd_buffer]
-       = event->frame_or_window);
+#else
+      *kbd_store_ptr = *event;
+#endif
 
-      kbd_store_ptr++;
+      idx = 2 * (kbd_store_ptr - kbd_buffer);
+      ASET (kbd_buffer_gcpro, idx, event->frame_or_window);
+      ASET (kbd_buffer_gcpro, idx + 1, event->arg);
+      ++kbd_store_ptr;
     }
 }
+
+
+/* Generate HELP_EVENT input_events in BUFP which has roon for
+   SIZE events.  If there's not enough room in BUFP, ignore this
+   event.
+
+   HELP is the help form.
+
+   FRAME is the frame on which the help is generated.  OBJECT is the
+   Lisp object where the help was found (a buffer, a string, an
+   overlay, or nil if neither from a string nor from a buffer.  POS is
+   the position within OBJECT where the help was found.
+
+   Value is the number of input_events generated.  */
+
+int
+gen_help_event (bufp, size, help, frame, window, object, pos)
+     struct input_event *bufp;
+     int size;
+     Lisp_Object help, frame, object, window;
+     int pos;
+{
+  int nevents_stored = 0;
+  
+  if (size >= 2)
+    {
+      bufp->kind = HELP_EVENT;
+      bufp->frame_or_window = frame;
+      bufp->arg = object;
+      bufp->x = make_number (pos);
+      bufp->code = 0;
+
+      ++bufp;
+      bufp->kind = HELP_EVENT;
+      bufp->frame_or_window = WINDOWP (window) ? window : frame;
+      bufp->arg = help;
+      bufp->code = 1;
+      nevents_stored = 2;
+    }
+
+  return nevents_stored;
+}
+
+
+/* Store HELP_EVENTs for HELP on FRAME in the input queue.  */
+
+void
+kbd_buffer_store_help_event (frame, help)
+     Lisp_Object frame, help;
+{
+  struct input_event event;
+
+  event.kind = HELP_EVENT;
+  event.frame_or_window = frame;
+  event.arg = Qnil;
+  event.x = make_number (0);
+  event.code = 0;
+  kbd_buffer_store_event (&event);
+  
+  event.kind = HELP_EVENT;
+  event.frame_or_window = frame;
+  event.arg = help;
+  event.x = make_number (0);
+  event.code = 1;
+  kbd_buffer_store_event (&event);
+}
+
 \f
 /* Discard any mouse events in the event buffer by setting them to
    no_event.  */
@@ -2952,7 +3273,49 @@ discard_mouse_events ()
        }
     }
 }
+
+
+/* Return non-zero if there are any real events waiting in the event
+   buffer, not counting `no_event's.
+
+   If DISCARD is non-zero, discard no_event events at the front of
+   the input queue, possibly leaving the input queue empty if there
+   are no real input events.  */
+
+int
+kbd_buffer_events_waiting (discard)
+     int discard;
+{
+  struct input_event *sp;
+  
+  for (sp = kbd_fetch_ptr;
+       sp != kbd_store_ptr && sp->kind == no_event;
+       ++sp)
+    {
+      if (sp == kbd_buffer + KBD_BUFFER_SIZE)
+       sp = kbd_buffer;
+    }
+
+  if (discard)
+    kbd_fetch_ptr = sp;
+
+  return sp != kbd_store_ptr && sp->kind != no_event;
+}
+
 \f
+/* Clear input event EVENT.  */
+
+static INLINE void
+clear_event (event)
+     struct input_event *event;
+{
+  int idx = 2 * (event - kbd_buffer);
+  ASET (kbd_buffer_gcpro, idx, Qnil);
+  ASET (kbd_buffer_gcpro, idx + 1, Qnil);
+  event->kind = no_event;
+}
+
+
 /* 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,
@@ -3150,15 +3513,51 @@ kbd_buffer_get_event (kbp, used_mouse_menu)
        kbd_fetch_ptr = event + 1;
       else if (event->kind == HELP_EVENT)
        {
-         /* The car of event->frame_or_window is a frame,
-            the cdr is the help to display.  */
-         obj = Fcons (Qhelp_echo, event->frame_or_window);
+         /* There are always two HELP_EVENTs in the input queue.  */
+         Lisp_Object object, position, help, frame, window;
+
+         xassert (event->code == 0);
+         frame = event->frame_or_window;
+         object = event->arg;
+         position = event->x;
+         clear_event (event);
+
+         kbd_fetch_ptr = event + 1;
+         event = ((kbd_fetch_ptr < kbd_buffer + KBD_BUFFER_SIZE)
+                  ? kbd_fetch_ptr
+                  : kbd_buffer);
+         xassert (event->code == 1);
+         help = event->arg;
+         window = event->frame_or_window;
+         if (!WINDOWP (window))
+           window = Qnil;
+         obj = Fcons (Qhelp_echo,
+                      list5 (frame, help, window, object, position));
+         clear_event (event);
+         kbd_fetch_ptr = event + 1;
+       }
+      else if (event->kind == FOCUS_IN_EVENT)
+       {
+         /* Notification of a FocusIn event.  The frame receiving the
+            focus is in event->frame_or_window.  Generate a
+            switch-frame event if necessary.  */
+         Lisp_Object frame, focus;
+
+         frame = event->frame_or_window;
+         focus = FRAME_FOCUS_FRAME (XFRAME (frame));
+         if (FRAMEP (focus))
+           frame = focus;
+
+         if (!EQ (frame, internal_last_event_frame)
+             && !EQ (frame, selected_frame))
+           obj = make_lispy_switch_frame (frame);
+         internal_last_event_frame = frame;
          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.  */
       else
        {
+         /* 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;
          Lisp_Object focus;
 
@@ -3182,25 +3581,25 @@ kbd_buffer_get_event (kbp, used_mouse_menu)
 
          if (NILP (obj))
            {
+             int idx;
+             
              obj = make_lispy_event (event);
+             
 #if defined (USE_X_TOOLKIT) || defined (HAVE_NTGUI)
              /* If this was a menu selection, then set the flag to inhibit
                 writing to last_nonmenu_event.  Don't do this if the event
                 we're returning is (menu-bar), though; that indicates the
                 beginning of the menu sequence, and we might as well leave
                 that as the `event with parameters' for this selection.  */
-             if ((event->kind == menu_bar_event
-                  || event->kind == TOOL_BAR_EVENT)
-                 && !(CONSP (obj) && EQ (XCAR (obj), Qmenu_bar))
-                 && !(CONSP (obj) && EQ (XCAR (obj), Qtool_bar))
-                 && used_mouse_menu)
+             if (used_mouse_menu
+                 && !EQ (event->frame_or_window, event->arg)
+                 && (event->kind == MENU_BAR_EVENT
+                     || event->kind == TOOL_BAR_EVENT))
                *used_mouse_menu = 1;
 #endif
 
              /* Wipe out this event, to catch bugs.  */
-             event->kind = no_event;
-             XVECTOR (kbd_buffer_frame_or_window)->contents[event - kbd_buffer] = Qnil;
-
+             clear_event (event);
              kbd_fetch_ptr = event + 1;
            }
        }
@@ -3516,7 +3915,7 @@ timer_check (do_it_now)
        }
       vector = XVECTOR (chosen_timer)->contents;
        
-      /* If timer is rupe, run it if it hasn't been run.  */
+      /* If timer is ripe, run it if it hasn't been run.  */
       if (EMACS_TIME_NEG_P (difference)
          || (EMACS_SECS (difference) == 0
              && EMACS_USECS (difference) == 0))
@@ -3525,9 +3924,6 @@ timer_check (do_it_now)
            {
              int was_locked = single_kboard;
              int count = specpdl_ptr - specpdl;
-#ifdef HAVE_WINDOW_SYSTEM
-             int old_inhibit_busy_cursor = inhibit_busy_cursor;
-#endif
 
              /* Mark the timer as triggered to prevent problems if the lisp
                 code fails to reschedule it right.  */
@@ -3535,17 +3931,9 @@ timer_check (do_it_now)
 
              specbind (Qinhibit_quit, Qt);
 
-#ifdef HAVE_WINDOW_SYSTEM
-             inhibit_busy_cursor = 2;
-#endif
-             
              call1 (Qtimer_event_handler, chosen_timer);
              timers_run++;
 
-#ifdef HAVE_WINDOW_SYSTEM
-             inhibit_busy_cursor = old_inhibit_busy_cursor;
-#endif
-
              unbind_to (count, Qnil);
 
              /* Resume allowing input from any kboard, if that was true before.  */
@@ -3982,10 +4370,7 @@ static char *iso_lispy_function_keys[] =
 
 #endif /* not HAVE_NTGUI */
 
-static char *lispy_mouse_names[] =
-{
-  "mouse-1", "mouse-2", "mouse-3", "mouse-4", "mouse-5"
-};
+Lisp_Object Vlispy_mouse_stem;
 
 #ifdef WINDOWSNT
 /* mouse-wheel events are generated by the wheel on devices such as
@@ -4012,12 +4397,12 @@ static char *lispy_drag_n_drop_names[] =
 /* Scroll bar parts.  */
 Lisp_Object Qabove_handle, Qhandle, Qbelow_handle;
 Lisp_Object Qup, Qdown, Qbottom, Qend_scroll;
-Lisp_Object Qtop;
+Lisp_Object Qtop, Qratio;
 
 /* An array of scroll bar parts, indexed by an enum scroll_bar_part value.  */
 Lisp_Object *scroll_bar_parts[] = {
   &Qabove_handle, &Qhandle, &Qbelow_handle,
-  &Qup, &Qdown, &Qtop, &Qbottom, &Qend_scroll
+  &Qup, &Qdown, &Qtop, &Qbottom, &Qend_scroll, &Qratio
 };
 
 /* User signal events.  */
@@ -4100,6 +4485,14 @@ make_lispy_event (event)
        return lispy_c;
       }
 
+    case multibyte_char_keystroke:
+      {
+       Lisp_Object lispy_c;
+
+       XSETFASTINT (lispy_c, event->code);
+       return lispy_c;
+      }
+
       /* A function key.  The symbol may need to have modifier prefixes
         tacked onto it.  */
     case non_ascii_keystroke:
@@ -4171,9 +4564,6 @@ make_lispy_event (event)
        Lisp_Object *start_pos_ptr;
        Lisp_Object start_pos;
 
-       if (button < 0 || button >= NUM_MOUSE_BUTTONS)
-         abort ();
-
        /* Build the position as appropriate for this mouse click.  */
        if (event->kind == mouse_click)
          {
@@ -4325,6 +4715,13 @@ make_lispy_event (event)
          }
 #endif /* not USE_TOOLKIT_SCROLL_BARS */
 
+       if (button >= XVECTOR (button_down_location)->size)
+         {
+           button_down_location = larger_vector (button_down_location,
+                                                 button + 1, Qnil);
+           mouse_syms = larger_vector (mouse_syms, button + 1, Qnil);
+         }
+       
        start_pos_ptr = &XVECTOR (button_down_location)->contents[button];
 
        start_pos = *start_pos_ptr;
@@ -4414,10 +4811,10 @@ make_lispy_event (event)
 
          head = modify_event_symbol (button,
                                      event->modifiers,
-                                     Qmouse_click, Qnil,
-                                     lispy_mouse_names, &mouse_syms,
-                                     (sizeof (lispy_mouse_names)
-                                      / sizeof (lispy_mouse_names[0])));
+                                     Qmouse_click, Vlispy_mouse_stem,
+                                     NULL,
+                                     &mouse_syms,
+                                     XVECTOR (mouse_syms)->size);
          if (event->modifiers & drag_modifier)
            return Fcons (head,
                          Fcons (start_pos,
@@ -4475,10 +4872,10 @@ make_lispy_event (event)
        /* Get the symbol we should use for the mouse click.  */
        head = modify_event_symbol (event->code,
                                    event->modifiers,
-                                   Qmouse_click, Qnil,
-                                   lispy_mouse_names, &mouse_syms,
-                                   (sizeof (lispy_mouse_names)
-                                    / sizeof (lispy_mouse_names[0])));
+                                   Qmouse_click, 
+                                   Vlispy_mouse_stem,
+                                   NULL, &mouse_syms,
+                                   XVECTOR (mouse_syms)->size);
        return Fcons (head, Fcons (position, Qnil));
       }
       
@@ -4493,9 +4890,6 @@ make_lispy_event (event)
        Lisp_Object *start_pos_ptr;
        Lisp_Object start_pos;
 
-       if (button < 0 || button >= NUM_MOUSE_BUTTONS)
-         abort ();
-
        {
          Lisp_Object window;
          Lisp_Object portion_whole;
@@ -4522,10 +4916,10 @@ make_lispy_event (event)
 
          head = modify_event_symbol (button,
                                      event->modifiers,
-                                     Qmouse_click, Qnil,
-                                     lispy_mouse_names, &mouse_syms,
-                                     (sizeof (lispy_mouse_names)
-                                      / sizeof (lispy_mouse_names[0])));
+                                     Qmouse_click, 
+                                     Vlispy_mouse_stem,
+                                     NULL, &mouse_syms,
+                                     XVECTOR (mouse_syms)->size);
          return Fcons (head,
                        Fcons (position,
                               Qnil));
@@ -4558,7 +4952,8 @@ make_lispy_event (event)
            int pixcolumn, pixrow;
            column -= XINT (XWINDOW (window)->left);
            row -= XINT (XWINDOW (window)->top);
-           glyph_to_pixel_coords (f, column, row, &pixcolumn, &pixrow);
+           glyph_to_pixel_coords (XWINDOW(window), column, row,
+                                   &pixcolumn, &pixrow);
            XSETINT (event->x, pixcolumn);
            XSETINT (event->y, pixrow);
 
@@ -4571,7 +4966,7 @@ make_lispy_event (event)
            else
              XSETINT (posn,
                       buffer_posn_from_coords (XWINDOW (window),
-                                               column, row));
+                                               &column, &row));
          }
 
        {
@@ -4675,25 +5070,26 @@ make_lispy_event (event)
 #endif /* HAVE_MOUSE */
 
 #if defined (USE_X_TOOLKIT) || defined (HAVE_NTGUI)
-    case menu_bar_event:
-      /* The event value is in the cdr of the frame_or_window slot.  */
-      if (!CONSP (event->frame_or_window))
-       abort ();
-      return XCDR (event->frame_or_window);
+    case MENU_BAR_EVENT:
+      if (EQ (event->arg, event->frame_or_window))
+       /* This is the prefix key.  We translate this to
+          `(menu_bar)' because the code in keyboard.c for menu
+          events, which we use, relies on this.  */
+       return Fcons (Qmenu_bar, Qnil);
+      return event->arg;
 #endif
 
     case TOOL_BAR_EVENT:
-      {
-       Lisp_Object key;
-       if (!CONSP (event->frame_or_window))
-         abort ();
-       key = XCDR (event->frame_or_window);
-       if (SYMBOLP (key))
-         key = apply_modifiers (event->modifiers, key);
-       return key;
-      }
-
-    case user_signal:
+      if (EQ (event->arg, event->frame_or_window))
+       /* This is the prefix key.  We translate this to
+          `(tool_bar)' because the code in keyboard.c for menu
+          events, which we use, relies on this.  */
+       return Fcons (Qtool_bar, Qnil);
+      else if (SYMBOLP (event->arg))
+       return apply_modifiers (event->modifiers, event->arg);
+      return event->arg;
+
+    case USER_SIGNAL_EVENT:
       /* A user signal.  */
       return *lispy_user_signals[event->code];
       
@@ -5115,8 +5511,11 @@ reorder_modifiers (symbol)
    is the name of the i'th symbol.  TABLE_SIZE is the number of elements
    in the table.
 
-   Alternatively, NAME_ALIST is an alist mapping codes into symbol names.
-   NAME_ALIST is used if it is non-nil; otherwise NAME_TABLE is used.
+   Alternatively, NAME_ALIST_OR_STEM is either an alist mapping codes
+   into symbol names, or a string specifying a name stem used to
+   construct a symbol name or the form `STEM-N', where N is the decimal
+   representation of SYMBOL_NUM.  NAME_ALIST_OR_STEM is used if it is
+   non-nil; otherwise NAME_TABLE is used.
 
    SYMBOL_TABLE should be a pointer to a Lisp_Object whose value will
    persist between calls to modify_event_symbol that it can use to
@@ -5136,12 +5535,12 @@ reorder_modifiers (symbol)
    in the symbol's name.  */
 
 static Lisp_Object
-modify_event_symbol (symbol_num, modifiers, symbol_kind, name_alist,
+modify_event_symbol (symbol_num, modifiers, symbol_kind, name_alist_or_stem,
                      name_table, symbol_table, table_size)
      int symbol_num;
      unsigned modifiers;
      Lisp_Object symbol_kind;
-     Lisp_Object name_alist;
+     Lisp_Object name_alist_or_stem;
      char **name_table;
      Lisp_Object *symbol_table;
      unsigned int table_size;
@@ -5181,8 +5580,16 @@ modify_event_symbol (symbol_num, modifiers, symbol_kind, name_alist,
   if (NILP (value))
     {
       /* No; let's create it.  */
-      if (!NILP (name_alist))
-       value = Fcdr_safe (Fassq (symbol_int, name_alist));
+      if (CONSP (name_alist_or_stem))
+       value = Fcdr_safe (Fassq (symbol_int, name_alist_or_stem));
+      else if (STRINGP (name_alist_or_stem))
+       {
+         int len = STRING_BYTES (XSTRING (name_alist_or_stem));
+         char *buf = (char *) alloca (len + 50);
+         sprintf (buf, "%s-%d", XSTRING (name_alist_or_stem)->data,
+                  XINT (symbol_int) + 1);
+         value = intern (buf);
+       }
       else if (name_table != 0 && name_table[symbol_num])
        value = intern (name_table[symbol_num]);
 
@@ -5456,6 +5863,7 @@ record_asynch_buffer_change ()
 
   event.kind = buffer_switch_event;
   event.frame_or_window = Qnil;
+  event.arg = Qnil;
 
 #ifdef subprocesses
   /* We don't need a buffer-switch event unless Emacs is waiting for input.
@@ -5617,6 +6025,7 @@ read_avail_input (expected)
 
          buf[i].code = cbuf[i];
          buf[i].frame_or_window = selected_frame;
+         buf[i].arg = Qnil;
        }
     }
 
@@ -5794,11 +6203,18 @@ menu_bar_items (old)
       }
     else
       {
-       /* No, so use major and minor mode keymaps.  */
+       /* No, so use major and minor mode keymaps and keymap property.  */
+       int extra_maps = 2;
+       Lisp_Object map = get_local_map (PT, current_buffer, keymap);
+       if (!NILP (map))
+         extra_maps = 3;
        nmaps = current_minor_maps (NULL, &tmaps);
-       maps = (Lisp_Object *) alloca ((nmaps + 2) * sizeof (maps[0]));
+       maps = (Lisp_Object *) alloca ((nmaps + extra_maps)
+                                      * sizeof (maps[0]));
        bcopy (tmaps, maps, nmaps * sizeof (maps[0]));
-       maps[nmaps++] = get_local_map (PT, current_buffer);
+       if (!NILP (map))
+         maps[nmaps++] = get_local_map (PT, current_buffer, keymap);
+       maps[nmaps++] = get_local_map (PT, current_buffer, local_map);
       }
     maps[nmaps++] = current_global_map;
   }
@@ -6078,7 +6494,7 @@ parse_menu_item (item, notreal, inmenubar)
          item = XCDR (item);
        }
          
-      /* Maybee key binding cache.  */
+      /* Maybe key binding cache.  */
       if (CONSP (item) && CONSP (XCAR (item))
          && (NILP (XCAR (XCAR (item)))
              || VECTORP (XCAR (XCAR (item)))))
@@ -6446,11 +6862,18 @@ tool_bar_items (reuse, nitems)
     }
   else
     {
-      /* No, so use major and minor mode keymaps.  */
+      /* No, so use major and minor mode keymaps and keymap property.  */
+      int extra_maps = 2;
+      Lisp_Object map = get_local_map (PT, current_buffer, keymap);
+      if (!NILP (map))
+       extra_maps = 3;
       nmaps = current_minor_maps (NULL, &tmaps);
-      maps = (Lisp_Object *) alloca ((nmaps + 2) * sizeof (maps[0]));
+      maps = (Lisp_Object *) alloca ((nmaps + extra_maps)
+                                    * sizeof (maps[0]));
       bcopy (tmaps, maps, nmaps * sizeof (maps[0]));
-      maps[nmaps++] = get_local_map (PT, current_buffer);
+      if (!NILP (map))
+       maps[nmaps++] = get_local_map (PT, current_buffer, keymap);
+      maps[nmaps++] = get_local_map (PT, current_buffer, local_map);
     }
 
   /* Add global keymap at the end.  */
@@ -6998,7 +7421,7 @@ read_char_minibuf_menu_prompt (commandflag, nmaps, maps)
                  char_matches = (XINT (upcased_event) == XSTRING (s)->data[0]
                                  || XINT (downcased_event) == XSTRING (s)->data[0]);
                  if (! char_matches)
-                   desc = Fsingle_key_description (event);
+                   desc = Fsingle_key_description (event, Qnil);
 
                  tem
                    = XVECTOR (item_properties)->contents[ITEM_PROPERTY_KEYEQ];
@@ -7143,7 +7566,7 @@ follow_key (key, nmaps, current, defs, next)
      followed by the corresponding non-meta character.
      Put the results into DEFS, since we are going to alter that anyway.
      Do not alter CURRENT or NEXT.  */
-  if (INTEGERP (key) && (XINT (key) & CHAR_META))
+  if (INTEGERP (key) && (XUINT (key) & CHAR_META))
     {
       for (i = 0; i < nmaps; i++)
        if (! NILP (current[i]))
@@ -7175,7 +7598,7 @@ follow_key (key, nmaps, current, defs, next)
          else
            map = current[i];
 
-         defs[i] = get_keyelt (access_keymap (map, key, 1, 0), 0);
+         defs[i] = get_keyelt (access_keymap (map, key, 1, 0), 1);
          if (! NILP (defs[i]))
            first_binding = i;
        }
@@ -7264,6 +7687,10 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last,
   /* The local map to start out with at start of key sequence.  */
   Lisp_Object orig_local_map;
 
+  /* The map from the `keymap' property to start out with at start of
+     key sequence.  */
+  Lisp_Object orig_keymap;
+
   /* 1 if we have already considered switching to the local-map property
      of the place where a mouse click occurred.  */
   int localized_local_map = 0;
@@ -7359,7 +7786,9 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last,
     {
       if (!NILP (prompt))
        echo_prompt (XSTRING (prompt)->data);
-      else if (cursor_in_echo_area && echo_keystrokes)
+      else if (cursor_in_echo_area
+              && (FLOATP (Vecho_keystrokes) || INTEGERP (Vecho_keystrokes))
+              && NILP (Fzerop (Vecho_keystrokes)))
        /* 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.  */
        echo_dash ();
@@ -7383,7 +7812,8 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last,
                           &junk);
 #endif /* GOBBLE_FIRST_EVENT */
 
-  orig_local_map = get_local_map (PT, current_buffer);
+  orig_local_map = get_local_map (PT, current_buffer, local_map);
+  orig_keymap = get_local_map (PT, current_buffer, keymap);
 
   /* 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,
@@ -7419,14 +7849,21 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last,
       }
     else
       {
+       int extra_maps = 2;
        nmaps = current_minor_maps (0, &maps);
-       if (nmaps + 2 > nmaps_allocated)
+       if (!NILP (orig_keymap))
+         extra_maps = 3;
+       if (nmaps + extra_maps > nmaps_allocated)
          {
-           submaps = (Lisp_Object *) alloca ((nmaps+2) * sizeof (submaps[0]));
-           defs    = (Lisp_Object *) alloca ((nmaps+2) * sizeof (defs[0]));
-           nmaps_allocated = nmaps + 2;
+           submaps = (Lisp_Object *) alloca ((nmaps+extra_maps)
+                                             * sizeof (submaps[0]));
+           defs    = (Lisp_Object *) alloca ((nmaps+extra_maps)
+                                             * sizeof (defs[0]));
+           nmaps_allocated = nmaps + extra_maps;
          }
        bcopy (maps, submaps, nmaps * sizeof (submaps[0]));
+       if (!NILP (orig_keymap))
+         submaps[nmaps++] = orig_keymap;
        submaps[nmaps++] = orig_local_map;
       }
     submaps[nmaps++] = current_global_map;
@@ -7505,7 +7942,8 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last,
        {
          key = keybuf[t];
          add_command_key (key);
-         if (echo_keystrokes)
+         if ((FLOATP (Vecho_keystrokes) || INTEGERP (Vecho_keystrokes))
+             && NILP (Fzerop (Vecho_keystrokes)))
            echo_char (key);
        }
 
@@ -7544,7 +7982,8 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last,
                               interrupted_kboard->kbd_queue);
                  }
                mock_input = 0;
-               orig_local_map = get_local_map (PT, current_buffer);
+               orig_local_map = get_local_map (PT, current_buffer, local_map);
+               orig_keymap = get_local_map (PT, current_buffer, keymap);
                goto replay_sequence;
              }
 #endif
@@ -7589,7 +8028,8 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last,
                    Fset_buffer (XWINDOW (selected_window)->buffer);
                }
 
-             orig_local_map = get_local_map (PT, current_buffer);
+             orig_local_map = get_local_map (PT, current_buffer, local_map);
+             orig_keymap = get_local_map (PT, current_buffer, keymap);
              goto replay_sequence;
            }
 
@@ -7603,7 +8043,8 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last,
              keybuf[t++] = key;
              mock_input = t;
              Vquit_flag = Qnil;
-             orig_local_map = get_local_map (PT, current_buffer);
+             orig_local_map = get_local_map (PT, current_buffer, local_map);
+             orig_keymap = get_local_map (PT, current_buffer, keymap);
              goto replay_sequence;
            }
 
@@ -7688,8 +8129,12 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last,
 
                  if (! FRAME_LIVE_P (XFRAME (selected_frame)))
                    Fkill_emacs (Qnil);
-                 set_buffer_internal (XBUFFER (XWINDOW (window)->buffer));
-                 orig_local_map = get_local_map (PT, current_buffer);
+                 set_buffer_internal (XBUFFER (XWINDOW
+                 (window)->buffer)
+);
+                 orig_local_map = get_local_map (PT, current_buffer,
+                                                 local_map);
+                 orig_keymap = get_local_map (PT, current_buffer, keymap);
                  goto replay_sequence;
                }
              
@@ -7710,13 +8155,24 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last,
                      if (INTEGERP (pos)
                          && XINT (pos) >= BEG && XINT (pos) <= Z)
                        {
-                         map_here = get_local_map (XINT (pos), current_buffer);
+                         map_here = get_local_map (XINT (pos),
+                                                   current_buffer, local_map);
                          if (!EQ (map_here, orig_local_map))
                            {
                              orig_local_map = map_here;
                              keybuf[t] = key;
                              mock_input = t + 1;
 
+                             goto replay_sequence;
+                           }
+                         map_here = get_local_map (XINT (pos),
+                                                    current_buffer, keymap);
+                         if (!EQ (map_here, orig_keymap))
+                           {
+                             orig_keymap = map_here;
+                             keybuf[t] = key;
+                             mock_input = t + 1;
+
                              goto replay_sequence;
                            }
                        }
@@ -7742,21 +8198,23 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last,
                     reconsider the key sequence with that keymap.  */
                  if (CONSP (POSN_STRING (EVENT_START (key))))
                    {
-                     Lisp_Object string, pos, map;
+                     Lisp_Object string, pos, map, map2;
 
                      string = POSN_STRING (EVENT_START (key));
                      pos = XCDR (string);
                      string = XCAR (string);
-                     
-                     if (pos >= 0
-                         && pos < XSTRING (string)->size
-                         && (map = Fget_text_property (pos, Qlocal_map,
-                                                       string),
-                             !NILP (map)))
-                       {
-                         orig_local_map = map;
-                         goto replay_sequence;
-                       }
+                      if (XINT (pos) >= 0
+                         && XINT (pos) < XSTRING (string)->size)
+                        {
+                          map = Fget_text_property (pos, Qlocal_map, string);
+                          if (!NILP (map))
+                            orig_local_map = map;
+                          map2 = Fget_text_property (pos, Qkeymap, string);
+                          if (!NILP (map2))
+                            orig_keymap = map2;
+                          if (!NILP (map) || !NILP (map2))
+                            goto replay_sequence;
+                        }
                    }
 
                  goto replay_key;
@@ -7963,7 +8421,7 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last,
              key = keybuf[fkey_end++];
              /* Look up meta-characters by prefixing them
                 with meta_prefix_char.  I hate this.  */
-             if (INTEGERP (key) && XINT (key) & meta_modifier)
+             if (INTEGERP (key) && XUINT (key) & meta_modifier)
                {
                  fkey_next
                    = get_keymap_1
@@ -8087,7 +8545,7 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last,
            key = keybuf[keytran_end++];
            /* Look up meta-characters by prefixing them
               with meta_prefix_char.  I hate this.  */
-           if (INTEGERP (key) && XINT (key) & meta_modifier)
+           if (INTEGERP (key) && XUINT (key) & meta_modifier)
              {
                keytran_next
                  = get_keymap_1
@@ -8294,7 +8752,8 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last,
      Better ideas?  */
   for (; t < mock_input; t++)
     {
-      if (echo_keystrokes)
+      if ((FLOATP (Vecho_keystrokes) || INTEGERP (Vecho_keystrokes))
+         && NILP (Fzerop (Vecho_keystrokes)))
        echo_char (keybuf[t]);
       add_command_key (keybuf[t]);
     }
@@ -8387,10 +8846,20 @@ DEFUN ("read-key-sequence", Fread_key_sequence, Sread_key_sequence, 1, 5, 0,
       this_single_command_key_start = 0;
     }
 
+#ifdef HAVE_X_WINDOWS
+  if (display_busy_cursor_p)
+    cancel_busy_cursor ();
+#endif
+
   i = read_key_sequence (keybuf, (sizeof keybuf/sizeof (keybuf[0])),
                         prompt, ! NILP (dont_downcase_last),
                         ! NILP (can_return_switch_frame), 0);
 
+#ifdef HAVE_X_WINDOWS
+  if (display_busy_cursor_p)
+    start_busy_cursor ();
+#endif
+
   if (i == -1)
     {
       Vquit_flag = Qt;
@@ -8432,10 +8901,20 @@ DEFUN ("read-key-sequence-vector", Fread_key_sequence_vector,
       this_single_command_key_start = 0;
     }
 
+#ifdef HAVE_X_WINDOWS
+  if (display_busy_cursor_p)
+    cancel_busy_cursor ();
+#endif
+
   i = read_key_sequence (keybuf, (sizeof keybuf/sizeof (keybuf[0])),
                         prompt, ! NILP (dont_downcase_last),
                         ! NILP (can_return_switch_frame), 0);
 
+#ifdef HAVE_X_WINDOWS
+  if (display_busy_cursor_p)
+    start_busy_cursor ();
+#endif
+
   if (i == -1)
     {
       Vquit_flag = Qt;
@@ -8715,11 +9194,18 @@ current_active_maps (maps_p)
     }
   else
     {
-      /* No, so use major and minor mode keymaps.  */
+      /* No, so use major and minor mode keymaps and keymap property.  */
+      int extra_maps = 2;
+      Lisp_Object map = get_local_map (PT, current_buffer, keymap);
+      if (!NILP (map))
+       extra_maps = 3;
       nmaps = current_minor_maps (NULL, &tmaps);
-      maps = (Lisp_Object *) xmalloc ((nmaps + 2) * sizeof (maps[0]));
+      maps = (Lisp_Object *) alloca ((nmaps + extra_maps)
+                                    * sizeof (maps[0]));
       bcopy (tmaps, maps, nmaps * sizeof (maps[0]));
-      maps[nmaps++] = get_local_map (PT, current_buffer);
+      if (!NILP (map))
+       maps[nmaps++] = get_local_map (PT, current_buffer, keymap);
+      maps[nmaps++] = get_local_map (PT, current_buffer, local_map);
     }
   maps[nmaps++] = current_global_map;
 
@@ -8887,10 +9373,18 @@ appears in the echo area and in the value of `this-command-keys.'.")
 
 DEFUN ("clear-this-command-keys", Fclear_this_command_keys,
   Sclear_this_command_keys, 0, 0, 0,
-  "Clear out the vector that `this-command-keys' returns.")
+  "Clear out the vector that `this-command-keys' returns.\n\
+Clear vector containing last 100 events.")
   ()
 {
+  int i;
+  
   this_command_key_count = 0;
+
+  for (i = 0; i < XVECTOR (recent_keys)->size; ++i)
+    XVECTOR (recent_keys)->contents[i] = Qnil;
+  total_keys = 0;
+  recent_keys_index = 0;
   return Qnil;
 }
 
@@ -8939,7 +9433,7 @@ Also cancel any kbd macro being defined.")
   discard_tty_input ();
 
   kbd_fetch_ptr =  kbd_store_ptr;
-  Ffillarray (kbd_buffer_frame_or_window, Qnil);
+  Ffillarray (kbd_buffer_gcpro, Qnil);
   input_pending = 0;
 
   return Qnil;
@@ -9023,20 +9517,25 @@ stuff_buffered_input (stuffstring)
        stuff_char (*p++);
       stuff_char ('\n');
     }
+  
   /* Anything we have read ahead, put back for the shell to read.  */
   /* ?? What should this do when we have multiple keyboards??
      Should we ignore anything that was typed in at the "wrong" kboard?  */
   for (; kbd_fetch_ptr != kbd_store_ptr; kbd_fetch_ptr++)
     {
+      int idx;
+      
       if (kbd_fetch_ptr == kbd_buffer + KBD_BUFFER_SIZE)
        kbd_fetch_ptr = kbd_buffer;
       if (kbd_fetch_ptr->kind == ascii_keystroke)
        stuff_char (kbd_fetch_ptr->code);
+      
       kbd_fetch_ptr->kind = no_event;
-      (XVECTOR (kbd_buffer_frame_or_window)->contents[kbd_fetch_ptr
-                                                     - kbd_buffer]
-       = Qnil);
+      idx = 2 * (kbd_fetch_ptr - kbd_buffer);
+      ASET (kbd_buffer_gcpro, idx, Qnil);
+      ASET (kbd_buffer_gcpro, idx + 1, Qnil);
     }
+  
   input_pending = 0;
 #endif
 #endif /* BSD_SYSTEM and not BSD4_1 */
@@ -9066,16 +9565,17 @@ clear_waiting_for_input ()
 }
 
 /* This routine is called at interrupt level in response to C-G.
- If interrupt_input, this is the handler for SIGINT.
- Otherwise, it is called from kbd_buffer_store_event,
- in handling SIGIO or SIGTINT.
+   
+   If interrupt_input, this is the handler for SIGINT.  Otherwise, it
+   is called from kbd_buffer_store_event, in handling SIGIO or
+   SIGTINT.
 
- If `waiting_for_input' is non zero, then unless `echoing' is nonzero,
- immediately throw back to read_char.
+   If `waiting_for_input' is non zero, then unless `echoing' is
  nonzero, immediately throw back to read_char.
 
- Otherwise it sets the Lisp variable  quit-flag  not-nil.
- This causes  eval  to throw, when it gets a chance.
If  quit-flag  is already non-nil, it stops the job right away.  */
+   Otherwise it sets the Lisp variable quit-flag not-nil.  This causes
+   eval to throw, when it gets a chance.  If quit-flag is already
  non-nil, it stops the job right away.  */
 
 SIGTYPE
 interrupt_signal (signalnum)   /* If we don't have an argument, */
@@ -9417,8 +9917,7 @@ init_keyboard ()
   recent_keys_index = 0;
   kbd_fetch_ptr = kbd_buffer;
   kbd_store_ptr = kbd_buffer;
-  kbd_buffer_frame_or_window
-    = Fmake_vector (make_number (KBD_BUFFER_SIZE), Qnil);
+  kbd_buffer_gcpro = Fmake_vector (make_number (2 * KBD_BUFFER_SIZE), Qnil);
 #ifdef HAVE_MOUSE
   do_mouse_tracking = Qnil;
 #endif
@@ -9435,11 +9934,6 @@ init_keyboard ()
   wipe_kboard (current_kboard);
   init_kboard (current_kboard);
 
-  if (initialized)
-    Ffillarray (kbd_buffer_frame_or_window, Qnil);
-
-  kbd_buffer_frame_or_window
-    = Fmake_vector (make_number (KBD_BUFFER_SIZE), Qnil);
   if (!noninteractive && !read_socket_hook && NILP (Vwindow_system))
     {
       signal (SIGINT, interrupt_signal);
@@ -9501,6 +9995,9 @@ struct event_head head_table[] = {
 void
 syms_of_keyboard ()
 {
+  Vlispy_mouse_stem = build_string ("mouse");
+  staticpro (&Vlispy_mouse_stem);
+  
   /* Tool-bars.  */
   QCimage = intern (":image");
   staticpro (&QCimage);
@@ -9621,6 +10118,8 @@ syms_of_keyboard ()
   staticpro (&Qbottom);
   Qend_scroll = intern ("end-scroll");
   staticpro (&Qend_scroll);
+  Qratio = intern ("ratio");
+  staticpro (&Qratio);
 
   Qevent_kind = intern ("event-kind");
   staticpro (&Qevent_kind);
@@ -9650,6 +10149,8 @@ syms_of_keyboard ()
   Fset (Qinput_method_exit_on_first_char, Qnil);
   Fset (Qinput_method_use_echo_area, Qnil);
 
+  last_point_position_buffer = Qnil;
+
   {
     struct event_head *p;
 
@@ -9664,8 +10165,10 @@ syms_of_keyboard ()
       }
   }
 
-  button_down_location = Fmake_vector (make_number (NUM_MOUSE_BUTTONS), Qnil);
+  button_down_location = Fmake_vector (make_number (1), Qnil);
   staticpro (&button_down_location);
+  mouse_syms = Fmake_vector (make_number (1), Qnil);
+  staticpro (&mouse_syms);
 
   {
     int i;
@@ -9691,9 +10194,8 @@ syms_of_keyboard ()
   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);
+  kbd_buffer_gcpro = Fmake_vector (make_number (2 * KBD_BUFFER_SIZE), Qnil);
+  staticpro (&kbd_buffer_gcpro);
 
   accent_key_syms = Qnil;
   staticpro (&accent_key_syms);
@@ -9701,9 +10203,6 @@ syms_of_keyboard ()
   func_key_syms = Qnil;
   staticpro (&func_key_syms);
 
-  mouse_syms = Qnil;
-  staticpro (&mouse_syms);
-
 #ifdef WINDOWSNT
   mouse_wheel_syms = Qnil;
   staticpro (&mouse_wheel_syms);
@@ -9829,9 +10328,10 @@ After auto-saving due to this many seconds of idle time,\n\
 Emacs also does a garbage collection if that seems to be warranted.");
   XSETFASTINT (Vauto_save_timeout, 30);
 
-  DEFVAR_INT ("echo-keystrokes", &echo_keystrokes,
-    "*Nonzero means echo unfinished commands after this many seconds of pause.");
-  echo_keystrokes = 1;
+  DEFVAR_LISP ("echo-keystrokes", &Vecho_keystrokes,
+    "*Nonzero means echo unfinished commands after this many seconds of pause.\n\
+The value may be integer or floating point.");
+  Vecho_keystrokes = make_number (1);
 
   DEFVAR_INT ("polling-period", &polling_period,
     "*Interval between polling for input during Lisp execution.\n\
@@ -10083,6 +10583,27 @@ before running the input method.  It is nil if there was no message.");
     "If non-nil, the function that implements the display of help.\n\
 It's called with one argument, the help string to display.");
   Vshow_help_function = Qnil;
+
+  DEFVAR_LISP ("disable-point-adjustment", &Vdisable_point_adjustment,
+    "If non-nil, suppress point adjustment after executing a command.\n\
+\n\
+After a command is executed, if point is moved into a region that has\n\
+special properties (e.g. composition, display), we adjust point to\n\
+the boundary of the region.  But, several special commands sets this\n\
+variable to non-nil, then we suppress the point adjustment.\n\
+\n\
+This variable is set to nil before reading a command, and is checked\n\
+just after executing the command");
+  Vdisable_point_adjustment = Qnil;
+
+  DEFVAR_LISP ("global-disable-point-adjustment",
+              &Vglobal_disable_point_adjustment,
+    "*If non-nil, always suppress point adjustment.\n\
+\n\
+The default value is nil, in which case, point adjustment are\n\
+suppressed only after special commands that set\n\
+`disable-point-adjustment' (which see) to non-nil.");
+  Vglobal_disable_point_adjustment = Qnil;
 }
 
 void