This makes the behavior consistent and ensures the data is flushed and any potential errors are properly reported back. Previously we would just unref() it, which would just lose all the errors.
972 lines
29 KiB
C
972 lines
29 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 "gdkcontentserializer.h"
|
|
|
|
#include "gdkcontentformats.h"
|
|
#include "gdkpixbuf.h"
|
|
#include "filetransferportalprivate.h"
|
|
#include "gdktextureprivate.h"
|
|
#include "gdkrgba.h"
|
|
|
|
#include <gdk-pixbuf/gdk-pixbuf.h>
|
|
#include <string.h>
|
|
|
|
|
|
/**
|
|
* SECTION:gdkcontentserializer
|
|
* @Short_description: Serialize content for transfer
|
|
* @Title: GdkContentSerializer
|
|
* @See_also: #GdkContentDeserializer, #GdkContentProvider
|
|
*
|
|
* A GdkContentSerializer is used to serialize content for inter-application
|
|
* data transfers.
|
|
*/
|
|
|
|
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;
|
|
gboolean returned;
|
|
GCancellable *cancellable;
|
|
gpointer user_data;
|
|
GAsyncReadyCallback callback;
|
|
gpointer callback_data;
|
|
|
|
gpointer task_data;
|
|
GDestroyNotify task_notify;
|
|
|
|
GError *error;
|
|
};
|
|
|
|
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);
|
|
|
|
if (serializer->task_notify)
|
|
serializer->task_notify (serializer->task_data);
|
|
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* gdk_content_serializer_get_mime_type:
|
|
* @serializer: a #GdkContentSerializer
|
|
*
|
|
* Gets the mime type to serialize to.
|
|
*
|
|
* Returns: (transfer none): the mime type for the current operation
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* gdk_content_serializer_get_gtype:
|
|
* @serializer: a #GdkContentSerializer
|
|
*
|
|
* Gets the GType to of the object to serialize.
|
|
*
|
|
* Returns: the GType for the current operation
|
|
*/
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* gdk_content_serializer_get_value:
|
|
* @serializer: a #GdkContentSerializer
|
|
*
|
|
* Gets the #GValue to read the object to serialize from.
|
|
*
|
|
* Returns: (transfer none): the #GValue for the current operation
|
|
*/
|
|
const GValue *
|
|
gdk_content_serializer_get_value (GdkContentSerializer *serializer)
|
|
{
|
|
g_return_val_if_fail (GDK_IS_CONTENT_SERIALIZER (serializer), NULL);
|
|
|
|
return &serializer->value;
|
|
}
|
|
|
|
/**
|
|
* gdk_content_serializer_get_output_stream:
|
|
* @serializer: a #GdkContentSerializer
|
|
*
|
|
* Gets the output stream that was passed to gdk_content_serialize_async().
|
|
*
|
|
* Returns: (transfer none): the output stream for the current operation
|
|
*/
|
|
GOutputStream *
|
|
gdk_content_serializer_get_output_stream (GdkContentSerializer *serializer)
|
|
{
|
|
g_return_val_if_fail (GDK_IS_CONTENT_SERIALIZER (serializer), NULL);
|
|
|
|
return serializer->stream;
|
|
}
|
|
|
|
/**
|
|
* gdk_content_serializer_get_priority:
|
|
* @serializer: a #GdkContentSerializer
|
|
*
|
|
* Gets the io priority that was passed to gdk_content_serialize_async().
|
|
*
|
|
* Returns: the io priority for the current operation
|
|
*/
|
|
int
|
|
gdk_content_serializer_get_priority (GdkContentSerializer *serializer)
|
|
{
|
|
g_return_val_if_fail (GDK_IS_CONTENT_SERIALIZER (serializer), G_PRIORITY_DEFAULT);
|
|
|
|
return serializer->priority;
|
|
}
|
|
|
|
/**
|
|
* gdk_content_serializer_get_cancellable:
|
|
* @serializer: a #GdkContentSerializer
|
|
*
|
|
* Gets the cancellable that was passed to gdk_content_serialize_async().
|
|
*
|
|
* Returns: (transfer none): the cancellable for the current operation
|
|
*/
|
|
GCancellable *
|
|
gdk_content_serializer_get_cancellable (GdkContentSerializer *serializer)
|
|
{
|
|
g_return_val_if_fail (GDK_IS_CONTENT_SERIALIZER (serializer), NULL);
|
|
|
|
return serializer->cancellable;
|
|
}
|
|
|
|
/**
|
|
* gdk_content_serializer_get_user_data:
|
|
* @serializer: a #GdkContentSerializer
|
|
*
|
|
* Gets the user data that was passed when the serializer was registered.
|
|
*
|
|
* Returns: (transfer none): the user data for this serializer
|
|
*/
|
|
gpointer
|
|
gdk_content_serializer_get_user_data (GdkContentSerializer *serializer)
|
|
{
|
|
g_return_val_if_fail (GDK_IS_CONTENT_SERIALIZER (serializer), NULL);
|
|
|
|
return serializer->user_data;
|
|
}
|
|
|
|
/**
|
|
* gdk_content_serializer_set_task_data:
|
|
* @serializer: a #GdkContentSerializer
|
|
* @data: data to associate with this operation
|
|
* @notify: destroy notify for @data
|
|
*
|
|
* Associate data with the current serialization operation.
|
|
*/
|
|
void
|
|
gdk_content_serializer_set_task_data (GdkContentSerializer *serializer,
|
|
gpointer data,
|
|
GDestroyNotify notify)
|
|
{
|
|
g_return_if_fail (GDK_IS_CONTENT_SERIALIZER (serializer));
|
|
|
|
if (serializer->task_notify)
|
|
serializer->task_notify (serializer->task_data);
|
|
|
|
serializer->task_data = data;
|
|
serializer->task_notify = notify;
|
|
}
|
|
|
|
/**
|
|
* gdk_content_serializer_get_task_data:
|
|
* @serializer: a #GdkContentSerializer
|
|
*
|
|
* Gets the data that was associated with @serializer via gdk_content_serializer_set_task_data().
|
|
*
|
|
* Returns: (transfer none): the task data for @serializer
|
|
*/
|
|
gpointer
|
|
gdk_content_serializer_get_task_data (GdkContentSerializer *serializer)
|
|
{
|
|
g_return_val_if_fail (GDK_IS_CONTENT_SERIALIZER (serializer), NULL);
|
|
|
|
return serializer->task_data;
|
|
}
|
|
|
|
static void
|
|
gdk_content_serializer_close_finish (GObject *stream,
|
|
GAsyncResult *res,
|
|
gpointer data)
|
|
{
|
|
GdkContentSerializer *serializer = data;
|
|
|
|
g_output_stream_close_finish (G_OUTPUT_STREAM (stream),
|
|
res,
|
|
serializer->error ? NULL : &serializer->error);
|
|
|
|
if (serializer->callback)
|
|
{
|
|
serializer->callback (NULL, G_ASYNC_RESULT (serializer), serializer->callback_data);
|
|
}
|
|
|
|
g_object_unref (serializer);
|
|
}
|
|
|
|
/**
|
|
* gdk_content_serializer_return_success:
|
|
* @serializer: a #GdkContentSerializer
|
|
*
|
|
* Indicate that the serialization has been successfully completed.
|
|
*
|
|
* The serializer will close the output stream and report success to the callback.
|
|
*/
|
|
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_output_stream_close_async (serializer->stream,
|
|
serializer->priority,
|
|
serializer->cancellable,
|
|
gdk_content_serializer_close_finish,
|
|
serializer);
|
|
}
|
|
|
|
/**
|
|
* gdk_content_serializer_return_error:
|
|
* @serializer: a #GdkContentSerializer
|
|
* @error: a #GError
|
|
*
|
|
* Indicate that the serialization has ended with an error.
|
|
* This function consumes @error.
|
|
*/
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* gdk_content_register_serializer:
|
|
* @type: the type of objects that the function can serialize
|
|
* @mime_type: the mime type to serialize to
|
|
* @serialize: the callback
|
|
* @data: data that @serialize can access
|
|
* @notify: destroy notify for @data
|
|
*
|
|
* Registers a function to convert objects of the given @type to
|
|
* a serialized representation with the given mime type.
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* gdk_content_formats_union_serialize_gtypes:
|
|
* @formats: (transfer full): a #GdkContentFormats
|
|
*
|
|
* Add GTypes for the mime types in @formats for which serializers are
|
|
* registered.
|
|
*
|
|
* Return: a new #GdkContentFormats
|
|
*/
|
|
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_to_formats (builder);
|
|
}
|
|
|
|
/**
|
|
* gdk_content_formats_union_serialize_mime_types:
|
|
* @formats: (transfer full): a #GdkContentFormats
|
|
*
|
|
* Add mime types for GTypes in @formats for which serializers are
|
|
* registered.
|
|
*
|
|
* Return: a new #GdkContentFormats
|
|
*/
|
|
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_to_formats (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);
|
|
}
|
|
|
|
/**
|
|
* gdk_content_serialize_async:
|
|
* @stream: a #GOutputStream to write the serialized content to
|
|
* @mime_type: the mime type to serialize to
|
|
* @value: the content to serialize
|
|
* @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
|
|
*
|
|
* Serialize content and write it to the given output stream, asynchronously.
|
|
* When the operation is finished, the @stream will be closed and @callback
|
|
* will be called. You can then call gdk_content_serialize_finish() to get
|
|
* the result of the operation.
|
|
*/
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* gdk_content_serialize_finish:
|
|
* @result: the #GAsyncResult
|
|
* @error: return location for an error
|
|
*
|
|
* Finishes a content serialization operation.
|
|
*
|
|
* Returns: %TRUE if the operation was successful, %FALSE if an
|
|
* error occurred. In this case, @error is set
|
|
*/
|
|
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 GValue *value;
|
|
GdkPixbuf *pixbuf;
|
|
const char *name;
|
|
|
|
name = gdk_content_serializer_get_user_data (serializer);
|
|
value = gdk_content_serializer_get_value (serializer);
|
|
|
|
if (G_VALUE_HOLDS (value, GDK_TYPE_PIXBUF))
|
|
{
|
|
pixbuf = g_value_dup_object (value);
|
|
}
|
|
else if (G_VALUE_HOLDS (value, GDK_TYPE_TEXTURE))
|
|
{
|
|
GdkTexture *texture = g_value_get_object (value);
|
|
cairo_surface_t *surface = gdk_texture_download_surface (texture);
|
|
pixbuf = gdk_pixbuf_get_from_surface (surface,
|
|
0, 0,
|
|
gdk_texture_get_width (texture), gdk_texture_get_height (texture));
|
|
cairo_surface_destroy (surface);
|
|
}
|
|
else
|
|
{
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
gdk_pixbuf_save_to_stream_async (pixbuf,
|
|
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);
|
|
g_object_unref (pixbuf);
|
|
}
|
|
|
|
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),
|
|
gdk_content_serializer_get_priority (serializer),
|
|
gdk_content_serializer_get_cancellable (serializer),
|
|
string_serializer_finish,
|
|
serializer);
|
|
g_object_unref (filter);
|
|
}
|
|
|
|
static void
|
|
file_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
|
|
file_uri_serializer (GdkContentSerializer *serializer)
|
|
{
|
|
GFile *file;
|
|
GString *str;
|
|
const GValue *value;
|
|
char *uri;
|
|
|
|
str = g_string_new (NULL);
|
|
value = gdk_content_serializer_get_value (serializer);
|
|
|
|
if (G_VALUE_HOLDS (value, G_TYPE_FILE))
|
|
{
|
|
file = g_value_get_object (gdk_content_serializer_get_value (serializer));
|
|
if (file)
|
|
{
|
|
uri = g_file_get_uri (file);
|
|
g_string_append (str, uri);
|
|
g_free (uri);
|
|
}
|
|
else
|
|
{
|
|
g_string_append (str, "# GTK does not crash when copying a NULL GFile!");
|
|
}
|
|
g_string_append (str, "\r\n");
|
|
}
|
|
else if (G_VALUE_HOLDS (value, GDK_TYPE_FILE_LIST))
|
|
{
|
|
GSList *l;
|
|
|
|
for (l = g_value_get_boxed (value); l; l = l->next)
|
|
{
|
|
uri = g_file_get_uri (l->data);
|
|
g_string_append (str, uri);
|
|
g_free (uri);
|
|
g_string_append (str, "\r\n");
|
|
}
|
|
}
|
|
|
|
g_output_stream_write_all_async (gdk_content_serializer_get_output_stream (serializer),
|
|
str->str,
|
|
str->len,
|
|
gdk_content_serializer_get_priority (serializer),
|
|
gdk_content_serializer_get_cancellable (serializer),
|
|
file_serializer_finish,
|
|
serializer);
|
|
gdk_content_serializer_set_task_data (serializer, g_string_free (str, FALSE), g_free);
|
|
}
|
|
|
|
static void
|
|
file_text_serializer (GdkContentSerializer *serializer)
|
|
{
|
|
const GValue *value;
|
|
char *path = NULL;
|
|
|
|
value = gdk_content_serializer_get_value (serializer);
|
|
|
|
if (G_VALUE_HOLDS (value, G_TYPE_FILE))
|
|
{
|
|
GFile *file;
|
|
|
|
file = g_value_get_object (value);
|
|
if (file)
|
|
{
|
|
path = g_file_get_path (file);
|
|
if (path == NULL)
|
|
path = g_file_get_uri (file);
|
|
}
|
|
}
|
|
else if (G_VALUE_HOLDS (value, GDK_TYPE_FILE_LIST))
|
|
{
|
|
GString *str;
|
|
GSList *l;
|
|
|
|
str = g_string_new (NULL);
|
|
|
|
for (l = g_value_get_boxed (value); l; l = l->next)
|
|
{
|
|
path = g_file_get_path (l->data);
|
|
if (path == NULL)
|
|
path = g_file_get_uri (l->data);
|
|
g_string_append (str, path);
|
|
g_free (path);
|
|
if (l->next)
|
|
g_string_append (str, " ");
|
|
}
|
|
path = g_string_free (str, FALSE);
|
|
}
|
|
|
|
g_assert (path != NULL);
|
|
|
|
g_output_stream_write_all_async (gdk_content_serializer_get_output_stream (serializer),
|
|
path,
|
|
strlen (path),
|
|
gdk_content_serializer_get_priority (serializer),
|
|
gdk_content_serializer_get_cancellable (serializer),
|
|
file_serializer_finish,
|
|
serializer);
|
|
gdk_content_serializer_set_task_data (serializer, path, g_free);
|
|
}
|
|
|
|
static void
|
|
color_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
|
|
color_serializer (GdkContentSerializer *serializer)
|
|
{
|
|
const GValue *value;
|
|
GdkRGBA *rgba;
|
|
guint16 *data;
|
|
|
|
value = gdk_content_serializer_get_value (serializer);
|
|
rgba = g_value_get_boxed (value);
|
|
data = g_new0 (guint16, 4);
|
|
if (rgba)
|
|
{
|
|
data[0] = (guint16) (rgba->red * 65535);
|
|
data[1] = (guint16) (rgba->green * 65535);
|
|
data[2] = (guint16) (rgba->blue * 65535);
|
|
data[3] = (guint16) (rgba->alpha * 65535);
|
|
}
|
|
|
|
g_output_stream_write_all_async (gdk_content_serializer_get_output_stream (serializer),
|
|
data,
|
|
4 * sizeof (guint16),
|
|
gdk_content_serializer_get_priority (serializer),
|
|
gdk_content_serializer_get_cancellable (serializer),
|
|
color_serializer_finish,
|
|
serializer);
|
|
gdk_content_serializer_set_task_data (serializer, data, g_free);
|
|
}
|
|
|
|
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;
|
|
char *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;
|
|
char **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_TEXTURE,
|
|
*m,
|
|
pixbuf_serializer,
|
|
gdk_pixbuf_format_get_name (fmt),
|
|
g_free);
|
|
gdk_content_register_serializer (GDK_TYPE_PIXBUF,
|
|
*m,
|
|
pixbuf_serializer,
|
|
gdk_pixbuf_format_get_name (fmt),
|
|
g_free);
|
|
}
|
|
g_strfreev (mimes);
|
|
}
|
|
|
|
g_slist_free (formats);
|
|
|
|
#if defined(G_OS_UNIX) && !defined(__APPLE__)
|
|
file_transfer_portal_register ();
|
|
#endif
|
|
|
|
gdk_content_register_serializer (G_TYPE_FILE,
|
|
"text/uri-list",
|
|
file_uri_serializer,
|
|
NULL,
|
|
NULL);
|
|
gdk_content_register_serializer (G_TYPE_FILE,
|
|
"text/plain;charset=utf-8",
|
|
file_text_serializer,
|
|
NULL,
|
|
NULL);
|
|
|
|
gdk_content_register_serializer (GDK_TYPE_FILE_LIST,
|
|
"text/uri-list",
|
|
file_uri_serializer,
|
|
NULL,
|
|
NULL);
|
|
gdk_content_register_serializer (GDK_TYPE_FILE_LIST,
|
|
"text/plain;charset=utf-8",
|
|
file_text_serializer,
|
|
NULL,
|
|
NULL);
|
|
|
|
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,
|
|
(gpointer) charset,
|
|
NULL);
|
|
}
|
|
gdk_content_register_serializer (G_TYPE_STRING,
|
|
"text/plain",
|
|
string_serializer,
|
|
(gpointer) "ASCII",
|
|
NULL);
|
|
|
|
gdk_content_register_serializer (GDK_TYPE_RGBA,
|
|
"application/x-color",
|
|
color_serializer,
|
|
NULL,
|
|
NULL);
|
|
}
|
|
|