/* vi:set et ai sw=2 sts=2 ts=2: */ /*- * Copyright (c) 2009-2011 Jannis Pohlmann * Copyright (c) 2015 Ali Abdallah * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #define THUMBNAILER_PATH TUMBLER_SERVICE_PATH_PREFIX "/Thumbnailer1" #define THUMBNAILER_SERVICE TUMBLER_SERVICE_NAME_PREFIX ".Thumbnailer1" #define THUMBNAILER_IFACE TUMBLER_SERVICE_NAME_PREFIX ".Thumbnailer1" /* property identifiers */ enum { PROP_0, PROP_CONNECTION, PROP_REGISTRY, }; typedef struct _SchedulerIdleInfo SchedulerIdleInfo; static void tumbler_service_constructed (GObject *object); static void tumbler_service_finalize (GObject *object); static void tumbler_service_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); static void tumbler_service_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); static gboolean tumbler_service_queue_cb (TumblerExportedService *skeleton, GDBusMethodInvocation *invocation, const gchar *const *uris, const gchar *const *mime_hints, const gchar *flavor_name, const gchar *scheduler_name, guint handle_to_dequeue, TumblerService *service); static gboolean tumbler_service_dequeue_cb (TumblerExportedService *skeleton, GDBusMethodInvocation *invocation, guint handle, TumblerService *service); static gboolean tumbler_service_get_schedulers_cb(TumblerExportedService *skeleton, GDBusMethodInvocation *invocation, TumblerService *service); static gboolean tumbler_service_get_supported_cb(TumblerExportedService *skeleton, GDBusMethodInvocation *invocation, TumblerService *service); static gboolean tumbler_service_get_flavors_cb (TumblerExportedService *skeleton, GDBusMethodInvocation *invocation, TumblerService *service); static void tumbler_service_scheduler_error (TumblerScheduler *scheduler, guint32 handle, const gchar *const *failed_uris, GQuark error_domain, gint error_code, const gchar *message, const gchar *origin, TumblerService *service); static void tumbler_service_scheduler_finished (TumblerScheduler *scheduler, guint32 handle, const gchar *origin, TumblerService *service); static void tumbler_service_scheduler_ready (TumblerScheduler *scheduler, guint32 handle, const gchar *const *uris, const gchar *origin, TumblerService *service); static void tumbler_service_scheduler_started (TumblerScheduler *scheduler, guint32 handle, const gchar *origin, TumblerService *service); static void tumbler_service_pre_unmount (TumblerService *service, GMount *mount, GVolumeMonitor *monitor); static void scheduler_idle_info_free (SchedulerIdleInfo *info); struct _TumblerServiceClass { TumblerComponentClass __parent__; }; struct _TumblerService { TumblerComponent __parent__; GDBusConnection *connection; TumblerExportedService *skeleton; gboolean dbus_interface_exported; TumblerRegistry *registry; TUMBLER_MUTEX (mutex); GList *schedulers; GVolumeMonitor *volume_monitor; }; struct _SchedulerIdleInfo { TumblerScheduler *scheduler; TumblerService *service; gchar **uris; gchar *message; gchar *origin; guint handle; gint error_code; }; G_DEFINE_TYPE (TumblerService, tumbler_service, TUMBLER_TYPE_COMPONENT); static void tumbler_service_class_init (TumblerServiceClass *klass) { GObjectClass *gobject_class; gobject_class = G_OBJECT_CLASS (klass); gobject_class->constructed = tumbler_service_constructed; gobject_class->finalize = tumbler_service_finalize; gobject_class->get_property = tumbler_service_get_property; gobject_class->set_property = tumbler_service_set_property; g_object_class_install_property (gobject_class, PROP_CONNECTION, g_param_spec_object ("connection", "connection", "connection", G_TYPE_DBUS_CONNECTION, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (gobject_class, PROP_REGISTRY, g_param_spec_object ("registry", "registry", "registry", TUMBLER_TYPE_REGISTRY, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); } static void tumbler_service_init (TumblerService *service) { tumbler_mutex_create (service->mutex); service->schedulers = NULL; service->volume_monitor = g_volume_monitor_get (); g_signal_connect_swapped (service->volume_monitor, "mount-pre-unmount", G_CALLBACK (tumbler_service_pre_unmount), service); } static void tumbler_service_add_scheduler (TumblerService *service, TumblerScheduler *scheduler) { /* add the scheduler to the list */ service->schedulers = g_list_append (service->schedulers, g_object_ref (scheduler)); /* connect to the scheduler signals */ g_signal_connect (scheduler, "error", G_CALLBACK (tumbler_service_scheduler_error), service); g_signal_connect (scheduler, "finished", G_CALLBACK (tumbler_service_scheduler_finished), service); g_signal_connect (scheduler, "ready", G_CALLBACK (tumbler_service_scheduler_ready), service); g_signal_connect (scheduler, "started", G_CALLBACK (tumbler_service_scheduler_started), service); } static void tumbler_service_remove_scheduler (TumblerScheduler *scheduler, TumblerService *service) { g_return_if_fail (TUMBLER_IS_SCHEDULER (scheduler)); g_return_if_fail (TUMBLER_IS_SERVICE (service)); g_signal_handlers_disconnect_matched (scheduler, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, service); g_object_unref (scheduler); } static void tumbler_service_constructed (GObject *object) { TumblerScheduler *scheduler; TumblerService *service = TUMBLER_SERVICE (object); GError *error = NULL; /* chain up to parent classes */ if (G_OBJECT_CLASS (tumbler_service_parent_class)->constructed != NULL) (G_OBJECT_CLASS (tumbler_service_parent_class)->constructed) (object); /* create the foreground scheduler */ scheduler = tumbler_lifo_scheduler_new ("foreground"); tumbler_service_add_scheduler (service, scheduler); g_object_unref (scheduler); /* create the background scheduler */ scheduler = tumbler_group_scheduler_new ("background"); tumbler_service_add_scheduler (service, scheduler); g_object_unref (scheduler); /* everything is fine, install the generic thumbnailer D-Bus info */ service->skeleton = tumbler_exported_service_skeleton_new(); g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON(service->skeleton), service->connection, THUMBNAILER_PATH, &error); if (error != NULL) { g_critical ("error exporting thumbnail service on session bus: %s", error->message); g_error_free (error); service->dbus_interface_exported = FALSE; } else { service->dbus_interface_exported = TRUE; g_signal_connect (service->skeleton, "handle-queue", G_CALLBACK(tumbler_service_queue_cb), service); g_signal_connect (service->skeleton, "handle-dequeue", G_CALLBACK(tumbler_service_dequeue_cb), service); g_signal_connect (service->skeleton, "handle-get-supported", G_CALLBACK(tumbler_service_get_supported_cb), service); g_signal_connect (service->skeleton, "handle-get-schedulers", G_CALLBACK(tumbler_service_get_schedulers_cb), service); g_signal_connect (service->skeleton, "handle-get-flavors", G_CALLBACK(tumbler_service_get_flavors_cb), service); } } static void tumbler_service_finalize (GObject *object) { TumblerService *service = TUMBLER_SERVICE (object); /* disconnect from the volume monitor */ g_signal_handlers_disconnect_matched (service->volume_monitor, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, service); /* release the volume monitor */ g_object_unref (service->volume_monitor); /* release all schedulers and the scheduler list */ g_list_foreach (service->schedulers, (GFunc) tumbler_service_remove_scheduler, service); g_list_free (service->schedulers); /* release the reference on the thumbnailer registry */ g_object_unref (service->registry); /* Unexport from dbus */ if (service->dbus_interface_exported) g_dbus_interface_skeleton_unexport_from_connection ( G_DBUS_INTERFACE_SKELETON (service->skeleton), service->connection ); /* release the Skeleton object */ g_object_unref (service->skeleton); /* release the D-Bus connection object */ g_object_unref (service->connection); tumbler_mutex_free (service->mutex); (*G_OBJECT_CLASS (tumbler_service_parent_class)->finalize) (object); } static void tumbler_service_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { TumblerService *service = TUMBLER_SERVICE (object); switch (prop_id) { case PROP_CONNECTION: g_value_set_object (value, service->connection); break; case PROP_REGISTRY: g_value_set_object (value, service->registry); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void tumbler_service_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { TumblerService *service = TUMBLER_SERVICE (object); switch (prop_id) { case PROP_CONNECTION: service->connection = g_object_ref (g_value_get_object (value)); break; case PROP_REGISTRY: service->registry = g_value_dup_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static gboolean tumbler_service_error_idle (gpointer user_data) { TumblerCache *cache; SchedulerIdleInfo *info = user_data; GVariant *signal_variant; g_return_val_if_fail (info != NULL, FALSE); g_return_val_if_fail (TUMBLER_IS_SCHEDULER (info->scheduler), FALSE); g_return_val_if_fail (info->uris != NULL && info->uris[0] != NULL && *info->uris[0] != '\0', FALSE); g_return_val_if_fail (info->message != NULL && *info->message != '\0', FALSE); g_return_val_if_fail (info->origin != NULL && *info->origin != '\0', FALSE); g_return_val_if_fail (TUMBLER_IS_SERVICE (info->service), FALSE); /* cache cleanup: any previous thumbnail is now invalid */ cache = tumbler_cache_get_default (); tumbler_cache_delete (cache, (const gchar *const *) info->uris); g_object_unref (cache); /* signal variant */ signal_variant = g_variant_new ("(u^asis)", info->handle, info->uris, info->error_code, info->message); /* send the signal message over D-Bus */ g_dbus_connection_emit_signal (info->service->connection, info->origin, THUMBNAILER_PATH, THUMBNAILER_IFACE, "Error", signal_variant, NULL); scheduler_idle_info_free (info); return FALSE; } static void tumbler_service_scheduler_error (TumblerScheduler *scheduler, guint32 handle, const gchar *const *failed_uris, GQuark error_domain, gint error_code, const gchar *message, const gchar *origin, TumblerService *service) { SchedulerIdleInfo *info; g_return_if_fail (TUMBLER_IS_SCHEDULER (scheduler)); g_return_if_fail (failed_uris != NULL); g_return_if_fail (message != NULL && *message != '\0'); g_return_if_fail (origin != NULL && *origin != '\0'); g_return_if_fail (TUMBLER_IS_SERVICE (service)); /* request cancelled at some level after being handled by the thumbnailer: no signal, * just as if it had been cancelled before */ if (error_domain == G_IO_ERROR && error_code == G_IO_ERROR_CANCELLED) return; info = g_slice_new0 (SchedulerIdleInfo); info->scheduler = g_object_ref (scheduler); info->handle = handle; info->uris = g_strdupv ((gchar **)failed_uris); info->origin = g_strdup (origin); info->service = g_object_ref (service); if (error_domain == TUMBLER_ERROR) { info->error_code = error_code; info->message = g_strdup (message); } else { info->error_code = TUMBLER_ERROR_OTHER_ERROR_DOMAIN; info->message = g_strdup_printf ("(domain %s, code %d) %s", g_quark_to_string (error_domain), error_code, message); } g_debug ("Error signal for job %d: Code %d, message: %s", handle, info->error_code, info->message); tumbler_util_dump_strv (G_LOG_DOMAIN, "URIs", failed_uris); g_idle_add (tumbler_service_error_idle, info); } static gboolean tumbler_service_finished_idle (gpointer user_data) { SchedulerIdleInfo *info = user_data; GVariant *signal_variant; g_return_val_if_fail (info != NULL, FALSE); g_return_val_if_fail (info->origin != NULL && *info->origin != '\0', FALSE); g_return_val_if_fail (TUMBLER_IS_SERVICE (info->service), FALSE); signal_variant = g_variant_new ("(u)", info->handle); /* send the signal message over D-Bus */ g_dbus_connection_emit_signal (info->service->connection, info->origin, THUMBNAILER_PATH, THUMBNAILER_IFACE, "Finished", signal_variant, NULL); /* allow the lifecycle manager to shut down the service again (unless there * are other requests still being processed) */ tumbler_component_decrement_use_count (TUMBLER_COMPONENT (info->service)); scheduler_idle_info_free (info); return FALSE; } static void tumbler_service_scheduler_finished (TumblerScheduler *scheduler, guint32 handle, const gchar *origin, TumblerService *service) { SchedulerIdleInfo *info; g_return_if_fail (TUMBLER_IS_SCHEDULER (scheduler)); g_return_if_fail (origin != NULL && *origin != '\0'); g_return_if_fail (TUMBLER_IS_SERVICE (service)); g_debug ("Finishing job %d\n", handle); info = g_slice_new0 (SchedulerIdleInfo); info->scheduler = g_object_ref (scheduler); info->handle = handle; info->origin = g_strdup (origin); info->service = g_object_ref (service); g_idle_add (tumbler_service_finished_idle, info); } static gboolean tumbler_service_ready_idle (gpointer user_data) { SchedulerIdleInfo *info = user_data; GVariant *signal_variant; g_return_val_if_fail (info != NULL, FALSE); g_return_val_if_fail (TUMBLER_IS_SCHEDULER (info->scheduler), FALSE); g_return_val_if_fail (info->uris != NULL && info->uris[0] != NULL && *info->uris[0] != '\0', FALSE); g_return_val_if_fail (info->origin != NULL && *info->origin != '\0', FALSE); g_return_val_if_fail (TUMBLER_IS_SERVICE (info->service), FALSE); signal_variant = g_variant_new ("(u^as)", info->handle, info->uris); /* send the signal message over D-Bus */ g_dbus_connection_emit_signal (info->service->connection, info->origin, THUMBNAILER_PATH, THUMBNAILER_IFACE, "Ready", signal_variant, NULL); scheduler_idle_info_free (info); return FALSE; } static void tumbler_service_scheduler_ready (TumblerScheduler *scheduler, guint32 handle, const gchar *const *uris, const gchar *origin, TumblerService *service) { SchedulerIdleInfo *info; g_return_if_fail (TUMBLER_IS_SCHEDULER (scheduler)); g_return_if_fail (origin != NULL && *origin != '\0'); g_return_if_fail (uris != NULL && uris[0] != NULL && *uris[0] != '\0'); g_return_if_fail (TUMBLER_IS_SERVICE (service)); g_debug ("Ready signal for job %d", handle); tumbler_util_dump_strv (G_LOG_DOMAIN, "URIs", uris); info = g_slice_new0 (SchedulerIdleInfo); info->scheduler = g_object_ref (scheduler); info->handle = handle; info->uris = g_strdupv ((gchar **)uris); info->origin = g_strdup (origin); info->service = g_object_ref (service); g_idle_add (tumbler_service_ready_idle, info); } static gboolean tumbler_service_started_idle (gpointer user_data) { SchedulerIdleInfo *info = user_data; GVariant *signal_variant; g_return_val_if_fail (info != NULL, FALSE); g_return_val_if_fail (TUMBLER_IS_SCHEDULER (info->scheduler), FALSE); g_return_val_if_fail (info->origin != NULL && *info->origin != '\0', FALSE); g_return_val_if_fail (TUMBLER_IS_SERVICE (info->service), FALSE); signal_variant = g_variant_new ("(u)", info->handle); /* send the signal message over D-Bus */ g_dbus_connection_emit_signal (info->service->connection, info->origin, THUMBNAILER_PATH, THUMBNAILER_IFACE, "Started", signal_variant, NULL); scheduler_idle_info_free (info); return FALSE; } static void tumbler_service_scheduler_started (TumblerScheduler *scheduler, guint32 handle, const gchar *origin, TumblerService *service) { SchedulerIdleInfo *info; g_return_if_fail (TUMBLER_IS_SCHEDULER (scheduler)); g_return_if_fail (origin != NULL && *origin != '\0'); g_return_if_fail (TUMBLER_IS_SERVICE (service)); g_debug ("Starting job %d", handle); info = g_slice_new0 (SchedulerIdleInfo); info->scheduler = g_object_ref (scheduler); info->handle = handle; info->origin = g_strdup (origin); info->service = g_object_ref (service); g_idle_add (tumbler_service_started_idle, info); } static void tumbler_service_pre_unmount (TumblerService *service, GMount *mount, GVolumeMonitor *volume_monitor) { GList *iter; g_return_if_fail (TUMBLER_IS_SERVICE (service)); g_return_if_fail (G_IS_MOUNT (mount)); g_return_if_fail (volume_monitor == service->volume_monitor); tumbler_mutex_lock (service->mutex); /* iterate over all schedulers, cancelling URIs belonging to the mount */ for (iter = service->schedulers; iter != NULL; iter = iter->next) tumbler_scheduler_cancel_by_mount (iter->data, mount); tumbler_mutex_unlock (service->mutex); } static void scheduler_idle_info_free (SchedulerIdleInfo *info) { if (info == NULL) return; g_free (info->message); g_free (info->origin); g_strfreev (info->uris); g_object_unref (info->scheduler); g_object_unref (info->service); g_slice_free (SchedulerIdleInfo, info); } TumblerService * tumbler_service_new (GDBusConnection *connection, TumblerLifecycleManager *lifecycle_manager, TumblerRegistry *registry) { return g_object_new (TUMBLER_TYPE_SERVICE, "connection", connection, "lifecycle-manager", lifecycle_manager, "registry", registry, NULL); } static gboolean tumbler_service_queue_cb (TumblerExportedService *skeleton, GDBusMethodInvocation *invocation, const gchar *const *uris, const gchar *const *mime_hints, const gchar *flavor_name, const gchar *scheduler_name, guint32 handle_to_dequeue, TumblerService *service) { TumblerSchedulerRequest *scheduler_request; TumblerThumbnailFlavor *flavor; TumblerScheduler *scheduler = NULL; TumblerFileInfo **infos; TumblerCache *cache; GList **thumbnailers; GList *iter; gchar *name; const gchar *origin; guint32 handle; guint length; g_dbus_async_return_val_if_fail (TUMBLER_IS_SERVICE (service), invocation, FALSE); g_dbus_async_return_val_if_fail (uris != NULL, invocation, FALSE); g_dbus_async_return_val_if_fail (mime_hints != NULL, invocation, FALSE); tumbler_mutex_lock (service->mutex); /* prevent the lifecycle manager to shut down the service as long * as the request is still being processed */ tumbler_component_increment_use_count (TUMBLER_COMPONENT (service)); /* if the scheduler is not defined, fall back to "default" */ if (scheduler_name == NULL || *scheduler_name == '\0') scheduler_name = "default"; cache = tumbler_cache_get_default (); flavor = tumbler_cache_get_flavor (cache, flavor_name); g_object_unref (cache); infos = tumbler_file_info_array_new_with_flavor (uris, mime_hints, flavor, &length); /* get an array with one thumbnailer for each URI in the request */ thumbnailers = tumbler_registry_get_thumbnailer_array (service->registry, infos, length); origin = g_dbus_method_invocation_get_sender (invocation); /* allocate a scheduler request */ scheduler_request = tumbler_scheduler_request_new (infos, thumbnailers, length, origin); /* release the file info array */ tumbler_file_info_array_free (infos); /* get the request handle */ handle = scheduler_request->handle; g_debug ("Handling request %d", handle); tumbler_util_dump_strvs_side_by_side (G_LOG_DOMAIN, "URIs", "Mime types", uris, mime_hints); /* iterate over all schedulers */ for (iter = service->schedulers; iter != NULL; iter = iter->next) { /* dequeue the request with the given dequeue handle, in case this * scheduler is responsible for the given handle */ if (handle_to_dequeue != 0) tumbler_scheduler_dequeue (TUMBLER_SCHEDULER (iter->data), handle_to_dequeue); /* determine the scheduler name */ name = tumbler_scheduler_get_name (TUMBLER_SCHEDULER (iter->data)); /* check if this is the scheduler we are looking for */ if (g_strcmp0 (name, scheduler_name) == 0) scheduler = TUMBLER_SCHEDULER (iter->data); /* free the scheduler name */ g_free (name); } /* default to the first scheduler in the list if we couldn't find * the scheduler with the desired name */ if (scheduler == NULL && service->schedulers != NULL) scheduler = TUMBLER_SCHEDULER (service->schedulers->data); /* report unsupported flavors back to the client */ if (flavor == NULL) { /* fake a started signal */ tumbler_service_scheduler_started (scheduler, handle, scheduler_request->origin, service); /* emit an error signal */ tumbler_service_scheduler_error (scheduler, handle, uris, TUMBLER_ERROR, TUMBLER_ERROR_UNSUPPORTED_FLAVOR, TUMBLER_ERROR_MESSAGE_UNSUPPORTED_FLAVOR, scheduler_request->origin, service); /* fake a finished signal */ tumbler_service_scheduler_finished (scheduler, handle, scheduler_request->origin, service); /* release the request */ tumbler_scheduler_request_free (scheduler_request); } else { /* let the scheduler take it from here */ tumbler_scheduler_push (scheduler, scheduler_request); } /* free the thumbnailer array */ tumbler_thumbnailer_array_free (thumbnailers, length); tumbler_mutex_unlock (service->mutex); tumbler_exported_service_complete_queue(skeleton, invocation, handle); /* try to keep tumbler alive */ tumbler_component_keep_alive (TUMBLER_COMPONENT (service), NULL); return TRUE; } static gboolean tumbler_service_dequeue_cb (TumblerExportedService *skeleton, GDBusMethodInvocation *invocation, guint32 handle, TumblerService *service) { GList *iter; tumbler_mutex_lock (service->mutex); if (handle != 0) { g_debug ("Dequeuing files for job %d", handle); /* iterate over all available schedulers */ for (iter = service->schedulers; iter != NULL; iter = iter->next) { /* dequeue the request with the given dequeue handle, in case this * scheduler is responsible for the given handle */ tumbler_scheduler_dequeue (TUMBLER_SCHEDULER (iter->data), handle); } } tumbler_mutex_unlock (service->mutex); tumbler_exported_service_complete_dequeue(skeleton, invocation); /* keep tumbler alive */ tumbler_component_keep_alive (TUMBLER_COMPONENT (service), NULL); return TRUE; } static gboolean tumbler_service_get_schedulers_cb (TumblerExportedService *skeleton, GDBusMethodInvocation *invocation, TumblerService *service) { gchar **supported_schedulers; GList *iter; guint n = 0; tumbler_mutex_lock (service->mutex); /* allocate an error for the schedulers */ supported_schedulers = g_new0 (gchar *, g_list_length (service->schedulers) + 2); /* always prepend the "default" scheduler */ supported_schedulers[n++] = g_strdup ("default"); /* append all supported scheduler names */ for (iter = service->schedulers; iter != NULL; iter = iter->next) { supported_schedulers[n++] = tumbler_scheduler_get_name (TUMBLER_SCHEDULER (iter->data)); } tumbler_mutex_unlock (service->mutex); /* NULL-terminate the array */ supported_schedulers[n] = NULL; /* return the scheduler array to the caller */ tumbler_exported_service_complete_get_schedulers (skeleton, invocation, (const char* const*)supported_schedulers); /* free the array */ g_strfreev (supported_schedulers); /* try to keep tumbler alive */ tumbler_component_keep_alive (TUMBLER_COMPONENT (service), NULL); return TRUE; } static gboolean tumbler_service_get_supported_cb (TumblerExportedService *skeleton, GDBusMethodInvocation *invocation, TumblerService *service) { const gchar *const *mime_types; const gchar *const *uri_schemes; g_dbus_async_return_val_if_fail (TUMBLER_IS_SERVICE (service), invocation, FALSE); tumbler_mutex_lock (service->mutex); /* fetch all supported URI scheme / MIME type pairs from the registry */ tumbler_registry_get_supported (service->registry, &uri_schemes, &mime_types); tumbler_mutex_unlock (service->mutex); /* return the arrays to the caller */ tumbler_exported_service_complete_get_supported(skeleton, invocation, uri_schemes, mime_types); /* try to keep tumbler alive */ tumbler_component_keep_alive (TUMBLER_COMPONENT (service), NULL); return TRUE; } static gboolean tumbler_service_get_flavors_cb (TumblerExportedService *skeleton, GDBusMethodInvocation *invocation, TumblerService *service) { TumblerCache *cache; const gchar **flavor_strings; GList *flavors; GList *iter; guint n; cache = tumbler_cache_get_default (); if (cache != NULL) { flavors = tumbler_cache_get_flavors (cache); flavor_strings = g_new0 (const gchar *, g_list_length (flavors) + 1); for (iter = flavors, n = 0; iter != NULL; iter = iter->next, ++n) flavor_strings[n] = tumbler_thumbnail_flavor_get_name (iter->data); flavor_strings[n] = NULL; tumbler_exported_service_complete_get_flavors (skeleton, invocation, flavor_strings); g_free (flavor_strings); g_list_free_full (flavors, g_object_unref); g_object_unref (cache); } else { flavor_strings = g_new0 (const gchar *, 1); flavor_strings[0] = NULL; tumbler_exported_service_complete_get_flavors (skeleton, invocation, flavor_strings); g_free (flavor_strings); } /* try to keep tumbler alive */ tumbler_component_keep_alive (TUMBLER_COMPONENT (service), NULL); return TRUE; } gboolean tumbler_service_is_exported (TumblerService *service) { g_return_val_if_fail (TUMBLER_IS_SERVICE(service), FALSE); return service->dbus_interface_exported; }