diff --git a/gdk/gdkdisplay.c b/gdk/gdkdisplay.c index 702f6b4891..48a556c597 100644 --- a/gdk/gdkdisplay.c +++ b/gdk/gdkdisplay.c @@ -897,15 +897,25 @@ 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_TOUCH) + 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 8084332c2c..d73449ea1e 100644 --- a/gdk/gdkdisplayprivate.h +++ b/gdk/gdkdisplayprivate.h @@ -76,6 +76,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 197b03ed55..ef2b95097c 100644 --- a/gdk/gdkevents.h +++ b/gdk/gdkevents.h @@ -385,6 +385,15 @@ 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_STATE_CHANGED: crossing because a GTK+ widget changed + * state (e.g. sensitivity). + * @GDK_CROSSING_TOUCH_PRESS: crossing because a touch device was pressed, + * this event is synthetic as the pointer might have not left the window. + * @GDK_CROSSING_TOUCH_RELEASE: crossing because a touch device was released. + * 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. */ @@ -395,7 +404,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_PRESS, + GDK_CROSSING_TOUCH_RELEASE, + GDK_CROSSING_DEVICE_SWITCH } GdkCrossingMode; /** diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c index 3a3b41998d..c767c265a5 100644 --- a/gdk/gdkwindow.c +++ b/gdk/gdkwindow.c @@ -8341,9 +8341,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) @@ -8356,7 +8358,13 @@ send_crossing_event (GdkDisplay *display, else window_event_mask = window->event_mask; - if (type == GDK_LEAVE_NOTIFY) + if (pointer_info->need_touch_press_enter && + mode != GDK_CROSSING_TOUCH_PRESS && + mode != GDK_CROSSING_TOUCH_RELEASE) + { + 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); @@ -9180,7 +9188,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); @@ -9200,6 +9208,13 @@ 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_TOUCH) + { + 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 @@ -9323,6 +9338,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 && @@ -9381,6 +9408,7 @@ proxy_button_event (GdkEvent *source_event, GdkWindow *pointer_window; GdkWindow *parent; GdkEvent *event; + GdkPointerWindowInfo *pointer_info; guint state; guint32 time_; GdkEventType type; @@ -9400,6 +9428,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 && @@ -9452,6 +9481,30 @@ 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_TOUCH) + mode = GDK_CROSSING_TOUCH_PRESS; + 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) @@ -9472,7 +9525,23 @@ 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_TOUCH) + { + /* 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_RELEASE, + toplevel_x, toplevel_y, + state, time_, NULL, + serial, FALSE); + } return TRUE; case GDK_SCROLL: