Refactor how shapes are done

As part of Wayland support, we should hold the shape and opaque regions
on the MetaWindow rather than fetching them in the MetaWindowActor, as
this gives us better flexibility as to where the regions are set, and
allows for easier Wayland support.

To make merging easier with the Wayland branch, we also append the _x11
suffix to functions that use the X SHAPE extension to fetch the shaped
regions.

https://bugzilla.gnome.org/show_bug.cgi?id=706930
This commit is contained in:
Jasper St. Pierre 2013-08-23 23:30:47 -04:00
parent 9d8e7371fb
commit f1df49ad17
5 changed files with 185 additions and 151 deletions

View File

@ -10,7 +10,6 @@
#include <math.h> #include <math.h>
#include <X11/extensions/shape.h>
#include <X11/extensions/Xcomposite.h> #include <X11/extensions/Xcomposite.h>
#include <X11/extensions/Xdamage.h> #include <X11/extensions/Xdamage.h>
#include <X11/extensions/Xrender.h> #include <X11/extensions/Xrender.h>
@ -400,7 +399,7 @@ meta_window_actor_constructed (GObject *object)
/* Start off with an empty region to maintain the invariant that /* Start off with an empty region to maintain the invariant that
the shape region is always set */ the shape region is always set */
priv->shape_region = cairo_region_create(); priv->shape_region = cairo_region_create ();
} }
static void static void
@ -1332,7 +1331,7 @@ meta_window_actor_should_unredirect (MetaWindowActor *self)
if (priv->opacity != 0xff) if (priv->opacity != 0xff)
return FALSE; return FALSE;
if (metaWindow->has_shape) if (metaWindow->shape_region != NULL)
return FALSE; return FALSE;
if (priv->argb32 && !meta_window_requested_bypass_compositor (metaWindow)) if (priv->argb32 && !meta_window_requested_bypass_compositor (metaWindow))
@ -2220,101 +2219,53 @@ build_and_scan_frame_mask (MetaWindowActor *self,
} }
static void 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; MetaWindowActorPrivate *priv = self->priv;
MetaScreen *screen = priv->screen;
MetaDisplay *display = meta_screen_get_display (screen);
MetaFrameBorders borders;
cairo_region_t *region = NULL; cairo_region_t *region = NULL;
cairo_rectangle_int_t client_area;
gboolean needs_mask;
if (!priv->mapped) if (priv->window->frame != NULL && priv->window->shape_region != NULL)
return;
if (!priv->needs_reshape)
return;
g_clear_pointer (&priv->shadow_shape, meta_window_shape_unref);
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);
#ifdef HAVE_SHAPE
if (priv->window->has_shape)
{ {
/* Translate the set of XShape rectangles that we region = cairo_region_copy (priv->window->shape_region);
* get from the X server to a cairo_region. */ cairo_region_translate (region, client_area->x, client_area->y);
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)
{
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;
}
XFree (rects);
region = cairo_region_create_rectangles (cairo_rects, n_rects);
g_free (cairo_rects);
}
} }
#endif else if (priv->window->shape_region != NULL)
needs_mask = (region != NULL) || (priv->window->frame != NULL);
if (region != NULL)
{ {
/* The shape we get back from the client may have coordinates region = cairo_region_reference (priv->window->shape_region);
* 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);
} }
else else
{ {
/* If we don't have a shape on the server, that means that /* If we don't have a shape on the server, that means that
* we have an implicit shape of one rectangle covering the * we have an implicit shape of one rectangle covering the
* entire window. */ * 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 meta_shaped_texture_set_mask_texture (META_SHAPED_TEXTURE (priv->actor), NULL);
* bounds of the client rectangle. */ 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;
g_clear_pointer (&priv->shadow_shape, meta_window_shape_unref);
meta_window_actor_invalidate_shadow (self);
}
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) 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 /* The opaque region is defined to be a part of the
* window which ARGB32 will always paint with opaque * window which ARGB32 will always paint with opaque
* pixels. For these regions, we want to avoid painting * pixels. For these regions, we want to avoid painting
@ -2326,24 +2277,40 @@ check_needs_reshape (MetaWindowActor *self)
* case, graphical glitches will occur. * case, graphical glitches will occur.
*/ */
priv->opaque_region = cairo_region_copy (priv->window->opaque_region); 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, borders.total.left, borders.total.top);
cairo_region_intersect (priv->opaque_region, region); cairo_region_intersect (priv->opaque_region, priv->shape_region);
} }
else if (priv->argb32) else if (priv->argb32)
priv->opaque_region = NULL; priv->opaque_region = NULL;
else else
priv->opaque_region = cairo_region_reference (region); priv->opaque_region = cairo_region_reference (priv->shape_region);
}
if (needs_mask) static void
{ check_needs_reshape (MetaWindowActor *self)
/* This takes the region, generates a mask using GTK+ {
* and scans the mask looking for all opaque pixels, MetaWindowActorPrivate *priv = self->priv;
* adding it to region. MetaFrameBorders borders;
*/ cairo_rectangle_int_t client_area;
build_and_scan_frame_mask (self, &client_area, region);
}
priv->shape_region = region; 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_opaque_region (self);
priv->needs_reshape = FALSE; priv->needs_reshape = FALSE;
meta_window_actor_invalidate_shadow (self); meta_window_actor_invalidate_shadow (self);

