From ac20bf2000857fe23e61f004080458f8317add9e Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Sun, 15 Apr 2018 16:26:32 +0200 Subject: [PATCH] frames: Handle touch events 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. https://bugzilla.gnome.org/show_bug.cgi?id=770185 --- src/ui/frames.c | 164 +++++++++++++++++++++++++++++++++++++++--------- src/ui/frames.h | 2 + 2 files changed, 137 insertions(+), 29 deletions(-) diff --git a/src/ui/frames.c b/src/ui/frames.c index 577e4b28a..850f2850a 100644 --- a/src/ui/frames.c +++ b/src/ui/frames.c @@ -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); diff --git a/src/ui/frames.h b/src/ui/frames.h index d9aaae22f..397f350f6 100644 --- a/src/ui/frames.h +++ b/src/ui/frames.h @@ -99,6 +99,8 @@ struct _MetaFrames guint grab_button; gdouble grab_x; gdouble grab_y; + + ClutterEventSequence *grab_touch; }; struct _MetaFramesClass