summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJussi Lahdenniemi <jussi@aprikoodi.fi>2016-02-26 12:51:24 +0200
committerEli Zaretskii <eliz@gnu.org>2016-02-26 12:51:24 +0200
commit97d7a0b8db4ce32a8e489dec48634b7e85212eaa (patch)
treee1477561851991fe7be1213747d201d744794286
parent22994735af588be9c2d6d438155b73328aaa0cf3 (diff)
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.
-rw-r--r--doc/emacs/msdos.texi81
-rw-r--r--etc/NEWS9
-rw-r--r--src/w32console.c3
-rw-r--r--src/w32fns.c556
-rw-r--r--src/w32inevt.c20
-rw-r--r--src/w32term.h6
6 files changed, 604 insertions, 71 deletions
diff --git a/doc/emacs/msdos.texi b/doc/emacs/msdos.texi
index 6ad12d646a..a87561ccf1 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
-@kbd{@key{Alt}-@key{TAB}}, for its own use. These key combinations are
-intercepted by the system before Emacs can see them. 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. 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
-@code{define-key}. 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}.
+@kbd{@key{Alt}-@key{TAB}} 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
+@code{define-key}. The control and shift modifiers have no effect on the
+argument. The meta modifier is interpreted as the @key{Alt} key if
+@code{w32-alt-is-meta} is @code{t} (the default), and the super and hyper
+modifiers are interpreted according to the bindings of
+@code{w32-lwindow-modifier} 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 M-TAB@r{, (MS-Windows)}
@cindex @kbd{M-@key{TAB}} vs @kbd{@key{Alt}-@key{TAB}} (MS-Windows)
@cindex @kbd{@key{Alt}-@key{TAB}} vs @kbd{M-@key{TAB}} (MS-Windows)
For example, @code{(w32-register-hot-key [M-tab])} lets you use
-@kbd{M-@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.
+@kbd{M-@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
+@code{w32-lwindow-modifier} bound to @code{super} disables all the
+Windows' own Windows key based shortcuts.@footnote{There is one known
+exception: The combination @kbd{@key{Windows}-@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
+@code{w32-[lr]window-modifier} 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
+@code{w32-rwindow-modifier} 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 @kbd{s-@key{a}} as a hotkey gives you @kbd{S-s-@key{a}},
+@kbd{C-s-@key{a}} and @kbd{C-S-s-@key{a}} as well.
+
+ On Windows 98 and ME, the hotkey registration is more restricted.
+The desired hotkey must always be fully specified, and
+@code{w32-phantom-key-code} 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, @kbd{@key{Lwindow}} opens the
-@code{Start} menu, etc.@footnote{
-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, @kbd{@key{Lwindow} r} always pops up the Windows
-@samp{Run} dialog. Customizing the value of
-@code{w32-phantom-key-code} might help in some cases, though.}
+@code{Start} menu, etc.
@vindex w32-recognize-altgr
@kindex AltGr @r{(MS-Windows)}
diff --git a/etc/NEWS b/etc/NEWS
index ec68cce533..29b013a3ed 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 6277f131b5..fdadd48f32 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 a5018ae9d3..10c8af7107 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 e714e27f4b..c7246c7a8e 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 50906241f9..aed89d8c25 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. */