Track the X Shape input region and use it for picking
We now track whether a window has an input shape specified via the X Shape extension. Intersecting that with the bounding shape (as required by the X Shape extension) we use the resulting rectangles to paint window silhouettes when picking. As well as improving the correctness of picking this should also be much more efficient because typically when only picking solid rectangles then the need to actually render and issue a read_pixels request can be optimized away and instead the picking is done on the cpu.
This commit is contained in:
parent
4373916d9d
commit
61881477ac
@ -72,6 +72,7 @@ struct _MetaShapedTexturePrivate
|
|||||||
|
|
||||||
cairo_region_t *clip_region;
|
cairo_region_t *clip_region;
|
||||||
cairo_region_t *opaque_region;
|
cairo_region_t *opaque_region;
|
||||||
|
cairo_region_t *input_shape_region;
|
||||||
|
|
||||||
guint tex_width, tex_height;
|
guint tex_width, tex_height;
|
||||||
|
|
||||||
@ -394,45 +395,60 @@ meta_shaped_texture_pick (ClutterActor *actor,
|
|||||||
MetaShapedTexture *stex = (MetaShapedTexture *) actor;
|
MetaShapedTexture *stex = (MetaShapedTexture *) actor;
|
||||||
MetaShapedTexturePrivate *priv = stex->priv;
|
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 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);
|
CLUTTER_ACTOR_CLASS (meta_shaped_texture_parent_class)->pick (actor, color);
|
||||||
else if (clutter_actor_should_pick_paint (actor))
|
else
|
||||||
{
|
{
|
||||||
CoglTexture *paint_tex;
|
int n_rects;
|
||||||
ClutterActorBox alloc;
|
float *rectangles;
|
||||||
guint tex_width, tex_height;
|
int i;
|
||||||
CoglPipeline *pipeline;
|
CoglPipeline *pipeline;
|
||||||
CoglContext *ctx;
|
CoglContext *ctx;
|
||||||
CoglFramebuffer *fb;
|
CoglFramebuffer *fb;
|
||||||
CoglColor cogl_color;
|
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)
|
n_rects = cairo_region_num_rectangles (priv->input_shape_region);
|
||||||
return;
|
rectangles = g_alloca (sizeof (float) * 4 * n_rects);
|
||||||
|
|
||||||
tex_width = cogl_texture_get_width (paint_tex);
|
for (i = 0; i < n_rects; i++)
|
||||||
tex_height = cogl_texture_get_height (paint_tex);
|
{
|
||||||
|
cairo_rectangle_int_t rect;
|
||||||
|
int pos = i * 4;
|
||||||
|
|
||||||
if (tex_width == 0 || tex_height == 0) /* no contents yet */
|
cairo_region_get_rectangle (priv->input_shape_region, i, &rect);
|
||||||
return;
|
|
||||||
|
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 ());
|
ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
|
||||||
fb = cogl_get_draw_framebuffer ();
|
fb = cogl_get_draw_framebuffer ();
|
||||||
|
|
||||||
cogl_color_init_from_4ub (&cogl_color, color->red, color->green, color->blue, color->alpha);
|
cogl_color_init_from_4ub (&cogl_color, color->red, color->green, color->blue, color->alpha);
|
||||||
|
|
||||||
pipeline = get_masked_pipeline (ctx);
|
pipeline = cogl_pipeline_new (ctx);
|
||||||
cogl_pipeline_set_layer_texture (pipeline, 1, priv->mask_texture);
|
|
||||||
cogl_pipeline_set_color (pipeline, &cogl_color);
|
cogl_pipeline_set_color (pipeline, &cogl_color);
|
||||||
|
|
||||||
clutter_actor_get_allocation_box (actor, &alloc);
|
cogl_framebuffer_draw_rectangles (fb, pipeline,
|
||||||
|
rectangles, n_rects);
|
||||||
cogl_framebuffer_draw_rectangle (fb, pipeline,
|
|
||||||
0, 0,
|
|
||||||
alloc.x2 - alloc.x1,
|
|
||||||
alloc.y2 - alloc.y1);
|
|
||||||
cogl_object_unref (pipeline);
|
cogl_object_unref (pipeline);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -691,6 +707,41 @@ meta_shaped_texture_get_texture (MetaShapedTexture *stex)
|
|||||||
return COGL_TEXTURE (stex->priv->texture);
|
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:
|
* meta_shaped_texture_set_clip_region:
|
||||||
* @stex: a #MetaShapedTexture
|
* @stex: a #MetaShapedTexture
|
||||||
|
@ -72,6 +72,8 @@ struct _MetaWindowActorPrivate
|
|||||||
|
|
||||||
/* A region that matches the shape of the window, including frame bounds */
|
/* A region that matches the shape of the window, including frame bounds */
|
||||||
cairo_region_t *shape_region;
|
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 opaque region, from _NET_WM_OPAQUE_REGION, intersected with
|
||||||
* the shape region. */
|
* the shape region. */
|
||||||
cairo_region_t *opaque_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->unobscured_region, cairo_region_destroy);
|
||||||
g_clear_pointer (&priv->shape_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->opaque_region, cairo_region_destroy);
|
||||||
g_clear_pointer (&priv->shadow_clip, 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;
|
priv->shape_region = region;
|
||||||
|
|
||||||
g_clear_pointer (&priv->shadow_shape, meta_window_shape_unref);
|
g_clear_pointer (&priv->shadow_shape, meta_window_shape_unref);
|
||||||
|
|
||||||
meta_window_actor_invalidate_shadow (self);
|
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
|
static void
|
||||||
meta_window_actor_update_opaque_region (MetaWindowActor *self)
|
meta_window_actor_update_opaque_region (MetaWindowActor *self)
|
||||||
{
|
{
|
||||||
@ -2319,10 +2359,10 @@ check_needs_reshape (MetaWindowActor *self)
|
|||||||
client_area.height = priv->window->rect.height;
|
client_area.height = priv->window->rect.height;
|
||||||
|
|
||||||
meta_window_actor_update_shape_region (self, &client_area);
|
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);
|
meta_window_actor_update_opaque_region (self);
|
||||||
|
|
||||||
priv->needs_reshape = FALSE;
|
priv->needs_reshape = FALSE;
|
||||||
meta_window_actor_invalidate_shadow (self);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -352,6 +352,9 @@ struct _MetaWindow
|
|||||||
/* if non-NULL, the opaque region _NET_WM_OPAQUE_REGION */
|
/* if non-NULL, the opaque region _NET_WM_OPAQUE_REGION */
|
||||||
cairo_region_t *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
|
/* if TRUE, the we have the new form of sync request counter which
|
||||||
* also handles application frames */
|
* also handles application frames */
|
||||||
guint extended_sync_request_counter : 1;
|
guint extended_sync_request_counter : 1;
|
||||||
@ -682,6 +685,10 @@ void meta_window_set_opaque_region (MetaWindow *window,
|
|||||||
cairo_region_t *region);
|
cairo_region_t *region);
|
||||||
void meta_window_update_opaque_region_x11 (MetaWindow *window);
|
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,
|
void meta_window_set_shape_region (MetaWindow *window,
|
||||||
cairo_region_t *region);
|
cairo_region_t *region);
|
||||||
void meta_window_update_shape_region_x11 (MetaWindow *window);
|
void meta_window_update_shape_region_x11 (MetaWindow *window);
|
||||||
|
@ -1194,6 +1194,7 @@ meta_window_new_with_attrs (MetaDisplay *display,
|
|||||||
meta_display_register_x_window (display, &window->xwindow, window);
|
meta_display_register_x_window (display, &window->xwindow, window);
|
||||||
|
|
||||||
meta_window_update_shape_region_x11 (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
|
/* Assign this #MetaWindow a sequence number which can be used
|
||||||
* for sorting.
|
* for sorting.
|
||||||
@ -7712,6 +7713,91 @@ meta_window_set_shape_region (MetaWindow *window,
|
|||||||
meta_compositor_window_shape_changed (window->display->compositor, 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
|
void
|
||||||
meta_window_update_shape_region_x11 (MetaWindow *window)
|
meta_window_update_shape_region_x11 (MetaWindow *window)
|
||||||
{
|
{
|
||||||
|
@ -83,6 +83,8 @@ CoglTexture * meta_shaped_texture_get_texture (MetaShapedTexture *stex);
|
|||||||
|
|
||||||
void meta_shaped_texture_set_mask_texture (MetaShapedTexture *stex,
|
void meta_shaped_texture_set_mask_texture (MetaShapedTexture *stex,
|
||||||
CoglTexture *mask_texture);
|
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,
|
void meta_shaped_texture_set_clip_region (MetaShapedTexture *stex,
|
||||||
cairo_region_t *clip_region);
|
cairo_region_t *clip_region);
|
||||||
|
Loading…
Reference in New Issue
Block a user