nautilus-3.6.3/libnautilus-private/nautilus-desktop-background.c

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 }