frames: Handle touch events
authorCarlos Garnacho <carlosg@gnome.org>
Sun, 15 Apr 2018 14:26:32 +0000 (16:26 +0200)
committerSimon McVittie <smcv@debian.org>
Sun, 8 Jul 2018 10:32:56 +0000 (11:32 +0100)
This is just done on wayland as it'll break horribly on X11, we let
this happen through pointer emulated events in XISelectEvents evmask
instead.

Some things had to be made slightly more generic to accomodate touch
events. The MetaFrames shall lock onto a single touch at a time, we
don't allow crazy stuff like multi-window drag nor multi-edge resizes.

Bug: https://bugzilla.gnome.org/show_bug.cgi?id=770185
Origin: upstream, 3.28.3, commit:47131b1dadc1a9b7afe9a22740ab69ce97fb02d6

Gbp-Pq: Name frames-Handle-touch-events.patch

src/ui/frames.c
src/ui/frames.h

index 577e4b28a09d85ed2aad898c5d5cdb7bedbacdc8..850f2850aea60e072ef2c94e07ade4d7d2ca6079 100644 (file)
@@ -71,6 +71,13 @@ static MetaFrameControl get_control  (MetaUIFrame       *frame,
 
 G_DEFINE_TYPE (MetaFrames, meta_frames, GTK_TYPE_WINDOW);
 
+enum {
+  META_ACTION_CLICK,
+  META_ACTION_RIGHT_CLICK,
+  META_ACTION_MIDDLE_CLICK,
+  META_ACTION_DOUBLE_CLICK
+};
+
 static GObject *
 meta_frames_constructor (GType                  gtype,
                          guint                  n_properties,
@@ -748,8 +755,11 @@ meta_frame_titlebar_event (MetaUIFrame        *frame,
 {
   MetaFrameFlags flags;
   Display *display;
-  guint32 evtime;
-  gfloat x, y;
+  uint32_t evtime;
+  float x, y;
+
+  g_assert (event->type == CLUTTER_BUTTON_PRESS ||
+            event->type == CLUTTER_TOUCH_BEGIN);
 
   display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
 
@@ -884,6 +894,8 @@ meta_frames_try_grab_op (MetaUIFrame *frame,
       frames->grab_x = grab_x;
       frames->grab_y = grab_y;
     }
+  else
+    frames->grab_touch = NULL;
 
   return ret;
 }
@@ -894,6 +906,7 @@ meta_frames_retry_grab_op (MetaFrames *frames,
 {
   Display *display;
   MetaGrabOp op;
+  gboolean ret;
 
   if (frames->current_grab_op == META_GRAB_OP_NONE)
     return TRUE;
@@ -902,16 +915,20 @@ meta_frames_retry_grab_op (MetaFrames *frames,
   frames->current_grab_op = META_GRAB_OP_NONE;
   display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
 
-  return meta_core_begin_grab_op (display,
-                                  frames->grab_frame->xwindow,
-                                  op,
-                                  FALSE,
-                                  TRUE,
-                                  frames->grab_frame->grab_button,
-                                  0,
-                                  time,
-                                  frames->grab_x,
-                                  frames->grab_y);
+  ret = meta_core_begin_grab_op (display,
+                                 frames->grab_frame->xwindow,
+                                 op,
+                                 FALSE,
+                                 TRUE,
+                                 frames->grab_frame->grab_button,
+                                 0,
+                                 time,
+                                 frames->grab_x,
+                                 frames->grab_y);
+  if (ret)
+    frames->grab_touch = NULL;
+
+  return ret;
 }
 
 static MetaGrabOp
@@ -940,18 +957,60 @@ grab_op_from_resize_control (MetaFrameControl control)
     }
 }
 
+static guint
+get_action (const ClutterEvent *event)
+{
+  if (event->type == CLUTTER_BUTTON_PRESS ||
+      event->type == CLUTTER_BUTTON_RELEASE)
+    {
+      switch (event->button.button)
+        {
+        case CLUTTER_BUTTON_PRIMARY:
+          if (clutter_event_get_click_count (event) == 2)
+            return META_ACTION_DOUBLE_CLICK;
+          else
+            return META_ACTION_CLICK;
+        case CLUTTER_BUTTON_SECONDARY:
+          return META_ACTION_RIGHT_CLICK;
+        case CLUTTER_BUTTON_MIDDLE:
+          return META_ACTION_MIDDLE_CLICK;
+        }
+    }
+  else if (event->type == CLUTTER_TOUCH_BEGIN ||
+           event->type == CLUTTER_TOUCH_UPDATE ||
+           event->type == CLUTTER_TOUCH_END)
+    {
+      return META_ACTION_CLICK;
+    }
+
+  g_assert_not_reached ();
+}
+
+static uint32_t
+get_button_number (const ClutterEvent *event)
+{
+  if (event->type == CLUTTER_TOUCH_BEGIN ||
+      event->type == CLUTTER_TOUCH_UPDATE ||
+      event->type == CLUTTER_TOUCH_END)
+    return -1;
+  else if (event->type == CLUTTER_BUTTON_PRESS ||
+           event->type == CLUTTER_BUTTON_RELEASE)
+    return clutter_event_get_button (event);
+
+  g_assert_not_reached ();
+}
+
 static gboolean
 meta_frame_left_click_event (MetaUIFrame        *frame,
                              const ClutterEvent *event)
 {
   Display *display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
   MetaFrameControl control;
-  guint32 evtime, button;
+  guint32 evtime;
   gfloat x, y;
 
   evtime = clutter_event_get_time (event);
   clutter_event_get_coords (event, &x, &y);
-  button = clutter_event_get_button (event);
   control = get_control (frame, x, y);
 
   switch (control)
@@ -962,7 +1021,7 @@ meta_frame_left_click_event (MetaUIFrame        *frame,
     case META_FRAME_CONTROL_DELETE:
     case META_FRAME_CONTROL_MENU:
     case META_FRAME_CONTROL_APPMENU:
-      frame->grab_button = button;
+      frame->grab_button = get_button_number (event);
       frame->button_state = META_BUTTON_STATE_PRESSED;
       frame->prelit_control = control;
       redraw_control (frame, control);
@@ -1050,21 +1109,24 @@ handle_press_event (MetaUIFrame        *frame,
 {
   MetaFrameControl control;
   Display *display;
-  guint evtime, button;
-  gfloat x, y;
+  uint32_t evtime, action;
+  float x, y;
+
+  g_assert (event->type == CLUTTER_BUTTON_PRESS ||
+            event->type == CLUTTER_TOUCH_BEGIN);
 
   display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
 
   evtime = clutter_event_get_time (event);
   clutter_event_get_coords (event, &x, &y);
   control = get_control (frame, x, y);
-  button = clutter_event_get_button (event);
+  action = get_action (event);
 
   /* don't do the rest of this if on client area */
   if (control == META_FRAME_CONTROL_CLIENT_AREA)
     return FALSE; /* not on the frame, just passed through from client */
 
-  if (button == 1 &&
+  if (action == META_ACTION_CLICK &&
       !(control == META_FRAME_CONTROL_MINIMIZE ||
         control == META_FRAME_CONTROL_DELETE ||
         control == META_FRAME_CONTROL_MAXIMIZE))
@@ -1079,8 +1141,7 @@ handle_press_event (MetaUIFrame        *frame,
    * if we double click the titlebar.
    */
   if (control == META_FRAME_CONTROL_TITLE &&
-      button == 1 &&
-      clutter_event_get_click_count (event) == 2)
+      action == META_ACTION_DOUBLE_CLICK)
     {
       meta_core_end_grab_op (display, evtime);
       return meta_frame_double_click_event (frame, event);
@@ -1089,15 +1150,15 @@ handle_press_event (MetaUIFrame        *frame,
   if (meta_core_get_grab_op (display) != META_GRAB_OP_NONE)
     return FALSE; /* already up to something */
 
-  frame->grab_button = button;
+  frame->grab_button = get_button_number (event);
 
-  switch (button)
+  switch (action)
     {
-    case 1:
+    case META_ACTION_CLICK:
       return meta_frame_left_click_event (frame, event);
-    case 2:
+    case META_ACTION_MIDDLE_CLICK:
       return meta_frame_middle_click_event (frame, (ClutterButtonEvent *) event);
-    case 3:
+    case META_ACTION_RIGHT_CLICK:
       return meta_frame_right_click_event (frame, (ClutterButtonEvent *) event);
     default:
       return FALSE;
@@ -1112,9 +1173,12 @@ handle_release_event (MetaUIFrame        *frame,
   guint32 evtime, button;
   gfloat x, y;
 
+  g_assert (event->type == CLUTTER_BUTTON_RELEASE ||
+            event->type == CLUTTER_TOUCH_END);
+
   evtime = clutter_event_get_time (event);
   clutter_event_get_coords (event, &x, &y);
-  button = clutter_event_get_button (event);
+  button = get_button_number (event);
 
   frame->frames->current_grab_op = META_GRAB_OP_NONE;
   meta_core_end_grab_op (display, evtime);
@@ -1265,6 +1329,9 @@ handle_motion_event (MetaUIFrame        *frame,
   guint32 evtime;
   gfloat x, y;
 
+  g_assert (event->type == CLUTTER_MOTION ||
+            event->type == CLUTTER_TOUCH_UPDATE);
+
   modifiers = clutter_event_get_state (event);
   evtime = clutter_event_get_time (event);
   clutter_event_get_coords (event, &x, &y);
@@ -1286,8 +1353,10 @@ handle_motion_event (MetaUIFrame        *frame,
       meta_ui_frame_update_prelit_control (frame, control);
     }
 
-  if ((modifiers & CLUTTER_BUTTON1_MASK) &&
-      frames->current_grab_op != META_GRAB_OP_NONE)
+  if (frames->current_grab_op != META_GRAB_OP_NONE &&
+      (event->type == CLUTTER_TOUCH_UPDATE ||
+       (event->type == CLUTTER_MOTION &&
+        (modifiers & CLUTTER_BUTTON1_MASK))))
     meta_frames_retry_grab_op (frames, evtime);
 
   return TRUE;
@@ -1542,13 +1611,50 @@ gboolean
 meta_ui_frame_handle_event (MetaUIFrame *frame,
                             const ClutterEvent *event)
 {
+  if (event->type == CLUTTER_TOUCH_BEGIN ||
+      event->type == CLUTTER_TOUCH_UPDATE ||
+      event->type == CLUTTER_TOUCH_END)
+    {
+      ClutterEventSequence *sequence;
+      MetaFrames *frames = frame->frames;
+
+      /* In X11, mutter sets up passive touch grabs which basically
+       * means we handle those events twice (once through the passive
+       * grab, and then through XISelectEvents).
+       *
+       * Receiving touch events here means we are going through the
+       * former, but passive grabs are exclusively for gesture
+       * recognition purposes.
+       *
+       * We do actually want this to happen though the regular event
+       * selection paths to avoid breaking internal state, which means
+       * we will get pointer events, because we don't select for XI_Touch*.
+       */
+      if (!meta_is_wayland_compositor ())
+        return FALSE;
+
+      sequence = clutter_event_get_event_sequence (event);
+
+      /* Lock onto a single touch */
+      if (frames->grab_touch && frames->grab_touch != sequence)
+        return FALSE;
+
+      if (event->type == CLUTTER_TOUCH_BEGIN)
+        frames->grab_touch = sequence;
+      else if (event->type == CLUTTER_TOUCH_END)
+        frames->grab_touch = NULL;
+    }
+
   switch (event->any.type)
     {
     case CLUTTER_BUTTON_PRESS:
+    case CLUTTER_TOUCH_BEGIN:
       return handle_press_event (frame, event);
     case CLUTTER_BUTTON_RELEASE:
+    case CLUTTER_TOUCH_END:
       return handle_release_event (frame, event);
     case CLUTTER_MOTION:
+    case CLUTTER_TOUCH_UPDATE:
       return handle_motion_event (frame, event);
     case CLUTTER_ENTER:
       return handle_enter_notify_event (frame, (ClutterCrossingEvent *) event);
index d9aaae22fb8528cd656f8b49b2f7ebb2be41f00e..397f350f61445ef1571a9fbf570f62a6ba2d6610 100644 (file)
@@ -99,6 +99,8 @@ struct _MetaFrames
   guint grab_button;
   gdouble grab_x;
   gdouble grab_y;
+
+  ClutterEventSequence *grab_touch;
 };
 
 struct _MetaFramesClass