import xemacs-21.2.37
[chise/xemacs-chise.git.1] / src / event-msw.c
index a949be8..a09c81f 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.
@@ -58,6 +58,7 @@ Boston, MA 02111-1307, USA.  */
 #include "process.h"
 #include "redisplay.h"
 #include "select.h"
+#include "window.h"
 #include "sysproc.h"
 #include "syswait.h"
 #include "systime.h"
@@ -68,20 +69,16 @@ Boston, MA 02111-1307, USA.  */
 #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
@@ -99,10 +96,13 @@ 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,
                                                  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;
 
@@ -151,6 +151,10 @@ 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;
@@ -158,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                  */
@@ -468,7 +474,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 +508,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 +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);
 
@@ -583,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;
 }
 
@@ -601,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 */
@@ -936,27 +962,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);
@@ -1245,27 +1274,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;
@@ -1369,7 +1414,7 @@ mswindows_need_event (int badly_p)
            {
              mswindows_drain_windows_queue ();
            }
-         else 
+         else
            {
 #ifdef HAVE_TTY
              /* Look for a TTY event */
@@ -1383,7 +1428,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 +1447,7 @@ mswindows_need_event (int badly_p)
                        {
                          Lisp_Process *p =
                            get_process_from_usid (FD_TO_USID(i));
-                         
+
                          mswindows_enqueue_process_event (p);
                        }
                      else
@@ -1594,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);
@@ -1664,6 +1709,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;
@@ -1675,23 +1723,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. */
@@ -1702,14 +2025,22 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
   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)
+  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:
@@ -1726,11 +2057,23 @@ 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)
+         {
+           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);
@@ -1744,12 +2087,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
@@ -1757,8 +2101,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,
@@ -1766,31 +2111,65 @@ 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;
        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));
-       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);
 
        /* 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();
@@ -1800,37 +2179,44 @@ 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;
                WPARAM ch = tranmsg.wParam;
@@ -1840,31 +2226,39 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
                   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))
+               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 &&
-                        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);
              } /* 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
@@ -1875,8 +2269,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:
@@ -1894,7 +2290,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
        {
@@ -1902,10 +2302,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;
 
@@ -1924,7 +2328,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
        {
@@ -1932,10 +2340,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;
 
@@ -1947,18 +2359,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
@@ -1966,6 +2388,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;
@@ -1978,18 +2401,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
@@ -1997,6 +2430,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;
@@ -2011,13 +2445,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
@@ -2048,7 +2488,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);
        }
@@ -2084,9 +2525,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
          }
@@ -2118,7 +2557,7 @@ 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)));
@@ -2190,21 +2629,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:
@@ -2345,6 +2794,8 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
          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*/
@@ -2353,7 +2804,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. */
@@ -2389,7 +2840,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
@@ -2398,7 +2849,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,
@@ -2406,7 +2857,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;
              }
          }
@@ -2436,7 +2887,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;
@@ -2453,7 +2905,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"))
              {
@@ -2461,7 +2913,7 @@ 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,
@@ -2490,7 +2942,7 @@ mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
              }
 #endif
 
-#ifdef __CYGWIN32__
+#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);
@@ -2515,7 +2967,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);
 }
@@ -2562,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;
@@ -2582,17 +3036,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;
@@ -2604,7 +3071,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;
 }
@@ -2613,7 +3084,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)
@@ -2622,6 +3092,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");
@@ -2633,6 +3104,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
@@ -2644,6 +3120,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");
@@ -2661,11 +3138,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");
@@ -2839,18 +3311,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;
@@ -2977,7 +3450,7 @@ emacs_mswindows_quit_p (void)
        {
          emacs_event = mswindows_cancel_dispatch_event (&match_against);
          assert (!NILP (emacs_event));
-         
+
          if (XEVENT(emacs_event)->event.key.modifiers & XEMACS_MOD_SHIFT)
            critical_p = 1;
 
@@ -3091,6 +3564,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.
@@ -3145,6 +3624,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
@@ -3165,6 +3646,20 @@ vars_of_event_mswindows (void)
   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.