1 /*
2  * Copyright (C) 2014 - 2020, Sean Davis <sean@bluesabre.org>
3  * Copyright (C) 2014, Andrew P. <pan.pav.7c5@gmail.com>
4  * Copyright (C) 2015, Robert Ancell <robert.ancell@canonical.com>
5  * Copyright (C) 2015, Simon Steinbeiß <ochosi@shimmerproject.org>
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
11  * license.
12  */
13 
14 #include <math.h>
15 #include <cairo-xlib.h>
16 #include <gtk/gtk.h>
17 #include <gdk/gdkx.h>
18 #include <gdk-pixbuf/gdk-pixbuf.h>
19 #include <string.h>
20 #include <X11/Xatom.h>
21 
22 #include "greeterbackground.h"
23 #include "greeterdeprecated.h"
24 
25 typedef enum
26 {
27     /* Broken/uninitialized configuration */
28     BACKGROUND_TYPE_INVALID,
29     /* Do not use this monitor */
30     BACKGROUND_TYPE_SKIP,
31     /* Do not override window background */
32     BACKGROUND_TYPE_DEFAULT,
33     /* Solid color */
34     BACKGROUND_TYPE_COLOR,
35     /* Path to image and scaling mode */
36     BACKGROUND_TYPE_IMAGE
37     /* Maybe other types (e.g. gradient) */
38 } BackgroundType;
39 
40 static const gchar* BACKGROUND_TYPE_SKIP_VALUE = "#skip";
41 static const gchar* BACKGROUND_TYPE_DEFAULT_VALUE = "#default";
42 
43 typedef enum
44 {
45     /* It is not really useful, used for debugging */
46     SCALING_MODE_SOURCE,
47     /* Default mode for values without mode prefix */
48     SCALING_MODE_ZOOMED,
49     SCALING_MODE_STRETCHED
50 } ScalingMode;
51 
52 static const gchar* SCALING_MODE_PREFIXES[] = {"#source:", "#zoomed:", "#stretched:", NULL};
53 
54 typedef gdouble (*TransitionFunction)(gdouble x);
55 typedef void (*TransitionDraw)(gconstpointer monitor, cairo_t* cr);
56 
57 /* Background configuration (parsed from background=... option).
58    Used to fill <Background> */
59 typedef struct
60 {
61     BackgroundType type;
62     union
63     {
64         GdkRGBA color;
65         struct
66         {
67             gchar *path;
68             ScalingMode mode;
69         } image;
70     } options;
71 } BackgroundConfig;
72 
73 /* Transition configuration
74    Used as part of <MonitorConfig> and <Monitor> */
75 typedef struct
76 {
77     /* Transition duration, in ms */
78     glong duration;
79     TransitionFunction func;
80     /* Function to draw monitor background */
81     TransitionDraw draw;
82 } TransitionConfig;
83 
84 /* Store monitor configuration */
85 typedef struct
86 {
87     BackgroundConfig bg;
88     gboolean user_bg;
89     gboolean laptop;
90 
91     TransitionConfig transition;
92 } MonitorConfig;
93 
94 /* Actual drawing information attached to monitor.
95  * Used to separate configured monitor background and user background. */
96 typedef struct
97 {
98     gint ref_count;
99     BackgroundType type;
100     union
101     {
102         GdkPixbuf* image;
103         GdkRGBA color;
104     } options;
105 } Background;
106 
107 typedef struct
108 {
109     GreeterBackground* object;
110     gint number;
111     gchar* name;
112     GdkRectangle geometry;
113     GtkWindow* window;
114     gulong window_draw_handler_id;
115 
116     /* Configured background */
117     Background* background_configured;
118     /* Current monitor background: &background_configured or &background_custom
119      * Monitors with type = BACKGROUND_TYPE_SKIP have background = NULL */
120     Background* background;
121 
122     struct
123     {
124         TransitionConfig config;
125 
126         /* Old background, stage == 0.0 */
127         Background* from;
128         /* New background, stage == 1.0 */
129         Background* to;
130 
131         guint timer_id;
132         gint64 started;
133         /* Current stage */
134         gdouble stage;
135     } transition;
136 } Monitor;
137 
138 static const Monitor INVALID_MONITOR_STRUCT = {0};
139 
140 struct _GreeterBackground
141 {
142 	GObject parent_instance;
143 	struct _GreeterBackgroundPrivate* priv;
144 };
145 
146 struct _GreeterBackgroundClass
147 {
148 	GObjectClass parent_class;
149 };
150 
151 typedef struct _GreeterBackgroundPrivate GreeterBackgroundPrivate;
152 
153 struct _GreeterBackgroundPrivate
154 {
155     GdkScreen* screen;
156     gulong screen_monitors_changed_handler_id;
157 
158     /* Widget to display on active monitor */
159     GtkWidget* child;
160     /* List of groups <GtkAccelGroup*> for greeter screens windows */
161     GSList* accel_groups;
162 
163     /* Mapping monitor name <gchar*> to its config <MonitorConfig*> */
164     GHashTable* configs;
165 
166     /* Default config for unlisted monitors */
167     MonitorConfig* default_config;
168 
169 	/* Array of configured monitors for current screen */
170     Monitor* monitors;
171     gsize monitors_size;
172 
173 	/* Name => <Monitor*>, "Number" => <Monitor*> */
174     GHashTable* monitors_map;
175 
176     GList* active_monitors_config;
177     const Monitor* active_monitor;
178     gboolean active_monitor_change_in_progress;
179     gint64 active_monitor_change_last_timestamp;
180 
181     /* List of monitors <Monitor*> with user-background=true*/
182     GSList* customized_monitors;
183     /* Initialized by set_custom_background() */
184     BackgroundConfig customized_background;
185 
186     /* List of monitors <Monitor*> with laptop=true */
187     GSList* laptop_monitors;
188     /* DBus proxy to catch lid state changing */
189     GDBusProxy* laptop_upower_proxy;
190     /* Cached lid state */
191     gboolean laptop_lid_closed;
192 
193     /* Use cursor position to determinate current active monitor (dynamic) */
194     gboolean follow_cursor;
195     /* Use cursor position to determinate initial active monitor */
196     gboolean follow_cursor_to_init;
197 };
198 
199 enum
200 {
201     BACKGROUND_SIGNAL_ACTIVE_MONITOR_CHANGED,
202     BACKGROUND_SIGNAL_LAST
203 };
204 
205 static guint background_signals[BACKGROUND_SIGNAL_LAST] = {0};
206 
207 static const gchar* DBUS_UPOWER_NAME                = "org.freedesktop.UPower";
208 static const gchar* DBUS_UPOWER_PATH                = "/org/freedesktop/UPower";
209 static const gchar* DBUS_UPOWER_INTERFACE           = "org.freedesktop.UPower";
210 static const gchar* DBUS_UPOWER_PROP_LID_IS_PRESENT = "LidIsPresent";
211 static const gchar* DBUS_UPOWER_PROP_LID_IS_CLOSED  = "LidIsClosed";
212 
213 static const gchar* ACTIVE_MONITOR_CURSOR_TAG       = "#cursor";
214 
215 G_DEFINE_TYPE_WITH_PRIVATE(GreeterBackground, greeter_background, G_TYPE_OBJECT);
216 
217 void greeter_background_disconnect                  (GreeterBackground* background);
218 static gboolean greeter_background_find_monitor_data(GreeterBackground* background,
219                                                      GHashTable* table,
220                                                      const Monitor* monitor,
221                                                      gpointer* data);
222 static void greeter_background_set_active_monitor   (GreeterBackground* background,
223                                                      const Monitor* active);
224 static void greeter_background_get_cursor_position  (GreeterBackground* background,
225                                                      gint* x, gint* y);
226 static void greeter_background_set_cursor_position  (GreeterBackground* background,
227                                                      gint x, gint y);
228 static void greeter_background_try_init_dbus        (GreeterBackground* background);
229 static void greeter_background_stop_dbus            (GreeterBackground* background);
230 static gboolean greeter_background_monitor_enabled  (GreeterBackground* background,
231                                                      const Monitor* monitor);
232 static void greeter_background_dbus_changed_cb      (GDBusProxy* proxy,
233                                                      GVariant* changed_properties,
234                                                      const gchar* const* invalidated_properties,
235                                                      GreeterBackground* background);
236 static void greeter_background_monitors_changed_cb  (GdkScreen* screen,
237                                                      GreeterBackground* background);
238 static void greeter_background_child_destroyed_cb   (GtkWidget* child,
239                                                      GreeterBackground* background);
240 
241 /* struct BackgroundConfig */
242 static gboolean background_config_initialize        (BackgroundConfig* config,
243                                                      const gchar* value);
244 static void background_config_finalize              (BackgroundConfig* config);
245 static void background_config_copy                  (const BackgroundConfig* source,
246                                                      BackgroundConfig* dest);
247 
248 /* struct MonitorConfig */
249 static void monitor_config_free                     (MonitorConfig* config);
250 /* Copy source config to dest, return dest. Allocate memory if dest == NULL. */
251 static MonitorConfig* monitor_config_copy           (const MonitorConfig* source,
252                                                      MonitorConfig* dest);
253 
254 /* struct Background */
255 static Background* background_new                   (const BackgroundConfig* config,
256                                                      const Monitor* monitor,
257                                                      GHashTable* images_cache);
258 static Background* background_ref                   (Background* bg);
259 static void background_unref                        (Background** bg);
260 static void background_finalize                     (Background* bg);
261 
262 /* struct Monitor */
263 static void monitor_finalize                        (Monitor* info);
264 static void monitor_set_background                  (Monitor* monitor,
265                                                      Background* background);
266 static void monitor_start_transition                (Monitor* monitor,
267                                                      Background* from,
268                                                      Background* to);
269 static void monitor_stop_transition                 (Monitor* monitor);
270 static gboolean monitor_transition_cb               (GtkWidget *widget,
271                                                      GdkFrameClock* frame_clock,
272                                                      Monitor* monitor);
273 static void monitor_transition_draw_alpha           (const Monitor* monitor,
274                                                      cairo_t* cr);
275 static void monitor_draw_background                 (const Monitor* monitor,
276                                                      const Background* background,
277                                                      cairo_t* cr);
278 static gboolean monitor_window_draw_cb              (GtkWidget* widget,
279                                                      cairo_t* cr,
280                                                      const Monitor* monitor);
281 static gboolean monitor_window_enter_notify_cb      (GtkWidget* widget,
282                                                      GdkEventCrossing* event,
283                                                      const Monitor* monitor);
284 
285 static GdkPixbuf* scale_image_file                  (const gchar* path,
286                                                      ScalingMode mode,
287                                                      gint width, gint height,
288                                                      GHashTable* cache);
289 static GdkPixbuf* scale_image                       (GdkPixbuf* source,
290                                                      ScalingMode mode,
291                                                      gint width, gint height);
292 static cairo_surface_t* create_root_surface         (GdkScreen* screen);
293 static void set_root_pixmap_id                      (GdkScreen* screen,
294                                                      Display* display,
295                                                      Pixmap xpixmap);
296 static void set_surface_as_root                     (GdkScreen* screen,
297                                                      cairo_surface_t* surface);
298 static gdouble transition_func_linear               (gdouble x);
299 static gdouble transition_func_ease_in_out          (gdouble x);
300 
301 /* Implemented in lightdm-gtk-greeter.c */
302 gpointer greeter_save_focus(GtkWidget* widget);
303 void greeter_restore_focus(const gpointer saved_data);
304 
305 static const MonitorConfig DEFAULT_MONITOR_CONFIG =
306 {
307     .bg =
308     {
309         .type = BACKGROUND_TYPE_COLOR,
310         .options =
311         {
312             .color = {.red = 0.0, .green = 0.0, .blue = 0.0, .alpha = 1.0}
313         }
314     },
315     .user_bg = TRUE,
316     .laptop = FALSE,
317     .transition =
318     {
319         .duration = 500,
320         .func = transition_func_ease_in_out,
321         .draw = (TransitionDraw)monitor_transition_draw_alpha
322     }
323 };
324 
325 /* Implementation */
326 
327 static void
greeter_background_class_init(GreeterBackgroundClass * klass)328 greeter_background_class_init(GreeterBackgroundClass* klass)
329 {
330 	GObjectClass* gobject_class = G_OBJECT_CLASS(klass);
331 
332     background_signals[BACKGROUND_SIGNAL_ACTIVE_MONITOR_CHANGED] =
333                             g_signal_new("active-monitor-changed",
334                                          G_TYPE_FROM_CLASS(gobject_class),
335                                          G_SIGNAL_RUN_FIRST,
336                                          0, /* class_offset */
337                                          NULL /* accumulator */, NULL /* accu_data */,
338                                          g_cclosure_marshal_VOID__VOID,
339                                          G_TYPE_NONE, 0);
340 }
341 
342 static void
greeter_background_init(GreeterBackground * self)343 greeter_background_init(GreeterBackground* self)
344 {
345     GreeterBackgroundPrivate* priv = greeter_background_get_instance_private (self);
346     self->priv = priv;
347 
348     priv->screen = NULL;
349     priv->screen_monitors_changed_handler_id = 0;
350     priv->accel_groups = NULL;
351 
352     priv->configs = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)monitor_config_free);
353     priv->default_config = monitor_config_copy(&DEFAULT_MONITOR_CONFIG, NULL);
354 
355     priv->monitors = NULL;
356     priv->monitors_size = 0;
357     priv->monitors_map = NULL;
358 
359     priv->customized_monitors = NULL;
360     priv->customized_background.type = BACKGROUND_TYPE_INVALID;
361     priv->active_monitors_config = NULL;
362     priv->active_monitor = NULL;
363 
364     priv->laptop_monitors = NULL;
365     priv->laptop_upower_proxy = NULL;
366     priv->laptop_lid_closed = FALSE;
367 }
368 
369 GreeterBackground*
greeter_background_new(GtkWidget * child)370 greeter_background_new(GtkWidget* child)
371 {
372     GreeterBackground *background;
373 
374     g_return_val_if_fail(child != NULL, NULL);
375 
376     background = GREETER_BACKGROUND(g_object_new(greeter_background_get_type(), NULL));
377     background->priv->child = child;
378     g_signal_connect(background->priv->child, "destroy", G_CALLBACK(greeter_background_child_destroyed_cb), background);
379 	return background;
380 }
381 
382 void
greeter_background_set_active_monitor_config(GreeterBackground * background,const gchar * value)383 greeter_background_set_active_monitor_config(GreeterBackground* background,
384                                              const gchar* value)
385 {
386     GreeterBackgroundPrivate     *priv;
387     gchar                       **iter;
388     gchar                       **values;
389 
390     g_return_if_fail(GREETER_IS_BACKGROUND(background));
391 
392     priv = background->priv;
393 
394     g_list_free_full(priv->active_monitors_config, g_free);
395     priv->active_monitors_config = NULL;
396     priv->active_monitor_change_in_progress = FALSE;
397     priv->active_monitor_change_last_timestamp = 0;
398 
399     priv->follow_cursor = FALSE;
400     priv->follow_cursor_to_init = FALSE;
401 
402     if (!value || !*value)
403         return;
404 
405     values = g_strsplit(value, ";", -1);
406 
407     for(iter = values; *iter; ++iter)
408     {
409         const gchar* tag = *iter;
410         if (g_strcmp0(tag, ACTIVE_MONITOR_CURSOR_TAG) == 0)
411         {
412             priv->follow_cursor = TRUE;
413             priv->follow_cursor_to_init = (priv->active_monitors_config == NULL);
414         }
415         else
416             priv->active_monitors_config = g_list_prepend(priv->active_monitors_config, g_strdup(tag));
417     }
418     g_strfreev(values);
419 
420     priv->active_monitors_config = g_list_reverse(priv->active_monitors_config);
421 }
422 
423 void
greeter_background_set_monitor_config(GreeterBackground * background,const gchar * name,const gchar * bg,gint user_bg,gint laptop,gint transition_duration,TransitionType transition_type)424 greeter_background_set_monitor_config(GreeterBackground* background,
425                                       const gchar* name,
426                                       const gchar* bg,
427                                       gint user_bg,
428                                       gint laptop,
429                                       gint transition_duration,
430                                       TransitionType transition_type)
431 {
432     GreeterBackgroundPrivate *priv;
433     MonitorConfig            *config;
434     const MonitorConfig      *FALLBACK;
435 
436     g_return_if_fail(GREETER_IS_BACKGROUND(background));
437 
438     priv = background->priv;
439 
440     config = g_new0(MonitorConfig, 1);
441 
442     FALLBACK = (g_strcmp0(name, GREETER_BACKGROUND_DEFAULT) == 0) ? &DEFAULT_MONITOR_CONFIG : priv->default_config;
443 
444     if(!background_config_initialize(&config->bg, bg))
445         background_config_copy(&FALLBACK->bg, &config->bg);
446     config->user_bg = user_bg >= 0 ? user_bg : FALLBACK->user_bg;
447     config->laptop = laptop >= 0 ? laptop : FALLBACK->laptop;
448     config->transition.duration = transition_duration >= 0 ? transition_duration : FALLBACK->transition.duration;
449     config->transition.draw = FALLBACK->transition.draw;
450 
451     switch(transition_type)
452     {
453         case TRANSITION_TYPE_NONE:
454             config->transition.func = NULL; break;
455         case TRANSITION_TYPE_LINEAR:
456             config->transition.func = transition_func_linear; break;
457         case TRANSITION_TYPE_EASE_IN_OUT:
458             config->transition.func = transition_func_ease_in_out; break;
459         case TRANSITION_TYPE_FALLBACK:
460         default:
461             config->transition.func = FALLBACK->transition.func;
462     }
463 
464     if(FALLBACK == priv->default_config)
465         g_hash_table_insert(priv->configs, g_strdup(name), config);
466     else
467     {
468         if(priv->default_config)
469             monitor_config_free(priv->default_config);
470         priv->default_config = config;
471     }
472 }
473 
474 void
greeter_background_remove_monitor_config(GreeterBackground * background,const gchar * name)475 greeter_background_remove_monitor_config(GreeterBackground* background,
476                                          const gchar* name)
477 {
478     g_return_if_fail(GREETER_IS_BACKGROUND(background));
479     g_hash_table_remove(background->priv->configs, name);
480 }
481 
482 gchar**
greeter_background_get_configured_monitors(GreeterBackground * background)483 greeter_background_get_configured_monitors(GreeterBackground* background)
484 {
485     GreeterBackgroundPrivate  *priv;
486     GHashTableIter             iter;
487     gpointer                   key;
488     gchar                    **names;
489     gint                       n;
490 
491     g_return_val_if_fail(GREETER_IS_BACKGROUND(background), NULL);
492 
493     priv = background->priv;
494 
495     n = g_hash_table_size(priv->configs);
496     names = g_new(gchar*, n + 1);
497     names[n--] = NULL;
498 
499     g_hash_table_iter_init(&iter, priv->configs);
500     while(g_hash_table_iter_next(&iter, &key, NULL))
501         names[n--] = g_strdup(key);
502 
503     return names;
504 }
505 
506 void
greeter_background_connect(GreeterBackground * background,GdkScreen * screen)507 greeter_background_connect(GreeterBackground* background,
508                            GdkScreen* screen)
509 {
510     GreeterBackgroundPrivate *priv;
511     GHashTable               *images_cache;
512     Monitor                  *first_not_skipped_monitor = NULL;
513     cairo_region_t           *screen_region;
514     gpointer                  saved_focus = NULL;
515     guint                     i;
516 
517     g_return_if_fail(GREETER_IS_BACKGROUND(background));
518     g_return_if_fail(GDK_IS_SCREEN(screen));
519 
520     g_debug("[Background] Connecting to screen: %p (%dx%dpx, %dx%dmm)", screen,
521             greeter_screen_get_width(screen), greeter_screen_get_height(screen),
522             greeter_screen_get_width_mm(screen), greeter_screen_get_height_mm(screen));
523 
524     priv = background->priv;
525     saved_focus = NULL;
526     if(priv->screen)
527     {
528         if (priv->active_monitor)
529             saved_focus = greeter_save_focus(priv->child);
530         greeter_background_disconnect(background);
531     }
532 
533     priv->screen = screen;
534     priv->monitors_size = greeter_screen_get_n_monitors(screen);
535     priv->monitors = g_new0(Monitor, priv->monitors_size);
536     priv->monitors_map = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
537 
538     g_debug("[Background] Monitors found: %" G_GSIZE_FORMAT, priv->monitors_size);
539 
540     /* Used to track situation when all monitors marked as "#skip" */
541     first_not_skipped_monitor = NULL;
542 
543     images_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
544     screen_region = cairo_region_create();
545 
546     for(i = 0; i < priv->monitors_size; ++i)
547     {
548         const MonitorConfig* config;
549         Monitor* monitor = &priv->monitors[i];
550         const gchar* printable_name;
551         gchar* window_name;
552         GSList* item;
553         Background* bg = NULL;
554 
555         monitor->object = background;
556         monitor->name = g_strdup(greeter_screen_get_monitor_plug_name(screen, i));
557         monitor->number = i;
558 
559         printable_name = monitor->name ? monitor->name : "<unknown>";
560 
561         greeter_screen_get_monitor_geometry(screen, i, &monitor->geometry);
562 
563         g_debug("[Background] Monitor: %s #%d (%dx%d at %dx%d)%s", printable_name, i,
564                 monitor->geometry.width, monitor->geometry.height,
565                 monitor->geometry.x, monitor->geometry.y,
566                 ((gint)i == greeter_screen_get_primary_monitor(screen)) ? " primary" : "");
567 
568         if(!greeter_background_find_monitor_data(background, priv->configs, monitor, (gpointer*)&config))
569         {
570             g_debug("[Background] No configuration options for monitor %s #%d, using default", printable_name, i);
571             config = priv->default_config;
572         }
573 
574         /* Force last skipped monitor to be active monitor, if there is no other choice */
575         if(config->bg.type == BACKGROUND_TYPE_SKIP)
576         {
577             if(i < priv->monitors_size - 1 || first_not_skipped_monitor)
578             {
579                 g_debug("[Background] Skipping monitor %s #%d", printable_name, i);
580                 continue;
581             }
582             g_debug("[Background] Monitor %s #%d can not be skipped, using default configuration for it", printable_name, i);
583             if(priv->default_config->bg.type != BACKGROUND_TYPE_SKIP)
584                 config = priv->default_config;
585             else
586                 config = &DEFAULT_MONITOR_CONFIG;
587         }
588 
589         /* Simple check to skip fully overlapped monitors.
590            Actually, it's can track only monitors in "mirrors" mode. Nothing more. */
591         if(cairo_region_contains_rectangle(screen_region, &monitor->geometry) == CAIRO_REGION_OVERLAP_IN)
592         {
593             g_debug("[Background] Skipping monitor %s #%d, its area is already used by other monitors", printable_name, i);
594             continue;
595         }
596         cairo_region_union_rectangle(screen_region, &monitor->geometry);
597 
598         if(!first_not_skipped_monitor)
599             first_not_skipped_monitor = monitor;
600 
601         monitor->window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
602         gtk_window_set_type_hint(monitor->window, GDK_WINDOW_TYPE_HINT_DESKTOP);
603         gtk_window_set_keep_below(monitor->window, TRUE);
604         gtk_window_set_resizable(monitor->window, FALSE);
605         gtk_widget_set_app_paintable(GTK_WIDGET(monitor->window), TRUE);
606         gtk_window_set_screen(monitor->window, screen);
607         gtk_widget_set_size_request(GTK_WIDGET(monitor->window), monitor->geometry.width, monitor->geometry.height);
608         gtk_window_move(monitor->window, monitor->geometry.x, monitor->geometry.y);
609         gtk_widget_show(GTK_WIDGET(monitor->window));
610         monitor->window_draw_handler_id = g_signal_connect(G_OBJECT(monitor->window), "draw",
611                                                            G_CALLBACK(monitor_window_draw_cb),
612                                                            monitor);
613 
614         window_name = monitor->name ? g_strdup_printf("monitor-%s", monitor->name) : g_strdup_printf("monitor-%d", i);
615         gtk_widget_set_name(GTK_WIDGET(monitor->window), window_name);
616         gtk_style_context_add_class(gtk_widget_get_style_context(GTK_WIDGET(monitor->window)), "lightdm-gtk-greeter");
617         g_free(window_name);
618 
619         for(item = priv->accel_groups; item != NULL; item = g_slist_next(item))
620             gtk_window_add_accel_group(monitor->window, item->data);
621 
622         g_signal_connect(G_OBJECT(monitor->window), "enter-notify-event",
623                          G_CALLBACK(monitor_window_enter_notify_cb), monitor);
624 
625         if(config->user_bg)
626             priv->customized_monitors = g_slist_prepend(priv->customized_monitors, monitor);
627 
628         if(config->laptop)
629             priv->laptop_monitors = g_slist_prepend(priv->laptop_monitors, monitor);
630 
631         if(config->transition.duration && config->transition.func)
632             monitor->transition.config = config->transition;
633 
634         monitor->background_configured = background_new(&config->bg, monitor, images_cache);
635         if(!monitor->background_configured)
636             monitor->background_configured = background_new(&DEFAULT_MONITOR_CONFIG.bg, monitor, images_cache);
637 
638         if(config->user_bg && priv->customized_background.type != BACKGROUND_TYPE_INVALID)
639             bg = background_new(&priv->customized_background, monitor, images_cache);
640 
641         if(bg)
642         {
643             monitor_set_background(monitor, bg);
644             background_unref(&bg);
645         }
646 		else
647             monitor_set_background(monitor, monitor->background_configured);
648 
649         if(monitor->name)
650             g_hash_table_insert(priv->monitors_map, g_strdup(monitor->name), monitor);
651         g_hash_table_insert(priv->monitors_map, g_strdup_printf("%d", i), monitor);
652     }
653     g_hash_table_unref(images_cache);
654 
655     if(priv->laptop_monitors && !priv->laptop_upower_proxy)
656         greeter_background_try_init_dbus(background);
657     else if(!priv->laptop_monitors)
658         greeter_background_stop_dbus(background);
659 
660     if(priv->follow_cursor_to_init)
661     {
662         gint x, y;
663         greeter_background_get_cursor_position(background, &x, &y);
664         for(i = 0; i < priv->monitors_size && !priv->active_monitor; ++i)
665         {
666             const Monitor* monitor = &priv->monitors[i];
667             if(greeter_background_monitor_enabled(background, monitor) &&
668                x >= monitor->geometry.x && x < monitor->geometry.x + monitor->geometry.width &&
669                y >= monitor->geometry.y && y < monitor->geometry.y + monitor->geometry.height)
670             {
671                 g_debug("[Background] Pointer position will be used to set active monitor: %dx%d", x, y);
672                 greeter_background_set_active_monitor(background, monitor);
673                 break;
674             }
675         }
676     }
677 
678     if(!priv->active_monitor)
679         greeter_background_set_active_monitor(background, NULL);
680 
681     if(saved_focus)
682     {
683         greeter_restore_focus(saved_focus);
684         g_free(saved_focus);
685     }
686 
687     priv->screen_monitors_changed_handler_id = g_signal_connect(G_OBJECT(screen), "monitors-changed",
688                                                                 G_CALLBACK(greeter_background_monitors_changed_cb),
689                                                                 background);
690 }
691 
692 void
greeter_background_disconnect(GreeterBackground * background)693 greeter_background_disconnect(GreeterBackground* background)
694 {
695     GreeterBackgroundPrivate *priv;
696     guint                      i;
697 
698     g_return_if_fail(GREETER_IS_BACKGROUND(background));
699 
700     priv = background->priv;
701 
702     if(priv->screen_monitors_changed_handler_id)
703         g_signal_handler_disconnect(priv->screen, priv->screen_monitors_changed_handler_id);
704     priv->screen_monitors_changed_handler_id = 0;
705     priv->screen = NULL;
706     priv->active_monitor = NULL;
707 
708     for(i = 0; i < priv->monitors_size; ++i)
709         monitor_finalize(&priv->monitors[i]);
710     g_free(priv->monitors);
711     priv->monitors = NULL;
712     priv->monitors_size = 0;
713 
714     g_hash_table_unref(priv->monitors_map);
715     priv->monitors_map = NULL;
716     g_slist_free(priv->customized_monitors);
717     priv->customized_monitors = NULL;
718     g_slist_free(priv->laptop_monitors);
719     priv->laptop_monitors = NULL;
720 }
721 
722 /* Moved to separate function to simplify needless and unnecessary syntax expansion in future (regex) */
723 static gboolean
greeter_background_find_monitor_data(GreeterBackground * background,GHashTable * table,const Monitor * monitor,gpointer * data)724 greeter_background_find_monitor_data(GreeterBackground* background,
725                                      GHashTable* table,
726                                      const Monitor* monitor,
727                                      gpointer* data)
728 {
729     if(!monitor->name || !g_hash_table_lookup_extended(table, monitor->name, NULL, data))
730     {
731         gchar* num_str = g_strdup_printf("%d", monitor->number);
732         gboolean result = g_hash_table_lookup_extended(table, num_str, NULL, data);
733         g_free(num_str);
734         return result;
735     }
736     return TRUE;
737 }
738 
739 static void
greeter_background_set_active_monitor(GreeterBackground * background,const Monitor * active)740 greeter_background_set_active_monitor(GreeterBackground* background,
741                                       const Monitor* active)
742 {
743     GreeterBackgroundPrivate* priv = background->priv;
744     gint x, y;
745     gint64 timestamp;
746 
747     if (priv->active_monitor_change_in_progress)
748         return;
749 
750     /* Prevents infinite signal emmission between two monitors (LP: #1410406, #1509780)
751      * There are some rare scenarios when using multiple monitors that cause the greeter
752      * to switch back and forth between the monitors indefinitely. By comparing the
753      * timestamp at this precision (1/10th of a second), this should no longer be
754      * possible.
755      */
756     if (active != NULL)
757     {
758         timestamp = floor(g_get_monotonic_time () * 0.00001);
759         if (timestamp == priv->active_monitor_change_last_timestamp)
760         {
761             g_debug("[Background] Preventing infinite monitor loop");
762             return;
763         }
764     }
765 
766     priv->active_monitor_change_in_progress = TRUE;
767     priv->active_monitor_change_last_timestamp = floor(g_get_monotonic_time () * 0.00001);
768 
769     if(active && !active->background)
770     {
771         if(priv->active_monitor)
772             goto active_monitor_change_complete;
773         active = NULL;
774     }
775 
776     /* Auto */
777     if(!active)
778     {
779         /* Normal way: at least one configured active monitor is not disabled */
780         GList* iter;
781         for(iter = priv->active_monitors_config; iter && !active; iter = g_list_next(iter))
782         {
783             const Monitor* monitor = g_hash_table_lookup(priv->monitors_map, iter->data);
784             if(monitor && monitor->background && greeter_background_monitor_enabled(background, monitor))
785                 active = monitor;
786             if(active)
787                 g_debug("[Background] Active monitor is not specified, using first enabled monitor from 'active-monitor' list");
788         }
789 
790         /* All monitors listed in active-monitor-config are disabled (or option is empty) */
791 
792         /* Using primary monitor */
793         if(!active)
794         {
795             gint num = greeter_screen_get_primary_monitor(priv->screen);
796             if ((guint)num >= priv->monitors_size)
797                 goto active_monitor_change_complete;
798 
799             active = &priv->monitors[num];
800             if(!active->background || !greeter_background_monitor_enabled(background, active))
801                 active = NULL;
802             if(active)
803                 g_debug("[Background] Active monitor is not specified, using primary monitor");
804         }
805 
806         /* Fallback: first enabled and/or not skipped monitor (screen always have one) */
807         if(!active)
808         {
809             guint i;
810             const Monitor* first_not_skipped = NULL;
811             for(i = 0; i < priv->monitors_size && !active; ++i)
812             {
813                 const Monitor* monitor = &priv->monitors[i];
814                 if(!monitor->background)
815                     continue;
816                 if(greeter_background_monitor_enabled(background, monitor))
817                     active = monitor;
818                 if(!first_not_skipped)
819                     first_not_skipped = active;
820             }
821             if(!active)
822                 active = first_not_skipped;
823             if(active)
824                 g_debug("[Background] Active monitor is not specified, using first enabled monitor");
825         }
826 
827         if(!active && priv->laptop_monitors)
828         {
829             active = priv->laptop_monitors->data;
830             g_debug("[Background] Active monitor is not specified, using laptop monitor");
831         }
832     }
833 
834     if(!active)
835     {
836         if(priv->active_monitor)
837             g_warning("[Background] Active monitor is not specified, failed to identify. Active monitor stays the same: %s #%d",
838                       priv->active_monitor->name, priv->active_monitor->number);
839         else
840             g_warning("[Background] Active monitor is not specified, failed to identify. Active monitor stays the same: <not defined>");
841         goto active_monitor_change_complete;
842     }
843 
844     if(active == priv->active_monitor)
845         goto active_monitor_change_complete;
846 
847     priv->active_monitor = active;
848 
849     if (priv->active_monitor == NULL)
850         goto active_monitor_change_complete;
851 
852     if(priv->child)
853     {
854         GtkWidget* old_parent = gtk_widget_get_parent(priv->child);
855         gpointer focus = greeter_save_focus(priv->child);
856 
857         if(old_parent)
858             greeter_widget_reparent(priv->child, GTK_WIDGET(active->window));
859         else
860             gtk_container_add(GTK_CONTAINER(active->window), priv->child);
861 
862         gtk_window_present(active->window);
863         greeter_restore_focus(focus);
864         g_free(focus);
865     }
866     else
867         g_warning("[Background] Child widget is destroyed or not defined");
868 
869     g_debug("[Background] Active monitor changed to: %s #%d", active->name, active->number);
870     g_signal_emit(background, background_signals[BACKGROUND_SIGNAL_ACTIVE_MONITOR_CHANGED], 0);
871 
872     greeter_background_get_cursor_position(background, &x, &y);
873     /* Do not center cursor if it is already inside active monitor */
874     if(x < active->geometry.x || x >= active->geometry.x + active->geometry.width ||
875        y < active->geometry.y || y >= active->geometry.y + active->geometry.height)
876         greeter_background_set_cursor_position(background,
877                                                active->geometry.x + active->geometry.width/2,
878                                                active->geometry.y + active->geometry.height/2);
879 
880 active_monitor_change_complete:
881     priv->active_monitor_change_in_progress = FALSE;
882     return;
883 }
884 
885 static void
greeter_background_get_cursor_position(GreeterBackground * background,gint * x,gint * y)886 greeter_background_get_cursor_position(GreeterBackground* background,
887                                        gint* x, gint* y)
888 {
889     GreeterBackgroundPrivate* priv = background->priv;
890 
891     GdkDisplay* display = gdk_screen_get_display(priv->screen);
892     GdkDeviceManager* device_manager = greeter_display_get_device_manager(display);
893     GdkDevice* device = greeter_device_manager_get_client_pointer(device_manager);
894     gdk_device_get_position(device, NULL, x, y);
895 }
896 
897 static void
greeter_background_set_cursor_position(GreeterBackground * background,gint x,gint y)898 greeter_background_set_cursor_position(GreeterBackground* background,
899                                        gint x, gint y)
900 {
901     GreeterBackgroundPrivate* priv = background->priv;
902 
903     GdkDisplay* display = gdk_screen_get_display(priv->screen);
904     GdkDeviceManager* device_manager = greeter_display_get_device_manager(display);
905     gdk_device_warp(greeter_device_manager_get_client_pointer(device_manager), priv->screen, x, y);
906 }
907 
908 static void
greeter_background_try_init_dbus(GreeterBackground * background)909 greeter_background_try_init_dbus(GreeterBackground* background)
910 {
911     GreeterBackgroundPrivate *priv;
912     GError                   *error = NULL;
913     GVariant                 *variant;
914     gboolean                  lid_present;
915 
916     g_debug("[Background] Creating DBus proxy");
917 
918     priv = background->priv;
919 
920     if(priv->laptop_upower_proxy)
921         greeter_background_stop_dbus(background);
922 
923     priv->laptop_upower_proxy = g_dbus_proxy_new_for_bus_sync(
924                                             G_BUS_TYPE_SYSTEM,
925                                             G_DBUS_PROXY_FLAGS_NONE,
926                                             NULL,   /* interface info */
927                                             DBUS_UPOWER_NAME,
928                                             DBUS_UPOWER_PATH,
929                                             DBUS_UPOWER_INTERFACE,
930                                             NULL,   /* cancellable */
931                                             &error);
932     if(!priv->laptop_upower_proxy)
933     {
934         if(error)
935             g_warning("[Background] Failed to create dbus proxy: %s", error->message);
936         g_clear_error(&error);
937         return;
938     }
939 
940     variant = g_dbus_proxy_get_cached_property(priv->laptop_upower_proxy, DBUS_UPOWER_PROP_LID_IS_PRESENT);
941     lid_present = g_variant_get_boolean(variant);
942     g_variant_unref(variant);
943 
944     g_debug("[Background] UPower.%s property value: %d", DBUS_UPOWER_PROP_LID_IS_PRESENT, lid_present);
945 
946     if(!lid_present)
947         greeter_background_stop_dbus(background);
948     else
949     {
950         variant = g_dbus_proxy_get_cached_property(priv->laptop_upower_proxy, DBUS_UPOWER_PROP_LID_IS_CLOSED);
951         priv->laptop_lid_closed = g_variant_get_boolean(variant);
952         g_variant_unref(variant);
953 
954         g_signal_connect(priv->laptop_upower_proxy, "g-properties-changed",
955                          G_CALLBACK(greeter_background_dbus_changed_cb), background);
956     }
957 }
958 
959 static void
greeter_background_stop_dbus(GreeterBackground * background)960 greeter_background_stop_dbus(GreeterBackground* background)
961 {
962     if(!background->priv->laptop_upower_proxy)
963         return;
964     g_clear_object(&background->priv->laptop_upower_proxy);
965 }
966 
967 static gboolean
greeter_background_monitor_enabled(GreeterBackground * background,const Monitor * monitor)968 greeter_background_monitor_enabled(GreeterBackground* background,
969                                    const Monitor* monitor)
970 {
971     GreeterBackgroundPrivate* priv = background->priv;
972 
973     if(priv->laptop_upower_proxy && g_slist_find(priv->laptop_monitors, monitor))
974         return !priv->laptop_lid_closed;
975     return TRUE;
976 }
977 
978 static void
greeter_background_dbus_changed_cb(GDBusProxy * proxy,GVariant * changed_properties,const gchar * const * invalidated_properties,GreeterBackground * background)979 greeter_background_dbus_changed_cb(GDBusProxy* proxy,
980                                    GVariant* changed_properties,
981                                    const gchar* const* invalidated_properties,
982                                    GreeterBackground* background)
983 {
984     GreeterBackgroundPrivate *priv;
985     GVariant                 *variant;
986     gboolean                  new_state;
987 
988     g_return_if_fail(GREETER_IS_BACKGROUND(background));
989 
990     priv = background->priv;
991 
992     variant = g_dbus_proxy_get_cached_property(priv->laptop_upower_proxy, DBUS_UPOWER_PROP_LID_IS_CLOSED);
993     new_state = g_variant_get_boolean(variant);
994     g_variant_unref(variant);
995 
996     if(new_state == priv->laptop_lid_closed)
997         return;
998 
999     priv->laptop_lid_closed = new_state;
1000     g_debug("[Background] UPower: lid state changed to '%s'", priv->laptop_lid_closed ? "closed" : "opened");
1001 
1002     if(priv->laptop_monitors)
1003     {
1004         if(priv->laptop_lid_closed)
1005         {
1006             if(g_slist_find(priv->laptop_monitors, priv->active_monitor))
1007                 greeter_background_set_active_monitor(background, NULL);
1008         }
1009         else
1010         {
1011             if(!priv->follow_cursor)
1012                 greeter_background_set_active_monitor(background, NULL);
1013         }
1014     }
1015 }
1016 
1017 static void
greeter_background_monitors_changed_cb(GdkScreen * screen,GreeterBackground * background)1018 greeter_background_monitors_changed_cb(GdkScreen* screen,
1019                                        GreeterBackground* background)
1020 {
1021     g_return_if_fail(GREETER_IS_BACKGROUND(background));
1022     greeter_background_connect(background, screen);
1023 }
1024 
1025 static void
greeter_background_child_destroyed_cb(GtkWidget * child,GreeterBackground * background)1026 greeter_background_child_destroyed_cb(GtkWidget* child,
1027                                       GreeterBackground* background)
1028 {
1029     background->priv->child = NULL;
1030 }
1031 
1032 void
greeter_background_set_custom_background(GreeterBackground * background,const gchar * value)1033 greeter_background_set_custom_background(GreeterBackground* background,
1034                                          const gchar* value)
1035 {
1036     GreeterBackgroundPrivate *priv;
1037     GHashTable               *images_cache = NULL;
1038     GSList                   *iter;
1039 
1040     g_return_if_fail(GREETER_IS_BACKGROUND(background));
1041 
1042     priv = background->priv;
1043 
1044     if(priv->customized_background.type != BACKGROUND_TYPE_INVALID)
1045         background_config_finalize(&priv->customized_background);
1046     background_config_initialize(&priv->customized_background, value);
1047 
1048     if(!priv->customized_monitors)
1049         return;
1050 
1051     if(priv->customized_background.type == BACKGROUND_TYPE_IMAGE)
1052         images_cache = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_object_unref);
1053 
1054     for(iter = priv->customized_monitors; iter; iter = g_slist_next(iter))
1055     {
1056         Monitor *monitor = iter->data;
1057 
1058         /* Old background_custom (if used) will be unrefed in monitor_set_background() */
1059         Background* bg = NULL;
1060         if(priv->customized_background.type != BACKGROUND_TYPE_INVALID)
1061             bg = background_new(&priv->customized_background, monitor, images_cache);
1062         if(bg)
1063         {
1064             monitor_set_background(monitor, bg);
1065             background_unref(&bg);
1066         }
1067         else
1068             monitor_set_background(monitor, monitor->background_configured);
1069     }
1070 
1071     if(images_cache)
1072         g_hash_table_unref(images_cache);
1073 }
1074 
1075 void
greeter_background_save_xroot(GreeterBackground * background)1076 greeter_background_save_xroot(GreeterBackground* background)
1077 {
1078     GreeterBackgroundPrivate *priv;
1079     cairo_surface_t          *surface;
1080     cairo_t                  *cr;
1081     gsize                     i;
1082 
1083     const GdkRGBA             ROOT_COLOR = {1.0, 1.0, 1.0, 1.0};
1084 
1085     g_return_if_fail(GREETER_IS_BACKGROUND(background));
1086 
1087     priv = background->priv;
1088     surface = create_root_surface(priv->screen);
1089     cr = cairo_create(surface);
1090 
1091     gdk_cairo_set_source_rgba(cr, &ROOT_COLOR);
1092     cairo_paint(cr);
1093 
1094     for(i = 0; i < priv->monitors_size; ++i)
1095     {
1096         const Monitor* monitor = &priv->monitors[i];
1097         if(!monitor->background)
1098             continue;
1099         cairo_save(cr);
1100         cairo_translate(cr, monitor->geometry.x, monitor->geometry.y);
1101         monitor_draw_background(monitor, monitor->background, cr);
1102         cairo_restore(cr);
1103     }
1104     set_surface_as_root(priv->screen, surface);
1105 
1106     cairo_destroy(cr);
1107     cairo_surface_destroy(surface);
1108 }
1109 
1110 const GdkRectangle*
greeter_background_get_active_monitor_geometry(GreeterBackground * background)1111 greeter_background_get_active_monitor_geometry(GreeterBackground* background)
1112 {
1113     GreeterBackgroundPrivate *priv;
1114 
1115     g_return_val_if_fail(GREETER_IS_BACKGROUND(background), NULL);
1116 
1117     priv = background->priv;
1118 
1119     return priv->active_monitor ? &priv->active_monitor->geometry : NULL;
1120 }
1121 
1122 void
greeter_background_add_accel_group(GreeterBackground * background,GtkAccelGroup * group)1123 greeter_background_add_accel_group(GreeterBackground* background,
1124                                    GtkAccelGroup* group)
1125 {
1126     GreeterBackgroundPrivate *priv;
1127 
1128     g_return_if_fail(GREETER_IS_BACKGROUND(background));
1129     g_return_if_fail(group != NULL);
1130 
1131     priv = background->priv;
1132 
1133     if(priv->monitors)
1134     {
1135         guint i;
1136         for(i = 0; i < priv->monitors_size; ++i)
1137             if(priv->monitors[i].window)
1138                 gtk_window_add_accel_group(priv->monitors[i].window, group);
1139     }
1140 
1141     priv->accel_groups = g_slist_append(priv->accel_groups, group);
1142 }
1143 
1144 static gboolean
background_config_initialize(BackgroundConfig * config,const gchar * value)1145 background_config_initialize(BackgroundConfig* config,
1146                              const gchar* value)
1147 {
1148     config->type = BACKGROUND_TYPE_INVALID;
1149     if(!value || strlen(value) == 0)
1150         return FALSE;
1151     if(g_strcmp0(value, BACKGROUND_TYPE_SKIP_VALUE) == 0)
1152         config->type = BACKGROUND_TYPE_SKIP;
1153     else if(g_strcmp0(value, BACKGROUND_TYPE_DEFAULT_VALUE) == 0)
1154         config->type = BACKGROUND_TYPE_DEFAULT;
1155     else if(gdk_rgba_parse(&config->options.color, value))
1156         config->type = BACKGROUND_TYPE_COLOR;
1157     else
1158     {
1159         const gchar** prefix = SCALING_MODE_PREFIXES;
1160         while(*prefix && !g_str_has_prefix(value, *prefix))
1161             ++prefix;
1162 
1163         if(*prefix)
1164         {
1165             config->options.image.mode = (ScalingMode)(prefix - SCALING_MODE_PREFIXES);
1166             value += strlen(*prefix);
1167         }
1168         else
1169             config->options.image.mode = SCALING_MODE_ZOOMED;
1170 
1171         config->options.image.path = g_strdup(value);
1172         config->type = BACKGROUND_TYPE_IMAGE;
1173     }
1174     return TRUE;
1175 }
1176 
1177 static void
background_config_finalize(BackgroundConfig * config)1178 background_config_finalize(BackgroundConfig* config)
1179 {
1180     switch(config->type)
1181     {
1182         case BACKGROUND_TYPE_IMAGE:
1183             g_free(config->options.image.path);
1184             break;
1185         case BACKGROUND_TYPE_COLOR:
1186         case BACKGROUND_TYPE_DEFAULT:
1187         case BACKGROUND_TYPE_SKIP:
1188             break;
1189         case BACKGROUND_TYPE_INVALID:
1190         default:
1191             g_return_if_reached();
1192     }
1193 
1194     config->type = BACKGROUND_TYPE_INVALID;
1195 }
1196 
1197 static void
background_config_copy(const BackgroundConfig * source,BackgroundConfig * dest)1198 background_config_copy(const BackgroundConfig* source,
1199                        BackgroundConfig* dest)
1200 {
1201     *dest = *source;
1202 
1203     switch(dest->type)
1204     {
1205         case BACKGROUND_TYPE_IMAGE:
1206             dest->options.image.path = g_strdup(source->options.image.path);
1207             break;
1208         case BACKGROUND_TYPE_COLOR:
1209         case BACKGROUND_TYPE_DEFAULT:
1210         case BACKGROUND_TYPE_SKIP:
1211             break;
1212         case BACKGROUND_TYPE_INVALID:
1213         default:
1214             g_return_if_reached();
1215     }
1216 }
1217 
1218 static void
monitor_config_free(MonitorConfig * config)1219 monitor_config_free(MonitorConfig* config)
1220 {
1221     background_config_finalize(&config->bg);
1222     g_free(config);
1223 }
1224 
monitor_config_copy(const MonitorConfig * source,MonitorConfig * dest)1225 static MonitorConfig* monitor_config_copy(const MonitorConfig* source,
1226                                           MonitorConfig* dest)
1227 {
1228     if(!dest)
1229         dest = g_new0(MonitorConfig, 1);
1230     background_config_copy(&source->bg, &dest->bg);
1231     dest->user_bg = source->user_bg;
1232     dest->laptop = source->laptop;
1233     dest->transition = source->transition;
1234     return dest;
1235 }
1236 
1237 static Background*
background_new(const BackgroundConfig * config,const Monitor * monitor,GHashTable * images_cache)1238 background_new(const BackgroundConfig* config,
1239                const Monitor* monitor,
1240                GHashTable* images_cache)
1241 {
1242     Background *result;
1243     Background  bg = {0};
1244 
1245     switch(config->type)
1246     {
1247         case BACKGROUND_TYPE_IMAGE:
1248             bg.options.image = scale_image_file(config->options.image.path, config->options.image.mode,
1249                                                 monitor->geometry.width, monitor->geometry.height,
1250                                                 images_cache);
1251             if(!bg.options.image)
1252             {
1253                 g_warning("[Background] Failed to read wallpaper: %s", config->options.image.path);
1254                 return NULL;
1255             }
1256             break;
1257         case BACKGROUND_TYPE_COLOR:
1258             bg.options.color = config->options.color;
1259             break;
1260         case BACKGROUND_TYPE_DEFAULT:
1261             break;
1262         case BACKGROUND_TYPE_SKIP:
1263         case BACKGROUND_TYPE_INVALID:
1264         default:
1265             g_return_val_if_reached(NULL);
1266     }
1267 
1268     bg.type = config->type;
1269     bg.ref_count = 1;
1270 
1271     result = g_new(Background, 1);
1272     *result = bg;
1273     return result;
1274 }
1275 
1276 static Background*
background_ref(Background * bg)1277 background_ref(Background* bg)
1278 {
1279     bg->ref_count++;
1280     return bg;
1281 }
1282 
1283 static void
background_unref(Background ** bg)1284 background_unref(Background** bg)
1285 {
1286     if(!*bg)
1287         return;
1288     (*bg)->ref_count--;
1289     if((*bg)->ref_count == 0)
1290     {
1291         background_finalize(*bg);
1292         *bg = NULL;
1293     }
1294 }
1295 
1296 static void
background_finalize(Background * bg)1297 background_finalize(Background* bg)
1298 {
1299     switch(bg->type)
1300     {
1301         case BACKGROUND_TYPE_IMAGE:
1302             g_clear_object(&bg->options.image);
1303             break;
1304         case BACKGROUND_TYPE_COLOR:
1305         case BACKGROUND_TYPE_DEFAULT:
1306             break;
1307         case BACKGROUND_TYPE_SKIP:
1308         case BACKGROUND_TYPE_INVALID:
1309         default:
1310             g_return_if_reached();
1311     }
1312 
1313     bg->type = BACKGROUND_TYPE_INVALID;
1314 }
1315 
1316 static void
monitor_set_background(Monitor * monitor,Background * background)1317 monitor_set_background(Monitor* monitor,
1318                        Background* background)
1319 {
1320     if(monitor->background == background)
1321         return;
1322     monitor_stop_transition(monitor);
1323 
1324     switch(background->type)
1325     {
1326         case BACKGROUND_TYPE_IMAGE:
1327         case BACKGROUND_TYPE_COLOR:
1328             gtk_widget_set_app_paintable(GTK_WIDGET(monitor->window), TRUE);
1329             if(monitor->transition.config.duration > 0 && monitor->background &&
1330                monitor->background->type != BACKGROUND_TYPE_DEFAULT)
1331                 monitor_start_transition(monitor, monitor->background, background);
1332             break;
1333         case BACKGROUND_TYPE_DEFAULT:
1334             gtk_widget_set_app_paintable(GTK_WIDGET(monitor->window), FALSE);
1335             break;
1336         case BACKGROUND_TYPE_SKIP:
1337         case BACKGROUND_TYPE_INVALID:
1338         default:
1339             g_return_if_reached();
1340     }
1341 
1342     background_unref(&monitor->background);
1343     monitor->background = background_ref(background);
1344     gtk_widget_queue_draw(GTK_WIDGET(monitor->window));
1345 }
1346 
1347 static void
monitor_start_transition(Monitor * monitor,Background * from,Background * to)1348 monitor_start_transition(Monitor* monitor,
1349                          Background* from,
1350                          Background* to)
1351 {
1352     monitor_stop_transition(monitor);
1353 
1354     monitor->transition.from = background_ref(from);
1355     monitor->transition.to = background_ref(to);
1356 
1357     monitor->transition.started = g_get_monotonic_time();
1358     monitor->transition.timer_id = gtk_widget_add_tick_callback(GTK_WIDGET(monitor->window),
1359                                                                 (GtkTickCallback)monitor_transition_cb,
1360                                                                 monitor,
1361                                                                 NULL);
1362     monitor->transition.stage = 0;
1363 }
1364 
1365 static void
monitor_stop_transition(Monitor * monitor)1366 monitor_stop_transition(Monitor* monitor)
1367 {
1368     if(!monitor->transition.timer_id)
1369         return;
1370     gtk_widget_remove_tick_callback(GTK_WIDGET(monitor->window), monitor->transition.timer_id);
1371     monitor->transition.timer_id = 0;
1372     monitor->transition.started = 0;
1373     monitor->transition.stage = 0;
1374     background_unref(&monitor->transition.to);
1375     background_unref(&monitor->transition.from);
1376 }
1377 
1378 static gboolean
monitor_transition_cb(GtkWidget * widget,GdkFrameClock * frame_clock,Monitor * monitor)1379 monitor_transition_cb(GtkWidget *widget,
1380                       GdkFrameClock* frame_clock,
1381                       Monitor* monitor)
1382 {
1383     gint64  span;
1384     gdouble x;
1385 
1386     if(!monitor->transition.timer_id)
1387         return G_SOURCE_REMOVE;
1388 
1389     span = g_get_monotonic_time() - monitor->transition.started;
1390     x = CLAMP(span/monitor->transition.config.duration/1000.0, 0.0, 1.0);
1391     monitor->transition.stage = monitor->transition.config.func(x);
1392 
1393     if(x >= 1.0)
1394         monitor_stop_transition(monitor);
1395 
1396     gtk_widget_queue_draw(GTK_WIDGET(monitor->window));
1397     return x >= 1.0 ? G_SOURCE_REMOVE : G_SOURCE_CONTINUE;
1398 }
1399 
1400 static void
monitor_transition_draw_alpha(const Monitor * monitor,cairo_t * cr)1401 monitor_transition_draw_alpha(const Monitor* monitor,
1402                               cairo_t* cr)
1403 {
1404     cairo_pattern_t* alpha_pattern;
1405 
1406     monitor_draw_background(monitor, monitor->transition.from, cr);
1407 
1408     cairo_push_group(cr);
1409     monitor_draw_background(monitor, monitor->transition.to, cr);
1410     cairo_pop_group_to_source(cr);
1411 
1412     alpha_pattern = cairo_pattern_create_rgba(0.0, 0.0, 0.0, monitor->transition.stage);
1413     cairo_mask(cr, alpha_pattern);
1414     cairo_pattern_destroy(alpha_pattern);
1415 }
1416 
1417 static void
monitor_finalize(Monitor * monitor)1418 monitor_finalize(Monitor* monitor)
1419 {
1420     if(monitor->transition.config.duration)
1421     {
1422         monitor_stop_transition(monitor);
1423         if(monitor->transition.timer_id)
1424             g_source_remove(monitor->transition.timer_id);
1425         monitor->transition.config.duration = 0;
1426     }
1427 
1428     if(monitor->window_draw_handler_id)
1429         g_signal_handler_disconnect(monitor->window, monitor->window_draw_handler_id);
1430 
1431     background_unref(&monitor->background_configured);
1432     background_unref(&monitor->background);
1433 
1434     if(monitor->window)
1435     {
1436         GtkWidget* child = gtk_bin_get_child(GTK_BIN(monitor->window));
1437         if(child) /* remove greeter widget to avoid "destroy" signal */
1438             gtk_container_remove(GTK_CONTAINER(monitor->window), child);
1439         gtk_widget_destroy(GTK_WIDGET(monitor->window));
1440     }
1441 
1442     g_free(monitor->name);
1443 
1444     *monitor = INVALID_MONITOR_STRUCT;
1445 }
1446 
1447 static void
monitor_draw_background(const Monitor * monitor,const Background * background,cairo_t * cr)1448 monitor_draw_background(const Monitor* monitor,
1449                         const Background* background,
1450                         cairo_t* cr)
1451 {
1452     g_return_if_fail(monitor != NULL);
1453     g_return_if_fail(background != NULL);
1454 
1455     switch(background->type)
1456     {
1457         case BACKGROUND_TYPE_IMAGE:
1458             if(background->options.image)
1459             {
1460                 gdk_cairo_set_source_pixbuf(cr, background->options.image, 0, 0);
1461                 cairo_paint(cr);
1462             }
1463             break;
1464         case BACKGROUND_TYPE_COLOR:
1465             cairo_rectangle(cr, 0, 0, monitor->geometry.width, monitor->geometry.height);
1466             gdk_cairo_set_source_rgba(cr, &background->options.color);
1467             cairo_fill(cr);
1468             break;
1469         case BACKGROUND_TYPE_DEFAULT:
1470             break;
1471         case BACKGROUND_TYPE_SKIP:
1472         case BACKGROUND_TYPE_INVALID:
1473         default:
1474             g_return_if_reached();
1475     }
1476 }
1477 
1478 static gboolean
monitor_window_draw_cb(GtkWidget * widget,cairo_t * cr,const Monitor * monitor)1479 monitor_window_draw_cb(GtkWidget* widget,
1480                        cairo_t* cr,
1481                        const Monitor* monitor)
1482 {
1483     if(!monitor->background)
1484         return FALSE;
1485 
1486     if(monitor->transition.started)
1487         monitor->transition.config.draw(monitor, cr);
1488     else
1489         monitor_draw_background(monitor, monitor->background, cr);
1490 
1491     return FALSE;
1492 }
1493 
1494 static gboolean
monitor_window_enter_notify_cb(GtkWidget * widget,GdkEventCrossing * event,const Monitor * monitor)1495 monitor_window_enter_notify_cb(GtkWidget* widget,
1496                                GdkEventCrossing* event,
1497                                const Monitor* monitor)
1498 {
1499     if(monitor->object->priv->active_monitor == monitor)
1500     {
1501         GdkWindow *gdkwindow = gtk_widget_get_window (widget);
1502         Window window = GDK_WINDOW_XID (gdkwindow);
1503         Display *display = GDK_WINDOW_XDISPLAY (gdkwindow);
1504         XEvent ev = {0};
1505 
1506         static Atom wm_protocols = None;
1507         static Atom wm_take_focus = None;
1508 
1509         if (!wm_protocols)
1510             wm_protocols = XInternAtom(display, "WM_PROTOCOLS", False);
1511         if (!wm_take_focus)
1512             wm_take_focus = XInternAtom(display, "WM_TAKE_FOCUS", False);
1513 
1514         ev.xclient.type = ClientMessage;
1515         ev.xclient.window = window;
1516         ev.xclient.message_type = wm_protocols;
1517         ev.xclient.format = 32;
1518         ev.xclient.data.l[0] = wm_take_focus;
1519         ev.xclient.data.l[1] = CurrentTime;
1520         XSendEvent(display, window, False, 0L, &ev);
1521     }
1522     else if(monitor->object->priv->follow_cursor && greeter_background_monitor_enabled(monitor->object, monitor))
1523         greeter_background_set_active_monitor(monitor->object, monitor);
1524     return FALSE;
1525 }
1526 
1527 static GdkPixbuf*
scale_image_file(const gchar * path,ScalingMode mode,gint width,gint height,GHashTable * cache)1528 scale_image_file(const gchar* path,
1529                  ScalingMode mode,
1530                  gint width, gint height,
1531                  GHashTable* cache)
1532 {
1533     gchar* key = NULL;
1534     GdkPixbuf* pixbuf = NULL;
1535 
1536     if(cache)
1537     {
1538         key = g_strdup_printf("%s\n%d %dx%d", path, mode, width, height);
1539         if(g_hash_table_lookup_extended(cache, key, NULL, (gpointer*)&pixbuf))
1540         {
1541             g_free(key);
1542             return GDK_PIXBUF(g_object_ref(pixbuf));
1543         }
1544     }
1545 
1546     if(!cache || !g_hash_table_lookup_extended(cache, path, NULL, (gpointer*)&pixbuf))
1547     {
1548         GError *error = NULL;
1549         pixbuf = gdk_pixbuf_new_from_file(path, &error);
1550         if(error)
1551         {
1552             g_warning("[Background] Failed to load background: %s", error->message);
1553             g_clear_error(&error);
1554         }
1555         else if(cache)
1556             g_hash_table_insert(cache, g_strdup(path), g_object_ref(pixbuf));
1557     }
1558     else
1559         pixbuf = g_object_ref(pixbuf);
1560 
1561     if(pixbuf)
1562     {
1563         GdkPixbuf* scaled = scale_image(pixbuf, mode, width, height);
1564         if(cache)
1565             g_hash_table_insert(cache, g_strdup(key), g_object_ref(scaled));
1566         g_object_unref(pixbuf);
1567         pixbuf = scaled;
1568     }
1569 
1570     g_free(key);
1571 
1572     return pixbuf;
1573 }
1574 
1575 static GdkPixbuf*
scale_image(GdkPixbuf * source,ScalingMode mode,gint width,gint height)1576 scale_image(GdkPixbuf* source,
1577             ScalingMode mode,
1578             gint width, gint height)
1579 {
1580     if(mode == SCALING_MODE_ZOOMED)
1581     {
1582         gint offset_x = 0;
1583         gint offset_y = 0;
1584         gint p_width = gdk_pixbuf_get_width(source);
1585         gint p_height = gdk_pixbuf_get_height(source);
1586         gdouble scale_x = (gdouble)width / p_width;
1587         gdouble scale_y = (gdouble)height / p_height;
1588         GdkPixbuf *pixbuf;
1589 
1590         if(scale_x < scale_y)
1591         {
1592             scale_x = scale_y;
1593             offset_x = (width - (p_width * scale_x)) / 2;
1594         }
1595         else
1596         {
1597             scale_y = scale_x;
1598             offset_y = (height - (p_height * scale_y)) / 2;
1599         }
1600 
1601         pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE,
1602                                            gdk_pixbuf_get_bits_per_sample (source),
1603                                            width, height);
1604         gdk_pixbuf_composite(source, pixbuf, 0, 0, width, height,
1605                              offset_x, offset_y, scale_x, scale_y, GDK_INTERP_BILINEAR, 0xFF);
1606         return pixbuf;
1607     }
1608     else if(mode == SCALING_MODE_STRETCHED)
1609     {
1610         return gdk_pixbuf_scale_simple(source, width, height, GDK_INTERP_BILINEAR);
1611     }
1612     return GDK_PIXBUF(g_object_ref(source));
1613 }
1614 
1615 /* The following code for setting a RetainPermanent background pixmap was taken
1616    originally from Gnome, with some fixes from MATE. see:
1617    https://github.com/mate-desktop/mate-desktop/blob/master/libmate-desktop/mate-bg.c */
1618 static cairo_surface_t*
create_root_surface(GdkScreen * screen)1619 create_root_surface(GdkScreen* screen)
1620 {
1621     gint number, width, height;
1622     Display *display;
1623     Pixmap pixmap;
1624     cairo_surface_t *surface;
1625 
1626     number = greeter_screen_get_number (screen);
1627     width = greeter_screen_get_width (screen);
1628     height = greeter_screen_get_height (screen);
1629 
1630     /* Open a new connection so with Retain Permanent so the pixmap remains when the greeter quits */
1631     greeter_gdk_flush ();
1632     display = XOpenDisplay (gdk_display_get_name (gdk_screen_get_display (screen)));
1633     if (!display)
1634     {
1635         g_warning("[Background] Failed to create root pixmap");
1636         return NULL;
1637     }
1638 
1639     XSetCloseDownMode (display, RetainPermanent);
1640     pixmap = XCreatePixmap (display, RootWindow (display, number), width, height, DefaultDepth (display, number));
1641     XCloseDisplay (display);
1642 
1643     /* Convert into a Cairo surface */
1644     surface = cairo_xlib_surface_create (GDK_SCREEN_XDISPLAY (screen),
1645                                          pixmap,
1646                                          GDK_VISUAL_XVISUAL (gdk_screen_get_system_visual (screen)),
1647                                          width, height);
1648 
1649     return surface;
1650 }
1651 
1652 /* Sets the "ESETROOT_PMAP_ID" property to later be used to free the pixmap */
1653 static void
set_root_pixmap_id(GdkScreen * screen,Display * display,Pixmap xpixmap)1654 set_root_pixmap_id(GdkScreen* screen,
1655                    Display* display,
1656                    Pixmap xpixmap)
1657 {
1658 
1659     Window xroot = RootWindow (display, greeter_screen_get_number (screen));
1660     const char *atom_names[] = {"_XROOTPMAP_ID", "ESETROOT_PMAP_ID"};
1661     Atom atoms[G_N_ELEMENTS(atom_names)] = {0};
1662 
1663     Atom type;
1664     int format;
1665     unsigned long nitems, after;
1666     unsigned char *data_root, *data_esetroot;
1667 
1668     /* Get atoms for both properties in an array, only if they exist.
1669      * This method is to avoid multiple round-trips to Xserver
1670      */
1671     if (XInternAtoms (display, (char **)atom_names, G_N_ELEMENTS(atom_names), True, atoms) &&
1672         atoms[0] != None && atoms[1] != None)
1673     {
1674 
1675         XGetWindowProperty (display, xroot, atoms[0], 0L, 1L, False, AnyPropertyType,
1676                             &type, &format, &nitems, &after, &data_root);
1677         if (data_root && type == XA_PIXMAP && format == 32 && nitems == 1)
1678         {
1679             XGetWindowProperty (display, xroot, atoms[1], 0L, 1L, False, AnyPropertyType,
1680                                 &type, &format, &nitems, &after, &data_esetroot);
1681             if (data_esetroot && type == XA_PIXMAP && format == 32 && nitems == 1)
1682             {
1683                 Pixmap xrootpmap = *((Pixmap *) data_root);
1684                 Pixmap esetrootpmap = *((Pixmap *) data_esetroot);
1685                 XFree (data_root);
1686                 XFree (data_esetroot);
1687 
1688                 greeter_error_trap_push ();
1689                 if (xrootpmap && xrootpmap == esetrootpmap) {
1690                     XKillClient (display, xrootpmap);
1691                 }
1692                 if (esetrootpmap && esetrootpmap != xrootpmap) {
1693                     XKillClient (display, esetrootpmap);
1694                 }
1695 
1696                 XSync (display, False);
1697                 greeter_error_trap_pop_ignored ();
1698             }
1699         }
1700     }
1701 
1702     /* Get atoms for both properties in an array, create them if needed.
1703      * This method is to avoid multiple round-trips to Xserver
1704      */
1705     if (!XInternAtoms (display, (char **)atom_names, G_N_ELEMENTS(atom_names), False, atoms) ||
1706         atoms[0] == None || atoms[1] == None) {
1707         g_warning("[Background] Could not create atoms needed to set root pixmap id/properties.\n");
1708         return;
1709     }
1710 
1711     /* Set new _XROOTMAP_ID and ESETROOT_PMAP_ID properties */
1712     XChangeProperty (display, xroot, atoms[0], XA_PIXMAP, 32,
1713                      PropModeReplace, (unsigned char *) &xpixmap, 1);
1714 
1715     XChangeProperty (display, xroot, atoms[1], XA_PIXMAP, 32,
1716                      PropModeReplace, (unsigned char *) &xpixmap, 1);
1717 }
1718 
1719 /**
1720 * set_surface_as_root:
1721 * @screen: the #GdkScreen to change root background on
1722 * @surface: the #cairo_surface_t to set root background from.
1723 * Must be an xlib surface backing a pixmap.
1724 *
1725 * Set the root pixmap, and properties pointing to it. We
1726 * do this atomically with a server grab to make sure that
1727 * we won't leak the pixmap if somebody else it setting
1728 * it at the same time. (This assumes that they follow the
1729 * same conventions we do). @surface should come from a call
1730 * to create_root_surface().
1731 **/
1732 static void
set_surface_as_root(GdkScreen * screen,cairo_surface_t * surface)1733 set_surface_as_root(GdkScreen* screen,
1734                     cairo_surface_t* surface)
1735 {
1736     Display *display;
1737     Pixmap   pixmap_id;
1738     Window   xroot;
1739 
1740     g_return_if_fail(cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_XLIB);
1741 
1742     /* Desktop background pixmap should be created from dummy X client since most
1743      * applications will try to kill it with XKillClient later when changing pixmap
1744      */
1745     display = GDK_DISPLAY_XDISPLAY (gdk_screen_get_display (screen));
1746     pixmap_id = cairo_xlib_surface_get_drawable (surface);
1747     xroot = RootWindow (display, greeter_screen_get_number(screen));
1748 
1749     XGrabServer (display);
1750 
1751     XSetWindowBackgroundPixmap (display, xroot, pixmap_id);
1752     set_root_pixmap_id (screen, display, pixmap_id);
1753     XClearWindow (display, xroot);
1754 
1755     XFlush (display);
1756     XUngrabServer (display);
1757 }
1758 
1759 static gdouble
transition_func_linear(gdouble x)1760 transition_func_linear(gdouble x)
1761 {
1762     return x;
1763 }
1764 
1765 static gdouble
transition_func_ease_in_out(gdouble x)1766 transition_func_ease_in_out(gdouble x)
1767 {
1768     return (1 - cos(M_PI*x))/2;
1769 }
1770