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
f0c503b5a9
commit
531be6c413
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user