summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am6
-rw-r--r--src/compositor/cogl-utils.c34
-rw-r--r--src/compositor/cogl-utils.h4
-rw-r--r--src/compositor/meta-background-actor.c541
-rw-r--r--src/compositor/meta-background-image.c342
-rw-r--r--src/compositor/meta-background-private.h15
-rw-r--r--src/compositor/meta-background.c1595
-rw-r--r--src/compositor/plugins/default.c17
-rw-r--r--src/meta/meta-background-actor.h15
-rw-r--r--src/meta/meta-background-image.h76
-rw-r--r--src/meta/meta-background.h75
11 files changed, 1642 insertions, 1078 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 1e336089e..75b694b64 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -100,8 +100,10 @@ libmutter_la_SOURCES = \
compositor/compositor.c \
compositor/compositor-private.h \
compositor/meta-background.c \
+ compositor/meta-background-private.h \
compositor/meta-background-actor.c \
compositor/meta-background-actor-private.h \
+ compositor/meta-background-image.c \
compositor/meta-background-group.c \
compositor/meta-cullable.c \
compositor/meta-cullable.h \
@@ -133,6 +135,7 @@ libmutter_la_SOURCES = \
meta/compositor.h \
meta/meta-background.h \
meta/meta-background-actor.h \
+ meta/meta-background-image.h \
meta/meta-background-group.h \
meta/meta-plugin.h \
meta/meta-shadow-factory.h \
@@ -282,9 +285,10 @@ libmutterinclude_headers = \
meta/keybindings.h \
meta/main.h \
meta/meta-backend.h \
+ meta/meta-background.h \
meta/meta-background-actor.h \
+ meta/meta-background-image.h \
meta/meta-background-group.h \
- meta/meta-background.h \
meta/meta-cursor-tracker.h \
meta/meta-idle-monitor.h \
meta/meta-plugin.h \
diff --git a/src/compositor/cogl-utils.c b/src/compositor/cogl-utils.c
index 68c2127c0..a1929e3e5 100644
--- a/src/compositor/cogl-utils.c
+++ b/src/compositor/cogl-utils.c
@@ -64,3 +64,37 @@ meta_create_texture_pipeline (CoglTexture *src_texture)
return pipeline;
}
+
+static gboolean is_pot(int x)
+{
+ return x > 0 && (x & (x - 1)) == 0;
+}
+
+CoglTexture *
+meta_create_large_texture (int width,
+ int height,
+ CoglTextureComponents components)
+{
+ ClutterBackend *backend = clutter_get_default_backend ();
+ CoglContext *ctx = clutter_backend_get_cogl_context (backend);
+ CoglTexture *texture;
+
+ gboolean should_use_rectangle = FALSE;
+
+ if (!(is_pot (width) && is_pot (height)) &&
+ !cogl_has_feature (ctx, COGL_FEATURE_ID_TEXTURE_NPOT))
+ {
+ if (cogl_has_feature (ctx, COGL_FEATURE_ID_TEXTURE_RECTANGLE))
+ should_use_rectangle = TRUE;
+ }
+
+ if (should_use_rectangle)
+ texture = COGL_TEXTURE (cogl_texture_rectangle_new_with_size (ctx,
+ width, height));
+ else
+ texture = COGL_TEXTURE (cogl_texture_2d_new_with_size (ctx,
+ width, height));
+ cogl_texture_set_components (texture, components);
+
+ return texture;
+}
diff --git a/src/compositor/cogl-utils.h b/src/compositor/cogl-utils.h
index 281f9b4c2..ffa51a55f 100644
--- a/src/compositor/cogl-utils.h
+++ b/src/compositor/cogl-utils.h
@@ -25,4 +25,8 @@
CoglPipeline * meta_create_texture_pipeline (CoglTexture *texture);
+CoglTexture *meta_create_large_texture (int width,
+ int height,
+ CoglTextureComponents components);
+
#endif /* __META_COGL_UTILS_H__ */
diff --git a/src/compositor/meta-background-actor.c b/src/compositor/meta-background-actor.c
index b7282875a..fcdfa30fc 100644
--- a/src/compositor/meta-background-actor.c
+++ b/src/compositor/meta-background-actor.c
@@ -1,7 +1,7 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* Copyright 2009 Sander Dijkhuis
- * Copyright 2010 Red Hat, Inc.
+ * Copyright 2014 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
@@ -26,23 +26,119 @@
*
*/
-#include <config.h>
+/*
+ * The overall model drawing model of this widget is that we have one
+ * texture, or two interpolated textures, possibly with alpha or
+ * margins that let the underlying background show through, blended
+ * over a solid color or a gradient. The result of that combination
+ * can then be affected by a "vignette" that darkens the background
+ * away from a central point (or as a no-GLSL fallback, simply darkens
+ * the background) and by overall opacity.
+ *
+ * As of GNOME 3.14, GNOME is only using a fraction of this when the
+ * user sets the background through the control center - what can be
+ * set is:
+ *
+ * A single image without a border
+ * An animation of images without a border that blend together,
+ * with the blend changing every 4-5 minutes
+ * A solid color with a repeated noise texture blended over it
+ *
+ * This all is pretty easy to do in a fragment shader, except when:
+ *
+ * A) We don't have GLSL - in this case, the operation of
+ * interpolating the two textures and blending the result over the
+ * background can't be expressed with Cogl's fixed-function layer
+ * combining (which is confined to what GL's texture environment
+ * combining can do) So we can only handle the above directly if
+ * there are no margins or alpha.
+ *
+ * B) The image textures are sliced. Texture size limits on older
+ * hardware (pre-965 intel hardware, r300, etc.) is often 2048,
+ * and it would be common to use a texture larger than this for a
+ * background and expect it to be scaled down. Cogl can compensate
+ * for this by breaking the texture up into multiple textures, but
+ * can't multitexture with sliced textures. So we can only handle
+ * the above if there's a single texture.
+ *
+ * However, even when we *can* represent everything in a single pass,
+ * it's not necessarily efficient. If we want to draw a 1024x768
+ * background, it's pretty inefficient to bilinearly texture from
+ * two 2560x1440 images and mix that. So the drawing model we take
+ * here is that MetaBackground generates a single texture (which
+ * might be a 1x1 texture for a solid color, or a 1x2 texture for a
+ * gradient, or a repeated texture for wallpaper, or a pre-rendered
+ * texture the size of the screen), and we draw with that, possibly
+ * adding the vignette and opacity.
+ */
-#include <cogl/cogl-texture-pixmap-x11.h>
+#include <config.h>
#include <clutter/clutter.h>
-#include <X11/Xatom.h>
-
#include "cogl-utils.h"
-#include "compositor-private.h"
+#include "clutter-utils.h"
#include <meta/errors.h>
-#include <meta/meta-background.h>
#include "meta-background-actor-private.h"
+#include "meta-background-private.h"
#include "meta-cullable.h"
+enum
+{
+ PROP_META_SCREEN = 1,
+ PROP_MONITOR,
+ PROP_BACKGROUND,
+};
+
+typedef enum {
+ CHANGED_BACKGROUND = 1 << 0,
+ CHANGED_EFFECTS = 1 << 2,
+ CHANGED_VIGNETTE_PARAMETERS = 1 << 3,
+ CHANGED_ALL = 0xFFFF
+} ChangedFlags;
+
+#define VERTEX_SHADER_DECLARATIONS \
+"uniform vec2 scale;\n" \
+"uniform vec2 offset;\n" \
+"varying vec2 position;\n" \
+
+#define VERTEX_SHADER_CODE \
+"position = cogl_tex_coord0_in.xy * scale + offset;\n" \
+
+#define FRAGMENT_SHADER_DECLARATIONS \
+"uniform float vignette_sharpness;\n" \
+"varying vec2 position;\n" \
+
+#define FRAGMENT_SHADER_CODE \
+"float t = 2.0 * length(position);\n" \
+"t = min(t, 1.0);\n" \
+"float pixel_brightness = 1 - t * vignette_sharpness;\n" \
+"cogl_color_out.rgb = cogl_color_out.rgb * pixel_brightness;\n" \
+
+typedef struct _MetaBackgroundLayer MetaBackgroundLayer;
+
+typedef enum {
+ PIPELINE_VIGNETTE = (1 << 0),
+ PIPELINE_BLEND = (1 << 1),
+} PipelineFlags;
+
struct _MetaBackgroundActorPrivate
{
+ MetaScreen *screen;
+ int monitor;
+
+ MetaBackground *background;
+
+ gboolean vignette;
+ float brightness;
+ float vignette_sharpness;
+
+ ChangedFlags changed;
+ CoglPipeline *pipeline;
+ PipelineFlags pipeline_flags;
+ cairo_rectangle_int_t texture_area;
+ gboolean force_bilinear;
+
cairo_region_t *clip_region;
};
@@ -66,27 +162,45 @@ static void
meta_background_actor_dispose (GObject *object)
{
MetaBackgroundActor *self = META_BACKGROUND_ACTOR (object);
+ MetaBackgroundActorPrivate *priv = self->priv;
set_clip_region (self, NULL);
+ meta_background_actor_set_background (self, NULL);
+ if (priv->pipeline)
+ {
+ cogl_object_unref (priv->pipeline);
+ priv->pipeline = NULL;
+ }
G_OBJECT_CLASS (meta_background_actor_parent_class)->dispose (object);
}
static void
+get_preferred_size (MetaBackgroundActor *self,
+ gfloat *width,
+ gfloat *height)
+{
+ MetaBackgroundActorPrivate *priv = META_BACKGROUND_ACTOR (self)->priv;
+ MetaRectangle monitor_geometry;
+
+ 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;
+}
+
+static void
meta_background_actor_get_preferred_width (ClutterActor *actor,
gfloat for_height,
gfloat *min_width_p,
gfloat *natural_width_p)
{
- ClutterContent *content;
gfloat width;
- content = clutter_actor_get_content (actor);
-
- if (content)
- clutter_content_get_preferred_size (content, &width, NULL);
- else
- width = 0;
+ get_preferred_size (META_BACKGROUND_ACTOR (actor), &width, NULL);
if (min_width_p)
*min_width_p = width;
@@ -101,15 +215,9 @@ meta_background_actor_get_preferred_height (ClutterActor *actor,
gfloat *natural_height_p)
{
- ClutterContent *content;
gfloat height;
- content = clutter_actor_get_content (actor);
-
- if (content)
- clutter_content_get_preferred_size (content, NULL, &height);
- else
- height = 0;
+ get_preferred_size (META_BACKGROUND_ACTOR (actor), NULL, &height);
if (min_height_p)
*min_height_p = height;
@@ -117,18 +225,306 @@ meta_background_actor_get_preferred_height (ClutterActor *actor,
*natural_height_p = height;
}
+static CoglPipeline *
+make_pipeline (PipelineFlags pipeline_flags)
+{
+ static CoglPipeline *templates[4];
+ CoglPipeline **templatep;
+
+ templatep = &templates[pipeline_flags];
+ if (*templatep == NULL)
+ {
+ /* Cogl automatically caches pipelines with no eviction policy,
+ * so we need to prevent identical pipelines from getting cached
+ * separately, by reusing the same shader snippets.
+ */
+ *templatep = COGL_PIPELINE (meta_create_texture_pipeline (NULL));
+
+ if ((pipeline_flags & PIPELINE_VIGNETTE) != 0)
+ {
+ static CoglSnippet *snippet;
+
+ if (!snippet)
+ snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_VERTEX,
+ VERTEX_SHADER_DECLARATIONS, VERTEX_SHADER_CODE);
+
+ cogl_pipeline_add_snippet (*templatep, snippet);
+ }
+
+ if ((pipeline_flags & PIPELINE_VIGNETTE) != 0)
+ {
+ static CoglSnippet *snippet;
+
+ if (!snippet)
+ snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT,
+ FRAGMENT_SHADER_DECLARATIONS, FRAGMENT_SHADER_CODE);
+
+ cogl_pipeline_add_snippet (*templatep, snippet);
+ }
+
+ if ((pipeline_flags & PIPELINE_BLEND) == 0)
+ cogl_pipeline_set_blend (*templatep, "RGBA = ADD (SRC_COLOR, 0)", NULL);
+ }
+
+ return cogl_pipeline_copy (*templatep);
+}
+
+static void
+setup_pipeline (MetaBackgroundActor *self,
+ cairo_rectangle_int_t *actor_pixel_rect)
+{
+ MetaBackgroundActorPrivate *priv = self->priv;
+ PipelineFlags pipeline_flags = 0;
+ guint8 opacity;
+ float color_component;
+ CoglPipelineFilter filter;
+
+ opacity = clutter_actor_get_paint_opacity (CLUTTER_ACTOR (self));
+ if (opacity < 255)
+ pipeline_flags |= PIPELINE_BLEND;
+ if (priv->vignette && clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL))
+ pipeline_flags |= PIPELINE_VIGNETTE;
+
+ if (priv->pipeline &&
+ pipeline_flags != priv->pipeline_flags)
+ {
+ cogl_object_unref (priv->pipeline);
+ priv->pipeline = NULL;
+ }
+
+ if (priv->pipeline == NULL)
+ {
+ priv->pipeline_flags = pipeline_flags;
+ priv->pipeline = make_pipeline (pipeline_flags);
+ priv->changed = CHANGED_ALL;
+ }
+
+ if ((priv->changed & CHANGED_BACKGROUND) != 0)
+ {
+ CoglPipelineWrapMode wrap_mode;
+ CoglTexture *texture = meta_background_get_texture (priv->background,
+ priv->monitor,
+ &priv->texture_area,
+ &wrap_mode);
+ priv->force_bilinear = texture &&
+ (priv->texture_area.width != (int)cogl_texture_get_width (texture) ||
+ priv->texture_area.height != (int)cogl_texture_get_height (texture));
+
+ cogl_pipeline_set_layer_texture (priv->pipeline, 0, texture);
+ cogl_pipeline_set_layer_wrap_mode (priv->pipeline, 0, wrap_mode);
+ }
+
+ if ((priv->changed & CHANGED_VIGNETTE_PARAMETERS) != 0)
+ {
+ cogl_pipeline_set_uniform_1f (priv->pipeline,
+ cogl_pipeline_get_uniform_location (priv->pipeline,
+ "vignette_sharpness"),
+ priv->vignette_sharpness);
+ }
+
+ if (priv->vignette)
+ color_component = priv->brightness * opacity / 255.;
+ else
+ color_component = opacity / 255.;
+
+ cogl_pipeline_set_color4f (priv->pipeline,
+ color_component,
+ color_component,
+ color_component,
+ opacity / 255.);
+
+ if (!priv->force_bilinear &&
+ meta_actor_painting_untransformed (actor_pixel_rect->width, actor_pixel_rect->height, NULL, NULL))
+ filter = COGL_PIPELINE_FILTER_NEAREST;
+ else
+ filter = COGL_PIPELINE_FILTER_LINEAR;
+
+ cogl_pipeline_set_layer_filters (priv->pipeline, 0, filter, filter);
+}
+
+static void
+set_glsl_parameters (MetaBackgroundActor *self,
+ cairo_rectangle_int_t *actor_pixel_rect)
+{
+ MetaBackgroundActorPrivate *priv = self->priv;
+ float scale[2];
+ float offset[2];
+
+ /* Compute a scale and offset for transforming texture coordinates to the
+ * coordinate system from [-0.5 to 0.5] across the area of the actor
+ */
+ scale[0] = priv->texture_area.width / (float)actor_pixel_rect->width;
+ scale[1] = priv->texture_area.height / (float)actor_pixel_rect->height;
+ offset[0] = priv->texture_area.x / (float)actor_pixel_rect->width - 0.5;
+ offset[1] = priv->texture_area.y / (float)actor_pixel_rect->height - 0.5;
+
+ cogl_pipeline_set_uniform_float (priv->pipeline,
+ cogl_pipeline_get_uniform_location (priv->pipeline,
+ "scale"),
+ 2, 1, scale);
+
+ cogl_pipeline_set_uniform_float (priv->pipeline,
+ cogl_pipeline_get_uniform_location (priv->pipeline,
+ "offset"),
+ 2, 1, offset);
+}
+
+static void
+meta_background_actor_paint (ClutterActor *actor)
+{
+ MetaBackgroundActor *self = META_BACKGROUND_ACTOR (actor);
+ MetaBackgroundActorPrivate *priv = self->priv;
+ ClutterActorBox actor_box;
+ cairo_rectangle_int_t actor_pixel_rect;
+ cairo_region_t *paintable_region = NULL;
+ int n_texture_subareas;
+ int i;
+
+ if ((priv->clip_region && cairo_region_is_empty (priv->clip_region)))
+ return;
+
+ clutter_actor_get_content_box (actor, &actor_box);
+ 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;
+
+ /* Now figure out what to actually paint.
+ */
+ paintable_region = cairo_region_create_rectangle (&actor_pixel_rect);
+ if (priv->clip_region != NULL)
+ cairo_region_intersect (paintable_region, priv->clip_region);
+
+ if (cairo_region_is_empty (paintable_region))
+ goto out;
+
+ setup_pipeline (self, &actor_pixel_rect);
+ set_glsl_parameters (self, &actor_pixel_rect);
+
+ /* 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 rect;
+ float tx1, tx2, ty1, ty2;
+
+ cairo_region_get_rectangle (paintable_region, i, &rect);
+
+ tx1 = (rect.x - actor_pixel_rect.x - priv->texture_area.x) / (float)priv->texture_area.width;
+ ty1 = (rect.y - actor_pixel_rect.y - priv->texture_area.y) / (float)priv->texture_area.height;
+ tx2 = (rect.x + rect.width - actor_pixel_rect.x - priv->texture_area.x) / (float)priv->texture_area.width;
+ ty2 = (rect.y + rect.height - actor_pixel_rect.y - priv->texture_area.y) / (float)priv->texture_area.height;
+
+ cogl_framebuffer_draw_textured_rectangle (cogl_get_draw_framebuffer (),
+ priv->pipeline,
+ rect.x, rect.y,
+ rect.x + rect.width, rect.y + rect.height,
+ tx1, ty1, tx2, ty2);
+ }
+
+ out:
+ cairo_region_destroy (paintable_region);
+}
+
+static void
+meta_background_actor_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ MetaBackgroundActor *self = META_BACKGROUND_ACTOR (object);
+ MetaBackgroundActorPrivate *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_BACKGROUND:
+ meta_background_actor_set_background (self, g_value_get_object (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+meta_background_actor_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ MetaBackgroundActorPrivate *priv = META_BACKGROUND_ACTOR (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_BACKGROUND:
+ g_value_set_object (value, priv->background);
+ 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 *param_spec;
g_type_class_add_private (klass, sizeof (MetaBackgroundActorPrivate));
object_class->dispose = meta_background_actor_dispose;
+ object_class->set_property = meta_background_actor_set_property;
+ object_class->get_property = meta_background_actor_get_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;
+
+ param_spec = g_param_spec_object ("meta-screen",
+ "MetaScreen",
+ "MetaScreen",
+ META_TYPE_SCREEN,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+
+ 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_ONLY);
+
+ g_object_class_install_property (object_class,
+ PROP_MONITOR,
+ param_spec);
+
+ param_spec = g_param_spec_object ("background",
+ "Background",
+ "MetaBackground object holding background parameters",
+ META_TYPE_BACKGROUND,
+ G_PARAM_READWRITE);
+
+ g_object_class_install_property (object_class,
+ PROP_BACKGROUND,
+ param_spec);
}
static void
@@ -141,19 +537,22 @@ meta_background_actor_init (MetaBackgroundActor *self)
/**
* meta_background_actor_new:
+ * @monitor: Index of the monitor for which to draw the background
*
* 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 (void)
+meta_background_actor_new (MetaScreen *screen,
+ int monitor)
{
MetaBackgroundActor *self;
- self = g_object_new (META_TYPE_BACKGROUND_ACTOR, NULL);
+ self = g_object_new (META_TYPE_BACKGROUND_ACTOR,
+ "meta-screen", screen,
+ "monitor", monitor,
+ NULL);
return CLUTTER_ACTOR (self);
}
@@ -195,3 +594,95 @@ meta_background_actor_get_clip_region (MetaBackgroundActor *self)
MetaBackgroundActorPrivate *priv = self->priv;
return priv->clip_region;
}
+
+static void
+invalidate_pipeline (MetaBackgroundActor *self,
+ ChangedFlags changed)
+{
+ MetaBackgroundActorPrivate *priv = self->priv;
+
+ priv->changed |= changed;
+}
+
+static void
+on_background_changed (MetaBackground *background,
+ MetaBackgroundActor *self)
+{
+ invalidate_pipeline (self, CHANGED_BACKGROUND);
+}
+
+void
+meta_background_actor_set_background (MetaBackgroundActor *self,
+ MetaBackground *background)
+{
+ MetaBackgroundActorPrivate *priv;
+
+ g_return_if_fail (META_IS_BACKGROUND_ACTOR (self));
+ g_return_if_fail (background == NULL || META_IS_BACKGROUND (background));
+
+ priv = self->priv;
+
+ if (background == priv->background)
+ return;
+
+ if (priv->background)
+ {
+ g_signal_handlers_disconnect_by_func (priv->background,
+ (gpointer)on_background_changed,
+ self);
+ g_object_unref (priv->background);
+ priv->background = NULL;
+ }
+
+ if (background)
+ {
+ priv->background = g_object_ref (background);
+ g_signal_connect (priv->background, "changed",
+ G_CALLBACK (on_background_changed), self);
+ }
+
+ invalidate_pipeline (self, CHANGED_BACKGROUND);
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
+}
+
+void
+meta_background_actor_add_vignette (MetaBackgroundActor *self,
+ double brightness,
+ double sharpness)
+{
+ MetaBackgroundActorPrivate *priv;
+
+ g_return_if_fail (META_IS_BACKGROUND_ACTOR (self));
+ g_return_if_fail (brightness >= 0. && brightness <= 1.);
+ g_return_if_fail (sharpness >= 0.);
+
+ priv = self->priv;
+
+ if (!priv->vignette)
+ invalidate_pipeline (self, CHANGED_EFFECTS);
+
+ priv->vignette = TRUE;
+ priv->brightness = brightness;
+ priv->vignette_sharpness = sharpness;
+ invalidate_pipeline (self, CHANGED_VIGNETTE_PARAMETERS);
+
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
+}
+
+void
+meta_background_actor_remove_vignette (MetaBackgroundActor *self)
+{
+ MetaBackgroundActorPrivate *priv;
+ priv = self->priv;
+
+ g_return_if_fail (META_IS_BACKGROUND_ACTOR (self));
+
+ if (!priv->vignette)
+ return;
+
+ priv->vignette = FALSE;
+
+ invalidate_pipeline (self, CHANGED_EFFECTS);
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
+}
+
diff --git a/src/compositor/meta-background-image.c b/src/compositor/meta-background-image.c
new file mode 100644
index 000000000..d932b6bb6
--- /dev/null
+++ b/src/compositor/meta-background-image.c
@@ -0,0 +1,342 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright 2014 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, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * SECTION:meta-background-image
+ * @title: MetaBackgroundImage
+ * @short_description: objects holding images loaded from files, used for backgrounds
+ */
+
+#include <config.h>
+
+#include <gio/gio.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <clutter/clutter.h>
+#include <meta/meta-background-image.h>
+#include "cogl-utils.h"
+
+enum
+{
+ LOADED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+struct _MetaBackgroundImageCache
+{
+ GObject parent_instance;
+
+ GHashTable *images;
+};
+
+struct _MetaBackgroundImageCacheClass
+{
+ GObjectClass parent_class;
+};
+
+struct _MetaBackgroundImage
+{
+ GObject parent_instance;
+ char *filename;
+ MetaBackgroundImageCache *cache;
+ gboolean in_cache;
+ gboolean loaded;
+ CoglTexture *texture;
+};
+
+struct _MetaBackgroundImageClass
+{
+ GObjectClass parent_class;
+};
+
+G_DEFINE_TYPE (MetaBackgroundImageCache, meta_background_image_cache, G_TYPE_OBJECT);
+
+static void
+meta_background_image_cache_init (MetaBackgroundImageCache *cache)
+{
+ cache->images = g_hash_table_new (g_str_hash, g_str_equal);
+}
+
+static void
+meta_background_image_cache_finalize (GObject *object)
+{
+ MetaBackgroundImageCache *cache = META_BACKGROUND_IMAGE_CACHE (object);
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_hash_table_iter_init (&iter, cache->images);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ {
+ MetaBackgroundImage *image = value;
+ image->in_cache = FALSE;
+ }
+
+ g_hash_table_destroy (cache->images);
+
+ G_OBJECT_CLASS (meta_background_image_cache_parent_class)->finalize (object);
+}
+
+static void
+meta_background_image_cache_class_init (MetaBackgroundImageCacheClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = meta_background_image_cache_finalize;
+}
+
+/**
+ * meta_background_image_cache_get_default:
+ *
+ * Return value: (transfer none): the global singleton background cache
+ */
+MetaBackgroundImageCache *
+meta_background_image_cache_get_default (void)
+{
+ static MetaBackgroundImageCache *cache;
+
+ if (cache == NULL)
+ cache = g_object_new (META_TYPE_BACKGROUND_IMAGE_CACHE, NULL);
+
+ return cache;
+}
+
+static void
+load_file (GTask *task,
+ MetaBackgroundImage *image,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ GError *error = NULL;
+ GdkPixbuf *pixbuf;
+
+ pixbuf = gdk_pixbuf_new_from_file (image->filename,
+ &error);
+
+ if (pixbuf == NULL)
+ {
+ g_task_return_error (task, error);
+ return;
+ }
+
+ g_task_return_pointer (task, pixbuf, (GDestroyNotify) g_object_unref);
+}
+
+static void
+file_loaded (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ MetaBackgroundImage *image = META_BACKGROUND_IMAGE (source_object);
+ GError *error = NULL;
+ GTask *task;
+ CoglTexture *texture;
+ GdkPixbuf *pixbuf;
+ int width, height, row_stride;
+ guchar *pixels;
+ gboolean has_alpha;
+
+ task = G_TASK (result);
+ pixbuf = g_task_propagate_pointer (task, &error);
+
+ if (pixbuf == NULL)
+ {
+ g_warning ("Failed to load background '%s': %s",
+ image->filename, error->message);
+ g_clear_error (&error);
+ goto out;
+ }
+
+ 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 = meta_create_large_texture (width, height,
+ has_alpha ? COGL_TEXTURE_COMPONENTS_RGBA : COGL_TEXTURE_COMPONENTS_RGB);
+ if (!cogl_texture_set_data (texture,
+ has_alpha ? COGL_PIXEL_FORMAT_RGBA_8888 : COGL_PIXEL_FORMAT_RGB_888,
+ row_stride,
+ pixels, 0,
+ NULL))
+ {
+ g_warning ("Failed to create texture for background");
+ cogl_object_unref (texture);
+ }
+
+ image->texture = texture;
+
+out:
+ image->loaded = TRUE;
+ g_signal_emit (image, signals[LOADED], 0);
+}
+
+/**
+ * meta_background_image_cache_load:
+ * @cache: a #MetaBackgroundImageCache
+ * @filename: filename to load
+ *
+ * Loads an image to use as a background, or returns a reference to an
+ * image that is already in the process of loading or loaded. In either
+ * case, what is returned is a #MetaBackgroundImage which can be derefenced
+ * to get a #CoglTexture. If meta_background_image_is_loaded() returns %TRUE,
+ * the background is loaded, otherwise the MetaBackgroundImage::loaded
+ * signal will be emitted exactly once. The 'loaded' state means that the
+ * loading process finished, whether it succeeded or failed.
+ *
+ * Return value: (transfer full): a #MetaBackgroundImage to dereference to get the loaded texture
+ */
+MetaBackgroundImage *
+meta_background_image_cache_load (MetaBackgroundImageCache *cache,
+ const char *filename)
+{
+ MetaBackgroundImage *image;
+ GTask *task;
+
+ g_return_val_if_fail (META_IS_BACKGROUND_IMAGE_CACHE (cache), NULL);
+ g_return_val_if_fail (filename != NULL, NULL);
+
+ image = g_hash_table_lookup (cache->images, filename);
+ if (image != NULL)
+ return g_object_ref (image);
+
+ image = g_object_new (META_TYPE_BACKGROUND_IMAGE, NULL);
+ image->cache = cache;
+ image->in_cache = TRUE;
+ image->filename = g_strdup (filename);
+ g_hash_table_insert (cache->images, image->filename, image);
+
+ task = g_task_new (image, NULL, file_loaded, NULL);
+
+ g_task_run_in_thread (task, (GTaskThreadFunc) load_file);
+ g_object_unref (task);
+
+ return image;
+}
+
+/**
+ * meta_background_image_cache_purge:
+ * @cache: a #MetaBackgroundImageCache
+ * @filename: filename to remove from the cache
+ *
+ * Remove an entry from the cache; this would be used if monitoring
+ * showed that the file changed.
+ */
+void
+meta_background_image_cache_purge (MetaBackgroundImageCache *cache,
+ const char *filename)
+{
+ MetaBackgroundImage *image;
+
+ g_return_val_if_fail (META_IS_BACKGROUND_IMAGE_CACHE (cache), NULL);
+ g_return_val_if_fail (filename != NULL, NULL);
+
+ image = g_hash_table_lookup (cache->images, filename);
+ if (image == NULL)
+ return;
+
+ g_hash_table_remove (cache->images, image->filename);
+ image->in_cache = FALSE;
+}
+
+G_DEFINE_TYPE (MetaBackgroundImage, meta_background_image, G_TYPE_OBJECT);
+
+static void
+meta_background_image_init (MetaBackgroundImage *image)
+{
+}
+
+static void
+meta_background_image_finalize (GObject *object)
+{
+ MetaBackgroundImage *image = META_BACKGROUND_IMAGE (object);
+
+ if (image->in_cache)
+ g_hash_table_remove (image->cache->images, image->filename);
+
+ if (image->texture)
+ cogl_object_unref (image->texture);
+ if (image->filename)
+ g_free (image->filename);
+
+ G_OBJECT_CLASS (meta_background_image_parent_class)->finalize (object);
+}
+
+static void
+meta_background_image_class_init (MetaBackgroundImageClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = meta_background_image_finalize;
+
+ signals[LOADED] =
+ g_signal_new ("loaded",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+}
+
+/**
+ * meta_background_image_is_loaded:
+ * @image: a #MetaBackgroundImage
+ *
+ * Return value: %TRUE if loading has already completed, %FALSE otherwise
+ */
+gboolean
+meta_background_image_is_loaded (MetaBackgroundImage *image)
+{
+ g_return_val_if_fail (META_IS_BACKGROUND_IMAGE (image), FALSE);
+
+ return image->loaded;
+}
+
+/**
+ * meta_background_image_get_success:
+ * @image: a #MetaBackgroundImage
+ *
+ * This function is a convenience function for checking for success,
+ * without having to call meta_background_image_get_success() and
+ * handle the return of a Cogl type.
+ *
+ * Return value: %TRUE if loading completed successfully, otherwise %FALSE
+ */
+gboolean
+meta_background_image_get_success (MetaBackgroundImage *image)
+{
+ g_return_val_if_fail (META_IS_BACKGROUND_IMAGE (image), FALSE);
+
+ return image->texture != NULL;
+}
+
+/**
+ * meta_background_image_get_texture:
+ * @image: a #MetaBackgroundImage
+ *
+ * Return value: (transfer none): a #CoglTexture if loading succeeded; if
+ * loading failed or has not yet finished, %NULL.
+ */
+CoglTexture *
+meta_background_image_get_texture (MetaBackgroundImage *image)
+{
+ g_return_val_if_fail (META_IS_BACKGROUND_IMAGE (image), NULL);
+
+ return image->texture;
+}
diff --git a/src/compositor/meta-background-private.h b/src/compositor/meta-background-private.h
new file mode 100644
index 000000000..2799b7c10
--- /dev/null
+++ b/src/compositor/meta-background-private.h
@@ -0,0 +1,15 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+#ifndef META_BACKGROUND_PRIVATE_H
+#define META_BACKGROUND_PRIVATE_H
+
+#include <config.h>
+
+#include "meta-background-private.h"
+
+CoglTexture *meta_background_get_texture (MetaBackground *self,
+ int monitor_index,
+ cairo_rectangle_int_t *texture_area,
+ CoglPipelineWrapMode *wrap_mode);
+
+#endif /* META_BACKGROUND_PRIVATE_H */
diff --git a/src/compositor/meta-background.c b/src/compositor/meta-background.c
index 0f6220fd2..94f673628 100644
--- a/src/compositor/meta-background.c
+++ b/src/compositor/meta-background.c
@@ -17,680 +17,279 @@
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
-/**
- * SECTION:meta-background
- * @title: MetaBackground
- * @short_description: ClutterContent for painting the system background
- *
- */
+#include <meta/meta-background.h>
+#include <meta/meta-background-image.h>
+#include "meta-background-private.h"
+#include "cogl-utils.h"
-#include <config.h>
+enum
+{
+ CHANGED,
+ LAST_SIGNAL
+};
-#include <clutter/clutter.h>
+static guint signals[LAST_SIGNAL] = { 0 };
-#include "cogl-utils.h"
-#include "compositor-private.h"
-#include "mutter-enum-types.h"
-#include <meta/errors.h>
-#include <meta/meta-background.h>
-#include "util-private.h"
-#include "meta-background-actor-private.h"
-
-#define FRAGMENT_SHADER_DECLARATIONS \
-"uniform vec2 texture_scale;\n" \
-"uniform vec2 actor_size;\n" \
-"uniform vec2 offset;\n" \
-"uniform float brightness;\n" \
-"uniform float vignette_sharpness;\n" \
-
-#define VIGNETTE_CODE \
-"vec2 position = cogl_tex_coord_in[0].xy * texture_scale - offset;\n" \
-"float t = length(2.0 * (position / actor_size));\n" \
-"t = clamp(t, 0.0, 1.0);\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 _MetaBackgroundMonitor MetaBackgroundMonitor;
-struct _MetaBackgroundPrivate
+struct _MetaBackgroundMonitor
{
- MetaScreen *screen;
- CoglTexture *texture;
- CoglPipeline *pipeline;
- int monitor;
+ gboolean dirty;
+ CoglTexture *texture;
+ CoglOffscreen *fbo;
+};
- MetaBackgroundEffects effects;
+struct _MetaBackgroundPrivate
+{
+ MetaScreen *screen;
+ MetaBackgroundMonitor *monitors;
+ int n_monitors;
GDesktopBackgroundStyle style;
GDesktopBackgroundShading shading_direction;
ClutterColor color;
ClutterColor second_color;
- char *filename;
+ char *filename1;
+ MetaBackgroundImage *background_image1;
+ char *filename2;
+ MetaBackgroundImage *background_image2;
- float brightness;
- float vignette_sharpness;
+ CoglTexture *color_texture;
+ CoglTexture *wallpaper_texture;
+
+ float blend_factor;
};
enum
{
PROP_META_SCREEN = 1,
PROP_MONITOR,
- PROP_EFFECTS,
- PROP_BRIGHTNESS,
- PROP_VIGNETTE_SHARPNESS,
};
-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;
-}
+G_DEFINE_TYPE (MetaBackground, meta_background, G_TYPE_OBJECT)
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)
+free_fbos (MetaBackground *self)
{
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;
- float x_offset, y_offset;
-
- 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);
+ int i;
- switch (priv->style)
+ for (i = 0; i < priv->n_monitors; i++)
{
- 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:
- /* The wallpaper should be centered in the middle of all monitors.
- * Therefore, the textured area is the union of all monitors plus
- * an additional bit to make up for the texture getting centered. */
- meta_screen_get_size (priv->screen, &screen_width, &screen_height);
-
- /* so start by making the unclipped texture area the whole screen */
- image_area.width = screen_width;
- image_area.height = screen_height;
-
- /* If one of the tiles is already centered in the screen, then that tile
- * will start tile_size/2.0 before the center of the screen. So find out
- * how far we are from that ideal and adjust by that offset.
- */
- x_offset = texture_width - ((int) ((screen_width / 2.0) - (texture_width / 2.0))) % ((int) texture_width);
- y_offset = texture_height - ((int) ((screen_height / 2.0) - (texture_height / 2.0))) % ((int) texture_height);
-
- image_area.width += x_offset;
- image_area.height += y_offset;
- image_area.x = -x_offset;
- image_area.y = -y_offset;
-
- /* now line up with the appropriate monitor */
- image_area.x -= monitor_geometry.x;
- image_area.y -= monitor_geometry.y;
-
- /* and scale to actor */
- image_area.x *= actor_x_scale;
- image_area.y *= actor_y_scale;
- image_area.width *= actor_x_scale;
- image_area.height *= actor_y_scale;
-
- *texture_area = image_area;
- *texture_x_scale = 1.0 / texture_width;
- *texture_y_scale = 1.0 / texture_height;
- 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:
+ MetaBackgroundMonitor *monitor = &priv->monitors[i];
+ if (monitor->fbo)
{
- /* 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;
+ cogl_object_unref (monitor->fbo);
+ monitor->fbo = NULL;
+ }
+ if (monitor->texture)
+ {
+ cogl_object_unref (monitor->texture);
+ monitor->texture = NULL;
}
}
}
-static CoglPipelineWrapMode
-get_wrap_mode (MetaBackground *self)
+static void
+free_color_texture (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 gboolean
-texture_has_alpha (CoglTexture *texture)
-{
- if (!texture)
- return FALSE;
-
- switch (cogl_texture_get_components (texture))
+ if (priv->color_texture != NULL)
{
- case COGL_TEXTURE_COMPONENTS_A:
- case COGL_TEXTURE_COMPONENTS_RGBA:
- return TRUE;
- case COGL_TEXTURE_COMPONENTS_RG:
- case COGL_TEXTURE_COMPONENTS_RGB:
- case COGL_TEXTURE_COMPONENTS_DEPTH:
- return FALSE;
- default:
- g_assert_not_reached ();
+ cogl_object_unref (priv->color_texture);
+ priv->color_texture = NULL;
}
}
-static ClutterPaintNode *
-meta_background_paint_node_new (MetaBackground *self,
- ClutterActor *actor)
+static void
+free_wallpaper_texture (MetaBackground *self)
{
MetaBackgroundPrivate *priv = self->priv;
- ClutterPaintNode *node;
- guint8 opacity;
- guint8 color_component;
- gboolean needs_blending;
- 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);
-
- needs_blending = (opacity < 255) || (texture_has_alpha (priv->texture));
-
- if (needs_blending)
- cogl_pipeline_set_blend (priv->pipeline, "RGBA = ADD (SRC_COLOR, DST_COLOR*(1-SRC_COLOR[A]))", NULL);
- else
- cogl_pipeline_set_blend (priv->pipeline, "RGBA = ADD (SRC_COLOR, 0)", NULL);
-
- return node;
+ if (priv->wallpaper_texture != NULL)
+ {
+ cogl_object_unref (priv->wallpaper_texture);
+ priv->wallpaper_texture = NULL;
+ }
}
static void
-clip_region_to_actor_box (cairo_region_t *region,
- ClutterActorBox *actor_box)
+on_monitors_changed (MetaScreen *screen,
+ MetaBackground *self)
{
- 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;
+ MetaBackgroundPrivate *priv = self->priv;
- cairo_region_intersect_rectangle (region, &clip_rect);
-}
+ free_fbos (self);
+ g_free (priv->monitors);
+ priv->monitors = NULL;
+ priv->n_monitors = 0;
-static void
-set_vignette_parameters (MetaBackground *self,
- ClutterActorBox *actor_box,
- cairo_rectangle_int_t *texture_area,
- float texture_x_scale,
- float texture_y_scale)
-{
- MetaBackgroundPrivate *priv = self->priv;
- float texture_scale[2];
- float actor_size[2];
- float offset[2];
-
- if (!(priv->effects & META_BACKGROUND_EFFECTS_VIGNETTE))
- return;
-
- texture_scale[0] = 1.0 / texture_x_scale;
- texture_scale[1] = 1.0 / texture_y_scale;
- actor_size[0] = actor_box->x2 - actor_box->x1;
- actor_size[1] = actor_box->y2 - actor_box->y1;
- offset[0] = -texture_area->x + (actor_size[0] / 2.0);
- offset[1] = -texture_area->y + (actor_size[1] / 2.0);
-
- cogl_pipeline_set_uniform_float (priv->pipeline,
- cogl_pipeline_get_uniform_location (priv->pipeline,
- "texture_scale"),
- 2, 1, texture_scale);
-
- cogl_pipeline_set_uniform_float (priv->pipeline,
- cogl_pipeline_get_uniform_location (priv->pipeline,
- "actor_size"),
- 2, 1, actor_size);
-
- cogl_pipeline_set_uniform_float (priv->pipeline,
- cogl_pipeline_get_uniform_location (priv->pipeline,
- "offset"),
- 2, 1, offset);
+ if (priv->screen)
+ {
+ priv->n_monitors = meta_screen_get_n_monitors (screen);
+ priv->monitors = g_new0 (MetaBackgroundMonitor, priv->n_monitors);
+ }
}
static void
-meta_background_paint_content (ClutterContent *content,
- ClutterActor *actor,
- ClutterPaintNode *root)
+set_screen (MetaBackground *self,
+ MetaScreen *screen)
{
- 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;
-
- clutter_actor_get_content_box (actor, &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);
-
- set_vignette_parameters (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))
+ if (priv->screen != NULL)
{
- cairo_region_t *clip_region;
- clip_region = meta_background_actor_get_clip_region (META_BACKGROUND_ACTOR (actor));
-
- if (clip_region != NULL)
- cairo_region_intersect (paintable_region, clip_region);
+ g_signal_handlers_disconnect_by_func (priv->screen,
+ (gpointer)on_monitors_changed,
+ self);
}
- if (cairo_region_is_empty (paintable_region))
- goto out;
-
- node = meta_background_paint_node_new (self, actor);
+ priv->screen = screen;
- /* 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++)
+ if (priv->screen != NULL)
{
- 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);
+ g_signal_connect (priv->screen, "monitors-changed",
+ G_CALLBACK (on_monitors_changed), self);
}
- clutter_paint_node_add_child (root, node);
- clutter_paint_node_unref (node);
- out:
- cairo_region_destroy (paintable_region);
+ on_monitors_changed (priv->screen, self);
}
static void
-clutter_content_iface_init (ClutterContentIface *iface)
+meta_background_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
{
- iface->get_preferred_size = meta_background_get_preferred_size;
- iface->paint_content = meta_background_paint_content;
+ switch (prop_id)
+ {
+ case PROP_META_SCREEN:
+ set_screen (META_BACKGROUND (object), g_value_get_object (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
}
static void
-meta_background_dispose (GObject *object)
+meta_background_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
{
- MetaBackground *self = META_BACKGROUND (object);
- MetaBackgroundPrivate *priv = self->priv;
-
- unset_texture (self);
-
- g_clear_pointer (&priv->pipeline,
- (GDestroyNotify)
- cogl_object_unref);
+ MetaBackgroundPrivate *priv = META_BACKGROUND (object)->priv;
- G_OBJECT_CLASS (meta_background_parent_class)->dispose (object);
+ switch (prop_id)
+ {
+ case PROP_META_SCREEN:
+ g_value_set_object (value, priv->screen);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
}
-static void
-meta_background_finalize (GObject *object)
+static gboolean
+need_prerender (MetaBackground *self)
{
- MetaBackground *self = META_BACKGROUND (object);
MetaBackgroundPrivate *priv = self->priv;
+ CoglTexture *texture1 = priv->background_image1 ? meta_background_image_get_texture (priv->background_image1) : NULL;
+ CoglTexture *texture2 = priv->background_image2 ? meta_background_image_get_texture (priv->background_image2) : NULL;
- g_free (priv->filename);
+ if (texture1 == NULL && texture2 == NULL)
+ return FALSE;
- G_OBJECT_CLASS (meta_background_parent_class)->finalize (object);
-}
+ if (texture2 == NULL && priv->style == G_DESKTOP_BACKGROUND_STYLE_WALLPAPER)
+ return FALSE;
-static void
-ensure_pipeline (MetaBackground *self)
-{
- if (self->priv->pipeline == NULL)
- self->priv->pipeline = COGL_PIPELINE (meta_create_texture_pipeline (NULL));
+ return TRUE;
}
static void
-set_brightness (MetaBackground *self,
- gfloat brightness)
+mark_changed (MetaBackground *self)
{
MetaBackgroundPrivate *priv = self->priv;
+ int i;
- if (priv->brightness == brightness)
- return;
-
- priv->brightness = brightness;
+ if (!need_prerender (self))
+ free_fbos (self);
- if (clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL) &&
- 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);
- }
- else
- {
- ensure_pipeline (self);
- CoglColor blend_color;
- cogl_color_init_from_4f (&blend_color, brightness, brightness, brightness, 1.0);
- cogl_pipeline_set_layer_combine (priv->pipeline, 1, "RGB=MODULATE(PREVIOUS, CONSTANT) A=REPLACE(PREVIOUS)", NULL);
- cogl_pipeline_set_layer_combine_constant (priv->pipeline, 1, &blend_color);
- }
-
- clutter_content_invalidate (CLUTTER_CONTENT (self));
+ for (i = 0; i < priv->n_monitors; i++)
+ priv->monitors[i].dirty = TRUE;
- g_object_notify (G_OBJECT (self), "brightness");
+ g_signal_emit (self, signals[CHANGED], 0);
}
static void
-set_vignette_sharpness (MetaBackground *self,
- gfloat sharpness)
+on_background_loaded (MetaBackgroundImage *image,
+ MetaBackground *self)
{
- MetaBackgroundPrivate *priv = self->priv;
-
- if (priv->vignette_sharpness == sharpness)
- return;
-
- priv->vignette_sharpness = sharpness;
-
- if (!clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL))
- return;
-
- 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");
+ mark_changed (self);
}
static void
-add_vignette (MetaBackground *self)
+set_filename (MetaBackground *self,
+ char **filenamep,
+ MetaBackgroundImage **imagep,
+ const char *filename)
{
- MetaBackgroundPrivate *priv = self->priv;
- static CoglSnippet *snippet = NULL;
-
- if (!clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL))
- return;
-
- ensure_pipeline (self);
-
- /* Cogl automatically caches pipelines with no eviction policy,
- * so we need to prevent identical pipelines from getting cached
- * separately, by reusing the same fragement shader snippet.
- */
- if (snippet == NULL)
- snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, FRAGMENT_SHADER_DECLARATIONS, VIGNETTE_CODE);
-
- cogl_pipeline_add_snippet (priv->pipeline, snippet);
+ if (g_strcmp0 (filename, *filenamep) != 0)
+ {
+ g_free (*filenamep);
+ *filenamep = g_strdup (filename);
- cogl_pipeline_set_uniform_1f (priv->pipeline,
- cogl_pipeline_get_uniform_location (priv->pipeline,
- "brightness"),
- priv->brightness);
+ if (*imagep)
+ {
+ g_signal_handlers_disconnect_by_func (*imagep,
+ (gpointer)on_background_loaded,
+ self);
+ g_object_unref (*imagep);
+ *imagep = NULL;
+ }
- cogl_pipeline_set_uniform_1f (priv->pipeline,
- cogl_pipeline_get_uniform_location (priv->pipeline,
- "vignette_sharpness"),
- priv->vignette_sharpness);
+ if (filename)
+ {
+ MetaBackgroundImageCache *cache = meta_background_image_cache_get_default ();
+ *imagep = meta_background_image_cache_load (cache, filename);
+ g_signal_connect (*imagep, "loaded",
+ G_CALLBACK (on_background_loaded), self);
+ }
+ }
}
static void
-set_effects (MetaBackground *self,
- MetaBackgroundEffects effects)
+meta_background_dispose (GObject *object)
{
+ MetaBackground *self = META_BACKGROUND (object);
MetaBackgroundPrivate *priv = self->priv;
- priv->effects = effects;
+ free_color_texture (self);
+ free_wallpaper_texture (self);
- if ((priv->effects & META_BACKGROUND_EFFECTS_VIGNETTE))
- add_vignette (self);
+ set_filename (self, &priv->filename1, &priv->background_image1, NULL);
+ set_filename (self, &priv->filename2, &priv->background_image2, NULL);
- clutter_content_invalidate (CLUTTER_CONTENT (self));
-}
+ set_screen (self, NULL);
-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;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
+ G_OBJECT_CLASS (meta_background_parent_class)->dispose (object);
}
static void
-meta_background_get_property (GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec)
+meta_background_finalize (GObject *object)
{
- 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;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
+ G_OBJECT_CLASS (meta_background_parent_class)->finalize (object);
}
static void
meta_background_class_init (MetaBackgroundClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
- GParamSpec *param_spec;
+ GParamSpec *param_spec;
g_type_class_add_private (klass, sizeof (MetaBackgroundPrivate));
@@ -699,49 +298,24 @@ meta_background_class_init (MetaBackgroundClass *klass)
object_class->set_property = meta_background_set_property;
object_class->get_property = meta_background_get_property;
+ signals[CHANGED] =
+ g_signal_new ("changed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+
param_spec = g_param_spec_object ("meta-screen",
"MetaScreen",
"MetaScreen",
META_TYPE_SCREEN,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
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_flags ("effects",
- "Effects",
- "Set to enable vignette",
- 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
@@ -753,492 +327,535 @@ meta_background_init (MetaBackground *self)
}
static void
-unset_texture (MetaBackground *self)
+get_texture_area (MetaBackground *self,
+ cairo_rectangle_int_t *monitor_rect,
+ CoglTexture *texture,
+ cairo_rectangle_int_t *texture_area)
{
MetaBackgroundPrivate *priv = self->priv;
- if (priv->pipeline != NULL)
- cogl_pipeline_set_layer_texture (priv->pipeline, 0, NULL);
+ cairo_rectangle_int_t image_area;
+ int screen_width, screen_height;
+ float texture_width, texture_height;
+ float monitor_x_scale, monitor_y_scale;
- g_clear_pointer (&priv->texture,
- (GDestroyNotify)
- cogl_object_unref);
-}
+ texture_width = cogl_texture_get_width (texture);
+ texture_height = cogl_texture_get_height (texture);
-static void
-set_texture (MetaBackground *self,
- CoglTexture *texture)
-{
- MetaBackgroundPrivate *priv = self->priv;
+ 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 = *monitor_rect;
+ break;
+ case G_DESKTOP_BACKGROUND_STYLE_WALLPAPER:
+ meta_screen_get_size (priv->screen, &screen_width, &screen_height);
+
+ /* Start off by centering a tile in the middle of the
+ * total screen area.
+ */
+ image_area.x = (screen_width - texture_width) / 2.0;
+ image_area.y = (screen_height - texture_height) / 2.0;
+ image_area.width = texture_width;
+ image_area.height = texture_height;
+
+ /* Translate into the coordinate system of the particular monitor */
+ image_area.x -= monitor_rect->x;
+ image_area.y -= monitor_rect->y;
+
+ *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 = monitor_rect->x + monitor_rect->width / 2 - image_area.width / 2;
+ image_area.y = monitor_rect->y + monitor_rect->height / 2 - image_area.height / 2;
+
+ *texture_area = image_area;
+ 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_rect->width / texture_width;
+ monitor_y_scale = monitor_rect->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 = monitor_rect->width;
+ image_area.height = texture_height * monitor_x_scale;
- priv->texture = texture;
- cogl_pipeline_set_layer_texture (priv->pipeline, 0, priv->texture);
-}
+ /* Position image centered vertically in actor */
+ image_area.x = monitor_rect->x;
+ image_area.y = monitor_rect->y + monitor_rect->height / 2 - image_area.height / 2;
+ }
+ else
+ {
+ /* Scale image to exactly fit actor vertically */
+ image_area.width = texture_width * monitor_y_scale;
+ image_area.height = monitor_rect->height;
-static void
-set_style (MetaBackground *self,
- GDesktopBackgroundStyle style)
-{
- MetaBackgroundPrivate *priv = self->priv;
- CoglPipelineWrapMode wrap_mode;
+ /* Position image centered horizontally in actor */
+ image_area.x = monitor_rect->x + monitor_rect->width / 2 - image_area.width / 2;
+ image_area.y = monitor_rect->y;
+ }
- priv->style = style;
+ *texture_area = image_area;
+ break;
- wrap_mode = get_wrap_mode (self);
- cogl_pipeline_set_layer_wrap_mode (priv->pipeline, 0, wrap_mode);
-}
+ 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);
-static void
-set_filename (MetaBackground *self,
- const char *filename)
-{
- MetaBackgroundPrivate *priv = self->priv;
+ /* unclipped texture area is whole screen */
+ image_area.width = screen_width;
+ image_area.height = screen_height;
- g_free (priv->filename);
- priv->filename = g_strdup (filename);
+ /* But make (0,0) line up with the appropriate monitor */
+ image_area.x = -monitor_rect->x;
+ image_area.y = -monitor_rect->y;
+
+ *texture_area = image_area;
+ break;
+ }
+ }
}
-/**
- * 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)
+static void
+draw_texture (MetaBackground *self,
+ CoglFramebuffer *framebuffer,
+ CoglPipeline *pipeline,
+ CoglTexture *texture,
+ cairo_rectangle_int_t *monitor_area)
{
- ClutterBackend *backend = clutter_get_default_backend ();
- CoglContext *ctx = clutter_backend_get_cogl_context (backend);
MetaBackgroundPrivate *priv = self->priv;
- CoglTexture *texture;
- guint width, height;
- uint8_t pixels[8];
-
- ensure_pipeline (self);
-
- unset_texture (self);
- set_style (self, G_DESKTOP_BACKGROUND_STYLE_NONE);
+ cairo_rectangle_int_t texture_area;
- priv->shading_direction = shading_direction;
+ get_texture_area (self, monitor_area, texture, &texture_area);
- switch (priv->shading_direction)
+ switch (priv->style)
{
- 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 ();
+ case G_DESKTOP_BACKGROUND_STYLE_STRETCHED:
+ case G_DESKTOP_BACKGROUND_STYLE_WALLPAPER:
+ case G_DESKTOP_BACKGROUND_STYLE_ZOOM:
+ case G_DESKTOP_BACKGROUND_STYLE_SPANNED:
+ /* Draw the entire monitor */
+ cogl_framebuffer_draw_textured_rectangle (framebuffer,
+ pipeline,
+ 0,
+ 0,
+ monitor_area->width,
+ monitor_area->height,
+ - texture_area.x / (float)texture_area.width,
+ - texture_area.y / (float)texture_area.height,
+ (monitor_area->width - texture_area.x) / (float)texture_area.width,
+ (monitor_area->height - texture_area.y) / (float)texture_area.height);
+ /* Draw just the texture */
+ break;
+ case G_DESKTOP_BACKGROUND_STYLE_CENTERED:
+ case G_DESKTOP_BACKGROUND_STYLE_SCALED:
+ cogl_framebuffer_draw_textured_rectangle (framebuffer,
+ pipeline,
+ texture_area.x, texture_area.y,
+ texture_area.x + texture_area.width,
+ texture_area.y + texture_area.height,
+ 0, 0, 1.0, 1.0);
+ case G_DESKTOP_BACKGROUND_STYLE_NONE:
+ break;
+ default:
+ g_return_if_reached();
}
-
- pixels[0] = color->red;
- pixels[1] = color->green;
- pixels[2] = color->blue;
- pixels[3] = 0xFF;
- pixels[4] = second_color->red;
- pixels[5] = second_color->green;
- pixels[6] = second_color->blue;
- pixels[7] = 0xFF;
-
- texture = COGL_TEXTURE (cogl_texture_2d_new_from_data (ctx, width, height,
- COGL_PIXEL_FORMAT_RGB_888,
- 4,
- pixels,
- NULL));
- 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)
+static void
+ensure_color_texture (MetaBackground *self)
{
- ClutterBackend *backend = clutter_get_default_backend ();
- CoglContext *ctx = clutter_backend_get_cogl_context (backend);
MetaBackgroundPrivate *priv = self->priv;
- CoglTexture *texture;
- ClutterActor *stage = meta_get_stage_for_screen (priv->screen);
- ClutterColor stage_color;
- uint8_t pixels[4];
- ensure_pipeline (self);
-
- unset_texture (self);
- set_style (self, G_DESKTOP_BACKGROUND_STYLE_NONE);
-
- if (color == NULL)
+ if (priv->color_texture == NULL)
{
- clutter_actor_get_background_color (stage, &stage_color);
- color = &stage_color;
- }
+ ClutterBackend *backend = clutter_get_default_backend ();
+ CoglContext *ctx = clutter_backend_get_cogl_context (backend);
+ uint8_t pixels[8];
+ int width, height;
- pixels[0] = color->red;
- pixels[1] = color->green;
- pixels[2] = color->blue;
- pixels[3] = 0xFF;
-
- texture = COGL_TEXTURE (cogl_texture_2d_new_from_data (ctx, 1, 1,
- COGL_PIXEL_FORMAT_RGB_888,
- 4,
- pixels,
- NULL));
- set_texture (self, COGL_TEXTURE (texture));
-}
-
-typedef struct
-{
- GDesktopBackgroundStyle style;
- char *filename;
-} LoadFileTaskData;
+ if (priv->shading_direction == G_DESKTOP_BACKGROUND_SHADING_SOLID)
+ {
+ width = 1;
+ height = 1;
-static LoadFileTaskData *
-load_file_task_data_new (const char *filename,
- GDesktopBackgroundStyle style)
-{
- LoadFileTaskData *task_data;
+ pixels[0] = priv->color.red;
+ pixels[1] = priv->color.green;
+ pixels[2] = priv->color.blue;
+ pixels[3] = 0xFF;
+ }
+ else
+ {
+ 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 ();
+ }
- task_data = g_slice_new (LoadFileTaskData);
- task_data->style = style;
- task_data->filename = g_strdup (filename);
+ pixels[0] = priv->color.red;
+ pixels[1] = priv->color.green;
+ pixels[2] = priv->color.blue;
+ pixels[3] = 0xFF;
+ pixels[4] = priv->second_color.red;
+ pixels[5] = priv->second_color.green;
+ pixels[6] = priv->second_color.blue;
+ pixels[7] = 0xFF;
+ }
- return task_data;
+ priv->color_texture = COGL_TEXTURE (cogl_texture_2d_new_from_data (ctx, width, height,
+ COGL_PIXEL_FORMAT_RGB_888,
+ 4,
+ pixels,
+ NULL));
+ }
}
-static void
-load_file_task_data_free (LoadFileTaskData *task_data)
-{
- g_free (task_data->filename);
- g_slice_free (LoadFileTaskData, task_data);
-}
+typedef enum {
+ PIPELINE_REPLACE,
+ PIPELINE_ADD,
+ PIPELINE_OVER_REVERSE,
+} PipelineType;
-static void
-load_file (GTask *task,
- MetaBackground *self,
- LoadFileTaskData *task_data,
- GCancellable *cancellable)
+static CoglPipeline *
+create_pipeline (PipelineType type)
{
- GError *error = NULL;
- GdkPixbuf *pixbuf;
-
- pixbuf = gdk_pixbuf_new_from_file (task_data->filename,
- &error);
+ const char * const blend_strings[3] = {
+ [PIPELINE_REPLACE] = "RGBA = ADD (SRC_COLOR, 0)",
+ [PIPELINE_ADD] = "RGBA = ADD (SRC_COLOR, DST_COLOR)",
+ [PIPELINE_OVER_REVERSE] = "RGBA = ADD (SRC_COLOR * (1 - DST_COLOR[A]), DST_COLOR)",
+ };
+ static CoglPipeline *templates[3];
- if (pixbuf == NULL)
+ if (templates[type] == NULL)
{
- g_task_return_error (task, error);
- return;
+ templates[type] = meta_create_texture_pipeline (NULL);
+ cogl_pipeline_set_blend (templates[type], blend_strings[type], NULL);
}
- g_task_return_pointer (task, pixbuf, (GDestroyNotify) g_object_unref);
+ return cogl_pipeline_copy (templates[type]);
}
-/**
- * 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)
+static gboolean
+texture_has_alpha (CoglTexture *texture)
{
- 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);
+ if (!texture)
+ return FALSE;
- g_task_run_in_thread (task, (GTaskThreadFunc) load_file);
- g_object_unref (task);
+ switch (cogl_texture_get_components (texture))
+ {
+ case COGL_TEXTURE_COMPONENTS_A:
+ case COGL_TEXTURE_COMPONENTS_RGBA:
+ return TRUE;
+ case COGL_TEXTURE_COMPONENTS_RG:
+ case COGL_TEXTURE_COMPONENTS_RGB:
+ case COGL_TEXTURE_COMPONENTS_DEPTH:
+ return FALSE;
+ default:
+ g_assert_not_reached ();
+ }
}
-/**
- * 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 void
+ensure_wallpaper_texture (MetaBackground *self,
+ CoglTexture *texture)
{
- ClutterBackend *backend = clutter_get_default_backend ();
- CoglContext *ctx = clutter_backend_get_cogl_context (backend);
- GTask *task;
- LoadFileTaskData *task_data;
- CoglTexture *texture;
- GdkPixbuf *pixbuf;
- int width, height, row_stride;
- guchar *pixels;
- gboolean has_alpha;
- gboolean loaded = FALSE;
- CoglPixelFormat pixel_format;
+ MetaBackgroundPrivate *priv = self->priv;
- g_return_val_if_fail (g_task_is_valid (result, self), FALSE);
+ if (priv->wallpaper_texture == NULL)
+ {
+ int width = cogl_texture_get_width (texture);
+ int height = cogl_texture_get_height (texture);
+ CoglFramebuffer *fbo;
+ CoglError *catch_error = NULL;
+ CoglPipeline *pipeline;
- task = G_TASK (result);
+ priv->wallpaper_texture = meta_create_large_texture (width, height,
+ COGL_TEXTURE_COMPONENTS_RGBA);
+ fbo = cogl_offscreen_new_with_texture (priv->wallpaper_texture);
- pixbuf = g_task_propagate_pointer (task, error);
+ if (!cogl_framebuffer_allocate (fbo, &catch_error))
+ {
+ cogl_error_free (catch_error);
+ return;
+ }
- if (pixbuf == NULL)
- goto out;
+ cogl_framebuffer_orthographic (fbo, 0, 0,
+ width, height, -1., 1.);
- task_data = g_task_get_task_data (task);
+ pipeline = create_pipeline (PIPELINE_REPLACE);
+ cogl_pipeline_set_layer_texture (pipeline, 0, texture);
+ cogl_framebuffer_draw_textured_rectangle (fbo, pipeline, 0, 0, width, height,
+ 0., 0., 1., 1.);
+ cogl_object_unref (pipeline);
- 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);
+ if (texture_has_alpha (texture))
+ {
+ ensure_color_texture (self);
- pixel_format = has_alpha ? COGL_PIXEL_FORMAT_RGBA_8888 : COGL_PIXEL_FORMAT_RGB_888;
+ pipeline = create_pipeline (PIPELINE_OVER_REVERSE);
+ cogl_pipeline_set_layer_texture (pipeline, 0, priv->color_texture);
+ cogl_framebuffer_draw_rectangle (fbo, pipeline, 0, 0, width, height);
+ cogl_object_unref (pipeline);
+ }
- texture = COGL_TEXTURE (cogl_texture_2d_new_from_data (ctx, width, height,
- pixel_format,
- row_stride,
- pixels,
- NULL));
+ cogl_object_unref (fbo);
+ }
+}
- if (texture == NULL)
+static CoglPipelineWrapMode
+get_wrap_mode (GDesktopBackgroundStyle style)
+{
+ switch (style)
{
- g_set_error_literal (error,
- COGL_BITMAP_ERROR,
- COGL_BITMAP_ERROR_FAILED,
- _("background texture could not be created from file"));
- goto out;
+ 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;
}
+}
- ensure_pipeline (self);
- unset_texture (self);
- set_style (self, task_data->style);
- set_filename (self, task_data->filename);
- set_texture (self, texture);
+CoglTexture *
+meta_background_get_texture (MetaBackground *self,
+ int monitor_index,
+ cairo_rectangle_int_t *texture_area,
+ CoglPipelineWrapMode *wrap_mode)
+{
+ MetaBackgroundPrivate *priv;
+ MetaBackgroundMonitor *monitor;
+ MetaRectangle geometry;
+ cairo_rectangle_int_t monitor_area;
+ CoglTexture *texture1, *texture2;
- clutter_content_invalidate (CLUTTER_CONTENT (self));
- loaded = TRUE;
+ g_return_if_fail (META_IS_BACKGROUND (self));
+ priv = self->priv;
+ g_return_if_fail (monitor_index <= 0 && monitor_index < priv->n_monitors);
-out:
- if (pixbuf != NULL)
- g_object_unref (pixbuf);
- return loaded;
-}
+ monitor = &priv->monitors[monitor_index];
-/**
- * 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;
+ meta_screen_get_monitor_geometry (priv->screen, monitor_index, &geometry);
+ monitor_area.x = geometry.x;
+ monitor_area.y = geometry.y;
+ monitor_area.width = geometry.width;
+ monitor_area.height = geometry.height;
- background = META_BACKGROUND (g_object_new (META_TYPE_BACKGROUND,
- "meta-screen", self->priv->screen,
- "monitor", monitor,
- NULL));
+ texture1 = priv->background_image1 ? meta_background_image_get_texture (priv->background_image1) : NULL;
+ texture2 = priv->background_image2 ? meta_background_image_get_texture (priv->background_image2) : NULL;
- background->priv->brightness = self->priv->brightness;
+ if (texture1 == NULL && texture2 == NULL)
+ {
+ ensure_color_texture (self);
+ if (texture_area)
+ *texture_area = monitor_area;
+ if (wrap_mode)
+ *wrap_mode = COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE;
+ return priv->color_texture;
+ }
- 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);
+ if (texture2 == NULL && priv->style == G_DESKTOP_BACKGROUND_STYLE_WALLPAPER &&
+ priv->shading_direction == G_DESKTOP_BACKGROUND_SHADING_SOLID)
+ {
+ ensure_wallpaper_texture (self, texture1);
+ if (texture_area)
+ get_texture_area (self, &monitor_area, priv->wallpaper_texture,
+ texture_area);
+ if (wrap_mode)
+ *wrap_mode = COGL_PIPELINE_WRAP_MODE_REPEAT;
+ return priv->wallpaper_texture;
+ }
- /* 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)
+ if (monitor->dirty)
{
- 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;
+ CoglError *catch_error = NULL;
- if (effects != self->priv->effects)
+ if (monitor->texture == NULL)
{
- set_effects (background, effects);
+ monitor->texture = meta_create_large_texture (monitor_area.width, monitor_area.height,
+ COGL_TEXTURE_COMPONENTS_RGBA);
+ monitor->fbo = cogl_offscreen_new_with_texture (monitor->texture);
+ }
- if (effects & META_BACKGROUND_EFFECTS_VIGNETTE)
- {
- set_brightness (background, self->priv->brightness);
- set_vignette_sharpness (background, self->priv->vignette_sharpness);
- }
+ if (!cogl_framebuffer_allocate (monitor->fbo, &catch_error))
+ {
+ cogl_error_free (catch_error);
+ return NULL;
+ }
+
+ cogl_framebuffer_orthographic (monitor->fbo, 0, 0,
+ monitor_area.width, monitor_area.height, -1., 1.);
+
+ if (texture2 != NULL && priv->blend_factor != 0.0)
+ {
+ CoglPipeline *pipeline = create_pipeline (PIPELINE_REPLACE);
+ cogl_pipeline_set_color4f (pipeline,
+ priv->blend_factor, priv->blend_factor, priv->blend_factor, priv->blend_factor);
+ cogl_pipeline_set_layer_texture (pipeline, 0, texture2);
+ cogl_pipeline_set_layer_wrap_mode (pipeline, 0, get_wrap_mode (priv->style));
+
+ draw_texture (self,
+ monitor->fbo, pipeline,
+ texture2, &monitor_area);
+
+ cogl_object_unref (pipeline);
}
else
{
- background->priv->effects = self->priv->effects;
+ cogl_framebuffer_clear4f (monitor->fbo,
+ COGL_BUFFER_BIT_COLOR,
+ 0.0, 0.0, 0.0, 0.0);
}
- }
- 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 (texture1 != NULL &&
+ !(texture2 != NULL && priv->blend_factor == 1.0 && !texture_has_alpha (texture2)))
+ {
+ CoglPipeline *pipeline = create_pipeline (PIPELINE_ADD);
+ cogl_pipeline_set_color4f (pipeline,
+ (1 - priv->blend_factor),
+ (1 - priv->blend_factor),
+ (1 - priv->blend_factor),
+ (1 - priv->blend_factor));;
+ cogl_pipeline_set_layer_texture (pipeline, 0, texture1);
+ cogl_pipeline_set_layer_wrap_mode (pipeline, 0, get_wrap_mode (priv->style));
+
+ draw_texture (self,
+ monitor->fbo, pipeline,
+ texture1, &monitor_area);
+
+ cogl_object_unref (pipeline);
+ }
- if (effects & META_BACKGROUND_EFFECTS_VIGNETTE)
+ if (!((texture2 != NULL && priv->blend_factor == 1.0 && !texture_has_alpha (texture2)) ||
+ (texture1 != NULL && !texture_has_alpha (texture1))))
{
- set_brightness (background, self->priv->brightness);
- set_vignette_sharpness (background, self->priv->vignette_sharpness);
+ CoglPipeline *pipeline = create_pipeline (PIPELINE_OVER_REVERSE);
+
+ ensure_color_texture (self);
+ cogl_pipeline_set_layer_texture (pipeline, 0, priv->color_texture);
+ cogl_framebuffer_draw_rectangle (monitor->fbo,
+ pipeline,
+ 0, 0,
+ monitor_area.width, monitor_area.height);
+ cogl_object_unref (pipeline);
}
- }
- clutter_content_invalidate (CLUTTER_CONTENT (background));
+ monitor->dirty = FALSE;
+ }
- return background;
+ if (texture_area)
+ *texture_area = monitor_area;
+ if (wrap_mode)
+ *wrap_mode = COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE;
+ return monitor->texture;
}
-/**
- * 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 given a vignette by setting @effects
- *
- * Return value: the newly created background content
- */
+
MetaBackground *
-meta_background_new (MetaScreen *screen,
- int monitor,
- MetaBackgroundEffects effects)
+meta_background_new (MetaScreen *screen)
{
- MetaBackground *background;
-
- background = META_BACKGROUND (g_object_new (META_TYPE_BACKGROUND,
- "meta-screen", screen,
- "monitor", monitor,
- "effects", effects,
- NULL));
- return background;
+ return g_object_new (META_TYPE_BACKGROUND,
+ "screen", screen,
+ NULL);
}
-/**
- * meta_background_get_style:
- * @self: a #MetaBackground
- *
- * Returns the current background style.
- *
- * Return value: a #GDesktopBackgroundStyle
- */
-GDesktopBackgroundStyle
-meta_background_get_style (MetaBackground *self)
+void
+meta_background_set_color (MetaBackground *self,
+ ClutterColor *color)
{
- return self->priv->style;
-}
+ ClutterColor dummy = { 0 };
-/**
- * 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;
+ g_return_if_fail (META_IS_BACKGROUND (self));
+ g_return_if_fail (color != NULL);
+
+ meta_background_set_gradient (self,
+ G_DESKTOP_BACKGROUND_SHADING_SOLID,
+ color, &dummy);
}
-/**
- * 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)
+void
+meta_background_set_gradient (MetaBackground *self,
+ GDesktopBackgroundShading shading_direction,
+ ClutterColor *color,
+ ClutterColor *second_color)
{
- return &self->priv->color;
+ MetaBackgroundPrivate *priv;
+
+ g_return_if_fail (META_IS_BACKGROUND (self));
+ g_return_if_fail (color != NULL);
+ g_return_if_fail (second_color != NULL);
+
+ priv = self->priv;
+
+ priv->shading_direction = shading_direction;
+ priv->color = *color;
+ priv->second_color = *second_color;
+
+ free_color_texture (self);
+ free_wallpaper_texture (self);
+ mark_changed (self);
}
-/**
- * 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)
+void
+meta_background_set_filename (MetaBackground *self,
+ const char *filename,
+ GDesktopBackgroundStyle style)
{
- return &self->priv->second_color;
+ g_return_if_fail (META_IS_BACKGROUND (self));
+
+ meta_background_set_blend (self, filename, NULL, 0.0, style);
}
-/**
- * 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)
+void
+meta_background_set_blend (MetaBackground *self,
+ const char *filename1,
+ const char *filename2,
+ double blend_factor,
+ GDesktopBackgroundStyle style)
{
- return self->priv->filename;
+ MetaBackgroundPrivate *priv;
+
+ g_return_if_fail (META_IS_BACKGROUND (self));
+ g_return_if_fail (blend_factor >= 0.0 && blend_factor <= 1.0);
+
+ priv = self->priv;
+
+ set_filename (self, &priv->filename1, &priv->background_image1, filename1);
+ set_filename (self, &priv->filename2, &priv->background_image2, filename2);
+
+ priv->blend_factor = blend_factor;
+ priv->style = style;
+
+ free_wallpaper_texture (self);
+ mark_changed (self);
}
diff --git a/src/compositor/plugins/default.c b/src/compositor/plugins/default.c
index 7c2fccf01..38a66e7df 100644
--- a/src/compositor/plugins/default.c
+++ b/src/compositor/plugins/default.c
@@ -303,15 +303,16 @@ on_monitors_changed (MetaScreen *screen,
for (i = 0; i < n; i++)
{
MetaRectangle rect;
- ClutterActor *background;
+ ClutterActor *background_actor;
+ MetaBackground *background;
ClutterColor color;
meta_screen_get_monitor_geometry (screen, i, &rect);
- background = meta_background_actor_new ();
+ background_actor = meta_background_actor_new (screen, i);
- clutter_actor_set_position (background, rect.x, rect.y);
- clutter_actor_set_size (background, rect.width, rect.height);
+ clutter_actor_set_position (background_actor, rect.x, rect.y);
+ clutter_actor_set_size (background_actor, rect.width, rect.height);
/* Don't use rand() here, mesa calls srand() internally when
parsing the driconf XML, but it's nice if the colors are
@@ -322,9 +323,13 @@ on_monitors_changed (MetaScreen *screen,
g_rand_int_range (rand, 0, 255),
g_rand_int_range (rand, 0, 255),
255);
- clutter_actor_set_background_color (background, &color);
- clutter_actor_add_child (self->priv->background_group, background);
+ background = meta_background_new (screen);
+ meta_background_set_color (background, &color);
+ meta_background_actor_set_background (META_BACKGROUND_ACTOR (background_actor), background);
+ g_object_unref (background);
+
+ clutter_actor_add_child (self->priv->background_group, background_actor);
}
g_rand_free (rand);
diff --git a/src/meta/meta-background-actor.h b/src/meta/meta-background-actor.h
index 56e97688f..08dfc2130 100644
--- a/src/meta/meta-background-actor.h
+++ b/src/meta/meta-background-actor.h
@@ -22,10 +22,8 @@
#define META_BACKGROUND_ACTOR_H
#include <clutter/clutter.h>
-#include <cogl/cogl.h>
-
-#include <meta/gradient.h>
#include <meta/screen.h>
+#include <meta/meta-background.h>
#include <gsettings-desktop-schemas/gdesktop-enums.h>
@@ -63,6 +61,15 @@ struct _MetaBackgroundActor
GType meta_background_actor_get_type (void);
-ClutterActor *meta_background_actor_new (void);
+ClutterActor *meta_background_actor_new (MetaScreen *screen,
+ int monitor);
+
+void meta_background_actor_set_background (MetaBackgroundActor *self,
+ MetaBackground *background);
+
+void meta_background_actor_add_vignette (MetaBackgroundActor *self,
+ double brightness,
+ double sharpness);
+void meta_background_actor_remove_vignette (MetaBackgroundActor *self);
#endif /* META_BACKGROUND_ACTOR_H */
diff --git a/src/meta/meta-background-image.h b/src/meta/meta-background-image.h
new file mode 100644
index 000000000..f0af93eb9
--- /dev/null
+++ b/src/meta/meta-background-image.h
@@ -0,0 +1,76 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * MetaBackgroundImageCache:
+ *
+ * Simple cache for background textures loaded from files
+ *
+ * Copyright 2014 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __META_BACKGROUND_IMAGE_H__
+#define __META_BACKGROUND_IMAGE_H__
+
+#include <glib-object.h>
+#include <cogl/cogl.h>
+
+#define META_TYPE_BACKGROUND_IMAGE (meta_background_image_get_type ())
+#define META_BACKGROUND_IMAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_BACKGROUND_IMAGE, MetaBackgroundImage))
+#define META_BACKGROUND_IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_BACKGROUND_IMAGE, MetaBackgroundImageClass))
+#define META_IS_BACKGROUND_IMAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_BACKGROUND_IMAGE))
+#define META_IS_BACKGROUND_IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_BACKGROUND_IMAGE))
+#define META_BACKGROUND_IMAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_BACKGROUND_IMAGE, MetaBackgroundImageClass))
+
+/**
+ * MetaBackgroundImage:
+ *
+ * #MetaBackgroundImage is an object that represents a loaded or loading background image.
+ */
+typedef struct _MetaBackgroundImage MetaBackgroundImage;
+typedef struct _MetaBackgroundImageClass MetaBackgroundImageClass;
+
+GType meta_background_image_get_type (void);
+
+gboolean meta_background_image_is_loaded (MetaBackgroundImage *image);
+gboolean meta_background_image_get_success (MetaBackgroundImage *image);
+CoglTexture *meta_background_image_get_texture (MetaBackgroundImage *image);
+
+#define META_TYPE_BACKGROUND_IMAGE_CACHE (meta_background_image_cache_get_type ())
+#define META_BACKGROUND_IMAGE_CACHE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_BACKGROUND_IMAGE_CACHE, MetaBackgroundImageCache))
+#define META_BACKGROUND_IMAGE_CACHE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_BACKGROUND_IMAGE_CACHE, MetaBackgroundImageCacheClass))
+#define META_IS_BACKGROUND_IMAGE_CACHE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_BACKGROUND_IMAGE_CACHE))
+#define META_IS_BACKGROUND_IMAGE_CACHE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_BACKGROUND_IMAGE_CACHE))
+#define META_BACKGROUND_IMAGE_CACHE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_BACKGROUND_IMAGE_CACHE, MetaBackgroundImageCacheClass))
+
+/**
+ * MetaBackgroundImageCache:
+ *
+ * #MetaBackgroundImageCache caches loading of textures for backgrounds; there's actually
+ * nothing background specific about it, other than it is tuned to work well for
+ * large images as typically are used for backgrounds.
+ */
+typedef struct _MetaBackgroundImageCache MetaBackgroundImageCache;
+typedef struct _MetaBackgroundImageCacheClass MetaBackgroundImageCacheClass;
+
+MetaBackgroundImageCache *meta_background_image_cache_get_default (void);
+
+GType meta_background_image_cache_get_type (void);
+
+MetaBackgroundImage *meta_background_image_cache_load (MetaBackgroundImageCache *cache,
+ const char *filename);
+void meta_background_image_cache_purge (MetaBackgroundImageCache *cache,
+ const char *filename);
+
+#endif /* __META_BACKGROUND_IMAGE_H__ */
diff --git a/src/meta/meta-background.h b/src/meta/meta-background.h
index a861600f8..38706dc6e 100644
--- a/src/meta/meta-background.h
+++ b/src/meta/meta-background.h
@@ -1,8 +1,8 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
- * meta-background.h: CoglTexture for paintnig the system background
+ * meta-background-actor.h: for painting the root window background
*
- * Copyright 2013 Red Hat, Inc.
+ * Copyright 2010 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
@@ -21,20 +21,16 @@
#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>
+#include <meta/screen.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.
+ * This class handles tracking and painting the root window background.
+ * By integrating with #MetaWindowGroup we can avoid painting parts of
+ * the background that are obscured by other windows.
*/
#define META_TYPE_BACKGROUND (meta_background_get_type ())
@@ -48,20 +44,6 @@ typedef struct _MetaBackground MetaBackground;
typedef struct _MetaBackgroundClass MetaBackgroundClass;
typedef struct _MetaBackgroundPrivate MetaBackgroundPrivate;
-/**
- * MetaBackgroundEffects:
- * @META_BACKGROUND_EFFECTS_NONE: No effect
- * @META_BACKGROUND_EFFECTS_VIGNETTE: Vignette
- *
- * Which effects to enable on the background
- */
-
-typedef enum
-{
- META_BACKGROUND_EFFECTS_NONE = 0,
- META_BACKGROUND_EFFECTS_VIGNETTE = 1 << 1,
-} MetaBackgroundEffects;
-
struct _MetaBackgroundClass
{
/*< private >*/
@@ -70,7 +52,6 @@ struct _MetaBackgroundClass
struct _MetaBackground
{
- /*< private >*/
GObject parent;
MetaBackgroundPrivate *priv;
@@ -78,33 +59,21 @@ struct _MetaBackground
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_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);
+MetaBackground *meta_background_new (MetaScreen *screen);
+
+void meta_background_set_color (MetaBackground *self,
+ ClutterColor *color);
+void meta_background_set_gradient (MetaBackground *self,
+ GDesktopBackgroundShading shading_direction,
+ ClutterColor *color,
+ ClutterColor *second_color);
+void meta_background_set_filename (MetaBackground *self,
+ const char *filename,
+ GDesktopBackgroundStyle style);
+void meta_background_set_blend (MetaBackground *self,
+ const char *filename1,
+ const char *filename2,
+ double blend_factor,
+ GDesktopBackgroundStyle style);
#endif /* META_BACKGROUND_H */