changeset 23803:f112aa8f8f5d

(Vw32_phantom_key_code): New variable. (Qhyper, Qsuper, Qmeta, Qalt, Qctrl, Qcontrol, Qshift): New variables. (syms_of_w32fns): Init and register them. (reset_modifiers): Use a more precise test for whether Emacs has keyboard focus. Synchronize state of all keys that can be used as modifiers, to overcome problems arising from hot-keys. (w32_key_to_modifier): Be careful not to call intern() to avoid requiring thread synchronization. (w32_msg_pump): Handle new WM_EMACS_TOGGLE_LOCK_KEY message. (w32_wnd_proc): Ensure lock key indicator lights are updated promptly on Windows 9x, by passing key events on to the system. (w32_wnd_proc): Use Vw32_phantom_key_code to stop system responding to Windows key events when not wanted. (w32_wnd_proc): Undo the mapping of Ctrl-Pause into cancel and Ctrl-NumLock into pause by the system. (w32_wnd_proc): Don't translate key combinations containing any modifiers other than shift (and capslock). (w32_wnd_proc): Reset modifier key states when receiving focus (we can't determine the key states until we have keyboard focus). (Fw32_toggle_lock_key): New function. (syms_of_w32fns): Defsubr it.
author Geoff Voelker <voelker@cs.washington.edu>
date Wed, 02 Dec 1998 23:52:47 +0000
parents 4052a2875390
children aa5a4d0b1591
files src/w32fns.c
diffstat 1 files changed, 217 insertions(+), 48 deletions(-) [+]
line wrap: on
line diff
--- a/src/w32fns.c	Wed Dec 02 23:01:50 1998 +0000
+++ b/src/w32fns.c	Wed Dec 02 23:52:47 1998 +0000
@@ -48,6 +48,7 @@
 extern void abort ();
 extern void free_frame_menubar ();
 extern struct scroll_bar *x_window_to_scroll_bar ();
+extern int w32_console_toggle_lock_key (int vk_code, Lisp_Object new_state);
 extern int quit_char;
 
 extern char *lispy_function_keys[];
@@ -70,6 +71,10 @@
    only affects whether "tapping" the key opens the Start menu).  */
 Lisp_Object Vw32_pass_rwindow_to_system;
 
+/* Virtual key code used to generate "phantom" key presses in order
+   to stop system from acting on Windows key events.  */
+Lisp_Object Vw32_phantom_key_code;
+
 /* Modifier associated with the left "Windows" key, or nil to act as a
    normal key.  */
 Lisp_Object Vw32_lwindow_modifier;
@@ -202,6 +207,14 @@
 Lisp_Object Quser_size;
 Lisp_Object Qdisplay;
 
+Lisp_Object Qhyper;
+Lisp_Object Qsuper;
+Lisp_Object Qmeta;
+Lisp_Object Qalt;
+Lisp_Object Qctrl;
+Lisp_Object Qcontrol;
+Lisp_Object Qshift;
+
 /* State variables for emulating a three button mouse. */
 #define LMOUSE 1
 #define MMOUSE 2
