2 * Copyright (C) 2004 Anders Carlsson <andersca@gnome.org>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
22 #include "gtkiconcache.h"
23 #include <glib/gstdio.h>
32 #include <sys/types.h>
44 #define MAJOR_VERSION 1
45 #define MINOR_VERSION 0
47 #define GET_UINT16(cache, offset) (GUINT16_FROM_BE (*(guint16 *)((cache) + (offset))))
48 #define GET_UINT32(cache, offset) (GUINT32_FROM_BE (*(guint32 *)((cache) + (offset))))
50 struct _GtkIconCache {
61 _gtk_icon_cache_ref (GtkIconCache *cache)
68 _gtk_icon_cache_unref (GtkIconCache *cache)
72 if (cache->ref_count == 0)
75 g_print ("unmapping icon cache\n"));
77 munmap (cache->buffer, cache->size);
80 UnmapViewOfFile (cache->buffer);
81 CloseHandle (cache->handle);
88 _gtk_icon_cache_new_for_path (const gchar *path)
90 GtkIconCache *cache = NULL;
92 #if defined(HAVE_MMAP) || defined(G_OS_WIN32)
93 gchar *cache_filename;
102 if (g_getenv ("GTK_NO_ICON_CACHE"))
105 /* Check if we have a cache file */
106 cache_filename = g_build_filename (path, "icon-theme.cache", NULL);
109 g_print ("look for cache in %s\n", path));
111 if (!g_file_test (cache_filename, G_FILE_TEST_IS_REGULAR))
114 if (g_stat (path, &path_st) < 0)
117 /* Open the file and map it into memory */
118 fd = g_open (cache_filename, O_RDONLY|_O_BINARY, 0);
122 g_free (cache_filename);
126 if (fstat (fd, &st) < 0 || st.st_size < 4)
129 /* Verify cache is uptodate */
130 if (st.st_mtime < path_st.st_mtime)
133 g_print ("cache outdated\n"));
138 buffer = (gchar *) mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
140 if (buffer == MAP_FAILED)
143 handle = CreateFileMapping (_get_osfhandle (fd), NULL, PAGE_READONLY,
148 buffer = MapViewOfFile (handle, FILE_MAP_READ, 0, 0, 0);
152 CloseHandle (handle);
158 if (GET_UINT16 (buffer, 0) != MAJOR_VERSION ||
159 GET_UINT16 (buffer, 2) != MINOR_VERSION)
162 munmap (buffer, st.st_size);
164 UnmapViewOfFile (buffer);
165 CloseHandle (handle);
168 g_print ("wrong cache version\n"));
173 g_print ("found cache for %s\n", path));
175 cache = g_new0 (GtkIconCache, 1);
176 cache->ref_count = 1;
177 cache->buffer = buffer;
179 cache->handle = handle;
181 cache->size = st.st_size;
183 g_free (cache_filename);
187 #endif /* HAVE_MMAP || G_OS_WIN32 */
193 get_directory_index (GtkIconCache *cache,
194 const gchar *directory)
196 guint32 dir_list_offset;
200 dir_list_offset = GET_UINT32 (cache->buffer, 8);
202 n_dirs = GET_UINT32 (cache->buffer, dir_list_offset);
204 for (i = 0; i < n_dirs; i++)
206 guint32 name_offset = GET_UINT32 (cache->buffer, dir_list_offset + 4 + 4 * i);
207 gchar *name = cache->buffer + name_offset;
208 if (strcmp (name, directory) == 0)
216 _gtk_icon_cache_has_directory (GtkIconCache *cache,
217 const gchar *directory)
219 return get_directory_index (cache, directory) != -1;
223 icon_name_hash (gconstpointer key)
229 for (p += 1; *p != '\0'; p++)
230 h = (h << 5) - h + *p;
236 _gtk_icon_cache_get_icon_flags (GtkIconCache *cache,
237 const gchar *icon_name,
238 const gchar *directory)
242 guint32 chain_offset;
243 int hash, directory_index;
244 guint32 image_list_offset, n_images;
245 gboolean found = FALSE;
248 hash_offset = GET_UINT32 (cache->buffer, 4);
249 n_buckets = GET_UINT32 (cache->buffer, hash_offset);
251 hash = icon_name_hash (icon_name) % n_buckets;
253 chain_offset = GET_UINT32 (cache->buffer, hash_offset + 4 + 4 * hash);
254 while (chain_offset != 0xffffffff)
256 guint32 name_offset = GET_UINT32 (cache->buffer, chain_offset + 4);
257 gchar *name = cache->buffer + name_offset;
259 if (strcmp (name, icon_name) == 0)
265 chain_offset = GET_UINT32 (cache->buffer, chain_offset);
271 /* We've found an icon list, now check if we have the right icon in it */
272 directory_index = get_directory_index (cache, directory);
273 image_list_offset = GET_UINT32 (cache->buffer, chain_offset + 8);
274 n_images = GET_UINT32 (cache->buffer, image_list_offset);
276 for (i = 0; i < n_images; i++)
278 if (GET_UINT16 (cache->buffer, image_list_offset + 4 + 8 * i) ==
280 return GET_UINT16 (cache->buffer, image_list_offset + 4 + 8 * i + 2);
287 _gtk_icon_cache_add_icons (GtkIconCache *cache,
288 const gchar *directory,
289 GHashTable *hash_table)
292 guint32 hash_offset, n_buckets;
293 guint32 chain_offset;
294 guint32 image_list_offset, n_images;
297 directory_index = get_directory_index (cache, directory);
299 if (directory_index == -1)
302 hash_offset = GET_UINT32 (cache->buffer, 4);
303 n_buckets = GET_UINT32 (cache->buffer, hash_offset);
305 for (i = 0; i < n_buckets; i++)
307 chain_offset = GET_UINT32 (cache->buffer, hash_offset + 4 + 4 * i);
308 while (chain_offset != 0xffffffff)
310 guint32 name_offset = GET_UINT32 (cache->buffer, chain_offset + 4);
311 gchar *name = cache->buffer + name_offset;
313 image_list_offset = GET_UINT32 (cache->buffer, chain_offset + 8);
314 n_images = GET_UINT32 (cache->buffer, image_list_offset);
316 for (j = 0; j < n_images; j++)
318 if (GET_UINT16 (cache->buffer, image_list_offset + 4 + 8 * j) ==
320 g_hash_table_insert (hash_table, name, NULL);
323 chain_offset = GET_UINT32 (cache->buffer, chain_offset);
329 _gtk_icon_cache_has_icon (GtkIconCache *cache,
330 const gchar *icon_name)
334 guint32 chain_offset;
337 hash_offset = GET_UINT32 (cache->buffer, 4);
338 n_buckets = GET_UINT32 (cache->buffer, hash_offset);
340 hash = icon_name_hash (icon_name) % n_buckets;
342 chain_offset = GET_UINT32 (cache->buffer, hash_offset + 4 + 4 * hash);
343 while (chain_offset != 0xffffffff)
345 guint32 name_offset = GET_UINT32 (cache->buffer, chain_offset + 4);
346 gchar *name = cache->buffer + name_offset;
348 if (strcmp (name, icon_name) == 0)
351 chain_offset = GET_UINT32 (cache->buffer, chain_offset);