import xemacs-21.2.37
[chise/xemacs-chise.git.1] / src / event-msw.c
index f31da31..a09c81f 100644 (file)
@@ -1,7 +1,7 @@
-/* The  mswindows event_stream interface.
+/* The mswindows event_stream interface.
    Copyright (C) 1991, 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
    Copyright (C) 1995 Sun Microsystems, Inc.
-   Copyright (C) 1996 Ben Wing.
+   Copyright (C) 1996, 2000 Ben Wing.
    Copyright (C) 1997 Jonathan Harris.
 
 This file is part of XEmacs.
@@ -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>
@@ -40,6 +41,7 @@ Boston, MA 02111-1307, USA.  */
 #endif
 
 #ifdef HAVE_MENUBARS
+# include "menubar.h"
 # include "menubar-msw.h"
 #endif
 
@@ -50,23 +52,33 @@ 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 "window.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"
-#elif defined(__CYGWIN32__)
+#include "console-tty.h"
+#elif defined(CYGWIN)
 typedef unsigned int SOCKET;
 #endif
 #include <io.h>
 #include <errno.h>
 
+#if !(defined(CYGWIN) || defined(MINGW))
+# include <shlobj.h>   /* For IShellLink */
+#endif
+
 #ifdef HAVE_MENUBARS
 #define ADJR_MENUFLAG TRUE
 #else
@@ -80,18 +92,17 @@ 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 int mswindows_modifier_state (BYTE* keymap, int has_AltGr);
+static Lisp_Object mswindows_key_to_emacs_keysym (int mswindows_key, int mods,
+                                                 int extendedp);
+static int mswindows_modifier_state (BYTE* keymap, DWORD fwKeys,
+                                    int has_AltGr);
 static void mswindows_set_chord_timer (HWND hwnd);
 static int mswindows_button2_near_enough (POINTS p1, POINTS p2);
 static int mswindows_current_layout_has_AltGr (void);
+static int mswindows_handle_sticky_modifiers (WPARAM wParam, LPARAM lParam,
+                                             int downp, int keyp);
 
 static struct event_stream *mswindows_event_stream;
 
@@ -115,11 +126,17 @@ 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()
@@ -128,11 +145,16 @@ int mswindows_quit_chars_count = 0;
 
 /* These are Lisp integers; see DEFVARS in this file for description. */
 int mswindows_dynamic_frame_resize;
+int mswindows_alt_by_itself_activates_menu;
 int mswindows_num_mouse_buttons;
 int mswindows_mouse_button_max_skew_x;
 int mswindows_mouse_button_max_skew_y;
 int mswindows_mouse_button_tolerance;
 
+#ifdef DEBUG_XEMACS
+int debug_mswindows_events;
+#endif
+
 /* This is the event signaled by the event pump.
    See mswindows_pump_outstanding_events for comments */
 static Lisp_Object mswindows_error_caught_in_modal_loop;
@@ -140,6 +162,8 @@ static int mswindows_in_modal_loop;
 
 /* Count of wound timers */
 static int mswindows_pending_timers_count;
+
+static DWORD mswindows_last_mouse_button_state;
 \f
 /************************************************************************/
 /*                Pipe instream - reads process output                  */
@@ -155,7 +179,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
@@ -166,11 +190,11 @@ static int mswindows_pending_timers_count;
 struct ntpipe_slurp_stream_shared_data
 {
   HANDLE hev_thread;   /* Our thread blocks on this, signaled by caller */
-                       /* This is a manual-reset object.                */
+  /* This is a manual-reset object.             */
   HANDLE hev_caller;   /* Caller blocks on this, and we signal it       */
-                       /* This is a manual-reset object.                */
+  /* This is a manual-reset object.             */
   HANDLE hev_unsleep;  /* Pipe read delay is canceled if this is set    */
-                       /* This is a manual-reset object.                */
+  /* This is a manual-reset object.             */
   HANDLE hpipe;                /* Pipe read end handle.                         */
   LONG   die_p;                /* Thread must exit ASAP if non-zero             */
   BOOL   eof_p   : 1;  /* Set when thread saw EOF                       */
@@ -181,7 +205,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
@@ -194,7 +218,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)
 {
@@ -209,7 +233,7 @@ slurper_free_shared_data_maybe (struct ntpipe_slurp_stream_shared_data* s)
 }
 
 static struct ntpipe_slurp_stream_shared_data*
-slurper_allocate_shared_data()
+slurper_allocate_shared_data (void)
 {
   int i=0;
   for (i=0; i<MAX_SLURP_STREAMS; i++)
@@ -253,7 +277,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. */
@@ -268,7 +292,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);
     }
@@ -342,11 +366,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)
@@ -355,7 +379,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);
@@ -406,7 +430,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;
@@ -415,11 +439,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,13 +474,13 @@ init_slurp_stream (void)
 #define NTPIPE_SHOVE_STREAM_DATA(stream) \
   LSTREAM_TYPE_DATA (stream, ntpipe_shove)
 
-#define MAX_SHOVE_BUFFER_SIZE 128
-     
+#define MAX_SHOVE_BUFFER_SIZE 512
+
 struct ntpipe_shove_stream
 {
   LPARAM user_data;    /* Any user data stored in the stream object     */
   HANDLE hev_thread;   /* Our thread blocks on this, signaled by caller */
-                       /* This is an auto-reset object.                 */
+  /* This is an auto-reset object.              */
   HANDLE hpipe;                /* Pipe write end handle.                        */
   HANDLE hthread;      /* Reader thread handle.                         */
   char  buffer[MAX_SHOVE_BUFFER_SIZE]; /* Buffer being written          */
@@ -470,6 +494,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)
 {
@@ -477,21 +502,24 @@ shove_thread (LPVOID vparam)
 
   for (;;)
     {
-      DWORD bytes_written; 
+      DWORD bytes_written;
 
       /* Block on event and wait for a job */
       InterlockedIncrement (&s->idle_p);
       WaitForSingleObject (s->hev_thread, INFINITE);
 
-      if (s->die_p)
-       break;
-
-      /* Write passed buffer */
-      if (!WriteFile (s->hpipe, s->buffer, s->size, &bytes_written, NULL)
-         || bytes_written != s->size)
+      /* Write passed buffer if any */
+      if (s->size > 0)
        {
-         s->error_p = TRUE;
-         InterlockedIncrement (&s->die_p);
+         if (!WriteFile (s->hpipe, s->buffer, s->size, &bytes_written, NULL)
+             || bytes_written != s->size)
+           {
+             s->error_p = TRUE;
+             InterlockedIncrement (&s->die_p);
+           }
+         /* Set size to zero so we won't write it again if the closer sets
+            die_p and kicks us */
+         s->size = 0;
        }
 
       if (s->die_p)
@@ -514,7 +542,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);
@@ -524,6 +552,15 @@ make_ntpipe_output_stream (HANDLE hpipe, LPARAM param)
       return Qnil;
     }
 
+  /* Set the priority of the thread higher so we don't end up waiting
+     on it to send things. */
+  if (!SetThreadPriority (s->hthread, THREAD_PRIORITY_HIGHEST))
+    {
+      CloseHandle (s->hthread);
+      Lstream_delete (lstr);
+      return Qnil;
+    }
+
   /* hev_thread is an auto-reset event, initially nonsignaled */
   s->hev_thread = CreateEvent (NULL, FALSE, FALSE, NULL);
 
@@ -541,8 +578,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);
@@ -563,6 +601,10 @@ ntpipe_shove_writer (Lstream *stream, const unsigned char *data, size_t size)
   /* Start output */
   InterlockedDecrement (&s->idle_p);
   SetEvent (s->hev_thread);
+  /* Give it a chance to run -- this dramatically improves performance
+     of things like crypt. */
+  if (xSwitchToThread) /* not in Win9x or NT 3.51 */
+    (void) xSwitchToThread ();
   return size;
 }
 
@@ -581,14 +623,18 @@ ntpipe_shove_closer (Lstream *stream)
   /* Force thread stop */
   InterlockedIncrement (&s->die_p);
 
-  /* Close pipe handle, possibly breaking it */
-  CloseHandle (s->hpipe);
-
-  /* Thread will end upon unblocking */
+  /* Thread will end upon unblocking.  If it's already unblocked this will
+     do nothing, but the thread won't look at die_p until it's written any
+     pending output. */
   SetEvent (s->hev_thread);
 
   /* Wait while thread terminates */
   WaitForSingleObject (s->hthread, INFINITE);
+
+  /* Close pipe handle, possibly breaking it */
+  CloseHandle (s->hpipe);
+
+  /* Close the thread handle */
   CloseHandle (s->hthread);
 
   /* Destroy the event */
@@ -618,8 +664,8 @@ struct winsock_stream
   SOCKET s;                    /* Socket handle (which is a Win32 handle)   */
   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 long bufsize;       /* Number of bytes last read                 */
+  unsigned long 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          */
@@ -651,7 +697,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);
@@ -684,7 +730,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);
@@ -697,8 +743,8 @@ winsock_reader (Lstream *stream, unsigned char *data, size_t size)
   return size;
 }
 
-static int
-winsock_writer (Lstream *stream, CONST unsigned char *data, size_t size)
+static ssize_t
+winsock_writer (Lstream *stream, const unsigned char *data, size_t size)
 {
   struct winsock_stream *str = WINSOCK_STREAM_DATA (stream);
 
@@ -725,7 +771,7 @@ winsock_writer (Lstream *stream, CONST unsigned char *data, size_t size)
 
   if (size == 0)
     return 0;
-  
+
   {
     ResetEvent (str->ov.hEvent);
 
@@ -771,7 +817,7 @@ winsock_was_blocked_p (Lstream *stream)
 }
 
 static Lisp_Object
-make_winsock_stream_1 (SOCKET s, LPARAM param, CONST char *mode)
+make_winsock_stream_1 (SOCKET s, LPARAM param, const char *mode)
 {
   Lisp_Object obj;
   Lstream *lstr = Lstream_new (lstream_winsock, mode);
@@ -839,7 +885,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
@@ -847,18 +893,18 @@ 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
+void
 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 : 
-                       &mswindows_s_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);
+                &mswindows_s_dispatch_event_queue_tail);
 
   /* Avoid blocking on WaitMessage */
   PostMessage (NULL, XM_BUMPQUEUE, 0, 0);
@@ -875,10 +921,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;
 
@@ -886,24 +933,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);
 
@@ -915,37 +962,47 @@ 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,
+                                     int mods, DWORD when)
 {
+  int downp = (msg == WM_LBUTTONDOWN || msg == WM_MBUTTONDOWN ||
+              msg == WM_RBUTTONDOWN);
 
   /* We always use last message time, because mouse button
      events may get delayed, and XEmacs double click
      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);
+  mswindows_handle_sticky_modifiers (0, 0, downp, 0);
+  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)
+  event->event.button.modifiers = mswindows_modifier_state (NULL, mods, 0);
+
+  if (downp)
     {
       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);
 }
 
@@ -953,7 +1010,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();
@@ -968,19 +1025,19 @@ mswindows_enqueue_keypress_event (HWND hwnd, Lisp_Object keysym, int mods)
  * Give a preference to user events over non-user ones.
  */
 static Lisp_Object
-mswindows_dequeue_dispatch_event ()
+mswindows_dequeue_dispatch_event (void)
 {
   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);
 
@@ -997,42 +1054,35 @@ 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 : 
-                              &mswindows_s_dispatch_event_queue;
-  Lisp_Object* tail = user_p ? &mswindows_u_dispatch_event_queue_tail : 
-                              &mswindows_s_dispatch_event_queue_tail;
+  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 :
+    &mswindows_s_dispatch_event_queue_tail;
 
   assert (match->event_type == timeout_event
          || match->event_type == key_press_event);
 
   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);
@@ -1042,7 +1092,7 @@ mswindows_cancel_dispatch_event (struct Lisp_Event* match)
              if (EQ (*tail, event))
                *tail = previous_event;
            }
-         
+
          return event;
        }
       previous_event = event;
