Compare commits

...

10 Commits

Author SHA1 Message Date
Ray Strode
8904c55a86 compositor: get rid of initial black rectangle on xwayland clients
At the moment when a legacy X11 application client is first mapped it
gets briefly shown as a black rectangle before redrawing.

That behavior is because the initial Configure operation on the window
leads to the surface immediately getting full window damage posted by
Xwayland before the client has a chance to draw.

This commit changes mutter to send a sync request right away, when a
window first shows up, then waits until the reply from the application
before showing the window.

It leverages the same code paths already used to prevent flicker from
subsequent (non-initial) resizes on the window.
2018-03-31 17:50:45 -04:00
Ray Strode
7f4f5f5c4c window: ensure window titlebars are drawn in sync during resize
mutter synchronizes drawing and resizes with Xwayland applications using
XSYNC counters, but it doesn't synchronize drawing and resizes with its
own titlebar painting code.

This commit makes mutter wait until the titlebar finishes drawing before
it unfreezes the corresponding window actor.  This ensures the titlebar
and client area don't get out of sync with each other.
2018-03-29 14:37:09 +00:00
Ray Strode
0d1cee5123 compositor: make sync_updates_frozen no-op for unmanaged windows
Right now meta_compositor_sync_updates_frozen will crash if it's called
on a window that's not yet managed.

For future convenience, make it be a no-op instead, in the same way:

meta_compositor_window_shape_changed
and
meta_compositor_window_opacity_changed

are no-ops for unmanaged windows.
2018-03-29 14:37:08 +00:00
Ray Strode
24b9f2fabc x11: Don't proceed with interactive resize until last iteration finishes
As mentioned in the previous commit, resizing an Xwayland client is an
asynchronous operation.

This commit makes sure we don't allow there to be two resizes in flight
at the same time.  We shouldn't resize the window until we've drawn the
window from this step of the resize.
2018-03-29 14:37:08 +00:00
Ray Strode
fbf7e0073e compositor: don't recalculate shape and masks until surface arrives
mutter uses the windows "buffer rect" to determine how big to draw the
titlebar, and what masks to apply to the window for opaque areas.

That buffer rect is updated the moment we initiate a resize operation on
a window, but not actually accurate until the resize operation finishes

This commit makes sure the window actor keeps using its old shapes and
masks until any pending resize operation finishes.
2018-03-29 14:37:08 +00:00
Ray Strode
db1adab828 window: track when resize is pending via new flag
For Xwayland clients, resizing is an asynchronous operation.  First, the
window manager part of mutter resizes the frame and client window, then
Xwayland waits for damage from the client, and commits a new surface to
the display server part of mutter.

In order to prevent flicker, we need to know when an initiated resize
operation has fully come out the other end of that multi-step process as
a new surface.

This commit lays the plumbing work for preventing the aforementioned
flicker problem, by adding a new resize_pending flag (and associated
accessors), and setting the flag appropriately based on when a resize
is started and subsequently when a new surface is attached.
2018-03-29 14:37:08 +00:00
Ray Strode
8369fde7f8 compositor: delay surface commits for frozen actors
Right now we defer processing damage on frozen actors, but don't defer
processing surface changes on frozen actors (like for instance when the
surface is changed during a resize).

This commit stops the compositor from processing xwayland client events
for a surface when the actor associated with it is frozen waiting for
the client to draw on the underlying window.

By deferring xwayland client event processing, we'll ensure the surface
commit comes after the corresponding sync counter update from the app,
and ensure we don't update the surface associated with an X window
until after client has finished drawing.
2018-03-29 14:37:07 +00:00
Ray Strode
80562bbb10 compositor: make set_frozen a vfunc
MetaSurfaceActor can be made "frozen" via it's set_frozen method.

That method could be useful as virtual function, so that the wayland
subclass can tie into it and freeze the underlying surface.

This commit does the prerequiste work of making set_frozen a vfunc.
A future commit will change the wayland subclass code to leverage
the vfunc.
2018-03-29 14:37:07 +00:00
Ray Strode
c21d12c4ac compositor: recompute shape when surface is changed
Anytime the surface is changed, we need to rebuild our masks and
recalculate our shapes since the new surface may have a different
size.

If the actor gets redrawn for any reason before that computation
happens then it will misrender.

