]> code.delx.au - gnu-emacs/blobdiff - src/keyboard.c
(Frename_file) [WINDOWSNT]: Remove conditional code.
[gnu-emacs] / src / keyboard.c
index f20631485ecfefdbf1a94b95f631c422b4d17079..15f8eed53cbfa5db5ad975c7b88e8f001d974683 100644 (file)
@@ -1,5 +1,5 @@
 /* Keyboard and mouse input; editor command loop.
-   Copyright (C) 1985,86,87,88,89,93,94,95 Free Software Foundation, Inc.
+   Copyright (C) 1985,86,87,88,89,93,94,95,96 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -15,7 +15,8 @@ GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with GNU Emacs; see the file COPYING.  If not, write to
-the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
 
 /* Allow config.h to undefine symbols found here.  */
 #include <signal.h>
@@ -434,6 +435,7 @@ Lisp_Object Qmake_frame_visible;
 /* Symbols to denote kinds of events.  */
 Lisp_Object Qfunction_key;
 Lisp_Object Qmouse_click;
+Lisp_Object Qtimer_event;
 /* Lisp_Object Qmouse_movement; - also an event header */
 
 /* Properties of event headers.  */
@@ -466,11 +468,21 @@ extern Lisp_Object Qmenu_enable;
 Lisp_Object recursive_edit_unwind (), command_loop ();
 Lisp_Object Fthis_command_keys ();
 Lisp_Object Qextended_command_history;
+EMACS_TIME timer_check ();
 
 extern char *x_get_keysym_name ();
 
 Lisp_Object Qpolling_period;
 
+/* List of absolute timers.  Appears in order of next scheduled event.  */
+Lisp_Object Vtimer_list;
+
+/* List of idle time timers.  Appears in order of next scheduled event.  */
+Lisp_Object Vtimer_idle_list;
+
+/* Incremented whenever a timer is run.  */
+int timers_run;
+
 extern Lisp_Object Vprint_level, Vprint_length;
 
 /* Address (if not 0) of EMACS_TIME to zero out if a SIGIO interrupt
@@ -484,7 +496,7 @@ int interrupt_input;
 /* Nonzero while interrupts are temporarily deferred during redisplay.  */
 int interrupts_deferred;
 
-/* nonzero means use ^S/^Q for flow control.  */
+/* Nonzero means use ^S/^Q for flow control.  */
 int flow_control;
 
 /* Allow m- file to inhibit use of FIONREAD.  */
@@ -554,7 +566,7 @@ echo_prompt (str)
 
   current_kboard->echo_after_prompt = len;
 
-  echo ();
+  echo_now ();
 }
 
 /* Add C to the echo string, if echoing is going on.
@@ -602,7 +614,7 @@ echo_char (c)
       *ptr = 0;
       current_kboard->echoptr = ptr;
 
-      echo ();
+      echo_now ();
     }
 }
 
@@ -627,13 +639,13 @@ echo_dash ()
   current_kboard->echoptr[0] = '-';
   current_kboard->echoptr[1] = 0;
 
-  echo ();
+  echo_now ();
 }
 
 /* Display the current echo string, and begin echoing if not already
    doing so.  */
 
-echo ()
+echo_now ()
 {
   if (!current_kboard->immediate_echo)
     {
@@ -1107,8 +1119,10 @@ command_loop_1 ()
          Fsit_for (make_number (2), Qnil, Qnil);
          unbind_to (count, Qnil);
 
-         echo_area_glyphs = 0;
-         no_direct = 1;
+         /* Clear the echo area.  */
+         message2 (0);
+
+         /* If a C-g came in before, treat it as input now.  */
          if (!NILP (Vquit_flag))
            {
              Vquit_flag = Qnil;
@@ -1345,7 +1359,7 @@ command_loop_1 ()
          nonundocount = 0;
          if (NILP (current_kboard->Vprefix_arg))
            Fundo_boundary ();
-         Fcommand_execute (this_command, Qnil, Qnil);
+         Fcommand_execute (this_command, Qnil, Qnil, Qnil);
 
        }
     directly_done: ;
@@ -1441,7 +1455,7 @@ safe_run_hooks (hook)
   int count = specpdl_ptr - specpdl;
   specbind (Qinhibit_quit, hook);
 
-  internal_condition_case (safe_run_hooks_1, Qerror, safe_run_hooks_error);
+  internal_condition_case (safe_run_hooks_1, Qt, safe_run_hooks_error);
 
   unbind_to (count, Qnil);
 }
@@ -1464,11 +1478,15 @@ SIGTYPE
 input_poll_signal (signalnum)  /* If we don't have an argument, */
      int signalnum;            /* some compilers complain in signal calls. */
 {
+  /* 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);
-  signal (SIGALRM, input_poll_signal);
-  alarm (polling_period);
+  /* Turn on the SIGALRM handler and request another alarm.  */
+  start_polling ();
 }
 
 #endif
@@ -1803,6 +1821,8 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu)
       goto non_reread;
     }
 
