Fix flashing in non-double-buffered widgets

Avoid copying back partially drawn double-buffer data
when flushing to avoid flicker. This means non double
buffered widgets must draw opaque pixels in its expose
handlers, and that you are not allowed to use direct
rendering (or modify GdkWindow pos/size/order) from
inside the expose handler of a double buffered widget.

See https://bugzilla.gnome.org/show_bug.cgi?id=679144 for more
details
This commit is contained in:
Alexander Larsson 2012-08-23 15:51:13 +02:00
parent 828a97d773
commit e112cdacd4

View File

@ -2783,60 +2783,55 @@ gdk_cairo_create_for_impl (GdkWindow *window)
return cr;
}
/* Ensure that all content related to this (sub)window is pushed to the
native region. If there is an active paint then that area is not
pushed, in order to not show partially finished double buffers. */
/* This is called whenever something is drawing directly to the
* window, bypassing the double buffering. When this happens we
* need to mark any the currently drawn data in the double buffer
* as invalid to avoid later drawing it back over the directly
* rendered pixels. We also need to mark this region as "flushed"
* so that if we later try to paint on it double-buffered we need
* to read back the on-window pixels rather than relying on what
* is in the current double-buffer pixmap.
*
* Note that this doesn't correctly handle the case where the
* non-double buffered drawing uses transparency and relies on
* what the windows below it draws. A fix for that would require
* drawing the existing double-buffered background to the window,
* but that causes ugly flashes. Non-double buffered drawing is
* typically only used in old code or when the drawed widget
* already has a double-buffering layer, and in these cases the
* pixels are opaque anyway. If you need transparency, don't
* disable double buffering.
*/
static void
gdk_window_flush_implicit_paint (GdkWindow *window)
{
GdkWindow *impl_window;
GdkWindowPaint *paint;
cairo_region_t *region;
GSList *list;
impl_window = gdk_window_get_impl_window (window);
if (impl_window->implicit_paint == NULL)
return;
paint = impl_window->implicit_paint;
region = cairo_region_copy (window->clip_region_with_children);
region = cairo_region_copy (window->clip_region_with_children);
cairo_region_translate (region, window->abs_x, window->abs_y);
/* Anything in the whole flushed window that was drawn is now
considered unpainted, so that we don't push it back at the
end of the implicit paint overwriting the directly rendered
pixels. */
cairo_region_subtract (paint->region, region);
/* Save flushed area so we can read it back if we draw over it later */
if (paint->flushed == NULL)
paint->flushed = cairo_region_copy (region);
paint->flushed = region;
else
cairo_region_union (paint->flushed, region);
cairo_region_intersect (region, paint->region);
/* Don't flush active double buffers, as that may show partially done
* rendering */
for (list = window->paint_stack; list != NULL; list = list->next)
{
GdkWindowPaint *tmp_paint = list->data;
cairo_region_subtract (region, tmp_paint->region);
cairo_region_union (paint->flushed, region);
cairo_region_destroy (region);
}
if (!GDK_WINDOW_DESTROYED (window) && !cairo_region_is_empty (region))
{
cairo_t *cr;
/* Remove flushed region from the implicit paint */
cairo_region_subtract (paint->region, region);
/* Some regions are valid, push these to window now */
cr = gdk_cairo_create_for_impl (window);
gdk_cairo_region (cr, region);
cairo_clip (cr);
cairo_set_source_surface (cr, paint->surface, 0, 0);
cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
cairo_paint (cr);
cairo_destroy (cr);
}
cairo_region_destroy (region);
}
/* Ends an implicit paint, paired with gdk_window_begin_implicit_paint returning TRUE */