emacs-diffs
[Top][All Lists]
Advanced

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

[Emacs-diffs] master 97d7a0b: Improve the register-hotkey functionality


From: Eli Zaretskii
Subject: [Emacs-diffs] master 97d7a0b: Improve the register-hotkey functionality on MS-Windows
Date: Fri, 26 Feb 2016 10:53:04 +0000

branch: master
commit 97d7a0b8db4ce32a8e489dec48634b7e85212eaa
Author: Jussi Lahdenniemi <address@hidden>
Commit: Eli Zaretskii <address@hidden>

    Improve the register-hotkey functionality on MS-Windows
    
    * src/w32fns.c (_WIN32_WINNT): Define to 0x0600, needed for
    keyboard hook functionality.
    Include w32inevt.h, basetyps.h and unknwn.h.
    (VK_ANY, WM_WTSSESSION_CHANGE, WTS_SESSION_LOCK): New macros.
    (kbdhook): A new struct definition.
    (funhook, setup_w32_kbdhook, remove_w32_kbdhook, hook_w32_key)
    (check_w32_winkey_state, reset_w32_kbdhook_state): New functions.
    (modifier_set): Call check_w32_winkey_state if a Win key was
    pressed and the keyboard hook is active.
    (w32_wnd_proc): Don't handle Win key combinations if the keyboard
    hook is active.  Only register/unregister the hotkeys if the
    keyboard hook is not active.  When WM_CREATE is received, call
    setup_w32_kbdhook.  When WM_DESTROY is received, call
    reset_w32_kbdhook_state.
    (lookup_vk_code): When the keyboard hook is active, map
    alphanumeric characters to themselves.
    (w32_parse_and_hook_hot_key): Renamed from w32_parse_hot_key.  Map
    modified keys to VK_ANY if the keyboard hook is active.  Register
    Alt-x and Win-x combinations.
    (Fw32_shell_execute): Update doc string to reflect new
    functionality.  Bypass the code that posts the
    WM_EMACS_REGISTER_HOT_KEY message if the keyboard hook is active.
    (Fw32_unregister_hot_key): Bypass the code that posts the
    WM_EMACS_UNREGISTER_HOT_KEY message if the keyboard hook is active.
    (syms_of_w32fns) <w32-pass-lwindow-to-system>
    <w32-pass-rwindow-to-system, w32-phantom-key-code>
    <w32-lwindow-modifier, w32-rwindow-modifier>: Update doc strings
    to reflect the new functionality.
    * src/w32console.c (initialize_w32_display): Install the low-level
    keyboard hook.
    * src/w32inevt.c (key_event): Handle Win-x combinations only if
    the keyboard hook is not active.  If the hook is active, use
    check_w32_winkey_state instead.
    * src/w32term.h (setup_w32_kbdhook, remove_w32_kbdhook)
    (check_w32_winkey_state): Add prototypes.
    (w32_kbdhook_active): New macro.
    
    * doc/emacs/msdos.texi (Windows Keyboard): Update to reflect the
    new functionality.
---
 doc/emacs/msdos.texi |   81 +++++---
 etc/NEWS             |    9 +
 src/w32console.c     |    3 +
 src/w32fns.c         |  556 ++++++++++++++++++++++++++++++++++++++++++++++----
 src/w32inevt.c       |   20 ++-
 src/w32term.h        |    6 +
 6 files changed, 604 insertions(+), 71 deletions(-)

diff --git a/doc/emacs/msdos.texi b/doc/emacs/msdos.texi
index 6ad12d6..a87561c 100644
--- a/doc/emacs/msdos.texi
+++ b/doc/emacs/msdos.texi
@@ -507,32 +507,64 @@ the variable @code{w32-alt-is-meta} to a @code{nil} value.
 @findex w32-register-hot-key
 @findex 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 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.
 
 @kindex address@hidden, (MS-Windows)}
 @cindex @address@hidden vs @address@hidden@key{TAB}} (MS-Windows)
 @cindex @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.
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.
+
+  On Windows 98 and ME, the hotkey registration is more restricted.
+The desired hotkey must always be fully specified, and
address@hidden can be customized to achieve desired
+results.
 
   The function @code{w32-unregister-hot-key} reverses the effect of
 @code{w32-register-hot-key} for its argument key sequence.
@@ -607,12 +639,7 @@ keys are passed to Windows or swallowed by Emacs.  If the 
value is
 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/etc/NEWS b/etc/NEWS
