diff options
author | Matthias Clasen <mclasen@redhat.com> | 2004-10-19 18:45:41 +0000 |
---|---|---|
committer | Matthias Clasen <matthiasc@src.gnome.org> | 2004-10-19 18:45:41 +0000 |
commit | 6fc2b8118a77a4d3f6d237d573805302db5e54b7 (patch) | |
tree | 3705cce0a4866838f8230b0e369e1c080c27cfc2 /gtk/gtkiconcache.c | |
parent | b087f7655108019d797ca6096077443761915431 (diff) | |
download | gtk+-6fc2b8118a77a4d3f6d237d573805302db5e54b7.tar.gz |
Implement icon theme caching. (#154034, Martijn Vernooij, caching schema
2004-10-19 Matthias Clasen <mclasen@redhat.com>
Implement icon theme caching. (#154034, Martijn Vernooij,
caching schema proposed by Owen Taylor, initial implementation
by Anders Carlsson)
* gtk/gtkdebug.h:
* gtk/gtkmain.c: Add a "icontheme" debug flag.
* gtk/Makefile.am (gtk_c_sources): Add gtkiconcache.c
(gtk_private_h_sources): Add gtkiconcache.h
(bin_PROGRAMS): Add gtk-update-icon-cache
* gtk/gtkicontheme.c: Use icon caches if they are available.
Currently, GTK+ uses the cache to get information about the
available sizes, image file formats and .icon files. The
actual image data, and the .icon file contents are not
cached yet.
* gtk/updateiconcache.c: A cmdline utility for generating
icon cache files.
* gtk/gtkiconcache.h:
* gtk/gtkiconcache.c: The glue code to mmap an icon cache
file and manage the information it contains.
Diffstat (limited to 'gtk/gtkiconcache.c')
-rw-r--r-- | gtk/gtkiconcache.c | 280 |
1 files changed, 280 insertions, 0 deletions
diff --git a/gtk/gtkiconcache.c b/gtk/gtkiconcache.c new file mode 100644 index 0000000000..766dfa0e23 --- /dev/null +++ b/gtk/gtkiconcache.c @@ -0,0 +1,280 @@ +/* gtkiconcache.c + * Copyright (C) 2004 Anders Carlsson <andersca@gnome.org> + * + * This 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. + * + * This 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 this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <config.h> +#include "gtkdebug.h" +#include "gtkiconcache.h" + +#include <sys/mman.h> +#include <sys/types.h> +#include <sys/stat.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <fcntl.h> +#include <string.h> + +#define MAJOR_VERSION 1 +#define MINOR_VERSION 0 + +#define GET_UINT16(cache, offset) (GUINT16_FROM_BE (*(guint16 *)((cache) + (offset)))) +#define GET_UINT32(cache, offset) (GUINT32_FROM_BE (*(guint32 *)((cache) + (offset)))) + +struct _GtkIconCache { + gint ref_count; + + gsize size; + gchar *buffer; +}; + +GtkIconCache * +_gtk_icon_cache_ref (GtkIconCache *cache) +{ + cache->ref_count ++; + + return cache; +} + +void +_gtk_icon_cache_unref (GtkIconCache *cache) +{ + cache->ref_count --; + + if (cache->ref_count == 0) + { + GTK_NOTE (ICONTHEME, + g_print ("unmapping icon cache\n")); + + munmap (cache->buffer, cache->size); + g_free (cache); + } +} + +GtkIconCache * +_gtk_icon_cache_new_for_path (const gchar *path) +{ + gchar *cache_filename; + gint fd; + struct stat st; + struct stat path_st; + gchar *buffer; + GtkIconCache *cache = NULL; + + if (g_getenv ("GTK_NO_ICON_CACHE")) + return NULL; + + /* Check if we have a cache file */ + cache_filename = g_build_filename (path, "icon-theme.cache", NULL); + + GTK_NOTE (ICONTHEME, + g_print ("look for cache in %s\n", path)); + + if (!g_file_test (cache_filename, G_FILE_TEST_IS_REGULAR)) + { + g_free (cache_filename); + return NULL; + }; + + /* Open the file and mmap it */ + fd = open (cache_filename, O_RDONLY); + + g_free (cache_filename); + + if (fd < 0) + return NULL; + + if (fstat (fd, &st) < 0) + goto done; + + if (stat (path, &path_st) < 0) + goto done; + + /* Verify cache is uptodate */ + if (st.st_mtime < path_st.st_mtime) + { + GTK_NOTE (ICONTHEME, + g_print ("cache outdated\n")); + goto done; + } + + buffer = mmap (0, st.st_size, PROT_READ, MAP_SHARED, fd, 0); + + if (buffer == MAP_FAILED) + goto done; + + /* Verify version */ + if (GET_UINT16 (buffer, 0) != MAJOR_VERSION || + GET_UINT16 (buffer, 2) != MINOR_VERSION) + { + munmap (buffer, st.st_size); + GTK_NOTE (ICONTHEME, + g_print ("wrong cache version\n")); + goto done; + } + + GTK_NOTE (ICONTHEME, + g_print ("found cache for %s\n", path)); + + cache = g_new0 (GtkIconCache, 1); + cache->ref_count = 1; + cache->buffer = buffer; + cache->size = st.st_size; + + done: + close (fd); + + return cache; +} + +static int +get_directory_index (GtkIconCache *cache, + const gchar *directory) +{ + guint32 dir_list_offset; + int n_dirs; + int i; + + dir_list_offset = GET_UINT32 (cache->buffer, 8); + + n_dirs = GET_UINT32 (cache->buffer, dir_list_offset); + + for (i = 0; i < n_dirs; i++) + { + guint32 name_offset = GET_UINT32 (cache->buffer, dir_list_offset + 4 + 4 * i); + gchar *name = cache->buffer + name_offset; + if (strcmp (name, directory) == 0) + return i; + } + + return -1; +} + +gboolean +_gtk_icon_cache_has_directory (GtkIconCache *cache, + const gchar *directory) +{ + return get_directory_index (cache, directory) != -1; +} + +static guint +icon_name_hash (gconstpointer key) +{ + const char *p = key; + guint h = *p; + + if (h) + for (p += 1; *p != '\0'; p++) + h = (h << 5) - h + *p; + + return h; +} + +gint +_gtk_icon_cache_get_icon_flags (GtkIconCache *cache, + const gchar *icon_name, + const gchar *directory) +{ + guint32 hash_offset; + guint32 n_buckets; + guint32 chain_offset; + int hash, directory_index; + guint32 image_list_offset, n_images; + gboolean found = FALSE; + int i; + + hash_offset = GET_UINT32 (cache->buffer, 4); + n_buckets = GET_UINT32 (cache->buffer, hash_offset); + + hash = icon_name_hash (icon_name) % n_buckets; + + chain_offset = GET_UINT32 (cache->buffer, hash_offset + 4 + 4 * hash); + while (chain_offset != 0xffffffff) + { + guint32 name_offset = GET_UINT32 (cache->buffer, chain_offset + 4); + gchar *name = cache->buffer + name_offset; + + if (strcmp (name, icon_name) == 0) + { + found = TRUE; + break; + } + + chain_offset = GET_UINT32 (cache->buffer, chain_offset); + } + + if (!found) + return 0; + + /* We've found an icon list, now check if we have the right icon in it */ + directory_index = get_directory_index (cache, directory); + image_list_offset = GET_UINT32 (cache->buffer, chain_offset + 8); + n_images = GET_UINT32 (cache->buffer, image_list_offset); + + for (i = 0; i < n_images; i++) + { + if (GET_UINT16 (cache->buffer, image_list_offset + 4 + 8 * i) == + directory_index) + return GET_UINT16 (cache->buffer, image_list_offset + 4 + 8 * i + 2); + } + + return 0; +} + +void +_gtk_icon_cache_add_icons (GtkIconCache *cache, + const gchar *directory, + GHashTable *hash_table) +{ + int directory_index; + guint32 hash_offset, n_buckets; + guint32 chain_offset; + guint32 image_list_offset, n_images; + int i, j; + + directory_index = get_directory_index (cache, directory); + + if (directory_index == -1) + return; + + hash_offset = GET_UINT32 (cache->buffer, 4); + n_buckets = GET_UINT32 (cache->buffer, hash_offset); + + for (i = 0; i < n_buckets; i++) + { + chain_offset = GET_UINT32 (cache->buffer, hash_offset + 4 + 4 * i); + while (chain_offset != 0xffffffff) + { + guint32 name_offset = GET_UINT32 (cache->buffer, chain_offset + 4); + gchar *name = cache->buffer + name_offset; + + image_list_offset = GET_UINT32 (cache->buffer, chain_offset + 8); + n_images = GET_UINT32 (cache->buffer, image_list_offset); + + for (j = 0; j < n_images; j++) + { + if (GET_UINT16 (cache->buffer, image_list_offset + 4 + 8 * j) == + directory_index) + g_hash_table_insert (hash_table, name, NULL); + } + + chain_offset = GET_UINT32 (cache->buffer, chain_offset); + } + } + +} |