← Back to team overview

lightdm-gtk-greeter-team team mailing list archive

[Merge] lp:~kalgasnik/lightdm-gtk-greeter/background-transition into lp:lightdm-gtk-greeter

 

Andrew P. has proposed merging lp:~kalgasnik/lightdm-gtk-greeter/background-transition into lp:lightdm-gtk-greeter.

Requested reviews:
  LightDM Gtk+ Greeter Development Team (lightdm-gtk-greeter-team)
Related bugs:
  Bug #1394639 in lightdm-gtk-greeter (Ubuntu): "lightdm fails to start with 3 connected displays but works 2 displays"
  https://bugs.launchpad.net/ubuntu/+source/lightdm-gtk-greeter/+bug/1394639

For more details, see:
https://code.launchpad.net/~kalgasnik/lightdm-gtk-greeter/background-transition/+merge/241082

Add transition to backgrounds change.

1. Transition function: easy-in-out
2. Animation: drawing new background with different alpha over old background
3. Duration: 500ms
4. Delay before changing to user's background: 250ms

At first I thought to make all these things configurable, but someone can say that it is too much.
"transition-duration" is enough.

Enabled by default, I'm not sure that it is good idea.
-- 
Your team LightDM Gtk+ Greeter Development Team is requested to review the proposed merge of lp:~kalgasnik/lightdm-gtk-greeter/background-transition into lp:lightdm-gtk-greeter.
=== modified file 'src/Makefile.am'
--- src/Makefile.am	2014-08-20 23:23:59 +0000
+++ src/Makefile.am	2014-12-10 07:59:47 +0000
@@ -41,7 +41,8 @@
 	$(LIBX11_LIBS) \
 	$(LIBINDICATOR_LIBS) \
 	$(LIBIDO_LIBS) \
-	$(LIBXKLAVIER_LIBS)
+	$(LIBXKLAVIER_LIBS)\
+	-lm
 
 if MAINTAINER_MODE
 
@@ -65,4 +66,4 @@
 EXTRA_DIST = \
 	lightdm-gtk-greeter.glade \
 	lightdm-gtk-greeter-fallback.css \
-	lightdm-gtk-greeter-application.css
\ No newline at end of file
+	lightdm-gtk-greeter-application.css

=== modified file 'src/greeterbackground.c'
--- src/greeterbackground.c	2014-08-31 17:45:52 +0000
+++ src/greeterbackground.c	2014-12-10 07:59:47 +0000
@@ -1,4 +1,5 @@
 
+#include <math.h>
 #include <cairo-xlib.h>
 #include <gtk/gtk.h>
 #include <gdk/gdkx.h>
@@ -14,6 +15,8 @@
     BACKGROUND_TYPE_INVALID,
     /* Do not use this monitor */
     BACKGROUND_TYPE_SKIP,
+    /* Do not override window background */
+    BACKGROUND_TYPE_DEFAULT,
     /* Solid color */
     BACKGROUND_TYPE_COLOR,
     /* Path to image and scaling mode */
@@ -22,6 +25,7 @@
 } BackgroundType;
 
 static const gchar* BACKGROUND_TYPE_SKIP_VALUE = "#skip";
+static const gchar* BACKGROUND_TYPE_DEFAULT_VALUE = "#default";
 
 typedef enum
 {
@@ -34,6 +38,9 @@
 
 static const gchar* SCALING_MODE_PREFIXES[] = {"#source:", "#zoomed:", "#stretched:", NULL};
 
+typedef gdouble (*TransitionFunction)(gdouble x);
+typedef void (*TransitionDraw)(gconstpointer monitor, cairo_t* cr);
+
 /* Background configuration (parsed from background=... option).
    Used to fill <Background> */
 typedef struct
@@ -50,18 +57,32 @@
     } options;
 } BackgroundConfig;
 
+/* Transition configuration
+   Used to as part of <MonitorConfig> and <Monitor> */
+typedef struct
+{
+    /* Transition duration, in ms */
+    glong duration;
+    TransitionFunction func;
+    /* Function to draw monitor background */
+    TransitionDraw draw;
+} TransitionConfig;
+
 /* Store monitor configuration */
 typedef struct
 {
     BackgroundConfig bg;
     gboolean user_bg;
     gboolean laptop;
+
+    TransitionConfig transition;
 } MonitorConfig;
 
 /* Actual drawing information attached to monitor.
  * Used to separate configured monitor background and user background. */
 typedef struct
 {
+    gint ref_count;
     BackgroundType type;
     union
     {
@@ -80,14 +101,29 @@
     gulong window_draw_handler_id;
 
     /* Configured background */
-    Background background_configured;
-    /* Background used to display user-background */
-    Background background_custom;
+    Background* background_configured;
     /* Current monitor background: &background_configured or &background_custom
      * Monitors with type = BACKGROUND_TYPE_SKIP have background = NULL */
-    const Background* background;
+    Background* background;
+
+    struct
+    {
+        TransitionConfig config;
+
+        /* Old background, stage == 0.0 */
+        Background* from;
+        /* New background, stage == 1.0 */
+        Background* to;
+
+        guint timer_id;
+        gint64 started;
+        /* Current stage */
+        gdouble stage;
+    } transition;
 } Monitor;
 
+static const Monitor INVALID_MONITOR_STRUCT = {0};
+
 struct _GreeterBackground
 {
 	GObject parent_instance;
@@ -141,6 +177,9 @@
     gboolean follow_cursor;
     /* Use cursor position to determinate initial active monitor */
     gboolean follow_cursor_to_init;
+
+    /* Name => transition function, inited in set_monitor_config() */
+    GHashTable* transition_types;
 };
 
 enum
@@ -151,20 +190,6 @@
 
 static guint background_signals[BACKGROUND_SIGNAL_LAST] = {0};
 
-static const MonitorConfig DEFAULT_MONITOR_CONFIG =
-{
-    .bg =
-    {
-        .type = BACKGROUND_TYPE_COLOR,
-        .options =
-        {
-            .color = {.red = 0.0, .green = 0.0, .blue = 0.0, .alpha = 1.0}
-        }
-    },
-    .user_bg = TRUE,
-    .laptop = FALSE
-};
-
 static const gchar* DBUS_UPOWER_NAME                = "org.freedesktop.UPower";
 static const gchar* DBUS_UPOWER_PATH                = "/org/freedesktop/UPower";
 static const gchar* DBUS_UPOWER_INTERFACE           = "org.freedesktop.UPower";
@@ -177,15 +202,13 @@
 
 void greeter_background_set_active_monitor_config   (GreeterBackground* background,
                                                      const gchar* value);
-void greeter_background_set_default_config          (GreeterBackground* background,
-                                                     const gchar* bg,
-                                                     gboolean user_bg,
-                                                     gboolean laptop);
 void greeter_background_set_monitor_config          (GreeterBackground* background,
                                                      const gchar* name,
-                                                     const gchar* bg,
+                                                     const gchar* bg,               /* NULL to use fallback value */
                                                      gboolean user_bg, gboolean user_bg_used,
-                                                     gboolean laptop, gboolean laptop_used);
+                                                     gboolean laptop, gboolean laptop_used,
+                                                     gint transition_duration,      /* -1 to use fallback value */
+                                                     const gchar* transition_type); /* NULL to use fallback value */
 void greeter_background_remove_monitor_config       (GreeterBackground* background,
                                                      const gchar* name);
 gchar** greeter_background_get_configured_monitors  (GreeterBackground* background);
