Fix corner cases where _NET_WM_FRAME_DRAWN might be missed

The WM spec requires _NET_WM_FRAME_DRAWN to *always* be sent when
there is an appropriate update to the sync counter value. We were
potentially missing _NET_WM_FRAME_DRAWN when an application did a
spontaneous update during an interactive resize and during effects.
Refactor the code to always send _NET_WM_FRAME_DRAWN, even when
a window is frozen.

https://bugzilla.gnome.org/show_bug.cgi?id=693833
This commit is contained in:
Owen W. Taylor 2013-02-14 13:40:55 -05:00
parent aeb589c176
commit 5876f2e3e5
6 changed files with 66 additions and 39 deletions

View File

@ -764,6 +764,21 @@ meta_compositor_set_updates_frozen (MetaCompositor *compositor,
meta_window_actor_set_updates_frozen (window_actor, updates_frozen); meta_window_actor_set_updates_frozen (window_actor, updates_frozen);
} }
void
meta_compositor_queue_frame_drawn (MetaCompositor *compositor,
MetaWindow *window,
gboolean no_delay_frame)
{
MetaWindowActor *window_actor;
DEBUG_TRACE ("meta_compositor_queue_frame_drawn\n");
window_actor = META_WINDOW_ACTOR (meta_window_get_compositor_private (window));
if (!window_actor)
return;
meta_window_actor_queue_frame_drawn (window_actor, no_delay_frame);
}
static gboolean static gboolean
is_grabbed_event (MetaDisplay *display, is_grabbed_event (MetaDisplay *display,
XEvent *event) XEvent *event)

View File

@ -51,6 +51,8 @@ void meta_window_actor_mapped (MetaWindowActor *self);
void meta_window_actor_unmapped (MetaWindowActor *self); void meta_window_actor_unmapped (MetaWindowActor *self);
void meta_window_actor_set_updates_frozen (MetaWindowActor *self, void meta_window_actor_set_updates_frozen (MetaWindowActor *self,
gboolean updates_frozen); gboolean updates_frozen);
void meta_window_actor_queue_frame_drawn (MetaWindowActor *self,
gboolean no_delay_frame);
cairo_region_t *meta_window_actor_get_obscured_region (MetaWindowActor *self); cairo_region_t *meta_window_actor_get_obscured_region (MetaWindowActor *self);

View File

