← Back to team overview

lightdm-gtk-greeter-team team mailing list archive

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

 

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

Requested reviews:
  LightDM Gtk+ Greeter Development Team (lightdm-gtk-greeter-team)
Related bugs:
  Bug #1273922 in LightDM GTK+ Greeter: "Greeter showed on laptop screen instead of external when laptop lid is closed"
  https://bugs.launchpad.net/lightdm-gtk-greeter/+bug/1273922
  Bug #1314603 in LightDM GTK+ Greeter: "Lightdm greeter on dual monitor system has bad-sized background"
  https://bugs.launchpad.net/lightdm-gtk-greeter/+bug/1314603
  Bug #1325719 in lightdm-gtk-greeter (Ubuntu): "Login screen wallaper doesn't fill screen"
  https://bugs.launchpad.net/ubuntu/+source/lightdm-gtk-greeter/+bug/1325719

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

Using GreeterBackground class to manage backgrounds.

New syntax for options:

background=monitor_0_background[;monitor_1_background;%named_monitor:named_monitor_background;...]
Last value used for unlisted monitors.

user-background=monitor_0_value[;monitor_1_value;%named_monitor:named_monitor_value;...]
Last value used for unlisted monitors.

New option: active-monitor - used to set monitor for greeter windows.
active-monitor=monitor_name_1;#lid:monitor_name_2;...

#lid:monitor_name - mark "monitor_name" as laptop display. Greeter will be moved to another monitor from the list if lid is closed.
-- 
https://code.launchpad.net/~lightdm-gtk-greeter-team/lightdm-gtk-greeter/greeter-background/+merge/225019
Your team LightDM Gtk+ Greeter Development Team is requested to review the proposed merge of lp:~lightdm-gtk-greeter-team/lightdm-gtk-greeter/greeter-background into lp:lightdm-gtk-greeter.
=== modified file 'src/Makefile.am'
--- src/Makefile.am	2014-06-09 15:49:08 +0000
+++ src/Makefile.am	2014-06-30 14:34:14 +0000
@@ -8,6 +8,7 @@
 lightdm_gtk_greeter_SOURCES = \
 	$(lightdm_gtk_greeter_built_sources) \
 	lightdm-gtk-greeter.c \
+	greeterbackground.c \
 	greetermenubar.c
 
 AM_CPPFLAGS = \

