emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH] Override Windows default Win-* key combinations when using E


From: Jussi Lahdenniemi
Subject: Re: [PATCH] Override Windows default Win-* key combinations when using Emacs
Date: Sat, 9 Jan 2016 21:58:52 +0200
User-agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:38.0) Gecko/20100101 Thunderbird/38.5.0

On 5.1.2016 19.05, Eli Zaretskii wrote:
Thank you for your contribution.  It is large enough to require legal
paperwork; I can send you the forms off-list if you agree.

I finished integrating the code more tightly into Emacs sources, removing the need for the additional process and adding functionality for grabbing Alt-based keyboard input (M-tab, M-ESC) in addition to the Windows keys. I also updated the documentation.

The patch is attached. I decided to place the new functionality in a new file (w32hook.c) - it could have been added to some existing file as well.

The assignment paperwork is in the mail.


----------------------- 8< 8< -----------------------
The Windows keyboard hooking code properly grabs system hotkeys such as Win-*
and Alt-Tab for Emacs use.  The new code replaces the w32-register-hot-key
functionality.  The hooking code also works for both windowed and console
(emacs -nw) mode.

* configure.ac: Added the new file src/w32hook.c
* src/w32term.h: Added new function prototypes
* src/w32fns.c: Updated the w32-register-hot-key related code to use
  the new hooking code instead
* src/w32console.c: Updated to support hooking for console Emacs
* src/w32inevt.c: Ditto
* src/w32hook.c: New file containing the hooking code
* doc/emacs/msdos.texi: Updated documentation for the new functionality

Fixes
---
 configure.ac         |   1 +
 doc/emacs/msdos.texi | 100 +++++++++------
 src/w32console.c     |   3 +
 src/w32fns.c         | 288 ++++++++++--------------------------------
src/w32hook.c | 343 +++++++++++++++++++++++++++++++++++++++++++++++++++
 src/w32inevt.c       |  12 +-
 src/w32term.h        |  10 ++
 7 files changed, 485 insertions(+), 272 deletions(-)
 create mode 100644 src/w32hook.c

diff --git a/configure.ac b/configure.ac
index 0aa863a..4ae0f26 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1959,6 +1959,7 @@ if test "${HAVE_W32}" = "yes"; then
                 [AC_MSG_ERROR([No resource compiler found.])])
   W32_OBJ="w32fns.o w32menu.o w32reg.o w32font.o w32term.o"
   W32_OBJ="$W32_OBJ w32xfns.o w32select.o w32uniscribe.o"
+  W32_OBJ="$W32_OBJ w32hook.o"
   EMACSRES="emacs.res"
   case "$canonical" in
     x86_64-*-*) EMACS_MANIFEST="emacs-x64.manifest" ;;
diff --git a/doc/emacs/msdos.texi b/doc/emacs/msdos.texi
index f1cdb26..34ad639 100644
--- a/doc/emacs/msdos.texi
+++ b/doc/emacs/msdos.texi
@@ -504,39 +504,6 @@ Windows Keyboard
 key.  If you wish it to produce the @code{Alt} modifier instead, set
 the variable @code{w32-alt-is-meta} to a @code{nil} value.

