[gtk+/wip/otte/clipboard: 11/11] gdk: Im[plement content deserializing



commit ae624135f9d7c76241bfa71dba65d4283679bbcf
Author: Benjamin Otte <otte redhat com>
Date:   Wed Nov 22 10:44:30 2017 +0100

    gdk: Im[plement content deserializing
    
    Add infrastructure to do GInputStream => GType conversions. Use that to
    implement gdk_clipboard_read_value() which reads into GValues.

 gdk/gdk.h                    |    1 +
 gdk/gdkclipboard.c           |  122 +++++++++++-
 gdk/gdkclipboard.h           |   11 +
 gdk/gdkcontentdeserializer.c |  488 ++++++++++++++++++++++++++++++++++++++++++
 gdk/gdkcontentdeserializer.h |   90 ++++++++
 gdk/meson.build              |    2 +
 tests/testclipboard2.c       |   47 +---
 7 files changed, 724 insertions(+), 37 deletions(-)
---
diff --git a/gdk/gdk.h b/gdk/gdk.h
index ec280f6..bdc2042 100644
--- a/gdk/gdk.h
+++ b/gdk/gdk.h
@@ -32,6 +32,7 @@
 #include <gdk/gdkapplaunchcontext.h>
 #include <gdk/gdkcairo.h>
 #include <gdk/gdkclipboard.h>
+#include <gdk/gdkcontentdeserializer.h>
 #include <gdk/gdkcontentformats.h>
 #include <gdk/gdkcursor.h>
 #include <gdk/gdkdevice.h>
diff --git a/gdk/gdkclipboard.c b/gdk/gdkclipboard.c
index 6cfa16f..94e7adb 100644
--- a/gdk/gdkclipboard.c
+++ b/gdk/gdkclipboard.c
@@ -19,6 +19,7 @@
 
 #include "gdkclipboardprivate.h"
 
+#include "gdkcontentdeserializer.h"
 #include "gdkcontentformats.h"
 #include "gdkdisplay.h"
 
@@ -338,6 +339,123 @@ gdk_clipboard_read_finish (GdkClipboard  *clipboard,
   return GDK_CLIPBOARD_GET_CLASS (clipboard)->read_finish (clipboard, out_mime_type, result, error);
 }
 
+static void
+gdk_clipboard_read_value_done (GObject      *source,
+                               GAsyncResult *result,
+                               gpointer      data)
+{
+  GTask *task = data;
+  GError *error = NULL;
+  const GValue *value;
+
+  value = gdk_content_deserialize_finish (result, &error);
+  if (value == NULL)
+    g_task_return_error (task, error);
+  else
+    g_task_return_pointer (task, (gpointer) value, NULL);
+
+  g_object_unref (task);
+}
+
+static void
+gdk_clipboard_read_value_got_stream (GObject      *source,
+                                     GAsyncResult *result,
+                                     gpointer      data)
+{
+  GInputStream *stream;
+  GError *error = NULL;
+  GTask *task = data;
+  const char *mime_type;
+
+  stream = gdk_clipboard_read_finish (GDK_CLIPBOARD (source), &mime_type, result, &error);
+  if (stream == NULL)
+    {
+      g_task_return_error (task, error);
+      return;
+    }
+
+  gdk_content_deserialize_async (stream,
+                                 mime_type,
+                                 GPOINTER_TO_SIZE (g_task_get_task_data (task)),
+                                 g_task_get_priority (task),
+                                 g_task_get_cancellable (task),
+                                 gdk_clipboard_read_value_done,
+                                 task);
+  g_object_unref (stream);
+}
+
+/**
+ * gdk_clipboard_read_value_async:
+ * @clipboard: a #GdkClipboard
+ * @type: a #GType to read
+ * @io_priority: the [I/O priority][io-priority]
+ *     of the request. 
+ * @cancellable: (nullable): optional #GCancellable object, %NULL to ignore.
+ * @callback: (scope async): callback to call when the request is satisfied
+ * @user_data: (closure): the data to pass to callback function
+ *
+ * Asynchronously request the @clipboard contents converted to the given
+ * @type. When the operation is finished @callback will be called. 
+ * You can then call gdk_clipboard_read_value_finish() to get the resulting
+ * #GValue.
+ *
+ * For local clipboard contents that are available in the given #GType, the
+ * value will be copied directly. Otherwise, GDK will try to use
+ * gdk_content_deserialize_async() to convert the clipboard's data.
+ **/
+void
+gdk_clipboard_read_value_async (GdkClipboard        *clipboard,
+                                GType                type,
+                                int                  io_priority,
+                                GCancellable        *cancellable,
+                                GAsyncReadyCallback  callback,
+                                gpointer             user_data)
+{
+  GdkContentFormatsBuilder *builder;
+  GdkContentFormats *formats;
+  GTask *task;
+
+  builder = gdk_content_formats_builder_new ();
+  gdk_content_formats_builder_add_gtype (builder, type);
+  formats = gdk_content_formats_builder_free (builder);
+  formats = gdk_content_formats_union_deserialize_mime_types (formats);
+
+  task = g_task_new (clipboard, cancellable, callback, user_data);
+  g_task_set_priority (task, io_priority);
+  g_task_set_source_tag (task, gdk_clipboard_read_value_async);
+  g_task_set_task_data (task, GSIZE_TO_POINTER (type), NULL);
+
+  gdk_clipboard_read_internal (clipboard,
+                               formats,
+                               io_priority,
+                               cancellable,
+                               gdk_clipboard_read_value_got_stream,
+                               task);
+}
+
+/**
+ * gdk_clipboard_read_value_finish:
+ * @clipboard: a #GdkClipboard
+ * @result: a #GAsyncResult
+ * @error: a #GError location to store the error occurring, or %NULL to 
+ * ignore.
+ *
+ * Finishes an asynchronous clipboard read started with
+ * gdk_clipboard_read_value_async().
+ *
+ * Returns: (transfer none): a #GValue containing the result.
+ **/
+const GValue *
+gdk_clipboard_read_value_finish (GdkClipboard  *clipboard,
+                                 GAsyncResult  *res,
+                                 GError       **error)
+{
+  g_return_val_if_fail (g_task_is_valid (res, clipboard), NULL);
+  g_return_val_if_fail (g_task_get_source_tag (G_TASK (res)) == gdk_clipboard_read_value_async, NULL);
+
+  return g_task_propagate_pointer (G_TASK (res), error);
+}
+
 GdkClipboard *
 gdk_clipboard_new (GdkDisplay *display)
 {
@@ -356,7 +474,9 @@ gdk_clipboard_claim_remote (GdkClipboard      *clipboard,
   g_return_if_fail (formats != NULL);
 
   gdk_content_formats_unref (priv->formats);
-  priv->formats = gdk_content_formats_ref (formats);
+  gdk_content_formats_ref (formats);
+  formats = gdk_content_formats_union_deserialize_gtypes (formats);
+  priv->formats = formats;
   g_object_notify_by_pspec (G_OBJECT (clipboard), properties[PROP_FORMATS]);
   if (priv->local)
     {
diff --git a/gdk/gdkclipboard.h b/gdk/gdkclipboard.h
index 222b947..1b1fe68 100644
--- a/gdk/gdkclipboard.h
+++ b/gdk/gdkclipboard.h
@@ -53,6 +53,17 @@ GInputStream *          gdk_clipboard_read_finish       (GdkClipboard          *
                                                          const char           **out_mime_type,
                                                          GAsyncResult          *result,
                                                          GError               **error);
+GDK_AVAILABLE_IN_3_94
+void                    gdk_clipboard_read_value_async  (GdkClipboard          *clipboard,
+                                                         GType                  type,
+                                                         int                    io_priority,
+                                                         GCancellable          *cancellable,
+                                                         GAsyncReadyCallback    callback,
+                                                         gpointer               user_data);
+GDK_AVAILABLE_IN_3_94
+const GValue *          gdk_clipboard_read_value_finish (GdkClipboard          *clipboard,
+                                                         GAsyncResult          *res,
+                                                         GError               **error);
 
 G_END_DECLS
 
diff --git a/gdk/gdkcontentdeserializer.c b/gdk/gdkcontentdeserializer.c
new file mode 100644
index 0000000..bcb7c44
--- /dev/null
+++ b/gdk/gdkcontentdeserializer.c
@@ -0,0 +1,488 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2017 Benjamin Otte
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+
+#include "gdkcontentdeserializer.h"
+#include "gdkcontentdeserializerprivate.h"
+
+#include "gdkcontentformats.h"
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+typedef struct _Deserializer Deserializer;
+
+struct _Deserializer 
+{
+  const char *                    mime_type; /* interned */
+  GType                           type;
+  GdkContentDeserializeFunc       deserialize;
+  gpointer                        data;
+  GDestroyNotify                  notify;
+};
+
+GQueue deserializers = G_QUEUE_INIT;
+
+static void init (void);
+
+#define GDK_CONTENT_DESERIALIZER_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), 
GDK_TYPE_CONTENT_DESERIALIZER, GdkContentDeserializerClass))
+#define GDK_IS_CONTENT_DESERIALIZER_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), 
GDK_TYPE_CONTENT_DESERIALIZER))
+#define GDK_CONTENT_DESERIALIZER_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), 
GDK_TYPE_CONTENT_DESERIALIZER, GdkContentDeserializerClass))
+
+typedef struct _GdkContentDeserializerClass GdkContentDeserializerClass;
+
+struct _GdkContentDeserializer
+{
+  GObject parent_instance;
+
+  const char *mime_type; /* interned */
+  GValue value;
+  GInputStream *stream;
+  int priority;
+  GCancellable *cancellable;
+  gpointer user_data;
+  GAsyncReadyCallback callback;
+  gpointer callback_data;
+
+  GError *error;
+  gboolean returned;
+};
+
+struct _GdkContentDeserializerClass
+{
+  GObjectClass parent_class;
+};
+
+static gpointer
+gdk_content_deserializer_async_result_get_user_data (GAsyncResult *res)
+{
+  return GDK_CONTENT_DESERIALIZER (res)->callback_data;
+}
+
+static GObject *
+gdk_content_deserializer_async_result_get_source_object (GAsyncResult *res)
+{
+  return NULL;
+}
+
+static void
+gdk_content_deserializer_async_result_iface_init (GAsyncResultIface *iface)
+{
+  iface->get_user_data = gdk_content_deserializer_async_result_get_user_data;
+  iface->get_source_object = gdk_content_deserializer_async_result_get_source_object;
+}
+
+G_DEFINE_TYPE_WITH_CODE (GdkContentDeserializer, gdk_content_deserializer, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_RESULT, 
gdk_content_deserializer_async_result_iface_init))
+
+static void
+gdk_content_deserializer_finalize (GObject *object)
+{
+  GdkContentDeserializer *deserializer = GDK_CONTENT_DESERIALIZER (object);
+
+  g_value_unset (&deserializer->value);
+  g_clear_object (&deserializer->stream);
+  g_clear_object (&deserializer->cancellable);
+  g_clear_error (&deserializer->error);
+
+  G_OBJECT_CLASS (gdk_content_deserializer_parent_class)->finalize (object);
+}
+
+static void
+gdk_content_deserializer_class_init (GdkContentDeserializerClass *content_deserializer_class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (content_deserializer_class);
+
+  object_class->finalize = gdk_content_deserializer_finalize;
+}
+
+static void
+gdk_content_deserializer_init (GdkContentDeserializer *content_deserializer)
+{
+}
+
+static void
+gdk_content_deserializer_run (const char                *mime_type,
+                              GType                      type,
+                              GInputStream              *stream,
+                              int                        priority,
+                              GCancellable              *cancellable,
+                              GdkContentDeserializeFunc  deserialize_func,
+                              gpointer                   user_data,
+                              GAsyncReadyCallback        callback,
+                              gpointer                   callback_data)
+{
+  GdkContentDeserializer *deserializer;
+
+  deserializer = g_object_new (GDK_TYPE_CONTENT_DESERIALIZER, NULL);
+
+  deserializer->mime_type = mime_type;
+  g_value_init (&deserializer->value, type);
+  deserializer->stream = g_object_ref (stream);
+  deserializer->priority = priority;
+  if (cancellable)
+    deserializer->cancellable = g_object_ref (cancellable);
+  deserializer->user_data = user_data;
+  deserializer->callback = callback;
+  deserializer->callback_data = callback_data;
+
+  deserialize_func (deserializer);
+}
+
+const char *
+gdk_content_deserializer_get_mime_type (GdkContentDeserializer *deserializer)
+{
+  g_return_val_if_fail (GDK_IS_CONTENT_DESERIALIZER (deserializer), NULL);
+
+  return deserializer->mime_type;
+}
+
+GType
+gdk_content_deserializer_get_gtype (GdkContentDeserializer *deserializer)
+{
+  g_return_val_if_fail (GDK_IS_CONTENT_DESERIALIZER (deserializer), G_TYPE_INVALID);
+
+  return G_VALUE_TYPE (&deserializer->value);
+}
+
+GValue *
+gdk_content_deserializer_get_value (GdkContentDeserializer *deserializer)
+{
+  g_return_val_if_fail (GDK_IS_CONTENT_DESERIALIZER (deserializer), NULL);
+
+  return &deserializer->value;
+}
+
+GInputStream *
+gdk_content_deserializer_get_input_stream (GdkContentDeserializer *deserializer)
+{
+  g_return_val_if_fail (GDK_IS_CONTENT_DESERIALIZER (deserializer), NULL);
+
+  return deserializer->stream;
+}
+
+int
+gdk_content_deserializer_get_priority (GdkContentDeserializer *deserializer)
+{
+  g_return_val_if_fail (GDK_IS_CONTENT_DESERIALIZER (deserializer), G_PRIORITY_DEFAULT);
+
+  return deserializer->priority;
+}
+
+GCancellable *
+gdk_content_deserializer_get_cancellable (GdkContentDeserializer *deserializer)
+{
+  g_return_val_if_fail (GDK_IS_CONTENT_DESERIALIZER (deserializer), NULL);
+
+  return deserializer->cancellable;
+}
+
+gpointer
+gdk_content_deserializer_get_user_data (GdkContentDeserializer *deserializer)
+{
+  g_return_val_if_fail (GDK_IS_CONTENT_DESERIALIZER (deserializer), NULL);
+
+  return deserializer->user_data;
+}
+
+static gboolean
+gdk_content_deserializer_emit_callback (gpointer data)
+{
+  GdkContentDeserializer *deserializer = data;
+
+  if (deserializer->callback)
+    {
+      deserializer->callback (NULL, G_ASYNC_RESULT (deserializer), deserializer->callback_data);
+    }
+
+  return G_SOURCE_REMOVE;
+}
+
+void
+gdk_content_deserializer_return_success (GdkContentDeserializer *deserializer)
+{
+  g_return_if_fail (GDK_IS_CONTENT_DESERIALIZER (deserializer));
+  g_return_if_fail (!deserializer->returned);
+
+  deserializer->returned = TRUE;
+  g_idle_add_full (deserializer->priority,
+                   gdk_content_deserializer_emit_callback,
+                   deserializer,
+                   g_object_unref);
+  /* NB: the idle will destroy our reference */
+}
+
+void
+gdk_content_deserializer_return_error (GdkContentDeserializer *deserializer,
+                                       GError                 *error)
+{
+  g_return_if_fail (GDK_IS_CONTENT_DESERIALIZER (deserializer));
+  g_return_if_fail (!deserializer->returned);
+  g_return_if_fail (error != NULL);
+
+  deserializer->error = error;
+  /* FIXME: naming */
+  gdk_content_deserializer_return_success (deserializer);
+}
+
+void
+gdk_content_register_deserializer (const char                *mime_type,
+                                   GType                      type,
+                                   GdkContentDeserializeFunc  deserialize,
+                                   gpointer                   data,
+                                   GDestroyNotify             notify)
+{
+  Deserializer *deserializer;
+
+  g_return_if_fail (mime_type != NULL);
+  g_return_if_fail (deserialize != NULL);
+
+  init ();
+
+  deserializer = g_slice_new0 (Deserializer);
+
+  deserializer->mime_type = g_intern_string (mime_type);
+  deserializer->type = type;
+  deserializer->deserialize = deserialize;
+  deserializer->data = data;
+  deserializer->notify = notify;
+
+  g_queue_push_tail (&deserializers, deserializer);
+}
+
+static Deserializer *
+lookup_deserializer (const char *mime_type,
+                     GType       type)
+{
+  GList *l;
+
+  g_return_val_if_fail (mime_type != NULL, NULL);
+
+  init ();
+
+  mime_type = g_intern_string (mime_type);
+
+  for (l = g_queue_peek_head_link (&deserializers); l; l = l->next)
+    {
+      Deserializer *deserializer = l->data;
+
+      if (deserializer->mime_type == mime_type &&
+          deserializer->type == type)
+        return deserializer;
+    }
+  
+  return NULL;
+}
+
+GdkContentFormats *
+gdk_content_formats_union_deserialize_gtypes (GdkContentFormats *formats)
+{
+  GdkContentFormatsBuilder *builder;
+  GList *l;
+
+  g_return_val_if_fail (formats != NULL, NULL);
+
+  init ();
+
+  builder = gdk_content_formats_builder_new ();
+  gdk_content_formats_builder_add_formats (builder, formats);
+
+  for (l = g_queue_peek_head_link (&deserializers); l; l = l->next)
+    {
+      Deserializer *deserializer = l->data;
+
+      if (gdk_content_formats_contain_mime_type (formats, deserializer->mime_type))
+        gdk_content_formats_builder_add_gtype (builder, deserializer->type);
+    }
+
+  gdk_content_formats_unref (formats);
+
+  return gdk_content_formats_builder_free (builder);
+}
+
+GdkContentFormats *
+gdk_content_formats_union_deserialize_mime_types (GdkContentFormats *formats)
+{
+  GdkContentFormatsBuilder *builder;
+  GList *l;
+
+  g_return_val_if_fail (formats != NULL, NULL);
+
+  init ();
+
+  builder = gdk_content_formats_builder_new ();
+  gdk_content_formats_builder_add_formats (builder, formats);
+
+  for (l = g_queue_peek_head_link (&deserializers); l; l = l->next)
+    {
+      Deserializer *deserializer = l->data;
+
+      if (gdk_content_formats_contain_gtype (formats, deserializer->type))
+        gdk_content_formats_builder_add_mime_type (builder, deserializer->mime_type);
+    }
+
+  gdk_content_formats_unref (formats);
+
+  return gdk_content_formats_builder_free (builder);
+}
+
+static void
+deserialize_not_found (GdkContentDeserializer *deserializer)
+{
+  GError *error = g_error_new (G_IO_ERROR,
+                               G_IO_ERROR_NOT_FOUND,
+                               "Could not convert data from %s to %s",
+                               gdk_content_deserializer_get_mime_type (deserializer),
+                               g_type_name (gdk_content_deserializer_get_gtype (deserializer)));
+  gdk_content_deserializer_return_error (deserializer, error);
+}
+
+void
+gdk_content_deserialize_async (GInputStream        *stream,
+                               const char          *mime_type,
+                               GType                type,
+                               int                  io_priority,
+                               GCancellable        *cancellable,
+                               GAsyncReadyCallback  callback,
+                               gpointer             user_data)
+{
+  Deserializer *deserializer;
+
+  g_return_if_fail (G_IS_INPUT_STREAM (stream));
+  g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+  g_return_if_fail (callback != NULL);
+
+  deserializer = lookup_deserializer (mime_type, type);
+
+  gdk_content_deserializer_run (mime_type,
+                                type,
+                                stream,
+                                io_priority,
+                                cancellable,
+                                deserializer ? deserializer->deserialize : deserialize_not_found,
+                                deserializer ? deserializer->data : NULL,
+                                callback,
+                                user_data);
+}
+
+const GValue *
+gdk_content_deserialize_finish (GAsyncResult  *result,
+                                GError       **error)
+{
+  GdkContentDeserializer *deserializer;
+
+  g_return_val_if_fail (GDK_IS_CONTENT_DESERIALIZER (result), NULL);
+  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+  deserializer = GDK_CONTENT_DESERIALIZER (result);
+
+  if (deserializer->error)
+    {
+      if (error)
+        *error = g_error_copy (deserializer->error);
+      return NULL;
+    }
+
+  return &deserializer->value;
+}
+
+/*** DESERIALIZERS ***/
+
+static void
+pixbuf_deserializer_finish (GObject      *source,
+                            GAsyncResult *res,
+                            gpointer      deserializer)
+{
+  GdkPixbuf *pixbuf;
+  GError *error = NULL;
+
+  pixbuf = gdk_pixbuf_new_from_stream_finish (res, &error);
+  if (pixbuf == NULL)
+    {
+      gdk_content_deserializer_return_error (deserializer, error);
+      return;
+    }
+
+  g_value_take_object (gdk_content_deserializer_get_value (deserializer),
+                       pixbuf);
+  gdk_content_deserializer_return_success (deserializer);
+}
+
+static void
+pixbuf_deserializer (GdkContentDeserializer *deserializer)
+{
+  gdk_pixbuf_new_from_stream_async (gdk_content_deserializer_get_input_stream (deserializer),
+                                   gdk_content_deserializer_get_cancellable (deserializer),
+                                    pixbuf_deserializer_finish,
+                                    deserializer);
+}
+
+static void
+init (void)
+{
+  static gboolean initialized = FALSE;
+  GSList *formats, *f;
+
+  if (initialized)
+    return;
+
+  initialized = TRUE;
+
+  formats = gdk_pixbuf_get_formats ();
+
+  /* Make sure png comes first */
+  for (f = formats; f; f = f->next)
+    {
+      GdkPixbufFormat *fmt = f->data;
+      gchar *name; 
+ 
+      name = gdk_pixbuf_format_get_name (fmt);
+      if (g_str_equal (name, "png"))
+       {
+         formats = g_slist_delete_link (formats, f);
+         formats = g_slist_prepend (formats, fmt);
+
+         g_free (name);
+
+         break;
+       }
+
+      g_free (name);
+    }  
+
+  for (f = formats; f; f = f->next)
+    {
+      GdkPixbufFormat *fmt = f->data;
+      gchar **mimes, **m;
+
+      mimes = gdk_pixbuf_format_get_mime_types (fmt);
+      for (m = mimes; *m; m++)
+       {
+          gdk_content_register_deserializer (*m,
+                                             GDK_TYPE_PIXBUF,
+                                             pixbuf_deserializer,
+                                             NULL,
+                                             NULL);
+       }
+      g_strfreev (mimes);
+    }
+
+  g_slist_free (formats);
+}
+
diff --git a/gdk/gdkcontentdeserializer.h b/gdk/gdkcontentdeserializer.h
new file mode 100644
index 0000000..481f15a
--- /dev/null
+++ b/gdk/gdkcontentdeserializer.h
@@ -0,0 +1,90 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2017 Benjamin Otte
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GTK_CONTENT_DESERIALIZER_H__
+#define __GTK_CONTENT_DESERIALIZER_H__
+
+#if !defined (__GDK_H_INSIDE__) && !defined (GDK_COMPILATION)
+#error "Only <gdk/gdk.h> can be included directly."
+#endif
+
+
+#include <gdk/gdkversionmacros.h>
+#include <gdk/gdktypes.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define GDK_TYPE_CONTENT_DESERIALIZER         (gdk_content_deserializer_get_type ())
+#define GDK_CONTENT_DESERIALIZER(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), 
GDK_TYPE_CONTENT_DESERIALIZER, GdkContentDeserializer))
+#define GDK_IS_CONTENT_DESERIALIZER(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), 
GDK_TYPE_CONTENT_DESERIALIZER))
+
+typedef struct _GdkContentDeserializer GdkContentDeserializer;
+
+typedef void (* GdkContentDeserializeFunc) (GdkContentDeserializer *deserializer);
+
+GDK_AVAILABLE_IN_3_94
+GType                   gdk_content_deserializer_get_type               (void) G_GNUC_CONST;
+
+GDK_AVAILABLE_IN_3_94
+const char *            gdk_content_deserializer_get_mime_type          (GdkContentDeserializer 
*deserializer);
+GDK_AVAILABLE_IN_3_94
+GType                   gdk_content_deserializer_get_gtype              (GdkContentDeserializer 
*deserializer);
+GDK_AVAILABLE_IN_3_94
+GValue *                gdk_content_deserializer_get_value              (GdkContentDeserializer 
*deserializer);
+GDK_AVAILABLE_IN_3_94
+GInputStream *          gdk_content_deserializer_get_input_stream       (GdkContentDeserializer 
*deserializer);
+GDK_AVAILABLE_IN_3_94
+int                     gdk_content_deserializer_get_priority           (GdkContentDeserializer 
*deserializer);
+GDK_AVAILABLE_IN_3_94
+GCancellable *          gdk_content_deserializer_get_cancellable        (GdkContentDeserializer 
*deserializer);
+GDK_AVAILABLE_IN_3_94
+gpointer                gdk_content_deserializer_get_user_data          (GdkContentDeserializer 
*deserializer);
+
+GDK_AVAILABLE_IN_3_94
+void                    gdk_content_deserializer_return_success         (GdkContentDeserializer 
*deserializer);
+GDK_AVAILABLE_IN_3_94
+void                    gdk_content_deserializer_return_error           (GdkContentDeserializer 
*deserializer,
+                                                                         GError                 *error);
+
+GDK_AVAILABLE_IN_3_94
+GdkContentFormats *     gdk_content_formats_union_deserialize_gtypes    (GdkContentFormats      *formats);
+GDK_AVAILABLE_IN_3_94
+GdkContentFormats *     gdk_content_formats_union_deserialize_mime_types(GdkContentFormats      *formats);
+
+GDK_AVAILABLE_IN_3_94
+void                    gdk_content_register_deserializer               (const char             *mime_type,
+                                                                         GType                   type,
+                                                                         GdkContentDeserializeFunc 
deserialize,
+                                                                         gpointer                data,
+                                                                         GDestroyNotify          notify);
+GDK_AVAILABLE_IN_3_94
+void                    gdk_content_deserialize_async                   (GInputStream           *stream,
+                                                                         const char             *mime_type,
+                                                                         GType                   type,
+                                                                         int                     io_priority,
+                                                                         GCancellable           *cancellable,
+                                                                         GAsyncReadyCallback     callback,
+                                                                         gpointer                user_data);
+GDK_AVAILABLE_IN_3_94
+const GValue *          gdk_content_deserialize_finish                  (GAsyncResult           *result,
+                                                                         GError                **error);
+
+
+G_END_DECLS
+
+#endif /* __GDK_CONTENT_DESERIALIZER_H__ */
diff --git a/gdk/meson.build b/gdk/meson.build
index 03b7758..07d664c 100644
--- a/gdk/meson.build
+++ b/gdk/meson.build
@@ -3,6 +3,7 @@ gdk_public_sources = files([
   'gdkapplaunchcontext.c',
   'gdkcairo.c',
   'gdkclipboard.c',
+  'gdkcontentdeserializer.c',
   'gdkcontentformats.c',
   'gdkcursor.c',
   'gdkdevice.c',
@@ -44,6 +45,7 @@ gdk_public_headers = files([
   'gdkapplaunchcontext.h',
   'gdkcairo.h',
   'gdkclipboard.h',
+  'gdkcontentdeserializer.h',
   'gdkcontentformats.h',
   'gdkcursor.h',
   'gdkdevice.h',
diff --git a/tests/testclipboard2.c b/tests/testclipboard2.c
index e141ecf..3652e71 100644
--- a/tests/testclipboard2.c
+++ b/tests/testclipboard2.c
@@ -30,47 +30,22 @@ clipboard_changed_cb (GdkClipboard *clipboard,
 }
 
 static void
-pixbuf_loaded_cb (GObject      *stream,
+pixbuf_loaded_cb (GObject      *clipboard,
                   GAsyncResult *res,
                   gpointer      data)
 {
-  GdkPixbuf *pixbuf;
+  const GValue *value;
   GError *error = NULL;
 
-  pixbuf = gdk_pixbuf_new_from_stream_finish (res, &error);
-  if (pixbuf == NULL)
+  value = gdk_clipboard_read_value_finish (GDK_CLIPBOARD (clipboard), res, &error);
+  if (value == NULL)
     {
       g_print ("%s\n", error->message);
       g_error_free (error);
       return;
     }
 
-  gtk_image_set_from_pixbuf (data, pixbuf);
-  g_object_unref (pixbuf);
-}
-
-static void
-clipboard_read_pixbuf_cb (GObject      *clipboard,
-                          GAsyncResult *result,
-                          gpointer      image)
-{
-  GInputStream *stream;
-  GError *error = NULL;
-
-  stream = gdk_clipboard_read_finish (GDK_CLIPBOARD (clipboard),
-                                      NULL,
-                                      result,
-                                      &error);
-  if (stream)
-    {
-      gdk_pixbuf_new_from_stream_async (stream, NULL, pixbuf_loaded_cb, image);
-      g_object_unref (stream);
-    }
-  else
-    {
-      g_print ("%s\n", error->message);
-      g_error_free (error);
-    }
+  gtk_image_set_from_pixbuf (data, g_value_get_object (value));
 }
 
 static void
@@ -88,12 +63,12 @@ visible_child_changed_cb (GtkWidget    *stack,
     {
       GtkWidget *image = gtk_stack_get_child_by_name (GTK_STACK (stack), "image");
 
-      gdk_clipboard_read_async (clipboard,
-                                (const gchar*[2]) { "image/png", NULL },
-                                G_PRIORITY_DEFAULT,
-                                NULL,
-                                clipboard_read_pixbuf_cb,
-                                image);
+      gdk_clipboard_read_value_async (clipboard,
+                                      GDK_TYPE_PIXBUF,
+                                      G_PRIORITY_DEFAULT,
+                                      NULL,
+                                      pixbuf_loaded_cb,
+                                      image);
     }
 }
                 


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