Cache launcher icons
@@ -4,18 +4,22 @@ - Fix get_version.sh so that it returns the correct version when .git is missing
- Create temporary files in /tmp - Compute task icon size correctly (issue #560) - Fix build on powerpc + - The XDG paths are used in the icon and application lookup in addition to the hardcoded defaults - Minor code cleanup in the system tray - tint2conf: - Sort applications correctly - Avoid duplicate icon themes due to symlinks - Avoid loading desktop files marked as NoDisplay - Enhancements: - - Use a better Name and GenericName in the .desktop files - - Use the XDG paths in addition to the defaults when looking for icons and applications - - Fallback icon themes are loaded lazily to speed up tint2 startup + - Launcher icon paths are now cached, which greatly improves loading time for tint2 and tint2conf. + The correct icon should be found even if you change the icon theme or install a new theme. + If this is not the case, delete the file ~/.cache/tint2/icon.cache, restart tint2 and please file a bug report + indicating the application name and the icon theme name. + - Fallback icon themes are loaded lazily to speed up tint2 and tint2conf startup + - A better Name and GenericName is used in the tint2 and tint2conf .desktop files - tint2conf: - - Sort icon themes - - Updated ru translation + - Sort icon themes in list + - Updated ru translation (thanks @Vladimir-csp) 2016-01-29 0.12.7 - Fixes:
@@ -300,24 +300,25 @@ g_slist_free(theme->list_directories);
theme->list_directories = NULL; } -void free_themes(IconThemeWrapper *themes) +void free_themes(IconThemeWrapper *wrapper) { - if (!themes) + if (!wrapper) return; - for (GSList *l = themes->themes; l; l = l->next) { + for (GSList *l = wrapper->themes; l; l = l->next) { IconTheme *theme = (IconTheme *)l->data; free_icon_theme(theme); free(theme); } - g_slist_free(themes->themes); - for (GSList *l = themes->themes_fallback; l; l = l->next) { + g_slist_free(wrapper->themes); + for (GSList *l = wrapper->themes_fallback; l; l = l->next) { IconTheme *theme = (IconTheme *)l->data; free_icon_theme(theme); free(theme); } - g_slist_free(themes->themes_fallback); - g_slist_free_full(themes->_queued, free); - free(themes); + g_slist_free(wrapper->themes_fallback); + g_slist_free_full(wrapper->_queued, free); + g_hash_table_destroy(wrapper->_cache); + free(wrapper); } void test_launcher_read_theme_file()@@ -405,21 +406,17 @@ free(l->data);
g_slist_free(queue); } -IconThemeWrapper *load_themes(const char *icon_theme_name) +void load_default_theme(IconThemeWrapper *wrapper) { - IconThemeWrapper *wrapper = calloc(1, sizeof(IconThemeWrapper)); + if (wrapper->_themes_loaded) + return; - if (!icon_theme_name) { - fprintf(stderr, "Missing icon_theme_name theme, default to 'hicolor'.\n"); - icon_theme_name = "hicolor"; - } else { - fprintf(stderr, "Loading %s. Icon theme :", icon_theme_name); - } + fprintf(stderr, GREEN "Loading icon theme %s:" RESET "\n", wrapper->icon_theme_name); - load_themes_helper(icon_theme_name, &wrapper->themes, &wrapper->_queued); + load_themes_helper(wrapper->icon_theme_name, &wrapper->themes, &wrapper->_queued); load_themes_helper("hicolor", &wrapper->themes, &wrapper->_queued); - return wrapper; + wrapper->_themes_loaded = TRUE; } void load_fallbacks(IconThemeWrapper *wrapper)@@ -450,6 +447,111 @@
wrapper->_fallback_loaded = TRUE; } +gchar *get_icon_cache_path() +{ + return g_build_filename(g_get_user_cache_dir(), "tint2", "icon.cache", NULL); +} + +void load_cache(GHashTable **cache, gchar *cache_path) +{ + *cache = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + + FILE *f = fopen(cache_path, "rt"); + if (!f) + return; + + char *line = NULL; + size_t line_size; + + while (getline(&line, &line_size, f) >= 0) { + char *key, *value; + + size_t line_len = strlen(line); + gboolean has_newline = FALSE; + if (line_len >= 1) { + if (line[line_len - 1] == '\n') { + line[line_len - 1] = '\0'; + line_len--; + has_newline = TRUE; + } + } + if (!has_newline) + break; + + if (line_len == 0) + continue; + + if (parse_theme_line(line, &key, &value)) { + g_hash_table_insert(*cache, g_strdup(key), g_strdup(value)); + } + } + free(line); + fclose(f); +} + +void write_cache_line(gpointer key, gpointer value, gpointer user_data) +{ + gchar *k = key; + gchar *v = value; + FILE *f = user_data; + + fprintf(f, "%s=%s\n", k, v); +} + +void save_cache(GHashTable *cache, gchar *cache_path) +{ + FILE *f = fopen(cache_path, "w"); + if (!f) { + gchar *dir_path = g_path_get_dirname(cache_path); + g_mkdir_with_parents(dir_path, 0700); + g_free(dir_path); + f = fopen(cache_path, "w"); + if (!f) { + fprintf(stderr, RED "Could not save icon theme cache!" RESET "\n"); + return; + } + } + g_hash_table_foreach(cache, write_cache_line, f); + fclose(f); +} + +void load_icon_cache(IconThemeWrapper *wrapper) +{ + if (wrapper->_cache) + return; + + fprintf(stderr, GREEN "Loading icon theme cache..." RESET "\n"); + + gchar *cache_path = get_icon_cache_path(); + load_cache(&wrapper->_cache, cache_path); + g_free(cache_path); +} + +void save_icon_cache(IconThemeWrapper *wrapper) +{ + if (!wrapper || !wrapper->_cache || !wrapper->_cache_dirty) + return; + + fprintf(stderr, GREEN "Saving icon theme cache..." RESET "\n"); + gchar *cache_path = get_icon_cache_path(); + save_cache(wrapper->_cache, cache_path); + g_free(cache_path); +} + +IconThemeWrapper *load_themes(const char *icon_theme_name) +{ + IconThemeWrapper *wrapper = calloc(1, sizeof(IconThemeWrapper)); + + if (!icon_theme_name) { + fprintf(stderr, "Missing icon_theme_name theme, default to 'hicolor'.\n"); + icon_theme_name = "hicolor"; + } + + wrapper->icon_theme_name = strdup(icon_theme_name); + + return wrapper; +} + int directory_matches_size(IconThemeDir *dir, int size) { if (dir->type == ICON_DIR_TYPE_FIXED) {@@ -651,6 +753,49 @@ g_slist_free(extensions);
return NULL; } +char *get_icon_path_from_cache(IconThemeWrapper *wrapper, const char *icon_name, int size) +{ + if (!wrapper || !icon_name || strlen(icon_name) == 0) + return NULL; + + load_icon_cache(wrapper); + + gchar *key = g_strdup_printf("%s\t%s\t%d", wrapper->icon_theme_name, icon_name, size); + gchar *value = g_hash_table_lookup(wrapper->_cache, key); + g_free(key); + if (!value) { + fprintf(stderr, + YELLOW "Icon path not found in cache: theme = %s, icon = %s, size = %d" RESET "\n", + wrapper->icon_theme_name, + icon_name, + size); + return NULL; + } + + // fprintf(stderr, "Icon path found in cache: theme = %s, icon = %s, size = %d, path = %s\n", wrapper->icon_theme_name, icon_name, size, value); + + return strdup(value); +} + +void add_icon_path_to_cache(IconThemeWrapper *wrapper, const char *icon_name, int size, const char *path) +{ + if (!wrapper || !icon_name || strlen(icon_name) == 0 || !path || strlen(path) == 0) + return; + + fprintf(stderr, "Adding icon path to cache: theme = %s, icon = %s, size = %d, path = %s\n", wrapper->icon_theme_name, icon_name, size, path); + + load_icon_cache(wrapper); + + gchar *key = g_strdup_printf("%s\t%s\t%d", wrapper->icon_theme_name, icon_name, size); + gchar *value = g_hash_table_lookup(wrapper->_cache, key); + if (value && g_str_equal(value, path)) { + g_free(key); + return; + } + g_hash_table_insert(wrapper->_cache, key, g_strdup(path)); + wrapper->_cache_dirty = TRUE; +} + char *get_icon_path(IconThemeWrapper *wrapper, const char *icon_name, int size) { if (!wrapper)@@ -659,17 +804,27 @@
if (!icon_name || strlen(icon_name) == 0) return NULL; + char *path = get_icon_path_from_cache(wrapper, icon_name, size); + if (path) + return path; + + load_default_theme(wrapper); + icon_name = icon_name ? icon_name : DEFAULT_ICON; - char *path = get_icon_path_helper(wrapper->themes, icon_name, size); - if (path) + path = get_icon_path_helper(wrapper->themes, icon_name, size); + if (path) { + add_icon_path_to_cache(wrapper, icon_name, size, path); return path; + } fprintf(stderr, YELLOW "Icon not found in default theme: %s" RESET "\n", icon_name); load_fallbacks(wrapper); path = get_icon_path_helper(wrapper->themes_fallback, icon_name, size); - if (path) + if (path) { + add_icon_path_to_cache(wrapper, icon_name, size, path); return path; + } fprintf(stderr, RED "Could not find icon %s, using default." RESET "\n", icon_name); path = get_icon_path_helper(wrapper->themes, DEFAULT_ICON, size);
@@ -9,12 +9,21 @@
#include <glib.h> typedef struct IconThemeWrapper { + // The icon theme name for which this wrapper was created + char *icon_theme_name; // List of IconTheme* GSList *themes; + // Themes are loaded lazily when needed. + gboolean _themes_loaded; // List of IconTheme* GSList *themes_fallback; + // Fallback themes are loaded lazily when needed. + gboolean _fallback_loaded; + GHashTable *_cache; + gboolean _cache_dirty; + // List of icon theme names that have been queued for loading. + // Used to avoid loading the same theme twice, and to avoid cycles. GSList *_queued; - gboolean _fallback_loaded; } IconThemeWrapper; typedef struct IconTheme {@@ -33,14 +42,16 @@ // Returns an IconThemeWrapper* containing the icon theme identified by the name icon_theme_name, all the
// inherited themes, the hicolor theme and possibly fallback themes. IconThemeWrapper *load_themes(const char *icon_theme_name); -void free_themes(IconThemeWrapper *themes); +void save_icon_cache(IconThemeWrapper *wrapper); + +void free_themes(IconThemeWrapper *wrapper); void free_icon_theme(IconTheme *theme); #define DEFAULT_ICON "application-x-executable" // Returns the full path to an icon file (or NULL) given the list of icon themes to search and the icon name // Note: needs to be released with free(). -char *get_icon_path(IconThemeWrapper *theme, const char *icon_name, int size); +char *get_icon_path(IconThemeWrapper *wrapper, const char *icon_name, int size); // Returns a list of the directories used to store icons. // Do not free the result, it is cached.
@@ -147,8 +147,8 @@ }
g_slist_free(launcher->list_icons); launcher->list_icons = NULL; - free_themes(launcher->list_themes); - launcher->list_themes = NULL; + free_themes(launcher->icon_theme_wrapper); + launcher->icon_theme_wrapper = NULL; } gboolean resize_launcher(void *obj)@@ -176,7 +176,7 @@ launcherIcon->area.height = launcherIcon->icon_size;
// Get the path for an icon file with the new size char *new_icon_path = - get_icon_path(launcher->list_themes, launcherIcon->icon_name, launcherIcon->icon_size); + get_icon_path(launcher->icon_theme_wrapper, launcherIcon->icon_name, launcherIcon->icon_size); if (!new_icon_path) { // Draw a blank icon free_icon(launcherIcon->image);@@ -195,7 +195,7 @@ launcherIcon->image = load_image(new_icon_path, 1);
// On loading error, fallback to default if (!launcherIcon->image) { free(new_icon_path); - new_icon_path = get_icon_path(launcher->list_themes, DEFAULT_ICON, launcherIcon->icon_size); + new_icon_path = get_icon_path(launcher->icon_theme_wrapper, DEFAULT_ICON, launcherIcon->icon_size); if (new_icon_path) launcherIcon->image = imlib_load_image_immediately(new_icon_path); }@@ -225,6 +225,7 @@ panel_config.mouse_pressed_saturation,
panel_config.mouse_pressed_brightness); } } + save_icon_cache(launcher->icon_theme_wrapper); int count = g_slist_length(launcher->list_icons);@@ -465,10 +466,10 @@ app = g_slist_next(app);
} } -// Populates the list_themes list +// Populates the icon_theme_wrapper list void launcher_load_themes(Launcher *launcher) { - launcher->list_themes = + launcher->icon_theme_wrapper = load_themes(launcher_icon_theme_override ? (icon_theme_name_config ? icon_theme_name_config : icon_theme_name_xsettings ? icon_theme_name_xsettings : "hicolor")
@@ -17,7 +17,7 @@ // always start with area
Area area; GSList *list_apps; // List of char*, each is a path to a app.desktop file GSList *list_icons; // List of LauncherIcon* - IconThemeWrapper *list_themes; + IconThemeWrapper *icon_theme_wrapper; } Launcher; typedef struct LauncherIcon {
@@ -485,6 +485,7 @@ gtk_tree_model_get(model, &iter, COL_THEME_FILE, &file, -1);
GtkWidget *prop; prop = create_properties(); config_read_file(file); + save_icon_cache(icon_theme); gtk_window_present(GTK_WINDOW(prop)); g_free(file); }
@@ -2222,6 +2222,7 @@ g_free(icon_theme_name);
load_icons(launcher_apps); load_icons(all_apps); + save_icon_cache(icon_theme); } void launcher_icon_theme_changed(GtkWidget *widget, gpointer data)