XEmacs 21.4.6 "Common Lisp".
[chise/xemacs-chise.git.1] / src / event-msw.c
index 681743e..e4e1be5 100644 (file)
@@ -1,4 +1,4 @@
-/* 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, 2000 Ben Wing.
@@ -49,39 +49,36 @@ Boston, MA 02111-1307, USA.  */
 # include "dragdrop.h"
 #endif
 
+#include "buffer.h"
 #include "device.h"
 #include "events.h"
-#include "frame.h"
-#include "buffer.h"
 #include "faces.h"
+#include "frame.h"
 #include "lstream.h"
+#include "objects-msw.h"
 #include "process.h"
 #include "redisplay.h"
 #include "select.h"
+#include "sysdep.h"
+#include "window.h"
+
+#include "sysfile.h"
 #include "sysproc.h"
-#include "syswait.h"
 #include "systime.h"
-#include "sysdep.h"
-#include "objects-msw.h"
+#include "syswait.h"
 
 #include "events-mod.h"
+
 #ifdef HAVE_MSG_SELECT
-#include "sysfile.h"
 #include "console-tty.h"
-#elif defined(__CYGWIN32__)
+#elif defined(CYGWIN)
 typedef unsigned int SOCKET;
 #endif
-#include <io.h>
-#include <errno.h>
 
-#if !(defined(__CYGWIN32__) || defined(__MINGW32__))
+#if !(defined(CYGWIN) || defined(MINGW))
 # include <shlobj.h>   /* For IShellLink */
 #endif
 
-#if defined (__CYGWIN32__) && (CYGWIN_VERSION_DLL_MAJOR < 20)
-typedef NMHDR *LPNMHDR;
-#endif
-
 #ifdef HAVE_MENUBARS
 #define ADJR_MENUFLAG TRUE
 #else
@@ -90,19 +87,23 @@ typedef NMHDR *LPNMHDR;
 
 /* Fake key modifier which is attached to a quit char event.
    Removed upon dequeueing an event */
-#define FAKE_MOD_QUIT  0x80
+#define FAKE_MOD_QUIT (1 << 20)
+#define FAKE_MOD_QUIT_CRITICAL (1 << 21)
 
 /* Timer ID used for button2 emulation */
 #define BUTTON_2_TIMER_ID 1
 
-static Lisp_Object mswindows_find_frame (HWND hwnd);
+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,
                                                  int extendedp);
-static int mswindows_modifier_state (BYTE* keymap, int has_AltGr);
+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;
 
@@ -146,10 +147,17 @@ 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;
+Fixnum mswindows_num_mouse_buttons;
+Fixnum mswindows_mouse_button_max_skew_x;
+Fixnum mswindows_mouse_button_max_skew_y;
+Fixnum mswindows_mouse_button_tolerance;
+
+#ifdef DEBUG_XEMACS
+Fixnum debug_mswindows_events;
+
+static void debug_output_mswin_message (HWND hwnd, UINT message_,
+                                       WPARAM wParam, LPARAM lParam);
+#endif
 
 /* This is the event signaled by the event pump.
    See mswindows_pump_outstanding_events for comments */
@@ -158,6 +166,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                  */
@@ -222,6 +232,7 @@ slurper_free_shared_data_maybe (struct ntpipe_slurp_stream_shared_data* s)
       CloseHandle (s->hev_thread);
       CloseHandle (s->hev_caller);
       CloseHandle (s->hev_unsleep);
+      CloseHandle (s->hpipe);
       s->inuse_p = 0;
     }
 }
@@ -360,8 +371,9 @@ get_ntpipe_input_stream_waitable (Lstream *stream)
   return s->thread_data->hev_caller;
 }
 
-static ssize_t
-ntpipe_slurp_reader (Lstream *stream, unsigned char *data, size_t size)
+static Lstream_data_count
+ntpipe_slurp_reader (Lstream *stream, unsigned char *data,
+                    Lstream_data_count size)
 {
   /* This function must be called from the main thread only */
   struct ntpipe_slurp_stream_shared_data* s =
@@ -468,7 +480,7 @@ 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
 {
@@ -502,15 +514,18 @@ shove_thread (LPVOID vparam)
       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)
@@ -543,6 +558,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);
 
@@ -562,8 +586,9 @@ get_ntpipe_output_stream_param (Lstream *stream)
 }
 #endif
 
-static ssize_t
-ntpipe_shove_writer (Lstream *stream, const unsigned char *data, size_t size)
+static Lstream_data_count
+ntpipe_shove_writer (Lstream *stream, const unsigned char *data,
+                    Lstream_data_count size)
 {
   struct ntpipe_shove_stream* s = NTPIPE_SHOVE_STREAM_DATA(stream);
 
@@ -583,6 +608,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;
 }
 
@@ -601,14 +630,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 */
@@ -637,7 +670,7 @@ struct winsock_stream
   LPARAM user_data;            /* Any user data stored in the stream object */
   SOCKET s;                    /* Socket handle (which is a Win32 handle)   */
   OVERLAPPED ov;               /* Overlapped I/O structure                  */
-  void* buffer;                        /* Buffer. Allocated for input stream only   */
+  void* buffer;                        /* Buffer.                                   */
   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                            */
@@ -671,8 +704,8 @@ winsock_initiate_read (struct winsock_stream *str)
     str->eof_p = 1;
 }
 
-static ssize_t
-winsock_reader (Lstream *stream, unsigned char *data, size_t size)
+static Lstream_data_count
+winsock_reader (Lstream *stream, unsigned char *data, Lstream_data_count size)
 {
   struct winsock_stream *str = WINSOCK_STREAM_DATA (stream);
 
@@ -706,7 +739,7 @@ winsock_reader (Lstream *stream, unsigned char *data, size_t size)
     return -1;
 
   /* Return as much of buffer as we have */
-  size = min (size, (size_t) (str->bufsize - str->bufpos));
+  size = min (size, (Lstream_data_count) (str->bufsize - str->bufpos));
   memcpy (data, (void*)((BYTE*)str->buffer + str->bufpos), size);
   str->bufpos += size;
 
@@ -717,8 +750,9 @@ winsock_reader (Lstream *stream, unsigned char *data, size_t size)
   return size;
 }
 