@@ -212,6 +235,8 @@
                                                      GreeterBackground* background);
 static void greeter_background_monitors_changed_cb  (GdkScreen* screen,
                                                      GreeterBackground* background);
+static void greeter_background_child_destroyed_cb   (GtkWidget* child,
+                                                     GreeterBackground* background);
 
 /* struct BackgroundConfig */
 static gboolean background_config_initialize        (BackgroundConfig* config,
@@ -227,17 +252,28 @@
                                                      MonitorConfig* dest);
 
 /* struct Background */
-static gboolean background_initialize               (Background* bg,
-                                                     const BackgroundConfig* config,
+static Background* background_new                   (const BackgroundConfig* config,
                                                      const Monitor* monitor,
                                                      GHashTable* images_cache);
+static Background* background_ref                   (Background* bg);
+static void background_unref                        (Background** bg);
 static void background_finalize                     (Background* bg);
 
 /* struct Monitor */
 static void monitor_finalize                        (Monitor* info);
 static void monitor_set_background                  (Monitor* monitor,
-                                                     const Background* background);
+                                                     Background* background);
+static void monitor_start_transition                (Monitor* monitor,
+                                                     Background* from,
+                                                     Background* to);
+static void monitor_stop_transition                 (Monitor* monitor);
+static gboolean monitor_transition_cb               (GtkWidget *widget,
+                                                     GdkFrameClock* frame_clock,
+                                                     Monitor* monitor);
+static void monitor_transition_draw_alpha           (const Monitor* monitor,
+                                                     cairo_t* cr);
 static void monitor_draw_background                 (const Monitor* monitor,
+                                                     const Background* background,
                                                      cairo_t* cr);
 static gboolean monitor_window_draw_cb              (GtkWidget* widget,
                                                      cairo_t* cr,
@@ -259,7 +295,32 @@
                                                      Pixmap xpixmap);
 static void set_surface_as_root                     (GdkScreen* screen,
                                                      cairo_surface_t* surface);
-
+static gdouble transition_func_linear               (gdouble x);
+static gdouble transition_func_easy_in_out          (gdouble x);
+
+/* Implemented in lightdm-gtk-greeter.c */
+gpointer greeter_save_focus(GtkWidget* widget);
+void greeter_restore_focus(const gpointer saved_data);
+
+static const MonitorConfig DEFAULT_MONITOR_CONFIG =
+{
+    .bg =
+    {
+        .type = BACKGROUND_TYPE_COLOR,
+        .options =
+        {
+            .color = {.red = 0.0, .green = 0.0, .blue = 0.0, .alpha = 1.0}
+        }
+    },
+    .user_bg = TRUE,
+    .laptop = FALSE,
+    .transition =
+    {
+        .duration = 500,
+        .func = transition_func_easy_in_out,
+        .draw = (TransitionDraw)monitor_transition_draw_alpha
+    }
+};
 
 /* Implementation */
 
@@ -302,11 +363,14 @@
     self->priv->laptop_lid_closed = FALSE;
 }
 
-GreeterBackground* 
+GreeterBackground*
 greeter_background_new(GtkWidget* child)
 {
+    g_return_val_if_fail(child != NULL, NULL);
+
     GreeterBackground* background = GREETER_BACKGROUND(g_object_new(greeter_background_get_type(), NULL));
     background->priv->child = child;
+    g_signal_connect(background->priv->child, "destroy", G_CALLBACK(greeter_background_child_destroyed_cb), background);
 	return background;
 }
 
@@ -346,42 +410,55 @@
 }
 
 void
-greeter_background_set_default_config(GreeterBackground* background,
-                                      const gchar* bg,
-                                      gboolean user_bg,
-                                      gboolean laptop)
-{
-    g_return_if_fail(GREETER_IS_BACKGROUND(background));
-    GreeterBackgroundPrivate* priv = background->priv;
-
-    if(priv->default_config)
-        monitor_config_free(priv->default_config);
-
-    priv->default_config = g_new0(MonitorConfig, 1);
-    if(!background_config_initialize(&priv->default_config->bg, bg))
-        background_config_copy(&DEFAULT_MONITOR_CONFIG.bg, &priv->default_config->bg);
-    priv->default_config->user_bg = user_bg;
-    priv->default_config->laptop = laptop;
-}
-
-void
 greeter_background_set_monitor_config(GreeterBackground* background,
                                       const gchar* name,
                                       const gchar* bg,
                                       gboolean user_bg, gboolean user_bg_used,
-                                      gboolean laptop, gboolean laptop_used)
+                                      gboolean laptop, gboolean laptop_used,
+                                      gint transition_duration,
+                                      const gchar* transition_type)
 {
     g_return_if_fail(GREETER_IS_BACKGROUND(background));
     GreeterBackgroundPrivate* priv = background->priv;
 
     MonitorConfig* config = g_new0(MonitorConfig, 1);
 
+    const MonitorConfig* FALLBACK = (g_strcmp0(name, GREETER_BACKGROUND_DEFAULT) == 0) ? &DEFAULT_MONITOR_CONFIG : priv->default_config;
+
     if(!background_config_initialize(&config->bg, bg))
-        background_config_copy(&priv->default_config->bg, &config->bg);
-    config->user_bg = user_bg_used ? user_bg : priv->default_config->user_bg;
-    config->laptop = laptop_used ? laptop : priv->default_config->laptop;
-
-    g_hash_table_insert(priv->configs, g_strdup(name), config);
+        background_config_copy(&FALLBACK->bg, &config->bg);
+    config->user_bg = user_bg_used ? user_bg : FALLBACK->user_bg;
+    config->laptop = laptop_used ? laptop : FALLBACK->laptop;
+    config->transition.duration = transition_duration >= 0 ? transition_duration : FALLBACK->transition.duration;
+    config->transition.draw = FALLBACK->transition.draw;
+
+    if(transition_type)
+    {
+        if(!priv->transition_types)
+        {
+            priv->transition_types = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
+            g_hash_table_insert(priv->transition_types, g_strdup("none"), NULL);
+            g_hash_table_insert(priv->transition_types, g_strdup("linear"), transition_func_linear);
+            g_hash_table_insert(priv->transition_types, g_strdup("easy-in-out"), transition_func_easy_in_out);
+        }
+        if(!g_hash_table_lookup_extended(priv->transition_types, transition_type, NULL, (gpointer*)&config->transition.func))
+        {
+            g_warning("[Background] Invalid transition type for '%s' monitor: '%s'. Using fallback value.",
+                      name, transition_type);
+            config->transition.func = FALLBACK->transition.func;
+        }
+    }
+    else
+        config->transition.func = FALLBACK->transition.func;
+
+    if(FALLBACK == priv->default_config)
+        g_hash_table_insert(priv->configs, g_strdup(name), config);
+    else
+    {
+        if(priv->default_config)
+            monitor_config_free(priv->default_config);
+        priv->default_config = config;
+    }
 }
 
 void
