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_PRESS/RELEASE are handled, and those are sent
around button press/release. 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.
This commit is contained in:
Carlos Garnacho
2011-12-03 15:11:08 +01:00
parent 4068758bb8
commit fafb78e38e
4 changed files with 105 additions and 13 deletions

View File

@@ -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,

View File

@@ -76,6 +76,7 @@ typedef struct
guint32 state;
guint32 button;
GdkDevice *last_slave;
guint need_touch_press_enter : 1;
} GdkPointerWindowInfo;
typedef struct

View File

@@ -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;
/**

View File

@@ -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: