compositor: rework how backgrounds are managed

Background handling in GNOME is very roundabout at the moment.

gnome-settings-daemon uses gnome-desktop to read the background from
disk into a screen-sized pixmap. It then sets the XID of that pixmap
on the _XROOTPMAP_ID root window property.

mutter puts that pixmap into a texture/actor which gnome-shell then
uses.

Having the gnome-settings-daemon detour from disk to screen means we
can't easily let the compositor handle transition effects when
switching backgrounds. Also, having the background actor be
per-screen instead of per-monitor means we may have oversized
textures in certain multihead setups.

This commit changes mutter to read backgrounds from disk itself, and
it changes backgrounds to be per-monitor.

This way background handling/compositing is left to the compositor.

https://bugzilla.gnome.org/show_bug.cgi?id=682427
This commit is contained in:
Ray Strode 2013-01-23 15:54:41 -05:00
parent 842bc4421c
commit 580feb0c85
12 changed files with 1693 additions and 635 deletions

View File

@ -8,6 +8,7 @@ SUBDIRS=wm-tester tools compositor/plugins
INCLUDES= \ INCLUDES= \
-DCLUTTER_ENABLE_EXPERIMENTAL_API \ -DCLUTTER_ENABLE_EXPERIMENTAL_API \
-DCOGL_ENABLE_EXPERIMENTAL_API \ -DCOGL_ENABLE_EXPERIMENTAL_API \
-DCOGL_ENABLE_EXPERIMENTAL_2_0_API \
$(MUTTER_CFLAGS) \ $(MUTTER_CFLAGS) \
-I$(srcdir) \ -I$(srcdir) \
-I$(srcdir)/core \ -I$(srcdir)/core \
@ -48,8 +49,11 @@ libmutter_la_SOURCES = \
compositor/cogl-utils.h \ compositor/cogl-utils.h \
compositor/compositor.c \ compositor/compositor.c \
compositor/compositor-private.h \ compositor/compositor-private.h \
compositor/meta-background.c \
compositor/meta-background-actor.c \ compositor/meta-background-actor.c \
compositor/meta-background-actor-private.h \ compositor/meta-background-actor-private.h \
compositor/meta-background-group.c \
compositor/meta-background-group-private.h \
compositor/meta-module.c \ compositor/meta-module.c \
compositor/meta-module.h \ compositor/meta-module.h \
compositor/meta-plugin.c \ compositor/meta-plugin.c \
@ -71,7 +75,9 @@ libmutter_la_SOURCES = \
compositor/region-utils.c \ compositor/region-utils.c \
compositor/region-utils.h \ compositor/region-utils.h \
meta/compositor.h \ meta/compositor.h \
meta/meta-background.h \
meta/meta-background-actor.h \ meta/meta-background-actor.h \
meta/meta-background-group.h \
meta/meta-plugin.h \ meta/meta-plugin.h \
meta/meta-shadow-factory.h \ meta/meta-shadow-factory.h \
meta/meta-window-actor.h \ meta/meta-window-actor.h \
@ -175,6 +181,8 @@ libmutterinclude_base_headers = \
meta/keybindings.h \ meta/keybindings.h \
meta/main.h \ meta/main.h \
meta/meta-background-actor.h \ meta/meta-background-actor.h \
meta/meta-background-group.h \
meta/meta-background.h \
meta/meta-plugin.h \ meta/meta-plugin.h \
meta/meta-shaped-texture.h \ meta/meta-shaped-texture.h \
meta/meta-shadow-factory.h \ meta/meta-shadow-factory.h \

View File

