[gtk+] GDK W32: Use keyboard hook to detect AeroSnap combinations better



commit c36d66bdb6fedba0d1c09a772b4eae9c6bcb3b23
Author: Руслан Ижбулатов <lrn1986 gmail com>
Date:   Sat Dec 24 21:01:23 2016 +0000

    GDK W32: Use keyboard hook to detect AeroSnap combinations better
    
    Windows WM handles AeroSnap for normal windows on keydown. We did this
    on keyup only because we do not get a keydown message, even if Windows WM
    does nothing with a combination. However, in some specific cases it DOES
    do something - and we have no way to detect that. Specifically, winkey+downarrow
    causes maximized window to be restored by WM, and GDK fails to detect that. Then
    GDK gets a keyup message, figures that winkey+downarrow was pressed and released,
    and handles the combination - by minimizing the window.
    
    To overcome this, install a low-level keyboard hook (high-level ones have
    the same problem as normal message loop - they don't get messages when
    Windows WM handles combinations) and use it to detect interesting key combinations
    before Windows WM has a chance to block them from being processed.
    
    Once an interesting combination is detected, post a message to the window, which
    will be handled in due order.
    
    It should be noted that this code handles key repetitions in a very crude manner.
    
    The downside is that AeroSnap will not work if hook installation function call fails.
    Also, this is a global hook, and if the hook procedure does something wrong, bad things
    can happen.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=776031

 gdk/win32/gdkevents-win32.c |  170 ++++++++++++++++++++++++++++++++-----------
 1 files changed, 128 insertions(+), 42 deletions(-)
---
diff --git a/gdk/win32/gdkevents-win32.c b/gdk/win32/gdkevents-win32.c
index a84774e..614fa68 100644
--- a/gdk/win32/gdkevents-win32.c
+++ b/gdk/win32/gdkevents-win32.c
@@ -144,6 +144,10 @@ static int debug_indent = 0;
 
 static int both_shift_pressed[2]; /* to store keycodes for shift keys */
 
+/* low-level keyboard hook handle */
+static HHOOK keyboard_hook = NULL;
+static UINT aerosnap_message;
+
 static void
 track_mouse_event (DWORD dwFlags,
                   HWND  hwnd)
@@ -292,6 +296,124 @@ _gdk_win32_window_procedure (HWND   hwnd,
   return retval;
 }
 
+static LRESULT
+low_level_keystroke_handler (WPARAM message,
+                                       KBDLLHOOKSTRUCT *kbdhook,
+                                       GdkWindow *window)
+{
+  GdkWindow *toplevel = gdk_window_get_toplevel (window);
+  static DWORD last_keydown = 0;
+
+  if (message == WM_KEYDOWN &&
+      !GDK_WINDOW_DESTROYED (toplevel) &&
+      _gdk_win32_window_lacks_wm_decorations (toplevel) && /* For CSD only */
+      last_keydown != kbdhook->vkCode &&
+      ((GetKeyState (VK_LWIN) & 0x8000) ||
+      (GetKeyState (VK_RWIN) & 0x8000)))
+       {
+         GdkWin32AeroSnapCombo combo = GDK_WIN32_AEROSNAP_COMBO_NOTHING;
+         gboolean lshiftdown = GetKeyState (VK_LSHIFT) & 0x8000;
+          gboolean rshiftdown = GetKeyState (VK_RSHIFT) & 0x8000;
+          gboolean oneshiftdown = (lshiftdown || rshiftdown) && !(lshiftdown && rshiftdown);
+          gboolean maximized = gdk_window_get_state (toplevel) & GDK_WINDOW_STATE_MAXIMIZED;
+
+         switch (kbdhook->vkCode)
+           {
+           case VK_UP:
+             combo = GDK_WIN32_AEROSNAP_COMBO_UP;
+             break;
+           case VK_DOWN:
+             combo = GDK_WIN32_AEROSNAP_COMBO_DOWN;
+             break;
+           case VK_LEFT:
+             combo = GDK_WIN32_AEROSNAP_COMBO_LEFT;
+             break;
+           case VK_RIGHT:
+             combo = GDK_WIN32_AEROSNAP_COMBO_RIGHT;
+             break;
+           }
+
+         if (oneshiftdown && combo != GDK_WIN32_AEROSNAP_COMBO_NOTHING)
+           combo += 4;
+
+         /* These are the only combos that Windows WM does handle for us */
+         if (combo == GDK_WIN32_AEROSNAP_COMBO_SHIFTLEFT ||
+              combo == GDK_WIN32_AEROSNAP_COMBO_SHIFTRIGHT)
+            combo = GDK_WIN32_AEROSNAP_COMBO_NOTHING;
+
+          /* On Windows 10 the WM will handle this specific combo */
+          if (combo == GDK_WIN32_AEROSNAP_COMBO_DOWN && maximized &&
+              g_win32_check_windows_version (6, 4, 0, G_WIN32_OS_ANY))
+            combo = GDK_WIN32_AEROSNAP_COMBO_NOTHING;
+
+         if (combo != GDK_WIN32_AEROSNAP_COMBO_NOTHING)
+            PostMessage (GDK_WINDOW_HWND (toplevel), aerosnap_message, (WPARAM) combo, 0);
+       }
+
+  if (message == WM_KEYDOWN)
+    last_keydown = kbdhook->vkCode;
+  else if (message = WM_KEYUP && last_keydown == kbdhook->vkCode)
+    last_keydown = 0;
+
+  return 0;
+}
+
+static LRESULT CALLBACK
+low_level_keyboard_proc (int    code,
+                         WPARAM wParam,
+                         LPARAM lParam)
+{
+  KBDLLHOOKSTRUCT *kbdhook;
+  HWND kbd_focus_owner;
+  GdkWindow *gdk_kbd_focus_owner;
+  LRESULT chain;
+
+  do
+  {
+    if (code < 0)
+      break;
+
+    kbd_focus_owner = GetFocus ();
+
+    if (kbd_focus_owner == NULL)
+      break;
+
+    gdk_kbd_focus_owner = gdk_win32_handle_table_lookup (kbd_focus_owner);
+
+    if (gdk_kbd_focus_owner == NULL)
+      break;
+
+    kbdhook = (KBDLLHOOKSTRUCT *) lParam;
+    chain = low_level_keystroke_handler (wParam, kbdhook, gdk_kbd_focus_owner);
+
+    if (chain != 0)
+      return chain;
+  } while (FALSE);
+
+  return CallNextHookEx (0, code, wParam, lParam);
+}
+
+static void
+set_up_low_level_keyboard_hook (void)
+{
+  HHOOK hook_handle;
+
+  if (keyboard_hook != NULL)
+    return;
+
+  hook_handle = SetWindowsHookEx (WH_KEYBOARD_LL,
+                                  (HOOKPROC) low_level_keyboard_proc,
+                                  _gdk_dll_hinstance,
+                                  0);
+
+  if (hook_handle != NULL)
+    keyboard_hook = hook_handle;
+  else
+    WIN32_API_FAILED ("SetWindowsHookEx");
+
+  aerosnap_message = RegisterWindowMessage ("GDK_WIN32_AEROSNAP_MESSAGE");
+}
+
 void
 _gdk_events_init (GdkDisplay *display)
 {
@@ -402,6 +524,8 @@ _gdk_events_init (GdkDisplay *display)
   g_source_add_poll (source, &event_source->event_poll_fd);
   g_source_set_can_recurse (source, TRUE);
   g_source_attach (source, NULL);
+
+  set_up_low_level_keyboard_hook ();
 }
 
 gboolean