index ec68cce..29b013a 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -129,6 +129,15 @@ changed size when `window-size-change-functions' are run.
 
 * Changes in Emacs 25.2 on Non-Free Operating Systems
 
+** Intercepting hotkeys on Windows 7 and later now works better.
+The new keyboard hooking code properly grabs system hotkeys such as
+Win-* and Alt-TAB, in a way that Emacs can get at them before the
+system.  This makes the `w32-register-hot-key' functionality work
+again on all versions of MS-Windows starting with Windows 7.  On
+Windows NT and later you can now register any hotkey combination.  (On
+Windows 9X, the previous limitations, spelled out in the Emacs manual,
+still apply.)
+
 
 * Installation Changes in Emacs 25.1
 
diff --git a/src/w32console.c b/src/w32console.c
index 6277f13..fdadd48 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 a5018ae..10c8af7 100644
--- a/src/w32fns.c
+++ b/src/w32fns.c
@@ -20,6 +20,9 @@ along with GNU Emacs.  If not, see 
<http://www.gnu.org/licenses/>.  */
 /* Added by Kevin Gallo */
 
 #include <config.h>
+/* Override API version to get the latest functionality.  */
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x0600
 
 #include <signal.h>
 #include <stdio.h>
@@ -41,6 +44,7 @@ along with GNU Emacs.  If not, see 
<http://www.gnu.org/licenses/>.  */
 #include "coding.h"
 
 #include "w32common.h"
+#include "w32inevt.h"
 
 #ifdef WINDOWSNT
 #include <mbstring.h>
@@ -52,6 +56,8 @@ along with GNU Emacs.  If not, see 
<http://www.gnu.org/licenses/>.  */
 #include "w32.h"
 #endif
 
+#include <basetyps.h>
+#include <unknwn.h>
 #include <commctrl.h>
 #include <commdlg.h>
 #include <shellapi.h>
@@ -251,6 +257,38 @@ HINSTANCE hinst = NULL;
 static unsigned int sound_type = 0xFFFFFFFF;
 #define MB_EMACS_SILENT (0xFFFFFFFF - 1)
 
+/* Special virtual key code for indicating "any" key.  */
+#define VK_ANY 0xFF
+
+#ifndef WM_WTSSESSION_CHANGE
+/* 32-bit MinGW does not define these constants.  */
+# define WM_WTSSESSION_CHANGE  0x02B1
+# define WTS_SESSION_LOCK      0x7
+#endif
+
+/* Keyboard hook state data.  */
+static struct
+{
+  int hook_count; /* counter, if several windows are created */
+  HHOOK hook;     /* hook handle */
+  HWND console;   /* console window handle */
+
+  int lwindown;      /* Left Windows key currently pressed (and hooked) */
+  int rwindown;      /* Right Windows key currently pressed (and hooked) */
+  int winsdown;      /* Number of handled keys currently pressed */
+  int send_win_up;   /* Pass through the keyup for this Windows key press? */
+  int suppress_lone; /* Suppress simulated Windows keydown-keyup for this 
press? */
+  int winseen;       /* Windows keys seen during this press? */
+
+  char alt_hooked[256];  /* hook Alt+[this key]? */
+  char lwin_hooked[256]; /* hook left Win+[this key]? */
+  char rwin_hooked[256]; /* hook right Win+[this key]? */
+} kbdhook;
+typedef HWND (WINAPI *GetConsoleWindow_Proc) (void);
+
+/* stdin, from w32console.c */
+extern HANDLE keyboard_handle;
+
 /* Let the user specify a display with a frame.
    nil stands for the selected frame--or, if that is not a w32 frame,
    the first display on the list.  */
@@ -2074,6 +2112,348 @@ my_post_msg (W32Msg * wmsg, HWND hwnd, UINT msg, WPARAM 
wParam, LPARAM lParam)
   post_msg (wmsg);
 }
 
