From deecdc7661046a97448328321fbc6ba3cbd8890e Mon Sep 17 00:00:00 2001 From: Eric Koegel Date: Sun, 10 May 2015 15:45:58 +0300 Subject: [PATCH] Load image list in the background (Bug #11817) When we monitor backdrop directories for changes we need to initially load the list of files in an async callback so it doesn't block the UI or slow down/halt xfdesktop's other tasks. --- settings/main.c | 4 ++ src/xfce-backdrop.c | 174 +++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 137 insertions(+), 41 deletions(-) diff --git a/settings/main.c b/settings/main.c index 312806a..e6106d7 100644 --- a/settings/main.c +++ b/settings/main.c @@ -683,6 +683,10 @@ xfdesktop_image_list_add_dir(GObject *source_object, TRACE("entering"); + /* If we failed to allocate memory, there's nothing we can do */ + if(dir_data == NULL) + return; + dir_data->panel = panel; dir_data->ls = gtk_list_store_new(N_COLS, GDK_TYPE_PIXBUF, G_TYPE_STRING, diff --git a/src/xfce-backdrop.c b/src/xfce-backdrop.c index af514be..d002dbe 100644 --- a/src/xfce-backdrop.c +++ b/src/xfce-backdrop.c @@ -102,10 +102,13 @@ struct _XfceBackdropPriv XfceBackdropImageStyle image_style; gchar *image_path; + GFile *image_dir_gfile; /* Cached list of images in the same folder as image_path */ GList *image_files; /* monitor for the image_files directory */ GFileMonitor *monitor; + GCancellable *cancel_enumeration; + guint add_dir_idle_id; gboolean cycle_backdrop; guint cycle_timer; @@ -125,6 +128,13 @@ struct _XfceBackdropImageData guchar *image_buffer; }; +typedef struct +{ + XfceBackdrop *backdrop; + GFileEnumerator *file_enumerator; + gchar *file_path; +} AddDirData; + enum { BACKDROP_CHANGED, @@ -336,40 +346,97 @@ cb_xfce_backdrop__image_files_changed(GFileMonitor *monitor, } } -/* Returns a GList of all the image files in the parent directory of filename */ -static GList * -list_image_files_in_dir(const gchar *filename) +static void +cb_destroy_add_dir_enumeration(gpointer user_data) { - GDir *dir; - gboolean needs_slash = TRUE; - const gchar *file; - GList *files = NULL; - gchar *dir_name; + AddDirData *dir_data = user_data; + XfceBackdrop *backdrop = dir_data->backdrop; - dir_name = g_path_get_dirname(filename); + TRACE("entering"); - dir = g_dir_open(dir_name, 0, 0); - if(!dir) { - g_free(dir_name); - return NULL; + g_free(dir_data->file_path); + + if(G_IS_FILE_ENUMERATOR(dir_data->file_enumerator)) + g_object_unref(dir_data->file_enumerator); + + g_free(dir_data); + + if(backdrop->priv->cancel_enumeration) { + g_object_unref(backdrop->priv->cancel_enumeration); + backdrop->priv->cancel_enumeration = NULL; } - if(dir_name[strlen(dir_name)-1] == '/') - needs_slash = FALSE; + backdrop->priv->add_dir_idle_id = 0; +} - while((file = g_dir_read_name(dir))) { - gchar *current_file = g_strdup_printf(needs_slash ? "%s/%s" : "%s%s", - dir_name, file); - if(xfdesktop_image_file_is_valid(current_file)) - files = g_list_insert_sorted(files, current_file, (GCompareFunc)compare_by_collate_key); - else +/* Adds an image file to the list (if the file is an image) */ +static gboolean +xfce_backdrop_add_image_to_list(gpointer user_data) +{ + AddDirData *dir_data = user_data; + XfceBackdrop *backdrop = dir_data->backdrop; + GFileInfo *info = NULL; + + /* If the enumeration gets canceled/destroyed return and + * cb_destroy_add_dir_enumeration will get called to clean up */ + if(!G_IS_FILE_ENUMERATOR(dir_data->file_enumerator)) + return FALSE; + + /* Add one item to the list at a time so we don't block the UI on the desktop */ + if((info = g_file_enumerator_next_file(dir_data->file_enumerator, NULL, NULL))) { + const gchar *file_name = g_file_info_get_name(info); + gchar *current_file = g_strconcat(dir_data->file_path, "/", file_name, NULL); + + if(xfdesktop_image_file_is_valid(current_file)) { + backdrop->priv->image_files = g_list_insert_sorted(backdrop->priv->image_files, + current_file, + (GCompareFunc)compare_by_collate_key); + } else { g_free(current_file); + } + + g_object_unref(info); + return TRUE; } - g_dir_close(dir); - g_free(dir_name); + return FALSE; +} + +static void +xfce_backdrop_finish_loading_files(GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + AddDirData *dir_data = g_new0(AddDirData, 1); + XfceBackdrop *backdrop = XFCE_BACKDROP(user_data); + + TRACE("entering"); + + g_return_if_fail(XFCE_IS_BACKDROP(user_data)); + + /* If we failed to allocate memory, there's nothing we can do */ + if(dir_data == NULL) + return; - return files; + dir_data->backdrop = backdrop; + dir_data->file_path = g_file_get_path(backdrop->priv->image_dir_gfile); + + dir_data->file_enumerator = g_file_enumerate_children_finish(backdrop->priv->image_dir_gfile, + res, + NULL); + + /* Individual items are added in an idle callback so everything is more + * responsive */ + backdrop->priv->add_dir_idle_id = g_idle_add_full(G_PRIORITY_LOW, + xfce_backdrop_add_image_to_list, + dir_data, + cb_destroy_add_dir_enumeration); + + /* Start monitoring the new directory for changes */ + backdrop->priv->monitor = g_file_monitor(backdrop->priv->image_dir_gfile, G_FILE_MONITOR_NONE, NULL, NULL); + g_signal_connect(backdrop->priv->monitor, "changed", + G_CALLBACK(cb_xfce_backdrop__image_files_changed), + backdrop); } static void @@ -380,11 +447,18 @@ xfce_backdrop_load_image_files(XfceBackdrop *backdrop) /* generate the image_files list if it doesn't exist and monitor that * directory so we can update the list */ if(backdrop->priv->image_files == NULL && backdrop->priv->image_path) { - gchar *dir_name = g_path_get_dirname(backdrop->priv->image_path); - GFile *gfile = g_file_new_for_path(dir_name); - - backdrop->priv->image_files = list_image_files_in_dir(backdrop->priv->image_path); - + /* load the files in an async callback because this can be very + * slow. Like a large number of files over a wifi NFS share. + */ + g_file_enumerate_children_async(backdrop->priv->image_dir_gfile, + XFDESKTOP_FILE_INFO_NAMESPACE, + G_FILE_QUERY_INFO_NONE, + G_PRIORITY_LOW, + backdrop->priv->cancel_enumeration, + xfce_backdrop_finish_loading_files, + backdrop); + + /* Remove the old file monitor */ if(backdrop->priv->monitor) { g_signal_handlers_disconnect_by_func(G_OBJECT(backdrop->priv->monitor), G_CALLBACK(cb_xfce_backdrop__image_files_changed), @@ -392,15 +466,6 @@ xfce_backdrop_load_image_files(XfceBackdrop *backdrop) g_object_unref(backdrop->priv->monitor); } - - /* monitor the new directory for changes */ - backdrop->priv->monitor = g_file_monitor(gfile, G_FILE_MONITOR_NONE, NULL, NULL); - g_signal_connect(backdrop->priv->monitor, "changed", - G_CALLBACK(cb_xfce_backdrop__image_files_changed), - backdrop); - - g_free(dir_name); - g_object_unref(gfile); } } @@ -1038,19 +1103,46 @@ xfce_backdrop_set_image_filename(XfceBackdrop *backdrop, const gchar *filename) g_object_unref(backdrop->priv->monitor); backdrop->priv->monitor = NULL; } + + /* Cancel any file enumeration that's running */ + if(backdrop->priv->cancel_enumeration != NULL) { + g_cancellable_cancel(backdrop->priv->cancel_enumeration); + g_object_unref(backdrop->priv->cancel_enumeration); + backdrop->priv->cancel_enumeration = NULL; + } + + /* Cancel the file enumeration for populating file list */ + if(backdrop->priv->add_dir_idle_id != 0) { + g_source_remove(backdrop->priv->add_dir_idle_id); + backdrop->priv->add_dir_idle_id = 0; + } + } g_free(old_dir); g_free(new_dir); } - /* Now we can free the old path and setup the new one */ + /* Now we can free the old path/gfile and setup the new one */ g_free(backdrop->priv->image_path); + + if(backdrop->priv->image_dir_gfile) + g_object_unref(backdrop->priv->image_dir_gfile); - if(filename) + if(filename) { + gchar *new_dir; + + /* copy/assign the new image path */ backdrop->priv->image_path = g_strdup(filename); - else + + /* Create the new image directory gfile */ + new_dir = g_path_get_dirname(filename); + backdrop->priv->image_dir_gfile = g_file_new_for_path(new_dir); + g_free(new_dir); + } else { backdrop->priv->image_path = NULL; + backdrop->priv->image_dir_gfile = NULL; + } xfce_backdrop_clear_cached_image(backdrop); -- 2.4.0