diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c index 87eb0d8ae..0ebea5383 100644 --- a/src/compositor/meta-window-actor.c +++ b/src/compositor/meta-window-actor.c @@ -1991,10 +1991,113 @@ meta_window_actor_sync_visibility (MetaWindowActor *self) } } +#define TAU (2*M_PI) + static void -generate_mask (MetaWindowActor *self, - MetaFrameBorders *borders, - cairo_region_t *shape_region) +install_corners (MetaWindow *window, + MetaFrameBorders *borders, + cairo_t *cr) +{ + float top_left, top_right, bottom_left, bottom_right; + int x, y; + MetaRectangle outer; + + meta_frame_get_corner_radiuses (window->frame, + &top_left, + &top_right, + &bottom_left, + &bottom_right); + + meta_window_get_outer_rect (window, &outer); + + /* top left */ + x = borders->invisible.left; + y = borders->invisible.top; + + cairo_arc (cr, + x + top_left, + y + top_left, + top_left, + 2 * TAU / 4, + 3 * TAU / 4); + + /* top right */ + x = borders->invisible.left + outer.width - top_right; + y = borders->invisible.top; + + cairo_arc (cr, + x, + y + top_right, + top_right, + 3 * TAU / 4, + 4 * TAU / 4); + + /* bottom right */ + x = borders->invisible.left + outer.width - bottom_right; + y = borders->invisible.top + outer.height - bottom_right; + + cairo_arc (cr, + x, + y, + bottom_right, + 0 * TAU / 4, + 1 * TAU / 4); + + /* bottom left */ + x = borders->invisible.left; + y = borders->invisible.top + outer.height - bottom_left; + + cairo_arc (cr, + x + bottom_left, + y, + bottom_left, + 1 * TAU / 4, + 2 * TAU / 4); + + cairo_set_source_rgba (cr, 1, 1, 1, 1); + cairo_fill (cr); +} + +static void +scan_visible_region (guchar *mask_data, + int stride, + cairo_region_t *scan_area, + cairo_region_t *union_against) +{ + int i, n_rects; + n_rects = cairo_region_num_rectangles (scan_area); + + for (i = 0; i < n_rects; i++) + { + int x, y; + cairo_rectangle_int_t rect; + + cairo_region_get_rectangle (scan_area, i, &rect); + + for (y = rect.y; y < (rect.y + rect.height); y++) + { + for (x = rect.x; x < (rect.x + rect.width); x++) + { + int w = x; + while (mask_data[y * stride + w] == 255 && w < (rect.x + rect.width)) + w++; + + if (w > 0) + { + cairo_rectangle_int_t tmp = { x, y, w - x, 1 }; + cairo_region_union_rectangle (union_against, &tmp); + x = w; + } + } + } + } +} + +static void +build_and_scan_frame_mask (MetaWindowActor *self, + MetaFrameBorders *borders, + cairo_rectangle_int_t *client_area, + cairo_region_t *shape_region) { MetaWindowActorPrivate *priv = self->priv; guchar *mask_data; @@ -2026,6 +2129,24 @@ generate_mask (MetaWindowActor *self, gdk_cairo_region (cr, shape_region); cairo_fill (cr); + if (priv->window->frame != NULL) + { + cairo_region_t *frame_paint_region; + cairo_rectangle_int_t rect = { 0, 0, tex_width, tex_height }; + + /* Make sure we don't paint the frame over the client window. */ + frame_paint_region = cairo_region_create_rectangle (&rect); + cairo_region_subtract_rectangle (frame_paint_region, client_area); + + gdk_cairo_region (cr, frame_paint_region); + cairo_clip (cr); + + install_corners (priv->window, borders, cr); + + cairo_surface_flush (surface); + scan_visible_region (mask_data, stride, frame_paint_region, shape_region); + } + cairo_destroy (cr); cairo_surface_destroy (surface); @@ -2066,44 +2187,30 @@ check_needs_reshape (MetaWindowActor *self) MetaDisplay *display = meta_screen_get_display (screen); MetaFrameBorders borders; cairo_region_t *region; + cairo_rectangle_int_t client_area; if (!priv->needs_reshape) return; - meta_shaped_texture_set_mask_texture (META_SHAPED_TEXTURE (priv->actor), COGL_INVALID_HANDLE); - meta_window_actor_clear_shape_region (self); - meta_frame_calc_borders (priv->window->frame, &borders); - region = meta_window_get_frame_bounds (priv->window); - if (region != NULL) - { - /* This returns the window's internal frame bounds region, - * so we need to copy it because we modify it below. */ - region = cairo_region_copy (region); - } - else - { - /* If we have no region, we have no frame. We have no frame, - * so just use the bounding region instead */ - region = cairo_region_copy (priv->bounding_region); - } + client_area.x = borders.total.left; + client_area.y = borders.total.top; + client_area.width = priv->window->rect.width; + client_area.height = priv->window->rect.height; + + meta_shaped_texture_set_mask_texture (META_SHAPED_TEXTURE (priv->actor), COGL_INVALID_HANDLE); + meta_window_actor_clear_shape_region (self); #ifdef HAVE_SHAPE if (priv->window->has_shape) { + /* Translate the set of XShape rectangles that we + * get from the X server to a cairo_region. */ Display *xdisplay = meta_display_get_xdisplay (display); XRectangle *rects; int n_rects, ordering; - cairo_rectangle_int_t client_area; - - client_area.width = priv->window->rect.width; - client_area.height = priv->window->rect.height; - client_area.x = borders.total.left; - client_area.y = borders.total.top; - - /* Punch out client area. */ - cairo_region_subtract_rectangle (region, &client_area); + cairo_rectangle_int_t *cairo_rects = NULL; meta_error_trap_push (display); rects = XShapeGetRectangles (xdisplay, @@ -2116,20 +2223,34 @@ check_needs_reshape (MetaWindowActor *self) if (rects) { int i; + cairo_rects = g_new (cairo_rectangle_int_t, n_rects); for (i = 0; i < n_rects; i ++) { - cairo_rectangle_int_t rect = { rects[i].x + client_area.x, - rects[i].y + client_area.y, - rects[i].width, - rects[i].height }; - cairo_region_union_rectangle (region, &rect); + cairo_rects[i].x = rects[i].x + client_area.x; + cairo_rects[i].y = rects[i].y + client_area.y; + cairo_rects[i].width = rects[i].width; + cairo_rects[i].height = rects[i].height; } XFree (rects); } - } -#endif - generate_mask (self, &borders, region); + region = cairo_region_create_rectangles (cairo_rects, n_rects); + g_free (cairo_rects); + } + else +#endif + { + /* If we don't have a shape on the server, that means that + * we have an implicit shape of one rectangle covering the + * entire window. */ + region = cairo_region_create_rectangle (&client_area); + } + + /* This takes the region, generates a mask using GTK+ + * and scans the mask looking for all opaque pixels, + * adding it to region. + */ + build_and_scan_frame_mask (self, &borders, &client_area, region); meta_window_actor_update_shape_region (self, region); cairo_region_destroy (region);