frames: Keep information about the ongoing grab operation, and retry if needed.

When a passive touch grab is rejected over the frame, management is punted to
the frame itself, and pointer events emulated, but the attempt to transfer the
grab from the GDK connection to the Clutter one fails with AlreadyGrabbed, and
will fail until the Clutter connection receives the XI_TouchEnd resulting from
XIRejectTouch, gotten after the XI_ButtonPress on the GDK connection.

In order to bypass this shortcoming, store the current grab operation on the
frame as long as the button is pressed, so it is retried once on the next
motion event happening during frame dragging, that will have a recent enough
timestamp to succeed. If no grabbing succeeded, the current grab operation
data will be reset on GDK_BUTTON_RELEASE.
This commit is contained in:
Carlos Garnacho 2014-06-19 23:26:15 +02:00
parent 66cdb1bb71
commit 2df807549e
3 changed files with 83 additions and 19 deletions

View File

@ -1136,6 +1136,64 @@ meta_frame_right_click_event(MetaUIFrame *frame,
return meta_frame_titlebar_event (frame, event, action);
}
static gboolean
meta_frames_try_grab_op (MetaFrames *frames,
MetaUIFrame *frame,
MetaGrabOp op,
gdouble grab_x,
gdouble grab_y,
guint32 time)
{
Display *display;
gboolean ret;
display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
ret = meta_core_begin_grab_op (display,
frame->xwindow,
op,
FALSE,
TRUE,
frame->grab_button,
0,
time,
grab_x, grab_y);
if (!ret)
{
frames->current_grab_op = op;
frames->grab_frame = frame;
frames->grab_x = grab_x;
frames->grab_y = grab_y;
}
return ret;
}
static gboolean
meta_frames_retry_grab_op (MetaFrames *frames,
guint time)
{
Display *display;
MetaGrabOp op;
if (frames->current_grab_op == META_GRAB_OP_NONE)
return TRUE;
op = frames->current_grab_op;
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);
}
static gboolean
meta_frames_button_press_event (GtkWidget *widget,
GdkEventButton *event)
@ -1190,6 +1248,8 @@ meta_frames_button_press_event (GtkWidget *widget,
if (meta_core_get_grab_op (display) != META_GRAB_OP_NONE)
return FALSE; /* already up to something */
frame->grab_button = event->button;
if (event->button == 1 &&
(control == META_FRAME_CONTROL_MAXIMIZE ||
control == META_FRAME_CONTROL_UNMAXIMIZE ||
@ -1293,16 +1353,9 @@ meta_frames_button_press_event (GtkWidget *widget,
break;
}
meta_core_begin_grab_op (display,
frame->xwindow,
op,
TRUE,
TRUE,
event->button,
0,
event->time,
event->x_root,
event->y_root);
meta_frames_try_grab_op (frames, frame, op,
event->x_root, event->y_root,
event->time);
}
else if (control == META_FRAME_CONTROL_TITLE &&
event->button == 1)
@ -1315,16 +1368,10 @@ meta_frames_button_press_event (GtkWidget *widget,
if (flags & META_FRAME_ALLOWS_MOVE)
{
meta_core_begin_grab_op (display,
frame->xwindow,
meta_frames_try_grab_op (frames, frame,
META_GRAB_OP_MOVING,
TRUE,
TRUE,
event->button,
0,
event->time,
event->x_root,
event->y_root);
event->x_root, event->y_root,
event->time);
}
}
else if (event->button == 2)
@ -1349,6 +1396,7 @@ meta_frames_button_release_event (GtkWidget *widget,
frames = META_FRAMES (widget);
display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
frames->current_grab_op = META_GRAB_OP_NONE;
frame = meta_frames_lookup_window (frames, GDK_WINDOW_XID (event->window));
if (frame == NULL)
@ -1560,6 +1608,10 @@ meta_frames_motion_notify_event (GtkWidget *widget,
meta_frames_update_prelit_control (frames, frame, control);
}
if ((event->state & GDK_BUTTON1_MASK) &&
frames->current_grab_op != META_GRAB_OP_NONE)
meta_frames_retry_grab_op (frames, event->time);
return TRUE;
}

View File

@ -100,6 +100,12 @@ struct _MetaFrames
GtkStyleContext *normal_style;
GHashTable *style_variants;
MetaGrabOp current_grab_op;
MetaUIFrame *grab_frame;
guint grab_button;
gdouble grab_x;
gdouble grab_y;
Window grab_xwindow;
};

View File

@ -205,6 +205,12 @@ maybe_redirect_mouse_event (XEvent *xevent)
gevent = gdk_event_new (GDK_MOTION_NOTIFY);
gevent->motion.type = GDK_MOTION_NOTIFY;
gevent->motion.window = g_object_ref (gdk_window);
gevent->motion.time = xev_d->time;
gevent->motion.x_root = xev_d->root_x;
gevent->motion.y_root = xev_d->root_y;
if (XIMaskIsSet (xev_d->buttons.mask, 1))
gevent->motion.state |= GDK_BUTTON1_MASK;
break;
case XI_Enter:
case XI_Leave: