Reduce overpaint in the window group
When we are painting a stack of 5-10 maximized windows, the standard bottom-to-top method of drawing every actor results in a tremendous amount of overdraw and can easily max out the available memory bandwidth on a low-end* graphics chipset. It's even worse if window textures are being accessed over the AGP bus. When we have opaque windows, we can go ahead and compute visibility ourselves (in classic X-server fashion) and use that information to restrict drawing obscured actors. * Add MutterWindowGroup - a ClutterGroup subclass with logic for figuring out obscured regions. * Add mutter_window_get_obscured_region() to get the region obscured by that window. * Add mutter_shaped_texture_set_clip_region() to hint a clip region to the painting code; this is set based on the computed visible region of MutterWindowGroup. * Add tidy_texture_frame_set_needs_paint() to hint that the paint can be skipped entirely; this is used when we detect that the window shadow is entirely obscured. http://bugzilla.gnome.org/show_bug.cgi?id=587344
This commit is contained in:
parent
a454ad5c41
commit
83f8bfd2ca
@ -31,6 +31,8 @@ mutter_SOURCES= \
|
||||
compositor/mutter-shaped-texture.c \
|
||||
compositor/mutter-window.c \
|
||||
compositor/mutter-window-private.h \
|
||||
compositor/mutter-window-group.c \
|
||||
compositor/mutter-window-group.h \
|
||||
compositor/shadow.c \
|
||||
compositor/shadow.h \
|
||||
compositor/mutter-shaped-texture.h \
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "xprops.h"
|
||||
#include "prefs.h"
|
||||
#include "mutter-window-private.h"
|
||||
#include "mutter-window-group.h"
|
||||
#include "../core/window-private.h" /* to check window->hidden */
|
||||
#include "../core/display-private.h" /* for meta_display_lookup_x_window() */
|
||||
#include <X11/extensions/shape.h>
|
||||
@ -420,7 +421,7 @@ meta_compositor_manage_screen (MetaCompositor *compositor,
|
||||
|
||||
XSelectInput (xdisplay, xwin, event_mask);
|
||||
|
||||
info->window_group = clutter_group_new ();
|
||||
info->window_group = mutter_window_group_new (screen);
|
||||
info->overlay_group = clutter_group_new ();
|
||||
info->hidden_group = clutter_group_new ();
|
||||
|
||||
|
@ -57,10 +57,13 @@ struct _MutterShapedTexturePrivate
|
||||
{
|
||||
CoglHandle mask_texture;
|
||||
CoglHandle material;
|
||||
CoglHandle material_unshaped;
|
||||
#if 1 /* see workaround comment in mutter_shaped_texture_paint */
|
||||
CoglHandle material_workaround;
|
||||
#endif
|
||||
|
||||
GdkRegion *clip_region;
|
||||
|
||||
guint mask_width, mask_height;
|
||||
|
||||
GArray *rectangles;
|
||||
@ -106,6 +109,11 @@ mutter_shaped_texture_dispose (GObject *object)
|
||||
cogl_material_unref (priv->material);
|
||||
priv->material = COGL_INVALID_HANDLE;
|
||||
}
|
||||
if (priv->material_unshaped != COGL_INVALID_HANDLE)
|
||||
{
|
||||
cogl_material_unref (priv->material_unshaped);
|
||||
priv->material_unshaped = COGL_INVALID_HANDLE;
|
||||
}
|
||||
#if 1 /* see comment in mutter_shaped_texture_paint */
|
||||
if (priv->material_workaround != COGL_INVALID_HANDLE)
|
||||
{
|
||||
@ -114,6 +122,8 @@ mutter_shaped_texture_dispose (GObject *object)
|
||||
}
|
||||
#endif
|
||||
|
||||
mutter_shaped_texture_set_clip_region (self, NULL);
|
||||
|
||||
G_OBJECT_CLASS (mutter_shaped_texture_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
@ -253,6 +263,9 @@ mutter_shaped_texture_paint (ClutterActor *actor)
|
||||
guint depth;
|
||||
#endif
|
||||
|
||||
if (priv->clip_region && gdk_region_empty (priv->clip_region))
|
||||
return;
|
||||
|
||||
if (!CLUTTER_ACTOR_IS_REALIZED (CLUTTER_ACTOR (stex)))
|
||||
clutter_actor_realize (CLUTTER_ACTOR (stex));
|
||||
|
||||
@ -264,61 +277,65 @@ mutter_shaped_texture_paint (ClutterActor *actor)
|
||||
if (tex_width == 0 || tex_width == 0) /* no contents yet */
|
||||
return;
|
||||
|
||||
/* If there are no rectangles fallback to the regular paint
|
||||
method */
|
||||
if (priv->rectangles->len < 1)
|
||||
{
|
||||
CLUTTER_ACTOR_CLASS (mutter_shaped_texture_parent_class)
|
||||
->paint (actor);
|
||||
return;
|
||||
}
|
||||
|
||||
if (paint_tex == COGL_INVALID_HANDLE)
|
||||
return;
|
||||
|
||||
mutter_shaped_texture_ensure_mask (stex);
|
||||
|
||||
if (priv->material == COGL_INVALID_HANDLE)
|
||||
if (priv->rectangles->len < 1)
|
||||
{
|
||||
priv->material = cogl_material_new ();
|
||||
/* If there are no rectangles use a single-layer texture */
|
||||
|
||||
cogl_material_set_layer_combine (priv->material, 1,
|
||||
"RGBA = MODULATE (PREVIOUS, TEXTURE[A])",
|
||||
NULL);
|
||||
if (priv->material_unshaped == COGL_INVALID_HANDLE)
|
||||
priv->material_unshaped = cogl_material_new ();
|
||||
|
||||
material = priv->material_unshaped;
|
||||
}
|
||||
material = priv->material;
|
||||
|
||||
#if 1
|
||||
/* This was added as a workaround. It seems that with the intel
|
||||
* drivers when multi-texturing using an RGB TFP texture, the
|
||||
* texture is actually setup internally as an RGBA texture, where
|
||||
* the alpha channel is mostly 0.0 so you only see a shimmer of the
|
||||
* window. This workaround forcibly defines the alpha channel as
|
||||
* 1.0. Maybe there is some clutter/cogl state that is interacting
|
||||
* with this that is being overlooked, but for now this seems to
|
||||
* work. */
|
||||
g_object_get (stex, "pixmap-depth", &depth, NULL);
|
||||
if (depth == 24)
|
||||
else
|
||||
{
|
||||
if (priv->material_workaround == COGL_INVALID_HANDLE)
|
||||
{
|
||||
material = priv->material_workaround = cogl_material_new ();
|
||||
mutter_shaped_texture_ensure_mask (stex);
|
||||
|
||||
cogl_material_set_layer_combine (material, 0,
|
||||
"RGB = MODULATE (TEXTURE, PREVIOUS)"
|
||||
"A = REPLACE (PREVIOUS)",
|
||||
NULL);
|
||||
cogl_material_set_layer_combine (material, 1,
|
||||
if (priv->material == COGL_INVALID_HANDLE)
|
||||
{
|
||||
priv->material = cogl_material_new ();
|
||||
|
||||
cogl_material_set_layer_combine (priv->material, 1,
|
||||
"RGBA = MODULATE (PREVIOUS, TEXTURE[A])",
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
material = priv->material;
|
||||
|
||||
material = priv->material_workaround;
|
||||
}
|
||||
#if 1
|
||||
/* This was added as a workaround. It seems that with the intel
|
||||
* drivers when multi-texturing using an RGB TFP texture, the
|
||||
* texture is actually setup internally as an RGBA texture, where
|
||||
* the alpha channel is mostly 0.0 so you only see a shimmer of the
|
||||
* window. This workaround forcibly defines the alpha channel as
|
||||
* 1.0. Maybe there is some clutter/cogl state that is interacting
|
||||
* with this that is being overlooked, but for now this seems to
|
||||
* work. */
|
||||
g_object_get (stex, "pixmap-depth", &depth, NULL);
|
||||
if (depth == 24)
|
||||
{
|
||||
if (priv->material_workaround == COGL_INVALID_HANDLE)
|
||||
{
|
||||
material = priv->material_workaround = cogl_material_new ();
|
||||
|
||||
cogl_material_set_layer_combine (material, 0,
|
||||
"RGB = MODULATE (TEXTURE, PREVIOUS)"
|
||||
"A = REPLACE (PREVIOUS)",
|
||||
NULL);
|
||||
cogl_material_set_layer_combine (material, 1,
|
||||
"RGBA = MODULATE (PREVIOUS, TEXTURE[A])",
|
||||
NULL);
|
||||
}
|
||||
|
||||
material = priv->material_workaround;
|
||||
}
|
||||
#endif
|
||||
|
||||
cogl_material_set_layer (material, 1, priv->mask_texture);
|
||||
}
|
||||
|
||||
cogl_material_set_layer (material, 0, paint_tex);
|
||||
cogl_material_set_layer (material, 1, priv->mask_texture);
|
||||
|
||||
{
|
||||
CoglColor color;
|
||||
@ -330,9 +347,51 @@ mutter_shaped_texture_paint (ClutterActor *actor)
|
||||
cogl_set_source (material);
|
||||
|
||||
clutter_actor_get_allocation_box (actor, &alloc);
|
||||
|
||||
if (priv->clip_region)
|
||||
{
|
||||
GdkRectangle *rects;
|
||||
int n_rects;
|
||||
int i;
|
||||
|
||||
/* Limit to how many separate rectangles we'll draw; beyond this just
|
||||
* fall back and draw the whole thing */
|
||||
# define MAX_RECTS 16
|
||||
|
||||
/* Would be nice to be able to check the number of rects first */
|
||||
gdk_region_get_rectangles (priv->clip_region, &rects, &n_rects);
|
||||
if (n_rects > MAX_RECTS)
|
||||
{
|
||||
g_free (rects);
|
||||
/* Fall through to following code */
|
||||
}
|
||||
else
|
||||
{
|
||||
float coords[MAX_RECTS * 8];
|
||||
for (i = 0; i < n_rects; i++)
|
||||
{
|
||||
GdkRectangle *rect = &rects[i];
|
||||
|
||||
coords[i * 8 + 0] = rect->x;
|
||||
coords[i * 8 + 1] = rect->y;
|
||||
coords[i * 8 + 2] = rect->x + rect->width;
|
||||
coords[i * 8 + 3] = rect->y + rect->height;
|
||||
coords[i * 8 + 4] = rect->x / (alloc.x2 - alloc.x1);
|
||||
coords[i * 8 + 5] = rect->y / (alloc.y2 - alloc.y1);
|
||||
coords[i * 8 + 6] = (rect->x + rect->width) / (alloc.x2 - alloc.x1);
|
||||
coords[i * 8 + 7] = (rect->y + rect->height) / (alloc.y2 - alloc.y1);
|
||||
}
|
||||
|
||||
g_free (rects);
|
||||
|
||||
cogl_rectangles_with_texture_coords (coords, n_rects);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
cogl_rectangle (0, 0,
|
||||
alloc.x2 - alloc.x1,
|
||||
alloc.y2 - alloc.y1);
|
||||
alloc.x2 - alloc.x1,
|
||||
alloc.y2 - alloc.y1);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -426,3 +485,37 @@ mutter_shaped_texture_add_rectangles (MutterShapedTexture *stex,
|
||||
mutter_shaped_texture_dirty_mask (stex);
|
||||
clutter_actor_queue_redraw (CLUTTER_ACTOR (stex));
|
||||
}
|
||||
|
||||
/**
|
||||
* mutter_shaped_texture_set_clip_region:
|
||||
* @frame: a #TidyTextureframe
|
||||
* @clip_region: (transfer full): the region of the texture that
|
||||
* is visible and should be painted. OWNERSHIP IS ASSUMED BY
|
||||
* THE FUNCTION (for efficiency to avoid a copy.)
|
||||
*
|
||||
* Provides a hint to the texture about what areas of the texture
|
||||
* are not completely obscured and thus need to be painted. This
|
||||
* is an optimization and is not supposed to have any effect on
|
||||
* the output.
|
||||
*
|
||||
* Typically a parent container will set the clip region before
|
||||
* painting its children, and then unset it afterwards.
|
||||
*/
|
||||
void
|
||||
mutter_shaped_texture_set_clip_region (MutterShapedTexture *stex,
|
||||
GdkRegion *clip_region)
|
||||
{
|
||||
MutterShapedTexturePrivate *priv;
|
||||
|
||||
g_return_if_fail (MUTTER_IS_SHAPED_TEXTURE (stex));
|
||||
|
||||
priv = stex->priv;
|
||||
|
||||
if (priv->clip_region)
|
||||
{
|
||||
gdk_region_destroy (priv->clip_region);
|
||||
priv->clip_region = NULL;
|
||||
}
|
||||
|
||||
priv->clip_region = clip_region;
|
||||
}
|
||||
|
@ -31,6 +31,8 @@
|
||||
#include <clutter/glx/clutter-glx.h>
|
||||
#endif /* HAVE_GLX_TEXTURE_PIXMAP */
|
||||
|
||||
#include <gdk/gdkregion.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define MUTTER_TYPE_SHAPED_TEXTURE \
|
||||
@ -90,6 +92,10 @@ void mutter_shaped_texture_add_rectangles (MutterShapedTexture *stex,
|
||||
size_t num_rects,
|
||||
const XRectangle *rects);
|
||||
|
||||
/* Assumes ownership of clip_region */
|
||||
void mutter_shaped_texture_set_clip_region (MutterShapedTexture *stex,
|
||||
GdkRegion *clip_region);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __MUTTER_SHAPED_TEXTURE_H__ */
|
||||
|
194
src/compositor/mutter-window-group.c
Normal file
194
src/compositor/mutter-window-group.c
Normal file
@ -0,0 +1,194 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
|
||||
#define _ISOC99_SOURCE /* for roundf */
|
||||
#include <math.h>
|
||||
|
||||
#include "mutter-window-private.h"
|
||||
#include "mutter-window-group.h"
|
||||
|
||||
struct _MutterWindowGroupClass
|
||||
{
|
||||
ClutterGroupClass parent_class;
|
||||
};
|
||||
|
||||
struct _MutterWindowGroup
|
||||
{
|
||||
ClutterGroup parent;
|
||||
|
||||
MetaScreen *screen;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (MutterWindowGroup, mutter_window_group, CLUTTER_TYPE_GROUP);
|
||||
|
||||
/* We want to find out if the window is "close enough" to
|
||||
* 1:1 transform. We do that by converting the transformed coordinates
|
||||
* to 24.8 fixed-point before checking if they look right.
|
||||
*/
|
||||
static inline int
|
||||
round_to_fixed (float x)
|
||||
{
|
||||
return roundf (x * 256);
|
||||
}
|
||||
|
||||
/* We can only (easily) apply our logic for figuring out what a window
|
||||
* obscures if is not transformed. This function does that check and
|
||||
* as a side effect gets the position of the upper-left corner of the
|
||||
* actors.
|
||||
*
|
||||
* (We actually could handle scaled and non-integrally positioned actors
|
||||
* too as long as they weren't shaped - no filtering is done at the
|
||||
* edges so a rectangle stays a rectangle. But the gain from that is
|
||||
* small, especally since most of our windows are shaped. The simple
|
||||
* case we handle here is the case that matters when the user is just
|
||||
* using the desktop normally.)
|
||||
*
|
||||
* If we assume that the window group is untransformed (it better not
|
||||
* be!) then we could also make this determination by checking directly
|
||||
* if the actor itself is rotated, scaled, or at a non-integral position.
|
||||
* However, the criterion for "close enough" in that case get trickier,
|
||||
* since, for example, the allowed rotation depends on the size of
|
||||
* actor. The approach we take here is to just require everything
|
||||
* to be within 1/256th of a pixel.
|
||||
*/
|
||||
static gboolean
|
||||
actor_is_untransformed (ClutterActor *actor,
|
||||
int *x_origin,
|
||||
int *y_origin)
|
||||
{
|
||||
gfloat widthf, heightf;
|
||||
int width, height;
|
||||
ClutterVertex verts[4];
|
||||
int v0x, v0y, v1x, v1y, v2x, v2y, v3x, v3y;
|
||||
int x, y;
|
||||
|
||||
clutter_actor_get_size (actor, &widthf, &heightf);
|
||||
width = round_to_fixed (widthf); height = round_to_fixed (heightf);
|
||||
|
||||
clutter_actor_get_abs_allocation_vertices (actor, verts);
|
||||
v0x = round_to_fixed (verts[0].x); v0y = round_to_fixed (verts[0].y);
|
||||
v1x = round_to_fixed (verts[1].x); v1y = round_to_fixed (verts[1].y);
|
||||
v2x = round_to_fixed (verts[2].x); v2y = round_to_fixed (verts[2].y);
|
||||
v3x = round_to_fixed (verts[3].x); v3y = round_to_fixed (verts[3].y);
|
||||
|
||||
/* Using shifting for converting fixed => int, gets things right for
|
||||
* negative values. / 256. wouldn't do the same
|
||||
*/
|
||||
x = v0x >> 8;
|
||||
y = v0y >> 8;
|
||||
|
||||
/* At integral coordinates? */
|
||||
if (x * 256 != v0x || y * 256 != v0y)
|
||||
return FALSE;
|
||||
|
||||
/* Not scaled? */
|
||||
if (v1x - v0x != width || v2y - v0y != height)
|
||||
return FALSE;
|
||||
|
||||
/* Not rotated/skewed? */
|
||||
if (v0x != v2x || v0y != v1y ||
|
||||
v3x != v1x || v3y != v2y)
|
||||
return FALSE;
|
||||
|
||||
*x_origin = x;
|
||||
*y_origin = y;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
mutter_window_group_paint (ClutterActor *actor)
|
||||
{
|
||||
MutterWindowGroup *window_group = MUTTER_WINDOW_GROUP (actor);
|
||||
GdkRegion *visible_region;
|
||||
GdkRectangle screen_rect = { 0 };
|
||||
GList *children, *l;
|
||||
|
||||
/* We walk the list from top to bottom (opposite of painting order),
|
||||
* and subtract the opaque area of each window out of the visible
|
||||
* region that we pass to the windows below.
|
||||
*/
|
||||
children = clutter_container_get_children (CLUTTER_CONTAINER (actor));
|
||||
children = g_list_reverse (children);
|
||||
|
||||
/* Start off with the full screen area (for a multihead setup, we
|
||||
* might want to use a more accurate union of the monitors to avoid
|
||||
* painting in holes from mismatched monitor sizes. That's just an
|
||||
* optimization, however.)
|
||||
*/
|
||||
meta_screen_get_size (window_group->screen, &screen_rect.width, &screen_rect.height);
|
||||
visible_region = gdk_region_rectangle (&screen_rect);
|
||||
|
||||
for (l = children; l; l = l->next)
|
||||
{
|
||||
MutterWindow *cw;
|
||||
gboolean x, y;
|
||||
|
||||
if (!MUTTER_IS_WINDOW (l->data) || !CLUTTER_ACTOR_IS_VISIBLE (l->data))
|
||||
continue;
|
||||
|
||||
cw = l->data;
|
||||
|
||||
if (!actor_is_untransformed (CLUTTER_ACTOR (cw), &x, &y))
|
||||
continue;
|
||||
|
||||
/* Temporarily move to the coordinate system of the actor */
|
||||
gdk_region_offset (visible_region, - x, - y);
|
||||
|
||||
mutter_window_set_visible_region (cw, visible_region);
|
||||
|
||||
if (clutter_actor_get_paint_opacity (actor) == 0xff)
|
||||
{
|
||||
GdkRegion *obscured_region = mutter_window_get_obscured_region (cw);
|
||||
if (obscured_region)
|
||||
gdk_region_subtract (visible_region, obscured_region);
|
||||
}
|
||||
|
||||
mutter_window_set_visible_region_beneath (cw, visible_region);
|
||||
gdk_region_offset (visible_region, x, y);
|
||||
}
|
||||
|
||||
gdk_region_destroy (visible_region);
|
||||
|
||||
CLUTTER_ACTOR_CLASS (mutter_window_group_parent_class)->paint (actor);
|
||||
|
||||
/* Now that we are done painting, unset the visible regions (they will
|
||||
* mess up painting clones of our actors)
|
||||
*/
|
||||
for (l = children; l; l = l->next)
|
||||
{
|
||||
MutterWindow *cw;
|
||||
|
||||
if (!MUTTER_IS_WINDOW (l->data))
|
||||
continue;
|
||||
|
||||
cw = l->data;
|
||||
mutter_window_reset_visible_regions (cw);
|
||||
}
|
||||
|
||||
g_list_free (children);
|
||||
}
|
||||
|
||||
static void
|
||||
mutter_window_group_class_init (MutterWindowGroupClass *klass)
|
||||
{
|
||||
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
|
||||
|
||||
actor_class->paint = mutter_window_group_paint;
|
||||
}
|
||||
|
||||
static void
|
||||
mutter_window_group_init (MutterWindowGroup *window_group)
|
||||
{
|
||||
}
|
||||
|
||||
ClutterActor *
|
||||
mutter_window_group_new (MetaScreen *screen)
|
||||
{
|
||||
MutterWindowGroup *window_group;
|
||||
|
||||
window_group = g_object_new (MUTTER_TYPE_WINDOW_GROUP, NULL);
|
||||
|
||||
window_group->screen = screen;
|
||||
|
||||
return CLUTTER_ACTOR (window_group);
|
||||
}
|
52
src/compositor/mutter-window-group.h
Normal file
52
src/compositor/mutter-window-group.h
Normal file
@ -0,0 +1,52 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
|
||||
#ifndef MUTTER_WINDOW_GROUP_H
|
||||
#define MUTTER_WINDOW_GROUP_H
|
||||
|
||||
#include <clutter/clutter.h>
|
||||
|
||||
#include "screen.h"
|
||||
|
||||
/**
|
||||
* MutterWindowGroup:
|
||||
*
|
||||
* This class is a subclass of ClutterGroup with special handling for
|
||||
* MutterWindow when painting the group. When we are painting a stack
|
||||
* of 5-10 maximized windows, the standard bottom-to-top method of
|
||||
* drawing every actor results in a tremendous amount of overdraw
|
||||
* and can easily max out the available memory bandwidth on a low-end
|
||||
* graphics chipset. It's even worse if window textures are being accessed
|
||||
* over the AGP bus.
|
||||
*
|
||||
* The basic technique applied here is to do a pre-pass before painting
|
||||
* where we walk window from top to bottom and compute the visible area
|
||||
* at each step by subtracting out the windows above it. The visible
|
||||
* area is passed to MutterWindow which uses it to clip the portion of
|
||||
* the window which drawn and avoid redrawing the shadow if it is completely
|
||||
* obscured.
|
||||
*
|
||||
* A caveat is that this is ineffective if applications are using ARGB
|
||||
* visuals, since we have no way of knowing whether a window obscures
|
||||
* the windows behind it or not. Alternate approaches using the depth
|
||||
* or stencil buffer rather than client side regions might be able to
|
||||
* handle alpha windows, but the combination of glAlphaFunc and stenciling
|
||||
* tends not to be efficient except on newer cards. (And on newer cards
|
||||
* we have lots of memory and bandwidth.)
|
||||
*/
|
||||
|
||||
#define MUTTER_TYPE_WINDOW_GROUP (mutter_window_group_get_type ())
|
||||
#define MUTTER_WINDOW_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MUTTER_TYPE_WINDOW_GROUP, MutterWindowGroup))
|
||||
#define MUTTER_WINDOW_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MUTTER_TYPE_WINDOW_GROUP, MutterWindowGroupClass))
|
||||
#define MUTTER_IS_WINDOW_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MUTTER_TYPE_WINDOW_GROUP))
|
||||
#define MUTTER_IS_WINDOW_GROU_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MUTTER_TYPE_WINDOW_GROUP))
|
||||
#define MUTTER_WINDOW_GROUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MUTTER_TYPE_WINDOW_GROUP, MutterWindowGroupClass))
|
||||
|
||||
typedef struct _MutterWindowGroup MutterWindowGroup;
|
||||
typedef struct _MutterWindowGroupClass MutterWindowGroupClass;
|
||||
typedef struct _MutterWindowGroupPrivate MutterWindowGroupPrivate;
|
||||
|
||||
GType mutter_window_group_get_type (void);
|
||||
|
||||
ClutterActor *mutter_window_group_new (MetaScreen *screen);
|
||||
|
||||
#endif /* MUTTER_WINDOW_GROUP_H */
|
@ -4,6 +4,7 @@
|
||||
#define MUTTER_WINDOW_PRIVATE_H
|
||||
|
||||
#include <X11/extensions/Xdamage.h>
|
||||
#include <gdk/gdk.h>
|
||||
#include "compositor-mutter.h"
|
||||
|
||||
MutterWindow *mutter_window_new (MetaWindow *window);
|
||||
@ -36,6 +37,14 @@ void mutter_window_update_opacity (MutterWindow *cw);
|
||||
void mutter_window_mapped (MutterWindow *cw);
|
||||
void mutter_window_unmapped (MutterWindow *cw);
|
||||
|
||||
GdkRegion *mutter_window_get_obscured_region (MutterWindow *cw);
|
||||
|
||||
void mutter_window_set_visible_region (MutterWindow *cw,
|
||||
GdkRegion *visible_region);
|
||||
void mutter_window_set_visible_region_beneath (MutterWindow *cw,
|
||||
GdkRegion *beneath_region);
|
||||
void mutter_window_reset_visible_regions (MutterWindow *cw);
|
||||
|
||||
void mutter_window_effect_completed (MutterWindow *actor,
|
||||
gulong event);
|
||||
|
||||
|
@ -1,5 +1,8 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
|
||||
#define _ISOC99_SOURCE /* for roundf */
|
||||
#include <math.h>
|
||||
|
||||
#include <X11/extensions/shape.h>
|
||||
#include <X11/extensions/Xcomposite.h>
|
||||
#include <X11/extensions/Xdamage.h>
|
||||
@ -18,6 +21,7 @@
|
||||
#include "mutter-shaped-texture.h"
|
||||
#include "mutter-window-private.h"
|
||||
#include "shadow.h"
|
||||
#include "tidy/tidy-texture-frame.h"
|
||||
|
||||
struct _MutterWindowPrivate
|
||||
{
|
||||
@ -38,6 +42,12 @@ struct _MutterWindowPrivate
|
||||
|
||||
gchar * desc;
|
||||
|
||||
/* If the window is shaped, a region that matches the shape */
|
||||
GdkRegion *shape_region;
|
||||
/* A rectangular region with the unshaped extends of the window
|
||||
* texture */
|
||||
GdkRegion *bounding_region;
|
||||
|
||||
/*
|
||||
* These need to be counters rather than flags, since more plugins
|
||||
* can implement same effect; the practicality of stacking effects
|
||||
@ -91,6 +101,8 @@ static void mutter_window_get_property (GObject *object,
|
||||
static void mutter_window_detach (MutterWindow *self);
|
||||
static gboolean mutter_window_has_shadow (MutterWindow *self);
|
||||
|
||||
static void mutter_window_clear_shape_region (MutterWindow *self);
|
||||
static void mutter_window_clear_bounding_region (MutterWindow *self);
|
||||
|
||||
static gboolean is_shaped (MetaDisplay *display,
|
||||
Window xwindow);
|
||||
@ -388,6 +400,9 @@ mutter_window_dispose (GObject *object)
|
||||
|
||||
mutter_window_detach (self);
|
||||
|
||||
mutter_window_clear_shape_region (self);
|
||||
mutter_window_clear_bounding_region (self);
|
||||
|
||||
if (priv->damage != None)
|
||||
{
|
||||
meta_error_trap_push (display);
|
||||
@ -1213,6 +1228,214 @@ mutter_window_unmapped (MutterWindow *self)
|
||||
priv->needs_repair = FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
mutter_window_clear_shape_region (MutterWindow *self)
|
||||
{
|
||||
MutterWindowPrivate *priv = self->priv;
|
||||
|
||||
if (priv->shape_region)
|
||||
{
|
||||
gdk_region_destroy (priv->shape_region);
|
||||
priv->shape_region = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
mutter_window_clear_bounding_region (MutterWindow *self)
|
||||
{
|
||||
MutterWindowPrivate *priv = self->priv;
|
||||
|
||||
if (priv->bounding_region)
|
||||
{
|
||||
gdk_region_destroy (priv->bounding_region);
|
||||
priv->bounding_region = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
mutter_window_update_bounding_region (MutterWindow *self,
|
||||
int width,
|
||||
int height)
|
||||
{
|
||||
MutterWindowPrivate *priv = self->priv;
|
||||
GdkRectangle bounding_rectangle = { 0, 0, width, height };
|
||||
|
||||
mutter_window_clear_bounding_region (self);
|
||||
|
||||
priv->bounding_region = gdk_region_rectangle (&bounding_rectangle);
|
||||
}
|
||||
|
||||
static void
|
||||
mutter_window_update_shape_region (MutterWindow *self,
|
||||
int n_rects,
|
||||
XRectangle *rects)
|
||||
{
|
||||
MutterWindowPrivate *priv = self->priv;
|
||||
int i;
|
||||
|
||||
mutter_window_clear_shape_region (self);
|
||||
|
||||
priv->shape_region = gdk_region_new ();
|
||||
for (i = 0; i < n_rects; i++)
|
||||
{
|
||||
GdkRectangle rect = { rects[i].x, rects[i].y, rects[i].width, rects[i].height };
|
||||
gdk_region_union_with_rect (priv->shape_region, &rect);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* mutter_window_get_obscured_region:
|
||||
* @self: a #MutterWindow
|
||||
*
|
||||
* Gets the region that is completely obscured by the window. Coordinates
|
||||
* are relative to the upper-left of the window.
|
||||
*
|
||||
* Return value: (transfer none): the area obscured by the window,
|
||||
* %NULL is the same as an empty region.
|
||||
*/
|
||||
GdkRegion *
|
||||
mutter_window_get_obscured_region (MutterWindow *self)
|
||||
{
|
||||
MutterWindowPrivate *priv = self->priv;
|
||||
|
||||
if (!priv->argb32 && priv->back_pixmap)
|
||||
{
|
||||
if (priv->shaped)
|
||||
return priv->shape_region;
|
||||
else
|
||||
return priv->bounding_region;
|
||||
}
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* Print out a region; useful for debugging */
|
||||
static void
|
||||
dump_region (GdkRegion *region)
|
||||
{
|
||||
GdkRectangle *rects;
|
||||
int n_rects;
|
||||
int i;
|
||||
|
||||
gdk_region_get_rectangles (region, &rects, &n_rects);
|
||||
g_print ("[");
|
||||
for (i = 0; i < n_rects; i++)
|
||||
{
|
||||
g_print ("+%d+%dx%dx%d ",
|
||||
rects[i].x, rects[i].y, rects[i].width, rects[i].height);
|
||||
}
|
||||
g_print ("]\n");
|
||||
g_free (rects);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* mutter_window_set_visible_region:
|
||||
* @self: a #MutterWindow
|
||||
* @visible_region: the region of the screen that isn't completely
|
||||
* obscured.
|
||||
*
|
||||
* Provides a hint as to what areas of the window need to be
|
||||
* drawn. Regions not in @visible_region are completely obscured.
|
||||
* This will be set before painting then unset afterwards.
|
||||
*/
|
||||
void
|
||||
mutter_window_set_visible_region (MutterWindow *self,
|
||||
GdkRegion *visible_region)
|
||||
{
|
||||
MutterWindowPrivate *priv = self->priv;
|
||||
GdkRegion *texture_clip_region = NULL;
|
||||
|
||||
/* Get the area of the window texture that would be drawn if
|
||||
* we weren't obscured at all
|
||||
*/
|
||||
if (priv->shaped)
|
||||
{
|
||||
if (priv->shape_region)
|
||||
texture_clip_region = gdk_region_copy (priv->shape_region);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (priv->bounding_region)
|
||||
texture_clip_region = gdk_region_copy (priv->bounding_region);
|
||||
}
|
||||
|
||||
if (!texture_clip_region)
|
||||
texture_clip_region = gdk_region_new ();
|
||||
|
||||
/* Then intersect that with the visible region to get the region
|
||||
* that we actually need to redraw.
|
||||
*/
|
||||
gdk_region_intersect (texture_clip_region, visible_region);
|
||||
|
||||
/* Assumes ownership */
|
||||
mutter_shaped_texture_set_clip_region (MUTTER_SHAPED_TEXTURE (priv->actor),
|
||||
texture_clip_region);
|
||||
}
|
||||
|
||||
/**
|
||||
* mutter_window_set_visible_region_beneath:
|
||||
* @self: a #MutterWindow
|
||||
* @visible_region: the region of the screen that isn't completely
|
||||
* obscured beneath the main window texture.
|
||||
*
|
||||
* Provides a hint as to what areas need to be drawn *beneath*
|
||||
* the main window texture. This is the relevant visible region
|
||||
* when drawing the shadow, properly accounting for areas of the
|
||||
* shadow hid by the window itself. This will be set before painting
|
||||
* then unset afterwards.
|
||||
*/
|
||||
void
|
||||
mutter_window_set_visible_region_beneath (MutterWindow *self,
|
||||
GdkRegion *beneath_region)
|
||||
{
|
||||
MutterWindowPrivate *priv = self->priv;
|
||||
|
||||
if (priv->shadow)
|
||||
{
|
||||
GdkRectangle shadow_rect;
|
||||
ClutterActorBox box;
|
||||
GdkOverlapType overlap;
|
||||
|
||||
/* We could compute an full clip region as we do for the window
|
||||
* texture, but the shadow is relatively cheap to draw, and
|
||||
* a little more complex to clip, so we just catch the case where
|
||||
* the shadow is completely obscured and doesn't need to be drawn
|
||||
* at all.
|
||||
*/
|
||||
clutter_actor_get_allocation_box (priv->shadow, &box);
|
||||
|
||||
shadow_rect.x = roundf (box.x1);
|
||||
shadow_rect.y = roundf (box.y1);
|
||||
shadow_rect.width = roundf (box.x2 - box.x1);
|
||||
shadow_rect.height = roundf (box.y2 - box.y1);
|
||||
|
||||
overlap = gdk_region_rect_in (beneath_region, &shadow_rect);
|
||||
|
||||
tidy_texture_frame_set_needs_paint (TIDY_TEXTURE_FRAME (priv->shadow),
|
||||
overlap != GDK_OVERLAP_RECTANGLE_OUT);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* mutter_window_reset_visible_regions:
|
||||
* @self: a #MutterWindow
|
||||
*
|
||||
* Unsets the regions set by mutter_window_reset_visible_region() and
|
||||
*mutter_window_reset_visible_region_beneath()
|
||||
*/
|
||||
void
|
||||
mutter_window_reset_visible_regions (MutterWindow *self)
|
||||
{
|
||||
MutterWindowPrivate *priv = self->priv;
|
||||
|
||||
mutter_shaped_texture_set_clip_region (MUTTER_SHAPED_TEXTURE (priv->actor),
|
||||
NULL);
|
||||
if (priv->shadow)
|
||||
tidy_texture_frame_set_needs_paint (TIDY_TEXTURE_FRAME (priv->shadow), TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
check_needs_repair (MutterWindow *self)
|
||||
{
|
||||
@ -1268,6 +1491,7 @@ check_needs_repair (MutterWindow *self)
|
||||
if (priv->back_pixmap == None)
|
||||
{
|
||||
meta_verbose ("Unable to get named pixmap for %p\n", self);
|
||||
mutter_window_update_bounding_region (self, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1293,6 +1517,8 @@ check_needs_repair (MutterWindow *self)
|
||||
if (priv->shadow)
|
||||
clutter_actor_set_size (priv->shadow, pxm_width, pxm_height);
|
||||
|
||||
mutter_window_update_bounding_region (self, pxm_width, pxm_height);
|
||||
|
||||
full = TRUE;
|
||||
}
|
||||
|
||||
@ -1389,6 +1615,7 @@ check_needs_reshape (MutterWindow *self)
|
||||
return;
|
||||
|
||||
mutter_shaped_texture_clear_rectangles (MUTTER_SHAPED_TEXTURE (priv->actor));
|
||||
mutter_window_clear_shape_region (self);
|
||||
|
||||
#ifdef HAVE_SHAPE
|
||||
if (priv->shaped)
|
||||
@ -1408,6 +1635,8 @@ check_needs_reshape (MutterWindow *self)
|
||||
mutter_shaped_texture_add_rectangles (MUTTER_SHAPED_TEXTURE (priv->actor),
|
||||
n_rects, rects);
|
||||
|
||||
mutter_window_update_shape_region (self, n_rects, rects);
|
||||
|
||||
XFree (rects);
|
||||
}
|
||||
}
|
||||
|
@ -64,6 +64,8 @@ struct _TidyTextureFramePrivate
|
||||
gfloat bottom;
|
||||
|
||||
CoglHandle material;
|
||||
|
||||
guint needs_paint : 1;
|
||||
};
|
||||
|
||||
static void
|
||||
@ -175,6 +177,9 @@ tidy_texture_frame_paint (ClutterActor *self)
|
||||
if (G_UNLIKELY (priv->parent_texture == NULL))
|
||||
return;
|
||||
|
||||
if (!priv->needs_paint)
|
||||
return;
|
||||
|
||||
/* parent texture may have been hidden, so need to make sure it gets
|
||||
* realized
|
||||
*/
|
||||
@ -608,3 +613,29 @@ tidy_texture_frame_get_frame (TidyTextureFrame *frame,
|
||||
if (left)
|
||||
*left = priv->left;
|
||||
}
|
||||
|
||||
/**
|
||||
* tidy_texture_frame_set_needs_paint:
|
||||
* @frame: a #TidyTextureframe
|
||||
* @needs_paint: if %FALSE, the paint will be skipped
|
||||
*
|
||||
* Provides a hint to the texture frame that it is totally obscured
|
||||
* and doesn't need to be painted. This would typically be called
|
||||
* by a parent container if it detects the condition prior to
|
||||
* painting its children and then unset afterwards.
|
||||
*
|
||||
* Since it is not supposed to have any effect on display, it does
|
||||
* not queue a repaint.
|
||||
*/
|
||||
void
|
||||
tidy_texture_frame_set_needs_paint (TidyTextureFrame *frame,
|
||||
gboolean needs_paint)
|
||||
{
|
||||
TidyTextureFramePrivate *priv;
|
||||
|
||||
g_return_if_fail (TIDY_IS_TEXTURE_FRAME (frame));
|
||||
|
||||
priv = frame->priv;
|
||||
|
||||
priv->needs_paint = needs_paint;
|
||||
}
|
||||
|
@ -76,6 +76,9 @@ void tidy_texture_frame_get_frame (TidyTextureFrame *frame,
|
||||
gfloat *bottom,
|
||||
gfloat *left);
|
||||
|
||||
void tidy_texture_frame_set_needs_paint (TidyTextureFrame *frame,
|
||||
gboolean needs_paint);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* _HAVE_TIDY_TEXTURE_FRAME_H */
|
||||
|
Loading…
Reference in New Issue
Block a user