all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
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






             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.