Bug Summary

File:libcafe-desktop/cafe-bg.c
Warning:line 2274, column 15
3rd function call argument is an uninitialized value

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-19/lib/clang/19 -D HAVE_CONFIG_H -I . -I .. -I /usr/include/gdk-pixbuf-2.0 -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/sysprof-6 -I /usr/include/libpng16 -I /usr/include/x86_64-linux-gnu -I /usr/include/webp -I /usr/include/libmount -I /usr/include/blkid -I /usr/include/ctk-3.0 -I /usr/include/pango-1.0 -I /usr/include/harfbuzz -I /usr/include/freetype2 -I /usr/include/fribidi -I /usr/include/cairo -I /usr/include/pixman-1 -I /usr/include/gio-unix-2.0 -I /usr/include/atk-1.0 -I /usr/include/at-spi2-atk/2.0 -I /usr/include/at-spi-2.0 -I /usr/include/dbus-1.0 -I /usr/lib/x86_64-linux-gnu/dbus-1.0/include -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/sysprof-6 -I /usr/include/libmount -I /usr/include/blkid -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-19/lib/clang/19/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../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 alpha.deadcode.UnreachableCode -analyzer-checker alpha.core.CastSize -analyzer-checker alpha.core.CastToStruct -analyzer-checker alpha.core.IdenticalExpr -analyzer-checker alpha.security.ArrayBoundV2 -analyzer-checker alpha.security.MallocOverflow -analyzer-checker alpha.security.ReturnPtrRange -analyzer-checker alpha.unix.SimpleStream -analyzer-checker alpha.unix.cstring.BufferOverlap -analyzer-checker alpha.unix.cstring.NotNullTerminated -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/2024-12-27-183908-15073-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
56
Assuming 'ent' is non-null
57
Taking true branch
61
Assuming 'ent' is non-null
62
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
58
Returning pointer, which participates in a condition later
63
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;
2020
2021 bg->blow_caches_id = 0;
2022
2023 if (bg->file_cache) {
2024 for (list = bg->file_cache; list != NULL((void*)0); list = list->next) {
2025 FileCacheEntry *ent = list->data;
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
2035 if (bg->pixbuf_cache) {
2036 g_object_unref (bg->pixbuf_cache);
2037 bg->pixbuf_cache = NULL((void*)0);
2038 }
2039
2040 return FALSE(0);
2041}
2042
2043static void
2044blow_expensive_caches_in_idle (CafeBG *bg)
2045{
2046 if (bg->blow_caches_id == 0) {
2047 bg->blow_caches_id =
2048 g_idle_add (blow_expensive_caches,
2049 bg);
2050 }
2051}
2052
2053
2054static gboolean
2055on_timeout (gpointer data)
2056{
2057 CafeBG *bg = data;
2058
2059 bg->timeout_id = 0;
2060
2061 queue_transitioned (bg);
2062
2063 return FALSE(0);
2064}
2065
2066static double
2067get_slide_timeout (Slide *slide)
2068{
2069 double timeout;
2070 if (slide->fixed) {
2071 timeout = slide->duration;
2072 } else {
2073 /* Maybe the number of steps should be configurable? */
2074
2075 /* In the worst case we will do a fade from 0 to 256, which mean
2076 * we will never use more than 255 steps, however in most cases
2077 * the first and last value are similar and users can't percieve
2078 * changes in pixel values as small as 1/255th. So, lets not waste
2079 * CPU cycles on transitioning to often.
2080 *
2081 * 64 steps is enough for each step to be just detectable in a 16bit
2082 * color mode in the worst case, so we'll use this as an approximation
2083 * of whats detectable.
2084 */
2085 timeout = slide->duration / 64.0;
2086 }
2087 return timeout;
2088}
2089
2090static void
2091ensure_timeout (CafeBG *bg,
2092 Slide *slide)
2093{
2094 if (!bg->timeout_id) {
2095 double timeout = get_slide_timeout (slide);
2096
2097 /* G_MAXUINT means "only one slide" */
2098 if (timeout < G_MAXUINT(2147483647 *2U +1U)) {
2099 bg->timeout_id = g_timeout_add_full (
2100 G_PRIORITY_LOW300,
2101 timeout * 1000, on_timeout, bg, NULL((void*)0));
2102 }
2103
2104 }
2105}
2106
2107static time_t
2108get_mtime (const char *filename)
2109{
2110 time_t mtime;
2111
2112 mtime = (time_t)-1;
2113
2114 if (filename) {
2115 GFile *file;
2116 GFileInfo *info;
2117
2118 file = g_file_new_for_path (filename);
2119 info = g_file_query_info (file, G_FILE_ATTRIBUTE_TIME_MODIFIED"time::modified",
2120 G_FILE_QUERY_INFO_NONE, NULL((void*)0), NULL((void*)0));
2121 if (info) {
2122 mtime = g_file_info_get_attribute_uint64 (info,
2123 G_FILE_ATTRIBUTE_TIME_MODIFIED"time::modified");
2124 g_object_unref (info);
2125 }
2126 g_object_unref (file);
2127 }
2128
2129 return mtime;
2130}
2131
2132static GdkPixbuf *
2133scale_thumbnail (CafeBGPlacement placement,
2134 const char *filename,
2135 GdkPixbuf *thumb,
2136 CdkScreen *screen,
2137 int dest_width,
2138 int dest_height)
2139{
2140 int o_width;
2141 int o_height;
2142
2143 if (placement != CAFE_BG_PLACEMENT_TILED &&
2144 placement != CAFE_BG_PLACEMENT_CENTERED) {
2145
2146 /* In this case, the pixbuf will be scaled to fit the screen anyway,
2147 * so just return the pixbuf here
2148 */
2149 return g_object_ref (thumb)((__typeof__ (thumb)) (g_object_ref) (thumb));
2150 }
2151
2152 if (get_thumb_annotations (thumb, &o_width, &o_height) ||
2153 (filename && get_original_size (filename, &o_width, &o_height))) {
2154
2155 int scr_height = HeightOfScreen (cdk_x11_screen_get_xscreen (screen))((cdk_x11_screen_get_xscreen (screen))->height);
2156 int scr_width = WidthOfScreen (cdk_x11_screen_get_xscreen (screen))((cdk_x11_screen_get_xscreen (screen))->width);
2157 int thumb_width = gdk_pixbuf_get_width (thumb);
2158 int thumb_height = gdk_pixbuf_get_height (thumb);
2159 double screen_to_dest = fit_factor (scr_width, scr_height,
2160 dest_width, dest_height);
2161 double thumb_to_orig = fit_factor (thumb_width, thumb_height,
2162 o_width, o_height);
2163 double f = thumb_to_orig * screen_to_dest;
2164 int new_width, new_height;
2165
2166 new_width = floor (thumb_width * f + 0.5);
2167 new_height = floor (thumb_height * f + 0.5);
2168
2169 if (placement == CAFE_BG_PLACEMENT_TILED) {
2170 /* Heuristic to make sure tiles don't become so small that
2171 * they turn into a blur.
2172 *
2173 * This is strictly speaking incorrect, but the resulting
2174 * thumbnail gives a much better idea what the background
2175 * will actually look like.
2176 */
2177
2178 if ((new_width < 32 || new_height < 32) &&
2179 (new_width < o_width / 4 || new_height < o_height / 4)) {
2180 new_width = o_width / 4;
2181 new_height = o_height / 4;
2182 }
2183 }
2184
2185 thumb = gdk_pixbuf_scale_simple (thumb, new_width, new_height,
2186 GDK_INTERP_BILINEAR);
2187 }
2188 else
2189 g_object_ref (thumb)((__typeof__ (thumb)) (g_object_ref) (thumb));
2190
2191 return thumb;
2192}
2193
2194/* frame_num determines which slide to thumbnail.
2195 * -1 means 'current slide'.
2196 */
2197static GdkPixbuf *
2198create_img_thumbnail (CafeBG *bg,
2199 CafeDesktopThumbnailFactory *factory,
2200 CdkScreen *screen,
2201 int dest_width,
2202 int dest_height,
2203 int frame_num)
2204{
2205 if (bg->filename
36.1
Field 'filename' is non-null
) {
37
Taking true branch
2206 GdkPixbuf *thumb;
2207
2208 thumb = get_as_thumbnail (bg, factory, bg->filename);
38
Calling 'get_as_thumbnail'
42
Returning from 'get_as_thumbnail'
2209
2210 if (thumb) {
43
Assuming 'thumb' is null
44
Taking false branch
2211 GdkPixbuf *result;
2212 result = scale_thumbnail (bg->placement,
2213 bg->filename,
2214 thumb,
2215 screen,
2216 dest_width,
2217 dest_height);
2218 g_object_unref (thumb);
2219 return result;
2220 }
2221 else {
2222 SlideShow *show = get_as_slideshow (bg, bg->filename);
45
Calling 'get_as_slideshow'
49
Returning from 'get_as_slideshow'
2223
2224 if (show
49.1
'show' is non-null
) {
50
Taking true branch
2225 double alpha;
51
'alpha' declared without an initial value
2226 Slide *slide;
2227
2228 if (frame_num == -1)
52
Taking false branch
2229 slide = get_current_slide (show, &alpha);
2230 else
2231 slide = g_queue_peek_nth (show->slides, frame_num);
2232
2233 if (slide->fixed) {
53
Assuming field 'fixed' is 0
54
Taking false branch
2234 GdkPixbuf *tmp;
2235 FileSize *fs;
2236 fs = find_best_size (slide->file1, dest_width, dest_height);
2237 tmp = get_as_thumbnail (bg, factory, fs->file);
2238 if (tmp) {
2239 thumb = scale_thumbnail (bg->placement,
2240 fs->file,
2241 tmp,
2242 screen,
2243 dest_width,
2244 dest_height);
2245 g_object_unref (tmp);
2246 }
2247 }
2248 else {
2249 FileSize *fs1, *fs2;
2250 GdkPixbuf *p1, *p2;
2251 fs1 = find_best_size (slide->file1, dest_width, dest_height);
2252 p1 = get_as_thumbnail (bg, factory, fs1->file);
55
Calling 'get_as_thumbnail'
59
Returning from 'get_as_thumbnail'
2253
2254 fs2 = find_best_size (slide->file2, dest_width, dest_height);
2255 p2 = get_as_thumbnail (bg, factory, fs2->file);
60
Calling 'get_as_thumbnail'
64
Returning from 'get_as_thumbnail'
2256
2257 if (p1 && p2) {
65
Assuming 'p1' is non-null
66
Assuming 'p2' is non-null
67
Taking true branch
2258 GdkPixbuf *thumb1, *thumb2;
2259
2260 thumb1 = scale_thumbnail (bg->placement,
2261 fs1->file,
2262 p1,
2263 screen,
2264 dest_width,
2265 dest_height);
2266
2267 thumb2 = scale_thumbnail (bg->placement,
2268 fs2->file,
2269 p2,
2270 screen,
2271 dest_width,
2272 dest_height);
2273
2274 thumb = blend (thumb1, thumb2, alpha);
68
3rd function call argument is an uninitialized value
2275
2276 g_object_unref (thumb1);
2277 g_object_unref (thumb2);
2278 }
2279 if (p1)
2280 g_object_unref (p1);
2281 if (p2)
2282 g_object_unref (p2);
2283 }
2284
2285 ensure_timeout (bg, slide);
2286
2287 slideshow_unref (show);
2288 }
2289 }
2290
2291 return thumb;
2292 }
2293
2294 return NULL((void*)0);
2295}
2296
2297/*
2298 * Find the FileSize that best matches the given size.
2299 * Do two passes; the first pass only considers FileSizes
2300 * that are larger than the given size.
2301 * We are looking for the image that best matches the aspect ratio.
2302 * When two images have the same aspect ratio, prefer the one whose
2303 * width is closer to the given width.
2304 */
2305static FileSize *
2306find_best_size (GSList *sizes, gint width, gint height)
2307{
2308 GSList *s;
2309 gdouble a, d, distance;
2310 FileSize *best = NULL((void*)0);
2311 gint pass;
2312
2313 a = width/(gdouble)height;
2314 distance = 10000.0;
2315
2316 for (pass = 0; pass < 2; pass++) {
2317 for (s = sizes; s; s = s->next) {
2318 FileSize *size = s->data;
2319
2320 if (pass == 0 && (size->width < width || size->height < height))
2321 continue;
2322
2323 d = fabs (a - size->width/(gdouble)size->height);
2324 if (d < distance) {
2325 distance = d;
2326 best = size;
2327 }
2328 else if (d == distance) {
2329 if (abs (size->width - width) < abs (best->width - width)) {
2330 best = size;
2331 }
2332 }
2333 }
2334
2335 if (best)
2336 break;
2337 }
2338
2339 return best;
2340}
2341
2342static GdkPixbuf *
2343get_pixbuf_for_size (CafeBG *bg,
2344 gint monitor,
2345 gint best_width,
2346 gint best_height)
2347{
2348 guint time_until_next_change;
2349 gboolean hit_cache = FALSE(0);
2350
2351 /* only hit the cache if the aspect ratio matches */
2352 if (bg->pixbuf_cache) {
2353 int width, height;
2354 width = gdk_pixbuf_get_width (bg->pixbuf_cache);
2355 height = gdk_pixbuf_get_height (bg->pixbuf_cache);
2356 hit_cache = 0.2 > fabs ((best_width / (double)best_height) - (width / (double)height));
2357 if (!hit_cache) {
2358 g_object_unref (bg->pixbuf_cache);
2359 bg->pixbuf_cache = NULL((void*)0);
2360 }
2361 }
2362
2363 if (!hit_cache && bg->filename) {
2364 bg->file_mtime = get_mtime (bg->filename);
2365
2366 bg->pixbuf_cache = get_as_pixbuf_for_size (bg, bg->filename, monitor,
2367 best_width, best_height);
2368 time_until_next_change = G_MAXUINT(2147483647 *2U +1U);
2369 if (!bg->pixbuf_cache) {
2370 SlideShow *show = get_as_slideshow (bg, bg->filename);
2371
2372 if (show) {
2373 double alpha;
2374 Slide *slide;
2375
2376 slideshow_ref (show);
2377
2378 slide = get_current_slide (show, &alpha);
2379 time_until_next_change = (guint)get_slide_timeout (slide);
2380 if (slide->fixed) {
2381 FileSize *size = find_best_size (slide->file1,
2382 best_width, best_height);
2383 bg->pixbuf_cache =
2384 get_as_pixbuf_for_size (bg, size->file, monitor,
2385 best_width, best_height);
2386 } else {
2387 FileSize *size;
2388 GdkPixbuf *p1, *p2;
2389
2390 size = find_best_size (slide->file1,
2391 best_width, best_height);
2392 p1 = get_as_pixbuf_for_size (bg, size->file, monitor,
2393 best_width, best_height);
2394
2395 size = find_best_size (slide->file2,
2396 best_width, best_height);
2397 p2 = get_as_pixbuf_for_size (bg, size->file, monitor,
2398 best_width, best_height);
2399
2400 if (p1 && p2)
2401 bg->pixbuf_cache = blend (p1, p2, alpha);
2402 if (p1)
2403 g_object_unref (p1);
2404 if (p2)
2405 g_object_unref (p2);
2406 }
2407
2408 ensure_timeout (bg, slide);
2409
2410 slideshow_unref (show);
2411 }
2412 }
2413
2414 /* If the next slideshow step is a long time away then
2415 we blow away the expensive stuff (large pixbufs) from
2416 the cache */
2417 if (time_until_next_change > KEEP_EXPENSIVE_CACHE_SECS60)
2418 blow_expensive_caches_in_idle (bg);
2419 }
2420
2421 if (bg->pixbuf_cache)
2422 g_object_ref (bg->pixbuf_cache)((__typeof__ (bg->pixbuf_cache)) (g_object_ref) (bg->pixbuf_cache
))
;
2423
2424 return bg->pixbuf_cache;
2425}
2426
2427static gboolean
2428is_different (CafeBG *bg,
2429 const char *filename)
2430{
2431 if (!filename && bg->filename) {
2432 return TRUE(!(0));
2433 }
2434 else if (filename && !bg->filename) {
2435 return TRUE(!(0));
2436 }
2437 else if (!filename && !bg->filename) {
2438 return FALSE(0);
2439 }
2440 else {
2441 time_t mtime = get_mtime (filename);
2442
2443 if (mtime != bg->file_mtime)
2444 return TRUE(!(0));
2445
2446 if (strcmp (filename, bg->filename) != 0)
2447 return TRUE(!(0));
2448
2449 return FALSE(0);
2450 }
2451}
2452
2453static void
2454clear_cache (CafeBG *bg)
2455{
2456 GList *list;
2457
2458 if (bg->file_cache) {
2459 for (list = bg->file_cache; list != NULL((void*)0); list = list->next) {
2460 FileCacheEntry *ent = list->data;
2461
2462 file_cache_entry_delete (ent);
2463 }
2464 g_list_free (bg->file_cache);
2465 bg->file_cache = NULL((void*)0);
2466 }
2467
2468 if (bg->pixbuf_cache) {
2469 g_object_unref (bg->pixbuf_cache);
2470
2471 bg->pixbuf_cache = NULL((void*)0);
2472 }
2473
2474 if (bg->timeout_id) {
2475 g_source_remove (bg->timeout_id);
2476
2477 bg->timeout_id = 0;
2478 }
2479}
2480
2481/* Pixbuf utilities */
2482static void
2483pixbuf_average_value (GdkPixbuf *pixbuf,
2484 CdkRGBA *result)
2485{
2486 guint64 a_total, r_total, g_total, b_total;
2487 guint row, column;
2488 int row_stride;
2489 const guchar *pixels, *p;
2490 int r, g, b;
2491 guint64 dividend;
2492 guint width, height;
2493 gdouble dd;
2494
2495 width = gdk_pixbuf_get_width (pixbuf);
2496 height = gdk_pixbuf_get_height (pixbuf);
2497 row_stride = gdk_pixbuf_get_rowstride (pixbuf);
2498 pixels = gdk_pixbuf_get_pixels (pixbuf);
2499
2500 /* iterate through the pixbuf, counting up each component */
2501 a_total = 0;
2502 r_total = 0;
2503 g_total = 0;
2504 b_total = 0;
2505
2506 if (gdk_pixbuf_get_has_alpha (pixbuf)) {
2507 for (row = 0; row < height; row++) {
2508 p = pixels + (row * row_stride);
2509 for (column = 0; column < width; column++) {
2510 int a;
2511
2512 r = *p++;
2513 g = *p++;
2514 b = *p++;
2515 a = *p++;
2516
2517 a_total += a;
2518 r_total += r * a;
2519 g_total += g * a;
2520 b_total += b * a;
2521 }
2522 }
2523 dividend = height * width * 0xFF;
2524 a_total *= 0xFF;
2525 } else {
2526 for (row = 0; row < height; row++) {
2527 p = pixels + (row * row_stride);
2528 for (column = 0; column < width; column++) {
2529 r = *p++;
2530 g = *p++;
2531 b = *p++;
2532
2533 r_total += r;
2534 g_total += g;
2535 b_total += b;
2536 }
2537 }
2538 dividend = height * width;
2539 a_total = dividend * 0xFF;
2540 }
2541
2542 dd = dividend * 0xFF;
2543 result->alpha = a_total / dd;
2544 result->red = r_total / dd;
2545 result->green = g_total / dd;
2546 result->blue = b_total / dd;
2547}
2548
2549static GdkPixbuf *
2550pixbuf_scale_to_fit (GdkPixbuf *src, int max_width, int max_height)
2551{
2552 double factor;
2553 int src_width, src_height;
2554 int new_width, new_height;
2555
2556 src_width = gdk_pixbuf_get_width (src);
2557 src_height = gdk_pixbuf_get_height (src);
2558
2559 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))
;
2560
2561 new_width = floor (src_width * factor + 0.5);
2562 new_height = floor (src_height * factor + 0.5);
2563
2564 return gdk_pixbuf_scale_simple (src, new_width, new_height, GDK_INTERP_BILINEAR);
2565}
2566
2567static GdkPixbuf *
2568pixbuf_scale_to_min (GdkPixbuf *src, int min_width, int min_height)
2569{
2570 double factor;
2571 int src_width, src_height;
2572 int new_width, new_height;
2573 GdkPixbuf *dest;
2574
2575 src_width = gdk_pixbuf_get_width (src);
2576 src_height = gdk_pixbuf_get_height (src);
2577
2578 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))
;
2579
2580 new_width = floor (src_width * factor + 0.5);
2581 new_height = floor (src_height * factor + 0.5);
2582
2583 dest = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
2584 gdk_pixbuf_get_has_alpha (src),
2585 8, min_width, min_height);
2586 if (!dest)
2587 return NULL((void*)0);
2588
2589 /* crop the result */
2590 gdk_pixbuf_scale (src, dest,
2591 0, 0,
2592 min_width, min_height,
2593 (new_width - min_width) / -2,
2594 (new_height - min_height) / -2,
2595 factor,
2596 factor,
2597 GDK_INTERP_BILINEAR);
2598 return dest;
2599}
2600
2601static guchar *
2602create_gradient (const CdkRGBA *primary,
2603 const CdkRGBA *secondary,
2604 int n_pixels)
2605{
2606 guchar *result = g_malloc (n_pixels * 3);
2607 int i;
2608
2609 for (i = 0; i < n_pixels; ++i) {
2610 double ratio = (i + 0.5) / n_pixels;
2611
2612 result[3 * i + 0] = (guchar) ((primary->red * (1 - ratio) + secondary->red * ratio) * 0x100);
2613 result[3 * i + 1] = (guchar) ((primary->green * (1 - ratio) + secondary->green * ratio) * 0x100);
2614 result[3 * i + 2] = (guchar) ((primary->blue * (1 - ratio) + secondary->blue * ratio) * 0x100);
2615 }
2616
2617 return result;
2618}
2619
2620static void
2621pixbuf_draw_gradient (GdkPixbuf *pixbuf,
2622 gboolean horizontal,
2623 CdkRGBA *primary,
2624 CdkRGBA *secondary,
2625 CdkRectangle *rect)
2626{
2627 int width;
2628 int height;
2629 int rowstride;
2630 guchar *dst;
2631 int n_channels = 3;
2632
2633 rowstride = gdk_pixbuf_get_rowstride (pixbuf);
2634 width = rect->width;
2635 height = rect->height;
2636 dst = gdk_pixbuf_get_pixels (pixbuf) + rect->x * n_channels + rowstride * rect->y;
2637
2638 if (horizontal) {
2639 guchar *gradient = create_gradient (primary, secondary, width);
2640 int copy_bytes_per_row = width * n_channels;
2641 int i;
2642
2643 for (i = 0; i < height; i++) {
2644 guchar *d;
2645 d = dst + rowstride * i;
2646 memcpy (d, gradient, copy_bytes_per_row);
2647 }
2648 g_free (gradient);
2649 } else {
2650 guchar *gb, *gradient;
2651 int i;
2652
2653 gradient = create_gradient (primary, secondary, height);
2654 for (i = 0; i < height; i++) {
2655 int j;
2656 guchar *d;
2657
2658 d = dst + rowstride * i;
2659 gb = gradient + n_channels * i;
2660 for (j = width; j > 0; j--) {
2661 int k;
2662
2663 for (k = 0; k < n_channels; k++) {
2664 *(d++) = gb[k];
2665 }
2666 }
2667 }
2668
2669 g_free (gradient);
2670 }
2671}
2672
2673static void
2674pixbuf_blend (GdkPixbuf *src,
2675 GdkPixbuf *dest,
2676 int src_x,
2677 int src_y,
2678 int src_width,
2679 int src_height,
2680 int dest_x,
2681 int dest_y,
2682 double alpha)
2683{
2684 int dest_width = gdk_pixbuf_get_width (dest);
2685 int dest_height = gdk_pixbuf_get_height (dest);
2686 int offset_x = dest_x - src_x;
2687 int offset_y = dest_y - src_y;
2688
2689 if (src_width < 0)
2690 src_width = gdk_pixbuf_get_width (src);
2691
2692 if (src_height < 0)
2693 src_height = gdk_pixbuf_get_height (src);
2694
2695 if (dest_x < 0)
2696 dest_x = 0;
2697
2698 if (dest_y < 0)
2699 dest_y = 0;
2700
2701 if (dest_x + src_width > dest_width) {
2702 src_width = dest_width - dest_x;
2703 }
2704
2705 if (dest_y + src_height > dest_height) {
2706 src_height = dest_height - dest_y;
2707 }
2708
2709 gdk_pixbuf_composite (src, dest,
2710 dest_x, dest_y,
2711 src_width, src_height,
2712 offset_x, offset_y,
2713 1, 1, GDK_INTERP_NEAREST,
2714 alpha * 0xFF + 0.5);
2715}
2716
2717static void
2718pixbuf_tile (GdkPixbuf *src, GdkPixbuf *dest)
2719{
2720 int x, y;
2721 int tile_width, tile_height;
2722 int dest_width = gdk_pixbuf_get_width (dest);
2723 int dest_height = gdk_pixbuf_get_height (dest);
2724
2725 tile_width = gdk_pixbuf_get_width (src);
2726 tile_height = gdk_pixbuf_get_height (src);
2727
2728 for (y = 0; y < dest_height; y += tile_height) {
2729 for (x = 0; x < dest_width; x += tile_width) {
2730 pixbuf_blend (src, dest, 0, 0,
2731 tile_width, tile_height, x, y, 1.0);
2732 }
2733 }
2734}
2735
2736static gboolean stack_is (SlideShow *parser, const char *s1, ...);
2737
2738/* Parser for fading background */
2739static void
2740handle_start_element (GMarkupParseContext *context G_GNUC_UNUSED__attribute__ ((__unused__)),
2741 const gchar *name,
2742 const gchar **attr_names,
2743 const gchar **attr_values,
2744 gpointer user_data,
2745 GError **err G_GNUC_UNUSED__attribute__ ((__unused__)))
2746{
2747 SlideShow *parser = user_data;
2748 gint i;
2749
2750 if (strcmp (name, "static") == 0 || strcmp (name, "transition") == 0) {
2751 Slide *slide = g_new0 (Slide, 1)((Slide *) g_malloc0_n ((1), sizeof (Slide)));
2752
2753 if (strcmp (name, "static") == 0)
2754 slide->fixed = TRUE(!(0));
2755
2756 g_queue_push_tail (parser->slides, slide);
2757 }
2758 else if (strcmp (name, "size") == 0) {
2759 Slide *slide = parser->slides->tail->data;
2760 FileSize *size = g_new0 (FileSize, 1)((FileSize *) g_malloc0_n ((1), sizeof (FileSize)));
2761 for (i = 0; attr_names[i]; i++) {
2762 if (strcmp (attr_names[i], "width") == 0)
2763 size->width = atoi (attr_values[i]);
2764 else if (strcmp (attr_names[i], "height") == 0)
2765 size->height = atoi (attr_values[i]);
2766 }
2767 if (parser->stack->tail &&
2768 (strcmp (parser->stack->tail->data, "file") == 0 ||
2769 strcmp (parser->stack->tail->data, "from") == 0)) {
2770 slide->file1 = g_slist_prepend (slide->file1, size);
2771 }
2772 else if (parser->stack->tail &&
2773 strcmp (parser->stack->tail->data, "to") == 0) {
2774 slide->file2 = g_slist_prepend (slide->file2, size);
2775 }
2776 else
2777 g_free (size);
2778 }
2779 g_queue_push_tail (parser->stack, g_strdup (name)g_strdup_inline (name));
2780}
2781
2782static void
2783handle_end_element (GMarkupParseContext *context G_GNUC_UNUSED__attribute__ ((__unused__)),
2784 const gchar *name G_GNUC_UNUSED__attribute__ ((__unused__)),
2785 gpointer user_data,
2786 GError **err G_GNUC_UNUSED__attribute__ ((__unused__)))
2787{
2788 SlideShow *parser = user_data;
2789
2790 g_free (g_queue_pop_tail (parser->stack));
2791}
2792
2793static gboolean
2794stack_is (SlideShow *parser,
2795 const char *s1,
2796 ...)
2797{
2798 GList *stack = NULL((void*)0);
2799 const char *s;
2800 GList *l1, *l2;
2801 va_list args;
2802
2803 stack = g_list_prepend (stack, (gpointer)s1);
2804
2805 va_start (args, s1)__builtin_va_start(args, s1);
2806
2807 s = va_arg (args, const char *)__builtin_va_arg(args, const char *);
2808 while (s) {
2809 stack = g_list_prepend (stack, (gpointer)s);
2810 s = va_arg (args, const char *)__builtin_va_arg(args, const char *);
2811 }
2812
2813 va_end (args)__builtin_va_end(args);
2814
2815 l1 = stack;
2816 l2 = parser->stack->head;
2817
2818 while (l1 && l2) {
2819 if (strcmp (l1->data, l2->data) != 0) {
2820 g_list_free (stack);
2821 return FALSE(0);
2822 }
2823
2824 l1 = l1->next;
2825 l2 = l2->next;
2826 }
2827
2828 g_list_free (stack);
2829
2830 return (!l1 && !l2);
2831}
2832
2833static int
2834parse_int (const char *text)
2835{
2836 return strtol (text, NULL((void*)0), 0);
2837}
2838
2839static void
2840handle_text (GMarkupParseContext *context G_GNUC_UNUSED__attribute__ ((__unused__)),
2841 const gchar *text,
2842 gsize text_len G_GNUC_UNUSED__attribute__ ((__unused__)),
2843 gpointer user_data,
2844 GError **err G_GNUC_UNUSED__attribute__ ((__unused__)))
2845{
2846 SlideShow *parser = user_data;
2847 FileSize *fs;
2848 gint i;
2849
2850 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)
;
2851 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)
;
2852
2853 Slide *slide = parser->slides->tail ? parser->slides->tail->data : NULL((void*)0);
2854
2855 if (stack_is (parser, "year", "starttime", "background", NULL((void*)0))) {
2856 parser->start_tm.tm_year = parse_int (text) - 1900;
2857 }
2858 else if (stack_is (parser, "month", "starttime", "background", NULL((void*)0))) {
2859 parser->start_tm.tm_mon = parse_int (text) - 1;
2860 }
2861 else if (stack_is (parser, "day", "starttime", "background", NULL((void*)0))) {
2862 parser->start_tm.tm_mday = parse_int (text);
2863 }
2864 else if (stack_is (parser, "hour", "starttime", "background", NULL((void*)0))) {
2865 parser->start_tm.tm_hour = parse_int (text) - 1;
2866 }
2867 else if (stack_is (parser, "minute", "starttime", "background", NULL((void*)0))) {
2868 parser->start_tm.tm_min = parse_int (text);
2869 }
2870 else if (stack_is (parser, "second", "starttime", "background", NULL((void*)0))) {
2871 parser->start_tm.tm_sec = parse_int (text);
2872 }
2873 else if (stack_is (parser, "duration", "static", "background", NULL((void*)0)) ||
2874 stack_is (parser, "duration", "transition", "background", NULL((void*)0))) {
2875 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)
;
2876
2877 slide->duration = g_strtod (text, NULL((void*)0));
2878 parser->total_duration += slide->duration;
2879 }
2880 else if (stack_is (parser, "file", "static", "background", NULL((void*)0)) ||
2881 stack_is (parser, "from", "transition", "background", NULL((void*)0))) {
2882 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)
;
2883
2884 for (i = 0; text[i]; i++) {
2885 if (!g_ascii_isspace (text[i])((g_ascii_table[(guchar) (text[i])] & G_ASCII_SPACE) != 0
)
)
2886 break;
2887 }
2888 if (text[i] == 0)
2889 return;
2890 fs = g_new (FileSize, 1)((FileSize *) g_malloc_n ((1), sizeof (FileSize)));
2891 fs->width = -1;
2892 fs->height = -1;
2893 fs->file = g_strdup (text)g_strdup_inline (text);
2894 slide->file1 = g_slist_prepend (slide->file1, fs);
2895 if (slide->file1->next != NULL((void*)0))
2896 parser->has_multiple_sizes = TRUE(!(0));
2897 }
2898 else if (stack_is (parser, "size", "file", "static", "background", NULL((void*)0)) ||
2899 stack_is (parser, "size", "from", "transition", "background", NULL((void*)0))) {
2900 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)
;
2901
2902 fs = slide->file1->data;
2903 fs->file = g_strdup (text)g_strdup_inline (text);
2904 if (slide->file1->next != NULL((void*)0))
2905 parser->has_multiple_sizes = TRUE(!(0));
2906 }
2907 else if (stack_is (parser, "to", "transition", "background", NULL((void*)0))) {
2908 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)
;
2909
2910 for (i = 0; text[i]; i++) {
2911 if (!g_ascii_isspace (text[i])((g_ascii_table[(guchar) (text[i])] & G_ASCII_SPACE) != 0
)
)
2912 break;
2913 }
2914 if (text[i] == 0)
2915 return;
2916 fs = g_new (FileSize, 1)((FileSize *) g_malloc_n ((1), sizeof (FileSize)));
2917 fs->width = -1;
2918 fs->height = -1;
2919 fs->file = g_strdup (text)g_strdup_inline (text);
2920 slide->file2 = g_slist_prepend (slide->file2, fs);
2921 if (slide->file2->next != NULL((void*)0))
2922 parser->has_multiple_sizes = TRUE(!(0));
2923 }
2924 else if (stack_is (parser, "size", "to", "transition", "background", NULL((void*)0))) {
2925 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)
;
2926
2927 fs = slide->file2->data;
2928 fs->file = g_strdup (text)g_strdup_inline (text);
2929 if (slide->file2->next != NULL((void*)0))
2930 parser->has_multiple_sizes = TRUE(!(0));
2931 }
2932}
2933
2934static SlideShow *
2935slideshow_ref (SlideShow *show)
2936{
2937 show->ref_count++;
2938 return show;
2939}
2940
2941static void
2942slideshow_unref (SlideShow *show)
2943{
2944 GList *list;
2945 GSList *slist;
2946 FileSize *size;
2947
2948 show->ref_count--;
2949 if (show->ref_count > 0)
2950 return;
2951
2952 for (list = show->slides->head; list != NULL((void*)0); list = list->next) {
2953 Slide *slide = list->data;
2954
2955 for (slist = slide->file1; slist != NULL((void*)0); slist = slist->next) {
2956 size = slist->data;
2957 g_free (size->file);
2958 g_free (size);
2959 }
2960 g_slist_free (slide->file1);
2961
2962 for (slist = slide->file2; slist != NULL((void*)0); slist = slist->next) {
2963 size = slist->data;
2964 g_free (size->file);
2965 g_free (size);
2966 }
2967 g_slist_free (slide->file2);
2968
2969 g_free (slide);
2970 }
2971
2972 g_queue_free (show->slides);
2973
2974 g_list_foreach (show->stack->head, (GFunc) g_free, NULL((void*)0));
2975 g_queue_free (show->stack);
2976
2977 g_free (show);
2978}
2979
2980static void
2981dump_bg (SlideShow *show G_GNUC_UNUSED__attribute__ ((__unused__)))
2982{
2983#if 0
2984 GList *list;
2985 GSList *slist;
2986
2987 for (list = show->slides->head; list != NULL((void*)0); list = list->next)
2988 {
2989 Slide *slide = list->data;
2990
2991 g_print ("\nSlide: %s\n", slide->fixed? "fixed" : "transition");
2992 g_print ("duration: %f\n", slide->duration);
2993 g_print ("File1:\n");
2994 for (slist = slide->file1; slist != NULL((void*)0); slist = slist->next) {
2995 FileSize *size = slist->data;
2996 g_print ("\t%s (%dx%d)\n",
2997 size->file, size->width, size->height);
2998 }
2999 g_print ("File2:\n");
3000 for (slist = slide->file2; slist != NULL((void*)0); slist = slist->next) {
3001 FileSize *size = slist->data;
3002 g_print ("\t%s (%dx%d)\n",
3003 size->file, size->width, size->height);
3004 }
3005 }
3006#endif
3007}
3008
3009static void
3010threadsafe_localtime (time_t time, struct tm *tm)
3011{
3012 struct tm *res;
3013
3014 G_LOCK_DEFINE_STATIC (localtime_mutex)static GMutex g__localtime_mutex_lock;
3015
3016 G_LOCK (localtime_mutex)g_mutex_lock (&g__localtime_mutex_lock);
3017
3018 res = localtime (&time);
3019 if (tm) {
3020 *tm = *res;
3021 }
3022
3023 G_UNLOCK (localtime_mutex)g_mutex_unlock (&g__localtime_mutex_lock);
3024}
3025
3026static SlideShow *
3027read_slideshow_file (const char *filename,
3028 GError **err)
3029{
3030 GMarkupParser parser = {
3031 handle_start_element,
3032 handle_end_element,
3033 handle_text,
3034 NULL((void*)0), /* passthrough */
3035 NULL((void*)0), /* error */
3036 };
3037
3038 GFile *file;
3039 char *contents = NULL((void*)0);
3040 gsize len;
3041 SlideShow *show = NULL((void*)0);
3042 GMarkupParseContext *context = NULL((void*)0);
3043
3044 if (!filename)
8
Assuming 'filename' is non-null
9
Taking false branch
3045 return NULL((void*)0);
3046
3047 file = g_file_new_for_path (filename);
3048 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
3049 g_object_unref (file);
3050 return NULL((void*)0);
3051 }
3052 g_object_unref (file);
3053
3054 show = g_new0 (SlideShow, 1)((SlideShow *) g_malloc0_n ((1), sizeof (SlideShow)));
3055 show->ref_count = 1;
3056 threadsafe_localtime ((time_t)0, &show->start_tm);
3057 show->stack = g_queue_new ();
3058 show->slides = g_queue_new ();
3059
3060 context = g_markup_parse_context_new (&parser, 0, show, NULL((void*)0));
3061
3062 if (!g_markup_parse_context_parse (context, contents, len, err)) {
12
Assuming the condition is false
13
Taking false branch
3063 slideshow_unref (show);
3064 show = NULL((void*)0);
3065 }
3066
3067
3068 if (show
13.1
'show' is non-null
) {
14
Taking true branch
3069 if (!g_markup_parse_context_end_parse (context, err)) {
15
Assuming the condition is false
16
Taking false branch
3070 slideshow_unref (show);
3071 show = NULL((void*)0);
3072 }
3073 }
3074
3075 g_markup_parse_context_free (context);
3076
3077 if (show
16.1
'show' is non-null
) {
17
Taking true branch
3078 time_t t;
3079 int len;
3080
3081 t = mktime (&show->start_tm);
3082
3083 show->start_time = (double)t;
3084
3085 dump_bg (show);
3086
3087 len = g_queue_get_length (show->slides);
3088
3089 /* no slides, that's not a slideshow */
3090 if (len == 0) {
18
Assuming 'len' is not equal to 0
19
Taking false branch
3091 slideshow_unref (show);
3092 show = NULL((void*)0);
3093 /* one slide, there's no transition */
3094 } else if (len == 1) {
20
Assuming 'len' is not equal to 1
21
Taking false branch
3095 Slide *slide = show->slides->head->data;
3096 slide->duration = show->total_duration = G_MAXUINT(2147483647 *2U +1U);
3097 }
3098 }
3099
3100 g_free (contents);
3101
3102 return show;
3103}
3104
3105/* Thumbnail utilities */
3106static GdkPixbuf *
3107create_thumbnail_for_filename (CafeDesktopThumbnailFactory *factory,
3108 const char *filename)
3109{
3110 char *thumb;
3111 time_t mtime;
3112 GdkPixbuf *result = NULL((void*)0);
3113 char *uri;
3114
3115 mtime = get_mtime (filename);
3116
3117 if (mtime == (time_t)-1)
3118 return NULL((void*)0);
3119
3120 uri = g_filename_to_uri (filename, NULL((void*)0), NULL((void*)0));
3121
3122 if (uri == NULL((void*)0))
3123 return NULL((void*)0);
3124
3125 thumb = cafe_desktop_thumbnail_factory_lookup (factory, uri, mtime);
3126
3127 if (thumb) {
3128 result = gdk_pixbuf_new_from_file (thumb, NULL((void*)0));
3129 g_free (thumb);
3130 }
3131 else {
3132 GdkPixbuf *orig;
3133
3134 orig = gdk_pixbuf_new_from_file (filename, NULL((void*)0));
3135 if (orig) {
3136 int orig_width = gdk_pixbuf_get_width (orig);
3137 int orig_height = gdk_pixbuf_get_height (orig);
3138
3139 result = pixbuf_scale_to_fit (orig, THUMBNAIL_SIZE256, THUMBNAIL_SIZE256);
3140
3141
3142 g_object_set_data_full (G_OBJECT (result)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((result)), (((GType) ((20) << (2))))))))
, "cafe-thumbnail-height",
3143 g_strdup_printf ("%d", orig_height), g_free);
3144 g_object_set_data_full (G_OBJECT (result)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((result)), (((GType) ((20) << (2))))))))
, "cafe-thumbnail-width",
3145 g_strdup_printf ("%d", orig_width), g_free);
3146
3147 g_object_unref (orig);
3148
3149 cafe_desktop_thumbnail_factory_save_thumbnail (factory, result, uri, mtime);
3150 }
3151 else {
3152 cafe_desktop_thumbnail_factory_create_failed_thumbnail (factory, uri, mtime);
3153 }
3154 }
3155
3156 g_free (uri);
3157
3158 return result;
3159}
3160
3161static gboolean
3162get_thumb_annotations (GdkPixbuf *thumb,
3163 int *orig_width,
3164 int *orig_height)
3165{
3166 char *end;
3167 const char *wstr, *hstr;
3168
3169 wstr = gdk_pixbuf_get_option (thumb, "tEXt::Thumb::Image::Width");
3170 hstr = gdk_pixbuf_get_option (thumb, "tEXt::Thumb::Image::Height");
3171
3172 if (hstr && wstr) {
3173 *orig_width = strtol (wstr, &end, 10);
3174 if (*end != 0)
3175 return FALSE(0);
3176
3177 *orig_height = strtol (hstr, &end, 10);
3178 if (*end != 0)
3179 return FALSE(0);
3180
3181 return TRUE(!(0));
3182 }
3183
3184 return FALSE(0);
3185}
3186
3187static gboolean
3188slideshow_has_multiple_sizes (SlideShow *show)
3189{
3190 return show->has_multiple_sizes;
3191}
3192
3193/*
3194 * Returns whether the background is a slideshow.
3195 */
3196gboolean
3197cafe_bg_changes_with_time (CafeBG *bg)
3198{
3199 SlideShow *show;
3200
3201 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)
;
3202
3203 if (!bg->filename)
3204 return FALSE(0);
3205
3206 show = get_as_slideshow (bg, bg->filename);
3207 if (show)
3208 return g_queue_get_length (show->slides) > 1;
3209
3210 return FALSE(0);
3211}
3212
3213/**
3214 * cafe_bg_create_frame_thumbnail:
3215 *
3216 * Creates a thumbnail for a certain frame, where 'frame' is somewhat
3217 * vaguely defined as 'suitable point to show while single-stepping
3218 * through the slideshow'.
3219 *
3220 * Returns: (transfer full): the newly created thumbnail or
3221 * or NULL if frame_num is out of bounds.
3222 */
3223GdkPixbuf *
3224cafe_bg_create_frame_thumbnail (CafeBG *bg,
3225 CafeDesktopThumbnailFactory *factory,
3226 CdkScreen *screen,
3227 int dest_width,
3228 int dest_height,
3229 int frame_num)
3230{
3231 SlideShow *show;
3232 GdkPixbuf *result;
3233 GList *l;
3234 int i, skipped;
3235 gboolean found;
3236
3237 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
3238
3239 show = get_as_slideshow (bg, bg->filename);
4
Calling 'get_as_slideshow'
24
Returning from 'get_as_slideshow'
3240
3241 if (!show
24.1
'show' is non-null
)
3242 return NULL((void*)0);
3243
3244
3245 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
3246 return NULL((void*)0);
3247
3248 i = 0;
3249 skipped = 0;
3250 found = FALSE(0);
3251 for (l = show->slides->head; l; l = l->next) {
28
Loop condition is true. Entering loop body
3252 Slide *slide = l->data;
3253 if (!slide->fixed) {
29
Assuming field 'fixed' is not equal to 0
30
Taking false branch
3254 skipped++;
3255 continue;
3256 }
3257 if (i == frame_num) {
31
Assuming 'i' is equal to 'frame_num'
32
Taking true branch
3258 found = TRUE(!(0));
3259 break;
3260 }
3261 i++;
3262 }
3263 if (!found
33.1
'found' is 1
)
33
Execution continues on line 3263
34
Taking false branch
3264 return NULL((void*)0);
3265
3266
3267 result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE(0), 8, dest_width, dest_height);
3268
3269 draw_color (bg, result);
3270
3271 if (bg->filename
34.1
Field 'filename' is non-null
) {
35
Taking true branch
3272 GdkPixbuf *thumb;
3273
3274 thumb = create_img_thumbnail (bg, factory, screen,
36
Calling 'create_img_thumbnail'
3275 dest_width, dest_height,
3276 frame_num + skipped);
3277
3278 if (thumb) {
3279 draw_image_for_thumb (bg, thumb, result);
3280 g_object_unref (thumb);
3281 }
3282 }
3283
3284 return result;
3285}
3286