From f1df49ad17ce0cd0309e6592cfaec7ba642e22ad Mon Sep 17 00:00:00 2001 From: "Jasper St. Pierre" Date: Fri, 23 Aug 2013 23:30:47 -0400 Subject: [PATCH] Refactor how shapes are done As part of Wayland support, we should hold the shape and opaque regions on the MetaWindow rather than fetching them in the MetaWindowActor, as this gives us better flexibility as to where the regions are set, and allows for easier Wayland support. To make merging easier with the Wayland branch, we also append the _x11 suffix to functions that use the X SHAPE extension to fetch the shaped regions. https://bugzilla.gnome.org/show_bug.cgi?id=706930 --- src/compositor/meta-window-actor.c | 151 +++++++++++------------------ src/core/display.c | 27 +----- src/core/window-private.h | 15 ++- src/core/window-props.c | 2 +- src/core/window.c | 141 +++++++++++++++++++++------ 5 files changed, 185 insertions(+), 151 deletions(-) diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c index db01638f8..4588905b9 100644 --- a/src/compositor/meta-window-actor.c +++ b/src/compositor/meta-window-actor.c @@ -10,7 +10,6 @@ #include -#include #include #include #include @@ -400,7 +399,7 @@ meta_window_actor_constructed (GObject *object) /* Start off with an empty region to maintain the invariant that the shape region is always set */ - priv->shape_region = cairo_region_create(); + priv->shape_region = cairo_region_create (); } static void @@ -1332,7 +1331,7 @@ meta_window_actor_should_unredirect (MetaWindowActor *self) if (priv->opacity != 0xff) return FALSE; - if (metaWindow->has_shape) + if (metaWindow->shape_region != NULL) return FALSE; if (priv->argb32 && !meta_window_requested_bypass_compositor (metaWindow)) @@ -2220,101 +2219,53 @@ build_and_scan_frame_mask (MetaWindowActor *self, } static void -check_needs_reshape (MetaWindowActor *self) +meta_window_actor_update_shape_region (MetaWindowActor *self, + cairo_rectangle_int_t *client_area) { MetaWindowActorPrivate *priv = self->priv; - MetaScreen *screen = priv->screen; - MetaDisplay *display = meta_screen_get_display (screen); - MetaFrameBorders borders; cairo_region_t *region = NULL; - cairo_rectangle_int_t client_area; - gboolean needs_mask; - if (!priv->mapped) - return; - - if (!priv->needs_reshape) - return; - - g_clear_pointer (&priv->shadow_shape, meta_window_shape_unref); - - meta_frame_calc_borders (priv->window->frame, &borders); - - client_area.x = borders.total.left; - client_area.y = borders.total.top; - client_area.width = priv->window->rect.width; - if (priv->window->shaded) - client_area.height = 0; - else - client_area.height = priv->window->rect.height; - - meta_shaped_texture_set_mask_texture (META_SHAPED_TEXTURE (priv->actor), NULL); - g_clear_pointer (&priv->shape_region, cairo_region_destroy); - g_clear_pointer (&priv->opaque_region, cairo_region_destroy); - -#ifdef HAVE_SHAPE - if (priv->window->has_shape) + if (priv->window->frame != NULL && priv->window->shape_region != NULL) { - /* 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; - - meta_error_trap_push (display); - rects = XShapeGetRectangles (xdisplay, - priv->window->xwindow, - ShapeBounding, - &n_rects, - &ordering); - meta_error_trap_pop (display); - - if (rects) - { - int i; - cairo_rectangle_int_t *cairo_rects = g_new (cairo_rectangle_int_t, n_rects); - - for (i = 0; i < n_rects; i ++) - { - 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); - region = cairo_region_create_rectangles (cairo_rects, n_rects); - g_free (cairo_rects); - } + region = cairo_region_copy (priv->window->shape_region); + cairo_region_translate (region, client_area->x, client_area->y); } -#endif - - needs_mask = (region != NULL) || (priv->window->frame != NULL); - - if (region != NULL) + else if (priv->window->shape_region != NULL) { - /* The shape we get back from the client may have coordinates - * outside of the frame. The X SHAPE Extension requires that - * the overall shape the client provides never exceeds the - * "bounding rectangle" of the window -- the shape that the - * window would have gotten if it was unshaped. In our case, - * this is simply the client area. - */ - cairo_region_intersect_rectangle (region, &client_area); + region = cairo_region_reference (priv->window->shape_region); } else { /* 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); + region = cairo_region_create_rectangle (client_area); } - /* The region at this point should be constrained to the - * bounds of the client rectangle. */ + meta_shaped_texture_set_mask_texture (META_SHAPED_TEXTURE (priv->actor), NULL); + if ((priv->window->shape_region != NULL) || (priv->window->frame != NULL)) + build_and_scan_frame_mask (self, client_area, region); + + g_clear_pointer (&priv->shape_region, cairo_region_destroy); + priv->shape_region = region; + + g_clear_pointer (&priv->shadow_shape, meta_window_shape_unref); + meta_window_actor_invalidate_shadow (self); +} + +static void +meta_window_actor_update_opaque_region (MetaWindowActor *self) +{ + MetaWindowActorPrivate *priv = self->priv; + + g_clear_pointer (&priv->opaque_region, cairo_region_destroy); if (priv->argb32 && priv->window->opaque_region != NULL) { + MetaFrameBorders borders; + + meta_frame_calc_borders (priv->window->frame, &borders); + /* The opaque region is defined to be a part of the * window which ARGB32 will always paint with opaque * pixels. For these regions, we want to avoid painting @@ -2326,24 +2277,40 @@ check_needs_reshape (MetaWindowActor *self) * case, graphical glitches will occur. */ priv->opaque_region = cairo_region_copy (priv->window->opaque_region); - cairo_region_translate (priv->opaque_region, client_area.x, client_area.y); - cairo_region_intersect (priv->opaque_region, region); + cairo_region_translate (priv->opaque_region, borders.total.left, borders.total.top); + cairo_region_intersect (priv->opaque_region, priv->shape_region); } else if (priv->argb32) priv->opaque_region = NULL; else - priv->opaque_region = cairo_region_reference (region); + priv->opaque_region = cairo_region_reference (priv->shape_region); +} - if (needs_mask) - { - /* 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, &client_area, region); - } +static void +check_needs_reshape (MetaWindowActor *self) +{ + MetaWindowActorPrivate *priv = self->priv; + MetaFrameBorders borders; + cairo_rectangle_int_t client_area; - priv->shape_region = region; + if (!priv->mapped) + return; + + if (!priv->needs_reshape) + return; + + meta_frame_calc_borders (priv->window->frame, &borders); + + client_area.x = borders.total.left; + client_area.y = borders.total.top; + client_area.width = priv->window->rect.width; + if (priv->window->shaded) + client_area.height = 0; + else + client_area.height = priv->window->rect.height; + + meta_window_actor_update_shape_region (self, &client_area); + meta_window_actor_update_opaque_region (self); priv->needs_reshape = FALSE; meta_window_actor_invalidate_shadow (self); diff --git a/src/core/display.c b/src/core/display.c index f412c66c9..3f3a45f74 100644 --- a/src/core/display.c +++ b/src/core/display.c @@ -2283,32 +2283,7 @@ event_callback (XEvent *event, XShapeEvent *sev = (XShapeEvent*) event; if (sev->kind == ShapeBounding) - { - if (sev->shaped && !window->has_shape) - { - window->has_shape = TRUE; - meta_topic (META_DEBUG_SHAPES, - "Window %s now has a shape\n", - window->desc); - } - else if (!sev->shaped && window->has_shape) - { - window->has_shape = FALSE; - meta_topic (META_DEBUG_SHAPES, - "Window %s no longer has a shape\n", - window->desc); - } - else - { - meta_topic (META_DEBUG_SHAPES, - "Window %s shape changed\n", - window->desc); - } - - if (display->compositor) - meta_compositor_window_shape_changed (display->compositor, - window); - } + meta_window_update_shape_region_x11 (window); } else { diff --git a/src/core/window-private.h b/src/core/window-private.h index e7255beab..757263241 100644 --- a/src/core/window-private.h +++ b/src/core/window-private.h @@ -325,9 +325,6 @@ struct _MetaWindow guint using_net_wm_icon_name : 1; /* vs. plain wm_icon_name */ guint using_net_wm_visible_icon_name : 1; /* tracked so we can clear it */ - /* has a shape mask */ - guint has_shape : 1; - /* icon props have changed */ guint need_reread_icon : 1; @@ -349,6 +346,9 @@ struct _MetaWindow /* if non-NULL, the bounds of the window frame */ cairo_region_t *frame_bounds; + /* if non-NULL, the bounding shape region of the window */ + cairo_region_t *shape_region; + /* if non-NULL, the opaque region _NET_WM_OPAQUE_REGION */ cairo_region_t *opaque_region; @@ -665,7 +665,6 @@ void meta_window_update_icon_now (MetaWindow *window); void meta_window_update_role (MetaWindow *window); void meta_window_update_net_wm_type (MetaWindow *window); -void meta_window_update_opaque_region (MetaWindow *window); void meta_window_update_for_monitors_changed (MetaWindow *window); void meta_window_update_on_all_workspaces (MetaWindow *window); @@ -679,4 +678,12 @@ void meta_window_compute_tile_match (MetaWindow *window); gboolean meta_window_updates_are_frozen (MetaWindow *window); +void meta_window_set_opaque_region (MetaWindow *window, + cairo_region_t *region); +void meta_window_update_opaque_region_x11 (MetaWindow *window); + +void meta_window_set_shape_region (MetaWindow *window, + cairo_region_t *region); +void meta_window_update_shape_region_x11 (MetaWindow *window); + #endif diff --git a/src/core/window-props.c b/src/core/window-props.c index adbfe59dd..13ee3bbdb 100644 --- a/src/core/window-props.c +++ b/src/core/window-props.c @@ -565,7 +565,7 @@ reload_opaque_region (MetaWindow *window, MetaPropValue *value, gboolean initial) { - meta_window_update_opaque_region (window); + meta_window_update_opaque_region_x11 (window); } static void diff --git a/src/core/window.c b/src/core/window.c index b109ec3f1..9b49cfbab 100644 --- a/src/core/window.c +++ b/src/core/window.c @@ -825,7 +825,6 @@ meta_window_new_with_attrs (MetaDisplay *display, gulong existing_wm_state; gulong event_mask; MetaMoveResizeFlags flags; - gboolean has_shape; MetaScreen *screen; g_assert (attrs != NULL); @@ -959,29 +958,9 @@ meta_window_new_with_attrs (MetaDisplay *display, XISelectEvents (display->xdisplay, xwindow, &mask, 1); } - has_shape = FALSE; #ifdef HAVE_SHAPE if (META_DISPLAY_HAS_SHAPE (display)) - { - int x_bounding, y_bounding, x_clip, y_clip; - unsigned w_bounding, h_bounding, w_clip, h_clip; - int bounding_shaped, clip_shaped; - - XShapeSelectInput (display->xdisplay, xwindow, ShapeNotifyMask); - - XShapeQueryExtents (display->xdisplay, xwindow, - &bounding_shaped, &x_bounding, &y_bounding, - &w_bounding, &h_bounding, - &clip_shaped, &x_clip, &y_clip, - &w_clip, &h_clip); - - has_shape = bounding_shaped != FALSE; - - meta_topic (META_DEBUG_SHAPES, - "Window has_shape = %d extents %d,%d %u x %u\n", - has_shape, x_bounding, y_bounding, - w_bounding, h_bounding); - } + XShapeSelectInput (display->xdisplay, xwindow, ShapeNotifyMask); #endif /* Get rid of any borders */ @@ -1041,8 +1020,6 @@ meta_window_new_with_attrs (MetaDisplay *display, /* avoid tons of stack updates */ meta_stack_freeze (window->screen->stack); - window->has_shape = has_shape; - window->rect.x = attrs->x; window->rect.y = attrs->y; window->rect.width = attrs->width; @@ -1213,6 +1190,8 @@ meta_window_new_with_attrs (MetaDisplay *display, meta_display_register_x_window (display, &window->xwindow, window); + meta_window_update_shape_region_x11 (window); + /* Assign this #MetaWindow a sequence number which can be used * for sorting. */ @@ -7637,14 +7616,25 @@ meta_window_update_net_wm_type (MetaWindow *window) } void -meta_window_update_opaque_region (MetaWindow *window) +meta_window_set_opaque_region (MetaWindow *window, + cairo_region_t *region) +{ + g_clear_pointer (&window->opaque_region, cairo_region_destroy); + + if (region != NULL) + window->opaque_region = cairo_region_reference (region); + + if (window->display->compositor) + meta_compositor_window_shape_changed (window->display->compositor, window); +} + +void +meta_window_update_opaque_region_x11 (MetaWindow *window) { cairo_region_t *opaque_region = NULL; gulong *region = NULL; int nitems; - g_clear_pointer (&window->opaque_region, cairo_region_destroy); - if (meta_prop_get_cardinal_list (window->display, window->xwindow, window->display->atom__NET_WM_OPAQUE_REGION, @@ -7687,13 +7677,108 @@ meta_window_update_opaque_region (MetaWindow *window) } out: - window->opaque_region = opaque_region; meta_XFree (region); + meta_window_set_opaque_region (window, opaque_region); + cairo_region_destroy (opaque_region); +} + +static cairo_region_t * +region_create_from_x_rectangles (const XRectangle *rects, + int n_rects) +{ + int i; + cairo_rectangle_int_t *cairo_rects = g_newa (cairo_rectangle_int_t, n_rects); + + for (i = 0; i < n_rects; i ++) + { + cairo_rects[i].x = rects[i].x; + cairo_rects[i].y = rects[i].y; + cairo_rects[i].width = rects[i].width; + cairo_rects[i].height = rects[i].height; + } + + return cairo_region_create_rectangles (cairo_rects, n_rects); +} + +void +meta_window_set_shape_region (MetaWindow *window, + cairo_region_t *region) +{ + g_clear_pointer (&window->shape_region, cairo_region_destroy); + + if (region != NULL) + window->shape_region = cairo_region_reference (region); + if (window->display->compositor) meta_compositor_window_shape_changed (window->display->compositor, window); } +void +meta_window_update_shape_region_x11 (MetaWindow *window) +{ + cairo_region_t *region = NULL; + +#ifdef HAVE_SHAPE + if (META_DISPLAY_HAS_SHAPE (window->display)) + { + /* Translate the set of XShape rectangles that we + * get from the X server to a cairo_region. */ + XRectangle *rects = NULL; + int n_rects, ordering; + + int x_bounding, y_bounding, x_clip, y_clip; + unsigned w_bounding, h_bounding, w_clip, h_clip; + int bounding_shaped, clip_shaped; + + meta_error_trap_push (window->display); + XShapeQueryExtents (window->display->xdisplay, window->xwindow, + &bounding_shaped, &x_bounding, &y_bounding, + &w_bounding, &h_bounding, + &clip_shaped, &x_clip, &y_clip, + &w_clip, &h_clip); + + if (bounding_shaped) + { + rects = XShapeGetRectangles (window->display->xdisplay, + window->xwindow, + ShapeBounding, + &n_rects, + &ordering); + } + meta_error_trap_pop (window->display); + + if (rects) + { + region = region_create_from_x_rectangles (rects, n_rects); + XFree (rects); + } + } +#endif + + if (region != NULL) + { + cairo_rectangle_int_t client_area; + + client_area.x = 0; + client_area.y = 0; + client_area.width = window->rect.width; + client_area.height = window->rect.height; + + /* The shape we get back from the client may have coordinates + * outside of the frame. The X SHAPE Extension requires that + * the overall shape the client provides never exceeds the + * "bounding rectangle" of the window -- the shape that the + * window would have gotten if it was unshaped. In our case, + * this is simply the client area. + */ + cairo_region_intersect_rectangle (region, &client_area); + } + + meta_window_set_shape_region (window, region); + cairo_region_destroy (region); +} + static void redraw_icon (MetaWindow *window) {