@@ -3091,16 +3104,13 @@
 {
   SHORT ctrl, alt;
 
-  if (!modifiers_recorded)
+  if (GetFocus () == NULL)
+    /* Emacs doesn't have keyboard focus.  Do nothing.  */
     return;
 
   ctrl = GetAsyncKeyState (VK_CONTROL);
   alt = GetAsyncKeyState (VK_MENU);
 
-  if (ctrl == 0 || alt == 0)
-    /* Emacs doesn't have keyboard focus.  Do nothing.  */
-    return;
-
   if (!(ctrl & 0x08000))
     /* Clear any recorded control modifier state.  */
     modifiers[EMACS_RCONTROL] = modifiers[EMACS_LCONTROL] = 0;
@@ -3109,8 +3119,27 @@
     /* Clear any recorded alt modifier state.  */
     modifiers[EMACS_RMENU] = modifiers[EMACS_LMENU] = 0;
 
-  /* Otherwise, leave the modifier state as it was when Emacs lost
-     keyboard focus.  */
+  /* Update the state of all modifier keys, because modifiers used in
+     hot-key combinations can get stuck on if Emacs loses focus as a
+     result of a hot-key being pressed.  */
+  {
+    BYTE keystate[256];
+
+#define CURRENT_STATE(key) ((GetAsyncKeyState (key) & 0x8000) >> 8)
+
+    GetKeyboardState (keystate);
+    keystate[VK_SHIFT] = CURRENT_STATE (VK_SHIFT);
+    keystate[VK_CONTROL] = CURRENT_STATE (VK_CONTROL);
+    keystate[VK_LCONTROL] = CURRENT_STATE (VK_LCONTROL);
+    keystate[VK_RCONTROL] = CURRENT_STATE (VK_RCONTROL);
+    keystate[VK_MENU] = CURRENT_STATE (VK_MENU);
+    keystate[VK_LMENU] = CURRENT_STATE (VK_LMENU);
+    keystate[VK_RMENU] = CURRENT_STATE (VK_RMENU);
+    keystate[VK_LWIN] = CURRENT_STATE (VK_LWIN);
+    keystate[VK_RWIN] = CURRENT_STATE (VK_RWIN);
+    keystate[VK_APPS] = CURRENT_STATE (VK_APPS);
+    SetKeyboardState (keystate);
+  }
 }
 
 /* Synchronize modifier state with what is reported with the current
@@ -3178,19 +3207,25 @@
       key_mapping = Qnil;
     }
 
-  if (EQ (key_mapping, intern ("hyper")))
+  /* NB. This code runs in the input thread, asychronously to the lisp
+     thread, so we must be careful to ensure access to lisp data is
+     thread-safe.  The following code is safe because the modifier
+     variable values are updated atomically from lisp and symbols are
+     not relocated by GC.  Also, we don't have to worry about seeing GC
+     markbits here.  */
+  if (EQ (key_mapping, Qhyper))
     return hyper_modifier;
-  if (EQ (key_mapping, intern ("super")))
+  if (EQ (key_mapping, Qsuper))
     return super_modifier;
-  if (EQ (key_mapping, intern ("meta")))
+  if (EQ (key_mapping, Qmeta))
     return meta_modifier;
-  if (EQ (key_mapping, intern ("alt")))
+  if (EQ (key_mapping, Qalt))
     return alt_modifier;
-  if (EQ (key_mapping, intern ("ctrl")))
+  if (EQ (key_mapping, Qctrl))
     return ctrl_modifier;
-  if (EQ (key_mapping, intern ("control"))) /* synonym for ctrl */
+  if (EQ (key_mapping, Qcontrol)) /* synonym for ctrl */
     return ctrl_modifier;
-  if (EQ (key_mapping, intern ("shift")))
+  if (EQ (key_mapping, Qshift))
     return shift_modifier;
 
   /* Don't generate any modifier if not explicitly requested.  */
@@ -3404,11 +3439,47 @@
 	      focus_window = GetFocus ();
 	      if (focus_window != NULL)
 		UnregisterHotKey (focus_window, HOTKEY_ID (msg.wParam));
-	      /* Mark item as erased.  */
+	      /* Mark item as erased.  NB: this code must be
+                 thread-safe.  The next line is okay because the cons
+                 cell is never made into garbage and is not relocated by
+                 GC.  */
 	      XCAR ((Lisp_Object) msg.lParam) = Qnil;
 	      if (!PostThreadMessage (dwMainThreadId, WM_EMACS_DONE, 0, 0))
 		abort ();
 	      break;