address@hidden w32-register-hot-key
address@hidden w32-unregister-hot-key
-  MS-Windows reserves certain key combinations, such as
address@hidden@address@hidden, for its own use.  These key combinations are
-intercepted by the system before Emacs can see them.  You can use the
address@hidden function to allow a key sequence to be
-seen by Emacs instead of being grabbed by Windows.  This function
-registers a key sequence as a @dfn{hot key}, overriding the special
-meaning of that key sequence for Windows.  (MS-Windows is told that
-the key sequence is a hot key only when one of the Emacs windows has
-focus, so that the special keys still have their usual meaning for
-other Windows applications.)
-
-  The argument to @code{w32-register-hot-key} must be a single key,
-with or without modifiers, in vector form that would be acceptable to
address@hidden  The meta modifier is interpreted as the @key{Alt}
-key if @code{w32-alt-is-meta} is @code{t} (the default), and the hyper
-modifier is always interpreted as the Windows key (usually labeled
-with @key{start} and the Windows logo).  If the function succeeds in
-registering the key sequence, it returns the hotkey ID, a number;
-otherwise it returns @code{nil}.
-
address@hidden address@hidden, (MS-Windows)}
address@hidden @address@hidden vs @address@hidden@key{TAB}} (MS-Windows)
address@hidden @address@hidden@key{TAB}} vs @address@hidden (MS-Windows)
-  For example, @code{(w32-register-hot-key [M-tab])} lets you use
address@hidden@key{TAB}} normally in Emacs; for instance, to complete the word 
or
-symbol at point at top level, or to complete the current search string
-against previously sought strings during incremental search.
-
-  The function @code{w32-unregister-hot-key} reverses the effect of
address@hidden for its argument key sequence.
-
 @vindex w32-capslock-is-shiftlock
   By default, the @key{CapsLock} key only affects normal character
 keys (it converts lower-case characters to their upper-case
@@ -582,6 +549,66 @@ Windows Keyboard
 right Windows key produces the symbol @code{rwindow} and @key{ScrLock}
 produces the symbol @code{scroll}.

address@hidden w32-register-hot-key
address@hidden w32-unregister-hot-key
+  MS-Windows reserves certain key combinations, such as
address@hidden@address@hidden and a number of Windows key combinations,
+for its own use.  These key combinations are intercepted by the system
+before Emacs can see them.  Also, on Windows 10, all Windows key
+combinations are reserved by the system in such a way that they are
+never propagated to applications, even if the system does not
+currently define a hotkey on the specific combination.  You can use
+the @code{w32-register-hot-key} function to allow a key sequence to be
+seen by Emacs instead of being grabbed by Windows.  When registered as
+a hot key, the key combination is pulled out of the system's input
+queue before it is handled by Windows, effectively overriding the
+special meaning of that key sequence for Windows.  The override is
+only effective when Emacs is active; with other applications on the
+foreground the keys behave normally.
+
+   The argument to @code{w32-register-hot-key} must be a single key with a
+single modifier, in vector form that would be acceptable to
address@hidden  The control and shift modifiers have no effect on the
+argument.  The meta modifier is interpreted as the @key{Alt} key if
address@hidden is @code{t} (the default), and the super and hyper
+modifiers are interpreted according to the bindings of
address@hidden and @code{w32-rwindow-modifier}. Additionally, a
+modifier with the trailing dash but with no key indicates that all
+Windows defined hotkeys for that modifier are to be overridden in the
+favor of Emacs.
+
address@hidden address@hidden, (MS-Windows)}
address@hidden @address@hidden vs @address@hidden@key{TAB}} (MS-Windows)
address@hidden @address@hidden@key{TAB}} vs @address@hidden (MS-Windows)
+   For example, @code{(w32-register-hot-key [M-tab])} lets you use
address@hidden@key{TAB}} normally in Emacs; for instance, to complete the
+word or symbol at point at top level, or to complete the current
+search string against previously sought strings during incremental
+search.  @code{(w32-register-hot-key [s-])} with
address@hidden bound to @code{super} disables all the
+Windows' own Windows key based address@hidden is one known
+exception: The combination @address@hidden@key{L}} that locks the
+workstation is handled by the system on a lower level.  For this
+reason, @code{w32-register-hot-key} cannot override this key
+combination - it always locks the computer.}
+
+   Note that @code{w32-register-hot-key} checks the
address@hidden values at the time of the function
+call.  Thus, you can set @code{w32-lwindow-modifier} as @code{super},
+then call @code{(w32-register-hot-key [s-r])}, and finally set
address@hidden as @code{super} as well.  The result is
+that the left Windows key together with @key{R} invokes whichever
+function you have bound for the combination in Emacs, and the right
+Windows key and @key{R} opens the Windows @code{Run} dialog.
+
+   The hotkey registrations always also include all the shift and
+control modifier combinations for the given hotkey; that is,
+registering @address@hidden as a hotkey gives you @address@hidden,
address@hidden@key{a}} and @address@hidden as well.
+
+  The function @code{w32-unregister-hot-key} reverses the effect of
address@hidden for its argument key sequence.
+
 @vindex w32-pass-alt-to-system
 @cindex Windows system menu
 @cindex @code{Alt} key invokes menu (Windows)
@@ -607,12 +634,7 @@ Windows Keyboard
 otherwise it is passed to Windows.  The default is @code{t} for both
 of these variables.  Passing each of these keys to Windows produces
 its normal effect: for example, @address@hidden opens the
address@hidden menu, address@hidden
-Some combinations of the ``Windows'' keys with other keys are caught
-by Windows at a low level in a way that Emacs currently cannot prevent.
-For example, @address@hidden r} always pops up the Windows
address@hidden dialog.  Customizing the value of
address@hidden might help in some cases, though.}
address@hidden menu, etc.

 @vindex w32-recognize-altgr
 @kindex AltGr @r{(MS-Windows)}
diff --git a/src/w32console.c b/src/w32console.c
index 7fffabf..57f74d1 100644
--- a/src/w32console.c
+++ b/src/w32console.c
@@ -759,6 +759,9 @@ initialize_w32_display (struct terminal *term, int *width, int *height)

   /* Setup w32_display_info structure for this frame. */
   w32_initialize_display_info (build_string ("Console"));
+
+  /* Set up the keyboard hook */
+  setup_w32_kbdhook ();
 }


diff --git a/src/w32fns.c b/src/w32fns.c
index c1d9bff..99c9ed1 100644
--- a/src/w32fns.c
+++ b/src/w32fns.c
@@ -2248,6 +2248,8 @@ modifier_set (int vkey)
       else
        return (GetKeyState (vkey) & 0x1);
     }
+  if (vkey == VK_LWIN || vkey == VK_RWIN)
+    return check_w32_winkey_state (vkey);

   if (!modifiers_recorded)
     return (GetKeyState (vkey) & 0x8000);
@@ -2387,60 +2389,6 @@ map_keypad_keys (unsigned int virt_key, unsigned int extended)
   return virt_key;
 }

