From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: Jussi Lahdenniemi Newsgroups: gmane.emacs.devel Subject: Re: [PATCH] Override Windows default Win-* key combinations when using Emacs Date: Sat, 9 Jan 2016 21:58:52 +0200 Organization: Aprikoodi Oy Message-ID: <5691667C.5000009@aprikoodi.fi> References: <568BBC58.50702@aprikoodi.fi> <83y4c43qkh.fsf@gnu.org> NNTP-Posting-Host: plane.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset=windows-1252; format=flowed Content-Transfer-Encoding: 7bit X-Trace: ger.gmane.org 1452369576 27948 80.91.229.3 (9 Jan 2016 19:59:36 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Sat, 9 Jan 2016 19:59:36 +0000 (UTC) Cc: emacs-devel@gnu.org To: Eli Zaretskii Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Sat Jan 09 20:59:24 2016 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by plane.gmane.org with esmtp (Exim 4.69) (envelope-from ) id 1aHzfX-0004qR-Ku for ged-emacs-devel@m.gmane.org; Sat, 09 Jan 2016 20:59:24 +0100 Original-Received: from localhost ([::1]:42632 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1aHzfW-0004nI-Tp for ged-emacs-devel@m.gmane.org; Sat, 09 Jan 2016 14:59:22 -0500 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:33887) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1aHzfQ-0004mH-P3 for emacs-devel@gnu.org; Sat, 09 Jan 2016 14:59:20 -0500 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1aHzfM-0003oy-Qj for emacs-devel@gnu.org; Sat, 09 Jan 2016 14:59:16 -0500 Original-Received: from nbjjceehcbh.turbo-smtp.net ([199.244.72.17]:52621) by eggs.gnu.org with smtp (Exim 4.71) (envelope-from ) id 1aHzfL-0003oR-6f for emacs-devel@gnu.org; Sat, 09 Jan 2016 14:59:12 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=aprikoodi.fi; s=turbo-smtp; x=1452974351; h=DomainKey-Signature: Received:Received:Subject:To:References:Cc:From:Organization: Message-ID:Date:User-Agent:MIME-Version:In-Reply-To:Content-Type: Content-Transfer-Encoding; bh=OCN5mg/lUDL4UYLmAxZjM7VpfFH+WYH7rm kYHLpESEk=; b=Rr4xs4q+aacI9V3rujESbaD1WpNJhz02lhWRjtUWqo/MrTaC/m W6xeKjEIDXST8XOuLk6+Y9JraNTTpncGM3h6P3N/JUV9rQTz9IFzN+3JIWH7iFZt ESUsqfy5TS9o+78ZSWz4IA5ryf9T0lPkBq806xQFElO+G2/qkDKdYtRG0= DomainKey-Signature: a=rsa-sha1; q=dns; c=nofws; s=turbo-smtp; d=aprikoodi.fi; h=Received:Received:X-TurboSMTP-Tracking:Subject:To:References:Cc:From:Organization:Message-ID:Date:User-Agent:MIME-Version:In-Reply-To:Content-Type:Content-Transfer-Encoding; b=WMZGxAi4W4znNrGAGGDzEuh4jI8RlircMhbnkCmT3tUHw+CtxmCceZb6w/+AD/ 5hj9UFPsJOODkLOgU0bACS1mLEJB47x2Ugxjf22Ro9QR+2jsBvDf3spIKOPNmzWm AuMFHSvIMZ5P3Cp5qJVqq38GY7e/z32+huDKFypwpOR0c=; Original-Received: (qmail 21758 invoked from network); 9 Jan 2016 19:59:07 -0000 Original-Received: from unknown (HELO ?192.168.100.12?) (authenticated@109.240.64.170) by turbo-smtp.com with SMTP; 9 Jan 2016 19:59:06 -0000 X-TurboSMTP-Tracking: 2725031885 User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:38.0) Gecko/20100101 Thunderbird/38.5.0 In-Reply-To: <83y4c43qkh.fsf@gnu.org> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 199.244.72.17 X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Original-Sender: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Xref: news.gmane.org gmane.emacs.devel:197919 Archived-At: On 5.1.2016 19.05, Eli Zaretskii wrote: > Thank you for your contribution. It is large enough to require legal > paperwork; I can send you the forms off-list if you agree. I finished integrating the code more tightly into Emacs sources, removing the need for the additional process and adding functionality for grabbing Alt-based keyboard input (M-tab, M-ESC) in addition to the Windows keys. I also updated the documentation. The patch is attached. I decided to place the new functionality in a new file (w32hook.c) - it could have been added to some existing file as well. The assignment paperwork is in the mail. ----------------------- 8< 8< ----------------------- The Windows keyboard hooking code properly grabs system hotkeys such as Win-* and Alt-Tab for Emacs use. The new code replaces the w32-register-hot-key functionality. The hooking code also works for both windowed and console (emacs -nw) mode. * configure.ac: Added the new file src/w32hook.c * src/w32term.h: Added new function prototypes * src/w32fns.c: Updated the w32-register-hot-key related code to use the new hooking code instead * src/w32console.c: Updated to support hooking for console Emacs * src/w32inevt.c: Ditto * src/w32hook.c: New file containing the hooking code * doc/emacs/msdos.texi: Updated documentation for the new functionality Fixes --- configure.ac | 1 + doc/emacs/msdos.texi | 100 +++++++++------ src/w32console.c | 3 + src/w32fns.c | 288 ++++++++++-------------------------------- src/w32hook.c | 343 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/w32inevt.c | 12 +- src/w32term.h | 10 ++ 7 files changed, 485 insertions(+), 272 deletions(-) create mode 100644 src/w32hook.c diff --git a/configure.ac b/configure.ac index 0aa863a..4ae0f26 100644 --- a/configure.ac +++ b/configure.ac @@ -1959,6 +1959,7 @@ if test "${HAVE_W32}" = "yes"; then [AC_MSG_ERROR([No resource compiler found.])]) W32_OBJ="w32fns.o w32menu.o w32reg.o w32font.o w32term.o" W32_OBJ="$W32_OBJ w32xfns.o w32select.o w32uniscribe.o" + W32_OBJ="$W32_OBJ w32hook.o" EMACSRES="emacs.res" case "$canonical" in x86_64-*-*) EMACS_MANIFEST="emacs-x64.manifest" ;; diff --git a/doc/emacs/msdos.texi b/doc/emacs/msdos.texi index f1cdb26..34ad639 100644 --- a/doc/emacs/msdos.texi +++ b/doc/emacs/msdos.texi @@ -504,39 +504,6 @@ Windows Keyboard key. If you wish it to produce the @code{Alt} modifier instead, set the variable @code{w32-alt-is-meta} to a @code{nil} value. -@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}. - -@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. - - The function @code{w32-unregister-hot-key} reverses the effect of -@code{w32-register-hot-key} for its argument key sequence. - @vindex w32-capslock-is-shiftlock By default, the @key{CapsLock} key only affects normal character keys (it converts lower-case characters to their upper-case @@ -582,6 +549,66 @@ Windows Keyboard right Windows key produces the symbol @code{rwindow} and @key{ScrLock} produces the symbol @code{scroll}. +@findex w32-register-hot-key +@findex w32-unregister-hot-key + MS-Windows reserves certain key combinations, such as +@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. @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-@key{A}}, +@kbd{C-s-@key{a}} and @kbd{C-s-@key{A}} as well. + + The function @code{w32-unregister-hot-key} reverses the effect of +@code{w32-register-hot-key} for its argument key sequence. + @vindex w32-pass-alt-to-system @cindex Windows system menu @cindex @code{Alt} key invokes menu (Windows) @@ -607,12 +634,7 @@ Windows Keyboard otherwise it is passed to Windows. The default is @code{t} for both of these variables. Passing each of these keys to Windows produces its normal effect: for example, @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/src/w32console.c b/src/w32console.c index 7fffabf..57f74d1 100644 --- a/src/w32console.c +++ b/src/w32console.c @@ -759,6 +759,9 @@ initialize_w32_display (struct terminal *term, int *width, int *height) /* Setup w32_display_info structure for this frame. */ w32_initialize_display_info (build_string ("Console")); + + /* Set up the keyboard hook */ + setup_w32_kbdhook (); } diff --git a/src/w32fns.c b/src/w32fns.c index c1d9bff..99c9ed1 100644 --- a/src/w32fns.c +++ b/src/w32fns.c @@ -2248,6 +2248,8 @@ modifier_set (int vkey) else return (GetKeyState (vkey) & 0x1); } + if (vkey == VK_LWIN || vkey == VK_RWIN) + return check_w32_winkey_state (vkey); if (!modifiers_recorded) return (GetKeyState (vkey) & 0x8000); @@ -2387,60 +2389,6 @@ map_keypad_keys (unsigned int virt_key, unsigned int extended) return virt_key; } -/* List of special key combinations which w32 would normally capture, - but Emacs should grab instead. Not directly visible to lisp, to - simplify synchronization. Each item is an integer encoding a virtual - key code and modifier combination to capture. */ -static Lisp_Object w32_grabbed_keys; - -#define HOTKEY(vk, mods) make_number (((vk) & 255) | ((mods) << 8)) -#define HOTKEY_ID(k) (XFASTINT (k) & 0xbfff) -#define HOTKEY_VK_CODE(k) (XFASTINT (k) & 255) -#define HOTKEY_MODIFIERS(k) (XFASTINT (k) >> 8) - -#define RAW_HOTKEY_ID(k) ((k) & 0xbfff) -#define RAW_HOTKEY_VK_CODE(k) ((k) & 255) -#define RAW_HOTKEY_MODIFIERS(k) ((k) >> 8) - -/* Register hot-keys for reserved key combinations when Emacs has - keyboard focus, since this is the only way Emacs can receive key - combinations like Alt-Tab which are used by the system. */ - -static void -register_hot_keys (HWND hwnd) -{ - Lisp_Object keylist; - - /* Use CONSP, since we are called asynchronously. */ - for (keylist = w32_grabbed_keys; CONSP (keylist); keylist = XCDR (keylist)) - { - Lisp_Object key = XCAR (keylist); - - /* Deleted entries get set to nil. */ - if (!INTEGERP (key)) - continue; - - RegisterHotKey (hwnd, HOTKEY_ID (key), - HOTKEY_MODIFIERS (key), HOTKEY_VK_CODE (key)); - } -} - -static void -unregister_hot_keys (HWND hwnd) -{ - Lisp_Object keylist; - - for (keylist = w32_grabbed_keys; CONSP (keylist); keylist = XCDR (keylist)) - { - Lisp_Object key = XCAR (keylist); - - if (!INTEGERP (key)) - continue; - - UnregisterHotKey (hwnd, HOTKEY_ID (key)); - } -} - #if EMACSDEBUG const char* w32_name_of_message (UINT msg) @@ -2570,27 +2518,6 @@ w32_msg_pump (deferred_msg * msg_buf) result, 0)) emacs_abort (); break; - case WM_EMACS_REGISTER_HOT_KEY: - focus_window = GetFocus (); - if (focus_window != NULL) - RegisterHotKey (focus_window, - RAW_HOTKEY_ID (msg.wParam), - RAW_HOTKEY_MODIFIERS (msg.wParam), - RAW_HOTKEY_VK_CODE (msg.wParam)); - /* Reply is not expected. */ - break; - case WM_EMACS_UNREGISTER_HOT_KEY: - focus_window = GetFocus (); - if (focus_window != NULL) - UnregisterHotKey (focus_window, RAW_HOTKEY_ID (msg.wParam)); - /* Mark item as erased. NB: this code must be - thread-safe. The next line is okay because the cons - cell is never made into garbage and is not relocated by - GC. */ - XSETCAR (make_lisp_ptr ((void *)msg.lParam, Lisp_Cons), Qnil); - if (!PostThreadMessage (dwMainThreadId, WM_EMACS_DONE, 0, 0)) - emacs_abort (); - break; case WM_EMACS_TOGGLE_LOCK_KEY: { int vk_code = (int) msg.wParam; @@ -3401,11 +3328,6 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) } goto dflt; - case WM_HOTKEY: - /* Synchronize hot keys with normal input. */ - PostMessage (hwnd, WM_KEYDOWN, HIWORD (lParam), 0); - return (0); - case WM_KEYUP: case WM_SYSKEYUP: record_keyup (wParam, lParam); @@ -3439,41 +3361,16 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) switch (wParam) { - case VK_LWIN: - if (NILP (Vw32_pass_lwindow_to_system)) - { - /* Prevent system from acting on keyup (which opens the - Start menu if no other key was pressed) by simulating a - press of Space which we will ignore. */ - if (GetAsyncKeyState (wParam) & 1) - { - if (NUMBERP (Vw32_phantom_key_code)) - key = XUINT (Vw32_phantom_key_code) & 255; - else - key = VK_SPACE; - dpyinfo->faked_key = key; - keybd_event (key, (BYTE) MapVirtualKey (key, 0), 0, 0); - } - } - if (!NILP (Vw32_lwindow_modifier)) - return 0; - break; - case VK_RWIN: - if (NILP (Vw32_pass_rwindow_to_system)) - { - if (GetAsyncKeyState (wParam) & 1) - { - if (NUMBERP (Vw32_phantom_key_code)) - key = XUINT (Vw32_phantom_key_code) & 255; - else - key = VK_SPACE; - dpyinfo->faked_key = key; - keybd_event (key, (BYTE) MapVirtualKey (key, 0), 0, 0); - } - } - if (!NILP (Vw32_rwindow_modifier)) - return 0; - break; + /* The Win keys are handled in w32hook.c and must be silently + ignored here when used as a modifier */ + case VK_LWIN: + if (!NILP (Vw32_lwindow_modifier)) + return 0; + break; + case VK_RWIN: + if (!NILP (Vw32_rwindow_modifier)) + return 0; + break; case VK_APPS: if (!NILP (Vw32_apps_modifier)) return 0; @@ -4316,10 +4213,8 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) case WM_SETFOCUS: dpyinfo->faked_key = 0; reset_modifiers (); - register_hot_keys (hwnd); goto command; case WM_KILLFOCUS: - unregister_hot_keys (hwnd); button_state = 0; ReleaseCapture (); /* Relinquish the system caret. */ @@ -4348,10 +4243,20 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) my_post_msg (&wmsg, hwnd, msg, wParam, lParam); goto dflt; + case WM_CREATE: + setup_w32_kbdhook (); + goto dflt; + case WM_DESTROY: + remove_w32_kbdhook (); CoUninitialize (); return 0; + case WM_WTSSESSION_CHANGE: + if (wParam == WTS_SESSION_LOCK) + reset_w32_kbdhook_state (); + goto dflt; + case WM_CLOSE: wmsg.dwModifiers = w32_get_modifiers (); my_post_msg (&wmsg, hwnd, msg, wParam, lParam); @@ -7617,19 +7522,31 @@ lookup_vk_code (char *key) && strcmp (lispy_function_keys[i], key) == 0) return i; + /* alphanumerics map to themselves */ + if (key[1] == 0) + { + if (key[0] >= 'A' && key[0] <= 'Z' || + key[0] >= '0' && key[0] <= '9') + return key[0]; + if (key[0] >= 'a' && key[0] <= 'z') + return toupper(key[0]); + } + return -1; } /* Convert a one-element vector style key sequence to a hot key definition. */ static Lisp_Object -w32_parse_hot_key (Lisp_Object key) +w32_parse_and_register_hot_key (Lisp_Object key, int hook) { /* Copied from Fdefine_key and store_in_keymap. */ register Lisp_Object c; int vk_code; int lisp_modifiers; int w32_modifiers; + Lisp_Object res = Qnil; + char* vkname; CHECK_VECTOR (key); @@ -7652,7 +7569,12 @@ w32_parse_hot_key (Lisp_Object key) c = Fcar (c); if (!SYMBOLP (c)) emacs_abort (); - vk_code = lookup_vk_code (SSDATA (SYMBOL_NAME (c))); + vkname = SSDATA (SYMBOL_NAME (c)); + /* [s-], [M-], [h-]: Register all keys for this modifier */ + if (vkname[0] == 0) + vk_code = VK_ANY; + else + vk_code = lookup_vk_code (vkname); } else if (INTEGERP (c)) { @@ -7668,21 +7590,27 @@ w32_parse_hot_key (Lisp_Object key) && !NILP (Vw32_alt_is_meta)) lisp_modifiers |= alt_modifier; - /* Supply defs missing from mingw32. */ -#ifndef MOD_ALT -#define MOD_ALT 0x0001 -#define MOD_CONTROL 0x0002 -#define MOD_SHIFT 0x0004 -#define MOD_WIN 0x0008 -#endif - - /* Convert lisp modifiers to Windows hot-key form. */ - w32_modifiers = (lisp_modifiers & hyper_modifier) ? MOD_WIN : 0; - w32_modifiers |= (lisp_modifiers & alt_modifier) ? MOD_ALT : 0; - w32_modifiers |= (lisp_modifiers & ctrl_modifier) ? MOD_CONTROL : 0; - w32_modifiers |= (lisp_modifiers & shift_modifier) ? MOD_SHIFT : 0; + /* Register Alt-x combinations */ + if (lisp_modifiers & alt_modifier) + { + hook_w32_key (hook, VK_MENU, vk_code); + res = Qt; + } + /* Register Win-x combinations based on modifier mappings */ + if ((lisp_modifiers & hyper_modifier) && EQ (Vw32_lwindow_modifier, Qhyper) || + (lisp_modifiers & super_modifier) && EQ (Vw32_lwindow_modifier, Qsuper)) + { + hook_w32_key (hook, VK_LWIN, vk_code); + res = Qt; + } + if ((lisp_modifiers & hyper_modifier) && EQ (Vw32_rwindow_modifier, Qhyper) || + (lisp_modifiers & super_modifier) && EQ (Vw32_rwindow_modifier, Qsuper)) + { + hook_w32_key (hook, VK_RWIN, vk_code); + res = Qt; + } - return HOTKEY (vk_code, w32_modifiers); + return res; } DEFUN ("w32-register-hot-key", Fw32_register_hot_key, @@ -7701,26 +7629,7 @@ is always interpreted as the Windows modifier keys. The return value is the hotkey-id if registered, otherwise nil. */) (Lisp_Object key) { - key = w32_parse_hot_key (key); - - if (!NILP (key) && NILP (Fmemq (key, w32_grabbed_keys))) - { - /* Reuse an empty slot if possible. */ - Lisp_Object item = Fmemq (Qnil, w32_grabbed_keys); - - /* Safe to add new key to list, even if we have focus. */ - if (NILP (item)) - w32_grabbed_keys = Fcons (key, w32_grabbed_keys); - else - XSETCAR (item, key); - - /* Notify input thread about new hot-key definition, so that it - takes effect without needing to switch focus. */ - PostThreadMessage (dwWindowsThreadId, WM_EMACS_REGISTER_HOT_KEY, - (WPARAM) XINT (key), 0); - } - - return key; + return w32_parse_and_register_hot_key (key, 1); } DEFUN ("w32-unregister-hot-key", Fw32_unregister_hot_key, @@ -7728,73 +7637,7 @@ DEFUN ("w32-unregister-hot-key", Fw32_unregister_hot_key, doc: /* Unregister KEY as a hot-key combination. */) (Lisp_Object key) { - Lisp_Object item; - - if (!INTEGERP (key)) - key = w32_parse_hot_key (key); - - item = Fmemq (key, w32_grabbed_keys); - - if (!NILP (item)) - { - LPARAM lparam; - - eassert (CONSP (item)); - /* Pass the tail of the list as a pointer to a Lisp_Cons cell, - so that it works in a --with-wide-int build as well. */ - lparam = (LPARAM) XUNTAG (item, Lisp_Cons); - - /* Notify input thread about hot-key definition being removed, so - that it takes effect without needing focus switch. */ - if (PostThreadMessage (dwWindowsThreadId, WM_EMACS_UNREGISTER_HOT_KEY, - (WPARAM) XINT (XCAR (item)), lparam)) - { - MSG msg; - GetMessage (&msg, NULL, WM_EMACS_DONE, WM_EMACS_DONE); - } - return Qt; - } - return Qnil; -} - -DEFUN ("w32-registered-hot-keys", Fw32_registered_hot_keys, - Sw32_registered_hot_keys, 0, 0, 0, - doc: /* Return list of registered hot-key IDs. */) - (void) -{ - return Fdelq (Qnil, Fcopy_sequence (w32_grabbed_keys)); -} - -DEFUN ("w32-reconstruct-hot-key", Fw32_reconstruct_hot_key, - Sw32_reconstruct_hot_key, 1, 1, 0, - doc: /* Convert hot-key ID to a lisp key combination. -usage: (w32-reconstruct-hot-key ID) */) - (Lisp_Object hotkeyid) -{ - int vk_code, w32_modifiers; - Lisp_Object key; - - CHECK_NUMBER (hotkeyid); - - vk_code = HOTKEY_VK_CODE (hotkeyid); - w32_modifiers = HOTKEY_MODIFIERS (hotkeyid); - - if (vk_code < 256 && lispy_function_keys[vk_code]) - key = intern (lispy_function_keys[vk_code]); - else - key = make_number (vk_code); - - key = Fcons (key, Qnil); - if (w32_modifiers & MOD_SHIFT) - key = Fcons (Qshift, key); - if (w32_modifiers & MOD_CONTROL) - key = Fcons (Qctrl, key); - if (w32_modifiers & MOD_ALT) - key = Fcons (NILP (Vw32_alt_is_meta) ? Qalt : Qmeta, key); - if (w32_modifiers & MOD_WIN) - key = Fcons (Qhyper, key); - - return key; + return w32_parse_and_register_hot_key (key, 0); } DEFUN ("w32-toggle-lock-key", Fw32_toggle_lock_key, @@ -9307,9 +9150,6 @@ syms_of_w32fns (void) Fput (Qundefined_color, Qerror_message, build_pure_c_string ("Undefined color")); - staticpro (&w32_grabbed_keys); - w32_grabbed_keys = Qnil; - DEFVAR_LISP ("w32-color-map", Vw32_color_map, doc: /* An array of color name mappings for Windows. */); Vw32_color_map = Qnil; @@ -9622,8 +9462,6 @@ This variable has effect only on Windows Vista and later. */); defsubr (&Sw32_shell_execute); defsubr (&Sw32_register_hot_key); defsubr (&Sw32_unregister_hot_key); - defsubr (&Sw32_registered_hot_keys); - defsubr (&Sw32_reconstruct_hot_key); defsubr (&Sw32_toggle_lock_key); defsubr (&Sw32_window_exists_p); defsubr (&Sw32_battery_status); diff --git a/src/w32hook.c b/src/w32hook.c new file mode 100644 index 0000000..1b1e35b --- /dev/null +++ b/src/w32hook.c @@ -0,0 +1,343 @@ +/* System hooks for GNU Emacs on the Microsoft Windows API. + Copyright (C) 1992, 1999, 2001-2015 Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +GNU Emacs is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs. If not, see . */ + +/* Written by Jussi Lahdenniemi . + + This code handles grabbing of the Windows keys and Alt-* key + combinations reserved by the system. Handling Alt is a bit easier, + as Windows intends Alt-* shortcuts for application use in Windows; + hotkeys such as Alt-tab and Alt-escape are special cases. Win-* + hotkeys, on the other hand, are primarily meant for system use. + + As a result, when we want Emacs to be able to grab the Win-* keys, + we must swallow all Win key presses in a low-level keyboard hook. + Unfortunately, thismeans that the Emacs window procedure (and + console input handler) never see the keypresses either. Thus, to + check the modifier states properly, Emacs code must use the + check_w32_winkey_state function that uses the flags directly + updated by the hook callback. + + The keyboard hook sees keyboard input on all processes (except + elevated ones, when Emacs itself is not elevated). As such, care + must be taken to only filter out keyboard input when Emacs itself + is on the foreground. + */ + +#include +#include +#include "lisp.h" +#include "w32common.h" +#include "w32term.h" +#include "w32inevt.h" + +static int hook_count = 0; /* counter, if several windows are created */ +static HHOOK hook = NULL; /* hook handle */ + +static int lwindown = 0; /* Left Windows key currently pressed (and hooked) */ +static int rwindown = 0; /* Right Windows key currently pressed (and hooked) */ +static int winsdown = 0; /* Number of handled keys currently pressed */ +static int send_win_up = 0; /* Pass through the keyup for this Windows key press? */ +static int suppress_lone = 0; /* Suppress simulated Windows keydown-keyup for this press? */ +static int winseen = 0; /* Windows keys seen during this press? */ + +static char alt_hooked[256] = {0}; /* hook Alt+[this key]? */ +static char lwin_hooked[256] = {0}; /* hook left Win+[this key]? */ +static char rwin_hooked[256] = {0}; /* hook right Win+[this key]? */ + +/* stdin, from w32console.c */ +extern HANDLE keyboard_handle; + +/* The Windows keyboard hook callback */ +static LRESULT CALLBACK funhook( int code, WPARAM w, LPARAM l ) +{ + INPUT inputs[2]; + HWND focus = GetFocus(); + int console = 0; + KBDLLHOOKSTRUCT const* hs = (KBDLLHOOKSTRUCT*)l; + + if( code < 0 || (hs->flags & LLKHF_INJECTED)) + { + return CallNextHookEx( 0, code, w, l ); + } + + /* GetFocus() returns a non-NULL window if another application is active, + and always for a console Emacs process. For a console Emacs, determine + focus by checking if the current foreground window is the process's + console window. */ + if (focus == NULL) + { + HWND con = GetConsoleWindow(); + if (con == GetForegroundWindow()) + { + focus = con; + console = 1; + } + } + + /* First, check hooks for the left and right Windows keys */ + if( hs->vkCode == VK_LWIN || hs->vkCode == VK_RWIN) + { + if( focus != NULL && (w == WM_KEYDOWN || w == WM_SYSKEYDOWN)) + { + /* The key is being pressed in an Emacs window */ + if( hs->vkCode == VK_LWIN && !lwindown ) + { + lwindown = 1; + winseen = 1; + winsdown++; + } + else if( hs->vkCode == VK_RWIN && !rwindown ) + { + rwindown = 1; + winseen = 1; + winsdown++; + } + /* Returning 1 here drops the keypress without further processing. + If the keypress was allowed to go through, the normal Windows + hotkeys would take over. */ + return 1; + } + else if( winsdown > 0 && (w == WM_KEYUP || w == WM_SYSKEYUP)) + { + /* A key that has been captured earlier is being released now. */ + if( hs->vkCode == VK_LWIN && lwindown ) + { + lwindown = 0; + winsdown--; + } + else if( hs->vkCode == VK_RWIN && rwindown ) + { + rwindown = 0; + winsdown--; + } + if( winsdown == 0 && winseen ) + { + if( !suppress_lone ) + { + /* The Windows key was pressed, then released, without any other + key pressed simultaneously. Normally, this opens the Start + menu, but the user can prevent this by setting the + w32-pass-[lr]window-to-system variable to NIL. */ + if (hs->vkCode == VK_LWIN && !NILP(Vw32_pass_lwindow_to_system) || + hs->vkCode == VK_RWIN && !NILP(Vw32_pass_rwindow_to_system)) + { + /* Not prevented - Simulate the keypress to the system */ + memset( inputs, 0, sizeof(inputs)); + inputs[0].type = INPUT_KEYBOARD; + inputs[0].ki.wVk = hs->vkCode; + inputs[0].ki.wScan = hs->vkCode; + inputs[0].ki.dwFlags = KEYEVENTF_EXTENDEDKEY; + inputs[0].ki.time = 0; + inputs[1].type = INPUT_KEYBOARD; + inputs[1].ki.wVk = hs->vkCode; + inputs[1].ki.wScan = hs->vkCode; + inputs[1].ki.dwFlags = KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP; + inputs[1].ki.time = 0; + SendInput( 2, inputs, sizeof(INPUT)); + } + else if( focus != NULL ) + { + /* When not passed to system, must simulate privately to Emacs */ + PostMessage( focus, WM_SYSKEYDOWN, hs->vkCode, 0 ); + PostMessage( focus, WM_SYSKEYUP, hs->vkCode, 0 ); + } + } + } + if( winsdown == 0 ) + { + /* No Windows keys pressed anymore - clear the state flags */ + suppress_lone = 0; + winseen = 0; + } + if( !send_win_up ) + { + /* Swallow this release message, as not to confuse applications who + did not get to see the original WM_KEYDOWN message either. */ + return 1; + } + send_win_up = 0; + } + } + else if( winsdown > 0 ) + { + /* Some other key was pressed while a captured Win key is down. + This is either an Emacs registered hotkey combination, or a + system hotkey. */ + if( lwindown && lwin_hooked[hs->vkCode] || + rwindown && rwin_hooked[hs->vkCode] ) + { + /* Hooked Win-x combination, do not pass the keypress to Windows */ + suppress_lone = 1; + } + else if (!suppress_lone) + { + /* Unhooked S-x combination; simulate the combination now + (will be seen by the system) */ + memset( inputs, 0, sizeof(inputs)); + inputs[0].type = INPUT_KEYBOARD; + inputs[0].ki.wVk = lwindown ? VK_LWIN : VK_RWIN; + inputs[0].ki.wScan = lwindown ? VK_LWIN : VK_RWIN; + inputs[0].ki.dwFlags = KEYEVENTF_EXTENDEDKEY; + inputs[0].ki.time = 0; + inputs[1].type = INPUT_KEYBOARD; + inputs[1].ki.wVk = hs->vkCode; + inputs[1].ki.wScan = hs->scanCode; + inputs[1].ki.dwFlags = (hs->flags & LLKHF_EXTENDED) ? KEYEVENTF_EXTENDEDKEY : 0; + inputs[1].ki.time = 0; + SendInput( 2, inputs, sizeof(INPUT)); + /* Stop processing of this Win sequence here; the + corresponding keyup messages will come through the normal + channel when the keys are released. */ + suppress_lone = 1; + send_win_up = 1; + /* Swallow the original keypress (as we want the Win key + down message simulated above to precede this real message). */ + return 1; + } + } + + /* Next, handle the registered Alt-* combinations */ + if( (w == WM_SYSKEYDOWN || w == WM_KEYDOWN) && + alt_hooked[hs->vkCode] && + focus != NULL && + (GetAsyncKeyState(VK_MENU) & 0x8000)) + { + /* Prevent the system from getting this Alt-* key - suppress the + message and post as a normal keypress to Emacs. + + For some reason, in case of the Alt-escape combination on a + console Emacs, the console process swallows the escape + keydown message here. Simulating the keypress with SendInput + just gives the standard functionality of sending the window + to back. So, to make Alt-escape to work on a console Emacs, + the keypress would need to be communicated directly from here + to the key_event functionality in w32inevt.c; but this hook + code does not run on the same thread as the normal keyboard + input code, so proper synchronization should be done. As + this is quite a corner case, it is left unimplemented for + now. */ + if( console ) + { + INPUT_RECORD rec; + DWORD n; + rec.EventType = KEY_EVENT; + rec.Event.KeyEvent.bKeyDown = TRUE; + rec.Event.KeyEvent.wVirtualKeyCode = hs->vkCode; + rec.Event.KeyEvent.wVirtualScanCode = hs->scanCode; + rec.Event.KeyEvent.uChar.UnicodeChar = 0; + rec.Event.KeyEvent.dwControlKeyState = + ((GetAsyncKeyState(VK_LMENU) & 0x8000) ? LEFT_ALT_PRESSED : 0) | + ((GetAsyncKeyState(VK_RMENU) & 0x8000) ? RIGHT_ALT_PRESSED : 0) | + ((GetAsyncKeyState(VK_LCONTROL) & 0x8000) ? LEFT_CTRL_PRESSED : 0) | + ((GetAsyncKeyState(VK_RCONTROL) & 0x8000) ? RIGHT_CTRL_PRESSED : 0) | + ((GetAsyncKeyState(VK_SHIFT) & 0x8000) ? SHIFT_PRESSED : 0) | + ((hs->flags & LLKHF_EXTENDED) ? ENHANCED_KEY : 0); + if( w32_console_unicode_input ) + WriteConsoleInputW(keyboard_handle, &rec, 1, &n); + else + WriteConsoleInputA(keyboard_handle, &rec, 1, &n); + } + else + PostMessage( focus, w, hs->vkCode, 1 | (1<<29)); + return 1; + } + + /* The normal case - pass the message through */ + return CallNextHookEx( 0, code, w, l ); +} + +/* Set up the hook; can be called several times, with matching + remove_w32_kbdhook calls. */ +void setup_w32_kbdhook (void) +{ + hook_count++; + if (hook_count == 1) + { + hook = SetWindowsHookEx( WH_KEYBOARD_LL, funhook, GetModuleHandle(NULL), 0 ); + } +} + +/* Remove the hook */ +void remove_w32_kbdhook (void) +{ + hook_count--; + if (hook_count == 0) + { + UnhookWindowsHookEx( hook ); + hook = NULL; + } +} + +/* Check the current Win key pressed state - see the discussion at the + top of the file for the rationale */ +int check_w32_winkey_state (int vkey) +{ + switch (vkey) + { + case VK_LWIN: return lwindown; + case VK_RWIN: return rwindown; + } + return 0; +} + +/* Mark a specific key combination as hooked, preventing it to be + handled by the system */ +void hook_w32_key (int hook, int modifier, int vkey) +{ + char* tbl = NULL; + + switch(modifier) + { + case VK_MENU: tbl = alt_hooked; break; + case VK_LWIN: tbl = lwin_hooked; break; + case VK_RWIN: tbl = rwin_hooked; break; + } + + if (tbl != NULL && vkey >= 0 && vkey <= 255) + { + /* VK_ANY hooks all keys for this modifier */ + if (vkey == VK_ANY) + memset(tbl, (char)hook, 256); + else + tbl[vkey] = (char)hook; + /* Alt-s should go through */ + alt_hooked[VK_MENU] = 0; + alt_hooked[VK_LMENU] = 0; + alt_hooked[VK_RMENU] = 0; + alt_hooked[VK_CONTROL] = 0; + alt_hooked[VK_LCONTROL] = 0; + alt_hooked[VK_RCONTROL] = 0; + alt_hooked[VK_SHIFT] = 0; + alt_hooked[VK_LSHIFT] = 0; + alt_hooked[VK_RSHIFT] = 0; + } +} + +/* Resets the keyboard hook state. Locking the workstation with Win-L + leaves the Win key(s) "down" from the hook's point of view - the + keyup event is never seen. Thus, this function must be called when + the system is locked. */ +void reset_w32_kbdhook_state (void) +{ + lwindown = 0; + rwindown = 0; + winsdown = 0; + send_win_up = 0; + suppress_lone = 0; + winseen = 0; +} diff --git a/src/w32inevt.c b/src/w32inevt.c index db2e218..931fb20 100644 --- a/src/w32inevt.c +++ b/src/w32inevt.c @@ -147,12 +147,6 @@ key_event (KEY_EVENT_RECORD *event, struct input_event *emacs_ev, int *isdead) { switch (event->wVirtualKeyCode) { - case VK_LWIN: - mod_key_state &= ~LEFT_WIN_PRESSED; - break; - case VK_RWIN: - mod_key_state &= ~RIGHT_WIN_PRESSED; - break; case VK_APPS: mod_key_state &= ~APPS_PRESSED; break; @@ -185,7 +179,6 @@ key_event (KEY_EVENT_RECORD *event, struct input_event *emacs_ev, int *isdead) keybd_event (faked_key, (BYTE) MapVirtualKey (faked_key, 0), 0, 0); } } - mod_key_state |= LEFT_WIN_PRESSED; if (!NILP (Vw32_lwindow_modifier)) return 0; break; @@ -201,7 +194,6 @@ key_event (KEY_EVENT_RECORD *event, struct input_event *emacs_ev, int *isdead) keybd_event (faked_key, (BYTE) MapVirtualKey (faked_key, 0), 0, 0); } } - mod_key_state |= RIGHT_WIN_PRESSED; if (!NILP (Vw32_rwindow_modifier)) return 0; break; @@ -267,6 +259,10 @@ key_event (KEY_EVENT_RECORD *event, struct input_event *emacs_ev, int *isdead) /* Recognize state of Windows and Apps keys. */ event->dwControlKeyState |= mod_key_state; + if (check_w32_winkey_state(VK_LWIN)) + event->dwControlKeyState |= LEFT_WIN_PRESSED; + if (check_w32_winkey_state(VK_RWIN)) + event->dwControlKeyState |= RIGHT_WIN_PRESSED; /* Distinguish numeric keypad keys from extended keys. */ event->wVirtualKeyCode = diff --git a/src/w32term.h b/src/w32term.h index 3377b53..8432843 100644 --- a/src/w32term.h +++ b/src/w32term.h @@ -738,6 +738,12 @@ extern int handle_file_notifications (struct input_event *); extern void w32_initialize_display_info (Lisp_Object); extern void initialize_w32_display (struct terminal *, int *, int *); +extern void setup_w32_kbdhook (void); +extern void remove_w32_kbdhook (void); +extern void hook_w32_key (int hook, int modifier, int vkey); +extern int check_w32_winkey_state (int vkey); +extern void reset_w32_kbdhook_state (void); + /* Keypad command key support. W32 doesn't have virtual keys defined for the function keys on the keypad (they are mapped to the standard function keys), so we define our own. */ @@ -762,6 +768,10 @@ extern void initialize_w32_display (struct terminal *, int *, int *); #define VK_APPS 0x5D #endif +/* Special virtual key code for indicating "any" key. This can be used + as an argument to the hook_w32_key function. */ +#define VK_ANY 0xFF + /* Support for treating Windows and Apps keys as modifiers. These constants must not overlap with any of the dwControlKeyState flags in KEY_EVENT_RECORD. */ -- 2.6.2