@@ -418,12 +495,9 @@
     g_return_if_fail(GREETER_IS_BACKGROUND(background));
     g_return_if_fail(GDK_IS_SCREEN(screen));
 
-    g_debug("Connecting to screen");
+    g_debug("[Background] Connecting to screen: %p", screen);
 
     GreeterBackgroundPrivate* priv = background->priv;
-    gulong screen_monitors_changed_handler_id = (priv->screen == screen) ? priv->screen_monitors_changed_handler_id : 0;
-    if(screen_monitors_changed_handler_id)
-        priv->screen_monitors_changed_handler_id = 0;
 
     if(priv->screen)
         greeter_background_disconnect(background);
@@ -433,6 +507,8 @@
     priv->monitors = g_new0(Monitor, priv->monitors_size);
     priv->monitors_map = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
 
+    g_debug("[Background] Monitors found: %" G_GSIZE_FORMAT, priv->monitors_size);
+
     /* Used to track situation when all monitors marked as "#skip" */
     Monitor* first_not_skipped_monitor = NULL;
 
@@ -449,25 +525,28 @@
 
         const gchar* printable_name = monitor->name ? monitor->name : "<unknown>";
 
-        if(!greeter_background_find_monitor_data(background, priv->configs, monitor, (gpointer*)&config))
-        {
-            g_debug("No configuration options for monitor %s #%d, using default", printable_name, i);
-            config = priv->default_config;
-        }
-
         gdk_screen_get_monitor_geometry(screen, i, &monitor->geometry);
 
-        g_debug("Monitor: %s #%d (%dx%d at %dx%d)%s", printable_name, i,
+        g_debug("[Background] Monitor: %s #%d (%dx%d at %dx%d)%s", printable_name, i,
                 monitor->geometry.width, monitor->geometry.height,
                 monitor->geometry.x, monitor->geometry.y,
                 (i == gdk_screen_get_primary_monitor(screen)) ? " primary" : "");
 
+        if(!greeter_background_find_monitor_data(background, priv->configs, monitor, (gpointer*)&config))
+        {
+            g_debug("[Background] No configuration options for monitor %s #%d, using default", printable_name, i);
+            config = priv->default_config;
+        }
+
         /* Force last skipped monitor to be active monitor, if there is no other choice */
         if(config->bg.type == BACKGROUND_TYPE_SKIP)
         {
             if(i < priv->monitors_size - 1 || first_not_skipped_monitor)
+            {
+                g_debug("[Background] Skipping monitor %s #%d", printable_name, i);
                 continue;
-            g_debug("Monitor %s #%d can not be skipped, using default configuration for it", printable_name, i);
+            }
+            g_debug("[Background] Monitor %s #%d can not be skipped, using default configuration for it", printable_name, i);
             if(priv->default_config->bg.type != BACKGROUND_TYPE_SKIP)
                 config = priv->default_config;
             else
@@ -489,6 +568,12 @@
         monitor->window_draw_handler_id = g_signal_connect(G_OBJECT(monitor->window), "draw",
                                                            G_CALLBACK(monitor_window_draw_cb),
                                                            monitor);
+
+        gchar* window_name = monitor->name ? g_strdup_printf("monitor-%s", monitor->name) : g_strdup_printf("monitor-%d", i);
+        gtk_widget_set_name(GTK_WIDGET(monitor->window), window_name);
+        gtk_style_context_add_class(gtk_widget_get_style_context(GTK_WIDGET(monitor->window)), "lightdm-gtk-greeter");
+        g_free(window_name);
+
         GSList* item;
         for(item = priv->accel_groups; item != NULL; item = g_slist_next(item))
             gtk_window_add_accel_group(monitor->window, item->data);
@@ -503,9 +588,13 @@
         if(config->laptop)
             priv->laptop_monitors = g_slist_prepend(priv->laptop_monitors, monitor);
 
-        if(!background_initialize(&monitor->background_configured, &config->bg, monitor, images_cache))
-            background_initialize(&monitor->background_configured, &DEFAULT_MONITOR_CONFIG.bg, monitor, images_cache);
-        monitor_set_background(monitor, &monitor->background_configured);
+        monitor->background_configured = background_new(&config->bg, monitor, images_cache);
+        if(!monitor->background_configured)
+            monitor->background_configured = background_new(&DEFAULT_MONITOR_CONFIG.bg, monitor, images_cache);
+        monitor_set_background(monitor, monitor->background_configured);
+
+        if(config->transition.duration && config->transition.func)
+            monitor->transition.config = config->transition;
 
         if(monitor->name)
             g_hash_table_insert(priv->monitors_map, g_strdup(monitor->name), monitor);
@@ -528,18 +617,19 @@
             if(greeter_background_monitor_enabled(background, monitor) &&
                x >= monitor->geometry.x && x < monitor->geometry.x + monitor->geometry.width &&
                y >= monitor->geometry.y && y < monitor->geometry.y + monitor->geometry.height)
+            {
+                g_debug("[Background] Pointer position will be used to set active monitor: %dx%d", x, y);
                 greeter_background_set_active_monitor(background, monitor);
+                break;
+            }
         }
     }
     if(!priv->active_monitor)
         greeter_background_set_active_monitor(background, NULL);
 
-    if(screen_monitors_changed_handler_id)
-        priv->screen_monitors_changed_handler_id = screen_monitors_changed_handler_id;
-    else
-        priv->screen_monitors_changed_handler_id = g_signal_connect(G_OBJECT(screen), "monitors-changed",
-                                                                    G_CALLBACK(greeter_background_monitors_changed_cb),
-                                                                    background);
+    priv->screen_monitors_changed_handler_id = g_signal_connect(G_OBJECT(screen), "monitors-changed",
+                                                                G_CALLBACK(greeter_background_monitors_changed_cb),
+                                                                background);
 }
 
 void
@@ -548,12 +638,11 @@
     g_return_if_fail(GREETER_IS_BACKGROUND(background));
     GreeterBackgroundPrivate* priv = background->priv;
 
-    priv->screen = NULL;
-    priv->active_monitor = NULL;
-
     if(priv->screen_monitors_changed_handler_id)
         g_signal_handler_disconnect(priv->screen, priv->screen_monitors_changed_handler_id);
     priv->screen_monitors_changed_handler_id = 0;
+    priv->screen = NULL;
+    priv->active_monitor = NULL;
 
     gint i;
     for(i = 0; i < priv->monitors_size; ++i)
@@ -610,6 +699,8 @@
             const Monitor* monitor = g_hash_table_lookup(priv->monitors_map, iter->data);
             if(monitor && monitor->background && greeter_background_monitor_enabled(background, monitor))
                 active = monitor;
+            if(active)
+                g_debug("[Background] Active monitor is not specified, using first enabled monitor from 'active-monitor' list");
         }
 
         /* All monitors listed in active-monitor-config are disabled (or option is empty) */