=== added file 'src/greeterbackground.c'
--- src/greeterbackground.c	1970-01-01 00:00:00 +0000
+++ src/greeterbackground.c	2014-06-30 14:34:14 +0000
@@ -0,0 +1,1354 @@
+
+#include <cairo-xlib.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <string.h>
+#include <X11/Xatom.h>
+
+#include "greeterbackground.h"
+
+
+typedef enum
+{
+    /* Broken/uninitialized configuration */
+    BACKGROUND_TYPE_INVALID,
+    /* Do not use this monitor */
+    BACKGROUND_TYPE_SKIP,
+    /* Solid color */
+    BACKGROUND_TYPE_COLOR,
+    /* Path to image and scaling mode */
+    BACKGROUND_TYPE_IMAGE
+} BackgroundType;
+
+static const gchar* BACKGROUND_TYPE_SKIP_VALUE = "#skip";
+
+typedef enum
+{
+    SCALING_MODE_SOURCE,
+    SCALING_MODE_ZOOMED,
+    SCALING_MODE_STRETCHED
+} ScalingMode;
+
+static const gchar* SCALING_MODE_PREFIXES[] = {"#source:", "#zoomed:", "#stretched:", NULL};
+
+/* Background configuration (parsed from background=... option).
+   Used to fill Background */
+typedef struct
+{
+    BackgroundType type;
+    union
+    {
+        #if GTK_CHECK_VERSION (3, 0, 0)
+        GdkRGBA color;
+        #else
+        GdkColor color;
+        #endif
+        struct
+        {
+            gchar *path;
+            ScalingMode mode;
+        } image;
+    } options;
+} BackgroundConfig;
+
+/* Actual drawing information attached to monitor */
+typedef struct
+{
+    BackgroundType type;
+    union
+    {
+        GdkPixbuf* image;
+        #if GTK_CHECK_VERSION (3, 0, 0)
+        GdkRGBA color;
+        #else
+        GdkColor color;
+        #endif
+    } options;
+} Background;
+
+typedef struct
+{
+    gint num;
+    const gchar* name; /* owned by priv->monitors_map */
+    GdkRectangle geometry;
+    GtkWindow* window;
+    gulong window_draw_handler_id;
+
+    const BackgroundConfig* config;
+    Background background_configured;
+    Background background_custom;
+    /* &background_configured or &background_custom */
+    const Background* background;
+} Monitor;
+
+struct _GreeterBackgroundPrivate
+{
+    GdkScreen* screen;
+    gulong screen_monitors_changed_handler_id;
+    /* one-window-gtk3-only
+    GtkWindow* greeter_widget;
+    */
+    GList* greeter_windows;
+    /* Monitor name => BackgroundConfig* */
+    GHashTable* monitors_config;
+    /* Default config for unknown (not in configs) monitors */
+    const BackgroundConfig* monitors_config_default;
+    Monitor* monitors;
+    gsize monitors_size;
+    GHashTable* monitors_map;
+
+    GList* active_monitors_config;
+    const Monitor* active_monitor;
+
+    /* Monitor name => user-background */
+    GHashTable* customized_monitors_config;
+    /* Default user-background value for unknown (not in customized_monitors_config) monitors */
+    gboolean customized_monitors_default;
+    /* List of Monitor* for current screen */
+    GList* customized_monitors;
+
+    /* Lid state handling */
+    GDBusProxy* upower_proxy;
+    gchar* lid_monitor_config;
+    const Monitor* lid_monitor;
+    gboolean lid_state;
+};
+
+enum
+{
+    BACKGROUND_PROP_0,
+    BACKGROUND_PROP_SCREEN,
+    BACKGROUND_PROP_WINDOWS,
+    BACKGROUND_PROP_CUSTOM_BACKGROUND,
+    BACKGROUND_PROP_BACKGROUND_CONFIG,          /* background= */
+    BACKGROUND_PROP_CUSTOM_BACKGROUND_CONFIG,   /* user-background= */
+    BACKGROUND_PROP_ACTIVE_MONITOR_CONFIG       /* active-monitor= */
+};
+
+enum
+{
+    BACKGROUND_SIGNAL_ACTIVE_MONITOR_CHANGED,
+    BACKGROUND_SIGNAL_LAST
+};
+
+static guint background_signals[BACKGROUND_SIGNAL_LAST] = {0};
+
+static const BackgroundConfig DEFAULT_BACKGROUND_CONFIG =
+{
+    .type = BACKGROUND_TYPE_COLOR,
+    .options =
+    {
+        #if GTK_CHECK_VERSION (3, 0, 0)
+        .color = {.red = 0.0, .green = 0.0, .blue = 0.0, .alpha = 1.0}
+        #else
+        .color = {.pixel = 0, .red = 0, .green = 0, .blue = 0}
+        #endif
+    }
+};
+
+static const gboolean DEFAULT_USER_BACKGROUND_CONFIG = TRUE;
+
+static const gchar* LAPTOP_MONITOR_PREFIX = "#lid:";
+
+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";
+static const gchar* DBUS_UPOWER_PROP_LID_IS_PRESENT = "LidIsPresent";
+static const gchar* DBUS_UPOWER_PROP_LID_IS_CLOSED = "LidIsClosed";
+
+G_DEFINE_TYPE_WITH_PRIVATE(GreeterBackground, greeter_background, G_TYPE_OBJECT);
+
+static void greeter_background_get_property         (GObject* object,
+                                                     guint prop_id,
+                                                     GValue* value,
+                                                     GParamSpec* pspec);
+static void greeter_background_set_property         (GObject* object,
+                                                     
+                                                     guint prop_id,
+                                                     const GValue* value,
+                                                     GParamSpec* pspec);
+void greeter_background_set_background_config       (GreeterBackground* background,
+                                                     const gchar* value);
+void greeter_background_set_custom_background_config(GreeterBackground* background,
+                                                     const gchar* value);
+void greeter_background_set_active_monitor_config   (GreeterBackground* background,
+                                                     const gchar* value);
+
+void greeter_background_connect                     (GreeterBackground* background,
+                                                     GdkScreen* screen);
+void greeter_background_disconnect                  (GreeterBackground* background);
+static void greeter_background_set_active_monitor   (GreeterBackground* background,
+                                                     const Monitor* active);
+static void greeter_background_try_init_dbus        (GreeterBackground* background);
+static void greeter_background_stop_dbus            (GreeterBackground* background);
+static gboolean greeter_background_monitor_enabled  (GreeterBackground* background,
+                                                     const Monitor* monitor);
+static void greeter_background_dbus_changed_cb      (GDBusProxy* proxy,
+                                                     GVariant* changed_properties,
+                                                     const gchar* const* invalidated_properties,
+                                                     GreeterBackground* background);
+static void greeter_background_monitors_changed_cb  (GdkScreen* screen,
+                                                     GreeterBackground* background);
+
+/* struct BackgroundConfig */
+static gboolean background_config_initialize        (BackgroundConfig* config,
+                                                     const gchar* value);
+static void background_config_finalize              (BackgroundConfig* config);
+static void background_config_free                  (BackgroundConfig* config);
+static void background_config_copy                  (const BackgroundConfig* source,
+                                                     BackgroundConfig* dest);
+
+/* struct Background */
+static gboolean background_initialize               (Background* bg,
+                                                     const BackgroundConfig* config,
+                                                     const Monitor* monitor,
+                                                     GHashTable* images_cache);
+static void background_finalize                     (Background* bg);
+
+/* struct Monitor */
+static void monitor_finalize                        (Monitor* info);
+static void monitor_set_background                  (Monitor* monitor,
+                                                     const Background* background);
+static void monitor_draw_background                 (const Monitor* monitor,
+                                                     cairo_t* cr);
+#if GTK_CHECK_VERSION(3, 0, 0)
+static gboolean monitor_window_draw_cb              (GtkWidget* widget,
+                                                     cairo_t* cr,
+                                                     const Monitor* monitor);
+static gboolean monitor_subwindow_draw_cb           (GtkWidget* widget,
+                                                     cairo_t* cr,
+                                                     GreeterBackground* background);
+#else
+static gboolean monitor_window_expose_cb            (GtkWidget* widget,
+                                                     GdkEventExpose *event,
+                                                     const Monitor* monitor);
+#endif
+
+static gchar* parse_monitor_name_from_config        (const gchar** value,
+                                                     gint* num);
+static GdkPixbuf* scale_image_file                  (const gchar* path,
+                                                     ScalingMode mode,
+                                                     gint width, gint height,
+                                                     GHashTable* cache);
+static GdkPixbuf* scale_image                       (GdkPixbuf* source,
+                                                     ScalingMode mode,
+                                                     gint width, gint height);
+static cairo_surface_t* create_root_surface         (GdkScreen* screen);
+static void set_root_pixmap_id                      (GdkScreen* screen,
+                                                     Display* display,
+                                                     Pixmap xpixmap);
+static void set_surface_as_root                     (GdkScreen* screen,
+                                                     cairo_surface_t* surface);
+
+
+/* Implementation */
+
+static void
+greeter_background_class_init(GreeterBackgroundClass* klass)
+{
+	GObjectClass* gobject_class = G_OBJECT_CLASS(klass);
+
+    gobject_class->get_property = greeter_background_get_property;
+    gobject_class->set_property = greeter_background_set_property;
+
+    g_object_class_install_property(gobject_class, BACKGROUND_PROP_SCREEN,
+                                    g_param_spec_object("screen", "screen", "Screen",
+                                                        GDK_TYPE_SCREEN, G_PARAM_READWRITE));
+    /*
+    g_object_class_install_property(gobject_class, BACKGROUND_PROP_GREETER_WIDGET,
+                                    g_param_spec_object("greeter-widget", "greeter-widget", "Greeter widget",
+                                                        GTK_TYPE_WINDOW, G_PARAM_READWRITE));
+    */
+    g_object_class_install_property(gobject_class, BACKGROUND_PROP_WINDOWS,
+                                    g_param_spec_pointer("windows", "Windows", "Greeter windows",
+                                                         G_PARAM_READWRITE));
+    g_object_class_install_property(gobject_class, BACKGROUND_PROP_CUSTOM_BACKGROUND,
+                                    g_param_spec_string("custom-background", "custom-background",
+                                                        "Custom background", NULL, G_PARAM_WRITABLE));
+    g_object_class_install_property(gobject_class, BACKGROUND_PROP_BACKGROUND_CONFIG,
+                                    g_param_spec_string("background-config", "background-config",
+                                                        "background= option", NULL, G_PARAM_WRITABLE));
+    g_object_class_install_property(gobject_class, BACKGROUND_PROP_CUSTOM_BACKGROUND_CONFIG,
+                                    g_param_spec_string("custom-background-config", "custom-background-config",
+                                                        "user-background= option", NULL, G_PARAM_WRITABLE));
+    g_object_class_install_property(gobject_class, BACKGROUND_PROP_BACKGROUND_CONFIG,
+                                    g_param_spec_string("active-monitor-config", "active-monitor-config",
+                                                        "active-monitor= option", NULL, G_PARAM_WRITABLE));
+
+    background_signals[BACKGROUND_SIGNAL_ACTIVE_MONITOR_CHANGED] =
+                            g_signal_new("active-monitor-changed",
+                                         G_TYPE_FROM_CLASS(gobject_class),
+                                         G_SIGNAL_RUN_FIRST,
+                                         0, /* class_offset */
+                                         NULL /* accumulator */, NULL /* accu_data */,
+                                         g_cclosure_marshal_VOID__VOID,
+                                         G_TYPE_NONE, 0);
+}
+
+static void
+greeter_background_init(GreeterBackground* self)
+{
+    self->priv = G_TYPE_INSTANCE_GET_PRIVATE(self, GREETER_BACKGROUND_TYPE, GreeterBackgroundPrivate);
+    self->priv->screen = NULL;
+    self->priv->screen_monitors_changed_handler_id = 0;
+    self->priv->greeter_windows = NULL;
+    self->priv->monitors_config = NULL;
+    self->priv->monitors_config_default = &DEFAULT_BACKGROUND_CONFIG;
+    self->priv->monitors = NULL;
+    self->priv->monitors_size = 0;
+    self->priv->monitors_map = NULL;
+    self->priv->customized_monitors_config = NULL;
+    self->priv->customized_monitors_default = DEFAULT_USER_BACKGROUND_CONFIG;
+    self->priv->customized_monitors = NULL;
+    self->priv->active_monitors_config = NULL;
+    self->priv->active_monitor = NULL;
+    self->priv->upower_proxy = NULL;
+    self->priv->lid_monitor_config = NULL;
+    self->priv->lid_monitor = NULL;
+}
+
+GreeterBackground* 
+greeter_background_new(void)
+{
+	return GREETER_BACKGROUND(g_object_new(greeter_background_get_type(), NULL));
+}
+
+static void
+greeter_background_get_property(GObject* object,
+                                guint prop_id,
+                                GValue* value,
+                                GParamSpec* pspec)
+{
+    GreeterBackground* background = GREETER_BACKGROUND(object);
+    switch(prop_id)
+    {
+        case BACKGROUND_PROP_SCREEN:
+            g_value_set_object(value, background->priv->screen);
+            break;
+        case BACKGROUND_PROP_WINDOWS:
+            g_value_set_pointer(value, background->priv->greeter_windows);
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+            break;
+    }
+}
+
+static void
+greeter_background_set_property(GObject* object,
+                                guint prop_id,
+                                const GValue* value,
+                                GParamSpec* pspec)
+{
+    GreeterBackground* background = GREETER_BACKGROUND(object);
+    switch(prop_id)
+    {
+        case BACKGROUND_PROP_SCREEN:
+            greeter_background_connect(background, GDK_SCREEN(g_value_get_object(value)));
+            break;
+        case BACKGROUND_PROP_CUSTOM_BACKGROUND:
+            greeter_background_set_custom_background(background, g_value_get_string(value));
+            break;
+        case BACKGROUND_PROP_BACKGROUND_CONFIG:
+            greeter_background_set_background_config(background, g_value_get_string(value));
+            break;
+        case BACKGROUND_PROP_CUSTOM_BACKGROUND_CONFIG:
+            greeter_background_set_custom_background_config(background, g_value_get_string(value));
+            break;
+        case BACKGROUND_PROP_ACTIVE_MONITOR_CONFIG:
+            greeter_background_set_active_monitor_config(background, g_value_get_string(value));
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+            break;
+    }
+}
+
+void
+greeter_background_set_background_config(GreeterBackground* background,
+                                         const gchar* value)
+{
+    g_debug("%s: %s", __FUNCTION__, value);
+    g_return_if_fail(GREETER_IS_BACKGROUND(background));
+    GreeterBackgroundPrivate* priv = background->priv;
+
+    if(priv->monitors_config)
+        g_hash_table_unref(priv->monitors_config);
+    priv->monitors_config = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)background_config_free);
+    priv->monitors_config_default = &DEFAULT_BACKGROUND_CONFIG;
+
+    if(!value || strlen(value) == 0)
+        return;
+
+    gchar** values = g_strsplit(value, ";", -1);
+    gchar** iter;
+    gint num = 0;
+    for(iter = values; *iter; ++iter)
+    {
+        const gchar* value = *iter;
+        gchar* name = parse_monitor_name_from_config(&value, &num);
+        BackgroundConfig* config = g_new0(BackgroundConfig, 1);
+
+        if(!background_config_initialize(config, value))
+            background_config_copy(priv->monitors_config_default, config);
+
+        priv->monitors_config_default = config;
+        g_hash_table_insert(priv->monitors_config, name, config);
+    }
+    g_strfreev(values);
+}
+
+void
+greeter_background_set_custom_background_config(GreeterBackground* background,
+                                                const gchar* value)
+{
+    g_debug("%s: %s", __FUNCTION__, value);
+    g_return_if_fail(GREETER_IS_BACKGROUND(background));
+    GreeterBackgroundPrivate* priv = background->priv;
+
+    if(priv->customized_monitors_config)
+        g_hash_table_unref(priv->customized_monitors_config);
+    priv->customized_monitors_config = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
+    priv->customized_monitors_default = DEFAULT_USER_BACKGROUND_CONFIG;
+
+    if(!value || strlen(value) == 0)
+        return;
+
+    gchar** values = g_strsplit(value, ";", -1);
+    gchar** iter;
+    gint num = 0;
+    for(iter = values; *iter; ++iter)
+    {
+        const gchar* value = *iter;
+        gchar* name = parse_monitor_name_from_config(&value, &num);
+        gboolean bool_value = g_ascii_strcasecmp(value, "true") == 0 || g_strcmp0(value, "1") == 0;
+        priv->customized_monitors_default = bool_value;
+        g_hash_table_insert(priv->customized_monitors_config, name, GINT_TO_POINTER(bool_value));
+    }
+    g_strfreev(values);
+}
+
+void
+greeter_background_set_active_monitor_config(GreeterBackground* background,
+                                             const gchar* value)
+{
+    g_debug("%s: %s", __FUNCTION__, value);
+    g_return_if_fail(GREETER_IS_BACKGROUND(background));
+    GreeterBackgroundPrivate* priv = background->priv;
+
+    if(priv->active_monitors_config)
+        g_list_free_full(priv->active_monitors_config, g_free);
+    g_free(priv->lid_monitor_config);
+    priv->active_monitors_config = NULL;
+    /* Do not modify current state: priv->active_monitor = NULL; */
+    priv->lid_monitor_config = NULL;
+
+    if(!value || strlen(value) == 0)
+        return;
+
+    gchar** values = g_strsplit(value, ";", -1);
+    gchar** iter;
+
+    for(iter = values; *iter; ++iter)
+    {
+        const gchar* value = *iter;
+        if(g_str_has_prefix(value, LAPTOP_MONITOR_PREFIX))
+        {
+            value += strlen(LAPTOP_MONITOR_PREFIX);
+            if(!priv->lid_monitor_config)  /* Use only first occurrence */
+                priv->lid_monitor_config = g_strdup(value);
+        }
+        priv->active_monitors_config = g_list_prepend(priv->active_monitors_config, g_strdup(value));
+    }
+    priv->active_monitors_config = g_list_reverse(priv->active_monitors_config);
+    g_strfreev(values);
+    g_debug("lid monitor: %s", priv->lid_monitor_config);
+}
+
+void
+greeter_background_connect(GreeterBackground* background,
+                           GdkScreen* screen)
+{
+    g_return_if_fail(GREETER_IS_BACKGROUND(background));
+    g_return_if_fail(GDK_IS_SCREEN(screen));
+    g_debug("Connecting to 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);
+
+    priv->screen = screen;
+    priv->monitors_size = gdk_screen_get_n_monitors(screen);
+    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);
+
+    /* Used to track situation when all monitors marked as "#skip" */
+    Monitor* first_not_skipped_monitor = NULL;
+
+    GHashTable* images_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+    gint i;
+    for(i = 0; i < priv->monitors_size; ++i)
+    {
+        Monitor* monitor = &priv->monitors[i];
+        gchar* monitor_name = gdk_screen_get_monitor_plug_name(screen, i);
+        gchar* monitor_num_str = g_strdup_printf("%d", i);
+        const BackgroundConfig* config = monitor_name ? g_hash_table_lookup(priv->monitors_config, monitor_name) : NULL;
+
+        monitor->num = i;
+        monitor->name = monitor_name;
+
+        if(!config)
+        {
+            config = g_hash_table_lookup(priv->monitors_config, monitor_num_str);
+            if(!config)
+            {
+                g_debug("No configuration options for monitor %s #%d, using default", monitor_name, i);
+                config = priv->monitors_config_default;
+            }
+        }
+
+        gdk_screen_get_monitor_geometry(screen, i, &monitor->geometry);
+
+        g_debug("Monitor: %s #%d (%dx%d at %dx%d)%s",
+                monitor_name ? monitor_name : "<unknown>", i,
+                monitor->geometry.width, monitor->geometry.height,
+                monitor->geometry.x, monitor->geometry.y,
+                (i == gdk_screen_get_primary_monitor(screen)) ? " primary" : "");
+
+        /* Force last skipped monitor to be active monitor, if there is no other choice */
+        if(config->type == BACKGROUND_TYPE_SKIP)
+        {
+            if(i < priv->monitors_size - 1 || first_not_skipped_monitor)
+                continue;
+            config = &DEFAULT_BACKGROUND_CONFIG;
+        }
+
+        if(!first_not_skipped_monitor)
+            first_not_skipped_monitor = monitor;
+        monitor->config = config;
+        monitor->window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
+        gtk_window_set_type_hint(monitor->window, GDK_WINDOW_TYPE_HINT_DESKTOP);
+        gtk_window_set_keep_below(monitor->window, TRUE);
+        gtk_window_set_resizable(monitor->window, FALSE);
+        gtk_widget_set_app_paintable(GTK_WIDGET(monitor->window), TRUE);
+        gtk_window_set_screen(monitor->window, screen);
+        gtk_widget_set_size_request(GTK_WIDGET(monitor->window), monitor->geometry.width, monitor->geometry.height);
+        gtk_window_move(monitor->window, monitor->geometry.x, monitor->geometry.y);
+        gtk_widget_show(GTK_WIDGET(monitor->window));
+        #if GTK_CHECK_VERSION(3, 0, 0)
+        monitor->window_draw_handler_id = g_signal_connect(G_OBJECT(monitor->window), "draw",
+                                                           G_CALLBACK(monitor_window_draw_cb),
+                                                           monitor);
+        #else
+        monitor->window_draw_handler_id = g_signal_connect(G_OBJECT(monitor->window), "expose-event",
+                                                           G_CALLBACK(monitor_window_expose_cb),
+                                                           monitor);
+        #endif
+
+        if(priv->lid_monitor_config && (g_strcmp0(priv->lid_monitor_config, monitor_name) == 0 ||
+                                        g_strcmp0(priv->lid_monitor_config, monitor_num_str) == 0))
+            priv->lid_monitor = monitor;
+
+        gpointer customized = GINT_TO_POINTER(priv->customized_monitors_default);
+        if(priv->customized_monitors_config)
+        {
+            if(!monitor_name || !g_hash_table_lookup_extended(priv->customized_monitors_config, monitor_name, NULL, &customized))
+                g_hash_table_lookup_extended(priv->customized_monitors_config, monitor_num_str, NULL, &customized);
+        }
+
+        if(GPOINTER_TO_INT(customized))
+            priv->customized_monitors = g_list_prepend(priv->customized_monitors, monitor);
+
+        if(!background_initialize(&monitor->background_configured, monitor->config, monitor, images_cache))
+            background_initialize(&monitor->background_configured, &DEFAULT_BACKGROUND_CONFIG, monitor, images_cache);
+        monitor_set_background(monitor, &monitor->background_configured);
+
+        if(monitor_name)
+            g_hash_table_insert(priv->monitors_map, monitor_name, monitor);
+        g_hash_table_insert(priv->monitors_map, monitor_num_str, monitor);
+    }
+    g_hash_table_unref(images_cache);
+
+    if(priv->lid_monitor && !priv->upower_proxy)
+        greeter_background_try_init_dbus(background);
+    else if(!priv->lid_monitor)
+        greeter_background_stop_dbus(background);
+
+    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);
+}
+
+void
+greeter_background_disconnect(GreeterBackground* background)
+{
+    g_return_if_fail(GREETER_IS_BACKGROUND(background));
+
+    GreeterBackgroundPrivate* priv = background->priv;
+
+    gsize i;
+    for(i = 0; i < priv->monitors_size; ++i)
+        monitor_finalize(&priv->monitors[i]);
+    g_free(priv->monitors);
+    if(priv->monitors_map)
+        g_hash_table_unref(priv->monitors_map);
+    g_list_free(priv->customized_monitors);
+    if(priv->screen_monitors_changed_handler_id)
+        g_signal_handler_disconnect(priv->screen, priv->screen_monitors_changed_handler_id);
+
+    priv->screen = NULL;
+    priv->screen_monitors_changed_handler_id = 0;
+    priv->monitors = NULL;
+    priv->monitors_size = 0;
+    priv->monitors_map = NULL;
+    priv->customized_monitors = NULL;
+    priv->active_monitor = NULL;
+    priv->lid_monitor = NULL;
+}
+
+static void
+greeter_background_set_active_monitor(GreeterBackground* background,
+                                      const Monitor* active)
+{
+    GreeterBackgroundPrivate* priv = background->priv;
+
+    if(!active)
+    {
+        /* Normal way: at least one configured active monitor is not disabled */
+        GList* iter;
+        for(iter = priv->active_monitors_config; iter && !active; iter = g_list_next(iter))
+        {
+            const Monitor* monitor = g_hash_table_lookup(priv->monitors_map, iter->data);
+            if(monitor && monitor->config->type != BACKGROUND_TYPE_SKIP &&
+               greeter_background_monitor_enabled(background, monitor))
+                active = monitor;
+        }
+
+        /* All monitors listed in active-monitor-config are disabled (or option is empty) */
+
+        /* Using primary monitor */
+        if(!active)
+        {
+            active = &priv->monitors[gdk_screen_get_primary_monitor(priv->screen)];
+            if(active->config->type == BACKGROUND_TYPE_SKIP ||
+               !greeter_background_monitor_enabled(background, active))
+                active = NULL;
+        }
+
+        /* Fallback: first enabled and/or not skipped monitor (screen always have one) */
+        if(!active)
+        {
+            gint i;
+            const Monitor* first_not_skipped = NULL;
+            for(i = 0; i < priv->monitors_size && !active; ++i)
+            {
+                const Monitor* monitor = &priv->monitors[i];
+                if(monitor->config->type == BACKGROUND_TYPE_SKIP)
+                    continue;
+                if(greeter_background_monitor_enabled(background, monitor))
+                    active = monitor;
+                if(!first_not_skipped)
+                    first_not_skipped = active;
+            }
+            if(!active)
+                active = first_not_skipped;
+        }
+    }
+
+    if(active == priv->active_monitor)
+        return;
+
+    priv->active_monitor = active;
+    g_debug("Active monitor changed: %s #%d", active->name, active->num);
+    g_signal_emit(background, background_signals[BACKGROUND_SIGNAL_ACTIVE_MONITOR_CHANGED], 0);
+
+    gint x, y;
+
+    GdkDisplay* display = gtk_widget_get_display(GTK_WIDGET(active->window));
+    #if GTK_CHECK_VERSION(3, 0, 0)
+    GdkDeviceManager* device_manager = gdk_display_get_device_manager(display);
+    GdkDevice* device = gdk_device_manager_get_client_pointer(device_manager);
+    gdk_device_get_position(device, NULL, &x, &y);
+    #else
+    gdk_display_get_pointer(display, NULL, &x, &y, NULL);
+    #endif
+
+    /* Do not center cursor if it is already inside active monitor */
+    if(x < active->geometry.x || x >= active->geometry.x + active->geometry.width ||
+       y < active->geometry.y || y >= active->geometry.y + active->geometry.height)
+    {
+        x = active->geometry.x + active->geometry.width/2;
+        y = active->geometry.y + active->geometry.height/2;
+        #if GTK_CHECK_VERSION(3, 0, 0)
+        gdk_device_warp(gdk_device_manager_get_client_pointer(device_manager), priv->screen, x, y);
+        #else
+        gdk_display_warp_pointer(display, priv->screen, x, y);
+        #endif
+    }
+
+    /* Toggle to bring active monitor background up */
+    gtk_widget_hide(GTK_WIDGET(active->window));
+    gtk_widget_show(GTK_WIDGET(active->window));
+
+    /* Update greeter windows */
+    GList* iter;
+    for(iter = priv->greeter_windows; iter; iter = g_list_next(iter))
+    {
+        gtk_window_set_screen(GTK_WINDOW(iter->data), priv->screen);
+        if(gtk_widget_get_visible(GTK_WIDGET(iter->data)))
+        {   /* Toggle window visibility to place window above of any 'background' windows */
+            gtk_widget_hide(GTK_WIDGET(iter->data));
+            gtk_widget_show(GTK_WIDGET(iter->data));
+            gtk_widget_queue_resize(GTK_WIDGET(iter->data));
+        }
+    }
+}
+
+static void
+greeter_background_try_init_dbus(GreeterBackground* background)
+{
+    g_debug("Creating DBus proxy");
+    GError* error = NULL;
+    GreeterBackgroundPrivate* priv = background->priv;
+
+    if(priv->upower_proxy)
+        greeter_background_stop_dbus(background);
+
+    priv->upower_proxy = g_dbus_proxy_new_for_bus_sync(
+                                            G_BUS_TYPE_SYSTEM,
+                                            G_DBUS_PROXY_FLAGS_NONE,
+                                            NULL,   /* interface info */
+                                            DBUS_UPOWER_NAME,
+                                            DBUS_UPOWER_PATH,
+                                            DBUS_UPOWER_INTERFACE,
+                                            NULL,   /* cancellable */
+                                            &error);
+    if(!priv->upower_proxy)
+    {
+        if(error)
+            g_warning("Failed to create dbus proxy: %s", error->message);
+        g_clear_error(&error);
+        return;
+    }
+
+    GVariant* variant = g_dbus_proxy_get_cached_property(priv->upower_proxy, DBUS_UPOWER_PROP_LID_IS_PRESENT);
+    gboolean lid_present = g_variant_get_boolean(variant);
+    g_variant_unref(variant);
+
+    g_debug("%s property value: %d", DBUS_UPOWER_PROP_LID_IS_PRESENT, lid_present);
+
+    if(!lid_present)
+        greeter_background_stop_dbus(background);
+    else
+    {
+        variant = g_dbus_proxy_get_cached_property(priv->upower_proxy, DBUS_UPOWER_PROP_LID_IS_CLOSED);
+        priv->lid_state = !g_variant_get_boolean(variant);
+        g_variant_unref(variant);
+
+        g_signal_connect(priv->upower_proxy, "g-properties-changed",
+                         G_CALLBACK(greeter_background_dbus_changed_cb), background);
+    }
+}
+
+static void
+greeter_background_stop_dbus(GreeterBackground* background)
+{
+    if(!background->priv->upower_proxy)
+        return;
+    g_object_unref(background->priv->upower_proxy);
+    background->priv->upower_proxy = NULL;
+}
+
+static gboolean
+greeter_background_monitor_enabled(GreeterBackground* background,
+                                   const Monitor* monitor)
+{
+    GreeterBackgroundPrivate* priv = background->priv;
+
+    if(priv->upower_proxy && priv->lid_monitor && priv->lid_monitor == monitor)
+        return priv->lid_state;
+    return TRUE;
+}
+
+static void
+greeter_background_dbus_changed_cb(GDBusProxy* proxy,
+                                   GVariant* changed_properties,
+                                   const gchar* const* invalidated_properties,
+                                   GreeterBackground* background)
+{
+    g_return_if_fail(GREETER_IS_BACKGROUND(background));
+    GreeterBackgroundPrivate* priv = background->priv;
+
+    GVariant* variant = g_dbus_proxy_get_cached_property(priv->upower_proxy, DBUS_UPOWER_PROP_LID_IS_CLOSED);
+    gboolean new_state = !g_variant_get_boolean(variant);
+    g_variant_unref(variant);
+
+    if(new_state == priv->lid_state)
+        return;
+
+    g_debug("lid state changed: %s", priv->lid_state ? "opened" : "closed");
+
+    priv->lid_state = new_state;
+    if(priv->lid_monitor)
+        greeter_background_set_active_monitor(background, NULL);
+}
+
+static void
+greeter_background_monitors_changed_cb(GdkScreen* screen,
+                                       GreeterBackground* background)
+{
+    g_return_if_fail(GREETER_IS_BACKGROUND(background));
+    greeter_background_connect(background, screen);
+}
+
+void greeter_background_add_subwindow(GreeterBackground* background,
+                                      GtkWindow* window)
+{
+    g_return_if_fail(GREETER_IS_BACKGROUND(background));
+    g_return_if_fail(GTK_IS_WINDOW(window));
+
+    GreeterBackgroundPrivate* priv = background->priv;
+
+    if(!g_list_find(priv->greeter_windows, window))
+    {
+        priv->greeter_windows = g_list_prepend(priv->greeter_windows, window);
+        #if GTK_CHECK_VERSION (3, 0, 0)
+        g_signal_connect(G_OBJECT(window), "draw", G_CALLBACK(monitor_subwindow_draw_cb), background);
+        #endif
+    }
+
+    if(priv->screen)
+        gtk_window_set_screen(window, priv->screen);
+}
+
+void greeter_background_remove_subwindow(GreeterBackground* background,
+                                         GtkWindow* window)
+{
+    g_return_if_fail(GREETER_IS_BACKGROUND(background));
+    g_return_if_fail(GTK_IS_WINDOW(window));
+
+    GreeterBackgroundPrivate* priv = background->priv;
+
+    GList* item = g_list_find(priv->greeter_windows, window);
+    if(item)
+    {
+        #if GTK_CHECK_VERSION (3, 0, 0)
+        g_object_disconnect(G_OBJECT(window),
+                            "any-signal", G_CALLBACK(monitor_subwindow_draw_cb), background,
+                            NULL);
+        #endif
+        priv->greeter_windows = g_list_delete_link(priv->greeter_windows, item);
+    }
+}
+
+void
+greeter_background_set_custom_background(GreeterBackground* background,
+                                         const gchar* value)
+{
+    g_return_if_fail(GREETER_IS_BACKGROUND(background));
+
+    GreeterBackgroundPrivate* priv = background->priv;
+    if(!priv->customized_monitors)
+        return;
+
+    BackgroundConfig config;
+    background_config_initialize(&config, value);
+
+    GHashTable *images_cache = NULL;
+    if(config.type == BACKGROUND_TYPE_IMAGE)
+        images_cache = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_object_unref);
+
+    GList* iter;
+    for(iter = priv->customized_monitors; iter; iter = g_list_next(iter))
+    {
+        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);
+        else
+            monitor_set_background(monitor, &monitor->background_configured);
+    }
+
+    if(images_cache)
+        g_hash_table_destroy(images_cache);
+    if(config.type != BACKGROUND_TYPE_INVALID)
+        background_config_finalize(&config);
+
+    for(iter = priv->greeter_windows; iter; iter = g_list_next(iter))
+        gtk_widget_queue_draw(GTK_WIDGET(iter->data));
+}
+
+void
+greeter_background_save_xroot(GreeterBackground* background)
+{
+    g_return_if_fail(GREETER_IS_BACKGROUND(background));
+
+    GreeterBackgroundPrivate* priv = background->priv;
+    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)
+    {
+        const Monitor* monitor = &priv->monitors[i];
+        if(monitor == priv->active_monitor)
+            continue;
+        else if(i == priv->monitors_size)
+            monitor = priv->active_monitor;
+        cairo_save(cr);
+        cairo_translate(cr, monitor->geometry.x, monitor->geometry.y);
+        monitor_draw_background(monitor, cr);
+        cairo_restore(cr);
+    }
+    set_surface_as_root(priv->screen, surface);
+
+    cairo_destroy(cr);
+    cairo_surface_destroy(surface);
+}
+
+const GdkRectangle*
+greeter_background_get_active_monitor_geometry(GreeterBackground* background)
+{
+    g_return_if_fail(GREETER_IS_BACKGROUND(background));
+    GreeterBackgroundPrivate* priv = background->priv;
+
+    return priv->active_monitor ? &priv->active_monitor->geometry : NULL;
+}
+
+static gboolean
+background_config_initialize(BackgroundConfig* config,
+                             const gchar* value)
+{
+    config->type = BACKGROUND_TYPE_INVALID;
+    if(!value || strlen(value) == 0)
+        return FALSE;
+    if(g_strcmp0(value, BACKGROUND_TYPE_SKIP_VALUE) == 0)
+        config->type = BACKGROUND_TYPE_SKIP;
+    #if GTK_CHECK_VERSION (3, 0, 0)
+    else if(gdk_rgba_parse(&config->options.color, value))
+    #else
+    else if(gdk_color_parse(value, &config->options.color))
+    #endif
+        config->type = BACKGROUND_TYPE_COLOR;
+    else
+    {
+        const gchar** prefix = SCALING_MODE_PREFIXES;
+        while(*prefix && !g_str_has_prefix(value, *prefix))
+            ++prefix;
+
+        if(*prefix)
+        {
+            config->options.image.mode = (ScalingMode)(prefix - SCALING_MODE_PREFIXES);
+            value += strlen(*prefix);
+        }
+        else
+            config->options.image.mode = SCALING_MODE_ZOOMED;
+        config->options.image.path = g_strdup(value);
+        config->type = BACKGROUND_TYPE_IMAGE;        
+    }
+    return TRUE;
+}
+
+static void
+background_config_finalize(BackgroundConfig* config)
+{
+    if(config->type == BACKGROUND_TYPE_IMAGE)
+        g_free(config->options.image.path);
+    config->type = BACKGROUND_TYPE_INVALID;
+}
+
+static void
+background_config_free(BackgroundConfig* config)
+{
+    background_config_finalize(config);
+    g_free(config);
+}
+
+static void
+background_config_copy(const BackgroundConfig* source,
+                       BackgroundConfig* dest)
+{
+    dest->type = source->type;
+    if(source->type == BACKGROUND_TYPE_IMAGE)
+        dest->options.image.path = g_strdup(source->options.image.path);
+    else if(source->type == BACKGROUND_TYPE_COLOR)
+        dest->options.color = source->options.color;
+}
+
+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 void
+background_finalize(Background* bg)
+{
+    if(bg->type == BACKGROUND_TYPE_IMAGE && bg->options.image)
+        g_object_unref(bg->options.image);
+    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));
+}
+
+static void
+monitor_finalize(Monitor* monitor)
+{
+    background_finalize(&monitor->background_configured);
+    background_finalize(&monitor->background_custom);
+    if(monitor->window_draw_handler_id)
+        g_signal_handler_disconnect(monitor->window, monitor->window_draw_handler_id);
+    if(monitor->window)
+        gtk_widget_destroy(GTK_WIDGET(monitor->window));
+    monitor->window = NULL;
+    monitor->window_draw_handler_id = 0;
+}
+
+static void
+monitor_draw_background(const Monitor* monitor,
+                        cairo_t* cr)
+{
+    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);
+        #if GTK_CHECK_VERSION (3, 0, 0)
+        gdk_cairo_set_source_rgba(cr, &monitor->background->options.color);
+        #else
+        gdk_cairo_set_source_color(cr, &monitor->background->options.color);
+        #endif
+        cairo_fill(cr);
+    }
+}
+
+#if GTK_CHECK_VERSION (3, 0, 0)
+static gboolean
+monitor_window_draw_cb(GtkWidget* widget,
+                       cairo_t* cr,
+                       const Monitor* monitor)
+#else
+static gboolean
+monitor_window_expose_cb(GtkWidget* widget,
+                         GdkEventExpose *event,
+                         const Monitor* monitor)
+#endif
+{
+    if(monitor)
+    {
+        #if GTK_CHECK_VERSION (3, 0, 0)
+        monitor_draw_background(monitor, cr);
+        #else
+        cairo_t *cr = gdk_cairo_create(gtk_widget_get_window(widget));
+        monitor_draw_background(monitor, cr);
+        cairo_destroy(cr);
+        #endif
+    }
+    return FALSE;
+}
+
+#if GTK_CHECK_VERSION (3, 0, 0)
+static gboolean
+monitor_subwindow_draw_cb(GtkWidget* widget,
+                          cairo_t* cr,
+                          GreeterBackground* background)
+{
+    g_return_val_if_fail(GREETER_IS_BACKGROUND(background), FALSE);
+    if(background->priv->active_monitor)
+    {
+        const GdkRectangle* geometry = &background->priv->active_monitor->geometry;
+        gint x = 0, y = 0;
+        gtk_window_get_position(GTK_WINDOW(widget), &x, &y);
+
+        cairo_save(cr);
+        cairo_translate(cr, geometry->x - x, geometry->y - y);
+        monitor_draw_background(background->priv->active_monitor, cr);
+        cairo_restore(cr);
+    }
+    return FALSE;
+}
+#endif
+
+static gchar*
+parse_monitor_name_from_config(const gchar** value, gint* num)
+{
+    if(*value && g_str_has_prefix(*value, "%"))
+    {
+        gchar* delim = g_strstr_len(*value, -1, ":");
+        if(delim)
+        {
+            gchar* name = g_strndup(*value + 1, delim - *value - 1);
+            *value = delim + 1;
+            return name;
+        }
+    }
+    (*num)++;
+    return g_strdup_printf("%d", *num - 1);
+}
+
+static GdkPixbuf*
+scale_image_file(const gchar* path,
+                 ScalingMode mode,
+                 gint width, gint height,
+                 GHashTable* cache)
+{
+    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))
+            return GDK_PIXBUF(g_object_ref(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_clear_error(&error);
+        }
+        if(cache)
+            g_hash_table_insert(cache, g_strdup (path), g_object_ref (pixbuf));
+    }
+
+    if(pixbuf)
+    {
+        GdkPixbuf* scaled = scale_image(pixbuf, mode, width, height);
+        if (cache)
+            g_hash_table_insert(cache, g_strdup(key), g_object_ref(scaled));
+        g_object_unref(pixbuf);
+        pixbuf = scaled;
+    }
+
+    return pixbuf;
+}
+
+static GdkPixbuf*
+scale_image(GdkPixbuf* source,
+            ScalingMode mode,
+            gint width, gint height)
+{
+    if(mode == SCALING_MODE_ZOOMED)
+    {
+        gint offset_x = 0;
+        gint offset_y = 0;
+        gint p_width = gdk_pixbuf_get_width(source);
+        gint p_height = gdk_pixbuf_get_height(source);
+        gdouble scale_x = (gdouble)width / p_width;
+        gdouble scale_y = (gdouble)height / p_height;
+
+        if(scale_x < scale_y)
+        {
+            scale_x = scale_y;
+            offset_x = (width - (p_width * scale_x)) / 2;
+        }
+        else
+        {
+            scale_y = scale_x;
+            offset_y = (height - (p_height * scale_y)) / 2;
+        }
+
+        GdkPixbuf *pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE,
+                                           gdk_pixbuf_get_bits_per_sample (source),
+                                           width, height);
+        gdk_pixbuf_composite(source, pixbuf, 0, 0, width, height,
+                             offset_x, offset_y, scale_x, scale_y, GDK_INTERP_BILINEAR, 0xFF);
+        return pixbuf;
+    }
+    else if(mode == SCALING_MODE_STRETCHED)
+    {
+        return gdk_pixbuf_scale_simple(source, width, height, GDK_INTERP_BILINEAR);
+    }
+    return GDK_PIXBUF(g_object_ref(source));
+}
+
+/* The following code for setting a RetainPermanent background pixmap was taken
+   originally from Gnome, with some fixes from MATE. see:
+   https://github.com/mate-desktop/mate-desktop/blob/master/libmate-desktop/mate-bg.c */
+static cairo_surface_t*
+create_root_surface(GdkScreen* screen)
+{
+    gint number, width, height;
+    Display *display;
+    Pixmap pixmap;
+    cairo_surface_t *surface;
+
+    number = gdk_screen_get_number (screen);
+    width = gdk_screen_get_width (screen);
+    height = gdk_screen_get_height (screen);
+
+    /* Open a new connection so with Retain Permanent so the pixmap remains when the greeter quits */
+    gdk_flush ();
+    display = XOpenDisplay (gdk_display_get_name (gdk_screen_get_display (screen)));
+    if (!display)
+    {
+        g_warning ("Failed to create root pixmap");
+        return NULL;
+    }
+
+    XSetCloseDownMode (display, RetainPermanent);
+    pixmap = XCreatePixmap (display, RootWindow (display, number), width, height, DefaultDepth (display, number));
+    XCloseDisplay (display);
+
+    /* Convert into a Cairo surface */
+    surface = cairo_xlib_surface_create (GDK_SCREEN_XDISPLAY (screen),
+                                         pixmap,
+                                         GDK_VISUAL_XVISUAL (gdk_screen_get_system_visual (screen)),
+                                         width, height);
+
+    return surface;
+}
+
+/* Sets the "ESETROOT_PMAP_ID" property to later be used to free the pixmap */
+static void
+set_root_pixmap_id(GdkScreen* screen,
+                   Display* display,
+                   Pixmap xpixmap)
+{
+    
+    Window xroot = RootWindow (display, gdk_screen_get_number (screen));
+    char atom_name_xrootmap[] = "_XROOTPMAP_ID";
+    char atom_name_esetroot[] = "ESETROOT_PMAP_ID";
+    char *atom_names[] = {atom_name_xrootmap, atom_name_esetroot};
+    Atom atoms[G_N_ELEMENTS(atom_names)] = {0};
+
+    Atom type;
+    int format;
+    unsigned long nitems, after;
+    unsigned char *data_root, *data_esetroot;
+
+    /* Get atoms for both properties in an array, only if they exist.
+     * This method is to avoid multiple round-trips to Xserver
+     */
+    if (XInternAtoms (display, atom_names, G_N_ELEMENTS(atom_names), True, atoms) &&
+        atoms[0] != None && atoms[1] != None)
+    {
+
+        XGetWindowProperty (display, xroot, atoms[0], 0L, 1L, False, AnyPropertyType,
+                            &type, &format, &nitems, &after, &data_root);
+        if (data_root && type == XA_PIXMAP && format == 32 && nitems == 1)
+        {
+            XGetWindowProperty (display, xroot, atoms[1], 0L, 1L, False, AnyPropertyType,
+                                &type, &format, &nitems, &after, &data_esetroot);
+            if (data_esetroot && type == XA_PIXMAP && format == 32 && nitems == 1)
+            {
+                Pixmap xrootpmap = *((Pixmap *) data_root);
+                Pixmap esetrootpmap = *((Pixmap *) data_esetroot);
+                XFree (data_root);
+                XFree (data_esetroot);
+
+                gdk_error_trap_push ();
+                if (xrootpmap && xrootpmap == esetrootpmap) {
+                    XKillClient (display, xrootpmap);
+                }
+                if (esetrootpmap && esetrootpmap != xrootpmap) {
+                    XKillClient (display, esetrootpmap);
+                }
+
+                XSync (display, False);
+#if GTK_CHECK_VERSION (3, 0, 0)
+                gdk_error_trap_pop_ignored ();
+#else
+                gdk_error_trap_pop ();
+#endif
+            }
+        }
+    }
+
+    /* Get atoms for both properties in an array, create them if needed.
+     * This method is to avoid multiple round-trips to Xserver
+     */
+    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");
+        return;
+    }
+
+    /* Set new _XROOTMAP_ID and ESETROOT_PMAP_ID properties */
+    XChangeProperty (display, xroot, atoms[0], XA_PIXMAP, 32,
+                     PropModeReplace, (unsigned char *) &xpixmap, 1);
+
+    XChangeProperty (display, xroot, atoms[1], XA_PIXMAP, 32,
+                     PropModeReplace, (unsigned char *) &xpixmap, 1);
+}
+
+/**
+* set_surface_as_root:
+* @screen: the #GdkScreen to change root background on
+* @surface: the #cairo_surface_t to set root background from.
+* Must be an xlib surface backing a pixmap.
+*
+* Set the root pixmap, and properties pointing to it. We
+* do this atomically with a server grab to make sure that
+* we won't leak the pixmap if somebody else it setting
+* it at the same time. (This assumes that they follow the
+* same conventions we do). @surface should come from a call
+* to create_root_surface().
+**/
+static void
+set_surface_as_root(GdkScreen* screen,
+                    cairo_surface_t* surface)
+{
+    g_return_if_fail(cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_XLIB);
+
+    /* Desktop background pixmap should be created from dummy X client since most
+     * applications will try to kill it with XKillClient later when changing pixmap
+     */
+    Display *display = GDK_DISPLAY_XDISPLAY (gdk_screen_get_display (screen));
+    Pixmap pixmap_id = cairo_xlib_surface_get_drawable (surface);
+    Window xroot = RootWindow (display, gdk_screen_get_number(screen));
+
+    XGrabServer (display);
+
+    XSetWindowBackgroundPixmap (display, xroot, pixmap_id);
+    set_root_pixmap_id (screen, display, pixmap_id);
+    XClearWindow (display, xroot);
+
+    XFlush (display);
+    XUngrabServer (display);
+}