@ -13,10 +13,11 @@
#include "xprops.h" #include "xprops.h"
#include <meta/prefs.h> #include <meta/prefs.h>
#include <meta/main.h> #include <meta/main.h>
#include <meta/meta-background-actor.h>
#include <meta/meta-background-group.h>
#include <meta/meta-shadow-factory.h> #include <meta/meta-shadow-factory.h>
#include "meta-window-actor-private.h" #include "meta-window-actor-private.h"
#include "meta-window-group.h" #include "meta-window-group.h"
#include "meta-background-actor-private.h"
#include "window-private.h" /* to check window->hidden */ #include "window-private.h" /* to check window->hidden */
#include "display-private.h" /* for meta_display_lookup_x_window() */ #include "display-private.h" /* for meta_display_lookup_x_window() */
#include <X11/extensions/shape.h> #include <X11/extensions/shape.h>
@ -117,21 +118,6 @@ process_property_notify (MetaCompositor *compositor,
{ {
MetaWindowActor *window_actor; MetaWindowActor *window_actor;
if (event->atom == compositor->atom_x_root_pixmap)
{
GSList *l;
for (l = meta_display_get_screens (compositor->display); l; l = l->next)
{
MetaScreen *screen = l->data;
if (event->window == meta_screen_get_xroot (screen))
{
meta_background_actor_update (screen);
return;
}
}
}
if (window == NULL) if (window == NULL)
return; return;
@ -254,27 +240,6 @@ meta_get_top_window_group_for_screen (MetaScreen *screen)
return info->top_window_group; return info->top_window_group;
} }
/**
* meta_get_background_actor_for_screen:
* @screen: a #MetaScreen
*
* Gets the actor that draws the root window background under the windows.
* The root window background automatically tracks the image or color set
* by the environment.
*
* Returns: (transfer none): The background actor corresponding to @screen
*/
ClutterActor *
meta_get_background_actor_for_screen (MetaScreen *screen)
{
MetaCompScreen *info = meta_screen_get_compositor_data (screen);
if (!info)
return NULL;
return info->background_actor;
}
/** /**
* meta_get_window_actors: * meta_get_window_actors:
* @screen: a #MetaScreen * @screen: a #MetaScreen
@ -606,14 +571,8 @@ meta_compositor_manage_screen (MetaCompositor *compositor,
info->window_group = meta_window_group_new (screen); info->window_group = meta_window_group_new (screen);
info->top_window_group = meta_window_group_new (screen); info->top_window_group = meta_window_group_new (screen);
info->background_actor = meta_background_actor_new_for_screen (screen);
clutter_actor_set_reactive (info->background_actor, TRUE);
info->overlay_group = clutter_group_new (); info->overlay_group = clutter_group_new ();
clutter_container_add (CLUTTER_CONTAINER (info->window_group),
info->background_actor,
NULL);
clutter_container_add (CLUTTER_CONTAINER (info->stage), clutter_container_add (CLUTTER_CONTAINER (info->stage),
info->window_group, info->window_group,
info->top_window_group, info->top_window_group,
@ -1066,6 +1025,7 @@ sync_actor_stacking (MetaCompScreen *info)
GList *expected_window_node; GList *expected_window_node;
GList *tmp; GList *tmp;
GList *old; GList *old;
GList *backgrounds;
gboolean has_windows; gboolean has_windows;
gboolean reordered; gboolean reordered;
@ -1083,15 +1043,19 @@ sync_actor_stacking (MetaCompScreen *info)
* (we really need extra API to make that reliable.) * (we really need extra API to make that reliable.)
*/ */
/* First we check if the background is at the bottom. Then /* First we collect a list of all backgrounds, and check if they're at the
* we check if the window actors are in the correct sequence */ * bottom. Then we check if the window actors are in the correct sequence */
backgrounds = NULL;
expected_window_node = info->windows; expected_window_node = info->windows;
for (old = children; old != NULL; old = old->next) for (old = children; old != NULL; old = old->next)
{ {
ClutterActor *actor = old->data; ClutterActor *actor = old->data;
if (actor == info->background_actor) if (META_IS_BACKGROUND_GROUP (actor) ||
META_IS_BACKGROUND_ACTOR (actor))
{ {
backgrounds = g_list_prepend (backgrounds, actor);
if (has_windows) if (has_windows)
reordered = TRUE; reordered = TRUE;
} }
@ -1109,7 +1073,10 @@ sync_actor_stacking (MetaCompScreen *info)
g_list_free (children); g_list_free (children);
if (!reordered) if (!reordered)
{
g_list_free (backgrounds);
return; return;
}
/* reorder the actors by lowering them in turn to the bottom of the stack. /* reorder the actors by lowering them in turn to the bottom of the stack.
* windows first, then background */ * windows first, then background */
@ -1120,7 +1087,16 @@ sync_actor_stacking (MetaCompScreen *info)
clutter_actor_lower_bottom (CLUTTER_ACTOR (window_actor)); clutter_actor_lower_bottom (CLUTTER_ACTOR (window_actor));
} }
clutter_actor_lower_bottom (info->background_actor); /* we prepended the backgrounds above so the last actor in the list
* should get lowered to the bottom last.
*/
for (tmp = backgrounds; tmp != NULL; tmp = tmp->next)
{
ClutterActor *actor = tmp->data;
clutter_actor_lower_bottom (CLUTTER_ACTOR (actor));
}
g_list_free (backgrounds);
} }
void void
@ -1276,8 +1252,6 @@ meta_compositor_sync_screen_size (MetaCompositor *compositor,
XResizeWindow (xdisplay, xwin, width, height); XResizeWindow (xdisplay, xwin, width, height);
meta_background_actor_screen_size_changed (screen);
meta_verbose ("Changed size for stage on screen %d to %dx%d\n", meta_verbose ("Changed size for stage on screen %d to %dx%d\n",
meta_screen_get_screen_number (screen), meta_screen_get_screen_number (screen),
width, height); width, height);

View File

@ -9,7 +9,6 @@
void meta_background_actor_set_visible_region (MetaBackgroundActor *self, void meta_background_actor_set_visible_region (MetaBackgroundActor *self,
cairo_region_t *visible_region); cairo_region_t *visible_region);
void meta_background_actor_update (MetaScreen *screen); cairo_region_t *meta_background_actor_get_visible_region (MetaBackgroundActor *self);
void meta_background_actor_screen_size_changed (MetaScreen *screen);
#endif /* META_BACKGROUND_ACTOR_PRIVATE_H */ #endif /* META_BACKGROUND_ACTOR_PRIVATE_H */

View File

@ -34,223 +34,23 @@
#include "cogl-utils.h" #include "cogl-utils.h"
#include "compositor-private.h" #include "compositor-private.h"
#include <meta/errors.h> #include <meta/errors.h>
#include <meta/meta-background.h>
#include "meta-background-actor-private.h" #include "meta-background-actor-private.h"
/* We allow creating multiple MetaBackgroundActors for the same MetaScreen to
* allow different rendering options to be set for different copies.
* But we want to share the same underlying CoglTexture for efficiency and
* to avoid driver bugs that might occur if we created multiple CoglTexturePixmaps
* for the same pixmap.
*
* This structure holds common information.
*/
typedef struct _MetaScreenBackground MetaScreenBackground;
struct _MetaScreenBackground
{
MetaScreen *screen;
GSList *actors;
float texture_width;
float texture_height;
CoglTexture *texture;
CoglMaterialWrapMode wrap_mode;
guint have_pixmap : 1;
};
struct _MetaBackgroundActorPrivate struct _MetaBackgroundActorPrivate
{ {
MetaScreenBackground *background;
CoglPipeline *pipeline;
cairo_region_t *visible_region; cairo_region_t *visible_region;
float dim_factor;
}; };
enum
{
PROP_0,
PROP_DIM_FACTOR,
PROP_LAST
};
static GParamSpec *obj_props[PROP_LAST];
G_DEFINE_TYPE (MetaBackgroundActor, meta_background_actor, CLUTTER_TYPE_ACTOR); G_DEFINE_TYPE (MetaBackgroundActor, meta_background_actor, CLUTTER_TYPE_ACTOR);
static void set_texture (MetaScreenBackground *background,
CoglHandle texture);
static void set_texture_to_stage_color (MetaScreenBackground *background);
static void
on_notify_stage_color (GObject *stage,
GParamSpec *pspec,
MetaScreenBackground *background)
{
if (!background->have_pixmap)
set_texture_to_stage_color (background);
}
static void
free_screen_background (MetaScreenBackground *background)
{
set_texture (background, COGL_INVALID_HANDLE);
if (background->screen != NULL)
{
ClutterActor *stage = meta_get_stage_for_screen (background->screen);
g_signal_handlers_disconnect_by_func (stage,
(gpointer) on_notify_stage_color,
background);
background->screen = NULL;
}
}
static MetaScreenBackground *
meta_screen_background_get (MetaScreen *screen)
{
MetaScreenBackground *background;
background = g_object_get_data (G_OBJECT (screen), "meta-screen-background");
if (background == NULL)
{
ClutterActor *stage;
background = g_new0 (MetaScreenBackground, 1);
background->screen = screen;
g_object_set_data_full (G_OBJECT (screen), "meta-screen-background",
background, (GDestroyNotify) free_screen_background);
stage = meta_get_stage_for_screen (screen);
g_signal_connect (stage, "notify::color",
G_CALLBACK (on_notify_stage_color), background);
meta_background_actor_update (screen);
}
return background;
}
static void
update_wrap_mode_of_actor (MetaBackgroundActor *self)
{
MetaBackgroundActorPrivate *priv = self->priv;
cogl_pipeline_set_layer_wrap_mode (priv->pipeline, 0, priv->background->wrap_mode);
}
static void
update_wrap_mode (MetaScreenBackground *background)
{
GSList *l;
int width, height;
meta_screen_get_size (background->screen, &width, &height);
/* We turn off repeating when we have a full-screen pixmap to keep from
* getting artifacts from one side of the image sneaking into the other
* side of the image via bilinear filtering.
*/
if (width == background->texture_width && height == background->texture_height)
background->wrap_mode = COGL_MATERIAL_WRAP_MODE_CLAMP_TO_EDGE;
else
background->wrap_mode = COGL_MATERIAL_WRAP_MODE_REPEAT;
for (l = background->actors; l; l = l->next)
update_wrap_mode_of_actor (l->data);
}
static void
set_texture_on_actor (MetaBackgroundActor *self)
{
MetaBackgroundActorPrivate *priv = self->priv;
MetaDisplay *display = meta_screen_get_display (priv->background->screen);
/* This may trigger destruction of an old texture pixmap, which, if
* the underlying X pixmap is already gone has the tendency to trigger
* X errors inside DRI. For safety, trap errors */
meta_error_trap_push (display);
cogl_pipeline_set_layer_texture (priv->pipeline, 0, priv->background->texture);
meta_error_trap_pop (display);
clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
}
static void
set_texture (MetaScreenBackground *background,
CoglHandle texture)
{
MetaDisplay *display = meta_screen_get_display (background->screen);
GSList *l;
/* This may trigger destruction of an old texture pixmap, which, if
* the underlying X pixmap is already gone has the tendency to trigger
* X errors inside DRI. For safety, trap errors */
meta_error_trap_push (display);
if (background->texture != COGL_INVALID_HANDLE)
{
cogl_handle_unref (background->texture);
background->texture = COGL_INVALID_HANDLE;
}
meta_error_trap_pop (display);
if (texture != COGL_INVALID_HANDLE)
background->texture = cogl_handle_ref (texture);
background->texture_width = cogl_texture_get_width (background->texture);
background->texture_height = cogl_texture_get_height (background->texture);
for (l = background->actors; l; l = l->next)
set_texture_on_actor (l->data);
update_wrap_mode (background);
}
/* Sets our pipeline to paint with a 1x1 texture of the stage's background
* color; doing this when we have no pixmap allows the application to turn
* off painting the stage. There might be a performance benefit to
* painting in this case with a solid color, but the normal solid color
* case is a 1x1 root pixmap, so we'd have to reverse-engineer that to
* actually pick up the (small?) performance win. This is just a fallback.
*/
static void
set_texture_to_stage_color (MetaScreenBackground *background)
{
ClutterActor *stage = meta_get_stage_for_screen (background->screen);
ClutterColor color;
CoglHandle texture;
clutter_stage_get_color (CLUTTER_STAGE (stage), &color);
/* Slicing will prevent COGL from using hardware texturing for
* the tiled 1x1 pixmap, and will cause it to draw the window
* background in millions of separate 1x1 rectangles */
texture = meta_create_color_texture_4ub (color.red, color.green,
color.blue, 0xff,
COGL_TEXTURE_NO_SLICING);
set_texture (background, texture);
cogl_handle_unref (texture);
}
static void static void
meta_background_actor_dispose (GObject *object) meta_background_actor_dispose (GObject *object)
{ {
MetaBackgroundActor *self = META_BACKGROUND_ACTOR (object); MetaBackgroundActor *self = META_BACKGROUND_ACTOR (object);
MetaBackgroundActorPrivate *priv = self->priv;
meta_background_actor_set_visible_region (self, NULL); meta_background_actor_set_visible_region (self, NULL);
if (priv->background != NULL)
{
priv->background->actors = g_slist_remove (priv->background->actors, self);
priv->background = NULL;
}
g_clear_pointer(&priv->pipeline, cogl_object_unref);
G_OBJECT_CLASS (meta_background_actor_parent_class)->dispose (object); G_OBJECT_CLASS (meta_background_actor_parent_class)->dispose (object);
} }
@ -260,11 +60,15 @@ meta_background_actor_get_preferred_width (ClutterActor *actor,
gfloat *min_width_p, gfloat *min_width_p,
gfloat *natural_width_p) gfloat *natural_width_p)
{ {
MetaBackgroundActor *self = META_BACKGROUND_ACTOR (actor); ClutterContent *content;
MetaBackgroundActorPrivate *priv = self->priv; gfloat width;
int width, height;
meta_screen_get_size (priv->background->screen, &width, &height); content = clutter_actor_get_content (actor);
if (content)
clutter_content_get_preferred_size (content, &width, NULL);
else
width = 0;
if (min_width_p) if (min_width_p)
*min_width_p = width; *min_width_p = width;
@ -279,11 +83,15 @@ meta_background_actor_get_preferred_height (ClutterActor *actor,
gfloat *natural_height_p) gfloat *natural_height_p)
{ {
MetaBackgroundActor *self = META_BACKGROUND_ACTOR (actor); ClutterContent *content;
MetaBackgroundActorPrivate *priv = self->priv; gfloat height;
int width, height;
meta_screen_get_size (priv->background->screen, &width, &height); content = clutter_actor_get_content (actor);
if (content)
clutter_content_get_preferred_size (content, NULL, &height);
else
height = 0;
if (min_height_p) if (min_height_p)
*min_height_p = height; *min_height_p = height;
@ -291,64 +99,19 @@ meta_background_actor_get_preferred_height (ClutterActor *actor,
*natural_height_p = height; *natural_height_p = height;
} }
static void
meta_background_actor_paint (ClutterActor *actor)
{
MetaBackgroundActor *self = META_BACKGROUND_ACTOR (actor);
MetaBackgroundActorPrivate *priv = self->priv;
guint8 opacity = clutter_actor_get_paint_opacity (actor);
guint8 color_component;
int width, height;
meta_screen_get_size (priv->background->screen, &width, &height);
color_component = (int)(0.5 + opacity * priv->dim_factor);
cogl_pipeline_set_color4ub (priv->pipeline,
color_component,
color_component,
color_component,
opacity);
cogl_set_source (priv->pipeline);
if (priv->visible_region)
{
int n_rectangles = cairo_region_num_rectangles (priv->visible_region);
int i;
for (i = 0; i < n_rectangles; i++)
{
cairo_rectangle_int_t rect;
cairo_region_get_rectangle (priv->visible_region, i, &rect);
cogl_rectangle_with_texture_coords (rect.x, rect.y,
rect.x + rect.width, rect.y + rect.height,
rect.x / priv->background->texture_width,
rect.y / priv->background->texture_height,
(rect.x + rect.width) / priv->background->texture_width,
(rect.y + rect.height) / priv->background->texture_height);
}
}
else
{
cogl_rectangle_with_texture_coords (0.0f, 0.0f,
width, height,
0.0f, 0.0f,
width / priv->background->texture_width,
height / priv->background->texture_height);
}
}
static gboolean static gboolean
meta_background_actor_get_paint_volume (ClutterActor *actor, meta_background_actor_get_paint_volume (ClutterActor *actor,
ClutterPaintVolume *volume) ClutterPaintVolume *volume)
{ {
MetaBackgroundActor *self = META_BACKGROUND_ACTOR (actor); ClutterContent *content;
MetaBackgroundActorPrivate *priv = self->priv; gfloat width, height;
int width, height;
meta_screen_get_size (priv->background->screen, &width, &height); content = clutter_actor_get_content (actor);
if (!content)
return FALSE;
clutter_content_get_preferred_size (content, &width, &height);
clutter_paint_volume_set_width (volume, width); clutter_paint_volume_set_width (volume, width);
clutter_paint_volume_set_height (volume, height); clutter_paint_volume_set_height (volume, height);
@ -356,215 +119,48 @@ meta_background_actor_get_paint_volume (ClutterActor *actor,
return TRUE; return TRUE;
} }
static void
meta_background_actor_set_dim_factor (MetaBackgroundActor *self,
gfloat dim_factor)
{
MetaBackgroundActorPrivate *priv = self->priv;
if (priv->dim_factor == dim_factor)
return;
priv->dim_factor = dim_factor;
clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_DIM_FACTOR]);
}
static void
meta_background_actor_get_property(GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
MetaBackgroundActor *self = META_BACKGROUND_ACTOR (object);
MetaBackgroundActorPrivate *priv = self->priv;
switch (prop_id)
{
case PROP_DIM_FACTOR:
g_value_set_float (value, priv->dim_factor);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
meta_background_actor_set_property(GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
MetaBackgroundActor *self = META_BACKGROUND_ACTOR (object);
switch (prop_id)
{
case PROP_DIM_FACTOR:
meta_background_actor_set_dim_factor (self, g_value_get_float (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void static void
meta_background_actor_class_init (MetaBackgroundActorClass *klass) meta_background_actor_class_init (MetaBackgroundActorClass *klass)
{ {
GObjectClass *object_class = G_OBJECT_CLASS (klass); GObjectClass *object_class = G_OBJECT_CLASS (klass);
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
GParamSpec *pspec;
g_type_class_add_private (klass, sizeof (MetaBackgroundActorPrivate)); g_type_class_add_private (klass, sizeof (MetaBackgroundActorPrivate));
object_class->dispose = meta_background_actor_dispose; object_class->dispose = meta_background_actor_dispose;
object_class->get_property = meta_background_actor_get_property;
object_class->set_property = meta_background_actor_set_property;
actor_class->get_preferred_width = meta_background_actor_get_preferred_width; actor_class->get_preferred_width = meta_background_actor_get_preferred_width;
actor_class->get_preferred_height = meta_background_actor_get_preferred_height; actor_class->get_preferred_height = meta_background_actor_get_preferred_height;
actor_class->paint = meta_background_actor_paint;
actor_class->get_paint_volume = meta_background_actor_get_paint_volume; actor_class->get_paint_volume = meta_background_actor_get_paint_volume;
/**
* MetaBackgroundActor:dim-factor:
*
* Factor to dim the background by, between 0.0 (black) and 1.0 (original
* colors)
*/
pspec = g_param_spec_float ("dim-factor",
"Dim factor",
"Factor to dim the background by",
0.0, 1.0,
1.0,
G_PARAM_READWRITE);
obj_props[PROP_DIM_FACTOR] = pspec;
g_object_class_install_property (object_class, PROP_DIM_FACTOR, pspec);
} }
static void static void
meta_background_actor_init (MetaBackgroundActor *self) meta_background_actor_init (MetaBackgroundActor *self)
{ {
MetaBackgroundActorPrivate *priv; self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
priv = self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
META_TYPE_BACKGROUND_ACTOR, META_TYPE_BACKGROUND_ACTOR,
MetaBackgroundActorPrivate); MetaBackgroundActorPrivate);
priv->dim_factor = 1.0;
} }
/** /**
* meta_background_actor_new: * meta_background_actor_new:
* @screen: the #MetaScreen
* *
* Creates a new actor to draw the background for the given screen. * Creates a new actor to draw the background for the given monitor.
* This actor should be associated with a #MetaBackground using
* clutter_actor_set_content()
* *
* Return value: the newly created background actor * Return value: the newly created background actor
*/ */
ClutterActor * ClutterActor *
meta_background_actor_new_for_screen (MetaScreen *screen) meta_background_actor_new (void)
{ {
MetaBackgroundActor *self; MetaBackgroundActor *self;
MetaBackgroundActorPrivate *priv;
g_return_val_if_fail (META_IS_SCREEN (screen), NULL);
self = g_object_new (META_TYPE_BACKGROUND_ACTOR, NULL); self = g_object_new (META_TYPE_BACKGROUND_ACTOR, NULL);
priv = self->priv;
priv->background = meta_screen_background_get (screen);
priv->background->actors = g_slist_prepend (priv->background->actors, self);
/* A CoglMaterial and a CoglPipeline are the same thing */
priv->pipeline = (CoglPipeline*) meta_create_texture_material (NULL);
set_texture_on_actor (self);
update_wrap_mode_of_actor (self);
return CLUTTER_ACTOR (self); return CLUTTER_ACTOR (self);
} }
/**
* meta_background_actor_update:
* @screen: a #MetaScreen
*
* Refetches the _XROOTPMAP_ID property for the root window and updates
* the contents of the background actor based on that. There's no attempt
* to optimize out pixmap values that don't change (since a root pixmap
* could be replaced by with another pixmap with the same ID under some
* circumstances), so this should only be called when we actually receive
* a PropertyNotify event for the property.
*/
void
meta_background_actor_update (MetaScreen *screen)
{
MetaScreenBackground *background;
MetaDisplay *display;
MetaCompositor *compositor;
Atom type;
int format;
gulong nitems;
gulong bytes_after;
guchar *data;
Pixmap root_pixmap_id;
background = meta_screen_background_get (screen);
display = meta_screen_get_display (screen);
compositor = meta_display_get_compositor (display);
root_pixmap_id = None;
if (!XGetWindowProperty (meta_display_get_xdisplay (display),
meta_screen_get_xroot (screen),
compositor->atom_x_root_pixmap,
0, LONG_MAX,
False,
AnyPropertyType,
&type, &format, &nitems, &bytes_after, &data) &&
type != None)
{
/* Got a property. */
if (type == XA_PIXMAP && format == 32 && nitems == 1)
{
/* Was what we expected. */
root_pixmap_id = *(Pixmap *)data;
}
XFree(data);
}
if (root_pixmap_id != None)
{
CoglHandle texture;
CoglContext *ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
GError *error = NULL;
meta_error_trap_push (display);
texture = cogl_texture_pixmap_x11_new (ctx, root_pixmap_id, FALSE, &error);
meta_error_trap_pop (display);
if (texture != COGL_INVALID_HANDLE)
{
set_texture (background, texture);
cogl_handle_unref (texture);
background->have_pixmap = True;
return;
}
else
{
g_warning ("Failed to create background texture from pixmap: %s",
error->message);
g_error_free (error);
}
}
background->have_pixmap = False;
set_texture_to_stage_color (background);
}
/** /**
* meta_background_actor_set_visible_region: * meta_background_actor_set_visible_region:
* @self: a #MetaBackgroundActor * @self: a #MetaBackgroundActor
@ -584,120 +180,44 @@ meta_background_actor_set_visible_region (MetaBackgroundActor *self,
priv = self->priv; priv = self->priv;
if (priv->visible_region) g_clear_pointer (&priv->visible_region,
{ (GDestroyNotify)
cairo_region_destroy (priv->visible_region); cairo_region_destroy);
priv->visible_region = NULL;
}
if (visible_region) if (visible_region)
{ priv->visible_region = cairo_region_copy (visible_region);
cairo_rectangle_int_t screen_rect = { 0 };
meta_screen_get_size (priv->background->screen, &screen_rect.width, &screen_rect.height);
/* Doing the intersection here is probably unnecessary - MetaWindowGroup
* should never compute a visible area that's larger than the root screen!
* but it's not that expensive and adds some extra robustness.
*/
priv->visible_region = cairo_region_create_rectangle (&screen_rect);
cairo_region_intersect (priv->visible_region, visible_region);
}
} }
/** /**
* meta_background_actor_screen_size_changed: * meta_background_actor_get_visible_region:
* @screen: a #MetaScreen * @self: a #MetaBackgroundActor
* *
* Called by the compositor when the size of the #MetaScreen changes * Return value (transfer full): a #cairo_region_t that represents the part of
* the background not obscured by other #MetaBackgroundActor or
* #MetaWindowActor objects.
*/ */
void cairo_region_t *
meta_background_actor_screen_size_changed (MetaScreen *screen) meta_background_actor_get_visible_region (MetaBackgroundActor *self)
{ {
MetaScreenBackground *background = meta_screen_background_get (screen); MetaBackgroundActorPrivate *priv = self->priv;
GSList *l; ClutterActorBox content_box;
cairo_rectangle_int_t content_area = { 0 };
cairo_region_t *visible_region;
update_wrap_mode (background); g_return_val_if_fail (META_IS_BACKGROUND_ACTOR (self), NULL);
for (l = background->actors; l; l = l->next) if (!priv->visible_region)
clutter_actor_queue_relayout (l->data); return NULL;
clutter_actor_get_content_box (CLUTTER_ACTOR (self), &content_box);
content_area.x = content_box.x1;
content_area.y = content_box.y1;
content_area.width = content_box.x2 - content_box.x1;
content_area.height = content_box.y2 - content_box.y1;
visible_region = cairo_region_create_rectangle (&content_area);
cairo_region_intersect (visible_region, priv->visible_region);
return visible_region;
} }
/**
* meta_background_actor_add_glsl_snippet:
* @actor: a #MetaBackgroundActor
* @hook: where to insert the code
* @declarations: GLSL declarations
* @code: GLSL code
* @is_replace: wheter Cogl code should be replaced by the custom shader
*
* Adds a GLSL snippet to the pipeline used for drawing the background.
* See #CoglSnippet for details.
*/
void
meta_background_actor_add_glsl_snippet (MetaBackgroundActor *actor,
MetaSnippetHook hook,
const char *declarations,
const char *code,
gboolean is_replace)
{
MetaBackgroundActorPrivate *priv;
CoglSnippet *snippet;
g_return_if_fail (META_IS_BACKGROUND_ACTOR (actor));
priv = actor->priv;
if (is_replace)
{
snippet = cogl_snippet_new (hook, declarations, NULL);
cogl_snippet_set_replace (snippet, code);
}
else
{
snippet = cogl_snippet_new (hook, declarations, code);
}
if (hook == META_SNIPPET_HOOK_VERTEX ||
hook == META_SNIPPET_HOOK_FRAGMENT)
cogl_pipeline_add_snippet (priv->pipeline, snippet);
else
cogl_pipeline_add_layer_snippet (priv->pipeline, 0, snippet);
cogl_object_unref (snippet);
}
/**
* meta_background_actor_set_uniform_float:
* @actor: a #MetaBackgroundActor
* @uniform_name:
* @n_components: number of components (for vector uniforms)
* @count: number of uniforms (for array uniforms)
* @uniform: (array length=uniform_length): the float values to set
* @uniform_length: the length of @uniform. Must be exactly @n_components x @count,
* and is provided mainly for language bindings.
*
* Sets a new GLSL uniform to the provided value. This is mostly
* useful in congiunction with meta_background_actor_add_glsl_snippet().
*/
void
meta_background_actor_set_uniform_float (MetaBackgroundActor *actor,
const char *uniform_name,
int n_components,
int count,
const float *uniform,
int uniform_length)
{
MetaBackgroundActorPrivate *priv;
g_return_if_fail (META_IS_BACKGROUND_ACTOR (actor));
g_return_if_fail (uniform_length == n_components * count);
priv = actor->priv;
cogl_pipeline_set_uniform_float (priv->pipeline,
cogl_pipeline_get_uniform_location (priv->pipeline,
uniform_name),
n_components, count, uniform);
}

View File

@ -0,0 +1,11 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#ifndef META_BACKGROUND_GROUP_PRIVATE_H
#define META_BACKGROUND_GROUP_PRIVATE_H
#include <meta/screen.h>
#include <meta/meta-background-group.h>
void meta_background_group_set_visible_region (MetaBackgroundGroup *self,
cairo_region_t *visible_region);
#endif /* META_BACKGROUND_GROUP_PRIVATE_H */

View File

@ -0,0 +1,92 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#include <config.h>
#include "compositor-private.h"
#include "clutter-utils.h"
#include "meta-background-actor-private.h"
#include "meta-background-group-private.h"
G_DEFINE_TYPE (MetaBackgroundGroup, meta_background_group, CLUTTER_TYPE_GROUP);
struct _MetaBackgroundGroupPrivate
{
ClutterLayoutManager *layout_manager;
};
static void
meta_background_group_dispose (GObject *object)
{
G_OBJECT_CLASS (meta_background_group_parent_class)->dispose (object);
}
static void
meta_background_group_class_init (MetaBackgroundGroupClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = meta_background_group_dispose;
g_type_class_add_private (klass, sizeof (MetaBackgroundGroupPrivate));
}
static void
meta_background_group_init (MetaBackgroundGroup *self)
{
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
META_TYPE_BACKGROUND_GROUP,
MetaBackgroundGroupPrivate);
self->priv->layout_manager = clutter_bin_layout_new (CLUTTER_BIN_ALIGNMENT_FIXED,
CLUTTER_BIN_ALIGNMENT_FIXED);
clutter_actor_set_layout_manager (CLUTTER_ACTOR (self), self->priv->layout_manager);
}
/**
* meta_background_group_set_visible_region:
* @self: a #MetaBackgroundGroup
* @visible_region: (allow-none): the parts of the background to paint
*
* Sets the area of the backgrounds that is unobscured by overlapping windows.
* This is used to optimize and only paint the visible portions.
*/
void
meta_background_group_set_visible_region (MetaBackgroundGroup *self,
cairo_region_t *region)
{
GList *children, *l;
children = clutter_actor_get_children (CLUTTER_ACTOR (self));
for (l = children; l; l = l->next)
{
ClutterActor *actor = l->data;
if (META_IS_BACKGROUND_ACTOR (actor))
{
meta_background_actor_set_visible_region (META_BACKGROUND_ACTOR (actor), region);
}
else if (META_IS_BACKGROUND_GROUP (actor))
{
int x, y;
if (!meta_actor_is_untransformed (actor, &x, &y))
continue;
cairo_region_translate (region, -x, -y);
meta_background_group_set_visible_region (META_BACKGROUND_GROUP (actor), region);
cairo_region_translate (region, x, y);
}
}
g_list_free (children);
}
ClutterActor *
meta_background_group_new (void)
{
MetaBackgroundGroup *background_group;
background_group = g_object_new (META_TYPE_BACKGROUND_GROUP, NULL);
return CLUTTER_ACTOR (background_group);
}

File diff suppressed because it is too large Load Diff

View File

@ -7,11 +7,12 @@
#include <gdk/gdk.h> /* for gdk_rectangle_intersect() */ #include <gdk/gdk.h> /* for gdk_rectangle_intersect() */
#include "clutter-utils.h"
#include "compositor-private.h" #include "compositor-private.h"
#include "meta-window-actor-private.h" #include "meta-window-actor-private.h"
#include "meta-window-group.h" #include "meta-window-group.h"
#include "meta-background-actor-private.h" #include "meta-background-actor-private.h"
#include "clutter-utils.h" #include "meta-background-group-private.h"
struct _MetaWindowGroupClass struct _MetaWindowGroupClass
{ {
@ -203,9 +204,10 @@ meta_window_group_paint (ClutterActor *actor)
meta_window_actor_set_visible_region_beneath (window_actor, visible_region); meta_window_actor_set_visible_region_beneath (window_actor, visible_region);
cairo_region_translate (visible_region, x, y); cairo_region_translate (visible_region, x, y);
} }
else if (META_IS_BACKGROUND_ACTOR (l->data)) else if (META_IS_BACKGROUND_ACTOR (l->data) ||
META_IS_BACKGROUND_GROUP (l->data))
{ {
MetaBackgroundActor *background_actor = l->data; ClutterActor *background_actor = l->data;
int x, y; int x, y;
if (!meta_actor_is_untransformed (CLUTTER_ACTOR (background_actor), &x, &y)) if (!meta_actor_is_untransformed (CLUTTER_ACTOR (background_actor), &x, &y))
@ -215,7 +217,11 @@ meta_window_group_paint (ClutterActor *actor)
y += paint_y_offset; y += paint_y_offset;
cairo_region_translate (visible_region, - x, - y); cairo_region_translate (visible_region, - x, - y);
meta_background_actor_set_visible_region (background_actor, visible_region);
if (META_IS_BACKGROUND_GROUP (background_actor))
meta_background_group_set_visible_region (META_BACKGROUND_GROUP (background_actor), visible_region);
else
meta_background_actor_set_visible_region (META_BACKGROUND_ACTOR (background_actor), visible_region);
cairo_region_translate (visible_region, x, y); cairo_region_translate (visible_region, x, y);
} }
} }