@@ -1050,6 +1100,7 @@ mswindows_cancel_dispatch_event (struct Lisp_Event* match)
   return Qnil;
 }
 \f
+#ifndef HAVE_MSG_SELECT
 /************************************************************************/
 /*                     Waitable handles manipulation                    */
 /************************************************************************/
@@ -1082,9 +1133,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
 /************************************************************************/
@@ -1105,7 +1157,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);
@@ -1129,7 +1181,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
@@ -1153,7 +1205,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;
@@ -1168,7 +1220,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.
@@ -1186,7 +1238,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
@@ -1201,29 +1253,83 @@ 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);
+    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 (void)
 {
   MSG msg;
+
+  /* should call mswindows_need_event_in_modal_loop() if in modal loop */
+  assert (!mswindows_in_modal_loop);
+
   while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
     {
+      char class_name_buf [sizeof (XEMACS_CLASS) + 2] = "";
+
+      /* Don't translate messages destined for a dialog box, this
+        makes keyboard traversal work. I think?? */
+      if (mswindows_is_dialog_msg (&msg))
+       {
+         mswindows_unmodalize_signal_maybe ();
+         continue;
+       }
+
+      /* We have to translate messages that are not sent to an XEmacs
+         frame. This is so that key presses work ok in things like
+         edit fields. However, we *musn't* translate message for XEmacs
+         frames as this is handled in the wnd proc.
+         We also have to avoid generating paint magic events for windows
+        that aren't XEmacs frames */
+      /* GetClassName will truncate a longer class name. By adding one
+        extra character, we are forcing textual comparison to fail
+        if the name is longer than XEMACS_CLASS */
+
+      GetClassName (msg.hwnd, class_name_buf, sizeof (class_name_buf) - 1);
+      if (stricmp (class_name_buf, XEMACS_CLASS) != 0)
+       {
+         /* Not an XEmacs frame */
+         TranslateMessage (&msg);
+       }
+      else if (msg.message == WM_PAINT)
+       {
+         struct mswindows_frame* msframe;
+
+         /* hdc will be NULL unless this is a subwindow - in which case we
+            shouldn't have received a paint message for it here. */
+         assert (msg.wParam == 0);
+
+         /* Queue a magic event for handling when safe */
+         msframe =
+           FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (msg.hwnd)));
+         if (!msframe->paint_pending)
+           {
+             msframe->paint_pending = 1;
+             mswindows_enqueue_magic_event (msg.hwnd, WM_PAINT);
+           }
+         /* Don't dispatch. WM_PAINT is always the last message in the
+            queue so it's OK to just return. */
+         return;
+       }
       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).
  *
@@ -1252,7 +1358,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);
@@ -1277,10 +1383,6 @@ mswindows_need_event (int badly_p)
       return;
     }
 
-  /* Have to drain Windows message queue first, otherwise, we may miss
-     quit char when called from quit_p */
-  mswindows_drain_windows_queue ();
-
   while (NILP (mswindows_u_dispatch_event_queue)
         && NILP (mswindows_s_dispatch_event_queue))
     {
@@ -1289,7 +1391,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
@@ -1299,32 +1401,11 @@ mswindows_need_event (int badly_p)
          pointer_to_this = &select_time_to_block;
        }
 
-      /* select() is slow and buggy so if we don't have any processes
-         just wait as normal */
-      if (memcmp (&process_only_mask, &zero_mask, sizeof(SELECT_TYPE))==0)
-       {
-         /* Now try getting a message or process event */
-         active = MsgWaitForMultipleObjects (0, mswindows_waitable_handles,
-                                             FALSE, badly_p ? INFINITE : 0,
-                                             QS_ALLINPUT);
-         
-         if (active == WAIT_TIMEOUT)
-           {
-             /* No luck trying - just return what we've already got */
-             return;
-           }
-         else if (active == WAIT_OBJECT_0)
-           {
-             /* Got your message, thanks */
-             mswindows_drain_windows_queue ();
-             continue;
-           }
-       }
-
       active = select (MAXDESC, &temp_mask, 0, 0, pointer_to_this);
-      
+
       if (active == 0)
        {
+         assert (!badly_p);
          return;               /* timeout */
        }
       else if (active > 0)
@@ -1333,32 +1414,51 @@ mswindows_need_event (int badly_p)
            {
              mswindows_drain_windows_queue ();
            }
-         
-         /* Look for a process event */
-         for (i = 0; i < MAXDESC-1; i++)
+         else
            {
-             if (FD_ISSET (i, &temp_mask))
+#ifdef HAVE_TTY
+             /* Look for a TTY event */
+             for (i = 0; i < MAXDESC-1; i++)
                {
-                 if (FD_ISSET (i, &process_only_mask))
+                 /* 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 Lisp_Process *p =
-                       get_process_from_usid (FD_TO_USID(i));
-                     
-                     mswindows_enqueue_process_event (p);
+                     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;
+                       }
                    }
-                 else if (FD_ISSET (i, &tty_only_mask))
-                   {
-                               /* do we care about tty events? Do we
-                                   ever get tty events? */
-                   }
-                 else
+               }
+#endif
+             /* Look for a process event */
+             for (i = 0; i < MAXDESC-1; i++)
+               {
+                 if (FD_ISSET (i, &temp_mask))
                    {
-                     /* We might get here when a fake event came
-                         through a signal. Return a dummy event, so
-                         that a cycle of the command loop will
-                         occur. */
-                     drain_signal_event_pipe ();
-                     mswindows_enqueue_magic_event (NULL, XM_BUMPQUEUE);
+                     if (FD_ISSET (i, &process_only_mask))
+                       {
+                         Lisp_Process *p =
+                           get_process_from_usid (FD_TO_USID(i));
+
+                         mswindows_enqueue_process_event (p);
+                       }
+                     else
+                       {
+                         /* We might get here when a fake event came
+                            through a signal. Return a dummy event, so
+                            that a cycle of the command loop will
+                            occur. */
+                         drain_signal_event_pipe ();
+                         mswindows_enqueue_magic_event (NULL, XM_BUMPQUEUE);
+                       }
                    }
                }
            }
@@ -1367,7 +1467,7 @@ mswindows_need_event (int badly_p)
        {
          if (errno != EINTR)
            {
-             /* something bad happended */
+             /* something bad happened */
              assert(0);
            }
        }
@@ -1377,67 +1477,79 @@ mswindows_need_event (int badly_p)
        }
 #else
       /* Now try getting a message or process event */
-    active = MsgWaitForMultipleObjects (mswindows_waitable_count,
-                                       mswindows_waitable_handles,
-                                       FALSE, badly_p ? INFINITE : 0,
-                                       QS_ALLINPUT);
-
-    /* This will assert if handle being waited for becomes abandoned.
-       Not the case currently tho */
-    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 */
-       return;
-      }
-    else if (active == WAIT_OBJECT_0 + mswindows_waitable_count)
-      {
-       /* Got your message, thanks */
-       mswindows_drain_windows_queue ();
-      }
-    else
-      {
-       int ix = active - WAIT_OBJECT_0;
-       /* First, try to find which process' ouptut has signaled */
-       struct Lisp_Process *p = 
-         get_process_from_usid (HANDLE_TO_USID (mswindows_waitable_handles[ix]));
-       if (p != NULL)
-         {
-           /* Found a signaled process input handle */
-           mswindows_enqueue_process_event (p);
-         }
-       else
-         {
-           /* None. This means that the process handle itself has signaled.
-              Remove the handle from the wait vector, and make status_ntoify
-              note the exited process */
-           mswindows_waitable_handles [ix] =
-             mswindows_waitable_handles [--mswindows_waitable_count];
-           kick_status_notify ();
-           /* Have to return something: there may be no accompanying
-              process event */
-           mswindows_enqueue_magic_event (NULL, XM_BUMPQUEUE);
-         }
-      }
+      active = MsgWaitForMultipleObjects (mswindows_waitable_count,
+                                         mswindows_waitable_handles,
+                                         FALSE, badly_p ? INFINITE : 0,
+                                         QS_ALLINPUT);
+
+      /* This will assert if handle being waited for becomes abandoned.
+        Not the case currently tho */
+      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 */
+         return;
+       }
+      else if (active == WAIT_OBJECT_0 + mswindows_waitable_count)
+       {
+         /* Got your message, thanks */
+         mswindows_drain_windows_queue ();
+       }
+      else
+       {
+         int ix = active - WAIT_OBJECT_0;
+         /* 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)
+           {
+             /* Found a signaled process input handle */
+             mswindows_enqueue_process_event (p);
+           }
+         else
+           {
+             /* None. This means that the process handle itself has signaled.
+                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];
+             kick_status_notify ();
+             /* We need to return a process event here so that
+                (1) accept-process-output will return when called on this
+                process, and (2) status notifications will happen in
+                accept-process-output, sleep-for, and sit-for. */
+             /* #### horrible kludge till my real process fixes go in.
+              */
+             if (!NILP (Vprocess_list))
+               {
+                 Lisp_Object vaffanculo = XCAR (Vprocess_list);
+                 mswindows_enqueue_process_event (XPROCESS (vaffanculo));
+               }
+             else /* trash me soon. */
+               /* Have to return something: there may be no accompanying
+                  process event */
+               mswindows_enqueue_magic_event (NULL, XM_BUMPQUEUE);
+           }
+       }
 #endif
-  } /* while */
+    } /* while */
 }
 
 /************************************************************************/
 /*                           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;
@@ -1446,11 +1558,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
@@ -1461,9 +1575,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;
@@ -1476,24 +1590,24 @@ mswindows_dde_callback (UINT uType, UINT uFmt, HCONV hconv,
          { mswindows_dde_service, mswindows_dde_topic_system }, { 0, 0 } };
 
        if (!(hszItem  || DdeCmpStringHandles (hszItem, mswindows_dde_service)) &&
-           !(hszTopic || DdeCmpStringHandles (hszTopic, mswindows_dde_topic_system)));
+           !(hszTopic || DdeCmpStringHandles (hszTopic, mswindows_dde_topic_system)))
          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))
        {
          DWORD len = DdeGetData (hdata, NULL, 0, 0);
-         char *cmd = alloca (len+1);
+         LPBYTE cmd = (LPBYTE) alloca (len+1);
          char *end;
          char *filename;
          struct gcpro gcpro1, gcpro2;
           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';
@@ -1525,7 +1639,7 @@ mswindows_dde_callback (UINT uType, UINT uFmt, HCONV hconv,
          if (*end)
            return DDE_FNOTPROCESSED;
 
-#ifdef __CYGWIN32__
+#ifdef CYGWIN
          filename = alloca (cygwin32_win32_to_posix_path_list_buf_size (cmd) + 5);
          strcpy (filename, "file:");
          cygwin32_win32_to_posix_path_list (cmd, filename+5);
@@ -1561,642 +1675,1300 @@ 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
 
 /*
- * The windows procedure for the window class XEMACS_CLASS
+ * Helper to do repainting - repaints can happen both from the windows
+ * procedure and from magic events
  */
-LRESULT WINAPI
-mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+static void
+mswindows_handle_paint (struct frame *frame)
 {
-  /* Note: Remember to initialise 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;
-  struct frame *frame;
-  struct mswindows_frame* msframe;
-
-  switch (message)
-  {
-  case WM_ERASEBKGND:
-    /* Erase background only during non-dynamic sizing */
-    msframe  = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
-    if (msframe->sizing && !mswindows_dynamic_frame_resize)
-      goto defproc;
-    return 1;
+  HWND hwnd = FRAME_MSWINDOWS_HANDLE (frame);
 
-  case WM_CLOSE:
-    fobj = mswindows_find_frame (hwnd);
-    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 */
+  /* According to the docs we need to check GetUpdateRect() before
+     actually doing a WM_PAINT */
+  if (GetUpdateRect (hwnd, NULL, FALSE))
     {
-      BYTE keymap[256];
+      PAINTSTRUCT paintStruct;
+      int x, y, width, height;
 
-      if (wParam == VK_CONTROL)
-        {
-         GetKeyboardState (keymap);
-         keymap [(lParam & 0x1000000) ? VK_RCONTROL : VK_LCONTROL] &= ~0x80;
-         SetKeyboardState (keymap);
-       }
-      else if (wParam == VK_MENU)
+      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. */
+      /* #### commenting out the next line seems to fix some problems
+        but not all.  only andy currently understands this stuff and
+        he needs to review it more carefully. --ben */
+      if (!check_for_ignored_expose (frame, x, y, width, height))
        {
-         GetKeyboardState (keymap);
-         keymap [(lParam & 0x1000000) ? VK_RMENU : VK_LMENU] &= ~0x80;
-         SetKeyboardState (keymap);
+         hold_ignored_expose_registration = 1;
+         mswindows_redraw_exposed_area (frame, x, y, width, height);
+         hold_ignored_expose_registration = 0;
        }
-    };
-    goto defproc;
-
-  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;
-      Lisp_Object keysym;
+      EndPaint (hwnd, &paintStruct);
+    }
+}
 