=== added file 'src/greeterbackground.h'
--- src/greeterbackground.h	1970-01-01 00:00:00 +0000
+++ src/greeterbackground.h	2014-06-30 14:34:14 +0000
@@ -0,0 +1,52 @@
+#ifndef GREETER_BACKGROUND_H
+#define GREETER_BACKGROUND_H
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GREETER_BACKGROUND_TYPE             (greeter_background_get_type())
+#define GREETER_BACKGROUND(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), GREETER_BACKGROUND_TYPE, GreeterBackground))
+#define GREETER_BACKGROUND_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), GREETER_BACKGROUND_TYPE, GreeterBackgroundClass))
+#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))
+
+typedef struct _GreeterBackground           GreeterBackground;
+typedef struct _GreeterBackgroundClass      GreeterBackgroundClass;
+typedef struct _GreeterBackgroundPrivate    GreeterBackgroundPrivate;
+
+struct _GreeterBackground
+{
+	GObject parent_instance;
+	struct _GreeterBackgroundPrivate* priv;
+};
+
+struct _GreeterBackgroundClass
+{
+	GObjectClass parent_class;
+};
+
+GType greeter_background_get_type(void) G_GNUC_CONST;
+
+GreeterBackground* greeter_background_new           (void);
+void greeter_background_set_background_config       (GreeterBackground* background,
+                                                     const gchar* value);
+void greeter_background_set_custom_background_config(GreeterBackground* background,
+                                                     const gchar* value);
+void greeter_background_set_active_monitor_config   (GreeterBackground* background,
+                                                     const gchar* value);
+void greeter_background_connect                     (GreeterBackground* background,
+                                                     GdkScreen* screen);
+void greeter_background_add_subwindow               (GreeterBackground* background,
+                                                     GtkWindow* window);
+void greeter_background_remove_subwindow            (GreeterBackground* background,
+                                                     GtkWindow* window);
+void greeter_background_set_custom_background       (GreeterBackground* background,
+                                                     const gchar* path);
+void greeter_background_save_xroot                  (GreeterBackground* background);
+const GdkRectangle* greeter_background_get_active_monitor_geometry(GreeterBackground* background);
+
+G_END_DECLS
+
+#endif // GREETER_BACKGROUND_H

=== modified file 'src/lightdm-gtk-greeter.c'
--- src/lightdm-gtk-greeter.c	2014-06-09 15:49:08 +0000
+++ src/lightdm-gtk-greeter.c	2014-06-30 14:34:14 +0000
@@ -20,15 +20,11 @@
 #include <glib-unix.h>
 
 #include <locale.h>
+#include <gdk/gdkx.h>
 #include <gtk/gtk.h>
 #include <glib/gi18n.h>
-#include <cairo-xlib.h>
 #include <sys/mman.h>
 #include <sys/wait.h>
-#include <X11/Xlib.h>
-#include <X11/Xatom.h>
-#include <gdk-pixbuf/gdk-pixbuf.h>
-#include <gdk/gdkx.h>
 #include <glib.h>
 #if GTK_CHECK_VERSION (3, 0, 0)
 #include <gtk/gtkx.h>
@@ -56,6 +52,7 @@
 #include <lightdm.h>
 
 #include "src/greetermenubar.h"
+#include "src/greeterbackground.h"
 #include "src/lightdm-gtk-greeter-ui.h"
 
 #if GTK_CHECK_VERSION (3, 0, 0)
@@ -69,8 +66,6 @@
 
 /* Defaults */
 static gchar *default_font_name, *default_theme_name, *default_icon_theme_name;
