~noskcaj/ubuntu/trusty/tumbler/0.1.30

« back to all changes in this revision

Viewing changes to tumblerd/tumbler-service.c

  • Committer: Bazaar Package Importer
  • Author(s): Lionel Le Folgoc
  • Date: 2010-11-07 16:34:58 UTC
  • Revision ID: james.westby@ubuntu.com-20101107163458-skwfq34vnuavipne
Tags: upstream-0.1.4
ImportĀ upstreamĀ versionĀ 0.1.4

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* vi:set et ai sw=2 sts=2 ts=2: */
 
2
/*-
 
3
 * Copyright (c) 2009 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 <glib.h>
 
26
#include <glib/gi18n.h>
 
27
#include <glib-object.h>
 
28
 
 
29
#include <gio/gio.h>
 
30
 
 
31
#include <dbus/dbus.h>
 
32
#include <dbus/dbus-glib.h>
 
33
#include <dbus/dbus-glib-lowlevel.h>
 
34
 
 
35
#include <tumbler/tumbler.h>
 
36
 
 
37
#include <tumblerd/tumbler-scheduler.h>
 
38
#include <tumblerd/tumbler-service.h>
 
39
#include <tumblerd/tumbler-service-dbus-bindings.h>
 
40
#include <tumblerd/tumbler-lifo-scheduler.h>
 
41
#include <tumblerd/tumbler-group-scheduler.h>
 
42
#include <tumblerd/tumbler-utils.h>
 
43
 
 
44
 
 
45
 
 
46
#define THUMBNAILER_PATH    "/org/freedesktop/thumbnails/Thumbnailer1"
 
47
#define THUMBNAILER_SERVICE "org.freedesktop.thumbnails.Thumbnailer1"
 
48
#define THUMBNAILER_IFACE   "org.freedesktop.thumbnails.Thumbnailer1"
 
49
 
 
50
 
 
51
 
 
52
/* signal identifiers */
 
53
enum
 
54
{
 
55
  SIGNAL_ERROR,
 
56
  SIGNAL_FINISHED,
 
57
  SIGNAL_READY,
 
58
  SIGNAL_STARTED,
 
59
  LAST_SIGNAL,
 
60
};
 
61
 
 
62
/* property identifiers */
 
63
enum
 
64
{
 
65
  PROP_0,
 
66
  PROP_CONNECTION,
 
67
  PROP_REGISTRY,
 
68
};
 
69
 
 
70
 
 
71
 
 
72
typedef struct _SchedulerIdleInfo SchedulerIdleInfo;
 
73
 
 
74
 
 
75
 
 
76
static void tumbler_service_constructed        (GObject            *object);
 
77
static void tumbler_service_finalize           (GObject            *object);
 
78
static void tumbler_service_get_property       (GObject            *object,
 
79
                                                guint               prop_id,
 
80
                                                GValue             *value,
 
81
                                                GParamSpec         *pspec);
 
82
static void tumbler_service_set_property       (GObject            *object,
 
83
                                                guint               prop_id,
 
84
                                                const GValue       *value,
 
85
                                                GParamSpec         *pspec);
 
86
static void tumbler_service_scheduler_error    (TumblerScheduler   *scheduler,
 
87
                                                guint               handle,
 
88
                                                const gchar *const *failed_uris,
 
89
                                                gint                error_code,
 
90
                                                const gchar        *message,
 
91
                                                const gchar        *origin,
 
92
                                                TumblerService     *service);
 
93
static void tumbler_service_scheduler_finished (TumblerScheduler   *scheduler,
 
94
                                                guint               handle,
 
95
                                                const gchar        *origin,
 
96
                                                TumblerService     *service);
 
97
static void tumbler_service_scheduler_ready    (TumblerScheduler   *scheduler,
 
98
                                                guint               handle,
 
99
                                                const GStrv         uris,
 
100
                                                const gchar        *origin,
 
101
                                                TumblerService     *service);
 
102
static void tumbler_service_scheduler_started  (TumblerScheduler   *scheduler,
 
103
                                                guint               handle,
 
104
                                                const gchar        *origin,
 
105
                                                TumblerService     *service);
 
106
static void tumbler_service_pre_unmount        (TumblerService     *service,
 
107
                                                GMount             *mount,
 
108
                                                GVolumeMonitor     *monitor);
 
109
static void scheduler_idle_info_free           (SchedulerIdleInfo  *info);
 
110
 
 
111
 
 
112
 
 
113
struct _TumblerServiceClass
 
114
{
 
115
  GObjectClass __parent__;
 
116
};
 
117
 
 
118
struct _TumblerService
 
119
{
 
120
  GObject __parent__;
 
121
 
 
122
  DBusGConnection  *connection;
 
123
  TumblerRegistry  *registry;
 
124
  GMutex           *mutex;
 
125
  GList            *schedulers;
 
126
 
 
127
  GVolumeMonitor   *volume_monitor;
 
128
};
 
129
 
 
130
struct _SchedulerIdleInfo
 
131
{
 
132
  TumblerScheduler *scheduler;
 
133
  TumblerService   *service;
 
134
  GStrv             uris;
 
135
  gchar            *message;
 
136
  gchar            *origin;
 
137
  guint             handle;
 
138
  gint              error_code;
 
139
};
 
140
 
 
141
 
 
142
 
 
143
static guint tumbler_service_signals[LAST_SIGNAL];
 
