summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew P. <pan.pav.7c5@gmail.com>2014-08-09 07:47:00 +0300
committerAndrew P. <pan.pav.7c5@gmail.com>2014-08-09 07:47:00 +0300
commit1cd2dead81f92294984ea53218b7ab3f538bd9ce (patch)
tree9b9438631b27466d98a5db5392bed09a4e159f6a
parenta5c887cd6689a666afb4c55aad474341667a31dd (diff)
parent16074cef046916af27688e58811a598a6504132e (diff)
downloadlightdm-gtk-greeter-git-1cd2dead81f92294984ea53218b7ab3f538bd9ce.tar.gz
Merge "greeter-background" branch
-rw-r--r--data/lightdm-gtk-greeter.conf3
-rw-r--r--src/Makefile.am1
-rw-r--r--src/greeterbackground.c1369
-rw-r--r--src/greeterbackground.h48
-rw-r--r--src/lightdm-gtk-greeter.c601
5 files changed, 1543 insertions, 479 deletions
diff --git a/data/lightdm-gtk-greeter.conf b/data/lightdm-gtk-greeter.conf
index 793bcc4..f9c435d 100644
--- a/data/lightdm-gtk-greeter.conf
+++ b/data/lightdm-gtk-greeter.conf
@@ -1,5 +1,7 @@
#
# background = Background file to use, either an image path or a color (e.g. #772953)
+# active-monitor = Monitor to display greeter window (name or number). Use #cursor value to display greeter at monitor with cursor.
+# user-background = Display user background (if available), true or false. true by default.
# theme-name = GTK+ theme to use
# icon-theme-name = Icon theme to use
# font-name = Font to use
@@ -17,6 +19,7 @@
#
[greeter]
#background=
+#user-background=
#theme-name=
#icon-theme-name=
#font-name=
diff --git a/src/Makefile.am b/src/Makefile.am
index f2752a9..854e8d3 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -8,6 +8,7 @@ lightdm_gtk_greeter_built_sources = \
lightdm_gtk_greeter_SOURCES = \
$(lightdm_gtk_greeter_built_sources) \
lightdm-gtk-greeter.c \
+ greeterbackground.c \
greetermenubar.c
AM_CPPFLAGS = \
diff --git a/src/greeterbackground.c b/src/greeterbackground.c
new file mode 100644
index 0000000..913c659
--- /dev/null
+++ b/src/greeterbackground.c
@@ -0,0 +1,1369 @@
+
+#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
+ /* Maybe other types (e.g. gradient) */
+} BackgroundType;
+
+static const gchar* BACKGROUND_TYPE_SKIP_VALUE = "#skip";
+
+typedef enum
+{
+ /* It is not really useful, used for debugging */
+ SCALING_MODE_SOURCE,
+ /* Default mode for values without mode prefix */
+ 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;
+
+/* Store monitor configuration */
+typedef struct
+{
+ BackgroundConfig bg;
+ gboolean user_bg;
+ gboolean laptop;
+} MonitorConfig;
+
+/* Actual drawing information attached to monitor.
+ * Used to separate configured monitor background and user background. */
+typedef struct
+{
+ BackgroundType type;
+ union
+ {
+ GdkPixbuf* image;
+ #if GTK_CHECK_VERSION (3, 0, 0)
+ GdkRGBA color;
+ #else
+ GdkColor color;
+ #endif
+ } options;
+} Background;
+
+typedef struct
+{
+ GreeterBackground* object;
+ gint number;
+ gchar* name;
+ GdkRectangle geometry;
+ GtkWindow* window;
+ gulong window_draw_handler_id;
+
+ /* Configured background */
+ Background background_configured;
+ /* Background used to display user-background */
+ Background background_custom;
+ /* Current monitor background: &background_configured or &background_custom
+ * Monitors with type = BACKGROUND_TYPE_SKIP have background = NULL */
+ const Background* background;
+} Monitor;
+
+struct _GreeterBackground
+{
+ GObject parent_instance;
+ struct _GreeterBackgroundPrivate* priv;
+};
+
+struct _GreeterBackgroundClass
+{
+ GObjectClass parent_class;
+};
+
+typedef struct _GreeterBackgroundPrivate GreeterBackgroundPrivate;
+
+struct _GreeterBackgroundPrivate
+{
+ GdkScreen* screen;
+ gulong screen_monitors_changed_handler_id;
+ /* one-window-gtk3-only
+ GtkWindow* greeter_widget;
+ */
+ GSList* greeter_windows;
+
+ /* Mapping monitor name <gchar*> to its config <MonitorConfig*> */
+ GHashTable* configs;
+ /* Default config for unlisted monitors */
+ MonitorConfig* default_config;
+
+ /* Array of configured monitors for current screen */
+ Monitor* monitors;
+ gsize monitors_size;
+ /* Name => <Monitor*>, "Number" => <Monitor*> */
+ GHashTable* monitors_map;
+
+ GList* active_monitors_config;
+ const Monitor* active_monitor;
+
+ /* List of monitors <Monitor*> with user-background=true*/
+ GSList* customized_monitors;
+
+ /* List of monitors <Monitor*> with laptop=true */
+ GSList* laptop_monitors;
+ /* DBus proxy to catch lid state changing */
+ GDBusProxy* laptop_upower_proxy;
+ /* Cached lid state */
+ gboolean laptop_lid_closed;
+
+ /* Use cursor position to determinate current active monitor (dynamic) */
+ gboolean follow_cursor;
+ /* Use cursor position to determinate initial active monitor */
+ gboolean follow_cursor_to_init;
+};
+
+enum
+{
+ BACKGROUND_SIGNAL_ACTIVE_MONITOR_CHANGED,
+ BACKGROUND_SIGNAL_LAST
+};
+
+static guint background_signals[BACKGROUND_SIGNAL_LAST] = {0};
+
+static const MonitorConfig DEFAULT_MONITOR_CONFIG =
+{
+ .bg =
+ {
+ .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
+ }
+ },
+ .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";
+static const gchar* DBUS_UPOWER_PROP_LID_IS_PRESENT = "LidIsPresent";
+static const gchar* DBUS_UPOWER_PROP_LID_IS_CLOSED = "LidIsClosed";
+
+static const gchar* ACTIVE_MONITOR_CURSOR_TAG = "#cursor";
+
+G_DEFINE_TYPE_WITH_PRIVATE(GreeterBackground, greeter_background, G_TYPE_OBJECT);
+
+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);
+void greeter_background_remove_monitor_config (GreeterBackground* background,
+ const gchar* name);
+gchar** greeter_background_get_configured_monitors (GreeterBackground* background);
+void greeter_background_connect (GreeterBackground* background,
+ GdkScreen* screen);
+void greeter_background_disconnect (GreeterBackground* background);
+static gboolean greeter_background_find_monitor_data(GreeterBackground* background,
+ GHashTable* table,
+ const Monitor* monitor,
+ gpointer* data);
+static void greeter_background_set_active_monitor (GreeterBackground* background,
+ const Monitor* active);
+static void greeter_background_get_cursor_position (GreeterBackground* background,
+ gint* x, gint* y);
+static void greeter_background_set_cursor_position (GreeterBackground* background,
+ gint x, gint y);
+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_copy (const BackgroundConfig* source,
+ BackgroundConfig* dest);
+
+/* struct MonitorConfig */
+static void monitor_config_free (MonitorConfig* config);
+/* Copy source config to dest, return dest. Allocate memory if dest == NULL. */
+static MonitorConfig* monitor_config_copy (const MonitorConfig* source,
+ MonitorConfig* 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 gboolean monitor_window_enter_notify_cb (GtkWidget* widget,
+ GdkEventCrossing* event,
+ const Monitor* monitor);
+
+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);
+
+ 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->configs = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)monitor_config_free);
+ self->priv->default_config = monitor_config_copy(&DEFAULT_MONITOR_CONFIG, NULL);
+
+ self->priv->monitors = NULL;
+ self->priv->monitors_size = 0;
+ self->priv->monitors_map = NULL;
+
+ self->priv->customized_monitors = NULL;
+ self->priv->active_monitors_config = NULL;
+ self->priv->active_monitor = NULL;
+
+ self->priv->laptop_monitors = NULL;
+ self->priv->laptop_upower_proxy = NULL;
+ self->priv->laptop_lid_closed = FALSE;
+}
+
+GreeterBackground*
+greeter_background_new(void)
+{
+ return GREETER_BACKGROUND(g_object_new(greeter_background_get_type(), NULL));
+}
+
+void
+greeter_background_set_active_monitor_config(GreeterBackground* background,
+ const gchar* value)
+{
+ g_return_if_fail(GREETER_IS_BACKGROUND(background));
+ GreeterBackgroundPrivate* priv = background->priv;
+
+ g_list_free_full(priv->active_monitors_config, g_free);
+ priv->active_monitors_config = NULL;
+
+ priv->follow_cursor = FALSE;
+ priv->follow_cursor_to_init = FALSE;
+
+ if(!value || !*value)
+ return;
+
+ gchar** iter;
+ gchar** values = g_strsplit(value, ";", -1);
+
+ for(iter = values; *iter; ++iter)
+ {
+ const gchar* value = *iter;
+ if(g_strcmp0(value, ACTIVE_MONITOR_CURSOR_TAG) == 0)
+ {
+ priv->follow_cursor = TRUE;
+ priv->follow_cursor_to_init = (priv->active_monitors_config == NULL);
+ }
+ else
+ priv->active_monitors_config = g_list_prepend(priv->active_monitors_config, g_strdup(value));
+ }
+ g_strfreev(values);
+
+ priv->active_monitors_config = g_list_reverse(priv->active_monitors_config);
+}
+
+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)
+{
+ g_return_if_fail(GREETER_IS_BACKGROUND(background));
+ GreeterBackgroundPrivate* priv = background->priv;
+
+ MonitorConfig* config = g_new0(MonitorConfig, 1);
+
+ 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);
+}
+
+void
+greeter_background_remove_monitor_config(GreeterBackground* background,
+ const gchar* name)
+{
+ g_return_if_fail(GREETER_IS_BACKGROUND(background));
+ g_hash_table_remove(background->priv->configs, name);
+}
+
+gchar**
+greeter_background_get_configured_monitors(GreeterBackground* background)
+{
+ g_return_if_fail(GREETER_IS_BACKGROUND(background));
+ GreeterBackgroundPrivate* priv = background->priv;
+
+ gint n = g_hash_table_size(priv->configs);
+ gchar** names = g_new(gchar*, n + 1);
+ names[n--] = NULL;
+
+ gpointer key;
+ GHashTableIter iter;
+ g_hash_table_iter_init(&iter, priv->configs);
+ while(g_hash_table_iter_next(&iter, &key, NULL))
+ names[n--] = g_strdup(key);
+
+ return names;
+}
+
+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)
+ {
+ const MonitorConfig* config;
+ Monitor* monitor = &priv->monitors[i];
+
+ monitor->object = background;
+ monitor->name = g_strdup(gdk_screen_get_monitor_plug_name(screen, i));
+ monitor->number = i;
+
+ 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,
+ 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->bg.type == BACKGROUND_TYPE_SKIP)
+ {
+ if(i < priv->monitors_size - 1 || first_not_skipped_monitor)
+ continue;
+ g_debug("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
+ config = &DEFAULT_MONITOR_CONFIG;
+ }
+
+ if(!first_not_skipped_monitor)
+ first_not_skipped_monitor = monitor;
+
+ 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->follow_cursor)
+ g_signal_connect(G_OBJECT(monitor->window), "enter-notify-event",
+ G_CALLBACK(monitor_window_enter_notify_cb), monitor);
+
+ if(config->user_bg)
+ priv->customized_monitors = g_slist_prepend(priv->customized_monitors, monitor);
+
+ 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);
+
+ if(monitor->name)
+ g_hash_table_insert(priv->monitors_map, g_strdup(monitor->name), monitor);
+ g_hash_table_insert(priv->monitors_map, g_strdup_printf("%d", i), monitor);
+ }
+ g_hash_table_unref(images_cache);
+
+ if(priv->laptop_monitors && !priv->laptop_upower_proxy)
+ greeter_background_try_init_dbus(background);
+ else if(!priv->laptop_monitors)
+ greeter_background_stop_dbus(background);
+
+ if(priv->follow_cursor_to_init)
+ {
+ gint x, y;
+ greeter_background_get_cursor_position(background, &x, &y);
+ for(i = 0; i < priv->monitors_size && !priv->active_monitor; ++i)
+ {
+ const Monitor* monitor = &priv->monitors[i];
+ 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)
+ greeter_background_set_active_monitor(background, monitor);
+ }
+ }
+ 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);
+}
+
+void
+greeter_background_disconnect(GreeterBackground* background)
+{
+ 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;
+
+ gint i;
+ for(i = 0; i < priv->monitors_size; ++i)
+ monitor_finalize(&priv->monitors[i]);
+ g_free(priv->monitors);
+ priv->monitors = NULL;
+ priv->monitors_size = 0;
+
+ g_hash_table_unref(priv->monitors_map);
+ priv->monitors_map = NULL;
+ g_slist_free(priv->customized_monitors);
+ priv->customized_monitors = NULL;
+ g_slist_free(priv->laptop_monitors);
+ priv->laptop_monitors = NULL;
+}
+
+/* Moved to separate function to simplify needless and unnecessary syntax expansion in future (regex) */
+static gboolean
+greeter_background_find_monitor_data(GreeterBackground* background,
+ GHashTable* table,
+ const Monitor* monitor,
+ gpointer* data)
+{
+ if(!monitor->name || !g_hash_table_lookup_extended(table, monitor->name, NULL, data))
+ {
+ gchar* num_str = g_strdup_printf("%d", monitor->number);
+ gboolean result = g_hash_table_lookup_extended(table, num_str, NULL, data);
+ g_free(num_str);
+ return result;
+ }
+ return TRUE;
+}
+
+static void
+greeter_background_set_active_monitor(GreeterBackground* background,
+ const Monitor* active)
+{
+ GreeterBackgroundPrivate* priv = background->priv;
+
+ if(active && !active->background)
+ {
+ if(priv->active_monitor)
+ return;
+ active = NULL;
+ }
+
+ /* Auto */
+ 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->background && 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->background || !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->background)
+ 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 to: %s #%d", active->name, active->number);
+ g_signal_emit(background, background_signals[BACKGROUND_SIGNAL_ACTIVE_MONITOR_CHANGED], 0);
+
+ gint x, y;
+ greeter_background_get_cursor_position(background, &x, &y);
+ /* 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)
+ greeter_background_set_cursor_position(background,
+ active->geometry.x + active->geometry.width/2,
+ active->geometry.y + active->geometry.height/2);
+
+ /* Update greeter windows */
+ GSList* iter;
+ for(iter = priv->greeter_windows; iter; iter = g_slist_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_get_cursor_position(GreeterBackground* background,
+ gint* x, gint* y)
+{
+ GreeterBackgroundPrivate* priv = background->priv;
+
+ GdkDisplay* display = gdk_screen_get_display(priv->screen);
+ #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
+}
+
+static void
+greeter_background_set_cursor_position(GreeterBackground* background,
+ gint x, gint y)
+{
+ GreeterBackgroundPrivate* priv = background->priv;
+
+ GdkDisplay* display = gdk_screen_get_display(priv->screen);
+ #if GTK_CHECK_VERSION(3, 0, 0)
+ GdkDeviceManager* device_manager = gdk_display_get_device_manager(display);
+ 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
+}
+
+static void
+greeter_background_try_init_dbus(GreeterBackground* background)
+{
+ g_debug("Creating DBus proxy");
+ GError* error = NULL;
+ GreeterBackgroundPrivate* priv = background->priv;
+
+ if(priv->laptop_upower_proxy)
+ greeter_background_stop_dbus(background);
+
+ priv->laptop_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->laptop_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->laptop_upower_proxy, DBUS_UPOWER_PROP_LID_IS_PRESENT);
+ 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);
+
+ if(!lid_present)
+ greeter_background_stop_dbus(background);
+ else
+ {
+ variant = g_dbus_proxy_get_cached_property(priv->laptop_upower_proxy, DBUS_UPOWER_PROP_LID_IS_CLOSED);
+ priv->laptop_lid_closed = g_variant_get_boolean(variant);
+ g_variant_unref(variant);
+
+ g_signal_connect(priv->laptop_upower_proxy, "g-properties-changed",
+ G_CALLBACK(greeter_background_dbus_changed_cb), background);
+ }
+}
+
+static void
+greeter_background_stop_dbus(GreeterBackground* background)
+{
+ if(!background->priv->laptop_upower_proxy)
+ return;
+ g_clear_object(&background->priv->laptop_upower_proxy);
+}
+
+static gboolean
+greeter_background_monitor_enabled(GreeterBackground* background,
+ const Monitor* monitor)
+{
+ GreeterBackgroundPrivate* priv = background->priv;
+
+ if(priv->laptop_upower_proxy && g_slist_find(priv->laptop_monitors, monitor))
+ return !priv->laptop_lid_closed;
+ 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->laptop_upower_proxy, DBUS_UPOWER_PROP_LID_IS_CLOSED);
+ gboolean new_state = g_variant_get_boolean(variant);
+ g_variant_unref(variant);
+
+ 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;
+ if(priv->laptop_monitors)
+ {
+ if(!priv->follow_cursor || (new_state && priv->laptop_monitors->data == priv->active_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_slist_find(priv->greeter_windows, window))
+ {
+ priv->greeter_windows = g_slist_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;
+
+ GSList* item = g_slist_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_slist_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);
+
+ GSList* iter;
+ for(iter = priv->customized_monitors; iter; iter = g_slist_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_unref(images_cache);
+ if(config.type != BACKGROUND_TYPE_INVALID)
+ background_config_finalize(&config);
+
+ for(iter = priv->greeter_windows; iter; iter = g_slist_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 || !monitor->background)
+ continue;
+ 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_copy(const BackgroundConfig* source,
+ BackgroundConfig* dest)
+{
+ *dest = *source;
+ if(source->type == BACKGROUND_TYPE_IMAGE)
+ dest->options.image.path = g_strdup(source->options.image.path);
+}
+
+static void
+monitor_config_free(MonitorConfig* config)
+{
+ background_config_finalize(&config->bg);
+ g_free(config);
+}
+
+static MonitorConfig* monitor_config_copy(const MonitorConfig* source,
+ MonitorConfig* dest)
+{
+ if(!dest)
+ dest = g_new0(MonitorConfig, 1);
+ background_config_copy(&source->bg, &dest->bg);
+ dest->user_bg = source->user_bg;
+ dest->laptop = source->laptop;
+ 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 void
+background_finalize(Background* bg)
+{
+ if(bg->type == BACKGROUND_TYPE_IMAGE)
+ g_clear_object(&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);
+ g_free(monitor->name);
+ 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->name = NULL;
+ monitor->window = NULL;
+ monitor->window_draw_handler_id = 0;
+}
+
+static void
+monitor_draw_background(const Monitor* monitor,
+ cairo_t* cr)
+{
+ g_return_if_fail(monitor != NULL);
+ g_return_if_fail(monitor->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);
+ #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->background)
+ {
+ #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 gboolean
+monitor_window_enter_notify_cb(GtkWidget* widget,
+ GdkEventCrossing* event,
+ const Monitor* monitor)
+{
+ if(monitor->object->priv->active_monitor != monitor &&
+ greeter_background_monitor_enabled(monitor->object, monitor))
+ greeter_background_set_active_monitor(monitor->object, monitor);
+ return FALSE;
+}
+
+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);
+ }
+ else 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_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);
+}
diff --git a/src/greeterbackground.h b/src/greeterbackground.h
new file mode 100644
index 0000000..8ee8049
--- /dev/null
+++ b/src/greeterbackground.h
@@ -0,0 +1,48 @@
+#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;
+
+GType greeter_background_get_type(void) G_GNUC_CONST;
+
+GreeterBackground* greeter_background_new (void);
+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);
+void greeter_background_remove_monitor_config (GreeterBackground* background,
+ const gchar* name);
+gchar** greeter_background_get_configured_monitors (GreeterBackground* background);
+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
diff --git a/src/lightdm-gtk-greeter.c b/src/lightdm-gtk-greeter.c
index 27df6f3..c6b7045 100644
--- a/src/lightdm-gtk-greeter.c
+++ b/src/lightdm-gtk-greeter.c
@@ -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 @@ static gchar *state_filename;
/* 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 @@ static GtkWindow *onboard_window;
/* Pending Questions */
static GSList *pending_questions = NULL;
-GSList *backgrounds = NULL;
-
/* Current choices */
static gchar *current_session;
static gchar *current_language;
@@ -108,11 +101,8 @@ static gchar *current_language;
/* 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 @@ typedef struct
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 *LANGUAGE_DATA_CODE = "language-code";
static const gchar *SESSION_DATA_KEY = "session-key";
static const gchar *LAYOUT_DATA_LABEL = "layout-label";
@@ -197,6 +187,19 @@ static const gchar *INDICATOR_ITEM_DATA_BOX = "indicator-item-data-box";
static const gchar *INDICATOR_DATA_MENUITEMS = "indicator-data-menuitems";
#endif
+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;
+}
+
static void
pam_message_finalize (PAMConversationMessage *message)
{
@@ -444,8 +447,9 @@ reassign_menu_item_accel (GtkWidget *item)
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
@@ -828,34 +832,17 @@ set_login_button_label (LightDMGreeter *greeter, const gchar *username)
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
@@ -914,58 +901,39 @@ get_absolute_position (const DimensionPosition *p, gint screen, gint window)
}
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;
+ 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 gboolean
-login_window_draw (GtkWidget *widget, cairo_t *cr, gpointer user_data)
+static void
+active_monitor_changed_cb (GreeterBackground *background, 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)
+ const GdkRectangle *monitor_geometry = greeter_background_get_active_monitor_geometry (greeter_background);
+ if (monitor_geometry)
{
- 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);
+ 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);
}
- else
- gdk_cairo_set_source_rgba (cr, default_background_color);
-
- cairo_paint (cr);
-
- g_free (allocation);
- return FALSE;
}
-#else
+#if !GTK_CHECK_VERSION (3, 0, 0)
static GdkRegion *
cairo_region_from_rectangle (gint width, gint height, gint radius)
{
@@ -1032,20 +1000,6 @@ login_window_size_allocate (GtkWidget *widget, GdkRectangle *allocation, gpointe
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
@@ -1175,6 +1129,8 @@ start_session (void)
/* 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);
@@ -1386,8 +1342,7 @@ set_displayed_user (LightDMGreeter *greeter, const gchar *username)
}
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)
@@ -1707,22 +1662,23 @@ show_power_prompt (const gchar* action, const gchar* message, const gchar* icon,
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;
}
@@ -2051,253 +2007,6 @@ load_user_list (void)
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)
{
@@ -2586,20 +2295,11 @@ main (int argc, char **argv)
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
@@ -2661,47 +2361,6 @@ main (int argc, char **argv)
/* 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;
@@ -2789,7 +2448,6 @@ main (int argc, char **argv)
#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"));
@@ -2800,6 +2458,10 @@ main (int argc, char **argv)
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());
@@ -2817,12 +2479,6 @@ main (int argc, char **argv)
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");
@@ -2902,7 +2558,7 @@ main (int argc, char **argv)
{
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_DATA_KEY, (gpointer) lightdm_session_get_key (session));
sessions = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (radiomenuitem));
@@ -3035,6 +2691,7 @@ main (int argc, char **argv)
#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),
@@ -3043,77 +2700,78 @@ main (int argc, char **argv)
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 ();
+
+ value = g_key_file_get_value (config, "greeter", "active-monitor", NULL);
+ 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;
+ g_debug ("Monitor configuration found: '%s'", name);
+
+ GError *user_bg_error = NULL, *laptop_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);
+
+ greeter_background_set_monitor_config (greeter_background, name, value,
+ user_bg, user_bg_error == NULL,
+ laptop, laptop_error == NULL);
+
+ g_free (value);
+ g_clear_error (&laptop_error);
+ g_clear_error (&user_bg_error);
+ }
+ g_strfreev (config_groups);
+
+ g_signal_connect (G_OBJECT (greeter_background), "active-monitor-changed", G_CALLBACK(active_monitor_changed_cb), NULL);
+ 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)
{
@@ -3129,21 +2787,17 @@ main (int argc, char **argv)
g_free (value);
}
+ panel_window_pos = WINDOW_POS_TOP_LEFT;
gtk_builder_connect_signals(builder, greeter);
- gtk_widget_show (GTK_WIDGET (login_window));
- 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);
+ 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);
+ g_signal_connect (GTK_WIDGET (login_window), "size-allocate", G_CALLBACK (center_window), &main_window_pos);
gdk_window_focus (gtk_widget_get_window (GTK_WIDGET (login_window)), GDK_CURRENT_TIME);
if (a11y_keyboard_command)
@@ -3192,16 +2846,5 @@ main (int argc, char **argv)
}
#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;
}