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: [PATCH] Override Windows default Win-* key combinations when using Emacs Date: Tue, 5 Jan 2016 14:51:36 +0200 Organization: Aprikoodi Oy Message-ID: <568BBC58.50702@aprikoodi.fi> NNTP-Posting-Host: plane.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 7bit X-Trace: ger.gmane.org 1451998346 9119 80.91.229.3 (5 Jan 2016 12:52:26 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Tue, 5 Jan 2016 12:52:26 +0000 (UTC) To: emacs-devel@gnu.org Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Tue Jan 05 13:52:16 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 1aGR5y-0007BL-H9 for ged-emacs-devel@m.gmane.org; Tue, 05 Jan 2016 13:52:14 +0100 Original-Received: from localhost ([::1]:49263 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1aGR5x-0006Qa-R5 for ged-emacs-devel@m.gmane.org; Tue, 05 Jan 2016 07:52:13 -0500 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:60907) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1aGR5g-0006QB-4k for emacs-devel@gnu.org; Tue, 05 Jan 2016 07:51:57 -0500 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1aGR5c-0000oF-QY for emacs-devel@gnu.org; Tue, 05 Jan 2016 07:51:56 -0500 Original-Received: from nbjjceehcbh.turbo-smtp.net ([199.244.72.17]:26099) by eggs.gnu.org with smtp (Exim 4.71) (envelope-from ) id 1aGR5c-0000oB-Bb for emacs-devel@gnu.org; Tue, 05 Jan 2016 07:51:52 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=aprikoodi.fi; s=turbo-smtp; x=1452603112; h=DomainKey-Signature: Received:Received:To:From:Subject:Organization:Message-ID:Date: User-Agent:MIME-Version:Content-Type:Content-Transfer-Encoding; bh=rpOcuF9WeHM2ggTAup80qXFdt8rnevH8RTwzQF99kfA=; b=R6cwUJ5KtKqWW AqY3FRgMEcRlOm8zdYqmOHSZgq2cKMy0XRNh6UvIm5UyEYd/9NycZOmQr0yafA1t eqhA2q+FaVKPYjB3Lja9bekiY6sBy32MS0yfW4L3HAKW7OFszUFRaZROzcTyeKGE T08jOogVW1M/uMSLkJY0adEJkRAZoA= DomainKey-Signature: a=rsa-sha1; q=dns; c=nofws; s=turbo-smtp; d=aprikoodi.fi; h=Received:Received:X-TurboSMTP-Tracking:To:From:Subject:Organization:Message-ID:Date:User-Agent:MIME-Version:Content-Type:Content-Transfer-Encoding; b=bfKcwiZ87vgXi+EyI+BT7SdqXFeONVy/Q9kmqDDru7XiqSsI/SwtJZ93tWCcqu xjM2D5VgdSrKMEw/qbISNuDeNpgTD+WKcZWZhoLdYT/UuAVNoGSzIngB4fIFSK2t s+Ln2o+hkW/mnapqvwEWNwTL0Mdj+2Y7+3NAXNXtP46SY=; Original-Received: (qmail 2517 invoked from network); 5 Jan 2016 12:51:49 -0000 Original-Received: from unknown (HELO ?192.168.100.12?) (authenticated@109.240.10.34) by turbo-smtp.com with SMTP; 5 Jan 2016 12:51:49 -0000 X-TurboSMTP-Tracking: 2715999759 User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:38.0) Gecko/20100101 Thunderbird/38.5.0 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:197653 Archived-At: 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 + * @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 +#include +#include + +#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