1/* GTK - The GIMP Toolkit
2 * Copyright (C) 2017 Benjamin Otte
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "config.h"
19
20#include <gio/gio.h>
21
22#include "gdkcontentdeserializer.h"
23
24#include "gdkcontentformats.h"
25#include "filetransferportalprivate.h"
26#include "gdktexture.h"
27#include "gdkrgbaprivate.h"
28#include "loaders/gdkpngprivate.h"
29#include "loaders/gdktiffprivate.h"
30
31#include <gdk-pixbuf/gdk-pixbuf.h>
32
33
34/**
35 * GdkContentDeserializer:
36 *
37 * A `GdkContentDeserializer` is used to deserialize content received via
38 * inter-application data transfers.
39 *
40 * The `GdkContentDeserializer` transforms serialized content that is
41 * identified by a mime type into an object identified by a GType.
42 *
43 * GTK provides serializers and deserializers for common data types
44 * such as text, colors, images or file lists. To register your own
45 * deserialization functions, use [func@content_register_deserializer].
46 *
47 * Also see [class@Gdk.ContentSerializer].
48 */
49
50typedef struct _Deserializer Deserializer;
51
52struct _Deserializer
53{
54 const char * mime_type; /* interned */
55 GType type;
56 GdkContentDeserializeFunc deserialize;
57 gpointer data;
58 GDestroyNotify notify;
59};
60
61GQueue deserializers = G_QUEUE_INIT;
62
63static void init (void);
64
65#define GDK_CONTENT_DESERIALIZER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_CONTENT_DESERIALIZER, GdkContentDeserializerClass))
66#define GDK_IS_CONTENT_DESERIALIZER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_CONTENT_DESERIALIZER))
67#define GDK_CONTENT_DESERIALIZER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_CONTENT_DESERIALIZER, GdkContentDeserializerClass))
68
69typedef struct _GdkContentDeserializerClass GdkContentDeserializerClass;
70
71struct _GdkContentDeserializer
72{
73 GObject parent_instance;
74
75 const char *mime_type; /* interned */
76 GValue value;
77 GInputStream *stream;
78 int priority;
79 gboolean returned;
80 GCancellable *cancellable;
81 gpointer user_data;
82 GAsyncReadyCallback callback;
83 gpointer callback_data;
84
85 gpointer task_data;
86 GDestroyNotify task_notify;
87
88 GError *error;
89};
90
91struct _GdkContentDeserializerClass
92{
93 GObjectClass parent_class;
94};
95
96static gpointer
97gdk_content_deserializer_async_result_get_user_data (GAsyncResult *res)
98{
99 return GDK_CONTENT_DESERIALIZER (res)->callback_data;
100}
101
102static GObject *
103gdk_content_deserializer_async_result_get_source_object (GAsyncResult *res)
104{
105 return NULL;
106}
107
108static void
109gdk_content_deserializer_async_result_iface_init (GAsyncResultIface *iface)
110{
111 iface->get_user_data = gdk_content_deserializer_async_result_get_user_data;
112 iface->get_source_object = gdk_content_deserializer_async_result_get_source_object;
113}
114
115G_DEFINE_TYPE_WITH_CODE (GdkContentDeserializer, gdk_content_deserializer, G_TYPE_OBJECT,
116 G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_RESULT, gdk_content_deserializer_async_result_iface_init))
117
118static void
119gdk_content_deserializer_finalize (GObject *object)
120{
121 GdkContentDeserializer *deserializer = GDK_CONTENT_DESERIALIZER (object);
122
123 g_value_unset (value: &deserializer->value);
124 g_clear_object (&deserializer->stream);
125 g_clear_object (&deserializer->cancellable);
126 g_clear_error (err: &deserializer->error);
127
128 if (deserializer->task_notify)
129 deserializer->task_notify (deserializer->task_data);
130
131 G_OBJECT_CLASS (gdk_content_deserializer_parent_class)->finalize (object);
132}
133
134static void
135gdk_content_deserializer_class_init (GdkContentDeserializerClass *content_deserializer_class)
136{
137 GObjectClass *object_class = G_OBJECT_CLASS (content_deserializer_class);
138
139 object_class->finalize = gdk_content_deserializer_finalize;
140}
141
142static void
143gdk_content_deserializer_init (GdkContentDeserializer *content_deserializer)
144{
145}
146
147static void
148gdk_content_deserializer_run (const char *mime_type,
149 GType type,
150 GInputStream *stream,
151 int priority,
152 GCancellable *cancellable,
153 GdkContentDeserializeFunc deserialize_func,
154 gpointer user_data,
155 GAsyncReadyCallback callback,
156 gpointer callback_data)
157{
158 GdkContentDeserializer *deserializer;
159
160 deserializer = g_object_new (GDK_TYPE_CONTENT_DESERIALIZER, NULL);
161
162 deserializer->mime_type = mime_type;
163 g_value_init (value: &deserializer->value, g_type: type);
164 deserializer->stream = g_object_ref (stream);
165 deserializer->priority = priority;
166 if (cancellable)
167 deserializer->cancellable = g_object_ref (cancellable);
168 deserializer->user_data = user_data;
169 deserializer->callback = callback;
170 deserializer->callback_data = callback_data;
171
172 deserialize_func (deserializer);
173}
174
175/**
176 * gdk_content_deserializer_get_mime_type:
177 * @deserializer: a `GdkContentDeserializer`
178 *
179 * Gets the mime type to deserialize from.
180 *
181 * Returns: (transfer none): the mime type for the current operation
182 */
183const char *
184gdk_content_deserializer_get_mime_type (GdkContentDeserializer *deserializer)
185{
186 g_return_val_if_fail (GDK_IS_CONTENT_DESERIALIZER (deserializer), NULL);
187
188 return deserializer->mime_type;
189}
190
191/**
192 * gdk_content_deserializer_get_gtype:
193 * @deserializer: a `GdkContentDeserializer`
194 *
195 * Gets the `GType` to create an instance of.
196 *
197 * Returns: the `GType` for the current operation
198 */
199GType
200gdk_content_deserializer_get_gtype (GdkContentDeserializer *deserializer)
201{
202 g_return_val_if_fail (GDK_IS_CONTENT_DESERIALIZER (deserializer), G_TYPE_INVALID);
203
204 return G_VALUE_TYPE (&deserializer->value);
205}
206
207/**
208 * gdk_content_deserializer_get_value:
209 * @deserializer: a `GdkContentDeserializer`
210 *
211 * Gets the `GValue` to store the deserialized object in.
212 *
213 * Returns: (transfer none): the `GValue` for the current operation
214 */
215GValue *
216gdk_content_deserializer_get_value (GdkContentDeserializer *deserializer)
217{
218 g_return_val_if_fail (GDK_IS_CONTENT_DESERIALIZER (deserializer), NULL);
219
220 return &deserializer->value;
221}
222
223/**
224 * gdk_content_deserializer_get_input_stream:
225 * @deserializer: a `GdkContentDeserializer`
226 *
227 * Gets the input stream for the current operation.
228 *
229 * This is the stream that was passed to [func@Gdk.content_deserialize_async].
230 *
231 * Returns: (transfer none): the input stream for the current operation
232 */
233GInputStream *
234gdk_content_deserializer_get_input_stream (GdkContentDeserializer *deserializer)
235{
236 g_return_val_if_fail (GDK_IS_CONTENT_DESERIALIZER (deserializer), NULL);
237
238 return deserializer->stream;
239}
240
241/**
242 * gdk_content_deserializer_get_priority:
243 * @deserializer: a `GdkContentDeserializer`
244 *
245 * Gets the I/O priority for the current operation.
246 *
247 * This is the priority that was passed to [func@Gdk.content_deserialize_async].
248 *
249 * Returns: the I/O priority for the current operation
250 */
251int
252gdk_content_deserializer_get_priority (GdkContentDeserializer *deserializer)
253{
254 g_return_val_if_fail (GDK_IS_CONTENT_DESERIALIZER (deserializer), G_PRIORITY_DEFAULT);
255
256 return deserializer->priority;
257}
258
259/**
260 * gdk_content_deserializer_get_cancellable:
261 * @deserializer: a `GdkContentDeserializer`
262 *
263 * Gets the cancellable for the current operation.
264 *
265 * This is the `GCancellable` that was passed to [func@Gdk.content_deserialize_async].
266 *
267 * Returns: (transfer none) (nullable): the cancellable for the current operation
268 */
269GCancellable *
270gdk_content_deserializer_get_cancellable (GdkContentDeserializer *deserializer)
271{
272 g_return_val_if_fail (GDK_IS_CONTENT_DESERIALIZER (deserializer), NULL);
273
274 return deserializer->cancellable;
275}
276
277/**
278 * gdk_content_deserializer_get_user_data:
279 * @deserializer: a `GdkContentDeserializer`
280 *
281 * Gets the user data that was passed when the deserializer was registered.
282 *
283 * Returns: (transfer none): the user data for this deserializer
284 */
285gpointer
286gdk_content_deserializer_get_user_data (GdkContentDeserializer *deserializer)
287{
288 g_return_val_if_fail (GDK_IS_CONTENT_DESERIALIZER (deserializer), NULL);
289
290 return deserializer->user_data;
291}
292
293/**
294 * gdk_content_deserializer_set_task_data:
295 * @deserializer: a `GdkContentDeserializer`
296 * @data: data to associate with this operation
297 * @notify: destroy notify for @data
298 *
299 * Associate data with the current deserialization operation.
300 */
301void
302gdk_content_deserializer_set_task_data (GdkContentDeserializer *deserializer,
303 gpointer data,
304 GDestroyNotify notify)
305{
306 g_return_if_fail (GDK_IS_CONTENT_DESERIALIZER (deserializer));
307
308 if (deserializer->task_notify)
309 deserializer->task_notify (deserializer->task_data);
310
311 deserializer->task_data = data;
312 deserializer->task_notify = notify;
313}
314
315/**
316 * gdk_content_deserializer_get_task_data:
317 * @deserializer: a `GdkContentDeserializer`
318 *
319 * Gets the data that was associated with the current operation.
320 *
321 * See [method@Gdk.ContentDeserializer.set_task_data].
322 *
323 * Returns: (transfer none): the task data for @deserializer
324 */
325gpointer
326gdk_content_deserializer_get_task_data (GdkContentDeserializer *deserializer)
327{
328 g_return_val_if_fail (GDK_IS_CONTENT_DESERIALIZER (deserializer), NULL);
329
330 return deserializer->task_data;
331}
332
333static gboolean
334gdk_content_deserializer_emit_callback (gpointer data)
335{
336 GdkContentDeserializer *deserializer = data;
337
338 if (deserializer->callback)
339 {
340 deserializer->callback (NULL, G_ASYNC_RESULT (deserializer), deserializer->callback_data);
341 }
342
343 return G_SOURCE_REMOVE;
344}
345
346/**
347 * gdk_content_deserializer_return_success:
348 * @deserializer: a `GdkContentDeserializer`
349 *
350 * Indicate that the deserialization has been successfully completed.
351 */
352void
353gdk_content_deserializer_return_success (GdkContentDeserializer *deserializer)
354{
355 g_return_if_fail (GDK_IS_CONTENT_DESERIALIZER (deserializer));
356 g_return_if_fail (!deserializer->returned);
357
358 deserializer->returned = TRUE;
359 g_idle_add_full (priority: deserializer->priority,
360 function: gdk_content_deserializer_emit_callback,
361 data: deserializer,
362 notify: g_object_unref);
363 /* NB: the idle will destroy our reference */
364}
365
366/**
367 * gdk_content_deserializer_return_error:
368 * @deserializer: a `GdkContentDeserializer`
369 * @error: (transfer full): a `GError`
370 *
371 * Indicate that the deserialization has ended with an error.
372 *
373 * This function consumes @error.
374 */
375void
376gdk_content_deserializer_return_error (GdkContentDeserializer *deserializer,
377 GError *error)
378{
379 g_return_if_fail (GDK_IS_CONTENT_DESERIALIZER (deserializer));
380 g_return_if_fail (!deserializer->returned);
381 g_return_if_fail (error != NULL);
382
383 deserializer->error = error;
384 /* FIXME: naming */
385 gdk_content_deserializer_return_success (deserializer);
386}
387
388/**
389 * gdk_content_register_deserializer:
390 * @mime_type: the mime type which the function can deserialize from
391 * @type: the type of objects that the function creates
392 * @deserialize: the callback
393 * @data: data that @deserialize can access
394 * @notify: destroy notify for @data
395 *
396 * Registers a function to deserialize object of a given type.
397 */
398void
399gdk_content_register_deserializer (const char *mime_type,
400 GType type,
401 GdkContentDeserializeFunc deserialize,
402 gpointer data,
403 GDestroyNotify notify)
404{
405 Deserializer *deserializer;
406
407 g_return_if_fail (mime_type != NULL);
408 g_return_if_fail (deserialize != NULL);
409
410 deserializer = g_slice_new0 (Deserializer);
411
412 deserializer->mime_type = g_intern_string (string: mime_type);
413 deserializer->type = type;
414 deserializer->deserialize = deserialize;
415 deserializer->data = data;
416 deserializer->notify = notify;
417
418 g_queue_push_tail (queue: &deserializers, data: deserializer);
419}
420
421static Deserializer *
422lookup_deserializer (const char *mime_type,
423 GType type)
424{
425 GList *l;
426
427 g_return_val_if_fail (mime_type != NULL, NULL);
428
429 init ();
430
431 mime_type = g_intern_string (string: mime_type);
432
433 for (l = g_queue_peek_head_link (queue: &deserializers); l; l = l->next)
434 {
435 Deserializer *deserializer = l->data;
436
437 if (deserializer->mime_type == mime_type &&
438 deserializer->type == type)
439 return deserializer;
440 }
441
442 return NULL;
443}
444
445/**
446 * gdk_content_formats_union_deserialize_gtypes:
447 * @formats: (transfer full): a `GdkContentFormats`
448 *
449 * Add GTypes for mime types in @formats for which deserializers are
450 * registered.
451 *
452 * Return: a new `GdkContentFormats`
453 */
454GdkContentFormats *
455gdk_content_formats_union_deserialize_gtypes (GdkContentFormats *formats)
456{
457 GdkContentFormatsBuilder *builder;
458 GList *l;
459
460 g_return_val_if_fail (formats != NULL, NULL);
461
462 init ();
463
464 builder = gdk_content_formats_builder_new ();
465 gdk_content_formats_builder_add_formats (builder, formats);
466
467 for (l = g_queue_peek_head_link (queue: &deserializers); l; l = l->next)
468 {
469 Deserializer *deserializer = l->data;
470
471 if (gdk_content_formats_contain_mime_type (formats, mime_type: deserializer->mime_type))
472 gdk_content_formats_builder_add_gtype (builder, type: deserializer->type);
473 }
474
475 gdk_content_formats_unref (formats);
476
477 return gdk_content_formats_builder_free_to_formats (builder);
478}
479
480/**
481 * gdk_content_formats_union_deserialize_mime_types:
482 * @formats: (transfer full): a `GdkContentFormats`
483 *
484 * Add mime types for GTypes in @formats for which deserializers are
485 * registered.
486 *
487 * Return: a new `GdkContentFormats`
488 */
489GdkContentFormats *
490gdk_content_formats_union_deserialize_mime_types (GdkContentFormats *formats)
491{
492 GdkContentFormatsBuilder *builder;
493 GList *l;
494
495 g_return_val_if_fail (formats != NULL, NULL);
496
497 init ();
498
499 builder = gdk_content_formats_builder_new ();
500 gdk_content_formats_builder_add_formats (builder, formats);
501
502 for (l = g_queue_peek_head_link (queue: &deserializers); l; l = l->next)
503 {
504 Deserializer *deserializer = l->data;
505
506 if (gdk_content_formats_contain_gtype (formats, type: deserializer->type))
507 gdk_content_formats_builder_add_mime_type (builder, mime_type: deserializer->mime_type);
508 }
509
510 gdk_content_formats_unref (formats);
511
512 return gdk_content_formats_builder_free_to_formats (builder);
513}
514
515static void
516deserialize_not_found (GdkContentDeserializer *deserializer)
517{
518 GError *error = g_error_new (G_IO_ERROR,
519 code: G_IO_ERROR_NOT_FOUND,
520 format: "Could not convert data from %s to %s",
521 gdk_content_deserializer_get_mime_type (deserializer),
522 g_type_name (type: gdk_content_deserializer_get_gtype (deserializer)));
523 gdk_content_deserializer_return_error (deserializer, error);
524}
525
526/**
527 * gdk_content_deserialize_async:
528 * @stream: a `GInputStream` to read the serialized content from
529 * @mime_type: the mime type to deserialize from
530 * @type: the GType to deserialize from
531 * @io_priority: the I/O priority of the operation
532 * @cancellable: (nullable): optional `GCancellable` object
533 * @callback: (scope async): callback to call when the operation is done
534 * @user_data: (closure): data to pass to the callback function
535 *
536 * Read content from the given input stream and deserialize it, asynchronously.
537 *
538 * The default I/O priority is %G_PRIORITY_DEFAULT (i.e. 0), and lower numbers
539 * indicate a higher priority.
540 *
541 * When the operation is finished, @callback will be called. You must then
542 * call [func@Gdk.content_deserialize_finish] to get the result of the operation.
543 */
544void
545gdk_content_deserialize_async (GInputStream *stream,
546 const char *mime_type,
547 GType type,
548 int io_priority,
549 GCancellable *cancellable,
550 GAsyncReadyCallback callback,
551 gpointer user_data)
552{
553 Deserializer *deserializer;
554
555 g_return_if_fail (G_IS_INPUT_STREAM (stream));
556 g_return_if_fail (mime_type != NULL);
557 g_return_if_fail (G_TYPE_IS_VALUE_TYPE (type));
558 g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
559 g_return_if_fail (callback != NULL);
560
561 deserializer = lookup_deserializer (mime_type, type);
562
563 gdk_content_deserializer_run (mime_type,
564 type,
565 stream,
566 priority: io_priority,
567 cancellable,
568 deserialize_func: deserializer ? deserializer->deserialize : deserialize_not_found,
569 user_data: deserializer ? deserializer->data : NULL,
570 callback,
571 callback_data: user_data);
572}
573
574/**
575 * gdk_content_deserialize_finish:
576 * @result: the `GAsyncResult`
577 * @value: (out): return location for the result of the operation
578 * @error: return location for an error
579 *
580 * Finishes a content deserialization operation.
581 *
582 * Returns: %TRUE if the operation was successful. In this case,
583 * @value is set. %FALSE if an error occurred. In this case,
584 * @error is set
585 */
586gboolean
587gdk_content_deserialize_finish (GAsyncResult *result,
588 GValue *value,
589 GError **error)
590{
591 GdkContentDeserializer *deserializer;
592
593 g_return_val_if_fail (GDK_IS_CONTENT_DESERIALIZER (result), FALSE);
594 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
595 deserializer = GDK_CONTENT_DESERIALIZER (result);
596 if (G_VALUE_TYPE (value) == 0)
597 g_value_init (value, G_VALUE_TYPE (&deserializer->value));
598 else
599 g_return_val_if_fail (G_VALUE_HOLDS (value, G_VALUE_TYPE (&deserializer->value)), FALSE);
600
601 if (deserializer->error)
602 {
603 if (error)
604 *error = g_error_copy (error: deserializer->error);
605 return FALSE;
606 }
607
608 g_value_copy (src_value: &deserializer->value, dest_value: value);
609 return TRUE;
610}
611
612/*** DESERIALIZERS ***/
613
614static void
615pixbuf_deserializer_finish (GObject *source,
616 GAsyncResult *res,
617 gpointer deserializer)
618{
619 GdkPixbuf *pixbuf;
620 GValue *value;
621 GError *error = NULL;
622
623 pixbuf = gdk_pixbuf_new_from_stream_finish (async_result: res, error: &error);
624 if (pixbuf == NULL)
625 {
626 gdk_content_deserializer_return_error (deserializer, error);
627 return;
628 }
629
630 value = gdk_content_deserializer_get_value (deserializer);
631 if (G_VALUE_HOLDS (value, GDK_TYPE_PIXBUF))
632 {
633 g_value_take_object (value, v_object: pixbuf);
634 }
635 else if (G_VALUE_HOLDS (value, GDK_TYPE_TEXTURE))
636 {
637 GdkTexture *texture;
638 texture = gdk_texture_new_for_pixbuf (pixbuf);
639 g_object_unref (object: pixbuf);
640 g_value_take_object (value, v_object: texture);
641 }
642 else
643 {
644 g_assert_not_reached ();
645 }
646 gdk_content_deserializer_return_success (deserializer);
647}
648
649static void
650pixbuf_deserializer (GdkContentDeserializer *deserializer)
651{
652 gdk_pixbuf_new_from_stream_async (stream: gdk_content_deserializer_get_input_stream (deserializer),
653 cancellable: gdk_content_deserializer_get_cancellable (deserializer),
654 callback: pixbuf_deserializer_finish,
655 user_data: deserializer);
656}
657
658static void
659texture_deserializer_finish (GObject *source,
660 GAsyncResult *result,
661 gpointer deserializer)
662{
663 GOutputStream *stream = G_OUTPUT_STREAM (source);
664 GBytes *bytes;
665 GError *error = NULL;
666 GdkTexture *texture = NULL;
667 gssize written;
668
669 written = g_output_stream_splice_finish (stream, result, error: &error);
670 if (written < 0)
671 {
672 gdk_content_deserializer_return_error (deserializer, error);
673 return;
674 }
675
676 bytes = g_memory_output_stream_steal_as_bytes (G_MEMORY_OUTPUT_STREAM (stream));
677
678 texture = gdk_texture_new_from_bytes (bytes, error: &error);
679 g_bytes_unref (bytes);
680 if (texture == NULL)
681 {
682 gdk_content_deserializer_return_error (deserializer, error);
683 return;
684 }
685
686 g_value_take_object (value: gdk_content_deserializer_get_value (deserializer), v_object: texture);
687 gdk_content_deserializer_return_success (deserializer);
688}
689
690static void
691texture_deserializer (GdkContentDeserializer *deserializer)
692{
693 GOutputStream *output;
694
695 output = g_memory_output_stream_new_resizable ();
696
697 g_output_stream_splice_async (stream: output,
698 source: gdk_content_deserializer_get_input_stream (deserializer),
699 flags: G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE
700 | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
701 io_priority: gdk_content_deserializer_get_priority (deserializer),
702 cancellable: gdk_content_deserializer_get_cancellable (deserializer),
703 callback: texture_deserializer_finish,
704 user_data: deserializer);
705 g_object_unref (object: output);
706}
707
708static void
709string_deserializer_finish (GObject *source,
710 GAsyncResult *result,
711 gpointer deserializer)
712{
713 GOutputStream *stream = G_OUTPUT_STREAM (source);
714 GError *error = NULL;
715 gssize written;
716
717 written = g_output_stream_splice_finish (stream, result, error: &error);
718 if (written < 0)
719 {
720 gdk_content_deserializer_return_error (deserializer, error);
721 return;
722 }
723 else if (written == 0)
724 {
725 /* Never return NULL, we only return that on error */
726 g_value_set_string (value: gdk_content_deserializer_get_value (deserializer), v_string: "");
727 }
728 else
729 {
730 GOutputStream *mem_stream = g_filter_output_stream_get_base_stream (G_FILTER_OUTPUT_STREAM (stream));
731
732 /* write a terminating NULL byte */
733 if (g_output_stream_write (stream: mem_stream, buffer: "", count: 1, NULL, error: &error) < 0 ||
734 !g_output_stream_close (stream: mem_stream, NULL, error: &error))
735 {
736 gdk_content_deserializer_return_error (deserializer, error);
737 return;
738 }
739
740 g_value_take_string (value: gdk_content_deserializer_get_value (deserializer),
741 v_string: g_memory_output_stream_steal_data (G_MEMORY_OUTPUT_STREAM (mem_stream)));
742 }
743 gdk_content_deserializer_return_success (deserializer);
744}
745
746static void
747string_deserializer (GdkContentDeserializer *deserializer)
748{
749 GOutputStream *output, *filter;
750 GCharsetConverter *converter;
751 GError *error = NULL;
752
753 converter = g_charset_converter_new (to_charset: "utf-8",
754 from_charset: gdk_content_deserializer_get_user_data (deserializer),
755 error: &error);
756 if (converter == NULL)
757 {
758 gdk_content_deserializer_return_error (deserializer, error);
759 return;
760 }
761 g_charset_converter_set_use_fallback (converter, TRUE);
762
763 output = g_memory_output_stream_new_resizable ();
764 filter = g_converter_output_stream_new (base_stream: output, G_CONVERTER (converter));
765 g_object_unref (object: output);
766 g_object_unref (object: converter);
767
768 g_output_stream_splice_async (stream: filter,
769 source: gdk_content_deserializer_get_input_stream (deserializer),
770 flags: G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE,
771 io_priority: gdk_content_deserializer_get_priority (deserializer),
772 cancellable: gdk_content_deserializer_get_cancellable (deserializer),
773 callback: string_deserializer_finish,
774 user_data: deserializer);
775 g_object_unref (object: filter);
776}
777
778static void
779file_uri_deserializer_finish (GObject *source,
780 GAsyncResult *result,
781 gpointer deserializer)
782{
783 GOutputStream *stream = G_OUTPUT_STREAM (source);
784 GError *error = NULL;
785 gssize written;
786 GValue *value;
787 char *str;
788 char **uris;
789
790 written = g_output_stream_splice_finish (stream, result, error: &error);
791 if (written < 0)
792 {
793 gdk_content_deserializer_return_error (deserializer, error);
794 return;
795 }
796
797 /* write terminating NULL */
798 if (g_output_stream_write (stream, buffer: "", count: 1, NULL, error: &error) < 0 ||
799 !g_output_stream_close (stream, NULL, error: &error))
800 {
801 gdk_content_deserializer_return_error (deserializer, error);
802 return;
803 }
804
805 str = g_memory_output_stream_steal_data (G_MEMORY_OUTPUT_STREAM (stream));
806 uris = g_uri_list_extract_uris (uri_list: str);
807 g_free (mem: str);
808
809 value = gdk_content_deserializer_get_value (deserializer);
810 if (G_VALUE_HOLDS (value, G_TYPE_FILE))
811 {
812 if (uris[0] != NULL)
813 g_value_take_object (value, v_object: g_file_new_for_uri (uri: uris[0]));
814 }
815 else
816 {
817 GSList *l = NULL;
818 gsize i;
819
820 for (i = 0; uris[i] != NULL; i++)
821 l = g_slist_prepend (list: l, data: g_file_new_for_uri (uri: uris[i]));
822
823 g_value_take_boxed (value, v_boxed: g_slist_reverse (list: l));
824 }
825 g_strfreev (str_array: uris);
826
827 gdk_content_deserializer_return_success (deserializer);
828}
829
830static void
831file_uri_deserializer (GdkContentDeserializer *deserializer)
832{
833 GOutputStream *output;
834
835 output = g_memory_output_stream_new_resizable ();
836
837 g_output_stream_splice_async (stream: output,
838 source: gdk_content_deserializer_get_input_stream (deserializer),
839 flags: G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE,
840 io_priority: gdk_content_deserializer_get_priority (deserializer),
841 cancellable: gdk_content_deserializer_get_cancellable (deserializer),
842 callback: file_uri_deserializer_finish,
843 user_data: deserializer);
844 g_object_unref (object: output);
845}
846
847static void
848color_deserializer_finish (GObject *source,
849 GAsyncResult *result,
850 gpointer deserializer)
851{
852 GOutputStream *stream = G_OUTPUT_STREAM (source);
853 GError *error = NULL;
854 gssize written;
855
856 written = g_output_stream_splice_finish (stream, result, error: &error);
857 if (written < 0)
858 {
859 gdk_content_deserializer_return_error (deserializer, error);
860 return;
861 }
862 else if (written == 0)
863 {
864 GdkRGBA black = GDK_RGBA ("000");
865
866 /* Never return NULL, we only return that on error */
867 g_value_set_boxed (value: gdk_content_deserializer_get_value (deserializer), v_boxed: &black);
868 }
869 else
870 {
871 guint16 *data;
872 GdkRGBA rgba;
873
874 data = (guint16 *)g_memory_output_stream_get_data (G_MEMORY_OUTPUT_STREAM (stream));
875 rgba.red = data[0] / 65535.0;
876 rgba.green = data[1] / 65535.0;
877 rgba.blue = data[2] / 65535.0;
878 rgba.alpha = data[3] / 65535.0;
879
880 g_value_set_boxed (value: gdk_content_deserializer_get_value (deserializer), v_boxed: &rgba);
881 }
882 gdk_content_deserializer_return_success (deserializer);
883}
884
885static void
886color_deserializer (GdkContentDeserializer *deserializer)
887{
888 GOutputStream *output;
889 guint16 *data;
890
891 data = g_new0 (guint16, 4);
892 output = g_memory_output_stream_new (data, size: 4 * sizeof (guint16), NULL, destroy_function: g_free);
893
894 g_output_stream_splice_async (stream: output,
895 source: gdk_content_deserializer_get_input_stream (deserializer),
896 flags: G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
897 io_priority: gdk_content_deserializer_get_priority (deserializer),
898 cancellable: gdk_content_deserializer_get_cancellable (deserializer),
899 callback: color_deserializer_finish,
900 user_data: deserializer);
901 g_object_unref (object: output);
902}
903
904static void
905init (void)
906{
907 static gboolean initialized = FALSE;
908 GSList *formats, *f;
909 const char *charset;
910
911 if (initialized)
912 return;
913
914 initialized = TRUE;
915
916 gdk_content_register_deserializer (mime_type: "image/png",
917 GDK_TYPE_TEXTURE,
918 deserialize: texture_deserializer,
919 NULL,
920 NULL);
921 gdk_content_register_deserializer (mime_type: "image/tiff",
922 GDK_TYPE_TEXTURE,
923 deserialize: texture_deserializer,
924 NULL,
925 NULL);
926 gdk_content_register_deserializer (mime_type: "image/jpeg",
927 GDK_TYPE_TEXTURE,
928 deserialize: texture_deserializer,
929 NULL,
930 NULL);
931
932
933 formats = gdk_pixbuf_get_formats ();
934
935 for (f = formats; f; f = f->next)
936 {
937 GdkPixbufFormat *fmt = f->data;
938 char **mimes, **m;
939 char *name;
940
941 name = gdk_pixbuf_format_get_name (format: fmt);
942 mimes = gdk_pixbuf_format_get_mime_types (format: fmt);
943 for (m = mimes; *m; m++)
944 {
945 /* Turning pngs, jpegs and tiffs into textures is handled above */
946 if (!g_str_equal (v1: name, v2: "png") &&
947 !g_str_equal (v1: name, v2: "jpeg") &&
948 !g_str_equal (v1: name, v2: "tiff"))
949 gdk_content_register_deserializer (mime_type: *m,
950 GDK_TYPE_TEXTURE,
951 deserialize: pixbuf_deserializer,
952 NULL,
953 NULL);
954 gdk_content_register_deserializer (mime_type: *m,
955 GDK_TYPE_PIXBUF,
956 deserialize: pixbuf_deserializer,
957 NULL,
958 NULL);
959 }
960 g_strfreev (str_array: mimes);
961 g_free (mem: name);
962 }
963
964 g_slist_free (list: formats);
965
966#if defined(G_OS_UNIX) && !defined(__APPLE__)
967 file_transfer_portal_register ();
968#endif
969
970 gdk_content_register_deserializer (mime_type: "text/uri-list",
971 GDK_TYPE_FILE_LIST,
972 deserialize: file_uri_deserializer,
973 NULL,
974 NULL);
975
976 gdk_content_register_deserializer (mime_type: "text/uri-list",
977 G_TYPE_FILE,
978 deserialize: file_uri_deserializer,
979 NULL,
980 NULL);
981
982 gdk_content_register_deserializer (mime_type: "text/plain;charset=utf-8",
983 G_TYPE_STRING,
984 deserialize: string_deserializer,
985 data: (gpointer) "utf-8",
986 NULL);
987 if (!g_get_charset (charset: &charset))
988 {
989 char *mime = g_strdup_printf (format: "text/plain;charset=%s", charset);
990
991 gdk_content_register_deserializer (mime_type: mime,
992 G_TYPE_STRING,
993 deserialize: string_deserializer,
994 data: (gpointer) charset,
995 notify: g_free);
996 g_free (mem: mime);
997 }
998 gdk_content_register_deserializer (mime_type: "text/plain",
999 G_TYPE_STRING,
1000 deserialize: string_deserializer,
1001 data: (gpointer) "ASCII",
1002 NULL);
1003
1004 gdk_content_register_deserializer (mime_type: "application/x-color",
1005 GDK_TYPE_RGBA,
1006 deserialize: color_deserializer,
1007 NULL,
1008 NULL);
1009}
1010
1011

source code of gtk/gdk/gdkcontentdeserializer.c