-static ssize_t
-winsock_writer (Lstream *stream, const unsigned char *data, size_t size)
+static Lstream_data_count
+winsock_writer (Lstream *stream, const unsigned char *data,
+               Lstream_data_count size)
 {
   struct winsock_stream *str = WINSOCK_STREAM_DATA (stream);
 
@@ -746,18 +780,24 @@ winsock_writer (Lstream *stream, const unsigned char *data, size_t size)
   if (size == 0)
     return 0;
 
-  {
-    ResetEvent (str->ov.hEvent);
-
-    /* Docs indicate that 4th parameter to WriteFile can be NULL since this is
-     * an overlapped operation. This fails on Win95 with winsock 1.x so we
-     * supply a spare address which is ignored by Win95 anyway. Sheesh. */
-    if (WriteFile ((HANDLE)str->s, data, size, (LPDWORD)&str->buffer, &str->ov)
-       || GetLastError() == ERROR_IO_PENDING)
-      str->pending_p = 1;
-    else
-      str->error_p = 1;
-  }
+  ResetEvent (str->ov.hEvent);
+
+  /* According to WriteFile docs, we must hold onto the data we pass to it
+     and not make any changes until it finishes -- which may not be until
+     the next time we get here, since we use asynchronous I/O.  We have
+     in fact seen data loss as a result of not doing this. */
+  str->buffer = xrealloc (str->buffer, size);
+  memcpy (str->buffer, data, size);
+
+  /* According to MSDN WriteFile docs, the fourth parameter cannot be NULL
+     on Win95 even when doing an overlapped operation, as we are, where
+     the return value through that parameter is not meaningful. */
+  if (WriteFile ((HANDLE)str->s, str->buffer, size, &str->bufsize,
+                &str->ov)
+      || GetLastError() == ERROR_IO_PENDING)
+    str->pending_p = 1;
+  else
+    str->error_p = 1;
 
   return str->error_p ? -1 : size;
 }
@@ -776,8 +816,11 @@ winsock_closer (Lstream *lstr)
   if (str->pending_p)
     WaitForSingleObject (str->ov.hEvent, INFINITE);
 
-  if (lstr->flags & LSTREAM_FL_READ)
-    xfree (str->buffer);
+  if (str->buffer)
+    {
+      xfree (str->buffer);
+      str->buffer = 0;
+    }
 
   CloseHandle (str->ov.hEvent);
   return 0;
@@ -797,14 +840,10 @@ make_winsock_stream_1 (SOCKET s, LPARAM param, const char *mode)
   Lstream *lstr = Lstream_new (lstream_winsock, mode);
   struct winsock_stream *str = WINSOCK_STREAM_DATA (lstr);
 
+  xzero (*str);
   str->s = s;
-  str->blocking_p = 0;
-  str->error_p = 0;
-  str->eof_p = 0;
-  str->pending_p = 0;
   str->user_data = param;
 
-  xzero (str->ov);
   str->ov.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
 
   if (lstr->flags & LSTREAM_FL_READ)
@@ -870,7 +909,7 @@ mswindows_user_event_p (Lisp_Event* sevt)
 /*
  * 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));
@@ -936,27 +975,30 @@ mswindows_enqueue_process_event (Lisp_Process* p)
 }
 
 static void
-mswindows_enqueue_mouse_button_event (HWND hwnd, UINT msg, 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);
-  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 =
     (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);
+  event->event.button.modifiers = mswindows_modifier_state (NULL, mods, 0);
 
-  if (msg==WM_LBUTTONDOWN || msg==WM_MBUTTONDOWN ||
-      msg==WM_RBUTTONDOWN)
+  if (downp)
     {
       event->event_type = button_press_event;
       SetCapture (hwnd);
@@ -1012,13 +1054,11 @@ mswindows_dequeue_dispatch_event (void)
                         &mswindows_s_dispatch_event_queue_tail :
                         &mswindows_u_dispatch_event_queue_tail);
 
-  sevt = XEVENT(event);
+  sevt = XEVENT (event);
   if (sevt->event_type == key_press_event
       && (sevt->event.key.modifiers & FAKE_MOD_QUIT))
-    {
-      sevt->event.key.modifiers &= ~FAKE_MOD_QUIT;
-      --mswindows_quit_chars_count;
-    }
+    sevt->event.key.modifiers &=
+      ~(FAKE_MOD_QUIT | FAKE_MOD_QUIT_CRITICAL);
 
   return event;
 }
@@ -1245,27 +1285,43 @@ mswindows_drain_windows_queue (void)
 
   while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
     {
-      /* We have to translate messages that are not sent to the main
-         window. This is so that key presses work ok in things like
-         edit fields. However, we *musn't* translate message for the
-         main window as this is handled in the wnd proc.
+      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 */
-      if (GetWindowLong (msg.hwnd, GWL_STYLE) & (WS_CHILD|WS_POPUP))
+      /* 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)));
+         msframe =
+           FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (msg.hwnd)));
          if (!msframe->paint_pending)
            {
              msframe->paint_pending = 1;
@@ -1292,6 +1348,10 @@ mswindows_drain_windows_queue (void)
  * fetching WM_TIMER messages. Instead of trying to fetch a WM_TIMER
  * which will never come when there are no pending timers, which leads
  * to deadlock, we simply signal an error.
+ *
+ * It might be possible to combine this with mswindows_drain_windows_queue
+ * which fetches events when not in a modal loop.  It's not clear
+ * whether the result would be more complex than is justified.
  */
 static void
 mswindows_need_event_in_modal_loop (int badly_p)
@@ -1315,8 +1375,8 @@ mswindows_need_event_in_modal_loop (int badly_p)
        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);
+      if (GetMessage (&msg, NULL, WM_TIMER, WM_TIMER) > 0)
+       DispatchMessage (&msg);
     }
 }
 