144
 
 
145
 
 
146
 
 
147
G_DEFINE_TYPE (TumblerService, tumbler_service, G_TYPE_OBJECT);
 
148
 
 
149
 
 
150
 
 
151
static void
 
152
tumbler_service_class_init (TumblerServiceClass *klass)
 
153
{
 
154
  GObjectClass *gobject_class;
 
155
 
 
156
  gobject_class = G_OBJECT_CLASS (klass);
 
157
  gobject_class->constructed = tumbler_service_constructed; 
 
158
  gobject_class->finalize = tumbler_service_finalize; 
 
159
  gobject_class->get_property = tumbler_service_get_property;
 
160
  gobject_class->set_property = tumbler_service_set_property;
 
161
 
 
162
  g_object_class_install_property (gobject_class, PROP_CONNECTION,
 
163
                                   g_param_spec_pointer ("connection",
 
164
                                                         "connection",
 
165
                                                         "connection",
 
166
                                                         G_PARAM_READWRITE |
 
167
                                                         G_PARAM_CONSTRUCT_ONLY));
 
168
 
 
169
  g_object_class_install_property (gobject_class, PROP_REGISTRY,
 
170
                                   g_param_spec_object ("registry",
 
171
                                                        "registry",
 
172
                                                        "registry",
 
173
                                                        TUMBLER_TYPE_REGISTRY,
 
174
                                                        G_PARAM_READWRITE |
 
175
                                                        G_PARAM_CONSTRUCT_ONLY));
 
176
 
 
177
  tumbler_service_signals[SIGNAL_ERROR] =
 
178
    g_signal_new ("error",
 
179
                  TUMBLER_TYPE_SERVICE,
 
180
                  G_SIGNAL_RUN_LAST,
 
181
                  0,
 
182
                  NULL,
 
183
                  NULL,
 
184
                  tumbler_marshal_VOID__UINT_POINTER_INT_STRING,
 
185
                  G_TYPE_NONE,
 
186
                  4,
 
187
                  G_TYPE_UINT,
 
188
                  G_TYPE_STRV,
 
189
                  G_TYPE_INT,
 
190
                  G_TYPE_STRING);
 
191
 
 
192
  tumbler_service_signals[SIGNAL_FINISHED] =
 
193
    g_signal_new ("finished",
 
194
                  TUMBLER_TYPE_SERVICE,
 
195
                  G_SIGNAL_RUN_LAST,
 
196
                  0,
 
197
                  NULL,
 
198
                  NULL,
 
199
                  g_cclosure_marshal_VOID__UINT,
 
200
                  G_TYPE_NONE,
 
201
                  1,
 
202
                  G_TYPE_UINT);
 
203
 
 
204
  tumbler_service_signals[SIGNAL_READY] =
 
205
    g_signal_new ("ready",
 
206
                  TUMBLER_TYPE_SERVICE,
 
207
                  G_SIGNAL_RUN_LAST,
 
208
                  0,
 
209
                  NULL,
 
210
                  NULL,
 
211
                  g_cclosure_marshal_VOID__POINTER,
 
212
                  G_TYPE_NONE,
 
213
                  1,
 
214
                  G_TYPE_STRV);
 
215
 
 
216
  tumbler_service_signals[SIGNAL_STARTED] =
 
217
    g_signal_new ("started",
 
218
                  TUMBLER_TYPE_SERVICE,
 
219
                  G_SIGNAL_RUN_LAST,
 
220
                  0,
 
221
                  NULL,
 
222
                  NULL,
 
223
                  g_cclosure_marshal_VOID__UINT,
 
224
                  G_TYPE_NONE,
 
225
                  1,
 
226
                  G_TYPE_UINT);
 
227
}
 
228
 
 
229
 
 
230
 
 
231
static void
 
232
tumbler_service_init (TumblerService *service)
 
233
{
 
234
  service->mutex = g_mutex_new ();
 
235
  service->schedulers = NULL;
 
236
 
 
237
  service->volume_monitor = g_volume_monitor_get ();
 
238
  g_signal_connect_swapped (service->volume_monitor, "mount-pre-unmount", 
 
239
                            G_CALLBACK (tumbler_service_pre_unmount), service);
 
240
}
 
241
 
 
242
 
 
243
 
 
244
static void
 
245
tumbler_service_add_scheduler (TumblerService   *service, 
 
246
                               TumblerScheduler *scheduler)
 
247
{
 
248
  /* add the scheduler to the list */
 
249
  service->schedulers = g_list_append (service->schedulers, g_object_ref (scheduler));
 
250
 
 
251
  /* connect to the scheduler signals */
 
252
  g_signal_connect (scheduler, "error",
 
253
                    G_CALLBACK (tumbler_service_scheduler_error), service);
 
254
  g_signal_connect (scheduler, "finished", 
 
255
                    G_CALLBACK (tumbler_service_scheduler_finished), service);
 
256
  g_signal_connect (scheduler, "ready", 
 
257
                    G_CALLBACK (tumbler_service_scheduler_ready), service);
 
258
  g_signal_connect (scheduler, "started", 
 
259
                    G_CALLBACK (tumbler_service_scheduler_started), service);
 
260
}
 
261
 
 
262
 
 
263
 
 
264
static void
 
