[mutter] compositor: rework how backgrounds are managed
- From: Ray Strode <halfline src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [mutter] compositor: rework how backgrounds are managed
- Date: Tue, 19 Feb 2013 23:38:18 +0000 (UTC)
commit 580feb0c85c2400de1432324fc62fc4b410aef36
Author: Ray Strode <rstrode redhat com>
Date: Wed Jan 23 15:54:41 2013 -0500
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
src/Makefile.am | 8 +
src/compositor/compositor.c | 74 +-
src/compositor/meta-background-actor-private.h | 3 +-
src/compositor/meta-background-actor.c | 596 +----------
src/compositor/meta-background-group-private.h | 11 +
src/compositor/meta-background-group.c | 92 ++
src/compositor/meta-background.c | 1324 ++++++++++++++++++++++++
src/compositor/meta-window-group.c | 14 +-
src/meta/compositor-mutter.h | 1 -
src/meta/meta-background-actor.h | 41 +-
src/meta/meta-background-group.h | 46 +
src/meta/meta-background.h | 110 ++
12 files changed, 1689 insertions(+), 631 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index 1ab326c..86b36ac 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -8,6 +8,7 @@ SUBDIRS=wm-tester tools compositor/plugins
INCLUDES= \
-DCLUTTER_ENABLE_EXPERIMENTAL_API \
-DCOGL_ENABLE_EXPERIMENTAL_API \
+ -DCOGL_ENABLE_EXPERIMENTAL_2_0_API \
$(MUTTER_CFLAGS) \
-I$(srcdir) \
-I$(srcdir)/core \
@@ -48,8 +49,11 @@ libmutter_la_SOURCES = \
compositor/cogl-utils.h \
compositor/compositor.c \
compositor/compositor-private.h \
+ compositor/meta-background.c \
compositor/meta-background-actor.c \
compositor/meta-background-actor-private.h \
+ compositor/meta-background-group.c \
+ compositor/meta-background-group-private.h \
compositor/meta-module.c \
compositor/meta-module.h \
compositor/meta-plugin.c \
@@ -71,7 +75,9 @@ libmutter_la_SOURCES = \
compositor/region-utils.c \
compositor/region-utils.h \
meta/compositor.h \
+ meta/meta-background.h \
meta/meta-background-actor.h \
+ meta/meta-background-group.h \
meta/meta-plugin.h \
meta/meta-shadow-factory.h \
meta/meta-window-actor.h \
@@ -175,6 +181,8 @@ libmutterinclude_base_headers = \
meta/keybindings.h \
meta/main.h \
meta/meta-background-actor.h \
+ meta/meta-background-group.h \
+ meta/meta-background.h \
meta/meta-plugin.h \
meta/meta-shaped-texture.h \
meta/meta-shadow-factory.h \
diff --git a/src/compositor/compositor.c b/src/compositor/compositor.c
index 10c86ea..adf71d9 100644
--- a/src/compositor/compositor.c
+++ b/src/compositor/compositor.c
@@ -13,10 +13,11 @@
#include "xprops.h"
#include <meta/prefs.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-window-actor-private.h"
#include "meta-window-group.h"
-#include "meta-background-actor-private.h"
#include "window-private.h" /* to check window->hidden */
#include "display-private.h" /* for meta_display_lookup_x_window() */
#include <X11/extensions/shape.h>
@@ -117,21 +118,6 @@ process_property_notify (MetaCompositor *compositor,
{
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)
return;
@@ -255,27 +241,6 @@ meta_get_top_window_group_for_screen (MetaScreen *screen)
}
/**
- * 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:
* @screen: a #MetaScreen
*
@@ -606,14 +571,8 @@ meta_compositor_manage_screen (MetaCompositor *compositor,
info->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 ();
- clutter_container_add (CLUTTER_CONTAINER (info->window_group),
- info->background_actor,
- NULL);
-
clutter_container_add (CLUTTER_CONTAINER (info->stage),
info->window_group,
info->top_window_group,
@@ -1066,6 +1025,7 @@ sync_actor_stacking (MetaCompScreen *info)
GList *expected_window_node;
GList *tmp;
GList *old;
+ GList *backgrounds;
gboolean has_windows;
gboolean reordered;
@@ -1083,15 +1043,19 @@ sync_actor_stacking (MetaCompScreen *info)
* (we really need extra API to make that reliable.)
*/
- /* First we check if the background is at the bottom. Then
- * we check if the window actors are in the correct sequence */
+ /* First we collect a list of all backgrounds, and check if they're at the
+ * bottom. Then we check if the window actors are in the correct sequence */
+ backgrounds = NULL;
expected_window_node = info->windows;
for (old = children; old != NULL; old = old->next)
{
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)
reordered = TRUE;
}
@@ -1109,7 +1073,10 @@ sync_actor_stacking (MetaCompScreen *info)
g_list_free (children);
if (!reordered)
- return;
+ {
+ g_list_free (backgrounds);
+ return;
+ }
/* reorder the actors by lowering them in turn to the bottom of the stack.
* 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 (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
@@ -1276,8 +1252,6 @@ meta_compositor_sync_screen_size (MetaCompositor *compositor,
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_screen_get_screen_number (screen),
width, height);
diff --git a/src/compositor/meta-background-actor-private.h b/src/compositor/meta-background-actor-private.h
index f5d656e..aeaf77f 100644
--- a/src/compositor/meta-background-actor-private.h
+++ b/src/compositor/meta-background-actor-private.h
@@ -9,7 +9,6 @@
void meta_background_actor_set_visible_region (MetaBackgroundActor *self,
cairo_region_t *visible_region);
-void meta_background_actor_update (MetaScreen *screen);
-void meta_background_actor_screen_size_changed (MetaScreen *screen);
+cairo_region_t *meta_background_actor_get_visible_region (MetaBackgroundActor *self);
#endif /* META_BACKGROUND_ACTOR_PRIVATE_H */
diff --git a/src/compositor/meta-background-actor.c b/src/compositor/meta-background-actor.c
index 41306b1..5060919 100644
--- a/src/compositor/meta-background-actor.c
+++ b/src/compositor/meta-background-actor.c
@@ -34,223 +34,23 @@
#include "cogl-utils.h"
#include "compositor-private.h"
#include <meta/errors.h>
+#include <meta/meta-background.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
{
- MetaScreenBackground *background;
- CoglPipeline *pipeline;
-
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);
-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
meta_background_actor_dispose (GObject *object)
{
MetaBackgroundActor *self = META_BACKGROUND_ACTOR (object);
- MetaBackgroundActorPrivate *priv = self->priv;
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);
}
@@ -260,11 +60,15 @@ meta_background_actor_get_preferred_width (ClutterActor *actor,
gfloat *min_width_p,
gfloat *natural_width_p)
{
- MetaBackgroundActor *self = META_BACKGROUND_ACTOR (actor);
- MetaBackgroundActorPrivate *priv = self->priv;
- int width, height;
+ ClutterContent *content;
+ gfloat width;
+
+ content = clutter_actor_get_content (actor);
- meta_screen_get_size (priv->background->screen, &width, &height);
+ if (content)
+ clutter_content_get_preferred_size (content, &width, NULL);
+ else
+ width = 0;
if (min_width_p)
*min_width_p = width;
@@ -279,11 +83,15 @@ meta_background_actor_get_preferred_height (ClutterActor *actor,
gfloat *natural_height_p)
{
- MetaBackgroundActor *self = META_BACKGROUND_ACTOR (actor);
- MetaBackgroundActorPrivate *priv = self->priv;
- int width, height;
+ ClutterContent *content;
+ gfloat 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)
*min_height_p = height;
@@ -291,64 +99,19 @@ meta_background_actor_get_preferred_height (ClutterActor *actor,
*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
meta_background_actor_get_paint_volume (ClutterActor *actor,
ClutterPaintVolume *volume)
{
- MetaBackgroundActor *self = META_BACKGROUND_ACTOR (actor);
- MetaBackgroundActorPrivate *priv = self->priv;
- int width, height;
+ ClutterContent *content;
+ gfloat width, height;
+
+ content = clutter_actor_get_content (actor);
- meta_screen_get_size (priv->background->screen, &width, &height);
+ if (!content)
+ return FALSE;
+
+ clutter_content_get_preferred_size (content, &width, &height);
clutter_paint_volume_set_width (volume, width);
clutter_paint_volume_set_height (volume, height);
@@ -357,215 +120,48 @@ meta_background_actor_get_paint_volume (ClutterActor *actor,
}
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
meta_background_actor_class_init (MetaBackgroundActorClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
- GParamSpec *pspec;
g_type_class_add_private (klass, sizeof (MetaBackgroundActorPrivate));
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_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;
-
- /**
- * 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
meta_background_actor_init (MetaBackgroundActor *self)
{
- MetaBackgroundActorPrivate *priv;
-
- priv = self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
- META_TYPE_BACKGROUND_ACTOR,
- MetaBackgroundActorPrivate);
- priv->dim_factor = 1.0;
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ META_TYPE_BACKGROUND_ACTOR,
+ MetaBackgroundActorPrivate);
}
/**
* 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
*/
ClutterActor *
-meta_background_actor_new_for_screen (MetaScreen *screen)
+meta_background_actor_new (void)
{
MetaBackgroundActor *self;
- MetaBackgroundActorPrivate *priv;
-
- g_return_val_if_fail (META_IS_SCREEN (screen), 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);
}
/**
- * 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:
* @self: a #MetaBackgroundActor
* @visible_region: (allow-none): the area of the actor (in allocate-relative
@@ -584,120 +180,44 @@ meta_background_actor_set_visible_region (MetaBackgroundActor *self,
priv = self->priv;
- if (priv->visible_region)
- {
- cairo_region_destroy (priv->visible_region);
- priv->visible_region = NULL;
- }
+ g_clear_pointer (&priv->visible_region,
+ (GDestroyNotify)
+ cairo_region_destroy);
if (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:
- * @screen: a #MetaScreen
- *
- * Called by the compositor when the size of the #MetaScreen changes
- */
-void
-meta_background_actor_screen_size_changed (MetaScreen *screen)
-{
- MetaScreenBackground *background = meta_screen_background_get (screen);
- GSList *l;
-
- update_wrap_mode (background);
-
- for (l = background->actors; l; l = l->next)
- clutter_actor_queue_relayout (l->data);
+ priv->visible_region = cairo_region_copy (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
+ * meta_background_actor_get_visible_region:
+ * @self: a #MetaBackgroundActor
*
- * Adds a GLSL snippet to the pipeline used for drawing the background.
- * See #CoglSnippet for details.
+ * Return value (transfer full): a #cairo_region_t that represents the part of
+ * the background not obscured by other #MetaBackgroundActor or
+ * #MetaWindowActor objects.
*/
-void
-meta_background_actor_add_glsl_snippet (MetaBackgroundActor *actor,
- MetaSnippetHook hook,
- const char *declarations,
- const char *code,
- gboolean is_replace)
+cairo_region_t *
+meta_background_actor_get_visible_region (MetaBackgroundActor *self)
{
- 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);
+ MetaBackgroundActorPrivate *priv = self->priv;
+ ClutterActorBox content_box;
+ cairo_rectangle_int_t content_area = { 0 };
+ cairo_region_t *visible_region;
- cogl_object_unref (snippet);
-}
+ g_return_val_if_fail (META_IS_BACKGROUND_ACTOR (self), NULL);
-/**
- * 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().
- */
+ if (!priv->visible_region)
+ return NULL;
-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;
+ clutter_actor_get_content_box (CLUTTER_ACTOR (self), &content_box);
- g_return_if_fail (META_IS_BACKGROUND_ACTOR (actor));
- g_return_if_fail (uniform_length == n_components * count);
+ 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;
- priv = actor->priv;
+ visible_region = cairo_region_create_rectangle (&content_area);
+ cairo_region_intersect (visible_region, priv->visible_region);
- cogl_pipeline_set_uniform_float (priv->pipeline,
- cogl_pipeline_get_uniform_location (priv->pipeline,
- uniform_name),
- n_components, count, uniform);
+ return visible_region;
}
-
diff --git a/src/compositor/meta-background-group-private.h b/src/compositor/meta-background-group-private.h
new file mode 100644
index 0000000..5eca268
--- /dev/null
+++ b/src/compositor/meta-background-group-private.h
@@ -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 */
diff --git a/src/compositor/meta-background-group.c b/src/compositor/meta-background-group.c
new file mode 100644
index 0000000..b26ed6a
--- /dev/null
+++ b/src/compositor/meta-background-group.c
@@ -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);
+}
diff --git a/src/compositor/meta-background.c b/src/compositor/meta-background.c
new file mode 100644
index 0000000..9a95fc0
--- /dev/null
+++ b/src/compositor/meta-background.c
@@ -0,0 +1,1324 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * meta-background.c: CoglTexture for painting 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.
+ */
+
+#include <config.h>
+
+#include <cogl/cogl-texture-pixmap-x11.h>
+
+#include <clutter/clutter.h>
+
+#include "cogl-utils.h"
+#include "compositor-private.h"
+#include "mutter-enum-types.h"
+#include <meta/errors.h>
+#include <meta/meta-background.h>
+#include "meta-background-actor-private.h"
+
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+#define TEXTURE_FORMAT COGL_PIXEL_FORMAT_BGRA_8888_PRE
+#else
+#define TEXTURE_FORMAT COGL_PIXEL_FORMAT_ARGB_8888_PRE
+#endif
+
+#define TEXTURE_LOOKUP_SHADER_DECLARATIONS \
+"uniform vec2 pixel_step;\n" \
+"vec4 apply_blur(in sampler2D texture, in vec2 coordinates) {\n" \
+" vec4 texel;\n" \
+" texel = texture2D(texture, coordinates.st);\n" \
+" texel += texture2D(texture, coordinates.st + pixel_step * vec2(-1.0, -1.0));\n"\
+" texel += texture2D(texture, coordinates.st + pixel_step * vec2( 0.0, -1.0));\n"\
+" texel += texture2D(texture, coordinates.st + pixel_step * vec2(+1.0, -1.0));\n"\
+" texel += texture2D(texture, coordinates.st + pixel_step * vec2(-1.0, 0.0));\n"\
+" texel += texture2D(texture, coordinates.st + pixel_step * vec2(+1.0, 0.0));\n"\
+" texel += texture2D(texture, coordinates.st + pixel_step * vec2(-1.0, +1.0));\n"\
+" texel += texture2D(texture, coordinates.st + pixel_step * vec2( 0.0, +1.0));\n"\
+" texel += texture2D(texture, coordinates.st + pixel_step * vec2(+1.0, +1.0));\n"\
+" texel /= 9.0;\n" \
+" return texel;\n" \
+"}\n" \
+"uniform float saturation;\n" \
+"vec3 desaturate(const vec3 color)\n" \
+"{\n" \
+" const vec3 gray_conv = vec3(0.299, 0.587, 0.114);\n" \
+" vec3 gray = vec3(dot(gray_conv, color));\n" \
+" return vec3(mix(color.rgb, gray, 1.0 - saturation));\n" \
+"}\n" \
+
+#define DESATURATE_CODE \
+"cogl_texel.rgb = desaturate(cogl_texel.rgb);\n"
+
+#define BLUR_CODE \
+"cogl_texel = apply_blur(cogl_sampler, cogl_tex_coord.st);\n"
+
+#define FRAGMENT_SHADER_DECLARATIONS \
+"uniform float brightness;\n" \
+"uniform float vignette_sharpness;\n" \
+
+#define VIGNETTE_CODE \
+"float unit_length = 0.5;\n" \
+"vec2 center = vec2(unit_length, unit_length);\n" \
+"vec2 position = cogl_tex_coord_in[0].xy - center;\n" \
+"float t = min(length(position), unit_length) / unit_length;\n" \
+"float pixel_brightness = mix(1.0, 1.0 - vignette_sharpness, t);\n" \
+"cogl_color_out.rgb = cogl_color_out.rgb * pixel_brightness * brightness;\n"
+
+/* We allow creating multiple MetaBackgrounds for the same monitor to
+ * allow different rendering options to be set for different copies.
+ * But we want to share the same underlying CoglTextures for efficiency and
+ * to avoid driver bugs that might occur if we created multiple CoglTexturePixmaps
+ * for the same pixmap.
+ *
+ * This object provides a ClutterContent object to assist in sharing between actors.
+ */
+typedef struct _MetaBackgroundPrivate MetaBackgroundPrivate;
+
+struct _MetaBackgroundPrivate
+{
+ MetaScreen *screen;
+ CoglTexture *texture;
+ CoglPipeline *pipeline;
+ int monitor;
+
+ MetaBackgroundEffects effects;
+
+ GDesktopBackgroundStyle style;
+ GDesktopBackgroundShading shading_direction;
+ ClutterColor color;
+ ClutterColor second_color;
+
+ char *filename;
+
+ float brightness;
+ float vignette_sharpness;
+ float saturation;
+};
+
+enum
+{
+ PROP_META_SCREEN = 1,
+ PROP_MONITOR,
+ PROP_EFFECTS,
+ PROP_BRIGHTNESS,
+ PROP_VIGNETTE_SHARPNESS,
+ PROP_SATURATION
+};
+
+static void clutter_content_iface_init (ClutterContentIface *iface);
+static void unset_texture (MetaBackground *self);
+
+G_DEFINE_TYPE_WITH_CODE (MetaBackground, meta_background, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTENT,
+ clutter_content_iface_init))
+
+
+static gboolean
+meta_background_get_preferred_size (ClutterContent *content,
+ gfloat *width,
+ gfloat *height)
+{
+ MetaBackgroundPrivate *priv = META_BACKGROUND (content)->priv;
+ MetaRectangle monitor_geometry;
+
+ if (priv->texture == NULL)
+ return FALSE;
+
+ meta_screen_get_monitor_geometry (priv->screen, priv->monitor, &monitor_geometry);
+
+ if (width != NULL)
+ *width = monitor_geometry.width;
+
+ if (height != NULL)
+ *height = monitor_geometry.height;
+
+ return TRUE;
+}
+
+static void
+get_texture_area_and_scale (MetaBackground *self,
+ ClutterActorBox *actor_box,
+ cairo_rectangle_int_t *texture_area,
+ float *texture_x_scale,
+ float *texture_y_scale)
+{
+ MetaBackgroundPrivate *priv = self->priv;
+ MetaRectangle monitor_geometry;
+ cairo_rectangle_int_t actor_pixel_rect;
+ cairo_rectangle_int_t image_area;
+ int screen_width, screen_height;
+ float texture_width, texture_height;
+ float actor_x_scale, actor_y_scale;
+ float monitor_x_scale, monitor_y_scale;
+
+ meta_screen_get_monitor_geometry (priv->screen, priv->monitor, &monitor_geometry);
+
+ actor_pixel_rect.x = actor_box->x1;
+ actor_pixel_rect.y = actor_box->y1;
+ actor_pixel_rect.width = actor_box->x2 - actor_box->x1;
+ actor_pixel_rect.height = actor_box->y2 - actor_box->y1;
+
+ texture_width = cogl_texture_get_width (priv->texture);
+ actor_x_scale = (1.0 * actor_pixel_rect.width / monitor_geometry.width);
+
+ texture_height = cogl_texture_get_height (priv->texture);
+ actor_y_scale = (1.0 * actor_pixel_rect.height / monitor_geometry.height);
+
+ switch (priv->style)
+ {
+ case G_DESKTOP_BACKGROUND_STYLE_STRETCHED:
+ default:
+ /* paint region is whole actor, and the texture
+ * is scaled disproportionately to fit the actor
+ */
+ *texture_area = actor_pixel_rect;
+ *texture_x_scale = 1.0 / actor_pixel_rect.width;
+ *texture_y_scale = 1.0 / actor_pixel_rect.height;
+ break;
+ case G_DESKTOP_BACKGROUND_STYLE_WALLPAPER:
+ /* paint region is whole actor, and the texture
+ * is left unscaled
+ */
+ image_area = actor_pixel_rect;
+ *texture_x_scale = 1.0 / texture_width;
+ *texture_y_scale = 1.0 / texture_height;
+
+ *texture_area = image_area;
+ break;
+ case G_DESKTOP_BACKGROUND_STYLE_CENTERED:
+ /* paint region is the original image size centered in the actor,
+ * and the texture is scaled to the original image size */
+ image_area.width = texture_width;
+ image_area.height = texture_height;
+ image_area.x = actor_pixel_rect.x + actor_pixel_rect.width / 2 - image_area.width / 2;
+ image_area.y = actor_pixel_rect.y + actor_pixel_rect.height / 2 - image_area.height / 2;
+
+ *texture_area = image_area;
+ *texture_x_scale = 1.0 / texture_width;
+ *texture_y_scale = 1.0 / texture_height;
+ break;
+ case G_DESKTOP_BACKGROUND_STYLE_SCALED:
+ case G_DESKTOP_BACKGROUND_STYLE_ZOOM:
+ /* paint region is the actor size in one dimension, and centered and
+ * scaled by proportional amount in the other dimension.
+ *
+ * SCALED forces the centered dimension to fit on screen.
+ * ZOOM forces the centered dimension to grow off screen
+ */
+ monitor_x_scale = monitor_geometry.width / texture_width;
+ monitor_y_scale = monitor_geometry.height / texture_height;
+
+ if ((priv->style == G_DESKTOP_BACKGROUND_STYLE_SCALED &&
+ (monitor_x_scale < monitor_y_scale)) ||
+ (priv->style == G_DESKTOP_BACKGROUND_STYLE_ZOOM &&
+ (monitor_x_scale > monitor_y_scale)))
+ {
+ /* Fill image to exactly fit actor horizontally */
+ image_area.width = actor_pixel_rect.width;
+ image_area.height = texture_height * monitor_x_scale * actor_y_scale;
+
+ /* Position image centered vertically in actor */
+ image_area.x = actor_pixel_rect.x;
+ image_area.y = actor_pixel_rect.y + actor_pixel_rect.height / 2 - image_area.height / 2;
+ }
+ else
+ {
+ /* Scale image to exactly fit actor vertically */
+ image_area.width = texture_width * monitor_y_scale * actor_x_scale;
+ image_area.height = actor_pixel_rect.height;
+
+ /* Position image centered horizontally in actor */
+ image_area.x = actor_pixel_rect.x + actor_pixel_rect.width / 2 - image_area.width / 2;
+ image_area.y = actor_pixel_rect.y;
+ }
+
+ *texture_area = image_area;
+ *texture_x_scale = 1.0 / image_area.width;
+ *texture_y_scale = 1.0 / image_area.height;
+ break;
+
+ case G_DESKTOP_BACKGROUND_STYLE_SPANNED:
+ {
+ /* paint region is the union of all monitors, with the origin
+ * of the region set to align with monitor associated with the background.
+ */
+ meta_screen_get_size (priv->screen, &screen_width, &screen_height);
+
+ /* unclipped texture area is whole screen */
+ image_area.width = screen_width * actor_x_scale;
+ image_area.height = screen_height * actor_y_scale;
+
+ /* But make (0,0) line up with the appropriate monitor */
+ image_area.x = -monitor_geometry.x * actor_x_scale;
+ image_area.y = -monitor_geometry.y * actor_y_scale;
+
+ *texture_area = image_area;
+ *texture_x_scale = 1.0 / image_area.width;
+ *texture_y_scale = 1.0 / image_area.height;
+ break;
+ }
+ }
+}
+
+static CoglPipelineWrapMode
+get_wrap_mode (MetaBackground *self)
+{
+ MetaBackgroundPrivate *priv = self->priv;
+ switch (priv->style)
+ {
+ case G_DESKTOP_BACKGROUND_STYLE_WALLPAPER:
+ return COGL_PIPELINE_WRAP_MODE_REPEAT;
+ case G_DESKTOP_BACKGROUND_STYLE_NONE:
+ case G_DESKTOP_BACKGROUND_STYLE_STRETCHED:
+ case G_DESKTOP_BACKGROUND_STYLE_CENTERED:
+ case G_DESKTOP_BACKGROUND_STYLE_SCALED:
+ case G_DESKTOP_BACKGROUND_STYLE_ZOOM:
+ case G_DESKTOP_BACKGROUND_STYLE_SPANNED:
+ default:
+ return COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE;
+ }
+}
+
+static ClutterPaintNode *
+meta_background_paint_node_new (MetaBackground *self,
+ ClutterActor *actor)
+{
+ MetaBackgroundPrivate *priv = self->priv;
+ ClutterPaintNode *node;
+ guint8 opacity;
+ guint8 color_component;
+
+ opacity = clutter_actor_get_paint_opacity (actor);
+ color_component = (guint8) (0.5 + opacity * priv->brightness);
+
+ cogl_pipeline_set_color4ub (priv->pipeline,
+ color_component,
+ color_component,
+ color_component,
+ opacity);
+
+ node = clutter_pipeline_node_new (priv->pipeline);
+
+ return node;
+}
+
+static void
+clip_region_to_actor_box (cairo_region_t *region,
+ ClutterActorBox *actor_box)
+{
+ cairo_rectangle_int_t clip_rect;
+
+ clip_rect.x = actor_box->x1;
+ clip_rect.y = actor_box->y1;
+ clip_rect.width = actor_box->x2 - actor_box->x1;
+ clip_rect.height = actor_box->y2 - actor_box->y1;
+
+ cairo_region_intersect_rectangle (region, &clip_rect);
+}
+
+static void
+set_blur_parameters (MetaBackground *self,
+ ClutterActorBox *actor_box)
+{
+ MetaBackgroundPrivate *priv = self->priv;
+ float pixel_step[2];
+
+ if (!(priv->effects & META_BACKGROUND_EFFECTS_BLUR))
+ return;
+
+ pixel_step[0] = 1.0 / (actor_box->x2 - actor_box->x1);
+ pixel_step[1] = 1.0 / (actor_box->y2 - actor_box->y1);
+
+ cogl_pipeline_set_uniform_float (priv->pipeline,
+ cogl_pipeline_get_uniform_location (priv->pipeline,
+ "pixel_step"),
+ 2, 1, pixel_step);
+}
+
+static void
+meta_background_paint_content (ClutterContent *content,
+ ClutterActor *actor,
+ ClutterPaintNode *root)
+{
+ MetaBackground *self = META_BACKGROUND (content);
+ MetaBackgroundPrivate *priv = self->priv;
+ ClutterPaintNode *node;
+ ClutterActorBox actor_box;
+ cairo_rectangle_int_t texture_area;
+ cairo_region_t *paintable_region = NULL;
+ int n_texture_subareas;
+ int i;
+ float texture_x_scale, texture_y_scale;
+ float tx1 = 0.0, ty1 = 0.0, tx2 = 1.0, ty2 = 1.0;
+
+ if (priv->texture == NULL)
+ return;
+
+ node = meta_background_paint_node_new (self, actor);
+
+ clutter_actor_get_content_box (actor, &actor_box);
+
+ set_blur_parameters (self, &actor_box);
+
+ /* First figure out where on the monitor the texture is supposed to be painted.
+ * If the actor is not the size of the monitor, this function makes sure to scale
+ * everything down to fit in the actor.
+ */
+ get_texture_area_and_scale (self,
+ &actor_box,
+ &texture_area,
+ &texture_x_scale,
+ &texture_y_scale);
+
+ /* Now figure out what to actually paint. We start by clipping the texture area to
+ * the actor's bounds.
+ */
+ paintable_region = cairo_region_create_rectangle (&texture_area);
+
+ clip_region_to_actor_box (paintable_region, &actor_box);
+
+ /* And then cut out any parts occluded by window actors
+ */
+ if (META_IS_BACKGROUND_ACTOR (actor))
+ {
+ cairo_region_t *visible_region;
+ visible_region = meta_background_actor_get_visible_region (META_BACKGROUND_ACTOR (actor));
+
+ if (visible_region != NULL)
+ {
+ cairo_region_intersect (paintable_region, visible_region);
+ cairo_region_destroy (visible_region);
+ }
+ }
+
+ /* Finally, split the paintable region up into distinct areas
+ * and paint each area one by one
+ */
+ n_texture_subareas = cairo_region_num_rectangles (paintable_region);
+ for (i = 0; i < n_texture_subareas; i++)
+ {
+ cairo_rectangle_int_t texture_subarea;
+ ClutterActorBox texture_rectangle;
+
+ cairo_region_get_rectangle (paintable_region, i, &texture_subarea);
+
+ tx1 = (texture_subarea.x - texture_area.x) * texture_x_scale;
+ ty1 = (texture_subarea.y - texture_area.y) * texture_y_scale;
+ tx2 = (texture_subarea.x + texture_subarea.width - texture_area.x) * texture_x_scale;
+ ty2 = (texture_subarea.y + texture_subarea.height - texture_area.y) * texture_y_scale;
+ texture_rectangle.x1 = texture_subarea.x;
+ texture_rectangle.y1 = texture_subarea.y;
+ texture_rectangle.x2 = texture_subarea.x + texture_subarea.width;
+ texture_rectangle.y2 = texture_subarea.y + texture_subarea.height;
+
+ clutter_paint_node_add_texture_rectangle (node, &texture_rectangle, tx1, ty1, tx2, ty2);
+ }
+ cairo_region_destroy (paintable_region);
+
+ clutter_paint_node_add_child (root, node);
+ clutter_paint_node_unref (node);
+}
+
+static void
+clutter_content_iface_init (ClutterContentIface *iface)
+{
+ iface->get_preferred_size = meta_background_get_preferred_size;
+ iface->paint_content = meta_background_paint_content;
+}
+
+static void
+meta_background_dispose (GObject *object)
+{
+ MetaBackground *self = META_BACKGROUND (object);
+ MetaBackgroundPrivate *priv = self->priv;
+
+ unset_texture (self);
+
+ g_clear_pointer (&priv->pipeline,
+ (GDestroyNotify)
+ cogl_object_unref);
+
+ G_OBJECT_CLASS (meta_background_parent_class)->dispose (object);
+}
+
+static void
+ensure_pipeline (MetaBackground *self)
+{
+ if (self->priv->pipeline == NULL)
+ self->priv->pipeline = COGL_PIPELINE (meta_create_texture_material (NULL));
+}
+
+static void
+set_brightness (MetaBackground *self,
+ gfloat brightness)
+{
+ MetaBackgroundPrivate *priv = self->priv;
+
+ if (priv->brightness == brightness)
+ return;
+
+ priv->brightness = brightness;
+
+ if (priv->effects & META_BACKGROUND_EFFECTS_VIGNETTE)
+ {
+ ensure_pipeline (self);
+ cogl_pipeline_set_uniform_1f (priv->pipeline,
+ cogl_pipeline_get_uniform_location (priv->pipeline,
+ "brightness"),
+ priv->brightness);
+ }
+
+ clutter_content_invalidate (CLUTTER_CONTENT (self));
+
+ g_object_notify (G_OBJECT (self), "brightness");
+}
+
+static void
+set_vignette_sharpness (MetaBackground *self,
+ gfloat sharpness)
+{
+ MetaBackgroundPrivate *priv = self->priv;
+
+ if (priv->vignette_sharpness == sharpness)
+ return;
+
+ priv->vignette_sharpness = sharpness;
+
+ if (priv->effects & META_BACKGROUND_EFFECTS_VIGNETTE)
+ {
+ ensure_pipeline (self);
+ cogl_pipeline_set_uniform_1f (priv->pipeline,
+ cogl_pipeline_get_uniform_location (priv->pipeline,
+ "vignette_sharpness"),
+ priv->vignette_sharpness);
+ }
+
+ clutter_content_invalidate (CLUTTER_CONTENT (self));
+
+ g_object_notify (G_OBJECT (self), "vignette-sharpness");
+}
+
+static void
+set_saturation (MetaBackground *self,
+ gfloat saturation)
+{
+ MetaBackgroundPrivate *priv = self->priv;
+
+ if (priv->saturation == saturation)
+ return;
+
+ priv->saturation = saturation;
+
+ ensure_pipeline (self);
+
+ cogl_pipeline_set_uniform_1f (priv->pipeline,
+ cogl_pipeline_get_uniform_location (priv->pipeline,
+ "saturation"),
+ priv->saturation);
+
+
+ clutter_content_invalidate (CLUTTER_CONTENT (self));
+
+ g_object_notify (G_OBJECT (self), "saturation");
+}
+
+static void
+add_texture_lookup_shader (MetaBackground *self)
+{
+ MetaBackgroundPrivate *priv = self->priv;
+ CoglSnippet *snippet;
+ const char *code;
+
+ ensure_pipeline (self);
+
+ snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_TEXTURE_LOOKUP,
+ TEXTURE_LOOKUP_SHADER_DECLARATIONS,
+ NULL);
+ if ((priv->effects & META_BACKGROUND_EFFECTS_BLUR) &&
+ (priv->effects & META_BACKGROUND_EFFECTS_DESATURATE))
+ code = BLUR_CODE "\n" DESATURATE_CODE;
+ else if (priv->effects & META_BACKGROUND_EFFECTS_BLUR)
+ code = BLUR_CODE;
+ else if (priv->effects & META_BACKGROUND_EFFECTS_DESATURATE)
+ code = DESATURATE_CODE;
+
+ cogl_snippet_set_replace (snippet, code);
+ cogl_pipeline_add_layer_snippet (priv->pipeline, 0, snippet);
+ cogl_object_unref (snippet);
+
+ if (priv->effects & META_BACKGROUND_EFFECTS_DESATURATE)
+ cogl_pipeline_set_uniform_1f (priv->pipeline,
+ cogl_pipeline_get_uniform_location (priv->pipeline,
+ "saturation"),
+ priv->saturation);
+}
+
+static void
+add_vignette (MetaBackground *self)
+{
+ MetaBackgroundPrivate *priv = self->priv;
+ CoglSnippet *snippet;
+
+ ensure_pipeline (self);
+
+ snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, FRAGMENT_SHADER_DECLARATIONS, VIGNETTE_CODE);
+ cogl_pipeline_add_snippet (priv->pipeline, snippet);
+ cogl_object_unref (snippet);
+
+ cogl_pipeline_set_uniform_1f (priv->pipeline,
+ cogl_pipeline_get_uniform_location (priv->pipeline,
+ "brightness"),
+ priv->brightness);
+
+ cogl_pipeline_set_uniform_1f (priv->pipeline,
+ cogl_pipeline_get_uniform_location (priv->pipeline,
+ "vignette_sharpness"),
+ priv->vignette_sharpness);
+}
+
+static void
+set_effects (MetaBackground *self,
+ MetaBackgroundEffects effects)
+{
+ MetaBackgroundPrivate *priv = self->priv;
+
+ priv->effects = effects;
+
+ if ((priv->effects & META_BACKGROUND_EFFECTS_BLUR) ||
+ (priv->effects & META_BACKGROUND_EFFECTS_DESATURATE))
+ add_texture_lookup_shader (self);
+
+ if ((priv->effects & META_BACKGROUND_EFFECTS_VIGNETTE))
+ add_vignette (self);
+
+ clutter_content_invalidate (CLUTTER_CONTENT (self));
+}
+
+static void
+meta_background_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ MetaBackground *self = META_BACKGROUND (object);
+ MetaBackgroundPrivate *priv = self->priv;
+
+ switch (prop_id)
+ {
+ case PROP_META_SCREEN:
+ priv->screen = g_value_get_object (value);
+ break;
+ case PROP_MONITOR:
+ priv->monitor = g_value_get_int (value);
+ break;
+ case PROP_EFFECTS:
+ set_effects (self, g_value_get_flags (value));
+ break;
+ case PROP_BRIGHTNESS:
+ set_brightness (self, g_value_get_float (value));
+ break;
+ case PROP_VIGNETTE_SHARPNESS:
+ set_vignette_sharpness (self, g_value_get_float (value));
+ break;
+ case PROP_SATURATION:
+ set_saturation (self, g_value_get_float (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+meta_background_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ MetaBackgroundPrivate *priv = META_BACKGROUND (object)->priv;
+
+ switch (prop_id)
+ {
+ case PROP_META_SCREEN:
+ g_value_set_object (value, priv->screen);
+ break;
+ case PROP_MONITOR:
+ g_value_set_int (value, priv->monitor);
+ break;
+ case PROP_EFFECTS:
+ g_value_set_flags (value, priv->effects);
+ break;
+ case PROP_BRIGHTNESS:
+ g_value_set_float (value, priv->brightness);
+ break;
+ case PROP_VIGNETTE_SHARPNESS:
+ g_value_set_float (value, priv->vignette_sharpness);
+ break;
+ case PROP_SATURATION:
+ g_value_set_float (value, priv->saturation);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+meta_background_class_init (MetaBackgroundClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GParamSpec *param_spec;
+
+ g_type_class_add_private (klass, sizeof (MetaBackgroundPrivate));
+
+ object_class->dispose = meta_background_dispose;
+ object_class->set_property = meta_background_set_property;
+ object_class->get_property = meta_background_get_property;
+
+ param_spec = g_param_spec_object ("meta-screen",
+ "MetaScreen",
+ "MetaScreen",
+ META_TYPE_SCREEN,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+
+ g_object_class_install_property (object_class,
+ PROP_META_SCREEN,
+ param_spec);
+
+ param_spec = g_param_spec_int ("monitor",
+ "monitor",
+ "monitor",
+ 0, G_MAXINT, 0,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+
+ g_object_class_install_property (object_class,
+ PROP_MONITOR,
+ param_spec);
+
+ param_spec = g_param_spec_float ("brightness",
+ "brightness",
+ "Values less than 1.0 dim background",
+ 0.0, 1.0,
+ 1.0,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+ g_object_class_install_property (object_class, PROP_BRIGHTNESS, param_spec);
+
+ param_spec = g_param_spec_float ("vignette-sharpness",
+ "vignette-sharpness",
+ "How obvious the vignette fringe is",
+ 0.0, 1.0,
+ 0.7,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+ g_object_class_install_property (object_class, PROP_VIGNETTE_SHARPNESS, param_spec);
+
+ param_spec = g_param_spec_float ("saturation",
+ "saturation",
+ "Values less than 1.0 grays background",
+ 0.0, 1.0,
+ 1.0,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+ g_object_class_install_property (object_class, PROP_SATURATION, param_spec);
+
+ param_spec = g_param_spec_flags ("effects",
+ "Effects",
+ "Set to alter saturation, to blur, etc",
+ meta_background_effects_get_type (),
+ META_BACKGROUND_EFFECTS_NONE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+ g_object_class_install_property (object_class, PROP_EFFECTS, param_spec);
+}
+
+static void
+meta_background_init (MetaBackground *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ META_TYPE_BACKGROUND,
+ MetaBackgroundPrivate);
+}
+
+static void
+unset_texture (MetaBackground *self)
+{
+ MetaBackgroundPrivate *priv = self->priv;
+ cogl_pipeline_set_layer_texture (priv->pipeline, 0, NULL);
+
+ g_clear_pointer (&priv->texture,
+ (GDestroyNotify)
+ cogl_handle_unref);
+}
+
+static void
+set_texture (MetaBackground *self,
+ CoglTexture *texture)
+{
+ MetaBackgroundPrivate *priv = self->priv;
+
+ priv->texture = texture;
+ cogl_pipeline_set_layer_texture (priv->pipeline, 0, priv->texture);
+}
+
+static void
+set_style (MetaBackground *self,
+ GDesktopBackgroundStyle style)
+{
+ MetaBackgroundPrivate *priv = self->priv;
+ CoglPipelineWrapMode wrap_mode;
+
+ priv->style = style;
+
+ wrap_mode = get_wrap_mode (self);
+ cogl_pipeline_set_layer_wrap_mode (priv->pipeline, 0, wrap_mode);
+}
+
+static void
+set_filename (MetaBackground *self,
+ const char *filename)
+{
+ MetaBackgroundPrivate *priv = self->priv;
+
+ g_free (priv->filename);
+ priv->filename = g_strdup (filename);
+}
+
+static Pixmap
+get_still_frame_for_monitor (MetaScreen *screen,
+ int monitor)
+{
+ MetaDisplay *display = meta_screen_get_display (screen);
+ Display *xdisplay = meta_display_get_xdisplay (display);
+ Window xroot = meta_screen_get_xroot (screen);
+ Pixmap pixmap;
+ GC gc;
+ XGCValues values;
+ MetaRectangle geometry;
+ int depth;
+
+ meta_screen_get_monitor_geometry (screen, monitor, &geometry);
+
+ depth = DefaultDepth (xdisplay, meta_screen_get_screen_number (screen));
+
+ pixmap = XCreatePixmap (xdisplay,
+ xroot,
+ geometry.width, geometry.height, depth);
+
+ values.function = GXcopy;
+ values.plane_mask = AllPlanes;
+ values.fill_style = FillSolid;
+ values.subwindow_mode = IncludeInferiors;
+
+ gc = XCreateGC (xdisplay,
+ xroot,
+ GCFunction | GCPlaneMask | GCFillStyle | GCSubwindowMode,
+ &values);
+
+ XCopyArea (xdisplay,
+ xroot, pixmap, gc,
+ geometry.x, geometry.y,
+ geometry.width, geometry.height,
+ 0, 0);
+
+ XFreeGC (xdisplay, gc);
+
+ return pixmap;
+}
+
+/**
+ * meta_background_load_still_frame:
+ * @self: the #MetaBackground
+ *
+ * Takes a screenshot of the desktop and uses it as the background
+ * source.
+ */
+void
+meta_background_load_still_frame (MetaBackground *self)
+{
+ MetaBackgroundPrivate *priv = self->priv;
+ MetaDisplay *display = meta_screen_get_display (priv->screen);
+ Pixmap still_frame;
+ CoglHandle texture;
+ CoglContext *context = clutter_backend_get_cogl_context (clutter_get_default_backend ());
+ GError *error = NULL;
+
+ ensure_pipeline (self);
+
+ unset_texture (self);
+ set_style (self, G_DESKTOP_BACKGROUND_STYLE_STRETCHED);
+
+ still_frame = get_still_frame_for_monitor (priv->screen, priv->monitor);
+ XSync (meta_display_get_xdisplay (display), False);
+
+ meta_error_trap_push (display);
+ texture = cogl_texture_pixmap_x11_new (context, still_frame, FALSE, &error);
+ meta_error_trap_pop (display);
+
+ if (error != NULL)
+ {
+ g_warning ("Failed to create background texture from pixmap: %s",
+ error->message);
+ g_error_free (error);
+ return;
+ }
+
+ set_texture (self, COGL_TEXTURE (texture));
+}
+
+/**
+ * meta_background_load_gradient:
+ * @self: the #MetaBackground
+ * @shading_direction: the orientation of the gradient
+ * @color: the start color of the gradient
+ * @second_color: the end color of the gradient
+ *
+ * Clears any previously set background, and sets the background gradient.
+ * The gradient starts with @color and
+ * progresses toward @second_color in the direction of @shading_direction.
+ */
+void
+meta_background_load_gradient (MetaBackground *self,
+ GDesktopBackgroundShading shading_direction,
+ ClutterColor *color,
+ ClutterColor *second_color)
+{
+ MetaBackgroundPrivate *priv = self->priv;
+ CoglHandle texture;
+ guint width, height;
+ uint8_t pixels[8];
+
+ ensure_pipeline (self);
+
+ unset_texture (self);
+ set_style (self, G_DESKTOP_BACKGROUND_STYLE_NONE);
+
+ priv->shading_direction = shading_direction;
+
+ switch (priv->shading_direction)
+ {
+ case G_DESKTOP_BACKGROUND_SHADING_VERTICAL:
+ width = 1;
+ height = 2;
+ break;
+ case G_DESKTOP_BACKGROUND_SHADING_HORIZONTAL:
+ width = 2;
+ height = 1;
+ break;
+ default:
+ g_return_if_reached ();
+ }
+
+ pixels[0] = color->red;
+ pixels[1] = color->green;
+ pixels[2] = color->blue;
+ pixels[3] = color->alpha;
+ pixels[4] = second_color->red;
+ pixels[5] = second_color->green;
+ pixels[6] = second_color->blue;
+ pixels[7] = second_color->alpha;
+ texture = cogl_texture_new_from_data (width, height,
+ COGL_TEXTURE_NO_SLICING,
+ TEXTURE_FORMAT,
+ COGL_PIXEL_FORMAT_ANY,
+ 4,
+ pixels);
+ set_texture (self, COGL_TEXTURE (texture));
+}
+
+/**
+ * meta_background_load_color:
+ * @self: the #MetaBackground
+ * @color: a #ClutterColor to solid fill background with
+ *
+ * Clears any previously set background, and sets the
+ * background to a solid color
+ *
+ * If @color is %NULL the stage color will be used.
+ */
+void
+meta_background_load_color (MetaBackground *self,
+ ClutterColor *color)
+{
+ MetaBackgroundPrivate *priv = self->priv;
+ CoglHandle texture;
+ ClutterActor *stage = meta_get_stage_for_screen (priv->screen);
+ ClutterColor stage_color;
+
+ ensure_pipeline (self);
+
+ unset_texture (self);
+ set_style (self, G_DESKTOP_BACKGROUND_STYLE_NONE);
+
+ if (color == NULL)
+ {
+ clutter_actor_get_background_color (stage, &stage_color);
+ color = &stage_color;
+ }
+
+ texture = meta_create_color_texture_4ub (color->red,
+ color->green,
+ color->blue,
+ 0xff,
+ COGL_TEXTURE_NO_SLICING);
+ set_texture (self, COGL_TEXTURE (texture));
+}
+
+typedef struct
+{
+ GDesktopBackgroundStyle style;
+ char *filename;
+} LoadFileTaskData;
+
+static LoadFileTaskData *
+load_file_task_data_new (const char *filename,
+ GDesktopBackgroundStyle style)
+{
+ LoadFileTaskData *task_data;
+
+ task_data = g_slice_new (LoadFileTaskData);
+ task_data->style = style;
+ task_data->filename = g_strdup (filename);
+
+ return task_data;
+}
+
+static void
+load_file_task_data_free (LoadFileTaskData *task_data)
+{
+ g_free (task_data->filename);
+ g_slice_free (LoadFileTaskData, task_data);
+}
+
+static void
+load_file (GTask *task,
+ MetaBackground *self,
+ LoadFileTaskData *task_data,
+ GCancellable *cancellable)
+{
+ GError *error = NULL;
+ GdkPixbuf *pixbuf;
+
+ pixbuf = gdk_pixbuf_new_from_file (task_data->filename,
+ &error);
+
+ if (pixbuf == NULL)
+ {
+ g_task_return_error (task, error);
+ return;
+ }
+
+ g_task_return_pointer (task, pixbuf, (GDestroyNotify) g_object_unref);
+}
+
+/**
+ * meta_background_load_file_async:
+ * @self: the #MetaBackground
+ * @filename: the image file to load
+ * @style: a #GDesktopBackgroundStyle to specify how background is laid out
+ * @cancellable: a #GCancellable
+ * @callback: call back to call when file is loaded or failed to load
+ * @user_data: user data for callback
+ *
+ * Loads the specified image and uses it as the background source.
+ */
+void
+meta_background_load_file_async (MetaBackground *self,
+ const char *filename,
+ GDesktopBackgroundStyle style,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ LoadFileTaskData *task_data;
+ GTask *task;
+
+ task = g_task_new (self, cancellable, callback, user_data);
+
+ task_data = load_file_task_data_new (filename, style);
+ g_task_set_task_data (task, task_data, (GDestroyNotify) load_file_task_data_free);
+
+ g_task_run_in_thread (task, (GTaskThreadFunc) load_file);
+}
+
+/**
+ * meta_background_load_file_finish:
+ * @self: the #MetaBackground
+ * @result: the result from the #GAsyncReadyCallback passed
+ * to meta_background_load_file_async()
+ * @error: a #GError
+ *
+ * The finish function for meta_background_load_file_async().
+ *
+ * Returns: whether or not the image was loaded
+ */
+gboolean
+meta_background_load_file_finish (MetaBackground *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ static CoglUserDataKey key;
+ GTask *task;
+ LoadFileTaskData *task_data;
+ CoglTexture *texture;
+ GdkPixbuf *pixbuf;
+ int width, height, row_stride;
+ guchar *pixels;
+ gboolean has_alpha;
+
+ g_return_val_if_fail (g_task_is_valid (result, self), FALSE);
+
+ task = G_TASK (result);
+
+ pixbuf = g_task_propagate_pointer (task, error);
+
+ if (pixbuf == NULL)
+ return FALSE;
+
+ task_data = g_task_get_task_data (task);
+
+ width = gdk_pixbuf_get_width (pixbuf);
+ height = gdk_pixbuf_get_height (pixbuf);
+ row_stride = gdk_pixbuf_get_rowstride (pixbuf);
+ pixels = gdk_pixbuf_get_pixels (pixbuf);
+ has_alpha = gdk_pixbuf_get_has_alpha (pixbuf);
+
+ texture = cogl_texture_new_from_data (width,
+ height,
+ COGL_TEXTURE_NO_SLICING,
+ has_alpha ?
+ COGL_PIXEL_FORMAT_RGBA_8888 :
+ COGL_PIXEL_FORMAT_RGB_888,
+ COGL_PIXEL_FORMAT_ANY,
+ row_stride,
+ pixels);
+
+ if (texture == NULL)
+ {
+ g_set_error_literal (error,
+ COGL_BITMAP_ERROR,
+ COGL_BITMAP_ERROR_FAILED,
+ _("background texture could not be created from file"));
+ return FALSE;
+ }
+
+ cogl_object_set_user_data (COGL_OBJECT (texture),
+ &key,
+ g_object_ref (pixbuf),
+ (CoglUserDataDestroyCallback)
+ g_object_unref);
+
+ ensure_pipeline (self);
+ unset_texture (self);
+ set_style (self, task_data->style);
+ set_filename (self, task_data->filename);
+ set_texture (self, texture);
+
+ clutter_content_invalidate (CLUTTER_CONTENT (self));
+
+ return TRUE;
+}
+
+/**
+ * meta_background_copy:
+ * @self: a #MetaBackground to copy
+ * @monitor: a monitor
+ * @effects: effects to use on copy of @self
+ *
+ * Creates a new #MetaBackground to draw the background for the given monitor.
+ * Background will be loaded from @self and will share state
+ * with @self, but may have different effects applied to it.
+ *
+ * Return value: (transfer full): the newly created background content
+ */
+MetaBackground *
+meta_background_copy (MetaBackground *self,
+ int monitor,
+ MetaBackgroundEffects effects)
+{
+ MetaBackground *background;
+
+ background = META_BACKGROUND (g_object_new (META_TYPE_BACKGROUND,
+ "meta-screen", self->priv->screen,
+ "monitor", monitor,
+ NULL));
+
+ background->priv->brightness = self->priv->brightness;
+
+ background->priv->shading_direction = self->priv->shading_direction;
+ background->priv->color = self->priv->color;
+ background->priv->second_color = self->priv->second_color;
+ background->priv->filename = g_strdup (self->priv->filename);
+
+ /* we can reuse the pipeline if it has no effects applied, or
+ * if it has the same effects applied
+ */
+ if (effects == self->priv->effects ||
+ self->priv->effects == META_BACKGROUND_EFFECTS_NONE)
+ {
+ ensure_pipeline (self);
+ background->priv->pipeline = cogl_pipeline_copy (self->priv->pipeline);
+ background->priv->texture = cogl_object_ref (self->priv->texture);
+ background->priv->style = self->priv->style;
+ background->priv->saturation = self->priv->saturation;
+
+ if (effects != self->priv->effects)
+ {
+ set_effects (background, effects);
+
+ if (effects & META_BACKGROUND_EFFECTS_DESATURATE)
+ set_saturation (background, self->priv->saturation);
+
+ if (effects & META_BACKGROUND_EFFECTS_VIGNETTE)
+ {
+ set_brightness (background, self->priv->brightness);
+ set_vignette_sharpness (background, self->priv->vignette_sharpness);
+ }
+ }
+ else
+ {
+ background->priv->effects = self->priv->effects;
+ }
+
+ }
+ else
+ {
+ ensure_pipeline (background);
+ if (self->priv->texture != NULL)
+ set_texture (background, cogl_object_ref (self->priv->texture));
+ set_style (background, self->priv->style);
+ set_effects (background, effects);
+
+ if (effects & META_BACKGROUND_EFFECTS_DESATURATE)
+ set_saturation (background, self->priv->saturation);
+
+ if (effects & META_BACKGROUND_EFFECTS_VIGNETTE)
+ {
+ set_brightness (background, self->priv->brightness);
+ set_vignette_sharpness (background, self->priv->vignette_sharpness);
+ }
+ }
+
+ clutter_content_invalidate (CLUTTER_CONTENT (background));
+
+ return background;
+}
+/**
+ * meta_background_new:
+ * @screen: the #MetaScreen
+ * @monitor: a monitor in @screen
+ * @effects: which effect flags to enable
+ *
+ * Creates a new #MetaBackground to draw the background for the given monitor.
+ * The returned object should be set on a #MetaBackgroundActor with
+ * clutter_actor_set_content().
+ *
+ * The background may be desaturated, blurred, or given a vignette depending
+ * on @effects.
+ *
+ * Return value: the newly created background content
+ */
+MetaBackground *
+meta_background_new (MetaScreen *screen,
+ int monitor,
+ MetaBackgroundEffects effects)
+{
+ MetaBackground *background;
+
+ background = META_BACKGROUND (g_object_new (META_TYPE_BACKGROUND,
+ "meta-screen", screen,
+ "monitor", monitor,
+ "effects", effects,
+ NULL));
+ return background;
+}
+
+/**
+ * meta_background_get_style:
+ * @self: a #MetaBackground
+ *
+ * Returns the current background style.
+ *
+ * Return value: a #GDesktopBackgroundStyle
+ */
+GDesktopBackgroundStyle
+meta_background_get_style (MetaBackground *self)
+{
+ return self->priv->style;
+}
+
+/**
+ * meta_background_get_shading:
+ * @self: a #MetaBackground
+ *
+ * Returns whether @self is a solid color,
+ * vertical gradient, horizontal gradient,
+ * or none of the above.
+ *
+ * Return value: a #GDesktopBackgroundShading
+ */
+GDesktopBackgroundShading
+meta_background_get_shading (MetaBackground *self)
+{
+ return self->priv->shading_direction;
+}
+
+/**
+ * meta_background_get_color:
+ * @self: a #MetaBackground
+ *
+ * Returns the first color of @self. If self
+ * is a gradient, the second color can be returned
+ * with meta_background_get_second_color().
+ *
+ * Return value: (transfer none): a #ClutterColor
+ */
+const ClutterColor *
+meta_background_get_color (MetaBackground *self)
+{
+ return &self->priv->color;
+}
+
+/**
+ * meta_background_get_second_color:
+ * @self: a #MetaBackground
+ *
+ * Returns the second color of @self. If @self
+ * is not a gradient this function is undefined.
+ *
+ * Return value: (transfer none): a #ClutterColor
+ */
+const ClutterColor *
+meta_background_get_second_color (MetaBackground *self)
+{
+ return &self->priv->second_color;
+}
+
+/**
+ * meta_background_get_filename:
+ * @self: a #MetaBackground
+ *
+ * Returns the filename of the currently loaded file.
+ * IF @self is not loaded from a file this function is
+ * undefined.
+ *
+ * Return value: (transfer none): the filename
+ */
+const char *
+meta_background_get_filename (MetaBackground *self)
+{
+ return self->priv->filename;
+}
diff --git a/src/compositor/meta-window-group.c b/src/compositor/meta-window-group.c
index e3aac58..5dba971 100644
--- a/src/compositor/meta-window-group.c
+++ b/src/compositor/meta-window-group.c
@@ -7,11 +7,12 @@
#include <gdk/gdk.h> /* for gdk_rectangle_intersect() */
+#include "clutter-utils.h"
#include "compositor-private.h"
#include "meta-window-actor-private.h"
#include "meta-window-group.h"
#include "meta-background-actor-private.h"
-#include "clutter-utils.h"
+#include "meta-background-group-private.h"
struct _MetaWindowGroupClass
{
@@ -203,9 +204,10 @@ meta_window_group_paint (ClutterActor *actor)
meta_window_actor_set_visible_region_beneath (window_actor, visible_region);
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;
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;
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);
}
}
diff --git a/src/meta/compositor-mutter.h b/src/meta/compositor-mutter.h
index 161cfad..665330a 100644
--- a/src/meta/compositor-mutter.h
+++ b/src/meta/compositor-mutter.h
@@ -44,7 +44,6 @@ ClutterActor *meta_get_top_window_group_for_screen (MetaScreen *screen);
void meta_disable_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,
XserverRegion region);
void meta_empty_stage_input_region (MetaScreen *screen);
diff --git a/src/meta/meta-background-actor.h b/src/meta/meta-background-actor.h
index 71831af..69ec377 100644
--- a/src/meta/meta-background-actor.h
+++ b/src/meta/meta-background-actor.h
@@ -24,9 +24,13 @@
#define META_BACKGROUND_ACTOR_H
#include <clutter/clutter.h>
+#include <cogl/cogl.h>
+#include <meta/gradient.h>
#include <meta/screen.h>
+#include <gsettings-desktop-schemas/gdesktop-enums.h>
+
/**
* MetaBackgroundActor:
*
@@ -60,41 +64,6 @@ struct _MetaBackgroundActor
GType meta_background_actor_get_type (void);
-ClutterActor *meta_background_actor_new_for_screen (MetaScreen *screen);
-
-/**
- * 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);
+ClutterActor *meta_background_actor_new (void);
#endif /* META_BACKGROUND_ACTOR_H */
diff --git a/src/meta/meta-background-group.h b/src/meta/meta-background-group.h
new file mode 100644
index 0000000..cddc1c5
--- /dev/null
+++ b/src/meta/meta-background-group.h
@@ -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 */
diff --git a/src/meta/meta-background.h b/src/meta/meta-background.h
new file mode 100644
index 0000000..bd73e92
--- /dev/null
+++ b/src/meta/meta-background.h
@@ -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 */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]