[gtk+] Add gtk_icon_info_load_icon_async



commit 7690846c3f18ff94c4d1213f722cab102cd7506a
Author: Alexander Larsson <alexl redhat com>
Date:   Thu Feb 14 13:32:30 2013 +0100

    Add gtk_icon_info_load_icon_async
    
    This lets you asynchronously load icons. We need this for gnome-shell
    to replace its current non-threadsafe use of GtkIconTheme.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=693802

 gtk/gtk.symbols       |    2 +
 gtk/gtkicontheme.c    |  188 +++++++++++++++++++++++++++++++++++++++++++++++++
 gtk/gtkicontheme.h    |    9 +++
 tests/testicontheme.c |   61 ++++++++++++++++
 4 files changed, 260 insertions(+), 0 deletions(-)
---
diff --git a/gtk/gtk.symbols b/gtk/gtk.symbols
index d0e219c..ff0ee48 100644
--- a/gtk/gtk.symbols
+++ b/gtk/gtk.symbols
@@ -1255,6 +1255,8 @@ gtk_icon_info_get_embedded_rect
 gtk_icon_info_get_filename
 gtk_icon_info_get_type
 gtk_icon_info_load_icon
+gtk_icon_info_load_icon_async
+gtk_icon_info_load_icon_finish
 gtk_icon_info_load_symbolic
 gtk_icon_info_load_symbolic_for_context
 gtk_icon_info_load_symbolic_for_style
diff --git a/gtk/gtkicontheme.c b/gtk/gtkicontheme.c
index 3b79a55..f2e9f6c 100644
--- a/gtk/gtkicontheme.c
+++ b/gtk/gtkicontheme.c
@@ -350,6 +350,7 @@ static void         do_theme_change   (GtkIconTheme     *icon_theme);
 static void     blow_themes               (GtkIconTheme    *icon_themes);
 static gboolean rescan_themes             (GtkIconTheme    *icon_themes);
 
+static GtkIconData *icon_data_dup      (GtkIconData     *icon_data);
 static void  icon_data_free            (GtkIconData     *icon_data);
 static void load_icon_data             (IconThemeDir    *dir,
                                        const char      *path,
@@ -2972,6 +2973,26 @@ icon_data_free (GtkIconData *icon_data)
   g_slice_free (GtkIconData, icon_data);
 }
 
+static GtkIconData *
+icon_data_dup (GtkIconData *icon_data)
+{
+  GtkIconData *dup = NULL;
+  if (icon_data)
+    {
+      dup = g_slice_new0 (GtkIconData);
+      *dup = *icon_data;
+      if (dup->n_attach_points > 0)
+       {
+         dup->attach_points = g_memdup (dup->attach_points,
+                                        sizeof (GdkPoint) * dup->n_attach_points);
+       }
+      dup->display_name = g_strdup (dup->display_name);
+    }
+
+  return dup;
+}
+
+
 /*
  * GtkIconInfo
  */
@@ -2992,6 +3013,45 @@ icon_info_new (void)
   return g_object_new (GTK_TYPE_ICON_INFO, NULL);
 }
 
+/* This only copies whatever is needed to load the pixbuf, so that we can do
+ * a load in a thread without affecting the original IconInfo from the thread.
+ */
+static GtkIconInfo *
+icon_info_dup (GtkIconInfo *icon_info)
+{
+  GtkIconInfo *dup;
+  GSList *l;
+
+  dup = icon_info_new ();
+
+  dup->filename = g_strdup (icon_info->filename);
+  if (icon_info->icon_file)
+    dup->icon_file = g_object_ref (icon_info->icon_file);
+  if (icon_info->loadable)
+    dup->loadable = g_object_ref (icon_info->loadable);
+
+  for (l = icon_info->emblem_infos; l != NULL; l = l->next)
+    {
+      dup->emblem_infos =
+       g_slist_append (dup->emblem_infos,
+                       icon_info_dup (l->data));
+    }
+
+  if (icon_info->cache_pixbuf)
+    dup->cache_pixbuf = g_object_ref (icon_info->cache_pixbuf);
+
+  dup->data = icon_data_dup (icon_info->data);
+  dup->dir_type = icon_info->dir_type;
+  dup->dir_size = icon_info->dir_size;
+  dup->threshold = icon_info->threshold;
+  dup->desired_size = icon_info->desired_size;
+  dup->raw_coordinates = icon_info->raw_coordinates;
+  dup->forced_size = icon_info->forced_size;
+  dup->emblems_applied = icon_info->emblems_applied;
+
+  return dup;
+}
+
 static GtkIconInfo *
 icon_info_new_builtin (BuiltinIcon *icon)
 {
@@ -3238,6 +3298,21 @@ apply_emblems (GtkIconInfo *info)
   info->emblems_applied = TRUE;
 }
 