-      GetKeyboardState (keymap);
-      mods = mswindows_modifier_state (keymap, has_AltGr);
+/*
+ * 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_alt_by_itself_activates_menu && vkey == VK_MENU
+      /* if we let ALT activate the menu like this, then sticky ALT-modified
+        keystrokes become impossible. */
+      && !modifier_keys_are_sticky)
+    return 1;
 
-      /* Handle those keys for which TranslateMessage won't generate a WM_CHAR */
-      if (!NILP (keysym = mswindows_key_to_emacs_keysym(wParam, mods)))
-       mswindows_enqueue_keypress_event (hwnd, keysym, mods);
-      else
-       {
-         int quit_ch = CONSOLE_QUIT_CHAR (XCONSOLE (mswindows_find_console (hwnd)));
-         BYTE keymap_orig[256];
-         MSG msg = { hwnd, message, wParam, lParam, GetMessageTime(), (GetMessagePos()) };
-
-         /* 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 [(lParam & 0x1000000) ? VK_RCONTROL : VK_LCONTROL] |= 0x80;
-         else if (wParam == VK_MENU)
-           keymap [(lParam & 0x1000000) ? 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 unless AltGr is pressed */
-         keymap [VK_RCONTROL] = 0;
-         keymap [VK_LMENU] = 0;
-         if (!has_AltGr || !(keymap [VK_LCONTROL] & 0x80) || !(keymap [VK_RMENU] & 0x80))
-           {
-             keymap [VK_LCONTROL] = 0;
-             keymap [VK_CONTROL] = 0;
-             keymap [VK_RMENU] = 0;
-             keymap [VK_MENU] = 0;
-           }
-         SetKeyboardState (keymap);
+  return 0;
+}
 
-         /* Maybe generate some WM_[SYS]CHARs in the queue */
-         TranslateMessage (&msg);
+/* key-handling code is always ugly.  It just ends up working out
+   that way.
 
-         while (PeekMessage (&msg, hwnd, WM_CHAR, WM_CHAR, PM_REMOVE)
-                || PeekMessage (&msg, hwnd, WM_SYSCHAR, WM_SYSCHAR, PM_REMOVE))
-           {
-             int mods1 = mods;
-             WPARAM ch = msg.wParam;
-
-             /* If a quit char with no modifiers other than control and
-                shift, then mark it with a fake modifier, which is removed
-                upon dequeueing the event */
-             /* #### This might also not withstand localization, if
-                quit character is not a latin-1 symbol */
-             if (((quit_ch < ' ' && (mods & MOD_CONTROL) && quit_ch + 'a' - 1 == ch)
-                  || (quit_ch >= ' ' && !(mods & MOD_CONTROL) && quit_ch == ch))
-                 && ((mods  & ~(MOD_CONTROL | MOD_SHIFT)) == 0))
-               {
-                 mods1 |= FAKE_MOD_QUIT;
-                 ++mswindows_quit_chars_count;
-               }
+   #### Most of the sticky-modifier code below is copied from similar
+   code in event-Xt.c.  They should somehow or other be merged.
 
-             mswindows_enqueue_keypress_event (hwnd, make_char(ch), mods1);
-           } /* while */
-         SetKeyboardState (keymap_orig);
-       } /* else */
-    }
-    /* F10 causes menu activation by default. We do not want this */
-    if (wParam != VK_F10)
-      goto defproc;
-    break;
-
-  case WM_MBUTTONDOWN:
-  case WM_MBUTTONUP:
-    /* Real middle mouse button has nothing to do with emulated one:
-       if one wants to exercise fingers playing chords on the mouse,
-       he is allowed to do that! */
-    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();
-
-    KillTimer (hwnd, BUTTON_2_TIMER_ID);
-    msframe->button2_need_lbutton = 0;
-    if (msframe->ignore_next_lbutton_up)
-      {
-       msframe->ignore_next_lbutton_up = 0;
-      }
-    else if (msframe->button2_is_down)
-      {
-       msframe->button2_is_down = 0;
-       msframe->ignore_next_rbutton_up = 1;
-       mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONUP,
-                                             MAKEPOINTS (lParam), GetMessageTime());
-      }
-    else
-      {
-       if (msframe->button2_need_rbutton)
-         {
-           msframe->button2_need_rbutton = 0;
-           mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN,
-                                                 MAKEPOINTS (lParam), GetMessageTime());
-         }
-       mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONUP,
-                                             MAKEPOINTS (lParam), GetMessageTime());
-      }
-    break;
+   Here are some pointers:
 
-  case WM_RBUTTONUP:
-    msframe  = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
-    msframe->last_click_time =  GetMessageTime();
+   -- DOWN_MASK indicates which modifiers should be treated as "down"
+      when the corresponding upstroke happens.  It gets reset for
+      a particular modifier when that modifier goes up, and reset
+      for all modifiers when a non-modifier key is pressed.  Example:
 
-    KillTimer (hwnd, BUTTON_2_TIMER_ID);
-    msframe->button2_need_rbutton = 0;
-    if (msframe->ignore_next_rbutton_up)
-      {
-       msframe->ignore_next_rbutton_up = 0;
-      }
-    else if (msframe->button2_is_down)
-      {
-       msframe->button2_is_down = 0;
-       msframe->ignore_next_lbutton_up = 1;
-       mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONUP,
-                                             MAKEPOINTS (lParam), GetMessageTime());
-      }
-    else
-      {
-       if (msframe->button2_need_lbutton)
-         {
-           msframe->button2_need_lbutton = 0;
-           mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN,
-                                                 MAKEPOINTS (lParam), GetMessageTime());
-         }
-       mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONUP,
-                                             MAKEPOINTS (lParam), GetMessageTime());
-      }
-    break;
+      I press Control-A-Shift and then release Control-A-Shift.
+      I want the Shift key to be sticky but not the Control key.
 
-  case WM_LBUTTONDOWN:
-    msframe  = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
+   -- If a modifier key is sticky, I can unstick it by pressing
+      the modifier key again. */
 
-    if (msframe->button2_need_lbutton)
-      {
-       KillTimer (hwnd, BUTTON_2_TIMER_ID);
-       msframe->button2_need_lbutton = 0;
-       msframe->button2_need_rbutton = 0;
-       if (mswindows_button2_near_enough (msframe->last_click_point, MAKEPOINTS (lParam)))
-         {
-           mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONDOWN,
-                                                 MAKEPOINTS (lParam), GetMessageTime());
-           msframe->button2_is_down = 1;
-         }
-       else
-         {
-           mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN,
-                       msframe->last_click_point, msframe->last_click_time);
-           mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN,
-                                                 MAKEPOINTS (lParam), GetMessageTime());
-         }
-      }
-    else
-      {
-       mswindows_set_chord_timer (hwnd);
-       msframe->button2_need_rbutton = 1;
-       msframe->last_click_point = MAKEPOINTS (lParam);
-      }
-    msframe->last_click_time =  GetMessageTime();
-    break;
+static WPARAM last_downkey;
+static int need_to_add_mask, down_mask;
 
-  case WM_RBUTTONDOWN:
-    msframe  = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
+#define XEMSW_LCONTROL (1<<0)
+#define XEMSW_RCONTROL (1<<1)
+#define XEMSW_LSHIFT (1<<2)
+#define XEMSW_RSHIFT (1<<3)
+#define XEMSW_LMENU (1<<4)
+#define XEMSW_RMENU (1<<5)
 
-    if (msframe->button2_need_rbutton)
-      {
-       KillTimer (hwnd, BUTTON_2_TIMER_ID);
-       msframe->button2_need_lbutton = 0;
-       msframe->button2_need_rbutton = 0;
-       if (mswindows_button2_near_enough (msframe->last_click_point, MAKEPOINTS (lParam)))
-         {
-           mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONDOWN,
-                                                 MAKEPOINTS (lParam), GetMessageTime());
-           msframe->button2_is_down = 1;
-         }
-       else
-         {
-           mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN,
-                               msframe->last_click_point, msframe->last_click_time);
-           mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN,
-                                                 MAKEPOINTS (lParam), GetMessageTime());
-         }
-      }
-    else
-      {
-       mswindows_set_chord_timer (hwnd);
-       msframe->button2_need_lbutton = 1;
-       msframe->last_click_point = MAKEPOINTS (lParam);
-      }
-    msframe->last_click_time =  GetMessageTime();
-    break;
-       
-  case WM_TIMER:
-    if (wParam == BUTTON_2_TIMER_ID)
-      {
-       msframe  = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
-       KillTimer (hwnd, BUTTON_2_TIMER_ID);
+static int
+mswindows_handle_sticky_modifiers (WPARAM wParam, LPARAM lParam,
+                                  int downp, int keyp)
+{
+  int mods = 0;
 
-       if (msframe->button2_need_lbutton)
-         {
-           msframe->button2_need_lbutton = 0;
-           mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN,
-                               msframe->last_click_point, msframe->last_click_time);
-         }
-       else if (msframe->button2_need_rbutton)
-         {
-           msframe->button2_need_rbutton = 0;
-           mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN,
-                               msframe->last_click_point, msframe->last_click_time);
-         }
-      }
-    else
-      assert ("Spurious timer fired" == 0);
-    break;
+  if (!modifier_keys_are_sticky) /* Optimize for non-sticky modifiers */
+    return 0;
 
-  case WM_MOUSEMOVE:
-    /* Optimization: don't report mouse movement while size is changind */
-    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 */
-      if ((msframe->button2_need_lbutton || msframe->button2_need_rbutton)
-         && !mswindows_button2_near_enough (msframe->last_click_point,
-                                            MAKEPOINTS (lParam)))
+  if (! (keyp &&
+        (wParam == VK_CONTROL || wParam == VK_LCONTROL ||
+         wParam == VK_RCONTROL ||
+         wParam == VK_MENU || wParam == VK_LMENU ||
+         wParam == VK_RMENU ||
+         wParam == VK_SHIFT || wParam == VK_LSHIFT ||
+         wParam == VK_RSHIFT)))
+    { /* Not a modifier key */
+      if (downp && keyp && !last_downkey)
+       last_downkey = wParam;
+      /* If I hold press-and-release the Control key and then press
+        and hold down the right arrow, I want it to auto-repeat
+        Control-Right.  On the other hand, if I do the same but
+        manually press the Right arrow a bunch of times, I want
+        to see one Control-Right and then a bunch of Rights.
+        This means that we need to distinguish between an
+        auto-repeated key and a key pressed and released a bunch
+        of times. */
+      else if ((downp && !keyp) ||
+              (downp && keyp && last_downkey &&
+               (wParam != last_downkey ||
+                /* the "previous key state" bit indicates autorepeat */
+                ! (lParam & (1 << 30)))))
        {
-         KillTimer (hwnd, BUTTON_2_TIMER_ID);
-         SendMessage (hwnd, WM_TIMER, BUTTON_2_TIMER_ID, 0);
+         need_to_add_mask = 0;
+         last_downkey = 0;
        }
+      if (downp)
+       down_mask = 0;
 
-      emacs_event = Fmake_event (Qnil, Qnil);
-      event = XEVENT(emacs_event);
-
-      event->channel = mswindows_find_frame(hwnd);
-      event->timestamp = GetMessageTime();
-      event->event_type = pointer_motion_event;
-      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);
+      mods = need_to_add_mask;
     }