@ -107,6 +107,7 @@ struct _MetaWindowActorPrivate
guint needs_damage_all : 1; guint needs_damage_all : 1;
guint received_damage : 1; guint received_damage : 1;
guint repaint_scheduled : 1;
/* If set, the client needs to be sent a _NET_WM_FRAME_DRAWN /* If set, the client needs to be sent a _NET_WM_FRAME_DRAWN
* client message using the most recent frame in ->frames */ * client message using the most recent frame in ->frames */
@ -932,6 +933,7 @@ meta_window_actor_damage_all (MetaWindowActor *self)
cogl_texture_get_height (texture)); cogl_texture_get_height (texture));
priv->needs_damage_all = FALSE; priv->needs_damage_all = FALSE;
priv->repaint_scheduled = TRUE;
} }
static void static void
@ -961,17 +963,41 @@ meta_window_actor_thaw (MetaWindowActor *self)
* don't know what real damage has happened. */ * don't know what real damage has happened. */
if (self->priv->needs_damage_all) if (self->priv->needs_damage_all)
meta_window_actor_damage_all (self); meta_window_actor_damage_all (self);
else if (self->priv->needs_frame_drawn != 0) }
void
meta_window_actor_queue_frame_drawn (MetaWindowActor *self,
gboolean no_delay_frame)
{
MetaWindowActorPrivate *priv = self->priv;
FrameData *frame = g_slice_new0 (FrameData);
priv->needs_frame_drawn = TRUE;
frame->sync_request_serial = priv->window->sync_request_serial;
priv->frames = g_list_prepend (priv->frames, frame);
if (no_delay_frame)
{ {
/* A frame was marked by the client without actually doing any damage; ClutterActor *stage = clutter_actor_get_stage (CLUTTER_ACTOR (self));
* we need to make sure that the pre_paint/post_paint functions clutter_stage_skip_sync_delay (CLUTTER_STAGE (stage));
* get called, enabling us to send a _NET_WM_FRAME_DRAWN. We do a }
* 1-pixel redraw to get consistent timing with non-empty frames.
if (!priv->repaint_scheduled)
{
/* A frame was marked by the client without actually doing any
* damage, or while we had the window frozen (e.g. during an
* interactive resize.) We need to make sure that the
* pre_paint/post_paint functions get called, enabling us to
* send a _NET_WM_FRAME_DRAWN. We do a 1-pixel redraw to get
* consistent timing with non-empty frames.
*/ */
if (self->priv->mapped && !self->priv->needs_pixmap) if (priv->mapped && !priv->needs_pixmap)
{ {
const cairo_rectangle_int_t clip = { 0, 0, 1, 1 }; const cairo_rectangle_int_t clip = { 0, 0, 1, 1 };
clutter_actor_queue_redraw_with_clip (self->priv->actor, &clip); clutter_actor_queue_redraw_with_clip (priv->actor, &clip);
priv->repaint_scheduled = TRUE;
} }
} }
} }
@ -1949,6 +1975,7 @@ meta_window_actor_process_damage (MetaWindowActor *self,
event->area.y, event->area.y,
event->area.width, event->area.width,
event->area.height); event->area.height);
priv->repaint_scheduled = TRUE;
} }
void void
@ -2350,25 +2377,6 @@ meta_window_actor_handle_updates (MetaWindowActor *self)
check_needs_pixmap (self); check_needs_pixmap (self);
check_needs_reshape (self); check_needs_reshape (self);
check_needs_shadow (self); check_needs_shadow (self);
if (priv->window->needs_frame_drawn)
{
FrameData *frame = g_slice_new0 (FrameData);
priv->needs_frame_drawn = TRUE;
frame->sync_request_serial = priv->window->sync_request_serial;
priv->frames = g_list_prepend (priv->frames, frame);
priv->window->needs_frame_drawn = FALSE;
if (priv->window->no_delay_frame)
{
ClutterActor *stage = clutter_actor_get_stage (CLUTTER_ACTOR (self));
clutter_stage_skip_sync_delay (CLUTTER_STAGE (stage));
}
}
} }
void void
@ -2396,6 +2404,8 @@ meta_window_actor_post_paint (MetaWindowActor *self)
{ {
MetaWindowActorPrivate *priv = self->priv; MetaWindowActorPrivate *priv = self->priv;
priv->repaint_scheduled = FALSE;
if (priv->needs_frame_drawn) if (priv->needs_frame_drawn)
{ {
MetaScreen *screen = priv->screen; MetaScreen *screen = priv->screen;

View File

@ -356,15 +356,6 @@ struct _MetaWindow
* also handles application frames */ * also handles application frames */
guint extended_sync_request_counter : 1; guint extended_sync_request_counter : 1;
/* if TRUE, we still need to send a _NET_WM_FRAME_DRAWN message for the
* last update the sync request counter */
guint needs_frame_drawn : 1;
/* if TRUE, the frame that was just drawn was drawn without any delay
* on the client's part and thus is high-priority - if we add delay
* we might cause the client to miss it's target frame rate */
guint no_delay_frame : 1;
/* Note: can be NULL */ /* Note: can be NULL */
GSList *struts; GSList *struts;

View File

@ -9510,11 +9510,13 @@ void
meta_window_update_sync_request_counter (MetaWindow *window, meta_window_update_sync_request_counter (MetaWindow *window,
gint64 new_counter_value) gint64 new_counter_value)
{ {
if (window->extended_sync_request_counter && gboolean needs_frame_drawn = FALSE;
new_counter_value % 2 == 0) gboolean no_delay_frame = FALSE;
if (window->extended_sync_request_counter && new_counter_value % 2 == 0)
{ {
window->needs_frame_drawn = TRUE; needs_frame_drawn = TRUE;
window->no_delay_frame = new_counter_value == window->sync_request_serial + 1; no_delay_frame = new_counter_value == window->sync_request_serial + 1;
} }
window->sync_request_serial = new_counter_value; window->sync_request_serial = new_counter_value;
@ -9547,6 +9549,10 @@ meta_window_update_sync_request_counter (MetaWindow *window,
window->display->grab_latest_motion_y, window->display->grab_latest_motion_y,
TRUE); TRUE);
} }
if (needs_frame_drawn)
meta_compositor_queue_frame_drawn (window->display->compositor, window,
no_delay_frame);
} }
#endif /* HAVE_XSYNC */ #endif /* HAVE_XSYNC */

View File

@ -156,6 +156,9 @@ void meta_compositor_sync_window_geometry (MetaCompositor *compositor,
void meta_compositor_set_updates_frozen (MetaCompositor *compositor, void meta_compositor_set_updates_frozen (MetaCompositor *compositor,
MetaWindow *window, MetaWindow *window,
gboolean updates_frozen); gboolean updates_frozen);
void meta_compositor_queue_frame_drawn (MetaCompositor *compositor,
MetaWindow *window,
gboolean no_delay_frame);
void meta_compositor_sync_stack (MetaCompositor *compositor, void meta_compositor_sync_stack (MetaCompositor *compositor,
MetaScreen *screen, MetaScreen *screen,