Blob Blame History Raw
From 9eeb657cd98eeb198e50130b4081b2b34b1d7469 Mon Sep 17 00:00:00 2001
From: Carlos Garnacho <carlosg@gnome.org>
Date: Tue, 11 Feb 2014 19:24:12 +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.
---
 src/core/display.c | 51 +++++++++++++++++++++++++++++++++++++++------------
 src/ui/ui.c        | 18 ++++++++++++++----
 2 files changed, 53 insertions(+), 16 deletions(-)

diff --git a/src/core/display.c b/src/core/display.c
index d611314..435d9d9 100644
--- a/src/core/display.c
+++ b/src/core/display.c
@@ -1838,6 +1838,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:
@@ -2041,6 +2044,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->type == XI_KeyPress) || (input_event->type == XI_ButtonPress)))
@@ -2076,19 +2080,32 @@ 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) &&
-               display->grab_button != device_event->detail &&
+               display->grab_button != button &&
                display->grab_window == window) ||
               grab_op_is_keyboard (display->grab_op))
             {
@@ -2130,8 +2147,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
@@ -2152,7 +2168,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
@@ -2168,7 +2184,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)
                     {
@@ -2210,21 +2226,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);
                 }
 
@@ -2267,7 +2283,7 @@ event_callback (XEvent   *event,
                                               META_GRAB_OP_MOVING,
                                               TRUE,
                                               FALSE,
-                                              device_event->detail,
+                                              button,
                                               0,
                                               device_event->time,
                                               device_event->root_x,
@@ -2438,6 +2454,14 @@ event_callback (XEvent   *event,
             filter_out_event = bypass_compositor = TRUE;
           break;
 #endif /* HAVE_XI23 */
+        case XI_TouchUpdate:
+        case XI_TouchEnd:
+          /* 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.
+           */
+          filter_out_event = TRUE;
+          break;
         }
     }
   else
@@ -2963,6 +2987,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:
diff --git a/src/ui/ui.c b/src/ui/ui.c
index af28263..582fdce 100644
--- a/src/ui/ui.c
+++ b/src/ui/ui.c
@@ -126,6 +126,7 @@ maybe_redirect_mouse_event (XEvent *xevent)
 
   switch (xev->evtype)
     {
+    case XI_TouchBegin:
     case XI_ButtonPress:
     case XI_ButtonRelease:
     case XI_Motion:
@@ -162,20 +163,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 &&
@@ -188,20 +196,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;
-- 
1.8.5.3