diff --git a/src/compositor/meta-shaped-texture.c b/src/compositor/meta-shaped-texture.c index 26727e9bf..d14f03f1e 100644 --- a/src/compositor/meta-shaped-texture.c +++ b/src/compositor/meta-shaped-texture.c @@ -72,6 +72,7 @@ struct _MetaShapedTexturePrivate cairo_region_t *clip_region; cairo_region_t *opaque_region; + cairo_region_t *input_shape_region; guint tex_width, tex_height; @@ -394,45 +395,60 @@ 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; CoglPipeline *pipeline; CoglContext *ctx; CoglFramebuffer *fb; CoglColor cogl_color; - 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); + rectangles = g_alloca (sizeof (float) * 4 * n_rects); - 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); + + rectangles[pos] = rect.x; + rectangles[pos + 1] = rect.y; + rectangles[pos + 2] = rect.x + rect.width; + rectangles[pos + 3] = rect.y + rect.height; + } ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ()); fb = cogl_get_draw_framebuffer (); cogl_color_init_from_4ub (&cogl_color, color->red, color->green, color->blue, color->alpha); - pipeline = get_masked_pipeline (ctx); - cogl_pipeline_set_layer_texture (pipeline, 1, priv->mask_texture); + pipeline = cogl_pipeline_new (ctx); cogl_pipeline_set_color (pipeline, &cogl_color); - clutter_actor_get_allocation_box (actor, &alloc); - - cogl_framebuffer_draw_rectangle (fb, pipeline, - 0, 0, - alloc.x2 - alloc.x1, - alloc.y2 - alloc.y1); + cogl_framebuffer_draw_rectangles (fb, pipeline, + rectangles, n_rects); cogl_object_unref (pipeline); } } @@ -691,6 +707,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 77a0f2589..b486be3dd 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_region; /* The opaque region, from _NET_WM_OPAQUE_REGION, intersected with * the shape region. */ cairo_region_t *opaque_region; @@ -432,6 +434,7 @@ meta_window_actor_dispose (GObject *object) g_clear_pointer (&priv->unobscured_region, cairo_region_destroy); g_clear_pointer (&priv->shape_region, cairo_region_destroy); + g_clear_pointer (&priv->input_region, cairo_region_destroy); g_clear_pointer (&priv->opaque_region, cairo_region_destroy); g_clear_pointer (&priv->shadow_clip, cairo_region_destroy); @@ -2256,9 +2259,46 @@ meta_window_actor_update_shape_region (MetaWindowActor *self, 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_input_region (MetaWindowActor *self, + cairo_rectangle_int_t *client_area) +{ + MetaWindowActorPrivate *priv = self->priv; + MetaShapedTexture *stex = META_SHAPED_TEXTURE (priv->actor); + cairo_region_t *region = NULL; + + if (priv->window->frame != NULL && priv->window->input_region != NULL) + { + region = meta_frame_get_frame_bounds (priv->window->frame); + + cairo_region_subtract_rectangle (region, client_area); + + /* input_region is in client window coordinates, so translate the + * input region into that coordinate system and back */ + cairo_region_translate (region, -client_area->x, -client_area->y); + cairo_region_union (region, priv->window->input_region); + cairo_region_translate (region, client_area->x, client_area->y); + } + else if (priv->window->shape_region != NULL) + { + region = cairo_region_reference (priv->window->input_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); + } + + meta_shaped_texture_set_input_shape_region (stex, region); + cairo_region_destroy (region); +} + static void meta_window_actor_update_opaque_region (MetaWindowActor *self) { @@ -2319,10 +2359,10 @@ check_needs_reshape (MetaWindowActor *self) client_area.height = priv->window->rect.height; meta_window_actor_update_shape_region (self, &client_area); + meta_window_actor_update_input_region (self, &client_area); meta_window_actor_update_opaque_region (self); priv->needs_reshape = FALSE; - meta_window_actor_invalidate_shadow (self); } void diff --git a/src/core/window-private.h b/src/core/window-private.h index 757263241..fef3fd542 100644 --- a/src/core/window-private.h +++ b/src/core/window-private.h @@ -352,6 +352,9 @@ struct _MetaWindow /* if non-NULL, the opaque region _NET_WM_OPAQUE_REGION */ cairo_region_t *opaque_region; + /* the input shape region for picking */ + cairo_region_t *input_region; + /* if TRUE, the we have the new form of sync request counter which * also handles application frames */ guint extended_sync_request_counter : 1; @@ -682,6 +685,10 @@ 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_input_region (MetaWindow *window, + cairo_region_t *region); +void meta_window_update_input_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); diff --git a/src/core/window.c b/src/core/window.c index d7056a8cb..a75fd6f94 100644 --- a/src/core/window.c +++ b/src/core/window.c @@ -1194,6 +1194,7 @@ meta_window_new_with_attrs (MetaDisplay *display, meta_display_register_x_window (display, &window->xwindow, window); meta_window_update_shape_region_x11 (window); + meta_window_update_input_region_x11 (window); /* Assign this #MetaWindow a sequence number which can be used * for sorting. @@ -7712,6 +7713,91 @@ meta_window_set_shape_region (MetaWindow *window, meta_compositor_window_shape_changed (window->display->compositor, window); } +void +meta_window_update_input_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); + + rects = XShapeGetRectangles (window->display->xdisplay, + window->xwindow, + ShapeInput, + &n_rects, + &ordering); + meta_error_trap_pop (window->display); + + /* 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. */ + if (rects) + { + if (n_rects > 1 || + (n_rects == 1 && + (rects[0].x != x_bounding || + rects[1].y != y_bounding || + rects[2].width != w_bounding || + rects[3].height != h_bounding))) + region = region_create_from_x_rectangles (rects, n_rects); + + XFree (rects); + } + } +#endif /* HAVE_SHAPE */ + + 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_input_region (window, region); + cairo_region_destroy (region); +} + +void +meta_window_set_input_region (MetaWindow *window, + cairo_region_t *region) +{ + g_clear_pointer (&window->input_region, cairo_region_destroy); + + if (region != NULL) + window->input_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) { diff --git a/src/meta/meta-shaped-texture.h b/src/meta/meta-shaped-texture.h index 9300cd306..0a5c1af95 100644 --- a/src/meta/meta-shaped-texture.h +++ b/src/meta/meta-shaped-texture.h @@ -83,6 +83,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); void meta_shaped_texture_set_clip_region (MetaShapedTexture *stex, cairo_region_t *clip_region);