From f9df0fc94c7b83f598b1dabedf5b45e09c0c6c47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A0=D1=83=D1=81=D0=BB=D0=B0=D0=BD=20=D0=98=D0=B6=D0=B1?= =?UTF-8?q?=D1=83=D0=BB=D0=B0=D1=82=D0=BE=D0=B2?= Date: Fri, 10 Feb 2017 10:49:00 +0000 Subject: [PATCH] GDK W32: Handle drivers that do not send WT_CSRCHANGE after WT_PROXIMITY Some drivers don't do that (not sure whether that is the correct behaviour or not). Remember each WT_PROXIMITY with LOWORD(lParam) != 0 that we get, then look for a WT_CSRCHANGE. If WT_CSRCHANGE doesn't come, but a WT_PACKET does, assume that this device is the one that sent WT_PROXIMITY. Also include fallback code to ensure that WT_PACKETs for an enabled device disable the system pointer, because WT_PROXIMITY handler might have enabled it by mistake, since it's not possible to know which device left the proximity (it might have been a disabled device). https://bugzilla.gnome.org/show_bug.cgi?id=778328 --- gdk/win32/gdkdevicemanager-win32.c | 93 +++++++++++++++++++++++------- gdk/win32/gdkdevicemanager-win32.h | 6 ++ gdk/win32/gdkdisplay-win32.c | 2 +- gdk/win32/gdkevents-win32.c | 4 +- gdk/win32/gdkglobals-win32.c | 2 +- gdk/win32/gdkprivate-win32.h | 2 +- 6 files changed, 83 insertions(+), 26 deletions(-) diff --git a/gdk/win32/gdkdevicemanager-win32.c b/gdk/win32/gdkdevicemanager-win32.c index 73ba2ad57c..c4740747ac 100644 --- a/gdk/win32/gdkdevicemanager-win32.c +++ b/gdk/win32/gdkdevicemanager-win32.c @@ -990,6 +990,54 @@ G_GNUC_END_IGNORE_DEPRECATIONS; switch (msg->message) { case WT_PACKET: + source_device = gdk_device_manager_find_wintab_device (device_manager, + (HCTX) msg->lParam, + packet.pkCursor); + + /* Check this first, as we get WT_PROXIMITY for disabled devices too */ + if (device_manager->dev_entered_proximity > 0) + { + /* This is the same code as in WT_CSRCHANGE. Some drivers send + * WT_CSRCHANGE after each WT_PROXIMITY with LOWORD(lParam) != 0, + * this code is for those that don't. + */ + device_manager->dev_entered_proximity -= 1; + + if (source_device != NULL && + source_device->sends_core && + gdk_device_get_mode (GDK_DEVICE (source_device)) != GDK_MODE_DISABLED) + { + _gdk_device_virtual_set_active (device_manager->core_pointer, + GDK_DEVICE (source_device)); + _gdk_input_ignore_core += 1; + } + } + else if (source_device != NULL && + source_device->sends_core && + gdk_device_get_mode (GDK_DEVICE (source_device)) != GDK_MODE_DISABLED && + _gdk_input_ignore_core == 0) + { + /* A fallback for cases when two devices (disabled and enabled) + * were in proximity simultaneously. + * In this case the removal of a disabled device would also + * make the system pointer active, as we don't know which + * device was removed and assume it was the enabled one. + * If we are still getting packets for the enabled device, + * it means that the device that was removed was the disabled + * device, so we must make the enabled device active again and + * start ignoring the core pointer events. In practice this means that + * removing a disabled device while an enabled device is still + * in proximity might briefly make the core pointer active/visible. + */ + _gdk_device_virtual_set_active (device_manager->core_pointer, + GDK_DEVICE (source_device)); + _gdk_input_ignore_core += 1; + } + + if (source_device == NULL || + gdk_device_get_mode (GDK_DEVICE (source_device)) == GDK_MODE_DISABLED) + return FALSE; + /* Don't produce any button or motion events while a window is being * moved or resized, see bug #151090. */ @@ -999,14 +1047,6 @@ G_GNUC_END_IGNORE_DEPRECATIONS; return FALSE; } - if ((source_device = gdk_device_manager_find_wintab_device (device_manager, - (HCTX) msg->lParam, - packet.pkCursor)) == NULL) - return FALSE; - - if (gdk_device_get_mode (GDK_DEVICE (source_device)) == GDK_MODE_DISABLED) - return FALSE; - last_grab = _gdk_display_get_last_device_grab (display, GDK_DEVICE (source_device)); if (last_grab && last_grab->window) @@ -1180,18 +1220,20 @@ G_GNUC_END_IGNORE_DEPRECATIONS; return TRUE; case WT_CSRCHANGE: + if (device_manager->dev_entered_proximity > 0) + device_manager->dev_entered_proximity -= 1; + if ((source_device = gdk_device_manager_find_wintab_device (device_manager, - (HCTX) msg->lParam, - packet.pkCursor)) == NULL) - return FALSE; + (HCTX) msg->lParam, + packet.pkCursor)) == NULL) + return FALSE; - if (gdk_device_get_mode (GDK_DEVICE (source_device)) == GDK_MODE_DISABLED) - return FALSE; - - if (source_device->sends_core) + if (source_device->sends_core && + gdk_device_get_mode (GDK_DEVICE (source_device)) != GDK_MODE_DISABLED) { - _gdk_device_virtual_set_active (device_manager->core_pointer, GDK_DEVICE (source_device)); - _gdk_input_ignore_core = TRUE; + _gdk_device_virtual_set_active (device_manager->core_pointer, + GDK_DEVICE (source_device)); + _gdk_input_ignore_core += 1; } return FALSE; @@ -1199,10 +1241,19 @@ G_GNUC_END_IGNORE_DEPRECATIONS; case WT_PROXIMITY: if (LOWORD (msg->lParam) == 0) { - _gdk_input_ignore_core = FALSE; - _gdk_device_virtual_set_active (device_manager->core_pointer, - device_manager->system_pointer); - } + if (_gdk_input_ignore_core > 0) + { + _gdk_input_ignore_core -= 1; + + if (_gdk_input_ignore_core == 0) + _gdk_device_virtual_set_active (device_manager->core_pointer, + device_manager->system_pointer); + } + } + else + { + device_manager->dev_entered_proximity += 1; + } return FALSE; } diff --git a/gdk/win32/gdkdevicemanager-win32.h b/gdk/win32/gdkdevicemanager-win32.h index 08bc4f5592..36a7cefb1f 100644 --- a/gdk/win32/gdkdevicemanager-win32.h +++ b/gdk/win32/gdkdevicemanager-win32.h @@ -42,6 +42,12 @@ struct _GdkDeviceManagerWin32 GdkDevice *system_pointer; GdkDevice *system_keyboard; GList *wintab_devices; + + /* Bumped up every time a wintab device enters the proximity + * of our context (WT_PROXIMITY). Bumped down when we either + * receive a WT_PACKET, or a WT_CSRCHANGE. + */ + gint dev_entered_proximity; }; struct _GdkDeviceManagerWin32Class diff --git a/gdk/win32/gdkdisplay-win32.c b/gdk/win32/gdkdisplay-win32.c index b72b1c7dc1..da5f2cfd47 100644 --- a/gdk/win32/gdkdisplay-win32.c +++ b/gdk/win32/gdkdisplay-win32.c @@ -404,7 +404,7 @@ _gdk_win32_display_open (const gchar *display_name) _gdk_events_init (_gdk_display); - _gdk_input_ignore_core = FALSE; + _gdk_input_ignore_core = 0; _gdk_display->device_manager = g_object_new (GDK_TYPE_DEVICE_MANAGER_WIN32, "display", _gdk_display, diff --git a/gdk/win32/gdkevents-win32.c b/gdk/win32/gdkevents-win32.c index 6dc379fe00..6601a95007 100644 --- a/gdk/win32/gdkevents-win32.c +++ b/gdk/win32/gdkevents-win32.c @@ -1834,7 +1834,7 @@ generate_button_event (GdkEventType type, GdkDeviceManagerWin32 *device_manager; GdkWindowImplWin32 *impl = GDK_WINDOW_IMPL_WIN32 (window->impl); - if (_gdk_input_ignore_core) + if (_gdk_input_ignore_core > 0) return; device_manager = GDK_DEVICE_MANAGER_WIN32 (gdk_display_get_device_manager (gdk_display_get_default ())); @@ -2936,7 +2936,7 @@ gdk_event_translate (MSG *msg, { gdk_win32_window_do_move_resize_drag (window, current_root_x, current_root_y); } - else if (!_gdk_input_ignore_core) + else if (_gdk_input_ignore_core == 0) { event = gdk_event_new (GDK_MOTION_NOTIFY); event->motion.window = window; diff --git a/gdk/win32/gdkglobals-win32.c b/gdk/win32/gdkglobals-win32.c index 2398ca1047..cb352a4198 100644 --- a/gdk/win32/gdkglobals-win32.c +++ b/gdk/win32/gdkglobals-win32.c @@ -35,7 +35,7 @@ HDC _gdk_display_hdc; HINSTANCE _gdk_dll_hinstance; HINSTANCE _gdk_app_hmodule; -gboolean _gdk_input_ignore_core; +gint _gdk_input_ignore_core; HKL _gdk_input_locale; gboolean _gdk_input_locale_is_ime; diff --git a/gdk/win32/gdkprivate-win32.h b/gdk/win32/gdkprivate-win32.h index 09010b2c53..27aec8ebfa 100644 --- a/gdk/win32/gdkprivate-win32.h +++ b/gdk/win32/gdkprivate-win32.h @@ -285,7 +285,7 @@ extern HDC _gdk_display_hdc; extern HINSTANCE _gdk_dll_hinstance; extern HINSTANCE _gdk_app_hmodule; -extern gboolean _gdk_input_ignore_core; +extern gint _gdk_input_ignore_core; /* These are thread specific, but GDK/win32 works OK only when invoked * from a single thread anyway.