-    break;
-
-  case WM_CANCELMODE:
-    ReleaseCapture ();
-    /* Queue a `cancel-mode-internal' misc user event, so mouse
-       selection would be canceled if any */
-    mswindows_enqueue_misc_user_event (mswindows_find_frame (hwnd),
-                                      Qcancel_mode_internal, Qnil);
-    break;
-
-#ifdef HAVE_TOOLBARS
-  case WM_NOTIFY:
+  else                          /* Modifier key pressed */
     {
-      LPTOOLTIPTEXT tttext = (LPTOOLTIPTEXT)lParam;
-      Lisp_Object btext;
-      if (tttext->hdr.code ==  TTN_NEEDTEXT)    
+      /* If a non-modifier key was pressed in the middle of a bunch
+        of modifiers, then it unsticks all the modifiers that were
+        previously pressed.  We cannot unstick the modifiers until
+        now because we want to check for auto-repeat of the
+        non-modifier key. */
+
+      if (last_downkey)
        {
-         /* find out which toolbar */
-         frame = XFRAME (mswindows_find_frame (hwnd));
-         btext = mswindows_get_toolbar_button_text ( frame, 
-                                                     tttext->hdr.idFrom );
-         
-         tttext->lpszText = NULL;
-         tttext->hinst = NULL;
-
-         if (!NILP(btext))
-           {
-             /* I think this is safe since the text will only go away
-                 when the toolbar does...*/
-             tttext->lpszText=XSTRING_DATA (btext);
-           }
-#if 0
-         tttext->uFlags |= TTF_DI_SETITEM;
-#endif
-       }    
-    }
-    break;
-#endif
-    
-  case WM_PAINT:
-    {
-      PAINTSTRUCT paintStruct;
-      
-      frame = XFRAME (mswindows_find_frame (hwnd));
+         last_downkey = 0;
+         need_to_add_mask = 0;
+       }
 
-      BeginPaint (hwnd, &paintStruct);
-      mswindows_redraw_exposed_area (frame,
-                       paintStruct.rcPaint.left, paintStruct.rcPaint.top,
-                       paintStruct.rcPaint.right, paintStruct.rcPaint.bottom);
-      EndPaint (hwnd, &paintStruct);
+#define FROB(mask)                             \
+do {                                           \
+  if (downp && keyp)                           \
+    {                                          \
+      /* If modifier key is already sticky,    \
+         then unstick it.  Note that we do     \
+         not test down_mask to deal with the   \
+        unlikely but possible case that the    \
+        modifier key auto-repeats. */          \
+      if (need_to_add_mask & mask)             \
+       {                                       \
+         need_to_add_mask &= ~mask;            \
+         down_mask &= ~mask;                   \
+       }                                       \
+      else                                     \
+       down_mask |= mask;                      \
+    }                                          \
+  else                                         \
+    {                                          \
+      if (down_mask & mask)                    \
+       {                                       \
+         down_mask &= ~mask;                   \
+         need_to_add_mask |= mask;             \
+       }                                       \
+    }                                          \
+} while (0)
+
+      if ((wParam == VK_CONTROL && (lParam & 0x1000000))
+         || wParam == VK_RCONTROL)
+       FROB (XEMSW_RCONTROL);
+      if ((wParam == VK_CONTROL && !(lParam & 0x1000000))
+         || wParam == VK_LCONTROL)
+       FROB (XEMSW_LCONTROL);
+
+      if ((wParam == VK_SHIFT && (lParam & 0x1000000))
+         || wParam == VK_RSHIFT)
+       FROB (XEMSW_RSHIFT);
+      if ((wParam == VK_SHIFT && !(lParam & 0x1000000))
+         || wParam == VK_LSHIFT)
+       FROB (XEMSW_LSHIFT);
+
+      if ((wParam == VK_MENU && (lParam & 0x1000000))
+         || wParam == VK_RMENU)
+       FROB (XEMSW_RMENU);
+      if ((wParam == VK_MENU && !(lParam & 0x1000000))
+         || wParam == VK_LMENU)
+       FROB (XEMSW_LMENU);
     }
-    break;
+#undef FROB
 
-  case WM_SIZE:
-    /* We only care about this message if our size has really changed */
-    if (wParam==SIZE_RESTORED || wParam==SIZE_MAXIMIZED || wParam==SIZE_MINIMIZED)
+  if (mods && downp)
     {
-      RECT rect;
-      int columns, rows;
-
-      fobj = mswindows_find_frame (hwnd);
-      frame = XFRAME (fobj);
-      msframe  = FRAME_MSWINDOWS_DATA (frame);
+      BYTE keymap[256];
 
-      /* We cannot handle frame map and unmap hooks right in
-        this routine, because these may throw. We queue
-        magic events to run these hooks instead - kkm */
+      GetKeyboardState (keymap);
 
-      if (wParam==SIZE_MINIMIZED)
+      if (mods & XEMSW_LCONTROL)
        {
-         /* Iconified */
-         FRAME_VISIBLE_P (frame) = 0;
-         mswindows_enqueue_magic_event (hwnd, XM_UNMAPFRAME);
+         keymap [VK_CONTROL] |= 0x80;
+         keymap [VK_LCONTROL] |= 0x80;
        }
-      else
+      if (mods & XEMSW_RCONTROL)
        {
-         GetClientRect(hwnd, &rect);
-         FRAME_PIXWIDTH(frame) = rect.right;
-         FRAME_PIXHEIGHT(frame) = rect.bottom;
-
-         pixel_to_real_char_size (frame, rect.right, rect.bottom,
-                                  &FRAME_MSWINDOWS_CHARWIDTH (frame),
-                                  &FRAME_MSWINDOWS_CHARHEIGHT (frame));
+         keymap [VK_CONTROL] |= 0x80;
+         keymap [VK_RCONTROL] |= 0x80;
+       }
 
-         pixel_to_char_size (frame, rect.right, rect.bottom, &columns, &rows);
-         change_frame_size (frame, rows, columns, 1);
+      if (mods & XEMSW_LSHIFT)
+       {
+         keymap [VK_SHIFT] |= 0x80;
+         keymap [VK_LSHIFT] |= 0x80;
+       }
+      if (mods & XEMSW_RSHIFT)
+       {
+         keymap [VK_SHIFT] |= 0x80;
+         keymap [VK_RSHIFT] |= 0x80;
+       }
 
-         /* If we are inside frame creation, we have to apply geometric
-            properties now. */
-         if (FRAME_MSWINDOWS_TARGET_RECT (frame))
-           {
-             /* Yes, we have to size again */
-             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. */
-             if (FRAME_MSWINDOWS_TARGET_RECT (frame))
-               {
-                 xfree (FRAME_MSWINDOWS_TARGET_RECT (frame));
-                 FRAME_MSWINDOWS_TARGET_RECT (frame) = 0;
-               }
-           }
-         else
-           {
-             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 ();
-           }
+      if (mods & XEMSW_LMENU)
+       {
+         keymap [VK_MENU] |= 0x80;
+         keymap [VK_LMENU] |= 0x80;
+       }
+      if (mods & XEMSW_RMENU)
+       {
+         keymap [VK_MENU] |= 0x80;
+         keymap [VK_RMENU] |= 0x80;
        }
+
+      SetKeyboardState (keymap);
+      return 1;
     }
-    break;
 
-  /* Misc magic events which only require that the frame be identified */
-  case WM_SETFOCUS:
-  case WM_KILLFOCUS:
-    mswindows_enqueue_magic_event (hwnd, message);
-    break;
+  return 0;
+}
 
-  case WM_WINDOWPOSCHANGING:
-    {
-      WINDOWPOS *wp = (LPWINDOWPOS) lParam;
-      WINDOWPLACEMENT wpl = { sizeof(WINDOWPLACEMENT) };
-      GetWindowPlacement(hwnd, &wpl);
-
-      /* Only interested if size is changing and we're not being iconified */
-      if (wpl.showCmd != SW_SHOWMINIMIZED
-         && wpl.showCmd != SW_SHOWMAXIMIZED
-         && !(wp->flags & SWP_NOSIZE))
-      {
-       RECT ncsize = { 0, 0, 0, 0 };
-       int pixwidth, pixheight;
-       AdjustWindowRectEx (&ncsize, GetWindowLong (hwnd, GWL_STYLE),
-                           GetMenu(hwnd) != NULL,
-                           GetWindowLong (hwnd, GWL_EXSTYLE));
-
-       round_size_to_real_char (XFRAME (mswindows_find_frame (hwnd)),
-                                wp->cx - (ncsize.right - ncsize.left),
-                                wp->cy - (ncsize.bottom - ncsize.top),
-                                &pixwidth, &pixheight);
-
-       /* Convert client sizes to window sizes */
-       pixwidth += (ncsize.right - ncsize.left);
-       pixheight += (ncsize.bottom - ncsize.top);
-
-       if (wpl.showCmd != SW_SHOWMAXIMIZED)
-         {
-           /* Adjust so that the bottom or right doesn't move if it's
-            * the top or left that's being changed */
-           RECT rect;
-           GetWindowRect (hwnd, &rect);
-
-           if (rect.left != wp->x)
-             wp->x += wp->cx - pixwidth;
-           if (rect.top != wp->y)
-             wp->y += wp->cy - pixheight;
-         }
+static void
+clear_sticky_modifiers (void)
+{
+  need_to_add_mask = 0;
+  last_downkey     = 0;
+  down_mask        = 0;
+}
 
-       wp->cx = pixwidth;
-       wp->cy = pixheight;
-      }
-      /* DefWindowProc sends useful WM_GETMINMAXINFO message, and adjusts
-        window position if the user tries to track window too small */
-    }
-    goto defproc;
+#ifdef DEBUG_XEMACS
 
-  case WM_ENTERSIZEMOVE:
-    msframe  = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
-    msframe->sizing = 1;
-    return 0;
+#if 0
 
-  case WM_EXITSIZEMOVE:
-    msframe  = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
-    msframe->sizing = 0;
-    /* Queue noop event */
-    mswindows_enqueue_magic_event (NULL, XM_BUMPQUEUE);
-    return 0;
+static void
+output_modifier_keyboard_state (void)
+{
+  BYTE keymap[256];
+
+  GetKeyboardState (keymap);
+
+  stderr_out ("GetKeyboardState VK_MENU %d %d VK_LMENU %d %d VK_RMENU %d %d\n",
+             keymap[VK_MENU] & 0x80 ? 1 : 0,
+             keymap[VK_MENU] & 0x1 ? 1 : 0,
+             keymap[VK_LMENU] & 0x80 ? 1 : 0,
+             keymap[VK_LMENU] & 0x1 ? 1 : 0,
+             keymap[VK_RMENU] & 0x80 ? 1 : 0,
+             keymap[VK_RMENU] & 0x1 ? 1 : 0);
+  stderr_out ("GetKeyboardState VK_CONTROL %d %d VK_LCONTROL %d %d VK_RCONTROL %d %d\n",
+             keymap[VK_CONTROL] & 0x80 ? 1 : 0,
+             keymap[VK_CONTROL] & 0x1 ? 1 : 0,
+             keymap[VK_LCONTROL] & 0x80 ? 1 : 0,
+             keymap[VK_LCONTROL] & 0x1 ? 1 : 0,
+             keymap[VK_RCONTROL] & 0x80 ? 1 : 0,
+             keymap[VK_RCONTROL] & 0x1 ? 1 : 0);
+  stderr_out ("GetKeyboardState VK_SHIFT %d %d VK_LSHIFT %d %d VK_RSHIFT %d %d\n",
+             keymap[VK_SHIFT] & 0x80 ? 1 : 0,
+             keymap[VK_SHIFT] & 0x1 ? 1 : 0,
+             keymap[VK_LSHIFT] & 0x80 ? 1 : 0,
+             keymap[VK_LSHIFT] & 0x1 ? 1 : 0,
+             keymap[VK_RSHIFT] & 0x80 ? 1 : 0,
+             keymap[VK_RSHIFT] & 0x1 ? 1 : 0);
+}
 
-#ifdef HAVE_SCROLLBARS
-  case WM_VSCROLL:
-  case WM_HSCROLL:
+#endif
+
+/* try to debug the stuck-alt-key problem.
+
+ #### this happens only inconsistently, and may only happen when using
+ StickyKeys in the Win2000 accessibility section of the control panel,
+ which is extremely broken for other reasons.  */
+
+static void
+output_alt_keyboard_state (void)
+{
+  BYTE keymap[256];
+  SHORT keystate[3];
+  // SHORT asyncstate[3];
+
+  GetKeyboardState (keymap);
+  keystate[0] = GetKeyState (VK_MENU);
+  keystate[1] = GetKeyState (VK_LMENU);
+  keystate[2] = GetKeyState (VK_RMENU);
+  /* Doing this interferes with key processing. */
+/*   asyncstate[0] = GetAsyncKeyState (VK_MENU); */
+/*   asyncstate[1] = GetAsyncKeyState (VK_LMENU); */
+/*   asyncstate[2] = GetAsyncKeyState (VK_RMENU); */
+
+  stderr_out ("GetKeyboardState VK_MENU %d %d VK_LMENU %d %d VK_RMENU %d %d\n",
+             keymap[VK_MENU] & 0x80 ? 1 : 0,
+             keymap[VK_MENU] & 0x1 ? 1 : 0,
+             keymap[VK_LMENU] & 0x80 ? 1 : 0,
+             keymap[VK_LMENU] & 0x1 ? 1 : 0,
+             keymap[VK_RMENU] & 0x80 ? 1 : 0,
+             keymap[VK_RMENU] & 0x1 ? 1 : 0);
+  stderr_out ("GetKeyState VK_MENU %d %d VK_LMENU %d %d VK_RMENU %d %d\n",
+             keystate[0] & 0x8000 ? 1 : 0,
+             keystate[0] & 0x1 ? 1 : 0,
+             keystate[1] & 0x8000 ? 1 : 0,
+             keystate[1] & 0x1 ? 1 : 0,
+             keystate[2] & 0x8000 ? 1 : 0,
+             keystate[2] & 0x1 ? 1 : 0);
+/*   stderr_out ("GetAsyncKeyState VK_MENU %d %d VK_LMENU %d %d VK_RMENU %d %d\n", */
+/*           asyncstate[0] & 0x8000 ? 1 : 0, */
+/*           asyncstate[0] & 0x1 ? 1 : 0, */
+/*           asyncstate[1] & 0x8000 ? 1 : 0, */
+/*           asyncstate[1] & 0x1 ? 1 : 0, */
+/*           asyncstate[2] & 0x8000 ? 1 : 0, */
+/*           asyncstate[2] & 0x1 ? 1 : 0); */
+}
+
+#endif /* DEBUG_XEMACS */
+
+
+/*
+ * 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 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;
+
+  Lisp_Event *event;
+  struct frame *frame;
+  struct mswindows_frame* msframe;
+
+  /* Not perfect but avoids crashes. There is potential for wierd
+     behavior here. */
+  if (gc_in_progress)
+    goto defproc;
+
+  assert (!GetWindowLong (hwnd, GWL_USERDATA));
+  switch (message_)
     {
-      /* Direction of scroll is determined by scrollbar instance. */
-      int code = (int) LOWORD(wParam);
-      int pos = (short int) HIWORD(wParam);
-      HWND hwndScrollBar = (HWND) lParam;
-      struct gcpro gcpro1, gcpro2;
-
-      mswindows_handle_scrollbar_event (hwndScrollBar, code,  pos);
-      GCPRO2 (emacs_event, fobj);
-      if (UNBOUNDP(mswindows_pump_outstanding_events()))       /* Can GC */
+    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, and destroy any memory
+         objects we hold on the clipboard that are not in the list of types
+         that Windows will delete itself. */
+      mswindows_destroy_selection (QCLIPBOARD);
+      handle_selection_clear (QCLIPBOARD);
+      break;
+
+    case WM_ERASEBKGND:
+      /* Erase background only during non-dynamic sizing */
+      msframe  = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
+      if (msframe->sizing && !mswindows_dynamic_frame_resize)
+       goto defproc;
+      return 1;
+
+    case WM_CLOSE:
+      fobj = mswindows_find_frame (hwnd);
+      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];
+       int should_set_keymap = 0;
+
+#ifdef DEBUG_XEMACS
+       if (debug_mswindows_events)
+         {
+           stderr_out ("%s wparam=%d lparam=%d\n",
+                       message_ == WM_KEYUP ? "WM_KEYUP" : "WM_SYSKEYUP",
+                       wParam, (int)lParam);
+           output_alt_keyboard_state ();
+         }
+#endif /* DEBUG_XEMACS */
+
+       mswindows_handle_sticky_modifiers (wParam, lParam, 0, 1);
+       if (wParam == VK_CONTROL)
+         {
+           GetKeyboardState (keymap);
+           keymap [(lParam & 0x1000000) ? VK_RCONTROL : VK_LCONTROL] &= ~0x80;
+           should_set_keymap = 1;
+         }
+       else if (wParam == VK_MENU)
+         {
+           GetKeyboardState (keymap);
+           keymap [(lParam & 0x1000000) ? VK_RMENU : VK_LMENU] &= ~0x80;
+           should_set_keymap = 1;
+         }
+
+       if (should_set_keymap)
+         //        && (message_ != WM_SYSKEYUP
+         //    || NILP (Vmenu_accelerator_enabled)))
+         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 characters 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_trans[256];
+       BYTE keymap_orig[256];
+       BYTE keymap_sticky[256];
+       int has_AltGr = mswindows_current_layout_has_AltGr ();
+       int mods = 0;
+       int extendedp = lParam & 0x1000000;
+       Lisp_Object keysym;
+       int sticky_changed;
+
+#ifdef DEBUG_XEMACS
+       if (debug_mswindows_events)
+         {
+           stderr_out ("%s wparam=%d lparam=%d\n",
+                       message_ == WM_KEYDOWN ? "WM_KEYDOWN" : "WM_SYSKEYDOWN",
+                       wParam, (int)lParam);
+           output_alt_keyboard_state ();
+         }
+#endif /* DEBUG_XEMACS */
+
+       GetKeyboardState (keymap_orig);
+       frame = XFRAME (mswindows_find_frame (hwnd));
+       if ((sticky_changed =
+            mswindows_handle_sticky_modifiers (wParam, lParam, 1, 1)))
+         {
+           GetKeyboardState (keymap_sticky);
+           if (keymap_sticky[VK_MENU] & 0x80)
+             {
+               message_ = WM_SYSKEYDOWN;
+               /* We have to set the "context bit" so that the
+                  TranslateMessage() call below that generates the
+                  SYSCHAR message does its thing; see the documentation
+                  on WM_SYSKEYDOWN */
+               lParam |= 1 << 29;
+             }
+         }
+       else
+         memcpy (keymap_sticky, keymap_orig, 256);
+
+       mods = mswindows_modifier_state (keymap_sticky, (DWORD) -1, has_AltGr);
+
+       /* Handle non-printables */
+       if (!NILP (keysym = mswindows_key_to_emacs_keysym (wParam, mods,
+                                                          extendedp)))
+         {
+           mswindows_enqueue_keypress_event (hwnd, keysym, mods);
+           if (sticky_changed)
+             SetKeyboardState (keymap_orig);
+         }
+       else    /* Normal keys & modifiers */
+         {
+           Emchar quit_ch =
+             CONSOLE_QUIT_CHAR (XCONSOLE (mswindows_find_console (hwnd)));
+           POINT pnt = { LOWORD (GetMessagePos()), HIWORD (GetMessagePos()) };
+           MSG msg, tranmsg;
+           int potential_accelerator = 0;
+           int got_accelerator = 0;
+
+           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_orig[extendedp ? VK_RCONTROL : VK_LCONTROL] |= 0x80;
+               keymap_sticky[extendedp ? VK_RCONTROL : VK_LCONTROL] |= 0x80;
+             }
+           else if (wParam == VK_MENU)
+             {
+               keymap_orig[extendedp ? VK_RMENU : VK_LMENU] |= 0x80;
+               keymap_sticky[extendedp ? VK_RMENU : VK_LMENU] |= 0x80;
+             }
+
+           if (!NILP (Vmenu_accelerator_enabled) &&
+               !(mods & XEMACS_MOD_SHIFT) && message_ == WM_SYSKEYDOWN)
+             potential_accelerator = 1;
+
+           /* Remove shift modifier from an ascii character */
+           mods &= ~XEMACS_MOD_SHIFT;
+
+           memcpy (keymap_trans, keymap_sticky, 256);
+
+           /* Clear control and alt modifiers unless AltGr is pressed */
+           keymap_trans[VK_RCONTROL] = 0;
+           keymap_trans[VK_LMENU] = 0;
+           if (!has_AltGr || !(keymap_trans[VK_LCONTROL] & 0x80)
+               || !(keymap_trans[VK_RMENU] & 0x80))
+             {
+               keymap_trans[VK_LCONTROL] = 0;
+               keymap_trans[VK_CONTROL] = 0;
+               keymap_trans[VK_RMENU] = 0;
+               keymap_trans[VK_MENU] = 0;
+             }
+           SetKeyboardState (keymap_trans);
+
+           /* Maybe generate some WM_[SYS]CHARs in the queue */
+           TranslateMessage (&msg);
+
+           while (PeekMessage (&tranmsg, hwnd, WM_CHAR, WM_CHAR, PM_REMOVE)
+                  || PeekMessage (&tranmsg, hwnd, WM_SYSCHAR, WM_SYSCHAR,
+                                  PM_REMOVE))
+             {
+               int mods1 = mods;
+               WPARAM ch = tranmsg.wParam;
+
+               /* If a quit char with no modifiers other than control and
+                  shift, then mark it with a fake modifier, which is removed
+                  upon dequeueing the event */
+               /* #### This might also not withstand localization, if
+                  quit character is not a latin-1 symbol */
+               if (((quit_ch < ' ' && (mods & XEMACS_MOD_CONTROL)
+                     && quit_ch + 'a' - 1 == ch)
+                    || (quit_ch >= ' ' && !(mods & XEMACS_MOD_CONTROL)
+                        && quit_ch == ch))
+                   && ((mods  & ~(XEMACS_MOD_CONTROL | XEMACS_MOD_SHIFT))
+                       == 0))
+                 {
+                   mods1 |= FAKE_MOD_QUIT;
+                   ++mswindows_quit_chars_count;
+                 }
+               else if (potential_accelerator && !got_accelerator &&
+                        mswindows_char_is_accelerator (frame, ch))
+                 {
+                   got_accelerator = 1;
+                   break;
+                 }
+               mswindows_enqueue_keypress_event (hwnd, make_char (ch), mods1);
+             } /* while */
+
+           /* This generates WM_SYSCHAR messages, which are interpreted
+              by DefWindowProc as the menu selections. */
+           if (got_accelerator)
+             {
+               SetKeyboardState (keymap_sticky);
+               TranslateMessage (&msg);
+               SetKeyboardState (keymap_orig);
+               goto defproc;
+             }
+
+           SetKeyboardState (keymap_orig);
+         } /* else */
+      }
+
+      if (key_needs_default_processing_p (wParam))
+       goto defproc;
+      else
+       break;
+
+    case WM_MBUTTONDOWN:
+    case WM_MBUTTONUP:
+      /* Real middle mouse button has nothing to do with emulated one:
+        if one wants to exercise fingers playing chords on the mouse,
+        he is allowed to do that! */
+      mswindows_enqueue_mouse_button_event (hwnd, message_,
+                                           MAKEPOINTS (lParam),
+                                           wParam &~ MK_MBUTTON,
+                                           GetMessageTime());
+      break;
+
+    case WM_LBUTTONUP:
+      msframe  = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
+      msframe->last_click_time =  GetMessageTime();
+
+      KillTimer (hwnd, BUTTON_2_TIMER_ID);
+      msframe->button2_need_lbutton = 0;
+      if (msframe->ignore_next_lbutton_up)
        {
-         /* Error during event pumping - cancel scroll */
-         SendMessage (hwndScrollBar, WM_CANCELMODE, 0, 0);
+         msframe->ignore_next_lbutton_up = 0;
        }
