forked from AuroraMiddleware/gtk
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
This commit is contained in:
parent
ca79296061
commit
eece8a7dd2
@ -147,6 +147,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)
|
||||
@ -295,6 +299,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)
|
||||
{
|
||||
@ -405,6 +527,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
|
||||
@ -2283,6 +2407,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:
|
||||
@ -2362,48 +2490,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;
|
||||
|
Loading…
Reference in New Issue
Block a user