Bug Summary

File:libcafe-desktop/cafe-bg.c
Warning:line 2328, column 42
Access to field 'width' results in a dereference of a null pointer (loaded from variable 'best')

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name cafe-bg.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/rootdir/libcafe-desktop -fcoverage-compilation-dir=/rootdir/libcafe-desktop -resource-dir /usr/lib/llvm-21/lib/clang/21 -D HAVE_CONFIG_H -I . -I .. -I /usr/include/ctk-3.0 -I /usr/include/pango-1.0 -I /usr/include/cairo -I /usr/include/gdk-pixbuf-2.0 -I /usr/include/glycin-2 -I /usr/include/at-spi2-atk/2.0 -I /usr/include/at-spi-2.0 -I /usr/include/atk-1.0 -I /usr/include/dbus-1.0 -I /usr/lib/x86_64-linux-gnu/dbus-1.0/include -I /usr/include/fribidi -I /usr/include/pixman-1 -I /usr/include/harfbuzz -I /usr/include/freetype2 -I /usr/include/libpng16 -I /usr/include/gio-unix-2.0 -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/libmount -I /usr/include/blkid -I /usr/include/sysprof-6 -I /usr/include/startup-notification-1.0 -I /usr/include/dconf -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/libmount -I /usr/include/blkid -I /usr/include/sysprof-6 -D G_LOG_DOMAIN="CafeDesktop" -D CAFELOCALEDIR="/usr/share/locale" -D PNP_IDS="/usr/share/libcafe-desktop/pnp.ids" -D ISO_CODES_PREFIX="/usr" -D PIC -internal-isystem /usr/lib/llvm-21/lib/clang/21/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/15/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -ferror-limit 19 -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -analyzer-checker deadcode.DeadStores -analyzer-checker security.ArrayBound -analyzer-checker unix.cstring.NotNullTerminated -analyzer-checker alpha.deadcode.UnreachableCode -analyzer-checker alpha.core.CastToStruct -analyzer-checker alpha.security.ReturnPtrRange -analyzer-checker alpha.unix.SimpleStream -analyzer-checker alpha.unix.cstring.BufferOverlap -analyzer-checker alpha.unix.cstring.OutOfBounds -analyzer-checker alpha.core.FixedAddr -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /rootdir/html-report/2026-05-02-100704-15377-1 -x c cafe-bg.c
1/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
2
3cafebg.c: Object for the desktop background.
4
5Copyright (C) 2000 Eazel, Inc.
6Copyright (C) 2007-2008 Red Hat, Inc.
7Copyright (C) 2012 Jasmine Hassan <jasmine.aura@gmail.com>
8
9This program is free software; you can redistribute it and/or
10modify it under the terms of the GNU Library General Public License as
11published by the Free Software Foundation; either version 2 of the
12License, or (at your option) any later version.
13
14This program is distributed in the hope that it will be useful,
15but WITHOUT ANY WARRANTY; without even the implied warranty of
16MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17Library General Public License for more details.
18
19You should have received a copy of the GNU Library General Public
20License along with this program; if not, write to the
21Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
22Boston, MA 02110-1301, USA.
23
24Derived from eel-background.c and eel-gdk-pixbuf-extensions.c by
25Darin Adler <darin@eazel.com> and Ramiro Estrugo <ramiro@eazel.com>
26
27Authors: Soren Sandmann <sandmann@redhat.com>
28 Jasmine Hassan <jasmine.aura@gmail.com>
29
30*/
31
32#include <string.h>
33#include <math.h>
34#include <stdarg.h>
35#include <stdlib.h>
36
37#include <glib/gstdio.h>
38#include <gio/gio.h>
39
40#include <cdk/cdkx.h>
41#include <X11/Xlib.h>
42#include <X11/Xatom.h>
43
44#include <cairo.h>
45
46#define CAFE_DESKTOP_USE_UNSTABLE_API
47#include <cafe-bg.h>
48#include <cafe-bg-crossfade.h>
49
50# include <cairo-xlib.h>
51
52#define CAFE_BG_CACHE_DIR"cafe/background" "cafe/background"
53
54/* We keep the large pixbufs around if the next update
55 in the slideshow is less than 60 seconds away */
56#define KEEP_EXPENSIVE_CACHE_SECS60 60
57
58typedef struct _SlideShow SlideShow;
59typedef struct _Slide Slide;
60
61struct _Slide {
62 double duration; /* in seconds */
63 gboolean fixed;
64
65 GSList* file1;
66 GSList* file2; /* NULL if fixed is TRUE */
67};
68
69typedef struct _FileSize FileSize;
70struct _FileSize {
71 gint width;
72 gint height;
73
74 char* file;
75};
76
77/* This is the size of the CdkRGB dither matrix, in order to avoid
78 * bad dithering when tiling the gradient
79 */
80#define GRADIENT_PIXMAP_TILE_SIZE128 128
81#define THUMBNAIL_SIZE256 256
82
83typedef struct FileCacheEntry FileCacheEntry;
84#define CACHE_SIZE4 4
85
86/*
87 * Implementation of the CafeBG class
88 */
89struct _CafeBG {
90 GObject parent_instance;
91 char *filename;
92 CafeBGPlacement placement;
93 CafeBGColorType color_type;
94 CdkRGBA primary;
95 CdkRGBA secondary;
96 gboolean is_enabled;
97
98 GFileMonitor* file_monitor;
99
100 guint changed_id;
101 guint transitioned_id;
102 guint blow_caches_id;
103
104 /* Cached information, only access through cache accessor functions */
105 SlideShow* slideshow;
106 time_t file_mtime;
107 GdkPixbuf* pixbuf_cache;
108 int timeout_id;
109
110 GList* file_cache;
111};
112
113struct _CafeBGClass {
114 GObjectClass parent_class;
115};
116
117enum {
118 CHANGED,
119 TRANSITIONED,
120 N_SIGNALS
121};
122
123static guint signals[N_SIGNALS] = {0};
124
125G_DEFINE_TYPE(CafeBG, cafe_bg, G_TYPE_OBJECT)static void cafe_bg_init (CafeBG *self); static void cafe_bg_class_init
(CafeBGClass *klass); static GType cafe_bg_get_type_once (void
); static gpointer cafe_bg_parent_class = ((void*)0); static gint
CafeBG_private_offset; static void cafe_bg_class_intern_init
(gpointer klass) { cafe_bg_parent_class = g_type_class_peek_parent
(klass); if (CafeBG_private_offset != 0) g_type_class_adjust_private_offset
(klass, &CafeBG_private_offset); cafe_bg_class_init ((CafeBGClass
*) klass); } __attribute__ ((__unused__)) static inline gpointer
cafe_bg_get_instance_private (CafeBG *self) { return (((gpointer
) ((guint8*) (self) + (glong) (CafeBG_private_offset)))); } GType
cafe_bg_get_type (void) { static GType static_g_define_type_id
= 0; if ((__extension__ ({ _Static_assert (sizeof *(&static_g_define_type_id
) == sizeof (gpointer), "Expression evaluates to false"); (void
) (0 ? (gpointer) * (&static_g_define_type_id) : ((void*)
0)); (!(__extension__ ({ _Static_assert (sizeof *(&static_g_define_type_id
) == sizeof (gpointer), "Expression evaluates to false"); __typeof__
(*(&static_g_define_type_id)) gapg_temp_newval; __typeof__
((&static_g_define_type_id)) gapg_temp_atomic = (&static_g_define_type_id
); __atomic_load (gapg_temp_atomic, &gapg_temp_newval, 5)
; gapg_temp_newval; })) && g_once_init_enter_pointer (
&static_g_define_type_id)); })) ) { GType g_define_type_id
= cafe_bg_get_type_once (); (__extension__ ({ _Static_assert
(sizeof *(&static_g_define_type_id) == sizeof (gpointer)
, "Expression evaluates to false"); 0 ? (void) (*(&static_g_define_type_id
) = (g_define_type_id)) : (void) 0; g_once_init_leave_pointer
((&static_g_define_type_id), (gpointer) (guintptr) (g_define_type_id
)); })) ; } return static_g_define_type_id; } __attribute__ (
(__noinline__)) static GType cafe_bg_get_type_once (void) { GType
g_define_type_id = g_type_register_static_simple (((GType) (
(20) << (2))), g_intern_static_string ("CafeBG"), sizeof
(CafeBGClass), (GClassInitFunc)(void (*)(void)) cafe_bg_class_intern_init
, sizeof (CafeBG), (GInstanceInitFunc)(void (*)(void)) cafe_bg_init
, (GTypeFlags) 0); { {{};} } return g_define_type_id; }
126
127static cairo_surface_t *make_root_pixmap (CdkWindow *window,
128 gint width,
129 gint height);
130
131/* Pixbuf utils */
132static void pixbuf_average_value (GdkPixbuf *pixbuf,
133 CdkRGBA *result);
134static GdkPixbuf *pixbuf_scale_to_fit (GdkPixbuf *src,
135 int max_width,
136 int max_height);
137static GdkPixbuf *pixbuf_scale_to_min (GdkPixbuf *src,
138 int min_width,
139 int min_height);
140
141static void pixbuf_draw_gradient (GdkPixbuf *pixbuf,
142 gboolean horizontal,
143 CdkRGBA *c1,
144 CdkRGBA *c2,
145 CdkRectangle *rect);
146
147static void pixbuf_tile (GdkPixbuf *src,
148 GdkPixbuf *dest);
149static void pixbuf_blend (GdkPixbuf *src,
150 GdkPixbuf *dest,
151 int src_x,
152 int src_y,
153 int width,
154 int height,
155 int dest_x,
156 int dest_y,
157 double alpha);
158
159/* Thumbnail utilities */
160static GdkPixbuf *create_thumbnail_for_filename (CafeDesktopThumbnailFactory *factory,
161 const char *filename);
162static gboolean get_thumb_annotations (GdkPixbuf *thumb,
163 int *orig_width,
164 int *orig_height);
165
166/* Cache */
167static GdkPixbuf *get_pixbuf_for_size (CafeBG *bg,
168 gint num_monitor,
169 int width,
170 int height);
171static void clear_cache (CafeBG *bg);
172static gboolean is_different (CafeBG *bg,
173 const char *filename);
174static time_t get_mtime (const char *filename);
175static GdkPixbuf *create_img_thumbnail (CafeBG *bg,
176 CafeDesktopThumbnailFactory *factory,
177 CdkScreen *screen,
178 int dest_width,
179 int dest_height,
180 int frame_num);
181static SlideShow * get_as_slideshow (CafeBG *bg,
182 const char *filename);
183static Slide * get_current_slide (SlideShow *show,
184 double *alpha);
185static gboolean slideshow_has_multiple_sizes (SlideShow *show);
186
187static SlideShow *read_slideshow_file (const char *filename,
188 GError **err);
189static SlideShow *slideshow_ref (SlideShow *show);
190static void slideshow_unref (SlideShow *show);
191
192static FileSize *find_best_size (GSList *sizes,
193 gint width,
194 gint height);
195
196static void
197color_from_string (const char *string,
198 CdkRGBA *colorp)
199{
200 /* If all else fails use black */
201 cdk_rgba_parse (colorp, "#000000");
202
203 if (!string)
204 return;
205
206 cdk_rgba_parse (colorp, string);
207}
208
209static char *
210color_to_string (const CdkRGBA *color)
211{
212 return g_strdup_printf ("#%02x%02x%02x",
213 ((guint) (color->red * 65535)) >> 8,
214 ((guint) (color->green * 65535)) >> 8,
215 ((guint) (color->blue * 65535)) >> 8);
216}
217
218static gboolean
219do_changed (CafeBG *bg)
220{
221 bg->changed_id = 0;
222
223 g_signal_emit (G_OBJECT (bg)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((bg)), (((GType) ((20) << (2))))))))
, signals[CHANGED], 0);
224
225 return FALSE(0);
226}
227
228static void
229queue_changed (CafeBG *bg)
230{
231 if (bg->changed_id > 0) {
232 g_source_remove (bg->changed_id);
233 }
234
235 bg->changed_id = g_timeout_add_full (G_PRIORITY_LOW300,
236 100,
237 (GSourceFunc)do_changed,
238 bg,
239 NULL((void*)0));
240}
241
242static gboolean
243do_transitioned (CafeBG *bg)
244{
245 bg->transitioned_id = 0;
246
247 if (bg->pixbuf_cache) {
248 g_object_unref (bg->pixbuf_cache);
249 bg->pixbuf_cache = NULL((void*)0);
250 }
251
252 g_signal_emit (G_OBJECT (bg)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((bg)), (((GType) ((20) << (2))))))))
, signals[TRANSITIONED], 0);
253
254 return FALSE(0);
255}
256
257static void
258queue_transitioned (CafeBG *bg)
259{
260 if (bg->transitioned_id > 0) {
261 g_source_remove (bg->transitioned_id);
262 }
263
264 bg->transitioned_id = g_timeout_add_full (G_PRIORITY_LOW300,
265 100,
266 (GSourceFunc)do_transitioned,
267 bg,
268 NULL((void*)0));
269}
270
271/* This function loads the user's preferences */
272void
273cafe_bg_load_from_preferences (CafeBG *bg)
274{
275 GSettings *settings;
276 settings = g_settings_new (CAFE_BG_SCHEMA"org.cafe.background");
277
278 cafe_bg_load_from_gsettings (bg, settings);
279 g_object_unref (settings);
280
281 /* Queue change to force background redraw */
282 queue_changed (bg);
283}
284
285/* This function loads default system settings */
286void
287cafe_bg_load_from_system_preferences (CafeBG *bg)
288{
289 GSettings *settings;
290
291 /* FIXME: we need to bind system settings instead of user but
292 * that's currently impossible, not implemented yet.
293 * Hence, reset to system default values.
294 */
295 settings = g_settings_new (CAFE_BG_SCHEMA"org.cafe.background");
296
297 cafe_bg_load_from_system_gsettings (bg, settings, FALSE(0));
298
299 g_object_unref (settings);
300}
301
302/* This function loads (and optionally resets to) default system settings */
303void
304cafe_bg_load_from_system_gsettings (CafeBG *bg,
305 GSettings *settings,
306 gboolean reset_apply)
307{
308 GSettingsSchema *schema;
309 gchar **keys;
310 gchar **k;
311
312 g_return_if_fail (CAFE_IS_BG (bg))do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((bg)); GType __t = ((cafe_bg_get_type ())); gboolean __r;
if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { }
else { g_return_if_fail_warning ("CafeDesktop", ((const char
*) (__func__)), "CAFE_IS_BG (bg)"); return; } } while (0)
;
313 g_return_if_fail (G_IS_SETTINGS (settings))do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((settings)); GType __t = ((g_settings_get_type ())); gboolean
__r; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { }
else { g_return_if_fail_warning ("CafeDesktop", ((const char
*) (__func__)), "G_IS_SETTINGS (settings)"); return; } } while
(0)
;
314
315 g_settings_delay (settings);
316
317 g_object_get (settings, "settings-schema", &schema, NULL((void*)0));
318 keys = g_settings_schema_list_keys (schema);
319 g_settings_schema_unref (schema);
320
321 for (k = keys; *k; k++) {
322 g_settings_reset (settings, *k);
323 }
324 g_strfreev (keys);
325
326 if (reset_apply) {
327 /* Apply changes atomically. */
328 g_settings_apply (settings);
329 } else {
330 cafe_bg_load_from_gsettings (bg, settings);
331 g_settings_revert (settings);
332 }
333}
334
335void
336cafe_bg_load_from_gsettings (CafeBG *bg,
337 GSettings *settings)
338{
339 char *tmp;
340 char *filename;
341 CafeBGColorType ctype;
342 CdkRGBA c1, c2;
343 CafeBGPlacement placement;
344
345 g_return_if_fail (CAFE_IS_BG (bg))do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((bg)); GType __t = ((cafe_bg_get_type ())); gboolean __r;
if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { }
else { g_return_if_fail_warning ("CafeDesktop", ((const char
*) (__func__)), "CAFE_IS_BG (bg)"); return; } } while (0)
;
346 g_return_if_fail (G_IS_SETTINGS (settings))do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((settings)); GType __t = ((g_settings_get_type ())); gboolean
__r; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { }
else { g_return_if_fail_warning ("CafeDesktop", ((const char
*) (__func__)), "G_IS_SETTINGS (settings)"); return; } } while
(0)
;
347
348 bg->is_enabled = g_settings_get_boolean (settings, CAFE_BG_KEY_DRAW_BACKGROUND"draw-background");
349
350 /* Filename */
351 filename = NULL((void*)0);
352 tmp = g_settings_get_string (settings, CAFE_BG_KEY_PICTURE_FILENAME"picture-filename");
353 if (tmp && *tmp != '\0') {
354 /* FIXME: UTF-8 checks should go away.
355 * picture-filename is of type string, which can only be used for
356 * UTF-8 strings, and some filenames are not, dependending on the
357 * locale used.
358 * It would be better (and simpler) to change to a URI instead,
359 * as URIs are UTF-8 encoded strings.
360 */
361 if (g_utf8_validate (tmp, -1, NULL((void*)0)) &&
362 g_file_test (tmp, G_FILE_TEST_EXISTS)) {
363 filename = g_strdup (tmp)g_strdup_inline (tmp);
364 } else {
365 filename = g_filename_from_utf8 (tmp, -1, NULL((void*)0), NULL((void*)0), NULL((void*)0));
366 }
367
368 /* Fallback to default BG if the filename set is non-existent */
369 if (filename != NULL((void*)0) && !g_file_test (filename, G_FILE_TEST_EXISTS)) {
370
371 g_free (filename);
372
373 g_settings_delay (settings);
374 g_settings_reset (settings, CAFE_BG_KEY_PICTURE_FILENAME"picture-filename");
375 filename = g_settings_get_string (settings, CAFE_BG_KEY_PICTURE_FILENAME"picture-filename");
376 g_settings_revert (settings);
377
378 //* Check if default background exists, also */
379 if (filename != NULL((void*)0) && !g_file_test (filename, G_FILE_TEST_EXISTS)) {
380 g_free (filename);
381 filename = NULL((void*)0);
382 }
383 }
384 }
385 g_free (tmp);
386
387 /* Colors */
388 tmp = g_settings_get_string (settings, CAFE_BG_KEY_PRIMARY_COLOR"primary-color");
389 color_from_string (tmp, &c1);
390 g_free (tmp);
391
392 tmp = g_settings_get_string (settings, CAFE_BG_KEY_SECONDARY_COLOR"secondary-color");
393 color_from_string (tmp, &c2);
394 g_free (tmp);
395
396 /* Color type */
397 ctype = g_settings_get_enum (settings, CAFE_BG_KEY_COLOR_TYPE"color-shading-type");
398
399 /* Placement */
400 placement = g_settings_get_enum (settings, CAFE_BG_KEY_PICTURE_PLACEMENT"picture-options");
401
402 cafe_bg_set_color (bg, ctype, &c1, &c2);
403 cafe_bg_set_placement (bg, placement);
404 cafe_bg_set_filename (bg, filename);
405
406 if (filename != NULL((void*)0))
407 g_free (filename);
408}
409
410void
411cafe_bg_save_to_preferences (CafeBG *bg)
412{
413 GSettings *settings;
414 settings = g_settings_new (CAFE_BG_SCHEMA"org.cafe.background");
415
416 cafe_bg_save_to_gsettings (bg, settings);
417 g_object_unref (settings);
418}
419
420void
421cafe_bg_save_to_gsettings (CafeBG *bg,
422 GSettings *settings)
423{
424 gchar *primary;
425 gchar *secondary;
426
427 g_return_if_fail (CAFE_IS_BG (bg))do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((bg)); GType __t = ((cafe_bg_get_type ())); gboolean __r;
if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { }
else { g_return_if_fail_warning ("CafeDesktop", ((const char
*) (__func__)), "CAFE_IS_BG (bg)"); return; } } while (0)
;
428 g_return_if_fail (G_IS_SETTINGS (settings))do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((settings)); GType __t = ((g_settings_get_type ())); gboolean
__r; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { }
else { g_return_if_fail_warning ("CafeDesktop", ((const char
*) (__func__)), "G_IS_SETTINGS (settings)"); return; } } while
(0)
;
429
430 primary = color_to_string (&bg->primary);
431 secondary = color_to_string (&bg->secondary);
432
433 g_settings_delay (settings);
434
435 g_settings_set_boolean (settings, CAFE_BG_KEY_DRAW_BACKGROUND"draw-background", bg->is_enabled);
436 g_settings_set_string (settings, CAFE_BG_KEY_PICTURE_FILENAME"picture-filename", bg->filename);
437 g_settings_set_enum (settings, CAFE_BG_KEY_PICTURE_PLACEMENT"picture-options", bg->placement);
438 g_settings_set_string (settings, CAFE_BG_KEY_PRIMARY_COLOR"primary-color", primary);
439 g_settings_set_string (settings, CAFE_BG_KEY_SECONDARY_COLOR"secondary-color", secondary);
440 g_settings_set_enum (settings, CAFE_BG_KEY_COLOR_TYPE"color-shading-type", bg->color_type);
441
442 /* Apply changes atomically. */
443 g_settings_apply (settings);
444
445 g_free (primary);
446 g_free (secondary);
447}
448
449
450static void
451cafe_bg_init (CafeBG *bg G_GNUC_UNUSED__attribute__ ((__unused__)))
452{
453}
454
455static void
456cafe_bg_dispose (GObject *object)
457{
458 CafeBG *bg = CAFE_BG (object)((((CafeBG*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((object)), ((cafe_bg_get_type ()))))))
;
459
460 if (bg->file_monitor) {
461 g_object_unref (bg->file_monitor);
462 bg->file_monitor = NULL((void*)0);
463 }
464
465 clear_cache (bg);
466
467 G_OBJECT_CLASS (cafe_bg_parent_class)((((GObjectClass*) (void *) g_type_check_class_cast ((GTypeClass
*) ((cafe_bg_parent_class)), (((GType) ((20) << (2)))))
)))
->dispose (object);
468}
469
470static void
471cafe_bg_finalize (GObject *object)
472{
473 CafeBG *bg = CAFE_BG (object)((((CafeBG*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((object)), ((cafe_bg_get_type ()))))))
;
474
475 if (bg->changed_id != 0) {
476 g_source_remove (bg->changed_id);
477 bg->changed_id = 0;
478 }
479
480 if (bg->transitioned_id != 0) {
481 g_source_remove (bg->transitioned_id);
482 bg->transitioned_id = 0;
483 }
484
485 if (bg->blow_caches_id != 0) {
486 g_source_remove (bg->blow_caches_id);
487 bg->blow_caches_id = 0;
488 }
489
490 g_free (bg->filename);
491 bg->filename = NULL((void*)0);
492
493 G_OBJECT_CLASS (cafe_bg_parent_class)((((GObjectClass*) (void *) g_type_check_class_cast ((GTypeClass
*) ((cafe_bg_parent_class)), (((GType) ((20) << (2)))))
)))
->finalize (object);
494}
495
496static void
497cafe_bg_class_init (CafeBGClass *klass)
498{
499 GObjectClass *object_class = G_OBJECT_CLASS (klass)((((GObjectClass*) (void *) g_type_check_class_cast ((GTypeClass
*) ((klass)), (((GType) ((20) << (2))))))))
;
500
501 object_class->dispose = cafe_bg_dispose;
502 object_class->finalize = cafe_bg_finalize;
503
504 signals[CHANGED] = g_signal_new ("changed",
505 G_OBJECT_CLASS_TYPE (object_class)((((GTypeClass*) (object_class))->g_type)),
506 G_SIGNAL_RUN_LAST,
507 0,
508 NULL((void*)0), NULL((void*)0),
509 g_cclosure_marshal_VOID__VOID,
510 G_TYPE_NONE((GType) ((1) << (2))), 0);
511
512 signals[TRANSITIONED] = g_signal_new ("transitioned",
513 G_OBJECT_CLASS_TYPE (object_class)((((GTypeClass*) (object_class))->g_type)),
514 G_SIGNAL_RUN_LAST,
515 0,
516 NULL((void*)0), NULL((void*)0),
517 g_cclosure_marshal_VOID__VOID,
518 G_TYPE_NONE((GType) ((1) << (2))), 0);
519}
520
521CafeBG *
522cafe_bg_new (void)
523{
524 return g_object_new (CAFE_TYPE_BG(cafe_bg_get_type ()), NULL((void*)0));
525}
526
527void
528cafe_bg_set_color (CafeBG *bg,
529 CafeBGColorType type,
530 CdkRGBA *primary,
531 CdkRGBA *secondary)
532{
533 g_return_if_fail (bg != NULL)do { if ((bg != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "bg != NULL"); return
; } } while (0)
;
534 g_return_if_fail (primary != NULL)do { if ((primary != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "primary != NULL"
); return; } } while (0)
;
535
536 if (bg->color_type != type ||
537 !cdk_rgba_equal (&bg->primary, primary) ||
538 (secondary && !cdk_rgba_equal (&bg->secondary, secondary))) {
539 bg->color_type = type;
540 bg->primary = *primary;
541 if (secondary) {
542 bg->secondary = *secondary;
543 }
544
545 queue_changed (bg);
546 }
547}
548
549void
550cafe_bg_set_placement (CafeBG *bg,
551 CafeBGPlacement placement)
552{
553 g_return_if_fail (bg != NULL)do { if ((bg != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "bg != NULL"); return
; } } while (0)
;
554
555 if (bg->placement != placement) {
556 bg->placement = placement;
557
558 queue_changed (bg);
559 }
560}
561
562CafeBGPlacement
563cafe_bg_get_placement (CafeBG *bg)
564{
565 g_return_val_if_fail (bg != NULL, -1)do { if ((bg != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "bg != NULL"); return
(-1); } } while (0)
;
566
567 return bg->placement;
568}
569
570void
571cafe_bg_get_color (CafeBG *bg,
572 CafeBGColorType *type,
573 CdkRGBA *primary,
574 CdkRGBA *secondary)
575{
576 g_return_if_fail (bg != NULL)do { if ((bg != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "bg != NULL"); return
; } } while (0)
;
577
578 if (type)
579 *type = bg->color_type;
580
581 if (primary)
582 *primary = bg->primary;
583
584 if (secondary)
585 *secondary = bg->secondary;
586}
587
588void
589cafe_bg_set_draw_background (CafeBG *bg,
590 gboolean draw_background)
591{
592 g_return_if_fail (bg != NULL)do { if ((bg != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "bg != NULL"); return
; } } while (0)
;
593
594 if (bg->is_enabled != draw_background) {
595 bg->is_enabled = draw_background;
596
597 queue_changed (bg);
598 }
599}
600
601gboolean
602cafe_bg_get_draw_background (CafeBG *bg)
603{
604 g_return_val_if_fail (bg != NULL, FALSE)do { if ((bg != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "bg != NULL"); return
((0)); } } while (0)
;
605
606 return bg->is_enabled;
607}
608
609const gchar *
610cafe_bg_get_filename (CafeBG *bg)
611{
612 g_return_val_if_fail (bg != NULL, NULL)do { if ((bg != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "bg != NULL"); return
(((void*)0)); } } while (0)
;
613
614 return bg->filename;
615}
616
617static inline gchar *
618get_wallpaper_cache_dir ()
619{
620 return g_build_filename (g_get_user_cache_dir(), CAFE_BG_CACHE_DIR"cafe/background", NULL((void*)0));
621}
622
623static inline gchar *
624get_wallpaper_cache_prefix_name (gint num_monitor,
625 CafeBGPlacement placement,
626 gint width,
627 gint height)
628{
629 return g_strdup_printf ("%i_%i_%i_%i", num_monitor, (gint) placement, width, height);
630}
631
632static char *
633get_wallpaper_cache_filename (const char *filename,
634 gint num_monitor,
635 CafeBGPlacement placement,
636 gint width,
637 gint height)
638{
639 gchar *cache_filename;
640 gchar *cache_prefix_name;
641 gchar *md5_filename;
642 gchar *cache_basename;
643 gchar *cache_dir;
644
645 md5_filename = g_compute_checksum_for_data (G_CHECKSUM_MD5, (const guchar *) filename,
646 strlen (filename));
647 cache_prefix_name = get_wallpaper_cache_prefix_name (num_monitor, placement, width, height);
648 cache_basename = g_strdup_printf ("%s_%s", cache_prefix_name, md5_filename);
649 cache_dir = get_wallpaper_cache_dir ();
650 cache_filename = g_build_filename (cache_dir, cache_basename, NULL((void*)0));
651
652 g_free (cache_prefix_name);
653 g_free (md5_filename);
654 g_free (cache_basename);
655 g_free (cache_dir);
656
657 return cache_filename;
658}
659
660static void
661cleanup_cache_for_monitor (gchar *cache_dir,
662 gint num_monitor)
663{
664 GDir *g_cache_dir;
665 gchar *monitor_prefix;
666 const gchar *file;
667
668 g_cache_dir = g_dir_open (cache_dir, 0, NULL((void*)0));
669 monitor_prefix = g_strdup_printf ("%i_", num_monitor);
670
671 file = g_dir_read_name (g_cache_dir);
672 while (file != NULL((void*)0)) {
673 gchar *path = g_build_filename (cache_dir, file, NULL((void*)0));
674
675 /* purge files with same monitor id */
676 if (g_str_has_prefix (file, monitor_prefix)(__builtin_constant_p (monitor_prefix)? __extension__ ({ const
char * const __str = (file); const char * const __prefix = (
monitor_prefix); gboolean __result = (0); if (__str == ((void
*)0) || __prefix == ((void*)0)) __result = (g_str_has_prefix)
(__str, __prefix); else { const size_t __str_len = strlen ((
(__str) + !(__str))); const size_t __prefix_len = strlen (((__prefix
) + !(__prefix))); if (__str_len >= __prefix_len) __result
= memcmp (((__str) + !(__str)), ((__prefix) + !(__prefix)), __prefix_len
) == 0; } __result; }) : (g_str_has_prefix) (file, monitor_prefix
) )
&&
677 g_file_test (path, G_FILE_TEST_IS_REGULAR))
678 g_unlink (path);
679
680 g_free (path);
681
682 file = g_dir_read_name (g_cache_dir);
683 }
684
685 g_free (monitor_prefix);
686 g_dir_close (g_cache_dir);
687}
688
689static gboolean
690cache_file_is_valid (const char *filename,
691 const char *cache_filename)
692{
693 time_t mtime;
694 time_t cache_mtime;
695
696 if (!g_file_test (cache_filename, G_FILE_TEST_IS_REGULAR))
697 return FALSE(0);
698
699 mtime = get_mtime (filename);
700 cache_mtime = get_mtime (cache_filename);
701
702 return (mtime < cache_mtime);
703}
704
705static void
706refresh_cache_file (CafeBG *bg,
707 GdkPixbuf *new_pixbuf,
708 gint num_monitor,
709 gint width,
710 gint height)
711{
712 gchar *cache_filename;
713 gchar *cache_dir;
714
715 if ((num_monitor == -1) || (width <= 300) || (height <= 300))
716 return;
717
718 cache_filename = get_wallpaper_cache_filename (bg->filename, num_monitor,
719 bg->placement, width, height);
720 cache_dir = get_wallpaper_cache_dir ();
721
722 /* Only refresh scaled file on disk if useful (and don't cache slideshow) */
723 if (!cache_file_is_valid (bg->filename, cache_filename)) {
724 GdkPixbufFormat *format;
725
726 format = gdk_pixbuf_get_file_info (bg->filename, NULL((void*)0), NULL((void*)0));
727
728 if (format != NULL((void*)0)) {
729 gchar *format_name;
730
731 if (!g_file_test (cache_dir, G_FILE_TEST_IS_DIR)) {
732 g_mkdir_with_parents (cache_dir, 0700);
733 } else {
734 cleanup_cache_for_monitor (cache_dir, num_monitor);
735 }
736
737 format_name = gdk_pixbuf_format_get_name (format);
738
739 if (strcmp (format_name, "jpeg") == 0)
740 gdk_pixbuf_save (new_pixbuf, cache_filename, format_name,
741 NULL((void*)0), "quality", "100", NULL((void*)0));
742 else
743 gdk_pixbuf_save (new_pixbuf, cache_filename, format_name,
744 NULL((void*)0), NULL((void*)0));
745
746 g_free (format_name);
747 }
748 }
749
750 g_free (cache_filename);
751 g_free (cache_dir);
752}
753
754static void
755file_changed (GFileMonitor *file_monitor G_GNUC_UNUSED__attribute__ ((__unused__)),
756 GFile *child G_GNUC_UNUSED__attribute__ ((__unused__)),
757 GFile *other_file G_GNUC_UNUSED__attribute__ ((__unused__)),
758 GFileMonitorEvent event_type G_GNUC_UNUSED__attribute__ ((__unused__)),
759 gpointer user_data)
760{
761 CafeBG *bg = CAFE_BG (user_data)((((CafeBG*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((user_data)), ((cafe_bg_get_type ()))))))
;
762
763 clear_cache (bg);
764 queue_changed (bg);
765}
766
767void
768cafe_bg_set_filename (CafeBG *bg,
769 const char *filename)
770{
771 g_return_if_fail (bg != NULL)do { if ((bg != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "bg != NULL"); return
; } } while (0)
;
772
773 if (is_different (bg, filename)) {
774 g_free (bg->filename);
775
776 bg->filename = g_strdup (filename)g_strdup_inline (filename);
777 bg->file_mtime = get_mtime (bg->filename);
778
779 if (bg->file_monitor) {
780 g_object_unref (bg->file_monitor);
781 bg->file_monitor = NULL((void*)0);
782 }
783
784 if (bg->filename) {
785 GFile *f = g_file_new_for_path (bg->filename);
786
787 bg->file_monitor = g_file_monitor_file (f, 0, NULL((void*)0), NULL((void*)0));
788 g_signal_connect (bg->file_monitor, "changed",g_signal_connect_data ((bg->file_monitor), ("changed"), ((
(GCallback) (file_changed))), (bg), ((void*)0), (GConnectFlags
) 0)
789 G_CALLBACK (file_changed), bg)g_signal_connect_data ((bg->file_monitor), ("changed"), ((
(GCallback) (file_changed))), (bg), ((void*)0), (GConnectFlags
) 0)
;
790
791 g_object_unref (f);
792 }
793
794 clear_cache (bg);
795
796 queue_changed (bg);
797 }
798}
799
800static void
801draw_color_area (CafeBG *bg,
802 GdkPixbuf *dest,
803 CdkRectangle *rect)
804{
805 guint32 pixel;
806 CdkRectangle extent;
807
808 extent.x = 0;
809 extent.y = 0;
810 extent.width = gdk_pixbuf_get_width (dest);
811 extent.height = gdk_pixbuf_get_height (dest);
812
813 cdk_rectangle_intersect (rect, &extent, rect);
814
815 switch (bg->color_type) {
816 case CAFE_BG_COLOR_SOLID:
817 /* not really a big deal to ignore the area of interest */
818 pixel = ((guint) (bg->primary.red * 0xff) << 24) |
819 ((guint) (bg->primary.green * 0xff) << 16) |
820 ((guint) (bg->primary.blue * 0xff) << 8) |
821 (0xff);
822
823 gdk_pixbuf_fill (dest, pixel);
824 break;
825
826 case CAFE_BG_COLOR_H_GRADIENT:
827 pixbuf_draw_gradient (dest, TRUE(!(0)), &(bg->primary), &(bg->secondary), rect);
828 break;
829
830 case CAFE_BG_COLOR_V_GRADIENT:
831 pixbuf_draw_gradient (dest, FALSE(0), &(bg->primary), &(bg->secondary), rect);
832 break;
833
834 default:
835 break;
836 }
837}
838
839static void
840draw_color (CafeBG *bg,
841 GdkPixbuf *dest)
842{
843 CdkRectangle rect;
844
845 rect.x = 0;
846 rect.y = 0;
847 rect.width = gdk_pixbuf_get_width (dest);
848 rect.height = gdk_pixbuf_get_height (dest);
849 draw_color_area (bg, dest, &rect);
850}
851
852static void
853draw_color_each_monitor (CafeBG *bg,
854 GdkPixbuf *dest,
855 CdkScreen *screen)
856{
857 CdkDisplay *display;
858 CdkRectangle rect;
859 gint num_monitors;
860 int monitor;
861
862 display = cdk_screen_get_display (screen);
863 num_monitors = cdk_display_get_n_monitors (display);
864 for (monitor = 0; monitor < num_monitors; monitor++) {
865 cdk_monitor_get_geometry (cdk_display_get_monitor (display, monitor), &rect);
866 draw_color_area (bg, dest, &rect);
867 }
868}
869
870static GdkPixbuf *
871pixbuf_clip_to_fit (GdkPixbuf *src,
872 int max_width,
873 int max_height)
874{
875 int src_width, src_height;
876 int w, h;
877 int src_x, src_y;
878 GdkPixbuf *pixbuf;
879
880 src_width = gdk_pixbuf_get_width (src);
881 src_height = gdk_pixbuf_get_height (src);
882
883 if (src_width < max_width && src_height < max_height)
884 return g_object_ref (src)((__typeof__ (src)) (g_object_ref) (src));
885
886 w = MIN(src_width, max_width)(((src_width) < (max_width)) ? (src_width) : (max_width));
887 h = MIN(src_height, max_height)(((src_height) < (max_height)) ? (src_height) : (max_height
))
;
888
889 pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
890 gdk_pixbuf_get_has_alpha (src),
891 8, w, h);
892
893 src_x = (src_width - w) / 2;
894 src_y = (src_height - h) / 2;
895 gdk_pixbuf_copy_area (src,
896 src_x, src_y,
897 w, h,
898 pixbuf,
899 0, 0);
900 return pixbuf;
901}
902
903static GdkPixbuf *
904get_scaled_pixbuf (CafeBGPlacement placement,
905 GdkPixbuf *pixbuf,
906 int width, int height,
907 int *x, int *y,
908 int *w, int *h)
909{
910 GdkPixbuf *new;
911
912#if 0
913 g_print ("original_width: %d %d\n",
914 gdk_pixbuf_get_width (pixbuf),
915 gdk_pixbuf_get_height (pixbuf));
916#endif
917
918 switch (placement) {
919 case CAFE_BG_PLACEMENT_SPANNED:
920 new = pixbuf_scale_to_fit (pixbuf, width, height);
921 break;
922 case CAFE_BG_PLACEMENT_ZOOMED:
923 new = pixbuf_scale_to_min (pixbuf, width, height);
924 break;
925
926 case CAFE_BG_PLACEMENT_FILL_SCREEN:
927 new = gdk_pixbuf_scale_simple (pixbuf, width, height,
928 GDK_INTERP_BILINEAR);
929 break;
930
931 case CAFE_BG_PLACEMENT_SCALED:
932 new = pixbuf_scale_to_fit (pixbuf, width, height);
933 break;
934
935 case CAFE_BG_PLACEMENT_CENTERED:
936 case CAFE_BG_PLACEMENT_TILED:
937 default:
938 new = pixbuf_clip_to_fit (pixbuf, width, height);
939 break;
940 }
941
942 *w = gdk_pixbuf_get_width (new);
943 *h = gdk_pixbuf_get_height (new);
944 *x = (width - *w) / 2;
945 *y = (height - *h) / 2;
946
947 return new;
948}
949
950
951static void
952draw_image_area (CafeBG *bg,
953 gint num_monitor,
954 GdkPixbuf *pixbuf,
955 GdkPixbuf *dest,
956 CdkRectangle *area)
957{
958 int dest_width = area->width;
959 int dest_height = area->height;
960 int x, y, w, h;
961 GdkPixbuf *scaled;
962
963 if (!pixbuf)
964 return;
965
966 scaled = get_scaled_pixbuf (bg->placement, pixbuf, dest_width, dest_height, &x, &y, &w, &h);
967
968 switch (bg->placement) {
969 case CAFE_BG_PLACEMENT_TILED:
970 pixbuf_tile (scaled, dest);
971 break;
972 case CAFE_BG_PLACEMENT_ZOOMED:
973 case CAFE_BG_PLACEMENT_CENTERED:
974 case CAFE_BG_PLACEMENT_FILL_SCREEN:
975 case CAFE_BG_PLACEMENT_SCALED:
976 pixbuf_blend (scaled, dest, 0, 0, w, h, x + area->x, y + area->y, 1.0);
977 break;
978 case CAFE_BG_PLACEMENT_SPANNED:
979 pixbuf_blend (scaled, dest, 0, 0, w, h, x, y, 1.0);
980 break;
981 default:
982 g_assert_not_reached ()do { g_assertion_message_expr ("CafeDesktop", "cafe-bg.c", 982
, ((const char*) (__func__)), ((void*)0)); } while (0)
;
983 break;
984 }
985
986 refresh_cache_file (bg, scaled, num_monitor, dest_width, dest_height);
987
988 g_object_unref (scaled);
989}
990
991static void
992draw_image_for_thumb (CafeBG *bg,
993 GdkPixbuf *pixbuf,
994 GdkPixbuf *dest)
995{
996 CdkRectangle rect;
997
998 rect.x = 0;
999 rect.y = 0;
1000 rect.width = gdk_pixbuf_get_width (dest);
1001 rect.height = gdk_pixbuf_get_height (dest);
1002
1003 draw_image_area (bg, -1, pixbuf, dest, &rect);
1004}
1005
1006static void
1007draw_once (CafeBG *bg,
1008 GdkPixbuf *dest,
1009 gboolean is_root)
1010{
1011 CdkRectangle rect;
1012 GdkPixbuf *pixbuf;
1013 gint monitor;
1014
1015 /* whether we're drawing on root window or normal (Baul) window */
1016 monitor = (is_root) ? 0 : -1;
1017
1018 rect.x = 0;
1019 rect.y = 0;
1020 rect.width = gdk_pixbuf_get_width (dest);
1021 rect.height = gdk_pixbuf_get_height (dest);
1022
1023 pixbuf = get_pixbuf_for_size (bg, monitor, rect.width, rect.height);
1024 if (pixbuf) {
1025 draw_image_area (bg, monitor, pixbuf, dest, &rect);
1026
1027 g_object_unref (pixbuf);
1028 }
1029}
1030
1031static void
1032draw_each_monitor (CafeBG *bg,
1033 GdkPixbuf *dest,
1034 CdkScreen *screen)
1035{
1036 CdkDisplay *display;
1037
1038 display = cdk_screen_get_display (screen);
1039 gint num_monitors = cdk_display_get_n_monitors (display);
1040 gint monitor = 0;
1041
1042 for (; monitor < num_monitors; monitor++) {
1043 CdkRectangle rect;
1044 GdkPixbuf *pixbuf;
1045
1046 cdk_monitor_get_geometry (cdk_display_get_monitor (display, monitor), &rect);
1047
1048 pixbuf = get_pixbuf_for_size (bg, monitor, rect.width, rect.height);
1049 if (pixbuf) {
1050 draw_image_area (bg, monitor, pixbuf, dest, &rect);
1051
1052 g_object_unref (pixbuf);
1053 }
1054 }
1055}
1056
1057void
1058cafe_bg_draw (CafeBG *bg,
1059 GdkPixbuf *dest,
1060 CdkScreen *screen,
1061 gboolean is_root)
1062{
1063 if (!bg)
1064 return;
1065
1066 if (is_root && (bg->placement != CAFE_BG_PLACEMENT_SPANNED)) {
1067 draw_color_each_monitor (bg, dest, screen);
1068 if (bg->filename) {
1069 draw_each_monitor (bg, dest, screen);
1070 }
1071 } else {
1072 draw_color (bg, dest);
1073 if (bg->filename) {
1074 draw_once (bg, dest, is_root);
1075 }
1076 }
1077}
1078
1079gboolean
1080cafe_bg_has_multiple_sizes (CafeBG *bg)
1081{
1082 SlideShow *show;
1083 gboolean ret;
1084
1085 g_return_val_if_fail (bg != NULL, FALSE)do { if ((bg != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "bg != NULL"); return
((0)); } } while (0)
;
1086
1087 ret = FALSE(0);
1088
1089 show = get_as_slideshow (bg, bg->filename);
1090 if (show) {
1091 ret = slideshow_has_multiple_sizes (show);
1092 slideshow_unref (show);
1093 }
1094
1095 return ret;
1096}
1097
1098static void
1099cafe_bg_get_pixmap_size (CafeBG *bg,
1100 int width,
1101 int height,
1102 int *pixmap_width,
1103 int *pixmap_height)
1104{
1105 int dummy;
1106
1107 if (!pixmap_width)
1108 pixmap_width = &dummy;
1109 if (!pixmap_height)
1110 pixmap_height = &dummy;
1111
1112 *pixmap_width = width;
1113 *pixmap_height = height;
1114
1115 if (!bg->filename) {
1116 switch (bg->color_type) {
1117 case CAFE_BG_COLOR_SOLID:
1118 *pixmap_width = 1;
1119 *pixmap_height = 1;
1120 break;
1121
1122 case CAFE_BG_COLOR_H_GRADIENT:
1123 case CAFE_BG_COLOR_V_GRADIENT:
1124 break;
1125 }
1126
1127 return;
1128 }
1129}
1130
1131/**
1132 * cafe_bg_create_surface:
1133 * @bg: CafeBG
1134 * @window:
1135 * @width:
1136 * @height:
1137 * @root:
1138 *
1139 * Create a surface that can be set as background for @window. If @root is
1140 * TRUE, the surface created will be created by a temporary X server connection
1141 * so that if someone calls XKillClient on it, it won't affect the application
1142 * who created it.
1143 **/
1144cairo_surface_t *
1145cafe_bg_create_surface (CafeBG *bg,
1146 CdkWindow *window,
1147 int width,
1148 int height,
1149 gboolean root)
1150{
1151 return cafe_bg_create_surface_scale (bg,
1152 window,
1153 width,
1154 height,
1155 1,
1156 root);
1157}
1158
1159/**
1160 * cafe_bg_create_surface_scale:
1161 * @bg: CafeBG
1162 * @window:
1163 * @width:
1164 * @height:
1165 * @scale:
1166 * @root:
1167 *
1168 * Create a scaled surface that can be set as background for @window. If @root is
1169 * TRUE, the surface created will be created by a temporary X server connection
1170 * so that if someone calls XKillClient on it, it won't affect the application
1171 * who created it.
1172 **/
1173cairo_surface_t *
1174cafe_bg_create_surface_scale (CafeBG *bg,
1175 CdkWindow *window,
1176 int width,
1177 int height,
1178 int scale,
1179 gboolean root)
1180{
1181 int pm_width, pm_height;
1182
1183 cairo_surface_t *surface;
1184 cairo_t *cr;
1185
1186 g_return_val_if_fail (bg != NULL, NULL)do { if ((bg != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "bg != NULL"); return
(((void*)0)); } } while (0)
;
1187 g_return_val_if_fail (window != NULL, NULL)do { if ((window != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "window != NULL"
); return (((void*)0)); } } while (0)
;
1188
1189 if (bg->pixbuf_cache &&
1190 (gdk_pixbuf_get_width (bg->pixbuf_cache) != width ||
1191 gdk_pixbuf_get_height (bg->pixbuf_cache) != height))
1192 {
1193 g_object_unref (bg->pixbuf_cache);
1194 bg->pixbuf_cache = NULL((void*)0);
1195 }
1196
1197 cafe_bg_get_pixmap_size (bg, width, height, &pm_width, &pm_height);
1198
1199 if (root)
1200 {
1201 surface = make_root_pixmap (window, pm_width * scale, pm_height * scale);
1202 }
1203 else
1204 {
1205 surface = cdk_window_create_similar_surface (window, CAIRO_CONTENT_COLOR,
1206 pm_width, pm_height);
1207 }
1208
1209 cr = cairo_create (surface);
1210 cairo_scale (cr, (double)scale, (double)scale);
1211
1212 if (!bg->filename && bg->color_type == CAFE_BG_COLOR_SOLID) {
1213 cdk_cairo_set_source_rgba (cr, &(bg->primary));
1214 }
1215 else
1216 {
1217 GdkPixbuf *pixbuf;
1218
1219 pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE(0), 8,
1220 width, height);
1221 cafe_bg_draw (bg, pixbuf, cdk_window_get_screen (window), root);
1222 cdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
1223 g_object_unref (pixbuf);
1224 }
1225
1226 cairo_paint (cr);
1227
1228 cairo_destroy (cr);
1229
1230 return surface;
1231}
1232
1233
1234/* determine if a background is darker or lighter than average, to help
1235 * clients know what colors to draw on top with
1236 */
1237gboolean
1238cafe_bg_is_dark (CafeBG *bg,
1239 int width,
1240 int height)
1241{
1242 CdkRGBA color;
1243 int intensity;
1244 GdkPixbuf *pixbuf;
1245
1246 g_return_val_if_fail (bg != NULL, FALSE)do { if ((bg != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "bg != NULL"); return
((0)); } } while (0)
;
1247
1248 if (bg->color_type == CAFE_BG_COLOR_SOLID) {
1249 color = bg->primary;
1250 } else {
1251 color.red = (bg->primary.red + bg->secondary.red) / 2;
1252 color.green = (bg->primary.green + bg->secondary.green) / 2;
1253 color.blue = (bg->primary.blue + bg->secondary.blue) / 2;
1254 }
1255 pixbuf = get_pixbuf_for_size (bg, -1, width, height);
1256 if (pixbuf) {
1257 CdkRGBA argb;
1258 guchar a, r, g, b;
1259
1260 pixbuf_average_value (pixbuf, &argb);
1261 a = argb.alpha * 0xff;
1262 r = argb.red * 0xff;
1263 g = argb.green * 0xff;
1264 b = argb.blue * 0xff;
1265
1266 color.red = (color.red * (0xFF - a) + r * 0x101 * a) / 0xFF;
1267 color.green = (color.green * (0xFF - a) + g * 0x101 * a) / 0xFF;
1268 color.blue = (color.blue * (0xFF - a) + b * 0x101 * a) / 0xFF;
1269 g_object_unref (pixbuf);
1270 }
1271
1272 intensity = ((guint) (color.red * 65535) * 77 +
1273 (guint) (color.green * 65535) * 150 +
1274 (guint) (color.blue * 65535) * 28) >> 16;
1275
1276 return intensity < 160; /* biased slightly to be dark */
1277}
1278
1279/*
1280 * Create a persistent pixmap. We create a separate display
1281 * and set the closedown mode on it to RetainPermanent.
1282 */
1283static cairo_surface_t *
1284make_root_pixmap (CdkWindow *window, gint width, gint height)
1285{
1286 CdkScreen *screen = cdk_window_get_screen(window);
1287 char *disp_name = DisplayString (CDK_WINDOW_XDISPLAY (window))(((_XPrivDisplay)(((cdk_x11_display_get_xdisplay (cdk_window_get_display
(window))))))->display_name)
;
1288 Display *display;
1289 Pixmap xpixmap;
1290 cairo_surface_t *surface;
1291 int depth;
1292
1293 /* Desktop background pixmap should be created from dummy X client since most
1294 * applications will try to kill it with XKillClient later when changing pixmap
1295 */
1296 display = XOpenDisplay (disp_name);
1297
1298 if (display == NULL((void*)0)) {
1299 g_warning ("Unable to open display '%s' when setting background pixmap\n",
1300 (disp_name) ? disp_name : "NULL");
1301 return NULL((void*)0);
1302 }
1303
1304 depth = DefaultDepth (display, cdk_x11_screen_get_screen_number (screen))((&((_XPrivDisplay)(display))->screens[cdk_x11_screen_get_screen_number
(screen)])->root_depth)
;
1305 xpixmap = XCreatePixmap (display, CDK_WINDOW_XID (window)(cdk_x11_window_get_xid (window)), width, height, depth);
1306
1307 XFlush (display);
1308 XSetCloseDownMode (display, RetainPermanent1);
1309 XCloseDisplay (display);
1310
1311 surface = cairo_xlib_surface_create (CDK_SCREEN_XDISPLAY (screen)(cdk_x11_display_get_xdisplay (cdk_screen_get_display (screen
)))
, xpixmap,
1312 CDK_VISUAL_XVISUAL (cdk_screen_get_system_visual (screen))(cdk_x11_visual_get_xvisual (cdk_screen_get_system_visual (screen
)))
,
1313 width, height);
1314
1315 return surface;
1316}
1317
1318static gboolean
1319get_original_size (const char *filename,
1320 int *orig_width,
1321 int *orig_height)
1322{
1323 gboolean result;
1324
1325 if (gdk_pixbuf_get_file_info (filename, orig_width, orig_height))
1326 result = TRUE(!(0));
1327 else
1328 result = FALSE(0);
1329
1330 return result;
1331}
1332
1333static const char *
1334get_filename_for_size (CafeBG *bg, gint best_width, gint best_height)
1335{
1336 SlideShow *show;
1337 Slide *slide;
1338 FileSize *size;
1339
1340 if (!bg->filename)
1341 return NULL((void*)0);
1342
1343 show = get_as_slideshow (bg, bg->filename);
1344 if (!show) {
1345 return bg->filename;
1346 }
1347
1348 slide = get_current_slide (show, NULL((void*)0));
1349 slideshow_unref (show);
1350 size = find_best_size (slide->file1, best_width, best_height);
1351 return size->file;
1352}
1353
1354gboolean
1355cafe_bg_get_image_size (CafeBG *bg,
1356 CafeDesktopThumbnailFactory *factory,
1357 int best_width,
1358 int best_height,
1359 int *width,
1360 int *height)
1361{
1362 GdkPixbuf *thumb;
1363 gboolean result = FALSE(0);
1364 const gchar *filename;
1365
1366 g_return_val_if_fail (bg != NULL, FALSE)do { if ((bg != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "bg != NULL"); return
((0)); } } while (0)
;
1367 g_return_val_if_fail (factory != NULL, FALSE)do { if ((factory != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "factory != NULL"
); return ((0)); } } while (0)
;
1368
1369 if (!bg->filename)
1370 return FALSE(0);
1371
1372 filename = get_filename_for_size (bg, best_width, best_height);
1373 thumb = create_thumbnail_for_filename (factory, filename);
1374 if (thumb) {
1375 if (get_thumb_annotations (thumb, width, height))
1376 result = TRUE(!(0));
1377
1378 g_object_unref (thumb);
1379 }
1380
1381 if (!result) {
1382 if (get_original_size (filename, width, height))
1383 result = TRUE(!(0));
1384 }
1385
1386 return result;
1387}
1388
1389static double
1390fit_factor (int from_width, int from_height,
1391 int to_width, int to_height)
1392{
1393 return MIN (to_width / (double) from_width, to_height / (double) from_height)(((to_width / (double) from_width) < (to_height / (double)
from_height)) ? (to_width / (double) from_width) : (to_height
/ (double) from_height))
;
1394}
1395
1396/**
1397 * cafe_bg_create_thumbnail:
1398 *
1399 * Returns: (transfer full): a #GdkPixbuf showing the background as a thumbnail
1400 */
1401GdkPixbuf *
1402cafe_bg_create_thumbnail (CafeBG *bg,
1403 CafeDesktopThumbnailFactory *factory,
1404 CdkScreen *screen,
1405 int dest_width,
1406 int dest_height)
1407{
1408 GdkPixbuf *result;
1409
1410 g_return_val_if_fail (bg != NULL, NULL)do { if ((bg != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "bg != NULL"); return
(((void*)0)); } } while (0)
;
1411
1412 result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE(0), 8, dest_width, dest_height);
1413
1414 draw_color (bg, result);
1415
1416 if (bg->filename) {
1417 GdkPixbuf *thumb;
1418
1419 thumb = create_img_thumbnail (bg, factory, screen, dest_width, dest_height, -1);
1420
1421 if (thumb) {
1422 draw_image_for_thumb (bg, thumb, result);
1423 g_object_unref (thumb);
1424 }
1425 }
1426
1427 return result;
1428}
1429
1430/**
1431 * cafe_bg_get_surface_from_root:
1432 * @screen: a #CdkScreen
1433 *
1434 * This function queries the _XROOTPMAP_ID property from
1435 * the root window associated with @screen to determine
1436 * the current root window background surface and returns
1437 * a copy of it. If the _XROOTPMAP_ID is not set, then
1438 * a black surface is returned.
1439 *
1440 * Return value: a #cairo_surface_t if successful or %NULL
1441 **/
1442cairo_surface_t *
1443cafe_bg_get_surface_from_root (CdkScreen *screen)
1444{
1445 int result;
1446 gint format;
1447 gulong nitems;
1448 gulong bytes_after;
1449 guchar *data;
1450 Atom type;
1451 Display *display;
1452 int screen_num;
1453 cairo_surface_t *surface;
1454 cairo_surface_t *source_pixmap;
1455 int width, height;
1456
1457 display = CDK_DISPLAY_XDISPLAY (cdk_screen_get_display (screen))(cdk_x11_display_get_xdisplay (cdk_screen_get_display (screen
)))
;
1458 screen_num = cdk_x11_screen_get_screen_number (screen);
1459
1460 result = XGetWindowProperty (display,
1461 RootWindow (display, screen_num)((&((_XPrivDisplay)(display))->screens[screen_num])->
root)
,
1462 cdk_x11_get_xatom_by_name ("_XROOTPMAP_ID"),
1463 0L, 1L, False0, XA_PIXMAP((Atom) 20),
1464 &type, &format, &nitems, &bytes_after,
1465 &data);
1466 surface = NULL((void*)0);
1467 source_pixmap = NULL((void*)0);
1468
1469 if (result != Success0 || type != XA_PIXMAP((Atom) 20) ||
1470 format != 32 || nitems != 1) {
1471 XFree (data);
1472 data = NULL((void*)0);
1473 }
1474
1475 if (data != NULL((void*)0)) {
1476 CdkDisplay *cdkdisplay;
1477
1478 cdkdisplay = cdk_screen_get_display (screen);
1479 cdk_x11_display_error_trap_push (cdkdisplay);
1480
1481 Pixmap xpixmap = *(Pixmap *) data;
1482 Window root_return;
1483 int x_ret, y_ret;
1484 unsigned int w_ret, h_ret, bw_ret, depth_ret;
1485
1486 if (XGetGeometry (CDK_SCREEN_XDISPLAY (screen)(cdk_x11_display_get_xdisplay (cdk_screen_get_display (screen
)))
,
1487 xpixmap,
1488 &root_return,
1489 &x_ret, &y_ret, &w_ret, &h_ret, &bw_ret, &depth_ret))
1490 {
1491 source_pixmap = cairo_xlib_surface_create (CDK_SCREEN_XDISPLAY (screen)(cdk_x11_display_get_xdisplay (cdk_screen_get_display (screen
)))
,
1492 xpixmap,
1493 CDK_VISUAL_XVISUAL (cdk_screen_get_system_visual (screen))(cdk_x11_visual_get_xvisual (cdk_screen_get_system_visual (screen
)))
,
1494 w_ret, h_ret);
1495 }
1496
1497 cdk_x11_display_error_trap_pop_ignored (cdkdisplay);
1498 }
1499
1500 width = WidthOfScreen (cdk_x11_screen_get_xscreen (screen))((cdk_x11_screen_get_xscreen (screen))->width);
1501 height = HeightOfScreen (cdk_x11_screen_get_xscreen (screen))((cdk_x11_screen_get_xscreen (screen))->height);
1502
1503 if (source_pixmap) {
1504 cairo_t *cr;
1505
1506 surface = cairo_surface_create_similar (source_pixmap,
1507 CAIRO_CONTENT_COLOR,
1508 width, height);
1509
1510 cr = cairo_create (surface);
1511 cairo_set_source_surface (cr, source_pixmap, 0, 0);
1512 cairo_paint (cr);
1513
1514 if (cairo_status (cr) != CAIRO_STATUS_SUCCESS) {
1515 cairo_surface_destroy (surface);
1516 surface = NULL((void*)0);
1517 }
1518
1519 cairo_destroy (cr);
1520 }
1521
1522 if (surface == NULL((void*)0)) {
1523 surface = cdk_window_create_similar_surface (cdk_screen_get_root_window (screen),
1524 CAIRO_CONTENT_COLOR,
1525 width, height);
1526 }
1527
1528 if (source_pixmap != NULL((void*)0))
1529 cairo_surface_destroy (source_pixmap);
1530
1531 if (data != NULL((void*)0))
1532 XFree (data);
1533
1534 return surface;
1535}
1536
1537/* Sets the "ESETROOT_PMAP_ID" property to later be used to free the pixmap,
1538 */
1539static void
1540cafe_bg_set_root_pixmap_id (CdkScreen *screen,
1541 Display *display,
1542 Pixmap xpixmap)
1543{
1544 Window xroot = RootWindow (display, cdk_x11_screen_get_screen_number (screen))((&((_XPrivDisplay)(display))->screens[cdk_x11_screen_get_screen_number
(screen)])->root)
;
1545 char *atom_names[] = {"_XROOTPMAP_ID", "ESETROOT_PMAP_ID"};
1546 Atom atoms[G_N_ELEMENTS(atom_names)(sizeof (atom_names) / sizeof ((atom_names)[0]))] = {0};
1547
1548 Atom type;
1549 int format;
1550 unsigned long nitems, after;
1551 unsigned char *data_root, *data_esetroot;
1552
1553 /* Get atoms for both properties in an array, only if they exist.
1554 * This method is to avoid multiple round-trips to Xserver
1555 */
1556 if (XInternAtoms (display, atom_names, G_N_ELEMENTS(atom_names)(sizeof (atom_names) / sizeof ((atom_names)[0])), True1, atoms) &&
1557 atoms[0] != None0L && atoms[1] != None0L) {
1558 int result;
1559
1560 result = XGetWindowProperty (display, xroot, atoms[0], 0L, 1L,
1561 False0, AnyPropertyType0L,
1562 &type, &format, &nitems, &after,
1563 &data_root);
1564
1565 if (data_root != NULL((void*)0) && result == Success0 &&
1566 type == XA_PIXMAP((Atom) 20) && format == 32 && nitems == 1) {
1567 result = XGetWindowProperty (display, xroot, atoms[1],
1568 0L, 1L, False0,
1569 AnyPropertyType0L,
1570 &type, &format, &nitems,
1571 &after, &data_esetroot);
1572
1573 if (data_esetroot != NULL((void*)0) && result == Success0 &&
1574 type == XA_PIXMAP((Atom) 20) && format == 32 && nitems == 1) {
1575 CdkDisplay *cdkdisplay;
1576
1577 Pixmap xrootpmap = *((Pixmap *) data_root);
1578 Pixmap esetrootpmap = *((Pixmap *) data_esetroot);
1579
1580 cdkdisplay = cdk_screen_get_display (screen);
1581 cdk_x11_display_error_trap_push (cdkdisplay);
1582 if (xrootpmap && xrootpmap == esetrootpmap) {
1583 XKillClient (display, xrootpmap);
1584 }
1585 if (esetrootpmap && esetrootpmap != xrootpmap) {
1586 XKillClient (display, esetrootpmap);
1587 }
1588 cdk_x11_display_error_trap_pop_ignored (cdkdisplay);
1589 }
1590 if (data_esetroot != NULL((void*)0)) {
1591 XFree (data_esetroot);
1592 }
1593 }
1594 if (data_root != NULL((void*)0)) {
1595 XFree (data_root);
1596 }
1597 }
1598
1599 /* Get atoms for both properties in an array, create them if needed.
1600 * This method is to avoid multiple round-trips to Xserver
1601 */
1602 if (!XInternAtoms (display, atom_names, G_N_ELEMENTS(atom_names)(sizeof (atom_names) / sizeof ((atom_names)[0])), False0, atoms) ||
1603 atoms[0] == None0L || atoms[1] == None0L) {
1604 g_warning ("Could not create atoms needed to set root pixmap id/properties.\n");
1605 return;
1606 }
1607
1608 /* Set new _XROOTMAP_ID and ESETROOT_PMAP_ID properties */
1609 XChangeProperty (display, xroot, atoms[0], XA_PIXMAP((Atom) 20), 32,
1610 PropModeReplace0, (unsigned char *) &xpixmap, 1);
1611
1612 XChangeProperty (display, xroot, atoms[1], XA_PIXMAP((Atom) 20), 32,
1613 PropModeReplace0, (unsigned char *) &xpixmap, 1);
1614}
1615
1616/**
1617 * cafe_bg_set_surface_as_root:
1618 * @screen: the #CdkScreen to change root background on
1619 * @surface: the #cairo_surface_t to set root background from.
1620 * Must be an xlib surface backing a pixmap.
1621 *
1622 * Set the root pixmap, and properties pointing to it. We
1623 * do this atomically with a server grab to make sure that
1624 * we won't leak the pixmap if somebody else it setting
1625 * it at the same time. (This assumes that they follow the
1626 * same conventions we do). @surface should come from a call
1627 * to cafe_bg_create_surface().
1628 **/
1629void
1630cafe_bg_set_surface_as_root (CdkScreen *screen, cairo_surface_t *surface)
1631{
1632 g_return_if_fail (screen != NULL)do { if ((screen != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "screen != NULL"
); return; } } while (0)
;
1633 g_return_if_fail (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_XLIB)do { if ((cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_XLIB
)) { } else { g_return_if_fail_warning ("CafeDesktop", ((const
char*) (__func__)), "cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_XLIB"
); return; } } while (0)
;
1634
1635 /* Desktop background pixmap should be created from dummy X client since most
1636 * applications will try to kill it with XKillClient later when changing pixmap
1637 */
1638 Display *display = CDK_DISPLAY_XDISPLAY (cdk_screen_get_display (screen))(cdk_x11_display_get_xdisplay (cdk_screen_get_display (screen
)))
;
1639 Pixmap pixmap_id = cairo_xlib_surface_get_drawable (surface);
1640 Window xroot = RootWindow (display, cdk_x11_screen_get_screen_number (screen))((&((_XPrivDisplay)(display))->screens[cdk_x11_screen_get_screen_number
(screen)])->root)
;
1641
1642 XGrabServer (display);
1643 cafe_bg_set_root_pixmap_id (screen, display, pixmap_id);
1644
1645 XSetWindowBackgroundPixmap (display, xroot, pixmap_id);
1646 XClearWindow (display, xroot);
1647
1648 XFlush (display);
1649 XUngrabServer (display);
1650}
1651
1652/**
1653 * cafe_bg_set_surface_as_root_with_crossfade:
1654 * @screen: the #CdkScreen to change root background on
1655 * @surface: the cairo xlib surface to set root background from
1656 *
1657 * Set the root pixmap, and properties pointing to it.
1658 * This function differs from cafe_bg_set_surface_as_root()
1659 * in that it adds a subtle crossfade animation from the
1660 * current root pixmap to the new one.
1661 *
1662 * Return value: (transfer full): a #CafeBGCrossfade object
1663 **/
1664CafeBGCrossfade *
1665cafe_bg_set_surface_as_root_with_crossfade (CdkScreen *screen,
1666 cairo_surface_t *surface)
1667{
1668 CdkWindow *root_window;
1669 int width, height;
1670 CafeBGCrossfade *fade;
1671 cairo_t *cr;
1672 cairo_surface_t *old_surface;
1673
1674 g_return_val_if_fail (screen != NULL, NULL)do { if ((screen != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "screen != NULL"
); return (((void*)0)); } } while (0)
;
1675 g_return_val_if_fail (surface != NULL, NULL)do { if ((surface != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "surface != NULL"
); return (((void*)0)); } } while (0)
;
1676
1677 root_window = cdk_screen_get_root_window (screen);
1678 width = cdk_window_get_width (root_window);
1679 height = cdk_window_get_height (root_window);
1680 fade = cafe_bg_crossfade_new (width, height);
1681 old_surface = cafe_bg_get_surface_from_root (screen);
1682
1683 cafe_bg_crossfade_set_start_surface (fade, old_surface);
1684 cafe_bg_crossfade_set_end_surface (fade, surface);
1685
1686 /* Before setting the surface as a root pixmap, let's have it draw
1687 * the old stuff, just so it won't be noticable
1688 * (crossfade will later get it back)
1689 */
1690 cr = cairo_create (surface);
1691 cairo_set_source_surface (cr, old_surface, 0, 0);
1692 cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT);
1693 cairo_paint (cr);
1694 cairo_destroy (cr);
1695 cairo_surface_destroy (old_surface);
1696
1697 cafe_bg_set_surface_as_root (screen, surface);
1698 cafe_bg_crossfade_start (fade, root_window);
1699
1700 return fade;
1701}
1702
1703/* Implementation of the pixbuf cache */
1704struct _SlideShow
1705{
1706 gint ref_count;
1707 double start_time;
1708 double total_duration;
1709
1710 GQueue *slides;
1711
1712 gboolean has_multiple_sizes;
1713
1714 /* used during parsing */
1715 struct tm start_tm;
1716 GQueue *stack;
1717};
1718
1719static double
1720now (void)
1721{
1722 const double microseconds_per_second = (double) G_USEC_PER_SEC1000000;
1723 gint64 tv;
1724
1725 tv = g_get_real_time ();
1726
1727 return (double) (tv / microseconds_per_second);
1728}
1729
1730static Slide *
1731get_current_slide (SlideShow *show,
1732 double *alpha)
1733{
1734 double delta = fmod (now() - show->start_time, show->total_duration);
1735 GList *list;
1736 double elapsed;
1737 int i;
1738
1739 if (delta < 0)
1740 delta += show->total_duration;
1741
1742 elapsed = 0;
1743 i = 0;
1744 for (list = show->slides->head; list != NULL((void*)0); list = list->next) {
1745 Slide *slide = list->data;
1746
1747 if (elapsed + slide->duration > delta) {
1748 if (alpha)
1749 *alpha = (delta - elapsed) / (double)slide->duration;
1750 return slide;
1751 }
1752
1753 i++;
1754 elapsed += slide->duration;
1755 }
1756
1757 /* this should never happen since we have slides and we should always
1758 * find a current slide for the elapsed time since beginning -- we're
1759 * looping with fmod() */
1760 g_assert_not_reached ()do { g_assertion_message_expr ("CafeDesktop", "cafe-bg.c", 1760
, ((const char*) (__func__)), ((void*)0)); } while (0)
;
1761
1762 return NULL((void*)0);
1763}
1764
1765static GdkPixbuf *
1766blend (GdkPixbuf *p1,
1767 GdkPixbuf *p2,
1768 double alpha)
1769{
1770 GdkPixbuf *result = gdk_pixbuf_copy (p1);
1771 GdkPixbuf *tmp;
1772
1773 if (gdk_pixbuf_get_width (p2) != gdk_pixbuf_get_width (p1) ||
1774 gdk_pixbuf_get_height (p2) != gdk_pixbuf_get_height (p1)) {
1775 tmp = gdk_pixbuf_scale_simple (p2,
1776 gdk_pixbuf_get_width (p1),
1777 gdk_pixbuf_get_height (p1),
1778 GDK_INTERP_BILINEAR);
1779 }
1780 else {
1781 tmp = g_object_ref (p2)((__typeof__ (p2)) (g_object_ref) (p2));
1782 }
1783
1784 pixbuf_blend (tmp, result, 0, 0, -1, -1, 0, 0, alpha);
1785
1786 g_object_unref (tmp);
1787
1788 return result;
1789}
1790
1791typedef enum {
1792 PIXBUF,
1793 SLIDESHOW,
1794 THUMBNAIL
1795} FileType;
1796
1797struct FileCacheEntry
1798{
1799 FileType type;
1800 char *filename;
1801 union {
1802 GdkPixbuf *pixbuf;
1803 SlideShow *slideshow;
1804 GdkPixbuf *thumbnail;
1805 } u;
1806};
1807
1808static void
1809file_cache_entry_delete (FileCacheEntry *ent)
1810{
1811 g_free (ent->filename);
1812
1813 switch (ent->type) {
1814 case PIXBUF:
1815 g_object_unref (ent->u.pixbuf);
1816 break;
1817 case SLIDESHOW:
1818 slideshow_unref (ent->u.slideshow);
1819 break;
1820 case THUMBNAIL:
1821 g_object_unref (ent->u.thumbnail);
1822 break;
1823 }
1824
1825 g_free (ent);
1826}
1827
1828static void
1829bound_cache (CafeBG *bg)
1830{
1831 while (g_list_length (bg->file_cache) >= CACHE_SIZE4) {
1832 GList *last_link = g_list_last (bg->file_cache);
1833 FileCacheEntry *ent = last_link->data;
1834
1835 file_cache_entry_delete (ent);
1836
1837 bg->file_cache = g_list_delete_link (bg->file_cache, last_link);
1838 }
1839}
1840
1841static const FileCacheEntry *
1842file_cache_lookup (CafeBG *bg, FileType type, const char *filename)
1843{
1844 GList *list;
1845
1846 for (list = bg->file_cache; list != NULL((void*)0); list = list->next) {
1847 FileCacheEntry *ent = list->data;
1848
1849 if (ent && ent->type == type &&
1850 strcmp (ent->filename, filename) == 0) {
1851 return ent;
1852 }
1853 }
1854
1855 return NULL((void*)0);
1856}
1857
1858static FileCacheEntry *
1859file_cache_entry_new (CafeBG *bg,
1860 FileType type,
1861 const char *filename)
1862{
1863 FileCacheEntry *ent = g_new0 (FileCacheEntry, 1)((FileCacheEntry *) g_malloc0_n ((1), sizeof (FileCacheEntry)
))
;
1864
1865 g_assert (!file_cache_lookup (bg, type, filename))do { if (!file_cache_lookup (bg, type, filename)) ; else g_assertion_message_expr
("CafeDesktop", "cafe-bg.c", 1865, ((const char*) (__func__)
), "!file_cache_lookup (bg, type, filename)"); } while (0)
;
1866
1867 ent->type = type;
1868 ent->filename = g_strdup (filename)g_strdup_inline (filename);
1869
1870 bg->file_cache = g_list_prepend (bg->file_cache, ent);
1871
1872 bound_cache (bg);
1873
1874 return ent;
1875}
1876
1877static void
1878file_cache_add_pixbuf (CafeBG *bg,
1879 const char *filename,
1880 GdkPixbuf *pixbuf)
1881{
1882 FileCacheEntry *ent = file_cache_entry_new (bg, PIXBUF, filename);
1883 ent->u.pixbuf = g_object_ref (pixbuf)((__typeof__ (pixbuf)) (g_object_ref) (pixbuf));
1884}
1885
1886static void
1887file_cache_add_thumbnail (CafeBG *bg,
1888 const char *filename,
1889 GdkPixbuf *pixbuf)
1890{
1891 FileCacheEntry *ent = file_cache_entry_new (bg, THUMBNAIL, filename);
1892 ent->u.thumbnail = g_object_ref (pixbuf)((__typeof__ (pixbuf)) (g_object_ref) (pixbuf));
1893}
1894
1895static void
1896file_cache_add_slide_show (CafeBG *bg,
1897 const char *filename,
1898 SlideShow *show)
1899{
1900 FileCacheEntry *ent = file_cache_entry_new (bg, SLIDESHOW, filename);
1901 ent->u.slideshow = slideshow_ref (show);
1902}
1903
1904static GdkPixbuf *
1905load_from_cache_file (CafeBG *bg,
1906 const char *filename,
1907 gint num_monitor,
1908 gint best_width,
1909 gint best_height)
1910{
1911 GdkPixbuf *pixbuf = NULL((void*)0);
1912 gchar *cache_filename;
1913
1914 cache_filename = get_wallpaper_cache_filename (filename, num_monitor, bg->placement,
1915 best_width, best_height);
1916
1917 if (cache_file_is_valid (filename, cache_filename))
1918 pixbuf = gdk_pixbuf_new_from_file (cache_filename, NULL((void*)0));
1919
1920 g_free (cache_filename);
1921
1922 return pixbuf;
1923}
1924
1925static GdkPixbuf *
1926get_as_pixbuf_for_size (CafeBG *bg,
1927 const char *filename,
1928 gint monitor,
1929 gint best_width,
1930 gint best_height)
1931{
1932 const FileCacheEntry *ent;
1933 if ((ent = file_cache_lookup (bg, PIXBUF, filename))) {
1934 return g_object_ref (ent->u.pixbuf)((__typeof__ (ent->u.pixbuf)) (g_object_ref) (ent->u.pixbuf
))
;
1935 } else {
1936 GdkPixbuf *pixbuf = NULL((void*)0);
1937 gchar *tmp = NULL((void*)0);
1938 GdkPixbuf *tmp_pixbuf;
1939
1940 /* Try to hit local cache first if relevant */
1941 if (monitor != -1)
1942 pixbuf = load_from_cache_file (bg, filename, monitor,
1943 best_width, best_height);
1944
1945 if (!pixbuf) {
1946 GdkPixbufFormat *format;
1947
1948 /* If scalable choose maximum size */
1949 format = gdk_pixbuf_get_file_info (filename, NULL((void*)0), NULL((void*)0));
1950 if (format != NULL((void*)0))
1951 tmp = gdk_pixbuf_format_get_name (format);
1952
1953 if (g_strcmp0 (tmp, "svg") == 0 &&
1954 (best_width > 0 && best_height > 0) &&
1955 (bg->placement == CAFE_BG_PLACEMENT_FILL_SCREEN ||
1956 bg->placement == CAFE_BG_PLACEMENT_SCALED ||
1957 bg->placement == CAFE_BG_PLACEMENT_ZOOMED))
1958 {
1959 pixbuf = gdk_pixbuf_new_from_file_at_size (filename,
1960 best_width,
1961 best_height, NULL((void*)0));
1962 } else {
1963 pixbuf = gdk_pixbuf_new_from_file (filename, NULL((void*)0));
1964 }
1965
1966 if (tmp != NULL((void*)0))
1967 g_free (tmp);
1968 }
1969
1970 if (pixbuf) {
1971 tmp_pixbuf = gdk_pixbuf_apply_embedded_orientation (pixbuf);
1972 g_object_unref (pixbuf);
1973 pixbuf = tmp_pixbuf;
1974 file_cache_add_pixbuf (bg, filename, pixbuf);
1975 }
1976
1977 return pixbuf;
1978 }
1979}
1980
1981static SlideShow *
1982get_as_slideshow (CafeBG *bg, const char *filename)
1983{
1984 const FileCacheEntry *ent;
1985 if ((ent = file_cache_lookup (bg, SLIDESHOW, filename))) {
5
Assuming 'ent' is null
6
Taking false branch
46
Assuming 'ent' is non-null
47
Taking true branch
1986 return slideshow_ref (ent->u.slideshow);
48
Returning pointer, which participates in a condition later
1987 }
1988 else {
1989 SlideShow *show = read_slideshow_file (filename, NULL((void*)0));
7
Calling 'read_slideshow_file'
22
Returning from 'read_slideshow_file'
1990
1991 if (show
22.1
'show' is non-null
)
23
Taking true branch
1992 file_cache_add_slide_show (bg, filename, show);
1993
1994 return show;
1995 }
1996}
1997
1998static GdkPixbuf *
1999get_as_thumbnail (CafeBG *bg, CafeDesktopThumbnailFactory *factory, const char *filename)
2000{
2001 const FileCacheEntry *ent;
2002 if ((ent = file_cache_lookup (bg, THUMBNAIL, filename))) {
39
Assuming 'ent' is non-null
40
Taking true branch
2003 return g_object_ref (ent->u.thumbnail)((__typeof__ (ent->u.thumbnail)) (g_object_ref) (ent->u
.thumbnail))
;
41
Returning pointer, which participates in a condition later
2004 }
2005 else {
2006 GdkPixbuf *thumb = create_thumbnail_for_filename (factory, filename);
2007
2008 if (thumb)
2009 file_cache_add_thumbnail (bg, filename, thumb);
2010
2011 return thumb;
2012 }
2013}
2014
2015static gboolean
2016blow_expensive_caches (gpointer data)
2017{
2018 CafeBG *bg = data;
2019 GList *list, *next;
2020
2021 bg->blow_caches_id = 0;
2022
2023 for (list = bg->file_cache; list != NULL((void*)0); list = next) {
2024 FileCacheEntry *ent = list->data;
2025 next = list->next;
2026
2027 if (ent->type == PIXBUF) {
2028 file_cache_entry_delete (ent);
2029 bg->file_cache = g_list_delete_link (bg->file_cache,
2030 list);
2031 }
2032 }
2033
2034 if (bg->pixbuf_cache) {
2035 g_object_unref (bg->pixbuf_cache);
2036 bg->pixbuf_cache = NULL((void*)0);
2037 }
2038
2039 return FALSE(0);
2040}
2041
2042static void
2043blow_expensive_caches_in_idle (CafeBG *bg)
2044{
2045 if (bg->blow_caches_id == 0) {
2046 bg->blow_caches_id =
2047 g_idle_add (blow_expensive_caches,
2048 bg);
2049 }
2050}
2051
2052
2053static gboolean
2054on_timeout (gpointer data)
2055{
2056 CafeBG *bg = data;
2057
2058 bg->timeout_id = 0;
2059
2060 queue_transitioned (bg);
2061
2062 return FALSE(0);
2063}
2064
2065static double
2066get_slide_timeout (Slide *slide)
2067{
2068 double timeout;
2069 if (slide->fixed) {
2070 timeout = slide->duration;
2071 } else {
2072 /* Maybe the number of steps should be configurable? */
2073
2074 /* In the worst case we will do a fade from 0 to 256, which mean
2075 * we will never use more than 255 steps, however in most cases
2076 * the first and last value are similar and users can't percieve
2077 * changes in pixel values as small as 1/255th. So, lets not waste
2078 * CPU cycles on transitioning to often.
2079 *
2080 * 64 steps is enough for each step to be just detectable in a 16bit
2081 * color mode in the worst case, so we'll use this as an approximation
2082 * of whats detectable.
2083 */
2084 timeout = slide->duration / 64.0;
2085 }
2086 return timeout;
2087}
2088
2089static void
2090ensure_timeout (CafeBG *bg,
2091 Slide *slide)
2092{
2093 if (!bg->timeout_id) {
2094 double timeout = get_slide_timeout (slide);
2095
2096 /* G_MAXUINT means "only one slide" */
2097 if (timeout < G_MAXUINT(2147483647 *2U +1U)) {
2098 bg->timeout_id = g_timeout_add_full (
2099 G_PRIORITY_LOW300,
2100 timeout * 1000, on_timeout, bg, NULL((void*)0));
2101 }
2102
2103 }
2104}
2105
2106static time_t
2107get_mtime (const char *filename)
2108{
2109 time_t mtime;
2110
2111 mtime = (time_t)-1;
2112
2113 if (filename) {
2114 GFile *file;
2115 GFileInfo *info;
2116
2117 file = g_file_new_for_path (filename);
2118 info = g_file_query_info (file, G_FILE_ATTRIBUTE_TIME_MODIFIED"time::modified",
2119 G_FILE_QUERY_INFO_NONE, NULL((void*)0), NULL((void*)0));
2120 if (info) {
2121 mtime = g_file_info_get_attribute_uint64 (info,
2122 G_FILE_ATTRIBUTE_TIME_MODIFIED"time::modified");
2123 g_object_unref (info);
2124 }
2125 g_object_unref (file);
2126 }
2127
2128 return mtime;
2129}
2130
2131static GdkPixbuf *
2132scale_thumbnail (CafeBGPlacement placement,
2133 const char *filename,
2134 GdkPixbuf *thumb,
2135 CdkScreen *screen,
2136 int dest_width,
2137 int dest_height)
2138{
2139 int o_width;
2140 int o_height;
2141
2142 if (placement != CAFE_BG_PLACEMENT_TILED &&
2143 placement != CAFE_BG_PLACEMENT_CENTERED) {
2144
2145 /* In this case, the pixbuf will be scaled to fit the screen anyway,
2146 * so just return the pixbuf here
2147 */
2148 return g_object_ref (thumb)((__typeof__ (thumb)) (g_object_ref) (thumb));
2149 }
2150
2151 if (get_thumb_annotations (thumb, &o_width, &o_height) ||
2152 (filename && get_original_size (filename, &o_width, &o_height))) {
2153
2154 int scr_height = HeightOfScreen (cdk_x11_screen_get_xscreen (screen))((cdk_x11_screen_get_xscreen (screen))->height);
2155 int scr_width = WidthOfScreen (cdk_x11_screen_get_xscreen (screen))((cdk_x11_screen_get_xscreen (screen))->width);
2156 int thumb_width = gdk_pixbuf_get_width (thumb);
2157 int thumb_height = gdk_pixbuf_get_height (thumb);
2158 double screen_to_dest = fit_factor (scr_width, scr_height,
2159 dest_width, dest_height);
2160 double thumb_to_orig = fit_factor (thumb_width, thumb_height,
2161 o_width, o_height);
2162 double f = thumb_to_orig * screen_to_dest;
2163 int new_width, new_height;
2164
2165 new_width = floor (thumb_width * f + 0.5);
2166 new_height = floor (thumb_height * f + 0.5);
2167
2168 if (placement == CAFE_BG_PLACEMENT_TILED) {
2169 /* Heuristic to make sure tiles don't become so small that
2170 * they turn into a blur.
2171 *
2172 * This is strictly speaking incorrect, but the resulting
2173 * thumbnail gives a much better idea what the background
2174 * will actually look like.
2175 */
2176
2177 if ((new_width < 32 || new_height < 32) &&
2178 (new_width < o_width / 4 || new_height < o_height / 4)) {
2179 new_width = o_width / 4;
2180 new_height = o_height / 4;
2181 }
2182 }
2183
2184 thumb = gdk_pixbuf_scale_simple (thumb, new_width, new_height,
2185 GDK_INTERP_BILINEAR);
2186 }
2187 else
2188 g_object_ref (thumb)((__typeof__ (thumb)) (g_object_ref) (thumb));
2189
2190 return thumb;
2191}
2192
2193/* frame_num determines which slide to thumbnail.
2194 * -1 means 'current slide'.
2195 */
2196static GdkPixbuf *
2197create_img_thumbnail (CafeBG *bg,
2198 CafeDesktopThumbnailFactory *factory,
2199 CdkScreen *screen,
2200 int dest_width,
2201 int dest_height,
2202 int frame_num)
2203{
2204 if (bg->filename
36.1
Field 'filename' is non-null
) {
37
Taking true branch
2205 GdkPixbuf *thumb;
2206
2207 thumb = get_as_thumbnail (bg, factory, bg->filename);
38
Calling 'get_as_thumbnail'
42
Returning from 'get_as_thumbnail'
2208
2209 if (thumb) {
43
Assuming 'thumb' is null
44
Taking false branch
2210 GdkPixbuf *result;
2211 result = scale_thumbnail (bg->placement,
2212 bg->filename,
2213 thumb,
2214 screen,
2215 dest_width,
2216 dest_height);
2217 g_object_unref (thumb);
2218 return result;
2219 }
2220 else {
2221 SlideShow *show = get_as_slideshow (bg, bg->filename);
45
Calling 'get_as_slideshow'
49
Returning from 'get_as_slideshow'
2222
2223 if (show
49.1
'show' is non-null
) {
50
Taking true branch
2224 double alpha;
2225 Slide *slide;
2226
2227 if (frame_num == -1)
51
Taking false branch
2228 slide = get_current_slide (show, &alpha);
2229 else
2230 slide = g_queue_peek_nth (show->slides, frame_num);
2231
2232 if (slide->fixed) {
52
Assuming field 'fixed' is not equal to 0
53
Taking true branch
2233 GdkPixbuf *tmp;
2234 FileSize *fs;
2235 fs = find_best_size (slide->file1, dest_width, dest_height);
54
Calling 'find_best_size'
2236 tmp = get_as_thumbnail (bg, factory, fs->file);
2237 if (tmp) {
2238 thumb = scale_thumbnail (bg->placement,
2239 fs->file,
2240 tmp,
2241 screen,
2242 dest_width,
2243 dest_height);
2244 g_object_unref (tmp);
2245 }
2246 }
2247 else {
2248 FileSize *fs1, *fs2;
2249 GdkPixbuf *p1, *p2;
2250 fs1 = find_best_size (slide->file1, dest_width, dest_height);
2251 p1 = get_as_thumbnail (bg, factory, fs1->file);
2252
2253 fs2 = find_best_size (slide->file2, dest_width, dest_height);
2254 p2 = get_as_thumbnail (bg, factory, fs2->file);
2255
2256 if (p1 && p2) {
2257 GdkPixbuf *thumb1, *thumb2;
2258
2259 thumb1 = scale_thumbnail (bg->placement,
2260 fs1->file,
2261 p1,
2262 screen,
2263 dest_width,
2264 dest_height);
2265
2266 thumb2 = scale_thumbnail (bg->placement,
2267 fs2->file,
2268 p2,
2269 screen,
2270 dest_width,
2271 dest_height);
2272
2273 thumb = blend (thumb1, thumb2, alpha);
2274
2275 g_object_unref (thumb1);
2276 g_object_unref (thumb2);
2277 }
2278 if (p1)
2279 g_object_unref (p1);
2280 if (p2)
2281 g_object_unref (p2);
2282 }
2283
2284 ensure_timeout (bg, slide);
2285
2286 slideshow_unref (show);
2287 }
2288 }
2289
2290 return thumb;
2291 }
2292
2293 return NULL((void*)0);
2294}
2295
2296/*
2297 * Find the FileSize that best matches the given size.
2298 * Do two passes; the first pass only considers FileSizes
2299 * that are larger than the given size.
2300 * We are looking for the image that best matches the aspect ratio.
2301 * When two images have the same aspect ratio, prefer the one whose
2302 * width is closer to the given width.
2303 */
2304static FileSize *
2305find_best_size (GSList *sizes, gint width, gint height)
2306{
2307 GSList *s;
2308 gdouble a, d, distance;
2309 FileSize *best = NULL((void*)0);
55
'best' initialized to a null pointer value
2310 gint pass;
2311
2312 a = width/(gdouble)height;
2313 distance = 10000.0;
2314
2315 for (pass = 0; pass < 2; pass++) {
56
Loop condition is true. Entering loop body
2316 for (s = sizes; s; s = s->next) {
57
Loop condition is true. Entering loop body
2317 FileSize *size = s->data;
2318
2319 if (pass
57.1
'pass' is equal to 0
== 0 && (size->width < width || size->height < height))
58
Assuming 'width' is <= field 'width'
59
Assuming 'height' is <= field 'height'
60
Taking false branch
2320 continue;
2321
2322 d = fabs (a - size->width/(gdouble)size->height);
2323 if (d < distance) {
61
Assuming 'd' is >= 'distance'
62
Taking false branch
2324 distance = d;
2325 best = size;
2326 }
2327 else if (d == distance) {
63
Assuming 'd' is equal to 'distance'
64
Taking true branch
2328 if (abs (size->width - width) < abs (best->width - width)) {
65
Access to field 'width' results in a dereference of a null pointer (loaded from variable 'best')
2329 best = size;
2330 }
2331 }
2332 }
2333
2334 if (best)
2335 break;
2336 }
2337
2338 return best;
2339}
2340
2341static GdkPixbuf *
2342get_pixbuf_for_size (CafeBG *bg,
2343 gint monitor,
2344 gint best_width,
2345 gint best_height)
2346{
2347 guint time_until_next_change;
2348 gboolean hit_cache = FALSE(0);
2349
2350 /* only hit the cache if the aspect ratio matches */
2351 if (bg->pixbuf_cache) {
2352 int width, height;
2353 width = gdk_pixbuf_get_width (bg->pixbuf_cache);
2354 height = gdk_pixbuf_get_height (bg->pixbuf_cache);
2355 hit_cache = 0.2 > fabs ((best_width / (double)best_height) - (width / (double)height));
2356 if (!hit_cache) {
2357 g_object_unref (bg->pixbuf_cache);
2358 bg->pixbuf_cache = NULL((void*)0);
2359 }
2360 }
2361
2362 if (!hit_cache && bg->filename) {
2363 bg->file_mtime = get_mtime (bg->filename);
2364
2365 bg->pixbuf_cache = get_as_pixbuf_for_size (bg, bg->filename, monitor,
2366 best_width, best_height);
2367 time_until_next_change = G_MAXUINT(2147483647 *2U +1U);
2368 if (!bg->pixbuf_cache) {
2369 SlideShow *show = get_as_slideshow (bg, bg->filename);
2370
2371 if (show) {
2372 double alpha;
2373 Slide *slide;
2374
2375 slideshow_ref (show);
2376
2377 slide = get_current_slide (show, &alpha);
2378 time_until_next_change = (guint)get_slide_timeout (slide);
2379 if (slide->fixed) {
2380 FileSize *size = find_best_size (slide->file1,
2381 best_width, best_height);
2382 bg->pixbuf_cache =
2383 get_as_pixbuf_for_size (bg, size->file, monitor,
2384 best_width, best_height);
2385 } else {
2386 FileSize *size;
2387 GdkPixbuf *p1, *p2;
2388
2389 size = find_best_size (slide->file1,
2390 best_width, best_height);
2391 p1 = get_as_pixbuf_for_size (bg, size->file, monitor,
2392 best_width, best_height);
2393
2394 size = find_best_size (slide->file2,
2395 best_width, best_height);
2396 p2 = get_as_pixbuf_for_size (bg, size->file, monitor,
2397 best_width, best_height);
2398
2399 if (p1 && p2)
2400 bg->pixbuf_cache = blend (p1, p2, alpha);
2401 if (p1)
2402 g_object_unref (p1);
2403 if (p2)
2404 g_object_unref (p2);
2405 }
2406
2407 ensure_timeout (bg, slide);
2408
2409 slideshow_unref (show);
2410 }
2411 }
2412
2413 /* If the next slideshow step is a long time away then
2414 we blow away the expensive stuff (large pixbufs) from
2415 the cache */
2416 if (time_until_next_change > KEEP_EXPENSIVE_CACHE_SECS60)
2417 blow_expensive_caches_in_idle (bg);
2418 }
2419
2420 if (bg->pixbuf_cache)
2421 g_object_ref (bg->pixbuf_cache)((__typeof__ (bg->pixbuf_cache)) (g_object_ref) (bg->pixbuf_cache
))
;
2422
2423 return bg->pixbuf_cache;
2424}
2425
2426static gboolean
2427is_different (CafeBG *bg,
2428 const char *filename)
2429{
2430 if (!filename && bg->filename) {
2431 return TRUE(!(0));
2432 }
2433 else if (filename && !bg->filename) {
2434 return TRUE(!(0));
2435 }
2436 else if (!filename && !bg->filename) {
2437 return FALSE(0);
2438 }
2439 else {
2440 time_t mtime = get_mtime (filename);
2441
2442 if (mtime != bg->file_mtime)
2443 return TRUE(!(0));
2444
2445 if (strcmp (filename, bg->filename) != 0)
2446 return TRUE(!(0));
2447
2448 return FALSE(0);
2449 }
2450}
2451
2452static void
2453clear_cache (CafeBG *bg)
2454{
2455 GList *list;
2456
2457 if (bg->file_cache) {
2458 for (list = bg->file_cache; list != NULL((void*)0); list = list->next) {
2459 FileCacheEntry *ent = list->data;
2460
2461 file_cache_entry_delete (ent);
2462 }
2463 g_list_free (bg->file_cache);
2464 bg->file_cache = NULL((void*)0);
2465 }
2466
2467 if (bg->pixbuf_cache) {
2468 g_object_unref (bg->pixbuf_cache);
2469
2470 bg->pixbuf_cache = NULL((void*)0);
2471 }
2472
2473 if (bg->timeout_id) {
2474 g_source_remove (bg->timeout_id);
2475
2476 bg->timeout_id = 0;
2477 }
2478}
2479
2480/* Pixbuf utilities */
2481static void
2482pixbuf_average_value (GdkPixbuf *pixbuf,
2483 CdkRGBA *result)
2484{
2485 guint64 a_total, r_total, g_total, b_total;
2486 guint row, column;
2487 int row_stride;
2488 const guchar *pixels, *p;
2489 int r, g, b;
2490 guint64 dividend;
2491 guint width, height;
2492 gdouble dd;
2493
2494 width = gdk_pixbuf_get_width (pixbuf);
2495 height = gdk_pixbuf_get_height (pixbuf);
2496 row_stride = gdk_pixbuf_get_rowstride (pixbuf);
2497 pixels = gdk_pixbuf_get_pixels (pixbuf);
2498
2499 /* iterate through the pixbuf, counting up each component */
2500 a_total = 0;
2501 r_total = 0;
2502 g_total = 0;
2503 b_total = 0;
2504
2505 if (gdk_pixbuf_get_has_alpha (pixbuf)) {
2506 for (row = 0; row < height; row++) {
2507 p = pixels + (row * row_stride);
2508 for (column = 0; column < width; column++) {
2509 int a;
2510
2511 r = *p++;
2512 g = *p++;
2513 b = *p++;
2514 a = *p++;
2515
2516 a_total += a;
2517 r_total += r * a;
2518 g_total += g * a;
2519 b_total += b * a;
2520 }
2521 }
2522 dividend = height * width * 0xFF;
2523 a_total *= 0xFF;
2524 } else {
2525 for (row = 0; row < height; row++) {
2526 p = pixels + (row * row_stride);
2527 for (column = 0; column < width; column++) {
2528 r = *p++;
2529 g = *p++;
2530 b = *p++;
2531
2532 r_total += r;
2533 g_total += g;
2534 b_total += b;
2535 }
2536 }
2537 dividend = height * width;
2538 a_total = dividend * 0xFF;
2539 }
2540
2541 dd = dividend * 0xFF;
2542 result->alpha = a_total / dd;
2543 result->red = r_total / dd;
2544 result->green = g_total / dd;
2545 result->blue = b_total / dd;
2546}
2547
2548static GdkPixbuf *
2549pixbuf_scale_to_fit (GdkPixbuf *src, int max_width, int max_height)
2550{
2551 double factor;
2552 int src_width, src_height;
2553 int new_width, new_height;
2554
2555 src_width = gdk_pixbuf_get_width (src);
2556 src_height = gdk_pixbuf_get_height (src);
2557
2558 factor = MIN (max_width / (double) src_width, max_height / (double) src_height)(((max_width / (double) src_width) < (max_height / (double
) src_height)) ? (max_width / (double) src_width) : (max_height
/ (double) src_height))
;
2559
2560 new_width = floor (src_width * factor + 0.5);
2561 new_height = floor (src_height * factor + 0.5);
2562
2563 return gdk_pixbuf_scale_simple (src, new_width, new_height, GDK_INTERP_BILINEAR);
2564}
2565
2566static GdkPixbuf *
2567pixbuf_scale_to_min (GdkPixbuf *src, int min_width, int min_height)
2568{
2569 double factor;
2570 int src_width, src_height;
2571 int new_width, new_height;
2572 GdkPixbuf *dest;
2573
2574 src_width = gdk_pixbuf_get_width (src);
2575 src_height = gdk_pixbuf_get_height (src);
2576
2577 factor = MAX (min_width / (double) src_width, min_height / (double) src_height)(((min_width / (double) src_width) > (min_height / (double
) src_height)) ? (min_width / (double) src_width) : (min_height
/ (double) src_height))
;
2578
2579 new_width = floor (src_width * factor + 0.5);
2580 new_height = floor (src_height * factor + 0.5);
2581
2582 dest = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
2583 gdk_pixbuf_get_has_alpha (src),
2584 8, min_width, min_height);
2585 if (!dest)
2586 return NULL((void*)0);
2587
2588 /* crop the result */
2589 gdk_pixbuf_scale (src, dest,
2590 0, 0,
2591 min_width, min_height,
2592 (new_width - min_width) / -2,
2593 (new_height - min_height) / -2,
2594 factor,
2595 factor,
2596 GDK_INTERP_BILINEAR);
2597 return dest;
2598}
2599
2600static guchar *
2601create_gradient (const CdkRGBA *primary,
2602 const CdkRGBA *secondary,
2603 int n_pixels)
2604{
2605 guchar *result = g_malloc (n_pixels * 3);
2606 int i;
2607
2608 for (i = 0; i < n_pixels; ++i) {
2609 double ratio = (i + 0.5) / n_pixels;
2610
2611 result[3 * i + 0] = (guchar) ((primary->red * (1 - ratio) + secondary->red * ratio) * 0x100);
2612 result[3 * i + 1] = (guchar) ((primary->green * (1 - ratio) + secondary->green * ratio) * 0x100);
2613 result[3 * i + 2] = (guchar) ((primary->blue * (1 - ratio) + secondary->blue * ratio) * 0x100);
2614 }
2615
2616 return result;
2617}
2618
2619static void
2620pixbuf_draw_gradient (GdkPixbuf *pixbuf,
2621 gboolean horizontal,
2622 CdkRGBA *primary,
2623 CdkRGBA *secondary,
2624 CdkRectangle *rect)
2625{
2626 int width;
2627 int height;
2628 int rowstride;
2629 guchar *dst;
2630 int n_channels = 3;
2631
2632 rowstride = gdk_pixbuf_get_rowstride (pixbuf);
2633 width = rect->width;
2634 height = rect->height;
2635 dst = gdk_pixbuf_get_pixels (pixbuf) + rect->x * n_channels + rowstride * rect->y;
2636
2637 if (horizontal) {
2638 guchar *gradient = create_gradient (primary, secondary, width);
2639 int copy_bytes_per_row = width * n_channels;
2640 int i;
2641
2642 for (i = 0; i < height; i++) {
2643 guchar *d;
2644 d = dst + rowstride * i;
2645 memcpy (d, gradient, copy_bytes_per_row);
2646 }
2647 g_free (gradient);
2648 } else {
2649 guchar *gb, *gradient;
2650 int i;
2651
2652 gradient = create_gradient (primary, secondary, height);
2653 for (i = 0; i < height; i++) {
2654 int j;
2655 guchar *d;
2656
2657 d = dst + rowstride * i;
2658 gb = gradient + n_channels * i;
2659 for (j = width; j > 0; j--) {
2660 int k;
2661
2662 for (k = 0; k < n_channels; k++) {
2663 *(d++) = gb[k];
2664 }
2665 }
2666 }
2667
2668 g_free (gradient);
2669 }
2670}
2671
2672static void
2673pixbuf_blend (GdkPixbuf *src,
2674 GdkPixbuf *dest,
2675 int src_x,
2676 int src_y,
2677 int src_width,
2678 int src_height,
2679 int dest_x,
2680 int dest_y,
2681 double alpha)
2682{
2683 int dest_width = gdk_pixbuf_get_width (dest);
2684 int dest_height = gdk_pixbuf_get_height (dest);
2685 int offset_x = dest_x - src_x;
2686 int offset_y = dest_y - src_y;
2687
2688 if (src_width < 0)
2689 src_width = gdk_pixbuf_get_width (src);
2690
2691 if (src_height < 0)
2692 src_height = gdk_pixbuf_get_height (src);
2693
2694 if (dest_x < 0)
2695 dest_x = 0;
2696
2697 if (dest_y < 0)
2698 dest_y = 0;
2699
2700 if (dest_x + src_width > dest_width) {
2701 src_width = dest_width - dest_x;
2702 }
2703
2704 if (dest_y + src_height > dest_height) {
2705 src_height = dest_height - dest_y;
2706 }
2707
2708 gdk_pixbuf_composite (src, dest,
2709 dest_x, dest_y,
2710 src_width, src_height,
2711 offset_x, offset_y,
2712 1, 1, GDK_INTERP_NEAREST,
2713 alpha * 0xFF + 0.5);
2714}
2715
2716static void
2717pixbuf_tile (GdkPixbuf *src, GdkPixbuf *dest)
2718{
2719 int x, y;
2720 int tile_width, tile_height;
2721 int dest_width = gdk_pixbuf_get_width (dest);
2722 int dest_height = gdk_pixbuf_get_height (dest);
2723
2724 tile_width = gdk_pixbuf_get_width (src);
2725 tile_height = gdk_pixbuf_get_height (src);
2726
2727 for (y = 0; y < dest_height; y += tile_height) {
2728 for (x = 0; x < dest_width; x += tile_width) {
2729 pixbuf_blend (src, dest, 0, 0,
2730 tile_width, tile_height, x, y, 1.0);
2731 }
2732 }
2733}
2734
2735static gboolean stack_is (SlideShow *parser, const char *s1, ...);
2736
2737/* Parser for fading background */
2738static void
2739handle_start_element (GMarkupParseContext *context G_GNUC_UNUSED__attribute__ ((__unused__)),
2740 const gchar *name,
2741 const gchar **attr_names,
2742 const gchar **attr_values,
2743 gpointer user_data,
2744 GError **err G_GNUC_UNUSED__attribute__ ((__unused__)))
2745{
2746 SlideShow *parser = user_data;
2747 gint i;
2748
2749 if (strcmp (name, "static") == 0 || strcmp (name, "transition") == 0) {
2750 Slide *slide = g_new0 (Slide, 1)((Slide *) g_malloc0_n ((1), sizeof (Slide)));
2751
2752 if (strcmp (name, "static") == 0)
2753 slide->fixed = TRUE(!(0));
2754
2755 g_queue_push_tail (parser->slides, slide);
2756 }
2757 else if (strcmp (name, "size") == 0) {
2758 Slide *slide = parser->slides->tail->data;
2759 FileSize *size = g_new0 (FileSize, 1)((FileSize *) g_malloc0_n ((1), sizeof (FileSize)));
2760 for (i = 0; attr_names[i]; i++) {
2761 if (strcmp (attr_names[i], "width") == 0)
2762 size->width = atoi (attr_values[i]);
2763 else if (strcmp (attr_names[i], "height") == 0)
2764 size->height = atoi (attr_values[i]);
2765 }
2766 if (parser->stack->tail &&
2767 (strcmp (parser->stack->tail->data, "file") == 0 ||
2768 strcmp (parser->stack->tail->data, "from") == 0)) {
2769 slide->file1 = g_slist_prepend (slide->file1, size);
2770 }
2771 else if (parser->stack->tail &&
2772 strcmp (parser->stack->tail->data, "to") == 0) {
2773 slide->file2 = g_slist_prepend (slide->file2, size);
2774 }
2775 else
2776 g_free (size);
2777 }
2778 g_queue_push_tail (parser->stack, g_strdup (name)g_strdup_inline (name));
2779}
2780
2781static void
2782handle_end_element (GMarkupParseContext *context G_GNUC_UNUSED__attribute__ ((__unused__)),
2783 const gchar *name G_GNUC_UNUSED__attribute__ ((__unused__)),
2784 gpointer user_data,
2785 GError **err G_GNUC_UNUSED__attribute__ ((__unused__)))
2786{
2787 SlideShow *parser = user_data;
2788
2789 g_free (g_queue_pop_tail (parser->stack));
2790}
2791
2792static gboolean
2793stack_is (SlideShow *parser,
2794 const char *s1,
2795 ...)
2796{
2797 GList *stack = NULL((void*)0);
2798 const char *s;
2799 GList *l1, *l2;
2800 va_list args;
2801
2802 stack = g_list_prepend (stack, (gpointer)s1);
2803
2804 va_start (args, s1)__builtin_va_start(args, s1);
2805
2806 s = va_arg (args, const char *)__builtin_va_arg(args, const char *);
2807 while (s) {
2808 stack = g_list_prepend (stack, (gpointer)s);
2809 s = va_arg (args, const char *)__builtin_va_arg(args, const char *);
2810 }
2811
2812 va_end (args)__builtin_va_end(args);
2813
2814 l1 = stack;
2815 l2 = parser->stack->head;
2816
2817 while (l1 && l2) {
2818 if (strcmp (l1->data, l2->data) != 0) {
2819 g_list_free (stack);
2820 return FALSE(0);
2821 }
2822
2823 l1 = l1->next;
2824 l2 = l2->next;
2825 }
2826
2827 g_list_free (stack);
2828
2829 return (!l1 && !l2);
2830}
2831
2832static int
2833parse_int (const char *text)
2834{
2835 return strtol (text, NULL((void*)0), 0);
2836}
2837
2838static void
2839handle_text (GMarkupParseContext *context G_GNUC_UNUSED__attribute__ ((__unused__)),
2840 const gchar *text,
2841 gsize text_len G_GNUC_UNUSED__attribute__ ((__unused__)),
2842 gpointer user_data,
2843 GError **err G_GNUC_UNUSED__attribute__ ((__unused__)))
2844{
2845 SlideShow *parser = user_data;
2846 FileSize *fs;
2847 gint i;
2848
2849 g_return_if_fail (parser != NULL)do { if ((parser != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "parser != NULL"
); return; } } while (0)
;
2850 g_return_if_fail (parser->slides != NULL)do { if ((parser->slides != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "parser->slides != NULL"
); return; } } while (0)
;
2851
2852 Slide *slide = parser->slides->tail ? parser->slides->tail->data : NULL((void*)0);
2853
2854 if (stack_is (parser, "year", "starttime", "background", NULL((void*)0))) {
2855 parser->start_tm.tm_year = parse_int (text) - 1900;
2856 }
2857 else if (stack_is (parser, "month", "starttime", "background", NULL((void*)0))) {
2858 parser->start_tm.tm_mon = parse_int (text) - 1;
2859 }
2860 else if (stack_is (parser, "day", "starttime", "background", NULL((void*)0))) {
2861 parser->start_tm.tm_mday = parse_int (text);
2862 }
2863 else if (stack_is (parser, "hour", "starttime", "background", NULL((void*)0))) {
2864 parser->start_tm.tm_hour = parse_int (text) - 1;
2865 }
2866 else if (stack_is (parser, "minute", "starttime", "background", NULL((void*)0))) {
2867 parser->start_tm.tm_min = parse_int (text);
2868 }
2869 else if (stack_is (parser, "second", "starttime", "background", NULL((void*)0))) {
2870 parser->start_tm.tm_sec = parse_int (text);
2871 }
2872 else if (stack_is (parser, "duration", "static", "background", NULL((void*)0)) ||
2873 stack_is (parser, "duration", "transition", "background", NULL((void*)0))) {
2874 g_return_if_fail (slide != NULL)do { if ((slide != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "slide != NULL")
; return; } } while (0)
;
2875
2876 slide->duration = g_strtod (text, NULL((void*)0));
2877 parser->total_duration += slide->duration;
2878 }
2879 else if (stack_is (parser, "file", "static", "background", NULL((void*)0)) ||
2880 stack_is (parser, "from", "transition", "background", NULL((void*)0))) {
2881 g_return_if_fail (slide != NULL)do { if ((slide != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "slide != NULL")
; return; } } while (0)
;
2882
2883 for (i = 0; text[i]; i++) {
2884 if (!g_ascii_isspace (text[i])((g_ascii_table[(guchar) (text[i])] & G_ASCII_SPACE) != 0
)
)
2885 break;
2886 }
2887 if (text[i] == 0)
2888 return;
2889 fs = g_new (FileSize, 1)((FileSize *) g_malloc_n ((1), sizeof (FileSize)));
2890 fs->width = -1;
2891 fs->height = -1;
2892 fs->file = g_strdup (text)g_strdup_inline (text);
2893 slide->file1 = g_slist_prepend (slide->file1, fs);
2894 if (slide->file1->next != NULL((void*)0))
2895 parser->has_multiple_sizes = TRUE(!(0));
2896 }
2897 else if (stack_is (parser, "size", "file", "static", "background", NULL((void*)0)) ||
2898 stack_is (parser, "size", "from", "transition", "background", NULL((void*)0))) {
2899 g_return_if_fail (slide != NULL)do { if ((slide != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "slide != NULL")
; return; } } while (0)
;
2900
2901 fs = slide->file1->data;
2902 fs->file = g_strdup (text)g_strdup_inline (text);
2903 if (slide->file1->next != NULL((void*)0))
2904 parser->has_multiple_sizes = TRUE(!(0));
2905 }
2906 else if (stack_is (parser, "to", "transition", "background", NULL((void*)0))) {
2907 g_return_if_fail (slide != NULL)do { if ((slide != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "slide != NULL")
; return; } } while (0)
;
2908
2909 for (i = 0; text[i]; i++) {
2910 if (!g_ascii_isspace (text[i])((g_ascii_table[(guchar) (text[i])] & G_ASCII_SPACE) != 0
)
)
2911 break;
2912 }
2913 if (text[i] == 0)
2914 return;
2915 fs = g_new (FileSize, 1)((FileSize *) g_malloc_n ((1), sizeof (FileSize)));
2916 fs->width = -1;
2917 fs->height = -1;
2918 fs->file = g_strdup (text)g_strdup_inline (text);
2919 slide->file2 = g_slist_prepend (slide->file2, fs);
2920 if (slide->file2->next != NULL((void*)0))
2921 parser->has_multiple_sizes = TRUE(!(0));
2922 }
2923 else if (stack_is (parser, "size", "to", "transition", "background", NULL((void*)0))) {
2924 g_return_if_fail (slide != NULL)do { if ((slide != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "slide != NULL")
; return; } } while (0)
;
2925
2926 fs = slide->file2->data;
2927 fs->file = g_strdup (text)g_strdup_inline (text);
2928 if (slide->file2->next != NULL((void*)0))
2929 parser->has_multiple_sizes = TRUE(!(0));
2930 }
2931}
2932
2933static SlideShow *
2934slideshow_ref (SlideShow *show)
2935{
2936 show->ref_count++;
2937 return show;
2938}
2939
2940static void
2941slideshow_unref (SlideShow *show)
2942{
2943 GList *list;
2944 GSList *slist;
2945 FileSize *size;
2946
2947 show->ref_count--;
2948 if (show->ref_count > 0)
2949 return;
2950
2951 for (list = show->slides->head; list != NULL((void*)0); list = list->next) {
2952 Slide *slide = list->data;
2953
2954 for (slist = slide->file1; slist != NULL((void*)0); slist = slist->next) {
2955 size = slist->data;
2956 g_free (size->file);
2957 g_free (size);
2958 }
2959 g_slist_free (slide->file1);
2960
2961 for (slist = slide->file2; slist != NULL((void*)0); slist = slist->next) {
2962 size = slist->data;
2963 g_free (size->file);
2964 g_free (size);
2965 }
2966 g_slist_free (slide->file2);
2967
2968 g_free (slide);
2969 }
2970
2971 g_queue_free (show->slides);
2972
2973 g_list_foreach (show->stack->head, (GFunc) g_free, NULL((void*)0));
2974 g_queue_free (show->stack);
2975
2976 g_free (show);
2977}
2978
2979static void
2980dump_bg (SlideShow *show G_GNUC_UNUSED__attribute__ ((__unused__)))
2981{
2982#if 0
2983 GList *list;
2984 GSList *slist;
2985
2986 for (list = show->slides->head; list != NULL((void*)0); list = list->next)
2987 {
2988 Slide *slide = list->data;
2989
2990 g_print ("\nSlide: %s\n", slide->fixed? "fixed" : "transition");
2991 g_print ("duration: %f\n", slide->duration);
2992 g_print ("File1:\n");
2993 for (slist = slide->file1; slist != NULL((void*)0); slist = slist->next) {
2994 FileSize *size = slist->data;
2995 g_print ("\t%s (%dx%d)\n",
2996 size->file, size->width, size->height);
2997 }
2998 g_print ("File2:\n");
2999 for (slist = slide->file2; slist != NULL((void*)0); slist = slist->next) {
3000 FileSize *size = slist->data;
3001 g_print ("\t%s (%dx%d)\n",
3002 size->file, size->width, size->height);
3003 }
3004 }
3005#endif
3006}
3007
3008static void
3009threadsafe_localtime (time_t time, struct tm *tm)
3010{
3011 struct tm *res;
3012
3013 G_LOCK_DEFINE_STATIC (localtime_mutex)static GMutex g__localtime_mutex_lock;
3014
3015 G_LOCK (localtime_mutex)g_mutex_lock (&g__localtime_mutex_lock);
3016
3017 res = localtime (&time);
3018 if (tm) {
3019 *tm = *res;
3020 }
3021
3022 G_UNLOCK (localtime_mutex)g_mutex_unlock (&g__localtime_mutex_lock);
3023}
3024
3025static SlideShow *
3026read_slideshow_file (const char *filename,
3027 GError **err)
3028{
3029 GMarkupParser parser = {
3030 handle_start_element,
3031 handle_end_element,
3032 handle_text,
3033 NULL((void*)0), /* passthrough */
3034 NULL((void*)0), /* error */
3035 };
3036
3037 GFile *file;
3038 char *contents = NULL((void*)0);
3039 gsize len;
3040 SlideShow *show = NULL((void*)0);
3041 GMarkupParseContext *context = NULL((void*)0);
3042
3043 if (!filename)
8
Assuming 'filename' is non-null
9
Taking false branch
3044 return NULL((void*)0);
3045
3046 file = g_file_new_for_path (filename);
3047 if (!g_file_load_contents (file, NULL((void*)0), &contents, &len, NULL((void*)0), NULL((void*)0))) {
10
Assuming the condition is false
11
Taking false branch
3048 g_object_unref (file);
3049 return NULL((void*)0);
3050 }
3051 g_object_unref (file);
3052
3053 show = g_new0 (SlideShow, 1)((SlideShow *) g_malloc0_n ((1), sizeof (SlideShow)));
3054 show->ref_count = 1;
3055 threadsafe_localtime ((time_t)0, &show->start_tm);
3056 show->stack = g_queue_new ();
3057 show->slides = g_queue_new ();
3058
3059 context = g_markup_parse_context_new (&parser, 0, show, NULL((void*)0));
3060
3061 if (!g_markup_parse_context_parse (context, contents, len, err)) {
12
Assuming the condition is false
13
Taking false branch
3062 slideshow_unref (show);
3063 show = NULL((void*)0);
3064 }
3065
3066
3067 if (show
13.1
'show' is non-null
) {
14
Taking true branch
3068 if (!g_markup_parse_context_end_parse (context, err)) {
15
Assuming the condition is false
16
Taking false branch
3069 slideshow_unref (show);
3070 show = NULL((void*)0);
3071 }
3072 }
3073
3074 g_markup_parse_context_free (context);
3075
3076 if (show
16.1
'show' is non-null
) {
17
Taking true branch
3077 time_t t;
3078 int len;
3079
3080 t = mktime (&show->start_tm);
3081
3082 show->start_time = (double)t;
3083
3084 dump_bg (show);
3085
3086 len = g_queue_get_length (show->slides);
3087
3088 /* no slides, that's not a slideshow */
3089 if (len == 0) {
18
Assuming 'len' is not equal to 0
19
Taking false branch
3090 slideshow_unref (show);
3091 show = NULL((void*)0);
3092 /* one slide, there's no transition */
3093 } else if (len == 1) {
20
Assuming 'len' is not equal to 1
21
Taking false branch
3094 Slide *slide = show->slides->head->data;
3095 slide->duration = show->total_duration = G_MAXUINT(2147483647 *2U +1U);
3096 }
3097 }
3098
3099 g_free (contents);
3100
3101 return show;
3102}
3103
3104/* Thumbnail utilities */
3105static GdkPixbuf *
3106create_thumbnail_for_filename (CafeDesktopThumbnailFactory *factory,
3107 const char *filename)
3108{
3109 char *thumb;
3110 time_t mtime;
3111 GdkPixbuf *result = NULL((void*)0);
3112 char *uri;
3113
3114 mtime = get_mtime (filename);
3115
3116 if (mtime == (time_t)-1)
3117 return NULL((void*)0);
3118
3119 uri = g_filename_to_uri (filename, NULL((void*)0), NULL((void*)0));
3120
3121 if (uri == NULL((void*)0))
3122 return NULL((void*)0);
3123
3124 thumb = cafe_desktop_thumbnail_factory_lookup (factory, uri, mtime);
3125
3126 if (thumb) {
3127 result = gdk_pixbuf_new_from_file (thumb, NULL((void*)0));
3128 g_free (thumb);
3129 }
3130 else {
3131 GdkPixbuf *orig;
3132
3133 orig = gdk_pixbuf_new_from_file (filename, NULL((void*)0));
3134 if (orig) {
3135 int orig_width = gdk_pixbuf_get_width (orig);
3136 int orig_height = gdk_pixbuf_get_height (orig);
3137
3138 result = pixbuf_scale_to_fit (orig, THUMBNAIL_SIZE256, THUMBNAIL_SIZE256);
3139
3140
3141 g_object_set_data_full (G_OBJECT (result)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((result)), (((GType) ((20) << (2))))))))
, "cafe-thumbnail-height",
3142 g_strdup_printf ("%d", orig_height), g_free);
3143 g_object_set_data_full (G_OBJECT (result)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((result)), (((GType) ((20) << (2))))))))
, "cafe-thumbnail-width",
3144 g_strdup_printf ("%d", orig_width), g_free);
3145
3146 g_object_unref (orig);
3147
3148 cafe_desktop_thumbnail_factory_save_thumbnail (factory, result, uri, mtime);
3149 }
3150 else {
3151 cafe_desktop_thumbnail_factory_create_failed_thumbnail (factory, uri, mtime);
3152 }
3153 }
3154
3155 g_free (uri);
3156
3157 return result;
3158}
3159
3160static gboolean
3161get_thumb_annotations (GdkPixbuf *thumb,
3162 int *orig_width,
3163 int *orig_height)
3164{
3165 char *end;
3166 const char *wstr, *hstr;
3167
3168 wstr = gdk_pixbuf_get_option (thumb, "tEXt::Thumb::Image::Width");
3169 hstr = gdk_pixbuf_get_option (thumb, "tEXt::Thumb::Image::Height");
3170
3171 if (hstr && wstr) {
3172 *orig_width = strtol (wstr, &end, 10);
3173 if (*end != 0)
3174 return FALSE(0);
3175
3176 *orig_height = strtol (hstr, &end, 10);
3177 if (*end != 0)
3178 return FALSE(0);
3179
3180 return TRUE(!(0));
3181 }
3182
3183 return FALSE(0);
3184}
3185
3186static gboolean
3187slideshow_has_multiple_sizes (SlideShow *show)
3188{
3189 return show->has_multiple_sizes;
3190}
3191
3192/*
3193 * Returns whether the background is a slideshow.
3194 */
3195gboolean
3196cafe_bg_changes_with_time (CafeBG *bg)
3197{
3198 SlideShow *show;
3199
3200 g_return_val_if_fail (bg != NULL, FALSE)do { if ((bg != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "bg != NULL"); return
((0)); } } while (0)
;
3201
3202 if (!bg->filename)
3203 return FALSE(0);
3204
3205 show = get_as_slideshow (bg, bg->filename);
3206 if (show)
3207 return g_queue_get_length (show->slides) > 1;
3208
3209 return FALSE(0);
3210}
3211
3212/**
3213 * cafe_bg_create_frame_thumbnail:
3214 *
3215 * Creates a thumbnail for a certain frame, where 'frame' is somewhat
3216 * vaguely defined as 'suitable point to show while single-stepping
3217 * through the slideshow'.
3218 *
3219 * Returns: (transfer full): the newly created thumbnail or
3220 * or NULL if frame_num is out of bounds.
3221 */
3222GdkPixbuf *
3223cafe_bg_create_frame_thumbnail (CafeBG *bg,
3224 CafeDesktopThumbnailFactory *factory,
3225 CdkScreen *screen,
3226 int dest_width,
3227 int dest_height,
3228 int frame_num)
3229{
3230 SlideShow *show;
3231 GdkPixbuf *result;
3232 GList *l;
3233 int i, skipped;
3234 gboolean found;
3235
3236 g_return_val_if_fail (bg != NULL, FALSE)do { if ((bg != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "bg != NULL"); return
((0)); } } while (0)
;
1
Assuming 'bg' is not equal to null
2
Taking true branch
3
Loop condition is false. Exiting loop
3237
3238 show = get_as_slideshow (bg, bg->filename);
4
Calling 'get_as_slideshow'
24
Returning from 'get_as_slideshow'
3239
3240 if (!show
24.1
'show' is non-null
)
3241 return NULL((void*)0);
3242
3243
3244 if (frame_num < 0 || frame_num >= g_queue_get_length (show->slides))
25
Assuming 'frame_num' is >= 0
26
Assuming the condition is false
27
Taking false branch
3245 return NULL((void*)0);
3246
3247 i = 0;
3248 skipped = 0;
3249 found = FALSE(0);
3250 for (l = show->slides->head; l; l = l->next) {
28
Loop condition is true. Entering loop body
3251 Slide *slide = l->data;
3252 if (!slide->fixed) {
29
Assuming field 'fixed' is not equal to 0
30
Taking false branch
3253 skipped++;
3254 continue;
3255 }
3256 if (i == frame_num) {
31
Assuming 'i' is equal to 'frame_num'
32
Taking true branch
3257 found = TRUE(!(0));
3258 break;
3259 }
3260 i++;
3261 }
3262 if (!found
33.1
'found' is 1
)
33
Execution continues on line 3262
34
Taking false branch
3263 return NULL((void*)0);
3264
3265
3266 result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE(0), 8, dest_width, dest_height);
3267
3268 draw_color (bg, result);
3269
3270 if (bg->filename
34.1
Field 'filename' is non-null
) {
35
Taking true branch
3271 GdkPixbuf *thumb;
3272
3273 thumb = create_img_thumbnail (bg, factory, screen,
36
Calling 'create_img_thumbnail'
3274 dest_width, dest_height,
3275 frame_num + skipped);
3276
3277 if (thumb) {
3278 draw_image_for_thumb (bg, thumb, result);
3279 g_object_unref (thumb);
3280 }
3281 }
3282
3283 return result;
3284}
3285