Compare commits

...

4 Commits

Author SHA1 Message Date
10fbaf7695 shaped-texture: Turn blending off when drawing entirely opaque regions
When drawing entirely opaque regions, we traditionally kept blending on
simply because it made the code more convenient and obvious to handle.
However, this can cause lots of performance issues on GPUs that aren't
too powerful, as they have to readback the buffer underneath.

Keep track of the opaque region set by windows (through _NET_WM_OPAQUE_REGION,
Wayland opaque_region hints, standard RGB32 frame masks or similar), and draw
those rectangles separately through a different path with blending turned off.

https://bugzilla.gnome.org/show_bug.cgi?id=706930
2013-08-28 13:23:09 -04:00
ef73402654 shaped-texture: Use non-deprecated cogl APIs
https://bugzilla.gnome.org/show_bug.cgi?id=706930
2013-08-28 13:23:09 -04:00
22cdf2d650 shaped-texture: Simplify pipeline creation
Split out pipeline creation to a separate function so that we don't
have so much dense code in the paint function itself, and remove some
indentation levels.

Also, don't use our own template for the unmasked pipeline, since it
has nothing different from the default pipeline template.

We also don't store the pipelines anymore since their creation isn't
really helping us; we set the mask texture and paint texture on every
paint anyway.

https://bugzilla.gnome.org/show_bug.cgi?id=706930
2013-08-28 13:23:08 -04:00
284b497b4c wayland: Add support for set_opaque_region / set_input_region 2013-08-28 13:18:04 -04:00
11 changed files with 527 additions and 400 deletions

View File