-static GdkPixbuf *default_background_pixbuf = NULL;
-static GdkPixbuf *background_pixbuf = NULL;
 
 /* Panel Widgets */
 static GtkWindow *panel_window;
@@ -99,8 +94,6 @@
 /* Pending Questions */
 static GSList *pending_questions = NULL;
 
-GSList *backgrounds = NULL;
-
 /* Current choices */
 static gchar *current_session;
 static gchar *current_language;
@@ -108,11 +101,8 @@
 /* Screensaver values */
 int timeout, interval, prefer_blanking, allow_exposures;
 
-#if GTK_CHECK_VERSION (3, 0, 0)
-static GdkRGBA *default_background_color = NULL;
-#else
-static GdkColor *default_background_color = NULL;
-#endif
+static GreeterBackground *greeter_background;
+
 static gboolean cancelling = FALSE, prompted = FALSE;
 static gboolean prompt_active = FALSE, password_prompted = FALSE;
 #if GTK_CHECK_VERSION (3, 0, 0)
@@ -146,14 +136,14 @@
     DimensionPosition x, y;
 } WindowPosition;
 
-static const WindowPosition CENTERED_WINDOW_POS = { .x = {50, +1, TRUE, 0}, .y = {50, +1, TRUE, 0} };
+static const WindowPosition WINDOW_POS_CENTER = {.x = {50, +1, TRUE, 0}, .y = {50, +1, TRUE, 0}};
+static const WindowPosition WINDOW_POS_TOP_LEFT = {.x = {0, +1, FALSE, -1}, .y = {0, +1, FALSE, -1}};
 static WindowPosition main_window_pos;
