1 /* vi:set et ai sw=2 sts=2 ts=2: */
2 /*-
3  * Copyright (c) 2011 Jannis Pohlmann <jannis@xfce.org>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of
8  * the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public
16  * License along with this program; if not, write to the Free
17  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 
25 #include <thunar/thunar-thumbnail-cache-proxy.h>
26 
27 #include <glib.h>
28 #include <glib-object.h>
29 #include <gio/gio.h>
30 
31 #include <thunar/thunar-private.h>
32 #include <thunar/thunar-thumbnail-cache.h>
33 #include <thunar/thunar-file.h>
34 
35 #define _thumbnail_cache_lock(cache)   g_mutex_lock (&((cache)->lock))
36 #define _thumbnail_cache_unlock(cache) g_mutex_unlock (&((cache)->lock))
37 
38 
39 
40 static void thunar_thumbnail_cache_finalize (GObject *object);
41 
42 
43 
44 enum
45 {
46   THUNAR_THUMBNAIL_CACHE_PROXY_AVAILABLE,
47   THUNAR_THUMBNAIL_CACHE_PROXY_WAITING,
48   THUNAR_THUMBNAIL_CACHE_PROXY_FAILED
49 };
50 
51 struct _ThunarThumbnailCacheClass
52 {
53   GObjectClass __parent__;
54 };
55 
56 struct _ThunarThumbnailCache
57 {
58   GObject     __parent__;
59 
60   ThunarThumbnailCacheDBus *cache_proxy;
61   int                       proxy_state;
62 
63   GList      *move_source_queue;
64   GList      *move_target_queue;
65   guint       move_queue_idle_id;
66 
67   GList      *copy_source_queue;
68   GList      *copy_target_queue;
69   guint       copy_queue_idle_id;
70 
71   GList      *delete_queue;
72   guint       delete_queue_idle_id;
73 
74   GList      *cleanup_queue;
75   guint       cleanup_queue_idle_id;
76 
77   GMutex      lock;
78 };
79 
80 
81 
G_DEFINE_TYPE(ThunarThumbnailCache,thunar_thumbnail_cache,G_TYPE_OBJECT)82 G_DEFINE_TYPE (ThunarThumbnailCache, thunar_thumbnail_cache, G_TYPE_OBJECT)
83 
84 
85 
86 static void
87 thunar_thumbnail_cache_class_init (ThunarThumbnailCacheClass *klass)
88 {
89   GObjectClass *gobject_class;
90 
91   /* Determine the parent type class */
92   thunar_thumbnail_cache_parent_class = g_type_class_peek_parent (klass);
93 
94   gobject_class = G_OBJECT_CLASS (klass);
95   gobject_class->finalize = thunar_thumbnail_cache_finalize;
96 }
97 
98 
99 
100 static void
thunar_thumbnail_cache_finalize(GObject * object)101 thunar_thumbnail_cache_finalize (GObject *object)
102 {
103   ThunarThumbnailCache *cache = THUNAR_THUMBNAIL_CACHE (object);
104 
105   /* acquire a cache lock */
106   _thumbnail_cache_lock (cache);
107 
108   /* drop the move queue idle and all queued files */
109   if (cache->move_queue_idle_id > 0)
110     g_source_remove (cache->move_queue_idle_id);
111   g_list_free_full (cache->move_source_queue, g_object_unref);
112   g_list_free_full (cache->move_target_queue, g_object_unref);
113 
114   /* drop the copy queue idle and all queued files */
115   if (cache->copy_queue_idle_id > 0)
116     g_source_remove (cache->copy_queue_idle_id);
117   g_list_free_full (cache->copy_source_queue, g_object_unref);
118   g_list_free_full (cache->copy_target_queue, g_object_unref);
119 
120   /* drop the delete queue idle and all queued files */
121   if (cache->delete_queue_idle_id > 0)
122     g_source_remove (cache->delete_queue_idle_id);
123   g_list_free_full (cache->delete_queue, g_object_unref);
124 
125   /* drop the cleanup queue idle and all queued files */
126   if (cache->cleanup_queue_idle_id > 0)
127     g_source_remove (cache->cleanup_queue_idle_id);
128   g_list_free_full (cache->cleanup_queue, g_object_unref);
129 
130   /* check if we have a valid cache proxy */
131   if (cache->cache_proxy != NULL)
132     g_object_unref (cache->cache_proxy);
133 
134   /* release the cache lock */
135   _thumbnail_cache_unlock (cache);
136 
137   /* release the mutex itself */
138   g_mutex_clear (&cache->lock);
139 
140   (*G_OBJECT_CLASS (thunar_thumbnail_cache_parent_class)->finalize) (object);
141 }
142 
143 
144 
145 static void
thunar_thumbnail_cache_copy_async_reply(ThunarThumbnailCacheDBus * proxy,GAsyncResult * res,gpointer user_data)146 thunar_thumbnail_cache_copy_async_reply (ThunarThumbnailCacheDBus *proxy,
147                                          GAsyncResult             *res,
148                                          gpointer                  user_data)
149 {
150   GList      *li;
151   ThunarFile *file;
152   GError     *error = NULL;
153 
154   _thunar_return_if_fail (THUNAR_IS_THUMBNAIL_CACHE_DBUS (proxy));
155 
156   if (!thunar_thumbnail_cache_dbus_call_copy_finish (proxy, res, &error))
157     {
158       g_printerr ("ThunarThumbnailCache: failed to call Copy(): %s\n", error->message);
159     }
160   g_clear_error (&error);
161 
162   for (li = user_data; li != NULL; li = li->next)
163     {
164       file = thunar_file_cache_lookup (G_FILE (li->data));
165 
166       if (G_LIKELY (file != NULL))
167         {
168           /* if visible, let the view know there might be a thumb */
169           thunar_file_changed (file);
170           g_object_unref (file);
171         }
172     }
173 
174   g_list_free_full (user_data, g_object_unref);
175 }
176 
177 
178 
179 static void
thunar_thumbnail_cache_move_async_reply(ThunarThumbnailCacheDBus * proxy,GAsyncResult * res,gpointer user_data)180 thunar_thumbnail_cache_move_async_reply (ThunarThumbnailCacheDBus *proxy,
181                                          GAsyncResult             *res,
182                                          gpointer                  user_data)
183 {
184   GList      *li;
185   ThunarFile *file;
186   GError     *error = NULL;
187 
188   _thunar_return_if_fail (THUNAR_IS_THUMBNAIL_CACHE_DBUS (proxy));
189 
190   if (!thunar_thumbnail_cache_dbus_call_move_finish (proxy, res, &error))
191     {
192       g_printerr ("ThunarThumbnailCache: failed to call Move(): %s\n", error->message);
193     }
194   g_clear_error (&error);
195 
196   for (li = user_data; li != NULL; li = li->next)
197     {
198       file = thunar_file_cache_lookup (G_FILE (li->data));
199 
200       if (G_LIKELY (file != NULL))
201         {
202           /* if visible, let the view know there might be a thumb */
203           thunar_file_changed (file);
204           g_object_unref (file);
205         }
206     }
207 
208   g_list_free_full (user_data, g_object_unref);
209 }
210 
211 
212 
213 static void
thunar_thumbnail_cache_move_async(ThunarThumbnailCache * cache,const gchar ** source_uris,const gchar ** target_uris,gpointer user_data)214 thunar_thumbnail_cache_move_async (ThunarThumbnailCache *cache,
215                                    const gchar         **source_uris,
216                                    const gchar         **target_uris,
217                                    gpointer              user_data)
218 {
219   _thunar_return_if_fail (THUNAR_IS_THUMBNAIL_CACHE (cache));
220   _thunar_return_if_fail (source_uris != NULL);
221   _thunar_return_if_fail (target_uris != NULL);
222 
223   /* request a thumbnail cache update asynchronously */
224   thunar_thumbnail_cache_dbus_call_move (cache->cache_proxy,
225                                          source_uris, target_uris,
226                                          NULL,
227                                          (GAsyncReadyCallback)thunar_thumbnail_cache_move_async_reply,
228                                          user_data);
229 }
230 
231 
232 
233 static void
thunar_thumbnail_cache_copy_async(ThunarThumbnailCache * cache,const gchar ** source_uris,const gchar ** target_uris,gpointer user_data)234 thunar_thumbnail_cache_copy_async (ThunarThumbnailCache *cache,
235                                    const gchar         **source_uris,
236                                    const gchar         **target_uris,
237                                    gpointer              user_data)
238 {
239   _thunar_return_if_fail (THUNAR_IS_THUMBNAIL_CACHE (cache));
240   _thunar_return_if_fail (source_uris != NULL);
241   _thunar_return_if_fail (target_uris != NULL);
242 
243   /* request a thumbnail cache update asynchronously */
244   thunar_thumbnail_cache_dbus_call_copy (cache->cache_proxy,
245                                          source_uris, target_uris,
246                                          NULL,
247                                          (GAsyncReadyCallback)thunar_thumbnail_cache_copy_async_reply,
248                                          user_data);
249 }
250 
251 
252 
253 static void
thunar_thumbnail_cache_delete_async(ThunarThumbnailCache * cache,const gchar ** uris)254 thunar_thumbnail_cache_delete_async (ThunarThumbnailCache *cache,
255                                      const gchar         **uris)
256 {
257   _thunar_return_if_fail (THUNAR_IS_THUMBNAIL_CACHE (cache));
258   _thunar_return_if_fail (uris != NULL);
259 
260   /* request a thumbnail cache update asynchronously */
261   thunar_thumbnail_cache_dbus_call_delete (cache->cache_proxy, uris,
262                                            NULL, NULL, NULL);
263 }
264 
265 
266 
267 static void
thunar_thumbnail_cache_cleanup_async(ThunarThumbnailCache * cache,const gchar * const * base_uris)268 thunar_thumbnail_cache_cleanup_async (ThunarThumbnailCache *cache,
269                                       const gchar *const    *base_uris)
270 {
271   _thunar_return_if_fail (THUNAR_IS_THUMBNAIL_CACHE (cache));
272   _thunar_return_if_fail (base_uris != NULL);
273 
274   /* request a thumbnail cache update asynchronously */
275   thunar_thumbnail_cache_dbus_call_cleanup (cache->cache_proxy,
276                                             (const gchar **)base_uris, 0,
277                                             NULL, NULL, NULL);
278 }
279 
280 
281 
282 static gboolean
thunar_thumbnail_cache_process_queue(ThunarThumbnailCache * cache,gboolean copy_async)283 thunar_thumbnail_cache_process_queue (ThunarThumbnailCache *cache,
284                                       gboolean              copy_async)
285 {
286   GList  *sp;
287   GList  *tp;
288   gchar **source_uris;
289   gchar **target_uris;
290   guint   n_uris;
291   guint   n;
292   GList  *source_queue;
293   GList  *target_queue;
294 
295   _thunar_return_val_if_fail (THUNAR_IS_THUMBNAIL_CACHE (cache), FALSE);
296 
297   /* acquire a cache lock */
298   _thumbnail_cache_lock (cache);
299 
300   /* steal the lists */
301   if (copy_async)
302     {
303       source_queue = cache->copy_source_queue;
304       cache->copy_source_queue = NULL;
305       target_queue = cache->copy_target_queue;
306       cache->copy_target_queue = NULL;
307     }
308   else
309     {
310       source_queue = cache->move_source_queue;
311       cache->move_source_queue = NULL;
312       target_queue = cache->move_target_queue;
313       cache->move_target_queue = NULL;
314     }
315 
316   /* compute how many URIs there are */
317   n_uris = g_list_length (source_queue);
318 
319   /* allocate a string array for the URIs */
320   source_uris = g_new0 (gchar *, n_uris + 1);
321   target_uris = g_new0 (gchar *, n_uris + 1);
322 
323   /* fill URI array with file URIs from the move queue */
324   for (n = 0,
325        sp = g_list_last (source_queue),
326        tp = g_list_last (target_queue);
327        sp != NULL && tp != NULL;
328        sp = sp->prev, tp = tp->prev, ++n)
329     {
330       source_uris[n] = g_file_get_uri (sp->data);
331       target_uris[n] = g_file_get_uri (tp->data);
332 
333       /* release the source object */
334       g_object_unref (sp->data);
335     }
336 
337   /* NULL-terminate the URI arrays */
338   source_uris[n] = NULL;
339   target_uris[n] = NULL;
340 
341   if (copy_async)
342     {
343       /* asynchronously copy the thumbnails */
344       thunar_thumbnail_cache_copy_async (cache,
345                                          (const gchar **)source_uris,
346                                          (const gchar **)target_uris,
347                                          target_queue);
348     }
349   else
350     {
351       /* asynchronously move the thumbnails */
352       thunar_thumbnail_cache_move_async (cache,
353                                          (const gchar **)source_uris,
354                                          (const gchar **)target_uris,
355                                          target_queue);
356     }
357 
358   /* free the URI arrays */
359   g_strfreev (source_uris);
360   g_strfreev (target_uris);
361 
362   /* release the move queue lists */
363   g_list_free (source_queue);
364 
365   /* release the cache lock */
366   _thumbnail_cache_unlock (cache);
367 
368   return FALSE;
369 }
370 
371 
372 
373 static gboolean
thunar_thumbnail_cache_process_move_queue(gpointer user_data)374 thunar_thumbnail_cache_process_move_queue (gpointer user_data)
375 {
376   return thunar_thumbnail_cache_process_queue (user_data, FALSE);
377 }
378 
379 
380 
381 static void
thunar_thumbnail_cache_process_move_queue_destroy(gpointer user_data)382 thunar_thumbnail_cache_process_move_queue_destroy (gpointer user_data)
383 {
384   THUNAR_THUMBNAIL_CACHE (user_data)->move_queue_idle_id = 0;
385 }
386 
387 
388 
389 static gboolean
thunar_thumbnail_cache_process_copy_queue(gpointer user_data)390 thunar_thumbnail_cache_process_copy_queue (gpointer user_data)
391 {
392   return thunar_thumbnail_cache_process_queue (user_data, TRUE);
393 }
394 
395 
396 
397 static void
thunar_thumbnail_cache_process_copy_queue_destroy(gpointer user_data)398 thunar_thumbnail_cache_process_copy_queue_destroy (gpointer user_data)
399 {
400   THUNAR_THUMBNAIL_CACHE (user_data)->copy_queue_idle_id = 0;
401 }
402 
403 
404 
405 static gboolean
thunar_thumbnail_cache_process_delete_queue(ThunarThumbnailCache * cache)406 thunar_thumbnail_cache_process_delete_queue (ThunarThumbnailCache *cache)
407 {
408   GList  *lp;
409   gchar **uris;
410   guint   n_uris;
411   guint   n;
412 
413   _thunar_return_val_if_fail (THUNAR_IS_THUMBNAIL_CACHE (cache), FALSE);
414 
415   /* acquire a cache lock */
416   _thumbnail_cache_lock (cache);
417 
418   /* compute how many URIs there are */
419   n_uris = g_list_length (cache->delete_queue);
420 
421   /* allocate a string array for the URIs */
422   uris = g_new0 (gchar *, n_uris + 1);
423 
424   /* fill URI array with file URIs from the delete queue */
425   for (lp = g_list_last (cache->delete_queue), n = 0; lp != NULL; lp = lp->prev, ++n)
426     {
427       uris[n] = g_file_get_uri (lp->data);
428 
429       /* release the file object */
430       g_object_unref (lp->data);
431     }
432 
433   /* NULL-terminate the URI array */
434   uris[n] = NULL;
435 
436   /* asynchronously delete the thumbnails */
437   thunar_thumbnail_cache_delete_async (cache, (const gchar **)uris);
438 
439   /* free the URI array */
440   g_strfreev (uris);
441 
442   /* release the delete queue list */
443   g_list_free (cache->delete_queue);
444   cache->delete_queue = NULL;
445 
446   /* reset the delete queue idle ID */
447   cache->delete_queue_idle_id = 0;
448 
449   /* release the cache lock */
450   _thumbnail_cache_unlock (cache);
451 
452   return FALSE;
453 }
454 
455 
456 
457 static gboolean
thunar_thumbnail_cache_process_cleanup_queue(ThunarThumbnailCache * cache)458 thunar_thumbnail_cache_process_cleanup_queue (ThunarThumbnailCache *cache)
459 {
460   GList  *lp;
461   gchar **uris;
462   guint   n_uris;
463   guint   n;
464 
465   _thunar_return_val_if_fail (THUNAR_IS_THUMBNAIL_CACHE (cache), FALSE);
466 
467   /* acquire a cache lock */
468   _thumbnail_cache_lock (cache);
469 
470   /* compute how many URIs there are */
471   n_uris = g_list_length (cache->cleanup_queue);
472 
473   /* allocate a string array for the URIs */
474   uris = g_new0 (gchar *, n_uris + 1);
475 
476 #ifndef NDEBUG
477   g_debug ("cleanup:");
478 #endif
479 
480   /* fill URI array with file URIs from the cleanup queue */
481   for (lp = g_list_last (cache->cleanup_queue), n = 0; lp != NULL; lp = lp->prev, ++n)
482     {
483       uris[n] = g_file_get_uri (lp->data);
484 
485 #ifndef NDEBUG
486       g_debug ("  %s", uris[n]);
487 #endif
488 
489       /* release the file object */
490       g_object_unref (lp->data);
491     }
492 
493   /* NULL-terminate the URI array */
494   uris[n] = NULL;
495 
496   /* asynchronously cleanup the thumbnails */
497   thunar_thumbnail_cache_cleanup_async (cache, (const gchar *const *)uris);
498 
499   /* free the URI array */
500   g_strfreev (uris);
501 
502   /* release the cleanup queue list */
503   g_list_free (cache->cleanup_queue);
504   cache->cleanup_queue = NULL;
505 
506   /* reset the cleanup queue idle ID */
507   cache->cleanup_queue_idle_id = 0;
508 
509   /* release the cache lock */
510   _thumbnail_cache_unlock (cache);
511 
512   return FALSE;
513 }
514 
515 
516 
517 ThunarThumbnailCache *
thunar_thumbnail_cache_new(void)518 thunar_thumbnail_cache_new (void)
519 {
520   return g_object_new (THUNAR_TYPE_THUMBNAIL_CACHE, NULL);
521 }
522 
523 
524 
525 void
thunar_thumbnail_cache_move_file(ThunarThumbnailCache * cache,GFile * source_file,GFile * target_file)526 thunar_thumbnail_cache_move_file (ThunarThumbnailCache *cache,
527                                   GFile                *source_file,
528                                   GFile                *target_file)
529 {
530   _thunar_return_if_fail (THUNAR_IS_THUMBNAIL_CACHE (cache));
531   _thunar_return_if_fail (G_IS_FILE (source_file));
532   _thunar_return_if_fail (G_IS_FILE (target_file));
533 
534   /* acquire a cache lock */
535   _thumbnail_cache_lock (cache);
536 
537   /* check if we have a valid proxy for the cache service */
538   if (cache->proxy_state != THUNAR_THUMBNAIL_CACHE_PROXY_FAILED)
539     {
540       /* add the files to the move queue */
541       cache->move_source_queue = g_list_prepend (cache->move_source_queue,
542                                                  g_object_ref (source_file));
543       cache->move_target_queue = g_list_prepend (cache->move_target_queue,
544                                                  g_object_ref (target_file));
545     }
546 
547   if (cache->proxy_state == THUNAR_THUMBNAIL_CACHE_PROXY_AVAILABLE)
548     {
549       /* cancel any pending timeout to process the move queue */
550       if (cache->move_queue_idle_id > 0)
551         {
552           g_source_remove (cache->move_queue_idle_id);
553           cache->move_queue_idle_id = 0;
554         }
555 
556       /* process the move queue in a 250ms timeout */
557       cache->move_queue_idle_id =
558         g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE, 250, thunar_thumbnail_cache_process_move_queue,
559                             cache, thunar_thumbnail_cache_process_move_queue_destroy);
560     }
561 
562   /* release the cache lock */
563   _thumbnail_cache_unlock (cache);
564 }
565 
566 
567 
568 void
thunar_thumbnail_cache_copy_file(ThunarThumbnailCache * cache,GFile * source_file,GFile * target_file)569 thunar_thumbnail_cache_copy_file (ThunarThumbnailCache *cache,
570                                   GFile                *source_file,
571                                   GFile                *target_file)
572 {
573   _thunar_return_if_fail (THUNAR_IS_THUMBNAIL_CACHE (cache));
574   _thunar_return_if_fail (G_IS_FILE (source_file));
575   _thunar_return_if_fail (G_IS_FILE (target_file));
576 
577   /* acquire a cache lock */
578   _thumbnail_cache_lock (cache);
579 
580   /* check if we have a valid proxy for the cache service */
581   if (cache->proxy_state != THUNAR_THUMBNAIL_CACHE_PROXY_FAILED)
582     {
583       /* add the files to the copy queues */
584       cache->copy_source_queue = g_list_prepend (cache->copy_source_queue,
585                                                  g_object_ref (source_file));
586       cache->copy_target_queue = g_list_prepend (cache->copy_target_queue,
587                                                  g_object_ref (target_file));
588     }
589 
590   if (cache->proxy_state == THUNAR_THUMBNAIL_CACHE_PROXY_AVAILABLE)
591     {
592       /* cancel any pending timeout to process the copy queue */
593       if (cache->copy_queue_idle_id > 0)
594         {
595           g_source_remove (cache->copy_queue_idle_id);
596           cache->copy_queue_idle_id = 0;
597         }
598 
599       /* process the copy queue in a 250ms timeout */
600       cache->copy_queue_idle_id =
601         g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE, 500, thunar_thumbnail_cache_process_copy_queue,
602                             cache, thunar_thumbnail_cache_process_copy_queue_destroy);
603     }
604 
605   /* release the cache lock */
606   _thumbnail_cache_unlock (cache);
607 }
608 
609 
610 
611 void
thunar_thumbnail_cache_delete_file(ThunarThumbnailCache * cache,GFile * file)612 thunar_thumbnail_cache_delete_file (ThunarThumbnailCache *cache,
613                                     GFile                *file)
614 {
615   _thunar_return_if_fail (THUNAR_IS_THUMBNAIL_CACHE (cache));
616   _thunar_return_if_fail (G_IS_FILE (file));
617 
618   /* acquire a cache lock */
619   _thumbnail_cache_lock (cache);
620 
621   /* check if we have a valid proxy for the cache service */
622   if (cache->proxy_state != THUNAR_THUMBNAIL_CACHE_PROXY_FAILED)
623     {
624       /* add the file to the delete queue */
625       cache->delete_queue = g_list_prepend (cache->delete_queue, g_object_ref (file));
626     }
627 
628   if (cache->proxy_state == THUNAR_THUMBNAIL_CACHE_PROXY_AVAILABLE)
629     {
630       /* cancel any pending timeout to process the delete queue */
631       if (cache->delete_queue_idle_id > 0)
632         {
633           g_source_remove (cache->delete_queue_idle_id);
634           cache->delete_queue_idle_id = 0;
635         }
636 
637       /* process the delete queue in a 250ms timeout */
638       cache->delete_queue_idle_id =
639         g_timeout_add (500, (GSourceFunc) thunar_thumbnail_cache_process_delete_queue,
640                        cache);
641     }
642 
643   /* release the cache lock */
644   _thumbnail_cache_unlock (cache);
645 }
646 
647 
648 
649 void
thunar_thumbnail_cache_cleanup_file(ThunarThumbnailCache * cache,GFile * file)650 thunar_thumbnail_cache_cleanup_file (ThunarThumbnailCache *cache,
651                                      GFile                *file)
652 {
653   _thunar_return_if_fail (THUNAR_IS_THUMBNAIL_CACHE (cache));
654   _thunar_return_if_fail (G_IS_FILE (file));
655 
656   /* acquire a cache lock */
657   _thumbnail_cache_lock (cache);
658 
659   /* check if we have a valid proxy for the cache service */
660   if (cache->proxy_state != THUNAR_THUMBNAIL_CACHE_PROXY_FAILED)
661     {
662       /* add the file to the cleanup queue */
663       cache->cleanup_queue = g_list_prepend (cache->cleanup_queue, g_object_ref (file));
664     }
665 
666   if (cache->proxy_state == THUNAR_THUMBNAIL_CACHE_PROXY_AVAILABLE)
667     {
668       /* cancel any pending timeout to process the cleanup queue */
669       if (cache->cleanup_queue_idle_id > 0)
670         {
671           g_source_remove (cache->cleanup_queue_idle_id);
672           cache->cleanup_queue_idle_id = 0;
673         }
674 
675       /* process the cleanup queue in a 250ms timeout */
676       cache->cleanup_queue_idle_id =
677         g_timeout_add (1000, (GSourceFunc) thunar_thumbnail_cache_process_cleanup_queue,
678                        cache);
679     }
680 
681   /* release the cache lock */
682   _thumbnail_cache_unlock (cache);
683 }
684 
685 
686 
687 static void
thunar_thumbnail_cache_proxy_created(GObject * source,GAsyncResult * res,gpointer userdata)688 thunar_thumbnail_cache_proxy_created (GObject      *source,
689                                       GAsyncResult *res,
690                                       gpointer      userdata)
691 {
692   ThunarThumbnailCache     *cache = THUNAR_THUMBNAIL_CACHE (userdata);
693   ThunarThumbnailCacheDBus *proxy;
694   GError                   *error = NULL;
695 
696   _thumbnail_cache_lock (cache);
697 
698   if ((proxy = thunar_thumbnail_cache_dbus_proxy_new_for_bus_finish (res, &error)))
699     {
700       cache->cache_proxy = proxy;
701       cache->proxy_state = THUNAR_THUMBNAIL_CACHE_PROXY_AVAILABLE;
702     }
703   else
704     {
705       cache->proxy_state = THUNAR_THUMBNAIL_CACHE_PROXY_FAILED;
706 
707       g_printerr ("ThunarThumbnailCache: Couldn't connect to bus service: %s\n", error->message);
708     }
709 
710   g_clear_error (&error);
711 
712   /* process the move queue in a 250ms timeout */
713   if (cache->move_source_queue)
714       cache->move_queue_idle_id =
715         g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE, 250, thunar_thumbnail_cache_process_move_queue,
716                             cache, thunar_thumbnail_cache_process_move_queue_destroy);
717 
718   /* process the copy queue in a 250ms timeout */
719   if (cache->copy_source_queue)
720       cache->copy_queue_idle_id =
721         g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE, 500, thunar_thumbnail_cache_process_copy_queue,
722                             cache, thunar_thumbnail_cache_process_copy_queue_destroy);
723 
724   /* process the delete queue in a 250ms timeout */
725   if (cache->delete_queue)
726       cache->delete_queue_idle_id =
727         g_timeout_add (500, (GSourceFunc) thunar_thumbnail_cache_process_delete_queue,
728                        cache);
729 
730   /* process the cleanup queue in a 250ms timeout */
731   if (cache->cleanup_queue)
732       cache->cleanup_queue_idle_id =
733         g_timeout_add (1000, (GSourceFunc) thunar_thumbnail_cache_process_cleanup_queue,
734                        cache);
735 
736   _thumbnail_cache_unlock (cache);
737 
738   /* drop additional reference */
739   g_object_unref (cache);
740 }
741 
742 
743 
744 static void
thunar_thumbnail_cache_init(ThunarThumbnailCache * cache)745 thunar_thumbnail_cache_init (ThunarThumbnailCache *cache)
746 {
747   /* create a new mutex for accessing the cache from different threads */
748   g_mutex_init (&cache->lock);
749 
750   /* add an additional reference to keep us alive while tre proxy initializes */
751   g_object_ref (cache);
752 
753   /* try to connect to D-Bus */
754   cache->proxy_state = THUNAR_THUMBNAIL_CACHE_PROXY_WAITING;
755   thunar_thumbnail_cache_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION,
756                                                  0,
757                                                  "org.freedesktop.thumbnails.Cache1",
758                                                  "/org/freedesktop/thumbnails/Cache1",
759                                                  NULL,
760                                                  thunar_thumbnail_cache_proxy_created,
761                                                  cache);
762 }
763 
764 
765