@ -930,8 +930,8 @@ is_grabbed_event (MetaDisplay *display,
}
void
meta_compositor_window_x11_shape_changed (MetaCompositor *compositor,
MetaWindow *window)
meta_compositor_window_shape_changed (MetaCompositor *compositor,
MetaWindow *window)
{
MetaWindowActor *window_actor;
window_actor = META_WINDOW_ACTOR (meta_window_get_compositor_private (window));

View File

@ -91,11 +91,10 @@ struct _MetaShapedTexturePrivate
CoglTexture *texture;
CoglTexture *mask_texture;
CoglPipeline *pipeline;
CoglPipeline *pipeline_unshaped;
cairo_region_t *clip_region;
cairo_region_t *input_shape_region;
cairo_region_t *opaque_region;
guint tex_width, tex_height;
@ -145,8 +144,6 @@ meta_shaped_texture_dispose (GObject *object)
meta_texture_tower_free (priv->paint_tower);
priv->paint_tower = NULL;
g_clear_pointer (&priv->pipeline, cogl_object_unref);
g_clear_pointer (&priv->pipeline_unshaped, cogl_object_unref);
g_clear_pointer (&priv->texture, cogl_object_unref);
meta_shaped_texture_set_mask_texture (self, NULL);
@ -155,6 +152,76 @@ meta_shaped_texture_dispose (GObject *object)
G_OBJECT_CLASS (meta_shaped_texture_parent_class)->dispose (object);
}
static CoglPipeline *
get_unmasked_pipeline (CoglContext *ctx)
{
return cogl_pipeline_new (ctx);
}
static CoglPipeline *
get_masked_pipeline (CoglContext *ctx)
{
static CoglPipeline *template = NULL;
if (G_UNLIKELY (template == NULL))
{
template = cogl_pipeline_new (ctx);
cogl_pipeline_set_layer_combine (template, 1,
"RGBA = MODULATE (PREVIOUS, TEXTURE[A])",
NULL);
}
return cogl_pipeline_copy (template);
}
static CoglPipeline *
get_unblended_pipeline (CoglContext *ctx)
{
static CoglPipeline *template = NULL;
if (G_UNLIKELY (template == NULL))
{
CoglColor color;
template = cogl_pipeline_new (ctx);
cogl_color_init_from_4ub (&color, 255, 255, 255, 255);
cogl_pipeline_set_blend (template,
"RGBA = ADD (SRC_COLOR, 0)",
NULL);
cogl_pipeline_set_color (template, &color);
}
return cogl_pipeline_copy (template);
}
static void
paint_clipped_rectangle (CoglFramebuffer *fb,
CoglPipeline *pipeline,
cairo_rectangle_int_t *rect,
ClutterActorBox *alloc)
{
float coords[8];
float x1, y1, x2, y2;
x1 = rect->x;
y1 = rect->y;
x2 = rect->x + rect->width;
y2 = rect->y + rect->height;
coords[0] = rect->x / (alloc->x2 - alloc->x1);
coords[1] = rect->y / (alloc->y2 - alloc->y1);
coords[2] = (rect->x + rect->width) / (alloc->x2 - alloc->x1);
coords[3] = (rect->y + rect->height) / (alloc->y2 - alloc->y1);
coords[4] = coords[0];
coords[5] = coords[1];
coords[6] = coords[2];
coords[7] = coords[3];
cogl_framebuffer_draw_multitextured_rectangle (fb, pipeline,
x1, y1, x2, y2,
&coords[0], 8);
}
static void
set_cogl_texture (MetaShapedTexture *stex,
CoglTexture *cogl_tex)
@ -171,12 +238,6 @@ set_cogl_texture (MetaShapedTexture *stex,
priv->texture = cogl_tex;
if (priv->pipeline != NULL)
cogl_pipeline_set_layer_texture (priv->pipeline, 0, COGL_TEXTURE (cogl_tex));
if (priv->pipeline_unshaped != NULL)
cogl_pipeline_set_layer_texture (priv->pipeline_unshaped, 0, COGL_TEXTURE (cogl_tex));
if (cogl_tex != NULL)
{
width = cogl_texture_get_width (COGL_TEXTURE (cogl_tex));
@ -210,14 +271,14 @@ meta_shaped_texture_paint (ClutterActor *actor)
{
MetaShapedTexture *stex = (MetaShapedTexture *) actor;
MetaShapedTexturePrivate *priv = stex->priv;
CoglTexture *paint_tex;
guint tex_width, tex_height;
guchar opacity;
CoglContext *ctx;
CoglFramebuffer *fb;
CoglPipeline *pipeline = NULL;
CoglTexture *paint_tex;
ClutterActorBox alloc;
static CoglPipeline *pipeline_template = NULL;
static CoglPipeline *pipeline_unshaped_template = NULL;
CoglPipeline *pipeline;
cairo_region_t *blended_region = NULL;
if (priv->clip_region && cairo_region_is_empty (priv->clip_region))
return;
@ -254,38 +315,67 @@ meta_shaped_texture_paint (ClutterActor *actor)
if (tex_width == 0 || tex_height == 0) /* no contents yet */
return;
ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
fb = cogl_get_draw_framebuffer ();
opacity = clutter_actor_get_paint_opacity (actor);
clutter_actor_get_allocation_box (actor, &alloc);
if (priv->opaque_region != NULL && opacity == 255)
{
CoglPipeline *opaque_pipeline;
cairo_region_t *region;
int n_rects;
int i;
region = cairo_region_copy (priv->clip_region);
cairo_region_intersect (region, priv->opaque_region);
if (cairo_region_is_empty (region))
goto paint_blended;
opaque_pipeline = get_unblended_pipeline (ctx);
cogl_pipeline_set_layer_texture (opaque_pipeline, 0, paint_tex);
n_rects = cairo_region_num_rectangles (region);
for (i = 0; i < n_rects; i++)
{
cairo_rectangle_int_t rect;
cairo_region_get_rectangle (region, i, &rect);
paint_clipped_rectangle (fb, opaque_pipeline, &rect, &alloc);
}
cogl_object_unref (opaque_pipeline);
if (priv->clip_region != NULL)
{
blended_region = cairo_region_copy (priv->clip_region);
}
else
{
cairo_rectangle_int_t rect = { 0, 0, tex_width, tex_height };
blended_region = cairo_region_create_rectangle (&rect);
}
cairo_region_subtract (blended_region, priv->opaque_region);
paint_blended:
cairo_region_destroy (region);
}
if (blended_region == NULL && priv->clip_region != NULL)
blended_region = cairo_region_reference (priv->clip_region);
if (blended_region != NULL && cairo_region_is_empty (blended_region))
goto out;
if (priv->mask_texture == NULL)
{
/* Use a single-layer texture if we don't have a mask. */
if (priv->pipeline_unshaped == NULL)
{
if (G_UNLIKELY (pipeline_unshaped_template == NULL))
{
CoglContext *ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
pipeline_unshaped_template = cogl_pipeline_new (ctx);
}
priv->pipeline_unshaped = cogl_pipeline_copy (pipeline_unshaped_template);
}
pipeline = priv->pipeline_unshaped;
pipeline = get_unmasked_pipeline (ctx);
}
else
{
if (priv->pipeline == NULL)
{
if (G_UNLIKELY (pipeline_template == NULL))
{
CoglContext *ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
pipeline_template = cogl_pipeline_new (ctx);
cogl_pipeline_set_layer_combine (pipeline_template, 1,
"RGBA = MODULATE (PREVIOUS, TEXTURE[A])",
NULL);
}
priv->pipeline = cogl_pipeline_copy (pipeline_template);
}
pipeline = priv->pipeline;
pipeline = get_masked_pipeline (ctx);
cogl_pipeline_set_layer_texture (pipeline, 1, priv->mask_texture);
}
@ -293,20 +383,13 @@ meta_shaped_texture_paint (ClutterActor *actor)
{
CoglColor color;
guchar opacity = clutter_actor_get_paint_opacity (actor);
cogl_color_set_from_4ub (&color, opacity, opacity, opacity, opacity);
cogl_color_init_from_4ub (&color, opacity, opacity, opacity, opacity);
cogl_pipeline_set_color (pipeline, &color);
}
cogl_set_source (pipeline);
clutter_actor_get_allocation_box (actor, &alloc);
if (priv->clip_region)
if (blended_region != NULL)
{
int n_rects;
int i;
cairo_rectangle_int_t tex_rect = { 0, 0, tex_width, tex_height };
/* Limit to how many separate rectangles we'll draw; beyond this just
* fall back and draw the whole thing */
@ -315,8 +398,8 @@ meta_shaped_texture_paint (ClutterActor *actor)
n_rects = cairo_region_num_rectangles (priv->clip_region);
if (n_rects <= MAX_RECTS)
{
float coords[8];
float x1, y1, x2, y2;
int i;
cairo_rectangle_int_t tex_rect = { 0, 0, tex_width, tex_height };
for (i = 0; i < n_rects; i++)
{
@ -327,32 +410,23 @@ meta_shaped_texture_paint (ClutterActor *actor)
if (!gdk_rectangle_intersect (&tex_rect, &rect, &rect))
continue;
x1 = rect.x;
y1 = rect.y;
x2 = rect.x + rect.width;
y2 = rect.y + rect.height;
coords[0] = rect.x / (alloc.x2 - alloc.x1);
coords[1] = rect.y / (alloc.y2 - alloc.y1);
coords[2] = (rect.x + rect.width) / (alloc.x2 - alloc.x1);
coords[3] = (rect.y + rect.height) / (alloc.y2 - alloc.y1);
coords[4] = coords[0];
coords[5] = coords[1];
coords[6] = coords[2];
coords[7] = coords[3];
cogl_rectangle_with_multitexture_coords (x1, y1, x2, y2,
&coords[0], 8);
paint_clipped_rectangle (fb, pipeline, &rect, &alloc);
}
return;
goto out;
}
}
cogl_rectangle (0, 0,
alloc.x2 - alloc.x1,
alloc.y2 - alloc.y1);
cogl_framebuffer_draw_rectangle (fb, pipeline,
0, 0,
alloc.x2 - alloc.x1,
alloc.y2 - alloc.y1);
out:
if (pipeline != NULL)
cogl_object_unref (pipeline);
if (blended_region != NULL)
cairo_region_destroy (blended_region);
}
static void
@ -368,13 +442,17 @@ meta_shaped_texture_pick (ClutterActor *actor,
/* If there is no region then use the regular pick */
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
{
int n_rects;
float *rectangles;
int i;
ClutterActorBox alloc;
CoglPipeline *pipeline;
CoglContext *ctx;
CoglFramebuffer *fb;
CoglColor cogl_color;
/* Note: We don't bother trying to intersect the pick and clip regions
* since needing to copy the region, do the intersection, and probably
@ -403,12 +481,17 @@ meta_shaped_texture_pick (ClutterActor *actor,
rectangles[pos + 3] = rect.y + rect.height;
}
cogl_set_source_color4ub (color->red,
color->green,
color->blue,
color->alpha);
ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
fb = cogl_get_draw_framebuffer ();
cogl_rectangles (rectangles, n_rects);
cogl_color_init_from_4ub (&cogl_color, color->red, color->green, color->blue, color->alpha);
pipeline = cogl_pipeline_new (ctx);
cogl_pipeline_set_color (pipeline, &cogl_color);
cogl_framebuffer_draw_rectangles (fb, pipeline,
rectangles, n_rects);
cogl_object_unref (pipeline);
}
}
@ -845,6 +928,36 @@ meta_shaped_texture_set_clip_region (MetaShapedTexture *stex,
priv->clip_region = NULL;
}
/**
* meta_shaped_texture_set_opaque_region:
* @stex: a #MetaShapedTexture
* @opaque_region: (transfer full): the region of the texture that
* can have blending turned off.
*
* As most windows have a large portion that does not require blending,
* we can easily turn off blending if we know the areas that do not
* require blending. This sets the region where we will not blend for
* optimization purposes.
*/
void
meta_shaped_texture_set_opaque_region (MetaShapedTexture *stex,
cairo_region_t *opaque_region)
{
MetaShapedTexturePrivate *priv;
g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));
priv = stex->priv;
if (priv->opaque_region)
cairo_region_destroy (priv->opaque_region);
if (opaque_region)
priv->opaque_region = cairo_region_reference (opaque_region);
else
priv->opaque_region = NULL;
}
/**
* meta_shaped_texture_get_image:
* @stex: A #MetaShapedTexture

View File

@ -10,7 +10,6 @@
#include <math.h>
#include <X11/extensions/shape.h>
#include <X11/extensions/Xcomposite.h>
#include <X11/extensions/Xdamage.h>
#include <X11/extensions/Xrender.h>
@ -71,7 +70,7 @@ 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;
cairo_region_t *input_region;
/* The opaque region, from _NET_WM_OPAQUE_REGION, intersected with
* the shape region. */
cairo_region_t *opaque_region;
@ -407,8 +406,8 @@ meta_window_actor_constructed (GObject *object)
/* Start off with empty regions to maintain the invariant that
these regions are always set */
priv->shape_region = cairo_region_create();
priv->input_shape_region = cairo_region_create();
priv->shape_region = cairo_region_create ();
priv->input_region = cairo_region_create ();
}
static void
@ -438,7 +437,7 @@ meta_window_actor_dispose (GObject *object)
}
g_clear_pointer (&priv->shape_region, cairo_region_destroy);
g_clear_pointer (&priv->input_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);
@ -1260,7 +1259,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))
@ -2171,85 +2170,21 @@ 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
meta_window_actor_update_x11_shape_region (MetaWindowActor *self,
cairo_rectangle_int_t *client_area)
meta_window_actor_update_shape_region (MetaWindowActor *self,
cairo_rectangle_int_t *client_area)
{
MetaWindowActorPrivate *priv = self->priv;
cairo_region_t *region = NULL;
gboolean needs_mask;
if (priv->shadow_shape != NULL)
if (priv->window->frame != NULL && priv->window->shape_region != NULL)
{
meta_window_shape_unref (priv->shadow_shape);
priv->shadow_shape = NULL;
region = cairo_region_copy (priv->window->shape_region);
cairo_region_translate (region, client_area->x, client_area->y);
}
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)
else if (priv->window->shape_region != NULL)
{
/* 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;
meta_error_trap_push (display);
rects = XShapeGetRectangles (xdisplay,
priv->window->xwindow,
ShapeBounding,
&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
needs_mask = (region != NULL) || (priv->window->frame != NULL);
if (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
{
@ -2259,11 +2194,70 @@ meta_window_actor_update_x11_shape_region (MetaWindowActor *self,
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;
if (priv->shadow_shape != NULL)
{
meta_window_shape_unref (priv->shadow_shape);
priv->shadow_shape = NULL;
}
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->shape_region != NULL)
{
cairo_region_t *client_region = cairo_region_copy (priv->window->input_region);
region = meta_frame_get_frame_bounds (priv->window->frame);
cairo_region_subtract_rectangle (region, client_area);
cairo_region_translate (client_region, client_area->x, client_area->y);
cairo_region_union (region, client_region);
cairo_region_destroy (client_region);
}
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)
{
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
@ -2275,91 +2269,16 @@ meta_window_actor_update_x11_shape_region (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);
}
priv->shape_region = region;
meta_window_actor_invalidate_shadow (self);
}
static void
meta_window_actor_update_x11_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);
meta_shaped_texture_set_opaque_region (META_SHAPED_TEXTURE (priv->actor),
priv->opaque_region);
}
static void
@ -2385,24 +2304,9 @@ check_needs_reshape (MetaWindowActor *self)
else
client_area.height = priv->window->rect.height;
if (priv->window->client_type == META_WINDOW_CLIENT_TYPE_X11)
{
meta_window_actor_update_x11_shape_region (self, &client_area);
meta_window_actor_update_x11_input_shape_region (self, &client_area);
}
else
{
/* TODO: properly support setting an input region as specified
* via the wayland protocol */
g_clear_pointer (&priv->shape_region, cairo_region_destroy);
g_clear_pointer (&priv->opaque_region, cairo_region_destroy);
g_clear_pointer (&priv->input_shape_region, cairo_region_destroy);
priv->shape_region = cairo_region_create_rectangle (&client_area);
priv->opaque_region = cairo_region_reference (priv->shape_region);
priv->input_shape_region = cairo_region_reference (priv->shape_region);
}
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;
}
@ -2463,6 +2367,8 @@ meta_window_actor_set_wayland_surface (MetaWindowActor *self,
surface);
if (surface && surface->buffer_ref.buffer)
maybe_emit_size_changed (self, surface->buffer_ref.buffer);
meta_window_actor_invalidate_shadow (self);
}
void

