diff --git a/gdk/gdk.h b/gdk/gdk.h index 89f13d2373..4f0bf80c78 100644 --- a/gdk/gdk.h +++ b/gdk/gdk.h @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include diff --git a/gdk/gdkclipboard.c b/gdk/gdkclipboard.c index b19b0904aa..b5dc5dbd75 100644 --- a/gdk/gdkclipboard.c +++ b/gdk/gdkclipboard.c @@ -24,6 +24,7 @@ #include "gdkcontentformats.h" #include "gdkcontentproviderimpl.h" #include "gdkcontentproviderprivate.h" +#include "gdkcontentserializer.h" #include "gdkdisplay.h" #include "gdkintl.h" #include "gdkpipeiostreamprivate.h" @@ -164,14 +165,10 @@ gdk_clipboard_read_local_async (GdkClipboard *clipboard, } content_formats = gdk_content_provider_ref_formats (priv->content); + content_formats = gdk_content_formats_union_serialize_mime_types (content_formats); - if (!gdk_content_formats_match (content_formats, formats, NULL, &mime_type) - || mime_type == NULL) - { - g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, - _("No compatible formats to transfer clipboard contents.")); - } - else + if (gdk_content_formats_match (content_formats, formats, NULL, &mime_type) + && mime_type != NULL) { GOutputStream *output_stream; GIOStream *stream; @@ -190,6 +187,11 @@ gdk_clipboard_read_local_async (GdkClipboard *clipboard, g_object_unref (stream); } + else + { + g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + _("No compatible formats to transfer clipboard contents.")); + } gdk_content_formats_unref (content_formats); g_object_unref (task); @@ -826,6 +828,20 @@ gdk_clipboard_write_done (GObject *content, g_object_unref (task); } +static void +gdk_clipboard_write_serialize_done (GObject *content, + GAsyncResult *result, + gpointer task) +{ + GError *error = NULL; + + if (gdk_content_serialize_finish (result, &error)) + g_task_return_boolean (task, TRUE); + else + g_task_return_error (task, error); + + g_object_unref (task); +} void gdk_clipboard_write_async (GdkClipboard *clipboard, const char *mime_type, @@ -836,8 +852,9 @@ gdk_clipboard_write_async (GdkClipboard *clipboard, gpointer user_data) { GdkClipboardPrivate *priv = gdk_clipboard_get_instance_private (clipboard); - GdkContentFormats *formats; + GdkContentFormats *formats, *mime_formats; GTask *task; + GType gtype; g_return_if_fail (GDK_IS_CLIPBOARD (clipboard)); g_return_if_fail (priv->local); @@ -873,11 +890,42 @@ gdk_clipboard_write_async (GdkClipboard *clipboard, return; } - g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, - _("FIXME: Implement serializing.")); + mime_formats = gdk_content_formats_new ((const gchar *[2]) { mime_type, NULL }, 1); + mime_formats = gdk_content_formats_union_serialize_gtypes (mime_formats); + if (gdk_content_formats_match (mime_formats, formats, >ype, NULL)) + { + GValue value = G_VALUE_INIT; + GError *error = NULL; + + g_assert (gtype != G_TYPE_INVALID); + + g_value_init (&value, gtype); + if (gdk_content_provider_get_value (priv->content, &value, &error)) + { + gdk_content_serialize_async (stream, + mime_type, + &value, + io_priority, + cancellable, + gdk_clipboard_write_serialize_done, + g_object_ref (task)); + } + else + { + g_task_return_error (task, error); + } + + g_value_unset (&value); + } + else + { + g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + _("No compatible formats to transfer clipboard contents.")); + } + + gdk_content_formats_unref (mime_formats); gdk_content_formats_unref (formats); g_object_unref (task); - return; } gboolean @@ -954,6 +1002,7 @@ gdk_clipboard_content_changed_cb (GdkContentProvider *provider, GdkContentFormats *formats; formats = gdk_content_provider_ref_formats (provider); + formats = gdk_content_formats_union_serialize_mime_types (formats); gdk_clipboard_claim (clipboard, formats, TRUE, provider); @@ -1000,6 +1049,7 @@ gdk_clipboard_set_content (GdkClipboard *clipboard, return; formats = gdk_content_provider_ref_formats (provider); + formats = gdk_content_formats_union_serialize_mime_types (formats); } else { diff --git a/gdk/gdkcontentserializer.c b/gdk/gdkcontentserializer.c new file mode 100644 index 0000000000..6860c97cb4 --- /dev/null +++ b/gdk/gdkcontentserializer.c @@ -0,0 +1,562 @@ +/* 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 . + */ + +#include "config.h" + +#include + +#include "gdkcontentserializer.h" + +#include "gdkcontentformats.h" + +#include +#include + +typedef struct _Serializer Serializer; + +struct _Serializer +{ + const char * mime_type; /* interned */ + GType type; + GdkContentSerializeFunc serialize; + gpointer data; + GDestroyNotify notify; +}; + +GQueue serializers = G_QUEUE_INIT; + +static void init (void); + +#define GDK_CONTENT_SERIALIZER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_CONTENT_SERIALIZER, GdkContentSerializerClass)) +#define GDK_IS_CONTENT_SERIALIZER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_CONTENT_SERIALIZER)) +#define GDK_CONTENT_SERIALIZER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_CONTENT_SERIALIZER, GdkContentSerializerClass)) + +typedef struct _GdkContentSerializerClass GdkContentSerializerClass; + +struct _GdkContentSerializer +{ + GObject parent_instance; + + const char *mime_type; /* interned */ + GValue value; + GOutputStream *stream; + int priority; + GCancellable *cancellable; + gpointer user_data; + GAsyncReadyCallback callback; + gpointer callback_data; + + GError *error; + gboolean returned; +}; + +struct _GdkContentSerializerClass +{ + GObjectClass parent_class; +}; + +static gpointer +gdk_content_serializer_async_result_get_user_data (GAsyncResult *res) +{ + return GDK_CONTENT_SERIALIZER (res)->callback_data; +} + +static GObject * +gdk_content_serializer_async_result_get_source_object (GAsyncResult *res) +{ + return NULL; +} + +static void +gdk_content_serializer_async_result_iface_init (GAsyncResultIface *iface) +{ + iface->get_user_data = gdk_content_serializer_async_result_get_user_data; + iface->get_source_object = gdk_content_serializer_async_result_get_source_object; +} + +G_DEFINE_TYPE_WITH_CODE (GdkContentSerializer, gdk_content_serializer, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_RESULT, gdk_content_serializer_async_result_iface_init)) + +static void +gdk_content_serializer_finalize (GObject *object) +{ + GdkContentSerializer *serializer = GDK_CONTENT_SERIALIZER (object); + + g_value_unset (&serializer->value); + g_clear_object (&serializer->stream); + g_clear_object (&serializer->cancellable); + g_clear_error (&serializer->error); + + G_OBJECT_CLASS (gdk_content_serializer_parent_class)->finalize (object); +} + +static void +gdk_content_serializer_class_init (GdkContentSerializerClass *content_serializer_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (content_serializer_class); + + object_class->finalize = gdk_content_serializer_finalize; +} + +static void +gdk_content_serializer_init (GdkContentSerializer *content_serializer) +{ +} + +static void +gdk_content_serializer_run (const char *mime_type, + const GValue *value, + GOutputStream *stream, + int priority, + GCancellable *cancellable, + GdkContentSerializeFunc serialize_func, + gpointer user_data, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + GdkContentSerializer *serializer; + + serializer = g_object_new (GDK_TYPE_CONTENT_SERIALIZER, NULL); + + serializer->mime_type = mime_type; + g_value_init (&serializer->value, G_VALUE_TYPE (value)); + g_value_copy (value, &serializer->value); + serializer->stream = g_object_ref (stream); + serializer->priority = priority; + if (cancellable) + serializer->cancellable = g_object_ref (cancellable); + serializer->user_data = user_data; + serializer->callback = callback; + serializer->callback_data = callback_data; + + serialize_func (serializer); +} + +const char * +gdk_content_serializer_get_mime_type (GdkContentSerializer *serializer) +{ + g_return_val_if_fail (GDK_IS_CONTENT_SERIALIZER (serializer), NULL); + + return serializer->mime_type; +} + +GType +gdk_content_serializer_get_gtype (GdkContentSerializer *serializer) +{ + g_return_val_if_fail (GDK_IS_CONTENT_SERIALIZER (serializer), G_TYPE_INVALID); + + return G_VALUE_TYPE (&serializer->value); +} + +const GValue * +gdk_content_serializer_get_value (GdkContentSerializer *serializer) +{ + g_return_val_if_fail (GDK_IS_CONTENT_SERIALIZER (serializer), NULL); + + return &serializer->value; +} + +GOutputStream * +gdk_content_serializer_get_output_stream (GdkContentSerializer *serializer) +{ + g_return_val_if_fail (GDK_IS_CONTENT_SERIALIZER (serializer), NULL); + + return serializer->stream; +} + +int +gdk_content_serializer_get_priority (GdkContentSerializer *serializer) +{ + g_return_val_if_fail (GDK_IS_CONTENT_SERIALIZER (serializer), G_PRIORITY_DEFAULT); + + return serializer->priority; +} + +GCancellable * +gdk_content_serializer_get_cancellable (GdkContentSerializer *serializer) +{ + g_return_val_if_fail (GDK_IS_CONTENT_SERIALIZER (serializer), NULL); + + return serializer->cancellable; +} + +gpointer +gdk_content_serializer_get_user_data (GdkContentSerializer *serializer) +{ + g_return_val_if_fail (GDK_IS_CONTENT_SERIALIZER (serializer), NULL); + + return serializer->user_data; +} + +static gboolean +gdk_content_serializer_emit_callback (gpointer data) +{ + GdkContentSerializer *serializer = data; + + if (serializer->callback) + { + serializer->callback (NULL, G_ASYNC_RESULT (serializer), serializer->callback_data); + } + + return G_SOURCE_REMOVE; +} + +void +gdk_content_serializer_return_success (GdkContentSerializer *serializer) +{ + g_return_if_fail (GDK_IS_CONTENT_SERIALIZER (serializer)); + g_return_if_fail (!serializer->returned); + + serializer->returned = TRUE; + g_idle_add_full (serializer->priority, + gdk_content_serializer_emit_callback, + serializer, + g_object_unref); + /* NB: the idle will destroy our reference */ +} + +void +gdk_content_serializer_return_error (GdkContentSerializer *serializer, + GError *error) +{ + g_return_if_fail (GDK_IS_CONTENT_SERIALIZER (serializer)); + g_return_if_fail (!serializer->returned); + g_return_if_fail (error != NULL); + + serializer->error = error; + /* FIXME: naming */ + gdk_content_serializer_return_success (serializer); +} + +void +gdk_content_register_serializer (GType type, + const char *mime_type, + GdkContentSerializeFunc serialize, + gpointer data, + GDestroyNotify notify) +{ + Serializer *serializer; + + g_return_if_fail (mime_type != NULL); + g_return_if_fail (serialize != NULL); + + init (); + + serializer = g_slice_new0 (Serializer); + + serializer->mime_type = g_intern_string (mime_type); + serializer->type = type; + serializer->serialize = serialize; + serializer->data = data; + serializer->notify = notify; + + g_queue_push_tail (&serializers, serializer); +} + +static Serializer * +lookup_serializer (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 (&serializers); l; l = l->next) + { + Serializer *serializer = l->data; + + if (serializer->mime_type == mime_type && + serializer->type == type) + return serializer; + } + + return NULL; +} + +GdkContentFormats * +gdk_content_formats_union_serialize_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 (&serializers); l; l = l->next) + { + Serializer *serializer = l->data; + + if (gdk_content_formats_contain_mime_type (formats, serializer->mime_type)) + gdk_content_formats_builder_add_gtype (builder, serializer->type); + } + + gdk_content_formats_unref (formats); + + return gdk_content_formats_builder_free (builder); +} + +GdkContentFormats * +gdk_content_formats_union_serialize_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 (&serializers); l; l = l->next) + { + Serializer *serializer = l->data; + + if (gdk_content_formats_contain_gtype (formats, serializer->type)) + gdk_content_formats_builder_add_mime_type (builder, serializer->mime_type); + } + + gdk_content_formats_unref (formats); + + return gdk_content_formats_builder_free (builder); +} + +static void +serialize_not_found (GdkContentSerializer *serializer) +{ + GError *error = g_error_new (G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "Could not convert data from %s to %s", + g_type_name (gdk_content_serializer_get_gtype (serializer)), + gdk_content_serializer_get_mime_type (serializer)); + gdk_content_serializer_return_error (serializer, error); +} + +void +gdk_content_serialize_async (GOutputStream *stream, + const char *mime_type, + const GValue *value, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + Serializer *serializer; + + g_return_if_fail (G_IS_OUTPUT_STREAM (stream)); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + g_return_if_fail (callback != NULL); + + serializer = lookup_serializer (mime_type, G_VALUE_TYPE (value)); + + gdk_content_serializer_run (mime_type, + value, + stream, + io_priority, + cancellable, + serializer ? serializer->serialize : serialize_not_found, + serializer ? serializer->data : NULL, + callback, + user_data); +} + +gboolean +gdk_content_serialize_finish (GAsyncResult *result, + GError **error) +{ + GdkContentSerializer *serializer; + + g_return_val_if_fail (GDK_IS_CONTENT_SERIALIZER (result), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + serializer = GDK_CONTENT_SERIALIZER (result); + + if (serializer->error) + { + if (error) + *error = g_error_copy (serializer->error); + return FALSE; + } + + return TRUE; +} + +/*** SERIALIZERS ***/ + +static void +pixbuf_serializer_finish (GObject *source, + GAsyncResult *res, + gpointer serializer) +{ + GError *error = NULL; + + if (!gdk_pixbuf_save_to_stream_finish (res, &error)) + gdk_content_serializer_return_error (serializer, error); + else + gdk_content_serializer_return_success (serializer); +} + +static void +pixbuf_serializer (GdkContentSerializer *serializer) +{ + const char *name = gdk_content_serializer_get_user_data (serializer); + + gdk_pixbuf_save_to_stream_async (g_value_get_object (gdk_content_serializer_get_value (serializer)), + gdk_content_serializer_get_output_stream (serializer), + name, + gdk_content_serializer_get_cancellable (serializer), + pixbuf_serializer_finish, + serializer, + g_str_equal (name, "png") ? "compression" : NULL, "2", + NULL); +} + +static void +string_serializer_finish (GObject *source, + GAsyncResult *result, + gpointer serializer) +{ + GOutputStream *stream = G_OUTPUT_STREAM (source); + GError *error = NULL; + + if (!g_output_stream_write_all_finish (stream, result, NULL, &error)) + gdk_content_serializer_return_error (serializer, error); + else + gdk_content_serializer_return_success (serializer); +} + +static void +string_serializer (GdkContentSerializer *serializer) +{ + GOutputStream *filter; + GCharsetConverter *converter; + GError *error = NULL; + const char *text; + + converter = g_charset_converter_new (gdk_content_serializer_get_user_data (serializer), + "utf-8", + &error); + if (converter == NULL) + { + gdk_content_serializer_return_error (serializer, error); + return; + } + g_charset_converter_set_use_fallback (converter, TRUE); + + filter = g_converter_output_stream_new (gdk_content_serializer_get_output_stream (serializer), + G_CONVERTER (converter)); + g_object_unref (converter); + + text = g_value_get_string (gdk_content_serializer_get_value (serializer)); + if (text == NULL) + text = ""; + + g_output_stream_write_all_async (filter, + text, + strlen (text) + 1, + gdk_content_serializer_get_priority (serializer), + gdk_content_serializer_get_cancellable (serializer), + string_serializer_finish, + serializer); + g_object_unref (filter); +} + +static void +init (void) +{ + static gboolean initialized = FALSE; + GSList *formats, *f; + const char *charset; + + 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; + + if (!gdk_pixbuf_format_is_writable (fmt)) + continue; + + mimes = gdk_pixbuf_format_get_mime_types (fmt); + for (m = mimes; *m; m++) + { + gdk_content_register_serializer (GDK_TYPE_PIXBUF, + *m, + pixbuf_serializer, + g_strdup (gdk_pixbuf_format_get_name (fmt)), + g_free); + } + g_strfreev (mimes); + } + + g_slist_free (formats); + + gdk_content_register_serializer (G_TYPE_STRING, + "text/plain;charset=utf-8", + string_serializer, + (gpointer) "utf-8", + NULL); + if (!g_get_charset (&charset)) + { + char *mime = g_strdup_printf ("text/plain;charset=%s", charset); + gdk_content_register_serializer (G_TYPE_STRING, + mime, + string_serializer, + mime, + g_free); + } + gdk_content_register_serializer (G_TYPE_STRING, + "text/plain", + string_serializer, + (gpointer) "ASCII", + NULL); +} + diff --git a/gdk/gdkcontentserializer.h b/gdk/gdkcontentserializer.h new file mode 100644 index 0000000000..b1acf7fe28 --- /dev/null +++ b/gdk/gdkcontentserializer.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 . + */ + +#ifndef __GTK_CONTENT_SERIALIZER_H__ +#define __GTK_CONTENT_SERIALIZER_H__ + +#if !defined (__GDK_H_INSIDE__) && !defined (GDK_COMPILATION) +#error "Only can be included directly." +#endif + + +#include +#include +#include + +G_BEGIN_DECLS + +#define GDK_TYPE_CONTENT_SERIALIZER (gdk_content_serializer_get_type ()) +#define GDK_CONTENT_SERIALIZER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDK_TYPE_CONTENT_SERIALIZER, GdkContentSerializer)) +#define GDK_IS_CONTENT_SERIALIZER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDK_TYPE_CONTENT_SERIALIZER)) + +typedef struct _GdkContentSerializer GdkContentSerializer; + +typedef void (* GdkContentSerializeFunc) (GdkContentSerializer *serializer); + +GDK_AVAILABLE_IN_3_94 +GType gdk_content_serializer_get_type (void) G_GNUC_CONST; + +GDK_AVAILABLE_IN_3_94 +const char * gdk_content_serializer_get_mime_type (GdkContentSerializer *serializer); +GDK_AVAILABLE_IN_3_94 +GType gdk_content_serializer_get_gtype (GdkContentSerializer *serializer); +GDK_AVAILABLE_IN_3_94 +const GValue * gdk_content_serializer_get_value (GdkContentSerializer *serializer); +GDK_AVAILABLE_IN_3_94 +GOutputStream * gdk_content_serializer_get_output_stream (GdkContentSerializer *serializer); +GDK_AVAILABLE_IN_3_94 +int gdk_content_serializer_get_priority (GdkContentSerializer *serializer); +GDK_AVAILABLE_IN_3_94 +GCancellable * gdk_content_serializer_get_cancellable (GdkContentSerializer *serializer); +GDK_AVAILABLE_IN_3_94 +gpointer gdk_content_serializer_get_user_data (GdkContentSerializer *serializer); + +GDK_AVAILABLE_IN_3_94 +void gdk_content_serializer_return_success (GdkContentSerializer *serializer); +GDK_AVAILABLE_IN_3_94 +void gdk_content_serializer_return_error (GdkContentSerializer *serializer, + GError *error); + +GDK_AVAILABLE_IN_3_94 +GdkContentFormats * gdk_content_formats_union_serialize_gtypes (GdkContentFormats *formats); +GDK_AVAILABLE_IN_3_94 +GdkContentFormats * gdk_content_formats_union_serialize_mime_types (GdkContentFormats *formats); + +GDK_AVAILABLE_IN_3_94 +void gdk_content_register_serializer (GType type, + const char *mime_type, + GdkContentSerializeFunc serialize, + gpointer data, + GDestroyNotify notify); +GDK_AVAILABLE_IN_3_94 +void gdk_content_serialize_async (GOutputStream *stream, + const char *mime_type, + const GValue *value, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +GDK_AVAILABLE_IN_3_94 +gboolean gdk_content_serialize_finish (GAsyncResult *result, + GError **error); + + +G_END_DECLS + +#endif /* __GDK_CONTENT_SERIALIZER_H__ */ diff --git a/gdk/meson.build b/gdk/meson.build index b13e9c79da..90f8001aac 100644 --- a/gdk/meson.build +++ b/gdk/meson.build @@ -7,6 +7,7 @@ gdk_public_sources = files([ 'gdkcontentformats.c', 'gdkcontentprovider.c', 'gdkcontentproviderimpl.c', + 'gdkcontentserializer.c', 'gdkcursor.c', 'gdkdevice.c', 'gdkdevicepad.c', @@ -51,6 +52,7 @@ gdk_public_headers = files([ 'gdkcontentformats.h', 'gdkcontentprovider.h', 'gdkcontentproviderimpl.h', + 'gdkcontentserializer.h', 'gdkcursor.h', 'gdkdevice.h', 'gdkdevicepad.h',