forked from AuroraMiddleware/gtk
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.
This commit is contained in:
parent
b087f76551
commit
6fc2b8118a
26
ChangeLog
26
ChangeLog
@ -1,3 +1,29 @@
|
||||
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.
|
||||
|
||||
2004-10-19 Matthias Clasen <mclasen@redhat.com>
|
||||
|
||||
* tests/testicontheme.c: Set the locale, tidy up output.
|
||||
|
@ -1,3 +1,29 @@
|
||||
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.
|
||||
|
||||
2004-10-19 Matthias Clasen <mclasen@redhat.com>
|
||||
|
||||
* tests/testicontheme.c: Set the locale, tidy up output.
|
||||
|
@ -1,3 +1,29 @@
|
||||
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.
|
||||
|
||||
2004-10-19 Matthias Clasen <mclasen@redhat.com>
|
||||
|
||||
* tests/testicontheme.c: Set the locale, tidy up output.
|
||||
|
@ -1,3 +1,29 @@
|
||||
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.
|
||||
|
||||
2004-10-19 Matthias Clasen <mclasen@redhat.com>
|
||||
|
||||
* tests/testicontheme.c: Set the locale, tidy up output.
|
||||
|
@ -1,3 +1,11 @@
|
||||
2004-10-19 Matthias Clasen <mclasen@redhat.com>
|
||||
|
||||
* gtk/gtk-update-icon-cache.xml: A man page for gtk-update-icon-cache.
|
||||
|
||||
* gtk/gtk-docs.sgml: Add it here
|
||||
|
||||
* gtk/Makefile.am (man_MANS): ...and here.
|
||||
|
||||
2004-10-16 Matthias Clasen <mclasen@redhat.com>
|
||||
|
||||
* gtk/glossary.xml: Additions.
|
||||
|
@ -104,6 +104,7 @@ content_files = \
|
||||
windows.sgml \
|
||||
x11.sgml \
|
||||
gtk-query-immodules-2.0.xml \
|
||||
gtk-update-icon-cache.xml \
|
||||
visual_index.xml
|
||||
|
||||
# Images to copy into HTML directory
|
||||
@ -240,7 +241,7 @@ EXTRA_DIST += version.xml.in
|
||||
|
||||
########################################################################
|
||||
|
||||
man_MANS = gtk-query-immodules-2.0.1
|
||||
man_MANS = gtk-query-immodules-2.0.1 gtk-update-icon-cache.1
|
||||
|
||||
if ENABLE_MAN
|
||||
|
||||
|
@ -193,6 +193,7 @@
|
||||
<!ENTITY gtk-migrating-GtkComboBox SYSTEM "migrating-GtkComboBox.sgml">
|
||||
<!ENTITY version SYSTEM "version.xml">
|
||||
<!ENTITY gtk-query-immodules SYSTEM "gtk-query-immodules-2.0.xml">
|
||||
<!ENTITY gtk-update-icon-cache SYSTEM "gtk-update-icon-cache.xml">
|
||||
<!ENTITY gtk-glossary SYSTEM "glossary.xml">
|
||||
]>
|
||||
<book id="index">
|
||||
@ -576,6 +577,7 @@ that is, GUI components such as <link linkend="GtkButton">GtkButton</link> or
|
||||
<title>GTK+ Tools</title>
|
||||
|
||||
>k-query-immodules;
|
||||
>k-update-icon-cache;
|
||||
</part>
|
||||
|
||||
>k-glossary;
|
||||
|
49
docs/reference/gtk/gtk-update-icon-cache.1
Normal file
49
docs/reference/gtk/gtk-update-icon-cache.1
Normal file
@ -0,0 +1,49 @@
|
||||
.\"Generated by db2man.xsl. Don't modify this, modify the source.
|
||||
.de Sh \" Subsection
|
||||
.br
|
||||
.if t .Sp
|
||||
.ne 5
|
||||
.PP
|
||||
\fB\\$1\fR
|
||||
.PP
|
||||
..
|
||||
.de Sp \" Vertical space (when we can't use .PP)
|
||||
.if t .sp .5v
|
||||
.if n .sp
|
||||
..
|
||||
.de Ip \" List item
|
||||
.br
|
||||
.ie \\n(.$>=3 .ne \\$3
|
||||
.el .ne 3
|
||||
.IP "\\$1" \\$2
|
||||
..
|
||||
.TH "GTK-UPDATE-ICON-CA" 1 "" "" ""
|
||||
.SH NAME
|
||||
gtk-update-icon-cache \- Icon theme caching utility
|
||||
.SH "SYNOPSIS"
|
||||
.ad l
|
||||
.hy 0
|
||||
.HP 22
|
||||
\fBgtk\-update\-icon\-cache\fR [\-\-force] {iconpath}
|
||||
.ad
|
||||
.hy
|
||||
|
||||
.SH "DESCRIPTION"
|
||||
|
||||
.PP
|
||||
\fBgtk\-update\-icon\-cache\fR creates mmap()able cache files for icon themes\&.
|
||||
|
||||
.PP
|
||||
If expects to be given the path to a icon theme directory, e\&.g\&. \fI/usr/share/icons/hicolor\fR, and writes a \fIicon\-theme\&.cache\fR containing cached information about the icons in the directory tree below the given directory\&.
|
||||
|
||||
.PP
|
||||
GTK+ can use the cache files created by \fBgtk\-update\-icon\-cache\fR to avoid a lot of system call and disk seek overhead when the application starts\&. Since the format of the cache files allows them to be mmap()ed shared between multiple applications, the overall memory consumption is reduced as well\&.
|
||||
|
||||
.PP
|
||||
If called with the [\-\-force] argument, \fBgtk\-update\-icon\-cache\fR will overwrite an existing cache file even if it appears to be uptodate\&.
|
||||
|
||||
.SH "BUGS"
|
||||
|
||||
.PP
|
||||
None known yet\&.
|
||||
|
53
docs/reference/gtk/gtk-update-icon-cache.xml
Normal file
53
docs/reference/gtk/gtk-update-icon-cache.xml
Normal file
@ -0,0 +1,53 @@
|
||||
<refentry id="gtk-update-icon-cache">
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>gtk-update-icon-cache</refentrytitle>
|
||||
<manvolnum>1</manvolnum>
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>gtk-update-icon-cache</refname>
|
||||
<refpurpose>Icon theme caching utility</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<cmdsynopsis>
|
||||
<command>gtk-update-icon-cache</command>
|
||||
<arg choice="opt">--force</arg>
|
||||
<arg choice="req">iconpath</arg>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1><title>Description</title>
|
||||
<para>
|
||||
<command>gtk-update-icon-cache</command> creates mmap()able cache files for
|
||||
icon themes.
|
||||
</para>
|
||||
<para>
|
||||
If expects to be given the path to a icon theme directory, e.g.
|
||||
<filename>/usr/share/icons/hicolor</filename>, and writes a
|
||||
<filename>icon-theme.cache</filename> containing cached information
|
||||
about the icons in the directory tree below the given directory.
|
||||
</para>
|
||||
<para>
|
||||
GTK+ can use the cache files created by <command>gtk-update-icon-cache</command>
|
||||
to avoid a lot of system call and disk seek overhead when the application starts.
|
||||
Since the format of the cache files allows them to be mmap()ed shared between
|
||||
multiple applications, the overall memory consumption is reduced as well.
|
||||
</para>
|
||||
<para>
|
||||
If called with the --force argument,
|
||||
<command>gtk-update-icon-cache</command> will overwrite an existing cache
|
||||
file even if it appears to be uptodate.
|
||||
</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1><title>Bugs</title>
|
||||
<para>
|
||||
None known yet.
|
||||
</para>
|
||||
</refsect1>
|
||||
|
||||
</refentry>
|
||||
|
||||
|
@ -297,6 +297,7 @@ gtk_private_h_sources = \
|
||||
gtkfilechooserutils.h \
|
||||
gtkfilesystemunix.h \
|
||||
gtkfilesystemmodel.h \
|
||||
gtkiconcache.h \
|
||||
gtkpathbar.h \
|
||||
gtkrbtree.h \
|
||||
gtksequence.h \
|
||||
@ -393,6 +394,7 @@ gtk_c_sources = \
|
||||
gtkhsv.c \
|
||||
gtkhsv.h \
|
||||
gtkiconfactory.c \
|
||||
gtkiconcache.c \
|
||||
gtkicontheme.c \
|
||||
gtkiconthemeparser.c \
|
||||
gtkiconthemeparser.h \
|
||||
@ -701,13 +703,19 @@ LDADDS = \
|
||||
#
|
||||
# Installed tools
|
||||
#
|
||||
bin_PROGRAMS = gtk-query-immodules-2.0
|
||||
bin_PROGRAMS = gtk-query-immodules-2.0 gtk-update-icon-cache
|
||||
|
||||
gtk_query_immodules_2_0_DEPENDENCIES = $(DEPS)
|
||||
gtk_query_immodules_2_0_LDADD = $(LDADDS)
|
||||
|
||||
gtk_query_immodules_2_0_SOURCES = queryimmodules.c
|
||||
|
||||
|
||||
gtk_update_icon_cache_DEPENDENCIES = $(DEPS)
|
||||
gtk_update_icon_cache_LDADD = $(LDADDS)
|
||||
|
||||
gtk_update_icon_cache_SOURCES = updateiconcache.c
|
||||
|
||||
.PHONY: files test test-debug
|
||||
|
||||
files:
|
||||
|
@ -40,7 +40,8 @@ typedef enum {
|
||||
GTK_DEBUG_KEYBINDINGS = 1 << 5,
|
||||
GTK_DEBUG_MULTIHEAD = 1 << 6,
|
||||
GTK_DEBUG_MODULES = 1 << 7,
|
||||
GTK_DEBUG_GEOMETRY = 1 << 8
|
||||
GTK_DEBUG_GEOMETRY = 1 << 8,
|
||||
GTK_DEBUG_ICONTHEME = 1 << 9
|
||||
} GtkDebugFlag;
|
||||
|
||||
#ifdef G_ENABLE_DEBUG
|
||||
|
280
gtk/gtkiconcache.c
Normal file
280
gtk/gtkiconcache.c
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
41
gtk/gtkiconcache.h
Normal file
41
gtk/gtkiconcache.h
Normal file
@ -0,0 +1,41 @@
|
||||
/* gtkiconcache.h
|
||||
* 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.
|
||||
*/
|
||||
#ifndef __GTK_ICON_CACHE_H__
|
||||
#define __GTK_ICON_CACHE_H__
|
||||
|
||||
#include <gdk-pixbuf/gdk-pixbuf.h>
|
||||
|
||||
typedef struct _GtkIconCache GtkIconCache;
|
||||
|
||||
GtkIconCache *_gtk_icon_cache_new_for_path (const gchar *path);
|
||||
gboolean _gtk_icon_cache_has_directory (GtkIconCache *cache,
|
||||
const gchar *directory);
|
||||
void _gtk_icon_cache_add_icons (GtkIconCache *cache,
|
||||
const gchar *directory,
|
||||
GHashTable *hash_table);
|
||||
|
||||
gint _gtk_icon_cache_get_icon_flags (GtkIconCache *cache,
|
||||
const gchar *icon_name,
|
||||
const gchar *directory);
|
||||
|
||||
GtkIconCache *_gtk_icon_cache_ref (GtkIconCache *cache);
|
||||
void _gtk_icon_cache_unref (GtkIconCache *cache);
|
||||
|
||||
|
||||
#endif /* __GTK_ICON_CACHE_H__ */
|
@ -37,6 +37,7 @@
|
||||
|
||||
#include "gtkicontheme.h"
|
||||
#include "gtkiconthemeparser.h"
|
||||
#include "gtkiconcache.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtksettings.h"
|
||||
#include "gtkprivate.h"
|
||||
@ -61,7 +62,8 @@ typedef enum
|
||||
ICON_SUFFIX_NONE = 0,
|
||||
ICON_SUFFIX_XPM = 1 << 0,
|
||||
ICON_SUFFIX_SVG = 1 << 1,
|
||||
ICON_SUFFIX_PNG = 1 << 2
|
||||
ICON_SUFFIX_PNG = 1 << 2,
|
||||
HAS_ICON_FILE = 1 << 3
|
||||
} IconSuffix;
|
||||
|
||||
|
||||
@ -81,7 +83,8 @@ struct _GtkIconThemePrivate
|
||||
*/
|
||||
GList *themes;
|
||||
GHashTable *unthemed_icons;
|
||||
|
||||
GList *unthemed_icons_caches;
|
||||
|
||||
/* Note: The keys of this hashtable are owned by the
|
||||
* themedir and unthemed hashtables.
|
||||
*/
|
||||
@ -132,6 +135,11 @@ typedef struct
|
||||
char *comment;
|
||||
char *example;
|
||||
|
||||
/* Icon caches, per theme directory, key is NULL if
|
||||
* no cache exists for that directory
|
||||
*/
|
||||
GHashTable *icon_caches;
|
||||
|
||||
/* In search order */
|
||||
GList *dirs;
|
||||
} IconTheme;
|
||||
@ -158,6 +166,9 @@ typedef struct
|
||||
int threshold;
|
||||
|
||||
char *dir;
|
||||
char *subdir;
|
||||
|
||||
GtkIconCache *cache;
|
||||
|
||||
GHashTable *icons;
|
||||
GHashTable *icon_data;
|
||||
@ -204,6 +215,14 @@ static void do_theme_change (GtkIconTheme *icon_theme);
|
||||
static void blow_themes (GtkIconTheme *icon_themes);
|
||||
|
||||
static void icon_data_free (GtkIconData *icon_data);
|
||||
static void load_icon_data (IconThemeDir *dir,
|
||||
const char *path,
|
||||
const char *name);
|
||||
|
||||
static IconSuffix theme_dir_get_icon_suffix (IconThemeDir *dir,
|
||||
const gchar *icon_name,
|
||||
gboolean *has_icon_file);
|
||||
|
||||
|
||||
static GtkIconInfo *icon_info_new (void);
|
||||
static GtkIconInfo *icon_info_new_builtin (BuiltinIcon *icon);
|
||||
@ -499,8 +518,12 @@ pixbuf_supports_svg ()
|
||||
{
|
||||
GSList *formats = gdk_pixbuf_get_formats ();
|
||||
GSList *tmp_list;
|
||||
gboolean found_svg = FALSE;
|
||||
static gboolean found_svg = FALSE;
|
||||
static gboolean value_known = FALSE;
|
||||
|
||||
if (value_known)
|
||||
return found_svg;
|
||||
|
||||
for (tmp_list = formats; tmp_list && !found_svg; tmp_list = tmp_list->next)
|
||||
{
|
||||
gchar **mime_types = gdk_pixbuf_format_get_mime_types (tmp_list->data);
|
||||
@ -516,7 +539,8 @@ pixbuf_supports_svg ()
|
||||
}
|
||||
|
||||
g_slist_free (formats);
|
||||
|
||||
value_known = TRUE;
|
||||
|
||||
return found_svg;
|
||||
}
|
||||
|
||||
@ -553,7 +577,8 @@ gtk_icon_theme_init (GtkIconTheme *icon_theme)
|
||||
priv->themes_valid = FALSE;
|
||||
priv->themes = NULL;
|
||||
priv->unthemed_icons = NULL;
|
||||
|
||||
priv->unthemed_icons_caches = NULL;
|
||||
|
||||
priv->pixbuf_supports_svg = pixbuf_supports_svg ();
|
||||
}
|
||||
|
||||
@ -569,6 +594,8 @@ do_theme_change (GtkIconTheme *icon_theme)
|
||||
{
|
||||
GtkIconThemePrivate *priv = icon_theme->priv;
|
||||
|
||||
GTK_NOTE (ICONTHEME,
|
||||
g_print ("change to icon theme \"%s\"\n", priv->current_theme));
|
||||
blow_themes (icon_theme);
|
||||
g_signal_emit (icon_theme, signal_changed, 0);
|
||||
|
||||
@ -579,6 +606,16 @@ do_theme_change (GtkIconTheme *icon_theme)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
free_cache (gpointer data,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkIconCache *cache = (GtkIconCache *)data;
|
||||
|
||||
if (cache)
|
||||
_gtk_icon_cache_unref (cache);
|
||||
}
|
||||
|
||||
static void
|
||||
blow_themes (GtkIconTheme *icon_theme)
|
||||
{
|
||||
@ -592,9 +629,13 @@ blow_themes (GtkIconTheme *icon_theme)
|
||||
g_list_foreach (priv->dir_mtimes, (GFunc)free_dir_mtime, NULL);
|
||||
g_list_free (priv->dir_mtimes);
|
||||
g_hash_table_destroy (priv->unthemed_icons);
|
||||
if (priv->unthemed_icons_caches)
|
||||
g_list_foreach (priv->unthemed_icons_caches, free_cache, NULL);
|
||||
g_list_free (priv->unthemed_icons_caches);
|
||||
}
|
||||
priv->themes = NULL;
|
||||
priv->unthemed_icons = NULL;
|
||||
priv->unthemed_icons_caches = NULL;
|
||||
priv->dir_mtimes = NULL;
|
||||
priv->all_icons = NULL;
|
||||
priv->themes_valid = FALSE;
|
||||
@ -830,7 +871,7 @@ insert_theme (GtkIconTheme *icon_theme, const char *theme_name)
|
||||
struct stat stat_buf;
|
||||
|
||||
priv = icon_theme->priv;
|
||||
|
||||
|
||||
for (l = priv->themes; l != NULL; l = l->next)
|
||||
{
|
||||
theme = l->data;
|
||||
@ -910,6 +951,7 @@ insert_theme (GtkIconTheme *icon_theme, const char *theme_name)
|
||||
|
||||
dirs = g_strsplit (directories, ",", 0);
|
||||
|
||||
theme->icon_caches = NULL;
|
||||
theme->dirs = NULL;
|
||||
for (i = 0; dirs[i] != NULL; i++)
|
||||
theme_subdir_load (icon_theme, theme, theme_file, dirs[i]);
|
||||
@ -973,13 +1015,25 @@ load_themes (GtkIconTheme *icon_theme)
|
||||
/* Always look in the "default" icon theme */
|
||||
insert_theme (icon_theme, DEFAULT_THEME_NAME);
|
||||
priv->themes = g_list_reverse (priv->themes);
|
||||
|
||||
|
||||
|
||||
priv->unthemed_icons = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||
g_free, (GDestroyNotify)free_unthemed_icon);
|
||||
|
||||
for (base = 0; base < icon_theme->priv->search_path_len; base++)
|
||||
{
|
||||
GtkIconCache *cache;
|
||||
dir = icon_theme->priv->search_path[base];
|
||||
|
||||
cache = _gtk_icon_cache_new_for_path (dir);
|
||||
|
||||
if (cache != NULL)
|
||||
{
|
||||
priv->unthemed_icons_caches = g_list_prepend (priv->unthemed_icons_caches, cache);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
gdir = g_dir_open (dir, 0, NULL);
|
||||
|
||||
if (gdir == NULL)
|
||||
@ -1036,7 +1090,7 @@ load_themes (GtkIconTheme *icon_theme)
|
||||
unthemed_icon->svg_filename = abs_file;
|
||||
else
|
||||
unthemed_icon->no_svg_filename = abs_file;
|
||||
|
||||
|
||||
g_hash_table_insert (priv->unthemed_icons,
|
||||
base_name,
|
||||
unthemed_icon);
|
||||
@ -1357,8 +1411,8 @@ gtk_icon_theme_get_icon_sizes (GtkIconTheme *icon_theme,
|
||||
for (d = theme->dirs; d; d = d->next)
|
||||
{
|
||||
IconThemeDir *dir = d->data;
|
||||
|
||||
suffix = GPOINTER_TO_UINT (g_hash_table_lookup (dir->icons, icon_name));
|
||||
|
||||
suffix = theme_dir_get_icon_suffix (dir, icon_name, NULL);
|
||||
if (suffix != ICON_SUFFIX_NONE)
|
||||
{
|
||||
if (suffix == ICON_SUFFIX_SVG)
|
||||
@ -1566,16 +1620,25 @@ theme_destroy (IconTheme *theme)
|
||||
|
||||
g_list_foreach (theme->dirs, (GFunc)theme_dir_destroy, NULL);
|
||||
g_list_free (theme->dirs);
|
||||
|
||||
if (theme->icon_caches)
|
||||
g_hash_table_destroy (theme->icon_caches);
|
||||
|
||||
g_free (theme);
|
||||
}
|
||||
|
||||
static void
|
||||
theme_dir_destroy (IconThemeDir *dir)
|
||||
{
|
||||
g_hash_table_destroy (dir->icons);
|
||||
if (dir->cache)
|
||||
_gtk_icon_cache_unref (dir->cache);
|
||||
else
|
||||
g_hash_table_destroy (dir->icons);
|
||||
|
||||
if (dir->icon_data)
|
||||
g_hash_table_destroy (dir->icon_data);
|
||||
g_free (dir->dir);
|
||||
g_free (dir->subdir);
|
||||
g_free (dir);
|
||||
}
|
||||
|
||||
@ -1663,6 +1726,31 @@ best_suffix (IconSuffix suffix,
|
||||
return ICON_SUFFIX_NONE;
|
||||
}
|
||||
|
||||
|
||||
static IconSuffix
|
||||
theme_dir_get_icon_suffix (IconThemeDir *dir,
|
||||
const gchar *icon_name,
|
||||
gboolean *has_icon_file)
|
||||
{
|
||||
IconSuffix suffix;
|
||||
|
||||
if (dir->cache)
|
||||
{
|
||||
suffix = (IconSuffix)_gtk_icon_cache_get_icon_flags (dir->cache,
|
||||
icon_name,
|
||||
dir->subdir);
|
||||
|
||||
if (has_icon_file)
|
||||
{
|
||||
*has_icon_file = suffix & HAS_ICON_FILE;
|
||||
}
|
||||
}
|
||||
else
|
||||
suffix = GPOINTER_TO_UINT (g_hash_table_lookup (dir->icons, icon_name));
|
||||
|
||||
return suffix;
|
||||
}
|
||||
|
||||
static GtkIconInfo *
|
||||
theme_lookup_icon (IconTheme *theme,
|
||||
const char *icon_name,
|
||||
@ -1700,7 +1788,7 @@ theme_lookup_icon (IconTheme *theme,
|
||||
{
|
||||
dir = l->data;
|
||||
|
||||
suffix = GPOINTER_TO_UINT (g_hash_table_lookup (dir->icons, icon_name));
|
||||
suffix = theme_dir_get_icon_suffix (dir, icon_name, NULL);
|
||||
|
||||
if (suffix != ICON_SUFFIX_NONE &&
|
||||
(allow_svg || suffix != ICON_SUFFIX_SVG))
|
||||
@ -1744,8 +1832,9 @@ theme_lookup_icon (IconTheme *theme,
|
||||
if (min_dir)
|
||||
{
|
||||
GtkIconInfo *icon_info = icon_info_new ();
|
||||
gboolean has_icon_file;
|
||||
|
||||
suffix = GPOINTER_TO_UINT (g_hash_table_lookup (min_dir->icons, icon_name));
|
||||
suffix = theme_dir_get_icon_suffix (min_dir, icon_name, &has_icon_file);
|
||||
suffix = best_suffix (suffix, allow_svg);
|
||||
g_assert (suffix != ICON_SUFFIX_NONE);
|
||||
|
||||
@ -1753,6 +1842,24 @@ theme_lookup_icon (IconTheme *theme,
|
||||
icon_info->filename = g_build_filename (min_dir->dir, file, NULL);
|
||||
g_free (file);
|
||||
|
||||
if (min_dir->cache && has_icon_file)
|
||||
{
|
||||
gchar *icon_file_name, *icon_file_path;
|
||||
|
||||
icon_file_name = g_strconcat (icon_name, ".icon", NULL);
|
||||
icon_file_path = g_build_filename (min_dir->dir, icon_file_name, NULL);
|
||||
|
||||
if (g_file_test (icon_file_path, G_FILE_TEST_IS_REGULAR))
|
||||
{
|
||||
if (min_dir->icon_data == NULL)
|
||||
min_dir->icon_data = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||
g_free, (GDestroyNotify)icon_data_free);
|
||||
load_icon_data (min_dir, icon_file_path, icon_file_name);
|
||||
}
|
||||
g_free (icon_file_name);
|
||||
g_free (icon_file_path);
|
||||
}
|
||||
|
||||
if (min_dir->icon_data != NULL)
|
||||
icon_info->data = g_hash_table_lookup (min_dir->icon_data, icon_name);
|
||||
|
||||
@ -1779,10 +1886,21 @@ theme_list_icons (IconTheme *theme, GHashTable *icons,
|
||||
|
||||
if (context == dir->context ||
|
||||
context == 0)
|
||||
g_hash_table_foreach (dir->icons,
|
||||
add_key_to_hash,
|
||||
icons);
|
||||
|
||||
{
|
||||
if (dir->cache)
|
||||
{
|
||||
_gtk_icon_cache_add_icons (dir->cache,
|
||||
dir->subdir,
|
||||
icons);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
g_hash_table_foreach (dir->icons,
|
||||
add_key_to_hash,
|
||||
icons);
|
||||
}
|
||||
}
|
||||
l = l->next;
|
||||
}
|
||||
}
|
||||
@ -1889,7 +2007,9 @@ scan_directory (GtkIconThemePrivate *icon_theme,
|
||||
char *base_name, *dot;
|
||||
char *path;
|
||||
IconSuffix suffix, hash_suffix;
|
||||
|
||||
|
||||
GTK_NOTE (ICONTHEME,
|
||||
g_print ("scanning directory %s\n", full_dir));
|
||||
dir->icons = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||
g_free, NULL);
|
||||
|
||||
@ -1998,11 +2118,35 @@ theme_subdir_load (GtkIconTheme *icon_theme,
|
||||
|
||||
for (base = 0; base < icon_theme->priv->search_path_len; base++)
|
||||
{
|
||||
GtkIconCache *cache;
|
||||
gchar *theme_path;
|
||||
|
||||
full_dir = g_build_filename (icon_theme->priv->search_path[base],
|
||||
theme->name,
|
||||
subdir,
|
||||
NULL);
|
||||
if (g_file_test (full_dir, G_FILE_TEST_IS_DIR))
|
||||
|
||||
/* First, see if we have a cache for the directory */
|
||||
if (!theme->icon_caches)
|
||||
theme->icon_caches = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||
g_free, (GDestroyNotify)free_cache);
|
||||
|
||||
theme_path = g_build_filename (icon_theme->priv->search_path[base],
|
||||
theme->name,
|
||||
NULL);
|
||||
|
||||
if (!g_hash_table_lookup_extended (theme->icon_caches, theme_path,
|
||||
NULL, (gpointer)&cache))
|
||||
{
|
||||
/* This will return NULL if the cache doesn't exist or is outdated */
|
||||
cache = _gtk_icon_cache_new_for_path (theme_path);
|
||||
|
||||
g_hash_table_insert (theme->icon_caches, g_strdup (theme_path), cache);
|
||||
}
|
||||
|
||||
g_free (theme_path);
|
||||
|
||||
if (cache != NULL || g_file_test (full_dir, G_FILE_TEST_IS_DIR))
|
||||
{
|
||||
dir = g_new (IconThemeDir, 1);
|
||||
dir->type = type;
|
||||
@ -2013,8 +2157,14 @@ theme_subdir_load (GtkIconTheme *icon_theme,
|
||||
dir->threshold = threshold;
|
||||
dir->dir = full_dir;
|
||||
dir->icon_data = NULL;
|
||||
|
||||
scan_directory (icon_theme->priv, dir, full_dir);
|
||||
dir->subdir = g_strdup (subdir);
|
||||
if (cache != NULL)
|
||||
dir->cache = _gtk_icon_cache_ref (cache);
|
||||
else
|
||||
{
|
||||
dir->cache = NULL;
|
||||
scan_directory (icon_theme->priv, dir, full_dir);
|
||||
}
|
||||
|
||||
theme->dirs = g_list_prepend (theme->dirs, dir);
|
||||
}
|
||||
|
@ -158,7 +158,8 @@ static const GDebugKey gtk_debug_keys[] = {
|
||||
{"keybindings", GTK_DEBUG_KEYBINDINGS},
|
||||
{"multihead", GTK_DEBUG_MULTIHEAD},
|
||||
{"modules", GTK_DEBUG_MODULES},
|
||||
{"geometry", GTK_DEBUG_GEOMETRY}
|
||||
{"geometry", GTK_DEBUG_GEOMETRY},
|
||||
{"icontheme", GTK_DEBUG_ICONTHEME}
|
||||
};
|
||||
|
||||
static const guint gtk_ndebug_keys = sizeof (gtk_debug_keys) / sizeof (GDebugKey);
|
||||
|
628
gtk/updateiconcache.c
Normal file
628
gtk/updateiconcache.c
Normal file
@ -0,0 +1,628 @@
|
||||
/* updateiconcache.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 <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <utime.h>
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#define CACHE_NAME "icon-theme.cache"
|
||||
|
||||
#define HAS_SUFFIX_XPM (1 << 0)
|
||||
#define HAS_SUFFIX_SVG (1 << 1)
|
||||
#define HAS_SUFFIX_PNG (1 << 2)
|
||||
#define HAS_ICON_FILE (1 << 3)
|
||||
|
||||
#define MAJOR_VERSION 1
|
||||
#define MINOR_VERSION 0
|
||||
#define HASH_OFFSET 12
|
||||
|
||||
#define ALIGN_VALUE(this, boundary) \
|
||||
(( ((unsigned long)(this)) + (((unsigned long)(boundary)) -1)) & (~(((unsigned long)(boundary))-1)))
|
||||
|
||||
gboolean
|
||||
is_cache_up_to_date (const gchar *path)
|
||||
{
|
||||
struct stat path_stat, cache_stat;
|
||||
gchar *cache_path;
|
||||
int retval;
|
||||
|
||||
retval = stat (path, &path_stat);
|
||||
|
||||
if (retval < 0)
|
||||
{
|
||||
/* We can't stat the path,
|
||||
* assume we have a updated cache */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
cache_path = g_build_filename (path, CACHE_NAME, NULL);
|
||||
retval = stat (cache_path, &cache_stat);
|
||||
g_free (cache_path);
|
||||
|
||||
if (retval < 0 && errno == ENOENT)
|
||||
{
|
||||
/* Cache file not found */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Check mtime */
|
||||
return cache_stat.st_mtime <= path_stat.st_mtime;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int flags;
|
||||
int dir_index;
|
||||
} Image;
|
||||
|
||||
static gboolean
|
||||
foreach_remove_func (gpointer key, gpointer value, gpointer user_data)
|
||||
{
|
||||
GHashTable *files = user_data;
|
||||
GList *list;
|
||||
gboolean free_key = FALSE;;
|
||||
|
||||
list = g_hash_table_lookup (files, key);
|
||||
if (list)
|
||||
free_key = TRUE;
|
||||
|
||||
list = g_list_prepend (list, value);
|
||||
g_hash_table_insert (files, key, list);
|
||||
|
||||
if (free_key)
|
||||
g_free (key);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
GList *
|
||||
scan_directory (const gchar *base_path,
|
||||
const gchar *subdir,
|
||||
GHashTable *files,
|
||||
GList *directories,
|
||||
gint depth)
|
||||
{
|
||||
GHashTable *dir_hash;
|
||||
GDir *dir;
|
||||
const gchar *name;
|
||||
gchar *dir_path;
|
||||
gboolean dir_added = FALSE;
|
||||
guint dir_index = 0xffff;
|
||||
|
||||
dir_path = g_build_filename (base_path, subdir, NULL);
|
||||
|
||||
/* FIXME: Use the gerror */
|
||||
dir = g_dir_open (dir_path, 0, NULL);
|
||||
|
||||
if (!dir)
|
||||
return directories;
|
||||
|
||||
dir_hash = g_hash_table_new (g_str_hash, g_str_equal);
|
||||
|
||||
while ((name = g_dir_read_name (dir)))
|
||||
{
|
||||
gchar *path;
|
||||
gboolean retval;
|
||||
int flags = 0;
|
||||
Image *image;
|
||||
gchar *basename, *dot;
|
||||
|
||||
path = g_build_filename (dir_path, name, NULL);
|
||||
retval = g_file_test (path, G_FILE_TEST_IS_DIR);
|
||||
if (retval)
|
||||
{
|
||||
gchar *subsubdir;
|
||||
|
||||
if (subdir)
|
||||
subsubdir = g_build_filename (subdir, name, NULL);
|
||||
else
|
||||
subsubdir = g_strdup (name);
|
||||
directories = scan_directory (base_path, subsubdir, files,
|
||||
directories, depth + 1);
|
||||
g_free (subsubdir);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
retval = g_file_test (path, G_FILE_TEST_IS_REGULAR);
|
||||
g_free (path);
|
||||
|
||||
if (retval)
|
||||
{
|
||||
if (g_str_has_suffix (name, ".png"))
|
||||
flags |= HAS_SUFFIX_PNG;
|
||||
else if (g_str_has_suffix (name, ".svg"))
|
||||
flags |= HAS_SUFFIX_SVG;
|
||||
else if (g_str_has_suffix (name, ".xpm"))
|
||||
flags |= HAS_SUFFIX_XPM;
|
||||
else if (g_str_has_suffix (name, ".icon"))
|
||||
flags |= HAS_ICON_FILE;
|
||||
|
||||
if (flags == 0)
|
||||
continue;
|
||||
|
||||
basename = g_strdup (name);
|
||||
dot = strrchr (basename, '.');
|
||||
*dot = '\0';
|
||||
|
||||
image = g_hash_table_lookup (dir_hash, basename);
|
||||
if (image)
|
||||
image->flags |= flags;
|
||||
else if ((flags & HAS_ICON_FILE) != HAS_ICON_FILE)
|
||||
{
|
||||
if (!dir_added)
|
||||
{
|
||||
dir_added = TRUE;
|
||||
if (subdir)
|
||||
{
|
||||
dir_index = g_list_length (directories);
|
||||
directories = g_list_append (directories, g_strdup (subdir));
|
||||
}
|
||||
else
|
||||
dir_index = 0xffff;
|
||||
}
|
||||
|
||||
image = g_new0 (Image, 1);
|
||||
image->flags = flags;
|
||||
image->dir_index = dir_index;
|
||||
|
||||
g_hash_table_insert (dir_hash, g_strdup (basename), image);
|
||||
}
|
||||
|
||||
g_free (basename);
|
||||
}
|
||||
}
|
||||
|
||||
g_dir_close (dir);
|
||||
|
||||
/* Move dir into the big file hash */
|
||||
g_hash_table_foreach_remove (dir_hash, foreach_remove_func, files);
|
||||
|
||||
g_hash_table_destroy (dir_hash);
|
||||
|
||||
return directories;
|
||||
}
|
||||
|
||||
typedef struct _HashNode HashNode;
|
||||
|
||||
struct _HashNode
|
||||
{
|
||||
HashNode *next;
|
||||
gchar *name;
|
||||
GList *image_list;
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
gint size;
|
||||
HashNode **nodes;
|
||||
} HashContext;
|
||||
|
||||
static gboolean
|
||||
convert_to_hash (gpointer key, gpointer value, gpointer user_data)
|
||||
{
|
||||
HashContext *context = user_data;
|
||||
guint hash;
|
||||
HashNode *node;
|
||||
|
||||
hash = icon_name_hash (key) % context->size;
|
||||
|
||||
node = g_new0 (HashNode, 1);
|
||||
node->next = NULL;
|
||||
node->name = key;
|
||||
node->image_list = value;
|
||||
|
||||
if (context->nodes[hash] != NULL)
|
||||
node->next = context->nodes[hash];
|
||||
|
||||
context->nodes[hash] = node;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
write_string (FILE *cache, const gchar *n)
|
||||
{
|
||||
gchar *s;
|
||||
int i, l;
|
||||
|
||||
l = ALIGN_VALUE (strlen (n) + 1, 4);
|
||||
|
||||
s = g_malloc0 (l);
|
||||
strcpy (s, n);
|
||||
|
||||
i = fwrite (s, l, 1, cache);
|
||||
|
||||
return i == 1;
|
||||
|
||||
}
|
||||
|
||||
gboolean
|
||||
write_card16 (FILE *cache, guint16 n)
|
||||
{
|
||||
int i;
|
||||
gchar s[2];
|
||||
|
||||
*((guint16 *)s) = GUINT16_TO_BE (n);
|
||||
|
||||
i = fwrite (s, 2, 1, cache);
|
||||
|
||||
return i == 1;
|
||||
}
|
||||
|
||||
gboolean
|
||||
write_card32 (FILE *cache, guint32 n)
|
||||
{
|
||||
int i;
|
||||
gchar s[4];
|
||||
|
||||
*((guint32 *)s) = GUINT32_TO_BE (n);
|
||||
|
||||
i = fwrite (s, 4, 1, cache);
|
||||
|
||||
return i == 1;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
write_header (FILE *cache, guint32 dir_list_offset)
|
||||
{
|
||||
return (write_card16 (cache, MAJOR_VERSION) &&
|
||||
write_card16 (cache, MINOR_VERSION) &&
|
||||
write_card32 (cache, HASH_OFFSET) &&
|
||||
write_card32 (cache, dir_list_offset));
|
||||
}
|
||||
|
||||
|
||||
guint
|
||||
get_single_node_size (HashNode *node)
|
||||
{
|
||||
int len = 0;
|
||||
|
||||
/* Node pointers */
|
||||
len += 12;
|
||||
|
||||
/* Name */
|
||||
len += ALIGN_VALUE (strlen (node->name) + 1, 4);
|
||||
|
||||
/* Image list */
|
||||
len += 4 + g_list_length (node->image_list) * 8;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
guint
|
||||
get_bucket_size (HashNode *node)
|
||||
{
|
||||
int len = 0;
|
||||
|
||||
while (node)
|
||||
{
|
||||
len += get_single_node_size (node);
|
||||
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
gboolean
|
||||
write_bucket (FILE *cache, HashNode *node, int *offset)
|
||||
{
|
||||
while (node != NULL)
|
||||
{
|
||||
int next_offset = *offset + get_single_node_size (node);
|
||||
int i, len;
|
||||
GList *list;
|
||||
|
||||
/* Chain offset */
|
||||
if (node->next != NULL)
|
||||
{
|
||||
if (!write_card32 (cache, next_offset))
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!write_card32 (cache, 0xffffffff))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Icon name offset */
|
||||
if (!write_card32 (cache, *offset + 12))
|
||||
return FALSE;
|
||||
|
||||
/* Image list offset */
|
||||
if (!write_card32 (cache, *offset + 12 + ALIGN_VALUE (strlen (node->name) + 1, 4)))
|
||||
return FALSE;
|
||||
|
||||
/* Icon name */
|
||||
if (!write_string (cache, node->name))
|
||||
return FALSE;
|
||||
|
||||
/* Image list */
|
||||
len = g_list_length (node->image_list);
|
||||
if (!write_card32 (cache, len))
|
||||
return FALSE;
|
||||
|
||||
list = node->image_list;
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
Image *image = list->data;
|
||||
|
||||
/* Directory index */
|
||||
if (!write_card16 (cache, image->dir_index))
|
||||
return FALSE;
|
||||
|
||||
/* Flags */
|
||||
if (!write_card16 (cache, image->flags))
|
||||
return FALSE;
|
||||
|
||||
/* Image data offset */
|
||||
if (!write_card32 (cache, 0))
|
||||
return FALSE;
|
||||
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
*offset = next_offset;
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
write_hash_table (FILE *cache, HashContext *context, int *new_offset)
|
||||
{
|
||||
int offset = HASH_OFFSET;
|
||||
int node_offset;
|
||||
int i;
|
||||
|
||||
if (!(write_card32 (cache, context->size)))
|
||||
return FALSE;
|
||||
|
||||
/* Size int + size * 4 */
|
||||
node_offset = offset + 4 + context->size * 4;
|
||||
|
||||
for (i = 0; i < context->size; i++)
|
||||
{
|
||||
if (context->nodes[i] != NULL)
|
||||
{
|
||||
if (!write_card32 (cache, node_offset))
|
||||
return FALSE;
|
||||
|
||||
node_offset += get_bucket_size (context->nodes[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!write_card32 (cache, 0xffffffff))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*new_offset = node_offset;
|
||||
|
||||
/* Now write the buckets */
|
||||
node_offset = offset + 4 + context->size * 4;
|
||||
|
||||
for (i = 0; i < context->size; i++)
|
||||
{
|
||||
if (!context->nodes[i])
|
||||
continue;
|
||||
|
||||
if (!write_bucket (cache, context->nodes[i], &node_offset))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
write_dir_index (FILE *cache, int offset, GList *directories)
|
||||
{
|
||||
int n_dirs;
|
||||
GList *d;
|
||||
char *dir;
|
||||
|
||||
n_dirs = g_list_length (directories);
|
||||
|
||||
if (!write_card32 (cache, n_dirs))
|
||||
return FALSE;
|
||||
|
||||
offset += 4 + n_dirs * 4;
|
||||
|
||||
for (d = directories; d; d = d->next)
|
||||
{
|
||||
dir = d->data;
|
||||
if (!write_card32 (cache, offset))
|
||||
return FALSE;
|
||||
|
||||
offset += ALIGN_VALUE (strlen (dir) + 1, 4);
|
||||
}
|
||||
|
||||
for (d = directories; d; d = d->next)
|
||||
{
|
||||
dir = d->data;
|
||||
|
||||
if (!write_string (cache, dir))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
write_file (FILE *cache, GHashTable *files, GList *directories)
|
||||
{
|
||||
HashContext context;
|
||||
int new_offset;
|
||||
|
||||
/* Convert the hash table into something looking a bit more
|
||||
* like what we want to write to disk.
|
||||
*/
|
||||
context.size = g_spaced_primes_closest (g_hash_table_size (files) / 3);
|
||||
context.nodes = g_new0 (HashNode *, context.size);
|
||||
|
||||
g_hash_table_foreach_remove (files, convert_to_hash, &context);
|
||||
|
||||
/* Now write the file */
|
||||
/* We write 0 as the directory list offset and go
|
||||
* back and change it later */
|
||||
if (!write_header (cache, 0))
|
||||
{
|
||||
g_printerr ("Failed to write header\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!write_hash_table (cache, &context, &new_offset))
|
||||
{
|
||||
g_printerr ("Failed to write hash table\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!write_dir_index (cache, new_offset, directories))
|
||||
{
|
||||
g_printerr ("Failed to write directory index\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
rewind (cache);
|
||||
|
||||
if (!write_header (cache, new_offset))
|
||||
{
|
||||
g_printerr ("Failed to rewrite header\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
build_cache (const gchar *path)
|
||||
{
|
||||
gchar *cache_path, *tmp_cache_path;
|
||||
GHashTable *files;
|
||||
gboolean retval;
|
||||
FILE *cache;
|
||||
struct stat path_stat, cache_stat;
|
||||
struct utimbuf utime_buf;
|
||||
GList *directories = NULL;
|
||||
|
||||
tmp_cache_path = g_build_filename (path, "."CACHE_NAME, NULL);
|
||||
cache = fopen (tmp_cache_path, "w");
|
||||
|
||||
if (!cache)
|
||||
{
|
||||
g_printerr ("Failed to write cache file: %s\n", g_strerror (errno));
|
||||
exit (1);
|
||||
}
|
||||
|
||||
files = g_hash_table_new (g_str_hash, g_str_equal);
|
||||
|
||||
directories = scan_directory (path, NULL, files, NULL, 0);
|
||||
|
||||
if (g_hash_table_size (files) == 0)
|
||||
{
|
||||
/* Empty table, just close and remove the file */
|
||||
|
||||
fclose (cache);
|
||||
unlink (tmp_cache_path);
|
||||
exit (0);
|
||||
}
|
||||
|
||||
/* FIXME: Handle failure */
|
||||
retval = write_file (cache, files, directories);
|
||||
fclose (cache);
|
||||
|
||||
g_list_foreach (directories, (GFunc)g_free, NULL);
|
||||
g_list_free (directories);
|
||||
|
||||
if (!retval)
|
||||
{
|
||||
unlink (tmp_cache_path);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
cache_path = g_build_filename (path, CACHE_NAME, NULL);
|
||||
|
||||
if (rename (tmp_cache_path, cache_path) == -1)
|
||||
{
|
||||
unlink (tmp_cache_path);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
/* Update time */
|
||||
/* FIXME: What do do if an error occurs here? */
|
||||
stat (path, &path_stat);
|
||||
stat (cache_path, &cache_stat);
|
||||
|
||||
utime_buf.actime = path_stat.st_atime;
|
||||
utime_buf.modtime = cache_stat.st_mtime;
|
||||
utime (path, &utime_buf);
|
||||
|
||||
g_printerr ("Cache file created successfully.\n");
|
||||
}
|
||||
|
||||
static gboolean force_update = FALSE;
|
||||
|
||||
static GOptionEntry args[] = {
|
||||
{ "force", 0, 0, G_OPTION_ARG_NONE, &force_update, "Overwrite an existing cache, even if uptodate", NULL },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
gchar *path;
|
||||
GOptionContext *context;
|
||||
|
||||
context = g_option_context_new ("ICONPATH");
|
||||
g_option_context_add_main_entries (context, args, NULL);
|
||||
|
||||
g_option_context_parse (context, &argc, &argv, NULL);
|
||||
|
||||
path = argv[1];
|
||||
|
||||
if (!force_update && is_cache_up_to_date (path))
|
||||
return 0;
|
||||
|
||||
build_cache (path);
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user