@@ -1332,12 +1392,6 @@ mswindows_need_event (int badly_p)
 {
   int active;
 
-  if (mswindows_in_modal_loop)
-    {
-      mswindows_need_event_in_modal_loop (badly_p);
-      return;
-    }
-
   while (NILP (mswindows_u_dispatch_event_queue)
         && NILP (mswindows_s_dispatch_event_queue))
     {
@@ -1354,6 +1408,10 @@ mswindows_need_event (int badly_p)
          EMACS_SET_SECS_USECS (sometime, 0, 0);
          EMACS_TIME_TO_SELECT_TIME (sometime, select_time_to_block);
          pointer_to_this = &select_time_to_block;
+         if (mswindows_in_modal_loop)
+           /* In modal loop with badly_p false, don't care about
+              Windows events. */
+           FD_CLR (windows_fd, &temp_mask);
        }
 
       active = select (MAXDESC, &temp_mask, 0, 0, pointer_to_this);
@@ -1367,9 +1425,12 @@ mswindows_need_event (int badly_p)
        {
          if (FD_ISSET (windows_fd, &temp_mask))
            {
-             mswindows_drain_windows_queue ();
+             if (mswindows_in_modal_loop)
+               mswindows_need_event_in_modal_loop (badly_p);
+             else
+               mswindows_drain_windows_queue ();
            }
-         else 
+         else
            {
 #ifdef HAVE_TTY
              /* Look for a TTY event */
@@ -1383,7 +1444,7 @@ mswindows_need_event (int badly_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))
                        {
@@ -1402,7 +1463,7 @@ mswindows_need_event (int badly_p)
                        {
                          Lisp_Process *p =
                            get_process_from_usid (FD_TO_USID(i));
-                         
+
                          mswindows_enqueue_process_event (p);
                        }
                      else
@@ -1432,10 +1493,24 @@ mswindows_need_event (int badly_p)
        }
 #else
       /* Now try getting a message or process event */
+      DWORD what_events;
+      if (mswindows_in_modal_loop)
+       /* In a modal loop, only look for timer events, and only if
+          we really need one. */
+       {
+         if (badly_p)
+           what_events = QS_TIMER;
+         else
+           what_events = 0;
+       }
+      else
+       /* Look for any event */
+       what_events = QS_ALLINPUT;
+
       active = MsgWaitForMultipleObjects (mswindows_waitable_count,
                                          mswindows_waitable_handles,
                                          FALSE, badly_p ? INFINITE : 0,
-                                         QS_ALLINPUT);
+                                         what_events);
 
       /* This will assert if handle being waited for becomes abandoned.
         Not the case currently tho */
@@ -1451,7 +1526,10 @@ mswindows_need_event (int badly_p)
       else if (active == WAIT_OBJECT_0 + mswindows_waitable_count)
        {
          /* Got your message, thanks */
-         mswindows_drain_windows_queue ();
+         if (mswindows_in_modal_loop)
+           mswindows_need_event_in_modal_loop (badly_p);
+         else
+           mswindows_drain_windows_queue ();
        }
       else
        {
@@ -1468,13 +1546,32 @@ mswindows_need_event (int badly_p)
            {
              /* 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 */
+                note the exited process.  First find the process object if
+                possible. */
+             LIST_LOOP_3 (vaffanculo, Vprocess_list, vproctail)
+               if (get_nt_process_handle (XPROCESS (vaffanculo)) ==
+                   mswindows_waitable_handles [ix])
+                 break;
              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);
+             /* 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.
+                #### Replaced with a slightly less horrible kluge that
+                     at least finds the right process instead of axing the
+                     first one on the list.
+              */
+             if (!NILP (vproctail))
+               {
+                 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
@@ -1514,6 +1611,8 @@ mswindows_wm_timer_callback (HWND hwnd, UINT umsg, UINT id_timer, DWORD dwtime)
  * depends on dnd support.
  */
 #ifdef HAVE_DRAGNDROP
+extern int mswindows_dde_enable;
+
 HDDEDATA CALLBACK
 mswindows_dde_callback (UINT uType, UINT uFmt, HCONV hconv,
                        HSZ hszTopic, HSZ hszItem, HDDEDATA hdata,
@@ -1540,6 +1639,9 @@ mswindows_dde_callback (UINT uType, UINT uFmt, HCONV hconv,
       return (HDDEDATA)NULL;
 
     case XTYP_EXECUTE:
+      if (!mswindows_dde_enable)
+       return (HDDEDATA) DDE_FBUSY;
+
       if (!DdeCmpStringHandles (hszTopic, mswindows_dde_topic_system))
        {
          DWORD len = DdeGetData (hdata, NULL, 0, 0);
@@ -1547,7 +1649,7 @@ mswindows_dde_callback (UINT uType, UINT uFmt, HCONV hconv,
          char *end;
          char *filename;
          struct gcpro gcpro1, gcpro2;
-          Lisp_Object l_dndlist = Qnil;
+         Lisp_Object l_dndlist = Qnil;
          Lisp_Object emacs_event = Fmake_event (Qnil, Qnil);
          Lisp_Object frmcons, devcons, concons;
          Lisp_Event *event = XEVENT (emacs_event);
@@ -1582,10 +1684,10 @@ mswindows_dde_callback (UINT uType, UINT uFmt, HCONV hconv,
          if (*end)
            return DDE_FNOTPROCESSED;
 
-#ifdef __CYGWIN32__
-         filename = alloca (cygwin32_win32_to_posix_path_list_buf_size (cmd) + 5);
+#ifdef CYGWIN
+         filename = alloca (cygwin_win32_to_posix_path_list_buf_size (cmd) + 5);
          strcpy (filename, "file:");
-         cygwin32_win32_to_posix_path_list (cmd, filename+5);
+         cygwin_win32_to_posix_path_list (cmd, filename+5);
 #else
          dostounix_filename (cmd);
          filename = alloca (strlen (cmd)+6);
@@ -1652,6 +1754,9 @@ mswindows_handle_paint (struct frame *frame)
         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))
        {
          hold_ignored_expose_registration = 1;
@@ -1663,23 +1768,298 @@ mswindows_handle_paint (struct frame *frame)
 }
 
 /*
- * Returns 1 if a key is a real modifier or special key, which 
+ * 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 (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;
 
   return 0;
 }
 
+/* key-handling code is always ugly.  It just ends up working out
+   that way.
+
+   #### Most of the sticky-modifier code below is copied from similar
+   code in event-Xt.c.  They should somehow or other be merged.
+
+   Here are some pointers:
+
+   -- 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:
+
+      I press Control-A-Shift and then release Control-A-Shift.
+      I want the Shift key to be sticky but not the Control key.
+
+   -- If a modifier key is sticky, I can unstick it by pressing
+      the modifier key again. */
+
+static WPARAM last_downkey;
+static int need_to_add_mask, down_mask;
+
+#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)
+
+static int
+mswindows_handle_sticky_modifiers (WPARAM wParam, LPARAM lParam,
+                                  int downp, int keyp)
+{
+  int mods = 0;
+
+  if (!modifier_keys_are_sticky) /* Optimize for non-sticky modifiers */
+    return 0;
+
+  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)))))
+       {
+         need_to_add_mask = 0;
+         last_downkey = 0;
+       }
+      if (downp)
+       down_mask = 0;
+
+      mods = need_to_add_mask;
+    }
+  else                          /* Modifier key pressed */
+    {
+      /* 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)
+       {
+         last_downkey = 0;
+         need_to_add_mask = 0;
+       }
+
+#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);
+    }
+#undef FROB
+
+  if (mods && downp)
+    {
+      BYTE keymap[256];
+
+      GetKeyboardState (keymap);
+
+      if (mods & XEMSW_LCONTROL)
+       {
+         keymap [VK_CONTROL] |= 0x80;
+         keymap [VK_LCONTROL] |= 0x80;
+       }
+      if (mods & XEMSW_RCONTROL)
+       {
+         keymap [VK_CONTROL] |= 0x80;
+         keymap [VK_RCONTROL] |= 0x80;
+       }
+
+      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 (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;
+    }
+
+  return 0;
+}
+
+static void
+clear_sticky_modifiers (void)
+{
+  need_to_add_mask = 0;
+  last_downkey     = 0;
+  down_mask        = 0;
+}
+
+#ifdef DEBUG_XEMACS
+
+#if 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);
+}
+
+#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)
+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. */
@@ -1690,14 +2070,26 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
   struct frame *frame;
   struct mswindows_frame* msframe;
 
+  /* If you hit this, rewrite the offending API call to occur after GC,
+     using register_post_gc_action(). */
+  assert (!gc_in_progress);
+
+#ifdef DEBUG_XEMACS
+  if (debug_mswindows_events)
+    debug_output_mswin_message (hwnd, message_, wParam, lParam);
+#endif /* DEBUG_XEMACS */
+
   assert (!GetWindowLong (hwnd, GWL_USERDATA));
-  switch (message)
+  switch (message_)
     {
     case WM_DESTROYCLIPBOARD:
       /* We own the clipboard and someone else wants it.  Delete our
         cached copy of the clipboard contents so we'll ask for it from
-        Windows again when someone does a paste. */
-      handle_selection_clear(QCLIPBOARD);
+        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:
@@ -1714,11 +2106,18 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
 
     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 > 2)
+         output_alt_keyboard_state ();
+#endif /* DEBUG_XEMACS */
+
+       mswindows_handle_sticky_modifiers (wParam, lParam, 0, 1);
        if (wParam == VK_CONTROL)
          {
            GetKeyboardState (keymap);
@@ -1732,12 +2131,13 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
            should_set_keymap = 1;
          }
 
-       if (should_set_keymap
-           && (message != WM_SYSKEYUP
-               || NILP (Vmenu_accelerator_enabled)))
+       if (should_set_keymap)
+         //        && (message_ != WM_SYSKEYUP
+         //    || NILP (Vmenu_accelerator_enabled)))
          SetKeyboardState (keymap);
 
       }
+
       if (key_needs_default_processing_p (wParam))
        goto defproc;
       else
@@ -1745,8 +2145,9 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
 
     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.
+       * 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,
@@ -1754,31 +2155,61 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
        * We get round this by removing all modifiers from the keymap before
        * calling TranslateMessage() unless AltGr is *really* down. */
       {
-       BYTE keymap[256];
+       BYTE keymap_trans[256];
+       BYTE keymap_orig[256];
+       BYTE keymap_sticky[256];
        int has_AltGr = mswindows_current_layout_has_AltGr ();
-       int mods;
+       int mods = 0, mods_with_shift = 0;
        int extendedp = lParam & 0x1000000;
        Lisp_Object keysym;
+       int sticky_changed;
 
+#ifdef DEBUG_XEMACS
+       if (debug_mswindows_events > 2)
+         output_alt_keyboard_state ();
+#endif /* DEBUG_XEMACS */
+
+       GetKeyboardState (keymap_orig);
        frame = XFRAME (mswindows_find_frame (hwnd));
-       GetKeyboardState (keymap);
-       mods = mswindows_modifier_state (keymap, has_AltGr);
+       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);
+       mods_with_shift = mods;
 
        /* Handle non-printables */
        if (!NILP (keysym = mswindows_key_to_emacs_keysym (wParam, mods,
                                                           extendedp)))
-         mswindows_enqueue_keypress_event (hwnd, keysym, mods);
+         {
+           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)));
-           BYTE keymap_orig[256];
+           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.message = message_;
            msg.wParam = wParam;
            msg.lParam = lParam;
            msg.time = GetMessageTime();
