(handle_async_input): New fun, extracted from input_available_signal.
[bpt/emacs.git] / src / keyboard.c
index 5a1fc4f..d6a7434 100644 (file)
@@ -1,5 +1,5 @@
 /* Keyboard and mouse input; editor command loop.
-   Copyright (C) 1985,86,87,88,89,93,94,95,96,97,99, 2000, 01, 02
+   Copyright (C) 1985,86,87,88,89,93,94,95,96,97,99,2000,01,02,03,04
      Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
@@ -547,9 +547,6 @@ Lisp_Object Qhelp_echo;
 /* Symbols to denote kinds of events.  */
 Lisp_Object Qfunction_key;
 Lisp_Object Qmouse_click;
-#if defined(WINDOWSNT) || defined(MAC_OSX)
-Lisp_Object Qmouse_wheel;
-#endif
 #ifdef WINDOWSNT
 Lisp_Object Qlanguage_change;
 #endif
@@ -589,6 +586,8 @@ Lisp_Object Qvertical_line;
 Lisp_Object Qvertical_scroll_bar;
 Lisp_Object Qmenu_bar;
 extern Lisp_Object Qleft_margin, Qright_margin;
+extern Lisp_Object Qleft_fringe, Qright_fringe;
+extern Lisp_Object QCmap;
 
 Lisp_Object recursive_edit_unwind (), command_loop ();
 Lisp_Object Fthis_command_keys ();
