From e956dfcdce70bc70587e346a8a9d2873290b34b8 Mon Sep 17 00:00:00 2001 From: Pierre Pronchery Date: Tue, 14 Oct 2014 01:06:14 +0200 Subject: [PATCH] Look through the XDG directories to look for applications --- src/desktop.c | 322 +++++++++++++++++++++++++++++--------------------- 1 file changed, 189 insertions(+), 133 deletions(-) diff --git a/src/desktop.c b/src/desktop.c index add6eca..80c4eb5 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -15,7 +15,8 @@ /* TODO: * - let the user define the desktop folder (possibly default to FDO's) * - set the font for the icons instantly - * - track multiple selection on delete/properties */ + * - track multiple selection on delete/properties + * - avoid code duplication with DeforaOS Panel ("main" applet) */ @@ -1114,6 +1115,9 @@ void desktop_refresh(Desktop * desktop) static void _refresh_applications(Desktop * desktop) { + g_slist_foreach(desktop->apps, (GFunc)config_delete, NULL); + g_slist_free(desktop->apps); + desktop->apps = NULL; desktop->refresh_source = g_idle_add(_desktop_on_refresh, desktop); } @@ -1144,7 +1148,8 @@ static void _refresh_files(Desktop * desktop) static void _refresh_none(Desktop * desktop) { - desktop->refresh_source = 0; + /* for cleanup */ + desktop->refresh_source = g_idle_add(_desktop_on_refresh, desktop); } @@ -2326,17 +2331,26 @@ static void _preferences_set_color(Config * config, char const * section, /* callbacks */ /* desktop_on_refresh */ static void _refresh_cleanup(Desktop * desktop); +static void _refresh_done_applications(Desktop * desktop); static void _refresh_done_categories(Desktop * desktop); static void _refresh_done_categories_open(Desktop * desktop, gpointer data); static gboolean _refresh_done_timeout(gpointer data); static int _refresh_loop(Desktop * desktop); static int _refresh_loop_applications(Desktop * desktop); -static int _refresh_loop_applications_path(Desktop * desktop, - char const * path); -static int _refresh_loop_applications_do(Desktop * desktop); static gint _categories_apps_compare(gconstpointer a, gconstpointer b); static int _refresh_loop_categories(Desktop * desktop); -static int _refresh_loop_categories_do(Desktop * desktop); +static int _refresh_loop_categories_dirs(Desktop * desktop); +static void _refresh_loop_categories_dirs_path(Desktop * desktop, + char const * path, char const * apppath); +static void _refresh_loop_categories_dirs_xdg(Desktop * desktop, + void (*callback)(Desktop * desktop, char const * path, + char const * apppath)); +static void _refresh_loop_categories_dirs_xdg_home(Desktop * desktop, + void (*callback)(Desktop * desktop, char const * path, + char const * apppath)); +static void _refresh_loop_categories_dirs_xdg_path(Desktop * desktop, + void (*callback)(Desktop * desktop, char const * path, + char const * apppath), char const * path); static int _refresh_loop_files(Desktop * desktop); static int _refresh_loop_lookup(Desktop * desktop, char const * name); static gboolean _refresh_done(Desktop * desktop); @@ -2369,6 +2383,9 @@ static gboolean _refresh_done(Desktop * desktop) { switch(desktop->prefs.icons) { + case DESKTOP_ICONS_APPLICATIONS: + _refresh_done_applications(desktop); + break; case DESKTOP_ICONS_CATEGORIES: _refresh_done_categories(desktop); break; @@ -2385,6 +2402,39 @@ static gboolean _refresh_done(Desktop * desktop) return FALSE; } +static void _refresh_done_applications(Desktop * desktop) +{ + GSList * p; + Config * config; + const char section[] = "Desktop Entry"; + char const * q; + char const * r; + DesktopCategory * dc = desktop->category; + char const * path; + DesktopIcon * icon; + + for(p = desktop->apps; p != NULL; p = p->next) + { + config = p->data; + path = config_get(config, NULL, "path"); + if(dc != NULL) + { + if((q = config_get(config, section, "Categories")) + == NULL) + continue; + if((r = string_find(q, dc->category)) == NULL) + continue; + r += string_length(dc->category); + if(*r != '\0' && *r != ';') + continue; + } + if((icon = desktopicon_new_application(desktop, path, NULL)) + == NULL) + continue; + _desktop_icon_add(desktop, icon); + } +} + static void _refresh_done_categories(Desktop * desktop) { GSList * p; @@ -2475,135 +2525,62 @@ static int _refresh_loop(Desktop * desktop) return _refresh_loop_files(desktop); case DESKTOP_ICONS_HOMESCREEN: case DESKTOP_ICONS_NONE: - break; /* nothing to do */ + return 0; /* nothing to do */ } return -1; } static int _refresh_loop_applications(Desktop * desktop) { - int ret; - char const * path; - size_t len; - - if(desktop->path == NULL) - return _refresh_loop_applications_path(desktop, DATADIR); - if((ret = _refresh_loop_applications_do(desktop)) == 0) - return 0; - /* XXX hack to also look at XDG_DATA_HOME */ - if((path = getenv("XDG_DATA_HOME")) != NULL && (len = strlen(path)) > 0 - && strncmp(desktop->path, path, len) != 0) - return _refresh_loop_applications_path(desktop, path); - return ret; -} - -static int _refresh_loop_applications_path(Desktop * desktop, - char const * path) -{ - const char applications[] = "/applications"; - char * p; - size_t len; - DIR * dir; - struct stat st; - - len = strlen(path) + sizeof(applications); - if((p = malloc(len)) == NULL) - return -desktop_error(NULL, path, 1); - snprintf(p, len, "%s%s", path, applications); - if((dir = browser_vfs_opendir(p, &st)) == NULL) - { - free(p); - return -desktop_error(NULL, p, 1); - } - desktop->path = p; - desktop->path_cnt = len; - desktop->refresh_dir = dir; - desktop->refresh_mti = st.st_mtime; - return 0; -} - -static int _refresh_loop_applications_do(Desktop * desktop) -{ - struct dirent * de; - size_t len; - const char ext[] = ".desktop"; - const char section[] = "Desktop Entry"; - char * path = NULL; - char * p; - Config * config; - char const * q; - DesktopIcon * icon; - - if(desktop->category == NULL) - return -1; - if((config = config_new()) == NULL) - return -_desktop_serror(desktop, NULL, 1); - while((de = browser_vfs_readdir(desktop->refresh_dir)) != NULL) - { - if(de->d_name[0] == '.') - if(de->d_name[1] == '\0' || (de->d_name[1] == '.' - && de->d_name[2] == '\0')) - continue; - len = strlen(de->d_name); - if(len < sizeof(ext)) - continue; - if(strncmp(&de->d_name[len - sizeof(ext) + 1], ext, - sizeof(ext)) != 0) - continue; - len = desktop->path_cnt + len + 1; - if((p = realloc(path, len)) == NULL) - { - error_set_print(PROGNAME, 1, "%s: %s", "realloc", - strerror(errno)); - continue; - } - path = p; - snprintf(path, len, "%s/%s", desktop->path, de->d_name); -#ifdef DEBUG - fprintf(stderr, "DEBUG: %s() \"%s\"\n", __func__, path); -#endif - config_reset(config); - if(config_load(config, path) != 0) - continue; - if((q = config_get(config, section, "Categories")) == NULL) - continue; - if(string_find(q, desktop->category->category) == NULL) - continue; - /* FIXME forward the corresponding datadir */ - if((icon = desktopicon_new_application(desktop, path, NULL)) - == NULL) - continue; - _desktop_icon_add(desktop, icon); - free(path); - config_delete(config); - return 0; - } - free(path); - config_delete(config); - return 1; + /* XXX */ + return _refresh_loop_categories(desktop); } static int _refresh_loop_categories(Desktop * desktop) { - if(desktop->path == NULL) - /* XXX hack */ - return _refresh_loop_applications_path(desktop, DATADIR); - return _refresh_loop_categories_do(desktop); + return _refresh_loop_categories_dirs(desktop); } -static int _refresh_loop_categories_do(Desktop * desktop) +static int _refresh_loop_categories_dirs(Desktop * desktop) { + _refresh_loop_categories_dirs_xdg(desktop, + _refresh_loop_categories_dirs_path); + return 1; +} + +static void _refresh_loop_categories_dirs_path(Desktop * desktop, + char const * path, char const * apppath) +{ + DIR * dir; + int fd; + struct stat st; struct dirent * de; size_t len; const char ext[] = ".desktop"; const char section[] = "Desktop Entry"; - char * path = NULL; + char * name = NULL; char * p; Config * config = NULL; - char const * q; - char const * r; + String const * q; + String const * r; - while((de = browser_vfs_readdir(desktop->refresh_dir)) != NULL) +#if defined(__sun__) + if((fd = open(apppath, O_RDONLY)) < 0 + || fstat(fd, &st) != 0 + || (dir = fdopendir(fd)) == NULL) +#else + if((dir = opendir(apppath)) == NULL + || (fd = dirfd(dir)) < 0 + || fstat(fd, &st) != 0) +#endif + { + if(errno != ENOENT) + desktop_error(NULL, apppath, 1); + return; + } + if(st.st_mtime > desktop->refresh_mti) + desktop->refresh_mti = st.st_mtime; + while((de = readdir(dir)) != NULL) { if(de->d_name[0] == '.') if(de->d_name[1] == '\0' || (de->d_name[1] == '.' @@ -2615,40 +2592,119 @@ static int _refresh_loop_categories_do(Desktop * desktop) if(strncmp(&de->d_name[len - sizeof(ext) + 1], ext, sizeof(ext)) != 0) continue; - len = desktop->path_cnt + len + 1; - if((p = realloc(path, len)) == NULL) + if((p = realloc(name, strlen(apppath) + len + 2)) == NULL) { - error_set_print(PROGNAME, 1, "%s: %s", "realloc", - strerror(errno)); + desktop_error(NULL, apppath, 1); continue; } - path = p; - snprintf(path, len, "%s/%s", desktop->path, de->d_name); + name = p; + snprintf(name, strlen(apppath) + len + 2, "%s/%s", apppath, + de->d_name); #ifdef DEBUG - fprintf(stderr, "DEBUG: %s() \"%s\"\n", __func__, path); + fprintf(stderr, "DEBUG: %s() name=\"%s\" path=\"%s\"\n", + __func__, name, path); #endif - if(config == NULL && (config = config_new()) == NULL) - continue; /* XXX report error */ - config_reset(config); - if(config_load(config, path) != 0) + if(config == NULL) + config = config_new(); + else + config_reset(config); + if(config == NULL || config_load(config, name) != 0) { - error_set_print(PROGNAME, 1, "%s: %s", path, - error_get()); + desktop_error(NULL, NULL, 1); /* XXX */ continue; } q = config_get(config, section, "Name"); r = config_get(config, section, "Exec"); if(q == NULL || r == NULL) continue; - config_set(config, NULL, "path", path); + /* remember the path */ + config_set(config, NULL, "path", name); desktop->apps = g_slist_insert_sorted(desktop->apps, config, _categories_apps_compare); - free(path); config = NULL; - return 0; } - free(path); - return -1; + free(name); + closedir(dir); + if(config != NULL) + config_delete(config); +} + +static void _refresh_loop_categories_dirs_xdg(Desktop * desktop, + void (*callback)(Desktop * desktop, char const * path, + char const * apppath)) +{ + char const * path; + char * p; + size_t i; + size_t j; + + if((path = getenv("XDG_DATA_DIRS")) == NULL || strlen(path) == 0) + /* FIXME use DATADIR */ + path = "/usr/local/share:/usr/share"; + if((p = strdup(path)) == NULL) + { + desktop_error(NULL, "strdup", 1); + return; + } + for(i = 0, j = 0;; i++) + if(p[i] == '\0') + { + _refresh_loop_categories_dirs_xdg_path(desktop, + callback, &p[j]); + break; + } + else if(p[i] == ':') + { + p[i] = '\0'; + _refresh_loop_categories_dirs_xdg_path(desktop, + callback, &p[j]); + j = i + 1; + } + free(p); + _refresh_loop_categories_dirs_xdg_home(desktop, callback); +} + +static void _refresh_loop_categories_dirs_xdg_home(Desktop * desktop, + void (*callback)(Desktop * desktop, char const * path, + char const * apppath)) +{ + char const fallback[] = ".local/share"; + char const * path; + char const * homedir; + size_t len; + char * p; + + /* use $XDG_DATA_HOME if set and not empty */ + if((path = getenv("XDG_DATA_HOME")) != NULL && strlen(path) > 0) + { + _refresh_loop_categories_dirs_xdg_path(desktop, callback, path); + return; + } + /* fallback to "$HOME/.local/share" */ + if((homedir = getenv("HOME")) == NULL) + homedir = g_get_home_dir(); + len = strlen(homedir) + 1 + sizeof(fallback); + if((p = malloc(len)) == NULL) + { + desktop_error(NULL, homedir, 1); + return; + } + snprintf(p, len, "%s/%s", homedir, fallback); + _refresh_loop_categories_dirs_xdg_path(desktop, callback, p); + free(p); +} + +static void _refresh_loop_categories_dirs_xdg_path(Desktop * desktop, + void (*callback)(Desktop * desktop, char const * path, + char const * apppath), char const * path) +{ + const char applications[] = "/applications"; + char * apppath; + + if((apppath = string_new_append(path, applications, NULL)) == NULL) + desktop_error(NULL, path, 1); + callback(desktop, path, apppath); + string_delete(apppath); } static gint _categories_apps_compare(gconstpointer a, gconstpointer b)