View File

@ -186,7 +186,6 @@ meta_window_group_paint (ClutterActor *actor)
if (META_IS_WINDOW_ACTOR (child))
{
MetaWindow *meta_window;
MetaWindowActor *window_actor = META_WINDOW_ACTOR (child);
int x, y;
@ -201,14 +200,7 @@ meta_window_group_paint (ClutterActor *actor)
meta_window_actor_set_visible_region (window_actor, visible_region);
/* TODO: Track the opaque regions of wayland clients.
* Although wayland clients can report opaque window
* regions, for now we assume that all wayland clients are
* transparent... */
meta_window = meta_window_actor_get_meta_window (window_actor);
if (meta_window->client_type != META_WINDOW_CLIENT_TYPE_WAYLAND &&
clutter_actor_get_paint_opacity (CLUTTER_ACTOR (window_actor)) == 0xff)
if (clutter_actor_get_paint_opacity (CLUTTER_ACTOR (window_actor)) == 0xff)
{
cairo_region_t *obscured_region = meta_window_actor_get_obscured_region (window_actor);
if (obscured_region)

View File

@ -2336,59 +2336,9 @@ meta_display_handle_event (MetaDisplay *display,
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_x11_shape_changed (display->compositor,
window);
}
meta_window_update_shape_region_x11 (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_x11_shape_changed (display->compositor,
window);
}
meta_window_update_input_region_x11 (window);
}
else
{

View File

@ -339,11 +339,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 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;
@ -365,9 +360,15 @@ 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;
/* 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;
@ -685,7 +686,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);
@ -699,4 +699,16 @@ 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_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);
#endif

View File

@ -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

View File

@ -822,8 +822,6 @@ meta_window_new_shared (MetaDisplay *display,
Window xwindow,
gboolean must_be_viewable,
gulong existing_wm_state,
gboolean has_shape,
gboolean has_input_shape,
MetaCompEffect effect,
XWindowAttributes *attrs)
{
@ -876,9 +874,6 @@ meta_window_new_shared (MetaDisplay *display,
/* avoid tons of stack updates */
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;
window->rect.width = attrs->width;
@ -1059,6 +1054,8 @@ meta_window_new_shared (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 the window to its group, or create a new group if needed
@ -1425,8 +1422,6 @@ meta_window_new_for_wayland (MetaDisplay *display,
None,
TRUE,
WithdrawnState,
FALSE, /* has shape */
FALSE, /* has input shape */
META_COMP_EFFECT_NONE,
&attrs);
@ -1455,8 +1450,6 @@ meta_window_new_with_attrs (MetaDisplay *display,
gulong existing_wm_state;
MetaWindow *window;
gulong event_mask;
gboolean has_shape = FALSE;
gboolean has_input_shape = FALSE;
meta_verbose ("Attempting to manage 0x%lx\n", xwindow);
@ -1576,53 +1569,6 @@ meta_window_new_with_attrs (MetaDisplay *display,
XISelectEvents (display->xdisplay, xwindow, &mask, 1);
}
#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);
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;
/* 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,
w_bounding, h_bounding);
}
#endif
/* Get rid of any borders */
if (attrs->border_width != 0)
XSetWindowBorderWidth (display->xdisplay, xwindow, 0);
@ -1656,8 +1602,6 @@ meta_window_new_with_attrs (MetaDisplay *display,
xwindow,
must_be_viewable,
existing_wm_state,
has_shape,
has_input_shape,
effect,
attrs);
@ -7810,14 +7754,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,
@ -7860,11 +7815,191 @@ 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_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_x11_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_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 /* 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_shape_region (window, region);
cairo_region_destroy (region);
}
static void

View File

@ -64,8 +64,8 @@ void meta_compositor_manage_screen (MetaCompositor *compositor,
void meta_compositor_unmanage_screen (MetaCompositor *compositor,
MetaScreen *screen);
void meta_compositor_window_x11_shape_changed (MetaCompositor *compositor,
MetaWindow *window);
void meta_compositor_window_shape_changed (MetaCompositor *compositor,
MetaWindow *window);
gboolean meta_compositor_process_event (MetaCompositor *compositor,
XEvent *event,

View File

@ -84,6 +84,9 @@ void meta_shaped_texture_set_input_shape_region (MetaShapedTexture *stex,
void meta_shaped_texture_set_clip_region (MetaShapedTexture *stex,
cairo_region_t *clip_region);
void meta_shaped_texture_set_opaque_region (MetaShapedTexture *stex,
cairo_region_t *opaque_region);
cairo_surface_t * meta_shaped_texture_get_image (MetaShapedTexture *stex,
cairo_rectangle_int_t *clip);

View File

@ -324,18 +324,34 @@ meta_wayland_surface_frame (struct wl_client *client,
static void
meta_wayland_surface_set_opaque_region (struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *region)
struct wl_resource *surface_resource,
struct wl_resource *region_resource)
{
g_warning ("TODO: support set_opaque_region request");
MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource);
MetaWaylandRegion *region = wl_resource_get_user_data (region_resource);
/* X11 unmanaged window */
if (!surface)
return;
if (surface->window)
meta_window_set_opaque_region (surface->window, cairo_region_copy (region->region));
}
static void
meta_wayland_surface_set_input_region (struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *region)
struct wl_resource *surface_resource,
struct wl_resource *region_resource)
{
g_warning ("TODO: support set_input_region request");
MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource);
MetaWaylandRegion *region = wl_resource_get_user_data (region_resource);
/* X11 unmanaged window */
if (!surface)
return;
if (surface->window)
meta_window_set_input_region (surface->window, cairo_region_copy (region->region));
}
static void