@@ -785,14 +784,19 @@ echo_char (c)
       /* Replace a dash from echo_dash with a space, otherwise
         add a space at the end as a separator between keys.  */
       if (STRINGP (echo_string)
-         && SCHARS (echo_string) > 0)
+         && SCHARS (echo_string) > 1)
        {
-         Lisp_Object last_char, idx;
+         Lisp_Object last_char, prev_char, idx;
+
+         idx = make_number (SCHARS (echo_string) - 2);
+         prev_char = Faref (echo_string, idx);
 
          idx = make_number (SCHARS (echo_string) - 1);
          last_char = Faref (echo_string, idx);
 
-         if (XINT (last_char) == '-')
+         /* We test PREV_CHAR to make sure this isn't the echoing
+            of a minus-sign.  */
+         if (XINT (last_char) == '-' && XINT (prev_char) != ' ')
            Faset (echo_string, idx, make_number (' '));
          else
            echo_string = concat2 (echo_string, build_string (" "));
@@ -1869,7 +1873,10 @@ adjust_point_for_property (last_pt, modified)
   int beg, end;
   Lisp_Object val, overlay, tmp;
   int check_composition = 1, check_display = 1, check_invisible = 1;
+  int orig_pt = PT;
 
+  /* FIXME: cycling is probably not necessary because these properties
+     can't be usefully combined anyway.  */
   while (check_composition || check_display || check_invisible)
     {
       if (check_composition
@@ -1936,7 +1943,19 @@ adjust_point_for_property (last_pt, modified)
          /* Move away from the inside area.  */
          if (beg < PT && end > PT)
            {
-             SET_PT (PT < last_pt ? beg : end);
+             SET_PT ((orig_pt == PT && (last_pt < beg || last_pt > end))
+                     /* We haven't moved yet (so we don't need to fear
+                        infinite-looping) and we were outside the range
+                        before (so either end of the range still corresponds
+                        to a move in the right direction): pretend we moved
+                        less than we actually did, so that we still have
+                        more freedom below in choosing which end of the range
+                        to go to.  */
+                     ? (orig_pt = -1, PT < last_pt ? end : beg)
+                     /* We either have moved already or the last point
+                        was already in the range: we don't get to choose
+                        which end of the range we have to go to.  */
+                     : (PT < last_pt ? beg : end));
              check_composition = check_display = 1;
            }
          xassert (PT == beg || PT == end);
@@ -1983,6 +2002,11 @@ static Lisp_Object
 safe_run_hooks_error (data)
      Lisp_Object data;
 {
+  Lisp_Object args[3];
+  args[0] = build_string ("Error in %s: %s");
+  args[1] = Vinhibit_quit;
+  args[2] = data;
+  Fmessage (3, args);
   return Fset (Vinhibit_quit, Qnil);
 }
 
@@ -2919,13 +2943,13 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu)
     {
       Lisp_Object posn;
 
-      posn = POSN_BUFFER_POSN (EVENT_START (c));
+      posn = POSN_POSN (EVENT_START (c));
       /* Handle menu-bar events:
         insert the dummy prefix event `menu-bar'.  */
       if (EQ (posn, Qmenu_bar) || EQ (posn, Qtool_bar))
        {
          /* Change menu-bar to (menu-bar) as the event "position".  */
-         POSN_BUFFER_SET_POSN (EVENT_START (c), Fcons (posn, Qnil));
+         POSN_SET_POSN (EVENT_START (c), Fcons (posn, Qnil));
 
          also_record = c;
          Vunread_command_events = Fcons (c, Vunread_command_events);
@@ -3712,6 +3736,7 @@ discard_mouse_events ()
        sp = kbd_buffer;
 
       if (sp->kind == MOUSE_CLICK_EVENT
+         || sp->kind == WHEEL_EVENT
 #ifdef WINDOWSNT
          || sp->kind == W32_SCROLL_BAR_CLICK_EVENT
 #endif
@@ -4000,14 +4025,6 @@ kbd_buffer_get_event (kbp, used_mouse_menu)
              && !EQ (frame, selected_frame))
            obj = make_lispy_switch_frame (frame);
          internal_last_event_frame = frame;
-         kbd_fetch_ptr = event + 1;
-       }
-      else if (event->kind == SELECT_WINDOW_EVENT)
-       {
-         /* Make an event (select-window (WINDOW)).  */
-         obj = Fcons (event->frame_or_window, Qnil);
-         obj = Fcons (Qselect_window, Fcons (obj, Qnil));
-
          kbd_fetch_ptr = event + 1;
        }
       else
@@ -4419,9 +4436,7 @@ timer_check (do_it_now)
 static Lisp_Object accent_key_syms;
 static Lisp_Object func_key_syms;
 static Lisp_Object mouse_syms;
-#if defined(WINDOWSNT) || defined(MAC_OSX)
-static Lisp_Object mouse_wheel_syms;
-#endif
+static Lisp_Object wheel_syms;
 static Lisp_Object drag_n_drop_syms;
 
 /* This is a list of keysym codes for special "accent" characters.
@@ -4875,21 +4890,11 @@ static char *iso_lispy_function_keys[] =
 
 Lisp_Object Vlispy_mouse_stem;
 
-#if defined(WINDOWSNT) || defined(MAC_OSX)
-/* mouse-wheel events are generated by the wheel on devices such as
-   the MS Intellimouse.  The wheel sits in between the left and right
-   mouse buttons, and is typically used to scroll or zoom the window
-   underneath the pointer.  mouse-wheel events specify the object on
-   which they operate, and a delta corresponding to the amount and
-   direction that the wheel is rotated.  Clicking the mouse-wheel
-   generates a mouse-2 event.  */
-static char *lispy_mouse_wheel_names[] =
-{
-  "mouse-wheel"
+static char *lispy_wheel_names[] =
+{
+  "wheel-up", "wheel-down"
 };
 
-#endif /* WINDOWSNT */
-
 /* drag-n-drop events are generated when a set of selected files are
    dragged from another application and dropped onto an Emacs window.  */
 static char *lispy_drag_n_drop_names[] =
@@ -4952,6 +4957,169 @@ EMACS_INT double_click_fuzz;
 
 int double_click_count;
 
+/* Return position of a mouse click or wheel event */
+
+static Lisp_Object
+make_lispy_position (f, x, y, time)
+     struct frame *f;
+     Lisp_Object *x, *y;
+     unsigned long time;
+{
+  Lisp_Object window;
+  enum window_part part;
+  Lisp_Object posn = Qnil;
+  Lisp_Object extra_info = Qnil;
+  int wx, wy;
+
+  /* Set `window' to the window under frame pixel coordinates (x,y)  */
+  if (f)
+    window = window_from_coordinates (f, XINT (*x), XINT (*y),
+                                     &part, &wx, &wy, 0);
+  else
+    window = Qnil;
+
+  if (WINDOWP (window))
+    {
+      /* It's a click in window window at frame coordinates (x,y)  */
+      struct window *w = XWINDOW (window);
+      Lisp_Object string_info = Qnil;
+      int textpos = -1, rx = -1, ry = -1;
+      int dx = -1, dy = -1;
+      int width = -1, height = -1;
+      Lisp_Object object = Qnil;
+
+      /* Set event coordinates to window-relative coordinates
+        for constructing the Lisp event below.  */
+      XSETINT (*x, wx);
+      XSETINT (*y, wy);
+
+      if (part == ON_MODE_LINE || part == ON_HEADER_LINE)
+       {
+         /* Mode line or header line.  Look for a string under
+            the mouse that may have a `local-map' property.  */
+         Lisp_Object string;
+         int charpos;
+
+         posn = part == ON_MODE_LINE ? Qmode_line : Qheader_line;
+         rx = wx, ry = wy;
+         string = mode_line_string (w, part, &rx, &ry, &charpos,
+                                    &object, &dx, &dy, &width, &height);
+         if (STRINGP (string))
+           string_info = Fcons (string, make_number (charpos));
+         if (w == XWINDOW (selected_window))
+           textpos = PT;
+         else
+           textpos = XMARKER (w->pointm)->charpos;
+       }
+      else if (part == ON_VERTICAL_BORDER)
+       {
+         posn = Qvertical_line;
+         wx = -1;
+         dx = 0;
+         width = 1;
+       }
+      else if (part == ON_LEFT_MARGIN || part == ON_RIGHT_MARGIN)
+       {
+         Lisp_Object string;
+         int charpos;
+         
+         posn = (part == ON_LEFT_MARGIN) ? Qleft_margin : Qright_margin;
+         rx = wx, ry = wy;
+         string = marginal_area_string (w, part, &rx, &ry, &charpos,
+                                        &object, &dx, &dy, &width, &height);
+         if (STRINGP (string))
+           string_info = Fcons (string, make_number (charpos));
+       }
+      else if (part == ON_LEFT_FRINGE || part == ON_RIGHT_FRINGE)
+       {
+         posn = (part == ON_LEFT_FRINGE) ? Qleft_fringe : Qright_fringe;
+         rx = 0;
+         dx = wx;
+         if (part == ON_RIGHT_FRINGE)
+           dx -= (window_box_width (w, LEFT_MARGIN_AREA)
+                  + window_box_width (w, TEXT_AREA)
+                  + (WINDOW_HAS_FRINGES_OUTSIDE_MARGINS (w)
+                     ? window_box_width (w, RIGHT_MARGIN_AREA)
+                     : 0));
+         else if (!WINDOW_HAS_FRINGES_OUTSIDE_MARGINS (w))
+           dx -= window_box_width (w, LEFT_MARGIN_AREA);
+       }
+
+      if (textpos < 0)
+       {
+         Lisp_Object string2, object2 = Qnil;
+         struct display_pos p;
+         int dx2, dy2;
+         int width2, height2;
+         wx = max (WINDOW_LEFT_MARGIN_WIDTH (w), wx);
+         string2 = buffer_posn_from_coords (w, &wx, &wy, &p,
+                                            &object2, &dx2, &dy2,
+                                            &width2, &height2);
+         textpos = CHARPOS (p.pos);
+         if (rx < 0) rx = wx;
+         if (ry < 0) ry = wy;
+         if (dx < 0) dx = dx2;
+         if (dy < 0) dy = dy2;
+         if (width < 0) width = width2;
+         if (height < 0) height = height2;
+
+         if (NILP (posn))
+           {
+             posn = make_number (textpos);
+             if (STRINGP (string2))
+               string_info = Fcons (string2,
+                                    make_number (CHARPOS (p.string_pos)));
+           }
+         if (NILP (object))
+           object = object2;
+       }
+
+#ifdef HAVE_WINDOW_SYSTEM
+      if (IMAGEP (object))
+       {
+         Lisp_Object image_map, hotspot;
+         if ((image_map = Fplist_get (XCDR (object), QCmap),
+              !NILP (image_map))
+             && (hotspot = find_hot_spot (image_map, dx, dy),
+                 CONSP (hotspot))
+             && (hotspot = XCDR (hotspot), CONSP (hotspot)))
+           posn = XCAR (hotspot);
+       }
+#endif
+
+      /* Object info */
+      extra_info = Fcons (object,
+                         Fcons (Fcons (make_number (dx),
+                                       make_number (dy)),
+                                Fcons (Fcons (make_number (width),
+                                              make_number (height)),
+                                       Qnil)));
+
+      /* String info */
+      extra_info = Fcons (string_info,
+                         Fcons (make_number (textpos),
+                                Fcons (Fcons (make_number (rx),
+                                              make_number (ry)),
+                                       extra_info)));
+    }
+  else if (f != 0)
+    {
+      XSETFRAME (window, f);
+    }
+  else
+    {
+      window = Qnil;
+      XSETFASTINT (*x, 0);
+      XSETFASTINT (*y, 0);
+    }
+
+  return Fcons (window,
+               Fcons (posn,
+                      Fcons (Fcons (*x, *y),
+                             Fcons (make_number (time),
+                                    extra_info))));
+}
+
 /* 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.
@@ -4996,8 +5164,17 @@ make_lispy_event (event)
     case MULTIBYTE_CHAR_KEYSTROKE_EVENT:
       {
        Lisp_Object lispy_c;
+       int c = event->code;
 
-       XSETFASTINT (lispy_c, event->code);
+       /* 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 | ctrl_modifier));
+       /* What about the `shift' modifier ?  */
+       button_down_time = 0;
+       XSETFASTINT (lispy_c, c);
        return lispy_c;
       }
 
@@ -5077,24 +5254,23 @@ make_lispy_event (event)
        Lisp_Object position;
        Lisp_Object *start_pos_ptr;
        Lisp_Object start_pos;
-       Lisp_Object window;
 
        position = Qnil;
 
        /* Build the position as appropriate for this mouse click.  */
        if (event->kind == MOUSE_CLICK_EVENT)
          {
-           enum window_part part;
            struct frame *f = XFRAME (event->frame_or_window);
-           Lisp_Object posn;
-           Lisp_Object string_info = Qnil;
+#if ! defined (USE_X_TOOLKIT) && ! defined (USE_GTK)
            int row, column;
+#endif
 
            /* Ignore mouse events that were made on frame that
               have been deleted.  */
            if (! FRAME_LIVE_P (f))
              return Qnil;
 
+#if ! defined (USE_X_TOOLKIT) && ! defined (USE_GTK)
            /* EVENT->x and EVENT->y are frame-relative pixel
               coordinates at this place.  Under old redisplay, COLUMN
               and ROW are set to frame relative glyph coordinates
@@ -5103,7 +5279,6 @@ make_lispy_event (event)
            pixel_to_glyph_coords (f, XINT (event->x), XINT (event->y),
                                   &column, &row, NULL, 1);
 
-#if ! defined (USE_X_TOOLKIT) && ! defined (USE_GTK)
            /* In the non-toolkit version, clicks on the menu bar
               are ordinary button events in the event buffer.
               Distinguish them, and invoke the menu.
@@ -5157,81 +5332,14 @@ make_lispy_event (event)
              }
 #endif /* not USE_X_TOOLKIT && not USE_GTK */
 
-           /* Set `window' to the window under frame pixel coordinates
-              event->x/event->y.  */
-           window = window_from_coordinates (f, XINT (event->x),
-                                             XINT (event->y), &part, 0);
-
-           if (!WINDOWP (window))
-             {
-               window = event->frame_or_window;
-               posn = Qnil;
-             }
-           else
-             {
-               /* It's a click in window window at frame coordinates
-                  event->x/ event->y.  */
-               struct window *w = XWINDOW (window);
-
-               /* Get window relative coordinates.  Original code
-                  `rounded' this to glyph boundaries.  */
-               int wx = FRAME_TO_WINDOW_PIXEL_X (w, XINT (event->x));
-               int wy = FRAME_TO_WINDOW_PIXEL_Y (w, XINT (event->y));
-
-               /* Set event coordinates to window-relative coordinates
-                  for constructing the Lisp event below.  */
-               XSETINT (event->x, wx);
-               XSETINT (event->y, wy);
-
-               if (part == ON_MODE_LINE || part == ON_HEADER_LINE)
-                 {
-                   /* Mode line or header line.  Look for a string under
-                      the mouse that may have a `local-map' property.  */
-                   Lisp_Object string;
-                   int charpos;
-
-                   posn = part == ON_MODE_LINE ? Qmode_line : Qheader_line;
-                   string = mode_line_string (w, wx, wy, part, &charpos);
-                   if (STRINGP (string))
-                     string_info = Fcons (string, make_number (charpos));
-                 }
-               else if (part == ON_VERTICAL_BORDER)
-                 posn = Qvertical_line;
-               else if (part == ON_LEFT_MARGIN || part == ON_RIGHT_MARGIN)
-                 {
-                   int charpos;
-                   Lisp_Object object = marginal_area_string (w, wx, wy, part,
-                                                              &charpos);
-                   posn = (part == ON_LEFT_MARGIN) ? Qleft_margin : Qright_margin;
-                   if (STRINGP (object))
-                     string_info = Fcons (object, make_number (charpos));
-                 }
-               else
-                 {
-                   Lisp_Object object;
-                   struct display_pos p;
-                   buffer_posn_from_coords (w, &wx, &wy, &object, &p);
-                   posn = make_number (CHARPOS (p.pos));
-                   if (STRINGP (object))
-                     string_info
-                       = Fcons (object,
-                                make_number (CHARPOS (p.string_pos)));
-                 }
-             }
-
-           position
-             = Fcons (window,
-                      Fcons (posn,
-                             Fcons (Fcons (event->x, event->y),
-                                    Fcons (make_number (event->timestamp),
-                                           (NILP (string_info)
-                                            ? Qnil
-                                            : Fcons (string_info, Qnil))))));
+           position = make_lispy_position (f, &event->x, &event->y,
+                                           event->timestamp);
          }
 #ifndef USE_TOOLKIT_SCROLL_BARS
        else
          {
            /* It's a scrollbar click.  */
+           Lisp_Object window;
            Lisp_Object portion_whole;
            Lisp_Object part;
 
@@ -5393,6 +5501,113 @@ make_lispy_event (event)
        }
       }
 
+    case WHEEL_EVENT:
+      {
+       Lisp_Object position;
+       Lisp_Object head;
+       
+       /* Build the position as appropriate for this mouse click.  */
+       struct frame *f = XFRAME (event->frame_or_window);
+
+       /* Ignore wheel events that were made on frame that have been
+          deleted.  */
+       if (! FRAME_LIVE_P (f))
+         return Qnil;
+
+       position = make_lispy_position (f, &event->x, &event->y,
+                                       event->timestamp);
+
+       /* Set double or triple modifiers to indicate the wheel speed.  */
+       {
+         /* On window-system frames, use the value of
+            double-click-fuzz as is.  On other frames, interpret it
+            as a multiple of 1/8 characters.  */
+         struct frame *f;
+         int fuzz;
+         int is_double;
+
+         if (WINDOWP (event->frame_or_window))
+           f = XFRAME (XWINDOW (event->frame_or_window)->frame);
+         else if (FRAMEP (event->frame_or_window))
+           f = XFRAME (event->frame_or_window);
+         else
+           abort ();
+
+         if (FRAME_WINDOW_P (f))
+           fuzz = double_click_fuzz;
+         else
+           fuzz = double_click_fuzz / 8;
+
+         is_double = (last_mouse_button < 0
+                      && (abs (XINT (event->x) - last_mouse_x) <= fuzz)
+                      && (abs (XINT (event->y) - last_mouse_y) <= fuzz)
+                      && button_down_time != 0
+                      && (EQ (Vdouble_click_time, Qt)
+                          || (INTEGERP (Vdouble_click_time)
+                              && ((int)(event->timestamp - button_down_time)
+                                  < XINT (Vdouble_click_time)))));
+         if (is_double)
+           {
+             double_click_count++;
+             event->modifiers |= ((double_click_count > 2)
+                                  ? triple_modifier
+                                  : double_modifier);
+           }
+         else
+           {
+             double_click_count = 1;
+             event->modifiers |= click_modifier;
+           }
+
+         button_down_time = event->timestamp;
+         /* Use a negative value to distinguish wheel from mouse button.  */
+         last_mouse_button = -1;
+         last_mouse_x = XINT (event->x);
+         last_mouse_y = XINT (event->y);
+       }
+
+       {
+         int symbol_num;
+
+         if (event->modifiers & up_modifier)
+           {
+             /* Emit a wheel-up event.  */
+             event->modifiers &= ~up_modifier;
+             symbol_num = 0;
+           }
+         else if (event->modifiers & down_modifier)
+           {
+             /* Emit a wheel-down event.  */
+             event->modifiers &= ~down_modifier;
+             symbol_num = 1;
+           }
+         else
+           /* Every wheel event should either have the down_modifier or
+              the up_modifier set.  */
+           abort ();
+
+         /* Get the symbol we should use for the wheel event.  */
+         head = modify_event_symbol (symbol_num,
+                                     event->modifiers,
+                                     Qmouse_click,
+                                     Qnil,
+                                     lispy_wheel_names,
+                                     &wheel_syms,
+                                     ASIZE (wheel_syms));
+       }
+
+       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,
+                              Qnil));
+      }
+
+
 #ifdef USE_TOOLKIT_SCROLL_BARS
 
       /* We don't have down and up events if using toolkit scroll bars,
@@ -5491,86 +5706,11 @@ make_lispy_event (event)
        }
       }
 #endif /* WINDOWSNT */
