gdk: handle implicit touch grabs

If the touch sequence happens on a window with GDK_TOUCH_MASK set,
a GdkTouchGrabInfo is created to back it up. Else a device grab is
only created if the sequence emulates the pointer.

If both a device and a touch grab are present on a window, the later
of them both is obeyed, Any grab on the device happening after a
touch grab generates grab-broken on all the windows an implicit
touch grab was going on.
This commit is contained in:
Carlos Garnacho 2011-12-29 00:06:45 +01:00 committed by Matthias Clasen
parent b5cfdf2db4
commit c72a77b04c
3 changed files with 188 additions and 48 deletions

View File

@ -1049,12 +1049,15 @@ _gdk_display_device_grab_update (GdkDisplay *display,
next_grab = NULL; /* Actually its not yet active */ next_grab = NULL; /* Actually its not yet active */
} }
if (next_grab)
_gdk_display_break_touch_grabs (display, device, next_grab->window);
if ((next_grab == NULL && current_grab->implicit_ungrab) || if ((next_grab == NULL && current_grab->implicit_ungrab) ||
(next_grab != NULL && current_grab->window != next_grab->window)) (next_grab != NULL && current_grab->window != next_grab->window))
generate_grab_broken_event (GDK_WINDOW (current_grab->window), generate_grab_broken_event (GDK_WINDOW (current_grab->window),
device, device,
current_grab->implicit, current_grab->implicit,
next_grab? next_grab->window : NULL); next_grab? next_grab->window : NULL);
/* Remove old grab */ /* Remove old grab */
grabs = g_list_delete_link (grabs, grabs); grabs = g_list_delete_link (grabs, grabs);

View File