+static WindowPosition panel_window_pos;
 
 static GdkPixbuf* default_user_pixbuf = NULL;
 static gchar* default_user_icon = "avatar-default";
 
-static gboolean use_user_background = TRUE;
-
 static const gchar *LAYOUT_DATA_LABEL = "layout-label";
 #ifdef HAVE_LIBXKLAVIER
 static const gchar *LAYOUT_DATA_GROUP = "layout-group";
@@ -442,8 +432,9 @@
         g_closure_unref (closure);
     }
 
-    gtk_container_foreach (GTK_CONTAINER (gtk_menu_item_get_submenu (GTK_MENU_ITEM (item))),
-                           (GtkCallback)reassign_menu_item_accel, NULL);
+    GtkWidget* submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (item));
+    if(submenu)
+        gtk_container_foreach (GTK_CONTAINER (submenu), (GtkCallback)reassign_menu_item_accel, NULL);
 }
 
 static void
@@ -817,34 +808,17 @@
     gtk_widget_set_sensitive (GTK_WIDGET (language_menuitem), !logged_in);
 }
 
-static void set_background (GdkPixbuf *new_bg);
-
 static void
 set_user_background (const gchar *username)
 {
-    LightDMUser *user;
-    const gchar *path;
-    GdkPixbuf *bg = NULL;
-    GError *error = NULL;
-
-    user = lightdm_user_list_get_user_by_name (lightdm_user_list_get_instance (), username);
-    if (user)
+    const gchar *path = NULL;
+    if (username)
     {
-        path = lightdm_user_get_background (user);
-        if (path)
-        {
-            bg = gdk_pixbuf_new_from_file (path, &error);
-            if (!bg)
-            {
-                g_warning ("Failed to load user background: %s", error->message);
-                g_clear_error (&error);
-            }
-        }
+        LightDMUser *user = lightdm_user_list_get_user_by_name (lightdm_user_list_get_instance (), username);
+        if (user)
+            path = lightdm_user_get_background (user);
     }
-
-    set_background (bg);
-    if (bg)
-        g_object_unref (bg);
+    greeter_background_set_custom_background(greeter_background, path);
 }
 
 static void
@@ -903,58 +877,39 @@
 }
 
 static void
-center_window (GtkWindow *window, GtkAllocation *unused, const WindowPosition *pos)
+center_window (GtkWindow *window, GtkAllocation *allocation, const WindowPosition *pos)
 {   
-    GdkScreen *screen = gtk_window_get_screen (window);
-    GtkAllocation allocation;
-    GdkRectangle monitor_geometry;
-
-    gdk_screen_get_monitor_geometry (screen, gdk_screen_get_primary_monitor (screen), &monitor_geometry);
-    gtk_widget_get_allocation (GTK_WIDGET (window), &allocation);
-    gtk_window_move (window,
-                     monitor_geometry.x + get_absolute_position (&pos->x, monitor_geometry.width, allocation.width),
-                     monitor_geometry.y + get_absolute_position (&pos->y, monitor_geometry.height, allocation.height));
-}
-
-#if GTK_CHECK_VERSION (3, 0, 0)
-/* Use the much simpler fake transparency by drawing the window background with Cairo for Gtk3 */
-static gboolean
-background_window_draw (GtkWidget *widget, cairo_t *cr, gpointer user_data)
-{
-    if (background_pixbuf)
-        gdk_cairo_set_source_pixbuf (cr, background_pixbuf, 0, 0);
-    else
-        gdk_cairo_set_source_rgba (cr, default_background_color);
-    cairo_paint (cr);
-    return FALSE;
-}
-
-static gboolean
-login_window_draw (GtkWidget *widget, cairo_t *cr, gpointer user_data)
-{
-    GdkScreen *screen = gtk_window_get_screen (GTK_WINDOW(widget));
-    GtkAllocation *allocation = g_new0 (GtkAllocation, 1);
-    GdkRectangle monitor_geometry;
-    gint x,y;
-
-    if (background_pixbuf)
-    {
-        gdk_screen_get_monitor_geometry (screen, gdk_screen_get_primary_monitor (screen), &monitor_geometry);
-        gtk_widget_get_allocation (widget, allocation);
-        x = get_absolute_position (&main_window_pos.x, monitor_geometry.width, allocation->width);
-        y = get_absolute_position (&main_window_pos.y, monitor_geometry.height, allocation->height);
-        gdk_cairo_set_source_pixbuf (cr, background_pixbuf, monitor_geometry.x - x, monitor_geometry.y - y);
-    }
-    else
-        gdk_cairo_set_source_rgba (cr, default_background_color);
-
-    cairo_paint (cr);
-
-    g_free (allocation);
-    return FALSE;
-}
-
-#else
+    const GdkRectangle *monitor_geometry = greeter_background_get_active_monitor_geometry (greeter_background);
+    GtkAllocation *new_allocation = NULL;
+    if (!allocation)
+    {
+        allocation = new_allocation = g_new0 (GtkAllocation, 1);
+        gtk_widget_get_allocation (GTK_WIDGET (window), new_allocation);
+    }
+    if (monitor_geometry)
+        gtk_window_move (window,
+                         monitor_geometry->x + get_absolute_position (&pos->x, monitor_geometry->width, allocation->width),
+                         monitor_geometry->y + get_absolute_position (&pos->y, monitor_geometry->height, allocation->height));
+    g_free (new_allocation);
+}
+
+static void
+active_monitor_changed_cb(GreeterBackground* background, gpointer user_data)
+{
+    const GdkRectangle *monitor_geometry = greeter_background_get_active_monitor_geometry (greeter_background);
+    if (monitor_geometry)
+    {
+        GdkGeometry hints;
+        hints.min_width = monitor_geometry->width;
+        hints.max_width = monitor_geometry->width;
+        hints.min_height = -1;
+        hints.max_height = -1;
+        gtk_window_set_geometry_hints (panel_window, GTK_WIDGET(panel_window),
+                                       &hints, GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE);
+    }
+}
+
+#if !GTK_CHECK_VERSION (3, 0, 0)
 static GdkRegion *
 cairo_region_from_rectangle (gint width, gint height, gint radius)
 {
@@ -1021,20 +976,6 @@
 
     return TRUE;
 }
-
-static gboolean
-background_window_expose (GtkWidget    *widget,
-                                       GdkEventExpose *event,
-                                       gpointer user_data)
-{
-    cairo_t *cr = gdk_cairo_create (gtk_widget_get_window (widget));
-    if (background_pixbuf)
-        gdk_cairo_set_source_pixbuf (cr, background_pixbuf, 0, 0);
-    else
-        gdk_cairo_set_source_color (cr, default_background_color);
-    cairo_paint (cr);
-    return FALSE;
-}
 #endif
 
 static void
@@ -1164,6 +1105,8 @@
     /* Remember last choice */
     g_key_file_set_value (state, "greeter", "last-session", session);
 
+    greeter_background_save_xroot (greeter_background);
+
     data = g_key_file_to_data (state, &data_length, &error);
     if (error)
         g_warning ("Failed to save state file: %s", error->message);
@@ -1370,8 +1313,7 @@
     }
 
     set_login_button_label (greeter, username);
-    if (use_user_background)
-        set_user_background (username);
+    set_user_background (username);
     set_user_image (username);
     user = lightdm_user_list_get_user_by_name (lightdm_user_list_get_instance (), username);
     if (user)
@@ -1655,22 +1597,22 @@
     gtk_style_context_add_class(GTK_STYLE_CONTEXT(gtk_widget_get_style_context(GTK_WIDGET(dialog))), "lightdm-gtk-greeter");
 #endif
     gtk_widget_set_name(dialog, dialog_name);
-#if GTK_CHECK_VERSION (3, 0, 0)
-    g_signal_connect (G_OBJECT (dialog), "draw", G_CALLBACK (login_window_draw), NULL);
-#else
+    gtk_container_set_border_width(GTK_CONTAINER (dialog), 18);
+#if !GTK_CHECK_VERSION (3, 0, 0)
     g_signal_connect (G_OBJECT (dialog), "size-allocate", G_CALLBACK (login_window_size_allocate), NULL);
 #endif
-    gtk_container_set_border_width(GTK_CONTAINER (dialog), 18);
-
+    greeter_background_add_subwindow(greeter_background, GTK_WINDOW (dialog));
     /* Hide the login window and show the dialog */
     gtk_widget_hide (GTK_WIDGET (login_window));
     gtk_widget_show_all (dialog);
-    center_window (GTK_WINDOW (dialog), NULL, &CENTERED_WINDOW_POS);
+    center_window (GTK_WINDOW (dialog), NULL, &WINDOW_POS_CENTER);
 
     result = gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK;
 
+    greeter_background_remove_subwindow(greeter_background, GTK_WINDOW (dialog));
     gtk_widget_destroy (dialog);
     gtk_widget_show (GTK_WIDGET (login_window));