+/* 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);
+
+  /* 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.
+
+     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 && kbdhook.console != NULL)
+    {
+      if (GetForegroundWindow () == kbdhook.console)
+       {
+         focus = kbdhook.console;
+         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 && !kbdhook.lwindown)
+           {
+             kbdhook.lwindown = 1;
+             kbdhook.winseen = 1;
+             kbdhook.winsdown++;
+           }
+         else if (hs->vkCode == VK_RWIN && !kbdhook.rwindown)
+           {
+             kbdhook.rwindown = 1;
+             kbdhook.winseen = 1;
+             kbdhook.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 (kbdhook.winsdown > 0 && (w == WM_KEYUP || w == WM_SYSKEYUP))
+       {
+         /* A key that has been captured earlier is being released now.  */
+         if (hs->vkCode == VK_LWIN && kbdhook.lwindown)
+           {
+             kbdhook.lwindown = 0;
+             kbdhook.winsdown--;
+           }
+         else if (hs->vkCode == VK_RWIN && kbdhook.rwindown)
+           {
+             kbdhook.rwindown = 0;
+             kbdhook.winsdown--;
+           }
+         if (kbdhook.winsdown == 0 && kbdhook.winseen)
+           {
+             if (!kbdhook.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 (kbdhook.winsdown == 0)
+           {
+             /* No Windows keys pressed anymore - clear the state flags.  */
+             kbdhook.suppress_lone = 0;
+             kbdhook.winseen = 0;
+           }
+         if (!kbdhook.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;
+           }
+         kbdhook.send_win_up = 0;
+       }
+    }
+  else if (kbdhook.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 (kbdhook.lwindown && kbdhook.lwin_hooked[hs->vkCode] ||
+         kbdhook.rwindown && kbdhook.rwin_hooked[hs->vkCode])
+       {
+         /* Hooked Win-x combination, do not pass the keypress to Windows.  */
+         kbdhook.suppress_lone = 1;
+       }
+      else if (!kbdhook.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 = kbdhook.lwindown ? VK_LWIN : VK_RWIN;
+         inputs[0].ki.wScan = kbdhook.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.  */
+         kbdhook.suppress_lone = 1;
+         kbdhook.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)
+      && kbdhook.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.  */
+      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)
+{
+  kbdhook.hook_count++;
+
+  /* Hooking is only available on NT architecture systems, as
+     indicated by the w32_kbdhook_active variable.  */
+  if (kbdhook.hook_count == 1 && w32_kbdhook_active)
+    {
+      /* Get the handle of the Emacs console window.  As the
+        GetConsoleWindow function is only available on Win2000+, a
+        hackish workaround described in Microsoft KB article 124103
+        (https://support.microsoft.com/en-us/kb/124103) is used for
+        NT 4 systems.  */
+      GetConsoleWindow_Proc get_console = (GetConsoleWindow_Proc)
+       GetProcAddress (GetModuleHandle ("kernel32.dll"), "GetConsoleWindow");
+
+      if (get_console != NULL)
+       kbdhook.console = get_console ();
+      else
+        {
+         GUID guid;
+         wchar_t *oldTitle = malloc (1024 * sizeof(wchar_t));
+         wchar_t newTitle[64];
+         int i;
+
+         CoCreateGuid (&guid);
+         StringFromGUID2 (&guid, newTitle, 64);
+         if (newTitle != NULL)
+           {
+             GetConsoleTitleW (oldTitle, 1024);
+             SetConsoleTitleW (newTitle);
+             for (i = 0; i < 25; i++)
+               {
+                 Sleep (40);
+                 kbdhook.console = FindWindowW (NULL, newTitle);
+                 if (kbdhook.console != NULL)
+                   break;
+               }
+             SetConsoleTitleW (oldTitle);
+           }
+         free (oldTitle);
+       }
+
+      /* Set the hook.  */
+      kbdhook.hook = SetWindowsHookEx (WH_KEYBOARD_LL, funhook,
+                                      GetModuleHandle (NULL), 0);
+    }
+}
+
+/* Remove the hook.  */
+void
+remove_w32_kbdhook (void)
+{
+  kbdhook.hook_count--;
+  if (kbdhook.hook_count == 0 && w32_kbdhook_active)
+    {
+      UnhookWindowsHookEx (kbdhook.hook);
+      kbdhook.hook = NULL;
+    }
+}
+
+/* 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 = kbdhook.alt_hooked;
+      break;
+    case VK_LWIN:
+      tbl = kbdhook.lwin_hooked;
+      break;
+    case VK_RWIN:
+      tbl = kbdhook.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 */
+       kbdhook.alt_hooked[VK_MENU] = 0;
+       kbdhook.alt_hooked[VK_LMENU] = 0;
+       kbdhook.alt_hooked[VK_RMENU] = 0;
+       kbdhook.alt_hooked[VK_CONTROL] = 0;
+       kbdhook.alt_hooked[VK_LCONTROL] = 0;
+       kbdhook.alt_hooked[VK_RCONTROL] = 0;
+       kbdhook.alt_hooked[VK_SHIFT] = 0;
+       kbdhook.alt_hooked[VK_LSHIFT] = 0;
+       kbdhook.alt_hooked[VK_RSHIFT] = 0;
+    }
+}
+
+/* Check the current Win key pressed state.  */
+int
+check_w32_winkey_state (int vkey)
+{
+  /* The hook 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, this means 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.  */
+  switch (vkey)
+    {
+    case VK_LWIN:
+      return kbdhook.lwindown;
+    case VK_RWIN:
+      return kbdhook.rwindown;
+    }
+  return 0;
+}
+
+/* Reset 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)
+{
+  kbdhook.lwindown = 0;
+  kbdhook.rwindown = 0;
+  kbdhook.winsdown = 0;
+  kbdhook.send_win_up = 0;
+  kbdhook.suppress_lone = 0;
+  kbdhook.winseen = 0;
+}
+
 /* GetKeyState and MapVirtualKey on Windows 95 do not actually distinguish
    between left and right keys as advertised.  We test for this
    support dynamically, and set a flag when the support is absent.  If
@@ -2248,6 +2628,8 @@ modifier_set (int vkey)
       else
        return (GetKeyState (vkey) & 0x1);
     }
+  if (w32_kbdhook_active && (vkey == VK_LWIN || vkey == VK_RWIN))
+    return check_w32_winkey_state (vkey);
 
   if (!modifiers_recorded)
     return (GetKeyState (vkey) & 0x8000);
@@ -2390,7 +2772,9 @@ map_keypad_keys (unsigned int virt_key, unsigned int 
extended)
 /* 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.  */
