[mutter/configurable-shadows: 7/13] Make window shadows globally configurable



commit 5bbbac65d63ca3346a651658fbdaaf8e3876c1db
Author: Owen W. Taylor <otaylor fishsoup net>
Date:   Thu Nov 11 16:24:43 2010 -0500

    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 fc13b61..ad964b6 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 2a05212..70a6623 100644
--- a/src/compositor/meta-shadow-factory-private.h
+++ b/src/compositor/meta-shadow-factory-private.h
@@ -54,11 +54,11 @@ void        meta_shadow_get_bounds  (MetaShadow            *shadow,
                                      int                    window_height,
                                      cairo_rectangle_int_t *bounds);
 
-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 935c8bc..ead49fb 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
@@ -263,10 +299,35 @@ meta_shadow_get_bounds  (MetaShadow            *shadow,
 }
 
 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 c1f7b25..23d52f8 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 6f0496f..35f6f3d 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 the 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);
-
-  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);
+  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_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)
+    {
+      g_free (priv->shadow_class);
+      priv->shadow_class = NULL;
+    }
+
+  if (priv->focused_shadow != NULL)
     {
-      meta_shadow_unref (priv->shadow);
-      priv->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));
-      }
-      break;
-    case PROP_SHADOW_RADIUS:
-      {
-        gint newv = g_value_get_int (value);
-
-        if (newv == priv->shadow_radius)
-          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);
-
-        if (newv == priv->shadow_top_fade)
-          return;
-
-        priv->shadow_top_fade = newv;
-        priv->recompute_shadow = TRUE;
-        clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
+        meta_window_actor_invalidate_shadow (self);
       }
       break;
-    case PROP_SHADOW_X_OFFSET:
+    case PROP_SHADOW_CLASS:
       {
-        gint newv = g_value_get_int (value);
+        const char *newv = g_value_get_string (value);
 
-        if (newv == priv->shadow_x_offset)
+        if (g_strcmp0 (newv, priv->shadow_class) == 0)
           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;
+        g_free (priv->shadow_class);
+        priv->shadow_class = g_strdup (newv);
 
-        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);
-      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);
+    case PROP_SHADOW_CLASS:
+      g_value_set_string (value, priv->shadow_class);
       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, &params);
+
+          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, &params);
 
-      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 (appears_focused)
+    {
+      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 || priv->recompute_shadow))
+  if (!should_have_shadow || recompute_shadow)
     {
-      old_shadow = priv->shadow;
-      priv->shadow = NULL;
+      if (*shadow_location != NULL)
+        {
+          old_shadow = *shadow_location;
+          *shadow_location = NULL;
+        }
     }
 
-  if (priv->shadow == NULL && should_have_shadow)
+  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
@@ -1998,6 +1982,16 @@ meta_window_actor_pre_paint (MetaWindowActor *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)
 {
   MetaWindowActorPrivate *priv = self->priv;
diff --git a/src/include/meta-shadow-factory.h b/src/include/meta-shadow-factory.h
index e27fc98..7b5a15d 100644
--- a/src/include/meta-shadow-factory.h
+++ b/src/include/meta-shadow-factory.h
@@ -27,6 +27,31 @@
 
 #include <glib-object.h>
 
+/**
+ * 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))
@@ -49,4 +74,13 @@ GType meta_shadow_factory_get_type (void);
 
 MetaShadowFactory *meta_shadow_factory_new        (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__ */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]