+	    case WM_EMACS_TOGGLE_LOCK_KEY:
+	      {
+		int vk_code = (int) msg.wParam;
+		int cur_state = (GetKeyState (vk_code) & 1);
+		Lisp_Object new_state = (Lisp_Object) msg.lParam;
+
+		/* NB: This code must be thread-safe.  It is safe to
+                   call NILP because symbols are not relocated by GC,
+                   and pointer here is not touched by GC (so the markbit
+                   can't be set).  Numbers are safe because they are
+                   immediate values.  */
+		if (NILP (new_state)
+		    || (NUMBERP (new_state)
+			&& (XUINT (new_state)) & 1 != cur_state))
+		  {
+		    one_w32_display_info.faked_key = vk_code;
+
+		    keybd_event ((BYTE) vk_code,
+				 (BYTE) MapVirtualKey (vk_code, 0),
+				 KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
+		    keybd_event ((BYTE) vk_code,
+				 (BYTE) MapVirtualKey (vk_code, 0),
+				 KEYEVENTF_EXTENDEDKEY | 0, 0);
+		    keybd_event ((BYTE) vk_code,
+				 (BYTE) MapVirtualKey (vk_code, 0),
+				 KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
+		    cur_state = !cur_state;
+		  }
+		if (!PostThreadMessage (dwMainThreadId, WM_EMACS_DONE,
+					cur_state, 0))
+		  abort ();
+	      }
+	      break;
 	    default:
 	      DebPrint (("msg %x not expected by w32_msg_pump\n", msg.message));
 	    }
@@ -3638,7 +3709,9 @@
       if (dpyinfo->faked_key == wParam)
 	{
 	  dpyinfo->faked_key = 0;
-	  return 0;
+	  /* Make sure TranslateMessage sees them though.  */
+	  windows_translate = 1;
+	  goto translate;
 	}
 
       /* Synchronize modifiers with current keystroke.  */
@@ -3658,33 +3731,44 @@
 		 press of Space which we will ignore.  */
 	      if (GetAsyncKeyState (wParam) & 1)
 		{
-		  dpyinfo->faked_key = VK_SPACE;
-		  keybd_event (VK_SPACE,
-			       (BYTE) MapVirtualKey (VK_SPACE, 0), 0, 0);
+		  if (NUMBERP (Vw32_phantom_key_code))
+		    wParam = XUINT (Vw32_phantom_key_code) & 255;
+		  else
+		    wParam = VK_SPACE;
+		  dpyinfo->faked_key = wParam;
+		  keybd_event (wParam, (BYTE) MapVirtualKey (wParam, 0), 0, 0);
 		}
 	    }
 	  if (!NILP (Vw32_lwindow_modifier))
 	    return 0;
+	  windows_translate = 1;
 	  break;
 	case VK_RWIN:
 	  if (NILP (Vw32_pass_rwindow_to_system))
 	    {
 	      if (GetAsyncKeyState (wParam) & 1)
 		{
-		  dpyinfo->faked_key = VK_SPACE;
-		  keybd_event (VK_SPACE,
-			       (BYTE) MapVirtualKey (VK_SPACE, 0), 0, 0);
+		  if (NUMBERP (Vw32_phantom_key_code))
+		    wParam = XUINT (Vw32_phantom_key_code) & 255;
+		  else
+		    wParam = VK_SPACE;
+		  dpyinfo->faked_key = wParam;
+		  keybd_event (wParam, (BYTE) MapVirtualKey (wParam, 0), 0, 0);
 		}
 	    }
 	  if (!NILP (Vw32_rwindow_modifier))
 	    return 0;
+	  windows_translate = 1;
 	  break;
 	case VK_APPS:
 	  if (!NILP (Vw32_apps_modifier))
 	    return 0;
+	  windows_translate = 1;
 	  break;
 	case VK_MENU:
 	  if (NILP (Vw32_pass_alt_to_system)) 
+	    /* Prevent DefWindowProc from activating the menu bar if an
+               Alt key is pressed and released by itself.  */
 	    return 0;
 	  windows_translate = 1;
 	  break;
@@ -3692,42 +3776,65 @@
 	  /* Decide whether to treat as modifier or function key.  */
 	  if (NILP (Vw32_enable_caps_lock))
 	    goto disable_lock_key;
-	  return 0;
+	  windows_translate = 1;
+	  break;
 	case VK_NUMLOCK:
 	  /* Decide whether to treat as modifier or function key.  */
 	  if (NILP (Vw32_enable_num_lock))
 	    goto disable_lock_key;
-	  return 0;
+	  windows_translate = 1;
+	  break;
 	case VK_SCROLL:
 	  /* Decide whether to treat as modifier or function key.  */
 	  if (NILP (Vw32_scroll_lock_modifier))
 	    goto disable_lock_key;
-	  return 0;
+	  windows_translate = 1;
+	  break;
 	disable_lock_key:
-	  /* Ensure the appropriate lock key state is off (and the
-             indicator light as well).  */
-	  if (GetAsyncKeyState (wParam) & 0x8000)
-	    {
-	      /* Fake another press of the relevant key.  Apparently,
-                 this really is the only way to turn off the indicator.  */
-	      dpyinfo->faked_key = wParam;
-	      keybd_event ((BYTE) wParam, (BYTE) MapVirtualKey (wParam, 0),
-			   KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
-	      keybd_event ((BYTE) wParam, (BYTE) MapVirtualKey (wParam, 0),
-			   KEYEVENTF_EXTENDEDKEY | 0, 0);
-	      keybd_event ((BYTE) wParam, (BYTE) MapVirtualKey (wParam, 0),
-			   KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
-	    }
+	  /* Ensure the appropriate lock key state (and indicator light)
+             remains in the same state. We do this by faking another
+             press of the relevant key.  Apparently, this really is the
+             only way to toggle the state of the indicator lights.  */
+	  dpyinfo->faked_key = wParam;
+	  keybd_event ((BYTE) wParam, (BYTE) MapVirtualKey (wParam, 0),
+		       KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
+	  keybd_event ((BYTE) wParam, (BYTE) MapVirtualKey (wParam, 0),
+		       KEYEVENTF_EXTENDEDKEY | 0, 0);
+	  keybd_event ((BYTE) wParam, (BYTE) MapVirtualKey (wParam, 0),
+		       KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
+	  /* Ensure indicator lights are updated promptly on Windows 9x
+             (TranslateMessage apparently does this), after forwarding
+             input event.  */
+	  post_character_message (hwnd, msg, wParam, lParam,
+				  w32_get_key_modifiers (wParam, lParam));
+	  windows_translate = 1;
 	  break;
 	case VK_CONTROL: 
 	case VK_SHIFT:
 	case VK_PROCESSKEY:  /* Generated by IME.  */
 	  windows_translate = 1;
 	  break;
+	case VK_CANCEL:
+	  /* Windows maps Ctrl-Pause (aka Ctrl-Break) into VK_CANCEL,
+             which is confusing for purposes of key binding; convert
+	     VK_CANCEL events into VK_PAUSE events.  */
+	  wParam = VK_PAUSE;
+	  break;
+	case VK_PAUSE:
+	  /* Windows maps Ctrl-NumLock into VK_PAUSE, which is confusing
+             for purposes of key binding; convert these back into
+             VK_NUMLOCK events, at least when we want to see NumLock key
+             presses.  (Note that there is never any possibility that
+             VK_PAUSE with Ctrl really is C-Pause as per above.)  */
+	  if (NILP (Vw32_enable_num_lock) && modifier_set (VK_CONTROL))
+	    wParam = VK_NUMLOCK;
+	  break;
 	default:
 	  /* If not defined as a function key, change it to a WM_CHAR message. */
 	  if (lispy_function_keys[wParam] == 0)
 	    {
+	      DWORD modifiers = construct_console_modifiers ();
+
 	      if (!NILP (Vw32_recognize_altgr)
 		  && modifier_set (VK_LCONTROL) && modifier_set (VK_RMENU))
 		{
@@ -3736,11 +3843,11 @@
 		     chords correctly.  */
 		  windows_translate = 1;
 		}
-	      else if (modifier_set (VK_CONTROL) || modifier_set (VK_MENU))
+	      else if ((modifiers & (~SHIFT_PRESSED & ~CAPSLOCK_ON)) != 0)
 		{
-		  /* Handle key chords including any modifiers other than shift
-		     directly, in order to preserve as much modifier information as
-		     possible.  */
+		  /* Handle key chords including any modifiers other
+		     than shift directly, in order to preserve as much
+		     modifier information as possible.  */
 		  if ('A' <= wParam && wParam <= 'Z')
 		    {
 		      /* Don't translate modified alphabetic keystrokes,
@@ -3766,7 +3873,7 @@
 		      key.wVirtualKeyCode = wParam;
 		      key.wVirtualScanCode = (lParam & 0xFF0000) >> 16;
 		      key.uChar.AsciiChar = 0;
-		      key.dwControlKeyState = construct_console_modifiers ();
+		      key.dwControlKeyState = modifiers;
 
 		      add = w32_kbd_patch_key (&key);
 		      /* 0 means an unrecognised keycode, negative means
@@ -3790,6 +3897,7 @@
 	    }
 	}
 
+    translate:
       if (windows_translate)
 	{
 	  MSG windows_msg = { hwnd, msg, wParam, lParam, 0, {0,0} };
@@ -3860,7 +3968,8 @@
 	  {
 	    /* Hold onto message for now. */
 	    mouse_button_timer =
-	      SetTimer (hwnd, MOUSE_BUTTON_ID, XINT (Vw32_mouse_button_tolerance), NULL);
+	      SetTimer (hwnd, MOUSE_BUTTON_ID,
+			XINT (Vw32_mouse_button_tolerance), NULL);
 	    saved_mouse_button_msg.msg.hwnd = hwnd;
 	    saved_mouse_button_msg.msg.message = msg;
 	    saved_mouse_button_msg.msg.wParam = wParam;
@@ -3960,7 +4069,8 @@
 
       if (saved_mouse_move_msg.msg.hwnd == 0)
 	mouse_move_timer =
-	  SetTimer (hwnd, MOUSE_MOVE_ID, XINT (Vw32_mouse_move_interval), NULL);
+	  SetTimer (hwnd, MOUSE_MOVE_ID,
+		    XINT (Vw32_mouse_move_interval), NULL);
 
       /* Hold onto message for now. */
       saved_mouse_move_msg.msg.hwnd = hwnd;
@@ -4146,8 +4256,6 @@
 #endif
 
     case WM_ACTIVATEAPP:
-      dpyinfo->faked_key = 0;
-      reset_modifiers ();
     case WM_ACTIVATE:
     case WM_WINDOWPOSCHANGED:
     case WM_SHOWWINDOW:
@@ -4157,6 +4265,8 @@
       goto dflt;
 
     case WM_SETFOCUS:
+      dpyinfo->faked_key = 0;
+      reset_modifiers ();
       register_hot_keys (hwnd);
       goto command;
     case WM_KILLFOCUS:
@@ -6600,6 +6710,39 @@
 
   return key;
 }
+
+DEFUN ("w32-toggle-lock-key", Fw32_toggle_lock_key, Sw32_toggle_lock_key, 1, 2, 0,
+   "Toggle the state of the lock key KEY.\n\
+KEY can be `capslock', `kp-numlock', or `scroll'.\n\
+If the optional parameter NEW-STATE is a number, then the state of KEY\n\
+is set to off if the low bit of NEW-STATE is zero, otherwise on.")
+  (key, new_state)
+     Lisp_Object key, new_state;
+{
+  int vk_code;
+  int cur_state;
+
+  if (EQ (key, intern ("capslock")))
+    vk_code = VK_CAPITAL;
+  else if (EQ (key, intern ("kp-numlock")))
+    vk_code = VK_NUMLOCK;
+  else if (EQ (key, intern ("scroll")))
+    vk_code = VK_SCROLL;
+  else
+    return Qnil;
+
+  if (!dwWindowsThreadId)
+    return make_number (w32_console_toggle_lock_key (vk_code, new_state));
+
+  if (PostThreadMessage (dwWindowsThreadId, WM_EMACS_TOGGLE_LOCK_KEY,
+			 (WPARAM) vk_code, (LPARAM) new_state))
+    {
+      MSG msg;
+      GetMessage (&msg, NULL, WM_EMACS_DONE, WM_EMACS_DONE);
+      return make_number (msg.wParam);
+    }
+  return Qnil;
+}
 
 syms_of_w32fns ()
 {
@@ -6677,6 +6820,21 @@
   staticpro (&Qdisplay);
   /* This is the end of symbol initialization.  */
 
+  Qhyper = intern ("hyper");
+  staticpro (&Qhyper);
+  Qsuper = intern ("super");
+  staticpro (&Qsuper);
+  Qmeta = intern ("meta");
+  staticpro (&Qmeta);
+  Qalt = intern ("alt");
+  staticpro (&Qalt);
+  Qctrl = intern ("ctrl");
+  staticpro (&Qctrl);
+  Qcontrol = intern ("control");
+  staticpro (&Qcontrol);
+  Qshift = intern ("shift");
+  staticpro (&Qshift);
+
   Qface_set_after_frame_default = intern ("face-set-after-frame-default");
   staticpro (&Qface_set_after_frame_default);
 
@@ -6715,6 +6873,16 @@
 When non-nil, the Start menu is opened by tapping the key.");
   Vw32_pass_rwindow_to_system = Qt;
 
+  DEFVAR_INT ("w32-phantom-key-code",
+	       &Vw32_phantom_key_code,
+	       "Virtual key code used to generate \"phantom\" key presses.\n\
+Value is a number between 0 and 255.\n\
+\n\
+Phantom key presses are generated in order to stop the system from\n\
+acting on \"Windows\" key events when `w32-pass-lwindow-to-system' or\n\
+`w32-pass-rwindow-to-system' is nil.");
+  Vw32_phantom_key_code = VK_SPACE;
+
   DEFVAR_LISP ("w32-enable-num-lock", 
 	       &Vw32_enable_num_lock,
 	       "Non-nil if Num Lock should act normally.\n\
@@ -6885,6 +7053,7 @@
   defsubr (&Sw32_unregister_hot_key);
   defsubr (&Sw32_registered_hot_keys);
   defsubr (&Sw32_reconstruct_hot_key);
+  defsubr (&Sw32_toggle_lock_key);
 
   /* Setting callback functions for fontset handler.  */
   get_font_info_func = w32_get_font_info;