diff src/event-msw.c @ 408:501cfd01ee6d r21-2-34

Import from CVS: tag r21-2-34
author cvs
date Mon, 13 Aug 2007 11:18:11 +0200
parents b8cc9ab3f761
children de805c49cfc1
line wrap: on
line diff
--- a/src/event-msw.c	Mon Aug 13 11:17:10 2007 +0200
+++ b/src/event-msw.c	Mon Aug 13 11:18:11 2007 +0200
@@ -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 @@
 #include "process.h"
 #include "redisplay.h"
 #include "select.h"
+#include "window.h"
 #include "sysproc.h"
 #include "syswait.h"
 #include "systime.h"
@@ -78,10 +79,6 @@
 # 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
@@ -103,6 +100,8 @@
 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 +150,10 @@
 int mswindows_mouse_button_max_skew_y;
 int mswindows_mouse_button_tolerance;
 
+#ifdef DEBUG_XEMACS
+int mswindows_debug_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;
@@ -936,17 +939,21 @@
 }
 
 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,
+				      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);
-
-  event->channel = mswindows_find_frame(hwnd);
+  Lisp_Event* event = XEVENT (emacs_event);
+
+  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 :
@@ -955,8 +962,7 @@
   event->event.button.y = where.y;
   event->event.button.modifiers = mswindows_modifier_state (NULL, 0);
 
-  if (msg==WM_LBUTTONDOWN || msg==WM_MBUTTONDOWN ||
-      msg==WM_RBUTTONDOWN)
+  if (downp)
     {
       event->event_type = button_press_event;
       SetCapture (hwnd);
@@ -1664,6 +1670,9 @@
 	 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;
@@ -1681,17 +1690,288 @@
 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
+
+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);
+}
+
+/* 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. */
@@ -1726,11 +2006,23 @@
 
     case WM_KEYUP:
     case WM_SYSKEYUP:
+
       /* See Win95 comment under WM_KEYDOWN */
       {
 	BYTE keymap[256];
 	int should_set_keymap = 0;
 
+#ifdef DEBUG_XEMACS
+	if (mswindows_debug_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 +2036,13 @@
 	    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,6 +2050,7 @@
 
     case WM_KEYDOWN:
     case WM_SYSKEYDOWN:
+
       /* In some locales the right-hand Alt key is labelled AltGr. This key
        * should produce alternative charcaters when combined with another key.
        * eg on a German keyboard pressing AltGr+q should produce '@'.
@@ -1766,24 +2060,58 @@
        * 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 (mswindows_debug_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, 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;
@@ -1800,11 +2128,15 @@
 	     * 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)
@@ -1813,24 +2145,27 @@
 	    /* 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,9 +2175,12 @@
 		   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;
@@ -1855,16 +2193,21 @@
 		  }
 		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
@@ -2389,7 +2732,7 @@
 		       (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 +2741,7 @@
 		   (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 +2749,7 @@
 		   (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;
 	      }
 	  }
@@ -2582,13 +2925,15 @@
 
 /* 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, int has_AltGr)
 {
   int mods = 0;
+  BYTE keymap2[256];
 
   if (keymap == NULL)
     {
-      keymap = (BYTE*) alloca(256);
+      keymap = keymap2;
       GetKeyboardState (keymap);
       has_AltGr = mswindows_current_layout_has_AltGr ();
     }
@@ -2839,18 +3184,19 @@
 	struct frame *f = XFRAME (frame);
 	int in_p = (EVENT_MSWINDOWS_MAGIC_TYPE(emacs_event) == WM_SETFOCUS);
 	Lisp_Object conser;
-
-	/* struct gcpro gcpro1; */
-
-	/* Clear sticky modifiers here (if we had any) */
+	struct gcpro gcpro1;
+
+	/* 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;
@@ -3165,6 +3511,20 @@
   mswindows_error_caught_in_modal_loop = Qnil;
   staticpro (&mswindows_error_caught_in_modal_loop);
 
+
+#ifdef DEBUG_XEMACS
+  DEFVAR_INT ("mswindows-debug-events", &mswindows_debug_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.
+*/ );
+  mswindows_debug_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.