@@ -1788,71 +2219,102 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
             * to loosely track Left and Right modifiers on behalf of the OS,
             * without screwing up Windows NT which tracks them properly. */
            if (wParam == VK_CONTROL)
-             keymap [extendedp ? VK_RCONTROL : VK_LCONTROL] |= 0x80;
+             {
+               keymap_orig[extendedp ? VK_RCONTROL : VK_LCONTROL] |= 0x80;
+               keymap_sticky[extendedp ? VK_RCONTROL : VK_LCONTROL] |= 0x80;
+             }
            else if (wParam == VK_MENU)
-             keymap [extendedp ? VK_RMENU : VK_LMENU] |= 0x80;
-
-           memcpy (keymap_orig, keymap, 256);
+             {
+               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)
+               !(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 [VK_RCONTROL] = 0;
-           keymap [VK_LMENU] = 0;
-           if (!has_AltGr || !(keymap [VK_LCONTROL] & 0x80)
-               || !(keymap [VK_RMENU] & 0x80))
+           keymap_trans[VK_RCONTROL] = 0;
+           keymap_trans[VK_LMENU] = 0;
+           if (!has_AltGr || !(keymap_trans[VK_LCONTROL] & 0x80)
+               || !(keymap_trans[VK_RMENU] & 0x80))
              {
-               keymap [VK_LCONTROL] = 0;
-               keymap [VK_CONTROL] = 0;
-               keymap [VK_RMENU] = 0;
-               keymap [VK_MENU] = 0;
+               keymap_trans[VK_LCONTROL] = 0;
+               keymap_trans[VK_CONTROL] = 0;
+               keymap_trans[VK_RMENU] = 0;
+               keymap_trans[VK_MENU] = 0;
              }
-           SetKeyboardState (keymap);
+           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))
+                  || PeekMessage (&tranmsg, hwnd, WM_SYSCHAR, WM_SYSCHAR,
+                                  PM_REMOVE))
              {
-               int mods1 = mods;
+               int mods_with_quit = mods;
                WPARAM ch = tranmsg.wParam;
 
+#ifdef DEBUG_XEMACS
+               if (debug_mswindows_events)
+                 {
+                   stderr_out ("-> ");
+                   debug_output_mswin_message (tranmsg.hwnd, tranmsg.message,
+                                               tranmsg.wParam,
+                                               tranmsg.lParam);
+                 }
+#endif /* DEBUG_XEMACS */
+
                /* 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))
+               /* !!#### Fix this in my mule ws -- replace current_buffer
+                  with 0 */
+               if (((quit_ch < ' ' && (mods & XEMACS_MOD_CONTROL)
+                     && DOWNCASE (current_buffer, quit_ch + 'a' - 1) ==
+                     DOWNCASE (current_buffer, ch))
+                    || (quit_ch >= ' ' && !(mods & XEMACS_MOD_CONTROL)
+                        && DOWNCASE (current_buffer, quit_ch) ==
+                        DOWNCASE (current_buffer, ch)))
+                   && ((mods_with_shift &
+                        ~(XEMACS_MOD_CONTROL | XEMACS_MOD_SHIFT))
+                       == 0))
                  {
-                   mods1 |= FAKE_MOD_QUIT;
-                   ++mswindows_quit_chars_count;
+                   mods_with_quit |= FAKE_MOD_QUIT;
+                   if (mods_with_shift & XEMACS_MOD_SHIFT)
+                     mods_with_quit |= FAKE_MOD_QUIT_CRITICAL;
+                   mswindows_quit_chars_count++;
                  }
                else if (potential_accelerator && !got_accelerator &&
-                        msw_char_is_accelerator (frame, ch))
+                        mswindows_char_is_accelerator (frame, ch))
                  {
                    got_accelerator = 1;
                    break;
                  }
-               mswindows_enqueue_keypress_event (hwnd, make_char (ch), mods1);
+               mswindows_enqueue_keypress_event (hwnd, make_char (ch),
+                                                 mods_with_quit);
              } /* while */
