From 4ff34b7ca38b5aec898b943960577a19b2da781a Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Sat, 3 Dec 2011 15:11:08 +0100 Subject: [PATCH] gdk: Generate crossing events around touch devices' press/release Anytime a touch device interacts, the crossing events generation will change to a touch mode where only events with mode GDK_CROSSING_TOUCH_BEGIN/END are handled, and those are sent around touch begin/end. Those are virtual as the master device may still stay on the window. Whenever there is a switch of slave device (the user starts using another non-touch device), a crossing event with mode GDK_CROSSING_DEVICE_SWITCH may generated if needed, and the normal crossing event handling is resumed. --- gdk/gdkdisplay.c | 29 ++++++++++----- gdk/gdkdisplayprivate.h | 1 + gdk/gdkevents.h | 12 ++++++- gdk/gdkwindow.c | 80 +++++++++++++++++++++++++++++++++++++++-- 4 files changed, 109 insertions(+), 13 deletions(-) diff --git a/gdk/gdkdisplay.c b/gdk/gdkdisplay.c index b177f2afc9..bb4be5f6b2 100644 --- a/gdk/gdkdisplay.c +++ b/gdk/gdkdisplay.c @@ -895,15 +895,26 @@ switch_to_pointer_grab (GdkDisplay *display, if (grab == NULL) /* Ungrabbed, send events */ { - pointer_window = NULL; - if (new_toplevel) - { - /* Find (possibly virtual) child window */ - pointer_window = - _gdk_window_find_descendant_at (new_toplevel, - x, y, - NULL, NULL); - } + /* If the source device is a touch device, do not + * propagate any enter event yet, until one is + * synthesized when needed. + */ + if (source_device && + (gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN || + gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHPAD)) + info->need_touch_press_enter = TRUE; + + pointer_window = NULL; + + if (new_toplevel && + !info->need_touch_press_enter) + { + /* Find (possibly virtual) child window */ + pointer_window = + _gdk_window_find_descendant_at (new_toplevel, + x, y, + NULL, NULL); + } if (pointer_window != last_grab->window) synthesize_crossing_events (display, device, source_device, diff --git a/gdk/gdkdisplayprivate.h b/gdk/gdkdisplayprivate.h index 7a1ccf8606..62c2cc067b 100644 --- a/gdk/gdkdisplayprivate.h +++ b/gdk/gdkdisplayprivate.h @@ -74,6 +74,7 @@ typedef struct guint32 state; guint32 button; GdkDevice *last_slave; + guint need_touch_press_enter : 1; } GdkPointerWindowInfo; typedef struct diff --git a/gdk/gdkevents.h b/gdk/gdkevents.h index e7753caa57..b582335b45 100644 --- a/gdk/gdkevents.h +++ b/gdk/gdkevents.h @@ -399,6 +399,13 @@ typedef enum * @GDK_CROSSING_GTK_UNGRAB: crossing because a GTK+ grab is deactivated. * @GDK_CROSSING_STATE_CHANGED: crossing because a GTK+ widget changed * state (e.g. sensitivity). + * @GDK_CROSSING_TOUCH_BEGIN: crossing because a touch sequence has begun, + * this event is synthetic as the pointer might have not left the window. + * @GDK_CROSSING_TOUCH_END: crossing because a touch sequence has ended, + * this event is synthetic as the pointer might have not left the window. + * @GDK_CROSSING_DEVICE_SWITCH: crossing because of a device switch (i.e. + * a mouse taking control of the pointer after a touch device), this event + * is synthetic as the pointer didn't leave the window. * * Specifies the crossing mode for #GdkEventCrossing. */ @@ -409,7 +416,10 @@ typedef enum GDK_CROSSING_UNGRAB, GDK_CROSSING_GTK_GRAB, GDK_CROSSING_GTK_UNGRAB, - GDK_CROSSING_STATE_CHANGED + GDK_CROSSING_STATE_CHANGED, + GDK_CROSSING_TOUCH_BEGIN, + GDK_CROSSING_TOUCH_END, + GDK_CROSSING_DEVICE_SWITCH } GdkCrossingMode; /** diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c index 83bc6645d3..06ba468182 100644 --- a/gdk/gdkwindow.c +++ b/gdk/gdkwindow.c @@ -8333,9 +8333,11 @@ send_crossing_event (GdkDisplay *display, GdkEvent *event; guint32 window_event_mask, type_event_mask; GdkDeviceGrabInfo *grab; + GdkPointerWindowInfo *pointer_info; gboolean block_event = FALSE; grab = _gdk_display_has_device_grab (display, device, serial); + pointer_info = _gdk_display_get_pointer_info (display, device); if (grab != NULL && !grab->owner_events) @@ -8348,7 +8350,14 @@ send_crossing_event (GdkDisplay *display, else window_event_mask = window->event_mask; - if (type == GDK_LEAVE_NOTIFY) + if (type == GDK_ENTER_NOTIFY && + pointer_info->need_touch_press_enter && + mode != GDK_CROSSING_TOUCH_BEGIN && + mode != GDK_CROSSING_TOUCH_END) + { + block_event = TRUE; + } + else if (type == GDK_LEAVE_NOTIFY) { type_event_mask = GDK_LEAVE_NOTIFY_MASK; window->devices_inside = g_list_remove (window->devices_inside, device); @@ -9172,7 +9181,7 @@ proxy_pointer_event (GdkDisplay *display, guint state; gdouble toplevel_x, toplevel_y; guint32 time_; - gboolean non_linear; + gboolean non_linear, need_synthetic_enter = FALSE; event_window = source_event->any.window; gdk_event_get_coords (source_event, &toplevel_x, &toplevel_y); @@ -9192,6 +9201,15 @@ proxy_pointer_event (GdkDisplay *display, source_event->crossing.detail == GDK_NOTIFY_NONLINEAR_VIRTUAL)) non_linear = TRUE; + if (pointer_info->need_touch_press_enter && + gdk_device_get_source (pointer_info->last_slave) != GDK_SOURCE_TOUCHSCREEN && + gdk_device_get_source (pointer_info->last_slave) != GDK_SOURCE_TOUCHPAD) + + { + pointer_info->need_touch_press_enter = FALSE; + need_synthetic_enter = TRUE; + } + /* If we get crossing events with subwindow unexpectedly being NULL that means there is a native subwindow that gdk doesn't know about. We track these and forward them, with the correct virtual window @@ -9315,6 +9333,18 @@ proxy_pointer_event (GdkDisplay *display, gdk_window_get_device_events (event_win, device) == 0) return TRUE; + /* The last device to interact with the window was a touch device, + * which synthesized a leave notify event, so synthesize another enter + * notify to tell the pointer is on the window. + */ + if (need_synthetic_enter) + _gdk_synthesize_crossing_events (display, + NULL, pointer_window, + device, source_device, + GDK_CROSSING_DEVICE_SWITCH, + toplevel_x, toplevel_y, + state, time_, NULL, + serial, FALSE); is_hint = FALSE; if (event_win && @@ -9373,6 +9403,7 @@ proxy_button_event (GdkEvent *source_event, GdkWindow *pointer_window; GdkWindow *parent; GdkEvent *event; + GdkPointerWindowInfo *pointer_info; guint state; guint32 time_; GdkEventType type; @@ -9392,6 +9423,7 @@ proxy_button_event (GdkEvent *source_event, toplevel_window = convert_native_coords_to_toplevel (event_window, toplevel_x, toplevel_y, &toplevel_x, &toplevel_y); + pointer_info = _gdk_display_get_pointer_info (display, device); if (type == GDK_BUTTON_PRESS && !source_event->any.send_event && @@ -9444,6 +9476,31 @@ proxy_button_event (GdkEvent *source_event, gdk_window_get_device_events (event_win, device) == 0) return TRUE; + if (type == GDK_BUTTON_PRESS && + pointer_info->need_touch_press_enter) + { + GdkCrossingMode mode; + + /* The last device to interact with the window was a touch device, + * which synthesized a leave notify event, so synthesize another enter + * notify to tell the pointer is on the window. + */ + if (gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN || + gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHPAD) + mode = GDK_CROSSING_TOUCH_BEGIN; + else + mode = GDK_CROSSING_DEVICE_SWITCH; + + pointer_info->need_touch_press_enter = FALSE; + _gdk_synthesize_crossing_events (display, + NULL, + pointer_info->window_under_pointer, + device, source_device, mode, + toplevel_x, toplevel_y, + state, time_, source_event, + serial, FALSE); + } + event = _gdk_make_event (event_win, type, source_event, FALSE); switch (type) @@ -9464,7 +9521,24 @@ proxy_button_event (GdkEvent *source_event, gdk_event_set_source_device (event, source_device); if (type == GDK_BUTTON_PRESS) - _gdk_event_button_generate (display, event); + _gdk_event_button_generate (display, event); + else if (type == GDK_BUTTON_RELEASE && + pointer_window == pointer_info->window_under_pointer && + (gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN || + gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHPAD)) + { + /* Synthesize a leave notify event + * whenever a touch device is released + */ + pointer_info->need_touch_press_enter = TRUE; + _gdk_synthesize_crossing_events (display, + pointer_window, NULL, + device, source_device, + GDK_CROSSING_TOUCH_END, + toplevel_x, toplevel_y, + state, time_, NULL, + serial, FALSE); + } return TRUE; case GDK_SCROLL: