summaryrefslogtreecommitdiff
path: root/libmenu/entry-directories.c
diff options
context:
space:
mode:
Diffstat (limited to 'libmenu/entry-directories.c')
-rw-r--r--libmenu/entry-directories.c248
1 files changed, 184 insertions, 64 deletions
diff --git a/libmenu/entry-directories.c b/libmenu/entry-directories.c
index 2b59b37..2a21807 100644
--- a/libmenu/entry-directories.c
+++ b/libmenu/entry-directories.c
@@ -61,7 +61,10 @@ struct CachedDir {
guint have_read_entries: 1;
guint deleted: 1;
- guint references: 28;
+ guint references;
+
+ GFunc notify;
+ gpointer notify_data;
};
struct CachedDirMonitor {
@@ -70,10 +73,23 @@ struct CachedDirMonitor {
gpointer user_data;
};
-static void cached_dir_free(CachedDir* dir);
-static gboolean cached_dir_load_entries_recursive(CachedDir* dir, const char* dirname);
-
-static void handle_cached_dir_changed(MenuMonitor* monitor, MenuMonitorEvent event, const char* path, CachedDir* dir);
+static void cached_dir_add_reference (CachedDir *dir);
+static void cached_dir_remove_reference (CachedDir *dir);
+static void cached_dir_free (CachedDir *dir);
+static gboolean cached_dir_load_entries_recursive (CachedDir *dir,
+ const char *dirname);
+static void cached_dir_unref (CachedDir *dir);
+static void cached_dir_unref_noparent (CachedDir *dir);
+static CachedDir * cached_dir_add_subdir (CachedDir *dir,
+ const char *basename,
+ const char *path);
+static gboolean cached_dir_remove_subdir (CachedDir *dir,
+ const char *basename);
+
+static void handle_cached_dir_changed (MenuMonitor *monitor,
+ MenuMonitorEvent event,
+ const char *path,
+ CachedDir *dir);
/*
* Entry directory cache
@@ -81,18 +97,41 @@ static void handle_cached_dir_changed(MenuMonitor* monitor, MenuMonitorEvent eve
static CachedDir* dir_cache = NULL;
-static CachedDir* cached_dir_new(const char *name)
+static void
+clear_cache (CachedDir *dir,
+ gpointer *cache)
{
- CachedDir* dir;
+ *cache = NULL;
+}
- dir = g_new0(CachedDir, 1);
+static CachedDir *
+cached_dir_new (const char *name)
+{
+ CachedDir* dir;
- dir->name = g_strdup(name);
+ dir = g_new0 (CachedDir, 1);
+ dir->name = g_strdup (name);
return dir;
}
-static void cached_dir_free(CachedDir* dir)
+static CachedDir *
+cached_dir_new_full (const char *name,
+ GFunc notify,
+ gpointer notify_data)
+{
+ CachedDir *dir;
+
+ dir = cached_dir_new (name);
+
+ dir->notify = notify;
+ dir->notify_data = notify_data;
+
+ return dir;
+}
+
+static void
+cached_dir_free (CachedDir *dir)
{
if (dir->dir_monitor)
{
@@ -114,7 +153,7 @@ static void cached_dir_free(CachedDir* dir)
dir->entries = NULL;
g_slist_foreach (dir->subdirs,
- (GFunc) cached_dir_free,
+ (GFunc) cached_dir_unref_noparent,
NULL);
g_slist_free (dir->subdirs);
dir->subdirs = NULL;
@@ -123,6 +162,44 @@ static void cached_dir_free(CachedDir* dir)
g_free (dir);
}
+static CachedDir *
+cached_dir_ref (CachedDir *dir)
+{
+ dir->references++;
+ return dir;
+}
+
+static void
+cached_dir_unref (CachedDir *dir)
+{
+ if (--dir->references == 0)
+ {
+ CachedDir *parent;
+
+ parent = dir->parent;
+
+ if (parent != NULL)
+ cached_dir_remove_subdir (parent, dir->name);
+
+ if (dir->notify)
+ dir->notify (dir, dir->notify_data);
+
+ cached_dir_free (dir);
+ }
+}
+
+static void
+cached_dir_unref_noparent (CachedDir *dir)
+{
+ if (--dir->references == 0)
+ {
+ if (dir->notify)
+ dir->notify (dir, dir->notify_data);
+
+ cached_dir_free (dir);
+ }
+}
+
static inline CachedDir* find_subdir(CachedDir* dir, const char* subdir)
{
GSList *tmp;
@@ -194,7 +271,9 @@ static CachedDir* cached_dir_lookup(const char* canonical)
int i;
if (dir_cache == NULL)
- dir_cache = cached_dir_new ("/");
+ dir_cache = cached_dir_new_full ("/",
+ (GFunc) clear_cache,
+ &dir_cache);
dir = dir_cache;
g_assert (canonical != NULL && canonical[0] == G_DIR_SEPARATOR);
@@ -208,12 +287,7 @@ static CachedDir* cached_dir_lookup(const char* canonical)
{
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;
- }
+ subdir = cached_dir_add_subdir (dir, split[i], NULL);
dir = subdir;
@@ -283,7 +357,10 @@ static gboolean cached_dir_remove_entry(CachedDir* dir, const char* basename)
return FALSE;
}
-static gboolean cached_dir_add_subdir(CachedDir* dir, const char* basename, const char* path)
+static CachedDir *
+cached_dir_add_subdir (CachedDir *dir,
+ const char *basename,
+ const char *path)
{
CachedDir *subdir;
@@ -292,23 +369,23 @@ static gboolean cached_dir_add_subdir(CachedDir* dir, const char* basename, cons
if (subdir != NULL)
{
subdir->deleted = FALSE;
- return TRUE;
+ return subdir;
}
subdir = cached_dir_new (basename);
- if (!cached_dir_load_entries_recursive (subdir, path))
+ if (path != NULL && !cached_dir_load_entries_recursive (subdir, path))
{
cached_dir_free (subdir);
- return FALSE;
+ return NULL;
}
menu_verbose ("Caching dir \"%s\"\n", basename);
subdir->parent = dir;
- dir->subdirs = g_slist_prepend (dir->subdirs, subdir);
+ dir->subdirs = g_slist_prepend (dir->subdirs, cached_dir_ref (subdir));
- return TRUE;
+ return subdir;
}
static gboolean cached_dir_remove_subdir(CachedDir* dir, const char* basename)
@@ -321,11 +398,8 @@ static gboolean cached_dir_remove_subdir(CachedDir* dir, const char* basename)
{
subdir->deleted = TRUE;
- if (subdir->references == 0)
- {
- cached_dir_free (subdir);
- dir->subdirs = g_slist_remove (dir->subdirs, subdir);
- }
+ cached_dir_unref (subdir);
+ dir->subdirs = g_slist_remove (dir->subdirs, subdir);
return TRUE;
}
@@ -333,7 +407,11 @@ static gboolean cached_dir_remove_subdir(CachedDir* dir, const char* basename)
return FALSE;
}
-static void cached_dir_invoke_monitors(CachedDir* dir)
+static guint monitors_idle_handler = 0;
+static GSList *pending_monitors_dirs = NULL;
+
+static void
+cached_dir_invoke_monitors (CachedDir *dir)
{
GSList *tmp;
@@ -348,9 +426,70 @@ static void cached_dir_invoke_monitors(CachedDir* dir)
tmp = next;
}
+ /* we explicitly don't invoke monitors of the parent since an
+ * event has been queued for it too */
+}
+
+static gboolean
+emit_monitors_in_idle (void)
+{
+ GSList *monitors_to_emit;
+ GSList *tmp;
+
+ monitors_to_emit = pending_monitors_dirs;
+
+ pending_monitors_dirs = NULL;
+ monitors_idle_handler = 0;
+
+ tmp = monitors_to_emit;
+ while (tmp != NULL)
+ {
+ CachedDir *dir = tmp->data;
+
+ cached_dir_invoke_monitors (dir);
+ cached_dir_remove_reference (dir);
+
+ tmp = tmp->next;
+ }
+
+ g_slist_free (monitors_to_emit);
+
+ return FALSE;
+}
+
+static void
+cached_dir_queue_monitor_event (CachedDir *dir)
+{
+ GSList *tmp;
+
+ tmp = pending_monitors_dirs;
+ while (tmp != NULL)
+ {
+ CachedDir *d = tmp->data;
+ GSList *next = tmp->next;
+
+ if (dir->parent == d->parent &&
+ g_strcmp0 (dir->name, d->name) == 0)
+ break;
+
+ tmp = next;
+ }
+
+ /* not found, so let's queue it */
+ if (tmp == NULL)
+ {
+ cached_dir_add_reference (dir);
+ pending_monitors_dirs = g_slist_append (pending_monitors_dirs, dir);
+ }
+
if (dir->parent)
{
- cached_dir_invoke_monitors (dir->parent);
+ cached_dir_queue_monitor_event (dir->parent);
+ }
+
+ if (monitors_idle_handler == 0)
+ {
+ monitors_idle_handler = g_idle_add ((GSourceFunc) emit_monitors_in_idle, NULL);
}
}
@@ -360,16 +499,11 @@ static void handle_cached_dir_changed (MenuMonitor* monitor, MenuMonitorEvent ev
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);
+ cached_dir_add_reference (dir);
if (g_str_has_suffix (basename, ".desktop") ||
g_str_has_suffix (basename, ".directory"))
@@ -390,12 +524,12 @@ static void handle_cached_dir_changed (MenuMonitor* monitor, MenuMonitorEvent ev
break;
}
}
- else /* Try recursing */
+ else if (g_file_test (path, G_FILE_TEST_IS_DIR)) /* Try recursing */
{
switch (event)
{
case MENU_MONITOR_EVENT_CREATED:
- handled = cached_dir_add_subdir (dir, basename, path);
+ handled = cached_dir_add_subdir (dir, basename, path) != NULL;
break;
case MENU_MONITOR_EVENT_CHANGED:
@@ -416,14 +550,22 @@ static void handle_cached_dir_changed (MenuMonitor* monitor, MenuMonitorEvent ev
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 */
if (event == MENU_MONITOR_EVENT_CREATED || event == MENU_MONITOR_EVENT_DELETED)
{
_entry_directory_list_empty_desktop_cache ();
}
- cached_dir_invoke_monitors (dir);
+ cached_dir_queue_monitor_event (dir);
}
+
+ cached_dir_remove_reference (dir);
}
static void cached_dir_ensure_monitor(CachedDir* dir, const char* dirname)
@@ -554,7 +696,7 @@ static void cached_dir_remove_monitor(CachedDir* dir, EntryDirectory* ed, EntryD
static void cached_dir_add_reference(CachedDir* dir)
{
- dir->references++;
+ cached_dir_ref (dir);
if (dir->parent != NULL)
{
@@ -568,29 +710,7 @@ static void cached_dir_remove_reference(CachedDir* dir)
parent = dir->parent;
- if (--dir->references == 0 && dir->deleted)
- {
- if (dir->parent != NULL)
- {
- GSList *tmp;
-
- tmp = parent->subdirs;
- while (tmp != NULL)
- {
- CachedDir *subdir = tmp->data;
-
- if (!strcmp (subdir->name, dir->name))
- {
- parent->subdirs = g_slist_delete_link (parent->subdirs, tmp);
- break;
- }
-
- tmp = tmp->next;
- }
- }
-
- cached_dir_free (dir);
- }
+ cached_dir_unref (dir);
if (parent != NULL)
{