/* * vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2 */ #include "e.h" /* TODO List: * * - If a .order file references a non-existing file, and the file * is added in 'all', it doesn't show! * - track app execution state, visibility state etc. and call callbacks * - calls to execute an app or query its runing/starting state etc. * - clean up the add app functions. To much similar code. */ #define DEBUG 0 #define IDLE_ICONS 1 /* These two can be used to turn on or off the new and old border matching code. * There is a printf at the end of e_app_border_find that gives the times and results. * You can have both turned on at once for a better comparison. * Just never turn them both off. */ #define CLEVER_BORDERS 1 #define OLD_BORDERS 0 /* local subsystem functions */ typedef struct _E_App_Change_Info E_App_Change_Info; typedef struct _E_App_Callback E_App_Callback; struct _E_App_Change_Info { E_App *app; E_App_Change change; }; struct _E_App_Callback { void (*func) (void *data, E_App *a, E_App_Change ch); void *data; unsigned char delete_me : 1; }; struct _E_App_Hash_Idler { Ecore_Idler *idler; /* We keep a seperate list so that nothing external can change it between * idler calls. Means we might miss some during the idler run, but since * this is just an optimization pre load thing, then that's no problem. */ Evas_List *apps; double begin, pass; }; static int _e_apps_hash_idler_cb (void *data); static void _e_app_free (E_App *a); static E_App *_e_app_subapp_file_find (E_App *a, const char *file); static void _e_app_change (E_App *a, E_App_Change ch); static int _e_apps_cb_exit (void *data, int type, void *event); static void _e_app_cb_monitor (void *data, Ecore_File_Monitor *em, Ecore_File_Event event, const char *path); static void _e_app_subdir_rescan (E_App *app); static int _e_app_is_eapp (const char *path); static int _e_app_copy (E_App *dst, E_App *src); static E_App_Icon_Type _e_app_icon_type (E_App *a); static void _e_app_fields_save_others(E_App *a); static void _e_app_save_order (E_App *app); static int _e_app_cb_event_border_add(void *data, int type, void *event); static int _e_app_cb_expire_timer (void *data); #if DEBUG static void _e_app_print (const char *path, Ecore_File_Event event); #endif static void _e_app_check_order (const char *file); static int _e_app_order_contains (E_App *a, const char *file); static void _e_app_resolve_file_name (char *buf, size_t size, const char *path, const char *file); /* local subsystem globals */ static int _e_apps_callbacks_walking = 0; static int _e_apps_callbacks_delete_me = 0; static Evas_List *_e_apps_change_callbacks = NULL; static Ecore_Event_Handler *_e_apps_exit_handler = NULL; static Ecore_Event_Handler *_e_apps_border_add_handler = NULL; static E_App *_e_apps_all = NULL; static const char *_e_apps_path_all = NULL; static const char *_e_apps_path_trash = NULL; static Evas_List *_e_apps_start_pending = NULL; static Evas_Hash *_e_apps_every_app = NULL; static struct _E_App_Hash_Idler _e_apps_hash_idler; #if CLEVER_BORDERS struct _E_App_Glob_List_Entry { const char *key; const char *path; }; struct _E_App_Glob_List_Winner { const char *path; int ok; }; static Evas_Hash *_e_apps_border_ng_win_class = NULL, *_e_apps_border_ng_win_title = NULL, *_e_apps_border_ng_win_name = NULL, *_e_apps_border_ng_win_role = NULL, *_e_apps_border_ng_exe = NULL; static Evas_List *_e_apps_border_g_win_class = NULL, *_e_apps_border_g_win_title = NULL, *_e_apps_border_g_win_name = NULL, *_e_apps_border_g_win_role = NULL; static double border_setup_time = 0.0; static int border_setup_count = 0, glob_count = 0; #endif static double border_time = 0.0, border_clever_time = 0.0; static int border_count = 0; /* externally accessible functions */ EAPI int e_app_init(void) { Ecore_List *_e_apps_all_filenames = NULL; const char *home; char buf[PATH_MAX]; double begin; home = e_user_homedir_get(); snprintf(buf, sizeof(buf), "%s/.e/e/applications/trash", home); _e_apps_path_trash = evas_stringshare_add(buf); snprintf(buf, sizeof(buf), "%s/.e/e/applications/all", home); _e_apps_path_all = evas_stringshare_add(buf); _e_apps_exit_handler = ecore_event_handler_add(ECORE_EXE_EVENT_DEL, _e_apps_cb_exit, NULL); _e_apps_border_add_handler = ecore_event_handler_add(E_EVENT_BORDER_ADD, _e_app_cb_event_border_add, NULL); #if CLEVER_BORDERS #endif /* Prefill with empty E_Apps from the all directory. */ ecore_desktop_instrumentation_reset(); begin = ecore_time_get(); _e_apps_all = e_app_new(_e_apps_path_all, 0); _e_apps_all_filenames = ecore_file_ls(_e_apps_path_all); if (_e_apps_all_filenames) { const char *file; while ((file = ecore_list_next(_e_apps_all_filenames))) { if (_e_app_is_eapp(file)) { E_App *app; snprintf(buf, sizeof(buf), "%s/%s", _e_apps_path_all, file); app = e_app_empty_new(buf); if ((app) && (app->path)) { _e_apps_every_app = evas_hash_direct_add(_e_apps_every_app, app->path, app); app->parent = _e_apps_all; _e_apps_all->subapps = evas_list_prepend(_e_apps_all->subapps, app); _e_apps_hash_idler.apps = evas_list_append(_e_apps_hash_idler.apps, app); e_object_ref(E_OBJECT(app)); } } } ecore_list_destroy(_e_apps_all_filenames); } #if DEBUG printf("INITIAL APP SCAN %3.3f\n", ecore_time_get() - begin); #endif ecore_desktop_instrumentation_print(); ecore_desktop_instrumentation_reset(); _e_apps_hash_idler.begin = ecore_time_get(); _e_apps_hash_idler.idler = ecore_idler_add(_e_apps_hash_idler_cb, &_e_apps_hash_idler); return 1; } static int _e_apps_hash_idler_cb(void *data) { struct _E_App_Hash_Idler *idler; idler = data; idler->pass = ecore_time_get(); while (idler->apps != NULL) { E_App *a; double t; a = idler->apps->data; E_OBJECT_CHECK(a); E_OBJECT_TYPE_CHECK(a, E_APP_TYPE); if ((!a->idle_fill) && (!a->filled)) { struct stat st; a->idle_fill = 1; if (stat(a->path, &st) >= 0) a->mtime = st.st_mtime; e_app_fields_fill(a, a->path); } #if IDLE_ICONS if ((!a->idle_icon) && (a->icon_type == E_APP_ICON_UNKNOWN)) { a->idle_icon = 1; _e_app_icon_type(a); } #endif /* FIXME: This time should be since the beginnig of all idler processing, * and the time limit should be tied to the frame rate. */ idler->apps = evas_list_remove_list(idler->apps, idler->apps); t = ecore_time_get() - idler->pass; if (t > 0.02) break; } if (idler->apps == NULL) { #if DEBUG printf("\nIDLE APP FILLING SCAN %3.3f\n", ecore_time_get() - idler->begin); #if CLEVER_BORDERS printf("APP BORDER SETUP %3.6f (average %2.6f) %d globs\n", border_setup_time, border_setup_time / border_setup_count, glob_count); #endif #endif idler->idler = NULL; ecore_desktop_instrumentation_print(); return 0; } return 1; } #if CLEVER_BORDERS static Evas_Bool _e_apps_border_hash_cb_free(Evas_Hash *hash, const char *key, void *data, void *fdata) { Evas_List *list; list = data; evas_list_free(list); return 0; } #endif EAPI int e_app_shutdown(void) { Evas_List *list; if (_e_apps_hash_idler.idler) { ecore_idler_del(_e_apps_hash_idler.idler); _e_apps_hash_idler.idler = NULL; } _e_apps_start_pending = evas_list_free(_e_apps_start_pending); if (_e_apps_all) { list = _e_apps_all->subapps; e_object_unref(E_OBJECT(_e_apps_all)); _e_apps_all = NULL; } if (_e_apps_exit_handler) { ecore_event_handler_del(_e_apps_exit_handler); _e_apps_exit_handler = NULL; } if (_e_apps_border_add_handler) { ecore_event_handler_del(_e_apps_border_add_handler); _e_apps_border_add_handler = NULL; } evas_stringshare_del(_e_apps_path_trash); evas_stringshare_del(_e_apps_path_all); { Evas_List *l; for (l = list; l; l = l->next) { E_App *a; a = l->data; printf("BUG: References %d %s\n", E_OBJECT(a)->references, a->path); } } evas_hash_free(_e_apps_every_app); _e_apps_every_app = NULL; #if CLEVER_BORDERS for (list = _e_apps_border_g_win_name; list; list = list->next) { struct _E_App_Glob_List_Entry *entry; entry = list->data; if (entry) free(entry); list->data = NULL; } for (list = _e_apps_border_g_win_class; list; list = list->next) { struct _E_App_Glob_List_Entry *entry; entry = list->data; if (entry) free(entry); list->data = NULL; } for (list = _e_apps_border_g_win_title; list; list = list->next) { struct _E_App_Glob_List_Entry *entry; entry = list->data; if (entry) free(entry); list->data = NULL; } for (list = _e_apps_border_g_win_role; list; list = list->next) { struct _E_App_Glob_List_Entry *entry; entry = list->data; if (entry) free(entry); list->data = NULL; } evas_hash_foreach(_e_apps_border_ng_win_name, _e_apps_border_hash_cb_free, NULL); evas_hash_foreach(_e_apps_border_ng_win_class, _e_apps_border_hash_cb_free, NULL); evas_hash_foreach(_e_apps_border_ng_win_title, _e_apps_border_hash_cb_free, NULL); evas_hash_foreach(_e_apps_border_ng_win_role, _e_apps_border_hash_cb_free, NULL); evas_hash_free(_e_apps_border_ng_win_name); _e_apps_border_ng_win_name = NULL; evas_hash_free(_e_apps_border_ng_win_class); _e_apps_border_ng_win_class = NULL; evas_hash_free(_e_apps_border_ng_win_title); _e_apps_border_ng_win_title = NULL; evas_hash_free(_e_apps_border_ng_win_role); _e_apps_border_ng_win_role = NULL; evas_hash_free(_e_apps_border_ng_exe); _e_apps_border_ng_exe = NULL; evas_list_free(_e_apps_border_g_win_name); _e_apps_border_g_win_name = NULL; evas_list_free(_e_apps_border_g_win_class); _e_apps_border_g_win_class = NULL; evas_list_free(_e_apps_border_g_win_title); _e_apps_border_g_win_title = NULL; evas_list_free(_e_apps_border_g_win_role); _e_apps_border_g_win_role = NULL; #endif return 1; } EAPI void e_app_unmonitor_all(void) { Evas_List *l; for (l = _e_apps_all->subapps; l; l = l->next) { E_App *a; a = l->data; if (a->monitor) { ecore_file_monitor_del(a->monitor); a->monitor = NULL; } } } /* FIXME: Not actualy used anywhere, should we nuke it or should we use it everywhere that an E_App is allocated? */ EAPI E_App * e_app_raw_new(void) { E_App *a; a = E_OBJECT_ALLOC(E_App, E_APP_TYPE, _e_app_free); return a; } EAPI E_App * e_app_new(const char *path, int scan_subdirs) { E_App *a; E_App *a2; struct stat st; int stated = 0; int new_app = 0; int virtual_app = 0; int in_all = 0; char buf[PATH_MAX]; if (!path) return NULL; a = e_app_path_find(path); /* Is it a virtual path from inside a .order file? */ if ((!a) && (!ecore_file_exists(path))) { _e_app_resolve_file_name(buf, sizeof(buf), _e_apps_path_all, ecore_file_get_file(path)); if (ecore_file_exists(buf)) { path = buf; a = e_app_path_find(path); in_all = 1; virtual_app = 1; } } /* Check if the cache is still valid. */ if (a) { if (stat(a->path, &st) >= 0) { stated = 1; if (st.st_mtime > a->mtime) { e_app_fields_empty(a); #if DEBUG printf("M"); #endif } #if DEBUG else printf("H"); #endif } e_object_ref(E_OBJECT(a)); } if ((!a) && (ecore_file_exists(path))) { /* Create it. */ a = e_app_empty_new(path); new_app = 1; #if DEBUG printf("+"); #endif } if ((a) && (a->path)) { if ((!in_all) && (strncmp(a->path, _e_apps_path_all, strlen(_e_apps_path_all)) == 0)) in_all = 1; if (ecore_file_is_dir(a->path)) { if ((!a->idle_fill) && (!a->filled)) { snprintf(buf, sizeof(buf), "%s/.directory", path); if (ecore_file_exists(buf)) e_app_fields_fill(a, buf); /* Not all .directory files are created equal, some unknown file manager can create bogus ones. */ if (!a->filled) { a->name = evas_stringshare_add(ecore_file_get_file(a->path)); a->filled = 1; a->idle_fill = 1; } } if (!a->filled) goto error; if (scan_subdirs) { if (stated) _e_app_subdir_rescan(a); else e_app_subdir_scan(a, scan_subdirs); } /* Don't monitor the all directory, all changes to that must go through e_app. */ if ((!stated) && (strcmp(_e_apps_path_all, a->path) != 0)) a->monitor = ecore_file_monitor_add(a->path, _e_app_cb_monitor, a); } else if (_e_app_is_eapp(a->path)) { if ((!a->idle_fill) && (!a->filled)) e_app_fields_fill(a, a->path); /* no exe field.. not valid. drop it */ // if (!_e_app_exe_valid_get(a->exe)) // goto error; } else goto error; if (virtual_app) { a2 = E_OBJECT_ALLOC(E_App, E_APP_TYPE, _e_app_free); if (a2) { if (_e_app_copy(a2, a)) a->references = evas_list_append(a->references, a2); else { e_object_del(E_OBJECT(a2)); goto error; } } else goto error; } /* Timestamp the cache, and no need to stat the file twice if the cache was stale. */ if ((stated) || (stat(a->path, &st) >= 0)) { a->mtime = st.st_mtime; stated = 1; } if (new_app) { _e_apps_every_app = evas_hash_direct_add(_e_apps_every_app, a->path, a); /* Link it in to all if needed. */ if ((_e_apps_all) && (a != _e_apps_all) && (in_all)) { a->parent = _e_apps_all; e_object_ref(E_OBJECT(a)); } } else if ((_e_apps_all) && (_e_apps_all->subapps) && (in_all)) _e_apps_all->subapps = evas_list_remove(_e_apps_all->subapps, a); if ((_e_apps_all) && (a != _e_apps_all) && (in_all)) _e_apps_all->subapps = evas_list_prepend(_e_apps_all->subapps, a); if (virtual_app) a = a2; } else goto error; return a; error: if (a) { if (a->monitor) ecore_file_monitor_del(a->monitor); if (a->path) evas_stringshare_del(a->path); /* Just going that little extra paranoid, coz rephorm got a strange seggie. */ a->monitor = NULL; a->path = NULL; e_app_fields_empty(a); free(a); } return NULL; } EAPI E_App * e_app_empty_new(const char *path) { E_App *a; a = E_OBJECT_ALLOC(E_App, E_APP_TYPE, _e_app_free); if (!a) return NULL; /* record the path, or make one up */ if (path) a->path = evas_stringshare_add(path); else if (_e_apps_path_all) { char buf[PATH_MAX]; snprintf(buf, sizeof(buf), "%s/_new_app_%1.1f.desktop", _e_apps_path_all, ecore_time_get()); a->path = evas_stringshare_add(buf); } a->icon_type = E_APP_ICON_UNKNOWN; #if DEBUG printf("NEW APP %p %s\n", a, a->path); #endif return a; } EAPI void e_app_image_size_set(E_App *a, int w, int h) { a->width = w; a->height = h; } EAPI int e_app_is_parent(E_App *parent, E_App *app) { E_App *current; current = app->parent; while (current) { if (current == parent) return 1; current = current->parent; } return 0; } EAPI int e_app_equals(E_App *app1, E_App *app2) { if ((!app1) || (!app2)) return 0; return ((app1 == app2) || (app1->orig == app2) || (app1 == app2->orig) || (app1->orig == app2->orig)); } EAPI void e_app_subdir_scan(E_App *a, int scan_subdirs) { Ecore_List *files; char *s; char buf[PATH_MAX]; E_OBJECT_CHECK(a); E_OBJECT_TYPE_CHECK(a, E_APP_TYPE); /* FIXME: This is probably the wrong test. */ if (a->exe) return; if (a->scanned) { Evas_List *l; if (!scan_subdirs) return; for (l = a->subapps; l; l = l->next) e_app_subdir_scan(l->data, scan_subdirs); return; } a->scanned = 1; files = e_app_dir_file_list_get(a); if (files) { while ((s = ecore_list_next(files))) { E_App *a2; a2 = NULL; _e_app_resolve_file_name(buf, sizeof(buf), a->path, s); a2 = e_app_new(buf, scan_subdirs); if (a2) { a2->parent = a; a->subapps = evas_list_append(a->subapps, a2); e_object_ref(E_OBJECT(a2)); } } ecore_list_destroy(files); } } EAPI int e_app_exec(E_App *a, int launch_id) { Ecore_Exe *exe; E_App *original; E_App_Instance *inst; Evas_List *l; char *command; E_OBJECT_CHECK_RETURN(a, 0); E_OBJECT_TYPE_CHECK_RETURN(a, E_APP_TYPE, 0); if (!a->exe) return 0; /* no exe field, don't exe it. */ // if (!_e_app_exe_valid_get(a->exe)) // return 0; /* FIXME: set up locale, encoding and input method env vars if they are in * the eapp file */ inst = E_NEW(E_App_Instance, 1); if (!inst) return 0; if (a->orig) original = a->orig; else original = a; if (a->desktop) { Ecore_List *commands; /* We are not passing a list of files, so we only expect one command. */ commands = ecore_desktop_get_command(a->desktop, NULL, 1); if (commands) { char *temp; temp = ecore_list_first(commands); if (temp) command = strdup(temp); ecore_list_destroy(commands); } } else command = strdup(a->exe); if (!command) { free(inst); e_util_dialog_show(_("Run Error"), _("Enlightenment was unable to process a command line:
" "
" "%s %s
"), a->exe, (a->exe_params != NULL) ? a->exe_params : "" ); return 0; } /* We want the stdout and stderr as lines for the error dialog if it exits abnormally. */ e_util_library_path_strip(); exe = ecore_exe_pipe_run(command, ECORE_EXE_PIPE_AUTO | ECORE_EXE_PIPE_READ | ECORE_EXE_PIPE_ERROR | ECORE_EXE_PIPE_READ_LINE_BUFFERED | ECORE_EXE_PIPE_ERROR_LINE_BUFFERED, inst); e_util_library_path_restore(); if (!exe) { free(command); free(inst); e_util_dialog_show(_("Run Error"), _("Enlightenment was unable to fork a child process:
" "
" "%s %s
"), a->exe, (a->exe_params != NULL) ? a->exe_params : "" ); return 0; } /* 20 lines at start and end, 20x100 limit on bytes at each end. */ ecore_exe_auto_limits_set(exe, 2000, 2000, 20, 20); ecore_exe_tag_set(exe, "E/app"); inst->app = original; inst->exe = exe; inst->launch_id = launch_id; inst->launch_time = ecore_time_get(); inst->expire_timer = ecore_timer_add(10.0, _e_app_cb_expire_timer, inst); if (original->parent == _e_apps_all) { _e_apps_all->subapps = evas_list_remove(_e_apps_all->subapps, original); _e_apps_all->subapps = evas_list_prepend(_e_apps_all->subapps, original); } original->instances = evas_list_append(original->instances, inst); _e_apps_start_pending = evas_list_append(_e_apps_start_pending, original); if (original->startup_notify) original->starting = 1; for (l = original->references; l; l = l->next) { E_App *a2; a2 = l->data; _e_app_change(a2, E_APP_EXEC); } _e_app_change(original, E_APP_EXEC); free(command); return 1; } EAPI int e_app_starting_get(E_App *a) { E_OBJECT_CHECK_RETURN(a, 0); E_OBJECT_TYPE_CHECK_RETURN(a, E_APP_TYPE, 0); return a->starting; } EAPI int e_app_running_get(E_App *a) { E_OBJECT_CHECK_RETURN(a, 0); E_OBJECT_TYPE_CHECK_RETURN(a, E_APP_TYPE, 0); if (a->orig) a = a->orig; if (a->instances) return 1; return 0; } static void _e_app_list_prepend_relative(E_App *add, E_App *before, E_App *parent) { FILE *f; char buf[PATH_MAX]; Evas_List *l; if ((!add) || (!parent)) return; snprintf(buf, sizeof(buf), "%s/.order", parent->path); f = fopen(buf, "wb"); if (!f) return; for (l = parent->subapps; l; l = l->next) { E_App *a; a = l->data; if (a == before) fprintf(f, "%s\n", ecore_file_get_file(add->path)); fprintf(f, "%s\n", ecore_file_get_file(a->path)); } if (before == NULL) fprintf(f, "%s\n", ecore_file_get_file(add->path)); fclose(f); } static void _e_app_files_list_prepend_relative(Evas_List *files, E_App *before, E_App *parent) { FILE *f; char buf[PATH_MAX]; Evas_List *l, *l2; if ((!files) || (!parent)) return; snprintf(buf, sizeof(buf), "%s/.order", parent->path); f = fopen(buf, "wb"); if (!f) return; for (l = parent->subapps; l; l = l->next) { E_App *a; a = l->data; if (a == before) { /* Add the new files */ for (l2 = files; l2; l2 = l2->next) { char *file; file = l2->data; fprintf(f, "%s\n", ecore_file_get_file(file)); } } fprintf(f, "%s\n", ecore_file_get_file(a->path)); } if (before == NULL) { /* Add the new files */ for (l2 = files; l2; l2 = l2->next) { char *file; file = l2->data; fprintf(f, "%s\n", ecore_file_get_file(file)); } } fclose(f); } static void _e_app_files_download(Evas_List *files) { Evas_List *l; for (l = files; l; l = l->next) { char *file; char buf[PATH_MAX]; file = l->data; if (!_e_app_is_eapp(file)) continue; snprintf(buf, sizeof(buf), "%s/%s", _e_apps_path_all, ecore_file_get_file(file)); if (!ecore_file_download(file, buf, NULL, NULL, NULL)) continue; } } EAPI void e_app_list_prepend_relative(E_App *add, E_App *before) { if ((!add) || (!before)) return; if (!before->parent) return; _e_app_list_prepend_relative(add, before, before->parent); } EAPI void e_app_list_append(E_App *add, E_App *parent) { if ((!add) || (!parent)) return; _e_app_list_prepend_relative(add, NULL, parent); } EAPI void e_app_files_list_prepend_relative(Evas_List *files, E_App *before) { _e_app_files_download(files); /* Force rescan of all subdir */ _e_app_subdir_rescan(_e_apps_all); /* Change .order file */ _e_app_files_list_prepend_relative(files, before, before->parent); } EAPI void e_app_files_list_append(Evas_List *files, E_App *parent) { _e_app_files_download(files); /* Force rescan of all subdir */ _e_app_subdir_rescan(_e_apps_all); /* Change .order file */ _e_app_files_list_prepend_relative(files, NULL, parent); } EAPI void e_app_prepend_relative(E_App *add, E_App *before) { char buf[PATH_MAX]; if ((!add) || (!before)) return; if (!before->parent) return; before->parent->subapps = evas_list_prepend_relative(before->parent->subapps, add, before); e_object_ref(E_OBJECT(add)); add->parent = before->parent; /* Check if this app is in the trash */ if (!strncmp(add->path, _e_apps_path_trash, strlen(_e_apps_path_trash))) { /* Move to all */ snprintf(buf, sizeof(buf), "%s/%s", _e_apps_path_all, ecore_file_get_file(add->path)); if (ecore_file_exists(buf)) snprintf(buf, sizeof(buf), "%s/%s", before->parent->path, ecore_file_get_file(add->path)); ecore_file_mv(add->path, buf); evas_stringshare_del(add->path); add->path = evas_stringshare_add(buf); } _e_app_save_order(before->parent); _e_app_change(add, E_APP_ADD); _e_app_change(before->parent, E_APP_ORDER); } EAPI void e_app_append(E_App *add, E_App *parent) { char buf[PATH_MAX]; if ((!add) || (!parent)) return; parent->subapps = evas_list_append(parent->subapps, add); e_object_ref(E_OBJECT(add)); add->parent = parent; /* Check if this app is in the trash */ if (!strncmp(add->path, _e_apps_path_trash, strlen(_e_apps_path_trash))) { /* Move to all */ snprintf(buf, sizeof(buf), "%s/%s", _e_apps_path_all, ecore_file_get_file(add->path)); if (ecore_file_exists(buf)) snprintf(buf, sizeof(buf), "%s/%s", parent->path, ecore_file_get_file(add->path)); ecore_file_mv(add->path, buf); _e_apps_every_app = evas_hash_del(_e_apps_every_app, add->path, add); evas_stringshare_del(add->path); add->path = evas_stringshare_add(buf); _e_apps_every_app = evas_hash_direct_add(_e_apps_every_app, add->path, add); } _e_app_save_order(parent); _e_app_change(add, E_APP_ADD); } EAPI void e_app_files_prepend_relative(Evas_List *files, E_App *before) { Evas_List *l; if (!before) return; if (!before->parent) return; _e_app_files_download(files); /* Force rescan of all subdir */ _e_app_subdir_rescan(_e_apps_all); /* Change .order file */ if (before->parent != _e_apps_all) { FILE *f; char buf[PATH_MAX]; snprintf(buf, sizeof(buf), "%s/.order", before->parent->path); f = fopen(buf, "wb"); if (!f) return; for (l = before->parent->subapps; l; l = l->next) { E_App *a; Evas_List *l2; a = l->data; if (a == before) { /* Add the new files */ for (l2 = files; l2; l2 = l2->next) { char *file; file = l2->data; fprintf(f, "%s\n", ecore_file_get_file(file)); } } fprintf(f, "%s\n", ecore_file_get_file(a->path)); } fclose(f); } } EAPI void e_app_files_append(Evas_List *files, E_App *parent) { Evas_List *l, *subapps; if (!parent) return; subapps = parent->subapps; _e_app_files_download(files); /* Force rescan of all subdir */ _e_app_subdir_rescan(_e_apps_all); /* Change .order file */ if (parent != _e_apps_all) { FILE *f; char buf[PATH_MAX]; snprintf(buf, sizeof(buf), "%s/.order", parent->path); f = fopen(buf, "wb"); if (!f) return; for (l = parent->subapps; l; l = l->next) { E_App *a; a = l->data; fprintf(f, "%s\n", ecore_file_get_file(a->path)); } /* Add the new files */ for (l = files; l; l = l->next) { char *file; file = l->data; fprintf(f, "%s\n", ecore_file_get_file(file)); } fclose(f); } } EAPI void e_app_remove(E_App *a) { Evas_List *l; char buf[PATH_MAX]; if (!a) return; E_OBJECT_CHECK(a); E_OBJECT_TYPE_CHECK(a, E_APP_TYPE); if (!a->parent) return; a->parent->subapps = evas_list_remove(a->parent->subapps, a); /* Check if this app is in a repository or in the parents dir */ snprintf(buf, sizeof(buf), "%s/%s", a->parent->path, ecore_file_get_file(a->path)); if (ecore_file_exists(buf)) { /* Move to trash */ snprintf(buf, sizeof(buf), "%s/%s", _e_apps_path_trash, ecore_file_get_file(a->path)); ecore_file_mv(a->path, buf); _e_apps_every_app = evas_hash_del(_e_apps_every_app, a->path, a); evas_stringshare_del(a->path); a->path = evas_stringshare_add(buf); if (a->path) _e_apps_every_app = evas_hash_direct_add(_e_apps_every_app, a->path, a); } _e_app_save_order(a->parent); for (l = a->references; l; l = l->next) { E_App *a2; a2 = l->data; e_app_remove(a2); } _e_app_change(a, E_APP_DEL); a->parent = NULL; e_object_unref(E_OBJECT(a)); } EAPI void e_app_change_callback_add(void (*func) (void *data, E_App *a, E_App_Change ch), void *data) { E_App_Callback *cb; cb = E_NEW(E_App_Callback, 1); cb->func = func; cb->data = data; _e_apps_change_callbacks = evas_list_append(_e_apps_change_callbacks, cb); } /* * Delete the registered callback which has been registered with the data * given data pointer. This function will return after the first match is * made. * * This will only delete the internal callback function reference. It will * not delete the data. If the data or callback pointers can not be matched * this function does nothing. * * @func pointer to function to be deleted * @data pointer that was initialy registered with the add function */ EAPI void e_app_change_callback_del(void (*func) (void *data, E_App *a, E_App_Change ch), void *data) { Evas_List *l; for (l = _e_apps_change_callbacks; l; l = l->next) { E_App_Callback *cb; cb = l->data; if ((cb->func == func) && (cb->data == data)) { if (_e_apps_callbacks_walking) { cb->delete_me = 1; _e_apps_callbacks_delete_me = 1; } else { _e_apps_change_callbacks = evas_list_remove_list(_e_apps_change_callbacks, l); free(cb); } return; } } } /* Used by e_border and ibar. */ EAPI E_App * e_app_launch_id_pid_find(int launch_id, pid_t pid) { Evas_List *l, *ll; for (l = _e_apps_all->subapps; l; l = l->next) { E_App *a; a = l->data; E_OBJECT_CHECK_RETURN(a, NULL); E_OBJECT_TYPE_CHECK_RETURN(a, E_APP_TYPE, NULL); /* No need to fill up unfilled E_Apps during this scan. */ for (ll = a->instances; ll; ll = ll->next) { E_App_Instance *ai; ai = ll->data; if (((launch_id > 0) && (ai->launch_id > 0) && (ai->launch_id == launch_id)) || ((pid > 1) && (ai->exe) && (ecore_exe_pid_get(ai->exe) == pid))) { _e_apps_all->subapps = evas_list_remove_list(_e_apps_all->subapps, l); _e_apps_all->subapps = evas_list_prepend(_e_apps_all->subapps, a); return a; } } } return NULL; } #if CLEVER_BORDERS static void _e_apps_border_setup(Evas_Hash **non_glob, Evas_List **glob, const char *text, const char *path, char t) { int is_glob = 0; #if DEBUG printf("%c", t); #endif if (glob) { /* Check if text is a glob. This is a really simple check, it could be more complex. */ if (strpbrk(text, "*?[]")) is_glob = 1; } if (is_glob) { struct _E_App_Glob_List_Entry *entry; /* Put it into the glob list. */ entry = E_NEW(struct _E_App_Glob_List_Entry, 1); if (entry) { entry->key = text; entry->path = path; (*glob) = evas_list_append(*glob, entry); glob_count++; } } else { Evas_List *entry = NULL; int found = 0; /* Put it into the list in the non_glob hash. */ entry = evas_hash_find(*non_glob, text); if (entry) found = 1; entry = evas_list_append(entry, path); if (entry) { if (found) evas_hash_modify(*non_glob, text, entry); else (*non_glob) = evas_hash_direct_add(*non_glob, text, entry); } } } static void _e_apps_winners_add(Evas_Hash **winners, const char *path, const char *name, int match_name) { int *count = NULL; int found = 0, do_it = 0, addition = 1; /* Do the name batching magic if we need to. */ if (match_name) { E_App *a; /* If class didn't match then we would not be here. */ /* If there is no name in the E_App, it is implicitily a match. */ /* Its a match if the E_App name and the border name are both NULL. */ /* If a name glob was specified in the E_App, then try to match that with the border name. */ addition = 2; a = e_app_path_find(path); if (a) if ( (!a->win_name) || ((!name) && (!a->win_name)) || (e_util_glob_match(name, a->win_name)) ) do_it = 1; } else do_it = 1; if (do_it) { count = evas_hash_find((*winners), path); if (count) found = 1; else count = calloc(1, sizeof(int)); // FIXME: not very efficient, allocate a bunch of them in one go and point into the next available one. if (count) { (*count) = (*count) + addition; if (found) evas_hash_modify((*winners), path, count); else (*winners) = evas_hash_direct_add((*winners), path, count); } } } static void _e_apps_winners_search(Evas_Hash *non_glob, Evas_List *glob, const char *text, const char *name, Evas_Hash **winners, int match_name) { Evas_List *l, *entry = NULL; /* Check the non_glob list first. */ entry = evas_hash_find(non_glob, text); if (entry) { for (l = entry; l; l = l->next) { const char *path; path = l->data; _e_apps_winners_add(winners, path, name, match_name); } } /* Then check the glob lilst. */ if (glob) { for (l = glob; l; l = l->next) { struct _E_App_Glob_List_Entry *glob; glob = l->data; if (e_util_glob_match(text, glob->key)) _e_apps_winners_add(winners, glob->path, name, match_name); } } } static Evas_Bool _e_apps_winners_hash_cb_check_free(Evas_Hash *hash, const char *key, void *data, void *fdata) { struct _E_App_Glob_List_Winner *winner; int *count; count = data; winner = fdata; #if DEBUG printf("%d %d %s\n", winner->ok, (*count), key); #endif if ((*count) > winner->ok) { winner->path = key; winner->ok = (*count); } free(count); return 1; } #endif /* Used by e_border and ibar. */ EAPI E_App * e_app_border_find(E_Border *bd) { Evas_List *l; int ok; E_App *a = NULL, *a_match = NULL, *clever_match = NULL; char *title; double begin, time, clever_time = 0.0; if ((!bd->client.icccm.name) && (!bd->client.icccm.class) && (!bd->client.icccm.title) && (!bd->client.netwm.name) && (!bd->client.icccm.window_role) && (!bd->client.icccm.command.argv)) return NULL; title = bd->client.netwm.name; if (!title) title = bd->client.icccm.title; begin = ecore_time_get(); /* About the CLEVER_BORDERS code. ASSUMPTIONS: When E execs an E_App, it will fill the E_App first. All E_Apps are eventually filled in by an idler early on. There will be enough non glob win_things that the extra complexity will be much faster than the current linear glob search through the list of E_Apps. The globs wont match so many E_Apps that the final check for highest count wont take too much time. It might be better to insert into a sorted by count list, then just pull out the top one. PROBLEMS: Apps started early, but outside E, most likely from a terminal, may get the wrong border. This will be a result of the two pass part that is designed to make sure the startup apps don't sit and wait for all the E_Apps to be filled. The same problem might hit all currently running apps during a restart. DATA: There will be non-glob hash + glob list for each of win_class, win_title, win_role, and win_name. There will also be a non-glob hash for exe. The non-glob hashs are keyed on the exe, win_class, win_title, win_role, or win_name. They store a list of E_App paths that contain that exe, win_class, win_title, win_role, or win_name. INIT: for each app put exe in the exe non-glob hash. (key = exe, data = list of paths) if win_class, win_title, win_role, and win_name is not a glob look it up in the non-glob hash, add path to the list stored there else put it in the glob list FIND: 2 passes (This speeds up startup programs, as the E_App should be filled.) first without calling e_app_fields_fill and skipping the unfilled ones if there is no winner only do the ones that need filling on the second pass Actually, this seems fast enough that I think we can get away with just running it on all after the fill. on each pass look up client strings in non-glob hashes add contents of resulting list to temp hash (key = path, data = count of matches) go through glob lists if a glob matches, store path in the temp hash go through temp hash, the hash entry with the highest count wins */ #if CLEVER_BORDERS { Evas_Hash *winners = NULL; struct _E_App_Glob_List_Winner winner; for (ok = 0; ok < 2; ok++) { winner.path = NULL; winner.ok = 0; if (ok) /* Fill all E_Apps and try again on the second pass. */ { #if DEBUG printf("SECOND PASS\n"); #endif for (l = _e_apps_all->subapps; l; l = l->next) { a = l->data; E_OBJECT_CHECK_RETURN(a, NULL); E_OBJECT_TYPE_CHECK_RETURN(a, E_APP_TYPE, NULL); if ((!a->idle_fill) && (!a->filled)) e_app_fields_fill(a, a->path); } } if ((bd->client.icccm.class)) _e_apps_winners_search(_e_apps_border_ng_win_class, _e_apps_border_g_win_class, bd->client.icccm.class, bd->client.icccm.name, &winners, TRUE); if ((title)) _e_apps_winners_search(_e_apps_border_ng_win_title, _e_apps_border_g_win_title, title, NULL, &winners, FALSE); if ((bd->client.icccm.window_role)) _e_apps_winners_search(_e_apps_border_ng_win_role, _e_apps_border_g_win_role, bd->client.icccm.window_role, NULL, &winners, FALSE); /* We deliberately ignore the exe arguments when matching, even though ICCCM supplies them. * Ask raster if you want to know why. */ if ((bd->client.icccm.command.argv) && (bd->client.icccm.command.argv[0])) _e_apps_winners_search(_e_apps_border_ng_exe, NULL, bd->client.icccm.command.argv[0], NULL, &winners, FALSE); evas_hash_foreach(winners, _e_apps_winners_hash_cb_check_free, &winner); if (winner.path) { clever_match = e_app_path_find(winner.path); if (clever_match) { if (winners) evas_hash_free(winners); break; } } if (winners) { evas_hash_free(winners); winners = NULL; } } } clever_time = ecore_time_get() - begin; #endif #if OLD_BORDERS for (l = _e_apps_all->subapps; l; l = l->next) { a = l->data; E_OBJECT_CHECK_RETURN(a, NULL); E_OBJECT_TYPE_CHECK_RETURN(a, E_APP_TYPE, NULL); if ((!a->idle_fill) && (!a->filled)) e_app_fields_fill(a, a->path); if (!a->filled) continue; ok = 0; if ((a->win_name) || (a->win_class) || (a->win_title) || (a->win_role) || (a->exe)) { if ((a->win_name) && (a->win_class) && (bd->client.icccm.name) && (bd->client.icccm.class)) { if ((e_util_glob_match(bd->client.icccm.name, a->win_name)) && (e_util_glob_match(bd->client.icccm.class, a->win_class))) ok += 2; } else if ((a->win_class) && (bd->client.icccm.class)) { if (e_util_glob_match(bd->client.icccm.class, a->win_class)) ok += 2; } if (//(!a->win_title) || ((a->win_title) && (title) && (e_util_glob_match(title, a->win_title)))) ok++; if (//(!a->win_role) || ((a->win_role) && (bd->client.icccm.window_role) && (e_util_glob_match(bd->client.icccm.window_role, a->win_role)))) ok++; if ((a->exe) && (bd->client.icccm.command.argv) && (bd->client.icccm.command.argv[0])) { /* a->exe is now already split at the space, so no need to do this. * I left it in in case that becomes false. char *ts, *p; ts = alloca(strlen(a->exe) + 1); strcpy(ts, a->exe); p = ts; while (*p) { if (isspace(*p)) { *p = 0; break; } p++; } if (!strcmp(ts, bd->client.icccm.command.argv[0])) ok++; */ if (!strcmp(a->exe, bd->client.icccm.command.argv[0])) ok++; } } if (ok > match) { match = ok; a_match = a; l_match = l; } } if ((a_match) && (l_match)) { _e_apps_all->subapps = evas_list_remove_list(_e_apps_all->subapps, l_match); _e_apps_all->subapps = evas_list_prepend(_e_apps_all->subapps, a_match); } #else a_match = clever_match; clever_match = NULL; #endif time = ecore_time_get() - begin; time -= clever_time; border_count++; border_time += time; border_clever_time += clever_time; #if DEBUG #if CLEVER_BORDERS && OLD_BORDERS printf("APP BORDER SCAN NEW - %2.6f, OLD - %2.6f (average %2.6f, %2.6f) FOUND %s AND %s\n", clever_time, time, border_clever_time / border_count, border_time / border_count, ((clever_match == NULL) ? "NOTHING" : clever_match->path), ((a_match == NULL) ? "NOTHING" : a_match->path)); #else #if CLEVER_BORDERS printf("APP BORDER SCAN NEW - %2.6f (average %2.6f) FOUND %s\n", clever_time, border_clever_time / border_count, ((a_match == NULL) ? "NOTHING" : a_match->path)); #endif #if OLD_BORDERS printf("APP BORDER SCAN OLD - %2.6f (average %2.6f) FOUND %s\n", time, border_time / border_count, ((a_match == NULL) ? "NOTHING" : a_match->path)); #endif #endif #endif return a_match; } /* Used by e_actions. */ EAPI E_App * e_app_file_find(const char *file) { Evas_List *l; if (!file) return NULL; for (l = _e_apps_all->subapps; l; l = l->next) { E_App *a; a = l->data; E_OBJECT_CHECK_RETURN(a, NULL); E_OBJECT_TYPE_CHECK_RETURN(a, E_APP_TYPE, NULL); if (a->path) { char *p; p = strrchr(a->path, '/'); if (p) { p++; if (!strcmp(p, file)) { _e_apps_all->subapps = evas_list_remove_list(_e_apps_all->subapps, l); _e_apps_all->subapps = evas_list_prepend(_e_apps_all->subapps, a); return a; } } } } return NULL; } /* Used by e_apps. */ EAPI E_App * e_app_path_find(const char *path) { E_App *a = NULL; if ((path) && (_e_apps_every_app)) a = evas_hash_find(_e_apps_every_app, path); return a; } /* Used by e_actions and e_exebuf. */ EAPI E_App * e_app_name_find(const char *name) { Evas_List *l; if (!name) return NULL; for (l = _e_apps_all->subapps; l; l = l->next) { E_App *a; a = l->data; E_OBJECT_CHECK_RETURN(a, NULL); E_OBJECT_TYPE_CHECK_RETURN(a, E_APP_TYPE, NULL); if ((!a->idle_fill) && (!a->filled)) e_app_fields_fill(a, a->path); if (!a->filled) continue; if (a->name) { if (!strcasecmp(a->name, name)) { _e_apps_all->subapps = evas_list_remove_list(_e_apps_all->subapps, l); _e_apps_all->subapps = evas_list_prepend(_e_apps_all->subapps, a); return a; } } } return NULL; } /* Used by e_actions and e_exebuf. */ EAPI E_App * e_app_generic_find(const char *generic) { Evas_List *l; if (!generic) return NULL; for (l = _e_apps_all->subapps; l; l = l->next) { E_App *a; a = l->data; E_OBJECT_CHECK_RETURN(a, NULL); E_OBJECT_TYPE_CHECK_RETURN(a, E_APP_TYPE, NULL); if ((!a->idle_fill) && (!a->filled)) e_app_fields_fill(a, a->path); if (!a->filled) continue; if (a->generic) { if (!strcasecmp(a->generic, generic)) { _e_apps_all->subapps = evas_list_remove_list(_e_apps_all->subapps, l); _e_apps_all->subapps = evas_list_prepend(_e_apps_all->subapps, a); return a; } } } return NULL; } /* Used by e_actions, e_exebuf, and e_zone. */ EAPI E_App * e_app_exe_find(const char *exe) { Evas_List *l; #if CLEVER_BORDERS E_App *a = NULL; l = evas_hash_find(_e_apps_border_ng_exe, exe); if (l) a = e_app_path_find((char *) l->data); /* FIXME: Don't know if it's important to still be returning the least recently used E_App. */ return a; #else if (!exe) return NULL; for (l = _e_apps_all->subapps; l; l = l->next) { E_App *a; a = l->data; E_OBJECT_CHECK_RETURN(a, NULL); E_OBJECT_TYPE_CHECK_RETURN(a, E_APP_TYPE, NULL); if ((!a->idle_fill) && (!a->filled)) e_app_fields_fill(a, a->path); if (!a->filled) continue; if (a->exe) { if (!strcmp(a->exe, exe)) { _e_apps_all->subapps = evas_list_remove_list(_e_apps_all->subapps, l); _e_apps_all->subapps = evas_list_prepend(_e_apps_all->subapps, a); return a; } } } return NULL; #endif } /* Used e_exebuf. */ EAPI Evas_List * e_app_name_glob_list(const char *name) { Evas_List *l, *list = NULL; if (!name) return NULL; for (l = _e_apps_all->subapps; l; l = l->next) { E_App *a; a = l->data; E_OBJECT_CHECK_RETURN(a, NULL); E_OBJECT_TYPE_CHECK_RETURN(a, E_APP_TYPE, NULL); if ((!a->idle_fill) && (!a->filled)) e_app_fields_fill(a, a->path); if (!a->filled) continue; if (a->name) { if (e_util_glob_case_match(a->name, name)) list = evas_list_append(list, a); } } return list; } /* Used e_exebuf. */ EAPI Evas_List * e_app_generic_glob_list(const char *generic) { Evas_List *l, *list = NULL; if (!generic) return NULL; for (l = _e_apps_all->subapps; l; l = l->next) { E_App *a; a = l->data; E_OBJECT_CHECK_RETURN(a, NULL); E_OBJECT_TYPE_CHECK_RETURN(a, E_APP_TYPE, NULL); if ((!a->idle_fill) && (!a->filled)) e_app_fields_fill(a, a->path); if (!a->filled) continue; if (a->generic) { if (e_util_glob_case_match(a->generic, generic)) list = evas_list_append(list, a); } } return list; } /* Used e_exebuf. */ EAPI Evas_List * e_app_exe_glob_list(const char *exe) { Evas_List *l, *list = NULL; if (!exe) return NULL; for (l = _e_apps_all->subapps; l; l = l->next) { E_App *a; a = l->data; E_OBJECT_CHECK_RETURN(a, NULL); E_OBJECT_TYPE_CHECK_RETURN(a, E_APP_TYPE, NULL); if ((!a->idle_fill) && (!a->filled)) e_app_fields_fill(a, a->path); if (!a->filled) continue; if (a->exe) { if (e_util_glob_match(a->exe, exe)) list = evas_list_append(list, a); } } return list; } /* Used e_exebuf. */ EAPI Evas_List * e_app_comment_glob_list(const char *comment) { Evas_List *l, *list = NULL; if (!comment) return NULL; for (l = _e_apps_all->subapps; l; l = l->next) { E_App *a; a = l->data; E_OBJECT_CHECK_RETURN(a, NULL); E_OBJECT_TYPE_CHECK_RETURN(a, E_APP_TYPE, NULL); if ((!a->idle_fill) && (!a->filled)) e_app_fields_fill(a, a->path); if (!a->filled) continue; if (a->comment) { if (e_util_glob_case_match(a->comment, comment)) list = evas_list_append(list, a); } } return list; } /* Used e_fm2. */ EAPI Evas_List * e_app_mime_list(const char *mime) { Evas_List *l, *list = NULL; if (!mime) return NULL; for (l = _e_apps_all->subapps; l; l = l->next) { E_App *a; a = l->data; E_OBJECT_CHECK_RETURN(a, NULL); E_OBJECT_TYPE_CHECK_RETURN(a, E_APP_TYPE, NULL); if ((!a->idle_fill) && (!a->filled)) e_app_fields_fill(a, a->path); if (!a->filled) continue; if ((a->desktop) && (a->desktop->MimeTypes)) { if (ecore_hash_get(a->desktop->MimeTypes, mime)) list = evas_list_append(list, a); } } return list; } EAPI void e_app_fields_fill(E_App *a, const char *path) { const char *lang; /* get our current language */ lang = e_intl_language_alias_get(); /* if its "C" its the default - so drop it */ if (!strcmp(lang, "C")) { lang = NULL; } if (!path) path = a->path; if (!path) return; if (_e_app_is_eapp(path)) { /* It's a .desktop file. */ Ecore_Desktop *desktop; desktop = ecore_desktop_get(path, lang); if (desktop) { a->desktop = desktop; #if DEBUG printf("."); #endif if (desktop->name) a->name = evas_stringshare_add(desktop->name); if (desktop->generic) a->generic = evas_stringshare_add(desktop->generic); if (desktop->comment) a->comment = evas_stringshare_add(desktop->comment); if (desktop->exec) a->exe = evas_stringshare_add(desktop->exec); if (desktop->exec_params) a->exe_params = evas_stringshare_add(desktop->exec_params); if (desktop->icon) a->icon = evas_stringshare_add(desktop->icon); if (desktop->icon_theme) a->icon_theme = evas_stringshare_add(desktop->icon_theme); if (desktop->icon_class) a->icon_class = evas_stringshare_add(desktop->icon_class); if (desktop->icon_path) a->icon_path = evas_stringshare_add(desktop->icon_path); if (desktop->window_name) a->win_name = evas_stringshare_add(desktop->window_name); if (desktop->window_class) a->win_class = evas_stringshare_add(desktop->window_class); if (desktop->window_title) a->win_title = evas_stringshare_add(desktop->window_title); if (desktop->window_role) a->win_role = evas_stringshare_add(desktop->window_role); a->icon_time = desktop->icon_time; a->startup_notify = desktop->startup; a->wait_exit = desktop->wait_exit; a->hard_icon = desktop->hard_icon; a->dirty_icon = 0; a->icon_type = E_APP_ICON_UNKNOWN; a->filled = 1; // if (desktop->type) a->type = evas_stringshare_add(desktop->type); // if (desktop->categories) a->categories = evas_stringshare_add(desktop->categories); } } #if CLEVER_BORDERS if (a->filled) { double begin, time; begin = ecore_time_get(); if ((a->path) && (a->win_class)) _e_apps_border_setup(&_e_apps_border_ng_win_class, &_e_apps_border_g_win_class, a->win_class, a->path, 'c'); if ((a->path) && (a->win_name)) _e_apps_border_setup(&_e_apps_border_ng_win_name, &_e_apps_border_g_win_name, a->win_name, a->path, 'n'); if ((a->path) && (a->win_title)) _e_apps_border_setup(&_e_apps_border_ng_win_title, &_e_apps_border_g_win_title, a->win_title, a->path, 't'); if ((a->path) && (a->win_role)) _e_apps_border_setup(&_e_apps_border_ng_win_role, &_e_apps_border_g_win_role, a->win_role, a->path, 'r'); if ((a->path) && (a->exe)) _e_apps_border_setup(&_e_apps_border_ng_exe, NULL, a->exe, a->path, 'e'); time = ecore_time_get() - begin; border_setup_count++; border_setup_time += time; } #endif } EAPI void e_app_fields_save(E_App *a) { char buf[PATH_MAX]; const char *ext = NULL; int new_eap = 0; E_OBJECT_CHECK(a); E_OBJECT_TYPE_CHECK(a, E_APP_TYPE); /* Check if it's a new one that has not been saved yet. */ if (a->path) ext = ecore_file_get_file(a->path); if ( (!a->path) || ((strncmp(ext, "_new_app_", 9) == 0) && (!ecore_file_exists(a->path))) ) { snprintf(buf, sizeof(buf), "%s/%s.desktop", _e_apps_path_all, a->name); if (a->path) evas_stringshare_del(a->path); a->path = evas_stringshare_add(buf); } if (!a->path) return; /* This still lets old ones that are not in all to be saved, but new ones are forced to be in all. */ if (!ecore_file_exists(a->path)) { /* Force it to be in all. */ snprintf(buf, sizeof(buf), "%s/%s", _e_apps_path_all, ecore_file_get_file(a->path)); if (a->path) evas_stringshare_del(a->path); a->path = evas_stringshare_add(buf); } if (!a->path) return; if (ecore_file_exists(a->path)) _e_apps_every_app = evas_hash_del(_e_apps_every_app, a->path, a); else new_eap = 1; if (_e_app_is_eapp(a->path)) { /* It's a .desktop file. */ Ecore_Desktop *desktop; int created = 0; desktop = ecore_desktop_get(a->path, NULL); if (!desktop) { desktop = E_NEW(Ecore_Desktop, 1); desktop->original_path = strdup(a->path); created = 1; } if (desktop) { desktop->name = (char *) a->name; desktop->generic = (char *) a->generic; desktop->comment = (char *) a->comment; desktop->exec = (char *) a->exe; desktop->exec_params = (char *) a->exe_params; desktop->icon = (char *) a->icon; desktop->icon_theme = (char *) a->icon_theme; desktop->icon_class = (char *) a->icon_class; desktop->icon_path = (char *) a->icon_path; desktop->window_name = (char *) a->win_name; desktop->window_class = (char *) a->win_class; desktop->window_title = (char *) a->win_title; desktop->window_role = (char *) a->win_role; desktop->icon_time = a->icon_time; desktop->hard_icon = a->hard_icon; desktop->startup = a->startup_notify; desktop->wait_exit = a->wait_exit; desktop->type = "Application"; // desktop.categories = a->categories; ecore_desktop_save(desktop); a->dirty_icon = 0; if (created) E_FREE(desktop); } } _e_apps_every_app = evas_hash_direct_add(_e_apps_every_app, a->path, a); if (new_eap) { /* Careful, if this is being created from the border icon, this E_App is already part of the border. */ a->parent = _e_apps_all; _e_apps_all->subapps = evas_list_append(_e_apps_all->subapps, a); e_object_ref(E_OBJECT(a)); /* FIXME: we need to copy and reference this since it is in the repository. */ _e_app_change(a, E_APP_ADD); } else _e_app_fields_save_others(a); } static void _e_app_fields_save_others(E_App *a) { Evas_List *l; for (l = a->references; l; l = l->next) { E_App *a2; a2 = l->data; if (_e_app_copy(a2, a)) _e_app_change(a2, E_APP_CHANGE); } if (a->orig) { E_App *a2; a2 = a->orig; if (_e_app_copy(a2, a)) _e_app_change(a2, E_APP_CHANGE); } _e_app_change(a, E_APP_CHANGE); if (a->parent) { _e_app_change(a->parent, E_APP_CHANGE); /* I don't think we need to rescan. * A) we should be changing all the relevant stuff here. * B) monitoring will catch other changes. * C) it's a waste of time. _e_app_subdir_rescan(a->parent); */ } } EAPI void e_app_fields_empty(E_App *a) { #if DEBUG printf("-"); #endif if (a->name) evas_stringshare_del(a->name); if (a->generic) evas_stringshare_del(a->generic); if (a->comment) evas_stringshare_del(a->comment); if (a->exe) evas_stringshare_del(a->exe); if (a->exe_params) evas_stringshare_del(a->exe_params); if (a->icon_theme) evas_stringshare_del(a->icon_theme); if (a->icon) evas_stringshare_del(a->icon); if (a->icon_class) evas_stringshare_del(a->icon_class); if (a->icon_path) evas_stringshare_del(a->icon_path); if (a->win_name) evas_stringshare_del(a->win_name); if (a->win_class) evas_stringshare_del(a->win_class); if (a->win_title) evas_stringshare_del(a->win_title); if (a->win_role) evas_stringshare_del(a->win_role); a->desktop = NULL; a->name = NULL; a->generic = NULL; a->comment = NULL; a->exe = NULL; a->exe_params = NULL; a->icon_theme = NULL; a->icon = NULL; a->icon_class = NULL; a->icon_path = NULL; a->win_name = NULL; a->win_class = NULL; a->win_title = NULL; a->win_role = NULL; a->dirty_icon = 0; a->hard_icon = 0; a->icon_type = E_APP_ICON_UNKNOWN; a->filled = 0; a->idle_fill = 0; a->idle_icon = 0; } EAPI Ecore_List * e_app_dir_file_list_get(E_App *a) { Ecore_List *files, *files2; char *file; FILE *f; char buf[PATH_MAX]; snprintf(buf, sizeof(buf), "%s/.order", a->path); // printf("APX: LOAD %s\n", buf); files = ecore_file_ls(a->path); f = fopen(buf, "rb"); if (f) { files2 = ecore_list_new(); ecore_list_set_free_cb(files2, free); while (fgets(buf, sizeof(buf), f)) { int len; len = strlen(buf); if (len > 0) { if (buf[len - 1] == '\n') { buf[len - 1] = 0; len--; } if (len > 0) { if (files) { ecore_list_goto_first(files); while ((file = ecore_list_current(files))) { if (!strcmp(buf, file)) { ecore_list_remove(files); free(file); break; } ecore_list_next(files); } } ecore_list_append(files2, strdup(buf)); } } } fclose(f); } else { files2 = files; files = NULL; } if (files) { ecore_list_goto_first(files); while ((file = ecore_list_next(files))) { if (ecore_file_get_file(file)[0] != '.') ecore_list_append(files2, strdup(file)); } ecore_list_destroy(files); } files = files2; if (files) ecore_list_goto_first(files); return files; } EAPI int e_app_valid_exe_get(E_App *a) { char *exe; int ok = 1; if (!a->exe) return 0; exe = ecore_file_app_exe_get(a->exe); if (!exe) return 0; ok = ecore_file_app_installed(exe); free(exe); return ok; } static E_App_Icon_Type _e_app_icon_type(E_App *a) { if (a->icon_type == E_APP_ICON_UNKNOWN) { int theme_match = 0; /* If the icon was hard coded into the .desktop files Icon field, then theming doesn't matter. */ if (a->hard_icon) theme_match = 1; else if ((e_config->icon_theme == NULL) && (a->icon_theme == NULL)) /* Check to see if the icon theme is different. */ theme_match = 1; else if ((e_config->icon_theme) && (a->icon_theme) && (strcmp(e_config->icon_theme, a->icon_theme) == 0)) theme_match = 1; /* Check if we already know the icon path. */ if ((theme_match) && (a->icon_path) && (a->icon_path[0] != 0)) { a->icon_type = E_APP_ICON_PATH; #if DEBUG printf("P"); #endif } else { /* Check the theme for icons. */ if (e_util_edje_icon_list_check(a->icon_class)) { a->icon_type = E_APP_ICON_CLASS; #if DEBUG printf("C"); #endif } else { if (edje_file_group_exists(a->path, "icon")) { a->icon_type = E_APP_ICON_GROUP; #if DEBUG printf("G"); #endif } else /* If that fails, then this might be an FDO icon. */ { if (a->icon_class) { char *v = NULL; v = (char *)ecore_desktop_icon_find(a->icon_class, "64x64", e_config->icon_theme); if (v) { if (a->icon_path) evas_stringshare_del(a->icon_path); a->icon_path = evas_stringshare_add(v); if (e_config->icon_theme) { if (a->icon_theme) evas_stringshare_del(a->icon_theme); a->icon_theme = evas_stringshare_add(e_config->icon_theme); } a->dirty_icon = 1; free(v); /* Copy the new found icon data to the original. */ _e_app_fields_save_others(a); } } } if (a->icon_path) { a->icon_type = E_APP_ICON_FDO; #if DEBUG printf("F"); #endif } else { a->icon_type = E_APP_ICON_NONE; #if DEBUG printf("N"); #endif } } } } return a->icon_type; } EAPI Evas_Object * e_app_icon_add(E_App *a, Evas *evas) { Evas_Object *o = NULL; E_App_Icon_Type type; if (a->orig) a = a->orig; #if DEBUG printf("e_app_icon_add(%s) %s %s %s\n", a->path, a->icon_class, e_config->icon_theme, a->icon_path); #endif type = _e_app_icon_type(a); switch (type) { case E_APP_ICON_CLASS : o = edje_object_add(evas); e_util_edje_icon_list_set(o, a->icon_class); break; case E_APP_ICON_GROUP : o = edje_object_add(evas); edje_object_file_set(o, a->path, "icon"); break; case E_APP_ICON_PATH : case E_APP_ICON_FDO : { char *ext; /* Be paranoid. */ if (a->icon_path) { o = e_icon_add(evas); ext = strrchr(a->icon_path, '.'); if (ext) { if (strcmp(ext, ".edj") == 0) e_icon_file_edje_set(o, a->icon_path, "icon"); else e_icon_file_set(o, a->icon_path); } else e_icon_file_set(o, a->icon_path); e_icon_fill_inside_set(o, 1); } break; } case E_APP_ICON_UNKNOWN : case E_APP_ICON_NONE : default : break; } return o; } /* Search order? - * * fixed path to icon * an .edj icon in ~/.e/e/icons * icon_class in theme * icon from a->path in theme * FDO search for icon_class */ EAPI void e_app_icon_add_to_menu_item(E_App *a, E_Menu_Item *mi) { E_App_Icon_Type type; if (a->orig) a = a->orig; mi->app = a; #if DEBUG printf("e_app_icon_add_to_menu_item(%s) %s %s %s\n", a->path, a->icon_class, e_config->icon_theme, a->icon_path); #endif /* e_menu_item_icon_edje_set() just tucks away the params, the actual call to edje_object_file_set() happens later. */ /* e_menu_item_icon_file_set() just tucks away the params, the actual call to e_icon_add() happens later. */ type = _e_app_icon_type(a); switch (type) { case E_APP_ICON_CLASS : e_util_menu_item_edje_icon_list_set(mi, a->icon_class); break; case E_APP_ICON_GROUP : e_menu_item_icon_edje_set(mi, a->path, "icon"); break; case E_APP_ICON_PATH : case E_APP_ICON_FDO : { char *ext; /* Be paranoid. */ if (a->icon_path) { ext = strrchr(a->icon_path, '.'); if (ext) { if (strcmp(ext, ".edj") == 0) e_menu_item_icon_edje_set(mi, a->icon_path, "icon"); else e_menu_item_icon_file_set(mi, a->icon_path); } else e_menu_item_icon_file_set(mi, a->icon_path); } break; } case E_APP_ICON_UNKNOWN : case E_APP_ICON_NONE : default : break; } } /* local subsystem functions */ static void _e_app_free(E_App *a) { if (a->path) _e_apps_every_app = evas_hash_del(_e_apps_every_app, a->path, a); /* FIXME: natsy - but trying to fix a bug */ /* somehow apps for "startup" end up in subapps of _e_apps_all and * when thet startup app aprent is freed the subapps of the startup are * freed but the list entries for _e_apps_all->subapps is still there * and so u have a nasty dangling pointer to garbage memory */ // if (_e_apps_all) // _e_apps_all->subapps = evas_list_remove(_e_apps_all->subapps, a); /* END FIXME */ while (evas_list_find(_e_apps_start_pending, a)) _e_apps_start_pending = evas_list_remove(_e_apps_start_pending, a); if (a->orig) { while (a->instances) { E_App_Instance *inst; inst = a->instances->data; inst->app = a->orig; a->orig->instances = evas_list_append(a->orig->instances, inst); a->instances = evas_list_remove_list(a->instances, a->instances); _e_apps_start_pending = evas_list_append(_e_apps_start_pending, a->orig); } /* If this is a copy, it shouldn't have any references! */ if (a->references) printf("BUG: A eapp copy shouldn't have any references!\n"); if (a->parent) { a->parent->subapps = evas_list_remove(a->parent->subapps, a); } a->orig->references = evas_list_remove(a->orig->references, a); e_object_unref(E_OBJECT(a->orig)); free(a); } else { while (a->instances) { E_App_Instance *inst; inst = a->instances->data; if (inst->expire_timer) { ecore_timer_del(inst->expire_timer); inst->expire_timer = NULL; } if (inst->exe) { ecore_exe_tag_set(inst->exe, NULL); // ecore_exe_free(inst->exe); inst->exe = NULL; } free(inst); a->instances = evas_list_remove_list(a->instances, a->instances); } while (a->subapps) { E_App *a2; a2 = a->subapps->data; a->subapps = evas_list_remove_list(a->subapps, a->subapps); /* remove us as the parent */ a2->parent = NULL; /* unref the child so it will be deleted too */ e_object_unref(E_OBJECT(a2)); } /* If this is an original, it wont be deleted until all references * are gone */ while (a->references) { E_App *a2; a2 = a->references->data; a2->orig = NULL; a->references = evas_list_remove_list(a->references, a->references); printf("BUG: An original eapp shouldn't have any references when freed! %d\n", evas_list_count(a->references)); } if (a->parent) a->parent->subapps = evas_list_remove(a->parent->subapps, a); if (a->monitor) ecore_file_monitor_del(a->monitor); e_app_fields_empty(a); if (a->path) evas_stringshare_del(a->path); /* Just going that little extra paranoid, coz rephorm got a strange seggie. */ a->monitor = NULL; a->path = NULL; free(a); } } /* Used by _e_app_cb_monitor and _e_app_subdir_rescan */ static E_App * _e_app_subapp_file_find(E_App *a, const char *file) { Evas_List *l; for (l = a->subapps; l; l = l->next) { E_App *a2; a2 = l->data; E_OBJECT_CHECK_RETURN(a2, NULL); E_OBJECT_TYPE_CHECK_RETURN(a2, E_APP_TYPE, NULL); if ((a2->deleted) || ((a2->orig) && (a2->orig->deleted))) continue; if (!strcmp(ecore_file_get_file(a2->path), ecore_file_get_file(file))) return a2; } return NULL; } static void _e_app_change(E_App *a, E_App_Change ch) { Evas_List *l; #if DEBUG if (ch == E_APP_DEL) printf("APP_DEL %s\n", a->path); if (ch == E_APP_CHANGE) printf("APP_CHANGE %p %s\n", a, a->path); if (ch == E_APP_ADD) printf("APP_ADD %s\n", a->path); #endif _e_apps_callbacks_walking = 1; for (l = _e_apps_change_callbacks; l; l = l->next) { E_App_Callback *cb; cb = l->data; if (!cb->delete_me) cb->func(cb->data, a, ch); } _e_apps_callbacks_walking = 0; if (_e_apps_callbacks_delete_me) { for (l = _e_apps_change_callbacks; l;) { E_App_Callback *cb; Evas_List *pl; cb = l->data; pl = l; l = l->next; if (cb->delete_me) { _e_apps_change_callbacks = evas_list_remove_list(_e_apps_change_callbacks, pl); free(cb); } } _e_apps_callbacks_delete_me = 0; } } static void _e_app_cb_monitor(void *data, Ecore_File_Monitor *em, Ecore_File_Event event, const char *path) { E_App *app; char *file; Evas_List *l; app = data; if ((!app) || (app->deleted)) { printf("Event on a deleted eapp\n"); return; } /* If this dir isn't scanned yet, no need to report changes! */ if (!app->scanned) return; #if DEBUG _e_app_print(path, event); #endif // printf("APX: CHG %s\n", path); file = (char *)ecore_file_get_file(path); if (!strcmp(file, ".order")) { if ((event == ECORE_FILE_EVENT_CREATED_FILE) || (event == ECORE_FILE_EVENT_DELETED_FILE) || (event == ECORE_FILE_EVENT_MODIFIED)) { _e_app_subdir_rescan(app); } else { printf("BUG: Weird event for .order: %d\n", event); } } else if (!strcmp(file, ".directory")) { if ((event == ECORE_FILE_EVENT_CREATED_FILE) || (event == ECORE_FILE_EVENT_MODIFIED)) { e_app_fields_empty(app); e_app_fields_fill(app, path); if (app->filled) { _e_app_change(app, E_APP_CHANGE); for (l = app->references; l; l = l->next) { E_App *a2; a2 = l->data; if (_e_app_copy(a2, app)) _e_app_change(a2, E_APP_CHANGE); } } } else if (event == ECORE_FILE_EVENT_DELETED_FILE) { e_app_fields_empty(app); app->name = evas_stringshare_add(ecore_file_get_file(app->path)); } else { printf("BUG: Weird event for .directory: %d\n", event); } } else { if (event == ECORE_FILE_EVENT_MODIFIED) { E_App *a; a = _e_app_subapp_file_find(app, file); if (a) { Evas_List *l; e_app_fields_empty(a); e_app_fields_fill(a, path); if (a->filled) { _e_app_change(a, E_APP_CHANGE); for (l = a->references; l; l = l->next) { E_App *a2; a2 = l->data; if (_e_app_copy(a2, a)) _e_app_change(a2, E_APP_CHANGE); } _e_app_subdir_rescan(app); } } } else if ((event == ECORE_FILE_EVENT_CREATED_FILE) || (event == ECORE_FILE_EVENT_CREATED_DIRECTORY)) { _e_app_subdir_rescan(app); /* If this is the all app, check if someone wants to reference this app */ if (app == _e_apps_all) _e_app_check_order(file); } else if (event == ECORE_FILE_EVENT_DELETED_FILE) { E_App *a; Evas_List *l; a = _e_app_subapp_file_find(app, file); if (a) { a->deleted = 1; for (l = a->references; l;) { E_App *a2; a2 = l->data; l = l->next; if (a2->parent) _e_app_subdir_rescan(a2->parent); } _e_app_subdir_rescan(app); } } else if (event == ECORE_FILE_EVENT_DELETED_SELF) { Evas_List *l; app->deleted = 1; for (l = app->references; l;) { E_App *a2; a2 = l->data; l = l->next; if (a2->parent) _e_app_subdir_rescan(a2->parent); } if (app->parent) _e_app_subdir_rescan(app->parent); else { /* A main e_app has been deleted! * We don't unref this, the code which holds this * eapp must do it. */ _e_app_change(app, E_APP_DEL); } } } } static void _e_app_subdir_rescan(E_App *app) { Ecore_List *files; Evas_List *subapps = NULL, *changes = NULL, *l, *l2; E_App_Change_Info *ch; char buf[PATH_MAX]; char *s; files = e_app_dir_file_list_get(app); if (files) { while ((s = ecore_list_next(files))) { E_App *a2; a2 = _e_app_subapp_file_find(app, s); if (a2) { subapps = evas_list_append(subapps, a2); e_object_ref(E_OBJECT(a2)); } else { /* If we still haven't found it, it is new! */ _e_app_resolve_file_name(buf, sizeof(buf), app->path, s); a2 = e_app_new(buf, 1); if (a2) { a2->parent = app; ch = E_NEW(E_App_Change_Info, 1); ch->app = a2; ch->change = E_APP_ADD; e_object_ref(E_OBJECT(ch->app)); changes = evas_list_append(changes, ch); subapps = evas_list_append(subapps, a2); e_object_ref(E_OBJECT(a2)); } } } ecore_list_destroy(files); } for (l = app->subapps; l; l = l->next) { E_App *a2; a2 = l->data; for (l2 = subapps; l2; l2 = l2->next) if (l->data == l2->data) { /* We still have this app */ e_object_unref(E_OBJECT(a2)); a2 = NULL; break; } if (a2) { a2->deleted = 1; ch = E_NEW(E_App_Change_Info, 1); ch->app = a2; ch->change = E_APP_DEL; e_object_ref(E_OBJECT(ch->app)); changes = evas_list_append(changes, ch); e_object_unref(E_OBJECT(a2)); } } /* FIXME: We only need to tell about order changes if there are! */ evas_list_free(app->subapps); app->subapps = subapps; ch = E_NEW(E_App_Change_Info, 1); ch->app = app; ch->change = E_APP_ORDER; e_object_ref(E_OBJECT(ch->app)); changes = evas_list_append(changes, ch); for (l = changes; l; l = l->next) { ch = l->data; _e_app_change(ch->app, ch->change); e_object_unref(E_OBJECT(ch->app)); free(ch); } evas_list_free(changes); } static int _e_app_is_eapp(const char *path) { char *p; if (!path) return 0; p = strrchr(path, '.'); if (!p) return 0; p++; if ((strcasecmp(p, "desktop") != 0) && (strcasecmp(p, "directory") != 0)) return 0; return 1; } static int _e_app_copy(E_App *dst, E_App *src) { if (src->deleted) { printf("BUG: This app is deleted, can't make a copy: %s\n", src->path); return 0; } if (!_e_app_is_eapp(src->path)) { printf("BUG: The app isn't an eapp: %s\n", src->path); return 0; } dst->orig = src; dst->desktop = src->desktop; dst->path = src->path; dst->name = src->name; dst->generic = src->generic; dst->comment = src->comment; dst->exe = src->exe; dst->exe_params = src->exe_params; dst->win_name = src->win_name; dst->win_class = src->win_class; dst->win_title = src->win_title; dst->win_role = src->win_role; dst->icon_theme = src->icon_theme; dst->icon = src->icon; dst->icon_class = src->icon_class; dst->icon_path = src->icon_path; dst->startup_notify = src->startup_notify; dst->wait_exit = src->wait_exit; dst->icon_time = src->icon_time; dst->starting = src->starting; dst->scanned = src->scanned; dst->icon_type = src->icon_type; dst->dirty_icon = src->dirty_icon; dst->hard_icon = src->hard_icon; dst->filled = src->filled; dst->idle_fill = src->idle_fill; dst->idle_icon = src->idle_icon; return 1; } static void _e_app_save_order(E_App *app) { FILE *f; char buf[PATH_MAX]; Evas_List *l; if (!app) return; snprintf(buf, sizeof(buf), "%s/.order", app->path); f = fopen(buf, "wb"); if (!f) return; for (l = app->subapps; l; l = l->next) { E_App *a; a = l->data; fprintf(f, "%s\n", ecore_file_get_file(a->path)); } fclose(f); } static int _e_apps_cb_exit(void *data, int type, void *event) { Ecore_Exe_Event_Del *ev; E_App_Instance *ai; E_App *a; Evas_List *l; ev = event; if (!ev->exe) return 1; if (!(ecore_exe_tag_get(ev->exe) && (!strcmp(ecore_exe_tag_get(ev->exe), "E/app")))) return 1; ai = ecore_exe_data_get(ev->exe); if (!ai) return 1; a = ai->app; if (!a) return 1; /* /bin/sh uses this if cmd not found */ if ((ev->exited) && ((ev->exit_code == 127) || (ev->exit_code == 255))) { E_Dialog *dia; dia = e_dialog_new(e_container_current_get(e_manager_current_get()), "E", "_app_run_error_dialog"); if (dia) { char buf[4096]; e_dialog_title_set(dia, _("Application run error")); snprintf(buf, sizeof(buf), _("Enlightenment was unable to run the application:
" "
" "%s
" "
" "The application failed to start."), a->exe); e_dialog_text_set(dia, buf); // e_dialog_icon_set(dia, "enlightenment/error", 64); e_dialog_button_add(dia, _("OK"), NULL, NULL, NULL); e_dialog_button_focus_num(dia, 1); e_win_centered_set(dia->win, 1); e_dialog_show(dia); } } /* Let's hope that everyhing returns this properly. */ else if (!((ev->exited) && (ev->exit_code == EXIT_SUCCESS))) { /* Show the error dialog with details from the exe. */ E_App_Autopsy *aut; aut = E_NEW(E_App_Autopsy, 1); aut->app = a; aut->del = *ev; aut->error = ecore_exe_event_data_get(ai->exe, ECORE_EXE_PIPE_ERROR); aut->read = ecore_exe_event_data_get(ai->exe, ECORE_EXE_PIPE_READ); e_app_error_dialog(NULL, aut); } a->instances = evas_list_remove(a->instances, ai); ai->exe = NULL; if (ai->expire_timer) ecore_timer_del(ai->expire_timer); free(ai); _e_apps_start_pending = evas_list_remove(_e_apps_start_pending, a); for (l = a->references; l; l = l->next) { E_App *a2; a2 = l->data; _e_app_change(a2, E_APP_EXIT); } _e_app_change(a, E_APP_EXIT); return 1; } static int _e_app_cb_event_border_add(void *data, int type, void *event) { E_Event_Border_Add *ev; Evas_List *l, *ll, *removes = NULL; E_App *a; E_App_Instance *inst; ev = event; if (ev->border->client.netwm.startup_id <= 0) return 1; for (l = _e_apps_start_pending; l; l = l->next) { a = l->data; for (ll = a->instances; ll; ll = ll->next) { inst = ll->data; if (inst->launch_id == ev->border->client.netwm.startup_id) { if (inst->expire_timer) { ecore_timer_del(inst->expire_timer); inst->expire_timer = NULL; } removes = evas_list_append(removes, a); e_object_ref(E_OBJECT(a)); break; } } } while (removes) { a = removes->data; for (l = a->references; l; l = l->next) { E_App *a2; a2 = l->data; _e_app_change(a2, E_APP_READY); } _e_app_change(a, E_APP_READY); _e_apps_start_pending = evas_list_remove(_e_apps_start_pending, a); e_object_unref(E_OBJECT(a)); removes = evas_list_remove_list(removes, removes); } return 1; } static int _e_app_cb_expire_timer(void *data) { E_App_Instance *inst; E_App *a; Evas_List *l; inst = data; a = inst->app; _e_apps_start_pending = evas_list_remove(_e_apps_start_pending, a); inst->expire_timer = NULL; for (l = a->references; l; l = l->next) { E_App *a2; a2 = l->data; _e_app_change(a2, E_APP_READY_EXPIRE); } _e_app_change(a, E_APP_READY_EXPIRE); return 0; } #if DEBUG static void _e_app_print(const char *path, Ecore_File_Event event) { switch (event) { case ECORE_FILE_EVENT_NONE: printf("E_App none: %s\n", path); break; case ECORE_FILE_EVENT_CREATED_FILE: printf("E_App created file: %s\n", path); break; case ECORE_FILE_EVENT_CREATED_DIRECTORY: printf("E_App created directory: %s\n", path); break; case ECORE_FILE_EVENT_DELETED_FILE: printf("E_App deleted file: %s\n", path); break; case ECORE_FILE_EVENT_DELETED_DIRECTORY: printf("E_App deleted directory: %s\n", path); break; case ECORE_FILE_EVENT_DELETED_SELF: printf("E_App deleted self: %s\n", path); break; case ECORE_FILE_EVENT_MODIFIED: printf("E_App modified: %s\n", path); break; } } #endif static void _e_app_check_order(const char *file) { Evas_List *l; for (l = _e_apps_all->subapps; l; l = l->next) { E_App *a; a = l->data; if (a->monitor) { /* This is a directory */ if (_e_app_order_contains(a, file)) _e_app_subdir_rescan(a); } } } static int _e_app_order_contains(E_App *a, const char *file) { char buf[4096]; int ret = 0; FILE *f; snprintf(buf, sizeof(buf), "%s/.order", a->path); if (!ecore_file_exists(buf)) return 0; f = fopen(buf, "rb"); if (!f) return 0; while (fgets(buf, sizeof(buf), f)) { int len; len = strlen(buf); if (len > 0) { if (buf[len - 1] == '\n') { buf[len - 1] = 0; len--; } if (!strcmp(buf, file)) { ret = 1; break; } } } fclose(f); return ret; } static void _e_app_resolve_file_name(char *buf, size_t size, const char *path, const char *file) { size_t length; length = strlen(path); if (file[0] == '/') snprintf(buf, size, "%s", file); else if ((length > 0) && (path[length - 1] == '/')) snprintf(buf, size, "%s%s", path, file); else snprintf(buf, size, "%s/%s", path, file); }