From e112cdacd4d500066c09cbdcad1034c3eed32f31 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Thu, 23 Aug 2012 15:51:13 +0200 Subject: [PATCH] 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 --- gdk/gdkwindow.c | 65 +++++++++++++++++++++++-------------------------- 1 file changed, 30 insertions(+), 35 deletions(-) diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c index 9714a74a36..48afda33a7 100644 --- a/gdk/gdkwindow.c +++ b/gdk/gdkwindow.c @@ -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 */