+   key code and modifier combination to capture.
+   Note: This code is not used if keyboard hooks are active
+   (Windows 2000 and later).  */
 static Lisp_Object w32_grabbed_keys;
 
 #define HOTKEY(vk, mods)      make_number (((vk) & 255) | ((mods) << 8))
@@ -3440,7 +3824,7 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM 
lParam)
       switch (wParam)
        {
        case VK_LWIN:
-         if (NILP (Vw32_pass_lwindow_to_system))
+         if (!w32_kbdhook_active && 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
@@ -3459,7 +3843,7 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM 
lParam)
            return 0;
          break;
        case VK_RWIN:
-         if (NILP (Vw32_pass_rwindow_to_system))
+         if (!w32_kbdhook_active && NILP (Vw32_pass_rwindow_to_system))
            {
              if (GetAsyncKeyState (wParam) & 1)
                {
@@ -4316,10 +4700,12 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, 
LPARAM lParam)
     case WM_SETFOCUS:
       dpyinfo->faked_key = 0;
       reset_modifiers ();
-      register_hot_keys (hwnd);
+      if (!w32_kbdhook_active)
+       register_hot_keys (hwnd);
       goto command;
     case WM_KILLFOCUS:
-      unregister_hot_keys (hwnd);
+      if (!w32_kbdhook_active)
+       unregister_hot_keys (hwnd);
       button_state = 0;
       ReleaseCapture ();
       /* Relinquish the system caret.  */
@@ -4348,10 +4734,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 +8013,34 @@ lookup_vk_code (char *key)
        && strcmp (lispy_function_keys[i], key) == 0)
       return i;
 
+  if (w32_kbdhook_active)
+    {
+      /* 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_hook_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 +8063,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 (w32_kbdhook_active && vkname[0] == 0)
+        vk_code = VK_ANY;
+      else
+        vk_code = lookup_vk_code (vkname);
     }
   else if (INTEGERP (c))
     {
@@ -7676,34 +8092,75 @@ w32_parse_hot_key (Lisp_Object key)
 #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;
+  if (w32_kbdhook_active)
+    {
+      /* 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 res;
+    }
+  else
+    {
+      /* 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;
 
-  return HOTKEY (vk_code, w32_modifiers);
+      return HOTKEY (vk_code, w32_modifiers);
+    }
 }
 
 DEFUN ("w32-register-hot-key", Fw32_register_hot_key,
        Sw32_register_hot_key, 1, 1, 0,
        doc: /* Register KEY as a hot-key combination.
-Certain key combinations like Alt-Tab are reserved for system use on
-Windows, and therefore are normally intercepted by the system.  However,
-most of these key combinations can be received by registering them as
-hot-keys, overriding their special meaning.
-
-KEY must be a one element key definition in vector form that would be
-acceptable to `define-key' (e.g. [A-tab] for Alt-Tab).  The meta
-modifier is interpreted as Alt if `w32-alt-is-meta' is t, and hyper
-is always interpreted as the Windows modifier keys.
-
-The return value is the hotkey-id if registered, otherwise nil.  */)
+Certain key combinations like Alt-Tab and Win-R are reserved for
+system use on Windows, and therefore are normally intercepted by the
+system.  These key combinations can be received by registering them
+as hot-keys, except for Win-L which always locks the computer.
+
+On Windows 98 and ME, KEY must be a one element key definition in
+vector form that would be acceptable to `define-key' (e.g. [A-tab] for
+Alt-Tab).  The meta modifier is interpreted as Alt if
+`w32-alt-is-meta' is t, and hyper is always interpreted as the Windows
+modifier keys.  The return value is the hotkey-id if registered, otherwise nil.
+
+On Windows versions since NT, KEY can also be specified as [M-], [s-] or
+[h-] to indicate that all combinations of that key should be processed
+by Emacs instead of the operating system.  The super and hyper
+modifiers are interpreted according to the current values of
+`w32-lwindow-modifier' and `w32-rwindow-modifier'.  For instance,
+setting `w32-lwindow-modifier' to `super' and then calling
+`(register-hot-key [s-])' grabs all combinations of the left Windows
+key to Emacs, but leaves the right Windows key free for the operating
+system keyboard shortcuts.  The return value is t if the call affected
+any key combinations, otherwise nil.  */)
   (Lisp_Object key)
 {
-  key = w32_parse_hot_key (key);
+  key = w32_parse_and_hook_hot_key (key, 1);
 
-  if (!NILP (key) && NILP (Fmemq (key, w32_grabbed_keys)))
+  if (!w32_kbdhook_active
+      && !NILP (key) && NILP (Fmemq (key, w32_grabbed_keys)))
     {
       /* Reuse an empty slot if possible.  */
       Lisp_Object item = Fmemq (Qnil, w32_grabbed_keys);
@@ -7731,7 +8188,10 @@ DEFUN ("w32-unregister-hot-key", Fw32_unregister_hot_key,
   Lisp_Object item;
 
   if (!INTEGERP (key))
-    key = w32_parse_hot_key (key);
+    key = w32_parse_and_hook_hot_key (key, 0);
+
+  if (w32_kbdhook_active)
+    return key;
 
   item = Fmemq (key, w32_grabbed_keys);
 
@@ -9338,11 +9798,15 @@ When non-nil, the Start menu is opened by tapping the 
key.
 If you set this to nil, the left \"Windows\" key is processed by Emacs
 according to the value of `w32-lwindow-modifier', which see.
 
-Note that some combinations of the left \"Windows\" key with other keys are
-caught by Windows at low level, and so binding them in Emacs will have no
-effect.  For example, <lwindow>-r always pops up the Windows Run dialog,
-<lwindow>-<Pause> pops up the "System Properties" dialog, etc.  However, see
-the doc string of `w32-phantom-key-code'.  */);
+Note that some combinations of the left \"Windows\" key with other
+keys are caught by Windows at low level.  For example, <lwindow>-r
+pops up the Windows Run dialog, <lwindow>-<Pause> pops up the "System
+Properties" dialog, etc.  On Windows 10, no \"Windows\" key
+combinations are normally handed to applications.  To enable Emacs to
+process \"Windows\" key combinations, use the function
+`w32-register-hot-key`.
+
+For Windows 98/ME, see the doc string of `w32-phantom-key-code'.  */);
   Vw32_pass_lwindow_to_system = Qt;
 
   DEFVAR_LISP ("w32-pass-rwindow-to-system",
@@ -9353,11 +9817,15 @@ When non-nil, the Start menu is opened by tapping the 
key.
 If you set this to nil, the right \"Windows\" key is processed by Emacs
 according to the value of `w32-rwindow-modifier', which see.
 
-Note that some combinations of the right \"Windows\" key with other keys are
-caught by Windows at low level, and so binding them in Emacs will have no
-effect.  For example, <rwindow>-r always pops up the Windows Run dialog,
-<rwindow>-<Pause> pops up the "System Properties" dialog, etc.  However, see
-the doc string of `w32-phantom-key-code'.  */);
+Note that some combinations of the right \"Windows\" key with other
+keys are caught by Windows at low level.  For example, <rwindow>-r
+pops up the Windows Run dialog, <rwindow>-<Pause> pops up the "System
+Properties" dialog, etc.  On Windows 10, no \"Windows\" key
+combinations are normally handed to applications.  To enable Emacs to
+process \"Windows\" key combinations, use the function
+`w32-register-hot-key`.
+
+For Windows 98/ME, see the doc string of `w32-phantom-key-code'.  */);
   Vw32_pass_rwindow_to_system = Qt;
 
   DEFVAR_LISP ("w32-phantom-key-code",
@@ -9367,7 +9835,11 @@ Value is a number between 0 and 255.
 
 Phantom key presses are generated in order to stop the system from
 acting on \"Windows\" key events when `w32-pass-lwindow-to-system' or
-`w32-pass-rwindow-to-system' is nil.  */);
+`w32-pass-rwindow-to-system' is nil.
+
+This variable is only used on Windows 98 and ME.  For other Windows
+versions, see the documentation of the `w32-register-hot-key`
+function.  */);
   /* Although 255 is technically not a valid key code, it works and
      means that this hack won't interfere with any real key code.  */
   XSETINT (Vw32_phantom_key_code, 255);
@@ -9397,7 +9869,9 @@ Any other value will cause the Scroll Lock key to be 
ignored.  */);
               doc: /* Modifier to use for the left \"Windows\" key.
 The value can be hyper, super, meta, alt, control or shift for the
 respective modifier, or nil to appear as the `lwindow' key.
-Any other value will cause the key to be ignored.  */);
+Any other value will cause the key to be ignored.
+
+Also see the documentation of the `w32-register-hot-key` function.  */);
   Vw32_lwindow_modifier = Qnil;
 
   DEFVAR_LISP ("w32-rwindow-modifier",
@@ -9405,7 +9879,9 @@ Any other value will cause the key to be ignored.  */);
               doc: /* Modifier to use for the right \"Windows\" key.
 The value can be hyper, super, meta, alt, control or shift for the
 respective modifier, or nil to appear as the `rwindow' key.
-Any other value will cause the key to be ignored.  */);
+Any other value will cause the key to be ignored.
+
+Also see the documentation of the `w32-register-hot-key` function.  */);
   Vw32_rwindow_modifier = Qnil;
 
   DEFVAR_LISP ("w32-apps-modifier",
diff --git a/src/w32inevt.c b/src/w32inevt.c
index e714e27..c7246c7 100644
--- a/src/w32inevt.c
+++ b/src/w32inevt.c
@@ -41,6 +41,7 @@ along with GNU Emacs.  If not, see 
<http://www.gnu.org/licenses/>.  */
 #include "termchar.h"  /* for Mouse_HLInfo, tty_display_info */
 #include "w32term.h"
 #include "w32inevt.h"
+#include "w32common.h"
 
 /* stdin, from w32console.c */
 extern HANDLE keyboard_handle;
@@ -148,10 +149,12 @@ 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;
+          if (!w32_kbdhook_active)
+            mod_key_state &= ~LEFT_WIN_PRESSED;
          break;
        case VK_RWIN:
-         mod_key_state &= ~RIGHT_WIN_PRESSED;
+          if (!w32_kbdhook_active)
+            mod_key_state &= ~RIGHT_WIN_PRESSED;
          break;
        case VK_APPS:
          mod_key_state &= ~APPS_PRESSED;
@@ -185,7 +188,8 @@ 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 (!w32_kbdhook_active)
+        mod_key_state |= LEFT_WIN_PRESSED;
       if (!NILP (Vw32_lwindow_modifier))
        return 0;
       break;
@@ -201,7 +205,8 @@ 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 (!w32_kbdhook_active)
+        mod_key_state |= RIGHT_WIN_PRESSED;
       if (!NILP (Vw32_rwindow_modifier))
        return 0;
       break;
@@ -267,6 +272,13 @@ 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 (w32_kbdhook_active)
+    {
+      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 5090624..aed89d8 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 *);
 
+/* Keyboard hooks.  */
+extern void setup_w32_kbdhook (void);
+extern void remove_w32_kbdhook (void);
+extern int check_w32_winkey_state (int);
+#define w32_kbdhook_active (os_subtype != OS_9X)
+
 /* 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.  */



reply via email to

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