mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-12-29 15:01:23 +00:00
69fb3648b2
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.
921 lines
28 KiB
C
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);
|
|
}
|
|
|