-/* List of special key combinations which w32 would normally capture,
-   but Emacs should grab instead.  Not directly visible to lisp, to
-   simplify synchronization.  Each item is an integer encoding a virtual
-   key code and modifier combination to capture.  */
-static Lisp_Object w32_grabbed_keys;
-
-#define HOTKEY(vk, mods)      make_number (((vk) & 255) | ((mods) << 8))
-#define HOTKEY_ID(k)          (XFASTINT (k) & 0xbfff)
-#define HOTKEY_VK_CODE(k)     (XFASTINT (k) & 255)
-#define HOTKEY_MODIFIERS(k)   (XFASTINT (k) >> 8)
-
-#define RAW_HOTKEY_ID(k)        ((k) & 0xbfff)
-#define RAW_HOTKEY_VK_CODE(k)   ((k) & 255)
-#define RAW_HOTKEY_MODIFIERS(k) ((k) >> 8)
-
-/* Register hot-keys for reserved key combinations when Emacs has
-   keyboard focus, since this is the only way Emacs can receive key
-   combinations like Alt-Tab which are used by the system.  */
-
-static void
-register_hot_keys (HWND hwnd)
-{
-  Lisp_Object keylist;
-
-  /* Use CONSP, since we are called asynchronously.  */
- for (keylist = w32_grabbed_keys; CONSP (keylist); keylist = XCDR (keylist))
-    {
-      Lisp_Object key = XCAR (keylist);
-
-      /* Deleted entries get set to nil.  */
-      if (!INTEGERP (key))
-       continue;
-
-      RegisterHotKey (hwnd, HOTKEY_ID (key),
-                     HOTKEY_MODIFIERS (key), HOTKEY_VK_CODE (key));
-    }
-}
-
-static void
-unregister_hot_keys (HWND hwnd)
-{
-  Lisp_Object keylist;
-
- for (keylist = w32_grabbed_keys; CONSP (keylist); keylist = XCDR (keylist))
-    {
-      Lisp_Object key = XCAR (keylist);
-
-      if (!INTEGERP (key))
-       continue;
-
-      UnregisterHotKey (hwnd, HOTKEY_ID (key));
-    }
-}
-
 #if EMACSDEBUG
 const char*
 w32_name_of_message (UINT msg)
@@ -2570,27 +2518,6 @@ w32_msg_pump (deferred_msg * msg_buf)
                                      result, 0))
                emacs_abort ();
              break;
-           case WM_EMACS_REGISTER_HOT_KEY:
-             focus_window = GetFocus ();
-             if (focus_window != NULL)
-               RegisterHotKey (focus_window,
-                               RAW_HOTKEY_ID (msg.wParam),
-                               RAW_HOTKEY_MODIFIERS (msg.wParam),
-                               RAW_HOTKEY_VK_CODE (msg.wParam));
-             /* Reply is not expected.  */
-             break;
-           case WM_EMACS_UNREGISTER_HOT_KEY:
-             focus_window = GetFocus ();
-             if (focus_window != NULL)
-               UnregisterHotKey (focus_window, RAW_HOTKEY_ID (msg.wParam));
-             /* 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.  */
-             XSETCAR (make_lisp_ptr ((void *)msg.lParam, Lisp_Cons), Qnil);
-             if (!PostThreadMessage (dwMainThreadId, WM_EMACS_DONE, 0, 0))
-               emacs_abort ();
-             break;
            case WM_EMACS_TOGGLE_LOCK_KEY:
              {
                int vk_code = (int) msg.wParam;
@@ -3401,11 +3328,6 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
       }
       goto dflt;

-    case WM_HOTKEY:
-      /* Synchronize hot keys with normal input.  */
-      PostMessage (hwnd, WM_KEYDOWN, HIWORD (lParam), 0);
-      return (0);
-
     case WM_KEYUP:
     case WM_SYSKEYUP:
       record_keyup (wParam, lParam);
@@ -3439,41 +3361,16 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

       switch (wParam)
        {
-       case VK_LWIN:
-         if (NILP (Vw32_pass_lwindow_to_system))
-           {
-             /* Prevent system from acting on keyup (which opens the
-                Start menu if no other key was pressed) by simulating a
-                press of Space which we will ignore.  */
-             if (GetAsyncKeyState (wParam) & 1)
-               {
-                 if (NUMBERP (Vw32_phantom_key_code))
-                   key = XUINT (Vw32_phantom_key_code) & 255;
-                 else
-                   key = VK_SPACE;
-                 dpyinfo->faked_key = key;
-                 keybd_event (key, (BYTE) MapVirtualKey (key, 0), 0, 0);
-               }
-           }
-         if (!NILP (Vw32_lwindow_modifier))
-           return 0;
-         break;
-       case VK_RWIN:
-         if (NILP (Vw32_pass_rwindow_to_system))
-           {
-             if (GetAsyncKeyState (wParam) & 1)
-               {
-                 if (NUMBERP (Vw32_phantom_key_code))
-                   key = XUINT (Vw32_phantom_key_code) & 255;
-                 else
-                   key = VK_SPACE;
-                 dpyinfo->faked_key = key;
-                 keybd_event (key, (BYTE) MapVirtualKey (key, 0), 0, 0);
-               }
-           }
-         if (!NILP (Vw32_rwindow_modifier))
-           return 0;
-         break;
+          /* The Win keys are handled in w32hook.c and must be silently
+             ignored here when used as a modifier */
+        case VK_LWIN:
+          if (!NILP (Vw32_lwindow_modifier))
+            return 0;
+          break;
+        case VK_RWIN:
+          if (!NILP (Vw32_rwindow_modifier))
+            return 0;
+          break;
        case VK_APPS:
          if (!NILP (Vw32_apps_modifier))
            return 0;
@@ -4316,10 +4213,8 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
     case WM_SETFOCUS:
       dpyinfo->faked_key = 0;
       reset_modifiers ();
-      register_hot_keys (hwnd);
       goto command;
     case WM_KILLFOCUS:
-      unregister_hot_keys (hwnd);
       button_state = 0;
       ReleaseCapture ();
       /* Relinquish the system caret.  */
@@ -4348,10 +4243,20 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
       my_post_msg (&wmsg, hwnd, msg, wParam, lParam);
       goto dflt;

+    case WM_CREATE:
+      setup_w32_kbdhook ();
+      goto dflt;
+
     case WM_DESTROY:
+      remove_w32_kbdhook ();
       CoUninitialize ();
       return 0;

+    case WM_WTSSESSION_CHANGE:
+      if (wParam == WTS_SESSION_LOCK)
+        reset_w32_kbdhook_state ();
+      goto dflt;
+
     case WM_CLOSE:
       wmsg.dwModifiers = w32_get_modifiers ();
       my_post_msg (&wmsg, hwnd, msg, wParam, lParam);
@@ -7617,19 +7522,31 @@ lookup_vk_code (char *key)
        && strcmp (lispy_function_keys[i], key) == 0)
       return i;