-#if defined(WINDOWSNT) || defined(MAC_OSX)
-    case MOUSE_WHEEL_EVENT:
-      {
-       enum window_part part;
-       FRAME_PTR f = XFRAME (event->frame_or_window);
-       Lisp_Object window;
-       Lisp_Object posn;
-       Lisp_Object head, position;
-       int row, column;
-
-       /* Ignore mouse events that were made on frame that
-          have been deleted.  */
-       if (! FRAME_LIVE_P (f))
-         return Qnil;
-       pixel_to_glyph_coords (f, XINT (event->x), XINT (event->y),
-                              &column, &row, NULL, 1);
-       window = window_from_coordinates (f, XINT (event->x),
-                                          XINT (event->y), &part, 0);
-
-       if (!WINDOWP (window))
-         {
-           window = event->frame_or_window;
-           posn = Qnil;
-         }
-       else
-         {
-           int pixcolumn, pixrow;
-           column -= XINT (XWINDOW (window)->left);
-           row -= XINT (XWINDOW (window)->top);
-           glyph_to_pixel_coords (XWINDOW(window), column, row,
-                                   &pixcolumn, &pixrow);
-           XSETINT (event->x, pixcolumn);
-           XSETINT (event->y, pixrow);
-
-           if (part == ON_MODE_LINE)
-             posn = Qmode_line;
-           else if (part == ON_VERTICAL_BORDER)
-             posn = Qvertical_line;
-           else if (part == ON_HEADER_LINE)
-             posn = Qheader_line;
-           else
-             {
-               Lisp_Object object;
-               struct display_pos p;
-               buffer_posn_from_coords (XWINDOW (window), &column, &row,
-                                        &object, &p);
-               posn = make_number (CHARPOS (p.pos));
-             }
-         }
-
-       {
-         Lisp_Object head, position;
-
-         position
-           = Fcons (window,
-                    Fcons (posn,
-                           Fcons (Fcons (event->x, event->y),
-                                  Fcons (make_number (event->timestamp),
-                                         Qnil))));
-
-         head = modify_event_symbol (0, event->modifiers,
-                                     Qmouse_wheel, Qnil,
-                                     lispy_mouse_wheel_names,
-                                     &mouse_wheel_syms, 1);
-         return Fcons (head,
-                       Fcons (position,
-                              /* Insert 1 here so event-click-count works.  */
-                              Fcons (make_number (1),
-                                     Fcons (make_number (event->code),
-                                            Qnil))));
-       }
-      }
-#endif /* WINDOWSNT || MAC_OSX */
 
     case DRAG_N_DROP_EVENT:
       {
-       enum window_part part;
        FRAME_PTR f;
-       Lisp_Object window;
-       Lisp_Object posn;
+       Lisp_Object head, position;
        Lisp_Object files;
 
        /* The frame_or_window field should be a cons of the frame in
@@ -5587,63 +5727,17 @@ make_lispy_event (event)
        if (! FRAME_LIVE_P (f))
          return Qnil;
 
-       window = window_from_coordinates (f, XINT (event->x),
-                                          XINT (event->y), &part, 0);
-
-       if (!WINDOWP (window))
-         {
-           window = XCAR (event->frame_or_window);
-           posn = Qnil;
-         }
-       else
-         {
-           /* It's an event in window `window' at frame coordinates
-              event->x/ event->y.  */
-           struct window *w = XWINDOW (window);
-
-           /* Get window relative coordinates.  */
-           int wx = FRAME_TO_WINDOW_PIXEL_X (w, XINT (event->x));
-           int wy = FRAME_TO_WINDOW_PIXEL_Y (w, XINT (event->y));
-
-           /* Set event coordinates to window-relative coordinates
-              for constructing the Lisp event below.  */
-           XSETINT (event->x, wx);
-           XSETINT (event->y, wy);
-
-           if (part == ON_MODE_LINE)
-             posn = Qmode_line;
-           else if (part == ON_VERTICAL_BORDER)
-             posn = Qvertical_line;
-           else if (part == ON_HEADER_LINE)
-             posn = Qheader_line;
-           else
-             {
-               Lisp_Object object;
-               struct display_pos p;
-               buffer_posn_from_coords (w, &wx, &wy, &object, &p);
-               posn = make_number (CHARPOS (p.pos));
-             }
-         }
-
-       {
-         Lisp_Object head, position;
-
-         position
-           = Fcons (window,
-                    Fcons (posn,
-                           Fcons (Fcons (event->x, event->y),
-                                  Fcons (make_number (event->timestamp),
-                                         Qnil))));
-
-         head = modify_event_symbol (0, event->modifiers,
-                                     Qdrag_n_drop, Qnil,
-                                     lispy_drag_n_drop_names,
-                                     &drag_n_drop_syms, 1);
-         return Fcons (head,
-                       Fcons (position,
-                              Fcons (files,
-                                     Qnil)));
-       }
+       position = make_lispy_position (f, &event->x, &event->y,
+                                       event->timestamp);
+
+       head = modify_event_symbol (0, event->modifiers,
+                                   Qdrag_n_drop, Qnil,
+                                   lispy_drag_n_drop_names,
+                                   &drag_n_drop_syms, 1);
+       return Fcons (head,
+                     Fcons (position,
+                            Fcons (files,
+                                   Qnil)));
       }
 #endif /* HAVE_MOUSE */
 