View File

@ -44,7 +44,6 @@ ClutterActor *meta_get_top_window_group_for_screen (MetaScreen *screen);
void meta_disable_unredirect_for_screen (MetaScreen *screen); void meta_disable_unredirect_for_screen (MetaScreen *screen);
void meta_enable_unredirect_for_screen (MetaScreen *screen); void meta_enable_unredirect_for_screen (MetaScreen *screen);
ClutterActor *meta_get_background_actor_for_screen (MetaScreen *screen);
void meta_set_stage_input_region (MetaScreen *screen, void meta_set_stage_input_region (MetaScreen *screen,
XserverRegion region); XserverRegion region);
void meta_empty_stage_input_region (MetaScreen *screen); void meta_empty_stage_input_region (MetaScreen *screen);

View File

@ -24,9 +24,13 @@
#define META_BACKGROUND_ACTOR_H #define META_BACKGROUND_ACTOR_H
#include <clutter/clutter.h> #include <clutter/clutter.h>
#include <cogl/cogl.h>
#include <meta/gradient.h>
#include <meta/screen.h> #include <meta/screen.h>
#include <gsettings-desktop-schemas/gdesktop-enums.h>
/** /**
* MetaBackgroundActor: * MetaBackgroundActor:
* *
@ -60,41 +64,6 @@ struct _MetaBackgroundActor
GType meta_background_actor_get_type (void); GType meta_background_actor_get_type (void);
ClutterActor *meta_background_actor_new_for_screen (MetaScreen *screen); ClutterActor *meta_background_actor_new (void);
/**
* MetaSnippetHook:
* Temporary hack to work around Cogl not exporting CoglSnippetHook in
* the 1.0 API. Don't use.
*/
typedef enum {
/* Per pipeline vertex hooks */
META_SNIPPET_HOOK_VERTEX = 0,
META_SNIPPET_HOOK_VERTEX_TRANSFORM,
/* Per pipeline fragment hooks */
META_SNIPPET_HOOK_FRAGMENT = 2048,
/* Per layer vertex hooks */
META_SNIPPET_HOOK_TEXTURE_COORD_TRANSFORM = 4096,
/* Per layer fragment hooks */
META_SNIPPET_HOOK_LAYER_FRAGMENT = 6144,
META_SNIPPET_HOOK_TEXTURE_LOOKUP
} MetaSnippetHook;
void meta_background_actor_add_glsl_snippet (MetaBackgroundActor *actor,
MetaSnippetHook hook,
const char *declarations,
const char *code,
gboolean is_replace);
void meta_background_actor_set_uniform_float (MetaBackgroundActor *actor,
const char *uniform_name,
int n_components,
int count,
const float *uniform,
int uniform_length);
#endif /* META_BACKGROUND_ACTOR_H */ #endif /* META_BACKGROUND_ACTOR_H */