@@ -617,9 +708,13 @@
         /* Using primary monitor */
         if(!active)
         {
-            active = &priv->monitors[gdk_screen_get_primary_monitor(priv->screen)];
+            gint num = gdk_screen_get_primary_monitor(priv->screen);
+            g_return_if_fail(num < priv->monitors_size);
+            active = &priv->monitors[num];
             if(!active->background || !greeter_background_monitor_enabled(background, active))
                 active = NULL;
+            if(active)
+                g_debug("[Background] Active monitor is not specified, using primary monitor");
         }
 
         /* Fallback: first enabled and/or not skipped monitor (screen always have one) */
@@ -639,7 +734,25 @@
             }
             if(!active)
                 active = first_not_skipped;
-        }
+            if(active)
+                g_debug("[Background] Active monitor is not specified, using first enabled monitor");
+        }
+
+        if(!active && priv->laptop_monitors)
+        {
+            active = priv->laptop_monitors->data;
+            g_debug("[Background] Active monitor is not specified, using laptop monitor");
+        }
+    }
+
+    if(!active)
+    {
+        if(priv->active_monitor)
+            g_warning("[Background] Active monitor is not specified, failed to identify. Active monitor stays the same: %s #%d",
+                      priv->active_monitor->name, priv->active_monitor->number);
+        else
+            g_warning("[Background] Active monitor is not specified, failed to identify. Active monitor stays the same: <not defined>");
+        return;
     }
 
     if(active == priv->active_monitor)
@@ -647,12 +760,25 @@
 
     priv->active_monitor = active;
 
-    GtkWidget* old_parent = gtk_widget_get_parent(priv->child);
-    if(old_parent)
-        gtk_container_remove(GTK_CONTAINER(old_parent), priv->child);
-    gtk_container_add(GTK_CONTAINER(active->window), priv->child);
-
-    g_debug("Active monitor changed to: %s #%d", active->name, active->number);
+    g_return_if_fail(priv->active_monitor != NULL);
+
+    if(priv->child)
+    {
+        GtkWidget* old_parent = gtk_widget_get_parent(priv->child);
+        gpointer focus = greeter_save_focus(priv->child);
+
+        if(old_parent)
+            gtk_container_remove(GTK_CONTAINER(old_parent), priv->child);
+        gtk_container_add(GTK_CONTAINER(active->window), priv->child);
+
+        gtk_window_present(active->window);
+        greeter_restore_focus(focus);
+        g_free(focus);
+    }
+    else
+        g_warning("[Background] Child widget is destroyed or not defined");
+
+    g_debug("[Background] Active monitor changed to: %s #%d", active->name, active->number);
     g_signal_emit(background, background_signals[BACKGROUND_SIGNAL_ACTIVE_MONITOR_CHANGED], 0);
 
     gint x, y;
@@ -691,7 +817,7 @@
 static void
 greeter_background_try_init_dbus(GreeterBackground* background)
 {
-    g_debug("Creating DBus proxy");
+    g_debug("[Background] Creating DBus proxy");
     GError* error = NULL;
     GreeterBackgroundPrivate* priv = background->priv;
 
@@ -710,7 +836,7 @@
     if(!priv->laptop_upower_proxy)
     {
         if(error)
-            g_warning("Failed to create dbus proxy: %s", error->message);
+            g_warning("[Background] Failed to create dbus proxy: %s", error->message);
         g_clear_error(&error);
         return;
     }
@@ -719,7 +845,7 @@
     gboolean lid_present = g_variant_get_boolean(variant);
     g_variant_unref(variant);
 
-    g_debug("UPower.%s property value: %d", DBUS_UPOWER_PROP_LID_IS_PRESENT, lid_present);
+    g_debug("[Background] UPower.%s property value: %d", DBUS_UPOWER_PROP_LID_IS_PRESENT, lid_present);
 
     if(!lid_present)
         greeter_background_stop_dbus(background);
@@ -769,13 +895,21 @@
     if(new_state == priv->laptop_lid_closed)
         return;
 
-    g_debug("UPower: lid state changed to '%s'", priv->laptop_lid_closed ? "closed" : "opened");
-
     priv->laptop_lid_closed = new_state;
+    g_debug("[Background] UPower: lid state changed to '%s'", priv->laptop_lid_closed ? "closed" : "opened");
+
     if(priv->laptop_monitors)
     {
-        if(!priv->follow_cursor || (new_state && priv->laptop_monitors->data == priv->active_monitor))
-            greeter_background_set_active_monitor(background, NULL);
+        if(priv->laptop_lid_closed)
+        {
+            if(g_slist_find(priv->laptop_monitors, priv->active_monitor))
+                greeter_background_set_active_monitor(background, NULL);
+        }
+        else
+        {
+            if(!priv->follow_cursor)
+                greeter_background_set_active_monitor(background, NULL);
+        }
     }
 }
 
@@ -787,6 +921,13 @@
     greeter_background_connect(background, screen);
 }
 
+static void
+greeter_background_child_destroyed_cb(GtkWidget* child,
+                                      GreeterBackground* background)
+{
+    background->priv->child = NULL;
+}
+
 void
 greeter_background_set_custom_background(GreeterBackground* background,
                                          const gchar* value)
@@ -809,13 +950,19 @@
     {
         Monitor *monitor = iter->data;
 
-        background_finalize(&monitor->background_custom);
-        if(config.type != BACKGROUND_TYPE_INVALID &&
-           background_initialize(&monitor->background_custom, &config, monitor, images_cache))
-            monitor_set_background(monitor, &monitor->background_custom);
+        /* Old background_custom (if used) will be unrefed in monitor_set_background() */
+        Background* bg = NULL;
+        if(config.type != BACKGROUND_TYPE_INVALID)
+            bg = background_new(&config, monitor, images_cache);
+        if(bg)
+        {
+            monitor_set_background(monitor, bg);
+            background_unref(&bg);
+        }
         else
-            monitor_set_background(monitor, &monitor->background_configured);
+            monitor_set_background(monitor, monitor->background_configured);
     }
+
     if(images_cache)
         g_hash_table_unref(images_cache);
     if(config.type != BACKGROUND_TYPE_INVALID)
@@ -831,18 +978,43 @@
     cairo_surface_t* surface = create_root_surface(priv->screen);
     cairo_t* cr = cairo_create(surface);
     gsize i;
-
-    for(i = 0; i <= priv->monitors_size; ++i)
+    gdouble child_opacity;
+
+    const GdkRGBA ROOT_COLOR = {1.0, 1.0, 1.0, 1.0};
+    gdk_cairo_set_source_rgba(cr, &ROOT_COLOR);
+    cairo_paint(cr);
+
+    for(i = 0; i < priv->monitors_size; ++i)
     {
         const Monitor* monitor = &priv->monitors[i];
-        if(monitor == priv->active_monitor || !monitor->background)
+        if(!monitor->background)
             continue;
-        if(i == priv->monitors_size)
-            monitor = priv->active_monitor;
+
+        #ifdef XROOT_DRAW_BACKGROUND_DIRECTLY
+        /* Old method: can't draw default GtkWindow background */
         cairo_save(cr);
         cairo_translate(cr, monitor->geometry.x, monitor->geometry.y);
-        monitor_draw_background(monitor, cr);
+        monitor_draw_background(monitor, monitor->background, cr);
         cairo_restore(cr);
+        #else
+        /* New - can draw anything, but looks tricky a bit */
+        child_opacity = gtk_widget_get_opacity(priv->child);
+        if(monitor == priv->active_monitor)
+        {
+            gtk_widget_set_opacity(priv->child, 0.0);
+            gdk_window_process_updates(gtk_widget_get_window(GTK_WIDGET(priv->child)), FALSE);
+        }
+
+        gdk_cairo_set_source_window(cr, gtk_widget_get_window(GTK_WIDGET(monitor->window)),
+                                    monitor->geometry.x, monitor->geometry.y);
+        cairo_paint(cr);
+
+        if(monitor == priv->active_monitor)
+        {
+            gtk_widget_set_opacity(priv->child, child_opacity);
+            gdk_window_process_updates(gtk_widget_get_window(GTK_WIDGET(priv->child)), FALSE);
+        }
+        #endif
     }
     set_surface_as_root(priv->screen, surface);
 