@@ -5658,6 +5752,12 @@ make_lispy_event (event)
       return event->arg;
 #endif
 
+    case SELECT_WINDOW_EVENT:
+      /* Make an event (select-window (WINDOW)).  */
+      return Fcons (Qselect_window,
+                   Fcons (Fcons (event->frame_or_window, Qnil),
+                          Qnil));
+
     case TOOL_BAR_EVENT:
       if (EQ (event->arg, event->frame_or_window))
        /* This is the prefix key.  We translate this to
@@ -5710,60 +5810,12 @@ make_lispy_movement (frame, bar_window, part, x, y, time)
   /* Or is it an ordinary mouse movement?  */
   else
     {
-      enum window_part area;
-      Lisp_Object window;
-      Lisp_Object posn;
+      Lisp_Object position;
 
-      if (frame)
-       /* It's in a frame; which window on that frame?  */
-       window = window_from_coordinates (frame, XINT (x), XINT (y), &area, 0);
-      else
-       window = Qnil;
-
-      if (WINDOWP (window))
-       {
-         struct window *w = XWINDOW (window);
-         int wx, wy;
-
-         /* Get window relative coordinates.  */
-         wx = FRAME_TO_WINDOW_PIXEL_X (w, XINT (x));
-         wy = FRAME_TO_WINDOW_PIXEL_Y (w, XINT (y));
-         XSETINT (x, wx);
-         XSETINT (y, wy);
-
-         if (area == ON_MODE_LINE)
-           posn = Qmode_line;
-         else if (area == ON_VERTICAL_BORDER)
-           posn = Qvertical_line;
-         else if (area == ON_HEADER_LINE)
-           posn = Qheader_line;
-         else
-           {
-             Lisp_Object object;
-             struct display_pos p;
-             buffer_posn_from_coords (w, &wx, &wy, &object, &p);
-             posn = make_number (CHARPOS (p.pos));
-           }
-       }
-      else if (frame != 0)
-       {
-         XSETFRAME (window, frame);
-         posn = Qnil;
-       }
-      else
-       {
-         window = Qnil;
-         posn = Qnil;
-         XSETFASTINT (x, 0);
-         XSETFASTINT (y, 0);
-       }
+      position = make_lispy_position (frame, &x, &y, time);
 
       return Fcons (Qmouse_movement,
-                   Fcons (Fcons (window,
-                                 Fcons (posn,
-                                        Fcons (Fcons (x, y),
-                                               Fcons (make_number (time),
-                                                      Qnil)))),
+                   Fcons (position,
                           Qnil));
     }
 }
@@ -5841,6 +5893,26 @@ parse_modifiers_uncached (symbol, modifier_end)
          break;
 
 #undef SINGLE_LETTER_MOD
+
+#define MULTI_LETTER_MOD(BIT, NAME, LEN)                       \
+         if (i + LEN + 1 <= SBYTES (name)                      \
+             && ! strncmp (SDATA (name) + i, NAME, LEN))       \
+           {                                                   \
+             this_mod_end = i + LEN;                           \
+             this_mod = BIT;                                   \
+           }
+
+       case 'd':
+         MULTI_LETTER_MOD (drag_modifier, "drag", 4);
+         MULTI_LETTER_MOD (down_modifier, "down", 4);
+         MULTI_LETTER_MOD (double_modifier, "double", 6);
+         break;
+
+       case 't':
+         MULTI_LETTER_MOD (triple_modifier, "triple", 6);
+         break;
+#undef MULTI_LETTER_MOD
+
        }
 
       /* If we found no modifier, stop looking for them.  */
@@ -5981,7 +6053,7 @@ parse_modifiers (symbol)
                                         SBYTES (SYMBOL_NAME (symbol)) - end),
                            Qnil);
 
-      if (modifiers & ~VALMASK)
+      if (modifiers & ~INTMASK)
        abort ();
       XSETFASTINT (mask, modifiers);
       elements = Fcons (unmodified, Fcons (mask, Qnil));
@@ -6018,7 +6090,7 @@ 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 &= VALMASK;
+  modifiers &= INTMASK;
 
   /* The click modifier never figures into cache indices.  */
   cache = Fget (base, Qmodifier_cache);
@@ -6039,12 +6111,15 @@ apply_modifiers (modifiers, base)
       entry = Fcons (index, new_symbol);
       Fput (base, Qmodifier_cache, Fcons (entry, cache));
 
-      /* We have the parsing info now for free, so add it to the caches.  */
-      XSETFASTINT (index, modifiers);
-      Fput (new_symbol, Qevent_symbol_element_mask,
-           Fcons (base, Fcons (index, Qnil)));
-      Fput (new_symbol, Qevent_symbol_elements,
-           Fcons (base, lispy_modifier_list (modifiers)));
+      /* We have the parsing info now for free, so we could add it to
+        the caches:
+         XSETFASTINT (index, modifiers);
+         Fput (new_symbol, Qevent_symbol_element_mask,
+               Fcons (base, Fcons (index, Qnil)));
+         Fput (new_symbol, Qevent_symbol_elements,
+               Fcons (base, lispy_modifier_list (modifiers)));
+        Sadly, this is only correct if `base' is indeed a base event,
+        which is not necessarily the case.  -stef  */
     }
 
   /* Make sure this symbol is of the same kind as BASE.
@@ -6479,6 +6554,7 @@ record_asynch_buffer_change ()
 {
   struct input_event event;
   Lisp_Object tem;
+  EVENT_INIT (event);
 
   event.kind = BUFFER_SWITCH_EVENT;
   event.frame_or_window = Qnil;
@@ -6537,6 +6613,9 @@ read_avail_input (expected)
   register int i;
   int nread;
 
+  for (i = 0; i < KBD_BUFFER_SIZE; i++)
+    EVENT_INIT (buf[i]);
+
   if (read_socket_hook)
     /* No need for FIONREAD or fcntl; just say don't wait.  */
     nread = (*read_socket_hook) (input_fd, buf, KBD_BUFFER_SIZE, expected);
