From 55789efca3eaba68550a36d93a6540e630836bbb Mon Sep 17 00:00:00 2001 From: Jussi Lahdenniemi Date: Wed, 13 Jan 2016 11:57:35 +0200 Subject: [PATCH] Implementation of a Windows keyboard hook 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 for NT based OSes (not Windows 98/ME). The hooking code works for both windowed and console (emacs -nw) mode. * src/w32term.h: Add new function prototypes * src/w32fns.c: Main location for new functionality * src/w32console.c: Update to support hooking for console Emacs * src/w32inevt.c: Ditto * doc/emacs/msdos.texi: Documentation update --- doc/emacs/msdos.texi | 81 +++++--- src/w32console.c | 3 + src/w32fns.c | 543 +++++++++++++++++++++++++++++++++++++++++++++++---- src/w32inevt.c | 20 +- src/w32term.h | 6 + 5 files changed, 582 insertions(+), 71 deletions(-) diff --git a/doc/emacs/msdos.texi b/doc/emacs/msdos.texi index ea8a24d..854d65f 100644 --- a/doc/emacs/msdos.texi +++ b/doc/emacs/msdos.texi @@ -507,32 +507,64 @@ Windows Keyboard @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 @@ 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 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 3778557..80bcebe 100644 --- a/src/w32fns.c +++ b/src/w32fns.c @@ -20,6 +20,9 @@ along with GNU Emacs. If not, see . */ /* Added by Kevin Gallo */ #include +/* Override API version to get the latest functionality. */ +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0600 #include #include @@ -41,6 +44,7 @@ along with GNU Emacs. If not, see . */ #include "coding.h" #include "w32common.h" +#include "w32inevt.h" #ifdef WINDOWSNT #include @@ -52,6 +56,8 @@ along with GNU Emacs. If not, see . */ #include "w32.h" #endif +#include +#include #include #include #include @@ -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,338 @@ 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-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; +} + +/* 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) +{ + 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 +2618,10 @@ 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 +2764,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 +3816,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 +3835,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 +4692,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 +4726,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 +8005,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 +8055,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 +8084,70 @@ 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 other Windows versions, 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 +8175,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 +9785,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, -r always pops up the Windows Run dialog, -- 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, -r +pops up the Windows Run dialog, - 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 +9804,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, -r always pops up the Windows Run dialog, -- 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, -r +pops up the Windows Run dialog, - 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 +9822,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 +9856,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 +9866,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 54b0b13..80f9067 100644 --- a/src/w32inevt.c +++ b/src/w32inevt.c @@ -41,6 +41,7 @@ along with GNU Emacs. If not, see . */ #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..6139414 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 vkey); +#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. */ -- 2.6.2