+/* If this returns TRUE, its safe to call
+   icon_info_ensure_scale_and_pixbuf without blocking */
+static gboolean
+icon_info_get_pixbuf_ready (GtkIconInfo *icon_info)
+{
+  if (icon_info->pixbuf &&
+      (icon_info->emblem_infos == NULL || icon_info->emblems_applied))
+    return TRUE;
+
+  if (icon_info->load_error)
+    return TRUE;
+
+  return FALSE;
+}
+
 /* This function contains the complicated logic for deciding
  * on the size at which to load the icon and loading it at
  * that size.
@@ -3504,6 +3579,119 @@ gtk_icon_info_load_icon (GtkIconInfo *icon_info,
   return icon_info->proxy_pixbuf;
 }
 
+static void
+load_icon_thread  (GTask           *task,
+                  gpointer         source_object,
+                  gpointer         task_data,
+                  GCancellable    *cancellable)
+{
+  GtkIconInfo *dup = task_data;
+
+  icon_info_ensure_scale_and_pixbuf (dup, FALSE);
+  g_task_return_pointer (task, NULL, NULL);
+}
+
+/**
+ * gtk_icon_info_load_icon_async:
+ * @icon_info: a #GtkIconInfo structure from gtk_icon_theme_lookup_icon()
+ * @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 load, render and scale an icon previously looked up
+ * from the icon theme using gtk_icon_theme_lookup_icon().
+ *
+ * For more details, see gtk_icon_info_load_icon() which is the synchronous
+ * version of this call.
+ *
+ * Since: 3.8
+ **/
+void
+gtk_icon_info_load_icon_async (GtkIconInfo          *icon_info,
+                              GCancellable         *cancellable,
+                              GAsyncReadyCallback   callback,
+                              gpointer              user_data)
+{
+  GTask *task;
+  GdkPixbuf *pixbuf;
+  GtkIconInfo *dup;
+  GError *error = NULL;
+
+  task = g_task_new (icon_info, cancellable, callback, user_data);
+
+  if (icon_info_get_pixbuf_ready (icon_info))
+    {
+      pixbuf = gtk_icon_info_load_icon (icon_info, &error);
+      if (pixbuf == NULL)
+       g_task_return_error (task, error);
+      else
+       g_task_return_pointer (task, pixbuf, g_object_unref);
+      g_object_unref (task);
+    }
+  else
+    {
+      dup = icon_info_dup (icon_info);
+      g_task_set_task_data (task, dup, g_object_unref);
+      g_task_run_in_thread (task, load_icon_thread);
+      g_object_unref (task);
+    }
+}
+
+/**
+ * gtk_icon_info_load_icon_finish:
+ * @icon_info: a #GtkIconInfo structure from gtk_icon_theme_lookup_icon()
+ * @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().
+ *
+ * Return value: (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.
+ *
+ * Since: 3.8
+ **/
+GdkPixbuf *
+gtk_icon_info_load_icon_finish (GtkIconInfo          *icon_info,
+                               GAsyncResult         *result,
+                               GError              **error)
+{
+  GTask *task = G_TASK (result);
+  GtkIconInfo *dup;
+
+  g_return_val_if_fail (g_task_is_valid (result, icon_info), NULL);
+
+  dup = g_task_get_task_data (task);
+  if (dup == NULL || g_task_had_error (task))
+    return g_task_propagate_pointer (task, error);
+
+  /* We ran the thread and it was not cancelled */
+
+  /* Check if someone else updated the icon_info in between */
+  if (!icon_info_get_pixbuf_ready (icon_info))
+    {
+      /* If not, copy results from dup back to icon_info */
+
+      icon_info->emblems_applied = dup->emblems_applied;
+      icon_info->scale = dup->scale;
+      g_clear_object (&icon_info->pixbuf);
+      if (dup->pixbuf)
+       icon_info->pixbuf = g_object_ref (dup->pixbuf);
+      g_clear_error (&icon_info->load_error);
+      if (dup->load_error)
+       icon_info->load_error = g_error_copy (dup->load_error);
+    }
+
+  g_assert (icon_info_get_pixbuf_ready (icon_info));
+
+  /* This is now guaranteed to not block */
+  return gtk_icon_info_load_icon (icon_info, error);
+}
+
 static gchar *
 gdk_color_to_css (GdkColor *color)
 {
diff --git a/gtk/gtkicontheme.h b/gtk/gtkicontheme.h
index cf28429..3888cda 100644
--- a/gtk/gtkicontheme.h
+++ b/gtk/gtkicontheme.h
@@ -203,6 +203,15 @@ const gchar *         gtk_icon_info_get_filename       (GtkIconInfo   *icon_info
 GdkPixbuf *           gtk_icon_info_get_builtin_pixbuf (GtkIconInfo   *icon_info);
 GdkPixbuf *           gtk_icon_info_load_icon          (GtkIconInfo   *icon_info,
                                                        GError       **error);
+GDK_AVAILABLE_IN_3_8
+void                  gtk_icon_info_load_icon_async   (GtkIconInfo          *icon_info,
+                                                      GCancellable         *cancellable,
+                                                      GAsyncReadyCallback   callback,
+                                                      gpointer              user_data);
+GDK_AVAILABLE_IN_3_8
+GdkPixbuf *           gtk_icon_info_load_icon_finish  (GtkIconInfo          *icon_info,
+                                                      GAsyncResult         *res,
+                                                      GError              **error);
 GdkPixbuf *           gtk_icon_info_load_symbolic      (GtkIconInfo   *icon_info,
                                                         const GdkRGBA *fg,
                                                         const GdkRGBA *success_color,
diff --git a/tests/testicontheme.c b/tests/testicontheme.c
index ad9a459..bc0ee69 100644
--- a/tests/testicontheme.c
+++ b/tests/testicontheme.c
@@ -34,6 +34,28 @@ usage (void)
           );
 }
 
+static void
+icon_loaded_cb (GObject *source_object,
+               GAsyncResult *res,
+               gpointer user_data)
+{
+  GdkPixbuf *pixbuf;
+  GError *error;
+
+  error = NULL;
+  pixbuf = gtk_icon_info_load_icon_finish (GTK_ICON_INFO (source_object),
+                                          res, &error);
+
+  if (pixbuf == NULL)
+    {
+      g_print ("%s\n", error->message);
+      exit (1);
+    }
+
+  gtk_image_set_from_pixbuf (GTK_IMAGE (user_data), pixbuf);
+  g_object_unref (pixbuf);
+}
+
 
 int
 main (int argc, char *argv[])
@@ -103,6 +125,45 @@ main (int argc, char *argv[])
       
       gtk_main ();
     }
+  else if (strcmp (argv[1], "display-async") == 0)
+    {
+      GtkWidget *window, *image;
+      GtkIconSize size;
+      GtkIconInfo *info;
+
+      if (argc < 4)
+       {
+         g_object_unref (icon_theme);
+         usage ();
+         return 1;
+       }
+
+      if (argc >= 5)
+       size = atoi (argv[4]);
+      else
+       size = GTK_ICON_SIZE_BUTTON;
+
+      window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+      image = gtk_image_new ();
+      gtk_container_add (GTK_CONTAINER (window), image);
+      g_signal_connect (window, "delete-event",
+                        G_CALLBACK (gtk_main_quit), window);
+      gtk_widget_show_all (window);
+
+      info = gtk_icon_theme_lookup_icon (icon_theme, argv[3], size,
+                                         GTK_ICON_LOOKUP_USE_BUILTIN);
+
+      if (info == NULL)
+       {
+          g_print ("Icon not found\n");
+          return 1;
+       }
+
+      gtk_icon_info_load_icon_async (info,
+                                    NULL, icon_loaded_cb, image);
+
+      gtk_main ();
+    }
   else if (strcmp (argv[1], "list") == 0)
     {
       if (argc >= 4)


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]