@@ -887,6 +1059,8 @@
         return FALSE;
     if(g_strcmp0(value, BACKGROUND_TYPE_SKIP_VALUE) == 0)
         config->type = BACKGROUND_TYPE_SKIP;
+    else if(g_strcmp0(value, BACKGROUND_TYPE_DEFAULT_VALUE) == 0)
+        config->type = BACKGROUND_TYPE_DEFAULT;
     else if(gdk_rgba_parse(&config->options.color, value))
         config->type = BACKGROUND_TYPE_COLOR;
     else
@@ -904,7 +1078,7 @@
             config->options.image.mode = SCALING_MODE_ZOOMED;
 
         config->options.image.path = g_strdup(value);
-        config->type = BACKGROUND_TYPE_IMAGE;        
+        config->type = BACKGROUND_TYPE_IMAGE;
     }
     return TRUE;
 }
@@ -912,8 +1086,19 @@
 static void
 background_config_finalize(BackgroundConfig* config)
 {
-    if(config->type == BACKGROUND_TYPE_IMAGE)
-        g_free(config->options.image.path);
+    switch(config->type)
+    {
+        case BACKGROUND_TYPE_IMAGE:
+            g_free(config->options.image.path);
+            break;
+        case BACKGROUND_TYPE_COLOR:
+        case BACKGROUND_TYPE_DEFAULT:
+        case BACKGROUND_TYPE_SKIP:
+            break;
+        case BACKGROUND_TYPE_INVALID:
+            g_return_if_reached();
+    }
+
     config->type = BACKGROUND_TYPE_INVALID;
 }
 
@@ -922,8 +1107,19 @@
                        BackgroundConfig* dest)
 {
     *dest = *source;
-    if(source->type == BACKGROUND_TYPE_IMAGE)
-        dest->options.image.path = g_strdup(source->options.image.path);
+
+    switch(dest->type)
+    {
+        case BACKGROUND_TYPE_IMAGE:
+            dest->options.image.path = g_strdup(source->options.image.path);
+            break;
+        case BACKGROUND_TYPE_COLOR:
+        case BACKGROUND_TYPE_DEFAULT:
+        case BACKGROUND_TYPE_SKIP:
+            break;
+        case BACKGROUND_TYPE_INVALID:
+            g_return_if_reached();
+    }
 }
 
 static void
@@ -941,84 +1137,238 @@
     background_config_copy(&source->bg, &dest->bg);
     dest->user_bg = source->user_bg;
     dest->laptop = source->laptop;
+    dest->transition = source->transition;
     return dest;
 }
 
