From: Jussi Lahdenniemi <jussi@aprikoodi.fi>
To: emacs-devel@gnu.org
Subject: [PATCH] Override Windows default Win-* key combinations when using Emacs
Date: Tue, 5 Jan 2016 14:51:36 +0200 [thread overview]
Message-ID: <568BBC58.50702@aprikoodi.fi> (raw)
Hello,
I finally got around to submitting a patch I wrote some four years ago
that enables Emacs users on Windows to override the default Windows key
combinations reserved by the operating system. Especially the newer
Windowses (7, 8, 10) define quite a large number of Win+* hotkeys to a
variety of shell functions, restricting the available S-* keys on Emacs.
This is accomplished by running an external process (supersuper.exe)
that captures keypresses, suppressing unwanted ones from the system, and
informing the currently active Emacs process about them as necessary.
All Win key combinations are thus blocked and made available for Emacs,
except Win+L which is handled on a lower level of the operating system
and cannot be intercepted.
The feature is enabled by executing (w32-supersuper-run t). nil as the
argument disables the functionality.
Being new to Emacs-devel, I am not sure this is the way to go with patch
submissions, but I have attached the git format-patch output below. Note
that near the end of the file the "^L" should be replaced with a real ^L
before applying the patch.
Please let me know if something is amiss.
---
nt/Makefile.in | 5 +-
nt/supersuper.c | 236
++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/w32fns.c | 49 ++++++++++++
3 files changed, 289 insertions(+), 1 deletion(-)
create mode 100644 nt/supersuper.c
diff --git a/nt/Makefile.in b/nt/Makefile.in
index fc6887f..658edb7 100644
--- a/nt/Makefile.in
+++ b/nt/Makefile.in
@@ -135,7 +135,7 @@ MKDIR_P = @MKDIR_P@
# ========================== Lists of Files ===========================
# Things that a user might actually run, which should be installed in
bindir.
-INSTALLABLES = runemacs${EXEEXT} addpm${EXEEXT}
+INSTALLABLES = runemacs${EXEEXT} addpm${EXEEXT} supersuper${EXEEXT}
# Things that Emacs runs internally, which should not be installed in
bindir.
UTILITIES = cmdproxy${EXEEXT} ddeclient${EXEEXT}
@@ -242,6 +242,9 @@ cmdproxy${EXEEXT}: ${srcdir}/cmdproxy.c
runemacs${EXEEXT}: ${srcdir}/runemacs.c $(EMACSRES)
$(AM_V_CCLD)$(CC) ${ALL_CFLAGS} $^ -mwindows -o $@
+supersuper${EXEEXT}: ${srcdir}/supersuper.c ../src/epaths.h
+ $(AM_V_CCLD)$(CC) ${ALL_CFLAGS} $< -o $@
+
## Also used in ../src/Makefile.
emacs.res ../src/emacs.res: emacs.rc ${srcdir}/icons/emacs.ico \
${srcdir}/icons/hand.cur ${srcdir}/$(EMACS_MANIFEST)
diff --git a/nt/supersuper.c b/nt/supersuper.c
new file mode 100644
index 0000000..2f3fd9b
--- /dev/null
+++ b/nt/supersuper.c
@@ -0,0 +1,236 @@
+/**
+ * @file supersuper.c
+ * @author Jussi Lahdenniemi <jussi@aprikoodi.fi>
+ * @date 2011-09-30 23:54
+ *
+ * @brief supersuper keyboard hook
+ *
+ * Hooks the keyboard, provides supersuper services to emacs.
+ */
+
+#define STRICT
+#ifdef _WIN32_WINNT
+# undef _WIN32_WINNT
+#endif
+#define _WIN32_WINNT 0x0501
+#include <windows.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifndef WM_WTSSESSION_CHANGE
+# define WM_WTSSESSION_CHANGE 0x02B1
+# define WTS_SESSION_LOCK 0x7
+#endif
+
+static HANDLE h_quit; /**< Event handle: quit the hook
executable */
+static HANDLE h_lwin; /**< Event handle: left Windows key
pressed */
+static HANDLE h_rwin; /**< Event handle: right Windows key
pressed */
+
+static int lwindown = 0; /**< Left Windows key currently pressed */
+static int rwindown = 0; /**< Right Windows key currently pressed */
+static int capsdown = 0; /**< Caps lock currently pressed */
+static int winsdown = 0; /**< Number of handled keys currently
pressed */
+static int suppress = 0; /**< Suppress Windows keyup for this
press? */
+static int winseen = 0; /**< Windows keys seen during this
press? */
+
+/**
+ * Determines whether an Emacs is currently on the foreground.
+ *
+ * @return nonzero if Emacs, zero if not.
+ */
+static int emacsp()
+{
+ HWND fg = GetForegroundWindow();
+ if( fg != 0 )
+ {
+ TCHAR cls[16];
+ GetClassName( fg, cls, 16 );
+ return memcmp( cls, TEXT("Emacs"), sizeof(TEXT("Emacs"))) == 0;
+ }
+ return 0;
+}
+
+/**
+ * The keyboard hook function.
+ *
+ * @param code Negative -> call next hook
+ * @param w Keyboard message ID
+ * @param l KBDLLHOOKSTRUCT'
+ *
+ * @return nonzero to terminate processing
+ */
+static LRESULT CALLBACK funhook( int code, WPARAM w, LPARAM l )
+{
+ KBDLLHOOKSTRUCT const* hs = (KBDLLHOOKSTRUCT*)l;
+ if( code < 0 || (hs->flags & LLKHF_INJECTED))
+ {
+ return CallNextHookEx( 0, code, w, l );
+ }
+
+ if( hs->vkCode == VK_LWIN ||
+ hs->vkCode == VK_RWIN ||
+ hs->vkCode == VK_CAPITAL )
+ {
+ if( emacsp() && w == WM_KEYDOWN )
+ {
+ /* pressing key in emacs */
+ if( hs->vkCode == VK_LWIN && !lwindown )
+ {
+ SetEvent( h_lwin );
+ lwindown = 1;
+ winseen = 1;
+ winsdown++;
+ }
+ else if( hs->vkCode == VK_RWIN && !rwindown )
+ {
+ SetEvent( h_rwin );
+ rwindown = 1;
+ winseen = 1;
+ winsdown++;
+ }
+ else if( hs->vkCode == VK_CAPITAL && !capsdown )
+ {
+ SetEvent( h_lwin );
+ capsdown = 1;
+ winsdown++;
+ }
+ return 1;
+ }
+ else if( winsdown > 0 && w == WM_KEYUP )
+ {
+ /* releasing captured key */
+ if( hs->vkCode == VK_LWIN && lwindown )
+ {
+ lwindown = 0;
+ winsdown--;
+ if( !capsdown ) ResetEvent( h_lwin );
+ }
+ else if( hs->vkCode == VK_RWIN && rwindown )
+ {
+ rwindown = 0;
+ winsdown--;
+ ResetEvent( h_rwin );
+ }
+ else if( hs->vkCode == VK_CAPITAL && capsdown )
+ {
+ capsdown = 0;
+ winsdown--;
+ if( !lwindown ) ResetEvent( h_lwin );
+ }
+ if( winsdown == 0 && !suppress && winseen )
+ {
+ /* Releasing Win without other keys inbetween */
+ INPUT inputs[2];
+ 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));
+ }
+ if( winsdown == 0 )
+ {
+ suppress = 0;
+ winseen = 0;
+ }
+ return 1;
+ }
+ }
+ else if( winsdown > 0 )
+ {
+ /* S-? combination detected, do not pass keypress to Windows */
+ suppress = 1;
+ }
+ return CallNextHookEx( 0, code, w, l );
+}
+
+/**
+ * Window procedure for the event listener window.
+ *
+ * @param wnd Window handle
+ * @param msg Message
+ * @param wparam Parameter
+ * @param lparam Parameter
+ *
+ * @return Message result
+ */
+static LRESULT WINAPI wndproc( HWND wnd, UINT msg, WPARAM wparam,
LPARAM lparam )
+{
+ if( msg == WM_WTSSESSION_CHANGE && wparam == WTS_SESSION_LOCK )
+ {
+ /* Clear keypress status on lock event - otherwise, when
+ the user presses Win+L to lock the workstation with
+ emacs as the foreground application, the Windows key
+ gets "stuck down" and after unlock all keys result in
+ S-* key combinations until Win is pressed and released. */
+ lwindown = 0;
+ rwindown = 0;
+ capsdown = 0;
+ winsdown = 0;
+ suppress = 0;
+ winseen = 0;
+ }
+ return DefWindowProc( wnd, msg, wparam, lparam );
+}
+
+/**
+ * Main function for the application.
+ *
+ * @param inst Instance handle
+ * @param HINSTANCE Not used
+ * @param LPSTR Not used
+ * @param int Not used
+ *
+ * @return Process exit code
+ */
+int CALLBACK WinMain( HINSTANCE inst, HINSTANCE prev, LPSTR args, int
cmdshow )
+{
+ MSG msg;
+ HHOOK hook;
+ WNDCLASSEX wcex;
+ HWND wnd;
+
+ (void)prev; (void)args; (void)cmdshow;
+
+ h_quit = CreateEvent( 0, TRUE, FALSE, "supersuper.quit" );
+ if( GetLastError() == ERROR_ALREADY_EXISTS )
+ {
+ /* do not run twice */
+ CloseHandle( h_quit );
+ return 0;
+ }
+ h_lwin = CreateEvent( 0, TRUE, FALSE, "supersuper.left" );
+ h_rwin = CreateEvent( 0, TRUE, FALSE, "supersuper.right" );
+ hook = SetWindowsHookEx( WH_KEYBOARD_LL, funhook, inst, 0 );
+
+ /* Create a dummy window so that we receive WM_WTSESSION_CHANGE
messages */
+ memset( &wcex, 0, sizeof(WNDCLASSEX));
+ wcex.cbSize = sizeof(wcex);
+ wcex.lpfnWndProc = wndproc;
+ wcex.hInstance = inst;
+ wcex.lpszClassName = "sswc";
+ RegisterClassEx( &wcex );
+ wnd = CreateWindow( "sswc", "", WS_POPUP, 0, 0, 0, 0, 0, 0, inst, 0 );
+
+ while( MsgWaitForMultipleObjects( 1, &h_quit, FALSE, INFINITE,
QS_ALLINPUT ) != WAIT_OBJECT_0 )
+ {
+ while( PeekMessage( &msg, 0, 0, 0, PM_REMOVE ))
+ {
+ TranslateMessage( &msg );
+ DispatchMessage( &msg );
+ }
+ }
+
+ UnhookWindowsHookEx( hook );
+ CloseHandle( h_lwin );
+ CloseHandle( h_rwin );
+ CloseHandle( h_quit );
+ DestroyWindow( wnd );
+ return 0;
+}
diff --git a/src/w32fns.c b/src/w32fns.c
index c1d9bff..8140f35 100644
--- a/src/w32fns.c
+++ b/src/w32fns.c
@@ -2218,6 +2218,20 @@ sync_modifiers (void)
}
static int
+supersuper_winkeystate (int vkey)
+{
+ static HANDLE evh_left = NULL;
+ static HANDLE evh_right = NULL;
+ HANDLE* h = (vkey == VK_RWIN) ? &evh_right : &evh_left;
+ if (*h == NULL)
+ *h = OpenEvent (SYNCHRONIZE, TRUE,
+ (vkey == VK_RWIN) ? "supersuper.right" :
"supersuper.left");
+ if (*h == NULL)
+ return (GetKeyState (vkey) & 0x8000);
+ return WaitForSingleObject (*h, 0) == WAIT_OBJECT_0;
+}
+
+static int
modifier_set (int vkey)
{
/* Warning: The fact that VK_NUMLOCK is not treated as the other 2
@@ -2248,6 +2262,8 @@ modifier_set (int vkey)
else
return (GetKeyState (vkey) & 0x1);
}
+ if (vkey == VK_LWIN || vkey == VK_RWIN)
+ return supersuper_winkeystate(vkey);
if (!modifiers_recorded)
return (GetKeyState (vkey) & 0x8000);
@@ -8223,6 +8239,38 @@ The following %-sequences are provided:
return status;
}
+DEFUN ("w32-supersuper-run", Fw32_supersuper_run, Sw32_supersuper_run,
1, 1, 0,
+ doc: /* Control running of the supersuper keyboard hook application.
+Specify nil as RUN to terminate supersuper, non-nil to start it.
+Returns t if the operation succeeds, nil if it fails. */)
+ (run)
+ Lisp_Object run;
+{
+ if (NILP (run))
+ {
+ /* Terminate supersuper by setting the quit event */
+ HANDLE quit = OpenEvent (EVENT_MODIFY_STATE, TRUE,
"supersuper.quit");
+ if (quit == NULL)
+ return Qnil;
+ SetEvent (quit);
+ CloseHandle (quit);
+ return Qt;
+ }
+ else
+ {
+ /* Start up the supersuper app */
+ STARTUPINFO sui;
+ PROCESS_INFORMATION pi;
+ sui.cb = sizeof(STARTUPINFO);
+ GetStartupInfo (&sui);
+ if (!CreateProcess (NULL, "supersuper.exe", NULL, NULL, TRUE,0,
NULL, NULL, &sui, &pi))
+ return Qnil;
+ CloseHandle (pi.hProcess);
+ CloseHandle (pi.hThread);
+ return Qt;
+ }
+}
+
^L
#ifdef WINDOWSNT
typedef BOOL (WINAPI *GetDiskFreeSpaceExW_Proc)
@@ -9628,6 +9676,7 @@ This variable has effect only on Windows Vista and
later. */);
defsubr (&Sw32_window_exists_p);
defsubr (&Sw32_battery_status);
defsubr (&Sw32__menu_bar_in_use);
+ defsubr (&Sw32_supersuper_run);
#if defined WINDOWSNT && !defined HAVE_DBUS
defsubr (&Sw32_notification_notify);
defsubr (&Sw32_notification_close);
--
2.6.2
next reply other threads:[~2016-01-05 12:51 UTC|newest]
Thread overview: 63+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-01-05 12:51 Jussi Lahdenniemi [this message]
2016-01-05 13:54 ` [PATCH] Override Windows default Win-* key combinations when using Emacs Herring, Davis
2016-01-05 17:05 ` Eli Zaretskii
2016-01-05 19:32 ` Jussi Lahdenniemi
2016-01-05 17:05 ` Eli Zaretskii
2016-01-05 19:03 ` Rasmus
2016-01-05 19:17 ` Eli Zaretskii
2016-01-05 19:31 ` Jussi Lahdenniemi
[not found] ` <<83d1tf4z0h.fsf@gnu.org>
2016-01-05 19:45 ` Drew Adams
2016-01-05 19:41 ` Jussi Lahdenniemi
2016-01-05 20:01 ` Eli Zaretskii
2016-01-09 19:58 ` Jussi Lahdenniemi
2016-01-11 18:54 ` Eli Zaretskii
2016-01-12 11:16 ` Jussi Lahdenniemi
2016-01-12 15:08 ` covici
2016-01-13 5:07 ` Jussi Lahdenniemi
2016-01-12 15:54 ` Eli Zaretskii
2016-01-13 5:31 ` Jussi Lahdenniemi
2016-01-13 15:49 ` Eli Zaretskii
2016-01-13 9:16 ` Jussi Lahdenniemi
2016-01-13 11:28 ` Jussi Lahdenniemi
2016-01-13 15:57 ` Eli Zaretskii
2016-01-14 12:49 ` Jussi Lahdenniemi
2016-01-14 17:10 ` Jussi Lahdenniemi
2016-01-14 18:07 ` Eli Zaretskii
2016-01-14 18:15 ` Eli Zaretskii
2016-01-15 6:56 ` Jussi Lahdenniemi
2016-01-15 7:52 ` Jussi Lahdenniemi
2016-01-15 8:08 ` Windows 9X crash (was: [PATCH] Override Windows default Win-* key combinations when using Emacs) Eli Zaretskii
2016-01-15 9:35 ` Windows 9X crash Jussi Lahdenniemi
2016-01-15 10:18 ` Eli Zaretskii
2016-01-15 10:31 ` Fabrice Popineau
2016-01-15 10:33 ` Jussi Lahdenniemi
2016-01-16 9:15 ` Eli Zaretskii
2016-01-15 9:47 ` Windows 9X crash (was: [PATCH] Override Windows default Win-* key combinations when using Emacs) Fabrice Popineau
2016-01-15 9:56 ` Windows 9X crash Jussi Lahdenniemi
2016-01-15 10:19 ` Eli Zaretskii
2016-01-15 9:23 ` [PATCH] Override Windows default Win-* key combinations when using Emacs Yuri Khan
2016-01-16 9:58 ` Eli Zaretskii
2016-01-16 11:00 ` Jussi Lahdenniemi
2016-01-16 11:41 ` Eli Zaretskii
2016-02-03 13:59 ` Jussi Lahdenniemi
2016-02-03 15:42 ` Eli Zaretskii
2016-02-26 10:55 ` Eli Zaretskii
2016-02-26 10:59 ` Jussi Lahdenniemi
2016-02-27 11:34 ` Eli Zaretskii
2016-02-28 8:42 ` Paul Eggert
2016-02-28 15:50 ` Eli Zaretskii
2016-02-28 20:03 ` Paul Eggert
2016-02-28 20:10 ` Eli Zaretskii
2016-02-28 23:27 ` Paul Eggert
2016-02-29 3:31 ` Eli Zaretskii
2016-02-29 7:07 ` Paul Eggert
2016-02-29 15:52 ` Eli Zaretskii
2016-02-29 19:01 ` Paul Eggert
2016-03-05 10:16 ` Eli Zaretskii
2016-03-08 2:57 ` Paul Eggert
2016-03-08 4:46 ` Clément Pit--Claudel
2016-02-28 23:33 ` John Wiegley
2016-02-27 2:11 ` Lars Ingebrigtsen
2016-02-27 7:47 ` Eli Zaretskii
2016-02-28 4:40 ` Lars Ingebrigtsen
2016-01-13 15:53 ` Eli Zaretskii
[not found] <<568BBC58.50702@aprikoodi.fi>
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=568BBC58.50702@aprikoodi.fi \
--to=jussi@aprikoodi.fi \
--cc=emacs-devel@gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this external index
https://git.savannah.gnu.org/cgit/emacs.git
https://git.savannah.gnu.org/cgit/emacs/org-mode.git
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.