summaryrefslogtreecommitdiff
path: root/src/bin/efreet
diff options
context:
space:
mode:
authorGustavo Sverzut Barbieri <barbieri@gmail.com>2012-12-29 23:04:40 +0000
committerGustavo Sverzut Barbieri <barbieri@gmail.com>2012-12-29 23:04:40 +0000
commit4bc0210bd31ed1de6554441562bd93ea863ee9d9 (patch)
tree5d83be12538f8c8d3816bbf65916ce383d050c2e /src/bin/efreet
parent727ddbeaf0c53f31cd62c254fdebe26823d537eb (diff)
efl: merge efreet.
seems to be fine, pass distcheck and friends. please report. changes: - documentation hierarchy fixes - replaced __UNUSED__ with EINA_UNUSED - replaced PKG_DATA_DIR with PACKAGE_DATA_DIR"/efreet" SVN revision: 81889
Diffstat (limited to 'src/bin/efreet')
-rw-r--r--src/bin/efreet/efreet_desktop_cache_create.c465
-rw-r--r--src/bin/efreet/efreet_icon_cache_create.c1070
-rw-r--r--src/bin/efreet/efreetd.c59
-rw-r--r--src/bin/efreet/efreetd.h34
-rw-r--r--src/bin/efreet/efreetd_cache.c575
-rw-r--r--src/bin/efreet/efreetd_cache.h13
-rw-r--r--src/bin/efreet/efreetd_dbus.c262
-rw-r--r--src/bin/efreet/efreetd_dbus.h10
8 files changed, 2488 insertions, 0 deletions
diff --git a/src/bin/efreet/efreet_desktop_cache_create.c b/src/bin/efreet/efreet_desktop_cache_create.c
new file mode 100644
index 0000000000..db3118ca1d
--- /dev/null
+++ b/src/bin/efreet/efreet_desktop_cache_create.c
@@ -0,0 +1,465 @@
1#ifdef HAVE_CONFIG_H
2# include <config.h>
3#endif
4#include <sys/stat.h>
5#include <fcntl.h>
6#include <unistd.h>
7#include <errno.h>
8
9#include <Eina.h>
10#include <Eet.h>
11#include <Ecore.h>
12#include <Ecore_File.h>
13
14#define EFREET_MODULE_LOG_DOM _efreet_desktop_cache_log_dom
15static int _efreet_desktop_cache_log_dom = -1;
16
17#include "Efreet.h"
18#include "efreet_private.h"
19#include "efreet_cache_private.h"
20
21static Eet_Data_Descriptor *edd = NULL;
22static Eet_File *ef = NULL;
23static Eet_File *util_ef = NULL;
24
25static Eina_Hash *desktops = NULL;
26
27static Eina_Hash *file_ids = NULL;
28static Efreet_Cache_Hash *old_file_ids = NULL;
29static Eina_Hash *paths = NULL;
30
31static Eina_Hash *mime_types = NULL;
32static Eina_Hash *categories = NULL;
33static Eina_Hash *startup_wm_class = NULL;
34static Eina_Hash *name = NULL;
35static Eina_Hash *generic_name = NULL;
36static Eina_Hash *comment = NULL;
37static Eina_Hash *exec = NULL;
38
39static int
40cache_add(const char *path, const char *file_id, int priority EINA_UNUSED, int *changed)
41{
42 Efreet_Desktop *desk;
43 char *ext;
44
45 INF("FOUND: %s", path);
46 if (file_id) INF(" (id): %s", file_id);
47 ext = strrchr(path, '.');
48 if (!ext || (strcmp(ext, ".desktop") && strcmp(ext, ".directory"))) return 1;
49 desk = efreet_desktop_new(path);
50 if (desk) INF(" OK");
51 else INF(" FAIL");
52 if (!desk) return 1;
53 if (!desk->eet)
54 {
55 /* This file isn't in cache */
56 *changed = 1;
57 INF(" NEW");
58 }
59 else if (ecore_file_mod_time(desk->orig_path) != desk->load_time)
60 {
61 efreet_desktop_free(desk);
62 *changed = 1;
63 desk = efreet_desktop_uncached_new(path);
64 if (desk) INF(" CHANGED");
65 else INF(" NO UNCACHED");
66 }
67 if (!desk) return 1;
68 if (file_id && old_file_ids && !eina_hash_find(old_file_ids->hash, file_id))
69 {
70 *changed = 1;
71 INF(" NOT IN UTILS");
72 }
73 if (!eina_hash_find(paths, desk->orig_path))
74 {
75 if (!eet_data_write(ef, edd, desk->orig_path, desk, 0))
76 return 0;
77 eina_hash_add(paths, desk->orig_path, (void *)1);
78 }
79 /* TODO: We should check priority, and not just hope we search in right order */
80 /* TODO: We need to find out if prioritized file id has changed because of
81 * changed search order. */
82 if (!desk->hidden && desk->type == EFREET_DESKTOP_TYPE_APPLICATION &&
83 file_id && !eina_hash_find(file_ids, file_id))
84 {
85 Eina_List *l;
86 char *data;
87 Efreet_Cache_Array_String *array;
88
89#define ADD_LIST(list, hash) \
90 EINA_LIST_FOREACH((list), l, data) \
91 { \
92 array = eina_hash_find((hash), data); \
93 if (!array) \
94 array = NEW(Efreet_Cache_Array_String, 1); \
95 array->array = realloc(array->array, sizeof (char *) * (array->array_count + 1)); \
96 array->array[array->array_count++] = desk->orig_path; \
97 eina_hash_set((hash), data, array); \
98 }
99#define ADD_ELEM(elem, hash) \
100 if ((elem)) \
101 { \
102 data = (elem); \
103 array = eina_hash_find((hash), data); \
104 if (!array) \
105 array = NEW(Efreet_Cache_Array_String, 1); \
106 array->array = realloc(array->array, sizeof (char *) * (array->array_count + 1)); \
107 array->array[array->array_count++] = desk->orig_path; \
108 eina_hash_set((hash), data, array); \
109 }
110 ADD_LIST(desk->mime_types, mime_types);
111 ADD_LIST(desk->categories, categories);
112 ADD_ELEM(desk->startup_wm_class, startup_wm_class);
113 ADD_ELEM(desk->name, name);
114 ADD_ELEM(desk->generic_name, generic_name);
115 ADD_ELEM(desk->comment, comment);
116 ADD_ELEM(desk->exec, exec);
117 eina_hash_add(file_ids, file_id, desk->orig_path);
118 eina_hash_add(desktops, desk->orig_path, desk);
119 }
120 else
121 efreet_desktop_free(desk);
122 return 1;
123}
124
125
126static int
127cache_scan(const char *path, const char *base_id, int priority, int recurse, int *changed)
128{
129 char *file_id = NULL;
130 char id[PATH_MAX];
131 char buf[PATH_MAX];
132 Eina_Iterator *it;
133 Eina_File_Direct_Info *info;
134
135 if (!ecore_file_is_dir(path)) return 1;
136
137 it = eina_file_stat_ls(path);
138 if (!it) return 1;
139
140 id[0] = '\0';
141 EINA_ITERATOR_FOREACH(it, info)
142 {
143 const char *fname;
144
145 fname = info->path + info->name_start;
146 if (base_id)
147 {
148 if (*base_id)
149 snprintf(id, sizeof(id), "%s-%s", base_id, fname);
150 else
151 strcpy(id, fname);
152 file_id = id;
153 }
154
155 snprintf(buf, sizeof(buf), "%s/%s", path, fname);
156 if (info->type == EINA_FILE_DIR)
157 {
158 if (recurse)
159 cache_scan(buf, file_id, priority, recurse, changed);
160 }
161 else
162 {
163 if (!cache_add(buf, file_id, priority, changed))
164 {
165 eina_iterator_free(it);
166 return 0;
167 }
168 }
169 }
170 eina_iterator_free(it);
171 return 1;
172}
173
174static int
175cache_lock_file(void)
176{
177 char file[PATH_MAX];
178 struct flock fl;
179 int lockfd;
180
181 snprintf(file, sizeof(file), "%s/efreet/desktop_data.lock", efreet_cache_home_get());
182 lockfd = open(file, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
183 if (lockfd < 0) return -1;
184 efreet_fsetowner(lockfd);
185
186 memset(&fl, 0, sizeof(struct flock));
187 fl.l_type = F_WRLCK;
188 fl.l_whence = SEEK_SET;
189 if (fcntl(lockfd, F_SETLK, &fl) < 0)
190 {
191 INF("LOCKED! You may want to delete %s if this persists", file);
192 close(lockfd);
193 return -1;
194 }
195
196 return lockfd;
197}
198
199int
200main(int argc, char **argv)
201{
202 /* TODO:
203 * - Add file monitor on files, so that we catch changes on files
204 * during whilst this program runs.
205 * - Maybe linger for a while to reduce number of cache re-creates.
206 */
207 Efreet_Cache_Hash hash;
208 Efreet_Cache_Version version;
209 Eina_List *dirs = NULL;
210 Eina_List *systemdirs = NULL;
211 Eina_List *extra_dirs = NULL;
212 Eina_List *l = NULL;
213 int priority = 0;
214 char *dir = NULL;
215 char *path;
216 int lockfd = -1, tmpfd;
217 int changed = 0;
218 int i;
219 char file[PATH_MAX] = { '\0' };
220 char util_file[PATH_MAX] = { '\0' };
221
222 if (!eina_init()) goto eina_error;
223 _efreet_desktop_cache_log_dom =
224 eina_log_domain_register("efreet_desktop_cache", EFREET_DEFAULT_LOG_COLOR);
225 if (_efreet_desktop_cache_log_dom < 0)
226 {
227 EINA_LOG_ERR("Efreet: Could not create a log domain for efreet_desktop_cache.");
228 return -1;
229 }
230
231 for (i = 1; i < argc; i++)
232 {
233 if (!strcmp(argv[i], "-v"))
234 eina_log_domain_level_set("efreet_desktop_cache", EINA_LOG_LEVEL_DBG);
235 else if ((!strcmp(argv[i], "-h")) ||
236 (!strcmp(argv[i], "-help")) ||
237 (!strcmp(argv[i], "--h")) ||
238 (!strcmp(argv[i], "--help")))
239 {
240 printf("Options:\n");
241 printf(" -v Verbose mode\n");
242 printf(" -d dir1 dir2 Extra dirs\n");
243 exit(0);
244 }
245 else if (!strcmp(argv[i], "-d"))
246 {
247 while ((i < (argc - 1)) && (argv[(i + 1)][0] != '-'))
248 extra_dirs = eina_list_append(extra_dirs, argv[++i]);
249 }
250 }
251 extra_dirs = eina_list_sort(extra_dirs, -1, EINA_COMPARE_CB(strcmp));
252
253 /* init external subsystems */
254 if (!eet_init()) goto eet_error;
255 if (!ecore_init()) goto ecore_error;
256
257 efreet_cache_update = 0;
258 /* finish efreet init */
259 if (!efreet_init()) goto efreet_error;
260
261 /* create homedir */
262 snprintf(file, sizeof(file), "%s/efreet", efreet_cache_home_get());
263 if (!ecore_file_exists(file))
264 {
265 if (!ecore_file_mkpath(file)) goto efreet_error;
266 efreet_setowner(file);
267 }
268
269 /* lock process, so that we only run one copy of this program */
270 lockfd = cache_lock_file();
271 if (lockfd == -1) goto efreet_error;
272
273 edd = efreet_desktop_edd();
274 if (!edd) goto edd_error;
275
276 /* read user dirs from old cache */
277 ef = eet_open(efreet_desktop_util_cache_file(), EET_FILE_MODE_READ);
278 if (ef)
279 {
280 old_file_ids = eet_data_read(ef, efreet_hash_string_edd(), "file_id");
281 eet_close(ef);
282 }
283
284 /* create cache */
285 snprintf(file, sizeof(file), "%s.XXXXXX", efreet_desktop_cache_file());
286 tmpfd = mkstemp(file);
287 if (tmpfd < 0) goto error;
288 close(tmpfd);
289 ef = eet_open(file, EET_FILE_MODE_READ_WRITE);
290 if (!ef) goto error;
291
292 snprintf(util_file, sizeof(util_file), "%s.XXXXXX", efreet_desktop_util_cache_file());
293 tmpfd = mkstemp(util_file);
294 if (tmpfd < 0) goto error;
295 close(tmpfd);
296 util_ef = eet_open(util_file, EET_FILE_MODE_READ_WRITE);
297 if (!util_ef) goto error;
298
299 /* write cache version */
300 version.major = EFREET_DESKTOP_UTILS_CACHE_MAJOR;
301 version.minor = EFREET_DESKTOP_UTILS_CACHE_MINOR;
302 eet_data_write(util_ef, efreet_version_edd(), EFREET_CACHE_VERSION, &version, 1);
303 version.major = EFREET_DESKTOP_CACHE_MAJOR;
304 version.minor = EFREET_DESKTOP_CACHE_MINOR;
305 eet_data_write(ef, efreet_version_edd(), EFREET_CACHE_VERSION, &version, 1);
306
307 desktops = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_desktop_free));
308
309 file_ids = eina_hash_string_superfast_new(NULL);
310 paths = eina_hash_string_superfast_new(NULL);
311
312 mime_types = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_array_string_free));
313 categories = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_array_string_free));
314 startup_wm_class = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_array_string_free));
315 name = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_array_string_free));
316 generic_name = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_array_string_free));
317 comment = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_array_string_free));
318 exec = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_array_string_free));
319
320 dirs = efreet_default_dirs_get(efreet_data_home_get(), efreet_data_dirs_get(),
321 "applications");
322 if (!dirs) goto error;
323
324 EINA_LIST_FREE(dirs, path)
325 {
326 char file_id[PATH_MAX] = { '\0' };
327
328 if (!cache_scan(path, file_id, priority++, 1, &changed)) goto error;
329 systemdirs = eina_list_append(systemdirs, path);
330 }
331
332 EINA_LIST_FOREACH(extra_dirs, l, path)
333 if (!cache_scan(path, NULL, priority, 0, &changed)) goto error;
334
335 /* store util */
336#define STORE_HASH_ARRAY(_hash) \
337 if (eina_hash_population((_hash)) > 0) \
338 { \
339 Eina_Iterator *it; \
340 Efreet_Cache_Array_String array; \
341 const char *str; \
342 \
343 hash.hash = (_hash); \
344 eet_data_write(util_ef, efreet_hash_array_string_edd(), #_hash "_hash", &hash, 1); \
345 array.array_count = 0; \
346 array.array = malloc(eina_hash_population(hash.hash) * sizeof(char *)); \
347 it = eina_hash_iterator_key_new(hash.hash); \
348 EINA_ITERATOR_FOREACH(it, str) \
349 array.array[array.array_count++] = str; \
350 eina_iterator_free(it); \
351 eet_data_write(util_ef, efreet_array_string_edd(), #_hash "_list", &array, 1); \
352 free(array.array); \
353 }
354 STORE_HASH_ARRAY(mime_types);
355 STORE_HASH_ARRAY(categories);
356 STORE_HASH_ARRAY(startup_wm_class);
357 STORE_HASH_ARRAY(name);
358 STORE_HASH_ARRAY(generic_name);
359 STORE_HASH_ARRAY(comment);
360 STORE_HASH_ARRAY(exec);
361 if (eina_hash_population(file_ids) > 0)
362 {
363 hash.hash = file_ids;
364 eet_data_write(util_ef, efreet_hash_string_edd(), "file_id", &hash, 1);
365 }
366
367 eina_hash_free(mime_types);
368 eina_hash_free(categories);
369 eina_hash_free(startup_wm_class);
370 eina_hash_free(name);
371 eina_hash_free(generic_name);
372 eina_hash_free(comment);
373 eina_hash_free(exec);
374
375 if (old_file_ids)
376 {
377 eina_hash_free(old_file_ids->hash);
378 free(old_file_ids);
379 }
380
381 eina_hash_free(file_ids);
382 eina_hash_free(paths);
383
384 eina_hash_free(desktops);
385
386 /* check if old and new caches contain the same number of entries */
387 if (!changed)
388 {
389 Eet_File *old;
390
391 old = eet_open(efreet_desktop_cache_file(), EET_FILE_MODE_READ);
392 if (!old || eet_num_entries(old) != eet_num_entries(ef)) changed = 1;
393 if (old) eet_close(old);
394 old = eet_open(efreet_desktop_util_cache_file(), EET_FILE_MODE_READ);
395 if (!old || eet_num_entries(old) != eet_num_entries(util_ef)) changed = 1;
396 if (old) eet_close(old);
397 }
398
399 /* cleanup */
400 eet_close(util_ef);
401 eet_close(ef);
402
403 /* unlink old cache files */
404 if (changed)
405 {
406 if (unlink(efreet_desktop_cache_file()) < 0)
407 {
408 if (errno != ENOENT) goto error;
409 }
410 if (unlink(efreet_desktop_util_cache_file()) < 0)
411 {
412 if (errno != ENOENT) goto error;
413 }
414 /* rename tmp files to real files */
415 if (rename(util_file, efreet_desktop_util_cache_file()) < 0) goto error;
416 efreet_setowner(efreet_desktop_util_cache_file());
417 if (rename(file, efreet_desktop_cache_file()) < 0) goto error;
418 efreet_setowner(efreet_desktop_cache_file());
419 }
420 else
421 {
422 unlink(util_file);
423 unlink(file);
424 }
425
426 {
427 char c = 'n';
428
429 if (changed) c = 'c';
430 printf("%c\n", c);
431 }
432
433 EINA_LIST_FREE(systemdirs, dir)
434 eina_stringshare_del(dir);
435 eina_list_free(extra_dirs);
436 efreet_shutdown();
437 ecore_shutdown();
438 eet_shutdown();
439 eina_log_domain_unregister(_efreet_desktop_cache_log_dom);
440 eina_shutdown();
441 close(lockfd);
442 return 0;
443error:
444 IF_FREE(dir);
445edd_error:
446 if (old_file_ids)
447 {
448 eina_hash_free(old_file_ids->hash);
449 free(old_file_ids);
450 }
451 efreet_shutdown();
452efreet_error:
453 ecore_shutdown();
454ecore_error:
455 eet_shutdown();
456eet_error:
457 EINA_LIST_FREE(systemdirs, dir)
458 eina_stringshare_del(dir);
459 eina_list_free(extra_dirs);
460 eina_log_domain_unregister(_efreet_desktop_cache_log_dom);
461 eina_shutdown();
462eina_error:
463 if (lockfd >= 0) close(lockfd);
464 return 1;
465}
diff --git a/src/bin/efreet/efreet_icon_cache_create.c b/src/bin/efreet/efreet_icon_cache_create.c
new file mode 100644
index 0000000000..662dd09469
--- /dev/null
+++ b/src/bin/efreet/efreet_icon_cache_create.c
@@ -0,0 +1,1070 @@
1#ifdef HAVE_CONFIG_H
2# include <config.h>
3#endif
4
5#include "efreet_alloca.h"
6
7#include <sys/stat.h>
8#include <fcntl.h>
9#include <unistd.h>
10#include <errno.h>
11
12#include <Eina.h>
13#include <Eet.h>
14#include <Ecore.h>
15#include <Ecore_File.h>
16
17#define EFREET_MODULE_LOG_DOM _efreet_icon_cache_log_dom
18static int _efreet_icon_cache_log_dom = -1;
19
20#include "Efreet.h"
21#include "efreet_private.h"
22#include "efreet_cache_private.h"
23
24static Eina_Array *exts = NULL;
25static Eina_Array *extra_dirs = NULL;
26static Eina_Array *strs = NULL;
27static Eina_Hash *icon_themes = NULL;
28
29static Eina_Bool
30cache_directory_modified(Eina_Hash *dirs, const char *dir)
31{
32 Efreet_Cache_Directory *dcache;
33 struct stat st;
34
35 if (!dirs) return EINA_TRUE;
36
37 if (stat(dir, &st) < 0) return EINA_FALSE;
38 dcache = eina_hash_find(dirs, dir);
39 if (!dcache)
40 {
41 dcache = malloc(sizeof (Efreet_Cache_Directory));
42 if (!dcache) return EINA_TRUE;
43
44 dcache->modified_time = (long long) st.st_mtime;
45 eina_hash_add(dirs, dir, dcache);
46 }
47 else if (dcache->modified_time == (long long) st.st_mtime) return EINA_FALSE;
48 dcache->modified_time = st.st_mtime;
49
50 return EINA_TRUE;
51}
52
53static Eina_Bool
54cache_extension_lookup(const char *ext)
55{
56 unsigned int i;
57
58 for (i = 0; i < exts->count; ++i)
59 if (!strcmp(exts->data[i], ext))
60 return EINA_TRUE;
61 return EINA_FALSE;
62}
63
64static Eina_Bool
65cache_fallback_scan_dir(Eina_Hash *icons, Eina_Hash *dirs, const char *dir)
66{
67 Eina_Iterator *it;
68 Eina_File_Direct_Info *entry;
69
70 if (!cache_directory_modified(dirs, dir)) return EINA_TRUE;
71
72 it = eina_file_stat_ls(dir);
73 if (!it) return EINA_TRUE;
74
75 EINA_ITERATOR_FOREACH(it, entry)
76 {
77 Efreet_Cache_Fallback_Icon *icon;
78 char *name;
79 char *ext;
80 unsigned int i;
81
82 if (entry->type == EINA_FILE_DIR)
83 continue;
84
85 ext = strrchr(entry->path + entry->name_start, '.');
86 if (!ext || !cache_extension_lookup(ext))
87 continue;
88
89 /* icon with known extension */
90 name = entry->path + entry->name_start;
91 *ext = '\0';
92
93 icon = eina_hash_find(icons, name);
94 if (!icon)
95 {
96 icon = NEW(Efreet_Cache_Fallback_Icon, 1);
97 icon->theme = NULL;
98 eina_hash_add(icons, name, icon);
99 }
100
101 *ext = '.';
102
103 for (i = 0; i < icon->icons_count; ++i)
104 if (!strcmp(icon->icons[i], entry->path))
105 break;
106
107 if (i != icon->icons_count)
108 continue;
109
110 icon->icons = realloc(icon->icons, sizeof (char *) * (icon->icons_count + 1));
111 icon->icons[icon->icons_count] = eina_stringshare_add(entry->path);
112 eina_array_push(strs, icon->icons[icon->icons_count++]);
113 }
114
115 eina_iterator_free(it);
116
117 return EINA_TRUE;
118}
119
120static Eina_Bool
121cache_fallback_scan(Eina_Hash *icons, Eina_Hash *dirs)
122{
123 unsigned int i;
124 Eina_List *xdg_dirs, *l;
125 const char *dir;
126 char path[PATH_MAX];
127
128 for (i = 0; i < extra_dirs->count; i++)
129 cache_fallback_scan_dir(icons, dirs, extra_dirs->data[i]);
130
131 cache_fallback_scan_dir(icons, dirs, efreet_icon_deprecated_user_dir_get());
132 cache_fallback_scan_dir(icons, dirs, efreet_icon_user_dir_get());
133
134 xdg_dirs = efreet_data_dirs_get();
135 EINA_LIST_FOREACH(xdg_dirs, l, dir)
136 {
137 snprintf(path, sizeof(path), "%s/icons", dir);
138 cache_fallback_scan_dir(icons, dirs, path);
139 }
140
141#ifndef STRICT_SPEC
142 EINA_LIST_FOREACH(xdg_dirs, l, dir)
143 {
144 snprintf(path, sizeof(path), "%s/pixmaps", dir);
145 cache_fallback_scan_dir(icons, dirs, path);
146 }
147#endif
148
149 cache_fallback_scan_dir(icons, dirs, "/usr/share/pixmaps");
150
151 return EINA_TRUE;
152}
153
154static Eina_Bool
155check_fallback_changed(Efreet_Cache_Icon_Theme *theme)
156{
157 unsigned int i;
158 Eina_List *xdg_dirs, *l;
159 const char *dir;
160 char path[PATH_MAX];
161
162 /* Check if the dirs we have cached are changed */
163 if (theme->dirs)
164 {
165 Eina_Iterator *it;
166 Eina_Bool changed = EINA_FALSE;
167
168 it = eina_hash_iterator_key_new(theme->dirs);
169 EINA_ITERATOR_FOREACH(it, dir)
170 {
171 changed = !ecore_file_exists(dir);
172 if (changed) break;
173 changed = cache_directory_modified(theme->dirs, dir);
174 if (changed) break;
175 }
176 eina_iterator_free(it);
177 if (changed) return EINA_TRUE;
178 }
179
180 /* Check if spec dirs have changed */
181 for (i = 0; i < extra_dirs->count; i++)
182 if (cache_directory_modified(theme->dirs, extra_dirs->data[i])) return EINA_TRUE;
183
184 if (cache_directory_modified(theme->dirs, efreet_icon_deprecated_user_dir_get())) return EINA_TRUE;
185 if (cache_directory_modified(theme->dirs, efreet_icon_user_dir_get())) return EINA_TRUE;
186
187 xdg_dirs = efreet_data_dirs_get();
188 EINA_LIST_FOREACH(xdg_dirs, l, dir)
189 {
190 snprintf(path, sizeof(path), "%s/icons", dir);
191 if (cache_directory_modified(theme->dirs, path)) return EINA_TRUE;
192 }
193
194#ifndef STRICT_SPEC
195 EINA_LIST_FOREACH(xdg_dirs, l, dir)
196 {
197 snprintf(path, sizeof(path), "%s/pixmaps", dir);
198 if (cache_directory_modified(theme->dirs, path)) return EINA_TRUE;
199 }
200#endif
201
202 if (cache_directory_modified(theme->dirs, "/usr/share/pixmaps")) return EINA_TRUE;
203 return EINA_FALSE;
204}
205
206static Eina_Bool
207cache_scan_path_dir(Efreet_Icon_Theme *theme,
208 const char *path,
209 Efreet_Icon_Theme_Directory *dir,
210 Eina_Hash *icons)
211{
212 Eina_Iterator *it;
213 char buf[PATH_MAX];
214 Eina_File_Direct_Info *entry;
215
216 snprintf(buf, sizeof(buf), "%s/%s", path, dir->name);
217
218 it = eina_file_stat_ls(buf);
219 if (!it) return EINA_TRUE;
220
221 EINA_ITERATOR_FOREACH(it, entry)
222 {
223 Efreet_Cache_Icon *icon;
224 char *name;
225 char *ext;
226 unsigned int i;
227
228 if (entry->type == EINA_FILE_DIR)
229 continue;
230
231 ext = strrchr(entry->path + entry->name_start, '.');
232 if (!ext || !cache_extension_lookup(ext))
233 continue;
234
235 /* icon with known extension */
236 name = entry->path + entry->name_start;
237 *ext = '\0';
238
239 icon = eina_hash_find(icons, name);
240 if (!icon)
241 {
242 icon = NEW(Efreet_Cache_Icon, 1);
243 icon->theme = eina_stringshare_add(theme->name.internal);
244 eina_array_push(strs, icon->theme);
245 eina_hash_add(icons, name, icon);
246 }
247
248 /* find if we have the same icon in another type */
249 for (i = 0; i < icon->icons_count; ++i)
250 {
251 if ((icon->icons[i]->type == dir->type) &&
252 (icon->icons[i]->normal == dir->size.normal) &&
253 (icon->icons[i]->max == dir->size.max) &&
254 (icon->icons[i]->min == dir->size.min))
255 break;
256 }
257
258 *ext = '.';
259
260 if (i != icon->icons_count)
261 {
262 unsigned int j;
263
264 /* check if the path already exist */
265 for (j = 0; j < icon->icons[i]->paths_count; ++j)
266 if (!strcmp(icon->icons[i]->paths[j], entry->path))
267 break;
268
269 if (j != icon->icons[i]->paths_count)
270 continue;
271
272 /* If we are inherited, check if we already have extension */
273 if (strcmp(icon->theme, theme->name.internal))
274 {
275 const char *ext2;
276 int has_ext = 0;
277 for (j = 0; j < icon->icons[i]->paths_count; ++j)
278 {
279 ext2 = strrchr(icon->icons[i]->paths[j], '.');
280 if (ext2)
281 {
282 ext2++;
283 has_ext = !strcmp((ext + 1), ext2);
284 if (has_ext) break;
285 }
286 }
287 if (has_ext)
288 continue;
289 }
290 }
291 /* no icon match so add a new one */
292 /* only allow to add new icon for main theme
293 * if we allow inherited theme to add new icons,
294 * we will get weird effects when icon scales
295 */
296 else if (!strcmp(icon->theme, theme->name.internal))
297 {
298 icon->icons = realloc(icon->icons,
299 sizeof (Efreet_Cache_Icon_Element*) * (++icon->icons_count));
300 icon->icons[i] = NEW(Efreet_Cache_Icon_Element, 1);
301 icon->icons[i]->type = dir->type;
302 icon->icons[i]->normal = dir->size.normal;
303 icon->icons[i]->min = dir->size.min;
304 icon->icons[i]->max = dir->size.max;
305 icon->icons[i]->paths = NULL;
306 icon->icons[i]->paths_count = 0;
307 }
308 else
309 {
310 continue;
311 }
312
313 /* and finally store the path */
314 icon->icons[i]->paths = realloc(icon->icons[i]->paths,
315 sizeof (char*) * (icon->icons[i]->paths_count + 1));
316 icon->icons[i]->paths[icon->icons[i]->paths_count] = eina_stringshare_add(entry->path);
317 eina_array_push(strs, icon->icons[i]->paths[icon->icons[i]->paths_count++]);
318 }
319
320 eina_iterator_free(it);
321
322 return EINA_TRUE;
323}
324
325static Eina_Bool
326cache_scan_path(Efreet_Icon_Theme *theme, Eina_Hash *icons, const char *path)
327{
328 Eina_List *l;
329 Efreet_Icon_Theme_Directory *dir;
330
331 EINA_LIST_FOREACH(theme->directories, l, dir)
332 if (!cache_scan_path_dir(theme, path, dir, icons)) return EINA_FALSE;
333
334 return EINA_TRUE;
335}
336
337static Eina_Bool
338cache_scan(Efreet_Icon_Theme *theme, Eina_Hash *themes, Eina_Hash *icons)
339{
340 Eina_List *l;
341 const char *path;
342 const char *name;
343
344 if (!theme) return EINA_TRUE;
345 if (eina_hash_find(themes, theme->name.internal)) return EINA_TRUE;
346 eina_hash_direct_add(themes, theme->name.internal, theme);
347
348 /* scan theme */
349 EINA_LIST_FOREACH(theme->paths, l, path)
350 if (!cache_scan_path(theme, icons, path)) return EINA_FALSE;
351
352 /* scan inherits */
353 if (theme->inherits)
354 {
355 EINA_LIST_FOREACH(theme->inherits, l, name)
356 {
357 Efreet_Icon_Theme *inherit;
358
359 inherit = eina_hash_find(icon_themes, name);
360 if (!inherit)
361 INF("Theme `%s` not found for `%s`.",
362 name, theme->name.internal);
363 if (!cache_scan(inherit, themes, icons)) return EINA_FALSE;
364 }
365 }
366 else if (strcmp(theme->name.internal, "hicolor"))
367 {
368 theme = eina_hash_find(icon_themes, "hicolor");
369 if (!cache_scan(theme, themes, icons)) return EINA_FALSE;
370 }
371
372 return EINA_TRUE;
373}
374
375static Eina_Bool
376check_changed(Efreet_Cache_Icon_Theme *theme)
377{
378 Eina_List *l;
379 const char *name;
380
381 if (!theme) return EINA_FALSE;
382
383 if (theme->changed) return EINA_TRUE;
384 if (theme->theme.inherits)
385 {
386 EINA_LIST_FOREACH(theme->theme.inherits, l, name)
387 {
388 Efreet_Cache_Icon_Theme *inherit;
389
390 inherit = eina_hash_find(icon_themes, name);
391 if (!inherit)
392 INF("Theme `%s` not found for `%s`.",
393 name, theme->theme.name.internal);
394 if (check_changed(inherit)) return EINA_TRUE;
395 }
396 }
397 else if (strcmp(theme->theme.name.internal, "hicolor"))
398 {
399 theme = eina_hash_find(icon_themes, "hicolor");
400 if (check_changed(theme)) return EINA_TRUE;
401 }
402 return EINA_FALSE;
403}
404
405static Efreet_Icon_Theme_Directory *
406icon_theme_directory_new(Efreet_Ini *ini, const char *name)
407{
408 Efreet_Icon_Theme_Directory *dir;
409 int val;
410 const char *tmp;
411
412 if (!ini) return NULL;
413
414 dir = NEW(Efreet_Icon_Theme_Directory, 1);
415 if (!dir) return NULL;
416 dir->name = eina_stringshare_add(name);
417 eina_array_push(strs, dir->name);
418
419 efreet_ini_section_set(ini, name);
420
421 tmp = efreet_ini_string_get(ini, "Context");
422 if (tmp)
423 {
424 if (!strcasecmp(tmp, "Actions"))
425 dir->context = EFREET_ICON_THEME_CONTEXT_ACTIONS;
426
427 else if (!strcasecmp(tmp, "Devices"))
428 dir->context = EFREET_ICON_THEME_CONTEXT_DEVICES;
429
430 else if (!strcasecmp(tmp, "FileSystems"))
431 dir->context = EFREET_ICON_THEME_CONTEXT_FILESYSTEMS;
432
433 else if (!strcasecmp(tmp, "MimeTypes"))
434 dir->context = EFREET_ICON_THEME_CONTEXT_MIMETYPES;
435 }
436
437 /* Threshold is fallback */
438 dir->type = EFREET_ICON_SIZE_TYPE_THRESHOLD;
439
440 tmp = efreet_ini_string_get(ini, "Type");
441 if (tmp)
442 {
443 if (!strcasecmp(tmp, "Fixed"))
444 dir->type = EFREET_ICON_SIZE_TYPE_FIXED;
445
446 else if (!strcasecmp(tmp, "Scalable"))
447 dir->type = EFREET_ICON_SIZE_TYPE_SCALABLE;
448 }
449
450 dir->size.normal = efreet_ini_int_get(ini, "Size");
451
452 if (dir->type == EFREET_ICON_SIZE_TYPE_THRESHOLD)
453 {
454 val = efreet_ini_int_get(ini, "Threshold");
455 if (val < 0) val = 2;
456 dir->size.max = dir->size.normal + val;
457 dir->size.min = dir->size.normal - val;
458 }
459 else if (dir->type == EFREET_ICON_SIZE_TYPE_SCALABLE)
460 {
461 val = efreet_ini_int_get(ini, "MinSize");
462 if (val < 0) dir->size.min = dir->size.normal;
463 else dir->size.min = val;
464
465 val = efreet_ini_int_get(ini, "MaxSize");
466 if (val < 0) dir->size.max = dir->size.normal;
467 else dir->size.max = val;
468 }
469
470 return dir;
471}
472
473static Eina_Bool
474icon_theme_index_read(Efreet_Cache_Icon_Theme *theme, const char *path)
475{
476 Efreet_Ini *ini;
477 Efreet_Icon_Theme_Directory *dir;
478 const char *tmp;
479 struct stat st;
480
481 if (!theme || !path) return EINA_FALSE;
482
483 if (stat(path, &st) < 0) return EINA_FALSE;
484 if (theme->path && !strcmp(theme->path, path) && theme->last_cache_check >= (long long) st.st_mtime)
485 {
486 /* no change */
487 theme->valid = 1;
488 return EINA_TRUE;
489 }
490 if (!theme->path || strcmp(theme->path, path))
491 {
492 theme->path = eina_stringshare_add(path);
493 eina_array_push(strs, theme->path);
494 }
495 if ((long long) st.st_mtime > theme->last_cache_check)
496 theme->last_cache_check = (long long) st.st_mtime;
497 theme->changed = 1;
498
499 ini = efreet_ini_new(path);
500 if (!ini) return EINA_FALSE;
501 if (!ini->data)
502 {
503 efreet_ini_free(ini);
504 return EINA_FALSE;
505 }
506
507 efreet_ini_section_set(ini, "Icon Theme");
508 tmp = efreet_ini_localestring_get(ini, "Name");
509 if (tmp)
510 {
511 theme->theme.name.name = eina_stringshare_add(tmp);
512 eina_array_push(strs, theme->theme.name.name);
513 }
514
515 tmp = efreet_ini_localestring_get(ini, "Comment");
516 if (tmp)
517 {
518 theme->theme.comment = eina_stringshare_add(tmp);
519 eina_array_push(strs, theme->theme.comment);
520 }
521
522 tmp = efreet_ini_string_get(ini, "Example");
523 if (tmp)
524 {
525 theme->theme.example_icon = eina_stringshare_add(tmp);
526 eina_array_push(strs, theme->theme.example_icon);
527 }
528
529 theme->hidden = efreet_ini_boolean_get(ini, "Hidden");
530
531 theme->valid = 1;
532
533 /* Check the inheritance. If there is none we inherit from the hicolor theme */
534 tmp = efreet_ini_string_get(ini, "Inherits");
535 if (tmp)
536 {
537 char *t, *s, *p;
538 const char *i;
539 size_t len;
540
541 len = strlen(tmp) + 1;
542 t = alloca(len);
543 memcpy(t, tmp, len);
544 s = t;
545 p = strchr(s, ',');
546
547 while (p)
548 {
549 *p = '\0';
550
551 i = eina_stringshare_add(s);
552 theme->theme.inherits = eina_list_append(theme->theme.inherits, i);
553 eina_array_push(strs, i);
554 s = ++p;
555 p = strchr(s, ',');
556 }
557 i = eina_stringshare_add(s);
558 theme->theme.inherits = eina_list_append(theme->theme.inherits, i);
559 eina_array_push(strs, i);
560 }
561
562 /* make sure this one is done last as setting the directory will change
563 * the ini section ... */
564 tmp = efreet_ini_string_get(ini, "Directories");
565 if (tmp)
566 {
567 char *t, *s, *p;
568 size_t len;
569
570 len = strlen(tmp) + 1;
571 t = alloca(len);
572 memcpy(t, tmp, len);
573 s = t;
574 p = s;
575
576 while ((p) && (*s))
577 {
578 p = strchr(s, ',');
579
580 if (p) *p = '\0';
581
582 dir = icon_theme_directory_new(ini, s);
583 if (!dir) goto error;
584 theme->theme.directories = eina_list_append(theme->theme.directories, dir);
585
586 if (p) s = ++p;
587 }
588 }
589
590error:
591 efreet_ini_free(ini);
592
593 return EINA_TRUE;
594}
595
596static Eina_Bool
597cache_theme_scan(const char *dir)
598{
599 Eina_Iterator *it;
600 Eina_File_Direct_Info *entry;
601
602 it = eina_file_stat_ls(dir);
603 if (!it) return EINA_TRUE;
604
605 EINA_ITERATOR_FOREACH(it, entry)
606 {
607 Efreet_Cache_Icon_Theme *theme;
608 const char *name;
609 const char *path;
610 char buf[PATH_MAX];
611 struct stat st;
612
613 if (stat(entry->path, &st) < 0) continue;
614
615 if ((entry->type != EINA_FILE_DIR) &&
616 (entry->type != EINA_FILE_LNK))
617 continue;
618
619 name = entry->path + entry->name_start;
620 theme = eina_hash_find(icon_themes, name);
621
622 if (!theme)
623 {
624 theme = NEW(Efreet_Cache_Icon_Theme, 1);
625 theme->theme.name.internal = eina_stringshare_add(name);
626 eina_array_push(strs, theme->theme.name.internal);
627 eina_hash_direct_add(icon_themes,
628 (void *)theme->theme.name.internal, theme);
629 theme->changed = 1;
630 }
631 if ((long long) st.st_mtime > theme->last_cache_check)
632 {
633 theme->last_cache_check = (long long) st.st_mtime;
634 theme->changed = 1;
635 }
636
637 /* TODO: We need to handle change in order of included paths */
638 if (!eina_list_search_unsorted(theme->theme.paths, EINA_COMPARE_CB(strcmp), entry->path))
639 {
640 path = eina_stringshare_add(entry->path);
641 theme->theme.paths = eina_list_append(theme->theme.paths, path);
642 eina_array_push(strs, path);
643 theme->changed = 1;
644 }
645
646 /* we're already valid so no reason to check for an index.theme file */
647 if (theme->valid) continue;
648
649 /* if the index.theme file exists we parse it into the theme */
650 memcpy(buf, entry->path, entry->path_length);
651 memcpy(buf + entry->path_length, "/index.theme", sizeof("/index.theme"));
652 if (ecore_file_exists(buf))
653 {
654 if (!icon_theme_index_read(theme, buf))
655 theme->valid = 0;
656 }
657 }
658 eina_iterator_free(it);
659 return EINA_TRUE;
660}
661
662static int
663cache_lock_file(void)
664{
665 char file[PATH_MAX];
666 struct flock fl;
667 int lockfd;
668
669 snprintf(file, sizeof(file), "%s/efreet/icon_data.lock", efreet_cache_home_get());
670 lockfd = open(file, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
671 if (lockfd < 0) return -1;
672 efreet_fsetowner(lockfd);
673
674 memset(&fl, 0, sizeof(struct flock));
675 fl.l_type = F_WRLCK;
676 fl.l_whence = SEEK_SET;
677 if (fcntl(lockfd, F_SETLK, &fl) < 0)
678 {
679 WRN("LOCKED! You may want to delete %s if this persists", file);
680 close(lockfd);
681 return -1;
682 }
683
684 return lockfd;
685}
686
687static void
688icon_theme_free(Efreet_Cache_Icon_Theme *theme)
689{
690 void *data;
691
692 eina_list_free(theme->theme.paths);
693 eina_list_free(theme->theme.inherits);
694 EINA_LIST_FREE(theme->theme.directories, data)
695 free(data);
696 if (theme->dirs) efreet_hash_free(theme->dirs, free);
697 free(theme);
698}
699
700int
701main(int argc, char **argv)
702{
703 /* TODO:
704 * - Add file monitor on files, so that we catch changes on files
705 * during whilst this program runs.
706 * - Maybe linger for a while to reduce number of cache re-creates.
707 */
708 Eina_Iterator *it;
709 Efreet_Cache_Version *icon_version;
710 Efreet_Cache_Version *theme_version;
711 Efreet_Cache_Icon_Theme *theme;
712 Eet_Data_Descriptor *theme_edd;
713 Eet_Data_Descriptor *icon_edd;
714 Eet_Data_Descriptor *fallback_edd;
715 Eet_File *icon_ef;
716 Eet_File *theme_ef;
717 Eina_List *xdg_dirs = NULL;
718 Eina_List *l = NULL;
719 char file[PATH_MAX];
720 const char *path;
721 char *dir = NULL;
722 Eina_Bool changed = EINA_FALSE;
723 Eina_Bool flush = EINA_FALSE;
724 int lockfd = -1;
725 char **keys;
726 int num, i;
727
728 /* init external subsystems */
729 if (!eina_init()) return -1;
730 _efreet_icon_cache_log_dom =
731 eina_log_domain_register("efreet_icon_cache", EFREET_DEFAULT_LOG_COLOR);
732 if (_efreet_icon_cache_log_dom < 0)
733 {
734 EINA_LOG_ERR("Efreet: Could not create a log domain for efreet_icon_cache.");
735 return -1;
736 }
737
738 eina_log_domain_level_set("efreet_icon_cache", EINA_LOG_LEVEL_ERR);
739
740 exts = eina_array_new(10);
741 extra_dirs = eina_array_new(10);
742
743 for (i = 1; i < argc; i++)
744 {
745 if (!strcmp(argv[i], "-v"))
746 eina_log_domain_level_set("efreet_icon_cache", EINA_LOG_LEVEL_DBG);
747 else if ((!strcmp(argv[i], "-h")) ||
748 (!strcmp(argv[i], "-help")) ||
749 (!strcmp(argv[i], "--h")) ||
750 (!strcmp(argv[i], "--help")))
751 {
752 printf("Options:\n");
753 printf(" -v Verbose mode\n");
754 printf(" -e .ext1 .ext2 Extensions\n");
755 printf(" -d dir1 dir2 Extra dirs\n");
756 printf(" -f Flush\n");
757 exit(0);
758 }
759 else if (!strcmp(argv[i], "-e"))
760 {
761 while ((i < (argc - 1)) && (argv[(i + 1)][0] != '-'))
762 eina_array_push(exts, argv[++i]);
763 }
764 else if (!strcmp(argv[i], "-d"))
765 {
766 while ((i < (argc - 1)) && (argv[(i + 1)][0] != '-'))
767 eina_array_push(extra_dirs, argv[++i]);
768 }
769 else if (!strcmp(argv[i], "-d"))
770 flush = EINA_TRUE;
771 }
772
773 if (!eet_init()) return -1;
774 if (!ecore_init()) return -1;
775
776 efreet_cache_update = 0;
777 /* finish efreet init */
778 if (!efreet_init()) goto on_error;
779
780 strs = eina_array_new(32);
781
782 /* create homedir */
783 snprintf(file, sizeof(file), "%s/efreet", efreet_cache_home_get());
784 if (!ecore_file_exists(file))
785 {
786 if (!ecore_file_mkpath(file)) return -1;
787 efreet_setowner(file);
788 }
789
790 /* lock process, so that we only run one copy of this program */
791 lockfd = cache_lock_file();
792 if (lockfd == -1) goto on_error;
793
794 /* Need to init edd's, so they are like we want, not like userspace wants */
795 icon_edd = efreet_icon_edd();
796 fallback_edd = efreet_icon_fallback_edd();
797 theme_edd = efreet_icon_theme_edd(EINA_TRUE);
798
799 icon_themes = eina_hash_string_superfast_new(EINA_FREE_CB(icon_theme_free));
800
801 INF("opening theme cache");
802 /* open theme file */
803 theme_ef = eet_open(efreet_icon_theme_cache_file(), EET_FILE_MODE_READ_WRITE);
804 if (!theme_ef) goto on_error_efreet;
805 theme_version = eet_data_read(theme_ef, efreet_version_edd(), EFREET_CACHE_VERSION);
806 if (theme_version &&
807 ((theme_version->major != EFREET_ICON_CACHE_MAJOR) ||
808 (theme_version->minor != EFREET_ICON_CACHE_MINOR)))
809 {
810 // delete old cache
811 eet_close(theme_ef);
812 if (unlink(efreet_icon_theme_cache_file()) < 0)
813 {
814 if (errno != ENOENT) goto on_error_efreet;
815 }
816 theme_ef = eet_open(efreet_icon_theme_cache_file(), EET_FILE_MODE_READ_WRITE);
817 if (!theme_ef) goto on_error_efreet;
818 }
819 if (!theme_version)
820 theme_version = NEW(Efreet_Cache_Version, 1);
821
822 theme_version->major = EFREET_ICON_CACHE_MAJOR;
823 theme_version->minor = EFREET_ICON_CACHE_MINOR;
824
825 if (flush)
826 changed = EINA_TRUE;
827
828 if (exts->count == 0)
829 {
830 ERR("Need to pass extensions to icon cache create process");
831 goto on_error_efreet;
832 }
833
834 keys = eet_list(theme_ef, "*", &num);
835 if (keys)
836 {
837 for (i = 0; i < num; i++)
838 {
839 if (!strncmp(keys[i], "__efreet", 8)) continue;
840 theme = eet_data_read(theme_ef, theme_edd, keys[i]);
841 if (theme)
842 {
843 theme->valid = 0;
844 eina_hash_direct_add(icon_themes, theme->theme.name.internal, theme);
845 }
846 }
847 free(keys);
848 }
849
850 INF("scan for themes");
851 /* scan themes */
852 cache_theme_scan(efreet_icon_deprecated_user_dir_get());
853 cache_theme_scan(efreet_icon_user_dir_get());
854
855 xdg_dirs = efreet_data_dirs_get();
856 EINA_LIST_FOREACH(xdg_dirs, l, dir)
857 {
858 snprintf(file, sizeof(file), "%s/icons", dir);
859 cache_theme_scan(file);
860 }
861
862#ifndef STRICT_SPEC
863 EINA_LIST_FOREACH(xdg_dirs, l, dir)
864 {
865 snprintf(file, sizeof(file), "%s/pixmaps", dir);
866 cache_theme_scan(file);
867 }
868#endif
869
870 cache_theme_scan("/usr/share/pixmaps");
871
872 /* scan icons */
873 it = eina_hash_iterator_data_new(icon_themes);
874 EINA_ITERATOR_FOREACH(it, theme)
875 {
876 if (!theme->valid) continue;
877#ifndef STRICT_SPEC
878 if (!theme->theme.name.name) continue;
879#endif
880 INF("scan theme %s", theme->theme.name.name);
881
882 theme->changed = check_changed(theme);
883 if (flush)
884 theme->changed = EINA_TRUE;
885
886 INF("open icon file");
887 /* open icon file */
888 icon_ef = eet_open(efreet_icon_cache_file(theme->theme.name.internal), EET_FILE_MODE_READ_WRITE);
889 if (!icon_ef) goto on_error_efreet;
890 icon_version = eet_data_read(icon_ef, efreet_version_edd(), EFREET_CACHE_VERSION);
891 if (theme->changed || (icon_version &&
892 ((icon_version->major != EFREET_ICON_CACHE_MAJOR) ||
893 (icon_version->minor != EFREET_ICON_CACHE_MINOR))))
894 {
895 // delete old cache
896 eet_close(icon_ef);
897 if (unlink(efreet_icon_cache_file(theme->theme.name.internal)) < 0)
898 {
899 if (errno != ENOENT) goto on_error_efreet;
900 }
901 icon_ef = eet_open(efreet_icon_cache_file(theme->theme.name.internal), EET_FILE_MODE_READ_WRITE);
902 if (!icon_ef) goto on_error_efreet;
903 theme->changed = EINA_TRUE;
904 }
905
906 if (theme->changed)
907 changed = EINA_TRUE;
908
909 if (!icon_version)
910 icon_version = NEW(Efreet_Cache_Version, 1);
911
912 icon_version->major = EFREET_ICON_CACHE_MAJOR;
913 icon_version->minor = EFREET_ICON_CACHE_MINOR;
914
915 if (theme->changed)
916 {
917 Eina_Hash *themes;
918 Eina_Hash *icons;
919
920 themes = eina_hash_string_superfast_new(NULL);
921 icons = eina_hash_string_superfast_new(NULL);
922
923 INF("scan icons\n");
924 if (cache_scan(&(theme->theme), themes, icons))
925 {
926 Eina_Iterator *icons_it;
927 Eina_Hash_Tuple *tuple;
928
929 INF("generated: '%s' %i (%i)",
930 theme->theme.name.internal,
931 theme->changed,
932 eina_hash_population(icons));
933
934 icons_it = eina_hash_iterator_tuple_new(icons);
935 EINA_ITERATOR_FOREACH(icons_it, tuple)
936 eet_data_write(icon_ef, icon_edd, tuple->key, tuple->data, 1);
937 eina_iterator_free(icons_it);
938
939 INF("theme change: %s %lld", theme->theme.name.internal, theme->last_cache_check);
940 eet_data_write(theme_ef, theme_edd, theme->theme.name.internal, theme, 1);
941 }
942 eina_hash_free(themes);
943 eina_hash_free(icons);
944 changed = EINA_TRUE;
945 }
946
947 eet_data_write(icon_ef, efreet_version_edd(), EFREET_CACHE_VERSION, icon_version, 1);
948 eet_close(icon_ef);
949 efreet_setowner(efreet_icon_cache_file(theme->theme.name.internal));
950 free(icon_version);
951 }
952 eina_iterator_free(it);
953
954 INF("scan fallback icons");
955 theme = eet_data_read(theme_ef, theme_edd, EFREET_CACHE_ICON_FALLBACK);
956 if (!theme)
957 {
958 theme = NEW(Efreet_Cache_Icon_Theme, 1);
959 theme->changed = EINA_TRUE;
960 }
961 if (flush)
962 theme->changed = EINA_TRUE;
963
964 INF("open fallback file");
965 /* open icon file */
966 icon_ef = eet_open(efreet_icon_cache_file(EFREET_CACHE_ICON_FALLBACK), EET_FILE_MODE_READ_WRITE);
967 if (!icon_ef) goto on_error_efreet;
968 icon_version = eet_data_read(icon_ef, efreet_version_edd(), EFREET_CACHE_VERSION);
969 if (theme->changed || (icon_version &&
970 ((icon_version->major != EFREET_ICON_CACHE_MAJOR) ||
971 (icon_version->minor != EFREET_ICON_CACHE_MINOR))))
972 {
973 // delete old cache
974 eet_close(icon_ef);
975 if (unlink(efreet_icon_cache_file(EFREET_CACHE_ICON_FALLBACK)) < 0)
976 {
977 if (errno != ENOENT) goto on_error_efreet;
978 }
979 icon_ef = eet_open(efreet_icon_cache_file(EFREET_CACHE_ICON_FALLBACK), EET_FILE_MODE_READ_WRITE);
980 if (!icon_ef) goto on_error_efreet;
981 theme->changed = EINA_TRUE;
982 }
983 if (!theme->changed)
984 theme->changed = check_fallback_changed(theme);
985 if (theme->changed && theme->dirs)
986 {
987 efreet_hash_free(theme->dirs, free);
988 theme->dirs = NULL;
989 }
990 if (!theme->dirs)
991 theme->dirs = eina_hash_string_superfast_new(NULL);
992
993 if (theme->changed)
994 changed = EINA_TRUE;
995
996 if (!icon_version)
997 icon_version = NEW(Efreet_Cache_Version, 1);
998
999 icon_version->major = EFREET_ICON_CACHE_MAJOR;
1000 icon_version->minor = EFREET_ICON_CACHE_MINOR;
1001
1002 if (theme->changed)
1003 {
1004 Eina_Hash *icons;
1005
1006 icons = eina_hash_string_superfast_new(NULL);
1007
1008 INF("scan fallback icons");
1009 /* Save fallback in the right part */
1010 if (cache_fallback_scan(icons, theme->dirs))
1011 {
1012 Eina_Iterator *icons_it;
1013 Eina_Hash_Tuple *tuple;
1014
1015 INF("generated: fallback %i (%i)", theme->changed, eina_hash_population(icons));
1016
1017 icons_it = eina_hash_iterator_tuple_new(icons);
1018 EINA_ITERATOR_FOREACH(icons_it, tuple)
1019 eet_data_write(icon_ef, fallback_edd, tuple->key, tuple->data, 1);
1020 eina_iterator_free(icons_it);
1021 }
1022 eina_hash_free(icons);
1023
1024 eet_data_write(theme_ef, theme_edd, EFREET_CACHE_ICON_FALLBACK, theme, 1);
1025 }
1026
1027 icon_theme_free(theme);
1028
1029 eet_data_write(icon_ef, efreet_version_edd(), EFREET_CACHE_VERSION, icon_version, 1);
1030 eet_close(icon_ef);
1031 efreet_setowner(efreet_icon_cache_file(EFREET_CACHE_ICON_FALLBACK));
1032 free(icon_version);
1033
1034 eina_hash_free(icon_themes);
1035
1036 /* save data */
1037 eet_data_write(theme_ef, efreet_version_edd(), EFREET_CACHE_VERSION, theme_version, 1);
1038
1039 eet_close(theme_ef);
1040 efreet_setowner(efreet_icon_theme_cache_file());
1041 free(theme_version);
1042
1043 {
1044 char c = 'n';
1045
1046 if (changed) c = 'c';
1047 printf("%c\n", c);
1048 }
1049
1050 INF("done");
1051on_error_efreet:
1052 efreet_shutdown();
1053 if (theme_ef) eet_close(theme_ef);
1054
1055on_error:
1056 if (lockfd >= 0) close(lockfd);
1057
1058 while ((path = eina_array_pop(strs)))
1059 eina_stringshare_del(path);
1060 eina_array_free(strs);
1061 eina_array_free(exts);
1062 eina_array_free(extra_dirs);
1063
1064 ecore_shutdown();
1065 eet_shutdown();
1066 eina_log_domain_unregister(_efreet_icon_cache_log_dom);
1067 eina_shutdown();
1068
1069 return 0;
1070}
diff --git a/src/bin/efreet/efreetd.c b/src/bin/efreet/efreetd.c
new file mode 100644
index 0000000000..a4ae720a4e
--- /dev/null
+++ b/src/bin/efreet/efreetd.c
@@ -0,0 +1,59 @@
1#ifdef HAVE_CONFIG_H
2# include <config.h>
3#endif
4
5#include <Ecore.h>
6#include <Ecore_File.h>
7
8#include "efreetd.h"
9#include "efreetd_dbus.h"
10#include "efreetd_cache.h"
11
12int efreetd_log_dom = -1;
13
14void
15quit(void)
16{
17 ecore_main_loop_quit();
18}
19
20int
21main(void)
22{
23 if (!eina_init()) return 1;
24 efreetd_log_dom = eina_log_domain_register("efreetd", EFREETD_DEFAULT_LOG_COLOR);
25 if (efreetd_log_dom < 0)
26 {
27 EINA_LOG_ERR("Efreet: Could not create a log domain for efreetd.");
28 goto ecore_error;
29 }
30 if (!ecore_init()) goto ecore_error;
31 if (!ecore_file_init()) goto ecore_file_error;
32
33 if (!dbus_init()) goto dbus_error;
34 if (!cache_init()) goto cache_error;
35
36 ecore_main_loop_begin();
37
38 cache_shutdown();
39 dbus_shutdown();
40 ecore_file_shutdown();
41 ecore_shutdown();
42 eina_log_domain_unregister(efreetd_log_dom);
43 efreetd_log_dom = -1;
44 eina_shutdown();
45 return 0;
46
47cache_error:
48 dbus_shutdown();
49dbus_error:
50 ecore_file_shutdown();
51ecore_file_error:
52 ecore_shutdown();
53ecore_error:
54 if (efreetd_log_dom >= 0)
55 eina_log_domain_unregister(efreetd_log_dom);
56 efreetd_log_dom = -1;
57 eina_shutdown();
58 return 1;
59}
diff --git a/src/bin/efreet/efreetd.h b/src/bin/efreet/efreetd.h
new file mode 100644
index 0000000000..a6931e3282
--- /dev/null
+++ b/src/bin/efreet/efreetd.h
@@ -0,0 +1,34 @@
1#ifndef __EFREETD_H
2#define __EFREETD_H
3
4#ifdef EFREETD_DEFAULT_LOG_COLOR
5#undef EFREETD_DEFAULT_LOG_COLOR
6#endif
7#define EFREETD_DEFAULT_LOG_COLOR "\033[36m"
8
9extern int efreetd_log_dom;
10
11#ifdef CRITICAL
12#undef CRITICAL
13#endif
14#define CRITICAL(...) EINA_LOG_DOM_CRIT(efreetd_log_dom, __VA_ARGS__)
15#ifdef ERR
16#undef ERR
17#endif
18#define ERR(...) EINA_LOG_DOM_ERR(efreetd_log_dom, __VA_ARGS__)
19#ifdef DBG
20#undef DBG
21#endif
22#define DBG(...) EINA_LOG_DOM_DBG(efreetd_log_dom, __VA_ARGS__)
23#ifdef INF
24#undef INF
25#endif
26#define INF(...) EINA_LOG_DOM_INFO(efreetd_log_dom, __VA_ARGS__)
27#ifdef WRN
28#undef WRN
29#endif
30#define WRN(...) EINA_LOG_DOM_WARN(efreetd_log_dom, __VA_ARGS__)
31
32void quit(void);
33
34#endif
diff --git a/src/bin/efreet/efreetd_cache.c b/src/bin/efreet/efreetd_cache.c
new file mode 100644
index 0000000000..5af928bde0
--- /dev/null
+++ b/src/bin/efreet/efreetd_cache.c
@@ -0,0 +1,575 @@
1#ifdef HAVE_CONFIG_H
2# include <config.h>
3#endif
4
5#include <Eina.h>
6#include <Ecore.h>
7#include <Ecore_File.h>
8
9#include "efreetd.h"
10#include "efreetd_dbus.h"
11
12#include "Efreet.h"
13#define EFREET_MODULE_LOG_DOM efreetd_log_dom
14#include "efreet_private.h"
15#include "efreetd_cache.h"
16
17static Eina_Hash *change_monitors = NULL;
18
19static Ecore_Event_Handler *cache_exe_del_handler = NULL;
20static Ecore_Event_Handler *cache_exe_data_handler = NULL;
21static Ecore_Exe *icon_cache_exe = NULL;
22static Ecore_Exe *desktop_cache_exe = NULL;
23static Ecore_Timer *icon_cache_timer = NULL;
24static Ecore_Timer *desktop_cache_timer = NULL;
25
26static Eina_Bool desktop_exists = EINA_FALSE;
27
28static Eina_List *desktop_system_dirs = NULL;
29static Eina_List *desktop_extra_dirs = NULL;
30static Eina_List *icon_extra_dirs = NULL;
31static Eina_List *icon_exts = NULL;
32static Eina_Bool icon_flush = EINA_FALSE;
33
34static Eina_Bool desktop_queue = EINA_FALSE;
35static Eina_Bool icon_queue = EINA_FALSE;
36
37static void desktop_changes_monitor_add(const char *path);
38
39/* internal */
40static Eina_Bool
41icon_cache_update_cache_cb(void *data EINA_UNUSED)
42{
43 char file[PATH_MAX];
44 int prio;
45
46 icon_cache_timer = NULL;
47
48 if (icon_cache_exe)
49 {
50 icon_queue = EINA_TRUE;
51 return ECORE_CALLBACK_CANCEL;
52 }
53 icon_queue = EINA_FALSE;
54 if ((!icon_flush) && (!icon_exts)) return ECORE_CALLBACK_CANCEL;
55
56 /* TODO: Queue if already running */
57 prio = ecore_exe_run_priority_get();
58 ecore_exe_run_priority_set(19);
59 // XXX: use eina_prefix, not hard-coded prefixes
60 eina_strlcpy(file, PACKAGE_LIB_DIR "/efreet/efreet_icon_cache_create", sizeof(file));
61 if (icon_extra_dirs)
62 {
63 Eina_List *ll;
64 char *p;
65
66 eina_strlcat(file, " -d", sizeof(file));
67 EINA_LIST_FOREACH(icon_extra_dirs, ll, p)
68 {
69 eina_strlcat(file, " ", sizeof(file));
70 eina_strlcat(file, p, sizeof(file));
71 }
72 }
73 if (icon_exts)
74 {
75 Eina_List *ll;
76 char *p;
77
78 eina_strlcat(file, " -e", sizeof(file));
79 EINA_LIST_FOREACH(icon_exts, ll, p)
80 {
81 eina_strlcat(file, " ", sizeof(file));
82 eina_strlcat(file, p, sizeof(file));
83 }
84 }
85 if (icon_flush)
86 eina_strlcat(file, " -f", sizeof(file));
87 icon_flush = EINA_FALSE;
88 icon_cache_exe =
89 ecore_exe_pipe_run(file, ECORE_EXE_PIPE_READ|ECORE_EXE_PIPE_READ_LINE_BUFFERED, NULL);
90 ecore_exe_run_priority_set(prio);
91
92 return ECORE_CALLBACK_CANCEL;
93}
94
95static void
96cache_icon_update(Eina_Bool flush)
97{
98 if (icon_cache_timer)
99 ecore_timer_del(icon_cache_timer);
100 if (flush)
101 icon_flush = flush;
102 icon_cache_timer = ecore_timer_add(1.0, icon_cache_update_cache_cb, NULL);
103}
104
105static Eina_Bool
106desktop_cache_update_cache_cb(void *data EINA_UNUSED)
107{
108 char file[PATH_MAX];
109 int prio;
110
111 desktop_cache_timer = NULL;
112
113 if (desktop_cache_exe)
114 {
115 desktop_queue = EINA_TRUE;
116 return ECORE_CALLBACK_CANCEL;
117 }
118 desktop_queue = EINA_FALSE;
119 prio = ecore_exe_run_priority_get();
120 ecore_exe_run_priority_set(19);
121 // XXX: use eina_prefix, not hard-coded prefixes
122 eina_strlcpy(file, PACKAGE_LIB_DIR "/efreet/efreet_desktop_cache_create", sizeof(file));
123 if (desktop_extra_dirs)
124 {
125 Eina_List *ll;
126 const char *str;
127
128 eina_strlcat(file, " -d", sizeof(file));
129 EINA_LIST_FOREACH(desktop_extra_dirs, ll, str)
130 {
131 eina_strlcat(file, " ", sizeof(file));
132 eina_strlcat(file, str, sizeof(file));
133 }
134 }
135 INF("Run desktop cache creation: %s", file);
136 desktop_cache_exe =
137 ecore_exe_pipe_run(file, ECORE_EXE_PIPE_READ|ECORE_EXE_PIPE_READ_LINE_BUFFERED, NULL);
138 ecore_exe_run_priority_set(prio);
139
140 return ECORE_CALLBACK_CANCEL;
141}
142
143static Eina_Bool
144cache_exe_data_cb(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
145{
146 Ecore_Exe_Event_Data *ev;
147
148 ev = event;
149 if (ev->exe == desktop_cache_exe)
150 {
151 Eina_Bool update = EINA_FALSE;
152
153 if ((ev->lines) && (*ev->lines->line == 'c'))
154 update = EINA_TRUE;
155
156 desktop_exists = EINA_TRUE;
157 send_signal_desktop_cache_update(update);
158 }
159 else if (ev->exe == icon_cache_exe)
160 {
161 Eina_Bool update = EINA_FALSE;
162
163 if ((ev->lines) && (*ev->lines->line == 'c'))
164 update = EINA_TRUE;
165
166 send_signal_icon_cache_update(update);
167 }
168 return ECORE_CALLBACK_RENEW;
169}
170
171static Eina_Bool
172cache_exe_del_cb(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
173{
174 Ecore_Exe_Event_Del *ev;
175
176 ev = event;
177 if (ev->exe == desktop_cache_exe)
178 {
179 desktop_cache_exe = NULL;
180 if (desktop_queue) cache_desktop_update();
181 }
182 else if (ev->exe == icon_cache_exe)
183 {
184 icon_cache_exe = NULL;
185 if (icon_queue) cache_icon_update(EINA_FALSE);
186 }
187 return ECORE_CALLBACK_RENEW;
188}
189
190static void
191icon_changes_cb(void *data EINA_UNUSED, Ecore_File_Monitor *em EINA_UNUSED,
192 Ecore_File_Event event, const char *path)
193{
194 /* TODO: If we get a stale symlink, we need to rerun cache creation */
195 switch (event)
196 {
197 case ECORE_FILE_EVENT_NONE:
198 /* noop */
199 break;
200
201 case ECORE_FILE_EVENT_CREATED_FILE:
202 case ECORE_FILE_EVENT_DELETED_FILE:
203 case ECORE_FILE_EVENT_MODIFIED:
204 case ECORE_FILE_EVENT_CLOSED:
205 case ECORE_FILE_EVENT_DELETED_DIRECTORY:
206 case ECORE_FILE_EVENT_CREATED_DIRECTORY:
207 cache_icon_update(EINA_FALSE);
208 break;
209
210 case ECORE_FILE_EVENT_DELETED_SELF:
211 eina_hash_del_by_key(change_monitors, path);
212 cache_icon_update(EINA_FALSE);
213 break;
214 }
215}
216
217static void
218icon_changes_monitor_add(const char *path)
219{
220 Ecore_File_Monitor *mon;
221
222 if (eina_hash_find(change_monitors, path)) return;
223 /* TODO: Check for symlink and monitor the real path */
224 mon = ecore_file_monitor_add(path,
225 icon_changes_cb,
226 NULL);
227 if (mon)
228 eina_hash_add(change_monitors, path, mon);
229}
230
231static void
232icon_changes_listen_recursive(const char *path, Eina_Bool base)
233{
234 Eina_Iterator *it;
235 Eina_File_Direct_Info *info;
236
237 if ((!ecore_file_is_dir(path)) && (base))
238 {
239 // XXX: if it doesn't exist... walk the parent dirs back down
240 // to this path until we find one that doesn't exist, then
241 // monitor its parent, and treat it specially as it needs
242 // to look for JUST the creation of this specific child
243 // and when this child is created, replace this monitor with
244 // monitoring the next specific child dir down until we are
245 // monitoring the original path again.
246 }
247 icon_changes_monitor_add(path);
248 it = eina_file_stat_ls(path);
249 if (!it) return;
250 EINA_ITERATOR_FOREACH(it, info)
251 {
252 if (((info->type == EINA_FILE_LNK) && (ecore_file_is_dir(info->path))) ||
253 (info->type == EINA_FILE_DIR))
254 icon_changes_monitor_add(info->path);
255 }
256 eina_iterator_free(it);
257}
258
259static void
260icon_changes_listen(void)
261{
262 Eina_List *l;
263 Eina_List *xdg_dirs;
264 char buf[PATH_MAX];
265 const char *dir;
266
267 icon_changes_listen_recursive(efreet_icon_deprecated_user_dir_get(), EINA_TRUE);
268 icon_changes_listen_recursive(efreet_icon_user_dir_get(), EINA_TRUE);
269 EINA_LIST_FOREACH(icon_extra_dirs, l, dir)
270 {
271 icon_changes_listen_recursive(dir, EINA_TRUE);
272 }
273
274 xdg_dirs = efreet_data_dirs_get();
275 EINA_LIST_FOREACH(xdg_dirs, l, dir)
276 {
277 snprintf(buf, sizeof(buf), "%s/icons", dir);
278 icon_changes_listen_recursive(buf, EINA_TRUE);
279 }
280
281#ifndef STRICT_SPEC
282 EINA_LIST_FOREACH(xdg_dirs, l, dir)
283 {
284 snprintf(buf, sizeof(buf), "%s/pixmaps", dir);
285 icon_changes_listen_recursive(buf, EINA_TRUE);
286 }
287#endif
288
289 icon_changes_monitor_add("/usr/share/pixmaps");
290}
291
292static void
293desktop_changes_cb(void *data EINA_UNUSED, Ecore_File_Monitor *em EINA_UNUSED,
294 Ecore_File_Event event, const char *path)
295{
296 const char *ext;
297
298 /* TODO: If we get a stale symlink, we need to rerun cache creation */
299 /* TODO: Check for desktop*.cache, as this will be created when app is installed */
300 /* TODO: Do efreet_cache_icon_update() when app is installed, as it has the same
301 * symlink problem */
302 switch (event)
303 {
304 case ECORE_FILE_EVENT_NONE:
305 /* noop */
306 break;
307
308 case ECORE_FILE_EVENT_CREATED_FILE:
309 case ECORE_FILE_EVENT_DELETED_FILE:
310 case ECORE_FILE_EVENT_MODIFIED:
311 case ECORE_FILE_EVENT_CLOSED:
312 ext = strrchr(path, '.');
313 if (ext && (!strcmp(ext, ".desktop") || !strcmp(ext, ".directory")))
314 cache_desktop_update();
315 break;
316
317 case ECORE_FILE_EVENT_DELETED_SELF:
318 case ECORE_FILE_EVENT_DELETED_DIRECTORY:
319 eina_hash_del_by_key(change_monitors, path);
320 cache_desktop_update();
321 break;
322
323 case ECORE_FILE_EVENT_CREATED_DIRECTORY:
324 desktop_changes_monitor_add(path);
325 cache_desktop_update();
326 break;
327 }
328}
329
330static void
331desktop_changes_monitor_add(const char *path)
332{
333 Ecore_File_Monitor *mon;
334
335 if (eina_hash_find(change_monitors, path)) return;
336 /* TODO: Check for symlink and monitor the real path */
337 mon = ecore_file_monitor_add(path,
338 desktop_changes_cb,
339 NULL);
340 if (mon)
341 eina_hash_add(change_monitors, path, mon);
342}
343
344static void
345desktop_changes_listen_recursive(const char *path)
346{
347 Eina_Iterator *it;
348 Eina_File_Direct_Info *info;
349
350 if (!ecore_file_is_dir(path)) return;
351 desktop_changes_monitor_add(path);
352 it = eina_file_stat_ls(path);
353 if (!it) return;
354 EINA_ITERATOR_FOREACH(it, info)
355 {
356 if (((info->type == EINA_FILE_LNK) && (ecore_file_is_dir(info->path))) ||
357 (info->type == EINA_FILE_DIR))
358 desktop_changes_listen_recursive(info->path);
359 }
360 eina_iterator_free(it);
361}
362
363static void
364desktop_changes_listen(void)
365{
366 Eina_List *l;
367 const char *path;
368
369 EINA_LIST_FOREACH(desktop_system_dirs, l, path)
370 desktop_changes_listen_recursive(path);
371
372 EINA_LIST_FOREACH(desktop_extra_dirs, l, path)
373 desktop_changes_listen_recursive(path);
374}
375
376static void
377fill_list(const char *file, Eina_List **l)
378{
379 Eina_File *f = NULL;
380 Eina_Iterator *it = NULL;
381 Eina_File_Line *line = NULL;
382 char buf[PATH_MAX];
383
384 snprintf(buf, sizeof(buf), "%s/efreet/%s", efreet_cache_home_get(), file);
385 f = eina_file_open(buf, EINA_FALSE);
386 if (!f) return;
387 it = eina_file_map_lines(f);
388 if (!it) goto error;
389 EINA_ITERATOR_FOREACH(it, line)
390 {
391 const char *end;
392 end = line->end - 1;
393 *l = eina_list_append(*l, eina_stringshare_add_length(line->start, end - line->start));
394 }
395 eina_iterator_free(it);
396error:
397 eina_file_close(f);
398}
399
400static void
401read_lists(void)
402{
403 fill_list("extra_desktop.dirs", &desktop_extra_dirs);
404 fill_list("extra_icon.dirs", &icon_extra_dirs);
405 fill_list("icon.exts", &icon_exts);
406}
407
408static void
409save_list(const char *file, Eina_List *l)
410{
411 FILE *f;
412 char buf[PATH_MAX];
413 Eina_List *ll;
414 const char *path;
415
416 snprintf(buf, sizeof(buf), "%s/efreet/%s", efreet_cache_home_get(), file);
417 f = fopen(buf, "wb");
418 if (!f) return;
419 EINA_LIST_FOREACH(l, ll, path)
420 fprintf(f, "%s\n", path);
421 fclose(f);
422}
423
424static int
425strcmplen(const void *data1, const void *data2)
426{
427 return strncmp(data1, data2, eina_stringshare_strlen(data1));
428}
429
430/* external */
431void
432cache_desktop_dir_add(const char *dir)
433{
434 char *san;
435 Eina_List *l;
436
437 san = eina_file_path_sanitize(dir);
438 if (!san) return;
439 if ((l = eina_list_search_unsorted_list(desktop_system_dirs, strcmplen, san)))
440 {
441 /* Path is registered, but maybe not monitored */
442 const char *path = eina_list_data_get(l);
443 if (!eina_hash_find(change_monitors, path))
444 cache_desktop_update();
445 }
446 else if (!eina_list_search_unsorted_list(desktop_extra_dirs, EINA_COMPARE_CB(strcmp), san))
447 {
448 /* Not a registered path */
449 desktop_extra_dirs = eina_list_append(desktop_extra_dirs, eina_stringshare_add(san));
450 save_list("extra_desktop.dirs", desktop_extra_dirs);
451 cache_desktop_update();
452 }
453 free(san);
454}
455
456void
457cache_icon_dir_add(const char *dir)
458{
459 char *san;
460
461 san = eina_file_path_sanitize(dir);
462 if (!san) return;
463 if (!eina_list_search_unsorted_list(icon_extra_dirs, EINA_COMPARE_CB(strcmp), san))
464 {
465 icon_extra_dirs = eina_list_append(icon_extra_dirs, eina_stringshare_add(san));
466 save_list("extra_icon.dirs", icon_extra_dirs);
467 cache_icon_update(EINA_TRUE);
468 }
469 free(san);
470}
471
472void
473cache_icon_ext_add(const char *ext)
474{
475 if (!eina_list_search_unsorted_list(icon_exts, EINA_COMPARE_CB(strcmp), ext))
476 {
477 icon_exts = eina_list_append(icon_exts, eina_stringshare_add(ext));
478 save_list("icon.exts", icon_exts);
479 cache_icon_update(EINA_TRUE);
480 }
481}
482
483void
484cache_desktop_update(void)
485{
486 if (desktop_cache_timer)
487 ecore_timer_del(desktop_cache_timer);
488 desktop_cache_timer = ecore_timer_add(1.0, desktop_cache_update_cache_cb, NULL);
489}
490
491Eina_Bool
492cache_desktop_exists(void)
493{
494 return desktop_exists;
495}
496
497Eina_Bool
498cache_init(void)
499{
500 char buf[PATH_MAX];
501
502 snprintf(buf, sizeof(buf), "%s/efreet", efreet_cache_home_get());
503 if (!ecore_file_mkpath(buf))
504 {
505 ERR("Failed to create directory '%s'", buf);
506 goto error;
507 }
508
509 cache_exe_del_handler = ecore_event_handler_add(ECORE_EXE_EVENT_DEL,
510 cache_exe_del_cb, NULL);
511 if (!cache_exe_del_handler)
512 {
513 ERR("Failed to add exe del handler");
514 goto error;
515 }
516 cache_exe_data_handler = ecore_event_handler_add(ECORE_EXE_EVENT_DATA,
517 cache_exe_data_cb, NULL);
518 if (!cache_exe_data_handler)
519 {
520 ERR("Failed to add exe data handler");
521 goto error;
522 }
523
524 change_monitors = eina_hash_string_superfast_new(EINA_FREE_CB(ecore_file_monitor_del));
525
526 efreet_cache_update = 0;
527 if (!efreet_init()) goto error;
528
529 read_lists();
530 /* TODO: Should check if system dirs has changed and handles extra_dirs */
531 desktop_system_dirs = efreet_default_dirs_get(efreet_data_home_get(),
532 efreet_data_dirs_get(), "applications");
533 desktop_system_dirs =
534 eina_list_merge(
535 desktop_system_dirs, efreet_default_dirs_get(efreet_data_home_get(),
536 efreet_data_dirs_get(), "desktop-directories"));
537 icon_changes_listen();
538 desktop_changes_listen();
539 cache_icon_update(EINA_FALSE);
540 cache_desktop_update();
541
542 return EINA_TRUE;
543error:
544 if (cache_exe_del_handler) ecore_event_handler_del(cache_exe_del_handler);
545 cache_exe_del_handler = NULL;
546 if (cache_exe_data_handler) ecore_event_handler_del(cache_exe_data_handler);
547 cache_exe_data_handler = NULL;
548 return EINA_FALSE;
549}
550
551Eina_Bool
552cache_shutdown(void)
553{
554 const char *data;
555
556 efreet_shutdown();
557
558 if (cache_exe_del_handler) ecore_event_handler_del(cache_exe_del_handler);
559 cache_exe_del_handler = NULL;
560 if (cache_exe_data_handler) ecore_event_handler_del(cache_exe_data_handler);
561 cache_exe_data_handler = NULL;
562
563 if (change_monitors)
564 eina_hash_free(change_monitors);
565 change_monitors = NULL;
566 EINA_LIST_FREE(desktop_system_dirs, data)
567 eina_stringshare_del(data);
568 EINA_LIST_FREE(desktop_extra_dirs, data)
569 eina_stringshare_del(data);
570 EINA_LIST_FREE(icon_extra_dirs, data)
571 eina_stringshare_del(data);
572 EINA_LIST_FREE(icon_exts, data)
573 eina_stringshare_del(data);
574 return EINA_TRUE;
575}
diff --git a/src/bin/efreet/efreetd_cache.h b/src/bin/efreet/efreetd_cache.h
new file mode 100644
index 0000000000..2fb520e384
--- /dev/null
+++ b/src/bin/efreet/efreetd_cache.h
@@ -0,0 +1,13 @@
1#ifndef __EFREETD_CACHE_H
2#define __EFREETD_CACHE_H
3
4void cache_desktop_dir_add(const char *dir);
5void cache_icon_dir_add(const char *dir);
6void cache_icon_ext_add(const char *ext);
7void cache_desktop_update(void);
8Eina_Bool cache_desktop_exists(void);
9
10Eina_Bool cache_init(void);
11Eina_Bool cache_shutdown(void);
12
13#endif
diff --git a/src/bin/efreet/efreetd_dbus.c b/src/bin/efreet/efreetd_dbus.c
new file mode 100644
index 0000000000..b5e98ad083
--- /dev/null
+++ b/src/bin/efreet/efreetd_dbus.c
@@ -0,0 +1,262 @@
1#ifdef HAVE_CONFIG_H
2# include <config.h>
3#endif
4
5#include <Ecore.h>
6#include <EDBus.h>
7
8#include "efreetd.h"
9#include "efreetd_cache.h"
10
11#define BUS "org.enlightenment.Efreet"
12#define PATH "/org/enlightenment/Efreet"
13#define INTERFACE "org.enlightenment.Efreet"
14
15/* internal */
16enum
17{
18 EFREET_SIGNAL_ICON_CACHE_UPDATE = 0,
19 EFREET_SIGNAL_DESKTOP_CACHE_UPDATE
20};
21
22static EDBus_Connection *conn;
23static EDBus_Service_Interface *iface;
24
25static Ecore_Timer *shutdown = NULL;
26static int clients = 0;
27
28static Eina_Bool
29do_shutdown(void *data EINA_UNUSED)
30{
31 quit();
32 return ECORE_CALLBACK_CANCEL;
33}
34
35static void
36disconnected(void *context EINA_UNUSED, EDBus_Connection *connection EINA_UNUSED, void *event_info EINA_UNUSED)
37{
38 INF("disconnected");
39 quit();
40}
41
42static void
43client_name_owner_changed_cb(void *data, const char *bus, const char *old_id, const char *new_id)
44{
45 if (new_id[0])
46 return;
47 edbus_name_owner_changed_callback_del(conn, bus,
48 client_name_owner_changed_cb, NULL);
49 clients--;
50 if (clients <= 0)
51 {
52 clients = 0;
53 if (shutdown) ecore_timer_del(shutdown);
54 shutdown = ecore_timer_add(10.0, do_shutdown, NULL);
55 }
56}
57
58static EDBus_Message *
59do_register(const EDBus_Service_Interface *ifc EINA_UNUSED, const EDBus_Message *message)
60{
61 EDBus_Message *reply;
62 const char *lang;
63
64 if (!edbus_message_arguments_get(message, "s", &lang))
65 {
66 ERR("Error getting arguments.");
67 return NULL;
68 }
69 setenv("LANG", lang, 1);
70
71 clients++;
72 if (shutdown) ecore_timer_del(shutdown);
73 shutdown = NULL;
74
75 edbus_name_owner_changed_callback_add(conn,
76 edbus_message_sender_get(message),
77 client_name_owner_changed_cb, NULL,
78 EINA_FALSE);
79 reply = edbus_message_method_return_new(message);
80 edbus_message_arguments_append(reply, "b", cache_desktop_exists());
81 return reply;
82}
83
84static EDBus_Message *
85add_desktop_dirs(const EDBus_Service_Interface *ifc EINA_UNUSED, const EDBus_Message *message)
86{
87 EDBus_Message_Iter *array = NULL;
88 const char *dir;
89
90 if (!edbus_message_arguments_get(message, "as", &array))
91 {
92 ERR("Error getting arguments.");
93 return NULL;
94 }
95
96 while (edbus_message_iter_get_and_next(array, 's', &dir))
97 {
98 cache_desktop_dir_add(dir);
99 }
100
101 return NULL;
102}
103
104static EDBus_Message *
105add_icon_dirs(const EDBus_Service_Interface *ifc EINA_UNUSED, const EDBus_Message *message)
106{
107 EDBus_Message_Iter *array = NULL;
108 const char *dir;
109
110 if (!edbus_message_arguments_get(message, "as", &array))
111 {
112 ERR("Error getting arguments.");
113 return NULL;
114 }
115
116 while (edbus_message_iter_get_and_next(array, 's', &dir))
117 {
118 cache_icon_dir_add(dir);
119 }
120
121 return NULL;
122}
123
124static EDBus_Message *
125build_desktop_cache(const EDBus_Service_Interface *ifc EINA_UNUSED, const EDBus_Message *message EINA_UNUSED)
126{
127 const char *lang;
128
129 if (!edbus_message_arguments_get(message, "s", &lang))
130 {
131 ERR("Error getting arguments.");
132 return NULL;
133 }
134 setenv("LANG", lang, 1);
135
136 cache_desktop_update();
137 return NULL;
138}
139
140static EDBus_Message *
141add_icon_exts(const EDBus_Service_Interface *ifc EINA_UNUSED, const EDBus_Message *message)
142{
143 EDBus_Message_Iter *array = NULL;
144 const char *ext;
145
146 if (!edbus_message_arguments_get(message, "as", &array))
147 {
148 ERR("Error getting arguments.");
149 return NULL;
150 }
151
152 while (edbus_message_iter_get_and_next(array, 's', &ext))
153 {
154 cache_icon_ext_add(ext);
155 }
156
157 return NULL;
158}
159
160static const EDBus_Signal signals[] = {
161 [EFREET_SIGNAL_ICON_CACHE_UPDATE] = {"IconCacheUpdate", EDBUS_ARGS({ "b", "update" }), 0},
162 [EFREET_SIGNAL_DESKTOP_CACHE_UPDATE] = {"DesktopCacheUpdate", EDBUS_ARGS({ "b", "update" }), 0},
163 { NULL, NULL, 0 }
164};
165
166static const EDBus_Method methods[] = {
167 {
168 "Register", EDBUS_ARGS({"s", "lang info"}), EDBUS_ARGS({"b", "cache exists"}),
169 do_register, 0
170 },
171 {
172 "AddDesktopDirs", EDBUS_ARGS({"as", "dirs"}), NULL,
173 add_desktop_dirs, EDBUS_METHOD_FLAG_NOREPLY
174 },
175 {
176 "BuildDesktopCache", EDBUS_ARGS({"s", "lang info"}), NULL,
177 build_desktop_cache, EDBUS_METHOD_FLAG_NOREPLY
178 },
179 {
180 "AddIconDirs", EDBUS_ARGS({"as", "dirs"}), NULL,
181 add_icon_dirs, EDBUS_METHOD_FLAG_NOREPLY
182 },
183 {
184 "AddIconExts", EDBUS_ARGS({"as", "exts"}), NULL,
185 add_icon_exts, EDBUS_METHOD_FLAG_NOREPLY
186 },
187 { NULL, NULL, NULL, NULL, 0 }
188};
189
190static const EDBus_Service_Interface_Desc desc = {
191 INTERFACE, methods, signals, NULL, NULL, NULL
192};
193
194static void
195on_name_request(void *data EINA_UNUSED, const EDBus_Message *msg, EDBus_Pending *pending EINA_UNUSED)
196{
197 unsigned int reply;
198
199 if (edbus_message_error_get(msg, NULL, NULL))
200 {
201 ERR("error on on_name_request");
202 quit();
203 return;
204 }
205
206 if (!edbus_message_arguments_get(msg, "u", &reply))
207 {
208 ERR("error getting arguments on on_name_request");
209 quit();
210 return;
211 }
212
213 if (reply != EDBUS_NAME_REQUEST_REPLY_PRIMARY_OWNER)
214 {
215 ERR("error name already in use");
216 quit();
217 return;
218 }
219 INF("name requested");
220}
221
222/* external */
223void
224send_signal_icon_cache_update(Eina_Bool update)
225{
226 edbus_service_signal_emit(iface, EFREET_SIGNAL_ICON_CACHE_UPDATE, update);
227}
228
229void
230send_signal_desktop_cache_update(Eina_Bool update)
231{
232 edbus_service_signal_emit(iface, EFREET_SIGNAL_DESKTOP_CACHE_UPDATE, update);
233}
234
235Eina_Bool
236dbus_init(void)
237{
238 if (!edbus_init()) return EINA_FALSE;
239
240 conn = edbus_connection_get(EDBUS_CONNECTION_TYPE_SESSION);
241 if (!conn) goto conn_error;
242
243 edbus_connection_event_callback_add(conn,
244 EDBUS_CONNECTION_EVENT_DISCONNECTED, disconnected, NULL);
245 iface = edbus_service_interface_register(conn, PATH, &desc);
246 edbus_name_request(conn, BUS, EDBUS_NAME_REQUEST_FLAG_DO_NOT_QUEUE,
247 on_name_request, NULL);
248
249 return EINA_TRUE;
250conn_error:
251 edbus_shutdown();
252 return EINA_FALSE;
253}
254
255Eina_Bool
256dbus_shutdown(void)
257{
258 edbus_connection_unref(conn);
259 edbus_shutdown();
260 return EINA_TRUE;
261
262}
diff --git a/src/bin/efreet/efreetd_dbus.h b/src/bin/efreet/efreetd_dbus.h
new file mode 100644
index 0000000000..9e9fd70700
--- /dev/null
+++ b/src/bin/efreet/efreetd_dbus.h
@@ -0,0 +1,10 @@
1#ifndef __EFREETD_DBUS_H
2#define __EFREETD_DBUS_H
3
4void send_signal_icon_cache_update(Eina_Bool update);
5void send_signal_desktop_cache_update(Eina_Bool update);
6
7Eina_Bool dbus_init(void);
8Eina_Bool dbus_shutdown(void);
9
10#endif