-static gboolean
-background_initialize(Background* bg,
-                      const BackgroundConfig* config,
-                      const Monitor* monitor,
-                      GHashTable* images_cache)
-{
-    if(config->type == BACKGROUND_TYPE_IMAGE)
-    {
-        GdkPixbuf* pixbuf = scale_image_file(config->options.image.path,
-                                             config->options.image.mode,
-                                             monitor->geometry.width, monitor->geometry.height,
-                                             images_cache);
-        if(!pixbuf)
-        {
-            g_warning("Failed to read wallpaper: %s", config->options.image.path);
-            return FALSE;
-        }
-        bg->options.image = pixbuf;
-    }
-    else if(config->type == BACKGROUND_TYPE_COLOR)
-        bg->options.color = config->options.color;
-    else
-        return FALSE;
-    bg->type = config->type;
-    return TRUE;
+static Background*
+background_new(const BackgroundConfig* config,
+               const Monitor* monitor,
+               GHashTable* images_cache)
+{
+    Background bg = {0};
+
+    switch(config->type)
+    {
+        case BACKGROUND_TYPE_IMAGE:
+            bg.options.image = scale_image_file(config->options.image.path, config->options.image.mode,
+                                                monitor->geometry.width, monitor->geometry.height,
+                                                images_cache);
+            if(!bg.options.image)
+            {
+                g_warning("[Background] Failed to read wallpaper: %s", config->options.image.path);
+                return NULL;
+            }
+            break;
+        case BACKGROUND_TYPE_COLOR:
+            bg.options.color = config->options.color;
+            break;
+        case BACKGROUND_TYPE_DEFAULT:
+            break;
+        case BACKGROUND_TYPE_SKIP:
+        case BACKGROUND_TYPE_INVALID:
+            g_return_val_if_reached(NULL);
+    }
+
+    bg.type = config->type;
+    bg.ref_count = 1;
+
+    Background* result = g_new(Background, 1);
+    *result = bg;
+    return result;
+}
+
+static Background*
+background_ref(Background* bg)
+{
+    bg->ref_count++;
+    return bg;
+}
+
+static void
+background_unref(Background** bg)
+{
+    if(!*bg)
+        return;
+    (*bg)->ref_count--;
+    if((*bg)->ref_count == 0)
+    {
+        background_finalize(*bg);
+        *bg = NULL;
+    }
 }
 
 static void
 background_finalize(Background* bg)
 {
-    if(bg->type == BACKGROUND_TYPE_IMAGE)
-        g_clear_object(&bg->options.image);
+    switch(bg->type)
+    {
+        case BACKGROUND_TYPE_IMAGE:
+            g_clear_object(&bg->options.image);
+            break;
+        case BACKGROUND_TYPE_COLOR:
+        case BACKGROUND_TYPE_DEFAULT:
+            break;
+        case BACKGROUND_TYPE_SKIP:
+        case BACKGROUND_TYPE_INVALID:
+            g_return_if_reached();
+    }
+
     bg->type = BACKGROUND_TYPE_INVALID;
 }
 
 static void
 monitor_set_background(Monitor* monitor,
-                       const Background* background)
-{
-    monitor->background = background;
-    gtk_widget_queue_draw(GTK_WIDGET(monitor->window));
+                       Background* background)
+{
+    if(monitor->background == background)
+        return;
+    monitor_stop_transition(monitor);
+
+    switch(background->type)
+    {
+        case BACKGROUND_TYPE_IMAGE:
+        case BACKGROUND_TYPE_COLOR:
+            gtk_widget_set_app_paintable(GTK_WIDGET(monitor->window), TRUE);
+            if(monitor->transition.config.duration > 0 && monitor->background &&
+               monitor->background->type != BACKGROUND_TYPE_DEFAULT)
+                monitor_start_transition(monitor, monitor->background, background);
+            break;
+        case BACKGROUND_TYPE_DEFAULT:
+            gtk_widget_set_app_paintable(GTK_WIDGET(monitor->window), FALSE);
+            break;
+        case BACKGROUND_TYPE_SKIP:
+        case BACKGROUND_TYPE_INVALID:
+            g_return_val_if_reached(NULL);
+    }
+
+    background_unref(&monitor->background);
+    monitor->background = background_ref(background);
+    gtk_widget_queue_draw(GTK_WIDGET(monitor->window));
+}
+
+static void
+monitor_start_transition(Monitor* monitor,
+                         Background* from,
+                         Background* to)
+{
+    monitor_stop_transition(monitor);
+
+    monitor->transition.from = background_ref(from);
+    monitor->transition.to = background_ref(to);
+
+    monitor->transition.started = g_get_monotonic_time();
+    monitor->transition.timer_id = gtk_widget_add_tick_callback(GTK_WIDGET(monitor->window),
+                                                                (GtkTickCallback)monitor_transition_cb,
+                                                                monitor,
+                                                                NULL);
+    monitor->transition.stage = 0;
+}
+
+static void
+monitor_stop_transition(Monitor* monitor)
+{
+    if(!monitor->transition.timer_id)
+        return;
+    gtk_widget_remove_tick_callback(GTK_WIDGET(monitor->window), monitor->transition.timer_id);
+    monitor->transition.timer_id = 0;
+    monitor->transition.started = 0;
+    monitor->transition.stage = 0;
+    background_unref(&monitor->transition.to);
+    background_unref(&monitor->transition.from);
+}
+
+static gboolean
+monitor_transition_cb(GtkWidget *widget,
+                      GdkFrameClock* frame_clock,
+                      Monitor* monitor)
+{
+    if(!monitor->transition.timer_id)
+        return G_SOURCE_REMOVE;
+
+    gint64 span = g_get_monotonic_time() - monitor->transition.started;
+    gdouble x = CLAMP(span/monitor->transition.config.duration/1000.0, 0.0, 1.0);
+    monitor->transition.stage = monitor->transition.config.func(x);
+
+    if(x >= 1.0)
+        monitor_stop_transition(monitor);
+
+    gtk_widget_queue_draw(GTK_WIDGET(monitor->window));
+    return x >= 1.0 ? G_SOURCE_REMOVE : G_SOURCE_CONTINUE;
+}
+
+static void
+monitor_transition_draw_alpha(const Monitor* monitor,
+                              cairo_t* cr)
+{
+    monitor_draw_background(monitor, monitor->transition.from, cr);
+
+    cairo_push_group(cr);
+    monitor_draw_background(monitor, monitor->transition.to, cr);
+    cairo_pop_group_to_source(cr);
+
+    cairo_pattern_t* alpha_pattern = cairo_pattern_create_rgba(0.0, 0.0, 0.0, monitor->transition.stage);
+    cairo_mask(cr, alpha_pattern);
+    cairo_pattern_destroy(alpha_pattern);
 }
 
 static void
 monitor_finalize(Monitor* monitor)
 {
-    background_finalize(&monitor->background_configured);
-    background_finalize(&monitor->background_custom);
-    g_free(monitor->name);
+    if(monitor->transition.config.duration)
+    {
+        monitor_stop_transition(monitor);
+        if(monitor->transition.timer_id)
+            g_source_remove(monitor->transition.timer_id);
+        monitor->transition.config.duration = 0;
+    }
+
     if(monitor->window_draw_handler_id)
         g_signal_handler_disconnect(monitor->window, monitor->window_draw_handler_id);
+
+    background_unref(&monitor->background_configured);
+    background_unref(&monitor->background);
+
     if(monitor->window)
+    {
+        GtkWidget* child = gtk_bin_get_child(GTK_BIN(monitor->window));
+        if(child) /* remove greeter widget to avoid "destroy" signal */
+            gtk_container_remove(GTK_CONTAINER(monitor->window), child);
         gtk_widget_destroy(GTK_WIDGET(monitor->window));
-    monitor->name = NULL;
-    monitor->window = NULL;
-    monitor->window_draw_handler_id = 0;
+    }
+
+    g_free(monitor->name);
+
+    *monitor = INVALID_MONITOR_STRUCT;
 }
 
 static void
 monitor_draw_background(const Monitor* monitor,
+                        const Background* background,
                         cairo_t* cr)
 {
     g_return_if_fail(monitor != NULL);
-    g_return_if_fail(monitor->background != NULL);
+    g_return_if_fail(background != NULL);
 
-    if(monitor->background->type == BACKGROUND_TYPE_IMAGE && monitor->background->options.image)
-    {
-        gdk_cairo_set_source_pixbuf(cr, monitor->background->options.image, 0, 0);
-        cairo_paint(cr);
-    }
-    else if(monitor->background->type == BACKGROUND_TYPE_COLOR)
-    {
-        cairo_rectangle(cr, 0, 0, monitor->geometry.width, monitor->geometry.height);
-        gdk_cairo_set_source_rgba(cr, &monitor->background->options.color);
-        cairo_fill(cr);
+    switch(background->type)
+    {
+        case BACKGROUND_TYPE_IMAGE:
+            if(background->options.image)
+            {
+                gdk_cairo_set_source_pixbuf(cr, background->options.image, 0, 0);
+                cairo_paint(cr);
+            }
+            break;
+        case BACKGROUND_TYPE_COLOR:
+            cairo_rectangle(cr, 0, 0, monitor->geometry.width, monitor->geometry.height);
+            gdk_cairo_set_source_rgba(cr, &background->options.color);
+            cairo_fill(cr);
+            break;
+        case BACKGROUND_TYPE_DEFAULT:
+            break;
+        case BACKGROUND_TYPE_SKIP:
+        case BACKGROUND_TYPE_INVALID:
+            g_return_if_reached();
     }
 }
 
@@ -1027,8 +1377,14 @@
                        cairo_t* cr,
                        const Monitor* monitor)
 {
-    if(monitor->background)
-        monitor_draw_background(monitor, cr);
+    if(!monitor->background)
+        return FALSE;
+
+    if(monitor->transition.started)
+        monitor->transition.config.draw(monitor, cr);
+    else
+        monitor_draw_background(monitor, monitor->background, cr);
+
     return FALSE;
 }
 
@@ -1051,35 +1407,43 @@
 {
     gchar* key = NULL;
     GdkPixbuf* pixbuf = NULL;
+
     if(cache)
     {
         key = g_strdup_printf("%s\n%d %dx%d", path, mode, width, height);
-        if (g_hash_table_lookup_extended(cache, key, NULL, (gpointer*)&pixbuf))
+        if(g_hash_table_lookup_extended(cache, key, NULL, (gpointer*)&pixbuf))
+        {
+            g_free(key);
             return GDK_PIXBUF(g_object_ref(pixbuf));
+        }
     }
 
-    if (!cache || !g_hash_table_lookup_extended(cache, path, NULL, (gpointer*)&pixbuf))
+    if(!cache || !g_hash_table_lookup_extended(cache, path, NULL, (gpointer*)&pixbuf))
     {
         GError *error = NULL;
         pixbuf = gdk_pixbuf_new_from_file(path, &error);
         if(error)
         {
-            g_warning("Failed to load background: %s", error->message);
+            g_warning("[Background] Failed to load background: %s", error->message);
             g_clear_error(&error);
         }
         else if(cache)
-            g_hash_table_insert(cache, g_strdup(path), g_object_ref (pixbuf));
+            g_hash_table_insert(cache, g_strdup(path), g_object_ref(pixbuf));
     }
+    else
+        pixbuf = g_object_ref(pixbuf);
 
     if(pixbuf)
     {
         GdkPixbuf* scaled = scale_image(pixbuf, mode, width, height);
-        if (cache)
+        if(cache)
             g_hash_table_insert(cache, g_strdup(key), g_object_ref(scaled));
         g_object_unref(pixbuf);
         pixbuf = scaled;
     }
 
+    g_free(key);
+
     return pixbuf;
 }
 
@@ -1142,7 +1506,7 @@
     display = XOpenDisplay (gdk_display_get_name (gdk_screen_get_display (screen)));
     if (!display)
     {
-        g_warning ("Failed to create root pixmap");
+        g_warning("[Background] Failed to create root pixmap");
         return NULL;
     }
 
@@ -1165,7 +1529,7 @@
                    Display* display,
                    Pixmap xpixmap)
 {
-    
+
     Window xroot = RootWindow (display, gdk_screen_get_number (screen));
     char *atom_names[] = {"_XROOTPMAP_ID", "ESETROOT_PMAP_ID"};
     Atom atoms[G_N_ELEMENTS(atom_names)] = {0};
@@ -1214,7 +1578,7 @@
      */
     if (!XInternAtoms (display, atom_names, G_N_ELEMENTS(atom_names), False, atoms) ||
         atoms[0] == None || atoms[1] == None) {
-        g_warning("Could not create atoms needed to set root pixmap id/properties.\n");
+        g_warning("[Background] Could not create atoms needed to set root pixmap id/properties.\n");
         return;
     }
 
@@ -1261,3 +1625,15 @@
     XFlush (display);
     XUngrabServer (display);
 }
