Commit 6fc2b811 authored by Matthias Clasen's avatar Matthias Clasen Committed by Matthias Clasen

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.
parent b087f765
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.
......
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.
......
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.
......
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.
......
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>
&gtk-query-immodules;
&gtk-update-icon-cache;
</part>
&gtk-glossary;
......
.\"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\&.
<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
......
/* 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);
}
}
}
/* 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