diff --git a/src/compositor/meta-shaped-texture.c b/src/compositor/meta-shaped-texture.c index c6239c9d2..c93bb473b 100644 --- a/src/compositor/meta-shaped-texture.c +++ b/src/compositor/meta-shaped-texture.c @@ -72,6 +72,7 @@ struct _MetaShapedTexturePrivate CoglPipeline *pipeline_unshaped; cairo_region_t *clip_region; + cairo_region_t *input_shape_region; guint tex_width, tex_height; @@ -285,38 +286,52 @@ meta_shaped_texture_pick (ClutterActor *actor, MetaShapedTexture *stex = (MetaShapedTexture *) actor; MetaShapedTexturePrivate *priv = stex->priv; + if (!clutter_actor_should_pick_paint (actor) || + (priv->clip_region && cairo_region_is_empty (priv->clip_region))) + return; + /* If there is no region then use the regular pick */ - if (priv->mask_texture == NULL) + if (priv->input_shape_region == NULL) CLUTTER_ACTOR_CLASS (meta_shaped_texture_parent_class) ->pick (actor, color); - else if (clutter_actor_should_pick_paint (actor)) + else { - CoglTexture *paint_tex; - ClutterActorBox alloc; - guint tex_width, tex_height; + int n_rects; + float *rectangles; + int i; - paint_tex = COGL_TEXTURE (priv->texture); + /* Note: We don't bother trying to intersect the pick and clip regions + * since needing to copy the region, do the intersection, and probably + * increase the number of rectangles seems more likely to have a negative + * effect. + * + * NB: Most of the time when just using rectangles for picking then + * picking shouldn't involve any rendering, and minimizing the number of + * rectangles has more benefit than reducing the area of the pick + * region. + */ - if (paint_tex == NULL) - return; + n_rects = cairo_region_num_rectangles (priv->input_shape_region); - tex_width = cogl_texture_get_width (paint_tex); - tex_height = cogl_texture_get_height (paint_tex); + for (i = 0; i < n_rects; i++) + { + cairo_rectangle_int_t rect; + int pos = i * 4; - if (tex_width == 0 || tex_height == 0) /* no contents yet */ - return; + cairo_region_get_rectangle (priv->input_shape_region, i, &rect); - cogl_set_source_color4ub (color->red, color->green, color->blue, - color->alpha); + rectangles[pos] = rect.x; + rectangles[pos + 1] = rect.y; + rectangles[pos + 2] = rect.x + rect.width; + rectangles[pos + 3] = rect.y + rect.height; + } - clutter_actor_get_allocation_box (actor, &alloc); + cogl_set_source_color4ub (color->red, + color->green, + color->blue, + color->alpha); - /* Paint the mask rectangle in the given color */ - cogl_set_source_texture (priv->mask_texture); - cogl_rectangle_with_texture_coords (0, 0, - alloc.x2 - alloc.x1, - alloc.y2 - alloc.y1, - 0, 0, 1, 1); + cogl_rectangles (rectangles, n_rects); } } @@ -533,6 +548,41 @@ meta_shaped_texture_get_texture (MetaShapedTexture *stex) return COGL_TEXTURE (stex->priv->texture); } +/** + * meta_shaped_texture_set_input_shape_region: + * @stex: a #MetaShapedTexture + * @shape_region: the region of the texture that should respond to + * input. + * + * Determines what region of the texture should accept input. For + * X based windows this is defined by the ShapeInput region of the + * window. + */ +void +meta_shaped_texture_set_input_shape_region (MetaShapedTexture *stex, + cairo_region_t *shape_region) +{ + MetaShapedTexturePrivate *priv; + + g_return_if_fail (META_IS_SHAPED_TEXTURE (stex)); + + priv = stex->priv; + + if (priv->input_shape_region != NULL) + { + cairo_region_destroy (priv->input_shape_region); + priv->input_shape_region = NULL; + } + + if (shape_region != NULL) + { + cairo_region_reference (shape_region); + priv->input_shape_region = shape_region; + } + + clutter_actor_queue_redraw (CLUTTER_ACTOR (stex)); +} + /** * meta_shaped_texture_set_clip_region: * @stex: a #MetaShapedTexture diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c index 8251432fb..34a8fa1b9 100644 --- a/src/compositor/meta-window-actor.c +++ b/src/compositor/meta-window-actor.c @@ -72,6 +72,8 @@ struct _MetaWindowActorPrivate /* A region that matches the shape of the window, including frame bounds */ cairo_region_t *shape_region; + /* If the window has an input shape, a region that matches the shape */ + cairo_region_t *input_shape_region; /* The opaque region, from _NET_WM_OPAQUE_REGION, intersected with * the shape region. */ cairo_region_t *opaque_region; @@ -413,6 +415,7 @@ meta_window_actor_dispose (GObject *object) meta_window_actor_detach (self); g_clear_pointer (&priv->shape_region, cairo_region_destroy); + g_clear_pointer (&priv->input_shape_region, cairo_region_destroy); g_clear_pointer (&priv->opaque_region, cairo_region_destroy); g_clear_pointer (&priv->shadow_clip, cairo_region_destroy); @@ -2082,39 +2085,40 @@ build_and_scan_frame_mask (MetaWindowActor *self, g_free (mask_data); } +static cairo_region_t * +region_create_from_x_rectangles (const XRectangle *rects, + int n_rects, + int dx, + int dy) +{ + 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 + dx; + cairo_rects[i].y = rects[i].y + dy; + cairo_rects[i].width = rects[i].width; + cairo_rects[i].height = rects[i].height; + } + + return cairo_region_create_rectangles (cairo_rects, n_rects); +} + 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; - if (priv->shadow_shape != NULL) { meta_window_shape_unref (priv->shadow_shape); priv->shadow_shape = NULL; } - 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); @@ -2124,6 +2128,8 @@ check_needs_reshape (MetaWindowActor *self) { /* Translate the set of XShape rectangles that we * get from the X server to a cairo_region. */ + MetaScreen *screen = priv->screen; + MetaDisplay *display = meta_screen_get_display (screen); Display *xdisplay = meta_display_get_xdisplay (display); XRectangle *rects; int n_rects, ordering; @@ -2138,20 +2144,10 @@ check_needs_reshape (MetaWindowActor *self) 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; - } - + region = region_create_from_x_rectangles (rects, n_rects, + client_area->x, + client_area->y); XFree (rects); - region = cairo_region_create_rectangles (cairo_rects, n_rects); - g_free (cairo_rects); } } #endif @@ -2167,14 +2163,14 @@ check_needs_reshape (MetaWindowActor *self) * window would have gotten if it was unshaped. In our case, * this is simply the client area. */ - cairo_region_intersect_rectangle (region, &client_area); + cairo_region_intersect_rectangle (region, client_area); } 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 @@ -2193,7 +2189,7 @@ 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_translate (priv->opaque_region, client_area->x, client_area->y); cairo_region_intersect (priv->opaque_region, region); } else if (priv->argb32) @@ -2207,15 +2203,108 @@ check_needs_reshape (MetaWindowActor *self) * and scans the mask looking for all opaque pixels, * adding it to region. */ - build_and_scan_frame_mask (self, &client_area, region); + build_and_scan_frame_mask (self, client_area, region); } priv->shape_region = region; - priv->needs_reshape = FALSE; meta_window_actor_invalidate_shadow (self); } +static void +meta_window_actor_update_input_shape_region (MetaWindowActor *self, + cairo_rectangle_int_t *client_area) +{ + MetaWindowActorPrivate *priv = self->priv; + cairo_region_t *region = NULL; + + g_clear_pointer (&priv->input_shape_region, cairo_region_destroy); + +#ifdef HAVE_SHAPE + /* Note: we currently assume that mutter never sets an input region + * when there is a frame. */ + if (priv->window->frame == NULL && priv->window->has_input_shape) + { + MetaScreen *screen = priv->screen; + MetaDisplay *display = meta_screen_get_display (screen); + Display *xdisplay = meta_display_get_xdisplay (display); + XRectangle *rects; + int n_rects, ordering; + + /* Note we only actually query the ShapeInput shape of a window + * when we don't have a frame because we assume currently that + * mutter never sets an ShapeInput shape on a frame. */ + meta_error_trap_push (display); + rects = XShapeGetRectangles (xdisplay, + priv->window->xwindow, + ShapeInput, + &n_rects, + &ordering); + meta_error_trap_pop (display); + if (rects) + { + region = region_create_from_x_rectangles (rects, n_rects, + client_area->x, + client_area->y); + XFree (rects); + } + } +#endif /* HAVE_SHAPE */ + + if (region != NULL) + { + /* The X shape extension requires us to intersect the input + * region with the effective bounding shape to determine the + * effective input region. + */ + if (priv->shape_region) + cairo_region_intersect (region, priv->shape_region); + else + cairo_region_intersect_rectangle (region, client_area); + } + 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); + } + + priv->input_shape_region = region; + + meta_shaped_texture_set_input_shape_region (META_SHAPED_TEXTURE (priv->actor), + priv->input_shape_region); +} + +static void +check_needs_reshape (MetaWindowActor *self) +{ + MetaWindowActorPrivate *priv = self->priv; + MetaFrameBorders borders; + cairo_rectangle_int_t client_area; + + 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_input_shape_region (self, &client_area); + + priv->needs_reshape = FALSE; +} + void meta_window_actor_update_shape (MetaWindowActor *self) { diff --git a/src/core/display.c b/src/core/display.c index 9d5a87cb0..9bcb5e5c7 100644 --- a/src/core/display.c +++ b/src/core/display.c @@ -2283,6 +2283,33 @@ event_callback (XEvent *event, window->desc); } + if (display->compositor) + meta_compositor_window_shape_changed (display->compositor, + window); + } + else if (sev->kind == ShapeInput) + { + if (sev->shaped && !window->has_input_shape) + { + window->has_input_shape = TRUE; + meta_topic (META_DEBUG_SHAPES, + "Window %s now has an input shape\n", + window->desc); + } + else if (!sev->shaped && window->has_input_shape) + { + window->has_input_shape = FALSE; + meta_topic (META_DEBUG_SHAPES, + "Window %s no longer has an input shape\n", + window->desc); + } + else + { + meta_topic (META_DEBUG_SHAPES, + "Window %s input shape changed\n", + window->desc); + } + if (display->compositor) meta_compositor_window_shape_changed (display->compositor, window); diff --git a/src/core/window-private.h b/src/core/window-private.h index ec94cca07..42d380f7e 100644 --- a/src/core/window-private.h +++ b/src/core/window-private.h @@ -325,8 +325,10 @@ 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 */ + /* has a bounding shape mask */ guint has_shape : 1; + /* has an input shape mask */ + guint has_input_shape : 1; /* icon props have changed */ guint need_reread_icon : 1; diff --git a/src/core/window.c b/src/core/window.c index 9252ab22d..faa9c773d 100644 --- a/src/core/window.c +++ b/src/core/window.c @@ -826,6 +826,7 @@ meta_window_new_with_attrs (MetaDisplay *display, gulong event_mask; MetaMoveResizeFlags flags; gboolean has_shape; + gboolean has_input_shape; MetaScreen *screen; g_assert (attrs != NULL); @@ -960,12 +961,15 @@ meta_window_new_with_attrs (MetaDisplay *display, } has_shape = FALSE; + has_input_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; + XRectangle *input_rectangles; + int n_rects, ordering; XShapeSelectInput (display->xdisplay, xwindow, ShapeNotifyMask); @@ -977,6 +981,27 @@ meta_window_new_with_attrs (MetaDisplay *display, has_shape = bounding_shaped != FALSE; + /* XXX: The x shape extension doesn't provide a way to only test if an + * input shape has been specified, so we have to query and throw away the + * rectangles. */ + meta_error_trap_push (display); + input_rectangles = XShapeGetRectangles (display->xdisplay, xwindow, + ShapeInput, &n_rects, &ordering); + meta_error_trap_pop (display); + if (input_rectangles) + { + if (n_rects > 1 || + (n_rects == 1 && + (input_rectangles[0].x != x_bounding || + input_rectangles[1].y != y_bounding || + input_rectangles[2].width != w_bounding || + input_rectangles[3].height != h_bounding))) + { + has_input_shape = TRUE; + } + XFree (input_rectangles); + } + meta_topic (META_DEBUG_SHAPES, "Window has_shape = %d extents %d,%d %u x %u\n", has_shape, x_bounding, y_bounding, @@ -1042,6 +1067,7 @@ meta_window_new_with_attrs (MetaDisplay *display, meta_stack_freeze (window->screen->stack); window->has_shape = has_shape; + window->has_input_shape = has_input_shape; window->rect.x = attrs->x; window->rect.y = attrs->y; diff --git a/src/meta/meta-shaped-texture.h b/src/meta/meta-shaped-texture.h index 28fb5f603..2e122843b 100644 --- a/src/meta/meta-shaped-texture.h +++ b/src/meta/meta-shaped-texture.h @@ -82,6 +82,8 @@ CoglTexture * meta_shaped_texture_get_texture (MetaShapedTexture *stex); void meta_shaped_texture_set_mask_texture (MetaShapedTexture *stex, CoglTexture *mask_texture); +void meta_shaped_texture_set_input_shape_region (MetaShapedTexture *stex, + cairo_region_t *shape_region); /* Assumes ownership of clip_region */ void meta_shaped_texture_set_clip_region (MetaShapedTexture *stex,