summaryrefslogtreecommitdiff
path: root/gdk
diff options
context:
space:
mode:
authorBenjamin Otte <otte@redhat.com>2017-11-22 10:44:30 +0100
committerBenjamin Otte <otte@redhat.com>2017-12-03 05:46:27 +0100
commit91910ba01319447243784a85fefad0fa17c19db7 (patch)
treecf9410387bc20b6f01457f7c421041ccdb1e43bf /gdk
parent970cb100aff72679b15c3c8b27b168335d0e5a50 (diff)
downloadgtk+-91910ba01319447243784a85fefad0fa17c19db7.tar.gz
gdk: Im[plement content deserializing
Add infrastructure to do GInputStream => GType conversions. Use that to implement gdk_clipboard_read_value() which reads into GValues.
Diffstat (limited to 'gdk')
-rw-r--r--gdk/gdk.h1
-rw-r--r--gdk/gdkclipboard.c124
-rw-r--r--gdk/gdkclipboard.h11
-rw-r--r--gdk/gdkcontentdeserializer.c487
-rw-r--r--gdk/gdkcontentdeserializer.h90
-rw-r--r--gdk/meson.build2
6 files changed, 714 insertions, 1 deletions
diff --git a/gdk/gdk.h b/gdk/gdk.h
index 64a06e2677..80dc657e4e 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 30b81fff3e..0fb39268bc 100644
--- a/gdk/gdkclipboard.c
+++ b/gdk/gdkclipboard.c
@@ -20,6 +20,7 @@
#include "gdkclipboardprivate.h"
+#include "gdkcontentdeserializer.h"
#include "gdkcontentformats.h"
#include "gdkdisplay.h"
@@ -339,6 +340,125 @@ 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_content_formats_unref (formats);
+}
+
+/**
+ * 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)
{
@@ -357,7 +477,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 5721fd10ff..2a357602ab 100644
--- a/gdk/gdkclipboard.h
+++ b/gdk/gdkclipboard.h
@@ -54,6 +54,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 0000000000..d6ec674b27
--- /dev/null
+++ b/gdk/gdkcontentdeserializer.c
@@ -0,0 +1,487 @@
+/* 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 "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 0000000000..481f15a838
--- /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 d1e75ce5b2..88045c38c6 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',
@@ -43,6 +44,7 @@ gdk_public_headers = files([
'gdkapplaunchcontext.h',
'gdkcairo.h',
'gdkclipboard.h',
+ 'gdkcontentdeserializer.h',
'gdkcontentformats.h',
'gdkcursor.h',
'gdkdevice.h',