+  /* alphanumerics map to themselves */
+  if (key[1] == 0)
+    {
+      if (key[0] >= 'A' && key[0] <= 'Z' ||
+          key[0] >= '0' && key[0] <= '9')
+        return key[0];
+      if (key[0] >= 'a' && key[0] <= 'z')
+        return toupper(key[0]);
+    }
+
   return -1;
 }

 /* Convert a one-element vector style key sequence to a hot key
    definition.  */
 static Lisp_Object
-w32_parse_hot_key (Lisp_Object key)
+w32_parse_and_register_hot_key (Lisp_Object key, int hook)
 {
   /* Copied from Fdefine_key and store_in_keymap.  */
   register Lisp_Object c;
   int vk_code;
   int lisp_modifiers;
   int w32_modifiers;
+  Lisp_Object res = Qnil;
+  char* vkname;

   CHECK_VECTOR (key);

@@ -7652,7 +7569,12 @@ w32_parse_hot_key (Lisp_Object key)
       c = Fcar (c);
       if (!SYMBOLP (c))
        emacs_abort ();
-      vk_code = lookup_vk_code (SSDATA (SYMBOL_NAME (c)));
+      vkname = SSDATA (SYMBOL_NAME (c));
+      /* [s-], [M-], [h-]: Register all keys for this modifier */
+      if (vkname[0] == 0)
+        vk_code = VK_ANY;
+      else
+        vk_code = lookup_vk_code (vkname);
     }
   else if (INTEGERP (c))
     {
@@ -7668,21 +7590,27 @@ w32_parse_hot_key (Lisp_Object key)
       && !NILP (Vw32_alt_is_meta))
     lisp_modifiers |= alt_modifier;

-  /* Supply defs missing from mingw32.  */
-#ifndef MOD_ALT
-#define MOD_ALT         0x0001
-#define MOD_CONTROL     0x0002
-#define MOD_SHIFT       0x0004
-#define MOD_WIN         0x0008
-#endif
-
-  /* Convert lisp modifiers to Windows hot-key form.  */
-  w32_modifiers  = (lisp_modifiers & hyper_modifier)    ? MOD_WIN : 0;
-  w32_modifiers |= (lisp_modifiers & alt_modifier)      ? MOD_ALT : 0;
-  w32_modifiers |= (lisp_modifiers & ctrl_modifier)     ? MOD_CONTROL : 0;
-  w32_modifiers |= (lisp_modifiers & shift_modifier)    ? MOD_SHIFT : 0;
+  /* Register Alt-x combinations */
+  if (lisp_modifiers & alt_modifier)
+    {
+      hook_w32_key (hook, VK_MENU, vk_code);
+      res = Qt;
+    }
+  /* Register Win-x combinations based on modifier mappings */
+ if ((lisp_modifiers & hyper_modifier) && EQ (Vw32_lwindow_modifier, Qhyper) || + (lisp_modifiers & super_modifier) && EQ (Vw32_lwindow_modifier, Qsuper))
+    {
+      hook_w32_key (hook, VK_LWIN, vk_code);
+      res = Qt;
+    }
+ if ((lisp_modifiers & hyper_modifier) && EQ (Vw32_rwindow_modifier, Qhyper) || + (lisp_modifiers & super_modifier) && EQ (Vw32_rwindow_modifier, Qsuper))
+    {
+      hook_w32_key (hook, VK_RWIN, vk_code);
+      res = Qt;
+    }