265
tumbler_service_remove_scheduler (TumblerScheduler *scheduler,
 
266
                                  TumblerService   *service)
 
267
{
 
268
  g_return_if_fail (TUMBLER_IS_SCHEDULER (scheduler));
 
269
  g_return_if_fail (TUMBLER_IS_SERVICE (service));
 
270
 
 
271
  g_signal_handlers_disconnect_matched (scheduler, G_SIGNAL_MATCH_DATA,
 
272
                                        0, 0, NULL, NULL, service);
 
273
  g_object_unref (scheduler);
 
274
}
 
275
 
 
276
 
 
277
 
 
278
static void
 
279
tumbler_service_constructed (GObject *object)
 
280
{
 
281
  TumblerService   *service = TUMBLER_SERVICE (object);
 
282
  TumblerScheduler *scheduler;
 
283
 
 
284
  /* chain up to parent classes */
 
285
  if (G_OBJECT_CLASS (tumbler_service_parent_class)->constructed != NULL)
 
286
    (G_OBJECT_CLASS (tumbler_service_parent_class)->constructed) (object);
 
287
 
 
288
  /* create the foreground scheduler */
 
289
  scheduler = tumbler_lifo_scheduler_new ("foreground");
 
290
  tumbler_service_add_scheduler (service, scheduler);
 
291
  g_object_unref (scheduler);
 
292
 
 
293
  /* create the background scheduler */
 
294
  scheduler = tumbler_group_scheduler_new ("background");
 
295
  tumbler_service_add_scheduler (service, scheduler);
 
296
  g_object_unref (scheduler);
 
297
}
 
298
 
 
299
 
 
300
 
 
301
static void
 
302
tumbler_service_finalize (GObject *object)
 
303
{
 
304
  TumblerService *service = TUMBLER_SERVICE (object);
 
305
 
 
306
  /* disconnect from the volume monitor */
 
307
  g_signal_handlers_disconnect_matched (service->volume_monitor, G_SIGNAL_MATCH_DATA,
 
308
                                        0, 0, NULL, NULL, service);
 
309
 
 
310
  /* release the volume monitor */
 
311
  g_object_unref (service->volume_monitor);
 
312
 
 
313
  /* release all schedulers and the scheduler list */
 
314
  g_list_foreach (service->schedulers, (GFunc) tumbler_service_remove_scheduler, service);
 
315
  g_list_free (service->schedulers);
 
316
 
 
317
  /* release the reference on the thumbnailer registry */
 
318
  g_object_unref (service->registry);
 
319
 
 
320
  dbus_g_connection_unref (service->connection);
 
321
 
 
322
  g_mutex_free (service->mutex);
 
323
 
 
324
  (*G_OBJECT_CLASS (tumbler_service_parent_class)->finalize) (object);
 
325
}
 
326
 
 
327
 
 
328
 
 
329
static void
 
330
tumbler_service_get_property (GObject    *object,
 
331
                              guint       prop_id,
 
332
                              GValue     *value,
 
333
                              GParamSpec *pspec)
 
334
{
 
335
  TumblerService *service = TUMBLER_SERVICE (object);
 
336
 
 
337
  switch (prop_id)
 
338
    {
 
339
    case PROP_CONNECTION:
 
340
      g_value_set_pointer (value, service->connection);
 
341
      break;
 
342
    case PROP_REGISTRY:
 
343
      g_value_set_object (value, service->registry);
 
344
      break;
 
345
    default:
 
346
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
347
      break;
 
348
    }
 
349
}
 
350
 
 
351
 
 
352
 
 
353
static void
 
354
tumbler_service_set_property (GObject      *object,
 
355
                              guint         prop_id,
 
356
                              const GValue *value,
 
357
                              GParamSpec   *pspec)
 
358
{
 
359
  TumblerService *service = TUMBLER_SERVICE (object);
 
360
 
 
361
  switch (prop_id)
 
362
    {
 
363
    case PROP_CONNECTION:
 
364
      service->connection = dbus_g_connection_ref (g_value_get_pointer (value));
 
365
      break;
 
366
    case PROP_REGISTRY:
 
367
      service->registry = g_value_dup_object (value);
 
368
      break;
 
369
    default:
 
370
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
371
      break;
 
372
    }
 
373
}
 
374
 
 
375
 
 
376
 
 
377
static gboolean
 
378
tumbler_service_error_idle (gpointer user_data)
 
379
{
 
380
  SchedulerIdleInfo *info = user_data;
 
381
  DBusMessageIter    iter;
 
382
  DBusMessageIter    strv_iter;
 
383
  DBusMessage       *message;
 
384
  guint              n;
 
385
 
 
386
  g_return_val_if_fail (info != NULL, FALSE);
 
387
  g_return_val_if_fail (TUMBLER_IS_SCHEDULER (info->scheduler), FALSE);
 
388
  g_return_val_if_fail (info->uris != NULL && info->uris[0] != NULL && *info->uris[0] != '\0', FALSE);
 
389
  g_return_val_if_fail (info->message != NULL && *info->message != '\0', FALSE);
 
390
  g_return_val_if_fail (info->origin != NULL && *info->origin != '\0', FALSE);
 
391
  g_return_val_if_fail (TUMBLER_IS_SERVICE (info->service), FALSE);
 
392
 
 
393
  /* create a D-Bus message for the error signal */
 
394
  message = dbus_message_new_signal (THUMBNAILER_PATH, THUMBNAILER_IFACE, "Error");
 
395
 
 
396
  /* define the destination (the thumbnailer client) if possible */
 
397
  if (info->origin)
 
398
    dbus_message_set_destination (message, info->origin);
 
399
 
 
400
  /* append the request handle */
 
401
  dbus_message_iter_init_append (message, &iter);
 
402
  dbus_message_iter_append_basic (&iter, DBUS_TYPE_UINT32, &info->handle);
 
403
 
 
404
  /* start the URI string array */
 
405
  dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, 
 
406
                                    DBUS_TYPE_STRING_AS_STRING, &strv_iter);
 