@@ -2279,6 +2403,10 @@ gdk_event_translate (MSG  *msg,
        }
     }
 
+  if (msg->message == aerosnap_message)
+    _gdk_win32_window_handle_aerosnap (gdk_window_get_toplevel (window),
+                                       (GdkWin32AeroSnapCombo) msg->wParam);
+
   switch (msg->message)
     {
     case WM_INPUTLANGCHANGE:
@@ -2358,48 +2486,6 @@ gdk_event_translate (MSG  *msg,
 
       impl = GDK_WINDOW_IMPL_WIN32 (window->impl);
 
-      if (msg->message == WM_KEYUP &&
-          !GDK_WINDOW_DESTROYED (gdk_window_get_toplevel (window)) &&
-          _gdk_win32_window_lacks_wm_decorations (gdk_window_get_toplevel (window)) && /* For CSD only */
-          ((GetKeyState (VK_LWIN) & 0x8000) ||
-           (GetKeyState (VK_RWIN) & 0x8000)))
-       {
-         GdkWin32AeroSnapCombo combo = GDK_WIN32_AEROSNAP_COMBO_NOTHING;
-         gboolean lshiftdown = GetKeyState (VK_LSHIFT) & 0x8000;
-          gboolean rshiftdown = GetKeyState (VK_RSHIFT) & 0x8000;
-          gboolean oneshiftdown = (lshiftdown || rshiftdown) && !(lshiftdown && rshiftdown);
-
-         switch (msg->wParam)
-           {
-           case VK_UP:
-             combo = GDK_WIN32_AEROSNAP_COMBO_UP;
-             break;
-           case VK_DOWN:
-             combo = GDK_WIN32_AEROSNAP_COMBO_DOWN;
-             break;
-           case VK_LEFT:
-             combo = GDK_WIN32_AEROSNAP_COMBO_LEFT;
-             break;
-           case VK_RIGHT:
-             combo = GDK_WIN32_AEROSNAP_COMBO_RIGHT;
-             break;
-           }
-
-         if (oneshiftdown && combo != GDK_WIN32_AEROSNAP_COMBO_NOTHING)
-           combo += 4;
-
-         /* These are the only combos that Windows WM does handle for us */
-         if (combo == GDK_WIN32_AEROSNAP_COMBO_SHIFTLEFT ||
-              combo == GDK_WIN32_AEROSNAP_COMBO_SHIFTRIGHT)
-            combo = GDK_WIN32_AEROSNAP_COMBO_NOTHING;
-
-         if (combo != GDK_WIN32_AEROSNAP_COMBO_NOTHING)
-           {
-             _gdk_win32_window_handle_aerosnap (gdk_window_get_toplevel (window), combo);
-             break;
-           }
-       }
-
       API_CALL (GetKeyboardState, (key_state));
 
       ccount = 0;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]