+
+static gdouble
+transition_func_linear(gdouble x)
+{
+    return x;
+}
+
+static gdouble
+transition_func_easy_in_out(gdouble x)
+{
+    return (1 - cos(M_PI*x))/2;
+}

=== modified file 'src/greeterbackground.h'
--- src/greeterbackground.h	2014-08-31 17:45:52 +0000
+++ src/greeterbackground.h	2014-12-10 07:59:47 +0000
@@ -12,6 +12,8 @@
 #define GREETER_IS_BACKGROUND(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj), GREETER_BACKGROUND_TYPE))
 #define GREETER_IS_BACKGROUND_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass), GREETER_BACKGROUND_TYPE))
 
+#define GREETER_BACKGROUND_DEFAULT          "*"
+
 typedef struct _GreeterBackground           GreeterBackground;
 typedef struct _GreeterBackgroundClass      GreeterBackgroundClass;
 
@@ -20,15 +22,13 @@
 GreeterBackground* greeter_background_new           (GtkWidget* child);
 void greeter_background_set_active_monitor_config   (GreeterBackground* background,
                                                      const gchar* value);
-void greeter_background_set_default_config          (GreeterBackground* background,
-                                                     const gchar* bg,
-                                                     gboolean user_bg,
-                                                     gboolean laptop);
 void greeter_background_set_monitor_config          (GreeterBackground* background,
                                                      const gchar* name,
                                                      const gchar* bg,
                                                      gboolean user_bg, gboolean user_bg_used,
-                                                     gboolean laptop, gboolean laptop_used);
+                                                     gboolean laptop, gboolean laptop_used,
+                                                     gint transition_duration,
+                                                     const gchar* transition_func);
 void greeter_background_remove_monitor_config       (GreeterBackground* background,
                                                      const gchar* name);
 gchar** greeter_background_get_configured_monitors  (GreeterBackground* background);

=== modified file 'src/lightdm-gtk-greeter.c'
--- src/lightdm-gtk-greeter.c	2014-09-01 07:58:56 +0000
+++ src/lightdm-gtk-greeter.c	2014-12-10 07:59:47 +0000
@@ -127,9 +127,6 @@
 static const WindowPosition KEYBOARD_POSITION   = {.x = { 50, +1, TRUE,   0}, .y = {  0, -1, FALSE, +1}, .use_size = TRUE,
                                                    .width = {50, 0, TRUE, 0}, .height = {25, 0, TRUE, 0}};
 
-/* Configuration */
-static gboolean key_file_get_boolean_extended (GKeyFile *key_file, const gchar *group_name, const gchar *key, gboolean default_value);
-
 /* Clock */
 static gchar *clock_format;
 static gboolean clock_timeout_thread (void);
@@ -183,6 +180,7 @@
 static int timeout, interval, prefer_blanking, allow_exposures;
 
 /* Handling monitors backgrounds */
+static const gint USER_BACKGROUND_DELAY = 250;
 static GreeterBackground *greeter_background;
 
 /* Authentication state */
@@ -285,6 +283,42 @@
 void restart_cb (GtkWidget *widget, LightDMGreeter *greeter);
 void shutdown_cb (GtkWidget *widget, LightDMGreeter *greeter);
 
+gpointer greeter_save_focus(GtkWidget* widget);
+void greeter_restore_focus(const gpointer saved_data);
+
+struct SavedFocusData
+{
+    GtkWidget *widget;
+    gint editable_pos;
+};
+
+gpointer
+greeter_save_focus(GtkWidget* widget)
+{
+    GtkWidget *window = gtk_widget_get_toplevel(widget);
+    if (!GTK_IS_WINDOW (window))
+        return NULL;
+
+    struct SavedFocusData *data = g_new0 (struct SavedFocusData, 1);
+    data->widget = gtk_window_get_focus (GTK_WINDOW (window));
+    data->editable_pos = GTK_IS_EDITABLE(data->widget) ? gtk_editable_get_position (GTK_EDITABLE (data->widget)) : -1;
+
+    return data;
+}
+
+void
+greeter_restore_focus(const gpointer saved_data)
+{
+    if (!saved_data)
+        return;
+
+    struct SavedFocusData *data = saved_data;
+    if (GTK_IS_WIDGET (data->widget))
+        gtk_widget_grab_focus (data->widget);
+    if (GTK_IS_EDITABLE(data->widget) && data->editable_pos > -1)
+        gtk_editable_set_position(GTK_EDITABLE(data->widget), data->editable_pos);
+}
+
 /* State file */
 
 static void
@@ -536,21 +570,6 @@
     return TRUE;
 }
 
-/* Configuration */
-
-static gboolean
-key_file_get_boolean_extended (GKeyFile *key_file, const gchar *group_name, const gchar *key, gboolean default_value)
-{
-    GError* error = NULL;
-    gboolean result = g_key_file_get_boolean (key_file, group_name, key, &error);
-    if (error)
-    {
-        g_clear_error (&error);
-        return default_value;
-    }
-    return result;
-}
-
 /* Clock */
 
 static gboolean
@@ -1771,24 +1790,47 @@
         gtk_button_set_label (login_button, _("Unlock"));
     else
         gtk_button_set_label (login_button, _("Log In"));