@@ -6566,7 +6645,12 @@ read_avail_input (expected)
        /* ??? Is it really right to send the signal just to this process
           rather than to the whole process group?
           Perhaps on systems with FIONREAD Emacs is alone in its group.  */
-       kill (getpid (), SIGHUP);
+       {
+         if (! noninteractive)
+           kill (getpid (), SIGHUP);
+         else
+           n_to_read = 0;
+       }
       if (n_to_read == 0)
        return 0;
       if (n_to_read > sizeof cbuf)
@@ -6663,18 +6747,39 @@ read_avail_input (expected)
 }
 #endif /* not VMS */
 \f
+void
+handle_async_input ()
+{
+#ifdef BSD4_1
+  extern int select_alarmed;
+#endif
+  interrupt_input_pending = 0;
+
+  while (1)
+    {
+      int nread;
+      nread = read_avail_input (1);
+      /* -1 means it's not ok to read the input now.
+        UNBLOCK_INPUT will read it later; now, avoid infinite loop.
+        0 means there was no keyboard input available.  */
+      if (nread <= 0)
+       break;
+
+#ifdef BSD4_1
+      select_alarmed = 1;  /* Force the select emulator back to life */
+#endif
+    }
+}
+
 #ifdef SIGIO   /* for entire page */
 /* Note SIGIO has been undef'd if FIONREAD is missing.  */
 
-SIGTYPE
+static SIGTYPE
 input_available_signal (signo)
      int signo;
 {
   /* Must preserve main program's value of errno.  */
   int old_errno = errno;
-#ifdef BSD4_1
-  extern int select_alarmed;
-#endif
 
 #if defined (USG) && !defined (POSIX_SIGNALS)
   /* USG systems forget handlers when they are used;
@@ -6689,20 +6794,11 @@ input_available_signal (signo)
   if (input_available_clear_time)
     EMACS_SET_SECS_USECS (*input_available_clear_time, 0, 0);
 
-  while (1)
-    {
-      int nread;
-      nread = read_avail_input (1);
-      /* -1 means it's not ok to read the input now.
-        UNBLOCK_INPUT will read it later; now, avoid infinite loop.
-        0 means there was no keyboard input available.  */
-      if (nread <= 0)
-       break;
-
-#ifdef BSD4_1
-      select_alarmed = 1;  /* Force the select emulator back to life */
+#ifdef SYNC_INPUT
+  interrupt_input_pending = 1;
+#else
+  handle_async_input ();
 #endif
-    }
 
 #ifdef BSD4_1
   sigfree ();
@@ -6721,14 +6817,14 @@ void
 reinvoke_input_signal ()
 {
 #ifdef SIGIO
-  kill (getpid (), SIGIO);
+  handle_async_input ();
 #endif
 }
 
 
 \f
-static void menu_bar_item P_ ((Lisp_Object, Lisp_Object));
-static void menu_bar_one_keymap P_ ((Lisp_Object));
+static void menu_bar_item P_ ((Lisp_Object, Lisp_Object, Lisp_Object, void*));
+static Lisp_Object menu_bar_one_keymap_changed_items;
 
 /* These variables hold the vector under construction within
    menu_bar_items and its subroutines, and the current index
@@ -6832,7 +6928,10 @@ menu_bar_items (old)
        def = get_keymap (access_keymap (maps[mapno], Qmenu_bar, 1, 0, 1),
                          0, 1);
        if (CONSP (def))
-         menu_bar_one_keymap (def);
+         {
+           menu_bar_one_keymap_changed_items = Qnil;
+           map_keymap (def, menu_bar_item, Qnil, NULL, 1);
+         }
       }
 
   /* Move to the end those items that should be at the end.  */
@@ -6886,48 +6985,15 @@ menu_bar_items (old)
   return menu_bar_items_vector;
 }
 \f
-/* Scan one map KEYMAP, accumulating any menu items it defines
-   in menu_bar_items_vector.  */
-
-static Lisp_Object menu_bar_one_keymap_changed_items;
-
-static void
-menu_bar_one_keymap (keymap)
-     Lisp_Object keymap;
-{
-  Lisp_Object tail, item;
-
-  menu_bar_one_keymap_changed_items = Qnil;
-
-  /* Loop over all keymap entries that have menu strings.  */
-  for (tail = keymap; CONSP (tail); tail = XCDR (tail))
-    {
-      item = XCAR (tail);
-      if (CONSP (item))
-       menu_bar_item (XCAR (item), XCDR (item));
-      else if (VECTORP (item))
-       {
-         /* Loop over the char values represented in the vector.  */
-         int len = XVECTOR (item)->size;
-         int c;
-         for (c = 0; c < len; c++)
-           {
-             Lisp_Object character;
-             XSETFASTINT (character, c);
-             menu_bar_item (character, XVECTOR (item)->contents[c]);
-           }
-       }
-    }
-}
-
 /* Add one item to menu_bar_items_vector, for KEY, ITEM_STRING and DEF.
    If there's already an item for KEY, add this DEF to it.  */
 
 Lisp_Object item_properties;
 
 static void
-menu_bar_item (key, item)
-     Lisp_Object key, item;
+menu_bar_item (key, item, dummy1, dummy2)
+     Lisp_Object key, item, dummy1;
+     void *dummy2;
 {
   struct gcpro gcpro1;
   int i;
@@ -7000,7 +7066,10 @@ menu_bar_item (key, item)
     {
       Lisp_Object old;
       old = XVECTOR (menu_bar_items_vector)->contents[i + 2];
-      XVECTOR (menu_bar_items_vector)->contents[i + 2] = Fcons (item, old);
+      /* If the new and the old items are not both keymaps,
+        the lookup will only find `item'.  */
+      item = Fcons (item, KEYMAPP (item) && KEYMAPP (XCAR (old)) ? old : Qnil);
+      XVECTOR (menu_bar_items_vector)->contents[i + 2] = item;
     }
 }
 \f
@@ -7901,7 +7970,7 @@ read_char_minibuf_menu_prompt (commandflag, nmaps, maps)
   register Lisp_Object name;
   int nlength;
   /* FIXME: Use the minibuffer's frame width.  */
-  int width = FRAME_WIDTH (SELECTED_FRAME ()) - 4;
+  int width = FRAME_COLS (SELECTED_FRAME ()) - 4;
   int idx = -1;
   int nobindings = 1;
   Lisp_Object rest, vector;