This commit makes sure we do the recomputation right away, before
any painting might occur.
2018-03-29 14:37:07 +00:00
Ray Strode
3488015d17 window: always disarm XSYNC watchdog when X client responds
At the moment we only disarm the watchdog timer set up for SYNC counter
requests if we're in the middle of a resize operation.

It's possible that the resize operation finished prematurely by the user
letting go of the mouse before the client responded.  If that happens, when the
client finally updates mutter will erroneously still have the watchdog timer
engaged from before until it times out, leading to resizes for the next second
or so to not get processed, and the client to get blacklisted from future sync
requests.
2018-03-29 14:37:06 +00:00
12 changed files with 206 additions and 27 deletions

View File

@@ -684,6 +684,9 @@ meta_compositor_sync_updates_frozen (MetaCompositor *compositor,
MetaWindow *window)
{
MetaWindowActor *window_actor = META_WINDOW_ACTOR (meta_window_get_compositor_private (window));
if (!window_actor)
return;
meta_window_actor_sync_updates_frozen (window_actor);
}

View File

@@ -99,6 +99,20 @@ meta_surface_actor_wayland_add_frame_callbacks (MetaSurfaceActorWayland *self,
wl_list_insert_list (&priv->frame_callback_list, frame_callbacks);
}
static void
meta_surface_actor_wayland_set_frozen (MetaSurfaceActor *actor,
gboolean is_frozen)
{
MetaSurfaceActorWayland *self = META_SURFACE_ACTOR_WAYLAND (actor);
MetaWaylandSurface *surface = meta_surface_actor_wayland_get_surface (self);
META_SURFACE_ACTOR_CLASS (meta_surface_actor_wayland_parent_class)->set_frozen (actor,
is_frozen);
if (surface)
meta_wayland_surface_set_frozen (surface, is_frozen);
}
static MetaWindow *
meta_surface_actor_wayland_get_window (MetaSurfaceActor *actor)
{
@@ -111,6 +125,17 @@ meta_surface_actor_wayland_get_window (MetaSurfaceActor *actor)
return surface->window;
}
static void
meta_surface_actor_wayland_size_changed (MetaSurfaceActor *actor)
{
MetaWindow *window;
window = meta_surface_actor_wayland_get_window (actor);
if (window)
meta_window_set_resize_pending (window, FALSE);
}
static void
meta_surface_actor_wayland_get_preferred_width (ClutterActor *actor,
gfloat for_height,
@@ -220,8 +245,11 @@ meta_surface_actor_wayland_class_init (MetaSurfaceActorWaylandClass *klass)
surface_actor_class->set_unredirected = meta_surface_actor_wayland_set_unredirected;
surface_actor_class->is_unredirected = meta_surface_actor_wayland_is_unredirected;
surface_actor_class->set_frozen = meta_surface_actor_wayland_set_frozen;
surface_actor_class->get_window = meta_surface_actor_wayland_get_window;
surface_actor_class->size_changed = meta_surface_actor_wayland_size_changed;
object_class->dispose = meta_surface_actor_wayland_dispose;
}

View File

@@ -31,6 +31,9 @@ struct _MetaSurfaceActorPrivate
static void cullable_iface_init (MetaCullableInterface *iface);
void meta_surface_actor_real_set_frozen (MetaSurfaceActor *self,
gboolean frozen);
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (MetaSurfaceActor, meta_surface_actor, CLUTTER_TYPE_ACTOR,
G_IMPLEMENT_INTERFACE (META_TYPE_CULLABLE, cullable_iface_init));
@@ -121,6 +124,8 @@ meta_surface_actor_class_init (MetaSurfaceActorClass *klass)
object_class->dispose = meta_surface_actor_dispose;
actor_class->pick = meta_surface_actor_pick;
klass->set_frozen = meta_surface_actor_real_set_frozen;
signals[REPAINT_SCHEDULED] = g_signal_new ("repaint-scheduled",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
@@ -130,8 +135,8 @@ meta_surface_actor_class_init (MetaSurfaceActorClass *klass)
signals[SIZE_CHANGED] = g_signal_new ("size-changed",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
0,
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (MetaSurfaceActorClass, size_changed),
NULL, NULL, NULL,
G_TYPE_NONE, 0);
@@ -331,6 +336,13 @@ meta_surface_actor_is_visible (MetaSurfaceActor *self)
void
meta_surface_actor_set_frozen (MetaSurfaceActor *self,
gboolean frozen)
{
return META_SURFACE_ACTOR_GET_CLASS (self)->set_frozen (self, frozen);
}
void
meta_surface_actor_real_set_frozen (MetaSurfaceActor *self,
gboolean frozen)
{
MetaSurfaceActorPrivate *priv = self->priv;

View File

@@ -36,7 +36,12 @@ struct _MetaSurfaceActorClass
gboolean unredirected);
gboolean (* is_unredirected) (MetaSurfaceActor *actor);
void (* set_frozen) (MetaSurfaceActor *actor,
gboolean is_frozen);
MetaWindow *(* get_window) (MetaSurfaceActor *actor);
void (* size_changed) (MetaSurfaceActor *actor);
};
struct _MetaSurfaceActor

