diff --git a/src/Makefile.am b/src/Makefile.am index 5ad84f33c..ff3c30ccc 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -25,6 +25,8 @@ mutter_SOURCES= \ compositor/cogl-utils.h \ compositor/compositor.c \ compositor/compositor-private.h \ + compositor/meta-background-actor.c \ + compositor/meta-background-actor.h \ compositor/meta-module.c \ compositor/meta-module.h \ compositor/meta-plugin.c \ diff --git a/src/compositor/compositor-private.h b/src/compositor/compositor-private.h index 37ace9633..03d218ca6 100644 --- a/src/compositor/compositor-private.h +++ b/src/compositor/compositor-private.h @@ -35,6 +35,7 @@ struct _MetaCompScreen MetaScreen *screen; ClutterActor *stage, *window_group, *overlay_group; + ClutterActor *background_actor; ClutterActor *hidden_group; GList *windows; GHashTable *windows_by_xid; diff --git a/src/compositor/compositor.c b/src/compositor/compositor.c index 7eb10defa..4b848b4d0 100644 --- a/src/compositor/compositor.c +++ b/src/compositor/compositor.c @@ -14,6 +14,7 @@ #include "meta-shadow-factory.h" #include "meta-window-actor-private.h" #include "meta-window-group.h" +#include "meta-background-actor.h" #include "../core/window-private.h" /* to check window->hidden */ #include "../core/display-private.h" /* for meta_display_lookup_x_window() */ #include @@ -136,6 +137,22 @@ 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)) + { + MetaCompScreen *info = meta_screen_get_compositor_data (screen); + meta_background_actor_update (META_BACKGROUND_ACTOR (info->background_actor)); + return; + } + } + } + if (window == NULL) return; @@ -236,6 +253,27 @@ meta_get_window_group_for_screen (MetaScreen *screen) return info->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 @@ -494,9 +532,14 @@ meta_compositor_manage_screen (MetaCompositor *compositor, XSelectInput (xdisplay, xwin, event_mask); info->window_group = meta_window_group_new (screen); + info->background_actor = meta_background_actor_new (screen); info->overlay_group = clutter_group_new (); info->hidden_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->overlay_group, @@ -844,6 +887,25 @@ sync_actor_stacking (MetaCompScreen *info) reordered = FALSE; old = children; + + /* We allow for actors in the window group other than the actors we + * know about, but it's up to a plugin to try and keep them stacked correctly + * (we really need extra API to make that reliable.) + */ + + /* Of the actors we know, the bottom actor should be the background actor */ + + while (old && old->data != info->background_actor && !META_IS_WINDOW_ACTOR (old->data)) + old = old->next; + if (old == NULL || old->data != info->background_actor) + { + reordered = TRUE; + goto done_with_check; + } + + /* Then the window actors should follow in sequence */ + + old = old->next; for (tmp = info->windows; tmp != NULL; tmp = tmp->next) { while (old && !META_IS_WINDOW_ACTOR (old->data)) @@ -854,14 +916,13 @@ sync_actor_stacking (MetaCompScreen *info) if (old == NULL || old->data != tmp->data) { reordered = TRUE; - break; + goto done_with_check; } old = old->next; } - if (old != NULL) /* An extra MetaWindowActor??? .... restack */ - reordered = TRUE; + done_with_check: g_list_free (children); @@ -874,6 +935,8 @@ sync_actor_stacking (MetaCompScreen *info) clutter_actor_lower_bottom (CLUTTER_ACTOR (window_actor)); } + + clutter_actor_lower_bottom (info->background_actor); } void @@ -1018,6 +1081,8 @@ meta_compositor_sync_screen_size (MetaCompositor *compositor, g_return_if_fail (info); clutter_actor_set_size (info->stage, width, height); + /* Background actor always requests the screen size */ + clutter_actor_queue_relayout (info->background_actor); meta_verbose ("Changed size for stage on screen %d to %dx%d\n", meta_screen_get_screen_number (screen), diff --git a/src/compositor/meta-background-actor.c b/src/compositor/meta-background-actor.c new file mode 100644 index 000000000..af8677803 --- /dev/null +++ b/src/compositor/meta-background-actor.c @@ -0,0 +1,380 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* + * meta-background-actor.c: Actor for painting the root window background + * + * Copyright 2009 Sander Dijkhuis + * 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 + * 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. + * + * Portions adapted from gnome-shell/src/shell-global.c + */ + +#include + +#define COGL_ENABLE_EXPERIMENTAL_API +#include + +#include + +#include "cogl-utils.h" +#include "compositor-private.h" +#include "errors.h" +#include "meta-background-actor.h" + +struct _MetaBackgroundActorClass +{ + ClutterActorClass parent_class; +}; + +struct _MetaBackgroundActor +{ + ClutterActor parent; + + CoglHandle material; + MetaScreen *screen; + cairo_region_t *visible_region; + float texture_width; + float texture_height; + + guint have_pixmap : 1; +}; + +G_DEFINE_TYPE (MetaBackgroundActor, meta_background_actor, CLUTTER_TYPE_ACTOR); + +static void +set_texture (MetaBackgroundActor *self, + CoglHandle texture) +{ + MetaDisplay *display; + + display = meta_screen_get_display (self->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_material_set_layer (self->material, 0, texture); + meta_error_trap_pop (display); + + self->texture_width = cogl_texture_get_width (texture); + self->texture_height = cogl_texture_get_height (texture); + + clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); +} + +/* Sets our material 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 (MetaBackgroundActor *self) +{ + ClutterActor *stage = meta_get_stage_for_screen (self->screen); + ClutterColor color; + CoglHandle texture; + + clutter_stage_get_color (CLUTTER_STAGE (stage), &color); + texture = meta_create_color_texture_4ub (color.red, color.green, + color.blue, 0xff); + set_texture (self, texture); + cogl_handle_unref (texture); +} + +static void +on_notify_stage_color (GObject *stage, + GParamSpec *pspec, + MetaBackgroundActor *self) +{ + if (!self->have_pixmap) + set_texture_to_stage_color (self); +} + +static void +meta_background_actor_dispose (GObject *object) +{ + MetaBackgroundActor *self = META_BACKGROUND_ACTOR (object); + + meta_background_actor_set_visible_region (self, NULL); + + if (self->material != COGL_INVALID_HANDLE) + { + cogl_handle_unref (self->material); + self->material = COGL_INVALID_HANDLE; + } + + if (self->screen != NULL) + { + ClutterActor *stage = meta_get_stage_for_screen (self->screen); + g_signal_handlers_disconnect_by_func (stage, + (gpointer) on_notify_stage_color, + self); + self->screen = NULL; + } +} + +static void +meta_background_actor_get_preferred_width (ClutterActor *actor, + gfloat for_height, + gfloat *min_width_p, + gfloat *natural_width_p) +{ + MetaBackgroundActor *self = META_BACKGROUND_ACTOR (actor); + int width, height; + + meta_screen_get_size (self->screen, &width, &height); + + if (min_width_p) + *min_width_p = width; + if (natural_width_p) + *natural_width_p = height; +} + +static void +meta_background_actor_get_preferred_height (ClutterActor *actor, + gfloat for_width, + gfloat *min_height_p, + gfloat *natural_height_p) + +{ + MetaBackgroundActor *self = META_BACKGROUND_ACTOR (actor); + int width, height; + + meta_screen_get_size (self->screen, &width, &height); + + if (min_height_p) + *min_height_p = height; + if (natural_height_p) + *natural_height_p = height; +} + +static void +meta_background_actor_paint (ClutterActor *actor) +{ + MetaBackgroundActor *self = META_BACKGROUND_ACTOR (actor); + int width, height; + + meta_screen_get_size (self->screen, &width, &height); + + cogl_set_source (self->material); + + if (self->visible_region) + { + int n_rectangles = cairo_region_num_rectangles (self->visible_region); + int i; + + for (i = 0; i < n_rectangles; i++) + { + cairo_rectangle_int_t rect; + cairo_region_get_rectangle (self->visible_region, i, &rect); + + cogl_rectangle_with_texture_coords (rect.x, rect.y, + rect.x + rect.width, rect.y + rect.height, + rect.x / self->texture_width, + rect.y / self->texture_height, + (rect.x + rect.width) / self->texture_width, + (rect.y + rect.height) / self->texture_height); + } + } + else + { + cogl_rectangle_with_texture_coords (0.0f, 0.0f, + width, height, + 0.0f, 0.0f, + width / self->texture_width, + height / self->texture_height); + } +} + +#if CLUTTER_CHECK_VERSION(1, 5, 2) +static gboolean +meta_background_actor_get_paint_volume (ClutterActor *actor, + ClutterPaintVolume *volume) +{ + MetaBackgroundActor *self = META_BACKGROUND_ACTOR (actor); + int width, height; + + meta_screen_get_size (self->screen, &width, &height); + + clutter_paint_volume_set_width (volume, width); + clutter_paint_volume_set_height (volume, height); + + return TRUE; +} +#endif + +static void +meta_background_actor_class_init (MetaBackgroundActorClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); + + object_class->dispose = meta_background_actor_dispose; + + 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; +#if CLUTTER_CHECK_VERSION(1, 5, 2) + actor_class->get_paint_volume = meta_background_actor_get_paint_volume; +#endif +} + +static void +meta_background_actor_init (MetaBackgroundActor *background_actor) +{ +} + +/** + * @screen: the #MetaScreen + * meta_background_actor_new: + * + * Creates a new actor to draw the background for the given screen. + * + * Return value: (transfer none): the newly created background actor + */ +ClutterActor * +meta_background_actor_new (MetaScreen *screen) +{ + MetaBackgroundActor *self; + ClutterActor *stage; + + g_return_val_if_fail (META_IS_SCREEN (screen), NULL); + + self = g_object_new (META_TYPE_BACKGROUND_ACTOR, NULL); + + self->screen = screen; + + self->material = meta_create_texture_material (NULL); + cogl_material_set_layer_wrap_mode (self->material, 0, + COGL_MATERIAL_WRAP_MODE_REPEAT); + + stage = meta_get_stage_for_screen (self->screen); + g_signal_connect (stage, "notify::color", + G_CALLBACK (on_notify_stage_color), self); + + meta_background_actor_update (self); + + return CLUTTER_ACTOR (self); +} + +/** + * meta_background_actor_update: + * @self: a #MetaBackgroundActor + * + * 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 (MetaBackgroundActor *self) +{ + MetaDisplay *display; + MetaCompositor *compositor; + Atom type; + int format; + gulong nitems; + gulong bytes_after; + guchar *data; + Pixmap root_pixmap_id; + + g_return_if_fail (META_IS_BACKGROUND_ACTOR (self)); + + display = meta_screen_get_display (self->screen); + compositor = meta_display_get_compositor (display); + + root_pixmap_id = None; + if (!XGetWindowProperty (meta_display_get_xdisplay (display), + meta_screen_get_xroot (self->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; + + meta_error_trap_push (display); + texture = cogl_texture_pixmap_x11_new (root_pixmap_id, FALSE); + meta_error_trap_pop (display); + + if (texture != COGL_INVALID_HANDLE) + { + set_texture (self, texture); + cogl_handle_unref (texture); + + self->have_pixmap = True; + return; + } + } + + self->have_pixmap = False; + set_texture_to_stage_color (self); +} + +/** + * meta_background_actor_set_visible_region: + * @self: a #MetaBackgroundActor + * @visible_region: (allow-none): the area of the actor (in allocate-relative + * coordinates) that is visible. + * + * Sets the area of the background that is unobscured by overlapping windows. + * This is used to optimize and only paint the visible portions. + */ +void +meta_background_actor_set_visible_region (MetaBackgroundActor *self, + cairo_region_t *visible_region) +{ + g_return_if_fail (META_IS_BACKGROUND_ACTOR (self)); + + if (self->visible_region) + { + cairo_region_destroy (self->visible_region); + self->visible_region = NULL; + } + + if (visible_region) + { + cairo_rectangle_int_t screen_rect = { 0 }; + meta_screen_get_size (self->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. + */ + self->visible_region = cairo_region_create_rectangle (&screen_rect); + cairo_region_intersect (self->visible_region, visible_region); + } +} + diff --git a/src/compositor/meta-background-actor.h b/src/compositor/meta-background-actor.h new file mode 100644 index 000000000..b211c4b64 --- /dev/null +++ b/src/compositor/meta-background-actor.h @@ -0,0 +1,57 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* + * meta-background-actor.h: Actor for painting the root window background + * + * 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 + * 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_ACTOR_H +#define META_BACKGROUND_ACTOR_H + +#include + +#include "screen.h" + +/** + * MetaBackgroundActor: + * + * 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_ACTOR (meta_background_actor_get_type ()) +#define META_BACKGROUND_ACTOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_BACKGROUND_ACTOR, MetaBackgroundActor)) +#define META_BACKGROUND_ACTOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_BACKGROUND_ACTOR, MetaBackgroundActorClass)) +#define META_IS_BACKGROUND_ACTOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_BACKGROUND_ACTOR)) +#define META_IS_BACKGROUND_ACTOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_BACKGROUND_ACTOR)) +#define META_BACKGROUND_ACTOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_BACKGROUND_ACTOR, MetaBackgroundActorClass)) + +typedef struct _MetaBackgroundActor MetaBackgroundActor; +typedef struct _MetaBackgroundActorClass MetaBackgroundActorClass; +typedef struct _MetaBackgroundActorPrivate MetaBackgroundActorPrivate; + +GType meta_background_actor_get_type (void); + +ClutterActor *meta_background_actor_new (MetaScreen *screen); + +void meta_background_actor_update (MetaBackgroundActor *actor); +void meta_background_actor_set_visible_region (MetaBackgroundActor *self, + cairo_region_t *visible_region); + +#endif /* META_BACKGROUND_ACTOR_H */ diff --git a/src/compositor/meta-plugin.c b/src/compositor/meta-plugin.c index 8a544c2f3..d18c8e117 100644 --- a/src/compositor/meta-plugin.c +++ b/src/compositor/meta-plugin.c @@ -351,6 +351,14 @@ meta_plugin_get_window_group (MetaPlugin *plugin) return meta_get_window_group_for_screen (priv->screen); } +ClutterActor * +meta_plugin_get_background_actor (MetaPlugin *plugin) +{ + MetaPluginPrivate *priv = META_PLUGIN (plugin)->priv; + + return meta_get_background_actor_for_screen (priv->screen); +} + /** * _meta_plugin_effect_started: * @plugin: the plugin diff --git a/src/compositor/meta-window-group.c b/src/compositor/meta-window-group.c index d501d837a..857eab0be 100644 --- a/src/compositor/meta-window-group.c +++ b/src/compositor/meta-window-group.c @@ -9,6 +9,7 @@ #include "meta-window-actor-private.h" #include "meta-window-group.h" +#include "meta-background-actor.h" struct _MetaWindowGroupClass { @@ -152,31 +153,37 @@ meta_window_group_paint (ClutterActor *actor) for (l = children; l; l = l->next) { - MetaWindowActor *window_actor; - gboolean x, y; - - if (!META_IS_WINDOW_ACTOR (l->data) || !CLUTTER_ACTOR_IS_VISIBLE (l->data)) + if (!CLUTTER_ACTOR_IS_VISIBLE (l->data)) continue; - window_actor = l->data; - - if (!actor_is_untransformed (CLUTTER_ACTOR (window_actor), &x, &y)) - continue; - - /* Temporarily move to the coordinate system of the actor */ - cairo_region_translate (visible_region, - x, - y); - - meta_window_actor_set_visible_region (window_actor, visible_region); - - if (clutter_actor_get_paint_opacity (CLUTTER_ACTOR (window_actor)) == 0xff) + if (META_IS_WINDOW_ACTOR (l->data)) { - cairo_region_t *obscured_region = meta_window_actor_get_obscured_region (window_actor); - if (obscured_region) - cairo_region_subtract (visible_region, obscured_region); - } + MetaWindowActor *window_actor = l->data; + gboolean x, y; - meta_window_actor_set_visible_region_beneath (window_actor, visible_region); - cairo_region_translate (visible_region, x, y); + if (!actor_is_untransformed (CLUTTER_ACTOR (window_actor), &x, &y)) + continue; + + /* Temporarily move to the coordinate system of the actor */ + cairo_region_translate (visible_region, - x, - y); + + meta_window_actor_set_visible_region (window_actor, visible_region); + + if (clutter_actor_get_paint_opacity (CLUTTER_ACTOR (window_actor)) == 0xff) + { + cairo_region_t *obscured_region = meta_window_actor_get_obscured_region (window_actor); + if (obscured_region) + cairo_region_subtract (visible_region, obscured_region); + } + + 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)) + { + MetaBackgroundActor *background_actor = l->data; + meta_background_actor_set_visible_region (background_actor, visible_region); + } } cairo_region_destroy (visible_region); @@ -188,13 +195,17 @@ meta_window_group_paint (ClutterActor *actor) */ for (l = children; l; l = l->next) { - MetaWindowActor *window_actor; - - if (!META_IS_WINDOW_ACTOR (l->data)) - continue; - - window_actor = l->data; - meta_window_actor_reset_visible_regions (window_actor); + if (META_IS_WINDOW_ACTOR (l->data)) + { + MetaWindowActor *window_actor = l->data; + window_actor = l->data; + meta_window_actor_reset_visible_regions (window_actor); + } + else if (META_IS_BACKGROUND_ACTOR (l->data)) + { + MetaBackgroundActor *background_actor = l->data; + meta_background_actor_set_visible_region (background_actor, NULL); + } } g_list_free (children); diff --git a/src/include/compositor-mutter.h b/src/include/compositor-mutter.h index d27e0da3f..b893106b2 100644 --- a/src/include/compositor-mutter.h +++ b/src/include/compositor-mutter.h @@ -39,4 +39,6 @@ Window meta_get_overlay_window (MetaScreen *screen); GList *meta_get_window_actors (MetaScreen *screen); ClutterActor *meta_get_window_group_for_screen (MetaScreen *screen); +ClutterActor *meta_get_background_actor_for_screen (MetaScreen *screen); + #endif diff --git a/src/include/meta-plugin.h b/src/include/meta-plugin.h index 78eabe9c8..36b7fbfd2 100644 --- a/src/include/meta-plugin.h +++ b/src/include/meta-plugin.h @@ -249,6 +249,9 @@ meta_plugin_get_overlay_group (MetaPlugin *plugin); ClutterActor * meta_plugin_get_window_group (MetaPlugin *plugin); +ClutterActor * +meta_plugin_get_background_actor (MetaPlugin *plugin); + ClutterActor * meta_plugin_get_stage (MetaPlugin *plugin);