1
/* vi:set et ai sw=2 sts=2 ts=2: */
3
* Copyright (c) 2009 Jannis Pohlmann <jannis@xfce.org>
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.
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.
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.
26
#include <dbus/dbus.h>
27
#include <dbus/dbus-glib.h>
29
#include <thunar/thunar-thumbnailer-proxy.h>
32
#include <thunar/thunar-marshal.h>
33
#include <thunar/thunar-private.h>
34
#include <thunar/thunar-thumbnailer.h>
39
* WARNING: The source code in this file may do harm to animals. Dead kittens
43
* ThunarThumbnailer is a class for requesting thumbnails from org.xfce.tumbler.*
44
* D-Bus services. This header contains an in-depth description of its design to make
45
* it easier to understand why things are the way they are.
47
* Please note that all D-Bus calls are performed asynchronously.
53
* ThunarThumbnailer maintains a wait queue to group individual thumbnail requests.
54
* The wait queue is processed at most every 100ms. This is done to reduce the
55
* overall D-Bus noise when dealing with a lot of requests. The more thumbnails are
56
* requested in a 100ms time slot, the less D-Bus methods are sent.
58
* Let N be the number of requests made for individual files in a 100ms slot.
59
* Compared to sending out one requests per file (which generates 4*N D-Bus messages,
60
* 1 Queue, 1 Started, 1 Ready/Error and 1 Finished for each of the N files), the wait
61
* queue technique only causes 3+N D-Bus messages to be sent (1 Queue, 1 Started,
62
* N Ready/Error and 1 Finished). This can be further improved on the service side
63
* if the D-Bus thumbnailer groups Ready/Error messages (which of course has drawbacks
64
* with regards to the overall thumbnailing responsiveness).
66
* Before a URI is added to the wait queue, it is checked whether it isn't already
67
* 1) in the wait queue, 2) part of a pending or active request or 3) part of a
68
* finished request which has an idle function pending.
70
* When a request call is finally sent out, an internal request ID is created and
71
* associated with the corresponding DBusGProxyCall via the request_call_mapping hash
72
* table. It also remembers the URIs for the internal request ID in the
73
* request_uris_mapping hash table.
75
* The D-Bus reply handler then checks if there was an delivery error or
76
* not. If the request method was sent successfully, the handle returned by the
77
* D-Bus thumbnailer is associated bidirectionally with the internal request ID via
78
* the request_handle_mapping and handle_request_mappings. If the request could
79
* not be sent at all, the URIs array is dropped from request_uris_mapping. In
80
* both cases, the association of the internal request ID with the DBusGProxyCall
81
* is removed from request_call_mapping.
83
* These hash tables play a major role in the Started, Finished, Error and Ready
90
* When a Started signal is emitted by the D-Bus thumbnailer, ThunarThumbnailer
91
* receives the handle and looks up the corresponding internal request ID. If
92
* it exists (which it should), it schedules an idle function to handle the
93
* signal in the application's main loop.
95
* The idle function then looks up the URIs array for the request ID from the
96
* request_uris_mapping. For each of these URIs the corresponding ThunarFile
97
* is looked up from the file cache (which represents files currently being
98
* used somewhere in the UI), and if the ThunarFile exists in the cache it's
99
* thumb state is set to _LOADING (unless it's already marked as _READY).
105
* The Ready and Error signal handlers work exactly like Started except that
106
* the Ready idle function sets the thumb state of the corresponding
107
* ThunarFile objects to _READY and the Error signal sets the state to _NONE.
113
* The Finished signal handler looks up the internal request ID based on
114
* the D-Bus thumbnailer handle. It then drops all corresponding information
115
* from handle_request_mapping, request_handle_mapping and request_uris_mapping.
123
THUNAR_THUMBNAILER_IDLE_ERROR,
124
THUNAR_THUMBNAILER_IDLE_READY,
125
THUNAR_THUMBNAILER_IDLE_STARTED,
126
} ThunarThumbnailerIdleType;
130
typedef struct _ThunarThumbnailerCall ThunarThumbnailerCall;
131
typedef struct _ThunarThumbnailerIdle ThunarThumbnailerIdle;
132
typedef struct _ThunarThumbnailerItem ThunarThumbnailerItem;
137
static void thunar_thumbnailer_finalize (GObject *object);
139
static void thunar_thumbnailer_init_thumbnailer_proxy (ThunarThumbnailer *thumbnailer,
140
DBusGConnection *connection);
141
static gboolean thunar_thumbnailer_file_is_supported (ThunarThumbnailer *thumbnailer,
143
static void thunar_thumbnailer_thumbnailer_finished (DBusGProxy *proxy,
145
ThunarThumbnailer *thumbnailer);
146
static void thunar_thumbnailer_thumbnailer_error (DBusGProxy *proxy,
150
const gchar *message,
151
ThunarThumbnailer *thumbnailer);
152
static void thunar_thumbnailer_thumbnailer_ready (DBusGProxy *proxy,
155
ThunarThumbnailer *thumbnailer);
156
static void thunar_thumbnailer_thumbnailer_started (DBusGProxy *proxy,
158
ThunarThumbnailer *thumbnailer);
159
static gpointer thunar_thumbnailer_queue_async (ThunarThumbnailer *thumbnailer,
161
const gchar **mime_hints);
162
static gboolean thunar_thumbnailer_error_idle (gpointer user_data);
163
static gboolean thunar_thumbnailer_ready_idle (gpointer user_data);
164
static gboolean thunar_thumbnailer_started_idle (gpointer user_data);
165
static void thunar_thumbnailer_call_free (ThunarThumbnailerCall *call);
166
static void thunar_thumbnailer_idle_free (gpointer data);
167
static ThunarThumbnailerItem *thunar_thumbnailer_item_new (GFile *file,
168
const gchar *mime_hint);
169
static void thunar_thumbnailer_item_free (gpointer data);
174
struct _ThunarThumbnailerClass
176
GObjectClass __parent__;
179
struct _ThunarThumbnailer
184
/* proxies to communicate with D-Bus services */
185
DBusGProxy *thumbnailer_proxy;
187
/* wait queue used to delay (and thereby group) thumbnail requests */
189
guint wait_queue_idle_id;
191
/* hash table to map D-Bus service handles to ThunarThumbnailer requests */
192
GHashTable *handle_request_mapping;
194
/* hash table to map ThunarThumbnailer requests to D-Bus service handles */
195
GHashTable *request_handle_mapping;
197
/* hash table to map ThunarThumbnailer requests to DBusGProxyCalls */
198
GHashTable *request_call_mapping;
200
/* hash table to map ThunarThumbnailer requests to URI arrays */
201
GHashTable *request_uris_mapping;
205
/* cached arrays of URI schemes and MIME types for which thumbnails
206
* can be generated */
207
gchar **supported_schemes;
208
gchar **supported_types;
210
/* last ThunarThumbnailer request ID */
211
gpointer last_request;
213
/* IDs of idle functions */
219
struct _ThunarThumbnailerCall
221
ThunarThumbnailer *thumbnailer;
225
struct _ThunarThumbnailerIdle
227
ThunarThumbnailerIdleType type;
228
ThunarThumbnailer *thumbnailer;
238
struct _ThunarThumbnailerItem
248
static DBusGProxy *thunar_thumbnailer_proxy;
253
G_DEFINE_TYPE (ThunarThumbnailer, thunar_thumbnailer, G_TYPE_OBJECT);
258
thunar_thumbnailer_class_init (ThunarThumbnailerClass *klass)
260
GObjectClass *gobject_class;
262
gobject_class = G_OBJECT_CLASS (klass);
263
gobject_class->finalize = thunar_thumbnailer_finalize;
269
thunar_thumbnailer_init (ThunarThumbnailer *thumbnailer)
272
DBusGConnection *connection;
274
thumbnailer->lock = g_mutex_new ();
275
thumbnailer->supported_schemes = NULL;
276
thumbnailer->supported_types = NULL;
277
thumbnailer->last_request = GUINT_TO_POINTER (0);
278
thumbnailer->idles = NULL;
279
thumbnailer->wait_queue_idle_id = 0;
281
/* try to connect to D-Bus */
282
connection = dbus_g_bus_get (DBUS_BUS_SESSION, NULL);
284
/* initialize the proxies */
285
thunar_thumbnailer_init_thumbnailer_proxy (thumbnailer, connection);
287
/* check if we have a thumbnailer proxy */
288
if (thumbnailer->thumbnailer_proxy != NULL)
290
/* we do, set up the hash tables */
291
thumbnailer->request_handle_mapping =
292
g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL);
293
thumbnailer->handle_request_mapping =
294
g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL);
295
thumbnailer->request_call_mapping =
296
g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL);
297
thumbnailer->request_uris_mapping =
298
g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
299
(GDestroyNotify) g_strfreev);
302
/* release the D-Bus connection if we have one */
303
if (connection != NULL)
304
dbus_g_connection_unref (connection);
311
thunar_thumbnailer_finalize (GObject *object)
314
ThunarThumbnailerIdle *idle;
315
ThunarThumbnailer *thumbnailer = THUNAR_THUMBNAILER (object);
319
/* acquire the thumbnailer lock */
320
g_mutex_lock (thumbnailer->lock);
322
/* clear the request queue */
323
g_list_foreach (thumbnailer->wait_queue, (GFunc) thunar_thumbnailer_item_free, NULL);
324
g_list_free (thumbnailer->wait_queue);
326
/* remove the request queue processing idle handler */
327
if (thumbnailer->wait_queue_idle_id > 0)
328
g_source_remove (thumbnailer->wait_queue_idle_id);
330
/* abort all pending idle functions */
331
for (lp = thumbnailer->idles; lp != NULL; lp = lp->next)
334
g_source_remove (idle->id);
337
/* free the idle list */
338
g_list_free (thumbnailer->idles);
340
if (thumbnailer->thumbnailer_proxy != NULL)
342
/* cancel all pending D-Bus calls */
343
list = g_hash_table_get_values (thumbnailer->request_call_mapping);
344
for (lp = list; lp != NULL; lp = lp->next)
345
dbus_g_proxy_cancel_call (thumbnailer->thumbnailer_proxy, lp->data);
348
g_hash_table_unref (thumbnailer->request_call_mapping);
351
/* unqueue all pending requests */
352
list = g_hash_table_get_keys (thumbnailer->handle_request_mapping);
353
for (lp = list; lp != NULL; lp = lp->next)
354
thunar_thumbnailer_unqueue_internal (thumbnailer, GPOINTER_TO_UINT (lp->data));
358
g_hash_table_unref (thumbnailer->handle_request_mapping);
359
g_hash_table_unref (thumbnailer->request_handle_mapping);
360
g_hash_table_unref (thumbnailer->request_uris_mapping);
362
/* disconnect from the thumbnailer proxy */
363
g_signal_handlers_disconnect_matched (thumbnailer->thumbnailer_proxy,
364
G_SIGNAL_MATCH_DATA, 0, 0,
365
NULL, NULL, thumbnailer);
367
/* release the thumbnailer proxy */
368
g_object_unref (thumbnailer->thumbnailer_proxy);
371
/* free the cached URI schemes and MIME types array */
372
g_strfreev (thumbnailer->supported_schemes);
373
g_strfreev (thumbnailer->supported_types);
375
/* release the thumbnailer lock */
376
g_mutex_unlock (thumbnailer->lock);
378
/* release the mutex */
379
g_mutex_free (thumbnailer->lock);
382
(*G_OBJECT_CLASS (thunar_thumbnailer_parent_class)->finalize) (object);
389
thunar_thumbnailer_init_thumbnailer_proxy (ThunarThumbnailer *thumbnailer,
390
DBusGConnection *connection)
392
/* we can't have a proxy without a D-Bus connection */
393
if (connection == NULL)
395
thumbnailer->thumbnailer_proxy = NULL;
399
/* create the thumbnailer proxy shared by all ThunarThumbnailers on demand */
400
if (thunar_thumbnailer_proxy == NULL)
402
/* create the shared thumbnailer proxy */
403
thunar_thumbnailer_proxy =
404
dbus_g_proxy_new_for_name (connection,
405
"org.freedesktop.thumbnails.Thumbnailer1",
406
"/org/freedesktop/thumbnails/Thumbnailer1",
407
"org.freedesktop.thumbnails.Thumbnailer1");
409
/* make sure to set it to NULL when the last reference is dropped */
410
g_object_add_weak_pointer (G_OBJECT (thunar_thumbnailer_proxy),
411
(gpointer) &thunar_thumbnailer_proxy);
413
thumbnailer->thumbnailer_proxy = thunar_thumbnailer_proxy;
415
/* TODO this should actually be VOID:UINT,BOXED,INT,STRING */
416
dbus_g_object_register_marshaller (_thunar_marshal_VOID__UINT_BOXED_UINT_STRING,
424
dbus_g_object_register_marshaller ((GClosureMarshal) _thunar_marshal_VOID__UINT_BOXED,
430
dbus_g_proxy_add_signal (thumbnailer->thumbnailer_proxy, "Error",
431
G_TYPE_UINT, G_TYPE_STRV, G_TYPE_UINT, G_TYPE_STRING,
433
dbus_g_proxy_add_signal (thumbnailer->thumbnailer_proxy, "Finished",
434
G_TYPE_UINT, G_TYPE_INVALID);
435
dbus_g_proxy_add_signal (thumbnailer->thumbnailer_proxy, "Ready",
436
G_TYPE_UINT, G_TYPE_STRV, G_TYPE_INVALID);
437
dbus_g_proxy_add_signal (thumbnailer->thumbnailer_proxy, "Started",
438
G_TYPE_UINT, G_TYPE_INVALID);
442
thumbnailer->thumbnailer_proxy = g_object_ref (thunar_thumbnailer_proxy);
445
dbus_g_proxy_connect_signal (thumbnailer->thumbnailer_proxy, "Error",
446
G_CALLBACK (thunar_thumbnailer_thumbnailer_error),
448
dbus_g_proxy_connect_signal (thumbnailer->thumbnailer_proxy, "Finished",
449
G_CALLBACK (thunar_thumbnailer_thumbnailer_finished),
451
dbus_g_proxy_connect_signal (thumbnailer->thumbnailer_proxy, "Ready",
452
G_CALLBACK (thunar_thumbnailer_thumbnailer_ready),
454
dbus_g_proxy_connect_signal (thumbnailer->thumbnailer_proxy, "Started",
455
G_CALLBACK (thunar_thumbnailer_thumbnailer_started),
462
thunar_thumbnailer_file_is_supported (ThunarThumbnailer *thumbnailer,
465
const gchar *content_type;
466
gboolean supported = FALSE;
469
_thunar_return_val_if_fail (THUNAR_IS_THUMBNAILER (thumbnailer), FALSE);
470
_thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
472
/* acquire the thumbnailer lock */
473
g_mutex_lock (thumbnailer->lock);
475
/* no types are supported if we don't have a thumbnailer */
476
if (thumbnailer->thumbnailer_proxy == NULL)
478
/* release the thumbnailer lock */
479
g_mutex_unlock (thumbnailer->lock);
483
/* request the supported types on demand */
484
if (thumbnailer->supported_schemes == NULL
485
|| thumbnailer->supported_types == NULL)
487
/* request the supported types from the thumbnailer D-Bus service. We only do
488
* this once, so using a non-async call should be ok */
489
thunar_thumbnailer_proxy_get_supported (thumbnailer->thumbnailer_proxy,
490
&thumbnailer->supported_schemes,
491
&thumbnailer->supported_types,
495
/* check if we have supported URI schemes and MIME types now */
496
if (thumbnailer->supported_schemes != NULL
497
&& thumbnailer->supported_types != NULL)
499
/* determine the content type of the passed file */
500
content_type = thunar_file_get_content_type (file);
502
/* go through all the URI schemes we support */
503
for (n = 0; !supported && thumbnailer->supported_schemes[n] != NULL; ++n)
505
/* check if the file has the current URI scheme */
506
if (thunar_file_has_uri_scheme (file, thumbnailer->supported_schemes[n]))
508
/* check if the type of the file is a subtype of the supported type */
509
if (g_content_type_is_a (content_type, thumbnailer->supported_types[n]))
515
/* release the thumbnailer lock */
516
g_mutex_unlock (thumbnailer->lock);
524
thunar_thumbnailer_thumbnailer_error (DBusGProxy *proxy,
528
const gchar *message,
529
ThunarThumbnailer *thumbnailer)
531
ThunarThumbnailerIdle *idle;
534
_thunar_return_if_fail (DBUS_IS_G_PROXY (proxy));
535
_thunar_return_if_fail (THUNAR_IS_THUMBNAILER (thumbnailer));
537
/* look up the request ID for this D-Bus service handle */
538
request = g_hash_table_lookup (thumbnailer->handle_request_mapping,
539
GUINT_TO_POINTER (handle));
541
/* check if we have a request for this handle */
544
/* allocate a new idle struct */
545
idle = g_slice_new0 (ThunarThumbnailerIdle);
546
idle->type = THUNAR_THUMBNAILER_IDLE_ERROR;
547
idle->thumbnailer = g_object_ref (thumbnailer);
549
/* copy the URIs because we need them in the idle function */
550
idle->data.uris = g_strdupv ((gchar **)uris);
552
/* remember the idle struct because we might have to remove it in finalize() */
553
thumbnailer->idles = g_list_prepend (thumbnailer->idles, idle);
555
/* call the error idle function when we have the time */
556
idle->id = g_idle_add_full (G_PRIORITY_LOW,
557
thunar_thumbnailer_error_idle, idle,
558
thunar_thumbnailer_idle_free);
565
thunar_thumbnailer_thumbnailer_finished (DBusGProxy *proxy,
567
ThunarThumbnailer *thumbnailer)
571
_thunar_return_if_fail (DBUS_IS_G_PROXY (proxy));
572
_thunar_return_if_fail (THUNAR_IS_THUMBNAILER (thumbnailer));
574
/* look up the request ID for this D-Bus service handle */
575
request = g_hash_table_lookup (thumbnailer->handle_request_mapping,
576
GUINT_TO_POINTER (handle));
578
/* check if we have a request for this handle */
581
/* the request is finished, drop all the information about it */
582
g_hash_table_remove (thumbnailer->handle_request_mapping, request);
583
g_hash_table_remove (thumbnailer->request_handle_mapping, request);
584
g_hash_table_remove (thumbnailer->request_uris_mapping, request);
591
thunar_thumbnailer_thumbnailer_ready (DBusGProxy *proxy,
594
ThunarThumbnailer *thumbnailer)
596
ThunarThumbnailerIdle *idle;
598
_thunar_return_if_fail (DBUS_IS_G_PROXY (proxy));
599
_thunar_return_if_fail (THUNAR_IS_THUMBNAILER (thumbnailer));
601
/* check if we have any ready URIs */
604
/* allocate a new idle struct */
605
idle = g_slice_new0 (ThunarThumbnailerIdle);
606
idle->type = THUNAR_THUMBNAILER_IDLE_READY;
607
idle->thumbnailer = g_object_ref (thumbnailer);
609
/* copy the URI array because we need it in the idle function */
610
idle->data.uris = g_strdupv ((gchar **)uris);
612
/* remember the idle struct because we might have to remove it in finalize() */
613
thumbnailer->idles = g_list_prepend (thumbnailer->idles, idle);
615
/* call the ready idle function when we have the time */
616
idle->id = g_idle_add_full (G_PRIORITY_LOW,
617
thunar_thumbnailer_ready_idle, idle,
618
thunar_thumbnailer_idle_free);
625
thunar_thumbnailer_thumbnailer_started (DBusGProxy *proxy,
627
ThunarThumbnailer *thumbnailer)
629
ThunarThumbnailerIdle *idle;
632
_thunar_return_if_fail (DBUS_IS_G_PROXY (proxy));
633
_thunar_return_if_fail (THUNAR_IS_THUMBNAILER (thumbnailer));
635
/* look up the request for this D-Bus service handle */
636
request = g_hash_table_lookup (thumbnailer->handle_request_mapping,
637
GUINT_TO_POINTER (handle));
639
/* check if we have a request for this handle */
642
/* allocate a new idle struct */
643
idle = g_slice_new0 (ThunarThumbnailerIdle);
644
idle->type = THUNAR_THUMBNAILER_IDLE_STARTED;
645
idle->thumbnailer = g_object_ref (thumbnailer);
647
/* remember the request because we need it in the idle function */
648
idle->data.request = request;
650
/* remember the idle struct because we might have to remove it in finalize() */
651
thumbnailer->idles = g_list_prepend (thumbnailer->idles, idle);
653
/* call the started idle function when we have the time */
654
idle->id = g_idle_add_full (G_PRIORITY_LOW,
655
thunar_thumbnailer_started_idle, idle,
656
thunar_thumbnailer_idle_free);
663
thunar_thumbnailer_queue_async_reply (DBusGProxy *proxy,
668
ThunarThumbnailerCall *call = user_data;
669
ThunarThumbnailer *thumbnailer = THUNAR_THUMBNAILER (call->thumbnailer);
674
_thunar_return_if_fail (DBUS_IS_G_PROXY (proxy));
675
_thunar_return_if_fail (call != NULL);
676
_thunar_return_if_fail (THUNAR_IS_THUMBNAILER (thumbnailer));
678
g_mutex_lock (thumbnailer->lock);
683
/* get the URIs array for this request */
684
uris = g_hash_table_lookup (thumbnailer->request_uris_mapping, call->request);
686
/* the array should always exist, otherwise there's a bug in the program */
687
_thunar_assert (uris != NULL);
690
/* the request is "finished", forget about its URIs */
691
g_hash_table_remove (thumbnailer->request_uris_mapping, call->request);
695
/* remember that this request and D-Bus handle belong together */
696
g_hash_table_insert (thumbnailer->request_handle_mapping,
697
call->request, GUINT_TO_POINTER (handle));
698
g_hash_table_insert (thumbnailer->handle_request_mapping,
699
GUINT_TO_POINTER (handle), call->request);
702
/* the queue call is finished, we can forget about its proxy call */
703
g_hash_table_remove (thumbnailer->request_call_mapping, call->request);
705
thunar_thumbnailer_call_free (call);
707
g_mutex_unlock (thumbnailer->lock);
713
thunar_thumbnailer_queue_async (ThunarThumbnailer *thumbnailer,
715
const gchar **mime_hints)
717
ThunarThumbnailerCall *thumbnailer_call;
718
DBusGProxyCall *call;
722
_thunar_return_val_if_fail (THUNAR_IS_THUMBNAILER (thumbnailer), 0);
723
_thunar_return_val_if_fail (uris != NULL, 0);
724
_thunar_return_val_if_fail (mime_hints != NULL, 0);
725
_thunar_return_val_if_fail (DBUS_IS_G_PROXY (thumbnailer->thumbnailer_proxy), 0);
727
/* compute the next request ID, making sure it's never 0 */
728
request_no = GPOINTER_TO_UINT (thumbnailer->last_request) + 1;
729
request_no = MAX (request_no, 1);
731
/* remember the ID for the next request */
732
thumbnailer->last_request = GUINT_TO_POINTER (request_no);
734
/* use the new request ID for this request */
735
request = thumbnailer->last_request;
737
/* allocate a new call struct for the async D-Bus call */
738
thumbnailer_call = g_slice_new0 (ThunarThumbnailerCall);
739
thumbnailer_call->request = request;
740
thumbnailer_call->thumbnailer = g_object_ref (thumbnailer);
742
/* remember the URIs for this request */
743
g_hash_table_insert (thumbnailer->request_uris_mapping, request, uris);
745
/* queue thumbnails for the given URIs asynchronously */
746
call = thunar_thumbnailer_proxy_queue_async (thumbnailer->thumbnailer_proxy,
747
(const gchar **)uris, mime_hints,
748
"normal", "foreground", 0,
749
thunar_thumbnailer_queue_async_reply,
752
/* remember to which request the call struct belongs */
753
g_hash_table_insert (thumbnailer->request_call_mapping, request, call);
755
/* return the request ID used for this request */
762
thunar_thumbnailer_error_idle (gpointer user_data)
764
ThunarThumbnailerIdle *idle = user_data;
769
_thunar_return_val_if_fail (idle != NULL, FALSE);
770
_thunar_return_val_if_fail (idle->type == THUNAR_THUMBNAILER_IDLE_ERROR, FALSE);
772
/* iterate over all failed URIs */
773
for (n = 0; idle->data.uris != NULL && idle->data.uris[n] != NULL; ++n)
775
/* look up the corresponding ThunarFile from the cache */
776
gfile = g_file_new_for_uri (idle->data.uris[n]);
777
file = thunar_file_cache_lookup (gfile);
778
g_object_unref (gfile);
780
/* check if we have a file for this URI in the cache */
783
/* set thumbnail state to none unless the thumbnail has already been created.
784
* This is to prevent race conditions with the other idle functions */
785
if (thunar_file_get_thumb_state (file) != THUNAR_FILE_THUMB_STATE_READY)
786
thunar_file_set_thumb_state (file, THUNAR_FILE_THUMB_STATE_NONE);
790
/* remove the idle struct */
791
g_mutex_lock (idle->thumbnailer->lock);
792
idle->thumbnailer->idles = g_list_remove (idle->thumbnailer->idles, idle);
793
g_mutex_unlock (idle->thumbnailer->lock);
795
/* remove the idle source, which also destroys the idle struct */
802
thunar_thumbnailer_ready_idle (gpointer user_data)
804
ThunarThumbnailerIdle *idle = user_data;
809
_thunar_return_val_if_fail (idle != NULL, FALSE);
810
_thunar_return_val_if_fail (idle->type == THUNAR_THUMBNAILER_IDLE_READY, FALSE);
812
/* iterate over all failed URIs */
813
for (n = 0; idle->data.uris != NULL && idle->data.uris[n] != NULL; ++n)
815
/* look up the corresponding ThunarFile from the cache */
816
gfile = g_file_new_for_uri (idle->data.uris[n]);
817
file = thunar_file_cache_lookup (gfile);
818
g_object_unref (gfile);
820
/* check if we have a file for this URI in the cache */
823
/* set thumbnail state to ready - we now have a thumbnail */
824
thunar_file_set_thumb_state (file, THUNAR_FILE_THUMB_STATE_READY);
828
/* remove the idle struct */
829
g_mutex_lock (idle->thumbnailer->lock);
830
idle->thumbnailer->idles = g_list_remove (idle->thumbnailer->idles, idle);
831
g_mutex_unlock (idle->thumbnailer->lock);
833
/* remove the idle source, which also destroys the idle struct */
840
thunar_thumbnailer_started_idle (gpointer user_data)
842
ThunarThumbnailerIdle *idle = user_data;
848
_thunar_return_val_if_fail (idle != NULL, FALSE);
849
_thunar_return_val_if_fail (idle->type == THUNAR_THUMBNAILER_IDLE_STARTED, FALSE);
851
g_mutex_lock (idle->thumbnailer->lock);
853
/* look up the URIs that belong to this request */
854
uris = g_hash_table_lookup (idle->thumbnailer->request_uris_mapping,
857
/* iterate over all URIs if there are any */
858
for (n = 0; uris != NULL && uris[n] != NULL; ++n)
860
/* look up the corresponding ThunarFile from the cache */
861
gfile = g_file_new_for_uri (uris[n]);
862
file = thunar_file_cache_lookup (gfile);
863
g_object_unref (gfile);
865
/* check if we have a file in the cache */
868
/* set the thumbnail state to loading unless we already have a thumbnail.
869
* This is to prevent race conditions with the other idle functions */
870
if (thunar_file_get_thumb_state (file) != THUNAR_FILE_THUMB_STATE_READY)
871
thunar_file_set_thumb_state (file, THUNAR_FILE_THUMB_STATE_LOADING);
876
/* remove the idle struct */
877
idle->thumbnailer->idles = g_list_remove (idle->thumbnailer->idles, idle);
879
g_mutex_unlock (idle->thumbnailer->lock);
881
/* remove the idle source, which also destroys the idle struct */
888
thunar_thumbnailer_file_is_in_wait_queue (ThunarThumbnailer *thumbnailer,
891
ThunarThumbnailerItem *item;
892
gboolean in_wait_queue = FALSE;
895
g_mutex_lock (thumbnailer->lock);
897
for (lp = thumbnailer->wait_queue; !in_wait_queue && lp != NULL; lp = lp->next)
901
if (g_file_equal (item->file, thunar_file_get_file (file)))
902
in_wait_queue = TRUE;
905
g_mutex_unlock (thumbnailer->lock);
907
return in_wait_queue;
913
thunar_thumbnailer_process_wait_queue (ThunarThumbnailer *thumbnailer)
915
ThunarThumbnailerItem *item;
923
_thunar_return_val_if_fail (THUNAR_IS_THUMBNAILER (thumbnailer), FALSE);
925
g_mutex_lock (thumbnailer->lock);
927
/* determine how many URIs are in the wait queue */
928
n_items = g_list_length (thumbnailer->wait_queue);
930
/* allocate arrays for URIs and mime hints */
931
uris = g_new0 (gchar *, n_items + 1);
932
mime_hints = g_new0 (gchar *, n_items + 1);
934
/* fill URI and MIME hint arrays with items from the wait queue */
935
for (lp = g_list_last (thumbnailer->wait_queue), n = 0; lp != NULL; lp = lp->prev, ++n)
937
/* fetch the next item from the queue */
940
/* save URI and MIME hint in the arrays */
941
uris[n] = g_file_get_uri (item->file);
942
mime_hints[n] = item->mime_hint;
944
/* destroy the GFile and the queue item. The MIME hints are free'd later */
945
g_object_unref (item->file);
946
g_slice_free (ThunarThumbnailerItem, item);
949
/* NULL-terminate both arrays */
951
mime_hints[n] = NULL;
953
/* queue a thumbnail request for the URIs from the wait queue */
954
request = thunar_thumbnailer_queue_async (thumbnailer, uris,
955
(const gchar **)mime_hints);
957
/* free mime hints array */
958
g_strfreev (mime_hints);
960
/* clear the wait queue */
961
g_list_free (thumbnailer->wait_queue);
962
thumbnailer->wait_queue = NULL;
964
/* reset the wait queue idle ID */
965
thumbnailer->wait_queue_idle_id = 0;
967
g_mutex_unlock (thumbnailer->lock);
975
thunar_thumbnailer_file_is_queued (ThunarThumbnailer *thumbnailer,
978
gboolean is_queued = FALSE;
985
/* get a list with all URI arrays of already queued requests */
986
values = g_hash_table_get_values (thumbnailer->request_uris_mapping);
988
/* if we have none, the file cannot be queued ... or can it? ;) */
992
/* determine the URI for this file */
993
uri = thunar_file_dup_uri (file);
995
/* iterate over all URI arrays */
996
for (lp = values; !is_queued && lp != NULL; lp = lp->next)
1000
/* check if the file is included in the URI array of the current request */
1001
for (n = 0; !is_queued && uris != NULL && uris[n] != NULL; ++n)
1002
if (g_utf8_collate (uri, uris[n]) == 0)
1006
/* free the file URI */
1009
/* free the URI array list */
1010
g_list_free (values);
1018
thunar_thumbnailer_file_is_ready (ThunarThumbnailer *thumbnailer,
1021
ThunarThumbnailerIdle *idle;
1022
gboolean is_ready = FALSE;
1027
/* determine the URI or this file */
1028
uri = thunar_file_dup_uri (file);
1030
/* iterate over all idle structs */
1031
for (lp = thumbnailer->idles; !is_ready && lp != NULL; lp = lp->next)
1033
/* skip invalid idles */
1034
if (lp->data != NULL)
1039
/* skip non-ready idles and idles without any URIs */
1040
if (idle->type != THUNAR_THUMBNAILER_IDLE_READY || idle->data.uris == NULL)
1043
/* check if the file is included in this ready idle */
1044
for (n = 0; !is_ready && idle->data.uris[n] != NULL; ++n)
1045
if (g_utf8_collate (uri, idle->data.uris[n]) == 0)
1049
/* free the file URI */
1058
thunar_thumbnailer_call_free (ThunarThumbnailerCall *call)
1060
_thunar_return_if_fail (call != NULL);
1062
/* drop the thumbnailer reference */
1063
g_object_unref (call->thumbnailer);
1065
/* free the struct */
1066
g_slice_free (ThunarThumbnailerCall, call);
1072
thunar_thumbnailer_idle_free (gpointer data)
1074
ThunarThumbnailerIdle *idle = data;
1076
_thunar_return_if_fail (idle != NULL);
1078
/* free the URI array if necessary */
1079
if (idle->type == THUNAR_THUMBNAILER_IDLE_READY
1080
|| idle->type == THUNAR_THUMBNAILER_IDLE_ERROR)
1082
g_strfreev (idle->data.uris);
1085
/* drop the thumbnailer reference */
1086
g_object_unref (idle->thumbnailer);
1088
/* free the struct */
1089
g_slice_free (ThunarThumbnailerIdle, idle);
1094
static ThunarThumbnailerItem *
1095
thunar_thumbnailer_item_new (GFile *file,
1096
const gchar *mime_hint)
1098
ThunarThumbnailerItem *item;
1100
_thunar_return_val_if_fail (G_IS_FILE (file), NULL);
1101
_thunar_return_val_if_fail (mime_hint != NULL && mime_hint != '\0', NULL);
1103
item = g_slice_new0 (ThunarThumbnailerItem);
1104
item->file = g_object_ref (file);
1105
item->mime_hint = g_strdup (mime_hint);
1112
thunar_thumbnailer_item_free (gpointer data)
1114
ThunarThumbnailerItem *item = data;
1116
g_object_unref (item->file);
1117
g_free (item->mime_hint);
1118
g_slice_free (ThunarThumbnailerItem, item);
1120
#endif /* HAVE_DBUS */
1125
* thunar_thumbnailer_new:
1127
* Allocates a new #ThunarThumbnailer object, which can be used to
1128
* generate and store thumbnails for files.
1130
* The caller is responsible to free the returned
1131
* object using g_object_unref() when no longer needed.
1133
* Return value: a newly allocated #ThunarThumbnailer.
1136
thunar_thumbnailer_new (void)
1138
return g_object_new (THUNAR_TYPE_THUMBNAILER, NULL);
1144
thunar_thumbnailer_queue_file (ThunarThumbnailer *thumbnailer,
1149
_thunar_return_val_if_fail (THUNAR_IS_THUMBNAILER (thumbnailer), FALSE);
1150
_thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
1152
/* fake a file list */
1157
/* queue a thumbnail request for the file */
1158
return thunar_thumbnailer_queue_files (thumbnailer, &files);
1164
thunar_thumbnailer_queue_files (ThunarThumbnailer *thumbnailer,
1168
ThunarThumbnailerItem *item;
1170
gboolean success = FALSE;
1173
GList *supported_files = NULL;
1176
_thunar_return_val_if_fail (THUNAR_IS_THUMBNAILER (thumbnailer), FALSE);
1177
_thunar_return_val_if_fail (files != NULL, FALSE);
1180
/* acquire the thumbnailer lock */
1181
g_mutex_lock (thumbnailer->lock);
1183
if (thumbnailer->thumbnailer_proxy == NULL)
1185
/* release the lock and abort */
1186
g_mutex_unlock (thumbnailer->lock);
1190
/* release the thumbnailer lock */
1191
g_mutex_unlock (thumbnailer->lock);
1193
/* collect all supported files from the list that are neither in the
1194
* about to be queued (wait queue), nor already queued, nor already
1195
* processed (and awaiting to be refreshed) */
1196
for (lp = g_list_last (files); lp != NULL; lp = lp->prev)
1197
if (thunar_thumbnailer_file_is_supported (thumbnailer, lp->data))
1199
if (!thunar_thumbnailer_file_is_in_wait_queue (thumbnailer, lp->data)
1200
&& !thunar_thumbnailer_file_is_queued (thumbnailer, lp->data)
1201
&& !thunar_thumbnailer_file_is_ready (thumbnailer, lp->data))
1203
supported_files = g_list_prepend (supported_files, lp->data);
1207
/* check if we have any supported files */
1208
if (supported_files != NULL)
1210
for (lp = supported_files; lp != NULL; lp = lp->next)
1212
g_mutex_lock (thumbnailer->lock);
1214
/* allocate a thumbnailer item for the wait queue */
1215
item = thunar_thumbnailer_item_new (thunar_file_get_file (lp->data),
1216
thunar_file_get_content_type (lp->data));
1218
/* add the item to the wait queue */
1219
thumbnailer->wait_queue = g_list_prepend (thumbnailer->wait_queue, item);
1221
g_mutex_unlock (thumbnailer->lock);
1224
g_mutex_lock (thumbnailer->lock);
1226
if (thumbnailer->wait_queue_idle_id == 0)
1228
thumbnailer->wait_queue_idle_id =
1229
g_timeout_add (100, (GSourceFunc) thunar_thumbnailer_process_wait_queue,
1233
g_mutex_unlock (thumbnailer->lock);
1235
/* free the list of supported files */
1236
g_list_free (supported_files);
1238
/* we assume success if we've come so far */
1241
#endif /* HAVE_DBUS */
1249
thunar_thumbnailer_unqueue (ThunarThumbnailer *thumbnailer,
1256
_thunar_return_if_fail (THUNAR_IS_THUMBNAILER (thumbnailer));
1259
/* acquire the thumbnailer lock */
1260
g_mutex_lock (thumbnailer->lock);
1262
if (thumbnailer->thumbnailer_proxy != NULL)
1264
handle = g_hash_table_lookup (thumbnailer->request_handle_mapping, request);
1266
thunar_thumbnailer_proxy_unqueue (thumbnailer->thumbnailer_proxy,
1267
GPOINTER_TO_UINT (handle), NULL);
1269
g_hash_table_remove (thumbnailer->handle_request_mapping, handle);
1270
g_hash_table_remove (thumbnailer->request_handle_mapping, request);
1271
g_hash_table_remove (thumbnailer->request_uris_mapping, request);
1274
/* release the thumbnailer lock */
1275
g_mutex_unlock (thumbnailer->lock);