+  timer_start_idle ();
+
   /* If in middle of key sequence and minibuffer not active,
      start echoing if enough time elapses.  */
 
@@ -1819,7 +1839,7 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu)
         This is because we are probably about to display a menu,
         and we don't want to delay before doing so.  */
       if (EVENT_HAS_PARAMETERS (prev_event))
-       echo ();
+       echo_now ();
       else
        {
          save_getcjmp (save_jump);
@@ -1827,7 +1847,7 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu)
          tem0 = sit_for (echo_keystrokes, 0, 1, 1);
          restore_getcjmp (save_jump);
          if (EQ (tem0, Qt))
-           echo ();
+           echo_now ();
        }
     }
 
@@ -1882,11 +1902,11 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu)
          && XINT (Vauto_save_timeout) > 0)
        {
          Lisp_Object tem0;
-         int delay = delay_level * XFASTINT (Vauto_save_timeout) / 4;
 
          save_getcjmp (save_jump);
          restore_getcjmp (local_getcjmp);
-         tem0 = sit_for (delay, 0, 1, 1);
+         tem0 = sit_for (delay_level * XFASTINT (Vauto_save_timeout) / 4,
+                         0, 1, 1);
          restore_getcjmp (save_jump);
 
          if (EQ (tem0, Qt))
@@ -1918,7 +1938,7 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu)
            = XCONS (current_kboard->kbd_queue)->cdr;
          if (NILP (current_kboard->kbd_queue))
            current_kboard->kbd_queue_has_data = 0;
-         input_pending = readable_events ();
+         input_pending = readable_events (0);
 #ifdef MULTI_FRAME
          if (EVENT_HAS_PARAMETERS (c)
              && EQ (EVENT_HEAD_KIND (EVENT_HEAD (c)), Qswitch_frame))
@@ -2006,6 +2026,11 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu)
 
  non_reread:
 
+  /* Now that we have read an event, Emacs is not idle--
+     unless the event was a timer event.  */
+  if (! (CONSP (c) && EQ (XCONS (c)->car, Qtimer_event)))
+    timer_stop_idle ();
+
   start_polling ();
 
   if (NILP (c))
@@ -2037,8 +2062,15 @@ read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu)
 
   if (!NILP (tem))
     {
+      int was_locked = single_kboard;
+
       last_input_char = c;
-      Fcommand_execute (tem, Qnil, Fvector (1, &last_input_char));
+      Fcommand_execute (tem, Qnil, Fvector (1, &last_input_char), Qt);
+
+      /* Resume allowing input from any kboard, if that was true before.  */
+      if (!was_locked)
+       any_kboard_state ();
+
       goto retry;
     }
 
@@ -2266,10 +2298,10 @@ tracking_off (old_value)
         input has been processed.  If the only input available was
         the sort that we have just disabled, then we need to call
         redisplay.  */
-      if (!readable_events ())
+      if (!readable_events (1))
        {
          redisplay_preserve_echo_area ();
-         get_input_pending (&input_pending);
+         get_input_pending (&input_pending, 1);
        }
     }
 }
@@ -2319,8 +2351,10 @@ some_mouse_moved ()
 /* Return true iff there are any events in the queue that read-char
    would return.  If this returns false, a read-char would block.  */
 static int