+    gtk_widget_queue_resize(GTK_WIDGET (login_window));
 
     return result;
 }
@@ -2001,253 +1943,6 @@
     g_free (last_user);
 }
 
-/* The following code for setting a RetainPermanent background pixmap was taken
-   originally from Gnome, with some fixes from MATE. see:
-   https://github.com/mate-desktop/mate-desktop/blob/master/libmate-desktop/mate-bg.c */
-static cairo_surface_t *
-create_root_surface (GdkScreen *screen)
-{
-    gint number, width, height;
-    Display *display;
-    Pixmap pixmap;
-    cairo_surface_t *surface;
-
-    number = gdk_screen_get_number (screen);
-    width = gdk_screen_get_width (screen);
-    height = gdk_screen_get_height (screen);
-
-    /* Open a new connection so with Retain Permanent so the pixmap remains when the greeter quits */
-    gdk_flush ();
-    display = XOpenDisplay (gdk_display_get_name (gdk_screen_get_display (screen)));
-    if (!display)
-    {
-        g_warning ("Failed to create root pixmap");
-        return NULL;
-    }
-
-    XSetCloseDownMode (display, RetainPermanent);
-    pixmap = XCreatePixmap (display, RootWindow (display, number), width, height, DefaultDepth (display, number));
-    XCloseDisplay (display);
-
-    /* Convert into a Cairo surface */
-    surface = cairo_xlib_surface_create (GDK_SCREEN_XDISPLAY (screen),
-                                         pixmap,
-                                         GDK_VISUAL_XVISUAL (gdk_screen_get_system_visual (screen)),
-                                         width, height);
-
-    return surface;
-}
-
-/* Sets the "ESETROOT_PMAP_ID" property to later be used to free the pixmap,
-*/
-static void
-set_root_pixmap_id (GdkScreen *screen,
-                         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};
-
-    Atom type;
-    int format;
-    unsigned long nitems, after;
-    unsigned char *data_root, *data_esetroot;
-
-    /* Get atoms for both properties in an array, only if they exist.
-     * This method is to avoid multiple round-trips to Xserver
-     */
-    if (XInternAtoms (display, atom_names, G_N_ELEMENTS(atom_names), True, atoms) &&
-        atoms[0] != None && atoms[1] != None)
-    {
-
-        XGetWindowProperty (display, xroot, atoms[0], 0L, 1L, False, AnyPropertyType,
-                            &type, &format, &nitems, &after, &data_root);
-        if (data_root && type == XA_PIXMAP && format == 32 && nitems == 1)
-        {
-            XGetWindowProperty (display, xroot, atoms[1], 0L, 1L, False, AnyPropertyType,
-                                &type, &format, &nitems, &after, &data_esetroot);
-            if (data_esetroot && type == XA_PIXMAP && format == 32 && nitems == 1)
-            {
-                Pixmap xrootpmap = *((Pixmap *) data_root);
-                Pixmap esetrootpmap = *((Pixmap *) data_esetroot);
-                XFree (data_root);
-                XFree (data_esetroot);
-
-                gdk_error_trap_push ();
-                if (xrootpmap && xrootpmap == esetrootpmap) {
-                    XKillClient (display, xrootpmap);
-                }
-                if (esetrootpmap && esetrootpmap != xrootpmap) {
-                    XKillClient (display, esetrootpmap);
-                }
-
-                XSync (display, False);
-#if GTK_CHECK_VERSION (3, 0, 0)
-                gdk_error_trap_pop_ignored ();
-#else
-                gdk_error_trap_pop ();
-#endif
-
-            }
-        }
-    }
-
-    /* Get atoms for both properties in an array, create them if needed.
-     * This method is to avoid multiple round-trips to Xserver
-     */
-    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");
-        return;
-    }
-
-    /* Set new _XROOTMAP_ID and ESETROOT_PMAP_ID properties */
-    XChangeProperty (display, xroot, atoms[0], XA_PIXMAP, 32,
-                     PropModeReplace, (unsigned char *) &xpixmap, 1);
-
-    XChangeProperty (display, xroot, atoms[1], XA_PIXMAP, 32,
-                     PropModeReplace, (unsigned char *) &xpixmap, 1);
-}
-
-/**
-* set_surface_as_root:
-* @screen: the #GdkScreen to change root background on
-* @surface: the #cairo_surface_t to set root background from.
-* Must be an xlib surface backing a pixmap.
-*
-* Set the root pixmap, and properties pointing to it. We
-* do this atomically with a server grab to make sure that
-* we won't leak the pixmap if somebody else it setting
-* it at the same time. (This assumes that they follow the
-* same conventions we do). @surface should come from a call
-* to create_root_surface().
-**/
-static void
-set_surface_as_root (GdkScreen *screen, cairo_surface_t *surface)
-{
-    g_return_if_fail (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_XLIB);
-
-    /* Desktop background pixmap should be created from dummy X client since most
-     * applications will try to kill it with XKillClient later when changing pixmap
-     */
-    Display *display = GDK_DISPLAY_XDISPLAY (gdk_screen_get_display (screen));
-    Pixmap pixmap_id = cairo_xlib_surface_get_drawable (surface);
-    Window xroot = RootWindow (display, gdk_screen_get_number (screen));
-
-    XGrabServer (display);
-
-    XSetWindowBackgroundPixmap (display, xroot, pixmap_id);
-    set_root_pixmap_id (screen, display, pixmap_id);
-    XClearWindow (display, xroot);
-
-    XFlush (display);
-    XUngrabServer (display);
-}
-
-static void
-set_background (GdkPixbuf *new_bg)
-{
-    GdkRectangle monitor_geometry;
-    GdkPixbuf *bg = NULL, *p = NULL;
-    GSList *iter;
-    gint i, p_height, p_width, offset_x, offset_y;
-    gdouble scale_x, scale_y, scale;
-    GdkInterpType interp_type;
-    gint num_screens = 1;
-
-    if (new_bg)
-        bg = new_bg;
-    else
-        bg = default_background_pixbuf;
-
-    #if GDK_VERSION_CUR_STABLE < G_ENCODE_VERSION(3, 10)
-        num_screens = gdk_display_get_n_screens (gdk_display_get_default ());
-    #endif
-
-    /* Set the background */
-    for (i = 0; i < num_screens; i++)
-    {
-        GdkScreen *screen;
-        cairo_surface_t *surface;
-        cairo_t *c;
-        gint monitor;
-
-        screen = gdk_display_get_screen (gdk_display_get_default (), i);
-        surface = create_root_surface (screen);
-        c = cairo_create (surface);
-
-        for (monitor = 0; monitor < gdk_screen_get_n_monitors (screen); monitor++)
-        {
-            gdk_screen_get_monitor_geometry (screen, monitor, &monitor_geometry);
-
-            if (bg)
-            {
-                p_width = gdk_pixbuf_get_width (bg);
-                p_height = gdk_pixbuf_get_height (bg);
-
-                scale_x = (gdouble)monitor_geometry.width / p_width;
-                scale_y = (gdouble)monitor_geometry.height / p_height;
-
-                if (scale_x < scale_y)
-                {
-                    scale = scale_y;
-                    offset_x = (monitor_geometry.width - (p_width * scale)) / 2;
-                    offset_y = 0;
-                }
-                else
-                {
-                    scale = scale_x;
-                    offset_x = 0;
-                    offset_y = (monitor_geometry.height - (p_height * scale)) / 2;
-                }
-
-                p = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, gdk_pixbuf_get_bits_per_sample (bg),
-                                    monitor_geometry.width, monitor_geometry.height);
-
-                /* Set interpolation type */
-                if (monitor_geometry.width == p_width && monitor_geometry.height == p_height)
-                    interp_type = GDK_INTERP_NEAREST;
-                else
-                    interp_type = GDK_INTERP_BILINEAR;
-
-                /* Zoom the background pixbuf to fit the screen */
-                gdk_pixbuf_composite (bg, p, 0, 0, monitor_geometry.width, monitor_geometry.height,
-                                      offset_x, offset_y, scale, scale, interp_type, 255);
-
-                gdk_cairo_set_source_pixbuf (c, p, monitor_geometry.x, monitor_geometry.y);
-
-                /* Make the background pixbuf globally accessible so it can be reused for fake transparency */
-                if (background_pixbuf)
-                    g_object_unref (background_pixbuf);
-
-                background_pixbuf = p;
-            }
-            else
-            {
-#if GTK_CHECK_VERSION (3, 0, 0)
-                gdk_cairo_set_source_rgba (c, default_background_color);
-#else
-                gdk_cairo_set_source_color (c, default_background_color);
-#endif
-                background_pixbuf = NULL;
-            }
-            cairo_paint (c);
-            iter = g_slist_nth (backgrounds, monitor);
-            gtk_widget_queue_draw (GTK_WIDGET (iter->data));
-        }
-
-        cairo_destroy (c);
-
-        /* Refresh background */
-        gdk_flush ();
-        set_surface_as_root (screen, surface);
-        cairo_surface_destroy (surface);
-    }
-    gtk_widget_queue_draw (GTK_WIDGET (login_window));
-    gtk_widget_queue_draw (GTK_WIDGET (panel_window));
-}
-
 static gboolean
 clock_timeout_thread (void)
 {
@@ -2536,20 +2231,11 @@
     GtkWidget *image, *infobar_compat, *content_area;
     gchar *value, *state_dir;
 #if GTK_CHECK_VERSION (3, 0, 0)
-    GdkRGBA background_color;
     GtkIconTheme *icon_theme;
     GtkCssProvider *css_provider;
-#else
-    GdkColor background_color;
 #endif
     GError *error = NULL;
 
-    /* Background windows */
-    gint monitor, scr;
-    gint numScreens = 1;
-    GdkScreen *screen;
-    GtkWidget *window;
-
     Display* display;
 
     #ifdef START_INDICATOR_SERVICES
@@ -2611,47 +2297,6 @@
     /* Set default cursor */
     gdk_window_set_cursor (gdk_get_default_root_window (), gdk_cursor_new (GDK_LEFT_PTR));
 
-    /* Load background */
-    value = g_key_file_get_value (config, "greeter", "background", NULL);
-    if (!value)
-        value = g_strdup ("#000000");
-#if GTK_CHECK_VERSION (3, 0, 0)
-    if (!gdk_rgba_parse (&background_color, value))
-#else
-    if (!gdk_color_parse (value, &background_color))
-#endif
-    {
-        gchar *path;
-        GError *error = NULL;
-
-        if (g_path_is_absolute (value))
-            path = g_strdup (value);
-        else
-            path = g_build_filename (GREETER_DATA_DIR, value, NULL);
-
-        g_debug ("Loading background %s", path);
-        default_background_pixbuf = gdk_pixbuf_new_from_file (path, &error);
-        if (!default_background_pixbuf)
-            g_warning ("Failed to load background: %s", error->message);
-        g_clear_error (&error);
-        g_free (path);
-    }
-    else
-    {
-        g_debug ("Using background color %s", value);
-#if GTK_CHECK_VERSION (3, 0, 0)
-        default_background_color = gdk_rgba_copy (&background_color);
-#else
-        default_background_color = gdk_color_copy (&background_color);
-#endif
-    }
-    g_free (value);
-
-    use_user_background = g_key_file_get_boolean(config, "greeter", "user-background", &error);
-    if (error)
-        use_user_background = TRUE;
-    g_clear_error(&error);
-
     /* Make the greeter behave a bit more like a screensaver if used as un/lock-screen by blanking the screen */
     gchar* end_ptr = NULL;
     int screensaver_timeout = 60;
@@ -2739,7 +2384,6 @@
 #if GTK_CHECK_VERSION (3, 0, 0)
     gtk_style_context_add_class(GTK_STYLE_CONTEXT(gtk_widget_get_style_context(GTK_WIDGET(panel_window))), "lightdm-gtk-greeter");
     gtk_style_context_add_class(GTK_STYLE_CONTEXT(gtk_widget_get_style_context(GTK_WIDGET(panel_window))), GTK_STYLE_CLASS_MENUBAR);
-    g_signal_connect (G_OBJECT (panel_window), "draw", G_CALLBACK (background_window_draw), NULL);
 #endif
     menubar = GTK_WIDGET (gtk_builder_get_object (builder, "menubar"));
 
@@ -2750,6 +2394,10 @@
     username_entry = GTK_ENTRY (gtk_builder_get_object (builder, "username_entry"));
     password_entry = GTK_ENTRY (gtk_builder_get_object (builder, "password_entry"));
 
+#if !GTK_CHECK_VERSION (3, 0, 0)
+    g_signal_connect (G_OBJECT (login_window), "size-allocate", G_CALLBACK (login_window_size_allocate), NULL);
+#endif
+
     /* Add InfoBar via code for GTK+2 compatability */
     infobar_compat = GTK_WIDGET(gtk_builder_get_object(builder, "infobar_compat"));
     info_bar = GTK_INFO_BAR (gtk_info_bar_new());
@@ -2768,12 +2416,6 @@
     cancel_button = GTK_BUTTON (gtk_builder_get_object (builder, "cancel_button"));
     login_button = GTK_BUTTON (gtk_builder_get_object (builder, "login_button"));
 
-#if GTK_CHECK_VERSION (3, 0, 0)
-    g_signal_connect (G_OBJECT (login_window), "draw", G_CALLBACK (login_window_draw), NULL);
-#else
-    g_signal_connect (G_OBJECT (login_window), "size-allocate", G_CALLBACK (login_window_size_allocate), NULL);
-#endif
-
     /* To maintain compatability with GTK+2, set special properties here */
 #if GTK_CHECK_VERSION (3, 0, 0)
     gtk_style_context_add_class(GTK_STYLE_CONTEXT(gtk_widget_get_style_context(GTK_WIDGET(login_window))), "lightdm-gtk-greeter");
@@ -2853,7 +2495,7 @@
         {
             LightDMSession *session = item->data;
             GtkWidget *radiomenuitem;
-            
+
             radiomenuitem = gtk_radio_menu_item_new_with_label (sessions, lightdm_session_get_name (session));
             g_object_set_data (G_OBJECT (radiomenuitem), "session-key", (gpointer) lightdm_session_get_key (session));
             sessions = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (radiomenuitem));
@@ -2986,6 +2628,7 @@
 
     #if GTK_CHECK_VERSION (3, 0, 0)
     /* A bit of CSS */
+    GdkRGBA lightdm_gtk_greeter_override_defaults;
     css_provider = gtk_css_provider_new ();
     gtk_css_provider_load_from_data (css_provider, lightdm_gtk_greeter_css_application, lightdm_gtk_greeter_css_application_length, NULL);
     gtk_style_context_add_provider_for_screen(gdk_screen_get_default (), GTK_STYLE_PROVIDER (css_provider),
@@ -2994,77 +2637,53 @@
     guint fallback_css_priority = GTK_STYLE_PROVIDER_PRIORITY_APPLICATION;
     if (gtk_style_context_lookup_color (gtk_widget_get_style_context (GTK_WIDGET (login_window)),
                                         "lightdm-gtk-greeter-override-defaults",
-                                        &background_color))
+                                        &lightdm_gtk_greeter_override_defaults))
         fallback_css_priority = GTK_STYLE_PROVIDER_PRIORITY_FALLBACK;
     gtk_css_provider_load_from_data (css_provider, lightdm_gtk_greeter_css_fallback, lightdm_gtk_greeter_css_fallback_length, NULL);
     gtk_style_context_add_provider_for_screen(gdk_screen_get_default (), GTK_STYLE_PROVIDER (css_provider),
                                               fallback_css_priority);
     #endif
 