407
 
 
408
  /* insert all failed URIs into the array */
 
409
  for (n = 0; info->uris[n] != NULL; n++)
 
410
    dbus_message_iter_append_basic (&strv_iter, DBUS_TYPE_STRING, &info->uris[n]);
 
411
 
 
412
  /* finish the URI string array */
 
413
  dbus_message_iter_close_container (&iter, &strv_iter);
 
414
 
 
415
  /* append the error code and error message */
 
416
  dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &info->error_code);
 
417
  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &info->message);
 
418
 
 
419
  /* send the signal message over D-Bus */
 
420
  dbus_connection_send (dbus_g_connection_get_connection (info->service->connection), 
 
421
                        message, NULL);
 
422
 
 
423
  /* free the allocated D-Bus message */
 
424
  dbus_message_unref (message);
 
425
 
 
426
  scheduler_idle_info_free (info);
 
427
 
 
428
  return FALSE;
 
429
}
 
430
 
 
431
 
 
432
 
 
433
static void
 
434
tumbler_service_scheduler_error (TumblerScheduler   *scheduler,
 
435
                                 guint               handle,
 
436
                                 const gchar *const *failed_uris,
 
437
                                 gint                error_code,
 
438
                                 const gchar        *message,
 
439
                                 const gchar        *origin,
 
440
                                 TumblerService     *service)
 
441
{
 
442
  SchedulerIdleInfo *info;
 
443
 
 
444
  g_return_if_fail (TUMBLER_IS_SCHEDULER (scheduler));
 
445
  g_return_if_fail (failed_uris != NULL);
 
446
  g_return_if_fail (message != NULL && *message != '\0');
 
447
  g_return_if_fail (origin != NULL && *origin != '\0');
 
448
  g_return_if_fail (TUMBLER_IS_SERVICE (service));
 
449
  
 
450
  info = g_slice_new0 (SchedulerIdleInfo);
 
451
 
 
452
  info->scheduler = g_object_ref (scheduler);
 
453
  info->handle = handle;
 
454
  info->uris = g_strdupv ((GStrv) failed_uris);
 
455
  info->error_code = error_code;
 
456
  info->message = g_strdup (message);
 
457
  info->origin = g_strdup (origin);
 
458
  info->service = g_object_ref (service);
 
459
 
 
460
  g_idle_add (tumbler_service_error_idle, info);
 
461
}
 
462
 
 
463
 
 
464
 
 
465
static gboolean
 
466
tumbler_service_finished_idle (gpointer user_data)
 
467
{
 
468
  SchedulerIdleInfo *info = user_data;
 
469
  DBusMessageIter    iter;
 
470
  DBusMessage       *message;
 
471
 
 
472
  g_return_val_if_fail (info != NULL, FALSE);
 
473
  g_return_val_if_fail (info->origin != NULL && *info->origin != '\0', FALSE);
 
474
  g_return_val_if_fail (TUMBLER_IS_SERVICE (info->service), FALSE);
 
475
 
 
476
  /* create a D-Bus message for the finished signal */
 
477
  message = dbus_message_new_signal (THUMBNAILER_PATH, THUMBNAILER_IFACE, "Finished");
 
478
 
 
479
  /* define the destination (the thumbnailer client) if possible */
 
480
  if (info->origin)
 
481
    dbus_message_set_destination (message, info->origin);
 
482
 
 
483
  /* append the request handle */
 
484
  dbus_message_iter_init_append (message, &iter);
 
485
  dbus_message_iter_append_basic (&iter, DBUS_TYPE_UINT32, &info->handle);
 
486
 
 
487
  /* send the signal message over D-Bus */
 
488
  dbus_connection_send (dbus_g_connection_get_connection (info->service->connection), 
 
489
                        message, NULL);
 
490
 
 
491
  /* free the allocated D-Bus message */
 
492
  dbus_message_unref (message);
 
493
 
 
494
  scheduler_idle_info_free (info);
 
495
 
 
496
  return FALSE;
 
497
}
 
498
 
 
499
 
 
500
 
 
501
static void
 
502
tumbler_service_scheduler_finished (TumblerScheduler *scheduler,
 
503
                                    guint             handle,
 
504
                                    const gchar      *origin,
 
505
                                    TumblerService   *service)
 
506
{
 
507
  SchedulerIdleInfo *info;
 
508
 
 
509
  g_return_if_fail (TUMBLER_IS_SCHEDULER (scheduler));
 
510
  g_return_if_fail (origin != NULL && *origin != '\0');
 
511
  g_return_if_fail (TUMBLER_IS_SERVICE (service));
 
512
  
 
513
  info = g_slice_new0 (SchedulerIdleInfo);
 
514
 
 
515
  info->scheduler = g_object_ref (scheduler);
 
516
  info->handle = handle;
 
517
  info->origin = g_strdup (origin);
 
518
  info->service = g_object_ref (service);
 
519
 
 
520
  g_idle_add (tumbler_service_finished_idle, info);
 
521
}
 
