DesktopEntry * desktop_entry_new (const char *path) { DesktopEntryType type; DesktopEntry *retval; menu_verbose ("Loading desktop entry \"%s\"\n", path); if (g_str_has_suffix (path, ".desktop")) { type = DESKTOP_ENTRY_DESKTOP; } else if (g_str_has_suffix (path, ".directory")) { type = DESKTOP_ENTRY_DIRECTORY; } else { menu_verbose ("Unknown desktop entry suffix in \"%s\"\n", path); return NULL; } retval = g_new0 (DesktopEntry, 1); retval->refcount = 1; retval->type = type; retval->basename = g_path_get_basename (path); retval->path = g_strdup (path); return desktop_entry_load (retval); }
EntryDirectory * entry_directory_new (DesktopEntryType entry_type, const char *path) { EntryDirectory *ed; char *canonical; menu_verbose ("Loading entry directory \"%s\"\n", path); canonical = realpath (path, NULL); if (canonical == NULL) { menu_verbose ("Failed to canonicalize \"%s\": %s\n", path, g_strerror (errno)); return NULL; } ed = g_new0 (EntryDirectory, 1); ed->dir = cached_dir_lookup (canonical); g_assert (ed->dir != NULL); cached_dir_add_reference (ed->dir); cached_dir_load_entries_recursive (ed->dir, canonical); ed->entry_type = entry_type; ed->refcount = 1; g_free (canonical); return ed; }
static MenuMonitor * register_monitor (const char *path, gboolean is_directory) { #if !GLIB_CHECK_VERSION(2, 36, 0) static gboolean initted = FALSE; #endif MenuMonitor *retval; GFile *file; #if !GLIB_CHECK_VERSION(2, 36, 0) if (!initted) { /* This is the only place where we're using GObject and the app can't * know we're using it, so we need to init the type system ourselves. */ g_type_init (); initted = TRUE; } #endif retval = g_new0 (MenuMonitor, 1); retval->path = g_strdup (path); retval->refcount = 1; retval->is_directory = is_directory != FALSE; file = g_file_new_for_path (retval->path); if (file == NULL) { menu_verbose ("Not adding monitor on '%s', failed to create GFile\n", retval->path); return retval; } if (retval->is_directory) retval->monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, NULL); else retval->monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, NULL); g_object_unref (G_OBJECT (file)); if (retval->monitor == NULL) { menu_verbose ("Not adding monitor on '%s', failed to create monitor\n", retval->path); return retval; } g_signal_connect (retval->monitor, "changed", G_CALLBACK (monitor_callback), retval); return retval; }
DesktopEntrySet * _entry_directory_list_get_all_desktops (EntryDirectoryList *list) { GList *tmp; DesktopEntrySet *set; /* The only tricky thing here is that desktop files later * in the search list with the same relative path * are "hidden" by desktop files earlier in the path, * so we have to do the earlier files first causing * the later files to replace the earlier files * in the DesktopEntrySet * * We go from the end of the list so we can just * g_hash_table_replace and not have to do two * hash lookups (check for existing entry, then insert new * entry) */ /* This method is -extremely- slow, so we have a simple one-entry cache here */ if (_entry_directory_list_compare (list, entry_directory_last_list)) { menu_verbose (" Hit desktop list (%p) cache\n", list); return desktop_entry_set_ref (entry_directory_last_set); } if (entry_directory_last_set != NULL) desktop_entry_set_unref (entry_directory_last_set); if (entry_directory_last_list != NULL) entry_directory_list_unref (entry_directory_last_list); set = desktop_entry_set_new (); menu_verbose (" Storing all of list %p in set %p\n", list, set); tmp = g_list_last (list->dirs); while (tmp != NULL) { entry_directory_foreach (tmp->data, get_all_func, set, NULL); tmp = tmp->prev; } entry_directory_last_list = entry_directory_list_ref (list); entry_directory_last_set = desktop_entry_set_ref (set); return set; }
static gboolean cached_dir_add_entry (CachedDir *dir, const char *basename, const char *path) { DesktopEntry *entry; DesktopEntryResultCode code; entry = desktop_entry_new (path, &code); if (entry == NULL) { if (code == DESKTOP_ENTRY_LOAD_FAIL_APPINFO) { menu_verbose ("Adding %s to the retry list (mimeinfo.cache maybe isn't done getting updated yet\n", path); dir->retry_later_desktop_entries = g_slist_prepend (dir->retry_later_desktop_entries, g_strdup (path)); } return FALSE; } dir->entries = g_slist_prepend (dir->entries, entry); return TRUE; }
void desktop_entry_add_legacy_category (DesktopEntry *entry) { GQuark *categories; int i; menu_verbose ("Adding Legacy category to \"%s\"\n", entry->basename); i = 0; if (entry->categories != NULL) { for (; entry->categories[i]; i++); } categories = g_new0 (GQuark, i + 2); i = 0; if (entry->categories != NULL) { for (; entry->categories[i]; i++) categories[i] = entry->categories[i]; } categories[i] = g_quark_from_string ("Legacy"); g_free (entry->categories); entry->categories = categories; }
static CachedDir * cached_dir_add_subdir (CachedDir *dir, const char *basename, const char *path) { CachedDir *subdir; subdir = find_subdir (dir, basename); if (subdir != NULL) { subdir->deleted = FALSE; return subdir; } subdir = cached_dir_new (basename); if (path != NULL && !cached_dir_load_entries_recursive (subdir, path)) { cached_dir_free (subdir); return NULL; } menu_verbose ("Caching dir \"%s\"\n", basename); subdir->parent = dir; dir->subdirs = g_slist_prepend (dir->subdirs, cached_dir_ref (subdir)); return subdir; }
DesktopEntry * desktop_entry_reload (DesktopEntry *entry) { g_return_val_if_fail (entry != NULL, NULL); menu_verbose ("Re-loading desktop entry \"%s\"\n", entry->path); g_free (entry->categories); entry->categories = NULL; g_free (entry->name); entry->name = NULL; g_free (entry->generic_name); entry->generic_name = NULL; g_free (entry->full_name); entry->full_name = NULL; g_free (entry->comment); entry->comment = NULL; g_free (entry->icon); entry->icon = NULL; g_free (entry->exec); entry->exec = NULL; entry->terminal = 0; entry->flags = 0; return desktop_entry_load (entry); }
DesktopEntry * desktop_entry_copy (DesktopEntry *entry) { DesktopEntry *retval; menu_verbose ("Copying desktop entry \"%s\"\n", entry->basename); if (entry->type == DESKTOP_ENTRY_DESKTOP) retval = (DesktopEntry*)g_new0 (DesktopEntryDesktop, 1); else if (entry->type == DESKTOP_ENTRY_DIRECTORY) retval = (DesktopEntry*)g_new0 (DesktopEntryDirectory, 1); else g_assert_not_reached (); retval->refcount = 1; retval->type = entry->type; retval->path = g_strdup (entry->path); retval->basename = unix_basename_from_path (retval->path); if (retval->type == DESKTOP_ENTRY_DESKTOP) { DesktopEntryDesktop *desktop_entry = (DesktopEntryDesktop*) entry; DesktopEntryDesktop *retval_desktop_entry = (DesktopEntryDesktop*) retval; int i; retval_desktop_entry->appinfo = g_object_ref (desktop_entry->appinfo); if (desktop_entry->categories != NULL) { i = 0; for (; desktop_entry->categories[i]; i++); retval_desktop_entry->categories = g_new0 (GQuark, i + 1); i = 0; for (; desktop_entry->categories[i]; i++) retval_desktop_entry->categories[i] = desktop_entry->categories[i]; } else retval_desktop_entry->categories = NULL; } else if (entry->type == DESKTOP_ENTRY_DIRECTORY) { DesktopEntryDirectory *entry_directory = (DesktopEntryDirectory*)entry; DesktopEntryDirectory *retval_directory = (DesktopEntryDirectory*)retval; retval_directory->name = g_strdup (entry_directory->name); retval_directory->comment = g_strdup (entry_directory->comment); retval_directory->icon = g_object_ref (entry_directory->icon); retval_directory->nodisplay = entry_directory->nodisplay; retval_directory->hidden = entry_directory->hidden; retval_directory->showin = entry_directory->showin; } return retval; }
DesktopEntry * desktop_entry_new (const char *path, DesktopEntryResultCode *res_code) { DesktopEntryType type; DesktopEntry *retval; DesktopEntryResultCode code; menu_verbose ("Loading desktop entry \"%s\"\n", path); if (g_str_has_suffix (path, ".desktop")) { type = DESKTOP_ENTRY_DESKTOP; retval = (DesktopEntry*)g_new0 (DesktopEntryDesktop, 1); } else if (g_str_has_suffix (path, ".directory")) { type = DESKTOP_ENTRY_DIRECTORY; retval = (DesktopEntry*)g_new0 (DesktopEntryDirectory, 1); } else { menu_verbose ("Unknown desktop entry suffix in \"%s\"\n", path); *res_code = DESKTOP_ENTRY_LOAD_FAIL_OTHER; return NULL; } retval->refcount = 1; retval->type = type; retval->path = g_strdup (path); retval->basename = unix_basename_from_path (retval->path); code = desktop_entry_load (retval); *res_code = code; if (code < DESKTOP_ENTRY_LOAD_SUCCESS) { desktop_entry_unref (retval); return NULL; } return retval; }
static void desktop_entry_set_clear (DesktopEntrySet *set) { menu_verbose (" Clearing set %p\n", set); if (set->hash != NULL) { g_hash_table_destroy (set->hash); set->hash = NULL; } }
void desktop_entry_set_swap_contents (DesktopEntrySet *a, DesktopEntrySet *b) { GHashTable *tmp; menu_verbose (" Swap contents of %p and %p\n", a, b); tmp = a->hash; a->hash = b->hash; b->hash = tmp; }
DesktopEntrySet * desktop_entry_set_new (void) { DesktopEntrySet *set; set = g_new0 (DesktopEntrySet, 1); set->refcount = 1; menu_verbose (" New entry set %p\n", set); return set; }
void desktop_entry_set_union (DesktopEntrySet *set, DesktopEntrySet *with) { menu_verbose (" Union of %p and %p\n", set, with); if (desktop_entry_set_get_count (with) == 0) return; /* A fast simple case */ g_hash_table_foreach (with->hash, (GHFunc) union_foreach, set); }
static EntryDirectory * entry_directory_new_full (DesktopEntryType entry_type, const char *path, gboolean is_legacy, const char *legacy_prefix) { EntryDirectory *ed; char *canonical; menu_verbose ("Loading entry directory \"%s\" (legacy %s)\n", path, is_legacy ? "<yes>" : "<no>"); canonical = menu_canonicalize_file_name (path, FALSE); if (canonical == NULL) { menu_verbose ("Failed to canonicalize \"%s\": %s\n", path, g_strerror (errno)); return NULL; } ed = g_new0 (EntryDirectory, 1); ed->dir = cached_dir_lookup (canonical); g_assert (ed->dir != NULL); cached_dir_add_reference (ed->dir); cached_dir_load_entries_recursive (ed->dir, canonical); ed->legacy_prefix = g_strdup (legacy_prefix); ed->entry_type = entry_type; ed->is_legacy = is_legacy != FALSE; ed->refcount = 1; g_free (canonical); return ed; }
static gboolean intersect_foreach_remove (const char *file_id, DesktopEntry *entry, IntersectData *id) { /* Remove everything in "set" which is not in "with" */ if (g_hash_table_lookup (id->with->hash, file_id) != NULL) return FALSE; menu_verbose (" Removing from %p entry %s\n", id->set, file_id); return TRUE; /* return TRUE to remove */ }
static gboolean subtract_foreach_remove (const char *file_id, DesktopEntry *entry, SubtractData *sd) { /* Remove everything in "set" which is not in "other" */ if (g_hash_table_lookup (sd->other->hash, file_id) == NULL) return FALSE; menu_verbose (" Removing from %p entry %s\n", sd->set, file_id); return TRUE; /* return TRUE to remove */ }
void desktop_entry_set_unref (DesktopEntrySet *set) { g_return_if_fail (set != NULL); g_return_if_fail (set->refcount > 0); set->refcount -= 1; if (set->refcount == 0) { menu_verbose (" Deleting entry set %p\n", set); if (set->hash) g_hash_table_destroy (set->hash); set->hash = NULL; g_free (set); } }
void desktop_entry_set_add_entry (DesktopEntrySet *set, DesktopEntry *entry, const char *file_id) { menu_verbose (" Adding to set %p entry %s\n", set, file_id); if (set->hash == NULL) { set->hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) desktop_entry_unref); } g_hash_table_replace (set->hash, g_strdup (file_id), desktop_entry_ref (entry)); }
void desktop_entry_set_subtract (DesktopEntrySet *set, DesktopEntrySet *other) { SubtractData sd; menu_verbose (" Subtract from %p set %p\n", set, other); if (desktop_entry_set_get_count (set) == 0 || desktop_entry_set_get_count (other) == 0) return; /* A fast simple case */ sd.set = set; sd.other = other; g_hash_table_foreach_remove (set->hash, (GHRFunc) subtract_foreach_remove, &sd); }
DesktopEntry * desktop_entry_copy (DesktopEntry *entry) { DesktopEntry *retval; int i; menu_verbose ("Copying desktop entry \"%s\"\n", entry->basename); retval = g_new0 (DesktopEntry, 1); retval->refcount = 1; retval->type = entry->type; retval->basename = g_strdup (entry->basename); retval->path = g_strdup (entry->path); retval->name = g_strdup (entry->name); retval->generic_name = g_strdup (entry->generic_name); retval->full_name = g_strdup (entry->full_name); retval->comment = g_strdup (entry->comment); retval->icon = g_strdup (entry->icon); retval->exec = g_strdup (entry->exec); retval->terminal = entry->terminal; retval->flags = entry->flags; i = 0; if (entry->categories != NULL) { for (; entry->categories[i]; i++); } retval->categories = g_new0 (GQuark, i + 1); i = 0; if (entry->categories != NULL) { for (; entry->categories[i]; i++) retval->categories[i] = entry->categories[i]; } return retval; }
DesktopEntry * desktop_entry_reload (DesktopEntry *entry) { g_return_val_if_fail (entry != NULL, NULL); menu_verbose ("Re-loading desktop entry \"%s\"\n", entry->path); if (entry->type == DESKTOP_ENTRY_DESKTOP) { DesktopEntryDesktop *entry_desktop = (DesktopEntryDesktop *) entry; g_object_unref (entry_desktop->appinfo); entry_desktop->appinfo = NULL; g_free (entry_desktop->categories); entry_desktop->categories = NULL; } else if (entry->type == DESKTOP_ENTRY_DIRECTORY) { DesktopEntryDirectory *entry_directory = (DesktopEntryDirectory*) entry; g_free (entry_directory->name); entry_directory->name = NULL; g_free (entry_directory->comment); entry_directory->comment = NULL; g_object_unref (entry_directory->icon); entry_directory->icon = NULL; } else g_assert_not_reached (); if (desktop_entry_load (entry) < DESKTOP_ENTRY_LOAD_SUCCESS) { desktop_entry_unref (entry); return NULL; } return entry; }
static CachedDir * cached_dir_lookup (const char *canonical) { CachedDir *dir; char **split; int i; if (dir_cache == NULL) dir_cache = cached_dir_new ("/"); dir = dir_cache; g_assert (canonical != NULL && canonical[0] == G_DIR_SEPARATOR); menu_verbose ("Looking up cached dir \"%s\"\n", canonical); split = g_strsplit (canonical + 1, "/", -1); i = 0; while (split[i] != NULL) { CachedDir *subdir; if ((subdir = find_subdir (dir, split[i])) == NULL) { subdir = cached_dir_new (split[i]); dir->subdirs = g_slist_prepend (dir->subdirs, subdir); subdir->parent = dir; } dir = subdir; ++i; } g_strfreev (split); g_assert (dir != NULL); return dir; }
void desktop_entry_set_intersection (DesktopEntrySet *set, DesktopEntrySet *with) { IntersectData id; menu_verbose (" Intersection of %p and %p\n", set, with); if (desktop_entry_set_get_count (set) == 0 || desktop_entry_set_get_count (with) == 0) { /* A fast simple case */ desktop_entry_set_clear (set); return; } id.set = set; id.with = with; g_hash_table_foreach_remove (set->hash, (GHRFunc) intersect_foreach_remove, &id); }
static CachedDir * cached_dir_lookup (const char *canonical) { CachedDir *dir; char **split; int i; if (dir_cache == NULL) dir_cache = cached_dir_new_full ("/", (GFunc) clear_cache, &dir_cache); dir = dir_cache; g_assert (canonical != NULL && canonical[0] == G_DIR_SEPARATOR); menu_verbose ("Looking up cached dir \"%s\"\n", canonical); split = g_strsplit (canonical + 1, "/", -1); i = 0; while (split[i] != NULL) { CachedDir *subdir; subdir = cached_dir_add_subdir (dir, split[i], NULL); dir = subdir; ++i; } g_strfreev (split); g_assert (dir != NULL); return dir; }
static DesktopEntry * desktop_entry_load (DesktopEntry *entry) { DesktopEntry *retval = NULL; GKeyFile *key_file; GError *error; const char *desktop_entry_group; char *name_str; char *type_str; key_file = g_key_file_new (); error = NULL; if (!g_key_file_load_from_file (key_file, entry->path, 0, &error)) { menu_verbose ("Failed to load \"%s\": %s\n", entry->path, error->message); g_error_free (error); goto out; } if (g_key_file_has_group (key_file, DESKTOP_ENTRY_GROUP)) { desktop_entry_group = DESKTOP_ENTRY_GROUP; } else { menu_verbose ("\"%s\" contains no \"" DESKTOP_ENTRY_GROUP "\" group\n", entry->path); if (g_key_file_has_group (key_file, KDE_DESKTOP_ENTRY_GROUP)) { desktop_entry_group = KDE_DESKTOP_ENTRY_GROUP; menu_verbose ("\"%s\" contains deprecated \"" KDE_DESKTOP_ENTRY_GROUP "\" group\n", entry->path); } else { goto out; } } if (!g_key_file_has_key (key_file, desktop_entry_group, "Name", NULL)) { menu_verbose ("\"%s\" contains no \"Name\" key\n", entry->path); goto out; } name_str = g_key_file_get_locale_string (key_file, desktop_entry_group, "Name", NULL, NULL); if (!name_str) { menu_verbose ("\"%s\" contains an invalid \"Name\" key\n", entry->path); goto out; } g_free (name_str); type_str = g_key_file_get_string (key_file, desktop_entry_group, "Type", NULL); if (!type_str) { menu_verbose ("\"%s\" contains no \"Type\" key\n", entry->path); goto out; } if ((entry->type == DESKTOP_ENTRY_DESKTOP && strcmp (type_str, "Application") != 0) || (entry->type == DESKTOP_ENTRY_DIRECTORY && strcmp (type_str, "Directory") != 0)) { menu_verbose ("\"%s\" does not contain the correct \"Type\" value\n", entry->path); g_free (type_str); goto out; } g_free (type_str); if (entry->type == DESKTOP_ENTRY_DESKTOP && !g_key_file_has_key (key_file, desktop_entry_group, "Exec", NULL)) { menu_verbose ("\"%s\" does not contain an \"Exec\" key\n", entry->path); goto out; } retval = entry; #define GET_LOCALE_STRING(n) g_key_file_get_locale_string (key_file, desktop_entry_group, (n), NULL, NULL) retval->name = GET_LOCALE_STRING ("Name"); retval->generic_name = GET_LOCALE_STRING ("GenericName"); retval->full_name = GET_LOCALE_STRING ("X-MATE-FullName"); retval->comment = GET_LOCALE_STRING ("Comment"); retval->icon = GET_LOCALE_STRING ("Icon"); retval->flags = get_flags_from_key_file (retval, key_file, desktop_entry_group); retval->categories = get_categories_from_key_file (retval, key_file, desktop_entry_group); if (entry->type == DESKTOP_ENTRY_DESKTOP) { retval->exec = g_key_file_get_string (key_file, desktop_entry_group, "Exec", NULL); retval->terminal = g_key_file_get_boolean (key_file, desktop_entry_group, "Terminal", NULL); } #undef GET_LOCALE_STRING menu_verbose ("Desktop entry \"%s\" (%s, %s, %s, %s, %s) flags: NoDisplay=%s, Hidden=%s, ShowInMATE=%s, TryExecFailed=%s\n", retval->basename, retval->name, retval->generic_name ? retval->generic_name : "(null)", retval->full_name ? retval->full_name : "(null)", retval->comment ? retval->comment : "(null)", retval->icon ? retval->icon : "(null)", retval->flags & DESKTOP_ENTRY_NO_DISPLAY ? "(true)" : "(false)", retval->flags & DESKTOP_ENTRY_HIDDEN ? "(true)" : "(false)", retval->flags & DESKTOP_ENTRY_SHOW_IN_MATE ? "(true)" : "(false)", retval->flags & DESKTOP_ENTRY_TRYEXEC_FAILED ? "(true)" : "(false)"); out: g_key_file_free (key_file); if (!retval) desktop_entry_unref (entry); return retval; }
static gboolean cached_dir_load_entries_recursive (CachedDir *dir, const char *dirname) { DIR *dp; struct dirent *dent; GString *fullpath; gsize fullpath_len; g_assert (dir != NULL); if (dir->have_read_entries) return TRUE; menu_verbose ("Attempting to read entries from %s (full path %s)\n", dir->name, dirname); dp = opendir (dirname); if (dp == NULL) { menu_verbose ("Unable to list directory \"%s\"\n", dirname); return FALSE; } cached_dir_ensure_monitor (dir, dirname); fullpath = g_string_new (dirname); if (fullpath->str[fullpath->len - 1] != G_DIR_SEPARATOR) g_string_append_c (fullpath, G_DIR_SEPARATOR); fullpath_len = fullpath->len; while ((dent = readdir (dp)) != NULL) { /* ignore . and .. */ if (dent->d_name[0] == '.' && (dent->d_name[1] == '\0' || (dent->d_name[1] == '.' && dent->d_name[2] == '\0'))) continue; g_string_append (fullpath, dent->d_name); if (g_str_has_suffix (dent->d_name, ".desktop") || g_str_has_suffix (dent->d_name, ".directory")) { cached_dir_add_entry (dir, dent->d_name, fullpath->str); } else /* Try recursing */ { cached_dir_add_subdir (dir, dent->d_name, fullpath->str); } g_string_truncate (fullpath, fullpath_len); } closedir (dp); g_string_free (fullpath, TRUE); dir->have_read_entries = TRUE; return TRUE; }
static void handle_cached_dir_changed (MenuMonitor *monitor, MenuMonitorEvent event, const char *path, CachedDir *dir) { gboolean handled = FALSE; char *basename; char *dirname; menu_verbose ("'%s' notified of '%s' %s - invalidating cache\n", dir->name, path, event == MENU_MONITOR_EVENT_CREATED ? ("created") : event == MENU_MONITOR_EVENT_DELETED ? ("deleted") : ("changed")); dirname = g_path_get_dirname (path); basename = g_path_get_basename (path); dir = cached_dir_lookup (dirname); if (g_str_has_suffix (basename, ".desktop") || g_str_has_suffix (basename, ".directory")) { switch (event) { case MENU_MONITOR_EVENT_CREATED: case MENU_MONITOR_EVENT_CHANGED: handled = cached_dir_update_entry (dir, basename, path); break; case MENU_MONITOR_EVENT_DELETED: handled = cached_dir_remove_entry (dir, basename); break; default: g_assert_not_reached (); break; } } else /* Try recursing */ { switch (event) { case MENU_MONITOR_EVENT_CREATED: handled = cached_dir_add_subdir (dir, basename, path) != NULL; break; case MENU_MONITOR_EVENT_CHANGED: break; case MENU_MONITOR_EVENT_DELETED: handled = cached_dir_remove_subdir (dir, basename); break; default: g_assert_not_reached (); break; } } g_free (basename); g_free (dirname); if (handled) { /* CHANGED events don't change the set of desktop entries */ if (event == MENU_MONITOR_EVENT_CREATED || event == MENU_MONITOR_EVENT_DELETED) { _entry_directory_list_empty_desktop_cache (); } cached_dir_queue_monitor_event (dir); } }
static DesktopEntryResultCode desktop_entry_load (DesktopEntry *entry) { if (strstr (entry->path, "/menu-xdg/")) return DESKTOP_ENTRY_LOAD_FAIL_OTHER; if (entry->type == DESKTOP_ENTRY_DESKTOP) { GKeyFile *key_file = NULL; DesktopEntryDesktop *entry_desktop = (DesktopEntryDesktop*)entry; const char *categories_str; entry_desktop->appinfo = g_desktop_app_info_new_from_filename (entry->path); if (!G_IS_DESKTOP_APP_INFO (((DesktopEntryDesktop*)entry)->appinfo)) { menu_verbose ("Failed to load \"%s\"\n", entry->path); return DESKTOP_ENTRY_LOAD_FAIL_APPINFO; } categories_str = g_desktop_app_info_get_categories (entry_desktop->appinfo); if (categories_str) { char **categories; int i; categories = g_strsplit (categories_str, ";", -1); entry_desktop->categories = g_new0 (GQuark, g_strv_length (categories) + 1); for (i = 0; categories[i]; i++) entry_desktop->categories[i] = g_quark_from_string (categories[i]); g_strfreev (categories); } key_file = g_key_file_new (); if (!g_key_file_load_from_file (key_file, entry->path, 0, NULL)) entry_desktop->showin = TRUE; else entry_desktop->showin = key_file_get_show_in (key_file); g_key_file_free (key_file); return DESKTOP_ENTRY_LOAD_SUCCESS; } else if (entry->type == DESKTOP_ENTRY_DIRECTORY) { GKeyFile *key_file = NULL; GError *error = NULL; DesktopEntryResultCode rescode = DESKTOP_ENTRY_LOAD_SUCCESS; key_file = g_key_file_new (); if (!g_key_file_load_from_file (key_file, entry->path, 0, &error)) { rescode = DESKTOP_ENTRY_LOAD_FAIL_OTHER; goto out; } if (!desktop_entry_load_directory (entry, key_file, &error)) { rescode = DESKTOP_ENTRY_LOAD_FAIL_OTHER; goto out; } rescode = DESKTOP_ENTRY_LOAD_SUCCESS; out: g_key_file_free (key_file); if (rescode == DESKTOP_ENTRY_LOAD_FAIL_OTHER) { if (error) { menu_verbose ("Failed to load \"%s\": %s\n", entry->path, error->message); g_error_free (error); } else menu_verbose ("Failed to load \"%s\"\n", entry->path); } return rescode; } else g_assert_not_reached (); return DESKTOP_ENTRY_LOAD_FAIL_OTHER; }
static void handle_cached_dir_changed (MenuMonitor *monitor, MenuMonitorEvent event, const char *path, CachedDir *dir) { gboolean handled = FALSE; gboolean retry_changes = FALSE; char *basename; char *dirname; dirname = g_path_get_dirname (path); basename = g_path_get_basename (path); dir = cached_dir_lookup (dirname); cached_dir_add_reference (dir); if (g_str_has_suffix (basename, ".desktop") || g_str_has_suffix (basename, ".directory")) { switch (event) { case MENU_MONITOR_EVENT_CREATED: case MENU_MONITOR_EVENT_CHANGED: handled = cached_dir_update_entry (dir, basename, path); break; case MENU_MONITOR_EVENT_DELETED: handled = cached_dir_remove_entry (dir, basename); break; default: g_assert_not_reached (); break; } } else if (g_strcmp0 (basename, "mimeinfo.cache") == 0) { /* The observed file notifies when a new desktop file is added * (but fails to load) go something like: * * NOTIFY: foo.desktop * NOTIFY: mimeinfo.cache.tempfile * NOTIFY: mimeinfo.cache.tempfile * NOTIFY: mimeinfo.cache * * Additionally, the failure is not upon trying to read the file, * but attempting to get its GAppInfo (g_desktop_app_info_new_from_filename() * in desktop-entries.c ln 277). If you jigger desktop_entry_load() around * and read the file as a keyfile *first*, it succeeds. If you then try * to run g_desktop_app_info_new_from_keyfile(), *then* it fails. * * The theory here is there is a race condition where app info (which includes * mimetype stuff) is unavailable because mimeinfo.cache is updated immediately * after the app is installed. * * What we do here is, when a desktop fails to load, we add it to a temporary * list. We wait until mimeinfo.cache changes, then retry that desktop file, * which succeeds this second time. * * Note: An alternative fix (presented more as a proof than a suggestion) is to * change line 151 in menu-monitor.c to use g_timeout_add_seconds, and delay * for one second. This also avoids the issue (but it remains a race condition). */ GSList *iter; menu_verbose ("mimeinfo changed, checking for failed entries\n"); for (iter = dir->retry_later_desktop_entries; iter != NULL; iter = iter->next) { const gchar *retry_path = iter->data; menu_verbose ("retrying %s\n", retry_path); char *retry_basename = g_path_get_basename (retry_path); if (cached_dir_update_entry (dir, retry_basename, retry_path)) retry_changes = TRUE; g_free (retry_basename); } g_slist_free_full (dir->retry_later_desktop_entries, g_free); dir->retry_later_desktop_entries = NULL; handled = retry_changes; } else /* Try recursing */ { switch (event) { case MENU_MONITOR_EVENT_CREATED: handled = cached_dir_add_subdir (dir, basename, path) != NULL; break; case MENU_MONITOR_EVENT_CHANGED: break; case MENU_MONITOR_EVENT_DELETED: handled = cached_dir_remove_subdir (dir, basename); break; default: g_assert_not_reached (); break; } } g_free (basename); g_free (dirname); if (handled) { menu_verbose ("'%s' notified of '%s' %s - invalidating cache\n", dir->name, path, event == MENU_MONITOR_EVENT_CREATED ? ("created") : event == MENU_MONITOR_EVENT_DELETED ? ("deleted") : ("changed")); /* CHANGED events don't change the set of desktop entries, unless it's the mimeinfo.cache file changing */ if (retry_changes || (event == MENU_MONITOR_EVENT_CREATED || event == MENU_MONITOR_EVENT_DELETED)) { _entry_directory_list_empty_desktop_cache (); } cached_dir_queue_monitor_event (dir); } cached_dir_remove_reference (dir); }