XEmacs 21.2.28 "Hermes".
[chise/xemacs-chise.git.1] / src / event-msw.c
index 09054fd..c5c3d2f 100644 (file)
@@ -28,6 +28,7 @@ Boston, MA 02111-1307, USA.  */
    Ultimately based on FSF.
    Rewritten by Ben Wing.
    Rewritten for mswindows by Jonathan Harris, November 1997 for 21.0.
+   Subprocess and modal loop support by Kirill M. Katsnelson.
  */
 
 #include <config.h>
@@ -50,23 +51,32 @@ Boston, MA 02111-1307, USA.  */
 #include "device.h"
 #include "events.h"
 #include "frame.h"
+#include "buffer.h"
+#include "faces.h"
 #include "lstream.h"
 #include "process.h"
 #include "redisplay.h"
+#include "select.h"
 #include "sysproc.h"
 #include "syswait.h"
 #include "systime.h"
 #include "sysdep.h"
+#include "objects-msw.h"
 
 #include "events-mod.h"
 #ifdef HAVE_MSG_SELECT
 #include "sysfile.h"
+#include "console-tty.h"
 #elif defined(__CYGWIN32__)
 typedef unsigned int SOCKET;
 #endif
 #include <io.h>
 #include <errno.h>
 
+#if defined (__CYGWIN32__) && (CYGWIN_VERSION_DLL_MAJOR < 20)
+typedef NMHDR *LPNMHDR;
+#endif
+
 #ifdef HAVE_MENUBARS
 #define ADJR_MENUFLAG TRUE
 #else
@@ -80,14 +90,10 @@ typedef unsigned int SOCKET;
 /* Timer ID used for button2 emulation */
 #define BUTTON_2_TIMER_ID 1
 
-extern Lisp_Object 
-mswindows_get_toolbar_button_text (struct frame* f, int command_id);
-extern Lisp_Object
-mswindows_handle_toolbar_wm_command (struct frame* f, HWND ctrl, WORD id);
-
 static Lisp_Object mswindows_find_frame (HWND hwnd);
 static Lisp_Object mswindows_find_console (HWND hwnd);
-static Lisp_Object mswindows_key_to_emacs_keysym(int mswindows_key, int mods);
+static Lisp_Object mswindows_key_to_emacs_keysym (int mswindows_key, int mods,
+                                                 int extendedp);
 static int mswindows_modifier_state (BYTE* keymap, int has_AltGr);
 static void mswindows_set_chord_timer (HWND hwnd);
 static int mswindows_button2_near_enough (POINTS p1, POINTS p2);
@@ -98,6 +104,7 @@ static struct event_stream *mswindows_event_stream;
 #ifdef HAVE_MSG_SELECT
 extern SELECT_TYPE input_wait_mask, non_fake_input_wait_mask;
 extern SELECT_TYPE process_only_mask, tty_only_mask;
+SELECT_TYPE zero_mask;
 extern int signal_event_pipe_initialized;
 int windows_fd;
 #endif
@@ -114,11 +121,16 @@ static Lisp_Object mswindows_s_dispatch_event_queue, mswindows_s_dispatch_event_
 /* The number of things we can wait on */
 #define MAX_WAITABLE (MAXIMUM_WAIT_OBJECTS - 1)
 
+#ifndef HAVE_MSG_SELECT
 /* List of mswindows waitable handles. */
 static HANDLE mswindows_waitable_handles[MAX_WAITABLE];
 
 /* Number of wait handles */
 static int mswindows_waitable_count=0;
+#endif /* HAVE_MSG_SELECT */
+/* Brush for painting widgets */
+static HBRUSH widget_brush = 0;
+static LONG    last_widget_brushed = 0;
 
 /* Count of quit chars currently in the queue */
 /* Incremented in WM_[SYS]KEYDOWN handler in the mswindows_wnd_proc()
@@ -127,6 +139,7 @@ int mswindows_quit_chars_count = 0;
 
 /* These are Lisp integers; see DEFVARS in this file for description. */
 int mswindows_dynamic_frame_resize;
+int mswindows_meta_activates_menu;
 int mswindows_num_mouse_buttons;
 int mswindows_mouse_button_max_skew_x;
 int mswindows_mouse_button_max_skew_y;
@@ -154,7 +167,7 @@ static int mswindows_pending_timers_count;
 /* This structure is allocated by the main thread, and is deallocated
    in the thread upon exit.  There are situations when a thread
    remains blocked for a long time, much longer than the lstream
-   exists. For exmaple, "start notepad" command is issued from the
+   exists. For example, "start notepad" command is issued from the
    shell, then the shell is closed by C-c C-d. Although the shell
    process exits, its output pipe will not get closed until the
    notepad process exits also, because it inherits the pipe form the
@@ -180,7 +193,7 @@ struct ntpipe_slurp_stream_shared_data
 };
 
 #define MAX_SLURP_STREAMS 32
-struct ntpipe_slurp_stream_shared_data 
+struct ntpipe_slurp_stream_shared_data
 shared_data_block[MAX_SLURP_STREAMS]={{0}};
 
 struct ntpipe_slurp_stream
@@ -193,7 +206,7 @@ DEFINE_LSTREAM_IMPLEMENTATION ("ntpipe-input", lstream_ntpipe_slurp,
                               sizeof (struct ntpipe_slurp_stream));
 
 /* This function is thread-safe, and is called from either thread
-   context. It serializes freeing shared dtata structure */
+   context. It serializes freeing shared data structure */
 static void
 slurper_free_shared_data_maybe (struct ntpipe_slurp_stream_shared_data* s)
 {
@@ -252,7 +265,7 @@ slurp_thread (LPVOID vparam)
 
       /* Now we got something to notify caller, either a byte or an
         error/eof indication. Before we do, allow internal pipe
-        buffer to accumulate little bit more data. 
+        buffer to accumulate little bit more data.
         Reader function pulses this event before waiting for
         a character, to avoid pipe delay, and to get the byte
         immediately. */
@@ -267,7 +280,7 @@ slurp_thread (LPVOID vparam)
       if (s->die_p)
        break;
 
-      /* Block until the client finishes with retireving the rest of
+      /* Block until the client finishes with retrieving the rest of
         pipe data */
       WaitForSingleObject (s->hev_thread, INFINITE);
     }
@@ -341,11 +354,11 @@ get_ntpipe_input_stream_waitable (Lstream *stream)
   return s->thread_data->hev_caller;
 }
 
-static int 
+static ssize_t
 ntpipe_slurp_reader (Lstream *stream, unsigned char *data, size_t size)
 {
   /* This function must be called from the main thread only */
-  struct ntpipe_slurp_stream_shared_data* s = 
+  struct ntpipe_slurp_stream_shared_data* s =
     NTPIPE_SLURP_STREAM_DATA(stream)->thread_data;
 
   if (!s->die_p)
@@ -354,7 +367,7 @@ ntpipe_slurp_reader (Lstream *stream, unsigned char *data, size_t size)
       /* Disallow pipe read delay for the thread: we need a character
          ASAP */
       SetEvent (s->hev_unsleep);
-  
+
       /* Check if we have a character ready. Give it a short delay,
         for the thread to awake from pipe delay, just ion case*/
       wait_result = WaitForSingleObject (s->hev_caller, 2);
@@ -405,7 +418,7 @@ ntpipe_slurp_reader (Lstream *stream, unsigned char *data, size_t size)
              ReadFile (s->hpipe, data, min (bytes_available, size),
                        &bytes_read, NULL);
          }
-      
+
        /* Now we can unblock thread, so it attempts to read more */
        SetEvent (s->hev_thread);
        return bytes_read + 1;
@@ -414,11 +427,11 @@ ntpipe_slurp_reader (Lstream *stream, unsigned char *data, size_t size)
   return 0;
 }
 