522
 
 
523
 
 
524
 
 
525
static gboolean 
 
526
tumbler_service_ready_idle (gpointer user_data)
 
527
{
 
528
  SchedulerIdleInfo *info = user_data;
 
529
  DBusMessageIter    iter;
 
530
  DBusMessageIter    strv_iter;
 
531
  DBusMessage       *message;
 
532
  guint              n;
 
533
 
 
534
  g_return_val_if_fail (info != NULL, FALSE);
 
535
  g_return_val_if_fail (TUMBLER_IS_SCHEDULER (info->scheduler), FALSE);
 
536
  g_return_val_if_fail (info->uris != NULL && info->uris[0] != NULL && *info->uris[0] != '\0', FALSE);
 
537
  g_return_val_if_fail (info->origin != NULL && *info->origin != '\0', FALSE);
 
538
  g_return_val_if_fail (TUMBLER_IS_SERVICE (info->service), FALSE);
 
539
 
 
540
  /* create a D-Bus message for the ready signal */
 
541
  message = dbus_message_new_signal (THUMBNAILER_PATH, THUMBNAILER_IFACE, "Ready");
 
542
 
 
543
  /* define the destination (the thumbnailer client) if possible */
 
544
  if (info->origin)
 
545
    dbus_message_set_destination (message, info->origin);
 
546
 
 
547
  dbus_message_iter_init_append (message, &iter);
 
548
 
 
549
  /* append the request handle */
 
550
  dbus_message_iter_append_basic (&iter, DBUS_TYPE_UINT32, &info->handle);
 
551
 
 
552
  /* start the URI string array */
 
553
  dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY,
 
554
                                    DBUS_TYPE_STRING_AS_STRING, &strv_iter);
 
555
 
 
556
  /* insert all URIs into the array for which we have thumbnails now */
 
557
  for (n = 0; info->uris[n] != NULL; n++)
 
558
    dbus_message_iter_append_basic (&strv_iter, DBUS_TYPE_STRING, &info->uris[n]);
 
559
 
 
560
  /* finish the URI string array */
 
561
  dbus_message_iter_close_container (&iter, &strv_iter);
 
562
 
 
563
  /* send the signal message over D-Bus */
 
564
  dbus_connection_send (dbus_g_connection_get_connection (info->service->connection), 
 
565
                        message, NULL);
 
566
 
 
567
  /* free the allocated D-Bus message */
 
568
  dbus_message_unref (message);
 
569
 
 
570
  scheduler_idle_info_free (info);
 
571
 
 
572
  return FALSE;
 
573
}
 
574
 
 
575
 
 
576
 
 
577
static void
 
578
tumbler_service_scheduler_ready (TumblerScheduler *scheduler,
 
579
                                 guint             handle,
 
580
                                 const GStrv       uris,
 
581
                                 const gchar      *origin,
 
582
                                 TumblerService   *service)
 
583
{
 
584
  SchedulerIdleInfo *info;
 
585
  
 
586
  g_return_if_fail (TUMBLER_IS_SCHEDULER (scheduler));
 
587
  g_return_if_fail (origin != NULL && *origin != '\0');
 
588
  g_return_if_fail (uris != NULL && uris[0] != NULL && *uris[0] != '\0');
 
589
  g_return_if_fail (TUMBLER_IS_SERVICE (service));
 
590
  
 
591
  info = g_slice_new0 (SchedulerIdleInfo);
 
592
 
 
593
  info->scheduler = g_object_ref (scheduler);
 
594
  info->handle = handle;
 
595
  info->uris = g_strdupv ((GStrv) uris);
 
596
  info->origin = g_strdup (origin);
 
597
  info->service = g_object_ref (service);
 
598
 
 
599
  g_idle_add (tumbler_service_ready_idle, info);
 
600
}
 
601
 
 
602
 
 
603
 
 
604
static gboolean
 
605
tumbler_service_started_idle (gpointer user_data)
 
606
{
 
607
  SchedulerIdleInfo *info = user_data;
 
608
  DBusMessageIter   iter;
 
609
  DBusMessage      *message;
 
610
 
 
611
  g_return_val_if_fail (info != NULL, FALSE);
 
612
  g_return_val_if_fail (TUMBLER_IS_SCHEDULER (info->scheduler), FALSE);
 
613
  g_return_val_if_fail (info->origin != NULL && *info->origin != '\0', FALSE);
 
614
  g_return_val_if_fail (TUMBLER_IS_SERVICE (info->service), FALSE);
 
615
 
 
616
  /* create a D-Bus message for the started signal */
 
617
  message = dbus_message_new_signal (THUMBNAILER_PATH, THUMBNAILER_IFACE, "Started");
 
618
 
 
619
  /* define the destination (the thumbnailer client) if possible */
 
620
  if (info->origin)
 
621
    dbus_message_set_destination (message, info->origin);
 
622
 
 
623
  /* append the request handle */
 
624
  dbus_message_iter_init_append (message, &iter);
 
625
  dbus_message_iter_append_basic (&iter, DBUS_TYPE_UINT32, &info->handle);
 
626
 
 
627
  /* send the signal message over D-Bus */
 
628
  dbus_connection_send (dbus_g_connection_get_connection (info->service->connection), 
 
629
                        message, NULL);
 
630
 
 
631
  /* free the allocated D-Bus message */
 
632
  dbus_message_unref (message);
 
633
 
 
634
  scheduler_idle_info_free (info);
 
635
 
 
636
  return FALSE;
 
637
}
 