@ -8156,6 +8156,8 @@ is_button_type (GdkEventType type)
type == GDK_2BUTTON_PRESS || type == GDK_2BUTTON_PRESS ||
type == GDK_3BUTTON_PRESS || type == GDK_3BUTTON_PRESS ||
type == GDK_BUTTON_RELEASE || type == GDK_BUTTON_RELEASE ||
type == GDK_TOUCH_BEGIN ||
type == GDK_TOUCH_END ||
type == GDK_SCROLL; type == GDK_SCROLL;
} }
@ -8163,6 +8165,7 @@ static gboolean
is_motion_type (GdkEventType type) is_motion_type (GdkEventType type)
{ {
return type == GDK_MOTION_NOTIFY || return type == GDK_MOTION_NOTIFY ||
type == GDK_TOUCH_UPDATE ||
type == GDK_ENTER_NOTIFY || type == GDK_ENTER_NOTIFY ||
type == GDK_LEAVE_NOTIFY; type == GDK_LEAVE_NOTIFY;
} }
@ -9109,18 +9112,37 @@ _gdk_synthesize_crossing_events_for_geometry_change (GdkWindow *changed_window)
static GdkWindow * static GdkWindow *
get_event_window (GdkDisplay *display, get_event_window (GdkDisplay *display,
GdkDevice *device, GdkDevice *device,
GdkWindow *pointer_window, GdkEventSequence *sequence,
GdkEventType type, GdkWindow *pointer_window,
GdkModifierType mask, GdkEventType type,
guint *evmask_out, GdkModifierType mask,
gulong serial) guint *evmask_out,
gulong serial)
{ {
guint evmask; guint evmask;
GdkWindow *grab_window; GdkWindow *grab_window;
GdkDeviceGrabInfo *grab; GdkDeviceGrabInfo *grab;
GdkTouchGrabInfo *touch_grab;
touch_grab = _gdk_display_has_touch_grab (display, device, sequence, serial);
grab = _gdk_display_has_device_grab (display, device, serial); grab = _gdk_display_has_device_grab (display, device, serial);
if (touch_grab != NULL &&
(!grab || grab->implicit || touch_grab->serial >= grab->serial_start))
{
evmask = touch_grab->event_mask;
evmask = update_evmask_for_button_motion (evmask, mask);
if (evmask & type_masks[type])
{
if (evmask_out)
*evmask_out = evmask;
return touch_grab->window;
}
else
return NULL;
}
if (grab != NULL && !grab->owner_events) if (grab != NULL && !grab->owner_events)
{ {
evmask = grab->event_mask; evmask = grab->event_mask;
@ -9320,14 +9342,19 @@ proxy_pointer_event (GdkDisplay *display,
serial, non_linear); serial, non_linear);
_gdk_display_set_window_under_pointer (display, device, pointer_window); _gdk_display_set_window_under_pointer (display, device, pointer_window);
} }
else if (source_event->type == GDK_MOTION_NOTIFY) else if (source_event->type == GDK_MOTION_NOTIFY ||
source_event->type == GDK_TOUCH_UPDATE)
{ {
GdkWindow *event_win; GdkWindow *event_win;
guint evmask; guint evmask;
gboolean is_hint; gboolean is_hint;
GdkEventSequence *sequence;
sequence = gdk_event_get_event_sequence (source_event);
event_win = get_event_window (display, event_win = get_event_window (display,
device, device,
sequence,
pointer_window, pointer_window,
source_event->type, source_event->type,
state, state,
@ -9351,6 +9378,7 @@ proxy_pointer_event (GdkDisplay *display,
toplevel_x, toplevel_y, toplevel_x, toplevel_y,
state, time_, NULL, state, time_, NULL,
serial, FALSE); serial, FALSE);
is_hint = FALSE; is_hint = FALSE;
if (event_win && if (event_win &&
@ -9371,21 +9399,34 @@ proxy_pointer_event (GdkDisplay *display,
} }
} }
if (event_win && !display->ignore_core_events) if (!event_win)
{ return TRUE;
event = _gdk_make_event (event_win, GDK_MOTION_NOTIFY, source_event, FALSE);
event->motion.time = time_; if (!display->ignore_core_events)
convert_toplevel_coords_to_window (event_win, {
toplevel_x, toplevel_y, GdkEventType event_type;
&event->motion.x, &event->motion.y);
event->motion.x_root = source_event->motion.x_root; event_type = source_event->type;
event->motion.y_root = source_event->motion.y_root;
event->motion.state = state; event = gdk_event_new (event_type);
event->motion.is_hint = is_hint; event->any.window = g_object_ref (event_win);
event->motion.device = source_event->motion.device; event->any.send_event = source_event->any.send_event;
event->motion.time = time_;
convert_toplevel_coords_to_window (event_win,
toplevel_x, toplevel_y,
&event->motion.x, &event->motion.y);
event->motion.x_root = source_event->motion.x_root;
event->motion.y_root = source_event->motion.y_root;
event->motion.state = state;
event->motion.is_hint = is_hint;
event->motion.device = source_event->motion.device;
event->motion.axes = g_memdup (source_event->motion.axes, event->motion.axes = g_memdup (source_event->motion.axes,
sizeof (gdouble) * gdk_device_get_n_axes (source_event->motion.device)); sizeof (gdouble) * gdk_device_get_n_axes (source_event->motion.device));
gdk_event_set_source_device (event, source_device); gdk_event_set_source_device (event, source_device);
/* Just insert the event */
_gdk_event_queue_insert_after (gdk_window_get_display (event_win),
source_event, event);
} }
} }
@ -9417,6 +9458,8 @@ proxy_button_event (GdkEvent *source_event,
GdkDisplay *display; GdkDisplay *display;
GdkWindow *w; GdkWindow *w;
GdkDevice *device, *source_device; GdkDevice *device, *source_device;
GdkEventMask evmask;
GdkEventSequence *sequence;
type = source_event->any.type; type = source_event->any.type;
event_window = source_event->any.window; event_window = source_event->any.window;
@ -9429,9 +9472,13 @@ proxy_button_event (GdkEvent *source_event,
toplevel_window = convert_native_coords_to_toplevel (event_window, toplevel_window = convert_native_coords_to_toplevel (event_window,
toplevel_x, toplevel_y, toplevel_x, toplevel_y,
&toplevel_x, &toplevel_y); &toplevel_x, &toplevel_y);
sequence = gdk_event_get_event_sequence (source_event);
pointer_info = _gdk_display_get_pointer_info (display, device); pointer_info = _gdk_display_get_pointer_info (display, device);
if (type == GDK_BUTTON_PRESS && if ((type == GDK_BUTTON_PRESS ||
type == GDK_TOUCH_BEGIN) &&
!source_event->any.send_event && !source_event->any.send_event &&
_gdk_display_has_device_grab (display, device, serial) == NULL) _gdk_display_has_device_grab (display, device, serial) == NULL)
{ {
@ -9446,23 +9493,46 @@ proxy_button_event (GdkEvent *source_event,
(parent = get_event_parent (w)) != NULL && (parent = get_event_parent (w)) != NULL &&
parent->window_type != GDK_WINDOW_ROOT) parent->window_type != GDK_WINDOW_ROOT)
{ {
if (w->event_mask & GDK_BUTTON_PRESS_MASK) if (w->event_mask & GDK_BUTTON_PRESS_MASK &&
(type == GDK_BUTTON_PRESS ||
_gdk_event_get_pointer_emulated (source_event)))
break; break;
if (type == GDK_TOUCH_BEGIN &&
w->event_mask & GDK_TOUCH_MASK)
break;
w = parent; w = parent;
} }
pointer_window = (GdkWindow *)w; pointer_window = w;
_gdk_display_add_device_grab (display, if (pointer_window)
device, {
pointer_window, if (type == GDK_TOUCH_BEGIN &&
event_window, pointer_window->event_mask & GDK_TOUCH_MASK)
GDK_OWNERSHIP_NONE, {
FALSE, _gdk_display_add_touch_grab (display, device, sequence,
gdk_window_get_events (pointer_window), pointer_window, event_window,
serial, gdk_window_get_events (pointer_window),
time_, serial, time_);
TRUE); }
_gdk_display_device_grab_update (display, device, source_device, serial); else if (type == GDK_BUTTON_PRESS ||
_gdk_event_get_pointer_emulated (source_event))
{
_gdk_display_add_device_grab (display,
device,
pointer_window,
event_window,
GDK_OWNERSHIP_NONE,
FALSE,
gdk_window_get_events (pointer_window),
serial,
time_,
TRUE);
_gdk_display_device_grab_update (display, device,
source_device, serial);
}
}
} }
pointer_window = get_pointer_window (display, toplevel_window, device, pointer_window = get_pointer_window (display, toplevel_window, device,
@ -9471,9 +9541,10 @@ proxy_button_event (GdkEvent *source_event,
event_win = get_event_window (display, event_win = get_event_window (display,
device, device,
pointer_window, sequence,
type, state, pointer_window,
NULL, serial); type, state,
&evmask, serial);
if (event_win == NULL || display->ignore_core_events) if (event_win == NULL || display->ignore_core_events)
return TRUE; return TRUE;
@ -9523,7 +9594,6 @@ proxy_button_event (GdkEvent *source_event,
event->button.device = source_event->button.device; event->button.device = source_event->button.device;
event->button.axes = g_memdup (source_event->button.axes, event->button.axes = g_memdup (source_event->button.axes,
sizeof (gdouble) * gdk_device_get_n_axes (source_event->button.device)); sizeof (gdouble) * gdk_device_get_n_axes (source_event->button.device));
gdk_event_set_source_device (event, source_device); gdk_event_set_source_device (event, source_device);
if (type == GDK_BUTTON_PRESS) if (type == GDK_BUTTON_PRESS)
@ -9547,6 +9617,40 @@ proxy_button_event (GdkEvent *source_event,
} }
return TRUE; return TRUE;
case GDK_TOUCH_BEGIN:
case GDK_TOUCH_END:
convert_toplevel_coords_to_window (event_win,
toplevel_x, toplevel_y,
&event->button.x, &event->button.y);
event->touch.x_root = source_event->touch.x_root;
event->touch.y_root = source_event->touch.y_root;
event->touch.state = state;
event->touch.device = source_event->touch.device;
event->touch.axes = g_memdup (source_event->touch.axes,
sizeof (gdouble) * gdk_device_get_n_axes (source_event->touch.device));
event->touch.sequence = source_event->touch.sequence;
gdk_event_set_source_device (event, source_device);
if ((type == GDK_TOUCH_END &&
_gdk_event_get_pointer_emulated (source_event)) &&
pointer_window == pointer_info->window_under_pointer &&
gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN)
{
/* 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: case GDK_SCROLL:
event->scroll.direction = source_event->scroll.direction; event->scroll.direction = source_event->scroll.direction;
convert_toplevel_coords_to_window (event_win, convert_toplevel_coords_to_window (event_win,
@ -9804,16 +9908,32 @@ _gdk_windowing_got_event (GdkDisplay *display,
else if (is_button_type (event->type)) else if (is_button_type (event->type))
unlink_event = proxy_button_event (event, serial); unlink_event = proxy_button_event (event, serial);
if (event->type == GDK_BUTTON_RELEASE && !event->any.send_event) if ((event->type == GDK_BUTTON_RELEASE ||
event->type == GDK_TOUCH_END) &&
!event->any.send_event)
{ {
button_release_grab = _gdk_display_has_device_grab (display, device, serial); GdkEventSequence *sequence;
if (button_release_grab &&
button_release_grab->implicit && sequence = gdk_event_get_event_sequence (event);
(event->button.state & GDK_ANY_BUTTON_MASK & ~(GDK_BUTTON1_MASK << (event->button.button - 1))) == 0) if (event->type == GDK_TOUCH_END && sequence)
{ {
button_release_grab->serial_end = serial; _gdk_display_end_touch_grab (display, device, sequence);
button_release_grab->implicit_ungrab = FALSE; }
_gdk_display_device_grab_update (display, device, source_device, serial);
if (event->type == GDK_BUTTON_RELEASE ||
_gdk_event_get_pointer_emulated (event))
{
button_release_grab =
_gdk_display_has_device_grab (display, device, serial);
if (button_release_grab &&
button_release_grab->implicit &&
(event->button.state & GDK_ANY_BUTTON_MASK & ~(GDK_BUTTON1_MASK << (event->button.button - 1))) == 0)
{
button_release_grab->serial_end = serial;
button_release_grab->implicit_ungrab = FALSE;
_gdk_display_device_grab_update (display, device, source_device, serial);
}
} }
} }

View File

@ -1339,6 +1339,14 @@ rewrite_event_for_window (GdkEvent *event,
new_window, new_window,
&event->motion.x, &event->motion.y); &event->motion.x, &event->motion.y);
break; break;
case GDK_TOUCH_BEGIN:
case GDK_TOUCH_UPDATE:
case GDK_TOUCH_END:
case GDK_TOUCH_CANCEL:
rewrite_events_translate (event->any.window,
new_window,
&event->touch.x, &event->touch.y);
break;
case GDK_KEY_PRESS: case GDK_KEY_PRESS:
case GDK_KEY_RELEASE: case GDK_KEY_RELEASE:
case GDK_PROXIMITY_IN: case GDK_PROXIMITY_IN:
@ -1384,6 +1392,10 @@ rewrite_event_for_grabs (GdkEvent *event)
case GDK_PROXIMITY_OUT: case GDK_PROXIMITY_OUT:
case GDK_KEY_PRESS: case GDK_KEY_PRESS:
case GDK_KEY_RELEASE: case GDK_KEY_RELEASE:
case GDK_TOUCH_BEGIN:
case GDK_TOUCH_UPDATE:
case GDK_TOUCH_END:
case GDK_TOUCH_CANCEL:
display = gdk_window_get_display (event->any.window); display = gdk_window_get_display (event->any.window);
device = gdk_event_get_device (event); device = gdk_event_get_device (event);
@ -1643,6 +1655,7 @@ gtk_main_do_event (GdkEvent *event)
case GDK_BUTTON_PRESS: case GDK_BUTTON_PRESS:
case GDK_2BUTTON_PRESS: case GDK_2BUTTON_PRESS:
case GDK_3BUTTON_PRESS: case GDK_3BUTTON_PRESS:
case GDK_TOUCH_BEGIN:
if (!_gtk_propagate_captured_event (grab_widget, event, topmost_widget)) if (!_gtk_propagate_captured_event (grab_widget, event, topmost_widget))
gtk_propagate_event (grab_widget, event); gtk_propagate_event (grab_widget, event);
break; break;
@ -1693,6 +1706,9 @@ gtk_main_do_event (GdkEvent *event)
case GDK_BUTTON_RELEASE: case GDK_BUTTON_RELEASE:
case GDK_PROXIMITY_IN: case GDK_PROXIMITY_IN:
case GDK_PROXIMITY_OUT: case GDK_PROXIMITY_OUT:
case GDK_TOUCH_UPDATE:
case GDK_TOUCH_END:
case GDK_TOUCH_CANCEL:
if (!_gtk_propagate_captured_event (grab_widget, event, topmost_widget)) if (!_gtk_propagate_captured_event (grab_widget, event, topmost_widget))
gtk_propagate_event (grab_widget, event); gtk_propagate_event (grab_widget, event);
break; break;
@ -1743,6 +1759,7 @@ gtk_main_do_event (GdkEvent *event)
|| event->type == GDK_DRAG_ENTER || event->type == GDK_DRAG_ENTER
|| event->type == GDK_GRAB_BROKEN || event->type == GDK_GRAB_BROKEN
|| event->type == GDK_MOTION_NOTIFY || event->type == GDK_MOTION_NOTIFY
|| event->type == GDK_TOUCH_UPDATE
|| event->type == GDK_SCROLL) || event->type == GDK_SCROLL)
{ {
_gtk_tooltip_handle_event (event); _gtk_tooltip_handle_event (event);