diff --git a/gdk/gdkinternals.h b/gdk/gdkinternals.h index a9a5917962..3042bcb9d4 100644 --- a/gdk/gdkinternals.h +++ b/gdk/gdkinternals.h @@ -204,6 +204,7 @@ struct _GdkWindow guint input_only : 1; guint modal_hint : 1; guint composited : 1; + guint has_alpha_background : 1; guint destroyed : 2; @@ -227,10 +228,18 @@ struct _GdkWindow gint abs_x, abs_y; /* Absolute offset in impl */ gint width, height; - guint32 clip_tag; - cairo_region_t *clip_region; /* Clip region (wrt toplevel) in window coords */ - cairo_region_t *clip_region_with_children; /* Clip region in window coords */ + /* The clip region is the part of the window, in window coordinates + that is fully or partially (i.e. semi transparently) visible in + the window hierarchy from the toplevel and down */ + cairo_region_t *clip_region; + /* This is the clip region, with additionally all the opaque + child windows removed */ + cairo_region_t *clip_region_with_children; + /* The layered region is the subset of clip_region that + is covered by non-opaque sibling or ancestor sibling window. */ + cairo_region_t *layered_region; + GdkCursor *cursor; GHashTable *device_cursor; @@ -391,11 +400,6 @@ void _gdk_display_set_window_under_pointer (GdkDisplay *display, void _gdk_synthesize_crossing_events_for_geometry_change (GdkWindow *changed_window); -cairo_region_t *_gdk_window_calculate_full_clip_region (GdkWindow *window, - GdkWindow *base_window, - gboolean do_children, - gint *base_x_offset, - gint *base_y_offset); gboolean _gdk_window_has_impl (GdkWindow *window); GdkWindow * _gdk_window_get_impl_window (GdkWindow *window); diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c index cc251a9e1a..ae57b27ef6 100644 --- a/gdk/gdkwindow.c +++ b/gdk/gdkwindow.c @@ -205,7 +205,6 @@ struct _GdkWindowPaint cairo_surface_t *surface; guint uses_implicit : 1; guint flushed : 1; - guint32 region_tag; }; typedef struct { @@ -256,6 +255,7 @@ static void gdk_window_invalidate_rect_full (GdkWindow *window, const GdkRectangle *rect, gboolean invalidate_children, ClearBg clear_bg); +static void _gdk_window_propagate_has_alpha_background (GdkWindow *window); static guint signals[LAST_SIGNAL] = { 0 }; @@ -263,14 +263,6 @@ static gpointer parent_class = NULL; static const cairo_user_data_key_t gdk_window_cairo_key; -static guint32 -new_region_tag (void) -{ - static guint32 tag = 0; - - return ++tag; -} - G_DEFINE_ABSTRACT_TYPE (GdkWindow, gdk_window, G_TYPE_OBJECT) GType @@ -658,10 +650,64 @@ gdk_window_has_no_impl (GdkWindow *window) } static void -remove_child_area (GdkWindow *private, +remove_layered_child_area (GdkWindow *window, + cairo_region_t *region) +{ + GdkWindow *child; + cairo_region_t *child_region; + GdkRectangle r; + GList *l; + + for (l = window->children; l; l = l->next) + { + child = l->data; + + /* If region is empty already, no need to do + anything potentially costly */ + if (cairo_region_is_empty (region)) + break; + + if (!GDK_WINDOW_IS_MAPPED (child) || child->input_only || child->composited) + continue; + + /* Ignore offscreen children, as they don't draw in their parent and + * don't take part in the clipping */ + if (gdk_window_is_offscreen (child)) + continue; + + /* Only non-impl children with alpha add to the layered region */ + if (!child->has_alpha_background && gdk_window_has_impl (child)) + continue; + + r.x = child->x; + r.y = child->y; + r.width = child->width; + r.height = child->height; + + /* Bail early if child totally outside region */ + if (cairo_region_contains_rectangle (region, &r) == CAIRO_REGION_OVERLAP_OUT) + continue; + + child_region = cairo_region_create_rectangle (&r); + if (child->shape) + { + /* Adjust shape region to parent window coords */ + cairo_region_translate (child->shape, child->x, child->y); + cairo_region_intersect (child_region, child->shape); + cairo_region_translate (child->shape, -child->x, -child->y); + } + + cairo_region_subtract (region, child_region); + cairo_region_destroy (child_region); + } +} + +static void +remove_child_area (GdkWindow *window, GdkWindow *until, gboolean for_input, - cairo_region_t *region) + cairo_region_t *region, + cairo_region_t *layered_region) { GdkWindow *child; cairo_region_t *child_region; @@ -669,7 +715,7 @@ remove_child_area (GdkWindow *private, GList *l; cairo_region_t *shape; - for (l = private->children; l; l = l->next) + for (l = window->children; l; l = l->next) { child = l->data; @@ -707,7 +753,7 @@ remove_child_area (GdkWindow *private, cairo_region_intersect (child_region, child->shape); cairo_region_translate (child->shape, -child->x, -child->y); } - else if (private->window_type == GDK_WINDOW_FOREIGN) + else if (window->window_type == GDK_WINDOW_FOREIGN) { shape = GDK_WINDOW_IMPL_GET_CLASS (child)->get_shape (child); if (shape) @@ -721,7 +767,7 @@ remove_child_area (GdkWindow *private, { if (child->input_shape) cairo_region_intersect (child_region, child->input_shape); - else if (private->window_type == GDK_WINDOW_FOREIGN) + else if (window->window_type == GDK_WINDOW_FOREIGN) { shape = GDK_WINDOW_IMPL_GET_CLASS (child)->get_input_shape (child); if (shape) @@ -732,9 +778,14 @@ remove_child_area (GdkWindow *private, } } - cairo_region_subtract (region, child_region); + if (child->has_alpha_background) + { + if (layered_region != NULL) + cairo_region_union (layered_region, child_region); + } + else + cairo_region_subtract (region, child_region); cairo_region_destroy (child_region); - } } @@ -877,7 +928,7 @@ recompute_visible_regions_internal (GdkWindow *private, GdkRectangle r; GList *l; GdkWindow *child; - cairo_region_t *new_clip, *old_clip_region_with_children; + cairo_region_t *new_clip, *new_layered; gboolean clip_region_changed; gboolean abs_pos_changed; int old_abs_x, old_abs_y; @@ -910,6 +961,7 @@ recompute_visible_regions_internal (GdkWindow *private, clip_region_changed = FALSE; if (recalculate_clip) { + new_layered = cairo_region_create (); if (private->viewable) { /* Calculate visible region (sans children) in parent window coords */ @@ -922,39 +974,45 @@ recompute_visible_regions_internal (GdkWindow *private, if (!gdk_window_is_toplevel (private)) { cairo_region_intersect (new_clip, private->parent->clip_region); + cairo_region_union (new_layered, private->parent->layered_region); /* Remove all overlapping children from parent. */ - remove_child_area (private->parent, private, FALSE, new_clip); + remove_child_area (private->parent, private, FALSE, new_clip, new_layered); } /* Convert from parent coords to window coords */ cairo_region_translate (new_clip, -private->x, -private->y); + cairo_region_translate (new_layered, -private->x, -private->y); if (private->shape) cairo_region_intersect (new_clip, private->shape); } else - new_clip = cairo_region_create (); + new_clip = cairo_region_create (); + cairo_region_intersect (new_layered, new_clip); + if (private->clip_region == NULL || !cairo_region_equal (private->clip_region, new_clip)) clip_region_changed = TRUE; + if (private->layered_region == NULL || + !cairo_region_equal (private->layered_region, new_layered)) + clip_region_changed = TRUE; + if (private->clip_region) cairo_region_destroy (private->clip_region); private->clip_region = new_clip; - old_clip_region_with_children = private->clip_region_with_children; + if (private->layered_region != NULL) + cairo_region_destroy (private->layered_region); + private->layered_region = new_layered; + + if (private->clip_region_with_children) + cairo_region_destroy (private->clip_region_with_children); private->clip_region_with_children = cairo_region_copy (private->clip_region); if (private->window_type != GDK_WINDOW_ROOT) - remove_child_area (private, NULL, FALSE, private->clip_region_with_children); - - if (clip_region_changed || - !cairo_region_equal (private->clip_region_with_children, old_clip_region_with_children)) - private->clip_tag = new_region_tag (); - - if (old_clip_region_with_children) - cairo_region_destroy (old_clip_region_with_children); + remove_child_area (private, NULL, FALSE, private->clip_region_with_children, NULL); } if (clip_region_changed) @@ -1632,10 +1690,22 @@ gdk_window_reparent (GdkWindow *window, _gdk_window_update_viewable (window); + if (window->background == NULL) + { + /* parent relative background, update has_alpha_background */ + if (window->parent == NULL || + window->parent->window_type == GDK_WINDOW_ROOT) + window->has_alpha_background = FALSE; + else + window->has_alpha_background = window->parent->has_alpha_background; + } + recompute_visible_regions (window, TRUE, FALSE); if (old_parent && GDK_WINDOW_TYPE (old_parent) != GDK_WINDOW_ROOT) recompute_visible_regions (old_parent, FALSE, TRUE); + _gdk_window_propagate_has_alpha_background (window); + /* We used to apply the clip as the shape, but no more. Reset this to the real shape */ if (gdk_window_has_impl (window) && @@ -2859,7 +2929,6 @@ gdk_window_begin_paint_region (GdkWindow *window, paint = g_new (GdkWindowPaint, 1); paint->region = cairo_region_copy (region); - paint->region_tag = new_region_tag (); cairo_region_intersect (paint->region, window->clip_region_with_children); cairo_region_get_extents (paint->region, &clip_box); @@ -2890,6 +2959,20 @@ gdk_window_begin_paint_region (GdkWindow *window, gdk_window_get_content (window), MAX (clip_box.width, 1), MAX (clip_box.height, 1)); + + /* Normally alpha backgrounded client side windows are composited on the implicit paint + by being drawn in back to front order. However, if implicit paints are not used, for + instance if it was flushed due to a non-double-buffered paint in the middle of the + expose we need to copy in the existing data here. */ + if (!gdk_window_has_impl (window) && window->has_alpha_background) + { + cairo_t *cr = cairo_create (paint->surface); + gdk_cairo_set_source_window (cr, impl_window, + - (window->abs_x + clip_box.x), + - (window->abs_y + clip_box.y)); + cairo_paint (cr); + cairo_destroy (cr); + } } cairo_surface_set_device_offset (paint->surface, -clip_box.x, -clip_box.y); @@ -3779,8 +3862,7 @@ _gdk_window_process_updates_recurse (GdkWindow *window, cairo_region_t *expose_region) { GdkWindow *child; - cairo_region_t *child_region; - GdkRectangle r; + cairo_region_t *clipped_expose_region; GList *l, *children; if (cairo_region_is_empty (expose_region)) @@ -3790,57 +3872,13 @@ _gdk_window_process_updates_recurse (GdkWindow *window, window == window->impl_window) _gdk_window_add_damage ((GdkWindow *) window->impl_window, expose_region); - /* Make this reentrancy safe for expose handlers freeing windows */ - children = g_list_copy (window->children); - g_list_foreach (children, (GFunc)g_object_ref, NULL); - /* Iterate over children, starting at topmost */ - for (l = children; l != NULL; l = l->next) - { - child = l->data; + /* Paint the window before the children, clipped to the window region + with visible child windows removed */ + clipped_expose_region = cairo_region_copy (expose_region); + cairo_region_intersect (clipped_expose_region, window->clip_region_with_children); - if (child->destroyed || !GDK_WINDOW_IS_MAPPED (child) || child->input_only || child->composited) - continue; - - /* Ignore offscreen children, as they don't draw in their parent and - * don't take part in the clipping */ - if (gdk_window_is_offscreen (child)) - continue; - - r.x = child->x; - r.y = child->y; - r.width = child->width; - r.height = child->height; - - child_region = cairo_region_create_rectangle (&r); - if (child->shape) - { - /* Adjust shape region to parent window coords */ - cairo_region_translate (child->shape, child->x, child->y); - cairo_region_intersect (child_region, child->shape); - cairo_region_translate (child->shape, -child->x, -child->y); - } - - if (child->impl == window->impl) - { - /* Client side child, expose */ - cairo_region_intersect (child_region, expose_region); - cairo_region_subtract (expose_region, child_region); - cairo_region_translate (child_region, -child->x, -child->y); - _gdk_window_process_updates_recurse ((GdkWindow *)child, child_region); - } - else - { - /* Native child, just remove area from expose region */ - cairo_region_subtract (expose_region, child_region); - } - cairo_region_destroy (child_region); - } - - g_list_foreach (children, (GFunc)g_object_unref, NULL); - g_list_free (children); - - if (!cairo_region_is_empty (expose_region) && + if (!cairo_region_is_empty (clipped_expose_region) && !window->destroyed) { if (window->event_mask & GDK_EXPOSURE_MASK) @@ -3851,8 +3889,8 @@ _gdk_window_process_updates_recurse (GdkWindow *window, event.expose.window = g_object_ref (window); event.expose.send_event = FALSE; event.expose.count = 0; - event.expose.region = expose_region; - cairo_region_get_extents (expose_region, &event.expose.area); + event.expose.region = clipped_expose_region; + cairo_region_get_extents (clipped_expose_region, &event.expose.area); _gdk_event_emit (&event); @@ -3871,11 +3909,42 @@ _gdk_window_process_updates_recurse (GdkWindow *window, * We use begin/end_paint around the clear so that we can * piggyback on the implicit paint */ - gdk_window_begin_paint_region (window, expose_region); - gdk_window_clear_region_internal (window, expose_region); + gdk_window_begin_paint_region (window, clipped_expose_region); + gdk_window_clear_region_internal (window, clipped_expose_region); gdk_window_end_paint (window); } } + cairo_region_destroy (clipped_expose_region); + + /* Make this reentrancy safe for expose handlers freeing windows */ + children = g_list_copy (window->children); + g_list_foreach (children, (GFunc)g_object_ref, NULL); + + /* Iterate over children, starting at bottommost */ + for (l = g_list_last (children); l != NULL; l = l->prev) + { + child = l->data; + + if (child->destroyed || !GDK_WINDOW_IS_MAPPED (child) || child->input_only || child->composited) + continue; + + /* Ignore offscreen children, as they don't draw in their parent and + * don't take part in the clipping */ + if (gdk_window_is_offscreen (child)) + continue; + + /* Client side child, expose */ + if (child->impl == window->impl) + { + cairo_region_translate (expose_region, -child->x, -child->y); + _gdk_window_process_updates_recurse ((GdkWindow *)child, expose_region); + cairo_region_translate (expose_region, child->x, child->y); + } + } + + g_list_foreach (children, (GFunc)g_object_unref, NULL); + g_list_free (children); + } /* Process and remove any invalid area on the native window by creating @@ -4581,7 +4650,7 @@ cairo_region_t * gdk_window_get_update_area (GdkWindow *window) { GdkWindow *impl_window; - cairo_region_t *tmp_region; + cairo_region_t *tmp_region, *to_remove; g_return_val_if_fail (GDK_IS_WINDOW (window), NULL); @@ -4601,7 +4670,21 @@ gdk_window_get_update_area (GdkWindow *window) } else { - cairo_region_subtract (impl_window->update_area, tmp_region); + /* Convert from impl coords */ + cairo_region_translate (tmp_region, -window->abs_x, -window->abs_y); + + /* Don't remove any update area that is overlapped by non-opaque windows + (children or siblings) with alpha, as these really need to be repainted + independently of this window. */ + to_remove = cairo_region_copy (tmp_region); + cairo_region_subtract (to_remove, window->parent->layered_region); + remove_layered_child_area (window, to_remove); + + /* Remove from update_area */ + cairo_region_translate (to_remove, window->abs_x, window->abs_y); + cairo_region_subtract (impl_window->update_area, to_remove); + + cairo_region_destroy (to_remove); if (cairo_region_is_empty (impl_window->update_area) && impl_window->outstanding_moves == NULL) @@ -4612,10 +4695,7 @@ gdk_window_get_update_area (GdkWindow *window) gdk_window_remove_update_window ((GdkWindow *)impl_window); } - /* Convert from impl coords */ - cairo_region_translate (tmp_region, -window->abs_x, -window->abs_y); return tmp_region; - } } else @@ -5261,8 +5341,6 @@ gdk_window_show_unraised (GdkWindow *window) void gdk_window_raise (GdkWindow *window) { - cairo_region_t *old_region, *new_region; - g_return_if_fail (GDK_IS_WINDOW (window)); if (window->destroyed) @@ -5270,26 +5348,14 @@ gdk_window_raise (GdkWindow *window) gdk_window_flush_if_exposing (window); - old_region = NULL; - if (gdk_window_is_viewable (window) && - !window->input_only) - old_region = cairo_region_copy (window->clip_region); - /* Keep children in (reverse) stacking order */ gdk_window_raise_internal (window); recompute_visible_regions (window, TRUE, FALSE); - if (old_region) - { - new_region = cairo_region_copy (window->clip_region); - - cairo_region_subtract (new_region, old_region); - gdk_window_invalidate_region_full (window, new_region, TRUE, CLEAR_BG_ALL); - - cairo_region_destroy (old_region); - cairo_region_destroy (new_region); - } + if (gdk_window_is_viewable (window) && + !window->input_only) + gdk_window_invalidate_region_full (window, window->clip_region, TRUE, CLEAR_BG_ALL); } static void @@ -5989,7 +6055,7 @@ gdk_window_move_resize_internal (GdkWindow *window, gint width, gint height) { - cairo_region_t *old_region, *new_region, *copy_area; + cairo_region_t *old_region, *old_layered, *new_region, *copy_area; cairo_region_t *old_native_child_region, *new_native_child_region; GdkWindow *impl_window; GdkWindowImplClass *impl_class; @@ -6022,6 +6088,7 @@ gdk_window_move_resize_internal (GdkWindow *window, expose = FALSE; old_region = NULL; + old_layered = NULL; impl_window = gdk_window_get_impl_window (window); @@ -6035,8 +6102,10 @@ gdk_window_move_resize_internal (GdkWindow *window, expose = TRUE; old_region = cairo_region_copy (window->clip_region); - /* Adjust region to parent window coords */ + old_layered = cairo_region_copy (window->layered_region); + /* Adjust regions to parent window coords */ cairo_region_translate (old_region, window->x, window->y); + cairo_region_translate (old_layered, window->x, window->y); old_native_child_region = collect_native_child_region (window, TRUE); if (old_native_child_region) @@ -6116,7 +6185,19 @@ gdk_window_move_resize_internal (GdkWindow *window, * Everything in the old and new regions that is not copied must be * invalidated (including children) as this is newly exposed */ - copy_area = cairo_region_copy (new_region); + if (window->has_alpha_background) + copy_area = cairo_region_create (); /* Copy nothing for alpha windows */ + else + copy_area = cairo_region_copy (new_region); + + /* Don't copy from a previously layered region */ + cairo_region_translate (old_layered, dx, dy); + cairo_region_subtract (copy_area, old_layered); + + /* Don't copy into a layered region */ + cairo_region_translate (copy_area, -window->x, -window->y); + cairo_region_subtract (copy_area, window->layered_region); + cairo_region_translate (copy_area, window->x, window->y); cairo_region_union (new_region, old_region); @@ -6165,6 +6246,7 @@ gdk_window_move_resize_internal (GdkWindow *window, gdk_window_invalidate_region_full (window->parent, new_region, TRUE, CLEAR_BG_ALL); cairo_region_destroy (old_region); + cairo_region_destroy (old_layered); cairo_region_destroy (new_region); } @@ -6274,7 +6356,7 @@ gdk_window_scroll (GdkWindow *window, gint dy) { GdkWindow *impl_window; - cairo_region_t *copy_area, *noncopy_area; + cairo_region_t *copy_area, *noncopy_area, *old_layered_area; cairo_region_t *old_native_child_region, *new_native_child_region; GList *tmp_list; @@ -6288,6 +6370,7 @@ gdk_window_scroll (GdkWindow *window, gdk_window_flush_if_exposing (window); + old_layered_area = cairo_region_copy (window->layered_region); old_native_child_region = collect_native_child_region (window, FALSE); if (old_native_child_region) { @@ -6328,7 +6411,11 @@ gdk_window_scroll (GdkWindow *window, impl_window = gdk_window_get_impl_window (window); /* Calculate the area that can be gotten by copying the old area */ - copy_area = cairo_region_copy (window->clip_region); + if (window->has_alpha_background) + copy_area = cairo_region_create (); /* Copy nothing for alpha windows */ + else + copy_area = cairo_region_copy (window->clip_region); + cairo_region_subtract (copy_area, old_layered_area); if (old_native_child_region) { /* Don't copy from inside native children, as this is copied by @@ -6342,6 +6429,7 @@ gdk_window_scroll (GdkWindow *window, } cairo_region_translate (copy_area, dx, dy); cairo_region_intersect (copy_area, window->clip_region); + cairo_region_subtract (copy_area, window->layered_region); /* And the rest need to be invalidated */ noncopy_area = cairo_region_copy (window->clip_region); @@ -6363,6 +6451,7 @@ gdk_window_scroll (GdkWindow *window, gdk_window_invalidate_region_full (window, noncopy_area, TRUE, CLEAR_BG_ALL); cairo_region_destroy (noncopy_area); + cairo_region_destroy (old_layered_area); if (old_native_child_region) { @@ -6410,12 +6499,19 @@ gdk_window_move_region (GdkWindow *window, impl_window = gdk_window_get_impl_window (window); /* compute source regions */ - copy_area = cairo_region_copy (region); + if (window->has_alpha_background) + copy_area = cairo_region_create (); /* Copy nothing for alpha windows */ + else + copy_area = cairo_region_copy (region); cairo_region_intersect (copy_area, window->clip_region_with_children); + cairo_region_subtract (copy_area, window->layered_region); + remove_layered_child_area (window, copy_area); /* compute destination regions */ cairo_region_translate (copy_area, dx, dy); cairo_region_intersect (copy_area, window->clip_region_with_children); + cairo_region_subtract (copy_area, window->layered_region); + remove_layered_child_area (window, copy_area); /* Invalidate parts of the region (source and dest) not covered by the copy */ @@ -6487,6 +6583,29 @@ gdk_window_set_background_rgba (GdkWindow *window, cairo_pattern_destroy (pattern); } + +/* Updates has_alpha_background recursively for all child windows + that have parent-relative alpha */ +static void +_gdk_window_propagate_has_alpha_background (GdkWindow *window) +{ + GdkWindow *child; + GList *l; + + for (l = window->children; l; l = l->next) + { + child = l->data; + + if (child->background == NULL && + child->has_alpha_background != window->has_alpha_background) + { + child->has_alpha_background = window->has_alpha_background; + recompute_visible_regions (child, TRUE, FALSE); + _gdk_window_propagate_has_alpha_background (child); + } + } +} + /** * gdk_window_set_background_pattern: * @window: a #GdkWindow @@ -6504,6 +6623,9 @@ void gdk_window_set_background_pattern (GdkWindow *window, cairo_pattern_t *pattern) { + gboolean has_alpha; + cairo_pattern_type_t type; + g_return_if_fail (GDK_IS_WINDOW (window)); if (window->input_only) @@ -6515,6 +6637,69 @@ gdk_window_set_background_pattern (GdkWindow *window, cairo_pattern_destroy (window->background); window->background = pattern; + has_alpha = TRUE; + + if (pattern == NULL) + { + /* parent-relative, copy has_alpha from parent */ + if (window->parent == NULL || + window->parent->window_type == GDK_WINDOW_ROOT) + has_alpha = FALSE; + else + has_alpha = window->parent->has_alpha_background; + } + else + { + type = cairo_pattern_get_type (pattern); + + if (type == CAIRO_PATTERN_TYPE_SOLID) + { + double alpha; + cairo_pattern_get_rgba (pattern, NULL, NULL, NULL, &alpha); + if (alpha == 1.0) + has_alpha = FALSE; + } + else if (type == CAIRO_PATTERN_TYPE_LINEAR || + type == CAIRO_PATTERN_TYPE_RADIAL) + { + int i, n; + double alpha; + + n = 0; + cairo_pattern_get_color_stop_count (pattern, &n); + has_alpha = FALSE; + for (i = 0; i < n; i++) + { + cairo_pattern_get_color_stop_rgba (pattern, i, NULL, + NULL, NULL, NULL, &alpha); + if (alpha != 1.0) + { + has_alpha = TRUE; + break; + } + } + } + else if (type == CAIRO_PATTERN_TYPE_SURFACE) + { + cairo_surface_t *surface; + cairo_content_t content; + + cairo_pattern_get_surface (pattern, &surface); + content = cairo_surface_get_content (surface); + has_alpha = + (content == CAIRO_CONTENT_ALPHA) || + (content == CAIRO_CONTENT_COLOR_ALPHA); + } + } + + if (has_alpha != window->has_alpha_background) + { + window->has_alpha_background = has_alpha; + recompute_visible_regions (window, TRUE, FALSE); + + _gdk_window_propagate_has_alpha_background (window); + } + if (gdk_window_has_impl (window)) { GdkWindowImplClass *impl_class = GDK_WINDOW_IMPL_GET_CLASS (window->impl); @@ -7140,7 +7325,7 @@ do_child_shapes (GdkWindow *window, r.height = window->height; region = cairo_region_create_rectangle (&r); - remove_child_area (window, NULL, FALSE, region); + remove_child_area (window, NULL, FALSE, region, NULL); if (merge && window->shape) cairo_region_subtract (region, window->shape); @@ -7259,7 +7444,7 @@ do_child_input_shapes (GdkWindow *window, r.height = window->height; region = cairo_region_create_rectangle (&r); - remove_child_area (window, NULL, TRUE, region); + remove_child_area (window, NULL, TRUE, region, NULL); if (merge && window->shape) cairo_region_subtract (region, window->shape); @@ -7525,114 +7710,6 @@ gdk_window_is_shaped (GdkWindow *window) return window->shaped; } -static void -window_get_size_rectangle (GdkWindow *window, - GdkRectangle *rect) -{ - rect->x = rect->y = 0; - rect->width = window->width; - rect->height = window->height; -} - -/* Calculates the real clipping region for a window, in window coordinates, - * taking into account other windows, gc clip region and gc clip mask. - */ -cairo_region_t * -_gdk_window_calculate_full_clip_region (GdkWindow *window, - GdkWindow *base_window, - gboolean do_children, - gint *base_x_offset, - gint *base_y_offset) -{ - GdkRectangle visible_rect; - cairo_region_t *real_clip_region; - gint x_offset, y_offset; - GdkWindow *parentwin, *lastwin; - - if (base_x_offset) - *base_x_offset = 0; - if (base_y_offset) - *base_y_offset = 0; - - if (!window->viewable || window->input_only) - return cairo_region_create (); - - window_get_size_rectangle (window, &visible_rect); - - /* real_clip_region is in window coordinates */ - real_clip_region = cairo_region_create_rectangle (&visible_rect); - - x_offset = y_offset = 0; - - lastwin = window; - if (do_children) - parentwin = lastwin; - else - parentwin = lastwin->parent; - - /* Remove the areas of all overlapping windows above parentwin in the hiearachy */ - for (; parentwin != NULL && - (parentwin == window || lastwin != base_window); - lastwin = parentwin, parentwin = lastwin->parent) - { - GList *cur; - GdkRectangle real_clip_rect; - - if (parentwin != window) - { - x_offset += lastwin->x; - y_offset += lastwin->y; - } - - /* children is ordered in reverse stack order */ - for (cur = parentwin->children; - cur && cur->data != lastwin; - cur = cur->next) - { - GdkWindow *child = cur->data; - - if (!GDK_WINDOW_IS_MAPPED (child) || child->input_only) - continue; - - /* Ignore offscreen children, as they don't draw in their parent and - * don't take part in the clipping */ - if (gdk_window_is_offscreen (child)) - continue; - - window_get_size_rectangle (child, &visible_rect); - - /* Convert rect to "window" coords */ - visible_rect.x += child->x - x_offset; - visible_rect.y += child->y - y_offset; - - /* This shortcut is really necessary for performance when there are a lot of windows */ - cairo_region_get_extents (real_clip_region, &real_clip_rect); - if (visible_rect.x >= real_clip_rect.x + real_clip_rect.width || - visible_rect.x + visible_rect.width <= real_clip_rect.x || - visible_rect.y >= real_clip_rect.y + real_clip_rect.height || - visible_rect.y + visible_rect.height <= real_clip_rect.y) - continue; - - cairo_region_subtract_rectangle (real_clip_region, &visible_rect); - } - - /* Clip to the parent */ - window_get_size_rectangle ((GdkWindow *)parentwin, &visible_rect); - /* Convert rect to "window" coords */ - visible_rect.x += - x_offset; - visible_rect.y += - y_offset; - - cairo_region_intersect_rectangle (real_clip_region, &visible_rect); - } - - if (base_x_offset) - *base_x_offset = x_offset; - if (base_y_offset) - *base_y_offset = y_offset; - - return real_clip_region; -} - void _gdk_window_add_damage (GdkWindow *toplevel, cairo_region_t *damaged_region) diff --git a/gdk/gdkwindow.h b/gdk/gdkwindow.h index 50779fe365..58f98bd75a 100644 --- a/gdk/gdkwindow.h +++ b/gdk/gdkwindow.h @@ -580,6 +580,10 @@ gboolean gdk_window_get_composited (GdkWindow *window); void gdk_window_set_composited (GdkWindow *window, gboolean composited); +gboolean gdk_window_get_layered (GdkWindow *window); +void gdk_window_set_layered (GdkWindow *window, + gboolean layered); + /* * This routine allows you to merge (ie ADD) child shapes to your * own window's shape keeping its current shape and ADDING the child diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c index 50370a589a..fd23dd9a71 100644 --- a/gtk/gtkwindow.c +++ b/gtk/gtkwindow.c @@ -5521,6 +5521,7 @@ resize_grip_create_window (GtkWindow *window) GdkWindowAttr attributes; gint attributes_mask; GdkRectangle rect; + GdkRGBA transparent = {0, 0, 0, 0}; priv = window->priv; widget = GTK_WIDGET (window); @@ -5545,6 +5546,7 @@ resize_grip_create_window (GtkWindow *window) priv->grip_window = gdk_window_new (gtk_widget_get_window (widget), &attributes, attributes_mask); + gdk_window_set_background_rgba (priv->grip_window, &transparent); gdk_window_set_user_data (priv->grip_window, widget); @@ -7440,7 +7442,8 @@ gtk_window_draw (GtkWidget *widget, gtk_style_context_save (context); - if (!gtk_widget_get_app_paintable (widget)) + if (!gtk_widget_get_app_paintable (widget) && + gtk_cairo_should_draw_window (cr, gtk_widget_get_window (widget))) { GtkStateFlags state; diff --git a/tests/testwindows.c b/tests/testwindows.c index a68f3bc20d..0535b6327b 100644 --- a/tests/testwindows.c +++ b/tests/testwindows.c @@ -15,12 +15,12 @@ static GtkWidget *main_window; GdkWindow * create_window (GdkWindow *parent, int x, int y, int w, int h, - GdkColor *color) + GdkRGBA *color) { GdkWindowAttr attributes; gint attributes_mask; GdkWindow *window; - GdkColor *bg; + GdkRGBA *bg; attributes.x = x; attributes.y = y; @@ -41,17 +41,18 @@ create_window (GdkWindow *parent, window = gdk_window_new (parent, &attributes, attributes_mask); gdk_window_set_user_data (window, darea); - bg = g_new (GdkColor, 1); + bg = g_new (GdkRGBA, 1); if (color) *bg = *color; else { - bg->red = g_random_int_range (0, 0xffff); - bg->blue = g_random_int_range (0, 0xffff); - bg->green = g_random_int_range (0, 0xffff);; + bg->red = g_random_double (); + bg->blue = g_random_double (); + bg->green = g_random_double (); + bg->alpha = 1.0; } - gdk_window_set_background (window, bg); + gdk_window_set_background_rgba (window, bg); g_object_set_data_full (G_OBJECT (window), "color", bg, g_free); gdk_window_show (window); @@ -238,16 +239,16 @@ save_window (GString *s, GdkWindow *window) { gint x, y; - GdkColor *color; + GdkRGBA *color; gdk_window_get_position (window, &x, &y); color = g_object_get_data (G_OBJECT (window), "color"); - g_string_append_printf (s, "%d,%d %dx%d (%d,%d,%d) %d %d\n", + g_string_append_printf (s, "%d,%d %dx%d (%f,%f,%f,%f) %d %d\n", x, y, gdk_window_get_width (window), gdk_window_get_height (window), - color->red, color->green, color->blue, + color->red, color->green, color->blue, color->alpha, gdk_window_has_native (window), g_list_length (gdk_window_peek_children (window))); @@ -273,6 +274,13 @@ save_children (GString *s, } +static void +refresh_clicked (GtkWidget *button, + gpointer data) +{ + gtk_widget_queue_draw (darea); +} + static void save_clicked (GtkWidget *button, gpointer data) @@ -330,21 +338,23 @@ destroy_children (GdkWindow *window) static char ** parse_window (GdkWindow *parent, char **lines) { - int x, y, w, h, r, g, b, native, n_children; + int x, y, w, h, native, n_children; + double r, g, b, a; GdkWindow *window; - GdkColor color; + GdkRGBA color; int i; if (*lines == NULL) return lines; - if (sscanf(*lines, "%d,%d %dx%d (%d,%d,%d) %d %d", - &x, &y, &w, &h, &r, &g, &b, &native, &n_children) == 9) + if (sscanf(*lines, "%d,%d %dx%d (%lf,%lf,%lf,%lf) %d %d", + &x, &y, &w, &h, &r, &g, &b, &a, &native, &n_children) == 10) { lines++; color.red = r; color.green = g; color.blue = b; + color.alpha = a; window = create_window (parent, x, y, w, h, &color); if (native) gdk_window_ensure_native (window); @@ -685,6 +695,39 @@ native_window_clicked (GtkWidget *button, update_store (); } +static void +alpha_clicked (GtkWidget *button, + gpointer data) +{ + GList *selected, *l; + GdkWindow *window; + GdkRGBA *color; + + selected = get_selected_windows (); + + for (l = selected; l != NULL; l = l->next) + { + window = l->data; + + color = g_object_get_data (G_OBJECT (window), "color"); + if (GPOINTER_TO_INT(data) > 0) + color->alpha += 0.2; + else + color->alpha -= 0.2; + + if (color->alpha < 0) + color->alpha = 0; + if (color->alpha > 1) + color->alpha = 1; + + gdk_window_set_background_rgba (window, color); + } + + g_list_free (selected); + + update_store (); +} + static gboolean darea_button_release_event (GtkWidget *widget, GdkEventButton *event) @@ -956,6 +999,20 @@ main (int argc, char **argv) gtk_grid_attach (GTK_GRID (grid), button, 3, 2, 1, 1); gtk_widget_show (button); + button = gtk_button_new_with_label ("More transparent"); + g_signal_connect (button, "clicked", + G_CALLBACK (alpha_clicked), + GINT_TO_POINTER (-1)); + gtk_grid_attach (GTK_GRID (grid), button, 0, 3, 1, 1); + gtk_widget_show (button); + + button = gtk_button_new_with_label ("Less transparent"); + g_signal_connect (button, "clicked", + G_CALLBACK (alpha_clicked), + GINT_TO_POINTER (1)); + gtk_grid_attach (GTK_GRID (grid), button, 1, 3, 1, 1); + gtk_widget_show (button); + button = gtk_button_new_with_label ("Restack above"); g_signal_connect (button, "clicked", G_CALLBACK (restack_clicked), @@ -1000,6 +1057,17 @@ main (int argc, char **argv) G_CALLBACK (save_clicked), NULL); + button = gtk_button_new_with_label ("Refresh"); + gtk_box_pack_start (GTK_BOX (vbox), + button, + FALSE, FALSE, + 2); + gtk_widget_show (button); + g_signal_connect (button, "clicked", + G_CALLBACK (refresh_clicked), + NULL); + + gtk_widget_show (window); if (argc == 2)