638
 
 
639
 
 
640
 
 
641
static void
 
642
tumbler_service_scheduler_started (TumblerScheduler *scheduler,
 
643
                                   guint             handle,
 
644
                                   const gchar      *origin,
 
645
                                   TumblerService   *service)
 
646
{
 
647
  SchedulerIdleInfo *info;
 
648
  
 
649
  g_return_if_fail (TUMBLER_IS_SCHEDULER (scheduler));
 
650
  g_return_if_fail (origin != NULL && *origin != '\0');
 
651
  g_return_if_fail (TUMBLER_IS_SERVICE (service));
 
652
 
 
653
  info = g_slice_new0 (SchedulerIdleInfo);
 
654
 
 
655
  info->scheduler = g_object_ref (scheduler);
 
656
  info->handle = handle;
 
657
  info->origin = g_strdup (origin);
 
658
  info->service = g_object_ref (service);
 
659
 
 
660
  g_idle_add (tumbler_service_started_idle, info);
 
661
}
 
662
 
 
663
 
 
664
 
 
665
static void
 
666
tumbler_service_pre_unmount (TumblerService *service,
 
667
                             GMount         *mount,
 
668
                             GVolumeMonitor *volume_monitor)
 
669
{
 
670
  GList *iter;
 
671
 
 
672
  g_return_if_fail (TUMBLER_IS_SERVICE (service));
 
673
  g_return_if_fail (G_IS_MOUNT (mount));
 
674
  g_return_if_fail (volume_monitor == service->volume_monitor);
 
675
 
 
676
  g_mutex_lock (service->mutex);
 
677
 
 
678
  /* iterate over all schedulers, cancelling URIs belonging to the mount */
 
679
  for (iter = service->schedulers; iter != NULL; iter = iter->next)
 
680
    tumbler_scheduler_cancel_by_mount (iter->data, mount);
 
681
 
 
682
  g_mutex_unlock (service->mutex);
 
683
}
 
684
 
 
685
 
 
686
 
 
687
static void
 
688
scheduler_idle_info_free (SchedulerIdleInfo *info)
 
689
{
 
690
  if (info == NULL)
 
691
    return;
 
692
 
 
693
  g_free (info->message);
 
694
  g_free (info->origin);
 
695
  g_strfreev (info->uris);
 
696
 
 
697
  g_object_unref (info->scheduler);
 
698
  g_object_unref (info->service);
 
699
 
 
700
  g_slice_free (SchedulerIdleInfo, info);
 
701
}
 
702
 
 
703
 
 
704
 
 
705
TumblerService *
 
706
tumbler_service_new (DBusGConnection *connection,
 
707
                     TumblerRegistry *registry)
 
708
{
 
709
  return g_object_new (TUMBLER_TYPE_SERVICE, "connection", connection, 
 
710
                       "registry", registry, NULL);
 
711
}
 
712
 
 
713
 
 
714
 
 
715
gboolean
 
716
tumbler_service_start (TumblerService *service,
 
717
                       GError        **error)
 
718
{
 
719
  DBusConnection *connection;
 
720
  DBusError       dbus_error;
 
721
  gint            result;
 
722
 
 
723
  g_return_val_if_fail (TUMBLER_IS_SERVICE (service), FALSE);
 
724
  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
725
 
 
726
  g_mutex_lock (service->mutex);
 
727
 
 
728
  dbus_error_init (&dbus_error);
 
729
 
 
730
  /* get the native D-Bus connection */
 
731
  connection = dbus_g_connection_get_connection (service->connection);
 
732
 
 
733
  /* request ownership for the generic thumbnailer interface */
 
734
  result = dbus_bus_request_name (connection, THUMBNAILER_SERVICE,
 
735
                                  DBUS_NAME_FLAG_DO_NOT_QUEUE, &dbus_error);
 
736
 
 
737
  /* check if that failed */
 
738
  if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
 
739
    {
 
740
      /* propagate the D-Bus error */
 
741
      if (dbus_error_is_set (&dbus_error))
 
742
        {
 
743
          if (error != NULL)
 
744
            dbus_set_g_error (error, &dbus_error);
 
745
 
 
746
          dbus_error_free (&dbus_error);
 
747
        }
 
748
      else if (error != NULL)
 
749
        {
 
750
          g_set_error (error, DBUS_GERROR, DBUS_GERROR_FAILED,
 
751
                       _("Another generic thumbnailer is already running"));
 
752
        }
 
753
 
 
754
      g_mutex_unlock (service->mutex);
 
755
 
 
756
      return FALSE;
 
757
    }
 
758
 
 
759
  /* everything is fine, install the generic thumbnailer D-Bus info */
 
760
  dbus_g_object_type_install_info (G_OBJECT_TYPE (service),
 
761
                                   &dbus_glib_tumbler_service_object_info);
 
762
 
 
763
  /* register the service instance as a handler of this interface */
 
764
  dbus_g_connection_register_g_object (service->connection, 
 
765
                                       THUMBNAILER_PATH, 
 
766
                                       G_OBJECT (service));
 
767
 
 
768
  g_mutex_unlock (service->mutex);
 
769
 
 
770
  return TRUE;
 
771
}
 