View File

@@ -53,6 +53,8 @@ struct _MetaWindowActorPrivate
MetaWindow *window;
MetaCompositor *compositor;
MetaCompEffect pending_effect;
MetaSurfaceActor *surface;
/* MetaShadowFactory only caches shadows that are actually in use;
@@ -117,6 +119,7 @@ struct _MetaWindowActorPrivate
guint updates_frozen : 1;
guint first_frame_state : 2; /* FirstFrameState */
guint has_pending_effect : 1;
};
typedef struct _FrameData FrameData;
@@ -296,6 +299,7 @@ surface_size_changed (MetaSurfaceActor *actor,
MetaWindowActor *self = META_WINDOW_ACTOR (user_data);
meta_window_actor_update_shape (self);
meta_window_actor_handle_updates (self);
}
static void
@@ -345,8 +349,11 @@ meta_window_actor_freeze (MetaWindowActor *self)
{
MetaWindowActorPrivate *priv = self->priv;
if (priv->freeze_count == 0 && priv->surface)
meta_surface_actor_set_frozen (priv->surface, TRUE);
if (priv->freeze_count == 0)
{
if (priv->surface)
meta_surface_actor_set_frozen (priv->surface, TRUE);
}
priv->freeze_count ++;
}
@@ -359,6 +366,12 @@ meta_window_actor_sync_thawed_state (MetaWindowActor *self)
if (priv->first_frame_state == INITIALLY_FROZEN)
priv->first_frame_state = DRAWING_FIRST_FRAME;
if (priv->has_pending_effect)
{
meta_window_actor_show (self, priv->pending_effect);
priv->has_pending_effect = FALSE;
}
if (priv->surface)
meta_surface_actor_set_frozen (priv->surface, FALSE);
@@ -1317,6 +1330,13 @@ meta_window_actor_show (MetaWindowActor *self,
g_return_if_fail (!priv->visible);
if (is_frozen (self))
{
priv->pending_effect = effect;
priv->has_pending_effect = TRUE;
return;
}
self->priv->visible = TRUE;
switch (effect)
@@ -1918,10 +1938,11 @@ meta_window_actor_handle_updates (MetaWindowActor *self)
{
MetaWindowActorPrivate *priv = self->priv;
if (is_frozen (self))
if (is_frozen (self) || meta_window_resize_is_pending (priv->window))
{
/* The window is frozen due to a pending animation: we'll wait until
* the animation finishes to reshape and repair the window */
/* If the window is frozen due to a pending animation or a resize is
* pending then we'll wait until the animation or resize finishes to
* reshape and repair the window */
return;
}

View File

@@ -407,6 +407,12 @@ struct _MetaWindow
/* whether or not the window is from a program running on another machine */
guint is_remote : 1;
/* if TRUE, the X server hasn't yet committed a new buffer following resize of the frame/client window */
guint resize_pending : 1;
/* if TRUE, the window frame has a redraw queued */
guint frame_redraw_pending : 1;
/* if non-NULL, the bounds of the window frame */
cairo_region_t *frame_bounds;
@@ -781,6 +787,12 @@ void meta_window_move_resize_internal (MetaWindow *window,
MetaMoveResizeFlags flags,
int gravity,
MetaRectangle frame_rect);
void meta_window_set_resize_pending (MetaWindow *window,
gboolean is_resize_pending);
gboolean meta_window_resize_is_pending (MetaWindow *window);
void meta_window_set_frame_redraw_pending (MetaWindow *window,
gboolean is_frame_redraw_pending);
void meta_window_grab_op_began (MetaWindow *window, MetaGrabOp op);
void meta_window_grab_op_ended (MetaWindow *window, MetaGrabOp op);

View File

@@ -3730,6 +3730,9 @@ meta_window_updates_are_frozen (MetaWindow *window)
if (window->sync_request_serial < window->sync_request_wait_serial)
return TRUE;
if (window->frame_redraw_pending)
return TRUE;
return FALSE;
}
@@ -6311,6 +6314,28 @@ meta_window_update_resize (MetaWindow *window,
update_resize (window, snap, x, y, force);
}
void
meta_window_set_resize_pending (MetaWindow *window,
gboolean is_resize_pending)
{
window->resize_pending = is_resize_pending;
}
gboolean
meta_window_resize_is_pending (MetaWindow *window)
{
return window->resize_pending;
}
void
meta_window_set_frame_redraw_pending (MetaWindow *window,
gboolean is_frame_redraw_pending)
{
window->frame_redraw_pending = is_frame_redraw_pending;
meta_compositor_sync_updates_frozen (window->display->compositor, window);
}
static void
end_grab_op (MetaWindow *window,
const ClutterEvent *event)

View File

@@ -149,6 +149,7 @@ static void
invalidate_whole_window (MetaUIFrame *frame)
{
gdk_window_invalidate_rect (frame->window, NULL, FALSE);
meta_window_set_frame_redraw_pending (frame->meta_window, TRUE);
}
static MetaStyleInfo *
@@ -484,6 +485,27 @@ meta_ui_frame_attach_style (MetaUIFrame *frame)
variant));
}
static void
after_paint (GdkFrameClock *frame_clock,
MetaUIFrame *frame)
{
MetaFrames *frames = frame->frames;
MetaWindow *window = frame->meta_window;
/* Make sure the damage is posted to the X server before
* we mark the frame redraw finished and unfreeze, so there's
* a wayland surface commit waiting for us at unfreeze time
*/
if (meta_is_wayland_compositor ())
gdk_display_sync (gtk_widget_get_display (GTK_WIDGET (frames)));
meta_window_set_frame_redraw_pending (window, FALSE);
g_signal_handlers_disconnect_by_func (G_OBJECT (gdk_window_get_frame_clock (frame->window)),
G_CALLBACK (after_paint),
frame);
}
MetaUIFrame *
meta_frames_manage_window (MetaFrames *frames,
MetaWindow *meta_window,
@@ -531,6 +553,10 @@ meta_ui_frame_unmanage (MetaUIFrame *frame)
frame->xwindow,
META_CURSOR_DEFAULT);
g_signal_handlers_disconnect_by_func (G_OBJECT (gdk_window_get_frame_clock (frame->window)),
G_CALLBACK (after_paint),
frame);
gdk_window_set_user_data (frame->window, NULL);
g_hash_table_remove (frames->frames, &frame->xwindow);
@@ -1396,6 +1422,11 @@ meta_frames_draw (GtkWidget *widget,
meta_ui_frame_paint (frame, cr);
cairo_region_destroy (region);
g_signal_connect (G_OBJECT (gdk_window_get_frame_clock (frame->window)),
"after-paint",
G_CALLBACK (after_paint),
frame);
return TRUE;
}