-           SetKeyboardState (keymap_orig);
+
            /* 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
@@ -1863,8 +2325,10 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
       /* 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());
+      mswindows_enqueue_mouse_button_event (hwnd, message_,
+                                           MAKEPOINTS (lParam),
+                                           wParam &~ MK_MBUTTON,
+                                           GetMessageTime());
       break;
 
     case WM_LBUTTONUP:
@@ -1882,7 +2346,11 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
          msframe->button2_is_down = 0;
          msframe->ignore_next_rbutton_up = 1;
          mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONUP,
-                                               MAKEPOINTS (lParam), GetMessageTime());
+                                               MAKEPOINTS (lParam),
+                                               wParam
+                                               &~ (MK_LBUTTON | MK_MBUTTON
+                                                   | MK_RBUTTON),
+                                               GetMessageTime());
        }
       else
        {
@@ -1890,10 +2358,14 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
            {
              msframe->button2_need_rbutton = 0;
              mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN,
-                                                   MAKEPOINTS (lParam), GetMessageTime());
+                                                   MAKEPOINTS (lParam),
+                                                   wParam &~ MK_LBUTTON,
+                                                   GetMessageTime());
            }
          mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONUP,
-                                               MAKEPOINTS (lParam), GetMessageTime());
+                                               MAKEPOINTS (lParam),
+                                               wParam &~ MK_LBUTTON,
+                                               GetMessageTime());
        }
       break;
 
@@ -1912,7 +2384,11 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
          msframe->button2_is_down = 0;
          msframe->ignore_next_lbutton_up = 1;
          mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONUP,
-                                               MAKEPOINTS (lParam), GetMessageTime());
+                                               MAKEPOINTS (lParam),
+                                               wParam
+                                               &~ (MK_LBUTTON | MK_MBUTTON
+                                                   | MK_RBUTTON),
+                                               GetMessageTime());
        }
       else
        {
@@ -1920,10 +2396,14 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
            {
              msframe->button2_need_lbutton = 0;
              mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN,
-                                                   MAKEPOINTS (lParam), GetMessageTime());
+                                                   MAKEPOINTS (lParam),
+                                                   wParam &~ MK_RBUTTON,
+                                                   GetMessageTime());
            }
          mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONUP,
-                                               MAKEPOINTS (lParam), GetMessageTime());
+                                               MAKEPOINTS (lParam),
+                                               wParam &~ MK_RBUTTON,
+                                               GetMessageTime());
        }
       break;
 
@@ -1935,18 +2415,28 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
          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)))
+         if (mswindows_button2_near_enough (msframe->last_click_point,
+                                            MAKEPOINTS (lParam)))
            {
              mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONDOWN,
-                                                   MAKEPOINTS (lParam), GetMessageTime());
+                                                   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_time);
+                                                   msframe->last_click_point,
+                                                   msframe->last_click_mods
+                                                   &~ MK_RBUTTON,
+                                                   msframe->last_click_time);
              mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN,
-                                                   MAKEPOINTS (lParam), GetMessageTime());
+                                                   MAKEPOINTS (lParam),
+                                                   wParam &~ MK_LBUTTON,
+                                                   GetMessageTime());
            }
        }
       else
@@ -1954,6 +2444,7 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
          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;
@@ -1966,18 +2457,28 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
          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)))
+         if (mswindows_button2_near_enough (msframe->last_click_point,
+                                            MAKEPOINTS (lParam)))
            {
              mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONDOWN,
-                                                   MAKEPOINTS (lParam), GetMessageTime());
+                                                   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_time);
+                                                   msframe->last_click_point,
+                                                   msframe->last_click_mods
+                                                   &~ MK_LBUTTON,
+                                                   msframe->last_click_time);
              mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN,
-                                                   MAKEPOINTS (lParam), GetMessageTime());
+                                                   MAKEPOINTS (lParam),
+                                                   wParam &~ MK_RBUTTON,
+                                                   GetMessageTime());
            }
        }
       else
@@ -1985,6 +2486,7 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
          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;
@@ -1999,13 +2501,19 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
            {
              msframe->button2_need_lbutton = 0;
              mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN,
-                                                   msframe->last_click_point, msframe->last_click_time);
+                                                   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_time);
+                                                   msframe->last_click_point,
+                                                   msframe->last_click_mods
+                                                   &~ MK_LBUTTON,
+                                                   msframe->last_click_time);
            }
        }
       else
@@ -2036,7 +2544,8 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
          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);
+         event->event.motion.modifiers =
+           mswindows_modifier_state (NULL, wParam, 0);
 
          mswindows_enqueue_dispatch_event (emacs_event);
        }
@@ -2072,9 +2581,7 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
              {
                /* I think this is safe since the text will only go away
                   when the toolbar does...*/
-               TO_EXTERNAL_FORMAT (LISP_STRING, btext,
-                                   C_STRING_ALLOCA, tttext->lpszText,
-                                   Qnative);
+               LISP_STRING_TO_EXTERNAL (btext, tttext->lpszText, Qnative);
              }
 #endif
          }
@@ -2106,12 +2613,39 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
         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 
+      /* 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_WINDOWPOSCHANGED:
+      /* This is sent before WM_SIZE; in fact, the processing of this
+        by DefWindowProc() sends WM_SIZE.  But WM_SIZE is not sent when
+        a window is hidden (make-frame-invisible), so we need to process
+        this and update the state flags. */
+      {
+       fobj = mswindows_find_frame (hwnd);
+       frame = XFRAME (fobj);
+       if (IsIconic (hwnd))
+         {
+           FRAME_VISIBLE_P (frame) = 0;
+           FRAME_ICONIFIED_P (frame) = 1;
+         }
+       else if (IsWindowVisible (hwnd))
+         {
+           FRAME_VISIBLE_P (frame) = 1;
+           FRAME_ICONIFIED_P (frame) = 0;
+         }
+       else
+         {
+           FRAME_VISIBLE_P (frame) = 0;
+           FRAME_ICONIFIED_P (frame) = 0;
+         }         
+
+       return DefWindowProc (hwnd, message_, wParam, lParam);
+      }
+
     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)
@@ -2130,7 +2664,6 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
          if (wParam==SIZE_MINIMIZED)
            {
              /* Iconified */
-             FRAME_VISIBLE_P (frame) = 0;
              mswindows_enqueue_magic_event (hwnd, XM_UNMAPFRAME);
            }
          else
@@ -2166,7 +2699,6 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
                {
                  if (!msframe->sizing && !FRAME_VISIBLE_P (frame))
                    mswindows_enqueue_magic_event (hwnd, XM_MAPFRAME);
-                 FRAME_VISIBLE_P (frame) = 1;
 
                  if (!msframe->sizing || mswindows_dynamic_frame_resize)
                    redisplay ();
@@ -2178,21 +2710,31 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
     case WM_DISPLAYCHANGE:
       {
        struct device *d;
+       DWORD message_tick = GetMessageTime ();
 
        fobj = mswindows_find_frame (hwnd);
        frame = XFRAME (fobj);
        d = XDEVICE (FRAME_DEVICE (frame));
 
-       DEVICE_MSWINDOWS_HORZRES(d) = LOWORD (lParam);
-       DEVICE_MSWINDOWS_VERTRES(d) = HIWORD (lParam);
-       DEVICE_MSWINDOWS_BITSPIXEL(d) = wParam;
-       break;
+       /* 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;
 
       /* Misc magic events which only require that the frame be identified */
     case WM_SETFOCUS:
     case WM_KILLFOCUS:
-      mswindows_enqueue_magic_event (hwnd, message);
+      mswindows_enqueue_magic_event (hwnd, message_);
       break;
 
     case WM_WINDOWPOSCHANGING:
@@ -2281,10 +2823,13 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
        int delta = (short) HIWORD (wParam); /* Wheel rotation amount */
        struct gcpro gcpro1, gcpro2;
 
-       if (mswindows_handle_mousewheel_event (mswindows_find_frame (hwnd), keys,  delta))
+       if (mswindows_handle_mousewheel_event (mswindows_find_frame (hwnd),
+                                              keys, delta,
+                                              MAKEPOINTS (lParam)))
          {
            GCPRO2 (emacs_event, fobj);
-           mswindows_pump_outstanding_events ();       /* Can GC */
+           if (UNBOUNDP(mswindows_pump_outstanding_events ())) /* Can GC */
+             SendMessage (hwnd, WM_CANCELMODE, 0, 0);
            UNGCPRO;
          }
        else
@@ -2341,7 +2886,7 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
          break;
 #endif
 
-       return DefWindowProc (hwnd, message, wParam, lParam);
+       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. */
@@ -2377,7 +2922,7 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
                       (XCOLOR_INSTANCE
                        (FACE_BACKGROUND
                         (XIMAGE_INSTANCE_WIDGET_FACE (image_instance),
-                         XIMAGE_INSTANCE_SUBWINDOW_FRAME (image_instance)))));
+                         XIMAGE_INSTANCE_FRAME (image_instance)))));
                  }
                last_widget_brushed = ii;
                SetTextColor