-    gtk_widget_set_can_default (GTK_WIDGET (login_button), TRUE);
-    gtk_widget_grab_default (GTK_WIDGET (login_button));
     /* and disable the session and language widgets */
     gtk_widget_set_sensitive (GTK_WIDGET (session_menuitem), !logged_in);
     gtk_widget_set_sensitive (GTK_WIDGET (language_menuitem), !logged_in);
 }
 
+static guint set_user_background_delayed_id = 0;
+
+static gboolean
+set_user_background_delayed_cb (const gchar *value)
+{
+    greeter_background_set_custom_background (greeter_background, value);
+    set_user_background_delayed_id = 0;
+    return G_SOURCE_REMOVE;
+}
+
 static void
-set_user_background (const gchar *username)
+set_user_background (const gchar *user_name)
 {
-    const gchar *path = NULL;
-    if (username)
+    const gchar *value = NULL;
+    if (user_name)
     {
-        LightDMUser *user = lightdm_user_list_get_user_by_name (lightdm_user_list_get_instance (), username);
+        LightDMUser *user = lightdm_user_list_get_user_by_name (lightdm_user_list_get_instance (), user_name);
         if (user)
-            path = lightdm_user_get_background (user);
-    }
-    greeter_background_set_custom_background (greeter_background, path);
+            value = lightdm_user_get_background (user);
+    }
+
+    if (set_user_background_delayed_id)
+    {
+        g_source_remove (set_user_background_delayed_id);
+        set_user_background_delayed_id = 0;
+    }
+
+    if (!value)
+        greeter_background_set_custom_background (greeter_background, NULL);
+    else
+    {
+        /* Small delay before changing background */
+        set_user_background_delayed_id = g_timeout_add_full (G_PRIORITY_DEFAULT, USER_BACKGROUND_DELAY,
+                                                             (GSourceFunc)set_user_background_delayed_cb,
+                                                             g_strdup (value), g_free);
+    }
 }
 
 static void
@@ -2852,36 +2894,45 @@
     greeter_background_set_active_monitor_config (greeter_background, value ? value : "#cursor");
     g_free (value);
 
-    value = g_key_file_get_value (config, "greeter", "background", NULL);
-    greeter_background_set_default_config (greeter_background, value,
-                                           key_file_get_boolean_extended (config, "greeter", "user-background", TRUE),
-                                           key_file_get_boolean_extended (config, "greeter", "laptop", FALSE));
-    g_free (value);
-
     const gchar *CONFIG_MONITOR_PREFIX = "monitor:";
     gchar **config_group;
     gchar **config_groups = g_key_file_get_groups (config, NULL);
     for (config_group = config_groups; *config_group; ++config_group)
     {
-        if (!g_str_has_prefix (*config_group, CONFIG_MONITOR_PREFIX))
-            continue;
-        const gchar *name = *config_group + sizeof (CONFIG_MONITOR_PREFIX);
-        while (*name && g_ascii_isspace (*name))
-            ++name;
+        gchar *name_to_free = NULL;
+        const gchar *name = *config_group;
+
+        if (g_strcmp0 (*config_group, "greeter") != 0)
+        {
+            if (!g_str_has_prefix (name, CONFIG_MONITOR_PREFIX))
+                continue;
+            name = *config_group + sizeof (CONFIG_MONITOR_PREFIX);
+            while (*name && g_ascii_isspace (*name))
+                ++name;
+        }
+        else
+            name = name_to_free = g_strdup (GREETER_BACKGROUND_DEFAULT);
+
         g_debug ("Monitor configuration found: '%s'", name);
 
-        GError *user_bg_error = NULL, *laptop_error = NULL;
+        GError *user_bg_error = NULL, *laptop_error = NULL, *duration_error = NULL;
         gboolean user_bg = g_key_file_get_boolean (config, *config_group, "user-background", &user_bg_error);
         gboolean laptop = g_key_file_get_boolean (config, *config_group, "laptop", &laptop_error);
-        value = g_key_file_get_value (config, *config_group, "background", NULL);
+        gchar *background = g_key_file_get_value (config, *config_group, "background", NULL);
+        gchar *tr_type = g_key_file_get_string (config, *config_group, "transition-type", NULL);
+        gint tr_duration = g_key_file_get_integer (config, *config_group, "transition-duration", &duration_error);
 
-        greeter_background_set_monitor_config (greeter_background, name, value,
+        greeter_background_set_monitor_config (greeter_background, name, background,
                                                user_bg, user_bg_error == NULL,
-                                               laptop, laptop_error == NULL);
+                                               laptop, laptop_error == NULL,
+                                               duration_error == NULL ? tr_duration : -1,
+                                               tr_type);
 
-        g_free (value);
+        g_free (tr_type);
+        g_free (background);
+        g_free (name_to_free);
+        g_clear_error (&user_bg_error);
         g_clear_error (&laptop_error);
-        g_clear_error (&user_bg_error);
     }
     g_strfreev (config_groups);
 

=== modified file 'src/lightdm-gtk-greeter.glade'
--- src/lightdm-gtk-greeter.glade	2014-09-24 17:41:33 +0000
+++ src/lightdm-gtk-greeter.glade	2014-12-10 07:59:47 +0000
@@ -282,7 +282,6 @@
                     <property name="name">cancel_button</property>
                     <property name="visible">True</property>
                     <property name="can_focus">True</property>
-                    <property name="receives_default">True</property>
                     <signal name="clicked" handler="power_button_clicked_cb" swapped="no"/>
                   </object>
                   <packing>
@@ -297,7 +296,6 @@
                     <property name="name">power_ok_button</property>
                     <property name="visible">True</property>
                     <property name="can_focus">True</property>
-                    <property name="receives_default">True</property>
                     <signal name="clicked" handler="power_button_clicked_cb" swapped="no"/>
                   </object>
                   <packing>
@@ -432,7 +430,6 @@
                   <object class="GtkEntry" id="username_entry">
                     <property name="name">prompt_entry</property>
                     <property name="can_focus">True</property>
-                    <property name="no_show_all">True</property>
                     <property name="hexpand">True</property>
                     <property name="invisible_char">•</property>
                     <property name="placeholder_text" translatable="yes">Enter your username</property>
@@ -480,7 +477,6 @@
         <child>
           <object class="GtkInfoBar" id="greeter_infobar">
             <property name="name">greeter_infobar</property>
-            <property name="app_paintable">True</property>
             <property name="can_focus">False</property>
             <child internal-child="action_area">
               <object class="GtkButtonBox" id="infobar-action_area">
@@ -545,7 +541,6 @@
                     <property name="name">cancel_button</property>
                     <property name="visible">True</property>
                     <property name="can_focus">True</property>
-                    <property name="receives_default">True</property>
                     <signal name="clicked" handler="cancel_cb" swapped="no"/>
                   </object>
                   <packing>
@@ -560,7 +555,6 @@
                     <property name="name">login_button</property>
                     <property name="visible">True</property>
                     <property name="can_focus">True</property>
-                    <property name="receives_default">True</property>
                     <signal name="clicked" handler="login_cb" swapped="no"/>
                   </object>
                   <packing>


Follow ups