@@ -8163,20 +8232,13 @@ follow_key (key, nmaps, current, defs, next)
      int nmaps;
 {
   int i, first_binding;
-  int did_meta = 0;
 
   first_binding = nmaps;
   for (i = nmaps - 1; i >= 0; i--)
     {
       if (! NILP (current[i]))
        {
-         Lisp_Object map;
-         if (did_meta)
-           map = defs[i];
-         else
-           map = current[i];
-
-         defs[i] = access_keymap (map, key, 1, 0, 1);
+         defs[i] = access_keymap (current[i], key, 1, 0, 1);
          if (! NILP (defs[i]))
            first_binding = i;
        }
@@ -8196,10 +8258,123 @@ follow_key (key, nmaps, current, defs, next)
    such as Vfunction_key_map and Vkey_translation_map.  */
 typedef struct keyremap
 {
-  Lisp_Object map;
+  Lisp_Object map, parent;
   int start, end;
 } keyremap;
 
+/* Lookup KEY in MAP.
+   MAP is a keymap mapping keys to key vectors or functions.
+   If the mapping is a function and DO_FUNCTION is non-zero, then
+   the function is called with PROMPT as parameter and its return
+   value is used as the return value of this function (after checking
+   that it is indeed a vector).  */
+
+static Lisp_Object
+access_keymap_keyremap (map, key, prompt, do_funcall)
+     Lisp_Object map, key, prompt;
+     int do_funcall;
+{
+  Lisp_Object next;
+  
+  next = access_keymap (map, key, 1, 0, 1);
+
+  /* Handle symbol with autoload definition.  */
+  if (SYMBOLP (next) && !NILP (Ffboundp (next))
+      && CONSP (XSYMBOL (next)->function)
+      && EQ (XCAR (XSYMBOL (next)->function), Qautoload))
+    do_autoload (XSYMBOL (next)->function, next);
+
+  /* Handle a symbol whose function definition is a keymap
+     or an array.  */
+  if (SYMBOLP (next) && !NILP (Ffboundp (next))
+      && (!NILP (Farrayp (XSYMBOL (next)->function))
+         || KEYMAPP (XSYMBOL (next)->function)))
+    next = XSYMBOL (next)->function;
+           
+  /* If the keymap gives a function, not an
+     array, then call the function with one arg and use
+     its value instead.  */
+  if (SYMBOLP (next) && !NILP (Ffboundp (next)) && do_funcall)
+    {
+      Lisp_Object tem;
+      tem = next;
+
+      next = call1 (next, prompt);
+      /* 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 (next) || STRINGP (next)))
+       error ("Function %s returns invalid key sequence", tem);
+    }
+  return next;
+}
+
+/* Do one step of the key remapping used for function-key-map and
+   key-translation-map:
+   KEYBUF is the buffer holding the input events.
+   BUFSIZE is its maximum size.
+   FKEY is a pointer to the keyremap structure to use.
+   INPUT is the index of the last element in KEYBUF.
+   DOIT if non-zero says that the remapping can actually take place.
+   DIFF is used to return the number of keys added/removed by the remapping.
+   PARENT is the root of the keymap.
+   PROMPT is the prompt to use if the remapping happens through a function.
+   The return value is non-zero if the remapping actually took place.  */
+
+static int
+keyremap_step (keybuf, bufsize, fkey, input, doit, diff, prompt)
+     Lisp_Object *keybuf, prompt;
+     keyremap *fkey;
+     int input, doit, *diff, bufsize;
+{
+  Lisp_Object next, key;
+
+  key = keybuf[fkey->end++];
+  next = access_keymap_keyremap (fkey->map, key, prompt, doit);
+
+  /* If keybuf[fkey->start..fkey->end] is bound in the
+     map and we're in a position to do the key remapping, replace it with
+     the binding and restart with fkey->start at the end. */
+  if ((VECTORP (next) || STRINGP (next)) && doit)
+    {
+      int len = XFASTINT (Flength (next));
+      int i;
+
+      *diff = len - (fkey->end - fkey->start);
+
+      if (input + *diff >= bufsize)
+       error ("Key sequence too long");
+
+      /* Shift the keys that follow fkey->end.  */
+      if (*diff < 0)
+       for (i = fkey->end; i < input; i++)
+         keybuf[i + *diff] = keybuf[i];
+      else if (*diff > 0)
+       for (i = input - 1; i >= fkey->end; i--)
+         keybuf[i + *diff] = keybuf[i];
+      /* Overwrite the old keys with the new ones.  */
+      for (i = 0; i < len; i++)
+       keybuf[fkey->start + i]
+         = Faref (next, make_number (i));
+
+      fkey->start = fkey->end += *diff;
+      fkey->map = fkey->parent;
+
+      return 1;
+    }
+
+  fkey->map = get_keymap (next, 0, 1);
+
+  /* If we no longer have a bound suffix, try a new position for
+     fkey->start.  */
+  if (!CONSP (fkey->map))
+    {
+      fkey->end = ++fkey->start;
+      fkey->map = fkey->parent;
+    }
+  return 0;
+}
 
 /* Read a sequence of keys that ends with a non prefix character,
    storing it in KEYBUF, a buffer of size BUFSIZE.
@@ -8319,8 +8494,9 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last,
   /* Likewise, for key_translation_map.  */
   volatile keyremap keytran;
 
-  /* 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.  */
+  /* If we receive a `switch-frame' or `select-window' event in the middle of
+     a key sequence, we put it off for later.
+     While we're reading, we keep the event here.  */
   volatile Lisp_Object delayed_switch_frame;
 
   /* See the comment below... */
@@ -8351,8 +8527,8 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last,
   last_nonmenu_event = Qnil;
 
   delayed_switch_frame = Qnil;
-  fkey.map = Vfunction_key_map;
-  keytran.map = Vkey_translation_map;
+  fkey.map = fkey.parent = Vfunction_key_map;
+  keytran.map = keytran.parent = Vkey_translation_map;
   /* If there is no translation-map, turn off scanning.  */
   fkey.start = fkey.end = KEYMAPP (fkey.map) ? 0 : bufsize + 1;
   keytran.start = keytran.end = KEYMAPP (keytran.map) ? 0 : bufsize + 1;
@@ -8491,6 +8667,9 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last,
         just one key.  */
       volatile int echo_local_start, keys_local_start, local_first_binding;
 
+      eassert (fkey.end == t || (fkey.end > t && fkey.end <= mock_input));
+      eassert (fkey.start <= fkey.end);
+      eassert (keytran.start <= keytran.end);
       /* key-translation-map is applied *after* function-key-map.  */
       eassert (keytran.end <= fkey.start);
 
@@ -8505,9 +8684,9 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last,
            keybuf[i - first_unbound - 1] = keybuf[i];
          mock_input = t - first_unbound - 1;
          fkey.end = fkey.start -= first_unbound + 1;
-         fkey.map = Vfunction_key_map;
+         fkey.map = fkey.parent;
          keytran.end = keytran.start -= first_unbound + 1;
-         keytran.map = Vkey_translation_map;
+         keytran.map = keytran.parent;
          goto replay_sequence;
        }
 
@@ -8659,6 +8838,7 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last,
          Vquit_flag = Qnil;
 
          if (EVENT_HAS_PARAMETERS (key)
+             /* Either a `switch-frame' or a `select-window' event.  */
              && EQ (EVENT_HEAD_KIND (EVENT_HEAD (key)), Qswitch_frame))
            {
              /* If we're at the beginning of a key sequence, and the caller
@@ -8692,6 +8872,7 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last,
       if (EVENT_HAS_PARAMETERS (key))
        {
          Lisp_Object kind;
+         Lisp_Object string;
 
          kind = EVENT_HEAD_KIND (EVENT_HEAD (key));
          if (EQ (kind, Qmouse_click))
@@ -8699,7 +8880,7 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last,
              Lisp_Object window, posn;
 
              window = POSN_WINDOW      (EVENT_START (key));
-             posn   = POSN_BUFFER_POSN (EVENT_START (key));
+             posn   = POSN_POSN (EVENT_START (key));
 
              if (CONSP (posn)
                  || (!NILP (fake_prefixed_keys)
@@ -8757,7 +8938,7 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last,
                  localized_local_map = 1;
                  start = EVENT_START (key);
 
-                 if (CONSP (start) && CONSP (XCDR (start)))
+                 if (CONSP (start) && POSN_INBUFFER_P (start))
                    {
                      pos = POSN_BUFFER_POSN (start);
                      if (INTEGERP (pos)
@@ -8808,11 +8989,11 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last,
 
                  /* If on a mode line string with a local keymap,
                     reconsider the key sequence with that keymap.  */
-                 if (CONSP (POSN_STRING (EVENT_START (key))))
+                 if (string = POSN_STRING (EVENT_START (key)),
+                     (CONSP (string) && STRINGP (XCAR (string))))
                    {
-                     Lisp_Object string, pos, map, map2;
+                     Lisp_Object pos, map, map2;
 
-                     string = POSN_STRING (EVENT_START (key));
                      pos = XCDR (string);
                      string = XCAR (string);
                       if (XINT (pos) >= 0
@@ -8831,16 +9012,16 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last,
 
                  goto replay_key;
                }
-             else if (CONSP (POSN_STRING (EVENT_START (key)))
-                      && NILP (from_string))
+             else if (NILP (from_string)
+                      && (string = POSN_STRING (EVENT_START (key)),
+                          (CONSP (string) && STRINGP (XCAR (string)))))
                {
                  /* For a click on a string, i.e. overlay string or a
                     string displayed via the `display' property,
                     consider `local-map' and `keymap' properties of
                     that string.  */
-                 Lisp_Object string, pos, map, map2;
+                 Lisp_Object pos, map, map2;
 
-                 string = POSN_STRING (EVENT_START (key));
                  pos = XCDR (string);
                  string = XCAR (string);
                  if (XINT (pos) >= 0
@@ -8867,7 +9048,7 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last,
            {
              Lisp_Object posn;
 
-             posn = POSN_BUFFER_POSN (EVENT_START (key));
+             posn = POSN_POSN (EVENT_START (key));
              /* Handle menu-bar events:
                 insert the dummy prefix event `menu-bar'.  */
              if (EQ (posn, Qmenu_bar) || EQ (posn, Qtool_bar))
@@ -8879,8 +9060,8 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last,
 
                  /* Zap the position in key, so we know that we've
                     expanded it, and don't try to do so again.  */
-                 POSN_BUFFER_SET_POSN (EVENT_START (key),
-                                       Fcons (posn, Qnil));
+                 POSN_SET_POSN (EVENT_START (key),
+                                Fcons (posn, Qnil));
 
                  mock_input = t + 2;
                  goto replay_sequence;
@@ -8999,6 +9180,27 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last,
                             xterm-mouse-mode.  -stef
 
                             Isn't this just the most wonderful code ever?  */
+
+                         /* If mock_input > t + 1, the above simplification
+                            will actually end up dropping keys on the floor.
+                            This is probably OK for now, but even
+                            if mock_input <= t + 1, we need to adjust fkey
+                            and keytran.
+                            Typical case [header-line down-mouse-N]:
+                            mock_input = 2, t = 1, fkey.end = 1,
+                            last_real_key_start = 0.  */
+                         if (fkey.end > last_real_key_start)
+                           {
+                             fkey.end = fkey.start
+                               = min (last_real_key_start, fkey.start);
+                             fkey.map = fkey.parent;
+                             if (keytran.end > last_real_key_start)
+                               {
+                                 keytran.end = keytran.start
+                                   = min (last_real_key_start, keytran.start);
+                                 keytran.map = keytran.parent;
+                               }
+                           }
                          if (t == last_real_key_start)
                            {
                              mock_input = 0;
@@ -9058,212 +9260,54 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last,
           invariant that keytran.end <= fkey.start).  */
        {
          if (fkey.start < t)
-           (fkey.start = fkey.end = t, fkey.map = Vfunction_key_map);
+           (fkey.start = fkey.end = t, fkey.map = fkey.parent);
        }
       else
        /* If the sequence is unbound, see if we can hang a function key
           off the end of it.  */
-       {
-         Lisp_Object next;
-
-         /* Continue scan from fkey.end until we find a bound suffix.
-            If we fail, increment fkey.start and start over from there.  */
-         while (fkey.end < t)
-           {
-             Lisp_Object key;
-
-             key = keybuf[fkey.end++];
-             next = access_keymap (fkey.map, key, 1, 0, 1);
-
-             /* Handle symbol with autoload definition.  */
-             if (SYMBOLP (next) && ! NILP (Ffboundp (next))
-                 && CONSP (XSYMBOL (next)->function)
-                 && EQ (XCAR (XSYMBOL (next)->function), Qautoload))
-               do_autoload (XSYMBOL (next)->function, next);
-
-             /* Handle a symbol whose function definition is a keymap
-                or an array.  */
-             if (SYMBOLP (next) && ! NILP (Ffboundp (next))
-                 && (!NILP (Farrayp (XSYMBOL (next)->function))
-                     || KEYMAPP (XSYMBOL (next)->function)))
-               next = XSYMBOL (next)->function;
-
-#if 0 /* I didn't turn this on, because it might cause trouble
-        for the mapping of return into C-m and tab into C-i.  */
-             /* Optionally don't map function keys into other things.
-                This enables the user to redefine kp- keys easily.  */
-             if (SYMBOLP (key) && !NILP (Vinhibit_function_key_mapping))
-               next = Qnil;
-#endif
-
-             /* 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 (next) && ! NILP (Ffboundp (next))
-                 /* If there's a binding (i.e. first_binding >= nmaps)
-                    we don't want to apply this function-key-mapping.  */
-                 && fkey.end == t && first_binding >= nmaps)
-               {
-                 struct gcpro gcpro1, gcpro2, gcpro3;
-                 Lisp_Object tem;
-                 tem = next;
-
-                 GCPRO3 (fkey.map, keytran.map, delayed_switch_frame);
-                 next = call1 (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 (next) || STRINGP (next)))
-                   error ("Function in key-translation-map returns invalid key sequence");
-               }
-
-             /* 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 ((VECTORP (next) || STRINGP (next))
-                 /* If there's a binding (i.e. first_binding >= nmaps)
-                    we don't want to apply this function-key-mapping.  */
-                 && fkey.end == t && first_binding >= nmaps)
-               {
-                 int len = XFASTINT (Flength (next));
-
-                 t = fkey.start + len;
-                 if (t >= bufsize)
-                   error ("Key sequence too long");
-
-                 if (VECTORP (next))
-                   bcopy (XVECTOR (next)->contents,
-                          keybuf + fkey.start,
-                          (t - fkey.start) * sizeof (keybuf[0]));
-                 else if (STRINGP (next))
-                   {
-                     int i;
-
-                     for (i = 0; i < len; i++)
-                       XSETFASTINT (keybuf[fkey.start + i], SREF (next, i));
-                   }
-
-                 mock_input = t;
-                 fkey.start = fkey.end = t;
-                 fkey.map = Vfunction_key_map;
-
-                 /* Do pass the results through key-translation-map.
-                    But don't retranslate what key-translation-map
-                    has already translated.  */
-                 keytran.end = keytran.start;
-                 keytran.map = Vkey_translation_map;
-
-                 goto replay_sequence;
-               }
-
-             fkey.map = get_keymap (next, 0, 1);
-
-             /* If we no longer have a bound suffix, try a new positions for
-                fkey.start.  */
-             if (!CONSP (fkey.map))
-               {
-                 fkey.end = ++fkey.start;
-                 fkey.map = Vfunction_key_map;
-               }
-           }
-       }
-
-      /* Look for this sequence in key-translation-map.  */
-      {
-       Lisp_Object next;
-
-       /* Scan from keytran.end until we find a bound suffix.  */
-       while (keytran.end < fkey.start)
+       /* Continue scan from fkey.end until we find a bound suffix.  */
+       while (fkey.end < t)
          {
-           Lisp_Object key;
-
-           key = keybuf[keytran.end++];
-           next = access_keymap (keytran.map, key, 1, 0, 1);
-
-           /* Handle symbol with autoload definition.  */
-           if (SYMBOLP (next) && ! NILP (Ffboundp (next))
-               && CONSP (XSYMBOL (next)->function)
-               && EQ (XCAR (XSYMBOL (next)->function), Qautoload))
-             do_autoload (XSYMBOL (next)->function, next);
-
-           /* Handle a symbol whose function definition is a keymap
-              or an array.  */
-           if (SYMBOLP (next) && ! NILP (Ffboundp (next))
-               && (!NILP (Farrayp (XSYMBOL (next)->function))
-                   || KEYMAPP (XSYMBOL (next)->function)))
-             next = XSYMBOL (next)->function;
-
-           /* If the key translation map gives a function, not an
-              array, then call the function with one arg and use
-              its value instead.  */
-           if (SYMBOLP (next) && ! NILP (Ffboundp (next)))
-             {
-               struct gcpro gcpro1, gcpro2, gcpro3;
-               Lisp_Object tem;
-               tem = next;
-
-               GCPRO3 (fkey.map, keytran.map, delayed_switch_frame);
-               next = call1 (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 (next) || STRINGP (next)))
-                 error ("Function in key-translation-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 (next) || STRINGP (next)))
+           struct gcpro gcpro1, gcpro2, gcpro3;
+           int done, diff;
+
+           GCPRO3 (fkey.map, keytran.map, delayed_switch_frame);
+           done = keyremap_step (keybuf, bufsize, &fkey,
+                                 max (t, mock_input),
+                                 /* If there's a binding (i.e.
+                                    first_binding >= nmaps) we don't want
+                                    to apply this function-key-mapping.  */
+                                 fkey.end + 1 == t && first_binding >= nmaps,
+                                 &diff, prompt);
+           UNGCPRO;
+           if (done)
              {
-               int len = XFASTINT (Flength (next));
-               int i, diff = len - (keytran.end - keytran.start);
-
-               mock_input = max (t, mock_input);
-               if (mock_input + diff >= bufsize)
-                 error ("Key sequence too long");
-
-               /* Shift the keys that are after keytran.end.  */
-               if (diff < 0)
-                 for (i = keytran.end; i < mock_input; i++)
-                   keybuf[i + diff] = keybuf[i];
-               else if (diff > 0)
-                 for (i = mock_input - 1; i >= keytran.end; i--)
-                   keybuf[i + diff] = keybuf[i];
-               /* Replace the keys between keytran.start and keytran.end
-                  with those from next.  */
-               for (i = 0; i < len; i++)
-                 keybuf[keytran.start + i]
-                   = Faref (next, make_number (i));
-
-               mock_input += diff;
-               keytran.start = keytran.end += diff;
-               keytran.map = Vkey_translation_map;
-
-               /* Adjust the function-key-map counters.  */
-               fkey.start += diff;
-               fkey.end += diff;
-
+               mock_input = diff + max (t, mock_input);
                goto replay_sequence;
              }
+         }
 
-           keytran.map = get_keymap (next, 0, 1);
+      /* Look for this sequence in key-translation-map.
+        Scan from keytran.end until we find a bound suffix.  */
+      while (keytran.end < fkey.start)
+       {
+         struct gcpro gcpro1, gcpro2, gcpro3;
+         int done, diff;
 
-           /* If we no longer have a bound suffix, try a new positions for
-              keytran.start.  */
-           if (!CONSP (keytran.map))
-             {
-               keytran.end = ++keytran.start;
-               keytran.map = Vkey_translation_map;
-             }
-         }
-      }
+         GCPRO3 (fkey.map, keytran.map, delayed_switch_frame);
+         done = keyremap_step (keybuf, bufsize, &keytran, max (t, mock_input),
+                               1, &diff, prompt);
+         UNGCPRO;
+         if (done)
+           {
+             mock_input = diff + max (t, mock_input);
+             /* Adjust the function-key-map counters.  */
+             fkey.end += diff;
+             fkey.start += diff;
+             
+             goto replay_sequence;
+           }
+       }
 
       /* If KEY is not defined in any of the keymaps,
         and cannot be part of a function key or translation,
@@ -9322,6 +9366,8 @@ read_key_sequence (keybuf, bufsize, prompt, dont_downcase_last,
 
              keybuf[t - 1] = new_key;
              mock_input = max (t, mock_input);
+             fkey.start = fkey.end = KEYMAPP (fkey.map) ? 0 : bufsize + 1;
+             keytran.start = keytran.end = KEYMAPP (keytran.map) ? 0 : bufsize + 1;
 
              goto replay_sequence;
            }
@@ -10584,6 +10630,11 @@ init_keyboard ()
   poll_suppress_count = 1;
   start_polling ();
 #endif
+
+#ifdef MAC_OSX
+  /* At least provide an escape route since C-g doesn't work.  */
+  signal (SIGINT, interrupt_signal);
+#endif
 }
 
 /* This type's only use is in syms_of_keyboard, to initialize the
@@ -10601,7 +10652,9 @@ struct event_head head_table[] = {
   {&Qdelete_frame,        "delete-frame",        &Qdelete_frame},
   {&Qiconify_frame,       "iconify-frame",       &Qiconify_frame},
   {&Qmake_frame_visible,  "make-frame-visible",  &Qmake_frame_visible},
-  {&Qselect_window,       "select-window",       &Qselect_window}
+  /* `select-window' should be handled just like `switch-frame'
+     in read_key_sequence.  */
+  {&Qselect_window,       "select-window",       &Qswitch_frame}
 };
 
 void
@@ -10671,10 +10724,6 @@ syms_of_keyboard ()
   staticpro (&Qfunction_key);
   Qmouse_click = intern ("mouse-click");
   staticpro (&Qmouse_click);
-#if defined(WINDOWSNT) || defined(MAC_OSX)
-  Qmouse_wheel = intern ("mouse-wheel");
-  staticpro (&Qmouse_wheel);
-#endif
 #ifdef WINDOWSNT
   Qlanguage_change = intern ("language-change");
   staticpro (&Qlanguage_change);
@@ -10789,6 +10838,8 @@ syms_of_keyboard ()
   staticpro (&button_down_location);
   mouse_syms = Fmake_vector (make_number (1), Qnil);
   staticpro (&mouse_syms);
+  wheel_syms = Fmake_vector (make_number (2), Qnil);
+  staticpro (&wheel_syms);
 
   {
     int i;
@@ -10823,12 +10874,8 @@ syms_of_keyboard ()
   func_key_syms = Qnil;
   staticpro (&func_key_syms);
 
-#if defined(WINDOWSNT) || defined(MAC_OSX)
-  mouse_wheel_syms = Qnil;
-  staticpro (&mouse_wheel_syms);
   drag_n_drop_syms = Qnil;
   staticpro (&drag_n_drop_syms);
-#endif
 
   unread_switch_frame = Qnil;
   staticpro (&unread_switch_frame);
@@ -11271,8 +11318,54 @@ keys_of_keyboard ()
                            "ignore-event");
   initial_define_lispy_key (Vspecial_event_map, "make-frame-visible",
                            "ignore-event");
-  initial_define_lispy_key (Vspecial_event_map, "select-window",
-                           "handle-select-window");
+  /* Handling it at such a low-level causes read_key_sequence to get
+   * confused because it doesn't realize that the current_buffer was
+   * changed by read_char.
+   * 
+   * initial_define_lispy_key (Vspecial_event_map, "select-window",
+   *                       "handle-select-window"); */
   initial_define_lispy_key (Vspecial_event_map, "save-session",
                            "handle-save-session");
 }
+
+/* Mark the pointers in the kboard objects.
+   Called by the Fgarbage_collector.  */
+void
+mark_kboards ()
+{
+  KBOARD *kb;
+  Lisp_Object *p;
+  for (kb = all_kboards; kb; kb = kb->next_kboard)
+    {
+      if (kb->kbd_macro_buffer)
+       for (p = kb->kbd_macro_buffer; p < kb->kbd_macro_ptr; p++)
+         mark_object (*p);
+      mark_object (kb->Voverriding_terminal_local_map);
+      mark_object (kb->Vlast_command);
+      mark_object (kb->Vreal_last_command);
+      mark_object (kb->Vprefix_arg);
+      mark_object (kb->Vlast_prefix_arg);
+      mark_object (kb->kbd_queue);
+      mark_object (kb->defining_kbd_macro);
+      mark_object (kb->Vlast_kbd_macro);
+      mark_object (kb->Vsystem_key_alist);
+      mark_object (kb->system_key_syms);
+      mark_object (kb->Vdefault_minibuffer_frame);
+      mark_object (kb->echo_string);
+    }
+  {
+    struct input_event *event;
+    for (event = kbd_fetch_ptr; event != kbd_store_ptr; event++)
+      {
+       if (event == kbd_buffer + KBD_BUFFER_SIZE)
+         event = kbd_buffer;
+       mark_object (event->x);
+       mark_object (event->y);
+       mark_object (event->frame_or_window);
+       mark_object (event->arg);
+      }
+  }
+}
+
+/* arch-tag: 774e34d7-6d31-42f3-8397-e079a4e4c9ca
+   (do not change this comment) */