[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [PATCH] Override Windows default Win-* key combinations when using E
From: |
Jussi Lahdenniemi |
Subject: |
Re: [PATCH] Override Windows default Win-* key combinations when using Emacs |
Date: |
Sat, 9 Jan 2016 21:58:52 +0200 |
User-agent: |
Mozilla/5.0 (Windows NT 10.0; WOW64; rv:38.0) Gecko/20100101 Thunderbird/38.5.0 |
On 5.1.2016 19.05, Eli Zaretskii wrote:
Thank you for your contribution. It is large enough to require legal
paperwork; I can send you the forms off-list if you agree.
I finished integrating the code more tightly into Emacs sources,
removing the need for the additional process and adding functionality
for grabbing Alt-based keyboard input (M-tab, M-ESC) in addition to the
Windows keys. I also updated the documentation.
The patch is attached. I decided to place the new functionality in a new
file (w32hook.c) - it could have been added to some existing file as well.
The assignment paperwork is in the mail.
----------------------- 8< 8< -----------------------
The Windows keyboard hooking code properly grabs system hotkeys such as
Win-*
and Alt-Tab for Emacs use. The new code replaces the w32-register-hot-key
functionality. The hooking code also works for both windowed and console
(emacs -nw) mode.
* configure.ac: Added the new file src/w32hook.c
* src/w32term.h: Added new function prototypes
* src/w32fns.c: Updated the w32-register-hot-key related code to use
the new hooking code instead
* src/w32console.c: Updated to support hooking for console Emacs
* src/w32inevt.c: Ditto
* src/w32hook.c: New file containing the hooking code
* doc/emacs/msdos.texi: Updated documentation for the new functionality
Fixes
---
configure.ac | 1 +
doc/emacs/msdos.texi | 100 +++++++++------
src/w32console.c | 3 +
src/w32fns.c | 288 ++++++++++--------------------------------
src/w32hook.c | 343
+++++++++++++++++++++++++++++++++++++++++++++++++++
src/w32inevt.c | 12 +-
src/w32term.h | 10 ++
7 files changed, 485 insertions(+), 272 deletions(-)
create mode 100644 src/w32hook.c
diff --git a/configure.ac b/configure.ac
index 0aa863a..4ae0f26 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1959,6 +1959,7 @@ if test "${HAVE_W32}" = "yes"; then
[AC_MSG_ERROR([No resource compiler found.])])
W32_OBJ="w32fns.o w32menu.o w32reg.o w32font.o w32term.o"
W32_OBJ="$W32_OBJ w32xfns.o w32select.o w32uniscribe.o"
+ W32_OBJ="$W32_OBJ w32hook.o"
EMACSRES="emacs.res"
case "$canonical" in
x86_64-*-*) EMACS_MANIFEST="emacs-x64.manifest" ;;
diff --git a/doc/emacs/msdos.texi b/doc/emacs/msdos.texi
index f1cdb26..34ad639 100644
--- a/doc/emacs/msdos.texi
+++ b/doc/emacs/msdos.texi
@@ -504,39 +504,6 @@ Windows Keyboard
key. If you wish it to produce the @code{Alt} modifier instead, set
the variable @code{w32-alt-is-meta} to a @code{nil} value.
address@hidden w32-register-hot-key
address@hidden w32-unregister-hot-key
- MS-Windows reserves certain key combinations, such as
address@hidden@address@hidden, for its own use. These key combinations are
-intercepted by the system before Emacs can see them. You can use the
address@hidden function to allow a key sequence to be
-seen by Emacs instead of being grabbed by Windows. This function
-registers a key sequence as a @dfn{hot key}, overriding the special
-meaning of that key sequence for Windows. (MS-Windows is told that
-the key sequence is a hot key only when one of the Emacs windows has
-focus, so that the special keys still have their usual meaning for
-other Windows applications.)
-
- The argument to @code{w32-register-hot-key} must be a single key,
-with or without modifiers, in vector form that would be acceptable to
address@hidden The meta modifier is interpreted as the @key{Alt}
-key if @code{w32-alt-is-meta} is @code{t} (the default), and the hyper
-modifier is always interpreted as the Windows key (usually labeled
-with @key{start} and the Windows logo). If the function succeeds in
-registering the key sequence, it returns the hotkey ID, a number;
-otherwise it returns @code{nil}.
-
address@hidden address@hidden, (MS-Windows)}
address@hidden @address@hidden vs @address@hidden@key{TAB}} (MS-Windows)
address@hidden @address@hidden@key{TAB}} vs @address@hidden (MS-Windows)
- For example, @code{(w32-register-hot-key [M-tab])} lets you use
address@hidden@key{TAB}} normally in Emacs; for instance, to complete the word
or
-symbol at point at top level, or to complete the current search string
-against previously sought strings during incremental search.
-
- The function @code{w32-unregister-hot-key} reverses the effect of
address@hidden for its argument key sequence.
-
@vindex w32-capslock-is-shiftlock
By default, the @key{CapsLock} key only affects normal character
keys (it converts lower-case characters to their upper-case
@@ -582,6 +549,66 @@ Windows Keyboard
right Windows key produces the symbol @code{rwindow} and @key{ScrLock}
produces the symbol @code{scroll}.
address@hidden w32-register-hot-key
address@hidden w32-unregister-hot-key
+ MS-Windows reserves certain key combinations, such as
address@hidden@address@hidden and a number of Windows key combinations,
+for its own use. These key combinations are intercepted by the system
+before Emacs can see them. Also, on Windows 10, all Windows key
+combinations are reserved by the system in such a way that they are
+never propagated to applications, even if the system does not
+currently define a hotkey on the specific combination. You can use
+the @code{w32-register-hot-key} function to allow a key sequence to be
+seen by Emacs instead of being grabbed by Windows. When registered as
+a hot key, the key combination is pulled out of the system's input
+queue before it is handled by Windows, effectively overriding the
+special meaning of that key sequence for Windows. The override is
+only effective when Emacs is active; with other applications on the
+foreground the keys behave normally.
+
+ The argument to @code{w32-register-hot-key} must be a single key with a
+single modifier, in vector form that would be acceptable to
address@hidden The control and shift modifiers have no effect on the
+argument. The meta modifier is interpreted as the @key{Alt} key if
address@hidden is @code{t} (the default), and the super and hyper
+modifiers are interpreted according to the bindings of
address@hidden and @code{w32-rwindow-modifier}.
Additionally, a
+modifier with the trailing dash but with no key indicates that all
+Windows defined hotkeys for that modifier are to be overridden in the
+favor of Emacs.
+
address@hidden address@hidden, (MS-Windows)}
address@hidden @address@hidden vs @address@hidden@key{TAB}} (MS-Windows)
address@hidden @address@hidden@key{TAB}} vs @address@hidden (MS-Windows)
+ For example, @code{(w32-register-hot-key [M-tab])} lets you use
address@hidden@key{TAB}} normally in Emacs; for instance, to complete the
+word or symbol at point at top level, or to complete the current
+search string against previously sought strings during incremental
+search. @code{(w32-register-hot-key [s-])} with
address@hidden bound to @code{super} disables all the
+Windows' own Windows key based address@hidden is one known
+exception: The combination @address@hidden@key{L}} that locks the
+workstation is handled by the system on a lower level. For this
+reason, @code{w32-register-hot-key} cannot override this key
+combination - it always locks the computer.}
+
+ Note that @code{w32-register-hot-key} checks the
address@hidden values at the time of the function
+call. Thus, you can set @code{w32-lwindow-modifier} as @code{super},
+then call @code{(w32-register-hot-key [s-r])}, and finally set
address@hidden as @code{super} as well. The result is
+that the left Windows key together with @key{R} invokes whichever
+function you have bound for the combination in Emacs, and the right
+Windows key and @key{R} opens the Windows @code{Run} dialog.
+
+ The hotkey registrations always also include all the shift and
+control modifier combinations for the given hotkey; that is,
+registering @address@hidden as a hotkey gives you @address@hidden,
address@hidden@key{a}} and @address@hidden as well.
+
+ The function @code{w32-unregister-hot-key} reverses the effect of
address@hidden for its argument key sequence.
+
@vindex w32-pass-alt-to-system
@cindex Windows system menu
@cindex @code{Alt} key invokes menu (Windows)
@@ -607,12 +634,7 @@ Windows Keyboard
otherwise it is passed to Windows. The default is @code{t} for both
of these variables. Passing each of these keys to Windows produces
its normal effect: for example, @address@hidden opens the
address@hidden menu, address@hidden
-Some combinations of the ``Windows'' keys with other keys are caught
-by Windows at a low level in a way that Emacs currently cannot prevent.
-For example, @address@hidden r} always pops up the Windows
address@hidden dialog. Customizing the value of
address@hidden might help in some cases, though.}
address@hidden menu, etc.
@vindex w32-recognize-altgr
@kindex AltGr @r{(MS-Windows)}
diff --git a/src/w32console.c b/src/w32console.c
index 7fffabf..57f74d1 100644
--- a/src/w32console.c
+++ b/src/w32console.c
@@ -759,6 +759,9 @@ initialize_w32_display (struct terminal *term, int
*width, int *height)
/* Setup w32_display_info structure for this frame. */
w32_initialize_display_info (build_string ("Console"));
+
+ /* Set up the keyboard hook */
+ setup_w32_kbdhook ();
}
diff --git a/src/w32fns.c b/src/w32fns.c
index c1d9bff..99c9ed1 100644
--- a/src/w32fns.c
+++ b/src/w32fns.c
@@ -2248,6 +2248,8 @@ modifier_set (int vkey)
else
return (GetKeyState (vkey) & 0x1);
}
+ if (vkey == VK_LWIN || vkey == VK_RWIN)
+ return check_w32_winkey_state (vkey);
if (!modifiers_recorded)
return (GetKeyState (vkey) & 0x8000);
@@ -2387,60 +2389,6 @@ map_keypad_keys (unsigned int virt_key, unsigned
int extended)
return virt_key;
}
-/* List of special key combinations which w32 would normally capture,
- but Emacs should grab instead. Not directly visible to lisp, to
- simplify synchronization. Each item is an integer encoding a virtual
- key code and modifier combination to capture. */
-static Lisp_Object w32_grabbed_keys;
-
-#define HOTKEY(vk, mods) make_number (((vk) & 255) | ((mods) << 8))
-#define HOTKEY_ID(k) (XFASTINT (k) & 0xbfff)
-#define HOTKEY_VK_CODE(k) (XFASTINT (k) & 255)
-#define HOTKEY_MODIFIERS(k) (XFASTINT (k) >> 8)
-
-#define RAW_HOTKEY_ID(k) ((k) & 0xbfff)
-#define RAW_HOTKEY_VK_CODE(k) ((k) & 255)
-#define RAW_HOTKEY_MODIFIERS(k) ((k) >> 8)
-
-/* Register hot-keys for reserved key combinations when Emacs has
- keyboard focus, since this is the only way Emacs can receive key
- combinations like Alt-Tab which are used by the system. */
-
-static void
-register_hot_keys (HWND hwnd)
-{
- Lisp_Object keylist;
-
- /* Use CONSP, since we are called asynchronously. */
- for (keylist = w32_grabbed_keys; CONSP (keylist); keylist = XCDR
(keylist))
- {
- Lisp_Object key = XCAR (keylist);
-
- /* Deleted entries get set to nil. */
- if (!INTEGERP (key))
- continue;
-
- RegisterHotKey (hwnd, HOTKEY_ID (key),
- HOTKEY_MODIFIERS (key), HOTKEY_VK_CODE (key));
- }
-}
-
-static void
-unregister_hot_keys (HWND hwnd)
-{
- Lisp_Object keylist;
-
- for (keylist = w32_grabbed_keys; CONSP (keylist); keylist = XCDR
(keylist))
- {
- Lisp_Object key = XCAR (keylist);
-
- if (!INTEGERP (key))
- continue;
-
- UnregisterHotKey (hwnd, HOTKEY_ID (key));
- }
-}
-
#if EMACSDEBUG
const char*
w32_name_of_message (UINT msg)
@@ -2570,27 +2518,6 @@ w32_msg_pump (deferred_msg * msg_buf)
result, 0))
emacs_abort ();
break;
- case WM_EMACS_REGISTER_HOT_KEY:
- focus_window = GetFocus ();
- if (focus_window != NULL)
- RegisterHotKey (focus_window,
- RAW_HOTKEY_ID (msg.wParam),
- RAW_HOTKEY_MODIFIERS (msg.wParam),
- RAW_HOTKEY_VK_CODE (msg.wParam));
- /* Reply is not expected. */
- break;
- case WM_EMACS_UNREGISTER_HOT_KEY:
- focus_window = GetFocus ();
- if (focus_window != NULL)
- UnregisterHotKey (focus_window, RAW_HOTKEY_ID (msg.wParam));
- /* Mark item as erased. NB: this code must be
- thread-safe. The next line is okay because the cons
- cell is never made into garbage and is not relocated by
- GC. */
- XSETCAR (make_lisp_ptr ((void *)msg.lParam, Lisp_Cons), Qnil);
- if (!PostThreadMessage (dwMainThreadId, WM_EMACS_DONE, 0, 0))
- emacs_abort ();
- break;
case WM_EMACS_TOGGLE_LOCK_KEY:
{
int vk_code = (int) msg.wParam;
@@ -3401,11 +3328,6 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam,
LPARAM lParam)
}
goto dflt;
- case WM_HOTKEY:
- /* Synchronize hot keys with normal input. */
- PostMessage (hwnd, WM_KEYDOWN, HIWORD (lParam), 0);
- return (0);
-
case WM_KEYUP:
case WM_SYSKEYUP:
record_keyup (wParam, lParam);
@@ -3439,41 +3361,16 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM
wParam, LPARAM lParam)
switch (wParam)
{
- case VK_LWIN:
- if (NILP (Vw32_pass_lwindow_to_system))
- {
- /* Prevent system from acting on keyup (which opens the
- Start menu if no other key was pressed) by simulating a
- press of Space which we will ignore. */
- if (GetAsyncKeyState (wParam) & 1)
- {
- if (NUMBERP (Vw32_phantom_key_code))
- key = XUINT (Vw32_phantom_key_code) & 255;
- else
- key = VK_SPACE;
- dpyinfo->faked_key = key;
- keybd_event (key, (BYTE) MapVirtualKey (key, 0), 0, 0);
- }
- }
- if (!NILP (Vw32_lwindow_modifier))
- return 0;
- break;
- case VK_RWIN:
- if (NILP (Vw32_pass_rwindow_to_system))
- {
- if (GetAsyncKeyState (wParam) & 1)
- {
- if (NUMBERP (Vw32_phantom_key_code))
- key = XUINT (Vw32_phantom_key_code) & 255;
- else
- key = VK_SPACE;
- dpyinfo->faked_key = key;
- keybd_event (key, (BYTE) MapVirtualKey (key, 0), 0, 0);
- }
- }
- if (!NILP (Vw32_rwindow_modifier))
- return 0;
- break;
+ /* The Win keys are handled in w32hook.c and must be silently
+ ignored here when used as a modifier */
+ case VK_LWIN:
+ if (!NILP (Vw32_lwindow_modifier))
+ return 0;
+ break;
+ case VK_RWIN:
+ if (!NILP (Vw32_rwindow_modifier))
+ return 0;
+ break;
case VK_APPS:
if (!NILP (Vw32_apps_modifier))
return 0;
@@ -4316,10 +4213,8 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam,
LPARAM lParam)
case WM_SETFOCUS:
dpyinfo->faked_key = 0;
reset_modifiers ();
- register_hot_keys (hwnd);
goto command;
case WM_KILLFOCUS:
- unregister_hot_keys (hwnd);
button_state = 0;
ReleaseCapture ();
/* Relinquish the system caret. */
@@ -4348,10 +4243,20 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM
wParam, LPARAM lParam)
my_post_msg (&wmsg, hwnd, msg, wParam, lParam);
goto dflt;
+ case WM_CREATE:
+ setup_w32_kbdhook ();
+ goto dflt;
+
case WM_DESTROY:
+ remove_w32_kbdhook ();
CoUninitialize ();
return 0;
+ case WM_WTSSESSION_CHANGE:
+ if (wParam == WTS_SESSION_LOCK)
+ reset_w32_kbdhook_state ();
+ goto dflt;
+
case WM_CLOSE:
wmsg.dwModifiers = w32_get_modifiers ();
my_post_msg (&wmsg, hwnd, msg, wParam, lParam);
@@ -7617,19 +7522,31 @@ lookup_vk_code (char *key)
&& strcmp (lispy_function_keys[i], key) == 0)
return i;
+ /* alphanumerics map to themselves */
+ if (key[1] == 0)
+ {
+ if (key[0] >= 'A' && key[0] <= 'Z' ||
+ key[0] >= '0' && key[0] <= '9')
+ return key[0];
+ if (key[0] >= 'a' && key[0] <= 'z')
+ return toupper(key[0]);
+ }
+
return -1;
}
/* Convert a one-element vector style key sequence to a hot key
definition. */
static Lisp_Object
-w32_parse_hot_key (Lisp_Object key)
+w32_parse_and_register_hot_key (Lisp_Object key, int hook)
{
/* Copied from Fdefine_key and store_in_keymap. */
register Lisp_Object c;
int vk_code;
int lisp_modifiers;
int w32_modifiers;
+ Lisp_Object res = Qnil;
+ char* vkname;
CHECK_VECTOR (key);
@@ -7652,7 +7569,12 @@ w32_parse_hot_key (Lisp_Object key)
c = Fcar (c);
if (!SYMBOLP (c))
emacs_abort ();
- vk_code = lookup_vk_code (SSDATA (SYMBOL_NAME (c)));
+ vkname = SSDATA (SYMBOL_NAME (c));
+ /* [s-], [M-], [h-]: Register all keys for this modifier */
+ if (vkname[0] == 0)
+ vk_code = VK_ANY;
+ else
+ vk_code = lookup_vk_code (vkname);
}
else if (INTEGERP (c))
{
@@ -7668,21 +7590,27 @@ w32_parse_hot_key (Lisp_Object key)
&& !NILP (Vw32_alt_is_meta))
lisp_modifiers |= alt_modifier;
- /* Supply defs missing from mingw32. */
-#ifndef MOD_ALT
-#define MOD_ALT 0x0001
-#define MOD_CONTROL 0x0002
-#define MOD_SHIFT 0x0004
-#define MOD_WIN 0x0008
-#endif
-
- /* Convert lisp modifiers to Windows hot-key form. */
- w32_modifiers = (lisp_modifiers & hyper_modifier) ? MOD_WIN : 0;
- w32_modifiers |= (lisp_modifiers & alt_modifier) ? MOD_ALT : 0;
- w32_modifiers |= (lisp_modifiers & ctrl_modifier) ? MOD_CONTROL : 0;
- w32_modifiers |= (lisp_modifiers & shift_modifier) ? MOD_SHIFT : 0;
+ /* Register Alt-x combinations */
+ if (lisp_modifiers & alt_modifier)
+ {
+ hook_w32_key (hook, VK_MENU, vk_code);
+ res = Qt;
+ }
+ /* Register Win-x combinations based on modifier mappings */
+ if ((lisp_modifiers & hyper_modifier) && EQ (Vw32_lwindow_modifier,
Qhyper) ||
+ (lisp_modifiers & super_modifier) && EQ (Vw32_lwindow_modifier,
Qsuper))
+ {
+ hook_w32_key (hook, VK_LWIN, vk_code);
+ res = Qt;
+ }
+ if ((lisp_modifiers & hyper_modifier) && EQ (Vw32_rwindow_modifier,
Qhyper) ||
+ (lisp_modifiers & super_modifier) && EQ (Vw32_rwindow_modifier,
Qsuper))
+ {
+ hook_w32_key (hook, VK_RWIN, vk_code);
+ res = Qt;
+ }
- return HOTKEY (vk_code, w32_modifiers);
+ return res;
}
DEFUN ("w32-register-hot-key", Fw32_register_hot_key,
@@ -7701,26 +7629,7 @@ is always interpreted as the Windows modifier keys.
The return value is the hotkey-id if registered, otherwise nil. */)
(Lisp_Object key)
{
- key = w32_parse_hot_key (key);
-
- if (!NILP (key) && NILP (Fmemq (key, w32_grabbed_keys)))
- {
- /* Reuse an empty slot if possible. */
- Lisp_Object item = Fmemq (Qnil, w32_grabbed_keys);
-
- /* Safe to add new key to list, even if we have focus. */
- if (NILP (item))
- w32_grabbed_keys = Fcons (key, w32_grabbed_keys);
- else
- XSETCAR (item, key);
-
- /* Notify input thread about new hot-key definition, so that it
- takes effect without needing to switch focus. */
- PostThreadMessage (dwWindowsThreadId, WM_EMACS_REGISTER_HOT_KEY,
- (WPARAM) XINT (key), 0);
- }
-
- return key;
+ return w32_parse_and_register_hot_key (key, 1);
}
DEFUN ("w32-unregister-hot-key", Fw32_unregister_hot_key,
@@ -7728,73 +7637,7 @@ DEFUN ("w32-unregister-hot-key",
Fw32_unregister_hot_key,
doc: /* Unregister KEY as a hot-key combination. */)
(Lisp_Object key)
{
- Lisp_Object item;
-
- if (!INTEGERP (key))
- key = w32_parse_hot_key (key);
-
- item = Fmemq (key, w32_grabbed_keys);
-
- if (!NILP (item))
- {
- LPARAM lparam;
-
- eassert (CONSP (item));
- /* Pass the tail of the list as a pointer to a Lisp_Cons cell,
- so that it works in a --with-wide-int build as well. */
- lparam = (LPARAM) XUNTAG (item, Lisp_Cons);
-
- /* Notify input thread about hot-key definition being removed, so
- that it takes effect without needing focus switch. */
- if (PostThreadMessage (dwWindowsThreadId,
WM_EMACS_UNREGISTER_HOT_KEY,
- (WPARAM) XINT (XCAR (item)), lparam))
- {
- MSG msg;
- GetMessage (&msg, NULL, WM_EMACS_DONE, WM_EMACS_DONE);
- }
- return Qt;
- }
- return Qnil;
-}
-
-DEFUN ("w32-registered-hot-keys", Fw32_registered_hot_keys,
- Sw32_registered_hot_keys, 0, 0, 0,
- doc: /* Return list of registered hot-key IDs. */)
- (void)
-{
- return Fdelq (Qnil, Fcopy_sequence (w32_grabbed_keys));
-}
-
-DEFUN ("w32-reconstruct-hot-key", Fw32_reconstruct_hot_key,
- Sw32_reconstruct_hot_key, 1, 1, 0,
- doc: /* Convert hot-key ID to a lisp key combination.
-usage: (w32-reconstruct-hot-key ID) */)
- (Lisp_Object hotkeyid)
-{
- int vk_code, w32_modifiers;
- Lisp_Object key;
-
- CHECK_NUMBER (hotkeyid);
-
- vk_code = HOTKEY_VK_CODE (hotkeyid);
- w32_modifiers = HOTKEY_MODIFIERS (hotkeyid);
-
- if (vk_code < 256 && lispy_function_keys[vk_code])
- key = intern (lispy_function_keys[vk_code]);
- else
- key = make_number (vk_code);
-
- key = Fcons (key, Qnil);
- if (w32_modifiers & MOD_SHIFT)
- key = Fcons (Qshift, key);
- if (w32_modifiers & MOD_CONTROL)
- key = Fcons (Qctrl, key);
- if (w32_modifiers & MOD_ALT)
- key = Fcons (NILP (Vw32_alt_is_meta) ? Qalt : Qmeta, key);
- if (w32_modifiers & MOD_WIN)
- key = Fcons (Qhyper, key);
-
- return key;
+ return w32_parse_and_register_hot_key (key, 0);
}
DEFUN ("w32-toggle-lock-key", Fw32_toggle_lock_key,
@@ -9307,9 +9150,6 @@ syms_of_w32fns (void)
Fput (Qundefined_color, Qerror_message,
build_pure_c_string ("Undefined color"));
- staticpro (&w32_grabbed_keys);
- w32_grabbed_keys = Qnil;
-
DEFVAR_LISP ("w32-color-map", Vw32_color_map,
doc: /* An array of color name mappings for Windows. */);
Vw32_color_map = Qnil;
@@ -9622,8 +9462,6 @@ This variable has effect only on Windows Vista and
later. */);
defsubr (&Sw32_shell_execute);
defsubr (&Sw32_register_hot_key);
defsubr (&Sw32_unregister_hot_key);
- defsubr (&Sw32_registered_hot_keys);
- defsubr (&Sw32_reconstruct_hot_key);
defsubr (&Sw32_toggle_lock_key);
defsubr (&Sw32_window_exists_p);
defsubr (&Sw32_battery_status);
diff --git a/src/w32hook.c b/src/w32hook.c
new file mode 100644
index 0000000..1b1e35b
--- /dev/null
+++ b/src/w32hook.c
@@ -0,0 +1,343 @@
+/* System hooks for GNU Emacs on the Microsoft Windows API.
+ Copyright (C) 1992, 1999, 2001-2015 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
+
+/* Written by Jussi Lahdenniemi <address@hidden>.
+
+ This code handles grabbing of the Windows keys and Alt-* key
+ combinations reserved by the system. Handling Alt is a bit easier,
+ as Windows intends Alt-* shortcuts for application use in Windows;
+ hotkeys such as Alt-tab and Alt-escape are special cases. Win-*
+ hotkeys, on the other hand, are primarily meant for system use.
+
+ As a result, when we want Emacs to be able to grab the Win-* keys,
+ we must swallow all Win key presses in a low-level keyboard hook.
+ Unfortunately, thismeans that the Emacs window procedure (and
+ console input handler) never see the keypresses either. Thus, to
+ check the modifier states properly, Emacs code must use the
+ check_w32_winkey_state function that uses the flags directly
+ updated by the hook callback.
+
+ The keyboard hook sees keyboard input on all processes (except
+ elevated ones, when Emacs itself is not elevated). As such, care
+ must be taken to only filter out keyboard input when Emacs itself
+ is on the foreground.
+ */
+
+#include <string.h>
+#include <config.h>
+#include "lisp.h"
+#include "w32common.h"
+#include "w32term.h"
+#include "w32inevt.h"
+
+static int hook_count = 0; /* counter, if several windows are created */
+static HHOOK hook = NULL; /* hook handle */
+
+static int lwindown = 0; /* Left Windows key currently pressed
(and hooked) */
+static int rwindown = 0; /* Right Windows key currently pressed
(and hooked) */
+static int winsdown = 0; /* Number of handled keys currently
pressed */
+static int send_win_up = 0; /* Pass through the keyup for this
Windows key press? */
+static int suppress_lone = 0; /* Suppress simulated Windows
keydown-keyup for this press? */
+static int winseen = 0; /* Windows keys seen during this press? */
+
+static char alt_hooked[256] = {0}; /* hook Alt+[this key]? */
+static char lwin_hooked[256] = {0}; /* hook left Win+[this key]? */
+static char rwin_hooked[256] = {0}; /* hook right Win+[this key]? */
+
+/* stdin, from w32console.c */
+extern HANDLE keyboard_handle;
+
+/* The Windows keyboard hook callback */
+static LRESULT CALLBACK funhook( int code, WPARAM w, LPARAM l )
+{
+ INPUT inputs[2];
+ HWND focus = GetFocus();
+ int console = 0;
+ KBDLLHOOKSTRUCT const* hs = (KBDLLHOOKSTRUCT*)l;
+
+ if( code < 0 || (hs->flags & LLKHF_INJECTED))
+ {
+ return CallNextHookEx( 0, code, w, l );
+ }
+
+ /* GetFocus() returns a non-NULL window if another application is active,
+ and always for a console Emacs process. For a console Emacs,
determine
+ focus by checking if the current foreground window is the process's
+ console window. */
+ if (focus == NULL)
+ {
+ HWND con = GetConsoleWindow();
+ if (con == GetForegroundWindow())
+ {
+ focus = con;
+ console = 1;
+ }
+ }
+
+ /* First, check hooks for the left and right Windows keys */
+ if( hs->vkCode == VK_LWIN || hs->vkCode == VK_RWIN)
+ {
+ if( focus != NULL && (w == WM_KEYDOWN || w == WM_SYSKEYDOWN))
+ {
+ /* The key is being pressed in an Emacs window */
+ if( hs->vkCode == VK_LWIN && !lwindown )
+ {
+ lwindown = 1;
+ winseen = 1;
+ winsdown++;
+ }
+ else if( hs->vkCode == VK_RWIN && !rwindown )
+ {
+ rwindown = 1;
+ winseen = 1;
+ winsdown++;
+ }
+ /* Returning 1 here drops the keypress without further
processing.
+ If the keypress was allowed to go through, the normal Windows
+ hotkeys would take over. */
+ return 1;
+ }
+ else if( winsdown > 0 && (w == WM_KEYUP || w == WM_SYSKEYUP))
+ {
+ /* A key that has been captured earlier is being released now. */
+ if( hs->vkCode == VK_LWIN && lwindown )
+ {
+ lwindown = 0;
+ winsdown--;
+ }
+ else if( hs->vkCode == VK_RWIN && rwindown )
+ {
+ rwindown = 0;
+ winsdown--;
+ }
+ if( winsdown == 0 && winseen )
+ {
+ if( !suppress_lone )
+ {
+ /* The Windows key was pressed, then released,
without any other
+ key pressed simultaneously. Normally, this opens
the Start
+ menu, but the user can prevent this by setting the
+ w32-pass-[lr]window-to-system variable to NIL. */
+ if (hs->vkCode == VK_LWIN &&
!NILP(Vw32_pass_lwindow_to_system) ||
+ hs->vkCode == VK_RWIN &&
!NILP(Vw32_pass_rwindow_to_system))
+ {
+ /* Not prevented - Simulate the keypress to the
system */
+ memset( inputs, 0, sizeof(inputs));
+ inputs[0].type = INPUT_KEYBOARD;
+ inputs[0].ki.wVk = hs->vkCode;
+ inputs[0].ki.wScan = hs->vkCode;
+ inputs[0].ki.dwFlags = KEYEVENTF_EXTENDEDKEY;
+ inputs[0].ki.time = 0;
+ inputs[1].type = INPUT_KEYBOARD;
+ inputs[1].ki.wVk = hs->vkCode;
+ inputs[1].ki.wScan = hs->vkCode;
+ inputs[1].ki.dwFlags = KEYEVENTF_EXTENDEDKEY |
KEYEVENTF_KEYUP;
+ inputs[1].ki.time = 0;
+ SendInput( 2, inputs, sizeof(INPUT));
+ }
+ else if( focus != NULL )
+ {
+ /* When not passed to system, must simulate
privately to Emacs */
+ PostMessage( focus, WM_SYSKEYDOWN, hs->vkCode, 0 );
+ PostMessage( focus, WM_SYSKEYUP, hs->vkCode, 0 );
+ }
+ }
+ }
+ if( winsdown == 0 )
+ {
+ /* No Windows keys pressed anymore - clear the state flags */
+ suppress_lone = 0;
+ winseen = 0;
+ }
+ if( !send_win_up )
+ {
+ /* Swallow this release message, as not to confuse
applications who
+ did not get to see the original WM_KEYDOWN message
either. */
+ return 1;
+ }
+ send_win_up = 0;
+ }
+ }
+ else if( winsdown > 0 )
+ {
+ /* Some other key was pressed while a captured Win key is down.
+ This is either an Emacs registered hotkey combination, or a
+ system hotkey. */
+ if( lwindown && lwin_hooked[hs->vkCode] ||
+ rwindown && rwin_hooked[hs->vkCode] )
+ {
+ /* Hooked Win-x combination, do not pass the keypress to
Windows */
+ suppress_lone = 1;
+ }
+ else if (!suppress_lone)
+ {
+ /* Unhooked S-x combination; simulate the combination now
+ (will be seen by the system) */
+ memset( inputs, 0, sizeof(inputs));
+ inputs[0].type = INPUT_KEYBOARD;
+ inputs[0].ki.wVk = lwindown ? VK_LWIN : VK_RWIN;
+ inputs[0].ki.wScan = lwindown ? VK_LWIN : VK_RWIN;
+ inputs[0].ki.dwFlags = KEYEVENTF_EXTENDEDKEY;
+ inputs[0].ki.time = 0;
+ inputs[1].type = INPUT_KEYBOARD;
+ inputs[1].ki.wVk = hs->vkCode;
+ inputs[1].ki.wScan = hs->scanCode;
+ inputs[1].ki.dwFlags = (hs->flags & LLKHF_EXTENDED) ?
KEYEVENTF_EXTENDEDKEY : 0;
+ inputs[1].ki.time = 0;
+ SendInput( 2, inputs, sizeof(INPUT));
+ /* Stop processing of this Win sequence here; the
+ corresponding keyup messages will come through the normal
+ channel when the keys are released. */
+ suppress_lone = 1;
+ send_win_up = 1;
+ /* Swallow the original keypress (as we want the Win key
+ down message simulated above to precede this real message). */
+ return 1;
+ }
+ }
+
+ /* Next, handle the registered Alt-* combinations */
+ if( (w == WM_SYSKEYDOWN || w == WM_KEYDOWN) &&
+ alt_hooked[hs->vkCode] &&
+ focus != NULL &&
+ (GetAsyncKeyState(VK_MENU) & 0x8000))
+ {
+ /* Prevent the system from getting this Alt-* key - suppress the
+ message and post as a normal keypress to Emacs.
+
+ For some reason, in case of the Alt-escape combination on a
+ console Emacs, the console process swallows the escape
+ keydown message here. Simulating the keypress with SendInput
+ just gives the standard functionality of sending the window
+ to back. So, to make Alt-escape to work on a console Emacs,
+ the keypress would need to be communicated directly from here
+ to the key_event functionality in w32inevt.c; but this hook
+ code does not run on the same thread as the normal keyboard
+ input code, so proper synchronization should be done. As
+ this is quite a corner case, it is left unimplemented for
+ now. */
+ if( console )
+ {
+ INPUT_RECORD rec;
+ DWORD n;
+ rec.EventType = KEY_EVENT;
+ rec.Event.KeyEvent.bKeyDown = TRUE;
+ rec.Event.KeyEvent.wVirtualKeyCode = hs->vkCode;
+ rec.Event.KeyEvent.wVirtualScanCode = hs->scanCode;
+ rec.Event.KeyEvent.uChar.UnicodeChar = 0;
+ rec.Event.KeyEvent.dwControlKeyState =
+ ((GetAsyncKeyState(VK_LMENU) & 0x8000) ? LEFT_ALT_PRESSED :
0) |
+ ((GetAsyncKeyState(VK_RMENU) & 0x8000) ? RIGHT_ALT_PRESSED
: 0) |
+ ((GetAsyncKeyState(VK_LCONTROL) & 0x8000) ?
LEFT_CTRL_PRESSED : 0) |
+ ((GetAsyncKeyState(VK_RCONTROL) & 0x8000) ?
RIGHT_CTRL_PRESSED : 0) |
+ ((GetAsyncKeyState(VK_SHIFT) & 0x8000) ? SHIFT_PRESSED : 0) |
+ ((hs->flags & LLKHF_EXTENDED) ? ENHANCED_KEY : 0);
+ if( w32_console_unicode_input )
+ WriteConsoleInputW(keyboard_handle, &rec, 1, &n);
+ else
+ WriteConsoleInputA(keyboard_handle, &rec, 1, &n);
+ }
+ else
+ PostMessage( focus, w, hs->vkCode, 1 | (1<<29));
+ return 1;
+ }
+
+ /* The normal case - pass the message through */
+ return CallNextHookEx( 0, code, w, l );
+}
+
+/* Set up the hook; can be called several times, with matching
+ remove_w32_kbdhook calls. */
+void setup_w32_kbdhook (void)
+{
+ hook_count++;
+ if (hook_count == 1)
+ {
+ hook = SetWindowsHookEx( WH_KEYBOARD_LL, funhook,
GetModuleHandle(NULL), 0 );
+ }
+}
+
+/* Remove the hook */
+void remove_w32_kbdhook (void)
+{
+ hook_count--;
+ if (hook_count == 0)
+ {
+ UnhookWindowsHookEx( hook );
+ hook = NULL;
+ }
+}
+
+/* Check the current Win key pressed state - see the discussion at the
+ top of the file for the rationale */
+int check_w32_winkey_state (int vkey)
+{
+ switch (vkey)
+ {
+ case VK_LWIN: return lwindown;
+ case VK_RWIN: return rwindown;
+ }
+ return 0;
+}
+
+/* Mark a specific key combination as hooked, preventing it to be
+ handled by the system */
+void hook_w32_key (int hook, int modifier, int vkey)
+{
+ char* tbl = NULL;
+
+ switch(modifier)
+ {
+ case VK_MENU: tbl = alt_hooked; break;
+ case VK_LWIN: tbl = lwin_hooked; break;
+ case VK_RWIN: tbl = rwin_hooked; break;
+ }
+
+ if (tbl != NULL && vkey >= 0 && vkey <= 255)
+ {
+ /* VK_ANY hooks all keys for this modifier */
+ if (vkey == VK_ANY)
+ memset(tbl, (char)hook, 256);
+ else
+ tbl[vkey] = (char)hook;
+ /* Alt-<modifier>s should go through */
+ alt_hooked[VK_MENU] = 0;
+ alt_hooked[VK_LMENU] = 0;
+ alt_hooked[VK_RMENU] = 0;
+ alt_hooked[VK_CONTROL] = 0;
+ alt_hooked[VK_LCONTROL] = 0;
+ alt_hooked[VK_RCONTROL] = 0;
+ alt_hooked[VK_SHIFT] = 0;
+ alt_hooked[VK_LSHIFT] = 0;
+ alt_hooked[VK_RSHIFT] = 0;
+ }
+}
+
+/* Resets the keyboard hook state. Locking the workstation with Win-L
+ leaves the Win key(s) "down" from the hook's point of view - the
+ keyup event is never seen. Thus, this function must be called when
+ the system is locked. */
+void reset_w32_kbdhook_state (void)
+{
+ lwindown = 0;
+ rwindown = 0;
+ winsdown = 0;
+ send_win_up = 0;
+ suppress_lone = 0;
+ winseen = 0;
+}
diff --git a/src/w32inevt.c b/src/w32inevt.c
index db2e218..931fb20 100644
--- a/src/w32inevt.c
+++ b/src/w32inevt.c
@@ -147,12 +147,6 @@ key_event (KEY_EVENT_RECORD *event, struct
input_event *emacs_ev, int *isdead)
{
switch (event->wVirtualKeyCode)
{
- case VK_LWIN:
- mod_key_state &= ~LEFT_WIN_PRESSED;
- break;
- case VK_RWIN:
- mod_key_state &= ~RIGHT_WIN_PRESSED;
- break;
case VK_APPS:
mod_key_state &= ~APPS_PRESSED;
break;
@@ -185,7 +179,6 @@ key_event (KEY_EVENT_RECORD *event, struct
input_event *emacs_ev, int *isdead)
keybd_event (faked_key, (BYTE) MapVirtualKey (faked_key, 0), 0,
0);
}
}
- mod_key_state |= LEFT_WIN_PRESSED;
if (!NILP (Vw32_lwindow_modifier))
return 0;
break;
@@ -201,7 +194,6 @@ key_event (KEY_EVENT_RECORD *event, struct
input_event *emacs_ev, int *isdead)
keybd_event (faked_key, (BYTE) MapVirtualKey (faked_key, 0), 0,
0);
}
}
- mod_key_state |= RIGHT_WIN_PRESSED;
if (!NILP (Vw32_rwindow_modifier))
return 0;
break;
@@ -267,6 +259,10 @@ key_event (KEY_EVENT_RECORD *event, struct
input_event *emacs_ev, int *isdead)
/* Recognize state of Windows and Apps keys. */
event->dwControlKeyState |= mod_key_state;
+ if (check_w32_winkey_state(VK_LWIN))
+ event->dwControlKeyState |= LEFT_WIN_PRESSED;
+ if (check_w32_winkey_state(VK_RWIN))
+ event->dwControlKeyState |= RIGHT_WIN_PRESSED;
/* Distinguish numeric keypad keys from extended keys. */
event->wVirtualKeyCode =
diff --git a/src/w32term.h b/src/w32term.h
index 3377b53..8432843 100644
--- a/src/w32term.h
+++ b/src/w32term.h
@@ -738,6 +738,12 @@ extern int handle_file_notifications (struct
input_event *);
extern void w32_initialize_display_info (Lisp_Object);
extern void initialize_w32_display (struct terminal *, int *, int *);
+extern void setup_w32_kbdhook (void);
+extern void remove_w32_kbdhook (void);
+extern void hook_w32_key (int hook, int modifier, int vkey);
+extern int check_w32_winkey_state (int vkey);
+extern void reset_w32_kbdhook_state (void);
+
/* Keypad command key support. W32 doesn't have virtual keys defined
for the function keys on the keypad (they are mapped to the standard
function keys), so we define our own. */
@@ -762,6 +768,10 @@ extern void initialize_w32_display (struct terminal
*, int *, int *);
#define VK_APPS 0x5D
#endif
+/* Special virtual key code for indicating "any" key. This can be used
+ as an argument to the hook_w32_key function. */
+#define VK_ANY 0xFF
+
/* Support for treating Windows and Apps keys as modifiers. These
constants must not overlap with any of the dwControlKeyState flags in
KEY_EVENT_RECORD. */
--
2.6.2
- RE: [PATCH] Override Windows default Win-* key combinations when using Emacs, (continued)
- RE: [PATCH] Override Windows default Win-* key combinations when using Emacs, Herring, Davis, 2016/01/05
- Re: [PATCH] Override Windows default Win-* key combinations when using Emacs, Eli Zaretskii, 2016/01/05
- Re: [PATCH] Override Windows default Win-* key combinations when using Emacs, Jussi Lahdenniemi, 2016/01/05
- Re: [PATCH] Override Windows default Win-* key combinations when using Emacs,
Jussi Lahdenniemi <=
- Re: [PATCH] Override Windows default Win-* key combinations when using Emacs, Eli Zaretskii, 2016/01/12
- Re: [PATCH] Override Windows default Win-* key combinations when using Emacs, Jussi Lahdenniemi, 2016/01/13
- Re: [PATCH] Override Windows default Win-* key combinations when using Emacs, Eli Zaretskii, 2016/01/13
- Re: [PATCH] Override Windows default Win-* key combinations when using Emacs, Jussi Lahdenniemi, 2016/01/13
- Re: [PATCH] Override Windows default Win-* key combinations when using Emacs, Jussi Lahdenniemi, 2016/01/13
- Re: [PATCH] Override Windows default Win-* key combinations when using Emacs, Eli Zaretskii, 2016/01/13