From 991c85f6a00be7cdc03b021d14b28b554d5e5b01 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Mon, 3 Feb 2014 19:08:06 +0100 Subject: [PATCH] core: Add minimal handling of touch events Currently touch events are ignored in the core event handler, and hence dealt with within GDK. If those touch events were emulating pointer events, GDK would attempt to convert back those events to pointer events as the frame GdkWindow doesn't have the GDK_TOUCH_MASK set. This results in XI_TouchBegin events being initially processed by GDK, converted to button events, and triggering a grab op that subverts touch events into pointer events, so the touch is never ever seen again by GDK. This leaves GDK in an inconsistent internal state wrt pointer grabs, so future pointer-emulating touches will refer to the same window forever. Fix this by handling touch events minimally, just enough to convert XI_TouchBegin to GDK_BUTTON_PRESS within mutter, so GDK is bypassed for every touch event just like it is for pointer events. This, and the XIGrabDevice() that keeps coercing pointer events when the grab operation starts, are enough to fix window drag and drop on touch devices. https://bugzilla.gnome.org/show_bug.cgi?id=723552 --- src/core/display.c | 76 ++++++++++++++++++++++++++++++++++++++-------- src/ui/ui.c | 18 ++++++++--- 2 files changed, 78 insertions(+), 16 deletions(-) diff --git a/src/core/display.c b/src/core/display.c index 31bc6463a..e8a284fe2 100644 --- a/src/core/display.c +++ b/src/core/display.c @@ -1815,6 +1815,9 @@ get_input_event (MetaDisplay *display, switch (input_event->evtype) { + case XI_TouchBegin: + case XI_TouchUpdate: + case XI_TouchEnd: case XI_Motion: case XI_ButtonPress: case XI_ButtonRelease: @@ -2293,6 +2296,7 @@ event_callback (XEvent *event, { XIDeviceEvent *device_event = (XIDeviceEvent *) input_event; XIEnterEvent *enter_event = (XIEnterEvent *) input_event; + gint button = 0; if (window && !window->override_redirect && ((input_event->evtype == XI_KeyPress) || (input_event->evtype == XI_ButtonPress))) @@ -2328,20 +2332,33 @@ event_callback (XEvent *event, if (meta_display_process_key_event (display, window, (XIDeviceEvent *) input_event)) filter_out_event = bypass_compositor = TRUE; break; + case XI_TouchBegin: + /* Filter out non-pointer-emulating touches */ + if ((((XIDeviceEvent *) input_event)->flags & XITouchEmulatingPointer) == 0) + break; + + /* Fall through */ case XI_ButtonPress: if (display->grab_op == META_GRAB_OP_COMPOSITOR) break; display->overlay_key_only_pressed = FALSE; - if (device_event->detail == 4 || device_event->detail == 5) - /* Scrollwheel event, do nothing and deliver event to compositor below */ - break; + if (input_event->evtype == XI_ButtonPress) + { + if (device_event->detail == 4 || device_event->detail == 5) + /* Scrollwheel event, do nothing and deliver event to compositor below */ + break; + else + button = device_event->detail; + } + else if (input_event->evtype == XI_TouchBegin) + button = 1; if ((window && meta_grab_op_is_mouse (display->grab_op) && (device_event->mods.effective & display->window_grab_modifiers) && - display->grab_button != device_event->detail && + display->grab_button != button && display->grab_window == window) || grab_op_is_keyboard (display->grab_op)) { @@ -2371,8 +2388,7 @@ event_callback (XEvent *event, */ unmodified = (device_event->mods.effective & grab_mask) == 0; - if (unmodified || - device_event->detail == 1) + if (unmodified || button == 1) { /* don't focus if frame received, will be lowered in * frames.c or special-cased if the click was on a @@ -2393,7 +2409,7 @@ event_callback (XEvent *event, { meta_topic (META_DEBUG_FOCUS, "Focusing %s due to unmodified button %u press (display.c)\n", - window->desc, device_event->detail); + window->desc, button); meta_window_focus (window, device_event->time); } else @@ -2409,7 +2425,7 @@ event_callback (XEvent *event, if (!unmodified) begin_move = TRUE; } - else if (!unmodified && device_event->detail == meta_prefs_get_mouse_button_resize()) + else if (!unmodified && button == meta_prefs_get_mouse_button_resize()) { if (window->has_resize_func) { @@ -2451,21 +2467,21 @@ event_callback (XEvent *event, op, TRUE, FALSE, - device_event->detail, + button, 0, device_event->time, device_event->root_x, device_event->root_y); } } - else if (device_event->detail == meta_prefs_get_mouse_button_menu()) + else if (button == meta_prefs_get_mouse_button_menu()) { if (meta_prefs_get_raise_on_click ()) meta_window_raise (window); meta_window_show_menu (window, device_event->root_x, device_event->root_y, - device_event->detail, + button, device_event->time); } @@ -2490,7 +2506,7 @@ event_callback (XEvent *event, META_GRAB_OP_MOVING, TRUE, FALSE, - device_event->detail, + button, 0, device_event->time, device_event->root_x, @@ -2640,6 +2656,18 @@ event_callback (XEvent *event, filter_out_event = bypass_compositor = TRUE; break; #endif /* HAVE_XI23 */ + case XI_TouchUpdate: + case XI_TouchEnd: + /* Filter out non-pointer-emulating touches */ + if ((((XIDeviceEvent *) input_event)->flags & XITouchEmulatingPointer) == 0) + break; + + /* Currently unhandled, if any grab_op is started through XI_TouchBegin, + * the XIGrabDevice() evmask drops touch events, so only emulated + * XI_Motions and XI_ButtonRelease will follow. + */ + g_assert_not_reached (); + break; } } else @@ -3115,6 +3143,9 @@ event_get_modified_window (MetaDisplay *display, case XI_ButtonRelease: case XI_KeyPress: case XI_KeyRelease: + case XI_TouchBegin: + case XI_TouchUpdate: + case XI_TouchEnd: return ((XIDeviceEvent *) input_event)->event; case XI_FocusIn: case XI_FocusOut: @@ -3401,6 +3432,15 @@ meta_spew_xi2_event (MetaDisplay *display, case XI_Leave: name = "XI_Leave"; break; + case XI_TouchBegin: + name = "XI_TouchBegin"; + break; + case XI_TouchUpdate: + name = "XI_TouchUpdate"; + break; + case XI_TouchEnd: + name = "XI_TouchEnd"; + break; #ifdef HAVE_XI23 case XI_BarrierHit: name = "XI_BarrierHit"; @@ -3458,6 +3498,18 @@ meta_spew_xi2_event (MetaDisplay *display, enter_event->root_x, enter_event->root_y); break; + case XI_TouchBegin: + case XI_TouchUpdate: + case XI_TouchEnd: + extra = g_strdup_printf ("win: 0x%lx root: 0x%lx touch sequence: %d x: %g y: %g state: 0x%x flags: 0x%x", + device_event->event, + device_event->root, + device_event->detail, + device_event->root_x, + device_event->root_y, + device_event->mods.effective, + device_event->flags); + break; } *name_p = name; diff --git a/src/ui/ui.c b/src/ui/ui.c index 7d023dc8f..c89028820 100644 --- a/src/ui/ui.c +++ b/src/ui/ui.c @@ -123,6 +123,7 @@ maybe_redirect_mouse_event (XEvent *xevent) switch (xev->evtype) { + case XI_TouchBegin: case XI_ButtonPress: case XI_ButtonRelease: case XI_Motion: @@ -159,20 +160,27 @@ maybe_redirect_mouse_event (XEvent *xevent) switch (xev->evtype) { + case XI_TouchBegin: case XI_ButtonPress: case XI_ButtonRelease: - if (xev_d->evtype == XI_ButtonPress) + if (xev_d->evtype == XI_ButtonPress || xev_d->evtype == XI_TouchBegin) { GtkSettings *settings = gtk_settings_get_default (); int double_click_time; int double_click_distance; + int button; g_object_get (settings, "gtk-double-click-time", &double_click_time, "gtk-double-click-distance", &double_click_distance, NULL); - if (xev_d->detail == ui->button_click_number && + if (xev->evtype == XI_TouchBegin) + button = 1; + else + button = xev_d->detail; + + if (button == ui->button_click_number && xev_d->event == ui->button_click_window && xev_d->time < ui->button_click_time + double_click_time && ABS (xev_d->event_x - ui->button_click_x) <= double_click_distance && @@ -185,20 +193,22 @@ maybe_redirect_mouse_event (XEvent *xevent) else { gevent = gdk_event_new (GDK_BUTTON_PRESS); - ui->button_click_number = xev_d->detail; + ui->button_click_number = button; ui->button_click_window = xev_d->event; ui->button_click_time = xev_d->time; ui->button_click_x = xev_d->event_x; ui->button_click_y = xev_d->event_y; } + + gevent->button.button = button; } else { gevent = gdk_event_new (GDK_BUTTON_RELEASE); + gevent->button.button = xev_d->detail; } gevent->button.window = g_object_ref (gdk_window); - gevent->button.button = xev_d->detail; gevent->button.time = xev_d->time; gevent->button.x = xev_d->event_x; gevent->button.y = xev_d->event_y;