From a613a55658c5717481e1f2d34987715d5f0a5ca4 Mon Sep 17 00:00:00 2001 From: "Jasper St. Pierre" Date: Mon, 14 Jan 2013 20:45:31 -0500 Subject: [PATCH] Support _NET_WM_OPAQUE_REGION This new hint allows compositors to know what portions of a window will be obscured, as a region above them is opaque. For an RGB window, possible to glean this information from the bounding shape region of a client window, but not for an ARGB32 window. This new hint allows clients that use ARGB32 windows to say which part of the window is opaque, allowing this sort of optimization. https://bugzilla.gnome.org/show_bug.cgi?id=679901 --- src/compositor/meta-window-actor.c | 29 +++++++++++++- src/core/window-private.h | 4 ++ src/core/window-props.c | 9 +++++ src/core/window.c | 61 ++++++++++++++++++++++++++++++ src/meta/atomnames.h | 1 + 5 files changed, 102 insertions(+), 2 deletions(-) diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c index 8c1617dea..ce691ae90 100644 --- a/src/compositor/meta-window-actor.c +++ b/src/compositor/meta-window-actor.c @@ -70,6 +70,9 @@ struct _MetaWindowActorPrivate /* A region that matches the shape of the window, including frame bounds */ cairo_region_t *shape_region; + /* The opaque region, from _NET_WM_OPAQUE_REGION, intersected with + * the shape region. */ + cairo_region_t *opaque_region; /* The region we should clip to when painting the shadow */ cairo_region_t *shadow_clip; @@ -387,6 +390,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->opaque_region, cairo_region_destroy); g_clear_pointer (&priv->shadow_clip, cairo_region_destroy); g_clear_pointer (&priv->shadow_class, g_free); @@ -1551,8 +1555,8 @@ meta_window_actor_get_obscured_region (MetaWindowActor *self) { MetaWindowActorPrivate *priv = self->priv; - if (!priv->argb32 && priv->opacity == 0xff && priv->back_pixmap) - return priv->shape_region; + if (priv->back_pixmap && priv->opacity == 0xff) + return priv->opaque_region; else return NULL; } @@ -2182,6 +2186,27 @@ check_needs_reshape (MetaWindowActor *self) /* The region at this point should be constrained to the * bounds of the client rectangle. */ + if (priv->argb32 && priv->window->opaque_region != NULL) + { + /* 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 + * windows and shadows beneath them. + * + * If the client gives bad coordinates where it does not + * fully paint, the behavior is defined by the specification + * to be undefined, and considered a client bug. In mutter's + * 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); + } + 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+ diff --git a/src/core/window-private.h b/src/core/window-private.h index becfef5d6..f7228ed2b 100644 --- a/src/core/window-private.h +++ b/src/core/window-private.h @@ -346,6 +346,9 @@ struct _MetaWindow /* if non-NULL, the bounds of the window frame */ cairo_region_t *frame_bounds; + /* if non-NULL, the opaque region _NET_WM_OPAQUE_REGION */ + cairo_region_t *opaque_region; + /* Note: can be NULL */ GSList *struts; @@ -648,6 +651,7 @@ 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); diff --git a/src/core/window-props.c b/src/core/window-props.c index e01934bcf..64bbdb28e 100644 --- a/src/core/window-props.c +++ b/src/core/window-props.c @@ -520,6 +520,14 @@ reload_wm_name (MetaWindow *window, } } +static void +reload_opaque_region (MetaWindow *window, + MetaPropValue *value, + gboolean initial) +{ + meta_window_update_opaque_region (window); +} + static void reload_mutter_hints (MetaWindow *window, MetaPropValue *value, @@ -1706,6 +1714,7 @@ meta_display_init_window_prop_hooks (MetaDisplay *display) { display->atom__NET_WM_PID, META_PROP_VALUE_CARDINAL, reload_net_wm_pid, TRUE, TRUE }, { XA_WM_NAME, META_PROP_VALUE_TEXT_PROPERTY, reload_wm_name, TRUE, TRUE }, { display->atom__MUTTER_HINTS, META_PROP_VALUE_TEXT_PROPERTY, reload_mutter_hints, TRUE, TRUE }, + { display->atom__NET_WM_OPAQUE_REGION, META_PROP_VALUE_CARDINAL_LIST, reload_opaque_region, TRUE, TRUE }, { display->atom__NET_WM_ICON_NAME, META_PROP_VALUE_UTF8, reload_net_wm_icon_name, TRUE, FALSE }, { XA_WM_ICON_NAME, META_PROP_VALUE_TEXT_PROPERTY, reload_wm_icon_name, TRUE, FALSE }, { display->atom__NET_WM_DESKTOP, META_PROP_VALUE_CARDINAL, reload_net_wm_desktop, TRUE, FALSE }, diff --git a/src/core/window.c b/src/core/window.c index e5023e4a4..ead4d810f 100644 --- a/src/core/window.c +++ b/src/core/window.c @@ -226,6 +226,9 @@ meta_window_finalize (GObject *object) if (window->frame_bounds) cairo_region_destroy (window->frame_bounds); + if (window->opaque_region) + cairo_region_destroy (window->opaque_region); + meta_icon_cache_free (&window->icon_cache); g_free (window->sm_client_id); @@ -7388,6 +7391,64 @@ meta_window_update_net_wm_type (MetaWindow *window) meta_window_recalc_window_type (window); } +void +meta_window_update_opaque_region (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, + ®ion, &nitems)) + { + cairo_rectangle_int_t *rects; + int i, rect_index, nrects; + + if (nitems % 4 != 0) + { + meta_verbose ("_NET_WM_OPAQUE_REGION does not have a list of 4-tuples."); + goto out; + } + + /* empty region */ + if (nitems == 0) + goto out; + + nrects = nitems / 4; + + rects = g_new (cairo_rectangle_int_t, nrects); + + rect_index = 0; + i = 0; + while (i < nitems) + { + cairo_rectangle_int_t *rect = &rects[rect_index]; + + rect->x = region[i++]; + rect->y = region[i++]; + rect->width = region[i++]; + rect->height = region[i++]; + + rect_index++; + } + + opaque_region = cairo_region_create_rectangles (rects, nrects); + + g_free (rects); + } + + out: + window->opaque_region = opaque_region; + meta_XFree (region); + + if (window->display->compositor) + meta_compositor_window_shape_changed (window->display->compositor, window); +} + static void redraw_icon (MetaWindow *window) { diff --git a/src/meta/atomnames.h b/src/meta/atomnames.h index a8598e653..ca0520fd0 100644 --- a/src/meta/atomnames.h +++ b/src/meta/atomnames.h @@ -173,6 +173,7 @@ item(_NET_WM_STATE_STICKY) item(_NET_WM_FULLSCREEN_MONITORS) item(_NET_WM_STATE_FOCUSED) item(_NET_WM_BYPASS_COMPOSITOR) +item(_NET_WM_OPAQUE_REGION) #if 0 /* We apparently never use: */