]> git.eshelyaron.com Git - emacs.git/commitdiff
(Vw32_phantom_key_code): New variable.
authorGeoff Voelker <voelker@cs.washington.edu>
Wed, 2 Dec 1998 23:52:47 +0000 (23:52 +0000)
committerGeoff Voelker <voelker@cs.washington.edu>
Wed, 2 Dec 1998 23:52:47 +0000 (23:52 +0000)
(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.

src/w32fns.c

index b501d19db2da4d17757056f41f00fd52f0838c3d..47423f466f8d6a773b1a0ee5f75cf7f975472a64 100644 (file)
@@ -48,6 +48,7 @@ Boston, MA 02111-1307, USA.  */
 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 @@ Lisp_Object Vw32_pass_lwindow_to_system;
    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_position;
 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 @@ reset_modifiers ()
 {
   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 @@ reset_modifiers ()
     /* 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 @@ w32_key_to_modifier (int key)
       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 @@ w32_msg_pump (deferred_msg * msg_buf)
              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 @@ w32_wnd_proc (hwnd, msg, wParam, lParam)
       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 @@ w32_wnd_proc (hwnd, msg, wParam, lParam)
                 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 @@ w32_wnd_proc (hwnd, msg, wParam, lParam)
          /* 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 @@ w32_wnd_proc (hwnd, msg, wParam, lParam)
                     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 @@ w32_wnd_proc (hwnd, msg, wParam, lParam)
                      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 @@ w32_wnd_proc (hwnd, msg, wParam, lParam)
            }
        }
 
+    translate:
       if (windows_translate)
        {
          MSG windows_msg = { hwnd, msg, wParam, lParam, 0, {0,0} };
@@ -3860,7 +3968,8 @@ w32_wnd_proc (hwnd, msg, wParam, lParam)
          {
            /* 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 @@ w32_wnd_proc (hwnd, msg, wParam, lParam)
 
       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 @@ w32_wnd_proc (hwnd, msg, wParam, lParam)
 #endif
 
     case WM_ACTIVATEAPP:
-      dpyinfo->faked_key = 0;
-      reset_modifiers ();
     case WM_ACTIVATE:
     case WM_WINDOWPOSCHANGED:
     case WM_SHOWWINDOW:
@@ -4157,6 +4265,8 @@ w32_wnd_proc (hwnd, msg, wParam, lParam)
       goto dflt;
 
     case WM_SETFOCUS:
+      dpyinfo->faked_key = 0;
+      reset_modifiers ();
       register_hot_keys (hwnd);
       goto command;
     case WM_KILLFOCUS:
@@ -6600,6 +6710,39 @@ DEFUN ("w32-reconstruct-hot-key", Fw32_reconstruct_hot_key, Sw32_reconstruct_hot
 
   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;
+}
 \f
 syms_of_w32fns ()
 {
@@ -6677,6 +6820,21 @@ syms_of_w32fns ()
   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.");
 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 @@ displayed according to the current fontset.");
   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;