+    /* Background */
+    greeter_background = greeter_background_new ();
+    g_signal_connect (G_OBJECT (greeter_background), "active-monitor-changed", G_CALLBACK(active_monitor_changed_cb), NULL);
+
+    value = g_key_file_get_value (config, "greeter", "background", NULL);
+    greeter_background_set_background_config (greeter_background, value);
+    g_free (value);
+
+    value = g_key_file_get_value (config, "greeter", "user-background", NULL);
+    greeter_background_set_custom_background_config (greeter_background, value);
+    g_free (value);
+
+    value = g_key_file_get_value (config, "greeter", "active-monitor", NULL);
+    greeter_background_set_active_monitor_config (greeter_background, value);
+    g_free (value);
+
+    greeter_background_add_subwindow (greeter_background, login_window);
+    greeter_background_add_subwindow (greeter_background, panel_window);
+    greeter_background_connect (greeter_background, gdk_screen_get_default ());
+
     /* Users combobox */
     renderer = gtk_cell_renderer_text_new();
     gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (user_combo), renderer, TRUE);
     gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (user_combo), renderer, "text", 1);
     gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (user_combo), renderer, "weight", 2);
 
-    #if GDK_VERSION_CUR_STABLE < G_ENCODE_VERSION(3, 10)
-        numScreens = gdk_display_get_n_screens (gdk_display_get_default());
-    #endif
-
-    /* Set up the background images */	
-    for (scr = 0; scr < numScreens; scr++)
-    {
-        screen = gdk_display_get_screen (gdk_display_get_default (), scr);
-        for (monitor = 0; monitor < gdk_screen_get_n_monitors (screen); monitor++)
-        {
-            gdk_screen_get_monitor_geometry (screen, monitor, &monitor_geometry);
-        
-            window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
-            gtk_window_set_type_hint(GTK_WINDOW(window), GDK_WINDOW_TYPE_HINT_DESKTOP);
-#if GTK_CHECK_VERSION (3, 0, 0)
-            gtk_widget_override_background_color(GTK_WIDGET(window), GTK_STATE_FLAG_NORMAL, &background_color);
-#else
-            gtk_widget_modify_bg(GTK_WIDGET(window), GTK_STATE_NORMAL, &background_color);
-#endif
-            gtk_window_set_screen(GTK_WINDOW(window), screen);
-            gtk_window_set_keep_below(GTK_WINDOW(window), TRUE);
-            gtk_widget_set_size_request(window, monitor_geometry.width, monitor_geometry.height);
-            gtk_window_set_resizable (GTK_WINDOW(window), FALSE);
-            gtk_widget_set_app_paintable (GTK_WIDGET(window), TRUE);
-            gtk_window_move (GTK_WINDOW(window), monitor_geometry.x, monitor_geometry.y);
-
-            backgrounds = g_slist_prepend(backgrounds, window);
-            gtk_widget_show (window);
-#if GTK_CHECK_VERSION (3, 0, 0)
-            g_signal_connect (G_OBJECT (window), "draw", G_CALLBACK (background_window_draw), NULL);
-#else
-            g_signal_connect (G_OBJECT (window), "expose-event", G_CALLBACK (background_window_expose), NULL);
-#endif
-            gtk_widget_queue_draw (GTK_WIDGET(window));
-        }
-    }
-    backgrounds = g_slist_reverse(backgrounds);
-
     if (lightdm_greeter_get_hide_users_hint (greeter))
     {
-        /* Set the background to default */
-        set_background (NULL);
         set_user_image (NULL);
         start_authentication ("*other");
     }
     else
     {
-        if (!use_user_background)
-            set_background (NULL);
-        /* This also sets the background to user's */
         load_user_list ();
         gtk_widget_hide (GTK_WIDGET (cancel_button));
         gtk_widget_show (GTK_WIDGET (user_combo));
     }
 
-    /* Window position */
-    /* Default: x-center, y-center */
-    main_window_pos = CENTERED_WINDOW_POS;
+    /* Windows positions */
+    main_window_pos = WINDOW_POS_CENTER;
     value = g_key_file_get_value (config, "greeter", "position", NULL);
     if (value)
     {
@@ -3080,21 +2699,17 @@
 
         g_free (value);
     }
+    panel_window_pos = WINDOW_POS_TOP_LEFT;
 
     gtk_builder_connect_signals(builder, greeter);
 
+    gtk_widget_show (GTK_WIDGET (panel_window));
+    center_window (panel_window, NULL, &panel_window_pos);
+    g_signal_connect (GTK_WIDGET (panel_window), "size-allocate", G_CALLBACK (center_window), &panel_window_pos);
+
     gtk_widget_show (GTK_WIDGET (login_window));
-    center_window (login_window,  NULL, &main_window_pos);
+    center_window (login_window, NULL, &main_window_pos);
     g_signal_connect (GTK_WIDGET (login_window), "size-allocate", G_CALLBACK (center_window), &main_window_pos);
-
-    gtk_widget_show (GTK_WIDGET (panel_window));
-    GtkAllocation allocation;
-    gtk_widget_get_allocation (GTK_WIDGET (panel_window), &allocation);
-    gdk_screen_get_monitor_geometry (gdk_screen_get_default (), gdk_screen_get_primary_monitor (gdk_screen_get_default ()), &monitor_geometry);
-    gtk_window_resize (panel_window, monitor_geometry.width, allocation.height);
-    gtk_window_move (panel_window, monitor_geometry.x, monitor_geometry.y);
-
-    gtk_widget_show (GTK_WIDGET (login_window));
     gdk_window_focus (gtk_widget_get_window (GTK_WIDGET (login_window)), GDK_CURRENT_TIME);
 
     if (a11y_keyboard_command)
@@ -3143,16 +2758,5 @@
     }
 #endif
 
-    if (background_pixbuf)
-        g_object_unref (background_pixbuf);
-    if (default_background_pixbuf)
-        g_object_unref (default_background_pixbuf);
-    if (default_background_color)
-#if GTK_CHECK_VERSION (3, 0, 0)
-        gdk_rgba_free (default_background_color);
-#else
-        gdk_color_free (default_background_color);
-#endif
-
     return EXIT_SUCCESS;
 }


Follow ups