View File

@ -0,0 +1,46 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#ifndef META_BACKGROUND_GROUP_H
#define META_BACKGROUND_GROUP_H
#include <clutter/clutter.h>
/**
* MetaBackgroundGroup:
*
* This class is a subclass of ClutterGroup with special handling for
* MetaBackgroundActor when painting the group. It makes sure to only
* draw the parts of the backgrounds not occluded by opaque windows.
*
* See #MetaWindowGroup for more information behind the motivation,
* and details on implementation.
*/
#define META_TYPE_BACKGROUND_GROUP (meta_background_group_get_type ())
#define META_BACKGROUND_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_BACKGROUND_GROUP, MetaBackgroundGroup))
#define META_BACKGROUND_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_BACKGROUND_GROUP, MetaBackgroundGroupClass))
#define META_IS_BACKGROUND_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_BACKGROUND_GROUP))
#define META_IS_BACKGROUND_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_BACKGROUND_GROUP))
#define META_BACKGROUND_GROUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_BACKGROUND_GROUP, MetaBackgroundGroupClass))
typedef struct _MetaBackgroundGroup MetaBackgroundGroup;
typedef struct _MetaBackgroundGroupClass MetaBackgroundGroupClass;
typedef struct _MetaBackgroundGroupPrivate MetaBackgroundGroupPrivate;
struct _MetaBackgroundGroupClass
{
ClutterGroupClass parent_class;
};
struct _MetaBackgroundGroup
{
ClutterGroup parent;
MetaBackgroundGroupPrivate *priv;
};
GType meta_background_group_get_type (void);
ClutterActor *meta_background_group_new (void);
#endif /* META_BACKGROUND_GROUP_H */