-static int 
+static int
 ntpipe_slurp_closer (Lstream *stream)
 {
   /* This function must be called from the main thread only */
-  struct ntpipe_slurp_stream_shared_data* s = 
+  struct ntpipe_slurp_stream_shared_data* s =
     NTPIPE_SLURP_STREAM_DATA(stream)->thread_data;
 
   /* Force thread to stop */
@@ -450,7 +463,7 @@ init_slurp_stream (void)
   LSTREAM_TYPE_DATA (stream, ntpipe_shove)
 
 #define MAX_SHOVE_BUFFER_SIZE 128
-     
+
 struct ntpipe_shove_stream
 {
   LPARAM user_data;    /* Any user data stored in the stream object     */
@@ -469,6 +482,7 @@ struct ntpipe_shove_stream
 DEFINE_LSTREAM_IMPLEMENTATION ("ntpipe-output", lstream_ntpipe_shove,
                               sizeof (struct ntpipe_shove_stream));
 
+#ifndef HAVE_MSG_SELECT
 static DWORD WINAPI
 shove_thread (LPVOID vparam)
 {
@@ -476,7 +490,7 @@ shove_thread (LPVOID vparam)
 
   for (;;)
     {
-      DWORD bytes_written; 
+      DWORD bytes_written;
 
       /* Block on event and wait for a job */
       InterlockedIncrement (&s->idle_p);
@@ -513,7 +527,7 @@ make_ntpipe_output_stream (HANDLE hpipe, LPARAM param)
   s->hpipe = hpipe;
   s->user_data = param;
 
-  /* Create reader thread. This could fail, so do not 
+  /* Create reader thread. This could fail, so do not
      create the event until thread is created */
   s->hthread = CreateThread (NULL, 0, shove_thread, (LPVOID)s,
                             CREATE_SUSPENDED, &thread_id_unused);
@@ -540,8 +554,9 @@ get_ntpipe_output_stream_param (Lstream *stream)
   struct ntpipe_shove_stream* s = NTPIPE_SHOVE_STREAM_DATA(stream);
   return s->user_data;
 }
+#endif
 
-static int
+static ssize_t
 ntpipe_shove_writer (Lstream *stream, const unsigned char *data, size_t size)
 {
   struct ntpipe_shove_stream* s = NTPIPE_SHOVE_STREAM_DATA(stream);
@@ -618,7 +633,7 @@ struct winsock_stream
   OVERLAPPED ov;               /* Overlapped I/O structure                  */
   void* buffer;                        /* Buffer. Allocated for input stream only   */
   unsigned int bufsize;                /* Number of bytes last read                 */
-  unsigned int bufpos;         /* Psition in buffer for next fetch          */
+  unsigned int bufpos;         /* Position in buffer for next fetch         */
   unsigned int error_p :1;     /* I/O Error seen                            */
   unsigned int eof_p :1;       /* EOF Error seen                            */
   unsigned int pending_p :1;   /* There is a pending I/O operation          */
@@ -650,7 +665,7 @@ winsock_initiate_read (struct winsock_stream *str)
     str->eof_p = 1;
 }
 
-static int
+static ssize_t
 winsock_reader (Lstream *stream, unsigned char *data, size_t size)
 {
   struct winsock_stream *str = WINSOCK_STREAM_DATA (stream);
@@ -683,7 +698,7 @@ winsock_reader (Lstream *stream, unsigned char *data, size_t size)
     return 0;
   if (str->error_p)
     return -1;
-  
+
   /* Return as much of buffer as we have */
   size = min (size, (size_t) (str->bufsize - str->bufpos));
   memcpy (data, (void*)((BYTE*)str->buffer + str->bufpos), size);
@@ -696,7 +711,7 @@ winsock_reader (Lstream *stream, unsigned char *data, size_t size)
   return size;
 }
 
-static int
+static ssize_t
 winsock_writer (Lstream *stream, CONST unsigned char *data, size_t size)
 {
   struct winsock_stream *str = WINSOCK_STREAM_DATA (stream);
@@ -724,11 +739,14 @@ winsock_writer (Lstream *stream, CONST unsigned char *data, size_t size)
 
   if (size == 0)
     return 0;
-  
+
   {
     ResetEvent (str->ov.hEvent);
-    
-    if (WriteFile ((HANDLE)str->s, data, size, NULL, &str->ov)
+
+    /* Docs indicate that 4th parameter to WriteFile can be NULL since this is
+     * an overlapped operation. This fails on Win95 with winsock 1.x so we
+     * supply a spare address which is ignored by Win95 anyway. Sheesh. */
+    if (WriteFile ((HANDLE)str->s, data, size, (LPDWORD)&str->buffer, &str->ov)
        || GetLastError() == ERROR_IO_PENDING)
       str->pending_p = 1;
     else
@@ -835,7 +853,7 @@ init_winsock_stream (void)
 /************************************************************************/
 
 static int
-mswindows_user_event_p (struct Lisp_Event* sevt)
+mswindows_user_event_p (Lisp_Event* sevt)
 {
   return (sevt->event_type == key_press_event
          || sevt->event_type == button_press_event
@@ -843,7 +861,7 @@ mswindows_user_event_p (struct Lisp_Event* sevt)
          || sevt->event_type == misc_user_event);
 }
 
-/* 
+/*
  * Add an emacs event to the proper dispatch queue
  */
 static void
@@ -851,7 +869,7 @@ mswindows_enqueue_dispatch_event (Lisp_Object event)
 {
   int user_p = mswindows_user_event_p (XEVENT(event));
   enqueue_event (event,
-                user_p ? &mswindows_u_dispatch_event_queue : 
+                user_p ? &mswindows_u_dispatch_event_queue :
                        &mswindows_s_dispatch_event_queue,
                 user_p ? &mswindows_u_dispatch_event_queue_tail :
                        &mswindows_s_dispatch_event_queue_tail);
@@ -871,10 +889,11 @@ mswindows_enqueue_misc_user_event (Lisp_Object channel, Lisp_Object function,
                                   Lisp_Object object)
 {
   Lisp_Object event = Fmake_event (Qnil, Qnil);
-  struct Lisp_Event* e = XEVENT (event);
+  Lisp_Event* e = XEVENT (event);
 
   e->event_type = misc_user_event;
   e->channel = channel;
+  e->timestamp = GetTickCount ();
   e->event.misc.function = function;
   e->event.misc.object = object;
 
@@ -882,24 +901,24 @@ mswindows_enqueue_misc_user_event (Lisp_Object channel, Lisp_Object function,
 }
 
 void
-mswindows_enqueue_magic_event (HWND hwnd, UINT message)
+mswindows_enqueue_magic_event (HWND hwnd, UINT msg)
 {
   Lisp_Object emacs_event = Fmake_event (Qnil, Qnil);
-  struct Lisp_Event* event = XEVENT (emacs_event);
+  Lisp_Event* event = XEVENT (emacs_event);
 
   event->channel = hwnd ? mswindows_find_frame (hwnd) : Qnil;
   event->timestamp = GetMessageTime();
   event->event_type = magic_event;
-  EVENT_MSWINDOWS_MAGIC_TYPE (event) = message;
+  EVENT_MSWINDOWS_MAGIC_TYPE (event) = msg;
 
   mswindows_enqueue_dispatch_event (emacs_event);
 }
 
 static void
-mswindows_enqueue_process_event (struct Lisp_Process* p)
+mswindows_enqueue_process_event (Lisp_Process* p)
 {
   Lisp_Object emacs_event = Fmake_event (Qnil, Qnil);
-  struct Lisp_Event* event = XEVENT (emacs_event);
+  Lisp_Event* event = XEVENT (emacs_event);
   Lisp_Object process;
   XSETPROCESS (process, p);
 
@@ -911,7 +930,7 @@ mswindows_enqueue_process_event (struct Lisp_Process* p)
 }
 
 static void
-mswindows_enqueue_mouse_button_event (HWND hwnd, UINT message, POINTS where, DWORD when)
+mswindows_enqueue_mouse_button_event (HWND hwnd, UINT msg, POINTS where, DWORD when)
 {
 
   /* We always use last message time, because mouse button
@@ -919,29 +938,36 @@ mswindows_enqueue_mouse_button_event (HWND hwnd, UINT message, POINTS where, DWO
      recognition will fail */
 
   Lisp_Object emacs_event = Fmake_event (Qnil, Qnil);
-  struct Lisp_Event* event = XEVENT(emacs_event);
+  Lisp_Event* event = XEVENT(emacs_event);
 
   event->channel = mswindows_find_frame(hwnd);
   event->timestamp = when;
   event->event.button.button =
-    (message==WM_LBUTTONDOWN || message==WM_LBUTTONUP) ? 1 :
-    ((message==WM_RBUTTONDOWN || message==WM_RBUTTONUP) ? 3 : 2);
+    (msg==WM_LBUTTONDOWN || msg==WM_LBUTTONUP) ? 1 :
+    ((msg==WM_RBUTTONDOWN || msg==WM_RBUTTONUP) ? 3 : 2);
   event->event.button.x = where.x;
   event->event.button.y = where.y;
   event->event.button.modifiers = mswindows_modifier_state (NULL, 0);
-      
-  if (message==WM_LBUTTONDOWN || message==WM_MBUTTONDOWN ||
-      message==WM_RBUTTONDOWN)
+
+  if (msg==WM_LBUTTONDOWN || msg==WM_MBUTTONDOWN ||
+      msg==WM_RBUTTONDOWN)
     {
       event->event_type = button_press_event;
       SetCapture (hwnd);
+      /* we need this to make sure the main window regains the focus
+         from control subwindows */
+      if (GetFocus() != hwnd)
+       {
+         SetFocus (hwnd);
+         mswindows_enqueue_magic_event (hwnd, WM_SETFOCUS);
+       }
     }
   else
     {
       event->event_type = button_release_event;
       ReleaseCapture ();
     }
-  
+
   mswindows_enqueue_dispatch_event (emacs_event);
 }
 
@@ -949,7 +975,7 @@ static void
 mswindows_enqueue_keypress_event (HWND hwnd, Lisp_Object keysym, int mods)
 {
   Lisp_Object emacs_event = Fmake_event (Qnil, Qnil);
-  struct Lisp_Event* event = XEVENT(emacs_event);
+  Lisp_Event* event = XEVENT(emacs_event);
 
   event->channel = mswindows_find_console(hwnd);
   event->timestamp = GetMessageTime();
@@ -967,16 +993,16 @@ static Lisp_Object
 mswindows_dequeue_dispatch_event ()
 {
   Lisp_Object event;
-  struct Lisp_Event* sevt;
+  Lisp_Event* sevt;
 
   assert (!NILP(mswindows_u_dispatch_event_queue) ||
          !NILP(mswindows_s_dispatch_event_queue));
 
   event = dequeue_event (
-                NILP(mswindows_u_dispatch_event_queue) ? 
-                        &mswindows_s_dispatch_event_queue : 
+                NILP(mswindows_u_dispatch_event_queue) ?
+                        &mswindows_s_dispatch_event_queue :
                         &mswindows_u_dispatch_event_queue,
-                NILP(mswindows_u_dispatch_event_queue) ? 
+                NILP(mswindows_u_dispatch_event_queue) ?
                         &mswindows_s_dispatch_event_queue_tail :
                         &mswindows_u_dispatch_event_queue_tail);
 
@@ -993,22 +1019,22 @@ mswindows_dequeue_dispatch_event ()
 
 /*
  * Remove and return the first emacs event on the dispatch queue that matches
- * the supplied event
- * Timeout event matches if interval_id equals to that of the given event.
+ * the supplied event.
+ * Timeout event matches if interval_id is equal to that of the given event.
  * Keypress event matches if logical AND between modifiers bitmask of the
- * event in the queue and that of the given event is non-zero
- * For all other event types, this function asserts.
+ * event in the queue and that of the given event is non-zero.
+ * For all other event types, this function aborts.
  */
 
 Lisp_Object
-mswindows_cancel_dispatch_event (struct Lisp_Event* match)
+mswindows_cancel_dispatch_event (Lisp_Event *match)
 {
   Lisp_Object event;
-  Lisp_Object previous_event=Qnil;
+  Lisp_Object previous_event = Qnil;
   int user_p = mswindows_user_event_p (match);
-  Lisp_Object* head = user_p ? &mswindows_u_dispatch_event_queue : 
+  Lisp_Object* head = user_p ? &mswindows_u_dispatch_event_queue :
                               &mswindows_s_dispatch_event_queue;
-  Lisp_Object* tail = user_p ? &mswindows_u_dispatch_event_queue_tail : 
+  Lisp_Object* tail = user_p ? &mswindows_u_dispatch_event_queue_tail :
                               &mswindows_s_dispatch_event_queue_tail;
 
   assert (match->event_type == timeout_event
@@ -1016,19 +1042,12 @@ mswindows_cancel_dispatch_event (struct Lisp_Event* match)
 
   EVENT_CHAIN_LOOP (event, *head)
     {
-      int found = 1;
-      if (XEVENT_TYPE (event) != match->event_type)
-       found = 0;
-      if (found && match->event_type == timeout_event
-         && (XEVENT(event)->event.timeout.interval_id !=
-             match->event.timeout.interval_id))
-       found = 0;
-      if (found && match->event_type == key_press_event
-         && ((XEVENT(event)->event.key.modifiers &
-             match->event.key.modifiers) == 0))
-       found = 0;
-
-      if (found)
+      Lisp_Event *e = XEVENT (event);
+      if ((e->event_type == match->event_type) &&
+         ((e->event_type == timeout_event) ?
+          (e->event.timeout.interval_id == match->event.timeout.interval_id) :
+          /* Must be key_press_event */
+          ((e->event.key.modifiers & match->event.key.modifiers) != 0)))
        {
          if (NILP (previous_event))
            dequeue_event (head, tail);
@@ -1038,7 +1057,7 @@ mswindows_cancel_dispatch_event (struct Lisp_Event* match)
              if (EQ (*tail, event))
                *tail = previous_event;
            }
-         
+
          return event;
        }
       previous_event = event;
@@ -1046,6 +1065,7 @@ mswindows_cancel_dispatch_event (struct Lisp_Event* match)
   return Qnil;
 }
 \f
+#ifndef HAVE_MSG_SELECT
 /************************************************************************/
 /*                     Waitable handles manipulation                    */
 /************************************************************************/
@@ -1078,9 +1098,10 @@ remove_waitable_handle (HANDLE h)
   if (ix < 0)
     return;
 
-  mswindows_waitable_handles [ix] = 
+  mswindows_waitable_handles [ix] =
     mswindows_waitable_handles [--mswindows_waitable_count];
 }
+#endif /* HAVE_MSG_SELECT */
 
 \f
 /************************************************************************/
@@ -1101,7 +1122,7 @@ mswindows_protect_modal_loop (Lisp_Object (*bfun) (Lisp_Object barg),
 {
   Lisp_Object tmp;
 
-  ++mswindows_in_modal_loop; 
+  ++mswindows_in_modal_loop;
   tmp = condition_case_1 (Qt,
                          bfun, barg,
                          mswindows_modal_loop_error_handler, Qnil);
@@ -1125,7 +1146,7 @@ mswindows_unmodalize_signal_maybe (void)
 }
 
 /*
- * This is an unsafe part of event pump, guarded by 
+ * This is an unsafe part of event pump, guarded by
  * condition_case. See mswindows_pump_outstanding_events
  */
 static Lisp_Object
@@ -1149,7 +1170,7 @@ mswindows_unsafe_pump_events (Lisp_Object u_n_u_s_e_d)
 
   Fdeallocate_event (event);
   UNGCPRO;
-  
+
   /* Qt becomes return value of mswindows_pump_outstanding_events
      once we get here */
   return Qt;
@@ -1164,7 +1185,7 @@ mswindows_unsafe_pump_events (Lisp_Object u_n_u_s_e_d)
  * neither are waitable handles checked. The function pumps
  * thus only dispatch events already queued, as well as those
  * resulted in dispatching thereof. This is done by setting
- * module local variable mswidows_in_modal_loop to nonzero.
+ * module local variable mswindows_in_modal_loop to nonzero.
  *
  * Return value is Qt if no errors was trapped, or Qunbound if
  * there was an error.
@@ -1182,7 +1203,7 @@ mswindows_unsafe_pump_events (Lisp_Object u_n_u_s_e_d)
  * If the value of mswindows_error_caught_in_modal_loop is not
  * nil already upon entry, the function just returns non-nil.
  * This situation means that a new event has been queued while
- * cancleng mode. The event will be dequeued on the next regular
+ * in cancel mode. The event will be dequeued on the next regular
  * call of next-event; the pump is off since error is caught.
  * The caller must *unconditionally* cancel modal loop if the
  * value returned by this function is nil. Otherwise, everything
@@ -1197,29 +1218,49 @@ mswindows_pump_outstanding_events (void)
   Lisp_Object result = Qt;
   struct gcpro gcpro1;
   GCPRO1 (result);
-  
+
   if (NILP(mswindows_error_caught_in_modal_loop))
       result = mswindows_protect_modal_loop (mswindows_unsafe_pump_events, Qnil);
   UNGCPRO;
   return result;
 }
 
-static void 
-mswindows_drain_windows_queue ()
+/*
+ * KEYBOARD_ONLY_P is set to non-zero when we are called from
+ * QUITP, and are interesting in keyboard messages only.
+ */
+static void
+mswindows_drain_windows_queue (int keyboard_only_till_quit_char_p)
 {
   MSG msg;
-  while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
+
+  /* Minimize the hassle of misordered events by not fetching
+     past quit char if called from QUITP; */
+  while (!(keyboard_only_till_quit_char_p &&
+          mswindows_quit_chars_count > 0) &&
+        PeekMessage (&msg, NULL,
+                     keyboard_only_till_quit_char_p ? WM_KEYFIRST : 0,
+                     keyboard_only_till_quit_char_p ? WM_KEYLAST : 0,
+                     PM_REMOVE))
     {
+      /* We have to translate messages that are not sent to the main
+         window. This is so that key presses work ok in things like
+         edit fields. However, we *musn't* translate message for the
+         main window as this is handled in the wnd proc. */
+      if (GetWindowLong (msg.hwnd, GWL_STYLE) & WS_CHILD)
+       {
+         TranslateMessage (&msg);
+       }
       DispatchMessage (&msg);
       mswindows_unmodalize_signal_maybe ();
     }
 }
 
-/* 
- * This is a special flavour of the mswindows_need_event function,
+/*
+ * This is a special flavor of the mswindows_need_event function,
  * used while in event pump. Actually, there is only kind of events
  * allowed while in event pump: a timer.  An attempt to fetch any
- * other event leads to a dealock, as there's no source of user input
+ * other event leads to a deadlock, as there's no source of user input
  * ('cause event pump mirrors windows modal loop, which is a sole
  * owner of thread message queue).
  *
@@ -1248,7 +1289,7 @@ mswindows_need_event_in_modal_loop (int badly_p)
       /* We'll deadlock if go waiting */
       if (mswindows_pending_timers_count == 0)
        error ("Deadlock due to an attempt to call next-event in a wrong context");
-      
+
       /* Fetch and dispatch any pending timers */
       GetMessage (&msg, NULL, WM_TIMER, WM_TIMER);
       DispatchMessage (&msg);
@@ -1273,9 +1314,13 @@ mswindows_need_event (int badly_p)
       return;
     }
 
+#if 0
   /* Have to drain Windows message queue first, otherwise, we may miss
      quit char when called from quit_p */
+  /* #### This is, ehm, not quite true -- this function is not
+     called from quit_p. --kkm */
   mswindows_drain_windows_queue ();
+#endif
 
   while (NILP (mswindows_u_dispatch_event_queue)
         && NILP (mswindows_s_dispatch_event_queue))
@@ -1285,7 +1330,7 @@ mswindows_need_event (int badly_p)
       SELECT_TYPE temp_mask = input_wait_mask;
       EMACS_TIME sometime;
       EMACS_SELECT_TIME select_time_to_block, *pointer_to_this;
-      
+
       if (badly_p)
        pointer_to_this = 0;
       else
@@ -1294,19 +1339,42 @@ mswindows_need_event (int badly_p)
          EMACS_TIME_TO_SELECT_TIME (sometime, select_time_to_block);
          pointer_to_this = &select_time_to_block;
        }
+
       active = select (MAXDESC, &temp_mask, 0, 0, pointer_to_this);
-      
+
       if (active == 0)
        {
+         assert (!badly_p);
          return;               /* timeout */
        }
       else if (active > 0)
        {
          if (FD_ISSET (windows_fd, &temp_mask))
            {
-             mswindows_drain_windows_queue ();
+             mswindows_drain_windows_queue (0);
            }
-         
+#ifdef HAVE_TTY
+         /* Look for a TTY event */
+         for (i = 0; i < MAXDESC-1; i++)
+           {
+             /* To avoid race conditions (among other things, an infinite
+                loop when called from Fdiscard_input()), we must return
+                user events ahead of process events. */
+             if (FD_ISSET (i, &temp_mask) && FD_ISSET (i, &tty_only_mask))
+               {
+                 struct console *c = tty_find_console_from_fd (i);
+                 Lisp_Object emacs_event = Fmake_event (Qnil, Qnil);
+                 Lisp_Event* event = XEVENT (emacs_event);
+
+                 assert (c);
+                 if (read_event_from_tty_or_stream_desc (event, c, i))
+                   {
+                     mswindows_enqueue_dispatch_event (emacs_event);
+                     return;
+                   }
+               }
+           }
+#endif
          /* Look for a process event */
          for (i = 0; i < MAXDESC-1; i++)
            {
@@ -1314,16 +1382,11 @@ mswindows_need_event (int badly_p)
                {
                  if (FD_ISSET (i, &process_only_mask))
                    {
-                     struct Lisp_Process *p =
+                     Lisp_Process *p =
                        get_process_from_usid (FD_TO_USID(i));
-                     
+
                      mswindows_enqueue_process_event (p);
                    }
-                 else if (FD_ISSET (i, &tty_only_mask))
-                   {
-                               /* do we care about tty events? Do we
-                                   ever get tty events? */
-                   }
                  else
                    {
                      /* We might get here when a fake event came
@@ -1340,7 +1403,7 @@ mswindows_need_event (int badly_p)
        {
          if (errno != EINTR)
            {
-             /* something bad happended */
+             /* something bad happened */
              assert(0);
            }
        }
@@ -1360,7 +1423,7 @@ mswindows_need_event (int badly_p)
     assert ((!badly_p && active == WAIT_TIMEOUT) ||
            (active >= WAIT_OBJECT_0 &&
             active <= WAIT_OBJECT_0 + mswindows_waitable_count));
-    
+
     if (active == WAIT_TIMEOUT)
       {
        /* No luck trying - just return what we've already got */
@@ -1369,13 +1432,13 @@ mswindows_need_event (int badly_p)
     else if (active == WAIT_OBJECT_0 + mswindows_waitable_count)
       {
        /* Got your message, thanks */
-       mswindows_drain_windows_queue ();
+       mswindows_drain_windows_queue (0);
       }
     else
       {
        int ix = active - WAIT_OBJECT_0;
-       /* First, try to find which process' ouptut has signaled */
-       struct Lisp_Process *p = 
+       /* First, try to find which process' output has signaled */
+       Lisp_Process *p =
          get_process_from_usid (HANDLE_TO_USID (mswindows_waitable_handles[ix]));
        if (p != NULL)
          {
@@ -1385,7 +1448,7 @@ mswindows_need_event (int badly_p)
        else
          {
            /* None. This means that the process handle itself has signaled.
-              Remove the handle from the wait vector, and make status_ntoify
+              Remove the handle from the wait vector, and make status_notify
               note the exited process */
            mswindows_waitable_handles [ix] =
              mswindows_waitable_handles [--mswindows_waitable_count];
@@ -1403,14 +1466,14 @@ mswindows_need_event (int badly_p)
 /*                           Event generators                           */
 /************************************************************************/
 
-/* 
+/*
  * Callback procedure for synchronous timer messages
  */
 static void CALLBACK
 mswindows_wm_timer_callback (HWND hwnd, UINT umsg, UINT id_timer, DWORD dwtime)
 {
   Lisp_Object emacs_event = Fmake_event (Qnil, Qnil);
-  struct Lisp_Event *event = XEVENT (emacs_event);
+  Lisp_Event *event = XEVENT (emacs_event);
 
   if (KillTimer (NULL, id_timer))
     --mswindows_pending_timers_count;
@@ -1419,11 +1482,13 @@ mswindows_wm_timer_callback (HWND hwnd, UINT umsg, UINT id_timer, DWORD dwtime)
   event->timestamp = dwtime;
   event->event_type = timeout_event;
   event->event.timeout.interval_id = id_timer;
+  event->event.timeout.function = Qnil;
+  event->event.timeout.object = Qnil;
 
   mswindows_enqueue_dispatch_event (emacs_event);
 }
 
-/* 
+/*
  * Callback procedure for dde messages
  *
  * We execute a dde Open("file") by simulating a file drop, so dde support
@@ -1434,9 +1499,9 @@ HDDEDATA CALLBACK
 mswindows_dde_callback (UINT uType, UINT uFmt, HCONV hconv,
                        HSZ hszTopic, HSZ hszItem, HDDEDATA hdata,
                        DWORD dwData1, DWORD dwData2)
-{ 
+{
   switch (uType)
-    { 
+    {
     case XTYP_CONNECT:
       if (!DdeCmpStringHandles (hszTopic, mswindows_dde_topic_system))
        return (HDDEDATA)TRUE;
@@ -1453,7 +1518,7 @@ mswindows_dde_callback (UINT uType, UINT uFmt, HCONV hconv,
          return (DdeCreateDataHandle (mswindows_dde_mlid, (LPBYTE)pairs,
                                       sizeof (pairs), 0L, 0, uFmt, 0));
       }
-      return (HDDEDATA)NULL; 
+      return (HDDEDATA)NULL;
 
     case XTYP_EXECUTE:
       if (!DdeCmpStringHandles (hszTopic, mswindows_dde_topic_system))
@@ -1466,7 +1531,7 @@ mswindows_dde_callback (UINT uType, UINT uFmt, HCONV hconv,
           Lisp_Object l_dndlist = Qnil;
          Lisp_Object emacs_event = Fmake_event (Qnil, Qnil);
          Lisp_Object frmcons, devcons, concons;
-         struct Lisp_Event *event = XEVENT (emacs_event);
+         Lisp_Event *event = XEVENT (emacs_event);
 
          DdeGetData (hdata, cmd, len, 0);
          cmd[len] = '\0';
@@ -1534,32 +1599,52 @@ mswindows_dde_callback (UINT uType, UINT uFmt, HCONV hconv,
          UNGCPRO;
          return (HDDEDATA) DDE_FACK;
        }
-      DdeFreeDataHandle (hdata); 
+      DdeFreeDataHandle (hdata);
       return (HDDEDATA) DDE_FNOTPROCESSED;
 
-    default: 
-      return (HDDEDATA) NULL; 
-    } 
+    default:
+      return (HDDEDATA) NULL;
+    }
 }
 #endif
 
 /*
+ * Returns 1 if a key is a real modifier or special key, which 
+ * is better handled by DefWindowProc
+ */
+static int
+key_needs_default_processing_p (UINT vkey)
+{
+  if (mswindows_meta_activates_menu && vkey == VK_MENU)
+    return 1;
+
+  return 0;
+}
+
+/*
  * The windows procedure for the window class XEMACS_CLASS
  */
 LRESULT WINAPI
 mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
 {
-  /* Note: Remember to initialise emacs_event and event before use.
+  /* Note: Remember to initialize emacs_event and event before use.
      This code calls code that can GC. You must GCPRO before calling such code. */
   Lisp_Object emacs_event = Qnil;
   Lisp_Object fobj = Qnil;
 
-  struct Lisp_Event *event;
+  Lisp_Event *event;
   struct frame *frame;
   struct mswindows_frame* msframe;
 
   switch (message)
   {
+  case WM_DESTROYCLIPBOARD:
+    /* We own the clipboard and someone else wants it.  Delete our
+       cached copy of the clipboard contents so we'll ask for it from
+       Windows again when someone does a paste. */
+    handle_selection_clear(QCLIPBOARD);
+    break;
+
   case WM_ERASEBKGND:
     /* Erase background only during non-dynamic sizing */
     msframe  = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
@@ -1572,34 +1657,86 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
     mswindows_enqueue_misc_user_event (fobj, Qeval, list3 (Qdelete_frame, fobj, Qt));
     break;
 
+  case WM_KEYUP:
+  case WM_SYSKEYUP:
+    /* See Win95 comment under WM_KEYDOWN */
+    {
+      BYTE keymap[256];
+
+      if (wParam == VK_CONTROL)
+        {
+         GetKeyboardState (keymap);
+         keymap [(lParam & 0x1000000) ? VK_RCONTROL : VK_LCONTROL] &= ~0x80;
+         SetKeyboardState (keymap);
+       }
+      else if (wParam == VK_MENU)
+       {
+         GetKeyboardState (keymap);
+         keymap [(lParam & 0x1000000) ? VK_RMENU : VK_LMENU] &= ~0x80;
+         SetKeyboardState (keymap);
+       }
+    };
+    if (key_needs_default_processing_p (wParam))
+      goto defproc;
+    else
+      break;
+
   case WM_KEYDOWN:
   case WM_SYSKEYDOWN:
+    /* In some locales the right-hand Alt key is labelled AltGr. This key
+     * should produce alternative charcaters when combined with another key.
+     * eg on a German keyboard pressing AltGr+q should produce '@'.
+     * AltGr generates exactly the same keystrokes as LCtrl+RAlt. But if
+     * TranslateMessage() is called with *any* combination of Ctrl+Alt down,
+     * it translates as if AltGr were down.
+     * We get round this by removing all modifiers from the keymap before
+     * calling TranslateMessage() unless AltGr is *really* down. */
     {
       BYTE keymap[256];
       int has_AltGr = mswindows_current_layout_has_AltGr ();
       int mods;
+      int extendedp = lParam & 0x1000000;
       Lisp_Object keysym;
 
       GetKeyboardState (keymap);
       mods = mswindows_modifier_state (keymap, has_AltGr);
 
-      /* Handle those keys that TranslateMessage won't generate a WM_CHAR for */
-      if (!NILP (keysym = mswindows_key_to_emacs_keysym(wParam, mods)))
+      /* Handle non-printables */
+      if (!NILP (keysym = mswindows_key_to_emacs_keysym (wParam, mods,
+                                                        extendedp)))
        mswindows_enqueue_keypress_event (hwnd, keysym, mods);
-      else
+      else     /* Normal keys & modifiers */
        {
          int quit_ch = CONSOLE_QUIT_CHAR (XCONSOLE (mswindows_find_console (hwnd)));
          BYTE keymap_orig[256];
-         MSG msg = { hwnd, message, wParam, lParam, GetMessageTime(), (GetMessagePos()) };
+         POINT pnt = { LOWORD (GetMessagePos()), HIWORD (GetMessagePos()) };
+         MSG msg;
+
+         msg.hwnd = hwnd;
+         msg.message = message;
+         msg.wParam = wParam;
+         msg.lParam = lParam;
+         msg.time = GetMessageTime();
+         msg.pt = pnt;
+
+         /* GetKeyboardState() does not work as documented on Win95. We have
+          * to loosely track Left and Right modifiers on behalf of the OS,
+          * without screwing up Windows NT which tracks them properly. */
+         if (wParam == VK_CONTROL)
+           keymap [extendedp ? VK_RCONTROL : VK_LCONTROL] |= 0x80;
+         else if (wParam == VK_MENU)
+           keymap [extendedp ? VK_RMENU : VK_LMENU] |= 0x80;
+
          memcpy (keymap_orig, keymap, 256);
 
          /* Remove shift modifier from an ascii character */
          mods &= ~MOD_SHIFT;
 
-         /* Clear control and alt modifiers out of the keymap */
+         /* Clear control and alt modifiers unless AltGr is pressed */
          keymap [VK_RCONTROL] = 0;
          keymap [VK_LMENU] = 0;
-         if (!has_AltGr || !(keymap [VK_LCONTROL] & 0x80) || !(keymap [VK_RMENU] & 0x80))
+         if (!has_AltGr || !(keymap [VK_LCONTROL] & 0x80)
+             || !(keymap [VK_RMENU] & 0x80))
            {
              keymap [VK_LCONTROL] = 0;
              keymap [VK_CONTROL] = 0;
@@ -1608,7 +1745,7 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
            }
          SetKeyboardState (keymap);
 
-         /* Have some WM_[SYS]CHARS in the queue */
+         /* Maybe generate some WM_[SYS]CHARs in the queue */
          TranslateMessage (&msg);
 
          while (PeekMessage (&msg, hwnd, WM_CHAR, WM_CHAR, PM_REMOVE)
@@ -1635,10 +1772,10 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
          SetKeyboardState (keymap_orig);
        } /* else */
     }
-    /* F10 causes menu activation by default. We do not want this */
-    if (wParam != VK_F10)
+    if (key_needs_default_processing_p (wParam))
       goto defproc;
-    break;
+    else
+      break;
 
   case WM_MBUTTONDOWN:
   case WM_MBUTTONUP:
@@ -1648,7 +1785,7 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
     mswindows_enqueue_mouse_button_event (hwnd, message,
                                          MAKEPOINTS (lParam), GetMessageTime());
     break;
-    
+
   case WM_LBUTTONUP:
     msframe  = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
     msframe->last_click_time =  GetMessageTime();
@@ -1770,7 +1907,7 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
       }
     msframe->last_click_time =  GetMessageTime();
     break;
-       
+
   case WM_TIMER:
     if (wParam == BUTTON_2_TIMER_ID)
       {
@@ -1795,13 +1932,13 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
     break;
 
   case WM_MOUSEMOVE:
-    /* Optimization: don't report mouse movement while size is changind */
+    /* Optimization: don't report mouse movement while size is changing */
     msframe  = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
     if (!msframe->sizing)
     {
       /* When waiting for the second mouse button to finish
         button2 emulation, and have moved too far, just pretend
-        as if timer has expired. This impoves drag-select feedback */
+        as if timer has expired. This improves drag-select feedback */
       if ((msframe->button2_need_lbutton || msframe->button2_need_rbutton)
          && !mswindows_button2_near_enough (msframe->last_click_point,
                                             MAKEPOINTS (lParam)))
@@ -1819,7 +1956,7 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
       event->event.motion.x = MAKEPOINTS(lParam).x;
       event->event.motion.y = MAKEPOINTS(lParam).y;
       event->event.motion.modifiers = mswindows_modifier_state (NULL, 0);
-      
+
       mswindows_enqueue_dispatch_event (emacs_event);
     }
     break;
@@ -1832,18 +1969,21 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
                                       Qcancel_mode_internal, Qnil);
     break;
 
-#ifdef HAVE_TOOLBARS
   case WM_NOTIFY:
     {
-      LPTOOLTIPTEXT tttext = (LPTOOLTIPTEXT)lParam;
-      Lisp_Object btext;
-      if (tttext->hdr.code ==  TTN_NEEDTEXT)    
+      LPNMHDR nmhdr = (LPNMHDR)lParam;
+
+      if (nmhdr->code ==  TTN_NEEDTEXT)
        {
+#ifdef HAVE_TOOLBARS
+         LPTOOLTIPTEXT tttext = (LPTOOLTIPTEXT)lParam;
+         Lisp_Object btext;
+
          /* find out which toolbar */
          frame = XFRAME (mswindows_find_frame (hwnd));
-         btext = mswindows_get_toolbar_button_text ( frame, 
-                                                     tttext->hdr.idFrom );
-         
+         btext = mswindows_get_toolbar_button_text ( frame,
+                                                     nmhdr->idFrom );
+
          tttext->lpszText = NULL;
          tttext->hinst = NULL;
 
@@ -1851,27 +1991,66 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
            {
              /* I think this is safe since the text will only go away
                  when the toolbar does...*/
-             tttext->lpszText=XSTRING_DATA (btext);
+             TO_EXTERNAL_FORMAT (LISP_STRING, btext,
+                                 C_STRING_ALLOCA, tttext->lpszText,
+                                 Qnative);
            }
-#if 0
-         tttext->uFlags |= TTF_DI_SETITEM;
 #endif
-       }    
+       }
+      /* handle tree view callbacks */
+      else if (nmhdr->code == TVN_SELCHANGED)
+       {
+         NM_TREEVIEW* ptree = (NM_TREEVIEW*)lParam;
+         frame = XFRAME (mswindows_find_frame (hwnd));
+         mswindows_handle_gui_wm_command (frame, 0, ptree->itemNew.lParam);
+       }
+      /* handle tab control callbacks */
+      else if (nmhdr->code == TCN_SELCHANGE)
+       {
+         TC_ITEM item;
+         int idx = SendMessage (nmhdr->hwndFrom, TCM_GETCURSEL, 0, 0);
+         frame = XFRAME (mswindows_find_frame (hwnd));
+
+         item.mask = TCIF_PARAM;
+         SendMessage (nmhdr->hwndFrom, TCM_GETITEM, (WPARAM)idx,
+                      (LPARAM)&item);
+
+         mswindows_handle_gui_wm_command (frame, 0, item.lParam);
+       }
     }
     break;
-#endif
-    
+
   case WM_PAINT:
     {
-      PAINTSTRUCT paintStruct;
-      
-      frame = XFRAME (mswindows_find_frame (hwnd));
+      /* According to the docs we need to check GetUpdateRect() before
+         actually doing a WM_PAINT */
+      if (GetUpdateRect (hwnd, NULL, FALSE))
+       {
+         PAINTSTRUCT paintStruct;
+         int x, y, width, height;
 
-      BeginPaint (hwnd, &paintStruct);
-      mswindows_redraw_exposed_area (frame,
-                       paintStruct.rcPaint.left, paintStruct.rcPaint.top,
-                       paintStruct.rcPaint.right, paintStruct.rcPaint.bottom);
-      EndPaint (hwnd, &paintStruct);
+         frame = XFRAME (mswindows_find_frame (hwnd));
+
+         BeginPaint (hwnd, &paintStruct);
+         x = paintStruct.rcPaint.left;
+         y = paintStruct.rcPaint.top;
+         width = paintStruct.rcPaint.right - paintStruct.rcPaint.left;
+         height = paintStruct.rcPaint.bottom - paintStruct.rcPaint.top;
+         /* Normally we want to ignore expose events when child
+            windows are unmapped, however once we are in the guts of
+            WM_PAINT we need to make sure that we don't register
+            unmaps then because they will not actually occur. */
+         if (!check_for_ignored_expose (frame, x, y, width, height))
+           {
+             hold_ignored_expose_registration = 1;
+             mswindows_redraw_exposed_area (frame, x, y, width, height);
+             hold_ignored_expose_registration = 0;
+           }
+
+         EndPaint (hwnd, &paintStruct);
+       }
+      else
+       goto defproc;
     }
     break;
 
@@ -1914,8 +2093,8 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
          if (FRAME_MSWINDOWS_TARGET_RECT (frame))
            {
              /* Yes, we have to size again */
-             mswindows_size_frame_internal ( frame, 
-                                             FRAME_MSWINDOWS_TARGET_RECT 
+             mswindows_size_frame_internal ( frame,
+                                             FRAME_MSWINDOWS_TARGET_RECT
                                              (frame));
              /* Reset so we do not get here again. The SetWindowPos call in
               * mswindows_size_frame_internal can cause recursion here. */
@@ -1930,7 +2109,7 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
              if (!msframe->sizing && !FRAME_VISIBLE_P (frame))
                mswindows_enqueue_magic_event (hwnd, XM_MAPFRAME);
              FRAME_VISIBLE_P (frame) = 1;
-             
+
              if (!msframe->sizing || mswindows_dynamic_frame_resize)
                redisplay ();
            }
@@ -2021,7 +2200,24 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
          SendMessage (hwndScrollBar, WM_CANCELMODE, 0, 0);
        }
       UNGCPRO;
-      break;     
+      break;
+    }
+
+  case WM_MOUSEWHEEL:
+    {
+      int keys = LOWORD (wParam); /* Modifier key flags */
+      int delta = (short) HIWORD (wParam); /* Wheel rotation amount */
+      struct gcpro gcpro1, gcpro2;
+
+      if (mswindows_handle_mousewheel_event (mswindows_find_frame (hwnd), keys,  delta))
+       {
+         GCPRO2 (emacs_event, fobj);
+         mswindows_pump_outstanding_events (); /* Can GC */
+         UNGCPRO;
+       }
+      else
+       goto defproc;
+      break;
     }
 #endif
 
@@ -2048,6 +2244,7 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
   case WM_COMMAND:
     {
       WORD id = LOWORD (wParam);
+      WORD nid = HIWORD (wParam);
       HWND cid = (HWND)lParam;
       frame = XFRAME (mswindows_find_frame (hwnd));
 
@@ -2055,17 +2252,83 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
       if (!NILP (mswindows_handle_toolbar_wm_command (frame, cid, id)))
        break;
 #endif
-
+      /* widgets in a buffer only eval a callback for suitable events.*/
+      switch (nid)
+       {
+       case BN_CLICKED:
+       case EN_CHANGE:
+       case CBN_EDITCHANGE:
+       case CBN_SELCHANGE:
+         if (!NILP (mswindows_handle_gui_wm_command (frame, cid, id)))
+           return 0;
+       }
+      /* menubars always must come last since the hashtables do not
+         always exist*/
 #ifdef HAVE_MENUBARS
       if (!NILP (mswindows_handle_wm_command (frame, id)))
        break;
 #endif
 
-      /* Bite me - a spurious command. This cannot happen. */
-      error ("XEMACS BUG: Cannot decode command message");
+      return DefWindowProc (hwnd, message, wParam, lParam);
+      /* Bite me - a spurious command. This used to not be able to
+         happen but with the introduction of widgets its now
+         possible. */
     }
   break;
 
+  case WM_CTLCOLORBTN:
+  case WM_CTLCOLORLISTBOX:
+  case WM_CTLCOLOREDIT:
+  case WM_CTLCOLORSTATIC:
+  case WM_CTLCOLORSCROLLBAR:
+    {
+      /* if we get an opportunity to paint a widget then do so if
+         there is an appropriate face */
+      HWND crtlwnd = (HWND)lParam;
+      LONG ii = GetWindowLong (crtlwnd, GWL_USERDATA);
+      if (ii)
+       {
+         Lisp_Object image_instance;
+         VOID_TO_LISP (image_instance, ii);
+         if (IMAGE_INSTANCEP (image_instance)
+             &&
+             IMAGE_INSTANCE_TYPE_P (image_instance, IMAGE_WIDGET))
+           {
+             /* set colors for the buttons */
+             HDC hdc = (HDC)wParam;
+             if (last_widget_brushed != ii)
+               {
+                 if (widget_brush)
+                   DeleteObject (widget_brush);
+                 widget_brush = CreateSolidBrush
+                   (COLOR_INSTANCE_MSWINDOWS_COLOR
+                    (XCOLOR_INSTANCE
+                     (FACE_BACKGROUND
+                      (XIMAGE_INSTANCE_WIDGET_FACE (image_instance),
+                       XIMAGE_INSTANCE_SUBWINDOW_FRAME (image_instance)))));
+               }
+             last_widget_brushed = ii;
+             SetTextColor
+               (hdc,
+                COLOR_INSTANCE_MSWINDOWS_COLOR
+                (XCOLOR_INSTANCE
+                 (FACE_FOREGROUND
+                  (XIMAGE_INSTANCE_WIDGET_FACE (image_instance),
+                   XIMAGE_INSTANCE_SUBWINDOW_FRAME (image_instance)))));
+             SetBkMode (hdc, OPAQUE);
+             SetBkColor
+               (hdc,
+                COLOR_INSTANCE_MSWINDOWS_COLOR
+                (XCOLOR_INSTANCE
+                 (FACE_BACKGROUND
+                  (XIMAGE_INSTANCE_WIDGET_FACE (image_instance),
+                   XIMAGE_INSTANCE_SUBWINDOW_FRAME (image_instance)))));
+             return (LRESULT)widget_brush;
+           }
+       }
+    }
+    goto defproc;
+
 #ifdef HAVE_DRAGNDROP
   case WM_DROPFILES:   /* implementation ripped-off from event-Xt.c */
     {
@@ -2229,67 +2492,100 @@ int mswindows_modifier_state (BYTE* keymap, int has_AltGr)
  * Only returns non-Qnil for keys that don't generate WM_CHAR messages
  * or whose ASCII codes (like space) xemacs doesn't like.
  * Virtual key values are defined in winresrc.h
- * XXX I'm not sure that KEYSYM("name") is the best thing to use here.
  */
-Lisp_Object mswindows_key_to_emacs_keysym(int mswindows_key, int mods)
+Lisp_Object mswindows_key_to_emacs_keysym (int mswindows_key, int mods,
+                                          int extendedp)
 {
-  switch (mswindows_key)
-  {
-  /* First the predefined ones */
-  case VK_BACK:                return QKbackspace;
-  case VK_TAB:         return QKtab;
-  case '\n':           return QKlinefeed;  /* No VK_LINEFEED in winresrc.h */
-  case VK_RETURN:      return QKreturn;
-  case VK_ESCAPE:      return QKescape;
-  case VK_SPACE:       return QKspace;
-  case VK_DELETE:      return QKdelete;
-
-  /* The rest */
-  case VK_CLEAR:       return KEYSYM ("clear");  /* Should do ^L ? */
-  case VK_PRIOR:       return KEYSYM ("prior");
-  case VK_NEXT:                return KEYSYM ("next");
-  case VK_END:         return KEYSYM ("end");
-  case VK_HOME:                return KEYSYM ("home");
-  case VK_LEFT:                return KEYSYM ("left");
-  case VK_UP:          return KEYSYM ("up");
-  case VK_RIGHT:       return KEYSYM ("right");
-  case VK_DOWN:                return KEYSYM ("down");
-  case VK_SELECT:      return KEYSYM ("select");
-  case VK_PRINT:       return KEYSYM ("print");
-  case VK_EXECUTE:     return KEYSYM ("execute");
-  case VK_SNAPSHOT:    return KEYSYM ("print");
-  case VK_INSERT:      return KEYSYM ("insert");
-  case VK_HELP:                return KEYSYM ("help");
-#if 0  /* XXX What are these supposed to do? */
-  case VK_LWIN         return KEYSYM ("");
-  case VK_RWIN         return KEYSYM ("");
+  if (extendedp)       /* Keys not present on a 82 key keyboard */
+    {
+      switch (mswindows_key)
+        {
+       case VK_RETURN:         return KEYSYM ("kp-enter");
+       case VK_PRIOR:          return KEYSYM ("prior");
+       case VK_NEXT:           return KEYSYM ("next");
+       case VK_END:            return KEYSYM ("end");
+       case VK_HOME:           return KEYSYM ("home");
+       case VK_LEFT:           return KEYSYM ("left");
+       case VK_UP:             return KEYSYM ("up");
+       case VK_RIGHT:          return KEYSYM ("right");
+       case VK_DOWN:           return KEYSYM ("down");
+       case VK_INSERT:         return KEYSYM ("insert");
+       case VK_DELETE:         return QKdelete;
+       }
+    }
+  else
+    {
+      switch (mswindows_key)
+       {
+       case VK_BACK:           return QKbackspace;
+       case VK_TAB:            return QKtab;
+       case '\n':              return QKlinefeed;
+       case VK_CLEAR:          return KEYSYM ("clear");
+       case VK_RETURN:         return QKreturn;
+       case VK_ESCAPE:         return QKescape;
+       case VK_SPACE:          return QKspace;
+       case VK_PRIOR:          return KEYSYM ("kp-prior");
+       case VK_NEXT:           return KEYSYM ("kp-next");
+       case VK_END:            return KEYSYM ("kp-end");
+       case VK_HOME:           return KEYSYM ("kp-home");
+       case VK_LEFT:           return KEYSYM ("kp-left");
+       case VK_UP:             return KEYSYM ("kp-up");
+       case VK_RIGHT:          return KEYSYM ("kp-right");
+       case VK_DOWN:           return KEYSYM ("kp-down");
+       case VK_SELECT:         return KEYSYM ("select");
+       case VK_PRINT:          return KEYSYM ("print");
+       case VK_EXECUTE:        return KEYSYM ("execute");
+       case VK_SNAPSHOT:       return KEYSYM ("print");
+       case VK_INSERT:         return KEYSYM ("kp-insert");
+       case VK_DELETE:         return KEYSYM ("kp-delete");
+       case VK_HELP:           return KEYSYM ("help");
+#if 0  /* FSF Emacs allows these to return configurable syms/mods */
+       case VK_LWIN            return KEYSYM ("");
+       case VK_RWIN            return KEYSYM ("");
 #endif
-  case VK_APPS:                return KEYSYM ("menu");
-  case VK_F1:          return KEYSYM ("f1");
-  case VK_F2:          return KEYSYM ("f2");
-  case VK_F3:          return KEYSYM ("f3");
-  case VK_F4:          return KEYSYM ("f4");
-  case VK_F5:          return KEYSYM ("f5");
-  case VK_F6:          return KEYSYM ("f6");
-  case VK_F7:          return KEYSYM ("f7");
-  case VK_F8:          return KEYSYM ("f8");
-  case VK_F9:          return KEYSYM ("f9");
-  case VK_F10:         return KEYSYM ("f10");
-  case VK_F11:         return KEYSYM ("f11");
-  case VK_F12:         return KEYSYM ("f12");
-  case VK_F13:         return KEYSYM ("f13");
-  case VK_F14:         return KEYSYM ("f14");
-  case VK_F15:         return KEYSYM ("f15");
-  case VK_F16:         return KEYSYM ("f16");
-  case VK_F17:         return KEYSYM ("f17");
-  case VK_F18:         return KEYSYM ("f18");
-  case VK_F19:         return KEYSYM ("f19");
-  case VK_F20:         return KEYSYM ("f20");
-  case VK_F21:         return KEYSYM ("f21");
-  case VK_F22:         return KEYSYM ("f22");
-  case VK_F23:         return KEYSYM ("f23");
-  case VK_F24:         return KEYSYM ("f24");
-  }
+       case VK_APPS:           return KEYSYM ("menu");
+       case VK_NUMPAD0:        return KEYSYM ("kp-0");
+       case VK_NUMPAD1:        return KEYSYM ("kp-1");
+       case VK_NUMPAD2:        return KEYSYM ("kp-2");
+       case VK_NUMPAD3:        return KEYSYM ("kp-3");
+       case VK_NUMPAD4:        return KEYSYM ("kp-4");
+       case VK_NUMPAD5:        return KEYSYM ("kp-5");
+       case VK_NUMPAD6:        return KEYSYM ("kp-6");
+       case VK_NUMPAD7:        return KEYSYM ("kp-7");
+       case VK_NUMPAD8:        return KEYSYM ("kp-8");
+       case VK_NUMPAD9:        return KEYSYM ("kp-9");
+       case VK_MULTIPLY:       return KEYSYM ("kp-multiply");
+       case VK_ADD:            return KEYSYM ("kp-add");
+       case VK_SEPARATOR:      return KEYSYM ("kp-separator");
+       case VK_SUBTRACT:       return KEYSYM ("kp-subtract");
+       case VK_DECIMAL:        return KEYSYM ("kp-decimal");
+       case VK_DIVIDE:         return KEYSYM ("kp-divide");
+       case VK_F1:             return KEYSYM ("f1");
+       case VK_F2:             return KEYSYM ("f2");
+       case VK_F3:             return KEYSYM ("f3");
+       case VK_F4:             return KEYSYM ("f4");
+       case VK_F5:             return KEYSYM ("f5");
+       case VK_F6:             return KEYSYM ("f6");
+       case VK_F7:             return KEYSYM ("f7");
+       case VK_F8:             return KEYSYM ("f8");
+       case VK_F9:             return KEYSYM ("f9");
+       case VK_F10:            return KEYSYM ("f10");
+       case VK_F11:            return KEYSYM ("f11");
+       case VK_F12:            return KEYSYM ("f12");
+       case VK_F13:            return KEYSYM ("f13");
+       case VK_F14:            return KEYSYM ("f14");
+       case VK_F15:            return KEYSYM ("f15");
+       case VK_F16:            return KEYSYM ("f16");
+       case VK_F17:            return KEYSYM ("f17");
+       case VK_F18:            return KEYSYM ("f18");
+       case VK_F19:            return KEYSYM ("f19");
+       case VK_F20:            return KEYSYM ("f20");
+       case VK_F21:            return KEYSYM ("f21");
+       case VK_F22:            return KEYSYM ("f22");
+       case VK_F23:            return KEYSYM ("f23");
+       case VK_F24:            return KEYSYM ("f24");
+       }
+    }
   return Qnil;
 }
 
@@ -2347,7 +2643,7 @@ emacs_mswindows_add_timeout (EMACS_TIME thyme)
 static void
 emacs_mswindows_remove_timeout (int id)
 {
-  struct Lisp_Event match_against;
+  Lisp_Event match_against;
   Lisp_Object emacs_event;
 
   if (KillTimer (NULL, id))
@@ -2383,13 +2679,13 @@ emacs_mswindows_event_pending_p (int user_p)
  * Return the next event
  */
 static void
-emacs_mswindows_next_event (struct Lisp_Event *emacs_event)
+emacs_mswindows_next_event (Lisp_Event *emacs_event)
 {
   Lisp_Object event, event2;
 
   mswindows_need_event (1);
 
-  event = mswindows_dequeue_dispatch_event (!NILP(mswindows_u_dispatch_event_queue));
+  event = mswindows_dequeue_dispatch_event ();
   XSETEVENT (event2, emacs_event);
   Fcopy_event (event, event2);
   Fdeallocate_event (event);
@@ -2399,13 +2695,13 @@ emacs_mswindows_next_event (struct Lisp_Event *emacs_event)
  * Handle a magic event off the dispatch queue.
  */
 static void
-emacs_mswindows_handle_magic_event (struct Lisp_Event *emacs_event)
+emacs_mswindows_handle_magic_event (Lisp_Event *emacs_event)
 {
   switch (EVENT_MSWINDOWS_MAGIC_TYPE(emacs_event))
     {
     case XM_BUMPQUEUE:
       break;
-    
+
     case WM_SETFOCUS:
     case WM_KILLFOCUS:
       {
@@ -2433,13 +2729,13 @@ emacs_mswindows_handle_magic_event (struct Lisp_Event *emacs_event)
     case XM_UNMAPFRAME:
       {
        Lisp_Object frame = EVENT_CHANNEL (emacs_event);
-       va_run_hook_with_args (EVENT_MSWINDOWS_MAGIC_TYPE(emacs_event) 
+       va_run_hook_with_args (EVENT_MSWINDOWS_MAGIC_TYPE(emacs_event)
                               == XM_MAPFRAME ?
-                              Qmap_frame_hook : Qunmap_frame_hook, 
+                              Qmap_frame_hook : Qunmap_frame_hook,
                               1, frame);
       }
       break;
-                           
+
       /* #### What about Enter & Leave */
 #if 0
       va_run_hook_with_args (in_p ? Qmouse_enter_frame_hook :
@@ -2451,8 +2747,9 @@ emacs_mswindows_handle_magic_event (struct Lisp_Event *emacs_event)
     }
 }
 
+#ifndef HAVE_MSG_SELECT
 static HANDLE
-get_process_input_waitable (struct Lisp_Process *process)
+get_process_input_waitable (Lisp_Process *process)
 {
   Lisp_Object instr, outstr, p;
   XSETPROCESS (p, process);
@@ -2468,7 +2765,7 @@ get_process_input_waitable (struct Lisp_Process *process)
 }
 
 static void
-emacs_mswindows_select_process (struct Lisp_Process *process)
+emacs_mswindows_select_process (Lisp_Process *process)
 {
   HANDLE hev = get_process_input_waitable (process);
 
@@ -2493,22 +2790,35 @@ emacs_mswindows_select_process (struct Lisp_Process *process)
 }
 
 static void
-emacs_mswindows_unselect_process (struct Lisp_Process *process)
+emacs_mswindows_unselect_process (Lisp_Process *process)
 {
   /* Process handle is removed in the event loop as soon
      as it is signaled, so don't bother here about it */
   HANDLE hev = get_process_input_waitable (process);
   remove_waitable_handle (hev);
 }
+#endif /* HAVE_MSG_SELECT */
 
 static void
 emacs_mswindows_select_console (struct console *con)
 {
+#ifdef HAVE_MSG_SELECT
+  if (CONSOLE_MSWINDOWS_P (con))
+    return; /* mswindows consoles are automatically selected */
+
+  event_stream_unixoid_select_console (con);
+#endif
 }
 
 static void
 emacs_mswindows_unselect_console (struct console *con)
 {
+#ifdef HAVE_MSG_SELECT
+  if (CONSOLE_MSWINDOWS_P (con))
+    return; /* mswindows consoles are automatically selected */
+
+  event_stream_unixoid_unselect_console (con);
+#endif
 }
 
 static void
@@ -2519,27 +2829,32 @@ emacs_mswindows_quit_p (void)
   if (mswindows_in_modal_loop)
     return;
 
-  /* Drain windows queue. This sets up number of quit
-     characters in in the queue */
-  mswindows_drain_windows_queue ();
+  /* Drain windows queue. This sets up number of quit characters in
+     the queue */
+  mswindows_drain_windows_queue (1);
 
   if (mswindows_quit_chars_count > 0)
     {
       /* Yes there's a hidden one... Throw it away */
-      struct Lisp_Event match_against;
+      Lisp_Event match_against;
       Lisp_Object emacs_event;
+      int critical_p = 0;
 
       match_against.event_type = key_press_event;
       match_against.event.key.modifiers = FAKE_MOD_QUIT;
 
-      emacs_event = mswindows_cancel_dispatch_event (&match_against);
-      assert (!NILP (emacs_event));
+      while (mswindows_quit_chars_count-- > 0)
+       {
+         emacs_event = mswindows_cancel_dispatch_event (&match_against);
+         assert (!NILP (emacs_event));
+         
+         if (XEVENT(emacs_event)->event.key.modifiers & MOD_SHIFT)
+           critical_p = 1;
 
-      Vquit_flag = (XEVENT(emacs_event)->event.key.modifiers & MOD_SHIFT
-                   ? Qcritical : Qt);
+         Fdeallocate_event(emacs_event);
+       }
 
-      Fdeallocate_event(emacs_event);
-      --mswindows_quit_chars_count;
+      Vquit_flag = critical_p ? Qcritical : Qt;
     }
 }
 
@@ -2651,7 +2966,7 @@ emacs_mswindows_delete_stream_pair (Lisp_Object instream,
    If we've still got pointers to it in this file, we're gonna lose hard.
  */
 void
-debug_process_finalization (struct Lisp_Process *p)
+debug_process_finalization (Lisp_Process *p)
 {
 #if 0 /* #### */
   Lisp_Object instr, outstr;
@@ -2669,20 +2984,10 @@ debug_process_finalization (struct Lisp_Process *p)
 /************************************************************************/
 /*                            initialization                            */
 /************************************************************************/
+
 void
-vars_of_event_mswindows (void)
+reinit_vars_of_event_mswindows (void)
 {
-  mswindows_u_dispatch_event_queue = Qnil;
-  staticpro (&mswindows_u_dispatch_event_queue);
-  mswindows_u_dispatch_event_queue_tail = Qnil;
-
-  mswindows_s_dispatch_event_queue = Qnil;
-  staticpro (&mswindows_s_dispatch_event_queue);
-  mswindows_s_dispatch_event_queue_tail = Qnil;
-
-  mswindows_error_caught_in_modal_loop = Qnil;
-  staticpro (&mswindows_error_caught_in_modal_loop);
   mswindows_in_modal_loop = 0;
   mswindows_pending_timers_count = 0;
 
@@ -2697,10 +3002,10 @@ vars_of_event_mswindows (void)
   mswindows_event_stream->select_console_cb    = emacs_mswindows_select_console;
   mswindows_event_stream->unselect_console_cb  = emacs_mswindows_unselect_console;
 #ifdef HAVE_MSG_SELECT
-  mswindows_event_stream->select_process_cb    = 
-    (void (*)(struct Lisp_Process*))event_stream_unixoid_select_process;
-  mswindows_event_stream->unselect_process_cb  = 
-    (void (*)(struct Lisp_Process*))event_stream_unixoid_unselect_process;
+  mswindows_event_stream->select_process_cb    =
+    (void (*)(Lisp_Process*))event_stream_unixoid_select_process;
+  mswindows_event_stream->unselect_process_cb  =
+    (void (*)(Lisp_Process*))event_stream_unixoid_unselect_process;
   mswindows_event_stream->create_stream_pair_cb = event_stream_unixoid_create_stream_pair;
   mswindows_event_stream->delete_stream_pair_cb = event_stream_unixoid_delete_stream_pair;
 #else
@@ -2709,6 +3014,31 @@ vars_of_event_mswindows (void)
   mswindows_event_stream->create_stream_pair_cb = emacs_mswindows_create_stream_pair;
   mswindows_event_stream->delete_stream_pair_cb = emacs_mswindows_delete_stream_pair;
 #endif
+}
+
+void
+vars_of_event_mswindows (void)
+{
+  reinit_vars_of_event_mswindows ();
+
+  mswindows_u_dispatch_event_queue = Qnil;
+  staticpro (&mswindows_u_dispatch_event_queue);
+  mswindows_u_dispatch_event_queue_tail = Qnil;
+  pdump_wire (&mswindows_u_dispatch_event_queue_tail);
+
+  mswindows_s_dispatch_event_queue = Qnil;
+  staticpro (&mswindows_s_dispatch_event_queue);
+  mswindows_s_dispatch_event_queue_tail = Qnil;
+  pdump_wire (&mswindows_s_dispatch_event_queue_tail);
+
+  mswindows_error_caught_in_modal_loop = Qnil;
+  staticpro (&mswindows_error_caught_in_modal_loop);
+
+  DEFVAR_BOOL ("mswindows-meta-activates-menu", &mswindows_meta_activates_menu /*
+*Controls whether pressing and releasing the Meta (Alt) key should
+activate the menubar.
+Default is t.
+*/ );
 
   DEFVAR_BOOL ("mswindows-dynamic-frame-resize", &mswindows_dynamic_frame_resize /*
 *Controls redrawing frame contents during mouse-drag or keyboard resize
@@ -2737,7 +3067,7 @@ Number of physical mouse buttons.
 
   DEFVAR_INT ("mswindows-mouse-button-max-skew-x", &mswindows_mouse_button_max_skew_x /*
 *Maximum horizontal distance in pixels between points in which left and
-right button clicks occured for them to be translated into single
+right button clicks occurred for them to be translated into single
 middle button event. Clicks must occur in time not longer than defined
 by the variable `mswindows-mouse-button-tolerance'.
 If negative or zero, currently set system default is used instead.
@@ -2745,7 +3075,7 @@ If negative or zero, currently set system default is used instead.
 
   DEFVAR_INT ("mswindows-mouse-button-max-skew-y", &mswindows_mouse_button_max_skew_y /*
 *Maximum vertical distance in pixels between points in which left and
-right button clicks occured for them to be translated into single
+right button clicks occurred for them to be translated into single
 middle button event. Clicks must occur in time not longer than defined
 by the variable `mswindows-mouse-button-tolerance'.
 If negative or zero, currently set system default is used instead.
@@ -2754,6 +3084,7 @@ If negative or zero, currently set system default is used instead.
   mswindows_mouse_button_max_skew_x = 0;
   mswindows_mouse_button_max_skew_y = 0;
   mswindows_mouse_button_tolerance = 0;
+  mswindows_meta_activates_menu = 1;
 }
 
 void
@@ -2778,9 +3109,7 @@ init_event_mswindows_late (void)
   windows_fd = open("/dev/windows", O_RDONLY | O_NONBLOCK, 0);
   assert (windows_fd>=0);
   FD_SET (windows_fd, &input_wait_mask);
-  /* for some reason I get blocks on the signal event pipe, which is
-     bad... 
-     signal_event_pipe_initialized = 0; */
+  FD_ZERO(&zero_mask);
 #endif
 
   event_stream = mswindows_event_stream;