entry-directories.c: Monitor mimeinfo.cache file and re-attempt
failed .desktop files when it changes (which is usually just after
the .desktop file is added, causing loading to fail due to
unavailable GAppInfo). See inline comments.
Michael Webster
7 years ago
56 | 56 | |
57 | 57 | GSList *entries; |
58 | 58 | GSList *subdirs; |
59 | GSList *retry_later_desktop_entries; | |
59 | 60 | |
60 | 61 | MenuMonitor *dir_monitor; |
61 | 62 | GSList *monitors; |
160 | 161 | g_slist_free (dir->subdirs); |
161 | 162 | dir->subdirs = NULL; |
162 | 163 | |
164 | g_slist_free_full (dir->retry_later_desktop_entries, g_free); | |
165 | dir->retry_later_desktop_entries = NULL; | |
166 | ||
163 | 167 | g_free (dir->name); |
164 | 168 | g_free (dir); |
165 | 169 | } |
316 | 320 | |
317 | 321 | entry = desktop_entry_new (path); |
318 | 322 | if (entry == NULL) |
319 | return FALSE; | |
323 | { | |
324 | menu_verbose ("Adding %s to the retry list (mimeinfo.cache maybe isn't done getting updated yet\n", path); | |
325 | ||
326 | dir->retry_later_desktop_entries = g_slist_prepend (dir->retry_later_desktop_entries, g_strdup (path)); | |
327 | return FALSE; | |
328 | } | |
320 | 329 | |
321 | 330 | dir->entries = g_slist_prepend (dir->entries, entry); |
322 | 331 | |
524 | 533 | CachedDir *dir) |
525 | 534 | { |
526 | 535 | gboolean handled = FALSE; |
536 | gboolean retry_changes = FALSE; | |
537 | ||
527 | 538 | char *basename; |
528 | 539 | char *dirname; |
529 | 540 | |
557 | 568 | break; |
558 | 569 | } |
559 | 570 | } |
571 | else if (g_strcmp0 (basename, "mimeinfo.cache") == 0) | |
572 | { | |
573 | /* The observed file notifies when a new desktop file is added | |
574 | * (but fails to load) go something like: | |
575 | * | |
576 | * NOTIFY: foo.desktop | |
577 | * NOTIFY: mimeinfo.cache.tempfile | |
578 | * NOTIFY: mimeinfo.cache.tempfile | |
579 | * NOTIFY: mimeinfo.cache | |
580 | * | |
581 | * Additionally, the failure is not upon trying to read the file, | |
582 | * but attempting to get its GAppInfo (g_desktop_app_info_new_from_filename() | |
583 | * in desktop-entries.c ln 277). If you jigger desktop_entry_load() around | |
584 | * and read the file as a keyfile *first*, it succeeds. If you then try | |
585 | * to run g_desktop_app_info_new_from_keyfile(), *then* it fails. | |
586 | * | |
587 | * The theory here is there is a race condition where app info (which includes | |
588 | * mimetype stuff) is unavailable because mimeinfo.cache is updated immediately | |
589 | * after the app is installed. | |
590 | * | |
591 | * What we do here is, when a desktop fails to load, we add it to a temporary | |
592 | * list. We wait until mimeinfo.cache changes, then retry that desktop file, | |
593 | * which succeeds this second time. | |
594 | * | |
595 | * Note: An alternative fix (presented more as a proof than a suggestion) is to | |
596 | * change line 151 in menu-monitor.c to use g_timeout_add_seconds, and delay | |
597 | * for one second. This also avoids the issue (but it remains a race condition). | |
598 | */ | |
599 | ||
600 | GSList *iter; | |
601 | ||
602 | menu_verbose ("mimeinfo changed, checking for failed entries\n"); | |
603 | ||
604 | for (iter = dir->retry_later_desktop_entries; iter != NULL; iter = iter->next) | |
605 | { | |
606 | const gchar *retry_path = iter->data; | |
607 | ||
608 | menu_verbose ("retrying %s\n", retry_path); | |
609 | ||
610 | char *retry_basename = g_path_get_basename (retry_path); | |
611 | ||
612 | if (cached_dir_update_entry (dir, retry_basename, retry_path)) | |
613 | retry_changes = TRUE; | |
614 | ||
615 | g_free (retry_basename); | |
616 | } | |
617 | ||
618 | g_slist_free_full (dir->retry_later_desktop_entries, g_free); | |
619 | dir->retry_later_desktop_entries = NULL; | |
620 | ||
621 | handled = retry_changes; | |
622 | } | |
560 | 623 | else /* Try recursing */ |
561 | 624 | { |
562 | 625 | switch (event) |
583 | 646 | |
584 | 647 | if (handled) |
585 | 648 | { |
586 | /* CHANGED events don't change the set of desktop entries */ | |
587 | if (event == MENU_MONITOR_EVENT_CREATED || event == MENU_MONITOR_EVENT_DELETED) | |
649 | /* CHANGED events don't change the set of desktop entries, unless it's the mimeinfo.cache file changing */ | |
650 | if (retry_changes || (event == MENU_MONITOR_EVENT_CREATED || event == MENU_MONITOR_EVENT_DELETED)) | |
588 | 651 | { |
589 | 652 | _entry_directory_list_empty_desktop_cache (); |
590 | 653 | } |