From b83238ae14282ff1354973155af64b9ba250979a Mon Sep 17 00:00:00 2001 From: Sebastian Dransfeld Date: Mon, 15 Nov 2010 21:07:22 +0000 Subject: [PATCH] Start of icon cache Please test efreet_icon_cache_create and efreet_icon_cache_dump, and check if it is fast enough on a slow system. SVN revision: 54575 --- legacy/efreet/configure.ac | 19 ++ legacy/efreet/src/bin/Makefile.am | 22 ++ .../efreet/src/bin/efreet_icon_cache_create.c | 319 ++++++++++++++++++ .../efreet/src/bin/efreet_icon_cache_dump.c | 100 ++++++ legacy/efreet/src/lib/Makefile.am | 5 + legacy/efreet/src/lib/efreet.c | 14 +- legacy/efreet/src/lib/efreet_cache.c | 123 +++++++ legacy/efreet/src/lib/efreet_private.h | 41 +++ 8 files changed, 642 insertions(+), 1 deletion(-) create mode 100644 legacy/efreet/src/bin/efreet_icon_cache_create.c create mode 100644 legacy/efreet/src/bin/efreet_icon_cache_dump.c create mode 100644 legacy/efreet/src/lib/efreet_cache.c diff --git a/legacy/efreet/configure.ac b/legacy/efreet/configure.ac index 1d504a4cb4..df783e22dc 100644 --- a/legacy/efreet/configure.ac +++ b/legacy/efreet/configure.ac @@ -86,6 +86,23 @@ if test "x${enable_sloppy_spec}" = "xyes" ; then AC_DEFINE([SLOPPY_SPEC], [1], [Sloppy Spec Compliance]) fi +AC_ARG_ENABLE([icon-cache], + [AC_HELP_STRING([--enable-icon-cache], [Enable icon cache compliance @<:@default=disabled@:>@])], + [ + if test "x${enableval}" = "xyes" ; then + enable_icon_cache="yes" + else + enable_icon_cache="no" + fi + ], + [enable_icon_cache="no"]) + +if test "x${enable_icon_cache}" = "xyes" ; then + AC_DEFINE([ICON_CACHE], [1], [Icon Cache]) +fi +AM_CONDITIONAL(ICON_CACHE, test "x$enable_icon_cache" = "xyes") + + #AC_ARG_ENABLE(hidden-visibility, # [AC_HELP_STRING([--enable-hidden-visibility],[Enable hidden visibility])], # [enable_hidden_visibility=$enableval], [enable_hidden_visibility="auto"]) @@ -240,6 +257,8 @@ echo " Specification compliance:" echo " Strict.............: ${enable_strict_spec}" echo " Sloppy.............: ${enable_sloppy_spec}" echo +echo " Icon cache...........: ${enable_icon_cache}" +echo echo " Tests................: ${enable_tests}" echo " Coverage.............: ${enable_coverage}" echo diff --git a/legacy/efreet/src/bin/Makefile.am b/legacy/efreet/src/bin/Makefile.am index 71886c112d..92968d815d 100644 --- a/legacy/efreet/src/bin/Makefile.am +++ b/legacy/efreet/src/bin/Makefile.am @@ -13,9 +13,31 @@ internal_bindir=$(libdir)/efreet internal_bin_PROGRAMS = \ efreet_desktop_cache_create +if ICON_CACHE +internal_bin_PROGRAMS += \ +efreet_icon_cache_create \ +efreet_icon_cache_dump +endif + efreet_desktop_cache_create_LDADD = \ $(top_builddir)/src/lib/libefreet.la \ @EFREET_LIBS@ efreet_desktop_cache_create_SOURCES = \ efreet_desktop_cache_create.c + +if ICON_CACHE +efreet_icon_cache_create_LDADD = \ +$(top_builddir)/src/lib/libefreet.la \ +@EFREET_LIBS@ + +efreet_icon_cache_create_SOURCES = \ +efreet_icon_cache_create.c + +efreet_icon_cache_dump_LDADD = \ +$(top_builddir)/src/lib/libefreet.la \ +@EFREET_LIBS@ + +efreet_icon_cache_dump_SOURCES = \ +efreet_icon_cache_dump.c +endif diff --git a/legacy/efreet/src/bin/efreet_icon_cache_create.c b/legacy/efreet/src/bin/efreet_icon_cache_create.c new file mode 100644 index 0000000000..dceff78fc8 --- /dev/null +++ b/legacy/efreet/src/bin/efreet_icon_cache_create.c @@ -0,0 +1,319 @@ +#ifdef HAVE_CONFIG_H +# include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "Efreet.h" +#include "efreet_private.h" + +static Eet_Data_Descriptor *edd = NULL; + +static Eina_List *extensions; + +int verbose = 0; + +static int +cache_scan_path_dir(Efreet_Icon_Theme *theme, const char *path, Efreet_Icon_Theme_Directory *dir, Eet_File *ef, int *changed) +{ + Eina_Iterator *it; + char buf[PATH_MAX]; + const char *ext, *file; + + snprintf(buf, sizeof(buf), "%s/%s", path, dir->name); + it = eina_file_ls(buf); + if (!it) return 1; + EINA_ITERATOR_FOREACH(it, file) + { + Eina_List *l; + Efreet_Cache_Icon *icon; + Efreet_Cache_Icon_Element *elem = NULL, *oelem = NULL; + char *name, *tmp; + + ext = strrchr(file, '.'); + if (!ext) continue; + ext = eina_stringshare_add(ext); + if (!eina_list_data_find(extensions, ext)) + { + eina_stringshare_del(ext); + continue; + } + /* icon with known extension */ + name = strdup(ecore_file_file_get(file)); + tmp = strrchr(name, '.'); + if (tmp) *tmp = '\0'; + icon = eet_data_read(ef, edd, name); + if (!icon) + { + icon = NEW(Efreet_Cache_Icon, 1); + icon->free = 1; +#if 0 + icon->name = eina_stringshare_add(name); +#endif + icon->theme = eina_stringshare_add(theme->name.internal); +#if 0 + icon->context = dir->context; +#endif + } + else if (strcmp(icon->theme, theme->name.internal)) + { + /* We got this icon from a parent theme */ + free(name); + continue; + } + + /* find if we have the same icon in another type */ + EINA_LIST_FOREACH(icon->icons, l, oelem) + { + if ((oelem->type == dir->type) && + (oelem->size.normal == dir->size.normal) && + (oelem->size.max == dir->size.max) && + (oelem->size.min == dir->size.min)) + { + elem = oelem; + break; + } + } + if (elem) + { + elem->paths = eina_list_append(elem->paths, eina_stringshare_ref(file)); + } + else + { + elem = NEW(Efreet_Cache_Icon_Element, 1); + elem->paths = eina_list_append(elem->paths, eina_stringshare_ref(file)); + elem->type = dir->type; + elem->size.normal = dir->size.normal; + elem->size.min = dir->size.min; + elem->size.max = dir->size.max; + icon->icons = eina_list_append(icon->icons, elem); + } + if (!eet_data_write(ef, edd, name, icon, 1)) + { + free(name); + break; + } + efreet_cache_icon_free(icon); + free(name); + } + eina_iterator_free(it); + return 1; +} + +static int +cache_scan_path(Efreet_Icon_Theme *theme, const char *path, Eet_File *ef, int *changed) +{ + Eina_List *l; + Efreet_Icon_Theme_Directory *dir; + + EINA_LIST_FOREACH(theme->directories, l, dir) + if (!cache_scan_path_dir(theme, path, dir, ef, changed)) return 0; + return 1; +} + +static int +cache_scan(Efreet_Icon_Theme *theme, Eina_Hash *themes, Eet_File *ef, int *changed) +{ + Eina_List *l; + const char *path; + const char *name; + + if (!theme) return 1; + if (eina_hash_find(themes, theme->name.internal)) return 1; + eina_hash_direct_add(themes, theme->name.internal, theme); + /* TODO: flush icons after each theme */ + /* TODO: Maybe always read entry from eet, so when can check changed */ + + /* scan theme */ + EINA_LIST_FOREACH(theme->paths, l, path) + if (!cache_scan_path(theme, path, ef, changed)) return 0; + + /* scan inherits */ + if (theme->inherits) + { + EINA_LIST_FOREACH(theme->inherits, l, name) + { + theme = efreet_icon_theme_find(name); + if (!cache_scan(theme, themes, ef, changed)) return 0; + } + } + else + { + theme = efreet_icon_theme_find("hicolor"); + if (!cache_scan(theme, themes, ef, changed)) return 0; + } + return 1; +} + +int +main(int argc, char **argv) +{ + /* TODO: + * - Add file monitor on files, so that we catch changes on files + * during whilst this program runs. + * - Maybe linger for a while to reduce number of cache re-creates. + */ + char file[PATH_MAX]; + Eina_List *l = NULL; + Efreet_Icon_Theme *theme; + char *dir = NULL; + int fd = -1, tmpfd; + int changed = 0; + int i; + struct flock fl; + const char *exts[] = { ".png", ".xpm", ".svg", NULL }; + + for (i = 1; i < argc; i++) + { + if (!strcmp(argv[i], "-v")) verbose = 1; + else if ((!strcmp(argv[i], "-h")) || + (!strcmp(argv[i], "-help")) || + (!strcmp(argv[i], "--h")) || + (!strcmp(argv[i], "--help"))) + { + printf("Options:\n"); + printf(" -v Verbose mode\n"); + exit(0); + } + } + /* init external subsystems */ + if (!eina_init()) goto eina_error; + if (!eet_init()) goto eet_error; + if (!ecore_init()) goto eet_error; + + for (i = 0; exts[i]; i++) + extensions = eina_list_append(extensions, eina_stringshare_add(exts[i])); + + efreet_cache_update = 0; + + /* create homedir */ + snprintf(file, sizeof(file), "%s/.efreet", efreet_home_dir_get()); + if (!ecore_file_mkpath(file)) goto efreet_error; + + /* lock process, so that we only run one copy of this program */ + snprintf(file, sizeof(file), "%s/.efreet/icon_data.lock", efreet_home_dir_get()); + fd = open(file, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); + if (fd < 0) goto efreet_error; + memset(&fl, 0, sizeof(struct flock)); + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + if (fcntl(fd, F_SETLK, &fl) < 0) + { + if (verbose) + { + printf("LOCKED! You may want to delete %s if this persists\n", file); + } + goto efreet_error; + } + + /* create dir for icon cache */ + dir = ecore_file_dir_get(efreet_icon_cache_file("")); + if (!ecore_file_mkpath(dir)) goto efreet_error; + free(dir); + + /* finish efreet init */ + if (!efreet_init()) goto efreet_error; + edd = efreet_icon_edd_init(); + if (!edd) goto edd_error; + + if (argc > 1) + { + for (i = 1; i < argc; i++) + { + theme = efreet_icon_theme_find(argv[i]); + if (theme) l = eina_list_append(l, theme); + } + } + if (!l) + l = efreet_icon_theme_list_get(); + EINA_LIST_FREE(l, theme) + { + Eet_File *ef; + Eina_Hash *themes; + changed = 0; + + /* create cache */ + /* TODO: Copy old cache to temp file, so we can check whether something has changed */ + snprintf(file, sizeof(file), "%s.XXXXXX", efreet_icon_cache_file(theme->name.internal)); + tmpfd = mkstemp(file); + if (tmpfd < 0) goto error; + close(tmpfd); + ef = eet_open(file, EET_FILE_MODE_READ_WRITE); + if (!ef) goto error; + + themes = eina_hash_string_superfast_new(NULL); + if (!cache_scan(theme, themes, ef, &changed)) + { + eet_close(ef); + eina_hash_free(themes); + goto error; + } + + changed = 1; + /* check if old and new caches contain the same number of entries */ +#if 0 + if (!changed) + { + Eet_File *old; + + old = eet_open(efreet_icon_cache_file(), EET_FILE_MODE_READ); + if (!old || eet_num_entries(old) != eet_num_entries(ef)) changed = 1; + if (old) eet_close(old); + + } +#endif + + /* cleanup */ + eet_close(ef); + eina_hash_free(themes); + + /* unlink old cache files */ + if (changed) + { + if (unlink(efreet_icon_cache_file(theme->name.internal)) < 0) + { + if (errno != ENOENT) goto error; + } + /* rename tmp files to real files */ + if (rename(file, efreet_icon_cache_file(theme->name.internal)) < 0) goto error; + } + else + { + unlink(file); + } + + eet_clearcache(); + } + + eina_list_free(extensions); + + efreet_shutdown(); + ecore_shutdown(); + eet_shutdown(); + eina_shutdown(); + close(fd); + return 0; +error: + printf("error\n"); + IF_FREE(dir); +edd_error: + efreet_shutdown(); +efreet_error: + eet_shutdown(); +eet_error: + eina_shutdown(); +eina_error: + if (fd > 0) close(fd); + return 1; +} diff --git a/legacy/efreet/src/bin/efreet_icon_cache_dump.c b/legacy/efreet/src/bin/efreet_icon_cache_dump.c new file mode 100644 index 0000000000..30b33e24d8 --- /dev/null +++ b/legacy/efreet/src/bin/efreet_icon_cache_dump.c @@ -0,0 +1,100 @@ +#ifdef HAVE_CONFIG_H +# include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "Efreet.h" +#include "efreet_private.h" + +static Eet_Data_Descriptor *edd = NULL; + +int verbose = 0; + +static void +dump(Efreet_Icon_Theme *theme) +{ + Eet_File *ef; + char **keys; + int i, num; + double start, max, avg; + + start = ecore_time_get(); + ef = eet_open(efreet_icon_cache_file(theme->name.internal), EET_FILE_MODE_READ); + printf("open: %s %f\n", theme->name.internal, ecore_time_get() - start); + if (!ef) return; + start = ecore_time_get(); + keys = eet_list(ef, "*", &num); + printf("list: %s %d, %f\n", theme->name.internal, num, ecore_time_get() - start); + if (!keys) + { + eet_close(ef); + return; + } + start = ecore_time_get(); + for (i = 0; i < num; i++) + { + Efreet_Cache_Icon *icon; + double dt; + + dt = ecore_time_get(); + icon = eet_data_read(ef, edd, keys[i]); + if (!icon) continue; + dt = ecore_time_get() - dt; + if (dt > max) + max = dt; + efreet_cache_icon_free(icon); + } + start = ecore_time_get() - start; + avg = start / num; + printf("read: %s %f %f %f\n", theme->name.internal, start, avg, max); + free(keys); + eet_close(ef); +} + +int +main(int argc, char **argv) +{ + Eina_List *l = NULL; + Efreet_Icon_Theme *theme; + int i; + + efreet_cache_update = 0; + + if (!efreet_init()) goto efreet_error; + edd = efreet_icon_edd_init(); + if (!edd) goto edd_error; + + if (argc > 1) + { + for (i = 1; i < argc; i++) + { + theme = efreet_icon_theme_find(argv[i]); + if (theme) l = eina_list_append(l, theme); + } + } + if (!l) + l = efreet_icon_theme_list_get(); + EINA_LIST_FREE(l, theme) + { + dump(theme); + } + + efreet_shutdown(); + return 0; +edd_error: + efreet_shutdown(); +efreet_error: + return 1; +} diff --git a/legacy/efreet/src/lib/Makefile.am b/legacy/efreet/src/lib/Makefile.am index 0b8228e85a..b064d6dfb7 100644 --- a/legacy/efreet/src/lib/Makefile.am +++ b/legacy/efreet/src/lib/Makefile.am @@ -25,6 +25,11 @@ efreet_menu.c \ efreet_utils.c \ efreet_uri.c +if ICON_CACHE +EFREETSOURCES += \ +efreet_cache.c +endif + includes_HEADERS = $(EFREETHEADERS) Efreet_Mime.h Efreet_Trash.h includesdir = $(includedir)/efreet-@VMAJ@ diff --git a/legacy/efreet/src/lib/efreet.c b/legacy/efreet/src/lib/efreet.c index 5f8d4aacde..46abae7738 100644 --- a/legacy/efreet/src/lib/efreet.c +++ b/legacy/efreet/src/lib/efreet.c @@ -46,8 +46,13 @@ efreet_init(void) if (!efreet_base_init()) goto unregister_log_domain; - if (!efreet_xml_init()) +#if ICON_CACHE + if (!efreet_cache_init()) goto shutdown_efreet_base; +#endif + + if (!efreet_xml_init()) + goto shutdown_efreet_cache; if (!efreet_icon_init()) goto shutdown_efreet_xml; @@ -76,7 +81,11 @@ shutdown_efreet_icon: efreet_icon_shutdown(); shutdown_efreet_xml: efreet_xml_shutdown(); +shutdown_efreet_cache: +#if ICON_CACHE + efreet_cache_shutdown(); shutdown_efreet_base: +#endif efreet_base_shutdown(); unregister_log_domain: eina_log_domain_unregister(_efreet_log_domain_global); @@ -106,6 +115,9 @@ efreet_shutdown(void) efreet_ini_shutdown(); efreet_icon_shutdown(); efreet_xml_shutdown(); +#if ICON_CACHE + efreet_cache_shutdown(); +#endif efreet_base_shutdown(); eina_log_domain_unregister(_efreet_log_domain_global); diff --git a/legacy/efreet/src/lib/efreet_cache.c b/legacy/efreet/src/lib/efreet_cache.c new file mode 100644 index 0000000000..4b1f8aad89 --- /dev/null +++ b/legacy/efreet/src/lib/efreet_cache.c @@ -0,0 +1,123 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "Efreet.h" +#include "efreet_private.h" + +/** + * Data for cache files + */ +static Eet_Data_Descriptor *cache_icon_edd = NULL; +static Eet_Data_Descriptor *cache_icon_element_edd = NULL; + +static void efreet_icon_edd_shutdown(void); + +int +efreet_cache_init(void) +{ + if (!efreet_icon_edd_init()) return 0; + return 1; +} + +void +efreet_cache_shutdown(void) +{ + efreet_icon_edd_shutdown(); +} + +/* + * Needs EAPI because of helper binaries + */ +EAPI const char * +efreet_icon_cache_file(const char *theme) +{ + static char cache_file[PATH_MAX] = { '\0' }; + const char *home; + + home = efreet_home_dir_get(); + + snprintf(cache_file, sizeof(cache_file), "%s/.efreet/icon_%s.cache", home, theme); + + return cache_file; +} + +/* + * Needs EAPI because of helper binaries + */ +EAPI Eet_Data_Descriptor * +efreet_icon_edd_init(void) +{ + Eet_Data_Descriptor_Class iconeddc; + Eet_Data_Descriptor_Class elemeddc; + + if (!cache_icon_edd) + { + EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&iconeddc, Efreet_Cache_Icon); + EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&elemeddc, Efreet_Cache_Icon_Element); + cache_icon_edd = eet_data_descriptor_file_new(&iconeddc); + if (!cache_icon_edd) + goto error; + cache_icon_element_edd = eet_data_descriptor_file_new(&elemeddc); + if (!cache_icon_element_edd) + goto error; + +#if 0 + EET_DATA_DESCRIPTOR_ADD_BASIC(cache_icon_edd, Efreet_Cache_Icon, "name", name, EET_T_STRING); +#endif + EET_DATA_DESCRIPTOR_ADD_BASIC(cache_icon_edd, Efreet_Cache_Icon, "theme", theme, EET_T_STRING); +#if 0 + EET_DATA_DESCRIPTOR_ADD_BASIC(cache_icon_edd, Efreet_Cache_Icon, "context", context, EET_T_INT); +#endif + EET_DATA_DESCRIPTOR_ADD_LIST(cache_icon_edd, Efreet_Cache_Icon, "icons", icons, cache_icon_element_edd); +#if 0 + EET_DATA_DESCRIPTOR_ADD_BASIC(cache_icon_element_edd, Efreet_Cache_Icon_Element, "type", type, EET_T_INT); +#endif + eet_data_descriptor_element_add(cache_icon_element_edd, "paths", EET_T_STRING, EET_G_LIST, offsetof(Efreet_Cache_Icon_Element, paths), 0, NULL, NULL); + EET_DATA_DESCRIPTOR_ADD_BASIC(cache_icon_element_edd, Efreet_Cache_Icon_Element, "size.normal", size.normal, EET_T_USHORT); + EET_DATA_DESCRIPTOR_ADD_BASIC(cache_icon_element_edd, Efreet_Cache_Icon_Element, "size.min", size.min, EET_T_USHORT); + EET_DATA_DESCRIPTOR_ADD_BASIC(cache_icon_element_edd, Efreet_Cache_Icon_Element, "size.max", size.max, EET_T_USHORT); + } + return cache_icon_edd; +error: + efreet_icon_edd_shutdown(); + return NULL; +} + +static void +efreet_icon_edd_shutdown(void) +{ + if (cache_icon_edd) eet_data_descriptor_free(cache_icon_edd); + cache_icon_edd = NULL; + if (cache_icon_element_edd) eet_data_descriptor_free(cache_icon_element_edd); + cache_icon_element_edd = NULL; +} + +/* + * Needs EAPI because of helper binaries + */ +EAPI void +efreet_cache_icon_free(Efreet_Cache_Icon *icon) +{ + Efreet_Cache_Icon_Element *elem; + if (icon->free) + { +#if 0 + eina_stringshare_del(icon->name); +#endif + eina_stringshare_del(icon->theme); + } + + EINA_LIST_FREE(icon->icons, elem) + { + const char *path; + + if (icon->free) + EINA_LIST_FREE(elem->paths, path) + eina_stringshare_del(path); + else + eina_list_free(elem->paths); + free(elem); + } + free(icon); +} diff --git a/legacy/efreet/src/lib/efreet_private.h b/legacy/efreet/src/lib/efreet_private.h index 694afc859f..60783dd07b 100644 --- a/legacy/efreet/src/lib/efreet_private.h +++ b/legacy/efreet/src/lib/efreet_private.h @@ -134,9 +134,43 @@ extern int _efreet_log_dom_global; #endif #define WRN(...) EINA_LOG_DOM_WARN(EFREET_MODULE_LOG_DOM, __VA_ARGS__) +#if ICON_CACHE +typedef struct Efreet_Cache_Icon Efreet_Cache_Icon; +struct Efreet_Cache_Icon +{ +#if 0 + const char *name; +#endif + const char *theme; +#if 0 + int context; /* the type of icon */ +#endif + Eina_List *icons; + unsigned char free:1; +}; + +typedef struct Efreet_Cache_Icon_Element Efreet_Cache_Icon_Element; +struct Efreet_Cache_Icon_Element +{ + int type; /* size type of icon */ + Eina_List *paths; /* possible paths for icon */ + struct + { + unsigned short normal; /* The size for this icon */ + unsigned short min; /* The minimum size for this icon */ + unsigned short max; /* The maximum size for this icon */ + } size; +}; +#endif + int efreet_base_init(void); void efreet_base_shutdown(void); +#if ICON_CACHE +int efreet_cache_init(void); +void efreet_cavoidche_shutdown(void); +#endif + int efreet_icon_init(void); void efreet_icon_shutdown(void); @@ -172,6 +206,13 @@ EAPI const char *efreet_desktop_util_cache_file(void); EAPI const char *efreet_desktop_cache_file(void); EAPI const char *efreet_desktop_cache_dirs(void); +#if ICON_CACHE +EAPI const char *efreet_icon_cache_file(const char *theme); + +EAPI Eet_Data_Descriptor *efreet_icon_edd_init(void); +EAPI void efreet_cache_icon_free(Efreet_Cache_Icon *icon); +#endif + #define NON_EXISTING (void *)-1 EAPI extern int efreet_cache_update;