From f2ce79172dea2db12a1d85f441990e8b56cae225 Mon Sep 17 00:00:00 2001 From: Stefano Karapetsas Date: Mon, 3 Sep 2012 15:45:37 +0200 Subject: update to latest thumbnail spec (code taken from gnome-desktop) --- libmate-desktop/mate-desktop-thumbnail.c | 922 ++++++++++++++++++++++--------- 1 file changed, 652 insertions(+), 270 deletions(-) diff --git a/libmate-desktop/mate-desktop-thumbnail.c b/libmate-desktop/mate-desktop-thumbnail.c index 8b45bab..2911ef5 100644 --- a/libmate-desktop/mate-desktop-thumbnail.c +++ b/libmate-desktop/mate-desktop-thumbnail.c @@ -2,6 +2,7 @@ * mate-thumbnail.c: Utilities for handling thumbnails * * Copyright (C) 2002 Red Hat, Inc. + * Copyright (C) 2010 Carlos Garcia Campos * * This file is part of the Mate Library. * @@ -17,8 +18,8 @@ * * You should have received a copy of the GNU Library General Public * License along with the Mate Library; see the file COPYING.LIB. If not, - * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. * * Author: Alexander Larsson */ @@ -41,60 +42,234 @@ #define MATE_DESKTOP_USE_UNSTABLE_API #include "libmateui/mate-desktop-thumbnail.h" -#include -#include #include +#include +#include #define SECONDS_BETWEEN_STATS 10 struct _MateDesktopThumbnailFactoryPrivate { - MateDesktopThumbnailSize size; + MateDesktopThumbnailSize size; - GMutex* lock; + GMutex lock; - GHashTable *scripts_hash; - guint thumbnailers_notify; - guint reread_scheduled; -}; + GList *thumbnailers; + GHashTable *mime_types_map; + GList *monitors; -static const char* appname = "mate-thumbnail-factory"; + GSettings *settings; + gboolean loaded : 1; + gboolean disabled : 1; + gchar **disabled_types; +}; -static void mate_desktop_thumbnail_factory_init(MateDesktopThumbnailFactory* factory); -static void mate_desktop_thumbnail_factory_class_init(MateDesktopThumbnailFactoryClass* class); +static const char *appname = "mate-thumbnail-factory"; -G_DEFINE_TYPE (MateDesktopThumbnailFactory, mate_desktop_thumbnail_factory, G_TYPE_OBJECT) +static void mate_desktop_thumbnail_factory_init (MateDesktopThumbnailFactory *factory); +static void mate_desktop_thumbnail_factory_class_init (MateDesktopThumbnailFactoryClass *class); +G_DEFINE_TYPE (MateDesktopThumbnailFactory, + mate_desktop_thumbnail_factory, + G_TYPE_OBJECT) #define parent_class mate_desktop_thumbnail_factory_parent_class #define MATE_DESKTOP_THUMBNAIL_FACTORY_GET_PRIVATE(object) \ - (G_TYPE_INSTANCE_GET_PRIVATE((object), MATE_DESKTOP_TYPE_THUMBNAIL_FACTORY, MateDesktopThumbnailFactoryPrivate)) + (G_TYPE_INSTANCE_GET_PRIVATE ((object), MATE_DESKTOP_TYPE_THUMBNAIL_FACTORY, MateDesktopThumbnailFactoryPrivate)) typedef struct { - gint width; - gint height; - gint input_width; - gint input_height; - gboolean preserve_aspect_ratio; + gint width; + gint height; + gint input_width; + gint input_height; + gboolean preserve_aspect_ratio; } SizePrepareContext; #define LOAD_BUFFER_SIZE 4096 +#define THUMBNAILER_ENTRY_GROUP "Thumbnailer Entry" +#define THUMBNAILER_EXTENSION ".thumbnailer" + +typedef struct { + volatile gint ref_count; + + gchar *path; + + gchar *try_exec; + gchar *command; + gchar **mime_types; +} Thumbnailer; + +static Thumbnailer * +thumbnailer_ref (Thumbnailer *thumb) +{ + g_return_val_if_fail (thumb != NULL, NULL); + g_return_val_if_fail (thumb->ref_count > 0, NULL); + + g_atomic_int_inc (&thumb->ref_count); + return thumb; +} + +static void +thumbnailer_unref (Thumbnailer *thumb) +{ + g_return_if_fail (thumb != NULL); + g_return_if_fail (thumb->ref_count > 0); + + if (g_atomic_int_dec_and_test (&thumb->ref_count)) + { + g_free (thumb->path); + g_free (thumb->try_exec); + g_free (thumb->command); + g_strfreev (thumb->mime_types); + + g_slice_free (Thumbnailer, thumb); + } +} + +static Thumbnailer * +thumbnailer_load (Thumbnailer *thumb) +{ + GKeyFile *key_file; + GError *error = NULL; + + key_file = g_key_file_new (); + if (!g_key_file_load_from_file (key_file, thumb->path, 0, &error)) + { + g_warning ("Failed to load thumbnailer from \"%s\": %s\n", thumb->path, error->message); + g_error_free (error); + thumbnailer_unref (thumb); + g_key_file_free (key_file); + + return NULL; + } + + if (!g_key_file_has_group (key_file, THUMBNAILER_ENTRY_GROUP)) + { + g_warning ("Invalid thumbnailer: missing group \"%s\"\n", THUMBNAILER_ENTRY_GROUP); + thumbnailer_unref (thumb); + g_key_file_free (key_file); + + return NULL; + } + + thumb->command = g_key_file_get_string (key_file, THUMBNAILER_ENTRY_GROUP, "Exec", NULL); + if (!thumb->command) + { + g_warning ("Invalid thumbnailer: missing Exec key\n"); + thumbnailer_unref (thumb); + g_key_file_free (key_file); + + return NULL; + } + + thumb->mime_types = g_key_file_get_string_list (key_file, THUMBNAILER_ENTRY_GROUP, "MimeType", NULL, NULL); + if (!thumb->mime_types) + { + g_warning ("Invalid thumbnailer: missing MimeType key\n"); + thumbnailer_unref (thumb); + g_key_file_free (key_file); + + return NULL; + } + + thumb->try_exec = g_key_file_get_string (key_file, THUMBNAILER_ENTRY_GROUP, "TryExec", NULL); + + g_key_file_free (key_file); + + return thumb; +} + +static Thumbnailer * +thumbnailer_reload (Thumbnailer *thumb) +{ + g_return_val_if_fail (thumb != NULL, NULL); + + g_free (thumb->command); + thumb->command = NULL; + g_strfreev (thumb->mime_types); + thumb->mime_types = NULL; + g_free (thumb->try_exec); + thumb->try_exec = NULL; + + return thumbnailer_load (thumb); +} + +static Thumbnailer * +thumbnailer_new (const gchar *path) +{ + Thumbnailer *thumb; + + thumb = g_slice_new0 (Thumbnailer); + thumb->ref_count = 1; + thumb->path = g_strdup (path); + + return thumbnailer_load (thumb); +} + +static gboolean +thumbnailer_try_exec (Thumbnailer *thumb) +{ + gchar *path; + gboolean retval; + + if (G_UNLIKELY (!thumb)) + return FALSE; + + /* TryExec is optinal, but Exec isn't, so we assume + * the thumbnailer can be run when TryExec is not present + */ + if (!thumb->try_exec) + return TRUE; + + path = g_find_program_in_path (thumb->try_exec); + retval = path != NULL; + g_free (path); + + return retval; +} + +static gpointer +init_thumbnailers_dirs (gpointer data) +{ + const gchar * const *data_dirs; + gchar **thumbs_dirs; + guint i, length; + + data_dirs = g_get_system_data_dirs (); + length = g_strv_length ((char **) data_dirs); + + thumbs_dirs = g_new (gchar *, length + 2); + thumbs_dirs[0] = g_build_filename (g_get_user_data_dir (), "thumbnailers", NULL); + for (i = 0; i < length; i++) + thumbs_dirs[i + 1] = g_build_filename (data_dirs[i], "thumbnailers", NULL); + thumbs_dirs[length + 1] = NULL; + + return thumbs_dirs; +} + +static const gchar * const * +get_thumbnailers_dirs (void) +{ + static GOnce once_init = G_ONCE_INIT; + return g_once (&once_init, init_thumbnailers_dirs, NULL); +} + static void -size_prepared_cb (GdkPixbufLoader *loader, +size_prepared_cb (GdkPixbufLoader *loader, int width, int height, gpointer data) { SizePrepareContext *info = data; - + g_return_if_fail (width > 0 && height > 0); - + info->input_width = width; info->input_height = height; - + if (width < info->width && height < info->height) return; - - if (info->preserve_aspect_ratio && + + if (info->preserve_aspect_ratio && (info->width > 0 || info->height > 0)) { if (info->width < 0) { @@ -120,10 +295,43 @@ size_prepared_cb (GdkPixbufLoader *loader, if (info->height > 0) height = info->height; } - + gdk_pixbuf_loader_set_size (loader, width, height); } +static GdkPixbufLoader * +create_loader (GFile *file, + const guchar *data, + gsize size) +{ + GdkPixbufLoader *loader; + GError *error = NULL; + char *mime_type; + char *filename; + + loader = NULL; + + /* need to specify the type here because the gdk_pixbuf_loader_write + doesn't have access to the filename in order to correct detect + the image type. */ + filename = g_file_get_basename (file); + mime_type = g_content_type_guess (filename, data, size, NULL); + g_free (filename); + + if (mime_type != NULL) { + loader = gdk_pixbuf_loader_new_with_mime_type (mime_type, &error); + } + + if (loader == NULL) { + g_warning ("Unable to create loader for mime type %s: %s", mime_type, error->message); + g_clear_error (&error); + loader = gdk_pixbuf_loader_new (); + } + g_free (mime_type); + + return loader; +} + static GdkPixbuf * _gdk_pixbuf_new_from_uri_at_scale (const char *uri, gint width, @@ -131,10 +339,10 @@ _gdk_pixbuf_new_from_uri_at_scale (const char *uri, gboolean preserve_aspect_ratio) { gboolean result; - char buffer[LOAD_BUFFER_SIZE]; + guchar buffer[LOAD_BUFFER_SIZE]; gsize bytes_read; - GdkPixbufLoader *loader; - GdkPixbuf *pixbuf; + GdkPixbufLoader *loader = NULL; + GdkPixbuf *pixbuf; GdkPixbufAnimation *animation; GdkPixbufAnimationIter *iter; gboolean has_frame; @@ -142,6 +350,7 @@ _gdk_pixbuf_new_from_uri_at_scale (const char *uri, GFile *file; GFileInfo *file_info; GInputStream *input_stream; + GError *error = NULL; g_return_val_if_fail (uri != NULL, NULL); @@ -171,22 +380,30 @@ _gdk_pixbuf_new_from_uri_at_scale (const char *uri, } if (input_stream == NULL) { - input_stream = G_INPUT_STREAM (g_file_read (file, NULL, NULL)); - if (input_stream == NULL) { - g_object_unref (file); - return NULL; + /* part of this code is taken from libgsystem */ + int fd; + char *path = g_file_get_path (file); + #ifdef O_NOATIME + fd = g_open (path, O_RDONLY | O_NOATIME, 0); + /* Only the owner or superuser may use O_NOATIME; so we may get + * EPERM. EINVAL may happen if the kernel is really old... + */ + if (fd == -1 && (errno == EPERM || errno == EINVAL)) + #endif + fd = g_open (path, O_RDONLY, 0); + if (fd >= 0) { + input_stream = g_unix_input_stream_new (fd, TRUE); + if (input_stream == NULL) { + if (error != NULL) { + g_warning ("Unable to create an input stream for %s: %s", uri, error->message); + g_clear_error (&error); + } + g_object_unref (file); + return NULL; + } } } - loader = gdk_pixbuf_loader_new (); - if (1 <= width || 1 <= height) { - info.width = width; - info.height = height; - info.input_width = info.input_height = 0; - info.preserve_aspect_ratio = preserve_aspect_ratio; - g_signal_connect (loader, "size-prepared", G_CALLBACK (size_prepared_cb), &info); - } - has_frame = FALSE; result = FALSE; @@ -196,7 +413,11 @@ _gdk_pixbuf_new_from_uri_at_scale (const char *uri, buffer, sizeof (buffer), NULL, - NULL); + &error); + if (error != NULL) { + g_warning ("Error reading from %s: %s", uri, error->message); + g_clear_error (&error); + } if (bytes_read == -1) { break; } @@ -205,10 +426,24 @@ _gdk_pixbuf_new_from_uri_at_scale (const char *uri, break; } + if (loader == NULL) { + loader = create_loader (file, buffer, bytes_read); + if (1 <= width || 1 <= height) { + info.width = width; + info.height = height; + info.input_width = info.input_height = 0; + info.preserve_aspect_ratio = preserve_aspect_ratio; + g_signal_connect (loader, "size-prepared", G_CALLBACK (size_prepared_cb), &info); + } + g_assert (loader != NULL); + } + if (!gdk_pixbuf_loader_write (loader, (unsigned char *)buffer, bytes_read, - NULL)) { + &error)) { + g_warning ("Error creating thumbnail for %s: %s", uri, error->message); + g_clear_error (&error); result = FALSE; break; } @@ -255,199 +490,338 @@ mate_desktop_thumbnail_factory_finalize (GObject *object) { MateDesktopThumbnailFactory *factory; MateDesktopThumbnailFactoryPrivate *priv; - MateConfClient *client; - + factory = MATE_DESKTOP_THUMBNAIL_FACTORY (object); priv = factory->priv; - if (priv->reread_scheduled != 0) { - g_source_remove (priv->reread_scheduled); - priv->reread_scheduled = 0; - } + if (priv->thumbnailers) + { + g_list_free_full (priv->thumbnailers, (GDestroyNotify)thumbnailer_unref); + priv->thumbnailers = NULL; + } - if (priv->thumbnailers_notify != 0) { - client = mateconf_client_get_default (); - mateconf_client_notify_remove (client, priv->thumbnailers_notify); - priv->thumbnailers_notify = 0; - g_object_unref (client); - } + if (priv->mime_types_map) + { + g_hash_table_destroy (priv->mime_types_map); + priv->mime_types_map = NULL; + } - if (priv->scripts_hash) + if (priv->monitors) { - g_hash_table_destroy (priv->scripts_hash); - priv->scripts_hash = NULL; + g_list_free_full (priv->monitors, (GDestroyNotify)g_object_unref); + priv->monitors = NULL; } - if (priv->lock) + g_mutex_clear (&priv->lock); + + if (priv->disabled_types) { - g_mutex_free (priv->lock); - priv->lock = NULL; + g_strfreev (priv->disabled_types); + priv->disabled_types = NULL; + } + + if (priv->settings) + { + g_object_unref (priv->settings); + priv->settings = NULL; } if (G_OBJECT_CLASS (parent_class)->finalize) (* G_OBJECT_CLASS (parent_class)->finalize) (object); } -/* Must be called on main thread */ -static GHashTable * -read_scripts (void) +/* These should be called with the lock held */ +static void +mate_desktop_thumbnail_factory_register_mime_types (MateDesktopThumbnailFactory *factory, + Thumbnailer *thumb) { - GHashTable *scripts_hash; - MateConfClient *client; - GSList *subdirs, *l; - char *subdir, *enable, *escape, *commandkey, *command, *mimetype; - - client = mateconf_client_get_default (); + MateDesktopThumbnailFactoryPrivate *priv = factory->priv; + gint i; - if (mateconf_client_get_bool (client, - "/desktop/mate/thumbnailers/disable_all", - NULL)) + for (i = 0; thumb->mime_types[i]; i++) { - g_object_unref (G_OBJECT (client)); - return NULL; + if (!g_hash_table_lookup (priv->mime_types_map, thumb->mime_types[i])) + g_hash_table_insert (priv->mime_types_map, + g_strdup (thumb->mime_types[i]), + thumbnailer_ref (thumb)); } +} - scripts_hash = g_hash_table_new_full (g_str_hash, - g_str_equal, - g_free, g_free); +static void +mate_desktop_thumbnail_factory_add_thumbnailer (MateDesktopThumbnailFactory *factory, + Thumbnailer *thumb) +{ + MateDesktopThumbnailFactoryPrivate *priv = factory->priv; + mate_desktop_thumbnail_factory_register_mime_types (factory, thumb); + priv->thumbnailers = g_list_prepend (priv->thumbnailers, thumb); +} - subdirs = mateconf_client_all_dirs (client, "/desktop/mate/thumbnailers", NULL); +static gboolean +mate_desktop_thumbnail_factory_is_disabled (MateDesktopThumbnailFactory *factory, + const gchar *mime_type) +{ + MateDesktopThumbnailFactoryPrivate *priv = factory->priv; + guint i; - for (l = subdirs; l != NULL; l = l->next) - { - subdir = l->data; + if (priv->disabled) + return TRUE; - enable = g_strdup_printf ("%s/enable", subdir); - if (mateconf_client_get_bool (client, - enable, - NULL)) - { - commandkey = g_strdup_printf ("%s/command", subdir); - command = mateconf_client_get_string (client, commandkey, NULL); - g_free (commandkey); - - if (command != NULL) { - mimetype = strrchr (subdir, '/'); - if (mimetype != NULL) - { - mimetype++; /* skip past slash */ - - /* Convert '@' to slash in mimetype */ - escape = strchr (mimetype, '@'); - if (escape != NULL) - *escape = '/'; - - /* Convert any remaining '@' to '+' in mimetype */ - while ((escape = strchr (mimetype, '@')) != NULL) - *escape = '+'; - - g_hash_table_insert (scripts_hash, - g_strdup (mimetype), command); - } - else - { - g_free (command); - } - } - } - g_free (enable); + if (!priv->disabled_types) + return FALSE; - g_free (subdir); + for (i = 0; priv->disabled_types[i]; i++) + { + if (g_strcmp0 (priv->disabled_types[i], mime_type) == 0) + return TRUE; } - g_slist_free(subdirs); - - g_object_unref (G_OBJECT (client)); + return FALSE; +} - return scripts_hash; +static gboolean +remove_thumbnailer_from_mime_type_map (gchar *key, + Thumbnailer *value, + gchar *path) +{ + return (strcmp (value->path, path) == 0); } -/* Must be called on main thread */ static void -mate_desktop_thumbnail_factory_reread_scripts (MateDesktopThumbnailFactory *factory) +update_or_create_thumbnailer (MateDesktopThumbnailFactory *factory, + const gchar *path) { MateDesktopThumbnailFactoryPrivate *priv = factory->priv; - GHashTable *scripts_hash; + GList *l; + Thumbnailer *thumb; + gboolean found = FALSE; - scripts_hash = read_scripts (); + g_mutex_lock (&priv->lock); - g_mutex_lock (priv->lock); + for (l = priv->thumbnailers; l && !found; l = g_list_next (l)) + { + thumb = (Thumbnailer *)l->data; - if (priv->scripts_hash != NULL) - g_hash_table_destroy (priv->scripts_hash); + if (strcmp (thumb->path, path) == 0) + { + found = TRUE; + + /* First remove the mime_types associated to this thumbnailer */ + g_hash_table_foreach_remove (priv->mime_types_map, + (GHRFunc)remove_thumbnailer_from_mime_type_map, + (gpointer)path); + if (!thumbnailer_reload (thumb)) + priv->thumbnailers = g_list_delete_link (priv->thumbnailers, l); + else + mate_desktop_thumbnail_factory_register_mime_types (factory, thumb); + } + } - priv->scripts_hash = scripts_hash; + if (!found) + { + thumb = thumbnailer_new (path); + if (thumb) + mate_desktop_thumbnail_factory_add_thumbnailer (factory, thumb); + } - g_mutex_unlock (priv->lock); + g_mutex_unlock (&priv->lock); } -static gboolean -reread_idle_callback (gpointer user_data) +static void +remove_thumbnailer (MateDesktopThumbnailFactory *factory, + const gchar *path) { - MateDesktopThumbnailFactory *factory = user_data; MateDesktopThumbnailFactoryPrivate *priv = factory->priv; + GList *l; + Thumbnailer *thumb; - mate_desktop_thumbnail_factory_reread_scripts (factory); + g_mutex_lock (&priv->lock); - g_mutex_lock (priv->lock); - priv->reread_scheduled = 0; - g_mutex_unlock (priv->lock); + for (l = priv->thumbnailers; l; l = g_list_next (l)) + { + thumb = (Thumbnailer *)l->data; - return FALSE; + if (strcmp (thumb->path, path) == 0) + { + priv->thumbnailers = g_list_delete_link (priv->thumbnailers, l); + g_hash_table_foreach_remove (priv->mime_types_map, + (GHRFunc)remove_thumbnailer_from_mime_type_map, + (gpointer)path); + thumbnailer_unref (thumb); + + break; + } + } + + g_mutex_unlock (&priv->lock); +} + +static void +thumbnailers_directory_changed (GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event_type, + MateDesktopThumbnailFactory *factory) +{ + gchar *path; + + switch (event_type) + { + case G_FILE_MONITOR_EVENT_CREATED: + case G_FILE_MONITOR_EVENT_CHANGED: + case G_FILE_MONITOR_EVENT_DELETED: + path = g_file_get_path (file); + if (!g_str_has_suffix (path, THUMBNAILER_EXTENSION)) + { + g_free (path); + return; + } + + if (event_type == G_FILE_MONITOR_EVENT_DELETED) + remove_thumbnailer (factory, path); + else + update_or_create_thumbnailer (factory, path); + + g_free (path); + break; + default: + break; + } } static void -schedule_reread (MateConfClient* client, - guint cnxn_id, - MateConfEntry *entry, - gpointer user_data) +mate_desktop_thumbnail_factory_load_thumbnailers (MateDesktopThumbnailFactory *factory) { - MateDesktopThumbnailFactory *factory = user_data; MateDesktopThumbnailFactoryPrivate *priv = factory->priv; + const gchar * const *dirs; + guint i; - g_mutex_lock (priv->lock); + if (priv->loaded) + return; - if (priv->reread_scheduled == 0) + dirs = get_thumbnailers_dirs (); + for (i = 0; dirs[i]; i++) { - priv->reread_scheduled = g_idle_add (reread_idle_callback, - factory); + const gchar *path = dirs[i]; + GDir *dir; + GFile *dir_file; + GFileMonitor *monitor; + const gchar *dirent; + + dir = g_dir_open (path, 0, NULL); + if (!dir) + continue; + + /* Monitor dir */ + dir_file = g_file_new_for_path (path); + monitor = g_file_monitor_directory (dir_file, + G_FILE_MONITOR_NONE, + NULL, NULL); + if (monitor) + { + g_signal_connect (monitor, "changed", + G_CALLBACK (thumbnailers_directory_changed), + factory); + priv->monitors = g_list_prepend (priv->monitors, monitor); + } + g_object_unref (dir_file); + + while ((dirent = g_dir_read_name (dir))) + { + Thumbnailer *thumb; + gchar *filename; + + if (!g_str_has_suffix (dirent, THUMBNAILER_EXTENSION)) + continue; + + filename = g_build_filename (path, dirent, NULL); + thumb = thumbnailer_new (filename); + g_free (filename); + + if (thumb) + mate_desktop_thumbnail_factory_add_thumbnailer (factory, thumb); + } + + g_dir_close (dir); } - g_mutex_unlock (priv->lock); + priv->loaded = TRUE; } - static void -mate_desktop_thumbnail_factory_init (MateDesktopThumbnailFactory *factory) +external_thumbnailers_disabled_all_changed_cb (GSettings *settings, + const gchar *key, + MateDesktopThumbnailFactory *factory) { - MateConfClient *client; - MateDesktopThumbnailFactoryPrivate *priv; + MateDesktopThumbnailFactoryPrivate *priv = factory->priv; - factory->priv = MATE_DESKTOP_THUMBNAIL_FACTORY_GET_PRIVATE (factory); + g_mutex_lock (&priv->lock); - priv = factory->priv; + priv->disabled = g_settings_get_boolean (priv->settings, "disable-all"); + if (priv->disabled) + { + g_strfreev (priv->disabled_types); + priv->disabled_types = NULL; + } + else + { + priv->disabled_types = g_settings_get_strv (priv->settings, "disable"); + mate_desktop_thumbnail_factory_load_thumbnailers (factory); + } - priv->size = MATE_DESKTOP_THUMBNAIL_SIZE_NORMAL; + g_mutex_unlock (&priv->lock); +} - priv->scripts_hash = NULL; +static void +external_thumbnailers_disabled_changed_cb (GSettings *settings, + const gchar *key, + MateDesktopThumbnailFactory *factory) +{ + MateDesktopThumbnailFactoryPrivate *priv = factory->priv; - priv->lock = g_mutex_new (); + g_mutex_lock (&priv->lock); - client = mateconf_client_get_default (); - mateconf_client_add_dir (client, - "/desktop/mate/thumbnailers", - MATECONF_CLIENT_PRELOAD_RECURSIVE, NULL); + if (priv->disabled) + return; + g_strfreev (priv->disabled_types); + priv->disabled_types = g_settings_get_strv (priv->settings, "disable"); - mate_desktop_thumbnail_factory_reread_scripts (factory); + g_mutex_unlock (&priv->lock); +} - priv->thumbnailers_notify = mateconf_client_notify_add (client, "/desktop/mate/thumbnailers", - schedule_reread, factory, NULL, - NULL); +static void +mate_desktop_thumbnail_factory_init (MateDesktopThumbnailFactory *factory) +{ + MateDesktopThumbnailFactoryPrivate *priv; + + factory->priv = MATE_DESKTOP_THUMBNAIL_FACTORY_GET_PRIVATE (factory); - g_object_unref (G_OBJECT (client)); + priv = factory->priv; + + priv->size = MATE_DESKTOP_THUMBNAIL_SIZE_NORMAL; + + priv->mime_types_map = g_hash_table_new_full (g_str_hash, + g_str_equal, + (GDestroyNotify)g_free, + (GDestroyNotify)thumbnailer_unref); + + g_mutex_init (&priv->lock); + + priv->settings = g_settings_new ("org.mate.desktop.thumbnailers"); + priv->disabled = g_settings_get_boolean (priv->settings, "disable-all"); + if (!priv->disabled) + priv->disabled_types = g_settings_get_strv (priv->settings, "disable"); + g_signal_connect (priv->settings, "changed::disable-all", + G_CALLBACK (external_thumbnailers_disabled_all_changed_cb), + factory); + g_signal_connect (priv->settings, "changed::disable", + G_CALLBACK (external_thumbnailers_disabled_changed_cb), + factory); + + if (!priv->disabled) + mate_desktop_thumbnail_factory_load_thumbnailers (factory); } static void @@ -456,7 +830,7 @@ mate_desktop_thumbnail_factory_class_init (MateDesktopThumbnailFactoryClass *cla GObjectClass *gobject_class; gobject_class = G_OBJECT_CLASS (class); - + gobject_class->finalize = mate_desktop_thumbnail_factory_finalize; g_type_class_add_private (class, sizeof (MateDesktopThumbnailFactoryPrivate)); @@ -469,7 +843,7 @@ mate_desktop_thumbnail_factory_class_init (MateDesktopThumbnailFactoryClass *cla * Creates a new #MateDesktopThumbnailFactory. * * This function must be called on the main thread. - * + * * Return value: a new #MateDesktopThumbnailFactory * * Since: 2.2 @@ -478,11 +852,11 @@ MateDesktopThumbnailFactory * mate_desktop_thumbnail_factory_new (MateDesktopThumbnailSize size) { MateDesktopThumbnailFactory *factory; - + factory = g_object_new (MATE_DESKTOP_TYPE_THUMBNAIL_FACTORY, NULL); - + factory->priv->size = size; - + return factory; } @@ -524,9 +898,9 @@ mate_desktop_thumbnail_factory_lookup (MateDesktopThumbnailFactory *factory, g_assert (digest_len == 16); file = g_strconcat (g_checksum_get_string (checksum), ".png", NULL); - - path = g_build_filename (g_get_home_dir (), - ".thumbnails", + + path = g_build_filename (g_get_user_cache_dir (), + "thumbnails", (priv->size == MATE_DESKTOP_THUMBNAIL_SIZE_NORMAL)?"normal":"large", file, NULL); @@ -586,8 +960,8 @@ mate_desktop_thumbnail_factory_has_valid_failed_thumbnail (MateDesktopThumbnailF file = g_strconcat (g_checksum_get_string (checksum), ".png", NULL); - path = g_build_filename (g_get_home_dir (), - ".thumbnails/fail", + path = g_build_filename (g_get_user_cache_dir (), + "thumbnails/fail", appname, file, NULL); @@ -607,55 +981,52 @@ mate_desktop_thumbnail_factory_has_valid_failed_thumbnail (MateDesktopThumbnailF return res; } -static gboolean mimetype_supported_by_gdk_pixbuf(const char* mime_type) +static gboolean +mimetype_supported_by_gdk_pixbuf (const char *mime_type) { - guint i; - static GHashTable* formats_hash = NULL; - gchar* key; - gboolean result; - - if (!formats_hash) - { - GSList* formats; - GSList* list; - - formats_hash = g_hash_table_new_full(g_str_hash, g_content_type_equals, g_free, NULL); + guint i; + static gsize formats_hash = 0; + gchar *key; + gboolean result; - formats = gdk_pixbuf_get_formats(); - list = formats; + if (g_once_init_enter (&formats_hash)) { + GSList *formats, *list; + GHashTable *hash; - while (list) - { - GdkPixbufFormat* format = list->data; - gchar** mime_types = gdk_pixbuf_format_get_mime_types(format); + hash = g_hash_table_new_full (g_str_hash, + (GEqualFunc) g_content_type_equals, + g_free, NULL); - for (i = 0; mime_types[i] != NULL; i++) - { - g_hash_table_insert(formats_hash, (gpointer) g_content_type_from_mime_type(mime_types[i]), GUINT_TO_POINTER(1)); - } + formats = gdk_pixbuf_get_formats (); + list = formats; - g_strfreev(mime_types); + while (list) { + GdkPixbufFormat *format = list->data; + gchar **mime_types; - list = list->next; - } + mime_types = gdk_pixbuf_format_get_mime_types (format); - g_slist_free(formats); - } + for (i = 0; mime_types[i] != NULL; i++) + g_hash_table_insert (hash, + (gpointer) g_content_type_from_mime_type (mime_types[i]), + GUINT_TO_POINTER (1)); - key = g_content_type_from_mime_type(mime_type); + g_strfreev (mime_types); + list = list->next; + } + g_slist_free (formats); - if (g_hash_table_lookup(formats_hash, key)) - { - result = TRUE; - } - else - { - result = FALSE; - } + g_once_init_leave (&formats_hash, (gsize) hash); + } - g_free(key); + key = g_content_type_from_mime_type (mime_type); + if (g_hash_table_lookup ((void*)formats_hash, key)) + result = TRUE; + else + result = FALSE; + g_free (key); - return result; + return result; } /** @@ -680,21 +1051,26 @@ mate_desktop_thumbnail_factory_can_thumbnail (MateDesktopThumbnailFactory *facto const char *mime_type, time_t mtime) { - gboolean have_script; + gboolean have_script = FALSE; /* Don't thumbnail thumbnails */ if (uri && strncmp (uri, "file:/", 6) == 0 && - strstr (uri, "/.thumbnails/") != NULL) + strstr (uri, "/thumbnails/") != NULL) return FALSE; - + if (!mime_type) return FALSE; - g_mutex_lock (factory->priv->lock); - have_script = (factory->priv->scripts_hash != NULL && - g_hash_table_lookup (factory->priv->scripts_hash, mime_type)); - g_mutex_unlock (factory->priv->lock); + g_mutex_lock (&factory->priv->lock); + if (!mate_desktop_thumbnail_factory_is_disabled (factory, mime_type)) + { + Thumbnailer *thumb; + + thumb = g_hash_table_lookup (factory->priv->mime_types_map, mime_type); + have_script = thumbnailer_try_exec (thumb); + } + g_mutex_unlock (&factory->priv->lock); if (have_script || mimetype_supported_by_gdk_pixbuf (mime_type)) { @@ -702,13 +1078,13 @@ mate_desktop_thumbnail_factory_can_thumbnail (MateDesktopThumbnailFactory *facto uri, mtime); } - + return FALSE; } static char * expand_thumbnailing_script (const char *script, - const int size, + const int size, const char *inuri, const char *outfile) { @@ -718,7 +1094,7 @@ expand_thumbnailing_script (const char *script, gboolean got_in; str = g_string_new (NULL); - + got_in = FALSE; last = script; while ((p = strchr (last, '%')) != NULL) @@ -786,7 +1162,7 @@ expand_thumbnailing_script (const char *script, * * Usage of this function is threadsafe. * - * Return value: thumbnail pixbuf if thumbnailing succeeded, %NULL otherwise. + * Return value: (transfer full): thumbnail pixbuf if thumbnailing succeeded, %NULL otherwise. * * Since: 2.2 **/ @@ -809,7 +1185,7 @@ mate_desktop_thumbnail_factory_generate_thumbnail (MateDesktopThumbnailFactory * g_return_val_if_fail (mime_type != NULL, NULL); /* Doesn't access any volatile fields in factory, so it's threadsafe */ - + size = 128; if (factory->priv->size == MATE_DESKTOP_THUMBNAIL_SIZE_LARGE) size = 256; @@ -817,15 +1193,17 @@ mate_desktop_thumbnail_factory_generate_thumbnail (MateDesktopThumbnailFactory * pixbuf = NULL; script = NULL; - g_mutex_lock (factory->priv->lock); - if (factory->priv->scripts_hash != NULL) + g_mutex_lock (&factory->priv->lock); + if (!mate_desktop_thumbnail_factory_is_disabled (factory, mime_type)) { - script = g_hash_table_lookup (factory->priv->scripts_hash, mime_type); - if (script) - script = g_strdup (script); - } - g_mutex_unlock (factory->priv->lock); + Thumbnailer *thumb; + thumb = g_hash_table_lookup (factory->priv->mime_types_map, mime_type); + if (thumb) + script = g_strdup (thumb->command); + } + g_mutex_unlock (&factory->priv->lock); + if (script) { int fd; @@ -846,7 +1224,7 @@ mate_desktop_thumbnail_factory_generate_thumbnail (MateDesktopThumbnailFactory * } g_free (expanded_script); - g_unlink(tmpname); + g_unlink (tmpname); g_free (tmpname); } @@ -866,7 +1244,7 @@ mate_desktop_thumbnail_factory_generate_thumbnail (MateDesktopThumbnailFactory * "mate-original-height")); } } - + if (pixbuf == NULL) return NULL; @@ -879,7 +1257,7 @@ mate_desktop_thumbnail_factory_generate_thumbnail (MateDesktopThumbnailFactory * width = gdk_pixbuf_get_width (pixbuf); height = gdk_pixbuf_get_height (pixbuf); - + if (width > size || height > size) { const gchar *orig_width, *orig_height; @@ -898,11 +1276,11 @@ mate_desktop_thumbnail_factory_generate_thumbnail (MateDesktopThumbnailFactory * if (orig_height != NULL) { gdk_pixbuf_set_option (scaled, "tEXt::Thumb::Image::Height", orig_height); } - + g_object_unref (pixbuf); pixbuf = scaled; } - + if (original_width > 0) { g_snprintf (dimension, sizeof (dimension), "%i", original_width); gdk_pixbuf_set_option (pixbuf, "tEXt::Thumb::Image::Width", dimension); @@ -924,8 +1302,8 @@ make_thumbnail_dirs (MateDesktopThumbnailFactory *factory) res = FALSE; - thumbnail_dir = g_build_filename (g_get_home_dir (), - ".thumbnails", + thumbnail_dir = g_build_filename (g_get_user_cache_dir (), + "thumbnails", NULL); if (!g_file_test (thumbnail_dir, G_FILE_TEST_IS_DIR)) { @@ -944,7 +1322,7 @@ make_thumbnail_dirs (MateDesktopThumbnailFactory *factory) g_free (thumbnail_dir); g_free (image_dir); - + return res; } @@ -958,8 +1336,8 @@ make_thumbnail_fail_dirs (MateDesktopThumbnailFactory *factory) res = FALSE; - thumbnail_dir = g_build_filename (g_get_home_dir (), - ".thumbnails", + thumbnail_dir = g_build_filename (g_get_user_cache_dir (), + "thumbnails", NULL); if (!g_file_test (thumbnail_dir, G_FILE_TEST_IS_DIR)) { @@ -988,7 +1366,7 @@ make_thumbnail_fail_dirs (MateDesktopThumbnailFactory *factory) g_free (thumbnail_dir); g_free (fail_dir); g_free (app_dir); - + return res; } @@ -996,9 +1374,9 @@ make_thumbnail_fail_dirs (MateDesktopThumbnailFactory *factory) /** * mate_desktop_thumbnail_factory_save_thumbnail: * @factory: a #MateDesktopThumbnailFactory - * @thumbnail: the thumbnail as a pixbuf + * @thumbnail: the thumbnail as a pixbuf * @uri: the uri of a file - * @original_mtime: the modification time of the original file + * @original_mtime: the modification time of the original file * * Saves @thumbnail at the right place. If the save fails a * failed thumbnail is written. @@ -1023,6 +1401,7 @@ mate_desktop_thumbnail_factory_save_thumbnail (MateDesktopThumbnailFactory *fact GChecksum *checksum; guint8 digest[16]; gsize digest_len = sizeof (digest); + GError *error; checksum = g_checksum_new (G_CHECKSUM_MD5); g_checksum_update (checksum, (const guchar *) uri, strlen (uri)); @@ -1032,8 +1411,8 @@ mate_desktop_thumbnail_factory_save_thumbnail (MateDesktopThumbnailFactory *fact file = g_strconcat (g_checksum_get_string (checksum), ".png", NULL); - path = g_build_filename (g_get_home_dir (), - ".thumbnails", + path = g_build_filename (g_get_user_cache_dir (), + "thumbnails", (priv->size == MATE_DESKTOP_THUMBNAIL_SIZE_NORMAL)?"normal":"large", file, NULL); @@ -1061,15 +1440,16 @@ mate_desktop_thumbnail_factory_save_thumbnail (MateDesktopThumbnailFactory *fact return; } close (tmp_fd); - + g_snprintf (mtime_str, 21, "%ld", original_mtime); width = gdk_pixbuf_get_option (thumbnail, "tEXt::Thumb::Image::Width"); height = gdk_pixbuf_get_option (thumbnail, "tEXt::Thumb::Image::Height"); - if (width != NULL && height != NULL) + error = NULL; + if (width != NULL && height != NULL) saved_ok = gdk_pixbuf_save (thumbnail, tmp_path, - "png", NULL, + "png", &error, "tEXt::Thumb::Image::Width", width, "tEXt::Thumb::Image::Height", height, "tEXt::Thumb::URI", uri, @@ -1079,21 +1459,24 @@ mate_desktop_thumbnail_factory_save_thumbnail (MateDesktopThumbnailFactory *fact else saved_ok = gdk_pixbuf_save (thumbnail, tmp_path, - "png", NULL, + "png", &error, "tEXt::Thumb::URI", uri, "tEXt::Thumb::MTime", mtime_str, "tEXt::Software", "MATE::ThumbnailFactory", NULL); - + if (saved_ok) { g_chmod (tmp_path, 0600); - g_rename(tmp_path, path); + g_rename (tmp_path, path); } else { + g_warning ("Failed to create thumbnail %s: %s", tmp_path, error->message); mate_desktop_thumbnail_factory_create_failed_thumbnail (factory, uri, original_mtime); + g_unlink (tmp_path); + g_clear_error (&error); } g_free (path); @@ -1136,8 +1519,8 @@ mate_desktop_thumbnail_factory_create_failed_thumbnail (MateDesktopThumbnailFact file = g_strconcat (g_checksum_get_string (checksum), ".png", NULL); - path = g_build_filename (g_get_home_dir (), - ".thumbnails/fail", + path = g_build_filename (g_get_user_cache_dir (), + "thumbnails/fail", appname, file, NULL); @@ -1163,12 +1546,12 @@ mate_desktop_thumbnail_factory_create_failed_thumbnail (MateDesktopThumbnailFact return; } close (tmp_fd); - + g_snprintf (mtime_str, 21, "%ld", mtime); pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, 1, 1); saved_ok = gdk_pixbuf_save (pixbuf, tmp_path, - "png", NULL, + "png", NULL, "tEXt::Thumb::URI", uri, "tEXt::Thumb::MTime", mtime_str, "tEXt::Software", "MATE::ThumbnailFactory", @@ -1194,8 +1577,7 @@ mate_desktop_thumbnail_factory_create_failed_thumbnail (MateDesktopThumbnailFact * Return value: A string with the MD5 digest of the uri string. * * Since: 2.2 - * - * @Deprecated: 2.22: Use #GChecksum instead + * Deprecated: 2.22: Use #GChecksum instead **/ char * mate_desktop_thumbnail_md5 (const char *uri) @@ -1227,13 +1609,13 @@ mate_desktop_thumbnail_path_for_uri (const char *uri, md5 = mate_desktop_thumbnail_md5 (uri); file = g_strconcat (md5, ".png", NULL); g_free (md5); - - path = g_build_filename (g_get_home_dir (), - ".thumbnails", + + path = g_build_filename (g_get_user_cache_dir (), + "thumbnails", (size == MATE_DESKTOP_THUMBNAIL_SIZE_NORMAL)?"normal":"large", file, NULL); - + g_free (file); return path; @@ -1256,7 +1638,7 @@ mate_desktop_thumbnail_has_uri (GdkPixbuf *pixbuf, const char *uri) { const char *thumb_uri; - + thumb_uri = gdk_pixbuf_get_option (pixbuf, "tEXt::Thumb::URI"); if (!thumb_uri) return FALSE; @@ -1284,19 +1666,19 @@ mate_desktop_thumbnail_is_valid (GdkPixbuf *pixbuf, { const char *thumb_uri, *thumb_mtime_str; time_t thumb_mtime; - + thumb_uri = gdk_pixbuf_get_option (pixbuf, "tEXt::Thumb::URI"); if (!thumb_uri) return FALSE; if (strcmp (uri, thumb_uri) != 0) return FALSE; - + thumb_mtime_str = gdk_pixbuf_get_option (pixbuf, "tEXt::Thumb::MTime"); if (!thumb_mtime_str) return FALSE; thumb_mtime = atol (thumb_mtime_str); if (mtime != thumb_mtime) return FALSE; - + return TRUE; } -- cgit v1.2.1