Commit 2d35e07f authored by Jasper St. Pierre's avatar Jasper St. Pierre
Browse files

wayland: Add support for set_opaque_region / set_input_region

https://bugzilla.gnome.org/show_bug.cgi?id=707019
parent 95e2d26d
......@@ -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));
......
......@@ -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;
......@@ -416,8 +415,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
......@@ -447,7 +446,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);
......@@ -1269,7 +1268,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))
......@@ -2180,33 +2179,36 @@ 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)
static void
meta_window_actor_update_shape_region (MetaWindowActor *self,
cairo_rectangle_int_t *client_area)
{
int i;
cairo_rectangle_int_t *cairo_rects = g_newa (cairo_rectangle_int_t, n_rects);
MetaWindowActorPrivate *priv = self->priv;
cairo_region_t *region = NULL;
for (i = 0; i < n_rects; i ++)
if (priv->window->frame != NULL && priv->window->shape_region != NULL)
{
region = cairo_region_copy (priv->window->shape_region);
cairo_region_translate (region, client_area->x, client_area->y);
}
else if (priv->window->shape_region != NULL)
{
region = cairo_region_reference (priv->window->shape_region);
}
else
{
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;
/* 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);
}
return cairo_region_create_rectangles (cairo_rects, n_rects);
}
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);
static void
meta_window_actor_update_x11_shape_region (MetaWindowActor *self,
cairo_rectangle_int_t *client_area)
{
MetaWindowActorPrivate *priv = self->priv;
cairo_region_t *region = NULL;
gboolean needs_mask;
g_clear_pointer (&priv->shape_region, cairo_region_destroy);
priv->shape_region = region;
if (priv->shadow_shape != NULL)
{
......@@ -2214,51 +2216,32 @@ meta_window_actor_update_x11_shape_region (MetaWindowActor *self,
priv->shadow_shape = NULL;
}
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);
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;
#ifdef HAVE_SHAPE
if (priv->window->has_shape)
if (priv->window->frame != NULL && priv->window->input_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;
region = meta_frame_get_frame_bounds (priv->window->frame);
meta_error_trap_push (display);
rects = XShapeGetRectangles (xdisplay,
priv->window->xwindow,
ShapeBounding,
&n_rects,
&ordering);
meta_error_trap_pop (display);
cairo_region_subtract_rectangle (region, client_area);
if (rects)
{
region = region_create_from_x_rectangles (rects, n_rects,
client_area->x,
client_area->y);
XFree (rects);
}
/* 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);
}
#endif
needs_mask = (region != NULL) || (priv->window->frame != NULL);
if (region != NULL)
else if (priv->window->shape_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->input_region);
}
else
{
......@@ -2268,11 +2251,23 @@ 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_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
......@@ -2284,91 +2279,13 @@ 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);
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);
priv->opaque_region = cairo_region_reference (priv->shape_region);
}
static void
......@@ -2394,24 +2311,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;
}
......
......@@ -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)
......
......@@ -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
{
......
......@@ -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
......@@ -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
......
......@@ -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