No issues found
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2
3 /*
4 * nautilus-desktop-background.c: Helper object to handle desktop background
5 * changes.
6 *
7 * Copyright (C) 2000 Eazel, Inc.
8 * Copyright (C) 2010 Cosimo Cecchi <cosimoc@gnome.org>
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License as
12 * published by the Free Software Foundation; either version 2 of the
13 * License, or (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public
21 * License along with this program; if not, write to the
22 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23 * Boston, MA 02111-1307, USA.
24 *
25 * Authors: Darin Adler <darin@bentspoon.com>
26 * Cosimo Cecchi <cosimoc@gnome.org>
27 */
28
29 #include <config.h>
30
31 #include "nautilus-desktop-background.h"
32
33 #include <eel/eel-gdk-extensions.h>
34 #include <eel/eel-gtk-extensions.h>
35
36 #include "nautilus-global-preferences.h"
37
38 #define GNOME_DESKTOP_USE_UNSTABLE_API
39 #include <libgnome-desktop/gnome-bg.h>
40 #include <gdesktop-enums.h>
41
42 #include <gtk/gtk.h>
43 #include <string.h>
44
45 static void init_fade (NautilusDesktopBackground *self);
46 static void free_fade (NautilusDesktopBackground *self);
47 static void queue_background_change (NautilusDesktopBackground *self);
48
49 static NautilusDesktopBackground *singleton = NULL;
50
51 G_DEFINE_TYPE (NautilusDesktopBackground, nautilus_desktop_background, G_TYPE_OBJECT);
52
53 enum {
54 PROP_WIDGET = 1,
55 NUM_PROPERTIES,
56 };
57
58 struct NautilusDesktopBackgroundDetails {
59
60 GtkWidget *widget;
61 GnomeBG *bg;
62
63 /* Realized data: */
64 cairo_surface_t *background_surface;
65 GnomeBGCrossfade *fade;
66 int background_entire_width;
67 int background_entire_height;
68 GdkColor default_color;
69
70 /* Desktop screen size watcher */
71 gulong screen_size_handler;
72 /* Desktop monitors configuration watcher */
73 gulong screen_monitors_handler;
74 guint change_idle_id;
75 };
76
77
78 static gboolean
79 background_settings_change_event_cb (GSettings *settings,
80 gpointer keys,
81 gint n_keys,
82 gpointer user_data);
83
84
85 static void
86 free_fade (NautilusDesktopBackground *self)
87 {
88 if (self->details->fade != NULL) {
89 g_object_unref (self->details->fade);
90 self->details->fade = NULL;
91 }
92 }
93
94 static void
95 free_background_surface (NautilusDesktopBackground *self)
96 {
97 cairo_surface_t *surface;
98
99 surface = self->details->background_surface;
100 if (surface != NULL) {
101 cairo_surface_destroy (surface);
102 self->details->background_surface = NULL;
103 }
104 }
105
106 static void
107 nautilus_desktop_background_finalize (GObject *object)
108 {
109 NautilusDesktopBackground *self;
110
111 self = NAUTILUS_DESKTOP_BACKGROUND (object);
112
113 g_signal_handlers_disconnect_by_func (gnome_background_preferences,
114 background_settings_change_event_cb,
115 self);
116
117 free_background_surface (self);
118 free_fade (self);
119
120 g_clear_object (&self->details->bg);
121
122 G_OBJECT_CLASS (nautilus_desktop_background_parent_class)->finalize (object);
123 }
124
125 static void
126 nautilus_desktop_background_unrealize (NautilusDesktopBackground *self)
127 {
128 free_background_surface (self);
129
130 self->details->background_entire_width = 0;
131 self->details->background_entire_height = 0;
132 self->details->default_color.red = 0xffff;
133 self->details->default_color.green = 0xffff;
134 self->details->default_color.blue = 0xffff;
135 }
136
137 static void
138 nautilus_desktop_background_set_image_uri (NautilusDesktopBackground *self,
139 const char *image_uri)
140 {
141 char *filename;
142
143 if (image_uri != NULL) {
144 filename = g_filename_from_uri (image_uri, NULL, NULL);
145 }
146 else {
147 filename = NULL;
148 }
149
150 gnome_bg_set_filename (self->details->bg, filename);
151
152 g_free (filename);
153 }
154
155 static void
156 init_fade (NautilusDesktopBackground *self)
157 {
158 GtkWidget *widget;
159 gboolean do_fade;
160
161 widget = self->details->widget;
162
163 if (widget == NULL || !gtk_widget_get_realized (widget))
164 return;
165
166 do_fade = g_settings_get_boolean (nautilus_desktop_preferences,
167 NAUTILUS_PREFERENCES_DESKTOP_BACKGROUND_FADE);
168
169 if (!do_fade) {
170 return;
171 }
172
173 if (self->details->fade == NULL) {
174 GdkWindow *window;
175 GdkScreen *screen;
176 int old_width, old_height, width, height;
177
178 /* If this was the result of a screen size change,
179 * we don't want to crossfade
180 */
181 window = gtk_widget_get_window (widget);
182 old_width = gdk_window_get_width (window);
183 old_height = gdk_window_get_height (window);
184
185 screen = gtk_widget_get_screen (widget);
186 width = gdk_screen_get_width (screen);
187 height = gdk_screen_get_height (screen);
188
189 if (old_width == width && old_height == height) {
190 self->details->fade = gnome_bg_crossfade_new (width, height);
191 g_signal_connect_swapped (self->details->fade,
192 "finished",
193 G_CALLBACK (free_fade),
194 self);
195 }
196 }
197
198 if (self->details->fade != NULL && !gnome_bg_crossfade_is_started (self->details->fade)) {
199 cairo_surface_t *start_surface;
200
201 if (self->details->background_surface == NULL) {
202 start_surface = gnome_bg_get_surface_from_root (gtk_widget_get_screen (widget));
203 } else {
204 start_surface = cairo_surface_reference (self->details->background_surface);
205 }
206 gnome_bg_crossfade_set_start_surface (self->details->fade,
207 start_surface);
208 cairo_surface_destroy (start_surface);
209 }
210 }
211
212 static void
213 screen_size_changed (GdkScreen *screen,
214 NautilusDesktopBackground *self)
215 {
216 queue_background_change (self);
217 }
218
219 static gboolean
220 nautilus_desktop_background_ensure_realized (NautilusDesktopBackground *self)
221 {
222 int entire_width;
223 int entire_height;
224 GdkScreen *screen;
225 GdkWindow *window;
226
227 screen = gtk_widget_get_screen (self->details->widget);
228 entire_height = gdk_screen_get_height (screen);
229 entire_width = gdk_screen_get_width (screen);
230
231 /* If the window size is the same as last time, don't update */
232 if (entire_width == self->details->background_entire_width &&
233 entire_height == self->details->background_entire_height) {
234 return FALSE;
235 }
236
237 free_background_surface (self);
238
239 window = gtk_widget_get_window (self->details->widget);
240 self->details->background_surface = gnome_bg_create_surface (self->details->bg,
241 window,
242 entire_width, entire_height,
243 TRUE);
244
245 /* We got the surface and everything, so we don't care about a change
246 that is pending (unless things actually change after this time) */
247 g_object_set_data (G_OBJECT (self),
248 "ignore-pending-change", GINT_TO_POINTER (TRUE));
249
250 self->details->background_entire_width = entire_width;
251 self->details->background_entire_height = entire_height;
252
253 return TRUE;
254 }
255
256 static void
257 on_fade_finished (GnomeBGCrossfade *fade,
258 GdkWindow *window,
259 gpointer user_data)
260 {
261 NautilusDesktopBackground *self = user_data;
262
263 nautilus_desktop_background_ensure_realized (self);
264 if (self->details->background_surface != NULL)
265 gnome_bg_set_surface_as_root (gdk_window_get_screen (window),
266 self->details->background_surface);
267 }
268
269 static gboolean
270 fade_to_surface (NautilusDesktopBackground *self,
271 GdkWindow *window,
272 cairo_surface_t *surface)
273 {
274 if (self->details->fade == NULL) {
275 return FALSE;
276 }
277
278 if (!gnome_bg_crossfade_set_end_surface (self->details->fade,
279 surface)) {
280 return FALSE;
281 }
282
283 if (!gnome_bg_crossfade_is_started (self->details->fade)) {
284 gnome_bg_crossfade_start (self->details->fade, window);
285 g_signal_connect (self->details->fade,
286 "finished",
287 G_CALLBACK (on_fade_finished), self);
288 }
289
290 return gnome_bg_crossfade_is_started (self->details->fade);
291 }
292
293 static void
294 nautilus_desktop_background_set_up_widget (NautilusDesktopBackground *self)
295 {
296 GdkWindow *window;
297 gboolean in_fade = FALSE;
298 GtkWidget *widget;
299
300 widget = self->details->widget;
301
302 if (!gtk_widget_get_realized (widget)) {
303 return;
304 }
305
306 nautilus_desktop_background_ensure_realized (self);
307 if (self->details->background_surface == NULL)
308 return;
309
310 window = gtk_layout_get_bin_window (GTK_LAYOUT (widget));
311
312 in_fade = fade_to_surface (self, window,
313 self->details->background_surface);
314
315 if (!in_fade) {
316 cairo_pattern_t *pattern;
317
318 pattern = cairo_pattern_create_for_surface (self->details->background_surface);
319 gdk_window_set_background_pattern (window, pattern);
320 cairo_pattern_destroy (pattern);
321
322 gnome_bg_set_surface_as_root (gtk_widget_get_screen (widget),
323 self->details->background_surface);
324 }
325 }
326
327 static gboolean
328 background_changed_cb (NautilusDesktopBackground *self)
329 {
330 self->details->change_idle_id = 0;
331
332 nautilus_desktop_background_unrealize (self);
333 nautilus_desktop_background_set_up_widget (self);
334
335 gtk_widget_queue_draw (self->details->widget);
336
337 return FALSE;
338 }
339
340 static void
341 queue_background_change (NautilusDesktopBackground *self)
342 {
343 if (self->details->change_idle_id != 0) {
344 g_source_remove (self->details->change_idle_id);
345 }
346
347 self->details->change_idle_id =
348 g_idle_add ((GSourceFunc) background_changed_cb, self);
349 }
350
351 static void
352 nautilus_desktop_background_changed (GnomeBG *bg,
353 gpointer user_data)
354 {
355 NautilusDesktopBackground *self;
356
357 self = user_data;
358 init_fade (self);
359 queue_background_change (self);
360 }
361
362 static void
363 nautilus_desktop_background_transitioned (GnomeBG *bg,
364 gpointer user_data)
365 {
366 NautilusDesktopBackground *self;
367
368 self = user_data;
369 free_fade (self);
370 queue_background_change (self);
371 }
372
373 static void
374 widget_realize_cb (GtkWidget *widget,
375 gpointer user_data)
376 {
377 GdkScreen *screen;
378 NautilusDesktopBackground *self = user_data;
379
380 screen = gtk_widget_get_screen (widget);
381
382 if (self->details->screen_size_handler > 0) {
383 g_signal_handler_disconnect (screen,
384 self->details->screen_size_handler);
385 }
386 self->details->screen_size_handler =
387 g_signal_connect (screen, "size_changed",
388 G_CALLBACK (screen_size_changed), self);
389
390 if (self->details->screen_monitors_handler > 0) {
391 g_signal_handler_disconnect (screen,
392 self->details->screen_monitors_handler);
393 }
394 self->details->screen_monitors_handler =
395 g_signal_connect (screen, "monitors-changed",
396 G_CALLBACK (screen_size_changed), self);
397
398 init_fade (self);
399 nautilus_desktop_background_set_up_widget (self);
400 }
401
402 static void
403 widget_unrealize_cb (GtkWidget *widget,
404 gpointer user_data)
405 {
406 NautilusDesktopBackground *self = user_data;
407
408 if (self->details->screen_size_handler > 0) {
409 g_signal_handler_disconnect (gtk_widget_get_screen (GTK_WIDGET (widget)),
410 self->details->screen_size_handler);
411 self->details->screen_size_handler = 0;
412 }
413 if (self->details->screen_monitors_handler > 0) {
414 g_signal_handler_disconnect (gtk_widget_get_screen (GTK_WIDGET (widget)),
415 self->details->screen_monitors_handler);
416 self->details->screen_monitors_handler = 0;
417 }
418 }
419
420 static void
421 on_widget_destroyed (GtkWidget *widget,
422 gpointer user_data)
423 {
424 NautilusDesktopBackground *self = user_data;
425
426 if (self->details->change_idle_id != 0) {
427 g_source_remove (self->details->change_idle_id);
428 self->details->change_idle_id = 0;
429 }
430
431 free_fade (self);
432 self->details->widget = NULL;
433 }
434
435 static gboolean
436 background_change_event_idle_cb (NautilusDesktopBackground *self)
437 {
438 gnome_bg_load_from_preferences (self->details->bg,
439 gnome_background_preferences);
440
441 g_object_unref (self);
442
443 return FALSE;
444 }
445
446 static gboolean
447 background_settings_change_event_cb (GSettings *settings,
448 gpointer keys,
449 gint n_keys,
450 gpointer user_data)
451 {
452 NautilusDesktopBackground *self = user_data;
453
454 /* Need to defer signal processing otherwise
455 * we would make the dconf backend deadlock.
456 */
457 g_idle_add ((GSourceFunc) background_change_event_idle_cb,
458 g_object_ref (self));
459
460 return FALSE;
461 }
462
463 static void
464 nautilus_desktop_background_constructed (GObject *obj)
465 {
466 NautilusDesktopBackground *self;
467 GtkWidget *widget;
468
469 self = NAUTILUS_DESKTOP_BACKGROUND (obj);
470
471 if (G_OBJECT_CLASS (nautilus_desktop_background_parent_class)->constructed != NULL) {
472 G_OBJECT_CLASS (nautilus_desktop_background_parent_class)->constructed (obj);
473 }
474
475 widget = self->details->widget;
476
477 g_assert (widget != NULL);
478
479 g_signal_connect_object (widget, "destroy",
480 G_CALLBACK (on_widget_destroyed), self, 0);
481 g_signal_connect_object (widget, "realize",
482 G_CALLBACK (widget_realize_cb), self, 0);
483 g_signal_connect_object (widget, "unrealize",
484 G_CALLBACK (widget_unrealize_cb), self, 0);
485
486 gnome_bg_load_from_preferences (self->details->bg,
487 gnome_background_preferences);
488
489 /* Let's receive batch change events instead of every single one */
490 g_signal_connect (gnome_background_preferences,
491 "change-event",
492 G_CALLBACK (background_settings_change_event_cb),
493 self);
494
495 queue_background_change (self);
496 }
497
498 static void
499 nautilus_desktop_background_set_property (GObject *object,
500 guint property_id,
501 const GValue *value,
502 GParamSpec *pspec)
503 {
504 NautilusDesktopBackground *self;
505
506 self = NAUTILUS_DESKTOP_BACKGROUND (object);
507
508 switch (property_id) {
509 case PROP_WIDGET:
510 self->details->widget = g_value_get_object (value);
511 break;
512 default:
513 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
514 break;
515 }
516 }
517
518 static GObject *
519 nautilus_desktop_background_constructor (GType type,
520 guint n_construct_params,
521 GObjectConstructParam *construct_params)
522 {
523 GObject *retval;
524
525 if (singleton != NULL) {
526 return g_object_ref (singleton);
527 }
528
529 retval = G_OBJECT_CLASS (nautilus_desktop_background_parent_class)->constructor
530 (type, n_construct_params, construct_params);
531
532 singleton = NAUTILUS_DESKTOP_BACKGROUND (retval);
533 g_object_add_weak_pointer (retval, (gpointer) &singleton);
534
535 return retval;
536 }
537
538 static void
539 nautilus_desktop_background_class_init (NautilusDesktopBackgroundClass *klass)
540 {
541 GObjectClass *object_class;
542 GParamSpec *pspec;
543
544 object_class = G_OBJECT_CLASS (klass);
545 object_class->finalize = nautilus_desktop_background_finalize;
546 object_class->set_property = nautilus_desktop_background_set_property;
547 object_class->constructor = nautilus_desktop_background_constructor;
548 object_class->constructed = nautilus_desktop_background_constructed;
549
550 pspec = g_param_spec_object ("widget", "The widget for this background",
551 "The widget that gets its background set",
552 NAUTILUS_TYPE_CANVAS_CONTAINER,
553 G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
554 g_object_class_install_property (object_class, PROP_WIDGET, pspec);
555
556 g_type_class_add_private (klass, sizeof (NautilusDesktopBackgroundDetails));
557 }
558
559 static void
560 nautilus_desktop_background_init (NautilusDesktopBackground *self)
561 {
562 self->details =
563 G_TYPE_INSTANCE_GET_PRIVATE (self,
564 NAUTILUS_TYPE_DESKTOP_BACKGROUND,
565 NautilusDesktopBackgroundDetails);
566
567 self->details->bg = gnome_bg_new ();
568 self->details->default_color.red = 0xffff;
569 self->details->default_color.green = 0xffff;
570 self->details->default_color.blue = 0xffff;
571
572 g_signal_connect (self->details->bg, "changed",
573 G_CALLBACK (nautilus_desktop_background_changed), self);
574 g_signal_connect (self->details->bg, "transitioned",
575 G_CALLBACK (nautilus_desktop_background_transitioned), self);
576 }
577
578 void
579 nautilus_desktop_background_receive_dropped_background_image (NautilusDesktopBackground *self,
580 const char *image_uri)
581 {
582 /* Currently, we only support tiled images. So we set the placement.
583 */
584 gnome_bg_set_placement (self->details->bg,
585 G_DESKTOP_BACKGROUND_STYLE_WALLPAPER);
586 gnome_bg_set_draw_background (self->details->bg, TRUE);
587 nautilus_desktop_background_set_image_uri (self, image_uri);
588
589 gnome_bg_save_to_preferences (self->details->bg,
590 gnome_background_preferences);
591 }
592
593 NautilusDesktopBackground *
594 nautilus_desktop_background_new (NautilusCanvasContainer *container)
595 {
596 return g_object_new (NAUTILUS_TYPE_DESKTOP_BACKGROUND,
597 "widget", container,
598 NULL);
599 }