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-actor.h \ - meta/meta-background-group.h \ meta/meta-background.h \ + meta/meta-background-actor.h \ + meta/meta-background-image.h \ + meta/meta-background-group.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 +/* + * 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 +#include #include -#include - #include "cogl-utils.h" -#include "compositor-private.h" +#include "clutter-utils.h" #include -#include #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 . + */ + +/** + * SECTION:meta-background-image + * @title: MetaBackgroundImage + * @short_description: objects holding images loaded from files, used for backgrounds + */ + +#include + +#include +#include +#include +#include +#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 + +#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,277 +17,552 @@ * along with this program; if not, see . */ -/** - * SECTION:meta-background - * @title: MetaBackground - * @short_description: ClutterContent for painting the system background - * - */ - -#include - -#include - -#include "cogl-utils.h" -#include "compositor-private.h" -#include "mutter-enum-types.h" -#include #include -#include "util-private.h" -#include "meta-background-actor-private.h" +#include +#include "meta-background-private.h" +#include "cogl-utils.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" \ +enum +{ + CHANGED, + LAST_SIGNAL +}; -#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" +static guint signals[LAST_SIGNAL] = { 0 }; -/* 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 _MetaBackgroundMonitor +{ + gboolean dirty; + CoglTexture *texture; + CoglOffscreen *fbo; +}; struct _MetaBackgroundPrivate { - MetaScreen *screen; - CoglTexture *texture; - CoglPipeline *pipeline; - int monitor; - - MetaBackgroundEffects effects; + 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 (MetaBackground, meta_background, G_TYPE_OBJECT) -G_DEFINE_TYPE_WITH_CODE (MetaBackground, meta_background, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTENT, - clutter_content_iface_init)) +static void +free_fbos (MetaBackground *self) +{ + MetaBackgroundPrivate *priv = self->priv; + + int i; + + for (i = 0; i < priv->n_monitors; i++) + { + MetaBackgroundMonitor *monitor = &priv->monitors[i]; + if (monitor->fbo) + { + cogl_object_unref (monitor->fbo); + monitor->fbo = NULL; + } + if (monitor->texture) + { + cogl_object_unref (monitor->texture); + monitor->texture = NULL; + } + } +} + +static void +free_color_texture (MetaBackground *self) +{ + MetaBackgroundPrivate *priv = self->priv; + + if (priv->color_texture != NULL) + { + cogl_object_unref (priv->color_texture); + priv->color_texture = NULL; + } +} + +static void +free_wallpaper_texture (MetaBackground *self) +{ + MetaBackgroundPrivate *priv = self->priv; + + if (priv->wallpaper_texture != NULL) + { + cogl_object_unref (priv->wallpaper_texture); + priv->wallpaper_texture = NULL; + } +} + +static void +on_monitors_changed (MetaScreen *screen, + MetaBackground *self) +{ + MetaBackgroundPrivate *priv = self->priv; + + free_fbos (self); + g_free (priv->monitors); + priv->monitors = NULL; + priv->n_monitors = 0; + + if (priv->screen) + { + priv->n_monitors = meta_screen_get_n_monitors (screen); + priv->monitors = g_new0 (MetaBackgroundMonitor, priv->n_monitors); + } +} + +static void +set_screen (MetaBackground *self, + MetaScreen *screen) +{ + MetaBackgroundPrivate *priv = self->priv; + + if (priv->screen != NULL) + { + g_signal_handlers_disconnect_by_func (priv->screen, + (gpointer)on_monitors_changed, + self); + } + + priv->screen = screen; + + if (priv->screen != NULL) + { + g_signal_connect (priv->screen, "monitors-changed", + G_CALLBACK (on_monitors_changed), self); + } + + on_monitors_changed (priv->screen, self); +} + +static void +meta_background_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + 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_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + MetaBackgroundPrivate *priv = META_BACKGROUND (object)->priv; + + switch (prop_id) + { + case PROP_META_SCREEN: + g_value_set_object (value, priv->screen); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} static gboolean -meta_background_get_preferred_size (ClutterContent *content, - gfloat *width, - gfloat *height) +need_prerender (MetaBackground *self) { - MetaBackgroundPrivate *priv = META_BACKGROUND (content)->priv; - MetaRectangle monitor_geometry; + 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; - if (priv->texture == NULL) + if (texture1 == NULL && texture2 == 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; + if (texture2 == NULL && priv->style == G_DESKTOP_BACKGROUND_STYLE_WALLPAPER) + return FALSE; return TRUE; } static void -get_texture_area_and_scale (MetaBackground *self, - ClutterActorBox *actor_box, - cairo_rectangle_int_t *texture_area, - float *texture_x_scale, - float *texture_y_scale) +mark_changed (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; + int i; - meta_screen_get_monitor_geometry (priv->screen, priv->monitor, &monitor_geometry); + if (!need_prerender (self)) + free_fbos (self); - 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; + for (i = 0; i < priv->n_monitors; i++) + priv->monitors[i].dirty = TRUE; - texture_width = cogl_texture_get_width (priv->texture); - actor_x_scale = (1.0 * actor_pixel_rect.width / monitor_geometry.width); + g_signal_emit (self, signals[CHANGED], 0); +} - texture_height = cogl_texture_get_height (priv->texture); - actor_y_scale = (1.0 * actor_pixel_rect.height / monitor_geometry.height); +static void +on_background_loaded (MetaBackgroundImage *image, + MetaBackground *self) +{ + mark_changed (self); +} - switch (priv->style) +static void +set_filename (MetaBackground *self, + char **filenamep, + MetaBackgroundImage **imagep, + const char *filename) +{ + if (g_strcmp0 (filename, *filenamep) != 0) { - 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); + g_free (*filenamep); + *filenamep = g_strdup (filename); - /* 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: + if (*imagep) { - /* 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); + g_signal_handlers_disconnect_by_func (*imagep, + (gpointer)on_background_loaded, + self); + g_object_unref (*imagep); + *imagep = NULL; + } - /* 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; + 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 CoglPipelineWrapMode -get_wrap_mode (MetaBackground *self) +static void +meta_background_dispose (GObject *object) +{ + MetaBackground *self = META_BACKGROUND (object); + MetaBackgroundPrivate *priv = self->priv; + + free_color_texture (self); + free_wallpaper_texture (self); + + set_filename (self, &priv->filename1, &priv->background_image1, NULL); + set_filename (self, &priv->filename2, &priv->background_image2, NULL); + + set_screen (self, NULL); + + G_OBJECT_CLASS (meta_background_parent_class)->dispose (object); +} + +static void +meta_background_finalize (GObject *object) +{ + 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; + + g_type_class_add_private (klass, sizeof (MetaBackgroundPrivate)); + + object_class->dispose = meta_background_dispose; + object_class->finalize = meta_background_finalize; + 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_ONLY); + + g_object_class_install_property (object_class, + PROP_META_SCREEN, + param_spec); + +} + +static void +meta_background_init (MetaBackground *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + META_TYPE_BACKGROUND, + MetaBackgroundPrivate); +} + +static void +get_texture_area (MetaBackground *self, + cairo_rectangle_int_t *monitor_rect, + CoglTexture *texture, + cairo_rectangle_int_t *texture_area) { MetaBackgroundPrivate *priv = self->priv; + cairo_rectangle_int_t image_area; + int screen_width, screen_height; + float texture_width, texture_height; + float monitor_x_scale, monitor_y_scale; + + texture_width = cogl_texture_get_width (texture); + texture_height = cogl_texture_get_height (texture); + 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; + 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; + + /* 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; + + /* 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; + } + + *texture_area = image_area; + break; + + case G_DESKTOP_BACKGROUND_STYLE_SPANNED: + { + /* paint region is the union of all monitors, with the origin + * of the region set to align with monitor associated with the background. + */ + meta_screen_get_size (priv->screen, &screen_width, &screen_height); + + /* unclipped texture area is whole screen */ + image_area.width = screen_width; + image_area.height = screen_height; + + /* 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; + } } } +static void +draw_texture (MetaBackground *self, + CoglFramebuffer *framebuffer, + CoglPipeline *pipeline, + CoglTexture *texture, + cairo_rectangle_int_t *monitor_area) +{ + MetaBackgroundPrivate *priv = self->priv; + cairo_rectangle_int_t texture_area; + + get_texture_area (self, monitor_area, texture, &texture_area); + + switch (priv->style) + { + 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(); + } +} + +static void +ensure_color_texture (MetaBackground *self) +{ + MetaBackgroundPrivate *priv = self->priv; + + if (priv->color_texture == NULL) + { + ClutterBackend *backend = clutter_get_default_backend (); + CoglContext *ctx = clutter_backend_get_cogl_context (backend); + uint8_t pixels[8]; + int width, height; + + if (priv->shading_direction == G_DESKTOP_BACKGROUND_SHADING_SOLID) + { + width = 1; + height = 1; + + 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 (); + } + + 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; + } + + priv->color_texture = COGL_TEXTURE (cogl_texture_2d_new_from_data (ctx, width, height, + COGL_PIXEL_FORMAT_RGB_888, + 4, + pixels, + NULL)); + } +} + +typedef enum { + PIPELINE_REPLACE, + PIPELINE_ADD, + PIPELINE_OVER_REVERSE, +} PipelineType; + +static CoglPipeline * +create_pipeline (PipelineType type) +{ + 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 (templates[type] == NULL) + { + templates[type] = meta_create_texture_pipeline (NULL); + cogl_pipeline_set_blend (templates[type], blend_strings[type], NULL); + } + + return cogl_pipeline_copy (templates[type]); +} + static gboolean texture_has_alpha (CoglTexture *texture) { @@ -308,937 +583,279 @@ texture_has_alpha (CoglTexture *texture) } } -static ClutterPaintNode * -meta_background_paint_node_new (MetaBackground *self, - ClutterActor *actor) +static void +ensure_wallpaper_texture (MetaBackground *self, + CoglTexture *texture) { 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; -} - -static void -clip_region_to_actor_box (cairo_region_t *region, - ClutterActorBox *actor_box) -{ - cairo_rectangle_int_t clip_rect; - - clip_rect.x = actor_box->x1; - clip_rect.y = actor_box->y1; - clip_rect.width = actor_box->x2 - actor_box->x1; - clip_rect.height = actor_box->y2 - actor_box->y1; - - cairo_region_intersect_rectangle (region, &clip_rect); -} - -static void -set_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); -} - -static void -meta_background_paint_content (ClutterContent *content, - ClutterActor *actor, - ClutterPaintNode *root) -{ - MetaBackground *self = META_BACKGROUND (content); - MetaBackgroundPrivate *priv = self->priv; - ClutterPaintNode *node; - ClutterActorBox actor_box; - cairo_rectangle_int_t texture_area; - cairo_region_t *paintable_region = NULL; - int n_texture_subareas; - int i; - float texture_x_scale, texture_y_scale; - float tx1 = 0.0, ty1 = 0.0, tx2 = 1.0, ty2 = 1.0; - - if (priv->texture == NULL) - return; - - 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->wallpaper_texture == NULL) { - cairo_region_t *clip_region; - clip_region = meta_background_actor_get_clip_region (META_BACKGROUND_ACTOR (actor)); + int width = cogl_texture_get_width (texture); + int height = cogl_texture_get_height (texture); + CoglFramebuffer *fbo; + CoglError *catch_error = NULL; + CoglPipeline *pipeline; - if (clip_region != NULL) - cairo_region_intersect (paintable_region, clip_region); - } + priv->wallpaper_texture = meta_create_large_texture (width, height, + COGL_TEXTURE_COMPONENTS_RGBA); + fbo = cogl_offscreen_new_with_texture (priv->wallpaper_texture); - if (cairo_region_is_empty (paintable_region)) - goto out; - - node = meta_background_paint_node_new (self, actor); - - /* Finally, split the paintable region up into distinct areas - * and paint each area one by one - */ - n_texture_subareas = cairo_region_num_rectangles (paintable_region); - for (i = 0; i < n_texture_subareas; i++) - { - cairo_rectangle_int_t texture_subarea; - ClutterActorBox texture_rectangle; - - cairo_region_get_rectangle (paintable_region, i, &texture_subarea); - - tx1 = (texture_subarea.x - texture_area.x) * texture_x_scale; - ty1 = (texture_subarea.y - texture_area.y) * texture_y_scale; - tx2 = (texture_subarea.x + texture_subarea.width - texture_area.x) * texture_x_scale; - ty2 = (texture_subarea.y + texture_subarea.height - texture_area.y) * texture_y_scale; - texture_rectangle.x1 = texture_subarea.x; - texture_rectangle.y1 = texture_subarea.y; - texture_rectangle.x2 = texture_subarea.x + texture_subarea.width; - texture_rectangle.y2 = texture_subarea.y + texture_subarea.height; - - clutter_paint_node_add_texture_rectangle (node, &texture_rectangle, tx1, ty1, tx2, ty2); - } - clutter_paint_node_add_child (root, node); - clutter_paint_node_unref (node); - - out: - cairo_region_destroy (paintable_region); -} - -static void -clutter_content_iface_init (ClutterContentIface *iface) -{ - iface->get_preferred_size = meta_background_get_preferred_size; - iface->paint_content = meta_background_paint_content; -} - -static void -meta_background_dispose (GObject *object) -{ - MetaBackground *self = META_BACKGROUND (object); - MetaBackgroundPrivate *priv = self->priv; - - unset_texture (self); - - g_clear_pointer (&priv->pipeline, - (GDestroyNotify) - cogl_object_unref); - - G_OBJECT_CLASS (meta_background_parent_class)->dispose (object); -} - -static void -meta_background_finalize (GObject *object) -{ - MetaBackground *self = META_BACKGROUND (object); - MetaBackgroundPrivate *priv = self->priv; - - g_free (priv->filename); - - G_OBJECT_CLASS (meta_background_parent_class)->finalize (object); -} - -static void -ensure_pipeline (MetaBackground *self) -{ - if (self->priv->pipeline == NULL) - self->priv->pipeline = COGL_PIPELINE (meta_create_texture_pipeline (NULL)); -} - -static void -set_brightness (MetaBackground *self, - gfloat brightness) -{ - MetaBackgroundPrivate *priv = self->priv; - - if (priv->brightness == brightness) - return; - - priv->brightness = brightness; - - 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)); - - g_object_notify (G_OBJECT (self), "brightness"); -} - -static void -set_vignette_sharpness (MetaBackground *self, - gfloat sharpness) -{ - MetaBackgroundPrivate *priv = self->priv; - - if (priv->vignette_sharpness == sharpness) - return; - - priv->vignette_sharpness = sharpness; - - if (!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"); -} - -static void -add_vignette (MetaBackground *self) -{ - 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); - - cogl_pipeline_set_uniform_1f (priv->pipeline, - cogl_pipeline_get_uniform_location (priv->pipeline, - "brightness"), - priv->brightness); - - cogl_pipeline_set_uniform_1f (priv->pipeline, - cogl_pipeline_get_uniform_location (priv->pipeline, - "vignette_sharpness"), - priv->vignette_sharpness); -} - -static void -set_effects (MetaBackground *self, - MetaBackgroundEffects effects) -{ - MetaBackgroundPrivate *priv = self->priv; - - priv->effects = effects; - - if ((priv->effects & META_BACKGROUND_EFFECTS_VIGNETTE)) - add_vignette (self); - - clutter_content_invalidate (CLUTTER_CONTENT (self)); -} - -static void -meta_background_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - MetaBackground *self = META_BACKGROUND (object); - MetaBackgroundPrivate *priv = self->priv; - - switch (prop_id) - { - case PROP_META_SCREEN: - priv->screen = g_value_get_object (value); - break; - case PROP_MONITOR: - priv->monitor = g_value_get_int (value); - break; - case PROP_EFFECTS: - set_effects (self, g_value_get_flags (value)); - break; - case PROP_BRIGHTNESS: - set_brightness (self, g_value_get_float (value)); - break; - case PROP_VIGNETTE_SHARPNESS: - set_vignette_sharpness (self, g_value_get_float (value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -meta_background_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - MetaBackgroundPrivate *priv = META_BACKGROUND (object)->priv; - - switch (prop_id) - { - case PROP_META_SCREEN: - g_value_set_object (value, priv->screen); - break; - case PROP_MONITOR: - g_value_set_int (value, priv->monitor); - break; - case PROP_EFFECTS: - g_value_set_flags (value, priv->effects); - break; - case PROP_BRIGHTNESS: - g_value_set_float (value, priv->brightness); - break; - case PROP_VIGNETTE_SHARPNESS: - g_value_set_float (value, priv->vignette_sharpness); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -meta_background_class_init (MetaBackgroundClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GParamSpec *param_spec; - - g_type_class_add_private (klass, sizeof (MetaBackgroundPrivate)); - - object_class->dispose = meta_background_dispose; - object_class->finalize = meta_background_finalize; - object_class->set_property = meta_background_set_property; - object_class->get_property = meta_background_get_property; - - param_spec = g_param_spec_object ("meta-screen", - "MetaScreen", - "MetaScreen", - META_TYPE_SCREEN, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT); - - g_object_class_install_property (object_class, - PROP_META_SCREEN, - param_spec); - - param_spec = g_param_spec_int ("monitor", - "monitor", - "monitor", - 0, G_MAXINT, 0, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT); - - g_object_class_install_property (object_class, - PROP_MONITOR, - param_spec); - - param_spec = g_param_spec_float ("brightness", - "brightness", - "Values less than 1.0 dim background", - 0.0, 1.0, - 1.0, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT); - g_object_class_install_property (object_class, PROP_BRIGHTNESS, param_spec); - - param_spec = g_param_spec_float ("vignette-sharpness", - "vignette-sharpness", - "How obvious the vignette fringe is", - 0.0, 1.0, - 0.7, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT); - g_object_class_install_property (object_class, PROP_VIGNETTE_SHARPNESS, param_spec); - - param_spec = g_param_spec_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 -meta_background_init (MetaBackground *self) -{ - self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, - META_TYPE_BACKGROUND, - MetaBackgroundPrivate); -} - -static void -unset_texture (MetaBackground *self) -{ - MetaBackgroundPrivate *priv = self->priv; - if (priv->pipeline != NULL) - cogl_pipeline_set_layer_texture (priv->pipeline, 0, NULL); - - g_clear_pointer (&priv->texture, - (GDestroyNotify) - cogl_object_unref); -} - -static void -set_texture (MetaBackground *self, - CoglTexture *texture) -{ - MetaBackgroundPrivate *priv = self->priv; - - priv->texture = texture; - cogl_pipeline_set_layer_texture (priv->pipeline, 0, priv->texture); -} - -static void -set_style (MetaBackground *self, - GDesktopBackgroundStyle style) -{ - MetaBackgroundPrivate *priv = self->priv; - CoglPipelineWrapMode wrap_mode; - - priv->style = style; - - wrap_mode = get_wrap_mode (self); - cogl_pipeline_set_layer_wrap_mode (priv->pipeline, 0, wrap_mode); -} - -static void -set_filename (MetaBackground *self, - const char *filename) -{ - MetaBackgroundPrivate *priv = self->priv; - - g_free (priv->filename); - priv->filename = g_strdup (filename); -} - -/** - * 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) -{ - 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); - - priv->shading_direction = shading_direction; - - switch (priv->shading_direction) - { - case G_DESKTOP_BACKGROUND_SHADING_VERTICAL: - width = 1; - height = 2; - break; - case G_DESKTOP_BACKGROUND_SHADING_HORIZONTAL: - width = 2; - height = 1; - break; - default: - g_return_if_reached (); - } - - pixels[0] = color->red; - pixels[1] = color->green; - pixels[2] = color->blue; - pixels[3] = 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) -{ - 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) - { - clutter_actor_get_background_color (stage, &stage_color); - color = &stage_color; - } - - 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; - -static LoadFileTaskData * -load_file_task_data_new (const char *filename, - GDesktopBackgroundStyle style) -{ - LoadFileTaskData *task_data; - - task_data = g_slice_new (LoadFileTaskData); - task_data->style = style; - task_data->filename = g_strdup (filename); - - return task_data; -} - -static void -load_file_task_data_free (LoadFileTaskData *task_data) -{ - g_free (task_data->filename); - g_slice_free (LoadFileTaskData, task_data); -} - -static void -load_file (GTask *task, - MetaBackground *self, - LoadFileTaskData *task_data, - GCancellable *cancellable) -{ - GError *error = NULL; - GdkPixbuf *pixbuf; - - pixbuf = gdk_pixbuf_new_from_file (task_data->filename, - &error); - - if (pixbuf == NULL) - { - g_task_return_error (task, error); - return; - } - - g_task_return_pointer (task, pixbuf, (GDestroyNotify) g_object_unref); -} - -/** - * meta_background_load_file_async: - * @self: the #MetaBackground - * @filename: the image file to load - * @style: a #GDesktopBackgroundStyle to specify how background is laid out - * @cancellable: a #GCancellable - * @callback: call back to call when file is loaded or failed to load - * @user_data: user data for callback - * - * Loads the specified image and uses it as the background source. - */ -void -meta_background_load_file_async (MetaBackground *self, - const char *filename, - GDesktopBackgroundStyle style, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - LoadFileTaskData *task_data; - GTask *task; - - task = g_task_new (self, cancellable, callback, user_data); - - task_data = load_file_task_data_new (filename, style); - g_task_set_task_data (task, task_data, (GDestroyNotify) load_file_task_data_free); - - g_task_run_in_thread (task, (GTaskThreadFunc) load_file); - g_object_unref (task); -} - -/** - * 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) -{ - 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; - - g_return_val_if_fail (g_task_is_valid (result, self), FALSE); - - task = G_TASK (result); - - pixbuf = g_task_propagate_pointer (task, error); - - if (pixbuf == NULL) - goto out; - - task_data = g_task_get_task_data (task); - - width = gdk_pixbuf_get_width (pixbuf); - height = gdk_pixbuf_get_height (pixbuf); - row_stride = gdk_pixbuf_get_rowstride (pixbuf); - pixels = gdk_pixbuf_get_pixels (pixbuf); - has_alpha = gdk_pixbuf_get_has_alpha (pixbuf); - - pixel_format = has_alpha ? COGL_PIXEL_FORMAT_RGBA_8888 : COGL_PIXEL_FORMAT_RGB_888; - - texture = COGL_TEXTURE (cogl_texture_2d_new_from_data (ctx, width, height, - pixel_format, - row_stride, - pixels, - NULL)); - - if (texture == NULL) - { - g_set_error_literal (error, - COGL_BITMAP_ERROR, - COGL_BITMAP_ERROR_FAILED, - _("background texture could not be created from file")); - goto out; - } - - ensure_pipeline (self); - unset_texture (self); - set_style (self, task_data->style); - set_filename (self, task_data->filename); - set_texture (self, texture); - - clutter_content_invalidate (CLUTTER_CONTENT (self)); - loaded = TRUE; - -out: - if (pixbuf != NULL) - g_object_unref (pixbuf); - return loaded; -} - -/** - * meta_background_copy: - * @self: a #MetaBackground to copy - * @monitor: a monitor - * @effects: effects to use on copy of @self - * - * Creates a new #MetaBackground to draw the background for the given monitor. - * Background will be loaded from @self and will share state - * with @self, but may have different effects applied to it. - * - * Return value: (transfer full): the newly created background content - */ -MetaBackground * -meta_background_copy (MetaBackground *self, - int monitor, - MetaBackgroundEffects effects) -{ - MetaBackground *background; - - background = META_BACKGROUND (g_object_new (META_TYPE_BACKGROUND, - "meta-screen", self->priv->screen, - "monitor", monitor, - NULL)); - - background->priv->brightness = self->priv->brightness; - - background->priv->shading_direction = self->priv->shading_direction; - background->priv->color = self->priv->color; - background->priv->second_color = self->priv->second_color; - background->priv->filename = g_strdup (self->priv->filename); - - /* we can reuse the pipeline if it has no effects applied, or - * if it has the same effects applied - */ - if (effects == self->priv->effects || - self->priv->effects == META_BACKGROUND_EFFECTS_NONE) - { - ensure_pipeline (self); - background->priv->pipeline = cogl_pipeline_copy (self->priv->pipeline); - background->priv->texture = cogl_object_ref (self->priv->texture); - background->priv->style = self->priv->style; - - if (effects != self->priv->effects) + if (!cogl_framebuffer_allocate (fbo, &catch_error)) { - set_effects (background, effects); + cogl_error_free (catch_error); + return; + } - if (effects & META_BACKGROUND_EFFECTS_VIGNETTE) - { - set_brightness (background, self->priv->brightness); - set_vignette_sharpness (background, self->priv->vignette_sharpness); - } + cogl_framebuffer_orthographic (fbo, 0, 0, + width, height, -1., 1.); + + 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); + + if (texture_has_alpha (texture)) + { + ensure_color_texture (self); + + 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); + } + + cogl_object_unref (fbo); + } +} + +static CoglPipelineWrapMode +get_wrap_mode (GDesktopBackgroundStyle style) +{ + switch (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; + } +} + +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; + + g_return_if_fail (META_IS_BACKGROUND (self)); + priv = self->priv; + g_return_if_fail (monitor_index <= 0 && monitor_index < priv->n_monitors); + + monitor = &priv->monitors[monitor_index]; + + 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; + + 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; + + 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; + } + + 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; + } + + if (monitor->dirty) + { + CoglError *catch_error = NULL; + + if (monitor->texture == NULL) + { + 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 (!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 (effects & META_BACKGROUND_EFFECTS_VIGNETTE) + if (texture1 != NULL && + !(texture2 != NULL && priv->blend_factor == 1.0 && !texture_has_alpha (texture2))) { - set_brightness (background, self->priv->brightness); - set_vignette_sharpness (background, self->priv->vignette_sharpness); + 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 (!((texture2 != NULL && priv->blend_factor == 1.0 && !texture_has_alpha (texture2)) || + (texture1 != NULL && !texture_has_alpha (texture1)))) + { + 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); + } + + monitor->dirty = FALSE; } - clutter_content_invalidate (CLUTTER_CONTENT (background)); - - 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 }; + + 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_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) +void +meta_background_set_gradient (MetaBackground *self, + GDesktopBackgroundShading shading_direction, + ClutterColor *color, + ClutterColor *second_color) { - return self->priv->shading_direction; + 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_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_filename (MetaBackground *self, + const char *filename, + GDesktopBackgroundStyle style) { - return &self->priv->color; + g_return_if_fail (META_IS_BACKGROUND (self)); + + meta_background_set_blend (self, filename, NULL, 0.0, style); } -/** - * 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_blend (MetaBackground *self, + const char *filename1, + const char *filename2, + double blend_factor, + GDesktopBackgroundStyle style) { - return &self->priv->second_color; -} + MetaBackgroundPrivate *priv; -/** - * meta_background_get_filename: - * @self: a #MetaBackground - * - * Returns the filename of the currently loaded file. - * IF @self is not loaded from a file this function is - * undefined. - * - * Return value: (transfer none): the filename - */ -const char * -meta_background_get_filename (MetaBackground *self) -{ - return self->priv->filename; + 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 -#include - -#include #include +#include #include @@ -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 . + */ + +#ifndef __META_BACKGROUND_IMAGE_H__ +#define __META_BACKGROUND_IMAGE_H__ + +#include +#include + +#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 #include - -#include -#include - #include +#include /** * 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); +MetaBackground *meta_background_new (MetaScreen *screen); -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); +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 */