From 71b3b542150d46227802aaf86c7aef831eb4345d Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Tue, 28 Jan 2020 13:47:59 +0100 Subject: [PATCH] icon-theme: Add async icon loader --- gtk/gtkicontheme.c | 274 ++++++++++++++++++++++++++++++++++++++------- gtk/gtkicontheme.h | 13 +++ 2 files changed, 249 insertions(+), 38 deletions(-) diff --git a/gtk/gtkicontheme.c b/gtk/gtkicontheme.c index 46e67733e1..1e1754e013 100644 --- a/gtk/gtkicontheme.c +++ b/gtk/gtkicontheme.c @@ -1621,34 +1621,42 @@ load_themes (GtkIconTheme *self) }); } -static void -ensure_valid_themes (GtkIconTheme *self) +static gboolean +ensure_valid_themes (GtkIconTheme *self, gboolean non_blocking) { GTimeVal tv; gboolean was_valid = self->themes_valid; gint64 before = g_get_monotonic_time (); if (self->loading_themes) - return; + return TRUE; self->loading_themes = TRUE; if (self->themes_valid) { g_get_current_time (&tv); - if (ABS (tv.tv_sec - self->last_stat_time) > 5 && - rescan_themes (self)) + if (ABS (tv.tv_sec - self->last_stat_time) > 5) { - G_LOCK(info_cache); - g_hash_table_remove_all (self->info_cache); - G_UNLOCK(info_cache); - blow_themes (self); - clear_lru_cache (self); + if (non_blocking) + return FALSE; + + if (rescan_themes (self)) + { + G_LOCK(info_cache); + g_hash_table_remove_all (self->info_cache); + G_UNLOCK(info_cache); + blow_themes (self); + clear_lru_cache (self); + } } } if (!self->themes_valid) { + if (non_blocking) + return FALSE; + load_themes (self); if (was_valid) @@ -1659,6 +1667,7 @@ ensure_valid_themes (GtkIconTheme *self) gdk_profiler_add_mark (before * 1000, (g_get_monotonic_time () - before) * 1000, "icon theme load", NULL); self->loading_themes = FALSE; + return TRUE; } static inline gboolean @@ -1714,7 +1723,9 @@ real_choose_icon (GtkIconTheme *self, const gchar *icon_names[], gint size, gint scale, - GtkIconLookupFlags flags) + GtkIconLookupFlags flags, + gboolean non_blocking, + gboolean *would_block) { GList *l; GtkIconInfo *icon_info = NULL; @@ -1726,7 +1737,11 @@ real_choose_icon (GtkIconTheme *self, gint i; IconInfoKey key; - ensure_valid_themes (self); + if (!ensure_valid_themes (self, non_blocking)) + { + *would_block = TRUE; + return NULL; + } key.icon_names = (gchar **)icon_names; key.size = size; @@ -1951,7 +1966,9 @@ choose_icon (GtkIconTheme *self, const gchar *icon_names[], gint size, gint scale, - GtkIconLookupFlags flags) + GtkIconLookupFlags flags, + gboolean non_blocking, + gboolean *would_block) { gboolean has_regular = FALSE, has_symbolic = FALSE; GtkIconInfo *icon_info; @@ -1995,7 +2012,8 @@ choose_icon (GtkIconTheme *self, (const gchar **) new_names->pdata, size, scale, - flags & ~(GTK_ICON_LOOKUP_FORCE_REGULAR | GTK_ICON_LOOKUP_FORCE_SYMBOLIC)); + flags & ~(GTK_ICON_LOOKUP_FORCE_REGULAR | GTK_ICON_LOOKUP_FORCE_SYMBOLIC), + non_blocking, would_block); g_ptr_array_free (new_names, TRUE); } @@ -2020,7 +2038,8 @@ choose_icon (GtkIconTheme *self, (const gchar **) new_names->pdata, size, scale, - flags & ~(GTK_ICON_LOOKUP_FORCE_REGULAR | GTK_ICON_LOOKUP_FORCE_SYMBOLIC)); + flags & ~(GTK_ICON_LOOKUP_FORCE_REGULAR | GTK_ICON_LOOKUP_FORCE_SYMBOLIC), + non_blocking, would_block); g_ptr_array_free (new_names, TRUE); } @@ -2037,7 +2056,8 @@ choose_icon (GtkIconTheme *self, (const gchar **) new_names->pdata, size, scale, - flags & ~(GTK_ICON_LOOKUP_FORCE_REGULAR | GTK_ICON_LOOKUP_FORCE_SYMBOLIC)); + flags & ~(GTK_ICON_LOOKUP_FORCE_REGULAR | GTK_ICON_LOOKUP_FORCE_SYMBOLIC), + non_blocking, would_block); g_ptr_array_free (new_names, TRUE); } @@ -2047,7 +2067,8 @@ choose_icon (GtkIconTheme *self, icon_names, size, scale, - flags & ~(GTK_ICON_LOOKUP_FORCE_REGULAR | GTK_ICON_LOOKUP_FORCE_SYMBOLIC)); + flags & ~(GTK_ICON_LOOKUP_FORCE_REGULAR | GTK_ICON_LOOKUP_FORCE_SYMBOLIC), + non_blocking, would_block); } return icon_info; @@ -2185,7 +2206,7 @@ gtk_icon_theme_lookup_icon_for_scale (GtkIconTheme *self, names = nonsymbolic_names; } - info = choose_icon (self, (const gchar **) names, size, scale, flags); + info = choose_icon (self, (const gchar **) names, size, scale, flags, FALSE, NULL); g_strfreev (names); } @@ -2196,7 +2217,7 @@ gtk_icon_theme_lookup_icon_for_scale (GtkIconTheme *self, names[0] = icon_name; names[1] = NULL; - info = choose_icon (self, names, size, scale, flags); + info = choose_icon (self, names, size, scale, flags, FALSE, NULL); } gtk_icon_theme_unlock (self); @@ -2245,7 +2266,7 @@ gtk_icon_theme_choose_icon (GtkIconTheme *self, gtk_icon_theme_lock (self); - info = choose_icon (self, icon_names, size, 1, flags); + info = choose_icon (self, icon_names, size, 1, flags, FALSE, NULL); gtk_icon_theme_unlock (self); @@ -2296,13 +2317,204 @@ gtk_icon_theme_choose_icon_for_scale (GtkIconTheme *self, gtk_icon_theme_lock (self); - info = choose_icon (self, icon_names, size, scale, flags); + info = choose_icon (self, icon_names, size, scale, flags, FALSE, NULL); gtk_icon_theme_unlock (self); return info; } +typedef struct { + char **icon_names; + int size; + int scale; + GtkIconLookupFlags flags; +} ChooseIconData; + +static ChooseIconData * +choose_icon_data_new (const char *icon_names[], + int size, + int scale, + GtkIconLookupFlags flags) +{ + ChooseIconData *data = g_new0 (ChooseIconData, 1); + data->icon_names = g_strdupv ((char **)icon_names); + data->size = size; + data->scale = scale; + data->flags = flags; + return data; +} + +static void +choose_icon_data_free (ChooseIconData *data) +{ + g_strfreev (data->icon_names); + g_free (data); +} + +static void +choose_icon_thread (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + ChooseIconData *data = task_data; + GtkIconTheme *self = GTK_ICON_THEME (source_object); + GtkIconInfo *icon; + + icon = gtk_icon_theme_choose_icon_for_scale (self, + (const char **)data->icon_names, + data->size, + data->scale, + data->flags); + + if (icon) + { + g_mutex_lock (&icon->cache_lock); + (void)icon_info_ensure_scale_and_texture__locked (icon); + + if (icon->texture) + g_task_return_pointer (task, g_object_ref (icon), g_object_unref); + else if (icon->load_error) + g_task_return_error (task, g_error_copy (icon->load_error)); + else + g_task_return_new_error (task, + GTK_ICON_THEME_ERROR, GTK_ICON_THEME_NOT_FOUND, + _("Icon not present in theme %s"), self->current_theme); + + g_mutex_unlock (&icon->cache_lock); + } + else + g_task_return_new_error (task, + GTK_ICON_THEME_ERROR, GTK_ICON_THEME_NOT_FOUND, + _("Icon not present in theme %s"), self->current_theme); +} + +static void +load_icon_thread (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + GtkIconInfo *icon = task_data; + + g_mutex_lock (&icon->cache_lock); + (void)icon_info_ensure_scale_and_texture__locked (icon); + g_mutex_unlock (&icon->cache_lock); + g_task_return_pointer (task, g_object_ref (icon), g_object_unref); +} + +/** + * gtk_icon_theme_choose_icon_async: + * @self: a #GtkIconTheme + * @icon_names: (array zero-terminated=1): %NULL-terminated array of + * icon names to lookup + * @size: desired icon size + * @scale: desired scale + * @flags: flags modifying the behavior of the icon lookup + * @cancellable: (allow-none): optional #GCancellable object, %NULL to ignore + * @callback: (scope async): a #GAsyncReadyCallback to call when the + * request is satisfied + * @user_data: (closure): the data to pass to callback function + * + * Asynchronously lookup, load, render and scale an icon . + * + * For more details, see gtk_icon_theme_choose_icon_for_scale() which is the synchronous + * version of this call. + * + * Returns: (nullable) (transfer full): a #GtkIconInfo object + * containing information about the icon, or %NULL if the + * icon wasn’t found. + */ +void +gtk_icon_theme_choose_icon_async (GtkIconTheme *self, + const gchar *icon_names[], + gint size, + gint scale, + GtkIconLookupFlags flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + GtkIconInfo *icon; + gboolean would_block = FALSE; + + g_return_if_fail (GTK_IS_ICON_THEME (self)); + g_return_if_fail (icon_names != NULL); + g_return_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 || + (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0); + g_warn_if_fail ((flags & GTK_ICON_LOOKUP_GENERIC_FALLBACK) == 0); + + task = g_task_new (self, cancellable, callback, user_data); + + gtk_icon_theme_lock (self); + + icon = choose_icon (self, icon_names, size, 1, flags, TRUE, &would_block); + if (icon == NULL) + { + if (would_block) + { + /* Don't have valid theme data, do everything in a thread */ + g_task_set_task_data (task, choose_icon_data_new (icon_names, size, scale, flags), (GDestroyNotify)choose_icon_data_free); + g_task_run_in_thread (task, choose_icon_thread); + } + else + { + g_task_return_new_error (task, + GTK_ICON_THEME_ERROR, GTK_ICON_THEME_NOT_FOUND, + _("Icon not present in theme %s"), self->current_theme); + } + } + + gtk_icon_theme_unlock (self); + + if (icon) + { + g_mutex_lock (&icon->cache_lock); + + if (icon->texture) + g_task_return_pointer (task, icon, g_object_unref); + else if (icon->load_error) + { + g_task_return_error (task, g_error_copy (icon->load_error)); + g_object_unref (icon); + } + else + { + /* Not here, load it in a thread */ + g_task_set_task_data (task, icon, g_object_unref); + g_task_run_in_thread (task, load_icon_thread); + } + g_mutex_unlock (&icon->cache_lock); + } +} + +/** + * gtk_icon_theme_choose_icon_finish: + * @self: a #GtkIconTheme + * @res: a #GAsyncResult + * @error: (allow-none): location to store error information on failure, + * or %NULL. + * + * Finishes an async icon load, see gtk_icon_info_load_icon_async(). + * + * Returns: (transfer full): the rendered icon; this may be a newly + * created icon or a new reference to an internal icon, so you must + * not modify the icon. Use g_object_unref() to release your reference + * to the icon. + */ +GtkIconInfo * +gtk_icon_theme_choose_icon_finish (GtkIconTheme *self, + GAsyncResult *result, + GError **error) +{ + GTask *task = G_TASK (result); + + g_return_val_if_fail (g_task_is_valid (result, self), NULL); + + return g_task_propagate_pointer (task, error); +} /* Error quark */ GQuark @@ -2446,7 +2658,7 @@ gtk_icon_theme_has_icon (GtkIconTheme *self, gtk_icon_theme_lock (self); - ensure_valid_themes (self); + ensure_valid_themes (self, FALSE); for (l = self->dir_mtimes; l; l = l->next) { @@ -2518,7 +2730,7 @@ gtk_icon_theme_get_icon_sizes (GtkIconTheme *self, gtk_icon_theme_lock (self); - ensure_valid_themes (self); + ensure_valid_themes (self, FALSE); sizes = g_hash_table_new (g_direct_hash, g_direct_equal); @@ -2606,7 +2818,7 @@ gtk_icon_theme_list_icons (GtkIconTheme *self, gtk_icon_theme_lock (self); - ensure_valid_themes (self); + ensure_valid_themes (self, FALSE); if (context) { @@ -4097,20 +4309,6 @@ gtk_icon_info_load_icon (GtkIconInfo *icon_info, return paintable; } -static void -load_icon_thread (GTask *task, - gpointer source_object, - gpointer task_data, - GCancellable *cancellable) -{ - GtkIconInfo *dup = task_data; - - g_mutex_lock (&dup->cache_lock); - (void)icon_info_ensure_scale_and_texture__locked (dup); - g_mutex_unlock (&dup->cache_lock); - g_task_return_pointer (task, NULL, NULL); -} - /** * gtk_icon_info_load_icon_async: * @self: a #GtkIconInfo from gtk_icon_theme_lookup_icon() diff --git a/gtk/gtkicontheme.h b/gtk/gtkicontheme.h index c938156117..6ec132b272 100644 --- a/gtk/gtkicontheme.h +++ b/gtk/gtkicontheme.h @@ -169,6 +169,19 @@ GtkIconInfo * gtk_icon_theme_choose_icon_for_scale (GtkIconTheme gint scale, GtkIconLookupFlags flags); GDK_AVAILABLE_IN_ALL +void gtk_icon_theme_choose_icon_async (GtkIconTheme *self, + const gchar *icon_names[], + gint size, + gint scale, + GtkIconLookupFlags flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +GDK_AVAILABLE_IN_ALL +GtkIconInfo * gtk_icon_theme_choose_icon_finish (GtkIconTheme *self, + GAsyncResult *result, + GError **error); +GDK_AVAILABLE_IN_ALL GdkPaintable *gtk_icon_theme_load_icon (GtkIconTheme *self, const char *icon_name, int size, -- 2.30.2