-      UNGCPRO;
-      break;     
-    }
+      else if (msframe->button2_is_down)
+       {
+         msframe->button2_is_down = 0;
+         msframe->ignore_next_rbutton_up = 1;
+         mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONUP,
+                                               MAKEPOINTS (lParam),
+                                               wParam
+                                               &~ (MK_LBUTTON | MK_MBUTTON
+                                                   | MK_RBUTTON),
+                                               GetMessageTime());
+       }
+      else
+       {
+         if (msframe->button2_need_rbutton)
+           {
+             msframe->button2_need_rbutton = 0;
+             mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN,
+                                                   MAKEPOINTS (lParam),
+                                                   wParam &~ MK_LBUTTON,
+                                                   GetMessageTime());
+           }
+         mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONUP,
+                                               MAKEPOINTS (lParam),
+                                               wParam &~ MK_LBUTTON,
+                                               GetMessageTime());
+       }
+      break;
+
+    case WM_RBUTTONUP:
+      msframe  = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
+      msframe->last_click_time =  GetMessageTime();
+
+      KillTimer (hwnd, BUTTON_2_TIMER_ID);
+      msframe->button2_need_rbutton = 0;
+      if (msframe->ignore_next_rbutton_up)
+       {
+         msframe->ignore_next_rbutton_up = 0;
+       }
+      else if (msframe->button2_is_down)
+       {
+         msframe->button2_is_down = 0;
+         msframe->ignore_next_lbutton_up = 1;
+         mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONUP,
+                                               MAKEPOINTS (lParam),
+                                               wParam
+                                               &~ (MK_LBUTTON | MK_MBUTTON
+                                                   | MK_RBUTTON),
+                                               GetMessageTime());
+       }
+      else
+       {
+         if (msframe->button2_need_lbutton)
+           {
+             msframe->button2_need_lbutton = 0;
+             mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN,
+                                                   MAKEPOINTS (lParam),
+                                                   wParam &~ MK_RBUTTON,
+                                                   GetMessageTime());
+           }
+         mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONUP,
+                                               MAKEPOINTS (lParam),
+                                               wParam &~ MK_RBUTTON,
+                                               GetMessageTime());
+       }
+      break;
+
+    case WM_LBUTTONDOWN:
+      msframe  = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
+
+      if (msframe->button2_need_lbutton)
+       {
+         KillTimer (hwnd, BUTTON_2_TIMER_ID);
+         msframe->button2_need_lbutton = 0;
+         msframe->button2_need_rbutton = 0;
+         if (mswindows_button2_near_enough (msframe->last_click_point,
+                                            MAKEPOINTS (lParam)))
+           {
+             mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONDOWN,
+                                                   MAKEPOINTS (lParam),
+                                                   wParam
+                                                   &~ (MK_LBUTTON | MK_MBUTTON
+                                                       | MK_RBUTTON),
+                                                   GetMessageTime());
+             msframe->button2_is_down = 1;
+           }
+         else
+           {
+             mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN,
+                                                   msframe->last_click_point,
+                                                   msframe->last_click_mods
+                                                   &~ MK_RBUTTON,
+                                                   msframe->last_click_time);
+             mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN,
+                                                   MAKEPOINTS (lParam),
+                                                   wParam &~ MK_LBUTTON,
+                                                   GetMessageTime());
+           }
+       }
+      else
+       {
+         mswindows_set_chord_timer (hwnd);
+         msframe->button2_need_rbutton = 1;
+         msframe->last_click_point = MAKEPOINTS (lParam);
+         msframe->last_click_mods = wParam;
+       }
+      msframe->last_click_time =  GetMessageTime();
+      break;
+
+    case WM_RBUTTONDOWN:
+      msframe  = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
+
+      if (msframe->button2_need_rbutton)
+       {
+         KillTimer (hwnd, BUTTON_2_TIMER_ID);
+         msframe->button2_need_lbutton = 0;
+         msframe->button2_need_rbutton = 0;
+         if (mswindows_button2_near_enough (msframe->last_click_point,
+                                            MAKEPOINTS (lParam)))
+           {
+             mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONDOWN,
+                                                   MAKEPOINTS (lParam),
+                                                   wParam
+                                                   &~ (MK_LBUTTON | MK_MBUTTON
+                                                       | MK_RBUTTON),
+                                                   GetMessageTime());
+             msframe->button2_is_down = 1;
+           }
+         else
+           {
+             mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN,
+                                                   msframe->last_click_point,
+                                                   msframe->last_click_mods
+                                                   &~ MK_LBUTTON,
+                                                   msframe->last_click_time);
+             mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN,
+                                                   MAKEPOINTS (lParam),
+                                                   wParam &~ MK_RBUTTON,
+                                                   GetMessageTime());
+           }
+       }
+      else
+       {
+         mswindows_set_chord_timer (hwnd);
+         msframe->button2_need_lbutton = 1;
+         msframe->last_click_point = MAKEPOINTS (lParam);
+         msframe->last_click_mods = wParam;
+       }
+      msframe->last_click_time =  GetMessageTime();
+      break;
+
+    case WM_TIMER:
+      if (wParam == BUTTON_2_TIMER_ID)
+       {
+         msframe  = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
+         KillTimer (hwnd, BUTTON_2_TIMER_ID);
+
+         if (msframe->button2_need_lbutton)
+           {
+             msframe->button2_need_lbutton = 0;
+             mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN,
+                                                   msframe->last_click_point,
+                                                   msframe->last_click_mods
+                                                   &~ MK_RBUTTON,
+                                                   msframe->last_click_time);
+           }
+         else if (msframe->button2_need_rbutton)
+           {
+             msframe->button2_need_rbutton = 0;
+             mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN,
+                                                   msframe->last_click_point,
+                                                   msframe->last_click_mods
+                                                   &~ MK_LBUTTON,
+                                                   msframe->last_click_time);
+           }
+       }
+      else
+       assert ("Spurious timer fired" == 0);
+      break;
+
+    case WM_MOUSEMOVE:
+      /* 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 improves drag-select feedback */
+         if ((msframe->button2_need_lbutton || msframe->button2_need_rbutton)
+             && !mswindows_button2_near_enough (msframe->last_click_point,
+                                                MAKEPOINTS (lParam)))
+           {
+             KillTimer (hwnd, BUTTON_2_TIMER_ID);
+             SendMessage (hwnd, WM_TIMER, BUTTON_2_TIMER_ID, 0);
+           }
+
+         emacs_event = Fmake_event (Qnil, Qnil);
+         event = XEVENT(emacs_event);
+
+         event->channel = mswindows_find_frame(hwnd);
+         event->timestamp = GetMessageTime();
+         event->event_type = pointer_motion_event;
+         event->event.motion.x = MAKEPOINTS(lParam).x;
+         event->event.motion.y = MAKEPOINTS(lParam).y;
+         event->event.motion.modifiers =
+           mswindows_modifier_state (NULL, wParam, 0);
+
+         mswindows_enqueue_dispatch_event (emacs_event);
+       }
+      break;
+
+    case WM_CANCELMODE:
+      ReleaseCapture ();
+      /* Queue a `cancel-mode-internal' misc user event, so mouse
+        selection would be canceled if any */
+      mswindows_enqueue_misc_user_event (mswindows_find_frame (hwnd),
+                                        Qcancel_mode_internal, Qnil);
+      break;
+
+    case WM_NOTIFY:
+      {
+       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,
+                                                       nmhdr->idFrom );
+
+           tttext->lpszText = NULL;
+           tttext->hinst = NULL;
+
+           if (!NILP(btext))
+             {
+               /* I think this is safe since the text will only go away
+                  when the toolbar does...*/
+               LISP_STRING_TO_EXTERNAL (btext, tttext->lpszText, Qnative);
+             }
 #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));
 
-#ifdef HAVE_MENUBARS
-  case WM_INITMENU:
-    if (UNBOUNDP (mswindows_handle_wm_initmenu (
-                       (HMENU) wParam,
-                       XFRAME (mswindows_find_frame (hwnd)))))
-      SendMessage (hwnd, WM_CANCELMODE, 0, 0);
-    break;
-
-  case WM_INITMENUPOPUP:
-    if (!HIWORD(lParam))
+           item.mask = TCIF_PARAM;
+           SendMessage (nmhdr->hwndFrom, TCM_GETITEM, (WPARAM)idx,
+                        (LPARAM)&item);
+
+           mswindows_handle_gui_wm_command (frame, 0, item.lParam);
+         }
+      }
+      break;
+
+    case WM_PAINT:
+      /* hdc will be NULL unless this is a subwindow - in which case we
+        shouldn't have received a paint message for it here. */
+      assert (wParam == 0);
+
+      /* Can't queue a magic event because windows goes modal and sends paint
+        messages directly to the windows procedure when doing solid drags
+        and the message queue doesn't get processed. */
+      mswindows_handle_paint (XFRAME (mswindows_find_frame (hwnd)));
+      break;
+
+    case WM_SIZE:
+      /* We only care about this message if our size has really changed */
+      if (wParam==SIZE_RESTORED || wParam==SIZE_MAXIMIZED || wParam==SIZE_MINIMIZED)
+       {
+         RECT rect;
+         int columns, rows;
+
+         fobj = mswindows_find_frame (hwnd);
+         frame = XFRAME (fobj);
+         msframe  = FRAME_MSWINDOWS_DATA (frame);
+
+         /* We cannot handle frame map and unmap hooks right in
+            this routine, because these may throw. We queue
+            magic events to run these hooks instead - kkm */
+
+         if (wParam==SIZE_MINIMIZED)
+           {
+             /* Iconified */
+             FRAME_VISIBLE_P (frame) = 0;
+             mswindows_enqueue_magic_event (hwnd, XM_UNMAPFRAME);
+           }
+         else
+           {
+             GetClientRect(hwnd, &rect);
+             FRAME_PIXWIDTH(frame) = rect.right;
+             FRAME_PIXHEIGHT(frame) = rect.bottom;
+
+             pixel_to_real_char_size (frame, rect.right, rect.bottom,
+                                      &FRAME_MSWINDOWS_CHARWIDTH (frame),
+                                      &FRAME_MSWINDOWS_CHARHEIGHT (frame));
+
+             pixel_to_char_size (frame, rect.right, rect.bottom, &columns, &rows);
+             change_frame_size (frame, rows, columns, 1);
+
+             /* If we are inside frame creation, we have to apply geometric
+                properties now. */
+             if (FRAME_MSWINDOWS_TARGET_RECT (frame))
+               {
+                 /* Yes, we have to size again */
+                 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. */
+                 if (FRAME_MSWINDOWS_TARGET_RECT (frame))
+                   {
+                     xfree (FRAME_MSWINDOWS_TARGET_RECT (frame));
+                     FRAME_MSWINDOWS_TARGET_RECT (frame) = 0;
+                   }
+               }
+             else
+               {
+                 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 ();
+               }
+           }
+       }
+      break;
+
+    case WM_DISPLAYCHANGE:
       {
-       if (UNBOUNDP (mswindows_handle_wm_initmenupopup (
-                       (HMENU) wParam,
-                        XFRAME (mswindows_find_frame (hwnd)))))
-         SendMessage (hwnd, WM_CANCELMODE, 0, 0);
+       struct device *d;
+       DWORD message_tick = GetMessageTime ();
+
+       fobj = mswindows_find_frame (hwnd);
+       frame = XFRAME (fobj);
+       d = XDEVICE (FRAME_DEVICE (frame));
+
+       /* Do this only once per message. XEmacs can receive this message
+          through as many frames as it currently has open. Message time
+          will be the same for all these messages. Despite extreme
+          efficiency, the code below has about one in 4 billion
+          probability that the HDC is not recreated, provided that
+          XEmacs is running sufficiently longer than 52 days. */
+       if (DEVICE_MSWINDOWS_UPDATE_TICK(d) != message_tick)
+         {
+           DEVICE_MSWINDOWS_UPDATE_TICK(d) = message_tick;
+           DeleteDC (DEVICE_MSWINDOWS_HCDC(d));
+           DEVICE_MSWINDOWS_HCDC(d) = CreateCompatibleDC (NULL);
+         }
       }