-  return HOTKEY (vk_code, w32_modifiers);
+  return res;
 }

 DEFUN ("w32-register-hot-key", Fw32_register_hot_key,
@@ -7701,26 +7629,7 @@ is always interpreted as the Windows modifier keys.
 The return value is the hotkey-id if registered, otherwise nil.  */)
   (Lisp_Object key)
 {
-  key = w32_parse_hot_key (key);
-
-  if (!NILP (key) && NILP (Fmemq (key, w32_grabbed_keys)))
-    {
-      /* Reuse an empty slot if possible.  */
-      Lisp_Object item = Fmemq (Qnil, w32_grabbed_keys);
-
-      /* Safe to add new key to list, even if we have focus.  */
-      if (NILP (item))
-       w32_grabbed_keys = Fcons (key, w32_grabbed_keys);
-      else
-       XSETCAR (item, key);
-
-      /* Notify input thread about new hot-key definition, so that it
-        takes effect without needing to switch focus.  */
-      PostThreadMessage (dwWindowsThreadId, WM_EMACS_REGISTER_HOT_KEY,
-                        (WPARAM) XINT (key), 0);
-    }
-
-  return key;
+  return w32_parse_and_register_hot_key (key, 1);
 }

 DEFUN ("w32-unregister-hot-key", Fw32_unregister_hot_key,
@@ -7728,73 +7637,7 @@ DEFUN ("w32-unregister-hot-key", Fw32_unregister_hot_key,
        doc: /* Unregister KEY as a hot-key combination.  */)
   (Lisp_Object key)
 {
-  Lisp_Object item;
-
-  if (!INTEGERP (key))
-    key = w32_parse_hot_key (key);
-
-  item = Fmemq (key, w32_grabbed_keys);
-
-  if (!NILP (item))
-    {
-      LPARAM lparam;
-
-      eassert (CONSP (item));
-      /* Pass the tail of the list as a pointer to a Lisp_Cons cell,
-        so that it works in a --with-wide-int build as well.  */
-      lparam = (LPARAM) XUNTAG (item, Lisp_Cons);
-
-      /* Notify input thread about hot-key definition being removed, so
-        that it takes effect without needing focus switch.  */
- if (PostThreadMessage (dwWindowsThreadId, WM_EMACS_UNREGISTER_HOT_KEY,
-                            (WPARAM) XINT (XCAR (item)), lparam))
-       {
-         MSG msg;
-         GetMessage (&msg, NULL, WM_EMACS_DONE, WM_EMACS_DONE);
-       }
-      return Qt;
-    }
-  return Qnil;
-}
-
-DEFUN ("w32-registered-hot-keys", Fw32_registered_hot_keys,
-       Sw32_registered_hot_keys, 0, 0, 0,
-       doc: /* Return list of registered hot-key IDs.  */)
-  (void)
-{
-  return Fdelq (Qnil, Fcopy_sequence (w32_grabbed_keys));
-}
-
-DEFUN ("w32-reconstruct-hot-key", Fw32_reconstruct_hot_key,
-       Sw32_reconstruct_hot_key, 1, 1, 0,
-       doc: /* Convert hot-key ID to a lisp key combination.
-usage: (w32-reconstruct-hot-key ID)  */)
-  (Lisp_Object hotkeyid)
-{
-  int vk_code, w32_modifiers;
-  Lisp_Object key;
-
-  CHECK_NUMBER (hotkeyid);
-
-  vk_code = HOTKEY_VK_CODE (hotkeyid);
-  w32_modifiers = HOTKEY_MODIFIERS (hotkeyid);
-
-  if (vk_code < 256 && lispy_function_keys[vk_code])
-    key = intern (lispy_function_keys[vk_code]);
-  else
-    key = make_number (vk_code);
-
-  key = Fcons (key, Qnil);
-  if (w32_modifiers & MOD_SHIFT)
-    key = Fcons (Qshift, key);
-  if (w32_modifiers & MOD_CONTROL)
-    key = Fcons (Qctrl, key);
-  if (w32_modifiers & MOD_ALT)
-    key = Fcons (NILP (Vw32_alt_is_meta) ? Qalt : Qmeta, key);
-  if (w32_modifiers & MOD_WIN)
-    key = Fcons (Qhyper, key);
-
-  return key;
+  return w32_parse_and_register_hot_key (key, 0);
 }

 DEFUN ("w32-toggle-lock-key", Fw32_toggle_lock_key,
@@ -9307,9 +9150,6 @@ syms_of_w32fns (void)
   Fput (Qundefined_color, Qerror_message,
        build_pure_c_string ("Undefined color"));

-  staticpro (&w32_grabbed_keys);
-  w32_grabbed_keys = Qnil;
-
   DEFVAR_LISP ("w32-color-map", Vw32_color_map,
               doc: /* An array of color name mappings for Windows.  */);
   Vw32_color_map = Qnil;
@@ -9622,8 +9462,6 @@ This variable has effect only on Windows Vista and later. */);
   defsubr (&Sw32_shell_execute);
   defsubr (&Sw32_register_hot_key);
   defsubr (&Sw32_unregister_hot_key);
-  defsubr (&Sw32_registered_hot_keys);
-  defsubr (&Sw32_reconstruct_hot_key);
   defsubr (&Sw32_toggle_lock_key);
   defsubr (&Sw32_window_exists_p);
   defsubr (&Sw32_battery_status);
diff --git a/src/w32hook.c b/src/w32hook.c
new file mode 100644
index 0000000..1b1e35b
--- /dev/null
+++ b/src/w32hook.c
@@ -0,0 +1,343 @@
+/* System hooks for GNU Emacs on the Microsoft Windows API.
+   Copyright (C) 1992, 1999, 2001-2015 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* Written by Jussi Lahdenniemi <address@hidden>.
+
+   This code handles grabbing of the Windows keys and Alt-* key
+   combinations reserved by the system.  Handling Alt is a bit easier,
+   as Windows intends Alt-* shortcuts for application use in Windows;
+   hotkeys such as Alt-tab and Alt-escape are special cases.  Win-*
+   hotkeys, on the other hand, are primarily meant for system use.
+
+   As a result, when we want Emacs to be able to grab the Win-* keys,
+   we must swallow all Win key presses in a low-level keyboard hook.
+   Unfortunately, thismeans that the Emacs window procedure (and
+   console input handler) never see the keypresses either.  Thus, to
+   check the modifier states properly, Emacs code must use the
+   check_w32_winkey_state function that uses the flags directly
+   updated by the hook callback.
+
+   The keyboard hook sees keyboard input on all processes (except
+   elevated ones, when Emacs itself is not elevated).  As such, care
+   must be taken to only filter out keyboard input when Emacs itself
+   is on the foreground.
+ */
+
+#include <string.h>
+#include <config.h>
+#include "lisp.h"
+#include "w32common.h"
+#include "w32term.h"
+#include "w32inevt.h"
+
+static int hook_count = 0; /* counter, if several windows are created */
+static HHOOK hook = NULL;  /* hook handle */
+
+static int lwindown = 0; /* Left Windows key currently pressed (and hooked) */ +static int rwindown = 0; /* Right Windows key currently pressed (and hooked) */ +static int winsdown = 0; /* Number of handled keys currently pressed */ +static int send_win_up = 0; /* Pass through the keyup for this Windows key press? */ +static int suppress_lone = 0; /* Suppress simulated Windows keydown-keyup for this press? */
+static int winseen = 0;       /* Windows keys seen during this press? */
+
+static char alt_hooked[256] = {0};  /* hook Alt+[this key]? */
+static char lwin_hooked[256] = {0}; /* hook left Win+[this key]? */
+static char rwin_hooked[256] = {0}; /* hook right Win+[this key]? */
+
+/* stdin, from w32console.c */
+extern HANDLE keyboard_handle;
+
+/* The Windows keyboard hook callback */
+static LRESULT CALLBACK funhook( int code, WPARAM w, LPARAM l )
+{
+  INPUT inputs[2];
+  HWND focus = GetFocus();
+  int console = 0;
+  KBDLLHOOKSTRUCT const* hs = (KBDLLHOOKSTRUCT*)l;
+
+  if( code < 0 || (hs->flags & LLKHF_INJECTED))
+    {
+      return CallNextHookEx( 0, code, w, l );
+    }
+
+  /* GetFocus() returns a non-NULL window if another application is active,
+ and always for a console Emacs process. For a console Emacs, determine
+     focus by checking if the current foreground window is the process's
+     console window. */
+  if (focus == NULL)
+    {
+      HWND con = GetConsoleWindow();
+      if (con == GetForegroundWindow())
+        {
+          focus = con;
+          console = 1;
+        }
+    }
+
+  /* First, check hooks for the left and right Windows keys */
+  if( hs->vkCode == VK_LWIN || hs->vkCode == VK_RWIN)
+    {
+      if( focus != NULL && (w == WM_KEYDOWN || w == WM_SYSKEYDOWN))
+        {
+          /* The key is being pressed in an Emacs window */
+          if( hs->vkCode == VK_LWIN && !lwindown )
+            {
+              lwindown = 1;
+              winseen = 1;
+              winsdown++;
+            }
+          else if( hs->vkCode == VK_RWIN && !rwindown )
+            {
+              rwindown = 1;
+              winseen = 1;
+              winsdown++;
+            }
+ /* Returning 1 here drops the keypress without further processing.
+             If the keypress was allowed to go through, the normal Windows
+             hotkeys would take over. */
+          return 1;
+        }
+      else if( winsdown > 0 && (w == WM_KEYUP || w == WM_SYSKEYUP))
+        {
+          /* A key that has been captured earlier is being released now. */
+          if( hs->vkCode == VK_LWIN && lwindown )
+            {
+              lwindown = 0;
+              winsdown--;
+            }
+          else if( hs->vkCode == VK_RWIN && rwindown )
+            {
+              rwindown = 0;
+              winsdown--;
+            }
+          if( winsdown == 0 && winseen )
+            {
+              if( !suppress_lone )
+                {
+ /* The Windows key was pressed, then released, without any other + key pressed simultaneously. Normally, this opens the Start
+                     menu, but the user can prevent this by setting the
+                     w32-pass-[lr]window-to-system variable to NIL. */
+ if (hs->vkCode == VK_LWIN && !NILP(Vw32_pass_lwindow_to_system) || + hs->vkCode == VK_RWIN && !NILP(Vw32_pass_rwindow_to_system))
+                    {
+ /* Not prevented - Simulate the keypress to the system */
+                      memset( inputs, 0, sizeof(inputs));
+                      inputs[0].type = INPUT_KEYBOARD;
+                      inputs[0].ki.wVk = hs->vkCode;
+                      inputs[0].ki.wScan = hs->vkCode;
+                      inputs[0].ki.dwFlags = KEYEVENTF_EXTENDEDKEY;
+                      inputs[0].ki.time = 0;
+                      inputs[1].type = INPUT_KEYBOARD;
+                      inputs[1].ki.wVk = hs->vkCode;
+                      inputs[1].ki.wScan = hs->vkCode;
+ inputs[1].ki.dwFlags = KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP;
+                      inputs[1].ki.time = 0;
+                      SendInput( 2, inputs, sizeof(INPUT));
+                    }
+                  else if( focus != NULL )
+                    {
+ /* When not passed to system, must simulate privately to Emacs */
+                      PostMessage( focus, WM_SYSKEYDOWN, hs->vkCode, 0 );
+                      PostMessage( focus, WM_SYSKEYUP, hs->vkCode, 0 );
+                    }
+                }
+            }
+          if( winsdown == 0 )
+            {
+              /* No Windows keys pressed anymore - clear the state flags */
+              suppress_lone = 0;
+              winseen = 0;
+            }
+          if( !send_win_up )
+            {
+ /* Swallow this release message, as not to confuse applications who + did not get to see the original WM_KEYDOWN message either. */
+              return 1;
+            }
+          send_win_up = 0;
+        }
+    }
+  else if( winsdown > 0 )
+    {
+      /* Some other key was pressed while a captured Win key is down.
+         This is either an Emacs registered hotkey combination, or a
+         system hotkey. */
+      if( lwindown && lwin_hooked[hs->vkCode] ||
+          rwindown && rwin_hooked[hs->vkCode] )
+        {
+ /* Hooked Win-x combination, do not pass the keypress to Windows */
+          suppress_lone = 1;
+        }
+      else if (!suppress_lone)
+        {
+          /* Unhooked S-x combination; simulate the combination now
+             (will be seen by the system) */
+          memset( inputs, 0, sizeof(inputs));
+          inputs[0].type = INPUT_KEYBOARD;
+          inputs[0].ki.wVk = lwindown ? VK_LWIN : VK_RWIN;
+          inputs[0].ki.wScan = lwindown ? VK_LWIN : VK_RWIN;
+          inputs[0].ki.dwFlags = KEYEVENTF_EXTENDEDKEY;
+          inputs[0].ki.time = 0;
+          inputs[1].type = INPUT_KEYBOARD;
+          inputs[1].ki.wVk = hs->vkCode;
+          inputs[1].ki.wScan = hs->scanCode;
+ inputs[1].ki.dwFlags = (hs->flags & LLKHF_EXTENDED) ? KEYEVENTF_EXTENDEDKEY : 0;
+          inputs[1].ki.time = 0;
+          SendInput( 2, inputs, sizeof(INPUT));
+          /* Stop processing of this Win sequence here; the
+             corresponding keyup messages will come through the normal
+             channel when the keys are released. */
+          suppress_lone = 1;
+          send_win_up = 1;
+          /* Swallow the original keypress (as we want the Win key
+             down message simulated above to precede this real message). */
+          return 1;
+        }
+    }
+
+  /* Next, handle the registered Alt-* combinations */
+  if( (w == WM_SYSKEYDOWN || w == WM_KEYDOWN) &&
+      alt_hooked[hs->vkCode] &&
+      focus != NULL &&
+      (GetAsyncKeyState(VK_MENU) & 0x8000))
+    {
+      /* Prevent the system from getting this Alt-* key - suppress the
+         message and post as a normal keypress to Emacs.
+
+         For some reason, in case of the Alt-escape combination on a
+         console Emacs, the console process swallows the escape
+         keydown message here.  Simulating the keypress with SendInput
+         just gives the standard functionality of sending the window
+         to back.  So, to make Alt-escape to work on a console Emacs,
+         the keypress would need to be communicated directly from here
+         to the key_event functionality in w32inevt.c; but this hook
+         code does not run on the same thread as the normal keyboard
+         input code, so proper synchronization should be done.  As
+         this is quite a corner case, it is left unimplemented for
+         now. */
+      if( console )
+        {
+          INPUT_RECORD rec;
+          DWORD n;
+          rec.EventType = KEY_EVENT;
+          rec.Event.KeyEvent.bKeyDown = TRUE;
+          rec.Event.KeyEvent.wVirtualKeyCode = hs->vkCode;
+          rec.Event.KeyEvent.wVirtualScanCode = hs->scanCode;
+          rec.Event.KeyEvent.uChar.UnicodeChar = 0;
+          rec.Event.KeyEvent.dwControlKeyState =
+ ((GetAsyncKeyState(VK_LMENU) & 0x8000) ? LEFT_ALT_PRESSED : 0) | + ((GetAsyncKeyState(VK_RMENU) & 0x8000) ? RIGHT_ALT_PRESSED : 0) | + ((GetAsyncKeyState(VK_LCONTROL) & 0x8000) ? LEFT_CTRL_PRESSED : 0) | + ((GetAsyncKeyState(VK_RCONTROL) & 0x8000) ? RIGHT_CTRL_PRESSED : 0) |
+            ((GetAsyncKeyState(VK_SHIFT) & 0x8000) ? SHIFT_PRESSED : 0) |
+            ((hs->flags & LLKHF_EXTENDED) ? ENHANCED_KEY : 0);
+          if( w32_console_unicode_input )
+            WriteConsoleInputW(keyboard_handle, &rec, 1, &n);
+          else
+            WriteConsoleInputA(keyboard_handle, &rec, 1, &n);
+        }
+      else
+        PostMessage( focus, w, hs->vkCode, 1 | (1<<29));
+      return 1;
+    }
+
+  /* The normal case - pass the message through */
+  return CallNextHookEx( 0, code, w, l );
+}
+
+/* Set up the hook; can be called several times, with matching
+   remove_w32_kbdhook calls. */
+void setup_w32_kbdhook (void)
+{
+  hook_count++;
+  if (hook_count == 1)
+    {
+ hook = SetWindowsHookEx( WH_KEYBOARD_LL, funhook, GetModuleHandle(NULL), 0 );
+    }
+}
+
+/* Remove the hook */
+void remove_w32_kbdhook (void)
+{
+  hook_count--;
+  if (hook_count == 0)
+    {
+      UnhookWindowsHookEx( hook );
+      hook = NULL;
+    }
+}
+
+/* Check the current Win key pressed state - see the discussion at the
+   top of the file for the rationale */
+int check_w32_winkey_state (int vkey)
+{
+  switch (vkey)
+  {
+    case VK_LWIN: return lwindown;
+    case VK_RWIN: return rwindown;
+  }
+  return 0;
+}
+
+/* Mark a specific key combination as hooked, preventing it to be
+   handled by the system */
+void hook_w32_key (int hook, int modifier, int vkey)
+{
+  char* tbl = NULL;
+
+  switch(modifier)
+    {
+    case VK_MENU: tbl = alt_hooked; break;
+    case VK_LWIN: tbl = lwin_hooked; break;
+    case VK_RWIN: tbl = rwin_hooked; break;
+    }
+
+  if (tbl != NULL && vkey >= 0 && vkey <= 255)
+    {
+      /* VK_ANY hooks all keys for this modifier */
+      if (vkey == VK_ANY)
+        memset(tbl, (char)hook, 256);
+      else
+        tbl[vkey] = (char)hook;
+      /* Alt-<modifier>s should go through */
+      alt_hooked[VK_MENU] = 0;
+      alt_hooked[VK_LMENU] = 0;
+      alt_hooked[VK_RMENU] = 0;
+      alt_hooked[VK_CONTROL] = 0;
+      alt_hooked[VK_LCONTROL] = 0;
+      alt_hooked[VK_RCONTROL] = 0;
+      alt_hooked[VK_SHIFT] = 0;
+      alt_hooked[VK_LSHIFT] = 0;
+      alt_hooked[VK_RSHIFT] = 0;
+    }
+}
+
+/* Resets the keyboard hook state. Locking the workstation with Win-L
+   leaves the Win key(s) "down" from the hook's point of view - the
+   keyup event is never seen.  Thus, this function must be called when
+   the system is locked. */
+void reset_w32_kbdhook_state (void)
+{
+  lwindown = 0;
+  rwindown = 0;
+  winsdown = 0;
+  send_win_up = 0;
+  suppress_lone = 0;
+  winseen = 0;
+}
diff --git a/src/w32inevt.c b/src/w32inevt.c
index db2e218..931fb20 100644
--- a/src/w32inevt.c
+++ b/src/w32inevt.c
@@ -147,12 +147,6 @@ key_event (KEY_EVENT_RECORD *event, struct input_event *emacs_ev, int *isdead)
     {
       switch (event->wVirtualKeyCode)
        {
-       case VK_LWIN:
-         mod_key_state &= ~LEFT_WIN_PRESSED;
-         break;
-       case VK_RWIN:
-         mod_key_state &= ~RIGHT_WIN_PRESSED;
-         break;
        case VK_APPS:
          mod_key_state &= ~APPS_PRESSED;
          break;
@@ -185,7 +179,6 @@ key_event (KEY_EVENT_RECORD *event, struct input_event *emacs_ev, int *isdead)
              keybd_event (faked_key, (BYTE) MapVirtualKey (faked_key, 0), 0, 
0);
            }
        }
-      mod_key_state |= LEFT_WIN_PRESSED;
       if (!NILP (Vw32_lwindow_modifier))
        return 0;
       break;
@@ -201,7 +194,6 @@ key_event (KEY_EVENT_RECORD *event, struct input_event *emacs_ev, int *isdead)
              keybd_event (faked_key, (BYTE) MapVirtualKey (faked_key, 0), 0, 
0);
            }
        }