@@ -2386,7 +2931,7 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
                   (XCOLOR_INSTANCE
                    (FACE_FOREGROUND
                     (XIMAGE_INSTANCE_WIDGET_FACE (image_instance),
-                     XIMAGE_INSTANCE_SUBWINDOW_FRAME (image_instance)))));
+                     XIMAGE_INSTANCE_FRAME (image_instance)))));
                SetBkMode (hdc, OPAQUE);
                SetBkColor
                  (hdc,
@@ -2394,7 +2939,7 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
                   (XCOLOR_INSTANCE
                    (FACE_BACKGROUND
                     (XIMAGE_INSTANCE_WIDGET_FACE (image_instance),
-                     XIMAGE_INSTANCE_SUBWINDOW_FRAME (image_instance)))));
+                     XIMAGE_INSTANCE_FRAME (image_instance)))));
                return (LRESULT)widget_brush;
              }
          }
@@ -2424,7 +2969,8 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
        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.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;
@@ -2441,7 +2987,7 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
            DragQueryFile ((HANDLE) wParam, i, fname, len+1);
 
            /* May be a shell link aka "shortcut" - replace fname if so */
-#if !(defined(__CYGWIN32__) || defined(__MINGW32__))
+#if !(defined(CYGWIN) || defined(MINGW))
            /* cygwin doesn't define this COM stuff */
            if (!stricmp (fname + strlen (fname) - 4, ".LNK"))
              {
@@ -2449,20 +2995,20 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
 
                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];
+                       OLECHAR wsz[PATH_MAX];
                        WIN32_FIND_DATA wfd;
-                       LPSTR resolved = (char *) xmalloc (MAX_PATH+1);
+                       LPSTR resolved = (char *) xmalloc (PATH_MAX+1);
 
-                       MultiByteToWideChar (CP_ACP,0, fname, -1, wsz, MAX_PATH);
+                       MultiByteToWideChar (CP_ACP,0, fname, -1, wsz, PATH_MAX);
 
                        if ((ppf->lpVtbl->Load (ppf, wsz, STGM_READ) == S_OK) &&
-                           (psl->lpVtbl->GetPath (psl, resolved, MAX_PATH,
+                           (psl->lpVtbl->GetPath (psl, resolved, PATH_MAX,
                                                   &wfd, 0)==S_OK))
                          {
                            xfree (fname);
@@ -2478,10 +3024,10 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
              }
 #endif
 
-#ifdef __CYGWIN32__
-           filename = xmalloc (cygwin32_win32_to_posix_path_list_buf_size (fname) + 5);
+#ifdef CYGWIN
+           filename = xmalloc (cygwin_win32_to_posix_path_list_buf_size (fname) + 5);
            strcpy (filename, "file:");
-           cygwin32_win32_to_posix_path_list (fname, filename+5);
+           cygwin_win32_to_posix_path_list (fname, filename+5);
 #else
            filename = (char *)xmalloc (len+6);
            strcat (strcpy (filename, "file:"), fname);
@@ -2503,7 +3049,7 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
 
     defproc:
     default:
-      return DefWindowProc (hwnd, message, wParam, lParam);
+      return DefWindowProc (hwnd, message_, wParam, lParam);
     }
   return (0);
 }
@@ -2550,8 +3096,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;
@@ -2570,17 +3118,30 @@ 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) ? XEMACS_MOD_META : 0;
@@ -2592,7 +3153,11 @@ int mswindows_modifier_state (BYTE* keymap, int has_AltGr)
       mods |= (keymap [VK_CONTROL] & 0x80) ? XEMACS_MOD_CONTROL : 0;
     }
 
-  mods |= (keymap [VK_SHIFT] & 0x80) ? XEMACS_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;
 }
@@ -2601,7 +3166,6 @@ 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
  */
 Lisp_Object mswindows_key_to_emacs_keysym (int mswindows_key, int mods,
                                           int extendedp)
