From 580feb0c85c2400de1432324fc62fc4b410aef36 Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Wed, 23 Jan 2013 15:54:41 -0500 Subject: [PATCH] compositor: rework how backgrounds are managed Background handling in GNOME is very roundabout at the moment. gnome-settings-daemon uses gnome-desktop to read the background from disk into a screen-sized pixmap. It then sets the XID of that pixmap on the _XROOTPMAP_ID root window property. mutter puts that pixmap into a texture/actor which gnome-shell then uses. Having the gnome-settings-daemon detour from disk to screen means we can't easily let the compositor handle transition effects when switching backgrounds. Also, having the background actor be per-screen instead of per-monitor means we may have oversized textures in certain multihead setups. This commit changes mutter to read backgrounds from disk itself, and it changes backgrounds to be per-monitor. This way background handling/compositing is left to the compositor. https://bugzilla.gnome.org/show_bug.cgi?id=682427 --- src/Makefile.am | 8 + src/compositor/compositor.c | 74 +- .../meta-background-actor-private.h | 3 +- src/compositor/meta-background-actor.c | 604 +------- .../meta-background-group-private.h | 11 + src/compositor/meta-background-group.c | 92 ++ src/compositor/meta-background.c | 1324 +++++++++++++++++ src/compositor/meta-window-group.c | 14 +- src/meta/compositor-mutter.h | 1 - src/meta/meta-background-actor.h | 41 +- src/meta/meta-background-group.h | 46 + src/meta/meta-background.h | 110 ++ 12 files changed, 1693 insertions(+), 635 deletions(-) create mode 100644 src/compositor/meta-background-group-private.h create mode 100644 src/compositor/meta-background-group.c create mode 100644 src/compositor/meta-background.c create mode 100644 src/meta/meta-background-group.h create mode 100644 src/meta/meta-background.h diff --git a/src/Makefile.am b/src/Makefile.am index 1ab326cfe..86b36acbd 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -8,6 +8,7 @@ SUBDIRS=wm-tester tools compositor/plugins INCLUDES= \ -DCLUTTER_ENABLE_EXPERIMENTAL_API \ -DCOGL_ENABLE_EXPERIMENTAL_API \ + -DCOGL_ENABLE_EXPERIMENTAL_2_0_API \ $(MUTTER_CFLAGS) \ -I$(srcdir) \ -I$(srcdir)/core \ @@ -48,8 +49,11 @@ libmutter_la_SOURCES = \ compositor/cogl-utils.h \ compositor/compositor.c \ compositor/compositor-private.h \ + compositor/meta-background.c \ compositor/meta-background-actor.c \ compositor/meta-background-actor-private.h \ + compositor/meta-background-group.c \ + compositor/meta-background-group-private.h \ compositor/meta-module.c \ compositor/meta-module.h \ compositor/meta-plugin.c \ @@ -71,7 +75,9 @@ libmutter_la_SOURCES = \ compositor/region-utils.c \ compositor/region-utils.h \ meta/compositor.h \ + meta/meta-background.h \ meta/meta-background-actor.h \ + meta/meta-background-group.h \ meta/meta-plugin.h \ meta/meta-shadow-factory.h \ meta/meta-window-actor.h \ @@ -175,6 +181,8 @@ libmutterinclude_base_headers = \ meta/keybindings.h \ meta/main.h \ meta/meta-background-actor.h \ + meta/meta-background-group.h \ + meta/meta-background.h \ meta/meta-plugin.h \ meta/meta-shaped-texture.h \ meta/meta-shadow-factory.h \ diff --git a/src/compositor/compositor.c b/src/compositor/compositor.c index 10c86eaec..adf71d9a8 100644 --- a/src/compositor/compositor.c +++ b/src/compositor/compositor.c @@ -13,10 +13,11 @@ #include "xprops.h" #include #include +#include +#include #include #include "meta-window-actor-private.h" #include "meta-window-group.h" -#include "meta-background-actor-private.h" #include "window-private.h" /* to check window->hidden */ #include "display-private.h" /* for meta_display_lookup_x_window() */ #include @@ -117,21 +118,6 @@ process_property_notify (MetaCompositor *compositor, { MetaWindowActor *window_actor; - if (event->atom == compositor->atom_x_root_pixmap) - { - GSList *l; - - for (l = meta_display_get_screens (compositor->display); l; l = l->next) - { - MetaScreen *screen = l->data; - if (event->window == meta_screen_get_xroot (screen)) - { - meta_background_actor_update (screen); - return; - } - } - } - if (window == NULL) return; @@ -254,27 +240,6 @@ meta_get_top_window_group_for_screen (MetaScreen *screen) return info->top_window_group; } -/** - * meta_get_background_actor_for_screen: - * @screen: a #MetaScreen - * - * Gets the actor that draws the root window background under the windows. - * The root window background automatically tracks the image or color set - * by the environment. - * - * Returns: (transfer none): The background actor corresponding to @screen - */ -ClutterActor * -meta_get_background_actor_for_screen (MetaScreen *screen) -{ - MetaCompScreen *info = meta_screen_get_compositor_data (screen); - - if (!info) - return NULL; - - return info->background_actor; -} - /** * meta_get_window_actors: * @screen: a #MetaScreen @@ -606,14 +571,8 @@ meta_compositor_manage_screen (MetaCompositor *compositor, info->window_group = meta_window_group_new (screen); info->top_window_group = meta_window_group_new (screen); - info->background_actor = meta_background_actor_new_for_screen (screen); - clutter_actor_set_reactive (info->background_actor, TRUE); info->overlay_group = clutter_group_new (); - clutter_container_add (CLUTTER_CONTAINER (info->window_group), - info->background_actor, - NULL); - clutter_container_add (CLUTTER_CONTAINER (info->stage), info->window_group, info->top_window_group, @@ -1066,6 +1025,7 @@ sync_actor_stacking (MetaCompScreen *info) GList *expected_window_node; GList *tmp; GList *old; + GList *backgrounds; gboolean has_windows; gboolean reordered; @@ -1083,15 +1043,19 @@ sync_actor_stacking (MetaCompScreen *info) * (we really need extra API to make that reliable.) */ - /* First we check if the background is at the bottom. Then - * we check if the window actors are in the correct sequence */ + /* First we collect a list of all backgrounds, and check if they're at the + * bottom. Then we check if the window actors are in the correct sequence */ + backgrounds = NULL; expected_window_node = info->windows; for (old = children; old != NULL; old = old->next) { ClutterActor *actor = old->data; - if (actor == info->background_actor) + if (META_IS_BACKGROUND_GROUP (actor) || + META_IS_BACKGROUND_ACTOR (actor)) { + backgrounds = g_list_prepend (backgrounds, actor); + if (has_windows) reordered = TRUE; } @@ -1109,7 +1073,10 @@ sync_actor_stacking (MetaCompScreen *info) g_list_free (children); if (!reordered) - return; + { + g_list_free (backgrounds); + return; + } /* reorder the actors by lowering them in turn to the bottom of the stack. * windows first, then background */ @@ -1120,7 +1087,16 @@ sync_actor_stacking (MetaCompScreen *info) clutter_actor_lower_bottom (CLUTTER_ACTOR (window_actor)); } - clutter_actor_lower_bottom (info->background_actor); + /* we prepended the backgrounds above so the last actor in the list + * should get lowered to the bottom last. + */ + for (tmp = backgrounds; tmp != NULL; tmp = tmp->next) + { + ClutterActor *actor = tmp->data; + + clutter_actor_lower_bottom (CLUTTER_ACTOR (actor)); + } + g_list_free (backgrounds); } void @@ -1276,8 +1252,6 @@ meta_compositor_sync_screen_size (MetaCompositor *compositor, XResizeWindow (xdisplay, xwin, width, height); - meta_background_actor_screen_size_changed (screen); - meta_verbose ("Changed size for stage on screen %d to %dx%d\n", meta_screen_get_screen_number (screen), width, height); diff --git a/src/compositor/meta-background-actor-private.h b/src/compositor/meta-background-actor-private.h index f5d656e0c..aeaf77fa0 100644 --- a/src/compositor/meta-background-actor-private.h +++ b/src/compositor/meta-background-actor-private.h @@ -9,7 +9,6 @@ void meta_background_actor_set_visible_region (MetaBackgroundActor *self, cairo_region_t *visible_region); -void meta_background_actor_update (MetaScreen *screen); -void meta_background_actor_screen_size_changed (MetaScreen *screen); +cairo_region_t *meta_background_actor_get_visible_region (MetaBackgroundActor *self); #endif /* META_BACKGROUND_ACTOR_PRIVATE_H */ diff --git a/src/compositor/meta-background-actor.c b/src/compositor/meta-background-actor.c index 41306b16c..506091993 100644 --- a/src/compositor/meta-background-actor.c +++ b/src/compositor/meta-background-actor.c @@ -34,223 +34,23 @@ #include "cogl-utils.h" #include "compositor-private.h" #include +#include #include "meta-background-actor-private.h" -/* We allow creating multiple MetaBackgroundActors for the same MetaScreen to - * allow different rendering options to be set for different copies. - * But we want to share the same underlying CoglTexture for efficiency and - * to avoid driver bugs that might occur if we created multiple CoglTexturePixmaps - * for the same pixmap. - * - * This structure holds common information. - */ -typedef struct _MetaScreenBackground MetaScreenBackground; - -struct _MetaScreenBackground -{ - MetaScreen *screen; - GSList *actors; - - float texture_width; - float texture_height; - CoglTexture *texture; - CoglMaterialWrapMode wrap_mode; - guint have_pixmap : 1; -}; - struct _MetaBackgroundActorPrivate { - MetaScreenBackground *background; - CoglPipeline *pipeline; - cairo_region_t *visible_region; - float dim_factor; }; -enum -{ - PROP_0, - - PROP_DIM_FACTOR, - - PROP_LAST -}; - -static GParamSpec *obj_props[PROP_LAST]; - G_DEFINE_TYPE (MetaBackgroundActor, meta_background_actor, CLUTTER_TYPE_ACTOR); -static void set_texture (MetaScreenBackground *background, - CoglHandle texture); -static void set_texture_to_stage_color (MetaScreenBackground *background); - -static void -on_notify_stage_color (GObject *stage, - GParamSpec *pspec, - MetaScreenBackground *background) -{ - if (!background->have_pixmap) - set_texture_to_stage_color (background); -} - -static void -free_screen_background (MetaScreenBackground *background) -{ - set_texture (background, COGL_INVALID_HANDLE); - - if (background->screen != NULL) - { - ClutterActor *stage = meta_get_stage_for_screen (background->screen); - g_signal_handlers_disconnect_by_func (stage, - (gpointer) on_notify_stage_color, - background); - background->screen = NULL; - } -} - -static MetaScreenBackground * -meta_screen_background_get (MetaScreen *screen) -{ - MetaScreenBackground *background; - - background = g_object_get_data (G_OBJECT (screen), "meta-screen-background"); - if (background == NULL) - { - ClutterActor *stage; - - background = g_new0 (MetaScreenBackground, 1); - - background->screen = screen; - g_object_set_data_full (G_OBJECT (screen), "meta-screen-background", - background, (GDestroyNotify) free_screen_background); - - stage = meta_get_stage_for_screen (screen); - g_signal_connect (stage, "notify::color", - G_CALLBACK (on_notify_stage_color), background); - - meta_background_actor_update (screen); - } - - return background; -} - -static void -update_wrap_mode_of_actor (MetaBackgroundActor *self) -{ - MetaBackgroundActorPrivate *priv = self->priv; - - cogl_pipeline_set_layer_wrap_mode (priv->pipeline, 0, priv->background->wrap_mode); -} - -static void -update_wrap_mode (MetaScreenBackground *background) -{ - GSList *l; - int width, height; - - meta_screen_get_size (background->screen, &width, &height); - - /* We turn off repeating when we have a full-screen pixmap to keep from - * getting artifacts from one side of the image sneaking into the other - * side of the image via bilinear filtering. - */ - if (width == background->texture_width && height == background->texture_height) - background->wrap_mode = COGL_MATERIAL_WRAP_MODE_CLAMP_TO_EDGE; - else - background->wrap_mode = COGL_MATERIAL_WRAP_MODE_REPEAT; - - for (l = background->actors; l; l = l->next) - update_wrap_mode_of_actor (l->data); -} - -static void -set_texture_on_actor (MetaBackgroundActor *self) -{ - MetaBackgroundActorPrivate *priv = self->priv; - MetaDisplay *display = meta_screen_get_display (priv->background->screen); - - /* This may trigger destruction of an old texture pixmap, which, if - * the underlying X pixmap is already gone has the tendency to trigger - * X errors inside DRI. For safety, trap errors */ - meta_error_trap_push (display); - cogl_pipeline_set_layer_texture (priv->pipeline, 0, priv->background->texture); - meta_error_trap_pop (display); - - clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); -} - -static void -set_texture (MetaScreenBackground *background, - CoglHandle texture) -{ - MetaDisplay *display = meta_screen_get_display (background->screen); - GSList *l; - - /* This may trigger destruction of an old texture pixmap, which, if - * the underlying X pixmap is already gone has the tendency to trigger - * X errors inside DRI. For safety, trap errors */ - meta_error_trap_push (display); - if (background->texture != COGL_INVALID_HANDLE) - { - cogl_handle_unref (background->texture); - background->texture = COGL_INVALID_HANDLE; - } - meta_error_trap_pop (display); - - if (texture != COGL_INVALID_HANDLE) - background->texture = cogl_handle_ref (texture); - - background->texture_width = cogl_texture_get_width (background->texture); - background->texture_height = cogl_texture_get_height (background->texture); - - for (l = background->actors; l; l = l->next) - set_texture_on_actor (l->data); - - update_wrap_mode (background); -} - -/* Sets our pipeline to paint with a 1x1 texture of the stage's background - * color; doing this when we have no pixmap allows the application to turn - * off painting the stage. There might be a performance benefit to - * painting in this case with a solid color, but the normal solid color - * case is a 1x1 root pixmap, so we'd have to reverse-engineer that to - * actually pick up the (small?) performance win. This is just a fallback. - */ -static void -set_texture_to_stage_color (MetaScreenBackground *background) -{ - ClutterActor *stage = meta_get_stage_for_screen (background->screen); - ClutterColor color; - CoglHandle texture; - - clutter_stage_get_color (CLUTTER_STAGE (stage), &color); - - /* Slicing will prevent COGL from using hardware texturing for - * the tiled 1x1 pixmap, and will cause it to draw the window - * background in millions of separate 1x1 rectangles */ - texture = meta_create_color_texture_4ub (color.red, color.green, - color.blue, 0xff, - COGL_TEXTURE_NO_SLICING); - set_texture (background, texture); - cogl_handle_unref (texture); -} - static void meta_background_actor_dispose (GObject *object) { MetaBackgroundActor *self = META_BACKGROUND_ACTOR (object); - MetaBackgroundActorPrivate *priv = self->priv; meta_background_actor_set_visible_region (self, NULL); - if (priv->background != NULL) - { - priv->background->actors = g_slist_remove (priv->background->actors, self); - priv->background = NULL; - } - - g_clear_pointer(&priv->pipeline, cogl_object_unref); - G_OBJECT_CLASS (meta_background_actor_parent_class)->dispose (object); } @@ -260,11 +60,15 @@ meta_background_actor_get_preferred_width (ClutterActor *actor, gfloat *min_width_p, gfloat *natural_width_p) { - MetaBackgroundActor *self = META_BACKGROUND_ACTOR (actor); - MetaBackgroundActorPrivate *priv = self->priv; - int width, height; + ClutterContent *content; + gfloat width; - meta_screen_get_size (priv->background->screen, &width, &height); + content = clutter_actor_get_content (actor); + + if (content) + clutter_content_get_preferred_size (content, &width, NULL); + else + width = 0; if (min_width_p) *min_width_p = width; @@ -279,11 +83,15 @@ meta_background_actor_get_preferred_height (ClutterActor *actor, gfloat *natural_height_p) { - MetaBackgroundActor *self = META_BACKGROUND_ACTOR (actor); - MetaBackgroundActorPrivate *priv = self->priv; - int width, height; + ClutterContent *content; + gfloat height; - meta_screen_get_size (priv->background->screen, &width, &height); + content = clutter_actor_get_content (actor); + + if (content) + clutter_content_get_preferred_size (content, NULL, &height); + else + height = 0; if (min_height_p) *min_height_p = height; @@ -291,64 +99,19 @@ meta_background_actor_get_preferred_height (ClutterActor *actor, *natural_height_p = height; } -static void -meta_background_actor_paint (ClutterActor *actor) -{ - MetaBackgroundActor *self = META_BACKGROUND_ACTOR (actor); - MetaBackgroundActorPrivate *priv = self->priv; - guint8 opacity = clutter_actor_get_paint_opacity (actor); - guint8 color_component; - int width, height; - - meta_screen_get_size (priv->background->screen, &width, &height); - - color_component = (int)(0.5 + opacity * priv->dim_factor); - - cogl_pipeline_set_color4ub (priv->pipeline, - color_component, - color_component, - color_component, - opacity); - - cogl_set_source (priv->pipeline); - - if (priv->visible_region) - { - int n_rectangles = cairo_region_num_rectangles (priv->visible_region); - int i; - - for (i = 0; i < n_rectangles; i++) - { - cairo_rectangle_int_t rect; - cairo_region_get_rectangle (priv->visible_region, i, &rect); - - cogl_rectangle_with_texture_coords (rect.x, rect.y, - rect.x + rect.width, rect.y + rect.height, - rect.x / priv->background->texture_width, - rect.y / priv->background->texture_height, - (rect.x + rect.width) / priv->background->texture_width, - (rect.y + rect.height) / priv->background->texture_height); - } - } - else - { - cogl_rectangle_with_texture_coords (0.0f, 0.0f, - width, height, - 0.0f, 0.0f, - width / priv->background->texture_width, - height / priv->background->texture_height); - } -} - static gboolean meta_background_actor_get_paint_volume (ClutterActor *actor, ClutterPaintVolume *volume) { - MetaBackgroundActor *self = META_BACKGROUND_ACTOR (actor); - MetaBackgroundActorPrivate *priv = self->priv; - int width, height; + ClutterContent *content; + gfloat width, height; - meta_screen_get_size (priv->background->screen, &width, &height); + content = clutter_actor_get_content (actor); + + if (!content) + return FALSE; + + clutter_content_get_preferred_size (content, &width, &height); clutter_paint_volume_set_width (volume, width); clutter_paint_volume_set_height (volume, height); @@ -356,215 +119,48 @@ meta_background_actor_get_paint_volume (ClutterActor *actor, return TRUE; } -static void -meta_background_actor_set_dim_factor (MetaBackgroundActor *self, - gfloat dim_factor) -{ - MetaBackgroundActorPrivate *priv = self->priv; - - if (priv->dim_factor == dim_factor) - return; - - priv->dim_factor = dim_factor; - - clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); - - g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_DIM_FACTOR]); -} - -static void -meta_background_actor_get_property(GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - MetaBackgroundActor *self = META_BACKGROUND_ACTOR (object); - MetaBackgroundActorPrivate *priv = self->priv; - - switch (prop_id) - { - case PROP_DIM_FACTOR: - g_value_set_float (value, priv->dim_factor); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -meta_background_actor_set_property(GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - MetaBackgroundActor *self = META_BACKGROUND_ACTOR (object); - - switch (prop_id) - { - case PROP_DIM_FACTOR: - meta_background_actor_set_dim_factor (self, g_value_get_float (value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - static void meta_background_actor_class_init (MetaBackgroundActorClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); - GParamSpec *pspec; g_type_class_add_private (klass, sizeof (MetaBackgroundActorPrivate)); object_class->dispose = meta_background_actor_dispose; - object_class->get_property = meta_background_actor_get_property; - object_class->set_property = meta_background_actor_set_property; actor_class->get_preferred_width = meta_background_actor_get_preferred_width; actor_class->get_preferred_height = meta_background_actor_get_preferred_height; - actor_class->paint = meta_background_actor_paint; actor_class->get_paint_volume = meta_background_actor_get_paint_volume; - - /** - * MetaBackgroundActor:dim-factor: - * - * Factor to dim the background by, between 0.0 (black) and 1.0 (original - * colors) - */ - pspec = g_param_spec_float ("dim-factor", - "Dim factor", - "Factor to dim the background by", - 0.0, 1.0, - 1.0, - G_PARAM_READWRITE); - obj_props[PROP_DIM_FACTOR] = pspec; - g_object_class_install_property (object_class, PROP_DIM_FACTOR, pspec); } static void meta_background_actor_init (MetaBackgroundActor *self) { - MetaBackgroundActorPrivate *priv; - - priv = self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, - META_TYPE_BACKGROUND_ACTOR, - MetaBackgroundActorPrivate); - priv->dim_factor = 1.0; + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + META_TYPE_BACKGROUND_ACTOR, + MetaBackgroundActorPrivate); } /** * meta_background_actor_new: - * @screen: the #MetaScreen * - * Creates a new actor to draw the background for the given screen. + * Creates a new actor to draw the background for the given monitor. + * This actor should be associated with a #MetaBackground using + * clutter_actor_set_content() * * Return value: the newly created background actor */ ClutterActor * -meta_background_actor_new_for_screen (MetaScreen *screen) +meta_background_actor_new (void) { MetaBackgroundActor *self; - MetaBackgroundActorPrivate *priv; - - g_return_val_if_fail (META_IS_SCREEN (screen), NULL); self = g_object_new (META_TYPE_BACKGROUND_ACTOR, NULL); - priv = self->priv; - - priv->background = meta_screen_background_get (screen); - priv->background->actors = g_slist_prepend (priv->background->actors, self); - - /* A CoglMaterial and a CoglPipeline are the same thing */ - priv->pipeline = (CoglPipeline*) meta_create_texture_material (NULL); - - set_texture_on_actor (self); - update_wrap_mode_of_actor (self); return CLUTTER_ACTOR (self); } -/** - * meta_background_actor_update: - * @screen: a #MetaScreen - * - * Refetches the _XROOTPMAP_ID property for the root window and updates - * the contents of the background actor based on that. There's no attempt - * to optimize out pixmap values that don't change (since a root pixmap - * could be replaced by with another pixmap with the same ID under some - * circumstances), so this should only be called when we actually receive - * a PropertyNotify event for the property. - */ -void -meta_background_actor_update (MetaScreen *screen) -{ - MetaScreenBackground *background; - MetaDisplay *display; - MetaCompositor *compositor; - Atom type; - int format; - gulong nitems; - gulong bytes_after; - guchar *data; - Pixmap root_pixmap_id; - - background = meta_screen_background_get (screen); - display = meta_screen_get_display (screen); - compositor = meta_display_get_compositor (display); - - root_pixmap_id = None; - if (!XGetWindowProperty (meta_display_get_xdisplay (display), - meta_screen_get_xroot (screen), - compositor->atom_x_root_pixmap, - 0, LONG_MAX, - False, - AnyPropertyType, - &type, &format, &nitems, &bytes_after, &data) && - type != None) - { - /* Got a property. */ - if (type == XA_PIXMAP && format == 32 && nitems == 1) - { - /* Was what we expected. */ - root_pixmap_id = *(Pixmap *)data; - } - - XFree(data); - } - - if (root_pixmap_id != None) - { - CoglHandle texture; - CoglContext *ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ()); - GError *error = NULL; - - meta_error_trap_push (display); - texture = cogl_texture_pixmap_x11_new (ctx, root_pixmap_id, FALSE, &error); - meta_error_trap_pop (display); - - if (texture != COGL_INVALID_HANDLE) - { - set_texture (background, texture); - cogl_handle_unref (texture); - - background->have_pixmap = True; - return; - } - else - { - g_warning ("Failed to create background texture from pixmap: %s", - error->message); - g_error_free (error); - } - } - - background->have_pixmap = False; - set_texture_to_stage_color (background); -} - /** * meta_background_actor_set_visible_region: * @self: a #MetaBackgroundActor @@ -584,120 +180,44 @@ meta_background_actor_set_visible_region (MetaBackgroundActor *self, priv = self->priv; - if (priv->visible_region) - { - cairo_region_destroy (priv->visible_region); - priv->visible_region = NULL; - } + g_clear_pointer (&priv->visible_region, + (GDestroyNotify) + cairo_region_destroy); if (visible_region) - { - cairo_rectangle_int_t screen_rect = { 0 }; - meta_screen_get_size (priv->background->screen, &screen_rect.width, &screen_rect.height); - - /* Doing the intersection here is probably unnecessary - MetaWindowGroup - * should never compute a visible area that's larger than the root screen! - * but it's not that expensive and adds some extra robustness. - */ - priv->visible_region = cairo_region_create_rectangle (&screen_rect); - cairo_region_intersect (priv->visible_region, visible_region); - } + priv->visible_region = cairo_region_copy (visible_region); } /** - * meta_background_actor_screen_size_changed: - * @screen: a #MetaScreen + * meta_background_actor_get_visible_region: + * @self: a #MetaBackgroundActor * - * Called by the compositor when the size of the #MetaScreen changes + * Return value (transfer full): a #cairo_region_t that represents the part of + * the background not obscured by other #MetaBackgroundActor or + * #MetaWindowActor objects. */ -void -meta_background_actor_screen_size_changed (MetaScreen *screen) +cairo_region_t * +meta_background_actor_get_visible_region (MetaBackgroundActor *self) { - MetaScreenBackground *background = meta_screen_background_get (screen); - GSList *l; + MetaBackgroundActorPrivate *priv = self->priv; + ClutterActorBox content_box; + cairo_rectangle_int_t content_area = { 0 }; + cairo_region_t *visible_region; - update_wrap_mode (background); + g_return_val_if_fail (META_IS_BACKGROUND_ACTOR (self), NULL); - for (l = background->actors; l; l = l->next) - clutter_actor_queue_relayout (l->data); + if (!priv->visible_region) + return NULL; + + clutter_actor_get_content_box (CLUTTER_ACTOR (self), &content_box); + + content_area.x = content_box.x1; + content_area.y = content_box.y1; + content_area.width = content_box.x2 - content_box.x1; + content_area.height = content_box.y2 - content_box.y1; + + visible_region = cairo_region_create_rectangle (&content_area); + cairo_region_intersect (visible_region, priv->visible_region); + + return visible_region; } - -/** - * meta_background_actor_add_glsl_snippet: - * @actor: a #MetaBackgroundActor - * @hook: where to insert the code - * @declarations: GLSL declarations - * @code: GLSL code - * @is_replace: wheter Cogl code should be replaced by the custom shader - * - * Adds a GLSL snippet to the pipeline used for drawing the background. - * See #CoglSnippet for details. - */ -void -meta_background_actor_add_glsl_snippet (MetaBackgroundActor *actor, - MetaSnippetHook hook, - const char *declarations, - const char *code, - gboolean is_replace) -{ - MetaBackgroundActorPrivate *priv; - CoglSnippet *snippet; - - g_return_if_fail (META_IS_BACKGROUND_ACTOR (actor)); - - priv = actor->priv; - - if (is_replace) - { - snippet = cogl_snippet_new (hook, declarations, NULL); - cogl_snippet_set_replace (snippet, code); - } - else - { - snippet = cogl_snippet_new (hook, declarations, code); - } - - if (hook == META_SNIPPET_HOOK_VERTEX || - hook == META_SNIPPET_HOOK_FRAGMENT) - cogl_pipeline_add_snippet (priv->pipeline, snippet); - else - cogl_pipeline_add_layer_snippet (priv->pipeline, 0, snippet); - - cogl_object_unref (snippet); -} - -/** - * meta_background_actor_set_uniform_float: - * @actor: a #MetaBackgroundActor - * @uniform_name: - * @n_components: number of components (for vector uniforms) - * @count: number of uniforms (for array uniforms) - * @uniform: (array length=uniform_length): the float values to set - * @uniform_length: the length of @uniform. Must be exactly @n_components x @count, - * and is provided mainly for language bindings. - * - * Sets a new GLSL uniform to the provided value. This is mostly - * useful in congiunction with meta_background_actor_add_glsl_snippet(). - */ - -void -meta_background_actor_set_uniform_float (MetaBackgroundActor *actor, - const char *uniform_name, - int n_components, - int count, - const float *uniform, - int uniform_length) -{ - MetaBackgroundActorPrivate *priv; - - g_return_if_fail (META_IS_BACKGROUND_ACTOR (actor)); - g_return_if_fail (uniform_length == n_components * count); - - priv = actor->priv; - - cogl_pipeline_set_uniform_float (priv->pipeline, - cogl_pipeline_get_uniform_location (priv->pipeline, - uniform_name), - n_components, count, uniform); -} - diff --git a/src/compositor/meta-background-group-private.h b/src/compositor/meta-background-group-private.h new file mode 100644 index 000000000..5eca2688b --- /dev/null +++ b/src/compositor/meta-background-group-private.h @@ -0,0 +1,11 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + +#ifndef META_BACKGROUND_GROUP_PRIVATE_H +#define META_BACKGROUND_GROUP_PRIVATE_H + +#include +#include + +void meta_background_group_set_visible_region (MetaBackgroundGroup *self, + cairo_region_t *visible_region); +#endif /* META_BACKGROUND_GROUP_PRIVATE_H */ diff --git a/src/compositor/meta-background-group.c b/src/compositor/meta-background-group.c new file mode 100644 index 000000000..b26ed6a3c --- /dev/null +++ b/src/compositor/meta-background-group.c @@ -0,0 +1,92 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + +#include + +#include "compositor-private.h" +#include "clutter-utils.h" +#include "meta-background-actor-private.h" +#include "meta-background-group-private.h" + +G_DEFINE_TYPE (MetaBackgroundGroup, meta_background_group, CLUTTER_TYPE_GROUP); + +struct _MetaBackgroundGroupPrivate +{ + ClutterLayoutManager *layout_manager; +}; + +static void +meta_background_group_dispose (GObject *object) +{ + G_OBJECT_CLASS (meta_background_group_parent_class)->dispose (object); +} + +static void +meta_background_group_class_init (MetaBackgroundGroupClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = meta_background_group_dispose; + + g_type_class_add_private (klass, sizeof (MetaBackgroundGroupPrivate)); +} + +static void +meta_background_group_init (MetaBackgroundGroup *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + META_TYPE_BACKGROUND_GROUP, + MetaBackgroundGroupPrivate); + + self->priv->layout_manager = clutter_bin_layout_new (CLUTTER_BIN_ALIGNMENT_FIXED, + CLUTTER_BIN_ALIGNMENT_FIXED); + + clutter_actor_set_layout_manager (CLUTTER_ACTOR (self), self->priv->layout_manager); +} + +/** + * meta_background_group_set_visible_region: + * @self: a #MetaBackgroundGroup + * @visible_region: (allow-none): the parts of the background to paint + * + * Sets the area of the backgrounds that is unobscured by overlapping windows. + * This is used to optimize and only paint the visible portions. + */ +void +meta_background_group_set_visible_region (MetaBackgroundGroup *self, + cairo_region_t *region) +{ + GList *children, *l; + + children = clutter_actor_get_children (CLUTTER_ACTOR (self)); + for (l = children; l; l = l->next) + { + ClutterActor *actor = l->data; + + if (META_IS_BACKGROUND_ACTOR (actor)) + { + meta_background_actor_set_visible_region (META_BACKGROUND_ACTOR (actor), region); + } + else if (META_IS_BACKGROUND_GROUP (actor)) + { + int x, y; + + if (!meta_actor_is_untransformed (actor, &x, &y)) + continue; + + cairo_region_translate (region, -x, -y); + meta_background_group_set_visible_region (META_BACKGROUND_GROUP (actor), region); + cairo_region_translate (region, x, y); + } + } + g_list_free (children); +} + +ClutterActor * +meta_background_group_new (void) +{ + MetaBackgroundGroup *background_group; + + background_group = g_object_new (META_TYPE_BACKGROUND_GROUP, NULL); + + return CLUTTER_ACTOR (background_group); +} diff --git a/src/compositor/meta-background.c b/src/compositor/meta-background.c new file mode 100644 index 000000000..9a95fc01b --- /dev/null +++ b/src/compositor/meta-background.c @@ -0,0 +1,1324 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* + * meta-background.c: CoglTexture for painting the system background + * + * Copyright 2013 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include + +#include + +#include + +#include "cogl-utils.h" +#include "compositor-private.h" +#include "mutter-enum-types.h" +#include +#include +#include "meta-background-actor-private.h" + +#if G_BYTE_ORDER == G_LITTLE_ENDIAN +#define TEXTURE_FORMAT COGL_PIXEL_FORMAT_BGRA_8888_PRE +#else +#define TEXTURE_FORMAT COGL_PIXEL_FORMAT_ARGB_8888_PRE +#endif + +#define TEXTURE_LOOKUP_SHADER_DECLARATIONS \ +"uniform vec2 pixel_step;\n" \ +"vec4 apply_blur(in sampler2D texture, in vec2 coordinates) {\n" \ +" vec4 texel;\n" \ +" texel = texture2D(texture, coordinates.st);\n" \ +" texel += texture2D(texture, coordinates.st + pixel_step * vec2(-1.0, -1.0));\n"\ +" texel += texture2D(texture, coordinates.st + pixel_step * vec2( 0.0, -1.0));\n"\ +" texel += texture2D(texture, coordinates.st + pixel_step * vec2(+1.0, -1.0));\n"\ +" texel += texture2D(texture, coordinates.st + pixel_step * vec2(-1.0, 0.0));\n"\ +" texel += texture2D(texture, coordinates.st + pixel_step * vec2(+1.0, 0.0));\n"\ +" texel += texture2D(texture, coordinates.st + pixel_step * vec2(-1.0, +1.0));\n"\ +" texel += texture2D(texture, coordinates.st + pixel_step * vec2( 0.0, +1.0));\n"\ +" texel += texture2D(texture, coordinates.st + pixel_step * vec2(+1.0, +1.0));\n"\ +" texel /= 9.0;\n" \ +" return texel;\n" \ +"}\n" \ +"uniform float saturation;\n" \ +"vec3 desaturate(const vec3 color)\n" \ +"{\n" \ +" const vec3 gray_conv = vec3(0.299, 0.587, 0.114);\n" \ +" vec3 gray = vec3(dot(gray_conv, color));\n" \ +" return vec3(mix(color.rgb, gray, 1.0 - saturation));\n" \ +"}\n" \ + +#define DESATURATE_CODE \ +"cogl_texel.rgb = desaturate(cogl_texel.rgb);\n" + +#define BLUR_CODE \ +"cogl_texel = apply_blur(cogl_sampler, cogl_tex_coord.st);\n" + +#define FRAGMENT_SHADER_DECLARATIONS \ +"uniform float brightness;\n" \ +"uniform float vignette_sharpness;\n" \ + +#define VIGNETTE_CODE \ +"float unit_length = 0.5;\n" \ +"vec2 center = vec2(unit_length, unit_length);\n" \ +"vec2 position = cogl_tex_coord_in[0].xy - center;\n" \ +"float t = min(length(position), unit_length) / unit_length;\n" \ +"float pixel_brightness = mix(1.0, 1.0 - vignette_sharpness, t);\n" \ +"cogl_color_out.rgb = cogl_color_out.rgb * pixel_brightness * brightness;\n" + +/* We allow creating multiple MetaBackgrounds for the same monitor to + * allow different rendering options to be set for different copies. + * But we want to share the same underlying CoglTextures for efficiency and + * to avoid driver bugs that might occur if we created multiple CoglTexturePixmaps + * for the same pixmap. + * + * This object provides a ClutterContent object to assist in sharing between actors. + */ +typedef struct _MetaBackgroundPrivate MetaBackgroundPrivate; + +struct _MetaBackgroundPrivate +{ + MetaScreen *screen; + CoglTexture *texture; + CoglPipeline *pipeline; + int monitor; + + MetaBackgroundEffects effects; + + GDesktopBackgroundStyle style; + GDesktopBackgroundShading shading_direction; + ClutterColor color; + ClutterColor second_color; + + char *filename; + + float brightness; + float vignette_sharpness; + float saturation; +}; + +enum +{ + PROP_META_SCREEN = 1, + PROP_MONITOR, + PROP_EFFECTS, + PROP_BRIGHTNESS, + PROP_VIGNETTE_SHARPNESS, + PROP_SATURATION +}; + +static void clutter_content_iface_init (ClutterContentIface *iface); +static void unset_texture (MetaBackground *self); + +G_DEFINE_TYPE_WITH_CODE (MetaBackground, meta_background, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTENT, + clutter_content_iface_init)) + + +static gboolean +meta_background_get_preferred_size (ClutterContent *content, + gfloat *width, + gfloat *height) +{ + MetaBackgroundPrivate *priv = META_BACKGROUND (content)->priv; + MetaRectangle monitor_geometry; + + if (priv->texture == NULL) + return FALSE; + + meta_screen_get_monitor_geometry (priv->screen, priv->monitor, &monitor_geometry); + + if (width != NULL) + *width = monitor_geometry.width; + + if (height != NULL) + *height = monitor_geometry.height; + + return TRUE; +} + +static void +get_texture_area_and_scale (MetaBackground *self, + ClutterActorBox *actor_box, + cairo_rectangle_int_t *texture_area, + float *texture_x_scale, + float *texture_y_scale) +{ + MetaBackgroundPrivate *priv = self->priv; + MetaRectangle monitor_geometry; + cairo_rectangle_int_t actor_pixel_rect; + cairo_rectangle_int_t image_area; + int screen_width, screen_height; + float texture_width, texture_height; + float actor_x_scale, actor_y_scale; + float monitor_x_scale, monitor_y_scale; + + meta_screen_get_monitor_geometry (priv->screen, priv->monitor, &monitor_geometry); + + actor_pixel_rect.x = actor_box->x1; + actor_pixel_rect.y = actor_box->y1; + actor_pixel_rect.width = actor_box->x2 - actor_box->x1; + actor_pixel_rect.height = actor_box->y2 - actor_box->y1; + + texture_width = cogl_texture_get_width (priv->texture); + actor_x_scale = (1.0 * actor_pixel_rect.width / monitor_geometry.width); + + texture_height = cogl_texture_get_height (priv->texture); + actor_y_scale = (1.0 * actor_pixel_rect.height / monitor_geometry.height); + + switch (priv->style) + { + case G_DESKTOP_BACKGROUND_STYLE_STRETCHED: + default: + /* paint region is whole actor, and the texture + * is scaled disproportionately to fit the actor + */ + *texture_area = actor_pixel_rect; + *texture_x_scale = 1.0 / actor_pixel_rect.width; + *texture_y_scale = 1.0 / actor_pixel_rect.height; + break; + case G_DESKTOP_BACKGROUND_STYLE_WALLPAPER: + /* paint region is whole actor, and the texture + * is left unscaled + */ + image_area = actor_pixel_rect; + *texture_x_scale = 1.0 / texture_width; + *texture_y_scale = 1.0 / texture_height; + + *texture_area = image_area; + break; + case G_DESKTOP_BACKGROUND_STYLE_CENTERED: + /* paint region is the original image size centered in the actor, + * and the texture is scaled to the original image size */ + image_area.width = texture_width; + image_area.height = texture_height; + image_area.x = actor_pixel_rect.x + actor_pixel_rect.width / 2 - image_area.width / 2; + image_area.y = actor_pixel_rect.y + actor_pixel_rect.height / 2 - image_area.height / 2; + + *texture_area = image_area; + *texture_x_scale = 1.0 / texture_width; + *texture_y_scale = 1.0 / texture_height; + break; + case G_DESKTOP_BACKGROUND_STYLE_SCALED: + case G_DESKTOP_BACKGROUND_STYLE_ZOOM: + /* paint region is the actor size in one dimension, and centered and + * scaled by proportional amount in the other dimension. + * + * SCALED forces the centered dimension to fit on screen. + * ZOOM forces the centered dimension to grow off screen + */ + monitor_x_scale = monitor_geometry.width / texture_width; + monitor_y_scale = monitor_geometry.height / texture_height; + + if ((priv->style == G_DESKTOP_BACKGROUND_STYLE_SCALED && + (monitor_x_scale < monitor_y_scale)) || + (priv->style == G_DESKTOP_BACKGROUND_STYLE_ZOOM && + (monitor_x_scale > monitor_y_scale))) + { + /* Fill image to exactly fit actor horizontally */ + image_area.width = actor_pixel_rect.width; + image_area.height = texture_height * monitor_x_scale * actor_y_scale; + + /* Position image centered vertically in actor */ + image_area.x = actor_pixel_rect.x; + image_area.y = actor_pixel_rect.y + actor_pixel_rect.height / 2 - image_area.height / 2; + } + else + { + /* Scale image to exactly fit actor vertically */ + image_area.width = texture_width * monitor_y_scale * actor_x_scale; + image_area.height = actor_pixel_rect.height; + + /* Position image centered horizontally in actor */ + image_area.x = actor_pixel_rect.x + actor_pixel_rect.width / 2 - image_area.width / 2; + image_area.y = actor_pixel_rect.y; + } + + *texture_area = image_area; + *texture_x_scale = 1.0 / image_area.width; + *texture_y_scale = 1.0 / image_area.height; + break; + + case G_DESKTOP_BACKGROUND_STYLE_SPANNED: + { + /* paint region is the union of all monitors, with the origin + * of the region set to align with monitor associated with the background. + */ + meta_screen_get_size (priv->screen, &screen_width, &screen_height); + + /* unclipped texture area is whole screen */ + image_area.width = screen_width * actor_x_scale; + image_area.height = screen_height * actor_y_scale; + + /* But make (0,0) line up with the appropriate monitor */ + image_area.x = -monitor_geometry.x * actor_x_scale; + image_area.y = -monitor_geometry.y * actor_y_scale; + + *texture_area = image_area; + *texture_x_scale = 1.0 / image_area.width; + *texture_y_scale = 1.0 / image_area.height; + break; + } + } +} + +static CoglPipelineWrapMode +get_wrap_mode (MetaBackground *self) +{ + MetaBackgroundPrivate *priv = self->priv; + switch (priv->style) + { + case G_DESKTOP_BACKGROUND_STYLE_WALLPAPER: + return COGL_PIPELINE_WRAP_MODE_REPEAT; + case G_DESKTOP_BACKGROUND_STYLE_NONE: + case G_DESKTOP_BACKGROUND_STYLE_STRETCHED: + case G_DESKTOP_BACKGROUND_STYLE_CENTERED: + case G_DESKTOP_BACKGROUND_STYLE_SCALED: + case G_DESKTOP_BACKGROUND_STYLE_ZOOM: + case G_DESKTOP_BACKGROUND_STYLE_SPANNED: + default: + return COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE; + } +} + +static ClutterPaintNode * +meta_background_paint_node_new (MetaBackground *self, + ClutterActor *actor) +{ + MetaBackgroundPrivate *priv = self->priv; + ClutterPaintNode *node; + guint8 opacity; + guint8 color_component; + + opacity = clutter_actor_get_paint_opacity (actor); + color_component = (guint8) (0.5 + opacity * priv->brightness); + + cogl_pipeline_set_color4ub (priv->pipeline, + color_component, + color_component, + color_component, + opacity); + + node = clutter_pipeline_node_new (priv->pipeline); + + return node; +} + +static void +clip_region_to_actor_box (cairo_region_t *region, + ClutterActorBox *actor_box) +{ + cairo_rectangle_int_t clip_rect; + + clip_rect.x = actor_box->x1; + clip_rect.y = actor_box->y1; + clip_rect.width = actor_box->x2 - actor_box->x1; + clip_rect.height = actor_box->y2 - actor_box->y1; + + cairo_region_intersect_rectangle (region, &clip_rect); +} + +static void +set_blur_parameters (MetaBackground *self, + ClutterActorBox *actor_box) +{ + MetaBackgroundPrivate *priv = self->priv; + float pixel_step[2]; + + if (!(priv->effects & META_BACKGROUND_EFFECTS_BLUR)) + return; + + pixel_step[0] = 1.0 / (actor_box->x2 - actor_box->x1); + pixel_step[1] = 1.0 / (actor_box->y2 - actor_box->y1); + + cogl_pipeline_set_uniform_float (priv->pipeline, + cogl_pipeline_get_uniform_location (priv->pipeline, + "pixel_step"), + 2, 1, pixel_step); +} + +static void +meta_background_paint_content (ClutterContent *content, + ClutterActor *actor, + ClutterPaintNode *root) +{ + MetaBackground *self = META_BACKGROUND (content); + MetaBackgroundPrivate *priv = self->priv; + ClutterPaintNode *node; + ClutterActorBox actor_box; + cairo_rectangle_int_t texture_area; + cairo_region_t *paintable_region = NULL; + int n_texture_subareas; + int i; + float texture_x_scale, texture_y_scale; + float tx1 = 0.0, ty1 = 0.0, tx2 = 1.0, ty2 = 1.0; + + if (priv->texture == NULL) + return; + + node = meta_background_paint_node_new (self, actor); + + clutter_actor_get_content_box (actor, &actor_box); + + set_blur_parameters (self, &actor_box); + + /* First figure out where on the monitor the texture is supposed to be painted. + * If the actor is not the size of the monitor, this function makes sure to scale + * everything down to fit in the actor. + */ + get_texture_area_and_scale (self, + &actor_box, + &texture_area, + &texture_x_scale, + &texture_y_scale); + + /* Now figure out what to actually paint. We start by clipping the texture area to + * the actor's bounds. + */ + paintable_region = cairo_region_create_rectangle (&texture_area); + + clip_region_to_actor_box (paintable_region, &actor_box); + + /* And then cut out any parts occluded by window actors + */ + if (META_IS_BACKGROUND_ACTOR (actor)) + { + cairo_region_t *visible_region; + visible_region = meta_background_actor_get_visible_region (META_BACKGROUND_ACTOR (actor)); + + if (visible_region != NULL) + { + cairo_region_intersect (paintable_region, visible_region); + cairo_region_destroy (visible_region); + } + } + + /* Finally, split the paintable region up into distinct areas + * and paint each area one by one + */ + n_texture_subareas = cairo_region_num_rectangles (paintable_region); + for (i = 0; i < n_texture_subareas; i++) + { + cairo_rectangle_int_t texture_subarea; + ClutterActorBox texture_rectangle; + + cairo_region_get_rectangle (paintable_region, i, &texture_subarea); + + tx1 = (texture_subarea.x - texture_area.x) * texture_x_scale; + ty1 = (texture_subarea.y - texture_area.y) * texture_y_scale; + tx2 = (texture_subarea.x + texture_subarea.width - texture_area.x) * texture_x_scale; + ty2 = (texture_subarea.y + texture_subarea.height - texture_area.y) * texture_y_scale; + texture_rectangle.x1 = texture_subarea.x; + texture_rectangle.y1 = texture_subarea.y; + texture_rectangle.x2 = texture_subarea.x + texture_subarea.width; + texture_rectangle.y2 = texture_subarea.y + texture_subarea.height; + + clutter_paint_node_add_texture_rectangle (node, &texture_rectangle, tx1, ty1, tx2, ty2); + } + cairo_region_destroy (paintable_region); + + clutter_paint_node_add_child (root, node); + clutter_paint_node_unref (node); +} + +static void +clutter_content_iface_init (ClutterContentIface *iface) +{ + iface->get_preferred_size = meta_background_get_preferred_size; + iface->paint_content = meta_background_paint_content; +} + +static void +meta_background_dispose (GObject *object) +{ + MetaBackground *self = META_BACKGROUND (object); + MetaBackgroundPrivate *priv = self->priv; + + unset_texture (self); + + g_clear_pointer (&priv->pipeline, + (GDestroyNotify) + cogl_object_unref); + + G_OBJECT_CLASS (meta_background_parent_class)->dispose (object); +} + +static void +ensure_pipeline (MetaBackground *self) +{ + if (self->priv->pipeline == NULL) + self->priv->pipeline = COGL_PIPELINE (meta_create_texture_material (NULL)); +} + +static void +set_brightness (MetaBackground *self, + gfloat brightness) +{ + MetaBackgroundPrivate *priv = self->priv; + + if (priv->brightness == brightness) + return; + + priv->brightness = brightness; + + if (priv->effects & META_BACKGROUND_EFFECTS_VIGNETTE) + { + ensure_pipeline (self); + cogl_pipeline_set_uniform_1f (priv->pipeline, + cogl_pipeline_get_uniform_location (priv->pipeline, + "brightness"), + priv->brightness); + } + + clutter_content_invalidate (CLUTTER_CONTENT (self)); + + g_object_notify (G_OBJECT (self), "brightness"); +} + +static void +set_vignette_sharpness (MetaBackground *self, + gfloat sharpness) +{ + MetaBackgroundPrivate *priv = self->priv; + + if (priv->vignette_sharpness == sharpness) + return; + + priv->vignette_sharpness = sharpness; + + if (priv->effects & META_BACKGROUND_EFFECTS_VIGNETTE) + { + ensure_pipeline (self); + cogl_pipeline_set_uniform_1f (priv->pipeline, + cogl_pipeline_get_uniform_location (priv->pipeline, + "vignette_sharpness"), + priv->vignette_sharpness); + } + + clutter_content_invalidate (CLUTTER_CONTENT (self)); + + g_object_notify (G_OBJECT (self), "vignette-sharpness"); +} + +static void +set_saturation (MetaBackground *self, + gfloat saturation) +{ + MetaBackgroundPrivate *priv = self->priv; + + if (priv->saturation == saturation) + return; + + priv->saturation = saturation; + + ensure_pipeline (self); + + cogl_pipeline_set_uniform_1f (priv->pipeline, + cogl_pipeline_get_uniform_location (priv->pipeline, + "saturation"), + priv->saturation); + + + clutter_content_invalidate (CLUTTER_CONTENT (self)); + + g_object_notify (G_OBJECT (self), "saturation"); +} + +static void +add_texture_lookup_shader (MetaBackground *self) +{ + MetaBackgroundPrivate *priv = self->priv; + CoglSnippet *snippet; + const char *code; + + ensure_pipeline (self); + + snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_TEXTURE_LOOKUP, + TEXTURE_LOOKUP_SHADER_DECLARATIONS, + NULL); + if ((priv->effects & META_BACKGROUND_EFFECTS_BLUR) && + (priv->effects & META_BACKGROUND_EFFECTS_DESATURATE)) + code = BLUR_CODE "\n" DESATURATE_CODE; + else if (priv->effects & META_BACKGROUND_EFFECTS_BLUR) + code = BLUR_CODE; + else if (priv->effects & META_BACKGROUND_EFFECTS_DESATURATE) + code = DESATURATE_CODE; + + cogl_snippet_set_replace (snippet, code); + cogl_pipeline_add_layer_snippet (priv->pipeline, 0, snippet); + cogl_object_unref (snippet); + + if (priv->effects & META_BACKGROUND_EFFECTS_DESATURATE) + cogl_pipeline_set_uniform_1f (priv->pipeline, + cogl_pipeline_get_uniform_location (priv->pipeline, + "saturation"), + priv->saturation); +} + +static void +add_vignette (MetaBackground *self) +{ + MetaBackgroundPrivate *priv = self->priv; + CoglSnippet *snippet; + + ensure_pipeline (self); + + snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, FRAGMENT_SHADER_DECLARATIONS, VIGNETTE_CODE); + cogl_pipeline_add_snippet (priv->pipeline, snippet); + cogl_object_unref (snippet); + + cogl_pipeline_set_uniform_1f (priv->pipeline, + cogl_pipeline_get_uniform_location (priv->pipeline, + "brightness"), + priv->brightness); + + cogl_pipeline_set_uniform_1f (priv->pipeline, + cogl_pipeline_get_uniform_location (priv->pipeline, + "vignette_sharpness"), + priv->vignette_sharpness); +} + +static void +set_effects (MetaBackground *self, + MetaBackgroundEffects effects) +{ + MetaBackgroundPrivate *priv = self->priv; + + priv->effects = effects; + + if ((priv->effects & META_BACKGROUND_EFFECTS_BLUR) || + (priv->effects & META_BACKGROUND_EFFECTS_DESATURATE)) + add_texture_lookup_shader (self); + + if ((priv->effects & META_BACKGROUND_EFFECTS_VIGNETTE)) + add_vignette (self); + + clutter_content_invalidate (CLUTTER_CONTENT (self)); +} + +static void +meta_background_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + MetaBackground *self = META_BACKGROUND (object); + MetaBackgroundPrivate *priv = self->priv; + + switch (prop_id) + { + case PROP_META_SCREEN: + priv->screen = g_value_get_object (value); + break; + case PROP_MONITOR: + priv->monitor = g_value_get_int (value); + break; + case PROP_EFFECTS: + set_effects (self, g_value_get_flags (value)); + break; + case PROP_BRIGHTNESS: + set_brightness (self, g_value_get_float (value)); + break; + case PROP_VIGNETTE_SHARPNESS: + set_vignette_sharpness (self, g_value_get_float (value)); + break; + case PROP_SATURATION: + set_saturation (self, g_value_get_float (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +meta_background_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + MetaBackgroundPrivate *priv = META_BACKGROUND (object)->priv; + + switch (prop_id) + { + case PROP_META_SCREEN: + g_value_set_object (value, priv->screen); + break; + case PROP_MONITOR: + g_value_set_int (value, priv->monitor); + break; + case PROP_EFFECTS: + g_value_set_flags (value, priv->effects); + break; + case PROP_BRIGHTNESS: + g_value_set_float (value, priv->brightness); + break; + case PROP_VIGNETTE_SHARPNESS: + g_value_set_float (value, priv->vignette_sharpness); + break; + case PROP_SATURATION: + g_value_set_float (value, priv->saturation); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +meta_background_class_init (MetaBackgroundClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GParamSpec *param_spec; + + g_type_class_add_private (klass, sizeof (MetaBackgroundPrivate)); + + object_class->dispose = meta_background_dispose; + object_class->set_property = meta_background_set_property; + object_class->get_property = meta_background_get_property; + + param_spec = g_param_spec_object ("meta-screen", + "MetaScreen", + "MetaScreen", + META_TYPE_SCREEN, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT); + + g_object_class_install_property (object_class, + PROP_META_SCREEN, + param_spec); + + param_spec = g_param_spec_int ("monitor", + "monitor", + "monitor", + 0, G_MAXINT, 0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT); + + g_object_class_install_property (object_class, + PROP_MONITOR, + param_spec); + + param_spec = g_param_spec_float ("brightness", + "brightness", + "Values less than 1.0 dim background", + 0.0, 1.0, + 1.0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT); + g_object_class_install_property (object_class, PROP_BRIGHTNESS, param_spec); + + param_spec = g_param_spec_float ("vignette-sharpness", + "vignette-sharpness", + "How obvious the vignette fringe is", + 0.0, 1.0, + 0.7, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT); + g_object_class_install_property (object_class, PROP_VIGNETTE_SHARPNESS, param_spec); + + param_spec = g_param_spec_float ("saturation", + "saturation", + "Values less than 1.0 grays background", + 0.0, 1.0, + 1.0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT); + g_object_class_install_property (object_class, PROP_SATURATION, param_spec); + + param_spec = g_param_spec_flags ("effects", + "Effects", + "Set to alter saturation, to blur, etc", + meta_background_effects_get_type (), + META_BACKGROUND_EFFECTS_NONE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (object_class, PROP_EFFECTS, param_spec); +} + +static void +meta_background_init (MetaBackground *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + META_TYPE_BACKGROUND, + MetaBackgroundPrivate); +} + +static void +unset_texture (MetaBackground *self) +{ + MetaBackgroundPrivate *priv = self->priv; + cogl_pipeline_set_layer_texture (priv->pipeline, 0, NULL); + + g_clear_pointer (&priv->texture, + (GDestroyNotify) + cogl_handle_unref); +} + +static void +set_texture (MetaBackground *self, + CoglTexture *texture) +{ + MetaBackgroundPrivate *priv = self->priv; + + priv->texture = texture; + cogl_pipeline_set_layer_texture (priv->pipeline, 0, priv->texture); +} + +static void +set_style (MetaBackground *self, + GDesktopBackgroundStyle style) +{ + MetaBackgroundPrivate *priv = self->priv; + CoglPipelineWrapMode wrap_mode; + + priv->style = style; + + wrap_mode = get_wrap_mode (self); + cogl_pipeline_set_layer_wrap_mode (priv->pipeline, 0, wrap_mode); +} + +static void +set_filename (MetaBackground *self, + const char *filename) +{ + MetaBackgroundPrivate *priv = self->priv; + + g_free (priv->filename); + priv->filename = g_strdup (filename); +} + +static Pixmap +get_still_frame_for_monitor (MetaScreen *screen, + int monitor) +{ + MetaDisplay *display = meta_screen_get_display (screen); + Display *xdisplay = meta_display_get_xdisplay (display); + Window xroot = meta_screen_get_xroot (screen); + Pixmap pixmap; + GC gc; + XGCValues values; + MetaRectangle geometry; + int depth; + + meta_screen_get_monitor_geometry (screen, monitor, &geometry); + + depth = DefaultDepth (xdisplay, meta_screen_get_screen_number (screen)); + + pixmap = XCreatePixmap (xdisplay, + xroot, + geometry.width, geometry.height, depth); + + values.function = GXcopy; + values.plane_mask = AllPlanes; + values.fill_style = FillSolid; + values.subwindow_mode = IncludeInferiors; + + gc = XCreateGC (xdisplay, + xroot, + GCFunction | GCPlaneMask | GCFillStyle | GCSubwindowMode, + &values); + + XCopyArea (xdisplay, + xroot, pixmap, gc, + geometry.x, geometry.y, + geometry.width, geometry.height, + 0, 0); + + XFreeGC (xdisplay, gc); + + return pixmap; +} + +/** + * meta_background_load_still_frame: + * @self: the #MetaBackground + * + * Takes a screenshot of the desktop and uses it as the background + * source. + */ +void +meta_background_load_still_frame (MetaBackground *self) +{ + MetaBackgroundPrivate *priv = self->priv; + MetaDisplay *display = meta_screen_get_display (priv->screen); + Pixmap still_frame; + CoglHandle texture; + CoglContext *context = clutter_backend_get_cogl_context (clutter_get_default_backend ()); + GError *error = NULL; + + ensure_pipeline (self); + + unset_texture (self); + set_style (self, G_DESKTOP_BACKGROUND_STYLE_STRETCHED); + + still_frame = get_still_frame_for_monitor (priv->screen, priv->monitor); + XSync (meta_display_get_xdisplay (display), False); + + meta_error_trap_push (display); + texture = cogl_texture_pixmap_x11_new (context, still_frame, FALSE, &error); + meta_error_trap_pop (display); + + if (error != NULL) + { + g_warning ("Failed to create background texture from pixmap: %s", + error->message); + g_error_free (error); + return; + } + + set_texture (self, COGL_TEXTURE (texture)); +} + +/** + * meta_background_load_gradient: + * @self: the #MetaBackground + * @shading_direction: the orientation of the gradient + * @color: the start color of the gradient + * @second_color: the end color of the gradient + * + * Clears any previously set background, and sets the background gradient. + * The gradient starts with @color and + * progresses toward @second_color in the direction of @shading_direction. + */ +void +meta_background_load_gradient (MetaBackground *self, + GDesktopBackgroundShading shading_direction, + ClutterColor *color, + ClutterColor *second_color) +{ + MetaBackgroundPrivate *priv = self->priv; + CoglHandle texture; + guint width, height; + uint8_t pixels[8]; + + ensure_pipeline (self); + + unset_texture (self); + set_style (self, G_DESKTOP_BACKGROUND_STYLE_NONE); + + priv->shading_direction = shading_direction; + + switch (priv->shading_direction) + { + case G_DESKTOP_BACKGROUND_SHADING_VERTICAL: + width = 1; + height = 2; + break; + case G_DESKTOP_BACKGROUND_SHADING_HORIZONTAL: + width = 2; + height = 1; + break; + default: + g_return_if_reached (); + } + + pixels[0] = color->red; + pixels[1] = color->green; + pixels[2] = color->blue; + pixels[3] = color->alpha; + pixels[4] = second_color->red; + pixels[5] = second_color->green; + pixels[6] = second_color->blue; + pixels[7] = second_color->alpha; + texture = cogl_texture_new_from_data (width, height, + COGL_TEXTURE_NO_SLICING, + TEXTURE_FORMAT, + COGL_PIXEL_FORMAT_ANY, + 4, + pixels); + set_texture (self, COGL_TEXTURE (texture)); +} + +/** + * meta_background_load_color: + * @self: the #MetaBackground + * @color: a #ClutterColor to solid fill background with + * + * Clears any previously set background, and sets the + * background to a solid color + * + * If @color is %NULL the stage color will be used. + */ +void +meta_background_load_color (MetaBackground *self, + ClutterColor *color) +{ + MetaBackgroundPrivate *priv = self->priv; + CoglHandle texture; + ClutterActor *stage = meta_get_stage_for_screen (priv->screen); + ClutterColor stage_color; + + ensure_pipeline (self); + + unset_texture (self); + set_style (self, G_DESKTOP_BACKGROUND_STYLE_NONE); + + if (color == NULL) + { + clutter_actor_get_background_color (stage, &stage_color); + color = &stage_color; + } + + texture = meta_create_color_texture_4ub (color->red, + color->green, + color->blue, + 0xff, + COGL_TEXTURE_NO_SLICING); + set_texture (self, COGL_TEXTURE (texture)); +} + +typedef struct +{ + GDesktopBackgroundStyle style; + char *filename; +} LoadFileTaskData; + +static LoadFileTaskData * +load_file_task_data_new (const char *filename, + GDesktopBackgroundStyle style) +{ + LoadFileTaskData *task_data; + + task_data = g_slice_new (LoadFileTaskData); + task_data->style = style; + task_data->filename = g_strdup (filename); + + return task_data; +} + +static void +load_file_task_data_free (LoadFileTaskData *task_data) +{ + g_free (task_data->filename); + g_slice_free (LoadFileTaskData, task_data); +} + +static void +load_file (GTask *task, + MetaBackground *self, + LoadFileTaskData *task_data, + GCancellable *cancellable) +{ + GError *error = NULL; + GdkPixbuf *pixbuf; + + pixbuf = gdk_pixbuf_new_from_file (task_data->filename, + &error); + + if (pixbuf == NULL) + { + g_task_return_error (task, error); + return; + } + + g_task_return_pointer (task, pixbuf, (GDestroyNotify) g_object_unref); +} + +/** + * meta_background_load_file_async: + * @self: the #MetaBackground + * @filename: the image file to load + * @style: a #GDesktopBackgroundStyle to specify how background is laid out + * @cancellable: a #GCancellable + * @callback: call back to call when file is loaded or failed to load + * @user_data: user data for callback + * + * Loads the specified image and uses it as the background source. + */ +void +meta_background_load_file_async (MetaBackground *self, + const char *filename, + GDesktopBackgroundStyle style, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + LoadFileTaskData *task_data; + GTask *task; + + task = g_task_new (self, cancellable, callback, user_data); + + task_data = load_file_task_data_new (filename, style); + g_task_set_task_data (task, task_data, (GDestroyNotify) load_file_task_data_free); + + g_task_run_in_thread (task, (GTaskThreadFunc) load_file); +} + +/** + * meta_background_load_file_finish: + * @self: the #MetaBackground + * @result: the result from the #GAsyncReadyCallback passed + * to meta_background_load_file_async() + * @error: a #GError + * + * The finish function for meta_background_load_file_async(). + * + * Returns: whether or not the image was loaded + */ +gboolean +meta_background_load_file_finish (MetaBackground *self, + GAsyncResult *result, + GError **error) +{ + static CoglUserDataKey key; + GTask *task; + LoadFileTaskData *task_data; + CoglTexture *texture; + GdkPixbuf *pixbuf; + int width, height, row_stride; + guchar *pixels; + gboolean has_alpha; + + g_return_val_if_fail (g_task_is_valid (result, self), FALSE); + + task = G_TASK (result); + + pixbuf = g_task_propagate_pointer (task, error); + + if (pixbuf == NULL) + return FALSE; + + task_data = g_task_get_task_data (task); + + width = gdk_pixbuf_get_width (pixbuf); + height = gdk_pixbuf_get_height (pixbuf); + row_stride = gdk_pixbuf_get_rowstride (pixbuf); + pixels = gdk_pixbuf_get_pixels (pixbuf); + has_alpha = gdk_pixbuf_get_has_alpha (pixbuf); + + texture = cogl_texture_new_from_data (width, + height, + COGL_TEXTURE_NO_SLICING, + has_alpha ? + COGL_PIXEL_FORMAT_RGBA_8888 : + COGL_PIXEL_FORMAT_RGB_888, + COGL_PIXEL_FORMAT_ANY, + row_stride, + pixels); + + if (texture == NULL) + { + g_set_error_literal (error, + COGL_BITMAP_ERROR, + COGL_BITMAP_ERROR_FAILED, + _("background texture could not be created from file")); + return FALSE; + } + + cogl_object_set_user_data (COGL_OBJECT (texture), + &key, + g_object_ref (pixbuf), + (CoglUserDataDestroyCallback) + g_object_unref); + + ensure_pipeline (self); + unset_texture (self); + set_style (self, task_data->style); + set_filename (self, task_data->filename); + set_texture (self, texture); + + clutter_content_invalidate (CLUTTER_CONTENT (self)); + + return TRUE; +} + +/** + * meta_background_copy: + * @self: a #MetaBackground to copy + * @monitor: a monitor + * @effects: effects to use on copy of @self + * + * Creates a new #MetaBackground to draw the background for the given monitor. + * Background will be loaded from @self and will share state + * with @self, but may have different effects applied to it. + * + * Return value: (transfer full): the newly created background content + */ +MetaBackground * +meta_background_copy (MetaBackground *self, + int monitor, + MetaBackgroundEffects effects) +{ + MetaBackground *background; + + background = META_BACKGROUND (g_object_new (META_TYPE_BACKGROUND, + "meta-screen", self->priv->screen, + "monitor", monitor, + NULL)); + + background->priv->brightness = self->priv->brightness; + + background->priv->shading_direction = self->priv->shading_direction; + background->priv->color = self->priv->color; + background->priv->second_color = self->priv->second_color; + background->priv->filename = g_strdup (self->priv->filename); + + /* we can reuse the pipeline if it has no effects applied, or + * if it has the same effects applied + */ + if (effects == self->priv->effects || + self->priv->effects == META_BACKGROUND_EFFECTS_NONE) + { + ensure_pipeline (self); + background->priv->pipeline = cogl_pipeline_copy (self->priv->pipeline); + background->priv->texture = cogl_object_ref (self->priv->texture); + background->priv->style = self->priv->style; + background->priv->saturation = self->priv->saturation; + + if (effects != self->priv->effects) + { + set_effects (background, effects); + + if (effects & META_BACKGROUND_EFFECTS_DESATURATE) + set_saturation (background, self->priv->saturation); + + if (effects & META_BACKGROUND_EFFECTS_VIGNETTE) + { + set_brightness (background, self->priv->brightness); + set_vignette_sharpness (background, self->priv->vignette_sharpness); + } + } + else + { + background->priv->effects = self->priv->effects; + } + + } + else + { + ensure_pipeline (background); + if (self->priv->texture != NULL) + set_texture (background, cogl_object_ref (self->priv->texture)); + set_style (background, self->priv->style); + set_effects (background, effects); + + if (effects & META_BACKGROUND_EFFECTS_DESATURATE) + set_saturation (background, self->priv->saturation); + + if (effects & META_BACKGROUND_EFFECTS_VIGNETTE) + { + set_brightness (background, self->priv->brightness); + set_vignette_sharpness (background, self->priv->vignette_sharpness); + } + } + + clutter_content_invalidate (CLUTTER_CONTENT (background)); + + return background; +} +/** + * meta_background_new: + * @screen: the #MetaScreen + * @monitor: a monitor in @screen + * @effects: which effect flags to enable + * + * Creates a new #MetaBackground to draw the background for the given monitor. + * The returned object should be set on a #MetaBackgroundActor with + * clutter_actor_set_content(). + * + * The background may be desaturated, blurred, or given a vignette depending + * on @effects. + * + * Return value: the newly created background content + */ +MetaBackground * +meta_background_new (MetaScreen *screen, + int monitor, + MetaBackgroundEffects effects) +{ + MetaBackground *background; + + background = META_BACKGROUND (g_object_new (META_TYPE_BACKGROUND, + "meta-screen", screen, + "monitor", monitor, + "effects", effects, + NULL)); + return background; +} + +/** + * meta_background_get_style: + * @self: a #MetaBackground + * + * Returns the current background style. + * + * Return value: a #GDesktopBackgroundStyle + */ +GDesktopBackgroundStyle +meta_background_get_style (MetaBackground *self) +{ + return self->priv->style; +} + +/** + * meta_background_get_shading: + * @self: a #MetaBackground + * + * Returns whether @self is a solid color, + * vertical gradient, horizontal gradient, + * or none of the above. + * + * Return value: a #GDesktopBackgroundShading + */ +GDesktopBackgroundShading +meta_background_get_shading (MetaBackground *self) +{ + return self->priv->shading_direction; +} + +/** + * meta_background_get_color: + * @self: a #MetaBackground + * + * Returns the first color of @self. If self + * is a gradient, the second color can be returned + * with meta_background_get_second_color(). + * + * Return value: (transfer none): a #ClutterColor + */ +const ClutterColor * +meta_background_get_color (MetaBackground *self) +{ + return &self->priv->color; +} + +/** + * meta_background_get_second_color: + * @self: a #MetaBackground + * + * Returns the second color of @self. If @self + * is not a gradient this function is undefined. + * + * Return value: (transfer none): a #ClutterColor + */ +const ClutterColor * +meta_background_get_second_color (MetaBackground *self) +{ + return &self->priv->second_color; +} + +/** + * meta_background_get_filename: + * @self: a #MetaBackground + * + * Returns the filename of the currently loaded file. + * IF @self is not loaded from a file this function is + * undefined. + * + * Return value: (transfer none): the filename + */ +const char * +meta_background_get_filename (MetaBackground *self) +{ + return self->priv->filename; +} diff --git a/src/compositor/meta-window-group.c b/src/compositor/meta-window-group.c index e3aac5877..5dba9711a 100644 --- a/src/compositor/meta-window-group.c +++ b/src/compositor/meta-window-group.c @@ -7,11 +7,12 @@ #include /* for gdk_rectangle_intersect() */ +#include "clutter-utils.h" #include "compositor-private.h" #include "meta-window-actor-private.h" #include "meta-window-group.h" #include "meta-background-actor-private.h" -#include "clutter-utils.h" +#include "meta-background-group-private.h" struct _MetaWindowGroupClass { @@ -203,9 +204,10 @@ meta_window_group_paint (ClutterActor *actor) meta_window_actor_set_visible_region_beneath (window_actor, visible_region); cairo_region_translate (visible_region, x, y); } - else if (META_IS_BACKGROUND_ACTOR (l->data)) + else if (META_IS_BACKGROUND_ACTOR (l->data) || + META_IS_BACKGROUND_GROUP (l->data)) { - MetaBackgroundActor *background_actor = l->data; + ClutterActor *background_actor = l->data; int x, y; if (!meta_actor_is_untransformed (CLUTTER_ACTOR (background_actor), &x, &y)) @@ -215,7 +217,11 @@ meta_window_group_paint (ClutterActor *actor) y += paint_y_offset; cairo_region_translate (visible_region, - x, - y); - meta_background_actor_set_visible_region (background_actor, visible_region); + + if (META_IS_BACKGROUND_GROUP (background_actor)) + meta_background_group_set_visible_region (META_BACKGROUND_GROUP (background_actor), visible_region); + else + meta_background_actor_set_visible_region (META_BACKGROUND_ACTOR (background_actor), visible_region); cairo_region_translate (visible_region, x, y); } } diff --git a/src/meta/compositor-mutter.h b/src/meta/compositor-mutter.h index 161cfad90..665330a8a 100644 --- a/src/meta/compositor-mutter.h +++ b/src/meta/compositor-mutter.h @@ -44,7 +44,6 @@ ClutterActor *meta_get_top_window_group_for_screen (MetaScreen *screen); void meta_disable_unredirect_for_screen (MetaScreen *screen); void meta_enable_unredirect_for_screen (MetaScreen *screen); -ClutterActor *meta_get_background_actor_for_screen (MetaScreen *screen); void meta_set_stage_input_region (MetaScreen *screen, XserverRegion region); void meta_empty_stage_input_region (MetaScreen *screen); diff --git a/src/meta/meta-background-actor.h b/src/meta/meta-background-actor.h index 71831af25..69ec37764 100644 --- a/src/meta/meta-background-actor.h +++ b/src/meta/meta-background-actor.h @@ -24,9 +24,13 @@ #define META_BACKGROUND_ACTOR_H #include +#include +#include #include +#include + /** * MetaBackgroundActor: * @@ -60,41 +64,6 @@ struct _MetaBackgroundActor GType meta_background_actor_get_type (void); -ClutterActor *meta_background_actor_new_for_screen (MetaScreen *screen); - -/** - * MetaSnippetHook: - * Temporary hack to work around Cogl not exporting CoglSnippetHook in - * the 1.0 API. Don't use. - */ -typedef enum { - /* Per pipeline vertex hooks */ - META_SNIPPET_HOOK_VERTEX = 0, - META_SNIPPET_HOOK_VERTEX_TRANSFORM, - - /* Per pipeline fragment hooks */ - META_SNIPPET_HOOK_FRAGMENT = 2048, - - /* Per layer vertex hooks */ - META_SNIPPET_HOOK_TEXTURE_COORD_TRANSFORM = 4096, - - /* Per layer fragment hooks */ - META_SNIPPET_HOOK_LAYER_FRAGMENT = 6144, - META_SNIPPET_HOOK_TEXTURE_LOOKUP -} MetaSnippetHook; - - -void meta_background_actor_add_glsl_snippet (MetaBackgroundActor *actor, - MetaSnippetHook hook, - const char *declarations, - const char *code, - gboolean is_replace); - -void meta_background_actor_set_uniform_float (MetaBackgroundActor *actor, - const char *uniform_name, - int n_components, - int count, - const float *uniform, - int uniform_length); +ClutterActor *meta_background_actor_new (void); #endif /* META_BACKGROUND_ACTOR_H */ diff --git a/src/meta/meta-background-group.h b/src/meta/meta-background-group.h new file mode 100644 index 000000000..cddc1c5f3 --- /dev/null +++ b/src/meta/meta-background-group.h @@ -0,0 +1,46 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + +#ifndef META_BACKGROUND_GROUP_H +#define META_BACKGROUND_GROUP_H + +#include + +/** + * MetaBackgroundGroup: + * + * This class is a subclass of ClutterGroup with special handling for + * MetaBackgroundActor when painting the group. It makes sure to only + * draw the parts of the backgrounds not occluded by opaque windows. + * + * See #MetaWindowGroup for more information behind the motivation, + * and details on implementation. + */ + +#define META_TYPE_BACKGROUND_GROUP (meta_background_group_get_type ()) +#define META_BACKGROUND_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_BACKGROUND_GROUP, MetaBackgroundGroup)) +#define META_BACKGROUND_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_BACKGROUND_GROUP, MetaBackgroundGroupClass)) +#define META_IS_BACKGROUND_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_BACKGROUND_GROUP)) +#define META_IS_BACKGROUND_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_BACKGROUND_GROUP)) +#define META_BACKGROUND_GROUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_BACKGROUND_GROUP, MetaBackgroundGroupClass)) + +typedef struct _MetaBackgroundGroup MetaBackgroundGroup; +typedef struct _MetaBackgroundGroupClass MetaBackgroundGroupClass; +typedef struct _MetaBackgroundGroupPrivate MetaBackgroundGroupPrivate; + +struct _MetaBackgroundGroupClass +{ + ClutterGroupClass parent_class; +}; + +struct _MetaBackgroundGroup +{ + ClutterGroup parent; + + MetaBackgroundGroupPrivate *priv; +}; + +GType meta_background_group_get_type (void); + +ClutterActor *meta_background_group_new (void); + +#endif /* META_BACKGROUND_GROUP_H */ diff --git a/src/meta/meta-background.h b/src/meta/meta-background.h new file mode 100644 index 000000000..bd73e9226 --- /dev/null +++ b/src/meta/meta-background.h @@ -0,0 +1,110 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* + * meta-background.h: CoglTexture for paintnig the system background + * + * Copyright 2013 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef META_BACKGROUND_H +#define META_BACKGROUND_H + +#include +#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. + */ + +#define META_TYPE_BACKGROUND (meta_background_get_type ()) +#define META_BACKGROUND(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_BACKGROUND, MetaBackground)) +#define META_BACKGROUND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_BACKGROUND, MetaBackgroundClass)) +#define META_IS_BACKGROUND(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_BACKGROUND)) +#define META_IS_BACKGROUND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_BACKGROUND)) +#define META_BACKGROUND_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_BACKGROUND, MetaBackgroundClass)) + +typedef struct _MetaBackground MetaBackground; +typedef struct _MetaBackgroundClass MetaBackgroundClass; +typedef struct _MetaBackgroundPrivate MetaBackgroundPrivate; + +/** + * MetaBackgroundEffects: + * Which effects to enable on the background + */ + +typedef enum +{ + META_BACKGROUND_EFFECTS_NONE = 0, + META_BACKGROUND_EFFECTS_DESATURATE = 1 << 0, + META_BACKGROUND_EFFECTS_BLUR = 1 << 1, + META_BACKGROUND_EFFECTS_VIGNETTE = 1 << 2, +} MetaBackgroundEffects; + +struct _MetaBackgroundClass +{ + GObjectClass parent_class; +}; + +struct _MetaBackground +{ + GObject parent; + + MetaBackgroundPrivate *priv; +}; + +GType meta_background_get_type (void); + +MetaBackground *meta_background_new (MetaScreen *screen, + int monitor, + MetaBackgroundEffects effects); +MetaBackground *meta_background_copy (MetaBackground *self, + int monitor, + MetaBackgroundEffects effects); + +void meta_background_load_gradient (MetaBackground *self, + GDesktopBackgroundShading shading_direction, + ClutterColor *color, + ClutterColor *second_color); +void meta_background_load_color (MetaBackground *self, + ClutterColor *color); +void meta_background_load_still_frame (MetaBackground *self); +void meta_background_load_file_async (MetaBackground *self, + const char *filename, + GDesktopBackgroundStyle style, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean meta_background_load_file_finish (MetaBackground *self, + GAsyncResult *result, + GError **error); + +const char *meta_background_get_filename (MetaBackground *self); +GDesktopBackgroundStyle meta_background_get_style (MetaBackground *self); +GDesktopBackgroundShading meta_background_get_shading (MetaBackground *self); +const ClutterColor *meta_background_get_color (MetaBackground *self); +const ClutterColor *meta_background_get_second_color (MetaBackground *self); + +#endif /* META_BACKGROUND_H */