diff options
author | Andrew P. <pan.pav.7c5@gmail.com> | 2014-08-09 07:47:00 +0300 |
---|---|---|
committer | Andrew P. <pan.pav.7c5@gmail.com> | 2014-08-09 07:47:00 +0300 |
commit | 1cd2dead81f92294984ea53218b7ab3f538bd9ce (patch) | |
tree | 9b9438631b27466d98a5db5392bed09a4e159f6a | |
parent | a5c887cd6689a666afb4c55aad474341667a31dd (diff) | |
parent | 16074cef046916af27688e58811a598a6504132e (diff) | |
download | lightdm-gtk-greeter-git-1cd2dead81f92294984ea53218b7ab3f538bd9ce.tar.gz |
Merge "greeter-background" branch
-rw-r--r-- | data/lightdm-gtk-greeter.conf | 3 | ||||
-rw-r--r-- | src/Makefile.am | 1 | ||||
-rw-r--r-- | src/greeterbackground.c | 1369 | ||||
-rw-r--r-- | src/greeterbackground.h | 48 | ||||
-rw-r--r-- | src/lightdm-gtk-greeter.c | 601 |
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; } |