1 /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8; tab-width: 8 -*-
2 *
3 * Copyright (C) 2005-2006 William Jon McCann <mccann@jhu.edu>
4 * Copyright (C) 2012-2021 MATE Developers
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
19 * 02110-1301, USA.
20 *
21 */
22
23 #include "config.h"
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <string.h>
29
30 #include <glib.h>
31 #include <gtk/gtk.h>
32
33 #include "gs-theme-engine.h"
34 #include "gste-slideshow.h"
35
36 static void gste_slideshow_finalize (GObject *object);
37
38 struct GSTESlideshowPrivate
39 {
40 /* Image at full opacity */
41 cairo_pattern_t *pat1;
42 /* Image at partial opacity */
43 cairo_pattern_t *pat2;
44 /* Alpha of pat2 */
45 gdouble alpha2;
46 /* edges of pat2 */
47 int pat2top;
48 int pat2bottom;
49 int pat2left;
50 int pat2right;
51
52 /* backbuffer that we do all the alpha drawing into (no round
53 * trips to the X server when the server doesn't support drawing
54 * pixmaps with alpha?) */
55 cairo_surface_t *surf;
56
57 gint64 fade_ticks;
58
59 GThread *load_thread;
60 GAsyncQueue *op_q;
61 GAsyncQueue *results_q;
62
63 guint results_pull_id;
64 guint update_image_id;
65
66 GSList *filename_list;
67 char *images_location;
68 gboolean sort_images;
69 int window_width;
70 int window_height;
71 PangoColor *background_color;
72 gboolean no_stretch_hint;
73
74 guint timeout_id;
75
76 GTimer *timer;
77 gboolean fade_disabled;
78 };
79
80 enum
81 {
82 PROP_0,
83 PROP_IMAGES_LOCATION,
84 PROP_SORT_IMAGES,
85 PROP_SOLID_BACKGROUND,
86 PROP_NO_STRETCH_HINT
87 };
88
89 static GObjectClass *parent_class = NULL;
90
91 G_DEFINE_TYPE_WITH_PRIVATE (GSTESlideshow, gste_slideshow, GS_TYPE_THEME_ENGINE)
92
93 #define N_FADE_TICKS 10
94 #define MINIMUM_FPS 3.0
95 #define DEFAULT_IMAGES_LOCATION DATADIR "/pixmaps/backgrounds"
96 #define IMAGE_LOAD_TIMEOUT 10000
97
98 typedef struct _Op
99 {
100 char *location;
101 GSTESlideshow *slideshow;
102 } Op;
103
104 typedef struct _OpResult
105 {
106 GdkPixbuf *pixbuf;
107 } OpResult;
108
109 static gboolean
push_load_image_func(GSTESlideshow * show)110 push_load_image_func (GSTESlideshow *show)
111 {
112 Op *op;
113
114 gs_theme_engine_profile_msg ("Starting a new image load");
115
116 op = g_new (Op, 1);
117
118 op->location = g_strdup (show->priv->images_location);
119 op->slideshow = g_object_ref (show);
120
121 g_async_queue_push (show->priv->op_q, op);
122
123 show->priv->update_image_id = 0;
124
125 return FALSE;
126 }
127
128 static void
start_new_load(GSTESlideshow * show,guint timeout)129 start_new_load (GSTESlideshow *show,
130 guint timeout)
131 {
132 gs_theme_engine_profile_msg ("Scheduling a new image load");
133
134 /* queue a new load */
135 if (show->priv->update_image_id <= 0)
136 {
137 show->priv->update_image_id = g_timeout_add_full (G_PRIORITY_LOW, timeout,
138 (GSourceFunc)push_load_image_func,
139 show, NULL);
140 }
141 }
142
143 static void
start_fade(GSTESlideshow * show,GdkPixbuf * pixbuf)144 start_fade (GSTESlideshow *show,
145 GdkPixbuf *pixbuf)
146 {
147 int pw;
148 int ph;
149 int x;
150 int y;
151 cairo_t *cr;
152 int window_width;
153 int window_height;
154
155 gs_theme_engine_profile_start ("start");
156
157 window_width = show->priv->window_width;
158 window_height = show->priv->window_height;
159
160 if (show->priv->pat2 != NULL)
161 {
162 cairo_pattern_destroy (show->priv->pat2);
163 }
164
165 pw = gdk_pixbuf_get_width (pixbuf);
166 ph = gdk_pixbuf_get_height (pixbuf);
167 x = (window_width - pw) / 2;
168 y = (window_height - ph) / 2;
169
170 if (gdk_pixbuf_get_has_alpha (pixbuf) && show->priv->background_color)
171 {
172 GdkPixbuf *colored;
173 guint32 color;
174
175 color = (show->priv->background_color->red << 16)
176 + (show->priv->background_color->green / 256 << 8)
177 + show->priv->background_color->blue / 256;
178 colored = gdk_pixbuf_composite_color_simple (pixbuf,
179 pw, ph,
180 GDK_INTERP_BILINEAR,
181 255,
182 256,
183 color,
184 color);
185
186 gdk_pixbuf_copy_area (colored, 0, 0,
187 gdk_pixbuf_get_width (colored),
188 gdk_pixbuf_get_height (colored),
189 pixbuf, 0, 0);
190
191 g_object_unref(colored);
192 }
193
194 cr = cairo_create (show->priv->surf);
195
196 /* XXX Handle out of memory? */
197 gdk_cairo_set_source_pixbuf (cr, pixbuf, x, y);
198 show->priv->pat2 = cairo_pattern_reference (cairo_get_source (cr));
199 show->priv->pat2top = y;
200 show->priv->pat2bottom = y + ph;
201 show->priv->pat2left = x;
202 show->priv->pat2right = x + pw;
203
204 cairo_destroy (cr);
205
206 show->priv->fade_ticks = 0;
207 g_timer_start (show->priv->timer);
208
209 gs_theme_engine_profile_end ("end");
210 }
211
212 static void
finish_fade(GSTESlideshow * show)213 finish_fade (GSTESlideshow *show)
214 {
215 gs_theme_engine_profile_start ("start");
216
217 if (show->priv->pat1 != NULL)
218 {
219 cairo_pattern_destroy (show->priv->pat1);
220 }
221
222 show->priv->pat1 = show->priv->pat2;
223 show->priv->pat2 = NULL;
224
225 start_new_load (show, IMAGE_LOAD_TIMEOUT);
226
227 gs_theme_engine_profile_end ("end");
228 }
229
230 static void
update_display(GSTESlideshow * show)231 update_display (GSTESlideshow *show)
232 {
233 int window_width;
234 int window_height;
235 cairo_t *cr;
236
237 gs_theme_engine_profile_start ("start");
238
239 cr = cairo_create (show->priv->surf);
240
241 gs_theme_engine_get_window_size (GS_THEME_ENGINE (show),
242 &window_width,
243 &window_height);
244
245 if (show->priv->pat2 != NULL)
246 {
247 /* fade out areas not covered by the new image */
248 /* top */
249 cairo_rectangle (cr, 0, 0, window_width, show->priv->pat2top);
250 if (show->priv->background_color)
251 {
252 cairo_set_source_rgba (cr, show->priv->background_color->red / 65535.0,
253 show->priv->background_color->green / 65535.0,
254 show->priv->background_color->blue / 65535.0, show->priv->alpha2);
255 }
256 else
257 {
258 cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, show->priv->alpha2);
259 }
260 cairo_fill (cr);
261 /* left (excluding what's covered by top and bottom) */
262 cairo_rectangle (cr, 0, show->priv->pat2top,
263 show->priv->pat2left,
264 show->priv->pat2bottom - show->priv->pat2top);
265 if (show->priv->background_color)
266 {
267 cairo_set_source_rgba (cr, show->priv->background_color->red / 65535.0,
268 show->priv->background_color->green / 65535.0,
269 show->priv->background_color->blue / 65535.0, show->priv->alpha2);
270 }
271 else
272 {
273 cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, show->priv->alpha2);
274 }
275 cairo_fill (cr);
276 /* bottom */
277 cairo_rectangle (cr, 0, show->priv->pat2bottom, window_width,
278 window_height - show->priv->pat2bottom);
279 if (show->priv->background_color)
280 {
281 cairo_set_source_rgba (cr, show->priv->background_color->red / 65535.0,
282 show->priv->background_color->green / 65535.0,
283 show->priv->background_color->blue / 65535.0, show->priv->alpha2);
284 }
285 else
286 {
287 cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, show->priv->alpha2);
288 }
289 cairo_fill (cr);
290 /* right (excluding what's covered by top and bottom) */
291 cairo_rectangle (cr, show->priv->pat2right,
292 show->priv->pat2top,
293 window_width - show->priv->pat2right,
294 show->priv->pat2bottom - show->priv->pat2top);
295 if (show->priv->background_color)
296 {
297 cairo_set_source_rgba (cr, show->priv->background_color->red / 65535.0,
298 show->priv->background_color->green / 65535.0,
299 show->priv->background_color->blue / 65535.0, show->priv->alpha2);
300 }
301 else
302 {
303 cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, show->priv->alpha2);
304 }
305 cairo_fill (cr);
306
307 gs_theme_engine_profile_start ("paint pattern to surface");
308 cairo_set_source (cr, show->priv->pat2);
309
310 cairo_paint_with_alpha (cr, show->priv->alpha2);
311 gs_theme_engine_profile_end ("paint pattern to surface");
312 }
313 else
314 {
315 if (show->priv->pat1 != NULL)
316 {
317 cairo_set_source (cr, show->priv->pat1);
318 cairo_paint (cr);
319 }
320 }
321
322 cairo_destroy (cr);
323
324 gtk_widget_queue_draw (GTK_WIDGET (show));
325 }
326
327 static gboolean
draw_iter(GSTESlideshow * show)328 draw_iter (GSTESlideshow *show)
329 {
330 double old_opacity;
331 double new_opacity;
332
333 if (show->priv->pat2 != NULL)
334 {
335 gdouble fps;
336 gdouble elapsed;
337
338 if (show->priv->fade_disabled)
339 {
340 show->priv->alpha2 = 1.0;
341 update_display (show);
342 finish_fade (show);
343 return TRUE;
344 }
345
346 /* we are in a fade */
347 show->priv->fade_ticks++;
348
349 /*
350 * We have currently drawn pat2 with old_opacity, and we
351 * want to set alpha2 so that drawing pat2 at alpha2
352 * yields it drawn with new_opacity
353 *
354 * Solving
355 * new_opacity = 1 - (1 - alpha2) * (1 - old_opacity)
356 * yields
357 * alpha2 = 1 - (1 - new_opacity) / (1 - old_opacity)
358 *
359 * XXX This assumes that cairo doesn't correct alpha for
360 * the color profile. However, any error is guaranteed
361 * to be cleaned up by the last iteration, where alpha2
362 * becomes 1 because new_opacity is 1.
363 */
364 old_opacity = (double) (show->priv->fade_ticks - 1) /
365 (double) N_FADE_TICKS;
366 new_opacity = (double) show->priv->fade_ticks /
367 (double) N_FADE_TICKS;
368 show->priv->alpha2 = 1.0 - (1.0 - new_opacity) /
369 (1.0 - old_opacity);
370
371 update_display (show);
372
373 elapsed = g_timer_elapsed (show->priv->timer, NULL);
374 fps = (gdouble)show->priv->fade_ticks / elapsed;
375 if (fps < MINIMUM_FPS)
376 {
377 g_warning ("Getting less than %.2f frames per second, disabling fade", MINIMUM_FPS);
378 show->priv->fade_ticks = N_FADE_TICKS - 1;
379 show->priv->fade_disabled = TRUE;
380 }
381
382 if (show->priv->fade_ticks >= N_FADE_TICKS)
383 {
384 finish_fade (show);
385 }
386 }
387
388 return TRUE;
389 }
390
391 static void
process_new_pixbuf(GSTESlideshow * show,GdkPixbuf * pixbuf)392 process_new_pixbuf (GSTESlideshow *show,
393 GdkPixbuf *pixbuf)
394 {
395 gs_theme_engine_profile_msg ("Processing a new image");
396
397 if (pixbuf != NULL)
398 {
399 start_fade (show, pixbuf);
400 }
401 else
402 {
403 start_new_load (show, 10);
404 }
405 }
406
407 static void
op_result_free(OpResult * result)408 op_result_free (OpResult *result)
409 {
410 if (result == NULL)
411 {
412 return;
413 }
414
415 if (result->pixbuf != NULL)
416 {
417 g_object_unref (result->pixbuf);
418 }
419
420 g_free (result);
421 }
422
423 static gboolean
results_pull_func(GSTESlideshow * show)424 results_pull_func (GSTESlideshow *show)
425 {
426 OpResult *result;
427
428 g_async_queue_lock (show->priv->results_q);
429
430 result = g_async_queue_try_pop_unlocked (show->priv->results_q);
431 g_assert (result);
432
433 while (result != NULL)
434 {
435 process_new_pixbuf (show, result->pixbuf);
436 op_result_free (result);
437
438 result = g_async_queue_try_pop_unlocked (show->priv->results_q);
439 }
440
441 show->priv->results_pull_id = 0;
442
443 g_async_queue_unlock (show->priv->results_q);
444
445 return FALSE;
446 }
447
448 static GdkPixbuf *
scale_pixbuf(GdkPixbuf * pixbuf,int max_width,int max_height,gboolean no_stretch_hint)449 scale_pixbuf (GdkPixbuf *pixbuf,
450 int max_width,
451 int max_height,
452 gboolean no_stretch_hint)
453 {
454 int pw;
455 int ph;
456 float scale_factor_x = 1.0;
457 float scale_factor_y = 1.0;
458 float scale_factor = 1.0;
459
460 pw = gdk_pixbuf_get_width (pixbuf);
461 ph = gdk_pixbuf_get_height (pixbuf);
462
463 /* If the image is less than 256 wide or high then it
464 is probably a thumbnail and we should ignore it */
465 if (pw < 256 || ph < 256)
466 {
467 return NULL;
468 }
469
470 /* Determine which dimension requires the smallest scale. */
471 scale_factor_x = (float) max_width / (float) pw;
472 scale_factor_y = (float) max_height / (float) ph;
473
474 if (scale_factor_x > scale_factor_y)
475 {
476 scale_factor = scale_factor_y;
477 }
478 else
479 {
480 scale_factor = scale_factor_x;
481 }
482
483 /* always scale down, allow to disable scaling up */
484 if (scale_factor < 1.0 || !no_stretch_hint)
485 {
486 int scale_x;
487 int scale_y;
488
489 scale_x = (int) (pw * scale_factor);
490 scale_y = (int) (ph * scale_factor);
491 return gdk_pixbuf_scale_simple (pixbuf,
492 scale_x,
493 scale_y,
494 GDK_INTERP_BILINEAR);
495 }
496 else
497 {
498 return g_object_ref (pixbuf);
499 }
500 }
501
502 static void
add_files_to_list(GSList ** list,const char * base)503 add_files_to_list (GSList **list,
504 const char *base)
505 {
506 GDir *d;
507 const char *d_name;
508
509 d = g_dir_open (base, 0, NULL);
510 if (d == NULL)
511 {
512 g_warning ("Could not open directory: %s", base);
513 return;
514 }
515
516 while ((d_name = g_dir_read_name (d)) != NULL)
517 {
518 char *path;
519
520 /* skip hidden files */
521 if (d_name[0] == '.')
522 {
523 continue;
524 }
525
526 path = g_build_filename (base, d_name, NULL);
527 if (g_file_test (path, G_FILE_TEST_IS_DIR))
528 {
529 add_files_to_list (list, path);
530 g_free (path);
531 }
532 else
533 {
534 *list = g_slist_prepend (*list, path);
535 }
536 }
537
538 g_dir_close (d);
539 }
540
541 static GSList *
build_filename_list_local_dir(const char * base)542 build_filename_list_local_dir (const char *base)
543 {
544 GSList *list = NULL;
545
546 add_files_to_list (&list, base);
547
548 return list;
549 }
550
551 static int
gste_strcmp_compare_func(gconstpointer string_a,gconstpointer string_b)552 gste_strcmp_compare_func (gconstpointer string_a, gconstpointer string_b)
553 {
554 return strcmp (string_a == NULL ? "" : string_a,
555 string_b == NULL ? "" : string_b);
556 }
557
558
559 static GdkPixbuf *
get_pixbuf_from_local_dir(GSTESlideshow * show,const char * location)560 get_pixbuf_from_local_dir (GSTESlideshow *show,
561 const char *location)
562 {
563 GdkPixbuf *pixbuf, *transformed_pixbuf;
564 char *filename;
565 int i;
566 GSList *l;
567
568 /* rebuild the cache */
569 if (show->priv->filename_list == NULL)
570 {
571 show->priv->filename_list = build_filename_list_local_dir (location);
572 }
573
574 if (show->priv->filename_list == NULL)
575 {
576 return NULL;
577 }
578 else
579 {
580 if (show->priv->sort_images)
581 {
582 show->priv->filename_list = g_slist_sort (show->priv->filename_list, gste_strcmp_compare_func);
583 }
584 }
585
586 /* get a random filename if needed */
587 if (! show->priv->sort_images)
588 {
589 i = g_random_int_range (0, g_slist_length (show->priv->filename_list));
590 l = g_slist_nth (show->priv->filename_list, i);
591 }
592 else
593 {
594 l = show->priv->filename_list;
595 }
596 filename = l->data;
597
598 pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
599
600 if (pixbuf != NULL)
601 {
602 transformed_pixbuf = gdk_pixbuf_apply_embedded_orientation (pixbuf);
603 g_object_unref (pixbuf);
604 }
605 else
606 {
607 transformed_pixbuf = NULL;
608 }
609
610 g_free (filename);
611 show->priv->filename_list = g_slist_delete_link (show->priv->filename_list, l);
612
613 return transformed_pixbuf;
614 }
615
616 static GdkPixbuf *
get_pixbuf_from_location(GSTESlideshow * show,const char * location)617 get_pixbuf_from_location (GSTESlideshow *show,
618 const char *location)
619 {
620 GdkPixbuf *pixbuf = NULL;
621 gboolean is_dir;
622
623 if (location == NULL)
624 {
625 return NULL;
626 }
627
628 is_dir = g_file_test (location, G_FILE_TEST_IS_DIR);
629
630 if (is_dir)
631 {
632 pixbuf = get_pixbuf_from_local_dir (show, location);
633 }
634
635 return pixbuf;
636 }
637
638 static GdkPixbuf *
get_pixbuf(GSTESlideshow * show,const char * location,int width,int height)639 get_pixbuf (GSTESlideshow *show,
640 const char *location,
641 int width,
642 int height)
643 {
644 GdkPixbuf *pixbuf;
645 GdkPixbuf *scaled = NULL;
646
647 if (location == NULL)
648 {
649 return NULL;
650 }
651
652 pixbuf = get_pixbuf_from_location (show, location);
653
654 if (pixbuf != NULL)
655 {
656 scaled = scale_pixbuf (pixbuf, width, height, show->priv->no_stretch_hint);
657 g_object_unref (pixbuf);
658 }
659
660 return scaled;
661 }
662
663 static void
op_load_image(GSTESlideshow * show,const char * location)664 op_load_image (GSTESlideshow *show,
665 const char *location)
666 {
667 OpResult *op_result;
668 int window_width;
669 int window_height;
670
671 window_width = show->priv->window_width;
672 window_height = show->priv->window_height;
673
674 op_result = g_new0 (OpResult, 1);
675
676 op_result->pixbuf = get_pixbuf (show,
677 location,
678 window_width,
679 window_height);
680
681 g_async_queue_lock (show->priv->results_q);
682 g_async_queue_push_unlocked (show->priv->results_q, op_result);
683
684 if (show->priv->results_pull_id == 0)
685 {
686 show->priv->results_pull_id = g_idle_add_full (G_PRIORITY_HIGH_IDLE,
687 (GSourceFunc)results_pull_func,
688 show, NULL);
689 }
690
691 g_async_queue_unlock (show->priv->results_q);
692 }
693
694 static gpointer
load_threadfunc(GAsyncQueue * op_q)695 load_threadfunc (GAsyncQueue *op_q)
696 {
697 Op *op;
698
699 op = g_async_queue_pop (op_q);
700 while (op)
701 {
702 op_load_image (op->slideshow,
703 op->location);
704
705 if (op->slideshow != NULL)
706 {
707 g_object_unref (op->slideshow);
708 }
709 g_free (op->location);
710 g_free (op);
711
712 op = g_async_queue_pop (op_q);
713 }
714
715 return NULL;
716 }
717
718 void
gste_slideshow_set_images_location(GSTESlideshow * show,const char * location)719 gste_slideshow_set_images_location (GSTESlideshow *show,
720 const char *location)
721 {
722 g_return_if_fail (GSTE_IS_SLIDESHOW (show));
723
724 g_free (show->priv->images_location);
725 show->priv->images_location = g_strdup (location);
726 }
727
728
729 void
gste_slideshow_set_sort_images(GSTESlideshow * show,gboolean sort_images)730 gste_slideshow_set_sort_images (GSTESlideshow *show,
731 gboolean sort_images)
732 {
733 g_return_if_fail (GSTE_IS_SLIDESHOW (show));
734
735 show->priv->sort_images = sort_images;
736 }
737
738 void
gste_slideshow_set_no_stretch_hint(GSTESlideshow * show,gboolean no_stretch_hint)739 gste_slideshow_set_no_stretch_hint (GSTESlideshow *show,
740 gboolean no_stretch_hint)
741 {
742 g_return_if_fail (GSTE_IS_SLIDESHOW (show));
743
744 show->priv->no_stretch_hint = no_stretch_hint;
745 }
746
747 void
gste_slideshow_set_background_color(GSTESlideshow * show,const char * background_color)748 gste_slideshow_set_background_color (GSTESlideshow *show,
749 const char *background_color)
750 {
751 g_return_if_fail (GSTE_IS_SLIDESHOW (show));
752
753 if (show->priv->background_color != NULL)
754 {
755 g_slice_free (PangoColor, show->priv->background_color);
756 show->priv->background_color = NULL;
757 }
758
759 if (background_color != NULL)
760 {
761 show->priv->background_color = g_slice_new (PangoColor);
762
763 if (pango_color_parse (show->priv->background_color, background_color) == FALSE)
764 {
765 g_slice_free (PangoColor, show->priv->background_color);
766 show->priv->background_color = NULL;
767 }
768 }
769 }
770
771 static void
gste_slideshow_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)772 gste_slideshow_set_property (GObject *object,
773 guint prop_id,
774 const GValue *value,
775 GParamSpec *pspec)
776 {
777 GSTESlideshow *self;
778
779 self = GSTE_SLIDESHOW (object);
780
781 switch (prop_id)
782 {
783 case PROP_IMAGES_LOCATION:
784 gste_slideshow_set_images_location (self, g_value_get_string (value));
785 break;
786 case PROP_SORT_IMAGES:
787 gste_slideshow_set_sort_images (self, g_value_get_boolean (value));
788 break;
789 case PROP_SOLID_BACKGROUND:
790 gste_slideshow_set_background_color (self, g_value_get_string (value));
791 break;
792 case PROP_NO_STRETCH_HINT:
793 gste_slideshow_set_no_stretch_hint (self, g_value_get_boolean (value));
794 break;
795 default:
796 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
797 break;
798 }
799 }
800
801 static void
gste_slideshow_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)802 gste_slideshow_get_property (GObject *object,
803 guint prop_id,
804 GValue *value,
805 GParamSpec *pspec)
806 {
807 GSTESlideshow *self;
808
809 self = GSTE_SLIDESHOW (object);
810
811 switch (prop_id)
812 {
813 case PROP_IMAGES_LOCATION:
814 g_value_set_string (value, self->priv->images_location);
815 break;
816 case PROP_SORT_IMAGES:
817 g_value_set_boolean (value, self->priv->sort_images);
818 break;
819 case PROP_SOLID_BACKGROUND:
820 {
821 char *color = NULL;
822 color = pango_color_to_string (self->priv->background_color);
823 g_value_set_string (value, color);
824 g_free (color);
825 break;
826 }
827 case PROP_NO_STRETCH_HINT:
828 g_value_set_boolean (value, self->priv->no_stretch_hint);
829 break;
830 default:
831 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
832 break;
833 }
834 }
835
836 static void
gste_slideshow_real_show(GtkWidget * widget)837 gste_slideshow_real_show (GtkWidget *widget)
838 {
839 GSTESlideshow *show = GSTE_SLIDESHOW (widget);
840 int delay;
841
842 if (GTK_WIDGET_CLASS (parent_class)->show)
843 {
844 GTK_WIDGET_CLASS (parent_class)->show (widget);
845 }
846
847 start_new_load (show, 10);
848
849 delay = 25;
850 show->priv->timeout_id = g_timeout_add (delay, (GSourceFunc)draw_iter, show);
851
852 if (show->priv->timer != NULL)
853 {
854 g_timer_destroy (show->priv->timer);
855 }
856 show->priv->timer = g_timer_new ();
857 }
858
859 static gboolean
gste_slideshow_real_draw(GtkWidget * widget,cairo_t * cr)860 gste_slideshow_real_draw (GtkWidget *widget,
861 cairo_t *cr)
862 {
863 GSTESlideshow *show = GSTE_SLIDESHOW (widget);
864
865 if (GTK_WIDGET_CLASS (parent_class)->draw) {
866 GTK_WIDGET_CLASS (parent_class)->draw (widget, cr);
867 }
868
869 cairo_set_source_surface (cr, show->priv->surf, 0, 0);
870
871 gs_theme_engine_profile_start ("paint surface to window");
872 cairo_paint (cr);
873 gs_theme_engine_profile_end ("paint surface to window");
874
875 return TRUE;
876 }
877
878 static gboolean
gste_slideshow_real_configure(GtkWidget * widget,GdkEventConfigure * event)879 gste_slideshow_real_configure (GtkWidget *widget,
880 GdkEventConfigure *event)
881 {
882 GSTESlideshow *show = GSTE_SLIDESHOW (widget);
883 gboolean handled = FALSE;
884 cairo_t *cr;
885
886 /* resize */
887 gs_theme_engine_get_window_size (GS_THEME_ENGINE (show),
888 &show->priv->window_width,
889 &show->priv->window_height);
890
891 gs_theme_engine_profile_msg ("Resize to x:%d y:%d",
892 show->priv->window_width,
893 show->priv->window_height);
894
895 if (show->priv->surf != NULL)
896 {
897 cairo_surface_destroy (show->priv->surf);
898 }
899
900 cr = gdk_cairo_create (gtk_widget_get_window (widget));
901 show->priv->surf = cairo_surface_create_similar (cairo_get_target (cr),
902 CAIRO_CONTENT_COLOR,
903 show->priv->window_width,
904 show->priv->window_height);
905 cairo_destroy (cr);
906
907 /* schedule a redraw */
908 gtk_widget_queue_draw (widget);
909
910 if (GTK_WIDGET_CLASS (parent_class)->configure_event)
911 {
912 handled = GTK_WIDGET_CLASS (parent_class)->configure_event (widget, event);
913 }
914
915 return handled;
916 }
917
918 static void
gste_slideshow_class_init(GSTESlideshowClass * klass)919 gste_slideshow_class_init (GSTESlideshowClass *klass)
920 {
921 GObjectClass *object_class = G_OBJECT_CLASS (klass);
922 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
923
924 parent_class = g_type_class_peek_parent (klass);
925
926 object_class->finalize = gste_slideshow_finalize;
927 object_class->get_property = gste_slideshow_get_property;
928 object_class->set_property = gste_slideshow_set_property;
929
930 widget_class->show = gste_slideshow_real_show;
931 widget_class->draw = gste_slideshow_real_draw;
932 widget_class->configure_event = gste_slideshow_real_configure;
933
934 g_object_class_install_property (object_class,
935 PROP_IMAGES_LOCATION,
936 g_param_spec_string ("images-location",
937 NULL,
938 NULL,
939 NULL,
940 G_PARAM_READWRITE));
941 g_object_class_install_property (object_class,
942 PROP_SORT_IMAGES,
943 g_param_spec_boolean ("sort-images",
944 NULL,
945 NULL,
946 FALSE,
947 G_PARAM_READWRITE));
948 g_object_class_install_property (object_class,
949 PROP_SOLID_BACKGROUND,
950 g_param_spec_string ("background-color",
951 NULL,
952 NULL,
953 NULL,
954 G_PARAM_READWRITE));
955 g_object_class_install_property (object_class,
956 PROP_NO_STRETCH_HINT,
957 g_param_spec_boolean ("no-stretch",
958 NULL,
959 NULL,
960 FALSE,
961 G_PARAM_READWRITE));
962 }
963
964 static void
set_visual(GtkWidget * widget)965 set_visual (GtkWidget *widget)
966 {
967 GdkScreen *screen;
968 GdkVisual *visual;
969
970 screen = gtk_widget_get_screen (widget);
971 visual = gdk_screen_get_rgba_visual (screen);
972 if (visual == NULL)
973 {
974 visual = gdk_screen_get_system_visual (screen);
975 }
976
977 gtk_widget_set_visual (widget, visual);
978 }
979
980 static void
gste_slideshow_init(GSTESlideshow * show)981 gste_slideshow_init (GSTESlideshow *show)
982 {
983 show->priv = gste_slideshow_get_instance_private (show);
984
985 show->priv->images_location = g_strdup (DEFAULT_IMAGES_LOCATION);
986
987 show->priv->op_q = g_async_queue_new ();
988 show->priv->results_q = g_async_queue_new ();
989
990 g_thread_new ("loadthread", (GThreadFunc)load_threadfunc, show->priv->op_q);
991
992 set_visual (GTK_WIDGET (show));
993 }
994
995 static void
gste_slideshow_finalize(GObject * object)996 gste_slideshow_finalize (GObject *object)
997 {
998 GSTESlideshow *show;
999 gpointer result;
1000
1001 g_return_if_fail (object != NULL);
1002 g_return_if_fail (GSTE_IS_SLIDESHOW (object));
1003
1004 show = GSTE_SLIDESHOW (object);
1005
1006 g_return_if_fail (show->priv != NULL);
1007
1008 if (show->priv->surf)
1009 {
1010 cairo_surface_destroy (show->priv->surf);
1011 }
1012
1013 if (show->priv->timeout_id > 0)
1014 {
1015 g_source_remove (show->priv->timeout_id);
1016 show->priv->timeout_id = 0;
1017 }
1018
1019 if (show->priv->results_pull_id > 0)
1020 {
1021 g_source_remove (show->priv->results_pull_id);
1022 show->priv->results_pull_id = 0;
1023 }
1024
1025 if (show->priv->results_q != NULL)
1026 {
1027 result = g_async_queue_try_pop (show->priv->results_q);
1028
1029 while (result)
1030 {
1031 result = g_async_queue_try_pop (show->priv->results_q);
1032 }
1033 g_async_queue_unref (show->priv->results_q);
1034 }
1035
1036 g_free (show->priv->images_location);
1037 show->priv->images_location = NULL;
1038
1039 if (show->priv->background_color)
1040 {
1041 g_slice_free (PangoColor, show->priv->background_color);
1042 show->priv->background_color = NULL;
1043 }
1044
1045 if (show->priv->timer != NULL)
1046 {
1047 g_timer_destroy (show->priv->timer);
1048 }
1049
1050 G_OBJECT_CLASS (parent_class)->finalize (object);
1051 }
1052