View File

@@ -703,6 +703,9 @@ cleanup:
static void
meta_wayland_surface_commit (MetaWaylandSurface *surface)
{
if (surface->is_frozen)
return;
/*
* If this is a sub-surface and it is in effective synchronous mode, only
* cache the pending surface state until either one of the following two
@@ -1099,6 +1102,7 @@ meta_wayland_surface_set_window (MetaWaylandSurface *surface,
"position-changed",
G_CALLBACK (window_position_changed),
surface, 0);
meta_window_set_resize_pending (window, FALSE);
}
}
@@ -1719,3 +1723,16 @@ meta_wayland_surface_is_shortcuts_inhibited (MetaWaylandSurface *surface,
return g_hash_table_contains (surface->shortcut_inhibited_seats, seat);
}
void
meta_wayland_surface_set_frozen (MetaWaylandSurface *surface,
gboolean is_frozen)
{
if (surface->is_frozen == is_frozen)
return;
surface->is_frozen = is_frozen;
if (!surface->is_frozen && surface->pending)
meta_wayland_surface_commit (surface);
}

View File

@@ -203,6 +203,8 @@ struct _MetaWaylandSurface
/* table of seats for which shortcuts are inhibited */
GHashTable *shortcut_inhibited_seats;
guint32 is_frozen : 1;
};
void meta_wayland_shell_init (MetaWaylandCompositor *compositor);
@@ -302,5 +304,7 @@ void meta_wayland_surface_restore_shortcuts (MetaWaylandSurface *
gboolean meta_wayland_surface_is_shortcuts_inhibited (MetaWaylandSurface *surface,
MetaWaylandSeat *seat);
void meta_wayland_surface_set_frozen (MetaWaylandSurface *surface,
gboolean is_frozen);
#endif

View File

@@ -71,6 +71,20 @@ meta_window_xwayland_shortcuts_inhibited (MetaWindow *window,
return meta_wayland_compositor_is_shortcuts_inhibited (compositor, source);
}
static void
meta_window_xwayland_move_resize_internal (MetaWindow *window,
int gravity,
MetaRectangle unconstrained_rect,
MetaRectangle constrained_rect,
MetaMoveResizeFlags flags,
MetaMoveResizeResultFlags *result)
{
META_WINDOW_CLASS (meta_window_xwayland_parent_class)->move_resize_internal (window, gravity, unconstrained_rect, constrained_rect, flags, result);
if (*result & META_MOVE_RESIZE_RESULT_RESIZED)
meta_window_set_resize_pending (window, TRUE);
}
static void
meta_window_xwayland_get_property (GObject *object,
guint prop_id,
@@ -117,6 +131,7 @@ meta_window_xwayland_class_init (MetaWindowXwaylandClass *klass)
window_class->force_restore_shortcuts = meta_window_xwayland_force_restore_shortcuts;
window_class->shortcuts_inhibited = meta_window_xwayland_shortcuts_inhibited;
window_class->move_resize_internal = meta_window_xwayland_move_resize_internal;
gobject_class->get_property = meta_window_xwayland_get_property;
gobject_class->set_property = meta_window_xwayland_set_property;

View File

@@ -1244,8 +1244,9 @@ meta_window_x11_move_resize_internal (MetaWindow *window,
{
meta_error_trap_push (window->display);
if (window == window->display->grab_window &&
meta_grab_op_is_resizing (window->display->grab_op) &&
if ((window->constructing ||
(window == window->display->grab_window &&
meta_grab_op_is_resizing (window->display->grab_op))) &&
!window->disable_sync &&
window->sync_request_counter != None &&
window->sync_request_alarm != None &&
@@ -3496,27 +3497,32 @@ meta_window_x11_update_sync_request_counter (MetaWindow *window,
window->sync_request_serial = new_counter_value;
meta_compositor_sync_updates_frozen (window->display->compositor, window);
if (window == window->display->grab_window &&
meta_grab_op_is_resizing (window->display->grab_op) &&
new_counter_value >= window->sync_request_wait_serial &&
(!window->extended_sync_request_counter || new_counter_value % 2 == 0) &&
window->sync_request_timeout_id)
if (new_counter_value >= window->sync_request_wait_serial && window->sync_request_timeout_id)
{
meta_topic (META_DEBUG_RESIZING,
"Alarm event received last motion x = %d y = %d\n",
window->display->grab_latest_motion_x,
window->display->grab_latest_motion_y);
if (!window->extended_sync_request_counter || new_counter_value % 2 == 0)
{
g_source_remove (window->sync_request_timeout_id);
window->sync_request_timeout_id = 0;
}
g_source_remove (window->sync_request_timeout_id);
window->sync_request_timeout_id = 0;
if (window == window->display->grab_window &&
meta_grab_op_is_resizing (window->display->grab_op) &&
!meta_window_resize_is_pending (window) &&
(!window->extended_sync_request_counter || new_counter_value % 2 == 0))
{
meta_topic (META_DEBUG_RESIZING,
"Alarm event received last motion x = %d y = %d\n",
window->display->grab_latest_motion_x,
window->display->grab_latest_motion_y);
/* This means we are ready for another configure;
* no pointer round trip here, to keep in sync */
meta_window_update_resize (window,
window->display->grab_last_user_action_was_snap,
window->display->grab_latest_motion_x,
window->display->grab_latest_motion_y,
TRUE);
/* This means we are ready for another configure;
* no pointer round trip here, to keep in sync */
meta_window_update_resize (window,
window->display->grab_last_user_action_was_snap,
window->display->grab_latest_motion_x,
window->display->grab_latest_motion_y,
TRUE);
}
}
/* If sync was previously disabled, turn it back on and hope