-      mod_key_state |= RIGHT_WIN_PRESSED;
       if (!NILP (Vw32_rwindow_modifier))
        return 0;
       break;
@@ -267,6 +259,10 @@ key_event (KEY_EVENT_RECORD *event, struct input_event *emacs_ev, int *isdead)

   /* Recognize state of Windows and Apps keys.  */
   event->dwControlKeyState |= mod_key_state;
+  if (check_w32_winkey_state(VK_LWIN))
+    event->dwControlKeyState |= LEFT_WIN_PRESSED;
+  if (check_w32_winkey_state(VK_RWIN))
+    event->dwControlKeyState |= RIGHT_WIN_PRESSED;

   /* Distinguish numeric keypad keys from extended keys.  */
   event->wVirtualKeyCode =
diff --git a/src/w32term.h b/src/w32term.h
index 3377b53..8432843 100644
--- a/src/w32term.h
+++ b/src/w32term.h
@@ -738,6 +738,12 @@ extern int handle_file_notifications (struct input_event *);
 extern void w32_initialize_display_info (Lisp_Object);
 extern void initialize_w32_display (struct terminal *, int *, int *);

+extern void setup_w32_kbdhook (void);
+extern void remove_w32_kbdhook (void);
+extern void hook_w32_key (int hook, int modifier, int vkey);
+extern int check_w32_winkey_state (int vkey);
+extern void reset_w32_kbdhook_state (void);
+
 /* Keypad command key support.  W32 doesn't have virtual keys defined
    for the function keys on the keypad (they are mapped to the standard
    function keys), so we define our own.  */
@@ -762,6 +768,10 @@ extern void initialize_w32_display (struct terminal *, int *, int *);
 #define VK_APPS                        0x5D
 #endif

+/* Special virtual key code for indicating "any" key.  This can be used
+   as an argument to the hook_w32_key function. */
+#define VK_ANY                  0xFF
+
 /* Support for treating Windows and Apps keys as modifiers.  These
    constants must not overlap with any of the dwControlKeyState flags in
    KEY_EVENT_RECORD.  */
--
2.6.2




reply via email to

[Prev in Thread] Current Thread [Next in Thread]