-readable_events ()
+readable_events (do_timers_now)
+     int do_timers_now;
 {
+  timer_check (do_timers_now);
   if (kbd_fetch_ptr != kbd_store_ptr)
     return 1;
 #ifdef HAVE_MOUSE
@@ -2500,6 +2534,7 @@ kbd_buffer_get_event (kbp, used_mouse_menu)
 {
   register int c;
   Lisp_Object obj;
+  EMACS_TIME next_timer_delay;
 
   if (noninteractive)
     {
@@ -2589,7 +2624,7 @@ kbd_buffer_get_event (kbp, used_mouse_menu)
             and process it again.  */
          copy = *event;
          kbd_fetch_ptr = event + 1;
-         input_pending = readable_events ();
+         input_pending = readable_events (0);
          x_handle_selection_request (&copy);
 #else
          /* We're getting selection request events, but we don't have
@@ -2606,7 +2641,7 @@ kbd_buffer_get_event (kbp, used_mouse_menu)
          /* Remove it from the buffer before processing it.  */
          copy = *event;
          kbd_fetch_ptr = event + 1;
-         input_pending = readable_events ();
+         input_pending = readable_events (0);
          x_handle_selection_clear (&copy);
 #else
          /* We're getting selection request events, but we don't have
@@ -2647,7 +2682,7 @@ kbd_buffer_get_event (kbp, used_mouse_menu)
       else if (event->kind == menu_bar_activate_event)
        {
          kbd_fetch_ptr = event + 1;
-         input_pending = readable_events ();
+         input_pending = readable_events (0);
          x_activate_menubar (XFRAME (event->frame_or_window));
        }
 #endif
@@ -2759,7 +2794,7 @@ kbd_buffer_get_event (kbp, used_mouse_menu)
        something for us to read!  */
     abort ();
 
-  input_pending = readable_events ();
+  input_pending = readable_events (0);
 
 #ifdef MULTI_FRAME
   Vlast_event_frame = internal_last_event_frame;
@@ -2772,8 +2807,11 @@ kbd_buffer_get_event (kbp, used_mouse_menu)
    then return, without reading any user-visible events.  */
 
 void
-swallow_events ()
+swallow_events (do_display)
+     int do_display;
 {
+  int old_timers_run;
+
   while (kbd_fetch_ptr != kbd_store_ptr)
     {
       struct input_event *event;
@@ -2796,7 +2834,7 @@ swallow_events ()
             and process it again.  */
          copy = *event;
          kbd_fetch_ptr = event + 1;
-         input_pending = readable_events ();
+         input_pending = readable_events (0);
          x_handle_selection_request (&copy);
 #else
          /* We're getting selection request events, but we don't have
@@ -2814,7 +2852,7 @@ swallow_events ()
          copy = *event;
 
          kbd_fetch_ptr = event + 1;
-         input_pending = readable_events ();
+         input_pending = readable_events (0);
          x_handle_selection_clear (&copy);
 #else
          /* We're getting selection request events, but we don't have
@@ -2822,11 +2860,303 @@ swallow_events ()
          abort ();
 #endif
        }
+      else if (event->kind == timer_event)
+       {
+         Lisp_Object tem, lisp_event;
+         int was_locked = single_kboard;
+
+         tem = get_keymap_1 (Vspecial_event_map, 0, 0);
+         tem = get_keyelt (access_keymap (tem, Qtimer_event, 0, 0),
+                           1);
+         lisp_event = Fcons (Qtimer_event,
+                             Fcons (Fcdr (event->frame_or_window), Qnil));
+         kbd_fetch_ptr = event + 1;
+         if (kbd_fetch_ptr == kbd_store_ptr)
+           input_pending = 0;
+         Fcommand_execute (tem, Qnil, Fvector (1, &lisp_event), Qt);
+         timers_run++;
+         if (do_display)
+           redisplay_preserve_echo_area ();
+
+         /* Resume allowing input from any kboard, if that was true before.  */
+         if (!was_locked)
+           any_kboard_state ();
+       }
       else
        break;
     }
 
-  get_input_pending (&input_pending);
+  old_timers_run = timers_run;
+  get_input_pending (&input_pending, 1);
+
+  if (timers_run != old_timers_run && do_display)
+    redisplay_preserve_echo_area ();
+}
+\f
+static EMACS_TIME timer_idleness_start_time;
+
+/* Record the start of when Emacs is idle,
+   for the sake of running idle-time timers.  */
+
+timer_start_idle ()
+{
+  Lisp_Object timers;
+
+  /* If we are already in the idle state, do nothing.  */
+  if (! EMACS_TIME_NEG_P (timer_idleness_start_time))
+    return;
+
+  EMACS_GET_TIME (timer_idleness_start_time);
+
+  /* Mark all idle-time timers as once again candidates for running.  */
+  for (timers = Vtimer_idle_list; CONSP (timers); timers = XCONS (timers)->cdr)
+    {
+      Lisp_Object timer;
+
+      timer = XCONS (timers)->car;
+
+      if (!VECTORP (timer) || XVECTOR (timer)->size != 8)
+       continue;
+      XVECTOR (timer)->contents[0] = Qnil;
+    }
+}
+
+/* Record that Emacs is no longer idle, so stop running idle-time timers.  */
+
+timer_stop_idle ()
+{
+  EMACS_SET_SECS_USECS (timer_idleness_start_time, -1, -1);
+}
+
+/* This is only for debugging.  */
+struct input_event last_timer_event;
+
+/* Check whether a timer has fired.  To prevent larger problems we simply
+   disregard elements that are not proper timers.  Do not make a circular
+   timer list for the time being.
+
+   Returns the number of seconds to wait until the next timer fires.  If a
+   timer is triggering now, return zero seconds.
+   If no timer is active, return -1 seconds.
+
+   If a timer is ripe now, either queue a timer-event,
+   or call the timer's handler function here if DO_IT_NOW is nonzero.  */
+
+EMACS_TIME
+timer_check (do_it_now)
+     int do_it_now;
+{
+  EMACS_TIME nexttime;
+  EMACS_TIME now, idleness_now;
+  Lisp_Object timers, idle_timers, chosen_timer;
+  /* Nonzero if we generate some events.  */
+  int events_generated = 0;
+  struct gcpro gcpro1, gcpro2, gcpro3;
+
+  EMACS_SET_SECS (nexttime, -1);
+  EMACS_SET_USECS (nexttime, -1);
+
+  /* Always consider the ordinary timers.  */
+  timers = Vtimer_list;
+  /* Consider the idle timers only if Emacs is idle.  */
+  if (! EMACS_TIME_NEG_P (timer_idleness_start_time))
+    idle_timers = Vtimer_idle_list;
+  else
+    idle_timers = Qnil;
+  chosen_timer = Qnil;
+  GCPRO3 (timers, idle_timers, chosen_timer);
+
+  if (CONSP (timers) || CONSP (idle_timers))
+    {
+      EMACS_GET_TIME (now);
+      if (! EMACS_TIME_NEG_P (timer_idleness_start_time))
+       EMACS_SUB_TIME (idleness_now, now, timer_idleness_start_time);
+    }
+
+  while (CONSP (timers) || CONSP (idle_timers))
+    {
+      int triggertime = EMACS_SECS (now);
+      Lisp_Object *vector;
+      Lisp_Object timer, idle_timer;
+      EMACS_TIME timer_time, idle_timer_time;
+      EMACS_TIME difference, timer_difference, idle_timer_difference;
+
+      /* Skip past invalid timers and timers already handled.  */
+      if (!NILP (timers))
+       {
+         timer = XCONS (timers)->car;
+         if (!VECTORP (timer) || XVECTOR (timer)->size != 8)
+           {
+             timers = XCONS (timers)->cdr;
+             continue;
+           }
+         vector = XVECTOR (timer)->contents;
+
+         if (!INTEGERP (vector[1]) || !INTEGERP (vector[2])
+             || !INTEGERP (vector[3])
+             || ! NILP (vector[0]))
+           {
+             timers = XCONS (timers)->cdr;
+             continue;
+           }
+       }
+      if (!NILP (idle_timers))
+       {
+         timer = XCONS (idle_timers)->car;
+         if (!VECTORP (timer) || XVECTOR (timer)->size != 8)
+           {
+             idle_timers = XCONS (idle_timers)->cdr;
+             continue;
+           }
+         vector = XVECTOR (timer)->contents;
+
+         if (!INTEGERP (vector[1]) || !INTEGERP (vector[2])
+             || !INTEGERP (vector[3])
+             || ! NILP (vector[0]))
+           {
+             idle_timers = XCONS (idle_timers)->cdr;
+             continue;
+           }
+       }
+
+      /* Set TIMER, TIMER_TIME and TIMER_DIFFERENCE
+        based on the next ordinary timer.
+        TIMER_DIFFERENCE is the distance in time from NOW to when
+        this timer becomes ripe (negative if it's already ripe).  */
+      if (!NILP (timers))
+       {
+         timer = XCONS (timers)->car;
+         vector = XVECTOR (timer)->contents;
+         EMACS_SET_SECS (timer_time,
+                         (XINT (vector[1]) << 16) | (XINT (vector[2])));
+         EMACS_SET_USECS (timer_time, XINT (vector[3]));
+         EMACS_SUB_TIME (timer_difference, timer_time, now);
+       }
+
+      /* Set IDLE_TIMER, IDLE_TIMER_TIME and IDLE_TIMER_DIFFERENCE
+        based on the next idle timer.  */
+      if (!NILP (idle_timers))
+       {
+         idle_timer = XCONS (idle_timers)->car;
+         vector = XVECTOR (idle_timer)->contents;
+         EMACS_SET_SECS (idle_timer_time,
+                         (XINT (vector[1]) << 16) | (XINT (vector[2])));
+         EMACS_SET_USECS (idle_timer_time, XINT (vector[3]));
+         EMACS_SUB_TIME (idle_timer_difference, idle_timer_time, idleness_now);
+       }
+
+      /* Decide which timer is the next timer,
+        and set CHOSEN_TIMER, VECTOR and DIFFERENCE accordingly.
+        Also step down the list where we found that timer.  */
+
+      if (! NILP (timers) && ! NILP (idle_timers))
+       {
+         EMACS_TIME temp;
+         EMACS_SUB_TIME (temp, timer_difference, idle_timer_difference);
+         if (EMACS_TIME_NEG_P (temp))
+           {
+             chosen_timer = timer;
+             timers = XCONS (timers)->cdr;
+             difference = timer_difference;
+           }
+         else
+           {
+             chosen_timer = idle_timer;
+             idle_timers = XCONS (idle_timers)->cdr;
+             difference = idle_timer_difference;
+           }
+       }
+      else if (! NILP (timers))
+       {
+         chosen_timer = timer;
+         timers = XCONS (timers)->cdr;
+         difference = timer_difference;
+       }
+      else
+       {
+         chosen_timer = idle_timer;
+         idle_timers = XCONS (idle_timers)->cdr;
+         difference = idle_timer_difference;
+       }
+      vector = XVECTOR (chosen_timer)->contents;
+       
+      /* If timer is rupe, run it if it hasn't been run.  */
+      if (EMACS_TIME_NEG_P (difference)
+         || (EMACS_SECS (difference) == 0
+             && EMACS_USECS (difference) == 0))
+       {
+         if (NILP (vector[0]))
+           {
+             /* Mark the timer as triggered to prevent problems if the lisp
+                code fails to reschedule it right.  */
+             vector[0] = Qt;
+
+             /* Run the timer or queue a timer event.  */
+             if (do_it_now)
+               {
+                 Lisp_Object tem, event;
+                 int was_locked = single_kboard;
+
+                 tem = get_keymap_1 (Vspecial_event_map, 0, 0);
+                 tem = get_keyelt (access_keymap (tem, Qtimer_event, 0, 0),
+                                   1);
+                 event = Fcons (Qtimer_event, Fcons (chosen_timer, Qnil));
+                 Fcommand_execute (tem, Qnil, Fvector (1, &event), Qt);
+                 timers_run++;
+
+                 /* Resume allowing input from any kboard, if that was true before.  */
+                 if (!was_locked)
+                   any_kboard_state ();
+
+                 /* Since we have handled the event,
+                    we don't need to tell the caller to wake up and do it.  */
+               }
+             else
+               {
+                 /* Generate a timer event so the caller will handle it.  */
+                 struct input_event event;
+
+                 event.kind = timer_event;
+                 event.modifiers = 0;
+                 event.x = event.y = Qnil;
+                 event.timestamp = triggertime;
+                 /* Store the timer in the frame slot.  */
+                 event.frame_or_window
+                   = Fcons (Fselected_frame (), chosen_timer);
+                 kbd_buffer_store_event (&event);
+
+                 last_timer_event = event;
+
+                 /* Tell caller to handle this event right away.  */
+                 events_generated = 1;
+                 EMACS_SET_SECS (nexttime, 0);
+                 EMACS_SET_USECS (nexttime, 0);
+
+                 /* Don't queue more than one event at once.
+                    When Emacs is ready for another, it will
+                    queue the next one.  */
+                 UNGCPRO;
+                 return nexttime;
+               }
+           }
+       }
+      else
+       /* When we encounter a timer that is still waiting,
+          return the amount of time to wait before it is ripe.  */
+       {
+         UNGCPRO;
+         /* But if we generated an event,
+            tell the caller to handle it now.  */
+         if (events_generated)
+           return nexttime;
+         return difference;
+       }
+    }
+
+  /* No timers are pending in the future.  */
+  /* Return 0 if we generated an event, and -1 if not.  */
+  UNGCPRO;
+  return nexttime;
 }
 \f
 /* Caches for modify_event_symbol.  */
@@ -2991,9 +3321,9 @@ char *lispy_function_keys[] =
     0, 0, 0, 0, 0, 0, 0, 0, 0, 
     0, 0, 0, 0, 0, 0, 0, 0,
     
-    0,               /* VK_LWIN           0x5B */
-    0,               /* VK_RWIN           0x5C */
-    0,               /* VK_APPS           0x5D */
+    "lwindow",       /* VK_LWIN           0x5B */
+    "rwindow",       /* VK_RWIN           0x5C */
+    "apps",          /* VK_APPS           0x5D */
     
     0, 0,            /*    0x5E .. 0x5F        */
     
@@ -3044,10 +3374,21 @@ char *lispy_function_keys[] =
     "kp-numlock",    /* VK_NUMLOCK        0x90 */
     "scroll",        /* VK_SCROLL         0x91 */
     
-    0, 0, 0, 0, 0,   /*    0x92 .. 0x96        */
-    0, 0, 0, 0, 0,   /*    0x97 .. 0x9B        */
-    0, 0, 0, 0,      /*    0x9C .. 0x9F        */
-    
+    "kp-space",             /* VK_NUMPAD_CLEAR   0x92 */
+    "kp-enter",             /* VK_NUMPAD_ENTER   0x93 */
+    "kp-prior",             /* VK_NUMPAD_PRIOR   0x94 */
+    "kp-next",      /* VK_NUMPAD_NEXT    0x95 */
+    "kp-end",       /* VK_NUMPAD_END     0x96 */
+    "kp-home",      /* VK_NUMPAD_HOME    0x97 */
+    "kp-left",      /* VK_NUMPAD_LEFT    0x98 */
+    "kp-up",        /* VK_NUMPAD_UP      0x99 */
+    "kp-right",             /* VK_NUMPAD_RIGHT   0x9A */
+    "kp-down",      /* VK_NUMPAD_DOWN    0x9B */
+    "kp-insert",     /* VK_NUMPAD_INSERT  0x9C */
+    "kp-delete",     /* VK_NUMPAD_DELETE  0x9D */
+
+    0, 0,           /*    0x9E .. 0x9F        */
+
     /*
      * VK_L* & VK_R* - left and right Alt, Ctrl and Shift virtual keys.
      * Used only as parameters to GetAsyncKeyState() and GetKeyState().
@@ -3296,6 +3637,9 @@ make_lispy_event (event)
                                   / sizeof (lispy_function_keys[0])));
       break;
 
+    case timer_event:
+      return Fcons (Qtimer_event, Fcons (Fcdr (event->frame_or_window), Qnil));
+
 #ifdef HAVE_MOUSE
       /* A mouse click.  Figure out where it is, decide whether it's
          a press, click or drag, and build the appropriate structure.  */
@@ -4082,21 +4426,21 @@ modify_event_symbol (symbol_num, modifiers, symbol_kind, name_alist,
    event type as a number or a symbol.  */
 
 DEFUN ("event-convert-list", Fevent_convert_list, Sevent_convert_list, 1, 1, 0,
-  "Convert the event description LIST to an event type.\n\
-LIST should contain one base event type (a character or symbol)\n\
+  "Convert the event description list EVENT-DESC to an event type.\n\
+EVENT-DESC should contain one base event type (a character or symbol)\n\
 and zero or more modifier names (control, meta, hyper, super, shift, alt,\n\
 drag, down, double or triple).\n\
 The return value is an event type (a character or symbol) which\n\
 has the same base event type and all the specified modifiers.")
-  (event)
-     Lisp_Object event;
+  (event_desc)
+     Lisp_Object event_desc;
 {
   Lisp_Object base;
   int modifiers = 0;
   Lisp_Object rest;
 
   base = Qnil;
-  rest = event;
+  rest = event_desc;
   while (CONSP (rest))
     {
       Lisp_Object elt;
@@ -4252,14 +4596,17 @@ lucid_event_type_list_p (object)
 /* Store into *addr a value nonzero if terminal input chars are available.
    Serves the purpose of ioctl (0, FIONREAD, addr)
    but works even if FIONREAD does not exist.
-   (In fact, this may actually read some input.)  */
+   (In fact, this may actually read some input.)
+
+   If DO_TIMERS_NOW is nonzero, actually run timer events that are ripe.  */
 
 static void
-get_input_pending (addr)
+get_input_pending (addr, do_timers_now)
      int *addr;
+     int do_timers_now;
 {
   /* First of all, have we already counted some input?  */
-  *addr = !NILP (Vquit_flag) || readable_events ();
+  *addr = !NILP (Vquit_flag) || readable_events (do_timers_now);
 
   /* If input is being read as it arrives, and we have none, there is none.  */
   if (*addr > 0 || (interrupt_input && ! interrupts_deferred))
@@ -4267,7 +4614,7 @@ get_input_pending (addr)
 
   /* Try to read some input and see how much we get.  */
   gobble_input (0);
-  *addr = !NILP (Vquit_flag) || readable_events ();
+  *addr = !NILP (Vquit_flag) || readable_events (do_timers_now);
 }
 
 /* Interface to read_avail_input, blocking SIGIO or SIGALRM if necessary.  */
@@ -4546,7 +4893,7 @@ void
 reinvoke_input_signal ()
 {
 #ifdef SIGIO
-  kill (0, SIGIO);
+  kill (getpid (), SIGIO);
 #endif
 }
 
@@ -4979,6 +5326,12 @@ read_char_x_menu_prompt (nmaps, maps, prev_event, used_mouse_menu)
   return Qnil ;
 }
 
+/* Buffer in use so far for the minibuf prompts for menu keymaps.
+   We make this bigger when necessary, and never free it.  */
+static char *read_char_minibuf_menu_text;
+/* Size of that buffer.  */
+static int read_char_minibuf_menu_width;
+
 static Lisp_Object
 read_char_minibuf_menu_prompt (commandflag, nmaps, maps)
      int commandflag ;
@@ -4989,14 +5342,28 @@ read_char_minibuf_menu_prompt (commandflag, nmaps, maps)
   register Lisp_Object name;
   int nlength;
   int width = FRAME_WIDTH (selected_frame) - 4;
-  char *menu = (char *) alloca (width + 4);
   int idx = -1;
   int nobindings = 1;
   Lisp_Object rest, vector;
+  char *menu;
 
   if (! menu_prompting)
     return Qnil;
 
+  /* Make sure we have a big enough buffer for the menu text.  */
+  if (read_char_minibuf_menu_text == 0)
+    {
+      read_char_minibuf_menu_width = width + 4;
+      read_char_minibuf_menu_text = (char *) xmalloc (width + 4);
+    }
+  else if (width + 4 > read_char_minibuf_menu_width)
+    {
+      read_char_minibuf_menu_width = width + 4;
+      read_char_minibuf_menu_text
+       = (char *) xrealloc (read_char_minibuf_menu_text, width + 4);
+    }
+  menu = read_char_minibuf_menu_text;
+
   /* Get the menu name from the first map that has one (a prompt string).  */
   for (mapno = 0; mapno < nmaps; mapno++)
     {
@@ -6367,16 +6734,18 @@ DEFUN ("read-key-sequence", Fread_key_sequence, Sread_key_sequence, 1, 4, 0,
   return make_event_array (i, keybuf);
 }
 \f
-DEFUN ("command-execute", Fcommand_execute, Scommand_execute, 1, 3, 0,
+DEFUN ("command-execute", Fcommand_execute, Scommand_execute, 1, 4, 0,
  "Execute CMD as an editor command.\n\
 CMD must be a symbol that satisfies the `commandp' predicate.\n\
 Optional second arg RECORD-FLAG non-nil\n\
 means unconditionally put this command in `command-history'.\n\
 Otherwise, that is done only if an arg is read using the minibuffer.\n\
 The argument KEYS specifies the value to use instead of (this-command-keys)\n\
-when reading the arguments; if it is nil, (this_command_key_count) is used.")
-     (cmd, record, keys)
-     Lisp_Object cmd, record, keys;
+when reading the arguments; if it is nil, (this_command_key_count) is used.\n\
+The argument SPECIAL, if non-nil, means that this command is executing\n\
+a special event, so ignore the prefix argument and don't clear it.")
+     (cmd, record_flag, keys, special)
+     Lisp_Object cmd, record_flag, keys, special;
 {
   register Lisp_Object final;
   register Lisp_Object tem;
@@ -6384,11 +6753,17 @@ when reading the arguments; if it is nil, (this_command_key_count) is used.")
   struct backtrace backtrace;
   extern int debug_on_next_call;
 
-  prefixarg = current_kboard->Vprefix_arg;
-  current_kboard->Vprefix_arg = Qnil;
-  Vcurrent_prefix_arg = prefixarg;
   debug_on_next_call = 0;
 
+  if (NILP (special))
+    {
+      prefixarg = current_kboard->Vprefix_arg;
+      Vcurrent_prefix_arg = prefixarg;
+      current_kboard->Vprefix_arg = Qnil;
+    }
+  else
+    prefixarg = Qnil;
+
   if (SYMBOLP (cmd))
     {
       tem = Fget (cmd, Qdisabled);
@@ -6415,7 +6790,7 @@ when reading the arguments; if it is nil, (this_command_key_count) is used.")
       /* If requested, place the macro in the command history.  For
         other sorts of commands, call-interactively takes care of
         this.  */
-      if (!NILP (record))
+      if (!NILP (record_flag))
        Vcommand_history
          = Fcons (Fcons (Qexecute_kbd_macro,
                          Fcons (final, Fcons (prefixarg, Qnil))),
@@ -6432,7 +6807,7 @@ when reading the arguments; if it is nil, (this_command_key_count) is used.")
       backtrace.nargs = 1;
       backtrace.evalargs = 0;
 
-      tem = Fcall_interactively (cmd, record, keys);
+      tem = Fcall_interactively (cmd, record_flag, keys);
 
       backtrace_list = backtrace.next;
       return tem;
@@ -6546,7 +6921,7 @@ DEFUN ("execute-extended-command", Fexecute_extended_command, Sexecute_extended_
        }
     }
 
-  return Fcommand_execute (function, Qt, Qnil);
+  return Fcommand_execute (function, Qt, Qnil, Qnil);
 }
 
 /* Find the set of keymaps now active.
@@ -6590,11 +6965,29 @@ current_active_maps (maps_p)
   return nmaps;
 }
 \f
+/* Return nonzero if input events are pending.  */
 
 detect_input_pending ()
 {
   if (!input_pending)
-    get_input_pending (&input_pending);
+    get_input_pending (&input_pending, 0);
+
+  return input_pending;
+}
+
+/* Return nonzero if input events are pending.
+   Execute timers immediately; don't make events for them.  */
+
+detect_input_pending_run_timers (do_display)
+     int do_display;
+{
+  int old_timers_run = timers_run;
+
+  if (!input_pending)
+    get_input_pending (&input_pending, 1);
+
+  if (old_timers_run != timers_run && do_display)
+    redisplay_preserve_echo_area ();
 
   return input_pending;
 }
@@ -6615,7 +7008,8 @@ Actually, the value is nil only if we can be sure that no input is available.")
   if (!NILP (Vunread_command_events) || unread_command_char != -1)
     return (Qt);
 
-  return detect_input_pending () ? Qt : Qnil;
+  get_input_pending (&input_pending, 1);
+  return input_pending > 0 ? Qt : Qnil;
 }
 
 DEFUN ("recent-keys", Frecent_keys, Srecent_keys, 0, 0, 0,
@@ -6685,18 +7079,17 @@ If FILE is nil, close any open dribble file.")
   (file)
      Lisp_Object file;
 {
-  if (NILP (file))
+  if (dribble)
     {
-      if (dribble)
-       {
-         fclose (dribble);
-         dribble = 0;
-       }
+      fclose (dribble);
+      dribble = 0;
     }
-  else
+  if (!NILP (file))
     {
       file = Fexpand_file_name (file, Qnil);
       dribble = fopen (XSTRING (file)->data, "w");
+      if (dribble == 0)
+       report_file_error ("Opening dribble", Fcons (file, Qnil));
     }
   return Qnil;
 }
@@ -7138,7 +7531,7 @@ init_kboard (kb)
 
 /*
  * Destroy the contents of a kboard object, but not the object itself.
- * We use this just before deleteing it, or if we're going to initialize
+ * We use this just before deleting it, or if we're going to initialize
  * it a second time.
  */
 static void
@@ -7172,6 +7565,7 @@ init_keyboard ()
   quit_char = Ctl ('g');
   Vunread_command_events = Qnil;
   unread_command_char = -1;
+  EMACS_SET_SECS_USECS (timer_idleness_start_time, -1, -1);
   total_keys = 0;
   recent_keys_index = 0;
   kbd_fetch_ptr = kbd_buffer;
@@ -7298,6 +7692,8 @@ syms_of_keyboard ()
   staticpro (&Qfunction_key);
   Qmouse_click = intern ("mouse-click");
   staticpro (&Qmouse_click);
+  Qtimer_event = intern ("timer-event");
+  staticpro (&Qtimer_event);
 
   Qmenu_enable = intern ("menu-enable");
   staticpro (&Qmenu_enable);
@@ -7599,7 +7995,7 @@ Errors running the hook are caught and ignored.");
   Vpost_command_idle_hook = Qnil;
 
   DEFVAR_INT ("post-command-idle-delay", &post_command_idle_delay,
-    "Delay time before running `post-command-idle-delay'.\n\
+    "Delay time before running `post-command-idle-hook'.\n\
 This is measured in microseconds.");
   post_command_idle_delay = 100000;
 
@@ -7663,6 +8059,14 @@ If the value is non-nil and not a number, we wait 2 seconds.");
   DEFVAR_LISP ("column-number-mode", &Vcolumn_number_mode,
     "Non-nil enables display of the current column number in the mode line.");
   Vcolumn_number_mode = Qnil;
+
+  DEFVAR_LISP ("timer-list", &Vtimer_list,
+    "List of active absolute time timers in order of increasing time");
+  Vtimer_list = Qnil;
+
+  DEFVAR_LISP ("timer-idle-list", &Vtimer_idle_list,
+    "List of active idle-time timers in order of increasing time");
+  Vtimer_idle_list = Qnil;
 }
 
 keys_of_keyboard ()