-    break;
+      break;
 
-#endif /* HAVE_MENUBARS */
+      /* Misc magic events which only require that the frame be identified */
+    case WM_SETFOCUS:
+    case WM_KILLFOCUS:
+      mswindows_enqueue_magic_event (hwnd, message_);
+      break;
 
-  case WM_COMMAND:
-    {
-      WORD id = LOWORD (wParam);
-      HWND cid = (HWND)lParam;
-      frame = XFRAME (mswindows_find_frame (hwnd));
+    case WM_WINDOWPOSCHANGING:
+      {
+       WINDOWPOS *wp = (LPWINDOWPOS) lParam;
+       WINDOWPLACEMENT wpl = { sizeof(WINDOWPLACEMENT) };
+       GetWindowPlacement(hwnd, &wpl);
+
+       /* Only interested if size is changing and we're not being iconified */
+       if (wpl.showCmd != SW_SHOWMINIMIZED
+           && wpl.showCmd != SW_SHOWMAXIMIZED
+           && !(wp->flags & SWP_NOSIZE))
+         {
+           RECT ncsize = { 0, 0, 0, 0 };
+           int pixwidth, pixheight;
+           AdjustWindowRectEx (&ncsize, GetWindowLong (hwnd, GWL_STYLE),
+                               GetMenu(hwnd) != NULL,
+                               GetWindowLong (hwnd, GWL_EXSTYLE));
+
+           round_size_to_real_char (XFRAME (mswindows_find_frame (hwnd)),
+                                    wp->cx - (ncsize.right - ncsize.left),
+                                    wp->cy - (ncsize.bottom - ncsize.top),
+                                    &pixwidth, &pixheight);
+
+           /* Convert client sizes to window sizes */
+           pixwidth += (ncsize.right - ncsize.left);
+           pixheight += (ncsize.bottom - ncsize.top);
+
+           if (wpl.showCmd != SW_SHOWMAXIMIZED)
+             {
+               /* Adjust so that the bottom or right doesn't move if it's
+                * the top or left that's being changed */
+               RECT rect;
+               GetWindowRect (hwnd, &rect);
+
+               if (rect.left != wp->x)
+                 wp->x += wp->cx - pixwidth;
+               if (rect.top != wp->y)
+                 wp->y += wp->cy - pixheight;
+             }
+
+           wp->cx = pixwidth;
+           wp->cy = pixheight;
+         }
+       /* DefWindowProc sends useful WM_GETMINMAXINFO message, and adjusts
+          window position if the user tries to track window too small */
+      }
+      goto defproc;
 
-#ifdef HAVE_TOOLBARS
-      if (!NILP (mswindows_handle_toolbar_wm_command (frame, cid, id)))
+    case WM_ENTERSIZEMOVE:
+      msframe  = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
+      msframe->sizing = 1;
+      return 0;
+
+    case WM_EXITSIZEMOVE:
+      msframe  = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
+      msframe->sizing = 0;
+      /* Queue noop event */
+      mswindows_enqueue_magic_event (NULL, XM_BUMPQUEUE);
+      return 0;
+
+#ifdef HAVE_SCROLLBARS
+    case WM_VSCROLL:
+    case WM_HSCROLL:
+      {
+       /* Direction of scroll is determined by scrollbar instance. */
+       int code = (int) LOWORD(wParam);
+       int pos = (short int) HIWORD(wParam);
+       HWND hwndScrollBar = (HWND) lParam;
+       struct gcpro gcpro1, gcpro2;
+
+       mswindows_handle_scrollbar_event (hwndScrollBar, code,  pos);
+       GCPRO2 (emacs_event, fobj);
+       if (UNBOUNDP(mswindows_pump_outstanding_events()))      /* Can GC */
+         {
+           /* Error during event pumping - cancel scroll */
+           SendMessage (hwndScrollBar, WM_CANCELMODE, 0, 0);
+         }
+       UNGCPRO;
        break;
-#endif
+      }
 
-#ifdef HAVE_MENUBARS
-      if (!NILP (mswindows_handle_wm_command (frame, id)))
+    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
 
-      /* Bite me - a spurious command. This cannot happen. */
-      error ("XEMACS BUG: Cannot decode command message");
-    }
-  break;
+#ifdef HAVE_MENUBARS
+    case WM_INITMENU:
+      if (UNBOUNDP (mswindows_handle_wm_initmenu (
+                                                 (HMENU) wParam,
+                                                 XFRAME (mswindows_find_frame (hwnd)))))
+       SendMessage (hwnd, WM_CANCELMODE, 0, 0);
+      break;
 
-#ifdef HAVE_DRAGNDROP
-  case WM_DROPFILES:   /* implementation ripped-off from event-Xt.c */
-    {
-      UINT filecount, i, len;
-      POINT point;
-      char* filename;
-#ifdef __CYGWIN32__
-      char* fname;
-#endif
-      Lisp_Object l_dndlist = Qnil, l_item = Qnil;
-      struct gcpro gcpro1, gcpro2, gcpro3;
+    case WM_INITMENUPOPUP:
+      if (!HIWORD(lParam))
+       {
+         if (UNBOUNDP (mswindows_handle_wm_initmenupopup (
+                                                          (HMENU) wParam,
+                                                          XFRAME (mswindows_find_frame (hwnd)))))
+           SendMessage (hwnd, WM_CANCELMODE, 0, 0);
+       }
+      break;
 
-      emacs_event = Fmake_event (Qnil, Qnil);
-      event = XEVENT(emacs_event);
+#endif /* HAVE_MENUBARS */
 
-      GCPRO3 (emacs_event, l_dndlist, l_item);
+    case WM_COMMAND:
+      {
+       WORD id = LOWORD (wParam);
+       WORD nid = HIWORD (wParam);
+       HWND cid = (HWND)lParam;
+       frame = XFRAME (mswindows_find_frame (hwnd));
 
-      if (!DragQueryPoint ((HANDLE) wParam, &point))
-       point.x = point.y = -1;         /* outside client area */
+#ifdef HAVE_TOOLBARS
+       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;
+         case BN_SETFOCUS:
+           
+         }
+       /* 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
 
-      event->event_type = misc_user_event;
-      event->channel = mswindows_find_frame(hwnd);
-      event->timestamp = GetMessageTime();
-      event->event.misc.button = 1;            /* #### Should try harder */
-      event->event.misc.modifiers = mswindows_modifier_state (NULL, 0);
-      event->event.misc.x = point.x;
-      event->event.misc.y = point.y;
-      event->event.misc.function = Qdragdrop_drop_dispatch;
+       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;
 
-      filecount = DragQueryFile ((HANDLE) wParam, 0xffffffff, NULL, 0);
-      for (i=0; i<filecount; i++)
-       {
-         len = DragQueryFile ((HANDLE) wParam, i, NULL, 0);
-         /* The URLs that we make here aren't correct according to section
-          * 3.10 of rfc1738 because they're missing the //<host>/ part and
-          * because they may contain reserved characters. But that's OK. */
-#ifdef __CYGWIN32__
-         fname = (char *)xmalloc (len+1);
-         DragQueryFile ((HANDLE) wParam, i, fname, len+1);
-         filename = xmalloc (cygwin32_win32_to_posix_path_list_buf_size (fname) + 5);
-         strcpy (filename, "file:");
-         cygwin32_win32_to_posix_path_list (fname, filename+5);
-         xfree (fname);
+    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_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_FRAME (image_instance)))));
+               SetBkMode (hdc, OPAQUE);
+               SetBkColor
+                 (hdc,
+                  COLOR_INSTANCE_MSWINDOWS_COLOR
+                  (XCOLOR_INSTANCE
+                   (FACE_BACKGROUND
+                    (XIMAGE_INSTANCE_WIDGET_FACE (image_instance),
+                     XIMAGE_INSTANCE_FRAME (image_instance)))));
+               return (LRESULT)widget_brush;
+             }
+         }
+      }
+      goto defproc;
+
+#ifdef HAVE_DRAGNDROP
+    case WM_DROPFILES: /* implementation ripped-off from event-Xt.c */
+      {
+       UINT filecount, i, len;
+       POINT point;
+       char* filename;
+       char* fname;
+
+       Lisp_Object l_dndlist = Qnil, l_item = Qnil;
+       struct gcpro gcpro1, gcpro2, gcpro3;
+
+       emacs_event = Fmake_event (Qnil, Qnil);
+       event = XEVENT(emacs_event);
+
+       GCPRO3 (emacs_event, l_dndlist, l_item);
+
+       if (!DragQueryPoint ((HDROP) wParam, &point))
+         point.x = point.y = -1;               /* outside client area */
+
+       event->event_type = misc_user_event;
+       event->channel = mswindows_find_frame(hwnd);
+       event->timestamp = GetMessageTime();
+       event->event.misc.button = 1;           /* #### Should try harder */
+       event->event.misc.modifiers = mswindows_modifier_state (NULL,
+                                                               (DWORD) -1, 0);
+       event->event.misc.x = point.x;
+       event->event.misc.y = point.y;
+       event->event.misc.function = Qdragdrop_drop_dispatch;
+
+       filecount = DragQueryFile ((HDROP) wParam, 0xffffffff, NULL, 0);
+       for (i=0; i<filecount; i++)
+         {
+           len = DragQueryFile ((HDROP) wParam, i, NULL, 0);
+           /* The URLs that we make here aren't correct according to section
+            * 3.10 of rfc1738 because they're missing the //<host>/ part and
+            * because they may contain reserved characters. But that's OK -
+            * they just need to be good enough to keep dragdrop.el happy. */
+           fname = (char *)xmalloc (len+1);
+           DragQueryFile ((HANDLE) wParam, i, fname, len+1);
+
+           /* May be a shell link aka "shortcut" - replace fname if so */
+#if !(defined(CYGWIN) || defined(MINGW))
+           /* cygwin doesn't define this COM stuff */
+           if (!stricmp (fname + strlen (fname) - 4, ".LNK"))
+             {
+               IShellLink* psl;
+
+               if (CoCreateInstance (&CLSID_ShellLink, NULL,
+                                     CLSCTX_INPROC_SERVER, &IID_IShellLink, &psl) == S_OK)
+                 {
+                   IPersistFile* ppf;
+
+                   if (psl->lpVtbl->QueryInterface (psl, &IID_IPersistFile,
+                                                    &ppf) == S_OK)
+                     {
+                       WORD wsz[MAX_PATH];
+                       WIN32_FIND_DATA wfd;
+                       LPSTR resolved = (char *) xmalloc (MAX_PATH+1);
+
+                       MultiByteToWideChar (CP_ACP,0, fname, -1, wsz, MAX_PATH);
+
+                       if ((ppf->lpVtbl->Load (ppf, wsz, STGM_READ) == S_OK) &&
+                           (psl->lpVtbl->GetPath (psl, resolved, MAX_PATH,
+                                                  &wfd, 0)==S_OK))
+                         {
+                           xfree (fname);
+                           fname = resolved;
+                           len = strlen (fname);
+                         }
+
+                       ppf->lpVtbl->Release (ppf);
+                     }
+
+                   psl->lpVtbl->Release (psl);
+                 }
+             }
+#endif
+
+#ifdef CYGWIN
+           filename = xmalloc (cygwin32_win32_to_posix_path_list_buf_size (fname) + 5);
+           strcpy (filename, "file:");
+           cygwin32_win32_to_posix_path_list (fname, filename+5);
 #else
-         filename = (char *)xmalloc (len+6);
-         strcpy (filename, "file:");
-         DragQueryFile ((HANDLE) wParam, i, filename+5, len+1);
-         dostounix_filename (filename+5);
+           filename = (char *)xmalloc (len+6);
+           strcat (strcpy (filename, "file:"), fname);
+           dostounix_filename (filename+5);
 #endif
-         l_item = make_string (filename, strlen (filename));
-         l_dndlist = Fcons (l_item, l_dndlist);
-         xfree (filename);
-       }
-      DragFinish ((HANDLE) wParam);
+           xfree (fname);
+           l_item = make_string (filename, strlen (filename));
+           l_dndlist = Fcons (l_item, l_dndlist);
+           xfree (filename);
+         }
+       DragFinish ((HDROP) wParam);
 
