diff --git a/configure.ac b/configure.ac index a7042df..72104d5 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ AC_PREREQ(2.62) -AC_INIT([cinnamon-menus], [3.0.0]) +AC_INIT([cinnamon-menus], [3.0.1]) AC_CONFIG_SRCDIR(libmenu/gmenu-tree.h) AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz]) diff --git a/libmenu/desktop-entries.c b/libmenu/desktop-entries.c index 57cba7c..2c8efac 100644 --- a/libmenu/desktop-entries.c +++ b/libmenu/desktop-entries.c @@ -263,72 +263,89 @@ return TRUE; } -static gboolean +static DesktopEntryResultCode desktop_entry_load (DesktopEntry *entry) { + DesktopEntryResultCode rescode = DESKTOP_ENTRY_LOAD_FAIL_OTHER; + if (strstr (entry->path, "/menu-xdg/")) - return FALSE; + return rescode; + 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 (!entry_desktop->appinfo || - !g_app_info_get_name (G_APP_INFO (entry_desktop->appinfo)) || - !g_app_info_get_executable (G_APP_INFO (entry_desktop->appinfo))) + + key_file = g_key_file_new (); + + if (g_key_file_load_from_file (key_file, entry->path, 0, NULL)) { - menu_verbose ("Failed to load \"%s\"\n", entry->path); - return FALSE; + entry_desktop->appinfo = g_desktop_app_info_new_from_keyfile (key_file); + + if (!entry_desktop->appinfo || + !g_app_info_get_name (G_APP_INFO (entry_desktop->appinfo)) || + !g_app_info_get_executable (G_APP_INFO (entry_desktop->appinfo))) + { + menu_verbose ("Failed to load appinfo for \"%s\"\n", entry->path); + rescode = DESKTOP_ENTRY_LOAD_FAIL_APPINFO; + } + else + { + const char *categories_str; + 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); + } + + entry_desktop->showin = key_file_get_show_in (key_file); + + rescode = DESKTOP_ENTRY_LOAD_SUCCESS; + } } - - categories_str = g_desktop_app_info_get_categories (entry_desktop->appinfo); - if (categories_str) + else { - 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); + menu_verbose ("Failed to read contents of \"%s\"\n", entry->path); + rescode = DESKTOP_ENTRY_LOAD_FAIL_OTHER; } - - 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 TRUE; } else if (entry->type == DESKTOP_ENTRY_DIRECTORY) { GKeyFile *key_file = NULL; GError *error = NULL; - gboolean retval = FALSE; + rescode = DESKTOP_ENTRY_LOAD_SUCCESS; key_file = g_key_file_new (); if (!g_key_file_load_from_file (key_file, entry->path, 0, &error)) - goto out; + { + rescode = DESKTOP_ENTRY_LOAD_FAIL_OTHER; + goto out; + } if (!desktop_entry_load_directory (entry, key_file, &error)) - goto out; - - retval = TRUE; + { + rescode = DESKTOP_ENTRY_LOAD_FAIL_OTHER; + goto out; + } + + rescode = DESKTOP_ENTRY_LOAD_SUCCESS; out: g_key_file_free (key_file); - if (!retval) + if (rescode == DESKTOP_ENTRY_LOAD_FAIL_OTHER) { if (error) { @@ -338,20 +355,27 @@ else menu_verbose ("Failed to load \"%s\"\n", entry->path); } - - return retval; } else g_assert_not_reached (); - return FALSE; + return rescode; +} + +static gboolean +code_failed (DesktopEntryResultCode code) +{ + return code == DESKTOP_ENTRY_LOAD_FAIL_OTHER || + code == DESKTOP_ENTRY_LOAD_FAIL_APPINFO; } DesktopEntry * -desktop_entry_new (const char *path) +desktop_entry_new (const char *path, + DesktopEntryResultCode *res_code) { DesktopEntryType type; DesktopEntry *retval; + DesktopEntryResultCode code; menu_verbose ("Loading desktop entry \"%s\"\n", path); @@ -369,6 +393,7 @@ { menu_verbose ("Unknown desktop entry suffix in \"%s\"\n", path); + *res_code = DESKTOP_ENTRY_LOAD_FAIL_OTHER; return NULL; } @@ -377,7 +402,10 @@ retval->path = g_strdup (path); retval->basename = unix_basename_from_path (retval->path); - if (!desktop_entry_load (retval)) + code = desktop_entry_load (retval); + *res_code = code; + + if (code_failed (code)) { desktop_entry_unref (retval); return NULL; @@ -419,7 +447,7 @@ else g_assert_not_reached (); - if (!desktop_entry_load (entry)) + if (code_failed (desktop_entry_load (entry))) { desktop_entry_unref (entry); return NULL; diff --git a/libmenu/desktop-entries.h b/libmenu/desktop-entries.h index 07b7c85..7684faa 100644 --- a/libmenu/desktop-entries.h +++ b/libmenu/desktop-entries.h @@ -31,9 +31,17 @@ DESKTOP_ENTRY_DIRECTORY } DesktopEntryType; +typedef enum +{ + DESKTOP_ENTRY_LOAD_SUCCESS = 0, + DESKTOP_ENTRY_LOAD_FAIL_OTHER, + DESKTOP_ENTRY_LOAD_FAIL_APPINFO +} DesktopEntryResultCode; + typedef struct DesktopEntry DesktopEntry; -DesktopEntry *desktop_entry_new (const char *path); +DesktopEntry *desktop_entry_new (const char *path, + DesktopEntryResultCode *res_code); DesktopEntry *desktop_entry_ref (DesktopEntry *entry); DesktopEntry *desktop_entry_copy (DesktopEntry *entry); diff --git a/libmenu/entry-directories.c b/libmenu/entry-directories.c index 67869b4..3dba079 100644 --- a/libmenu/entry-directories.c +++ b/libmenu/entry-directories.c @@ -57,6 +57,7 @@ GSList *entries; GSList *subdirs; + GSList *retry_later_desktop_entries; MenuMonitor *dir_monitor; GSList *monitors; @@ -161,6 +162,9 @@ g_slist_free (dir->subdirs); dir->subdirs = NULL; + g_slist_free_full (dir->retry_later_desktop_entries, g_free); + dir->retry_later_desktop_entries = NULL; + g_free (dir->name); g_free (dir); } @@ -314,10 +318,20 @@ const char *path) { DesktopEntry *entry; - - entry = desktop_entry_new (path); + DesktopEntryResultCode code; + + entry = desktop_entry_new (path, &code); if (entry == NULL) - return FALSE; + { + 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); @@ -525,6 +539,8 @@ CachedDir *dir) { gboolean handled = FALSE; + gboolean retry_changes = FALSE; + char *basename; char *dirname; @@ -558,6 +574,58 @@ 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) @@ -584,8 +652,8 @@ if (handled) { - /* CHANGED events don't change the set of desktop entries */ - if (event == MENU_MONITOR_EVENT_CREATED || event == MENU_MONITOR_EVENT_DELETED) + /* 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 (); }