772
 
 
773
 
 
774
 
 
775
void
 
776
tumbler_service_queue (TumblerService        *service,
 
777
                       const gchar *const    *uris,
 
778
                       const gchar *const    *mime_hints,
 
779
                       const gchar           *flavor_name,
 
780
                       const gchar           *scheduler_name,
 
781
                       guint                  handle_to_dequeue,
 
782
                       DBusGMethodInvocation *context)
 
783
{
 
784
  TumblerSchedulerRequest *scheduler_request;
 
785
  TumblerThumbnailFlavor  *flavor;
 
786
  TumblerThumbnailer     **thumbnailers;
 
787
  TumblerScheduler        *scheduler = NULL;
 
788
  TumblerFileInfo        **infos;
 
789
  TumblerCache            *cache;
 
790
  GList                   *iter;
 
791
  gchar                   *name;
 
792
  gchar                   *origin;
 
793
  guint                    handle;
 
794
  guint                    length;
 
795
 
 
796
  dbus_async_return_if_fail (TUMBLER_IS_SERVICE (service), context);
 
797
  dbus_async_return_if_fail (uris != NULL, context);
 
798
  dbus_async_return_if_fail (mime_hints != NULL, context);
 
799
 
 
800
  /* if the scheduler is not defined, fall back to "default" */
 
801
  if (scheduler_name == NULL || *scheduler_name == '\0')
 
802
    scheduler_name = "default";
 
803
 
 
804
  g_mutex_lock (service->mutex);
 
805
 
 
806
  cache = tumbler_cache_get_default ();
 
807
  flavor = tumbler_cache_get_flavor (cache, flavor_name);
 
808
  g_object_unref (cache);
 
809
 
 
810
  g_assert (TUMBLER_IS_CACHE (cache));
 
811
 
 
812
  infos = tumbler_file_info_array_new_with_flavor (uris, mime_hints, flavor,
 
813
                                                   &length);
 
814
 
 
815
  /* get an array with one thumbnailer for each URI in the request */
 
816
  thumbnailers = tumbler_registry_get_thumbnailer_array (service->registry, infos,
 
817
                                                         length);
 
818
 
 
819
  origin = dbus_g_method_get_sender (context);
 
820
 
 
821
  /* allocate a scheduler request */
 
822
  scheduler_request = tumbler_scheduler_request_new (infos, thumbnailers, 
 
823
                                                     length, origin);
 
824
 
 
825
  /* release the file info array */
 
826
  tumbler_file_info_array_free (infos);
 
827
 
 
828
  g_free (origin);
 
829
 
 
830
  /* get the request handle */
 
831
  handle = scheduler_request->handle;
 
832
 
 
833
  /* iterate over all schedulers */
 
834
  for (iter = service->schedulers; iter != NULL; iter = iter->next)
 
835
    {
 
836
      /* dequeue the request with the given dequeue handle, in case this 
 
837
       * scheduler is responsible for the given handle */
 
838
      if (handle_to_dequeue != 0)
 
839
        tumbler_scheduler_dequeue (TUMBLER_SCHEDULER (iter->data), handle_to_dequeue);
 
840
 
 
841
      /* determine the scheduler name */
 
842
      name = tumbler_scheduler_get_name (TUMBLER_SCHEDULER (iter->data));
 
843
 
 
844
      /* check if this is the scheduler we are looking for */
 
845
      if (g_strcmp0 (name, scheduler_name) == 0)
 
846
        scheduler = TUMBLER_SCHEDULER (iter->data);
 
847
 
 
848
      /* free the scheduler name */
 
849
      g_free (name);
 
850
    }
 
851
 
 
852
  /* default to the first scheduler in the list if we couldn't find
 
853
   * the scheduler with the desired name */
 
854
  if (scheduler == NULL) 
 
855
    scheduler = TUMBLER_SCHEDULER (service->schedulers->data);
 
856
 
 
857
  /* report unsupported flavors back to the client */
 
858
  if (flavor == NULL)
 
859
    {
 
860
      /* fake a started signal */
 
861
      tumbler_service_scheduler_started (scheduler, handle, scheduler_request->origin,
 
862
                                         service);
 
863
 
 
864
      /* emit an error signal */
 
865
      tumbler_service_scheduler_error (scheduler, handle, uris, 
 
866
                                       TUMBLER_ERROR_UNSUPPORTED_FLAVOR, 
 
867
                                       _("Unsupported thumbnail flavor requested"),
 
868
                                       scheduler_request->origin,
 
869
                                       service);
 
870
 
 
871
      /* fake a finished signal */
 
872
      tumbler_service_scheduler_finished (scheduler, handle, scheduler_request->origin,
 
873
                                          service);
 
874
 
 
875
      /* release the request */
 
876
      tumbler_scheduler_request_free (scheduler_request);
 
877
    }
 
878
  else
 
879
    {
 
880
      /* let the scheduler take it from here */
 
881
      tumbler_scheduler_push (scheduler, scheduler_request);
 
882
    }
 
883
  
 
884
  /* free the thumbnailer array */
 
885
  tumbler_thumbnailer_array_free (thumbnailers, length);
 
886
 
 
887
  g_mutex_unlock (service->mutex);
 
888
 
 
889
  dbus_g_method_return (context, handle);
 
890
}
 