View File

@ -2283,32 +2283,7 @@ event_callback (XEvent *event,
XShapeEvent *sev = (XShapeEvent*) event; XShapeEvent *sev = (XShapeEvent*) event;
if (sev->kind == ShapeBounding) if (sev->kind == ShapeBounding)
{ meta_window_update_shape_region_x11 (window);
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_shape_changed (display->compositor,
window);
}
} }
else else
{ {

View File

@ -325,9 +325,6 @@ struct _MetaWindow
guint using_net_wm_icon_name : 1; /* vs. plain wm_icon_name */ 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 */ guint using_net_wm_visible_icon_name : 1; /* tracked so we can clear it */
/* has a shape mask */
guint has_shape : 1;
/* icon props have changed */ /* icon props have changed */
guint need_reread_icon : 1; guint need_reread_icon : 1;
@ -349,6 +346,9 @@ struct _MetaWindow
/* if non-NULL, the bounds of the window frame */ /* if non-NULL, the bounds of the window frame */
cairo_region_t *frame_bounds; 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 */ /* if non-NULL, the opaque region _NET_WM_OPAQUE_REGION */
cairo_region_t *opaque_region; cairo_region_t *opaque_region;
@ -665,7 +665,6 @@ void meta_window_update_icon_now (MetaWindow *window);
void meta_window_update_role (MetaWindow *window); void meta_window_update_role (MetaWindow *window);
void meta_window_update_net_wm_type (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_for_monitors_changed (MetaWindow *window);
void meta_window_update_on_all_workspaces (MetaWindow *window); void meta_window_update_on_all_workspaces (MetaWindow *window);
@ -679,4 +678,12 @@ void meta_window_compute_tile_match (MetaWindow *window);
gboolean meta_window_updates_are_frozen (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_shape_region (MetaWindow *window,
cairo_region_t *region);
void meta_window_update_shape_region_x11 (MetaWindow *window);
#endif #endif

View File

@ -565,7 +565,7 @@ reload_opaque_region (MetaWindow *window,
MetaPropValue *value, MetaPropValue *value,
gboolean initial) gboolean initial)
{ {
meta_window_update_opaque_region (window); meta_window_update_opaque_region_x11 (window);
} }
static void static void

View File

@ -825,7 +825,6 @@ meta_window_new_with_attrs (MetaDisplay *display,
gulong existing_wm_state; gulong existing_wm_state;
gulong event_mask; gulong event_mask;
MetaMoveResizeFlags flags; MetaMoveResizeFlags flags;
gboolean has_shape;
MetaScreen *screen; MetaScreen *screen;
g_assert (attrs != NULL); g_assert (attrs != NULL);
@ -959,29 +958,9 @@ meta_window_new_with_attrs (MetaDisplay *display,
XISelectEvents (display->xdisplay, xwindow, &mask, 1); XISelectEvents (display->xdisplay, xwindow, &mask, 1);
} }
has_shape = FALSE;
#ifdef HAVE_SHAPE #ifdef HAVE_SHAPE
if (META_DISPLAY_HAS_SHAPE (display)) if (META_DISPLAY_HAS_SHAPE (display))
{ XShapeSelectInput (display->xdisplay, xwindow, ShapeNotifyMask);
int x_bounding, y_bounding, x_clip, y_clip;
unsigned w_bounding, h_bounding, w_clip, h_clip;
int bounding_shaped, clip_shaped;
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;
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 #endif
/* Get rid of any borders */ /* Get rid of any borders */
@ -1041,8 +1020,6 @@ meta_window_new_with_attrs (MetaDisplay *display,
/* avoid tons of stack updates */ /* avoid tons of stack updates */
meta_stack_freeze (window->screen->stack); meta_stack_freeze (window->screen->stack);
window->has_shape = has_shape;
window->rect.x = attrs->x; window->rect.x = attrs->x;
window->rect.y = attrs->y; window->rect.y = attrs->y;
window->rect.width = attrs->width; window->rect.width = attrs->width;
@ -1213,6 +1190,8 @@ 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);
/* Assign this #MetaWindow a sequence number which can be used /* Assign this #MetaWindow a sequence number which can be used
* for sorting. * for sorting.
*/ */
@ -7637,14 +7616,25 @@ meta_window_update_net_wm_type (MetaWindow *window)
} }
void 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; cairo_region_t *opaque_region = NULL;
gulong *region = NULL; gulong *region = NULL;
int nitems; int nitems;
g_clear_pointer (&window->opaque_region, cairo_region_destroy);
if (meta_prop_get_cardinal_list (window->display, if (meta_prop_get_cardinal_list (window->display,
window->xwindow, window->xwindow,
window->display->atom__NET_WM_OPAQUE_REGION, window->display->atom__NET_WM_OPAQUE_REGION,
@ -7687,13 +7677,108 @@ meta_window_update_opaque_region (MetaWindow *window)
} }
out: out:
window->opaque_region = opaque_region;
meta_XFree (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_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) if (window->display->compositor)
meta_compositor_window_shape_changed (window->display->compositor, window); 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
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 static void
redraw_icon (MetaWindow *window) redraw_icon (MetaWindow *window)
{ {