-      event->event.misc.object = Fcons (Qdragdrop_URL, l_dndlist);
-      mswindows_enqueue_dispatch_event (emacs_event);
-      UNGCPRO;
-    }
-  break;
+       event->event.misc.object = Fcons (Qdragdrop_URL, l_dndlist);
+       mswindows_enqueue_dispatch_event (emacs_event);
+       UNGCPRO;
+      }
+      break;
 #endif
 
-  defproc:
-  default:
-    return DefWindowProc (hwnd, message, wParam, lParam);
-  }
+    defproc:
+    default:
+      return DefWindowProc (hwnd, message_, wParam, lParam);
+    }
   return (0);
 }
 
@@ -2242,8 +3014,10 @@ mswindows_current_layout_has_AltGr (void)
      time when a key typed at autorepeat rate of 30 cps! */
   static HKL last_hkl = 0;
   static int last_hkl_has_AltGr;
+  HKL current_hkl = (HKL) -1;
 
-  HKL current_hkl = GetKeyboardLayout (0);
+  if (xGetKeyboardLayout) /* not in NT 3.5 */
+    current_hkl = xGetKeyboardLayout (0);
   if (current_hkl != last_hkl)
     {
       TCHAR c;
@@ -2262,29 +3036,46 @@ mswindows_current_layout_has_AltGr (void)
 
 /* Returns the state of the modifier keys in the format expected by the
  * Lisp_Event key_data, button_data and motion_data modifiers member */
-int mswindows_modifier_state (BYTE* keymap, int has_AltGr)
+static int
+mswindows_modifier_state (BYTE* keymap, DWORD fwKeys, int has_AltGr)
 {
   int mods = 0;
+  int keys_is_real = 0;
+  BYTE keymap2[256];
+
+  if (fwKeys == (DWORD) -1)
+    fwKeys = mswindows_last_mouse_button_state;
+  else
+    {
+      keys_is_real = 1;
+      mswindows_last_mouse_button_state = fwKeys;
+    }
 
   if (keymap == NULL)
     {
-      keymap = (BYTE*) alloca(256);
+      keymap = keymap2;
       GetKeyboardState (keymap);
       has_AltGr = mswindows_current_layout_has_AltGr ();
     }
 
+  /* #### should look at fwKeys for MK_CONTROL.  I don't understand how
+     AltGr works. */
   if (has_AltGr && (keymap [VK_LCONTROL] & 0x80) && (keymap [VK_RMENU] & 0x80))
     {
-      mods |= (keymap [VK_LMENU] & 0x80) ? MOD_META : 0;
-      mods |= (keymap [VK_RCONTROL] & 0x80) ? MOD_CONTROL : 0;
+      mods |= (keymap [VK_LMENU] & 0x80) ? XEMACS_MOD_META : 0;
+      mods |= (keymap [VK_RCONTROL] & 0x80) ? XEMACS_MOD_CONTROL : 0;
     }
   else
     {
-      mods |= (keymap [VK_MENU] & 0x80) ? MOD_META : 0;
-      mods |= (keymap [VK_CONTROL] & 0x80) ? MOD_CONTROL : 0;
+      mods |= (keymap [VK_MENU] & 0x80) ? XEMACS_MOD_META : 0;
+      mods |= (keymap [VK_CONTROL] & 0x80) ? XEMACS_MOD_CONTROL : 0;
     }
 
-  mods |= (keymap [VK_SHIFT] & 0x80) ? MOD_SHIFT : 0;
+  mods |= (keys_is_real ? fwKeys & MK_SHIFT : (keymap [VK_SHIFT] & 0x80))
+    ? XEMACS_MOD_SHIFT : 0;
+  mods |= fwKeys & MK_LBUTTON ? XEMACS_MOD_BUTTON1 : 0;
+  mods |= fwKeys & MK_MBUTTON ? XEMACS_MOD_BUTTON2 : 0;
+  mods |= fwKeys & MK_RBUTTON ? XEMACS_MOD_BUTTON3 : 0;
 
   return mods;
 }
@@ -2293,68 +3084,102 @@ int mswindows_modifier_state (BYTE* keymap, int has_AltGr)
  * Translate a mswindows virtual key to a keysym.
  * 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_CANCEL:         return KEYSYM ("pause");
+       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;
+#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");
+       }
+    }
+  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_PAUSE:          return KEYSYM ("pause");
+       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");
+       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;
 }
 
@@ -2412,7 +3237,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))
@@ -2448,13 +3273,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);
@@ -2464,13 +3289,21 @@ 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_PAINT:
+      {
+       struct frame *f = XFRAME (EVENT_CHANNEL (emacs_event));
+       mswindows_handle_paint (f);
+       (FRAME_MSWINDOWS_DATA (f))->paint_pending = 0;
+      }
+      break;
+
     case WM_SETFOCUS:
     case WM_KILLFOCUS:
       {
@@ -2478,18 +3311,19 @@ emacs_mswindows_handle_magic_event (struct Lisp_Event *emacs_event)
        struct frame *f = XFRAME (frame);
        int in_p = (EVENT_MSWINDOWS_MAGIC_TYPE(emacs_event) == WM_SETFOCUS);
        Lisp_Object conser;
+       struct gcpro gcpro1;
 
-       /* struct gcpro gcpro1; */
-
-       /* Clear sticky modifiers here (if we had any) */
+       /* On focus change, clear all memory of sticky modifiers
+          to avoid non-intuitive behavior. */
+       clear_sticky_modifiers ();
 
        conser = Fcons (frame, Fcons (FRAME_DEVICE (f), in_p ? Qt : Qnil));
-       /* GCPRO1 (conser); XXX Not necessary? */
+       GCPRO1 (conser);
        emacs_handle_focus_change_preliminary (conser);
        /* Under X the stuff up to here is done in the X event handler.
           I Don't know why */
        emacs_handle_focus_change_final (conser);
-       /* UNGCPRO; */
+       UNGCPRO;
 
       }
       break;
@@ -2498,13 +3332,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 :
@@ -2516,8 +3350,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);
@@ -2528,12 +3363,12 @@ get_process_input_waitable (struct Lisp_Process *process)
          ? get_winsock_stream_waitable (XLSTREAM (instr))
          : get_ntpipe_input_stream_waitable (XLSTREAM (instr)));
 #else
-    return get_ntpipe_input_stream_waitable (XLSTREAM (instr));
+  return get_ntpipe_input_stream_waitable (XLSTREAM (instr));
 #endif
 }
 
 static void
-emacs_mswindows_select_process (struct Lisp_Process *process)
+emacs_mswindows_select_process (Lisp_Process *process)
 {
   HANDLE hev = get_process_input_waitable (process);
 
@@ -2558,22 +3393,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
@@ -2584,27 +3432,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 */
+  /* Drain windows queue. This sets up number of quit characters in
+     the queue */
   mswindows_drain_windows_queue ();
 
   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));
 
-      Vquit_flag = (XEVENT(emacs_event)->event.key.modifiers & MOD_SHIFT
-                   ? Qcritical : Qt);
+         if (XEVENT(emacs_event)->event.key.modifiers & XEMACS_MOD_SHIFT)
+           critical_p = 1;
 
-      Fdeallocate_event(emacs_event);
-      --mswindows_quit_chars_count;
+         Fdeallocate_event(emacs_event);
+       }
+
+      Vquit_flag = critical_p ? Qcritical : Qt;
     }
 }
 
@@ -2682,7 +3535,7 @@ emacs_mswindows_create_stream_pair (void* inhandle, void* outhandle,
 
 USID
 emacs_mswindows_delete_stream_pair (Lisp_Object instream,
-                                        Lisp_Object outstream)
+                                   Lisp_Object outstream)
 {
   /* Oh nothing special here for Win32 at all */
 #if defined (HAVE_UNIX_PROCESSES)
@@ -2711,12 +3564,18 @@ emacs_mswindows_delete_stream_pair (Lisp_Object instream,
          : HANDLE_TO_USID (get_ntpipe_input_stream_waitable (XLSTREAM (instream))));
 }
 
+static int
+emacs_mswindows_current_event_timestamp (struct console *c)
+{
+  return GetTickCount ();
+}
+
 #ifndef HAVE_X_WINDOWS
 /* This is called from GC when a process object is about to be freed.
    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;
@@ -2734,26 +3593,17 @@ 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;
 
   mswindows_event_stream = xnew (struct event_stream);
 
   mswindows_event_stream->event_pending_p      = emacs_mswindows_event_pending_p;
+  mswindows_event_stream->force_event_pending = 0;
   mswindows_event_stream->next_event_cb                = emacs_mswindows_next_event;
   mswindows_event_stream->handle_magic_event_cb = emacs_mswindows_handle_magic_event;
   mswindows_event_stream->add_timeout_cb       = emacs_mswindows_add_timeout;
@@ -2762,10 +3612,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
@@ -2774,8 +3624,52 @@ 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
+  mswindows_event_stream->current_event_timestamp_cb =
+    emacs_mswindows_current_event_timestamp;
+}
+
+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);
+
+
+#ifdef DEBUG_XEMACS
+  DEFVAR_INT ("debug-mswindows-events", &debug_mswindows_events /*
+If non-zero, display debug information about Windows events that XEmacs sees.
+Information is displayed in a console window.  Currently defined values are:
+
+1 == non-verbose output
+2 == verbose output
+
+#### Unfortunately, not yet implemented.
+*/ );
+  debug_mswindows_events = 0;
+#endif
+
+  DEFVAR_BOOL ("mswindows-alt-by-itself-activates-menu",
+              &mswindows_alt_by_itself_activates_menu /*
+*Controls whether pressing and releasing the Alt key activates the menubar.
+This applies only if no intervening key was pressed.  See also
+`menu-accelerator-enabled', which is probably the behavior you actually want.
+Default is t.
+*/ );
 
-  DEFVAR_BOOL ("mswindows-dynamic-frame-resize", &mswindows_dynamic_frame_resize /*
+  DEFVAR_BOOL ("mswindows-dynamic-frame-resize",
+              &mswindows_dynamic_frame_resize /*
 *Controls redrawing frame contents during mouse-drag or keyboard resize
 operation. When non-nil, the frame is redrawn while being resized. When
 nil, frame is not redrawn, and exposed areas are filled with default
@@ -2785,8 +3679,8 @@ settings.
 Default is t on fast machines, nil on slow.
 */ );
 
-/* The description copied verbatim from nt-emacs. (C) Geoff Voelker */
-  DEFVAR_INT ("mswindows-mouse-button-tolerance", &mswindows_mouse_button_tolerance /*
+  DEFVAR_INT ("mswindows-mouse-button-tolerance",
+             &mswindows_mouse_button_tolerance /*
 *Analogue of double click interval for faking middle mouse events.
 The value is the minimum time in milliseconds that must elapse between
 left/right button down events before they are considered distinct events.
@@ -2795,22 +3689,23 @@ button down event is generated instead.
 If negative or zero, currently set system default is used instead.
 */ );
 
-/* The description copied verbatim from nt-emacs. (C) Geoff Voelker */
   DEFVAR_INT ("mswindows-num-mouse-buttons", &mswindows_num_mouse_buttons /*
 Number of physical mouse buttons.
 */ );
 
-  DEFVAR_INT ("mswindows-mouse-button-max-skew-x", &mswindows_mouse_button_max_skew_x /*
+  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.
 */ );
 
-  DEFVAR_INT ("mswindows-mouse-button-max-skew-y", &mswindows_mouse_button_max_skew_y /*
+  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.
@@ -2819,6 +3714,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_alt_by_itself_activates_menu = 1;
 }
 
 void