diff options
Diffstat (limited to 'src/gnome-desktop/gnome-desktop-thumbnail.c')
-rw-r--r-- | src/gnome-desktop/gnome-desktop-thumbnail.c | 1303 |
1 files changed, 0 insertions, 1303 deletions
diff --git a/src/gnome-desktop/gnome-desktop-thumbnail.c b/src/gnome-desktop/gnome-desktop-thumbnail.c deleted file mode 100644 index 566fbeb84..000000000 --- a/src/gnome-desktop/gnome-desktop-thumbnail.c +++ /dev/null @@ -1,1303 +0,0 @@ -/* - * gnome-thumbnail.c: Utilities for handling thumbnails - * - * Copyright (C) 2002 Red Hat, Inc. - * Copyright (C) 2010 Carlos Garcia Campos <carlosgc@gnome.org> - * - * This file is part of the Gnome Library. - * - * The Gnome Library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * The Gnome Library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with the Gnome Library; see the file COPYING.LIB. If not, - * write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - * Author: Alexander Larsson <alexl@redhat.com> - */ - -/** - * SECTION:gnome-desktop-thumbnail - * @short_description: Generates and looks up thumbnails of files and - * directories - * @stability: Unstable - * @include: libgnome-desktop/gnome-desktop-thumbnail.h - * - * #GnomeDesktopThumbnailFactory allows generation and loading of thumbnails for - * local and remote files and directories. It uses a collection of programs - * called <firstterm>thumbnailers</firstterm>, each one generating thumbnails - * for a specific set of content-types of files. For example, - * <application>totem-video-thumbnailer</application> generates thumbnails for - * video files using GStreamer; <application>evince-thumbnailer</application> - * generates thumbnails for PDFs and other document files. If no specific - * thumbnailer exists for a file, or if the thumbnailer fails, gdk-pixbuf is - * used as a fallback. - * - * To generate a thumbnail, an appropriate thumbnailer program is selected then - * executed, passing it the URI of the file to thumbnail, plus a path to write - * the thumbnail image to. If thumbnailing succeeds, the thumbnailer should have - * written the image to disk before terminating; but if thumbnailing fails, no - * image should be written, and the thumbnailer should return a non-zero exit - * status. #GnomeDesktopThumbnailFactory will then fall back to using gdk-pixbuf - * to generate a thumbnail, if possible. - * - * Thumbnailers are chosen by examining a series of - * <filename>.thumbnailer</filename> files in - * <filename><replaceable>$PREFIX</replaceable>/share/thumbnailers</filename>. - * Each is in a simple key-file format: - * <informalexample><programlisting> - * [Thumbnailer Entry] - * Exec=evince-thumbnailer -s %s %u %o - * MimeType=application/pdf;application/x-bzpdf;application/x-gzpdf; - * </programlisting></informalexample> - * - * The <filename>.thumbnailer</filename> format supports three keys: - * <variablelist> - * <varlistentry><term><code>Exec</code></term><listitem><para> - * Required. The command to execute the thumbnailer. It supports a few different - * parameters which are replaced before calling the thumbnailer: - * <replaceable>%u</replaceable> is the URI of the file being thumbnailed; - * <replaceable>%i</replaceable> is its path; <replaceable>%o</replaceable> - * is the path of the image file to be written to; - * <replaceable>%s</replaceable> is the maximum desired size of the thumbnail - * image (the maximum width or height, in pixels); and - * <replaceable>%%</replaceable> is a literal percent character. - * </para></listitem></varlistentry> - * <varlistentry><term><code>MimeType</code></term><listitem><para> - * Required. A semicolon-separated list of MIME types which the thumbnailer - * supports generating thumbnails for. - * </para></listitem></varlistentry> - * </variablelist> - * - * So in the example <filename>.thumbnailer</filename> file above, the command - * passes the requested thumbnail size, then the input file’s URI, then the - * path for the output image file to - * <application>evince-thumbnailer</application>. - * - * The code to examine and call a thumbnailer is contained in - * #GnomeDesktopThumbnailFactory, which handles looking up the right thumbnailer - * script, building and executing the command for it, and loading the resulting - * thumbnail image into a #GdkPixbuf. - * - * Thumbnail caching is also supported by #GnomeDesktopThumbnailFactory. When - * calling a thumbnailer, the path passed for the output image file is in - * <filename><envar>$XDG_CACHE_HOME</envar>/thumbnails/ - * <replaceable>$SIZE</replaceable>/</filename>. The cached image file is given - * a (probably) unique filename, generated by hashing the original file’s URI, - * so the thumbnail can be looked up in future. #GnomeDesktopThumbnailFactory - * supports two sizes of thumbnails: %GNOME_DESKTOP_THUMBNAIL_SIZE_NORMAL and - * %GNOME_DESKTOP_THUMBNAIL_SIZE_LARGE. Normal thumbnails are up to 128×128 - * pixels, whereas large thumbnails are up to 256×256 pixels. Thumbnails which - * are larger than this are scaled down before being cached, and non-square - * thumbnails are scaled so their largest dimension is at most 128 or 256 - * pixels. - * - * #GnomeDesktopThumbnailFactory also handles failed thumbnails. If a - * thumbnailer can’t generate a thumbnail for a file (e.g. because the file is - * corrupt or because the right video codecs aren’t available), it returns a - * non-zero exit status. The thumbnail factory then writes an entry to - * <filename><envar>$XDG_CACHE_HOME</envar>/thumbnails/fail/ - * gnome-thumbnail-factory/</filename> which is named after the hash of the - * input file URI (just like a successful cached thumbnail). For future queries - * for thumbnails for that file, #GnomeDesktopThumbnailFactory can immediately - * return an error after looking up the fail entry. - * - * If a file changes content, #GnomeDesktopThumbnailFactory will generate a new - * thumbnail because each cached image has associated metadata (stored as PNG - * tEXt keys) storing the full URI of the thumbnailed file (to check for hash - * collisions) and its last modification time at the point of thumbnailing. If - * the stored modification time doesn’t match the file’s current one, a new - * thumbnail is generated. - * - * Since: 2.2 - */ - -#include <config.h> - -#include <glib.h> -#include <glib/gstdio.h> -#include <gdk-pixbuf/gdk-pixbuf.h> -#include <string.h> -#include <stdlib.h> -#include <sys/stat.h> -#include <fcntl.h> - -#define GNOME_DESKTOP_USE_UNSTABLE_API -#include "gnome-desktop-thumbnail.h" -#include "gnome-desktop-thumbnail-script.h" - -static void -thumbnailers_directory_changed (GFileMonitor *monitor, - GFile *file, - GFile *other_file, - GFileMonitorEvent event_type, - GnomeDesktopThumbnailFactory *factory); - -struct _GnomeDesktopThumbnailFactoryPrivate { - GnomeDesktopThumbnailSize size; - - GMutex lock; - - GList *thumbnailers; - GHashTable *mime_types_map; - GList *monitors; - - GSettings *settings; - gboolean loaded : 1; - gboolean disabled : 1; - gchar **disabled_types; -}; - -static const char *appname = "gnome-thumbnail-factory"; - -G_DEFINE_TYPE (GnomeDesktopThumbnailFactory, - gnome_desktop_thumbnail_factory, - G_TYPE_OBJECT) -#define parent_class gnome_desktop_thumbnail_factory_parent_class - -#define GNOME_DESKTOP_THUMBNAIL_FACTORY_GET_PRIVATE(object) \ - (G_TYPE_INSTANCE_GET_PRIVATE ((object), GNOME_DESKTOP_TYPE_THUMBNAIL_FACTORY, GnomeDesktopThumbnailFactoryPrivate)) - -#define THUMBNAILER_ENTRY_GROUP "Thumbnailer Entry" -#define THUMBNAILER_EXTENSION ".thumbnailer" - -typedef struct { - volatile gint ref_count; - - gchar *path; - - 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->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; - } - - 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; - - 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 gpointer -init_thumbnailers_dirs (gpointer data) -{ - const gchar * const *data_dirs; - GPtrArray *thumbs_dirs; - guint i; - - data_dirs = g_get_system_data_dirs (); - thumbs_dirs = g_ptr_array_new (); - - g_ptr_array_add (thumbs_dirs, g_build_filename (g_get_user_data_dir (), "thumbnailers", NULL)); - for (i = 0; data_dirs[i] != NULL; i++) - g_ptr_array_add (thumbs_dirs, g_build_filename (data_dirs[i], "thumbnailers", NULL)); - g_ptr_array_add (thumbs_dirs, NULL); - - return g_ptr_array_free (thumbs_dirs, FALSE); -} - -static const gchar * const * -get_thumbnailers_dirs (void) -{ - static GOnce once_init = G_ONCE_INIT; - return g_once (&once_init, init_thumbnailers_dirs, NULL); -} - -/* These should be called with the lock held */ -static void -gnome_desktop_thumbnail_factory_register_mime_types (GnomeDesktopThumbnailFactory *factory, - Thumbnailer *thumb) -{ - GnomeDesktopThumbnailFactoryPrivate *priv = factory->priv; - gint i; - - for (i = 0; thumb->mime_types[i]; i++) - { - 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)); - } -} - -static void -gnome_desktop_thumbnail_factory_add_thumbnailer (GnomeDesktopThumbnailFactory *factory, - Thumbnailer *thumb) -{ - GnomeDesktopThumbnailFactoryPrivate *priv = factory->priv; - - gnome_desktop_thumbnail_factory_register_mime_types (factory, thumb); - priv->thumbnailers = g_list_prepend (priv->thumbnailers, thumb); -} - -static gboolean -gnome_desktop_thumbnail_factory_is_disabled (GnomeDesktopThumbnailFactory *factory, - const gchar *mime_type) -{ - GnomeDesktopThumbnailFactoryPrivate *priv = factory->priv; - guint i; - - if (priv->disabled) - return TRUE; - - if (!priv->disabled_types) - return FALSE; - - for (i = 0; priv->disabled_types[i]; i++) - { - if (g_strcmp0 (priv->disabled_types[i], mime_type) == 0) - return TRUE; - } - - return FALSE; -} - -static gboolean -remove_thumbnailer_from_mime_type_map (gchar *key, - Thumbnailer *value, - gchar *path) -{ - return (strcmp (value->path, path) == 0); -} - -static void -update_or_create_thumbnailer (GnomeDesktopThumbnailFactory *factory, - const gchar *path) -{ - GnomeDesktopThumbnailFactoryPrivate *priv = factory->priv; - GList *l; - Thumbnailer *thumb; - gboolean found = FALSE; - - g_mutex_lock (&priv->lock); - - for (l = priv->thumbnailers; l && !found; l = g_list_next (l)) - { - thumb = (Thumbnailer *)l->data; - - 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 - gnome_desktop_thumbnail_factory_register_mime_types (factory, thumb); - } - } - - if (!found) - { - thumb = thumbnailer_new (path); - if (thumb) - gnome_desktop_thumbnail_factory_add_thumbnailer (factory, thumb); - } - - g_mutex_unlock (&priv->lock); -} - -static void -remove_thumbnailer (GnomeDesktopThumbnailFactory *factory, - const gchar *path) -{ - GnomeDesktopThumbnailFactoryPrivate *priv = factory->priv; - GList *l; - Thumbnailer *thumb; - - g_mutex_lock (&priv->lock); - - for (l = priv->thumbnailers; l; l = g_list_next (l)) - { - thumb = (Thumbnailer *)l->data; - - 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 -remove_thumbnailers_for_dir (GnomeDesktopThumbnailFactory *factory, - const gchar *thumbnailer_dir, - GFileMonitor *monitor) -{ - GnomeDesktopThumbnailFactoryPrivate *priv = factory->priv; - GList *l; - Thumbnailer *thumb; - - g_mutex_lock (&priv->lock); - - /* Remove all the thumbnailers inside this @thumbnailer_dir. */ - for (l = priv->thumbnailers; l; l = g_list_next (l)) - { - thumb = (Thumbnailer *)l->data; - - if (g_str_has_prefix (thumb->path, thumbnailer_dir) == TRUE) - { - 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)thumb->path); - thumbnailer_unref (thumb); - - break; - } - } - - /* Remove the monitor for @thumbnailer_dir. */ - priv->monitors = g_list_remove (priv->monitors, monitor); - g_signal_handlers_disconnect_by_func (monitor, thumbnailers_directory_changed, factory); - - g_mutex_unlock (&priv->lock); -} - -static void -gnome_desktop_thumbnail_factory_load_thumbnailers_for_dir (GnomeDesktopThumbnailFactory *factory, - const gchar *path) -{ - GnomeDesktopThumbnailFactoryPrivate *priv = factory->priv; - GDir *dir; - GFile *dir_file; - GFileMonitor *monitor; - const gchar *dirent; - - dir = g_dir_open (path, 0, NULL); - if (!dir) - return; - - /* 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) - gnome_desktop_thumbnail_factory_add_thumbnailer (factory, thumb); - } - - g_dir_close (dir); -} - -static void -thumbnailers_directory_changed (GFileMonitor *monitor, - GFile *file, - GFile *other_file, - GFileMonitorEvent event_type, - GnomeDesktopThumbnailFactory *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; - case G_FILE_MONITOR_EVENT_UNMOUNTED: - case G_FILE_MONITOR_EVENT_MOVED: - path = g_file_get_path (file); - remove_thumbnailers_for_dir (factory, path, monitor); - - if (event_type == G_FILE_MONITOR_EVENT_MOVED) - gnome_desktop_thumbnail_factory_load_thumbnailers_for_dir (factory, path); - - g_free (path); - break; - case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT: - case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED: - case G_FILE_MONITOR_EVENT_PRE_UNMOUNT: - case G_FILE_MONITOR_EVENT_RENAMED: - case G_FILE_MONITOR_EVENT_MOVED_IN: - case G_FILE_MONITOR_EVENT_MOVED_OUT: - default: - break; - } -} - -static void -gnome_desktop_thumbnail_factory_load_thumbnailers (GnomeDesktopThumbnailFactory *factory) -{ - GnomeDesktopThumbnailFactoryPrivate *priv = factory->priv; - const gchar * const *dirs; - guint i; - - if (priv->loaded) - return; - - dirs = get_thumbnailers_dirs (); - for (i = 0; dirs[i]; i++) - { - gnome_desktop_thumbnail_factory_load_thumbnailers_for_dir (factory, dirs[i]); - } - - priv->loaded = TRUE; -} - -static void -external_thumbnailers_disabled_all_changed_cb (GSettings *settings, - const gchar *key, - GnomeDesktopThumbnailFactory *factory) -{ - GnomeDesktopThumbnailFactoryPrivate *priv = factory->priv; - - g_mutex_lock (&priv->lock); - - 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"); - gnome_desktop_thumbnail_factory_load_thumbnailers (factory); - } - - g_mutex_unlock (&priv->lock); -} - -static void -external_thumbnailers_disabled_changed_cb (GSettings *settings, - const gchar *key, - GnomeDesktopThumbnailFactory *factory) -{ - GnomeDesktopThumbnailFactoryPrivate *priv = factory->priv; - - g_mutex_lock (&priv->lock); - - if (!priv->disabled) - { - g_strfreev (priv->disabled_types); - priv->disabled_types = g_settings_get_strv (priv->settings, "disable"); - } - - g_mutex_unlock (&priv->lock); -} - -static void -gnome_desktop_thumbnail_factory_init (GnomeDesktopThumbnailFactory *factory) -{ - GnomeDesktopThumbnailFactoryPrivate *priv; - - factory->priv = GNOME_DESKTOP_THUMBNAIL_FACTORY_GET_PRIVATE (factory); - - priv = factory->priv; - - priv->size = GNOME_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.gnome.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) - gnome_desktop_thumbnail_factory_load_thumbnailers (factory); -} - -static void -gnome_desktop_thumbnail_factory_finalize (GObject *object) -{ - GnomeDesktopThumbnailFactory *factory; - GnomeDesktopThumbnailFactoryPrivate *priv; - - factory = GNOME_DESKTOP_THUMBNAIL_FACTORY (object); - - priv = factory->priv; - - if (priv->thumbnailers) - { - g_list_free_full (priv->thumbnailers, (GDestroyNotify)thumbnailer_unref); - priv->thumbnailers = NULL; - } - - g_clear_pointer (&priv->mime_types_map, g_hash_table_destroy); - - if (priv->monitors) - { - g_list_free_full (priv->monitors, (GDestroyNotify)g_object_unref); - priv->monitors = NULL; - } - - g_mutex_clear (&priv->lock); - - g_clear_pointer (&priv->disabled_types, g_strfreev); - - if (priv->settings) - { - g_signal_handlers_disconnect_by_func (priv->settings, - external_thumbnailers_disabled_all_changed_cb, - factory); - g_signal_handlers_disconnect_by_func (priv->settings, - external_thumbnailers_disabled_changed_cb, - factory); - g_clear_object (&priv->settings); - } - - if (G_OBJECT_CLASS (parent_class)->finalize) - (* G_OBJECT_CLASS (parent_class)->finalize) (object); -} - -static void -gnome_desktop_thumbnail_factory_class_init (GnomeDesktopThumbnailFactoryClass *class) -{ - GObjectClass *gobject_class; - - gobject_class = G_OBJECT_CLASS (class); - - gobject_class->finalize = gnome_desktop_thumbnail_factory_finalize; - - g_type_class_add_private (class, sizeof (GnomeDesktopThumbnailFactoryPrivate)); -} - -/** - * gnome_desktop_thumbnail_factory_new: - * @size: The thumbnail size to use - * - * Creates a new #GnomeDesktopThumbnailFactory. - * - * This function must be called on the main thread and is non-blocking. - * - * Return value: a new #GnomeDesktopThumbnailFactory - * - * Since: 2.2 - **/ -GnomeDesktopThumbnailFactory * -gnome_desktop_thumbnail_factory_new (GnomeDesktopThumbnailSize size) -{ - GnomeDesktopThumbnailFactory *factory; - - factory = g_object_new (GNOME_DESKTOP_TYPE_THUMBNAIL_FACTORY, NULL); - - factory->priv->size = size; - - return factory; -} - -static char * -thumbnail_filename (const char *uri) -{ - GChecksum *checksum; - guint8 digest[16]; - gsize digest_len = sizeof (digest); - char *file; - - checksum = g_checksum_new (G_CHECKSUM_MD5); - g_checksum_update (checksum, (const guchar *) uri, strlen (uri)); - - g_checksum_get_digest (checksum, digest, &digest_len); - g_assert (digest_len == 16); - - file = g_strconcat (g_checksum_get_string (checksum), ".png", NULL); - - g_checksum_free (checksum); - - return file; -} - -static char * -thumbnail_path (const char *uri, - GnomeDesktopThumbnailSize size) -{ - char *path, *file; - - file = thumbnail_filename (uri); - path = g_build_filename (g_get_user_cache_dir (), - "thumbnails", - size == GNOME_DESKTOP_THUMBNAIL_SIZE_LARGE ? "large" : "normal", - file, - NULL); - g_free (file); - return path; -} - -static char * -thumbnail_failed_path (const char *uri) -{ - char *path, *file; - - file = thumbnail_filename (uri); - /* XXX: appname is only used for failed thumbnails. Is this a mistake? */ - path = g_build_filename (g_get_user_cache_dir (), - "thumbnails", - "fail", - appname, - file, - NULL); - g_free (file); - return path; -} - -static char * -validate_thumbnail_path (char *path, - const char *uri, - time_t mtime, - GnomeDesktopThumbnailSize size) -{ - GdkPixbuf *pixbuf; - - pixbuf = gdk_pixbuf_new_from_file (path, NULL); - if (pixbuf == NULL || - !gnome_desktop_thumbnail_is_valid (pixbuf, uri, mtime)) { - g_free (path); - return NULL; - } - - g_clear_object (&pixbuf); - - return path; -} - -static char * -lookup_thumbnail_path (const char *uri, - time_t mtime, - GnomeDesktopThumbnailSize size) -{ - char *path = thumbnail_path (uri, size); - return validate_thumbnail_path (path, uri, mtime, size); -} - -static char * -lookup_failed_thumbnail_path (const char *uri, - time_t mtime, - GnomeDesktopThumbnailSize size) -{ - char *path = thumbnail_failed_path (uri); - return validate_thumbnail_path (path, uri, mtime, size); -} - -/** - * gnome_desktop_thumbnail_factory_lookup: - * @factory: a #GnomeDesktopThumbnailFactory - * @uri: the uri of a file - * @mtime: the mtime of the file - * - * Tries to locate an existing thumbnail for the file specified. - * - * Usage of this function is threadsafe and does blocking I/O. - * - * Return value: The absolute path of the thumbnail, or %NULL if none exist. - * - * Since: 2.2 - **/ -char * -gnome_desktop_thumbnail_factory_lookup (GnomeDesktopThumbnailFactory *factory, - const char *uri, - time_t mtime) -{ - GnomeDesktopThumbnailFactoryPrivate *priv = factory->priv; - - g_return_val_if_fail (uri != NULL, NULL); - - return lookup_thumbnail_path (uri, mtime, priv->size); -} - -/** - * gnome_desktop_thumbnail_factory_has_valid_failed_thumbnail: - * @factory: a #GnomeDesktopThumbnailFactory - * @uri: the uri of a file - * @mtime: the mtime of the file - * - * Tries to locate an failed thumbnail for the file specified. Writing - * and looking for failed thumbnails is important to avoid to try to - * thumbnail e.g. broken images several times. - * - * Usage of this function is threadsafe and does blocking I/O. - * - * Return value: TRUE if there is a failed thumbnail for the file. - * - * Since: 2.2 - **/ -gboolean -gnome_desktop_thumbnail_factory_has_valid_failed_thumbnail (GnomeDesktopThumbnailFactory *factory, - const char *uri, - time_t mtime) -{ - char *path; - - g_return_val_if_fail (uri != NULL, FALSE); - - path = lookup_failed_thumbnail_path (uri, mtime, factory->priv->size); - if (path == NULL) - return FALSE; - - g_free (path); - - return TRUE; -} - -/** - * gnome_desktop_thumbnail_factory_can_thumbnail: - * @factory: a #GnomeDesktopThumbnailFactory - * @uri: the uri of a file - * @mime_type: the mime type of the file - * @mtime: the mtime of the file - * - * Returns TRUE if this GnomeDesktopThumbnailFactory can (at least try) to thumbnail - * this file. Thumbnails or files with failed thumbnails won't be thumbnailed. - * - * Usage of this function is threadsafe and does blocking I/O. - * - * Return value: TRUE if the file can be thumbnailed. - * - * Since: 2.2 - **/ -gboolean -gnome_desktop_thumbnail_factory_can_thumbnail (GnomeDesktopThumbnailFactory *factory, - const char *uri, - const char *mime_type, - time_t mtime) -{ - gboolean have_script = FALSE; - - /* Don't thumbnail thumbnails */ - if (uri && - strncmp (uri, "file:/", 6) == 0 && - strstr (uri, "/thumbnails/") != NULL) - return FALSE; - - if (!mime_type) - return FALSE; - - g_mutex_lock (&factory->priv->lock); - if (!gnome_desktop_thumbnail_factory_is_disabled (factory, mime_type)) - { - Thumbnailer *thumb; - - thumb = g_hash_table_lookup (factory->priv->mime_types_map, mime_type); - have_script = (thumb != NULL); - } - g_mutex_unlock (&factory->priv->lock); - - if (have_script) - { - return !gnome_desktop_thumbnail_factory_has_valid_failed_thumbnail (factory, - uri, - mtime); - } - - return FALSE; -} - -static GdkPixbuf * -get_preview_thumbnail (const char *uri, - int size) -{ - GdkPixbuf *pixbuf; - GFile *file; - GFileInfo *file_info; - GInputStream *input_stream; - GObject *object; - - g_return_val_if_fail (uri != NULL, NULL); - - input_stream = NULL; - - file = g_file_new_for_uri (uri); - - /* First see if we can get an input stream via preview::icon */ - file_info = g_file_query_info (file, - G_FILE_ATTRIBUTE_PREVIEW_ICON, - G_FILE_QUERY_INFO_NONE, - NULL, /* GCancellable */ - NULL); /* return location for GError */ - g_object_unref (file); - - if (file_info == NULL) - return NULL; - - object = g_file_info_get_attribute_object (file_info, - G_FILE_ATTRIBUTE_PREVIEW_ICON); - if (object) - g_object_ref (object); - g_object_unref (file_info); - - if (!object) - return NULL; - if (!G_IS_LOADABLE_ICON (object)) { - g_object_unref (object); - return NULL; - } - - input_stream = g_loadable_icon_load (G_LOADABLE_ICON (object), - 0, /* size */ - NULL, /* return location for type */ - NULL, /* GCancellable */ - NULL); /* return location for GError */ - g_object_unref (object); - - if (!input_stream) - return NULL; - - pixbuf = gdk_pixbuf_new_from_stream_at_scale (input_stream, - size, size, - TRUE, NULL, NULL); - g_object_unref (input_stream); - - return pixbuf; -} - -static GdkPixbuf * -pixbuf_new_from_bytes (GBytes *bytes, - GError **error) -{ - g_autoptr(GdkPixbufLoader) loader = NULL; - - loader = gdk_pixbuf_loader_new_with_mime_type ("image/png", error); - if (!loader) - return NULL; - - if (!gdk_pixbuf_loader_write (loader, - g_bytes_get_data (bytes, NULL), - g_bytes_get_size (bytes), - error)) - { - return NULL; - } - - if (!gdk_pixbuf_loader_close (loader, error)) - return NULL; - - return g_object_ref (gdk_pixbuf_loader_get_pixbuf (loader)); -} - -/** - * gnome_desktop_thumbnail_factory_generate_thumbnail: - * @factory: a #GnomeDesktopThumbnailFactory - * @uri: the uri of a file - * @mime_type: the mime type of the file - * - * Tries to generate a thumbnail for the specified file. If it succeeds - * it returns a pixbuf that can be used as a thumbnail. - * - * Usage of this function is threadsafe and does blocking I/O. - * - * Return value: (transfer full): thumbnail pixbuf if thumbnailing succeeded, %NULL otherwise. - * - * Since: 2.2 - **/ -GdkPixbuf * -gnome_desktop_thumbnail_factory_generate_thumbnail (GnomeDesktopThumbnailFactory *factory, - const char *uri, - const char *mime_type) -{ - GdkPixbuf *pixbuf; - char *script; - int size; - - g_return_val_if_fail (uri != NULL, NULL); - 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 == GNOME_DESKTOP_THUMBNAIL_SIZE_LARGE) - size = 256; - - pixbuf = get_preview_thumbnail (uri, size); - if (pixbuf != NULL) - return pixbuf; - - script = NULL; - g_mutex_lock (&factory->priv->lock); - if (!gnome_desktop_thumbnail_factory_is_disabled (factory, mime_type)) - { - 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) - { - GBytes *data; - GError *error = NULL; - - data = gnome_desktop_thumbnail_script_exec (script, size, uri, &error); - if (data) - { - pixbuf = pixbuf_new_from_bytes (data, &error); - if (!pixbuf) - { - g_debug ("Could not load thumbnail pixbuf: %s", error->message); - g_error_free (error); - } - g_bytes_unref (data); - } - else - { - g_debug ("Thumbnail script ('%s') failed for '%s': %s", - script, uri, error ? error->message : "no details"); - g_clear_error (&error); - } - } - else - { - g_debug ("Could not find thumbnailer for mime-type '%s'", - mime_type); - } - - g_free (script); - - return pixbuf; -} - -static gboolean -save_thumbnail (GdkPixbuf *pixbuf, - char *path, - const char *uri, - time_t mtime) -{ - char *dirname; - char *tmp_path = NULL; - int tmp_fd; - char mtime_str[21]; - gboolean ret = FALSE; - GError *error = NULL; - const char *width, *height; - - if (pixbuf == NULL) - return FALSE; - - dirname = g_path_get_dirname (path); - - if (g_mkdir_with_parents (dirname, 0700) != 0) - goto out; - - tmp_path = g_strconcat (path, ".XXXXXX", NULL); - tmp_fd = g_mkstemp (tmp_path); - - if (tmp_fd == -1) - goto out; - close (tmp_fd); - - g_snprintf (mtime_str, 21, "%" G_GINT64_FORMAT, (gint64) mtime); - width = gdk_pixbuf_get_option (pixbuf, "tEXt::Thumb::Image::Width"); - height = gdk_pixbuf_get_option (pixbuf, "tEXt::Thumb::Image::Height"); - - error = NULL; - if (width != NULL && height != NULL) - ret = gdk_pixbuf_save (pixbuf, - tmp_path, - "png", &error, - "tEXt::Thumb::Image::Width", width, - "tEXt::Thumb::Image::Height", height, - "tEXt::Thumb::URI", uri, - "tEXt::Thumb::MTime", mtime_str, - "tEXt::Software", "GNOME::ThumbnailFactory", - NULL); - else - ret = gdk_pixbuf_save (pixbuf, - tmp_path, - "png", &error, - "tEXt::Thumb::URI", uri, - "tEXt::Thumb::MTime", mtime_str, - "tEXt::Software", "GNOME::ThumbnailFactory", - NULL); - - if (!ret) - goto out; - - g_chmod (tmp_path, 0600); - g_rename (tmp_path, path); - - out: - if (error != NULL) - { - g_warning ("Failed to create thumbnail %s: %s", tmp_path, error->message); - g_error_free (error); - } - g_unlink (tmp_path); - g_free (tmp_path); - g_free (dirname); - return ret; -} - -static GdkPixbuf * -make_failed_thumbnail (void) -{ - GdkPixbuf *pixbuf; - - pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, 1, 1); - gdk_pixbuf_fill (pixbuf, 0x00000000); - return pixbuf; -} - -/** - * gnome_desktop_thumbnail_factory_save_thumbnail: - * @factory: a #GnomeDesktopThumbnailFactory - * @thumbnail: the thumbnail as a pixbuf - * @uri: the uri of a 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. - * - * Usage of this function is threadsafe and does blocking I/O. - * - * Since: 2.2 - **/ -void -gnome_desktop_thumbnail_factory_save_thumbnail (GnomeDesktopThumbnailFactory *factory, - GdkPixbuf *thumbnail, - const char *uri, - time_t original_mtime) -{ - char *path; - - path = thumbnail_path (uri, factory->priv->size); - if (!save_thumbnail (thumbnail, path, uri, original_mtime)) - { - thumbnail = make_failed_thumbnail (); - g_free (path); - path = thumbnail_failed_path (uri); - save_thumbnail (thumbnail, path, uri, original_mtime); - g_object_unref (thumbnail); - } - g_free (path); -} - -/** - * gnome_desktop_thumbnail_factory_create_failed_thumbnail: - * @factory: a #GnomeDesktopThumbnailFactory - * @uri: the uri of a file - * @mtime: the modification time of the file - * - * Creates a failed thumbnail for the file so that we don't try - * to re-thumbnail the file later. - * - * Usage of this function is threadsafe and does blocking I/O. - * - * Since: 2.2 - **/ -void -gnome_desktop_thumbnail_factory_create_failed_thumbnail (GnomeDesktopThumbnailFactory *factory, - const char *uri, - time_t mtime) -{ - char *path; - GdkPixbuf *pixbuf; - - path = thumbnail_failed_path (uri); - pixbuf = make_failed_thumbnail (); - save_thumbnail (pixbuf, path, uri, mtime); - g_free (path); - g_object_unref (pixbuf); -} - -/** - * gnome_desktop_thumbnail_path_for_uri: - * @uri: an uri - * @size: a thumbnail size - * - * Returns the filename that a thumbnail of size @size for @uri would have. - * This function is threadsafe and does no blocking I/O. - * - * Return value: an absolute filename - * - * Since: 2.2 - **/ -char * -gnome_desktop_thumbnail_path_for_uri (const char *uri, - GnomeDesktopThumbnailSize size) -{ - return thumbnail_path (uri, size); -} - -/** - * gnome_desktop_thumbnail_is_valid: - * @pixbuf: an loaded thumbnail #GdkPixbuf - * @uri: a uri - * @mtime: the mtime - * - * Returns whether the thumbnail has the correct uri and mtime embedded in the - * png options. This function is threadsafe and does no blocking I/O. - * - * Return value: TRUE if the thumbnail has the right @uri and @mtime - * - * Since: 2.2 - **/ -gboolean -gnome_desktop_thumbnail_is_valid (GdkPixbuf *pixbuf, - const char *uri, - time_t mtime) -{ - const char *thumb_uri, *thumb_mtime_str; - time_t thumb_mtime; - - thumb_uri = gdk_pixbuf_get_option (pixbuf, "tEXt::Thumb::URI"); - if (g_strcmp0 (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; -} |