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
This commit is contained in:
Carlos Garnacho 2018-04-15 16:26:32 +02:00
parent 3561082aba
commit ac20bf2000
2 changed files with 137 additions and 29 deletions

View File

@ -71,6 +71,13 @@ static MetaFrameControl get_control (MetaUIFrame *frame,
G_DEFINE_TYPE (MetaFrames, meta_frames, GTK_TYPE_WINDOW); 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 * static GObject *
meta_frames_constructor (GType gtype, meta_frames_constructor (GType gtype,
guint n_properties, guint n_properties,
@ -748,8 +755,11 @@ meta_frame_titlebar_event (MetaUIFrame *frame,
{ {
MetaFrameFlags flags; MetaFrameFlags flags;
Display *display; Display *display;
guint32 evtime; uint32_t evtime;
gfloat x, y; float x, y;
g_assert (event->type == CLUTTER_BUTTON_PRESS ||
event->type == CLUTTER_TOUCH_BEGIN);
display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); 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_x = grab_x;
frames->grab_y = grab_y; frames->grab_y = grab_y;
} }
else
frames->grab_touch = NULL;
return ret; return ret;
} }
@ -894,6 +906,7 @@ meta_frames_retry_grab_op (MetaFrames *frames,
{ {
Display *display; Display *display;
MetaGrabOp op; MetaGrabOp op;
gboolean ret;
if (frames->current_grab_op == META_GRAB_OP_NONE) if (frames->current_grab_op == META_GRAB_OP_NONE)
return TRUE; return TRUE;
@ -902,16 +915,20 @@ meta_frames_retry_grab_op (MetaFrames *frames,
frames->current_grab_op = META_GRAB_OP_NONE; frames->current_grab_op = META_GRAB_OP_NONE;
display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
return meta_core_begin_grab_op (display, ret = meta_core_begin_grab_op (display,
frames->grab_frame->xwindow, frames->grab_frame->xwindow,
op, op,
FALSE, FALSE,
TRUE, TRUE,
frames->grab_frame->grab_button, frames->grab_frame->grab_button,
0, 0,
time, time,
frames->grab_x, frames->grab_x,
frames->grab_y); frames->grab_y);
if (ret)
frames->grab_touch = NULL;
return ret;
} }
static MetaGrabOp 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 static gboolean
meta_frame_left_click_event (MetaUIFrame *frame, meta_frame_left_click_event (MetaUIFrame *frame,
const ClutterEvent *event) const ClutterEvent *event)
{ {
Display *display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); Display *display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
MetaFrameControl control; MetaFrameControl control;
guint32 evtime, button; guint32 evtime;
gfloat x, y; gfloat x, y;
evtime = clutter_event_get_time (event); evtime = clutter_event_get_time (event);
clutter_event_get_coords (event, &x, &y); clutter_event_get_coords (event, &x, &y);
button = clutter_event_get_button (event);
control = get_control (frame, x, y); control = get_control (frame, x, y);
switch (control) switch (control)
@ -962,7 +1021,7 @@ meta_frame_left_click_event (MetaUIFrame *frame,
case META_FRAME_CONTROL_DELETE: case META_FRAME_CONTROL_DELETE:
case META_FRAME_CONTROL_MENU: case META_FRAME_CONTROL_MENU:
case META_FRAME_CONTROL_APPMENU: case META_FRAME_CONTROL_APPMENU:
frame->grab_button = button; frame->grab_button = get_button_number (event);
frame->button_state = META_BUTTON_STATE_PRESSED; frame->button_state = META_BUTTON_STATE_PRESSED;
frame->prelit_control = control; frame->prelit_control = control;
redraw_control (frame, control); redraw_control (frame, control);
@ -1050,21 +1109,24 @@ handle_press_event (MetaUIFrame *frame,
{ {
MetaFrameControl control; MetaFrameControl control;
Display *display; Display *display;
guint evtime, button; uint32_t evtime, action;
gfloat x, y; float x, y;
g_assert (event->type == CLUTTER_BUTTON_PRESS ||
event->type == CLUTTER_TOUCH_BEGIN);
display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
evtime = clutter_event_get_time (event); evtime = clutter_event_get_time (event);
clutter_event_get_coords (event, &x, &y); clutter_event_get_coords (event, &x, &y);
control = get_control (frame, 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 */ /* don't do the rest of this if on client area */
if (control == META_FRAME_CONTROL_CLIENT_AREA) if (control == META_FRAME_CONTROL_CLIENT_AREA)
return FALSE; /* not on the frame, just passed through from client */ 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_MINIMIZE ||
control == META_FRAME_CONTROL_DELETE || control == META_FRAME_CONTROL_DELETE ||
control == META_FRAME_CONTROL_MAXIMIZE)) control == META_FRAME_CONTROL_MAXIMIZE))
@ -1079,8 +1141,7 @@ handle_press_event (MetaUIFrame *frame,
* if we double click the titlebar. * if we double click the titlebar.
*/ */
if (control == META_FRAME_CONTROL_TITLE && if (control == META_FRAME_CONTROL_TITLE &&
button == 1 && action == META_ACTION_DOUBLE_CLICK)
clutter_event_get_click_count (event) == 2)
{ {
meta_core_end_grab_op (display, evtime); meta_core_end_grab_op (display, evtime);
return meta_frame_double_click_event (frame, event); 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) if (meta_core_get_grab_op (display) != META_GRAB_OP_NONE)
return FALSE; /* already up to something */ 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); return meta_frame_left_click_event (frame, event);
case 2: case META_ACTION_MIDDLE_CLICK:
return meta_frame_middle_click_event (frame, (ClutterButtonEvent *) event); 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); return meta_frame_right_click_event (frame, (ClutterButtonEvent *) event);
default: default:
return FALSE; return FALSE;
@ -1112,9 +1173,12 @@ handle_release_event (MetaUIFrame *frame,
guint32 evtime, button; guint32 evtime, button;
gfloat x, y; gfloat x, y;
g_assert (event->type == CLUTTER_BUTTON_RELEASE ||
event->type == CLUTTER_TOUCH_END);
evtime = clutter_event_get_time (event); evtime = clutter_event_get_time (event);
clutter_event_get_coords (event, &x, &y); 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; frame->frames->current_grab_op = META_GRAB_OP_NONE;
meta_core_end_grab_op (display, evtime); meta_core_end_grab_op (display, evtime);
@ -1265,6 +1329,9 @@ handle_motion_event (MetaUIFrame *frame,
guint32 evtime; guint32 evtime;
gfloat x, y; gfloat x, y;
g_assert (event->type == CLUTTER_MOTION ||
event->type == CLUTTER_TOUCH_UPDATE);
modifiers = clutter_event_get_state (event); modifiers = clutter_event_get_state (event);
evtime = clutter_event_get_time (event); evtime = clutter_event_get_time (event);
clutter_event_get_coords (event, &x, &y); clutter_event_get_coords (event, &x, &y);
@ -1286,8 +1353,10 @@ handle_motion_event (MetaUIFrame *frame,
meta_ui_frame_update_prelit_control (frame, control); meta_ui_frame_update_prelit_control (frame, control);
} }
if ((modifiers & CLUTTER_BUTTON1_MASK) && if (frames->current_grab_op != META_GRAB_OP_NONE &&
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); meta_frames_retry_grab_op (frames, evtime);
return TRUE; return TRUE;
@ -1542,13 +1611,50 @@ gboolean
meta_ui_frame_handle_event (MetaUIFrame *frame, meta_ui_frame_handle_event (MetaUIFrame *frame,
const ClutterEvent *event) 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) switch (event->any.type)
{ {
case CLUTTER_BUTTON_PRESS: case CLUTTER_BUTTON_PRESS:
case CLUTTER_TOUCH_BEGIN:
return handle_press_event (frame, event); return handle_press_event (frame, event);
case CLUTTER_BUTTON_RELEASE: case CLUTTER_BUTTON_RELEASE:
case CLUTTER_TOUCH_END:
return handle_release_event (frame, event); return handle_release_event (frame, event);
case CLUTTER_MOTION: case CLUTTER_MOTION:
case CLUTTER_TOUCH_UPDATE:
return handle_motion_event (frame, event); return handle_motion_event (frame, event);
case CLUTTER_ENTER: case CLUTTER_ENTER:
return handle_enter_notify_event (frame, (ClutterCrossingEvent *) event); return handle_enter_notify_event (frame, (ClutterCrossingEvent *) event);

View File

@ -99,6 +99,8 @@ struct _MetaFrames
guint grab_button; guint grab_button;
gdouble grab_x; gdouble grab_x;
gdouble grab_y; gdouble grab_y;
ClutterEventSequence *grab_touch;
}; };
struct _MetaFramesClass struct _MetaFramesClass