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 */
|
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
|
static void
|
||||||
track_mouse_event (DWORD dwFlags,
|
track_mouse_event (DWORD dwFlags,
|
||||||
HWND hwnd)
|
HWND hwnd)
|
||||||
@ -295,6 +299,124 @@ _gdk_win32_window_procedure (HWND hwnd,
|
|||||||
return retval;
|
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
|
void
|
||||||
_gdk_events_init (GdkDisplay *display)
|
_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_add_poll (source, &event_source->event_poll_fd);
|
||||||
g_source_set_can_recurse (source, TRUE);
|
g_source_set_can_recurse (source, TRUE);
|
||||||
g_source_attach (source, NULL);
|
g_source_attach (source, NULL);
|
||||||
|
|
||||||
|
set_up_low_level_keyboard_hook ();
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean
|
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)
|
switch (msg->message)
|
||||||
{
|
{
|
||||||
case WM_INPUTLANGCHANGE:
|
case WM_INPUTLANGCHANGE:
|
||||||
@ -2362,48 +2490,6 @@ gdk_event_translate (MSG *msg,
|
|||||||
|
|
||||||
impl = GDK_WINDOW_IMPL_WIN32 (window->impl);
|
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));
|
API_CALL (GetKeyboardState, (key_state));
|
||||||
|
|
||||||
ccount = 0;
|
ccount = 0;
|
||||||
|
Loading…
Reference in New Issue
Block a user