@@ -2610,6 +3174,7 @@ Lisp_Object mswindows_key_to_emacs_keysym (int mswindows_key, int mods,
     {
       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");
@@ -2621,6 +3186,11 @@ Lisp_Object mswindows_key_to_emacs_keysym (int mswindows_key, int mods,
        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");
        }
     }
   else
@@ -2632,6 +3202,7 @@ Lisp_Object mswindows_key_to_emacs_keysym (int mswindows_key, int mods,
        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");
@@ -2649,11 +3220,6 @@ Lisp_Object mswindows_key_to_emacs_keysym (int mswindows_key, int mods,
        case VK_INSERT:         return KEYSYM ("kp-insert");
        case VK_DELETE:         return KEYSYM ("kp-delete");
        case VK_HELP:           return KEYSYM ("help");
-#if 0  /* FSF Emacs allows these to return configurable syms/mods */
-         case VK_LWIN          return KEYSYM ("");
-         case VK_RWIN          return KEYSYM ("");
-#endif
-       case VK_APPS:           return KEYSYM ("menu");
        case VK_NUMPAD0:        return KEYSYM ("kp-0");
        case VK_NUMPAD1:        return KEYSYM ("kp-1");
        case VK_NUMPAD2:        return KEYSYM ("kp-2");
@@ -2712,7 +3278,7 @@ mswindows_find_console (HWND hwnd)
 /*
  * Find the frame that matches the supplied mswindows window handle
  */
-static Lisp_Object
+Lisp_Object
 mswindows_find_frame (HWND hwnd)
 {
   LONG l = GetWindowLong (hwnd, XWL_FRAMEOBJ);
@@ -2827,18 +3393,19 @@ emacs_mswindows_handle_magic_event (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;
@@ -2947,8 +3514,9 @@ emacs_mswindows_quit_p (void)
   if (mswindows_in_modal_loop)
     return;
 
-  /* Drain windows queue. This sets up number of quit characters in
-     the queue */
+  mswindows_quit_chars_count = 0;
+  /* Drain windows queue.  This sets up number of quit characters in
+     the queue. */
   mswindows_drain_windows_queue ();
 
   if (mswindows_quit_chars_count > 0)
@@ -2961,15 +3529,17 @@ emacs_mswindows_quit_p (void)
       match_against.event_type = key_press_event;
       match_against.event.key.modifiers = FAKE_MOD_QUIT;
 
-      while (mswindows_quit_chars_count-- > 0)
+      while (mswindows_quit_chars_count > 0)
        {
          emacs_event = mswindows_cancel_dispatch_event (&match_against);
          assert (!NILP (emacs_event));
-         
-         if (XEVENT(emacs_event)->event.key.modifiers & XEMACS_MOD_SHIFT)
+
+         if (XEVENT (emacs_event)->event.key.modifiers &
+             FAKE_MOD_QUIT_CRITICAL)
            critical_p = 1;
 
-         Fdeallocate_event(emacs_event);
+         Fdeallocate_event (emacs_event);
+         mswindows_quit_chars_count--;
        }
 
       Vquit_flag = critical_p ? Qcritical : Qt;
@@ -3079,6 +3649,12 @@ 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.
@@ -3099,6 +3675,322 @@ debug_process_finalization (Lisp_Process *p)
 }
 #endif
 
+#ifdef DEBUG_XEMACS
+
+struct mswin_message_debug
+{
+  int mess;
+  char *string;
+};
+
+#define FROB(val) { val, #val, },
+
+struct mswin_message_debug debug_mswin_messages[] =
+{
+FROB (WM_NULL)
+FROB (WM_CREATE)
+FROB (WM_DESTROY)
+FROB (WM_MOVE)
+FROB (WM_SIZE)
+
+FROB (WM_ACTIVATE)
+
+FROB (WM_SETFOCUS)
+FROB (WM_KILLFOCUS)
+FROB (WM_ENABLE)
+FROB (WM_SETREDRAW)
+FROB (WM_SETTEXT)
+FROB (WM_GETTEXT)
+FROB (WM_GETTEXTLENGTH)
+FROB (WM_PAINT)
+FROB (WM_CLOSE)
+FROB (WM_QUERYENDSESSION)
+FROB (WM_QUIT)
+FROB (WM_QUERYOPEN)
+FROB (WM_ERASEBKGND)
+FROB (WM_SYSCOLORCHANGE)
+FROB (WM_ENDSESSION)
+FROB (WM_SHOWWINDOW)
+FROB (WM_WININICHANGE)
+#if(WINVER >= 0x0400)
+FROB (WM_SETTINGCHANGE)
+#endif /* WINVER >= 0x0400 */
+
+FROB (WM_DEVMODECHANGE)
+FROB (WM_ACTIVATEAPP)
+FROB (WM_FONTCHANGE)
+FROB (WM_TIMECHANGE)
+FROB (WM_CANCELMODE)
+FROB (WM_SETCURSOR)
+FROB (WM_MOUSEACTIVATE)
+FROB (WM_CHILDACTIVATE)
+FROB (WM_QUEUESYNC)
+
+FROB (WM_GETMINMAXINFO)
+
+FROB (WM_PAINTICON)
+FROB (WM_ICONERASEBKGND)
+FROB (WM_NEXTDLGCTL)
+FROB (WM_SPOOLERSTATUS)
+FROB (WM_DRAWITEM)
+FROB (WM_MEASUREITEM)
+FROB (WM_DELETEITEM)
+FROB (WM_VKEYTOITEM)
+FROB (WM_CHARTOITEM)
+FROB (WM_SETFONT)
+FROB (WM_GETFONT)
+FROB (WM_SETHOTKEY)
+FROB (WM_GETHOTKEY)
+FROB (WM_QUERYDRAGICON)
+FROB (WM_COMPAREITEM)
+#if(WINVER >= 0x0500)
+FROB (WM_GETOBJECT)
+#endif /* WINVER >= 0x0500 */
+FROB (WM_COMPACTING)
+FROB (WM_COMMNOTIFY)
+FROB (WM_WINDOWPOSCHANGING)
+FROB (WM_WINDOWPOSCHANGED)
+
+FROB (WM_POWER)
+
+FROB (WM_COPYDATA)
+FROB (WM_CANCELJOURNAL)
+
+#if(WINVER >= 0x0400)
+FROB (WM_NOTIFY)
+FROB (WM_INPUTLANGCHANGEREQUEST)
+FROB (WM_INPUTLANGCHANGE)
+FROB (WM_TCARD)
+FROB (WM_HELP)
+FROB (WM_USERCHANGED)
+FROB (WM_NOTIFYFORMAT)
+
+FROB (WM_CONTEXTMENU)
+FROB (WM_STYLECHANGING)
+FROB (WM_STYLECHANGED)
+FROB (WM_DISPLAYCHANGE)
+FROB (WM_GETICON)
+FROB (WM_SETICON)
+#endif /* WINVER >= 0x0400 */
+
+FROB (WM_NCCREATE)
+FROB (WM_NCDESTROY)
+FROB (WM_NCCALCSIZE)
+FROB (WM_NCHITTEST)
+FROB (WM_NCPAINT)
+FROB (WM_NCACTIVATE)
+FROB (WM_GETDLGCODE)
+#ifdef WM_SYNCPAINT /* not in VC 5 */
+FROB (WM_SYNCPAINT)
+#endif /* WM_SYNCPAINT */
+FROB (WM_NCMOUSEMOVE)
+FROB (WM_NCLBUTTONDOWN)
+FROB (WM_NCLBUTTONUP)
+FROB (WM_NCLBUTTONDBLCLK)
+FROB (WM_NCRBUTTONDOWN)
+FROB (WM_NCRBUTTONUP)
+FROB (WM_NCRBUTTONDBLCLK)
+FROB (WM_NCMBUTTONDOWN)
+FROB (WM_NCMBUTTONUP)
+FROB (WM_NCMBUTTONDBLCLK)
+
+/* FROB (WM_KEYFIRST) */
+FROB (WM_KEYDOWN)
+FROB (WM_KEYUP)
+FROB (WM_CHAR)
+FROB (WM_DEADCHAR)
+FROB (WM_SYSKEYDOWN)
+FROB (WM_SYSKEYUP)
+FROB (WM_SYSCHAR)
+FROB (WM_SYSDEADCHAR)
+FROB (WM_KEYLAST)
+
+#if(WINVER >= 0x0400) && defined (WM_IME_STARTCOMPOSITION)
+FROB (WM_IME_STARTCOMPOSITION)
+FROB (WM_IME_ENDCOMPOSITION)
+FROB (WM_IME_COMPOSITION)
+FROB (WM_IME_KEYLAST)
+#endif /* WINVER >= 0x0400 && defined (WM_IME_STARTCOMPOSITION) */
+
+FROB (WM_INITDIALOG)
+FROB (WM_COMMAND)
+FROB (WM_SYSCOMMAND)
+FROB (WM_TIMER)
+FROB (WM_HSCROLL)
+FROB (WM_VSCROLL)
+FROB (WM_INITMENU)
+FROB (WM_INITMENUPOPUP)
+FROB (WM_MENUSELECT)
+FROB (WM_MENUCHAR)
+FROB (WM_ENTERIDLE)
+#if(WINVER >= 0x0500)
+FROB (WM_MENURBUTTONUP)
+FROB (WM_MENUDRAG)
+FROB (WM_MENUGETOBJECT)
+FROB (WM_UNINITMENUPOPUP)
+FROB (WM_MENUCOMMAND)
+#endif /* WINVER >= 0x0500 */
+
+
+FROB (WM_CTLCOLORMSGBOX)
+FROB (WM_CTLCOLOREDIT)
+FROB (WM_CTLCOLORLISTBOX)
+FROB (WM_CTLCOLORBTN)
+FROB (WM_CTLCOLORDLG)
+FROB (WM_CTLCOLORSCROLLBAR)
+FROB (WM_CTLCOLORSTATIC)
+
+
+/* FROB (WM_MOUSEFIRST) */
+FROB (WM_MOUSEMOVE)
+FROB (WM_LBUTTONDOWN)
+FROB (WM_LBUTTONUP)
+FROB (WM_LBUTTONDBLCLK)
+FROB (WM_RBUTTONDOWN)
+FROB (WM_RBUTTONUP)
+FROB (WM_RBUTTONDBLCLK)
+FROB (WM_MBUTTONDOWN)
+FROB (WM_MBUTTONUP)
+FROB (WM_MBUTTONDBLCLK)
+
+#if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)
+FROB (WM_MOUSEWHEEL)
+FROB (WM_MOUSELAST)
+#else
+FROB (WM_MOUSELAST)
+#endif /* if (_WIN32_WINNT < 0x0400) */
+
+FROB (WM_PARENTNOTIFY)
+FROB (WM_ENTERMENULOOP)
+FROB (WM_EXITMENULOOP)
+
+#if(WINVER >= 0x0400)
+FROB (WM_NEXTMENU)
+
+FROB (WM_SIZING)
+FROB (WM_CAPTURECHANGED)
+FROB (WM_MOVING)
+FROB (WM_POWERBROADCAST)
+
+FROB (WM_DEVICECHANGE)
+
+#endif /* WINVER >= 0x0400 */
+
+FROB (WM_MDICREATE)
+FROB (WM_MDIDESTROY)
+FROB (WM_MDIACTIVATE)
+FROB (WM_MDIRESTORE)
+FROB (WM_MDINEXT)
+FROB (WM_MDIMAXIMIZE)
+FROB (WM_MDITILE)
+FROB (WM_MDICASCADE)
+FROB (WM_MDIICONARRANGE)
+FROB (WM_MDIGETACTIVE)
+
+
+FROB (WM_MDISETMENU)
+FROB (WM_ENTERSIZEMOVE)
+FROB (WM_EXITSIZEMOVE)
+FROB (WM_DROPFILES)
+FROB (WM_MDIREFRESHMENU)
+
+#ifdef WM_IME_SETCONTEXT /* not in Cygwin? */
+
+#if(WINVER >= 0x0400) && !defined(CYGWIN)
+FROB (WM_IME_SETCONTEXT)
+FROB (WM_IME_NOTIFY)
+FROB (WM_IME_CONTROL)
+FROB (WM_IME_COMPOSITIONFULL)
+FROB (WM_IME_SELECT)
+FROB (WM_IME_CHAR)
+#endif /* WINVER >= 0x0400 */
+#if(WINVER >= 0x0500)
+FROB (WM_IME_REQUEST)
+#endif /* WINVER >= 0x0500 */
+#if(WINVER >= 0x0400) && !defined(CYGWIN)
+FROB (WM_IME_KEYDOWN)
+FROB (WM_IME_KEYUP)
+#endif /* WINVER >= 0x0400 */
+
+#endif /* WM_IME_SETCONTEXT */
+
+#if(_WIN32_WINNT >= 0x0400)
+FROB (WM_MOUSEHOVER)
+FROB (WM_MOUSELEAVE)
+#endif /* _WIN32_WINNT >= 0x0400 */
+
+FROB (WM_CUT)
+FROB (WM_COPY)
+FROB (WM_PASTE)
+FROB (WM_CLEAR)
+FROB (WM_UNDO)
+FROB (WM_RENDERFORMAT)
+FROB (WM_RENDERALLFORMATS)
+FROB (WM_DESTROYCLIPBOARD)
+FROB (WM_DRAWCLIPBOARD)
+FROB (WM_PAINTCLIPBOARD)
+FROB (WM_VSCROLLCLIPBOARD)
+FROB (WM_SIZECLIPBOARD)
+FROB (WM_ASKCBFORMATNAME)
+FROB (WM_CHANGECBCHAIN)
+FROB (WM_HSCROLLCLIPBOARD)
+FROB (WM_QUERYNEWPALETTE)
+FROB (WM_PALETTEISCHANGING)
+FROB (WM_PALETTECHANGED)
+FROB (WM_HOTKEY)
+
+#if(WINVER >= 0x0400)
+FROB (WM_PRINT)
+FROB (WM_PRINTCLIENT)
+
+FROB (WM_HANDHELDFIRST)
+FROB (WM_HANDHELDLAST)
+
+FROB (WM_AFXFIRST)
+FROB (WM_AFXLAST)
+#endif /* WINVER >= 0x0400 */
+
+FROB (WM_PENWINFIRST)
+FROB (WM_PENWINLAST)
+};
+
+#undef FROB
+
+static void
+debug_output_mswin_message (HWND hwnd, UINT message_, WPARAM wParam,
+                           LPARAM lParam)
+{
+  Lisp_Object frame = mswindows_find_frame (hwnd);
+  int i;
+  char *str = 0;
+  /* struct mswin_message_debug *i_hate_cranking_out_code_like_this; */
+
+  for (i = 0; i < countof (debug_mswin_messages); i++)
+    {
+      if (debug_mswin_messages[i].mess == message_)
+       {
+         str = debug_mswin_messages[i].string;
+         break;
+       }
+    }
+
+  if (str)
+    stderr_out ("%s", str);
+  else
+    stderr_out ("%x", message_);
+
+  if (debug_mswindows_events > 1)
+    {
+      stderr_out (" wparam=%d lparam=%d hwnd=%x frame: ",
+                 wParam, (int) lParam, (unsigned int) hwnd);
+      debug_print (frame);
+    }
+  else
+    stderr_out ("\n");
+}
+
+#endif /* DEBUG_XEMACS */
+
 /************************************************************************/
 /*                            initialization                            */
 /************************************************************************/
@@ -3133,6 +4025,8 @@ reinit_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
@@ -3143,16 +4037,29 @@ vars_of_event_mswindows (void)
   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);
+  dump_add_root_object (&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);
+  dump_add_root_object (&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 messages that XEmacs sees.
+Information is displayed in a console window.  Currently defined values are:
+
+1 == non-verbose output (just the message name)
+2 == verbose output (all parameters)
+3 == even more verbose output (extra debugging info)
+*/ );
+  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.
@@ -3220,7 +4127,7 @@ lstream_type_create_mswindows_selectable (void)
 {
   init_slurp_stream ();
   init_shove_stream ();
-#if defined (HAVE_SOCKETS) && !defined(HAVE_MSG_SELECT)
+#if defined (HAVE_SOCKETS) && !defined (HAVE_MSG_SELECT)
   init_winsock_stream ();
 #endif
 }