110
src/meta/meta-background.h Normal file
View File

@ -0,0 +1,110 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* meta-background.h: CoglTexture for paintnig the system background
*
* Copyright 2013 Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#ifndef META_BACKGROUND_H
#define META_BACKGROUND_H
#include <cogl/cogl.h>
#include <clutter/clutter.h>
#include <meta/gradient.h>
#include <meta/screen.h>
#include <gsettings-desktop-schemas/gdesktop-enums.h>
/**
* MetaBackground:
*
* This class handles loading a background from file, screenshot, or
* color scheme. The resulting object can be associated with one or
* more #MetaBackgroundActor objects to handle loading the background.
*/
#define META_TYPE_BACKGROUND (meta_background_get_type ())
#define META_BACKGROUND(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_BACKGROUND, MetaBackground))
#define META_BACKGROUND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_BACKGROUND, MetaBackgroundClass))
#define META_IS_BACKGROUND(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_BACKGROUND))
#define META_IS_BACKGROUND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_BACKGROUND))
#define META_BACKGROUND_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_BACKGROUND, MetaBackgroundClass))
typedef struct _MetaBackground MetaBackground;
typedef struct _MetaBackgroundClass MetaBackgroundClass;
typedef struct _MetaBackgroundPrivate MetaBackgroundPrivate;
/**
* MetaBackgroundEffects:
* Which effects to enable on the background
*/
typedef enum
{
META_BACKGROUND_EFFECTS_NONE = 0,
META_BACKGROUND_EFFECTS_DESATURATE = 1 << 0,
META_BACKGROUND_EFFECTS_BLUR = 1 << 1,
META_BACKGROUND_EFFECTS_VIGNETTE = 1 << 2,
} MetaBackgroundEffects;
struct _MetaBackgroundClass
{
GObjectClass parent_class;
};
struct _MetaBackground
{
GObject parent;
MetaBackgroundPrivate *priv;
};
GType meta_background_get_type (void);
MetaBackground *meta_background_new (MetaScreen *screen,
int monitor,
MetaBackgroundEffects effects);
MetaBackground *meta_background_copy (MetaBackground *self,
int monitor,
MetaBackgroundEffects effects);
void meta_background_load_gradient (MetaBackground *self,
GDesktopBackgroundShading shading_direction,
ClutterColor *color,
ClutterColor *second_color);
void meta_background_load_color (MetaBackground *self,
ClutterColor *color);
void meta_background_load_still_frame (MetaBackground *self);
void meta_background_load_file_async (MetaBackground *self,
const char *filename,
GDesktopBackgroundStyle style,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean meta_background_load_file_finish (MetaBackground *self,
GAsyncResult *result,
GError **error);
const char *meta_background_get_filename (MetaBackground *self);
GDesktopBackgroundStyle meta_background_get_style (MetaBackground *self);
GDesktopBackgroundShading meta_background_get_shading (MetaBackground *self);
const ClutterColor *meta_background_get_color (MetaBackground *self);
const ClutterColor *meta_background_get_second_color (MetaBackground *self);
#endif /* META_BACKGROUND_H */