[mutter] compositor: rework how backgrounds are managed



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]