891
 
 
892
 
 
893
 
 
894
void
 
895
tumbler_service_dequeue (TumblerService        *service,
 
896
                         guint                  handle,
 
897
                         DBusGMethodInvocation *context)
 
898
{
 
899
  GList *iter;
 
900
 
 
901
  dbus_async_return_if_fail (TUMBLER_IS_SERVICE (service), context);
 
902
 
 
903
  g_mutex_lock (service->mutex);
 
904
 
 
905
  if (handle != 0) 
 
906
    {
 
907
      /* iterate over all available schedulers */
 
908
      for (iter = service->schedulers; iter != NULL; iter = iter->next)
 
909
        {
 
910
          /* dequeue the request with the given dequeue handle, in case this
 
911
           * scheduler is responsible for the given handle */
 
912
          tumbler_scheduler_dequeue (TUMBLER_SCHEDULER (iter->data), handle);
 
913
        }
 
914
    }
 
915
 
 
916
  g_mutex_unlock (service->mutex);
 
917
 
 
918
  dbus_g_method_return (context);
 
919
}
 
920
 
 
921
 
 
922
 
 
923
void
 
924
tumbler_service_get_supported (TumblerService        *service,
 
925
                               DBusGMethodInvocation *context)
 
926
{
 
927
  const gchar *const *uri_schemes;
 
928
  const gchar *const *mime_types;
 
929
 
 
930
  dbus_async_return_if_fail (TUMBLER_IS_SERVICE (service), context);
 
931
 
 
932
  g_mutex_lock (service->mutex);
 
933
 
 
934
  /* fetch all supported URI scheme / MIME type pairs from the registry */
 
935
  tumbler_registry_get_supported (service->registry, &uri_schemes, &mime_types);
 
936
 
 
937
  g_mutex_unlock (service->mutex);
 
938
 
 
939
  /* return the arrays to the caller */
 
940
  dbus_g_method_return (context, uri_schemes, mime_types);
 
941
}
 
942
 
 
943
 
 
944
 
 
945
void 
 
946
tumbler_service_get_flavors (TumblerService        *service,
 
947
                              DBusGMethodInvocation *context)
 
948
{
 
949
  TumblerCache *cache;
 
950
  const gchar **flavor_strings;
 
951
  GList        *flavors;
 
952
  GList        *iter;
 
953
  guint         n;
 
954
 
 
955
  cache = tumbler_cache_get_default ();
 
956
 
 
957
  if (cache != NULL)
 
958
    {
 
959
      flavors = tumbler_cache_get_flavors (cache);
 
960
      flavor_strings = g_new0 (const gchar *, g_list_length (flavors) + 1);
 
961
 
 
962
      for (iter = flavors, n = 0; iter != NULL; iter = iter->next, ++n)
 
963
        flavor_strings[n] = tumbler_thumbnail_flavor_get_name (iter->data);
 
964
      flavor_strings[n] = NULL;
 
965
    
 
966
      dbus_g_method_return (context, flavor_strings);
 
967
 
 
968
      g_free (flavor_strings);
 
969
 
 
970
      g_list_foreach (flavors, (GFunc) g_object_unref, NULL);
 
971
      g_list_free (flavors);
 
972
 
 
973
      g_object_unref (cache);
 
974
    }
 
975
  else
 
976
    {
 
977
      flavor_strings = g_new0 (const gchar *, 1);
 
978
      flavor_strings[0] = NULL;
 
979
 
 
980
      dbus_g_method_return (context, flavor_strings);
 
981
 
 
982
      g_free (flavor_strings);
 
983
    }
 
984
}
 
985
 
 
986
 
 
987
void
 
988
tumbler_service_get_schedulers (TumblerService        *service,
 
989
                                DBusGMethodInvocation *context)
 
990
{
 
991
  GStrv  supported_schedulers;
 
992
  GList *iter;
 
993
  guint  n = 0;
 
994
 
 
995
  dbus_async_return_if_fail (TUMBLER_IS_SERVICE (service), context);
 
996
 
 
997
  g_mutex_lock (service->mutex);
 
998
 
 
999
  /* allocate an error for the schedulers */
 
1000
  supported_schedulers = g_new0 (gchar *, g_list_length (service->schedulers) + 2);
 
1001
 
 
1002
  /* always prepend the "default" scheduler */
 
1003
  supported_schedulers[n++] = g_strdup ("default");
 
1004
 
 
1005
  /* append all supported scheduler names */
 
1006
  for (iter = service->schedulers; iter != NULL; iter = iter->next)
 
1007
    {
 
1008
      supported_schedulers[n++] = 
 
1009
        tumbler_scheduler_get_name (TUMBLER_SCHEDULER (iter->data));
 
1010
    }
 
1011
 
 
1012
  g_mutex_unlock (service->mutex);
 
1013
 
 
1014
  /* NULL-terminate the array */
 
1015
  supported_schedulers[n] = NULL;
 
1016
 
 
1017
  /* return the scheduler array to the caller */
 
1018
  dbus_g_method_return (context, supported_schedulers);
 
1019
 
 
1020
  /* free the array */
 
1021
  g_strfreev (supported_schedulers);
 
1022
}