gtk/gdk/gdkcontentdeserializer.c
Matthias Clasen 69fb3648b2 Tweak the file transfer portal _again_
This is a neverending story. I was seeing problems in tests where
the nested mainloop was picking up unrelated timeouts.

Break down and make this async. This changes the ordering in which
the (de)serializers are registered. If this is causing issues, we
can introduce priorities or something else.
2020-01-17 23:46:37 -05:00

921 lines
28 KiB
C

/* 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 "filetransferportalprivate.h"
#include "gdktexture.h"
#include "gdkrgbaprivate.h"
#include <gdk-pixbuf/gdk-pixbuf.h>
/**
* SECTION:gdkcontentdeserializer
* @Short_description: Deserialize content for transfer
* @Title: GdkContentDeserializer
* @See_also: #GdkContentSerializer
*
* A GdkContentDeserializer is used to deserialize content received via
* inter-application data transfers.
*/
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;
gpointer task_data;
GDestroyNotify task_notify;
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);
if (deserializer->task_notify)
deserializer->task_notify (deserializer->task_data);
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);
}
/**
* gdk_content_deserializer_get_mime_type:
* @deserializer: a #GdkContentDeserializer
*
* Gets the mime type to deserialize from.
*
* Returns: (transfer none): the mime type for the current operation
*/
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;
}
/**
* gdk_content_deserializer_get_gtype:
* @deserializer: a #GdkContentDeserializer
*
* Gets the GType to create an instance of.
*
* Returns: the GType for the current operation
*/
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);
}
/**
* gdk_content_deserializer_get_value:
* @deserializer: a #GdkContentDeserializer
*
* Gets the #GValue to store the deserialized object in.
*
* Returns: (transfer none): the #GValue for the current operation
*/
GValue *
gdk_content_deserializer_get_value (GdkContentDeserializer *deserializer)
{
g_return_val_if_fail (GDK_IS_CONTENT_DESERIALIZER (deserializer), NULL);
return &deserializer->value;
}
/**
* gdk_content_deserializer_get_input_stream:
* @deserializer: a #GdkContentDeserializer
*
* Gets the input stream that was passed to gdk_content_deserialize_async().
*
* Returns: (transfer none): the input stream for the current operation
*/
GInputStream *
gdk_content_deserializer_get_input_stream (GdkContentDeserializer *deserializer)
{
g_return_val_if_fail (GDK_IS_CONTENT_DESERIALIZER (deserializer), NULL);
return deserializer->stream;
}
/**
* gdk_content_deserializer_get_priority:
* @deserializer: a #GdkContentDeserializer
*
* Gets the io priority that was passed to gdk_content_deserialize_async().
*
* Returns: the io priority for the current operation
*/
int
gdk_content_deserializer_get_priority (GdkContentDeserializer *deserializer)
{
g_return_val_if_fail (GDK_IS_CONTENT_DESERIALIZER (deserializer), G_PRIORITY_DEFAULT);
return deserializer->priority;
}
/**
* gdk_content_deserializer_get_cancellable:
* @deserializer: a #GdkContentDeserializer
*
* Gets the cancellable that was passed to gdk_content_deserialize_async().
*
* Returns: (transfer none): the cancellable for the current operation
*/
GCancellable *
gdk_content_deserializer_get_cancellable (GdkContentDeserializer *deserializer)
{
g_return_val_if_fail (GDK_IS_CONTENT_DESERIALIZER (deserializer), NULL);
return deserializer->cancellable;
}
/**
* gdk_content_deserializer_get_user_data:
* @deserializer: a #GdkContentDeserializer
*
* Gets the user data that was passed when the deserializer was registered.
*
* Returns: (transfer none): the user data for this deserializer
*/
gpointer
gdk_content_deserializer_get_user_data (GdkContentDeserializer *deserializer)
{
g_return_val_if_fail (GDK_IS_CONTENT_DESERIALIZER (deserializer), NULL);
return deserializer->user_data;
}
/**
* gdk_content_deserializer_set_task_data:
* @deserializer: a #GdkContentDeserializer
* @data: data to associate with this operation
* @notify: destroy notify for @data
*
* Associate data with the current deserialization operation.
*/
void
gdk_content_deserializer_set_task_data (GdkContentDeserializer *deserializer,
gpointer data,
GDestroyNotify notify)
{
g_return_if_fail (GDK_IS_CONTENT_DESERIALIZER (deserializer));
if (deserializer->task_notify)
deserializer->task_notify (deserializer->task_data);
deserializer->task_data = data;
deserializer->task_notify = notify;
}
/**
* gdk_content_deserializer_get_task_data:
* @deserializer: a #GdkContentDeserializer
*
* Gets the data that was associated with @deserializer via gdk_content_deserializer_set_task_data().
*
* Returns: (transfer none): the task data for @deserializer
*/
gpointer
gdk_content_deserializer_get_task_data (GdkContentDeserializer *deserializer)
{
g_return_val_if_fail (GDK_IS_CONTENT_DESERIALIZER (deserializer), NULL);
return deserializer->task_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;
}
/**
* gdk_content_deserializer_return_success:
* @deserializer: a #GdkContentDeserializer
*
* Indicate that the deserialization has been successfully completed.
*/
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 */
}
/**
* gdk_content_deserializer_return_error:
* @deserializer: a #GdkContentDeserializer
* @error: a #GError
*
* Indicate that the deserialization has ended with an error.
* This function consumes @error.
*/
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);
}
/**
* gdk_content_register_deserializer:
* @mime_type: the mime type which the function can deserialize from
* @type: the type of objects that the function creates
* @deserialize: the callback
* @data: data that @deserialize can access
* @notify: destroy notify for @data
*
* Registers a function to create objects of a given @type from
* a serialized representation with the given mime type.
*/
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;
}
/**
* gdk_content_formats_union_deserialize_gtypes:
* @formats: a #GdkContentFormats
*
* Add GTypes for mime types in @formats for which deserializers are
* registered.
*
* Return: a new #GdkContentFormats
*/
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_to_formats (builder);
}
/**
* gdk_content_formats_union_deserialize_mime_types:
* @formats: a #GdkContentFormats
*
* Add mime types for GTypes in @formats for which deserializers are
* registered.
*
* Return: a new #GdkContentFormats
*/
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_to_formats (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);
}
/**
* gdk_content_deserialize_async:
* @stream: a #GInputStream to read the serialized content from
* @mime_type: the mime type to deserialize from
* @type: the GType to deserialize from
* @io_priority: the io priority of the operation
* @cancellable: (nullable): optional #GCancellable object
* @callback: (scope async): callback to call when the operation is done
* @user_data: (closure): data to pass to the callback function
*
* Read content from the given input stream and deserialize it, asynchronously.
* When the operation is finished, @callback will be called. You can then
* call gdk_content_deserialize_finish() to get the result of the operation.
*/
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);
}
/**
* gdk_content_deserialize_finish:
* @result: the #GAsyncResult
* @value: return location for the result of the operation
* @error: return location for an error
*
* Finishes a content deserialization operation.
*
* Returns: %TRUE if the operation was successful. In this case, @value is set.
* %FALSE if an error occurred. In this case, @error is set
*/
gboolean
gdk_content_deserialize_finish (GAsyncResult *result,
GValue *value,
GError **error)
{
GdkContentDeserializer *deserializer;
g_return_val_if_fail (GDK_IS_CONTENT_DESERIALIZER (result), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
deserializer = GDK_CONTENT_DESERIALIZER (result);
g_return_val_if_fail (G_VALUE_HOLDS (value, G_VALUE_TYPE (&deserializer->value)), FALSE);
if (deserializer->error)
{
if (error)
*error = g_error_copy (deserializer->error);
return FALSE;
}
g_value_copy (&deserializer->value, value);
return TRUE;
}
/*** DESERIALIZERS ***/
static void
pixbuf_deserializer_finish (GObject *source,
GAsyncResult *res,
gpointer deserializer)
{
GdkPixbuf *pixbuf;
GValue *value;
GError *error = NULL;
pixbuf = gdk_pixbuf_new_from_stream_finish (res, &error);
if (pixbuf == NULL)
{
gdk_content_deserializer_return_error (deserializer, error);
return;
}
value = gdk_content_deserializer_get_value (deserializer);
if (G_VALUE_HOLDS (value, GDK_TYPE_PIXBUF))
{
g_value_take_object (value, pixbuf);
}
else if (G_VALUE_HOLDS (value, GDK_TYPE_TEXTURE))
{
GdkTexture *texture;
texture = gdk_texture_new_for_pixbuf (pixbuf);
g_object_unref (pixbuf);
g_value_take_object (value, texture);
}
else
{
g_assert_not_reached ();
}
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
string_deserializer_finish (GObject *source,
GAsyncResult *result,
gpointer deserializer)
{
GOutputStream *stream = G_OUTPUT_STREAM (source);
GError *error = NULL;
gssize written;
written = g_output_stream_splice_finish (stream, result, &error);
if (written < 0)
{
gdk_content_deserializer_return_error (deserializer, error);
return;
}
else if (written == 0)
{
/* Never return NULL, we only return that on error */
g_value_set_string (gdk_content_deserializer_get_value (deserializer), "");
}
else
{
g_value_take_string (gdk_content_deserializer_get_value (deserializer),
g_memory_output_stream_steal_data (G_MEMORY_OUTPUT_STREAM (
g_filter_output_stream_get_base_stream (G_FILTER_OUTPUT_STREAM (stream)))));
}
gdk_content_deserializer_return_success (deserializer);
}
static void
string_deserializer (GdkContentDeserializer *deserializer)
{
GOutputStream *output, *filter;
GCharsetConverter *converter;
GError *error = NULL;
converter = g_charset_converter_new ("utf-8",
gdk_content_deserializer_get_user_data (deserializer),
&error);
if (converter == NULL)
{
gdk_content_deserializer_return_error (deserializer, error);
return;
}
g_charset_converter_set_use_fallback (converter, TRUE);
output = g_memory_output_stream_new_resizable ();
filter = g_converter_output_stream_new (output, G_CONVERTER (converter));
g_object_unref (output);
g_object_unref (converter);
g_output_stream_splice_async (filter,
gdk_content_deserializer_get_input_stream (deserializer),
G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
gdk_content_deserializer_get_priority (deserializer),
gdk_content_deserializer_get_cancellable (deserializer),
string_deserializer_finish,
deserializer);
g_object_unref (filter);
}
static void
file_uri_deserializer_finish (GObject *source,
GAsyncResult *result,
gpointer deserializer)
{
GOutputStream *stream = G_OUTPUT_STREAM (source);
GError *error = NULL;
gssize written;
GValue *value;
char *str;
char **uris;
written = g_output_stream_splice_finish (stream, result, &error);
if (written < 0)
{
gdk_content_deserializer_return_error (deserializer, error);
return;
}
/* write terminating NULL */
if (!g_output_stream_write (stream, "", 1, NULL, &error))
{
gdk_content_deserializer_return_error (deserializer, error);
return;
}
str = g_memory_output_stream_steal_data (G_MEMORY_OUTPUT_STREAM (stream));
uris = g_uri_list_extract_uris (str);
g_free (str);
value = gdk_content_deserializer_get_value (deserializer);
if (G_VALUE_HOLDS (value, G_TYPE_FILE))
{
if (uris[0] != NULL)
g_value_take_object (value, g_file_new_for_uri (uris[0]));
}
else
{
GSList *l = NULL;
gsize i;
for (i = 0; uris[i] != NULL; i++)
l = g_slist_prepend (l, g_file_new_for_uri (uris[i]));
g_value_take_boxed (value, g_slist_reverse (l));
}
g_strfreev (uris);
gdk_content_deserializer_return_success (deserializer);
}
static void
file_uri_deserializer (GdkContentDeserializer *deserializer)
{
GOutputStream *output;
output = g_memory_output_stream_new_resizable ();
g_output_stream_splice_async (output,
gdk_content_deserializer_get_input_stream (deserializer),
G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
gdk_content_deserializer_get_priority (deserializer),
gdk_content_deserializer_get_cancellable (deserializer),
file_uri_deserializer_finish,
deserializer);
g_object_unref (output);
}
static void
color_deserializer_finish (GObject *source,
GAsyncResult *result,
gpointer deserializer)
{
GOutputStream *stream = G_OUTPUT_STREAM (source);
GError *error = NULL;
gssize written;
written = g_output_stream_splice_finish (stream, result, &error);
if (written < 0)
{
gdk_content_deserializer_return_error (deserializer, error);
return;
}
else if (written == 0)
{
GdkRGBA black = GDK_RGBA ("000");
/* Never return NULL, we only return that on error */
g_value_set_boxed (gdk_content_deserializer_get_value (deserializer), &black);
}
else
{
guint16 *data;
GdkRGBA rgba;
data = (guint16 *)g_memory_output_stream_get_data (G_MEMORY_OUTPUT_STREAM (stream));
rgba.red = data[0] / 65535.0;
rgba.green = data[1] / 65535.0;
rgba.blue = data[2] / 65535.0;
rgba.alpha = data[3] / 65535.0;
g_value_set_boxed (gdk_content_deserializer_get_value (deserializer), &rgba);
}
gdk_content_deserializer_return_success (deserializer);
}
static void
color_deserializer (GdkContentDeserializer *deserializer)
{
GOutputStream *output;
guint16 *data;
data = g_new0 (guint16, 4);
output = g_memory_output_stream_new (data, 4 * sizeof (guint16), NULL, g_free);
g_output_stream_splice_async (output,
gdk_content_deserializer_get_input_stream (deserializer),
G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
gdk_content_deserializer_get_priority (deserializer),
gdk_content_deserializer_get_cancellable (deserializer),
color_deserializer_finish,
deserializer);
g_object_unref (output);
}
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;
mimes = gdk_pixbuf_format_get_mime_types (fmt);
for (m = mimes; *m; m++)
{
gdk_content_register_deserializer (*m,
GDK_TYPE_TEXTURE,
pixbuf_deserializer,
NULL,
NULL);
gdk_content_register_deserializer (*m,
GDK_TYPE_PIXBUF,
pixbuf_deserializer,
NULL,
NULL);
}
g_strfreev (mimes);
}
g_slist_free (formats);
#ifdef G_OS_UNIX
file_transfer_portal_register ();
#endif
gdk_content_register_deserializer ("text/uri-list",
GDK_TYPE_FILE_LIST,
file_uri_deserializer,
NULL,
NULL);
gdk_content_register_deserializer ("text/uri-list",
G_TYPE_FILE,
file_uri_deserializer,
NULL,
NULL);
gdk_content_register_deserializer ("text/plain;charset=utf-8",
G_TYPE_STRING,
string_deserializer,
(gpointer) "utf-8",
NULL);
if (!g_get_charset (&charset))
{
char *mime = g_strdup_printf ("text/plain;charset=%s", charset);
gdk_content_register_deserializer (mime,
G_TYPE_STRING,
string_deserializer,
mime,
g_free);
}
gdk_content_register_deserializer ("text/plain",
G_TYPE_STRING,
string_deserializer,
(gpointer) "ASCII",
NULL);
gdk_content_register_deserializer ("application/x-color",
GDK_TYPE_RGBA,
color_deserializer,
NULL,
NULL);
}