From 1bbaec81ced680fc1b3ea16dd708402cb81455a0 Mon Sep 17 00:00:00 2001 From: "Owen W. Taylor" Date: Thu, 11 Nov 2010 16:24:43 -0500 Subject: [PATCH] Make window shadows globally configurable Instead of setting shadow parameters on individual windows, add the idea of a "shadow class". Windows have default shadow classes based on their frame and window type, which can be overriden by setting the shadow-class property. Each shadow class has separably configurable parameters for the focused and unfocused state. New shadow classes can be defined with arbitrary names. https://bugzilla.gnome.org/show_bug.cgi?id=592382 --- src/compositor/compositor.c | 26 ++ src/compositor/meta-shadow-factory-private.h | 12 +- src/compositor/meta-shadow-factory.c | 207 ++++++++++-- src/compositor/meta-window-actor-private.h | 2 + src/compositor/meta-window-actor.c | 314 +++++++++---------- src/include/meta-shadow-factory.h | 34 ++ 6 files changed, 411 insertions(+), 184 deletions(-) diff --git a/src/compositor/compositor.c b/src/compositor/compositor.c index fc13b6137..ad964b6ad 100644 --- a/src/compositor/compositor.c +++ b/src/compositor/compositor.c @@ -11,6 +11,7 @@ #include "compositor-mutter.h" #include "xprops.h" #include "prefs.h" +#include "meta-shadow-factory.h" #include "meta-window-actor-private.h" #include "meta-window-group.h" #include "../core/window-private.h" /* to check window->hidden */ @@ -1017,6 +1018,26 @@ meta_repaint_func (gpointer data) return TRUE; } +static void +on_shadow_factory_changed (MetaShadowFactory *factory, + MetaCompositor *compositor) +{ + GSList *screens = meta_display_get_screens (compositor->display); + GList *l; + GSList *sl; + + for (sl = screens; sl; sl = sl->next) + { + MetaScreen *screen = sl->data; + MetaCompScreen *info = meta_screen_get_compositor_data (screen); + if (!info) + continue; + + for (l = info->windows; l; l = l->next) + meta_window_actor_invalidate_shadow (l->data); + } +} + /** * meta_compositor_new: (skip) * @@ -1047,6 +1068,11 @@ meta_compositor_new (MetaDisplay *display) XInternAtoms (xdisplay, atom_names, G_N_ELEMENTS (atom_names), False, atoms); + g_signal_connect (meta_shadow_factory_get_default (), + "changed", + G_CALLBACK (on_shadow_factory_changed), + compositor); + compositor->atom_x_root_pixmap = atoms[0]; compositor->atom_x_set_root = atoms[1]; compositor->atom_net_wm_window_opacity = atoms[2]; diff --git a/src/compositor/meta-shadow-factory-private.h b/src/compositor/meta-shadow-factory-private.h index 713ddc601..591dd07bc 100644 --- a/src/compositor/meta-shadow-factory-private.h +++ b/src/compositor/meta-shadow-factory-private.h @@ -56,11 +56,11 @@ void meta_shadow_get_bounds (MetaShadow *shadow, MetaShadowFactory *meta_shadow_factory_new (void); -MetaShadow * meta_shadow_factory_get_shadow (MetaShadowFactory *factory, - MetaWindowShape *shape, - int width, - int height, - int radius, - int top_fade); +MetaShadow *meta_shadow_factory_get_shadow (MetaShadowFactory *factory, + MetaWindowShape *shape, + int width, + int height, + const char *class_name, + gboolean focused); #endif /* __META_SHADOW_FACTORY_PRIVATE_H__ */ diff --git a/src/compositor/meta-shadow-factory.c b/src/compositor/meta-shadow-factory.c index 2dc426acc..bf0aadf52 100644 --- a/src/compositor/meta-shadow-factory.c +++ b/src/compositor/meta-shadow-factory.c @@ -48,7 +48,8 @@ * - We approximate the 1D gaussian blur as 3 successive box filters. */ -typedef struct _MetaShadowCacheKey MetaShadowCacheKey; +typedef struct _MetaShadowCacheKey MetaShadowCacheKey; +typedef struct _MetaShadowClassInfo MetaShadowClassInfo; struct _MetaShadowCacheKey { @@ -82,6 +83,13 @@ struct _MetaShadow guint scale_height : 1; }; +struct _MetaShadowClassInfo +{ + const char *name; /* const so we can reuse for static definitions */ + MetaShadowParams focused; + MetaShadowParams unfocused; +}; + struct _MetaShadowFactory { GObject parent_instance; @@ -89,6 +97,9 @@ struct _MetaShadowFactory /* MetaShadowCacheKey => MetaShadow; the shadows are not referenced * by the factory, they are simply removed from the table when freed */ GHashTable *shadows; + + /* class name => MetaShadowClassInfo */ + GHashTable *shadow_classes; }; struct _MetaShadowFactoryClass @@ -96,6 +107,31 @@ struct _MetaShadowFactoryClass GObjectClass parent_class; }; +enum +{ + CHANGED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +/* The first element in this array also defines the default parameters + * for newly created classes */ +MetaShadowClassInfo default_shadow_classes[] = { + { "normal", { 12, -1, 0, 8, 255 }, { 6, -1, 0, 4, 255 } }, + { "dialog", { 12, -1, 0, 8, 255 }, { 6, -1, 0, 4, 255 } }, + { "modal_dialog", { 12, -1, 0, 8, 255 }, { 6, -1, 0, 4, 255 } }, + { "utility", { 12, -1, 0, 8, 255 }, { 6, -1, 0, 4, 255 } }, + { "border", { 12, -1, 0, 8, 255 }, { 6, -1, 0, 4, 255 } }, + { "menu", { 12, -1, 0, 8, 255 }, { 6, -1, 0, 4, 255 } }, + + { "popup-menu", { 6, -1, 0, 4, 255 }, { 6, -1, 0, 4, 255 } }, + + { "dropdown-menu", { 6, 25, 0, 4, 255 }, { 6, 100, 0, 4, 255 } }, + { "attached", { 6, 25, 0, 4, 255 }, { 6, 100, 0, 4, 255 } } +}; + G_DEFINE_TYPE (MetaShadowFactory, meta_shadow_factory, G_TYPE_OBJECT); static guint @@ -262,11 +298,36 @@ meta_shadow_get_bounds (MetaShadow *shadow, bounds->height = window_height + shadow->outer_border_top + shadow->outer_border_bottom; } +static void +meta_shadow_class_info_free (MetaShadowClassInfo *class_info) +{ + g_free ((char *)class_info->name); + g_slice_free (MetaShadowClassInfo, class_info); +} + static void meta_shadow_factory_init (MetaShadowFactory *factory) { + guint i; + factory->shadows = g_hash_table_new (meta_shadow_cache_key_hash, meta_shadow_cache_key_equal); + + factory->shadow_classes = g_hash_table_new_full (g_str_hash, + g_str_equal, + NULL, + (GDestroyNotify)meta_shadow_class_info_free); + + for (i = 0; i < G_N_ELEMENTS (default_shadow_classes); i++) + { + MetaShadowClassInfo *class_info = g_slice_new (MetaShadowClassInfo); + + *class_info = default_shadow_classes[i]; + class_info->name = g_strdup (class_info->name); + + g_hash_table_insert (factory->shadow_classes, + (char *)class_info->name, class_info); + } } static void @@ -286,6 +347,7 @@ meta_shadow_factory_finalize (GObject *object) } g_hash_table_destroy (factory->shadows); + g_hash_table_destroy (factory->shadow_classes); G_OBJECT_CLASS (meta_shadow_factory_parent_class)->finalize (object); } @@ -296,6 +358,15 @@ meta_shadow_factory_class_init (MetaShadowFactoryClass *klass) GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_shadow_factory_finalize; + + signals[CHANGED] = + g_signal_new ("changed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); } MetaShadowFactory * @@ -657,15 +728,45 @@ make_shadow (MetaShadow *shadow, cogl_material_set_layer (shadow->material, 0, shadow->texture); } +static MetaShadowParams * +get_shadow_params (MetaShadowFactory *factory, + const char *class_name, + gboolean focused, + gboolean create) +{ + MetaShadowClassInfo *class_info = g_hash_table_lookup (factory->shadow_classes, + class_name); + if (class_info == NULL) + { + if (create) + { + class_info = g_slice_new0 (MetaShadowClassInfo); + *class_info = default_shadow_classes[0]; + class_info->name = g_strdup (class_info->name); + + g_hash_table_insert (factory->shadow_classes, + (char *)class_info->name, class_info); + } + else + { + class_info = &default_shadow_classes[0]; + } + } + + if (focused) + return &class_info->focused; + else + return &class_info->unfocused; +} + /** * meta_shadow_factory_get_shadow: * @factory: a #MetaShadowFactory * @shape: the size-invariant shape of the window's region * @width: the actual width of the window's region - * @width: the actual height of the window's region - * @radius: the radius (gaussian standard deviation) of the shadow - * @top_fade: if >= 0, the shadow doesn't extend above the top - * of the shape, and fades out over the given number of pixels + * @height: the actual height of the window's region + * @class_name: name of the class of window shadows + * @focused: whether the shadow is for a focused window * * Gets the appropriate shadow object for drawing shadows for the * specified window shape. The region that we are shadowing is specified @@ -677,13 +778,14 @@ make_shadow (MetaShadow *shadow, * meta_shadow_unref() */ MetaShadow * -meta_shadow_factory_get_shadow (MetaShadowFactory *factory, - MetaWindowShape *shape, - int width, - int height, - int radius, - int top_fade) +meta_shadow_factory_get_shadow (MetaShadowFactory *factory, + MetaWindowShape *shape, + int width, + int height, + const char *class_name, + gboolean focused) { + MetaShadowParams *params; MetaShadowCacheKey key; MetaShadow *shadow; cairo_region_t *region; @@ -721,15 +823,18 @@ meta_shadow_factory_get_shadow (MetaShadowFactory *factory, * In the case where we are fading a the top, that also has to fit * within the top unscaled border. */ - spread = get_shadow_spread (radius); + + params = get_shadow_params (factory, class_name, focused, FALSE); + + spread = get_shadow_spread (params->radius); meta_window_shape_get_borders (shape, &shape_border_top, &shape_border_right, &shape_border_bottom, &shape_border_left); - inner_border_top = MAX (shape_border_top + spread, top_fade); - outer_border_top = top_fade >= 0 ? 0 : spread; + inner_border_top = MAX (shape_border_top + spread, params->top_fade); + outer_border_top = params->top_fade >= 0 ? 0 : spread; inner_border_right = shape_border_right + spread; outer_border_right = spread; inner_border_bottom = shape_border_bottom + spread; @@ -744,8 +849,8 @@ meta_shadow_factory_get_shadow (MetaShadowFactory *factory, if (cacheable) { key.shape = shape; - key.radius = radius; - key.top_fade = top_fade; + key.radius = params->radius; + key.top_fade = params->top_fade; shadow = g_hash_table_lookup (factory->shadows, &key); if (shadow) @@ -757,8 +862,8 @@ meta_shadow_factory_get_shadow (MetaShadowFactory *factory, shadow->ref_count = 1; shadow->factory = factory; shadow->key.shape = meta_window_shape_ref (shape); - shadow->key.radius = radius; - shadow->key.top_fade = top_fade; + shadow->key.radius = params->radius; + shadow->key.top_fade = params->top_fade; shadow->outer_border_top = outer_border_top; shadow->inner_border_top = inner_border_top; @@ -793,3 +898,69 @@ meta_shadow_factory_get_shadow (MetaShadowFactory *factory, return shadow; } + +/** + * meta_shadow_factory_set_params: + * @factory: a #MetaShadowFactory + * @class_name: name of the class of shadow to set the params for. + * the default shadow classes are the names of the different + * theme frame types (normal, dialog, modal_dialog, utility, + * border, menu, attached) and in addition, popup-menu + * and dropdown-menu. + * @focused: whether the shadow is for a focused window + * @params: new parameter values + * + * Updates the shadow parameters for a particular class of shadows + * for either the focused or unfocused state. If the class name + * does not name an existing class, a new class will be created + * (the other focus state for that class will have default values + * assigned to it.) + */ +void +meta_shadow_factory_set_params (MetaShadowFactory *factory, + const char *class_name, + gboolean focused, + MetaShadowParams *params) +{ + MetaShadowParams *stored_params; + + g_return_if_fail (META_IS_SHADOW_FACTORY (factory)); + g_return_if_fail (class_name != NULL); + g_return_if_fail (params != NULL); + g_return_if_fail (params->radius >= 0); + + stored_params = get_shadow_params (factory, class_name, focused, TRUE); + + *stored_params = *params; + + g_signal_emit (factory, signals[CHANGED], 0); +} + +/** + * meta_shadow_factory_get_params: + * @factory: a #MetaShadowFactory + * @class_name: name of the class of shadow to get the params for + * @focused: whether the shadow is for a focused window + * @params: (out caller-allocates): location to store the current parameter values + * + * Gets the shadow parameters for a particular class of shadows + * for either the focused or unfocused state. If the class name + * does not name an existing class, default values will be returned + * without printing an error. + */ +void +meta_shadow_factory_get_params (MetaShadowFactory *factory, + const char *class_name, + gboolean focused, + MetaShadowParams *params) +{ + MetaShadowParams *stored_params; + + g_return_if_fail (META_IS_SHADOW_FACTORY (factory)); + g_return_if_fail (class_name != NULL); + + stored_params = get_shadow_params (factory, class_name, focused, FALSE); + + if (params) + *params = *stored_params; +} diff --git a/src/compositor/meta-window-actor-private.h b/src/compositor/meta-window-actor-private.h index c1f7b25ea..23d52f83b 100644 --- a/src/compositor/meta-window-actor-private.h +++ b/src/compositor/meta-window-actor-private.h @@ -28,6 +28,8 @@ void meta_window_actor_process_damage (MetaWindowActor *self, XDamageNotifyEvent *event); void meta_window_actor_pre_paint (MetaWindowActor *self); +void meta_window_actor_invalidate_shadow (MetaWindowActor *self); + gboolean meta_window_actor_effect_in_progress (MetaWindowActor *self); void meta_window_actor_sync_actor_position (MetaWindowActor *self); void meta_window_actor_sync_visibility (MetaWindowActor *self); diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c index 6f0496fbd..35227ca9d 100644 --- a/src/compositor/meta-window-actor.c +++ b/src/compositor/meta-window-actor.c @@ -30,7 +30,21 @@ struct _MetaWindowActorPrivate MetaScreen *screen; ClutterActor *actor; - MetaShadow *shadow; + + /* MetaShadowFactory only caches shadows that are actually in use; + * to avoid unnecessary recomputation we do two things: 1) we store + * both a focused and unfocused shadow for the window. If the window + * doesn't have different focused and unfocused shadow parameters, + * these will be the same. 2) when the shadow potentially changes we + * don't immediately unreference the old shadow, we just flag it as + * dirty and recompute it when we next need it (recompute_focused_shadow, + * recompute_unfocused_shadow.) Because of our extraction of + * size-invariant window shape, we'll often find that the new shadow + * is the same as the old shadow. + */ + MetaShadow *focused_shadow; + MetaShadow *unfocused_shadow; + Pixmap back_pixmap; Damage damage; @@ -51,10 +65,7 @@ struct _MetaWindowActorPrivate gint freeze_count; - gint shadow_radius; - gint shadow_top_fade; - gint shadow_x_offset; - gint shadow_y_offset; + char * shadow_class; /* * These need to be counters rather than flags, since more plugins @@ -79,7 +90,8 @@ struct _MetaWindowActorPrivate guint needs_pixmap : 1; guint needs_reshape : 1; - guint recompute_shadow : 1; + guint recompute_focused_shadow : 1; + guint recompute_unfocused_shadow : 1; guint paint_shadow : 1; guint size_changed : 1; @@ -97,11 +109,7 @@ enum PROP_X_WINDOW, PROP_X_WINDOW_ATTRIBUTES, PROP_NO_SHADOW, - PROP_SHADOW_RADIUS, - PROP_SHADOW_TOP_FADE, - PROP_SHADOW_X_OFFSET, - PROP_SHADOW_Y_OFFSET, - PROP_SHADOW_OPACITY + PROP_SHADOW_CLASS }; #define DEFAULT_SHADOW_RADIUS 12 @@ -246,56 +254,14 @@ meta_window_actor_class_init (MetaWindowActorClass *klass) PROP_NO_SHADOW, pspec); - pspec = g_param_spec_int ("shadow-radius", - "Shadow Radius", - "Radius (standard deviation of gaussian blur) of window's shadow", - 0, 128, DEFAULT_SHADOW_RADIUS, - G_PARAM_READWRITE); + pspec = g_param_spec_string ("shadow-class", + "Name of the shadow class for this window.", + "NULL means to use the default shadow class for this window type", + NULL, + G_PARAM_READWRITE); g_object_class_install_property (object_class, - PROP_SHADOW_RADIUS, - pspec); - - pspec = g_param_spec_int ("shadow-top-fade", - "Shadow Top Fade", - "If >= 0, the shadow doesn't extend above the top " - "of the window, and fades out over the given number of pixels", - -1, G_MAXINT, -1, - G_PARAM_READWRITE); - - g_object_class_install_property (object_class, - PROP_SHADOW_TOP_FADE, - pspec); - - pspec = g_param_spec_int ("shadow-x-offset", - "Shadow X Offset", - "Distance shadow is offset in the horizontal direction in pixels", - G_MININT, G_MAXINT, DEFAULT_SHADOW_X_OFFSET, - G_PARAM_READWRITE); - - g_object_class_install_property (object_class, - PROP_SHADOW_X_OFFSET, - pspec); - - pspec = g_param_spec_int ("shadow-y-offset", - "Shadow Y Offset", - "Distance shadow is offset in the vertical direction in piyels", - G_MININT, G_MAXINT, DEFAULT_SHADOW_Y_OFFSET, - G_PARAM_READWRITE); - - g_object_class_install_property (object_class, - PROP_SHADOW_Y_OFFSET, - pspec); - - pspec = g_param_spec_uint ("shadow-opacity", - "Shadow Opacity", - "Opacity of the window's shadow", - 0, 255, - 255, - G_PARAM_READWRITE); - - g_object_class_install_property (object_class, - PROP_SHADOW_OPACITY, + PROP_SHADOW_CLASS, pspec); } @@ -308,11 +274,7 @@ meta_window_actor_init (MetaWindowActor *self) META_TYPE_WINDOW_ACTOR, MetaWindowActorPrivate); priv->opacity = 0xff; - priv->shadow_radius = DEFAULT_SHADOW_RADIUS; - priv->shadow_top_fade = -1; - priv->shadow_x_offset = DEFAULT_SHADOW_X_OFFSET; - priv->shadow_y_offset = DEFAULT_SHADOW_Y_OFFSET; - priv->shadow_opacity = 0xff; + priv->shadow_class = NULL; priv->paint_shadow = TRUE; } @@ -470,10 +432,22 @@ meta_window_actor_dispose (GObject *object) meta_window_actor_clear_shape_region (self); meta_window_actor_clear_bounding_region (self); - if (priv->shadow != NULL) + if (priv->shadow_class != NULL) { - meta_shadow_unref (priv->shadow); - priv->shadow = NULL; + g_free (priv->shadow_class); + priv->shadow_class = NULL; + } + + if (priv->focused_shadow != NULL) + { + meta_shadow_unref (priv->focused_shadow); + priv->focused_shadow = NULL; + } + + if (priv->unfocused_shadow != NULL) + { + meta_shadow_unref (priv->unfocused_shadow); + priv->unfocused_shadow = NULL; } if (priv->shadow_shape != NULL) @@ -545,65 +519,20 @@ meta_window_actor_set_property (GObject *object, priv->no_shadow = newv; - priv->recompute_shadow = TRUE; - clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); + meta_window_actor_invalidate_shadow (self); } break; - case PROP_SHADOW_RADIUS: + case PROP_SHADOW_CLASS: { - gint newv = g_value_get_int (value); + const char *newv = g_value_get_string (value); - if (newv == priv->shadow_radius) + if (g_strcmp0 (newv, priv->shadow_class) == 0) return; - priv->shadow_radius = newv; - priv->recompute_shadow = TRUE; - clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); - } - break; - case PROP_SHADOW_TOP_FADE: - { - gint newv = g_value_get_int (value); + g_free (priv->shadow_class); + priv->shadow_class = g_strdup (newv); - if (newv == priv->shadow_top_fade) - return; - - priv->shadow_top_fade = newv; - priv->recompute_shadow = TRUE; - clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); - } - break; - case PROP_SHADOW_X_OFFSET: - { - gint newv = g_value_get_int (value); - - if (newv == priv->shadow_x_offset) - return; - - priv->shadow_x_offset = newv; - clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); - } - break; - case PROP_SHADOW_Y_OFFSET: - { - gint newv = g_value_get_int (value); - - if (newv == priv->shadow_y_offset) - return; - - priv->shadow_y_offset = newv; - clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); - } - break; - case PROP_SHADOW_OPACITY: - { - guint newv = g_value_get_uint (value); - - if (newv == priv->shadow_opacity) - return; - - priv->shadow_opacity = newv; - clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); + meta_window_actor_invalidate_shadow (self); } break; default: @@ -637,26 +566,53 @@ meta_window_actor_get_property (GObject *object, case PROP_NO_SHADOW: g_value_set_boolean (value, priv->no_shadow); break; - case PROP_SHADOW_RADIUS: - g_value_set_int (value, priv->shadow_radius); + case PROP_SHADOW_CLASS: + g_value_set_string (value, priv->shadow_class); break; - case PROP_SHADOW_TOP_FADE: - g_value_set_int (value, priv->shadow_top_fade); - break; - case PROP_SHADOW_X_OFFSET: - g_value_set_int (value, priv->shadow_x_offset); - break; - case PROP_SHADOW_Y_OFFSET: - g_value_set_int (value, priv->shadow_y_offset); - break; - case PROP_SHADOW_OPACITY: - g_value_set_uint (value, priv->shadow_opacity); default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } +static const char * +meta_window_actor_get_shadow_class (MetaWindowActor *self) +{ + MetaWindowActorPrivate *priv = self->priv; + + if (priv->shadow_class != NULL) + return priv->shadow_class; + else + { + MetaWindowType window_type = meta_window_get_window_type (priv->window); + + switch (window_type) + { + case META_WINDOW_DROPDOWN_MENU: + return "dropdown-menu"; + case META_WINDOW_POPUP_MENU: + return "popup-menu"; + default: + { + MetaFrameType frame_type = meta_window_get_frame_type (priv->window); + return meta_frame_type_to_string (frame_type); + } + } + } +} + +static void +meta_window_actor_get_shadow_params (MetaWindowActor *self, + gboolean appears_focused, + MetaShadowParams *params) +{ + const char *shadow_class = meta_window_actor_get_shadow_class (self); + + meta_shadow_factory_get_params (meta_shadow_factory_get_default (), + shadow_class, appears_focused, + params); +} + static void meta_window_actor_get_shape_bounds (MetaWindowActor *self, cairo_rectangle_int_t *bounds) @@ -675,17 +631,26 @@ meta_window_actor_paint (ClutterActor *actor) MetaWindowActor *self = META_WINDOW_ACTOR (actor); MetaWindowActorPrivate *priv = self->priv; - if (priv->shadow != NULL && priv->paint_shadow) + if (priv->paint_shadow) { - cairo_rectangle_int_t shape_bounds; - meta_window_actor_get_shape_bounds (self, &shape_bounds); + gboolean appears_focused = meta_window_appears_focused (priv->window); + MetaShadow *shadow = appears_focused ? priv->focused_shadow : priv->unfocused_shadow; - meta_shadow_paint (priv->shadow, - priv->shadow_x_offset + shape_bounds.x, - priv->shadow_y_offset + shape_bounds.y, - shape_bounds.width, - shape_bounds.height, - (clutter_actor_get_paint_opacity (actor) * priv->shadow_opacity) / 255); + if (shadow != NULL) + { + MetaShadowParams params; + cairo_rectangle_int_t shape_bounds; + + meta_window_actor_get_shape_bounds (self, &shape_bounds); + meta_window_actor_get_shadow_params (self, appears_focused, ¶ms); + + meta_shadow_paint (shadow, + params.x_offset + shape_bounds.x, + params.y_offset + shape_bounds.y, + shape_bounds.width, + shape_bounds.height, + (clutter_actor_get_paint_opacity (actor) * params.opacity) / 255); + } } CLUTTER_ACTOR_CLASS (meta_window_actor_parent_class)->paint (actor); @@ -719,9 +684,6 @@ meta_window_actor_has_shadow (MetaWindowActor *self) if (priv->no_shadow) return FALSE; - if (priv->shadow_radius == 0) - return FALSE; - /* * Always put a shadow around windows with a frame - This should override * the restriction about not putting a shadow around ARGB windows. @@ -1536,7 +1498,7 @@ meta_window_actor_update_bounding_region (MetaWindowActor *self, * the shadow when the size changes. */ if (!priv->shaped) - priv->recompute_shadow = TRUE; + meta_window_actor_invalidate_shadow (self); } static void @@ -1666,8 +1628,10 @@ meta_window_actor_set_visible_region_beneath (MetaWindowActor *self, { MetaWindowActorPrivate *priv = self->priv; - if (priv->shadow) + if (priv->focused_shadow) { + gboolean appears_focused = meta_window_appears_focused (priv->window); + MetaShadowParams params; cairo_rectangle_int_t shape_bounds; cairo_rectangle_int_t shadow_bounds; cairo_region_overlap_t overlap; @@ -1679,10 +1643,11 @@ meta_window_actor_set_visible_region_beneath (MetaWindowActor *self, * at all. */ meta_window_actor_get_shape_bounds (self, &shape_bounds); + meta_window_actor_get_shadow_params (self, appears_focused, ¶ms); - meta_shadow_get_bounds (priv->shadow, - priv->shadow_x_offset + shape_bounds.x, - priv->shadow_y_offset + shape_bounds.y, + meta_shadow_get_bounds (appears_focused ? priv->focused_shadow : priv->unfocused_shadow, + params.x_offset + shape_bounds.x, + params.y_offset + shape_bounds.y, shape_bounds.width, shape_bounds.height, &shadow_bounds); @@ -1804,7 +1769,10 @@ check_needs_shadow (MetaWindowActor *self) { MetaWindowActorPrivate *priv = self->priv; MetaShadow *old_shadow = NULL; + MetaShadow **shadow_location; + gboolean recompute_shadow; gboolean should_have_shadow; + gboolean appears_focused; if (!priv->mapped) return; @@ -1817,16 +1785,34 @@ check_needs_shadow (MetaWindowActor *self) */ should_have_shadow = meta_window_actor_has_shadow (self); + appears_focused = meta_window_appears_focused (priv->window); - if (priv->shadow != NULL && (!should_have_shadow || priv->recompute_shadow)) + if (appears_focused) { - old_shadow = priv->shadow; - priv->shadow = NULL; + recompute_shadow = priv->recompute_focused_shadow; + priv->recompute_focused_shadow = FALSE; + shadow_location = &priv->focused_shadow; + } + else + { + recompute_shadow = priv->recompute_unfocused_shadow; + priv->recompute_unfocused_shadow = FALSE; + shadow_location = &priv->unfocused_shadow; } - if (priv->shadow == NULL && should_have_shadow) + if (!should_have_shadow || recompute_shadow) + { + if (*shadow_location != NULL) + { + old_shadow = *shadow_location; + *shadow_location = NULL; + } + } + + if (*shadow_location == NULL && should_have_shadow) { MetaShadowFactory *factory = meta_shadow_factory_get_default (); + const char *shadow_class = meta_window_actor_get_shadow_class (self); cairo_rectangle_int_t shape_bounds; if (priv->shadow_shape == NULL) @@ -1839,16 +1825,14 @@ check_needs_shadow (MetaWindowActor *self) meta_window_actor_get_shape_bounds (self, &shape_bounds); - priv->shadow = meta_shadow_factory_get_shadow (factory, - priv->shadow_shape, - shape_bounds.width, shape_bounds.height, - priv->shadow_radius, priv->shadow_top_fade); + *shadow_location = meta_shadow_factory_get_shadow (factory, + priv->shadow_shape, + shape_bounds.width, shape_bounds.height, + shadow_class, appears_focused); } if (old_shadow != NULL) meta_shadow_unref (old_shadow); - - priv->recompute_shadow = FALSE; } static gboolean @@ -1949,7 +1933,7 @@ check_needs_reshape (MetaWindowActor *self) #endif priv->needs_reshape = FALSE; - priv->recompute_shadow = TRUE; + meta_window_actor_invalidate_shadow (self); } void @@ -1997,6 +1981,16 @@ meta_window_actor_pre_paint (MetaWindowActor *self) check_needs_shadow (self); } +void +meta_window_actor_invalidate_shadow (MetaWindowActor *self) +{ + MetaWindowActorPrivate *priv = self->priv; + + priv->recompute_focused_shadow = TRUE; + priv->recompute_unfocused_shadow = TRUE; + clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); +} + void meta_window_actor_update_opacity (MetaWindowActor *self) { diff --git a/src/include/meta-shadow-factory.h b/src/include/meta-shadow-factory.h index c121c7cf2..2782ac485 100644 --- a/src/include/meta-shadow-factory.h +++ b/src/include/meta-shadow-factory.h @@ -27,6 +27,31 @@ #include +/** + * MetaShadowParams: + * The #MetaShadowParams structure holds information about how to draw + * a particular style of shadow. + * @radius: the radius (gaussian standard deviation) of the shadow + * @top_fade: if >= 0, the shadow doesn't extend above the top + * of the shape, and fades out over the given number of pixels + * @x_offset: horizontal offset of the shadow with respect to the + * shape being shadowed, in pixels + * @y_offset: vertical offset of the shadow with respect to the + * shape being shadowed, in pixels + * @opacity: opacity of the shadow, from 0 to 255 + */ + +typedef struct _MetaShadowParams MetaShadowParams; + +struct _MetaShadowParams +{ + int radius; + int top_fade; + int x_offset; + int y_offset; + guint8 opacity; +}; + #define META_TYPE_SHADOW_FACTORY (meta_shadow_factory_get_type ()) #define META_SHADOW_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_SHADOW_FACTORY, MetaShadowFactory)) #define META_SHADOW_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_SHADOW_FACTORY, MetaShadowFactoryClass)) @@ -47,4 +72,13 @@ MetaShadowFactory *meta_shadow_factory_get_default (void); GType meta_shadow_factory_get_type (void); +void meta_shadow_factory_set_params (MetaShadowFactory *factory, + const char *class_name, + gboolean focused, + MetaShadowParams *params); +void meta_shadow_factory_get_params (MetaShadowFactory *factory, + const char *class_name, + gboolean focused, + MetaShadowParams *params); + #endif /* __META_SHADOW_FACTORY_H__ */