[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH] Override Windows default Win-* key combinations when using Emacs
From: |
Jussi Lahdenniemi |
Subject: |
[PATCH] Override Windows default Win-* key combinations when using Emacs |
Date: |
Tue, 5 Jan 2016 14:51:36 +0200 |
User-agent: |
Mozilla/5.0 (Windows NT 10.0; WOW64; rv:38.0) Gecko/20100101 Thunderbird/38.5.0 |
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 <address@hidden>
+ * @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
- [PATCH] Override Windows default Win-* key combinations when using Emacs,
Jussi Lahdenniemi <=