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 "gdkcontentserializer.h"
23
24#include "gdkcontentformats.h"
25#include "gdkpixbuf.h"
26#include "filetransferportalprivate.h"
27#include "gdktextureprivate.h"
28#include "gdkrgba.h"
29#include "loaders/gdkpngprivate.h"
30#include "loaders/gdktiffprivate.h"
31#include "loaders/gdkjpegprivate.h"
32#include "gdkmemorytextureprivate.h"
33
34#include <gdk-pixbuf/gdk-pixbuf.h>
35#include <string.h>
36
37
38/**
39 * GdkContentSerializer:
40 *
41 * A `GdkContentSerializer` is used to serialize content for
42 * inter-application data transfers.
43 *
44 * The `GdkContentSerializer` transforms an object that is identified
45 * by a GType into a serialized form (i.e. a byte stream) that is
46 * identified by a mime type.
47 *
48 * GTK provides serializers and deserializers for common data types
49 * such as text, colors, images or file lists. To register your own
50 * serialization functions, use [func@Gdk.content_register_serializer].
51 *
52 * Also see [class@Gdk.ContentDeserializer].
53 */
54
55typedef struct _Serializer Serializer;
56
57struct _Serializer
58{
59 const char * mime_type; /* interned */
60 GType type;
61 GdkContentSerializeFunc serialize;
62 gpointer data;
63 GDestroyNotify notify;
64};
65
66GQueue serializers = G_QUEUE_INIT;
67
68static void init (void);
69
70#define GDK_CONTENT_SERIALIZER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_CONTENT_SERIALIZER, GdkContentSerializerClass))
71#define GDK_IS_CONTENT_SERIALIZER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_CONTENT_SERIALIZER))
72#define GDK_CONTENT_SERIALIZER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_CONTENT_SERIALIZER, GdkContentSerializerClass))
73
74typedef struct _GdkContentSerializerClass GdkContentSerializerClass;
75
76struct _GdkContentSerializer
77{
78 GObject parent_instance;
79
80 const char *mime_type; /* interned */
81 GValue value;
82 GOutputStream *stream;
83 int priority;
84 gboolean returned;
85 GCancellable *cancellable;
86 gpointer user_data;
87 GAsyncReadyCallback callback;
88 gpointer callback_data;
89
90 gpointer task_data;
91 GDestroyNotify task_notify;
92
93 GError *error;
94};
95
96struct _GdkContentSerializerClass
97{
98 GObjectClass parent_class;
99};
100
101static gpointer
102gdk_content_serializer_async_result_get_user_data (GAsyncResult *res)
103{
104 return GDK_CONTENT_SERIALIZER (res)->callback_data;
105}
106
107static GObject *
108gdk_content_serializer_async_result_get_source_object (GAsyncResult *res)
109{
110 return NULL;
111}
112
113static void
114gdk_content_serializer_async_result_iface_init (GAsyncResultIface *iface)
115{
116 iface->get_user_data = gdk_content_serializer_async_result_get_user_data;
117 iface->get_source_object = gdk_content_serializer_async_result_get_source_object;
118}
119
120G_DEFINE_TYPE_WITH_CODE (GdkContentSerializer, gdk_content_serializer, G_TYPE_OBJECT,
121 G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_RESULT, gdk_content_serializer_async_result_iface_init))
122
123static void
124gdk_content_serializer_finalize (GObject *object)
125{
126 GdkContentSerializer *serializer = GDK_CONTENT_SERIALIZER (object);
127
128 g_value_unset (value: &serializer->value);
129 g_clear_object (&serializer->stream);
130 g_clear_object (&serializer->cancellable);
131 g_clear_error (err: &serializer->error);
132
133 if (serializer->task_notify)
134 serializer->task_notify (serializer->task_data);
135
136 G_OBJECT_CLASS (gdk_content_serializer_parent_class)->finalize (object);
137}
138
139static void
140gdk_content_serializer_class_init (GdkContentSerializerClass *content_serializer_class)
141{
142 GObjectClass *object_class = G_OBJECT_CLASS (content_serializer_class);
143
144 object_class->finalize = gdk_content_serializer_finalize;
145}
146
147static void
148gdk_content_serializer_init (GdkContentSerializer *content_serializer)
149{
150}
151
152static void
153gdk_content_serializer_run (const char *mime_type,
154 const GValue *value,
155 GOutputStream *stream,
156 int priority,
157 GCancellable *cancellable,
158 GdkContentSerializeFunc serialize_func,
159 gpointer user_data,
160 GAsyncReadyCallback callback,
161 gpointer callback_data)
162{
163 GdkContentSerializer *serializer;
164
165 serializer = g_object_new (GDK_TYPE_CONTENT_SERIALIZER, NULL);
166
167 serializer->mime_type = mime_type;
168 g_value_init (value: &serializer->value, G_VALUE_TYPE (value));
169 g_value_copy (src_value: value, dest_value: &serializer->value);
170 serializer->stream = g_object_ref (stream);
171 serializer->priority = priority;
172 if (cancellable)
173 serializer->cancellable = g_object_ref (cancellable);
174 serializer->user_data = user_data;
175 serializer->callback = callback;
176 serializer->callback_data = callback_data;
177
178 serialize_func (serializer);
179}
180
181/**
182 * gdk_content_serializer_get_mime_type:
183 * @serializer: a `GdkContentSerializer`
184 *
185 * Gets the mime type to serialize to.
186 *
187 * Returns: (transfer none): the mime type for the current operation
188 */
189const char *
190gdk_content_serializer_get_mime_type (GdkContentSerializer *serializer)
191{
192 g_return_val_if_fail (GDK_IS_CONTENT_SERIALIZER (serializer), NULL);
193
194 return serializer->mime_type;
195}
196
197/**
198 * gdk_content_serializer_get_gtype:
199 * @serializer: a `GdkContentSerializer`
200 *
201 * Gets the `GType` to of the object to serialize.
202 *
203 * Returns: the `GType` for the current operation
204 */
205GType
206gdk_content_serializer_get_gtype (GdkContentSerializer *serializer)
207{
208 g_return_val_if_fail (GDK_IS_CONTENT_SERIALIZER (serializer), G_TYPE_INVALID);
209
210 return G_VALUE_TYPE (&serializer->value);
211}
212
213/**
214 * gdk_content_serializer_get_value:
215 * @serializer: a `GdkContentSerializer`
216 *
217 * Gets the `GValue` to read the object to serialize from.
218 *
219 * Returns: (transfer none): the `GValue` for the current operation
220 */
221const GValue *
222gdk_content_serializer_get_value (GdkContentSerializer *serializer)
223{
224 g_return_val_if_fail (GDK_IS_CONTENT_SERIALIZER (serializer), NULL);
225
226 return &serializer->value;
227}
228
229/**
230 * gdk_content_serializer_get_output_stream:
231 * @serializer: a `GdkContentSerializer`
232 *
233 * Gets the output stream for the current operation.
234 *
235 * This is the stream that was passed to [func@content_serialize_async].
236 *
237 * Returns: (transfer none): the output stream for the current operation
238 */
239GOutputStream *
240gdk_content_serializer_get_output_stream (GdkContentSerializer *serializer)
241{
242 g_return_val_if_fail (GDK_IS_CONTENT_SERIALIZER (serializer), NULL);
243
244 return serializer->stream;
245}
246
247/**
248 * gdk_content_serializer_get_priority:
249 * @serializer: a `GdkContentSerializer`
250 *
251 * Gets the I/O priority for the current operation.
252 *
253 * This is the priority that was passed to [func@content_serialize_async].
254 *
255 * Returns: the I/O priority for the current operation
256 */
257int
258gdk_content_serializer_get_priority (GdkContentSerializer *serializer)
259{
260 g_return_val_if_fail (GDK_IS_CONTENT_SERIALIZER (serializer), G_PRIORITY_DEFAULT);
261
262 return serializer->priority;
263}
264
265/**
266 * gdk_content_serializer_get_cancellable:
267 * @serializer: a `GdkContentSerializer`
268 *
269 * Gets the cancellable for the current operation.
270 *
271 * This is the `GCancellable` that was passed to [func@content_serialize_async].
272 *
273 * Returns: (transfer none) (nullable): the cancellable for the current operation
274 */
275GCancellable *
276gdk_content_serializer_get_cancellable (GdkContentSerializer *serializer)
277{
278 g_return_val_if_fail (GDK_IS_CONTENT_SERIALIZER (serializer), NULL);
279
280 return serializer->cancellable;
281}
282
283/**
284 * gdk_content_serializer_get_user_data:
285 * @serializer: a `GdkContentSerializer`
286 *
287 * Gets the user data that was passed when the serializer was registered.
288 *
289 * Returns: (transfer none): the user data for this serializer
290 */
291gpointer
292gdk_content_serializer_get_user_data (GdkContentSerializer *serializer)
293{
294 g_return_val_if_fail (GDK_IS_CONTENT_SERIALIZER (serializer), NULL);
295
296 return serializer->user_data;
297}
298
299/**
300 * gdk_content_serializer_set_task_data:
301 * @serializer: a `GdkContentSerializer`
302 * @data: data to associate with this operation
303 * @notify: destroy notify for @data
304 *
305 * Associate data with the current serialization operation.
306 */
307void
308gdk_content_serializer_set_task_data (GdkContentSerializer *serializer,
309 gpointer data,
310 GDestroyNotify notify)
311{
312 g_return_if_fail (GDK_IS_CONTENT_SERIALIZER (serializer));
313
314 if (serializer->task_notify)
315 serializer->task_notify (serializer->task_data);
316
317 serializer->task_data = data;
318 serializer->task_notify = notify;
319}
320
321/**
322 * gdk_content_serializer_get_task_data:
323 * @serializer: a `GdkContentSerializer`
324 *
325 * Gets the data that was associated with the current operation.
326 *
327 * See [method@Gdk.ContentSerializer.set_task_data].
328 *
329 * Returns: (transfer none): the task data for @serializer
330 */
331gpointer
332gdk_content_serializer_get_task_data (GdkContentSerializer *serializer)
333{
334 g_return_val_if_fail (GDK_IS_CONTENT_SERIALIZER (serializer), NULL);
335
336 return serializer->task_data;
337}
338
339static gboolean
340gdk_content_serializer_emit_callback (gpointer data)
341{
342 GdkContentSerializer *serializer = data;
343
344 if (serializer->callback)
345 {
346 serializer->callback (NULL, G_ASYNC_RESULT (serializer), serializer->callback_data);
347 }
348
349 return G_SOURCE_REMOVE;
350}
351
352/**
353 * gdk_content_serializer_return_success:
354 * @serializer: a `GdkContentSerializer`
355 *
356 * Indicate that the serialization has been successfully completed.
357 */
358void
359gdk_content_serializer_return_success (GdkContentSerializer *serializer)
360{
361 g_return_if_fail (GDK_IS_CONTENT_SERIALIZER (serializer));
362 g_return_if_fail (!serializer->returned);
363
364 serializer->returned = TRUE;
365 g_idle_add_full (priority: serializer->priority,
366 function: gdk_content_serializer_emit_callback,
367 data: serializer,
368 notify: g_object_unref);
369 /* NB: the idle will destroy our reference */
370}
371
372/**
373 * gdk_content_serializer_return_error:
374 * @serializer: a `GdkContentSerializer`
375 * @error: (transfer full): a `GError`
376 *
377 * Indicate that the serialization has ended with an error.
378 *
379 * This function consumes @error.
380 */
381void
382gdk_content_serializer_return_error (GdkContentSerializer *serializer,
383 GError *error)
384{
385 g_return_if_fail (GDK_IS_CONTENT_SERIALIZER (serializer));
386 g_return_if_fail (!serializer->returned);
387 g_return_if_fail (error != NULL);
388
389 serializer->error = error;
390 /* FIXME: naming */
391 gdk_content_serializer_return_success (serializer);
392}
393
394/**
395 * gdk_content_register_serializer:
396 * @type: the type of objects that the function can serialize
397 * @mime_type: the mime type to serialize to
398 * @serialize: the callback
399 * @data: data that @serialize can access
400 * @notify: destroy notify for @data
401 *
402 * Registers a function to serialize objects of a given type.
403 */
404void
405gdk_content_register_serializer (GType type,
406 const char *mime_type,
407 GdkContentSerializeFunc serialize,
408 gpointer data,
409 GDestroyNotify notify)
410{
411 Serializer *serializer;
412
413 g_return_if_fail (mime_type != NULL);
414 g_return_if_fail (serialize != NULL);
415
416 serializer = g_slice_new0 (Serializer);
417
418 serializer->mime_type = g_intern_string (string: mime_type);
419 serializer->type = type;
420 serializer->serialize = serialize;
421 serializer->data = data;
422 serializer->notify = notify;
423
424 g_queue_push_tail (queue: &serializers, data: serializer);
425}
426
427static Serializer *
428lookup_serializer (const char *mime_type,
429 GType type)
430{
431 GList *l;
432
433 g_return_val_if_fail (mime_type != NULL, NULL);
434
435 init ();
436
437 mime_type = g_intern_string (string: mime_type);
438
439 for (l = g_queue_peek_head_link (queue: &serializers); l; l = l->next)
440 {
441 Serializer *serializer = l->data;
442
443 if (serializer->mime_type == mime_type &&
444 serializer->type == type)
445 return serializer;
446 }
447
448 return NULL;
449}
450
451/**
452 * gdk_content_formats_union_serialize_gtypes:
453 * @formats: (transfer full): a `GdkContentFormats`
454 *
455 * Add GTypes for the mime types in @formats for which serializers are
456 * registered.
457 *
458 * Return: a new `GdkContentFormats`
459 */
460GdkContentFormats *
461gdk_content_formats_union_serialize_gtypes (GdkContentFormats *formats)
462{
463 GdkContentFormatsBuilder *builder;
464 GList *l;
465
466 g_return_val_if_fail (formats != NULL, NULL);
467
468 init ();
469
470 builder = gdk_content_formats_builder_new ();
471 gdk_content_formats_builder_add_formats (builder, formats);
472
473 for (l = g_queue_peek_head_link (queue: &serializers); l; l = l->next)
474 {
475 Serializer *serializer = l->data;
476
477 if (gdk_content_formats_contain_mime_type (formats, mime_type: serializer->mime_type))
478 gdk_content_formats_builder_add_gtype (builder, type: serializer->type);
479 }
480
481 gdk_content_formats_unref (formats);
482
483 return gdk_content_formats_builder_free_to_formats (builder);
484}
485
486/**
487 * gdk_content_formats_union_serialize_mime_types:
488 * @formats: (transfer full): a `GdkContentFormats`
489 *
490 * Add mime types for GTypes in @formats for which serializers are
491 * registered.
492 *
493 * Return: a new `GdkContentFormats`
494 */
495GdkContentFormats *
496gdk_content_formats_union_serialize_mime_types (GdkContentFormats *formats)
497{
498 GdkContentFormatsBuilder *builder;
499 GList *l;
500
501 g_return_val_if_fail (formats != NULL, NULL);
502
503 init ();
504
505 builder = gdk_content_formats_builder_new ();
506 gdk_content_formats_builder_add_formats (builder, formats);
507
508 for (l = g_queue_peek_head_link (queue: &serializers); l; l = l->next)
509 {
510 Serializer *serializer = l->data;
511
512 if (gdk_content_formats_contain_gtype (formats, type: serializer->type))
513 gdk_content_formats_builder_add_mime_type (builder, mime_type: serializer->mime_type);
514 }
515
516 gdk_content_formats_unref (formats);
517
518 return gdk_content_formats_builder_free_to_formats (builder);
519}
520
521static void
522serialize_not_found (GdkContentSerializer *serializer)
523{
524 GError *error = g_error_new (G_IO_ERROR,
525 code: G_IO_ERROR_NOT_FOUND,
526 format: "Could not convert data from %s to %s",
527 g_type_name (type: gdk_content_serializer_get_gtype (serializer)),
528 gdk_content_serializer_get_mime_type (serializer));
529 gdk_content_serializer_return_error (serializer, error);
530}
531
532/**
533 * gdk_content_serialize_async:
534 * @stream: a `GOutputStream` to write the serialized content to
535 * @mime_type: the mime type to serialize to
536 * @value: the content to serialize
537 * @io_priority: the I/O priority of the operation
538 * @cancellable: (nullable): optional `GCancellable` object
539 * @callback: (scope async): callback to call when the operation is done
540 * @user_data: (closure): data to pass to the callback function
541 *
542 * Serialize content and write it to the given output stream, asynchronously.
543 *
544 * The default I/O priority is %G_PRIORITY_DEFAULT (i.e. 0), and lower numbers
545 * indicate a higher priority.
546 *
547 * When the operation is finished, @callback will be called. You must then
548 * call [func@Gdk.content_serialize_finish] to get the result of the operation.
549 */
550void
551gdk_content_serialize_async (GOutputStream *stream,
552 const char *mime_type,
553 const GValue *value,
554 int io_priority,
555 GCancellable *cancellable,
556 GAsyncReadyCallback callback,
557 gpointer user_data)
558{
559 Serializer *serializer;
560
561 g_return_if_fail (G_IS_OUTPUT_STREAM (stream));
562 g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
563 g_return_if_fail (callback != NULL);
564
565 serializer = lookup_serializer (mime_type, G_VALUE_TYPE (value));
566
567 gdk_content_serializer_run (mime_type,
568 value,
569 stream,
570 priority: io_priority,
571 cancellable,
572 serialize_func: serializer ? serializer->serialize : serialize_not_found,
573 user_data: serializer ? serializer->data : NULL,
574 callback,
575 callback_data: user_data);
576}
577
578/**
579 * gdk_content_serialize_finish:
580 * @result: the `GAsyncResult`
581 * @error: return location for an error
582 *
583 * Finishes a content serialization operation.
584 *
585 * Returns: %TRUE if the operation was successful, %FALSE if an
586 * error occurred. In this case, @error is set
587 */
588gboolean
589gdk_content_serialize_finish (GAsyncResult *result,
590 GError **error)
591{
592 GdkContentSerializer *serializer;
593
594 g_return_val_if_fail (GDK_IS_CONTENT_SERIALIZER (result), FALSE);
595 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
596
597 serializer = GDK_CONTENT_SERIALIZER (result);
598
599 if (serializer->error)
600 {
601 if (error)
602 *error = g_error_copy (error: serializer->error);
603 return FALSE;
604 }
605
606 return TRUE;
607}
608
609/*** SERIALIZERS ***/
610
611
612static void
613pixbuf_serializer_finish (GObject *source,
614 GAsyncResult *res,
615 gpointer serializer)
616{
617 GError *error = NULL;
618
619 if (!gdk_pixbuf_save_to_stream_finish (async_result: res, error: &error))
620 gdk_content_serializer_return_error (serializer, error);
621 else
622 gdk_content_serializer_return_success (serializer);
623}
624
625static void
626pixbuf_serializer (GdkContentSerializer *serializer)
627{
628 const GValue *value;
629 GdkPixbuf *pixbuf;
630 const char *name;
631
632 name = gdk_content_serializer_get_user_data (serializer);
633 value = gdk_content_serializer_get_value (serializer);
634
635 if (G_VALUE_HOLDS (value, GDK_TYPE_PIXBUF))
636 {
637 pixbuf = g_value_dup_object (value);
638 }
639 else if (G_VALUE_HOLDS (value, GDK_TYPE_TEXTURE))
640 {
641 GdkTexture *texture = g_value_get_object (value);
642 pixbuf = gdk_pixbuf_get_from_texture (texture);
643 }
644 else
645 {
646 g_assert_not_reached ();
647 }
648
649 gdk_pixbuf_save_to_stream_async (pixbuf,
650 stream: gdk_content_serializer_get_output_stream (serializer),
651 type: name,
652 cancellable: gdk_content_serializer_get_cancellable (serializer),
653 callback: pixbuf_serializer_finish,
654 user_data: serializer,
655 g_str_equal (v1: name, v2: "png") ? "compression" : NULL, "2",
656 NULL);
657 g_object_unref (object: pixbuf);
658}
659
660static void
661texture_serializer_finish (GObject *source,
662 GAsyncResult *res,
663 gpointer user_data)
664{
665 GdkContentSerializer *serializer = GDK_CONTENT_SERIALIZER (source);
666 GError *error = NULL;
667
668 if (!g_task_propagate_boolean (G_TASK (res), error: &error))
669 gdk_content_serializer_return_error (serializer, error);
670 else
671 gdk_content_serializer_return_success (serializer);
672}
673
674static void
675serialize_texture_in_thread (GTask *task,
676 gpointer source_object,
677 gpointer task_data,
678 GCancellable *cancellable)
679{
680 GdkContentSerializer *serializer = source_object;
681 const GValue *value;
682 GdkTexture *texture;
683 GBytes *bytes = NULL;
684 GError *error = NULL;
685 gboolean result = FALSE;
686 GInputStream *input;
687 gssize spliced;
688
689 value = gdk_content_serializer_get_value (serializer);
690 texture = g_value_get_object (value);
691
692 if (strcmp (s1: gdk_content_serializer_get_mime_type (serializer), s2: "image/png") == 0)
693 bytes = gdk_save_png (texture);
694 else if (strcmp (s1: gdk_content_serializer_get_mime_type (serializer), s2: "image/tiff") == 0)
695 bytes = gdk_save_tiff (texture);
696 else if (strcmp (s1: gdk_content_serializer_get_mime_type (serializer), s2: "image/jpeg") == 0)
697 bytes = gdk_save_jpeg (texture);
698 else
699 g_assert_not_reached ();
700
701 input = g_memory_input_stream_new_from_bytes (bytes);
702 spliced = g_output_stream_splice (stream: gdk_content_serializer_get_output_stream (serializer),
703 source: input,
704 flags: G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE,
705 cancellable: gdk_content_serializer_get_cancellable (serializer),
706 error: &error);
707 g_object_unref (object: input);
708 g_bytes_unref (bytes);
709
710 result = spliced != -1;
711
712 if (result)
713 g_task_return_boolean (task, result);
714 else
715 g_task_return_error (task, error);
716}
717
718static void
719texture_serializer (GdkContentSerializer *serializer)
720{
721 GTask *task;
722
723 task = g_task_new (source_object: serializer,
724 cancellable: gdk_content_serializer_get_cancellable (serializer),
725 callback: texture_serializer_finish,
726 NULL);
727 g_task_run_in_thread (task, task_func: serialize_texture_in_thread);
728 g_object_unref (object: task);
729}
730
731static void
732string_serializer_finish (GObject *source,
733 GAsyncResult *result,
734 gpointer serializer)
735{
736 GOutputStream *stream = G_OUTPUT_STREAM (source);
737 GError *error = NULL;
738
739 if (!g_output_stream_write_all_finish (stream, result, NULL, error: &error))
740 gdk_content_serializer_return_error (serializer, error);
741 else
742 gdk_content_serializer_return_success (serializer);
743}
744
745static void
746string_serializer (GdkContentSerializer *serializer)
747{
748 GOutputStream *filter;
749 GCharsetConverter *converter;
750 GError *error = NULL;
751 const char *text;
752
753 converter = g_charset_converter_new (to_charset: gdk_content_serializer_get_user_data (serializer),
754 from_charset: "utf-8",
755 error: &error);
756 if (converter == NULL)
757 {
758 gdk_content_serializer_return_error (serializer, error);
759 return;
760 }
761 g_charset_converter_set_use_fallback (converter, TRUE);
762
763 filter = g_converter_output_stream_new (base_stream: gdk_content_serializer_get_output_stream (serializer),
764 G_CONVERTER (converter));
765 g_object_unref (object: converter);
766
767 text = g_value_get_string (value: gdk_content_serializer_get_value (serializer));
768 if (text == NULL)
769 text = "";
770
771 g_output_stream_write_all_async (stream: filter,
772 buffer: text,
773 count: strlen (s: text),
774 io_priority: gdk_content_serializer_get_priority (serializer),
775 cancellable: gdk_content_serializer_get_cancellable (serializer),
776 callback: string_serializer_finish,
777 user_data: serializer);
778 g_object_unref (object: filter);
779}
780
781static void
782file_serializer_finish (GObject *source,
783 GAsyncResult *result,
784 gpointer serializer)
785{
786 GOutputStream *stream = G_OUTPUT_STREAM (source);
787 GError *error = NULL;
788
789 if (!g_output_stream_write_all_finish (stream, result, NULL, error: &error))
790 gdk_content_serializer_return_error (serializer, error);
791 else
792 gdk_content_serializer_return_success (serializer);
793}
794
795static void
796file_uri_serializer (GdkContentSerializer *serializer)
797{
798 GFile *file;
799 GString *str;
800 const GValue *value;
801 char *uri;
802
803 str = g_string_new (NULL);
804 value = gdk_content_serializer_get_value (serializer);
805
806 if (G_VALUE_HOLDS (value, G_TYPE_FILE))
807 {
808 file = g_value_get_object (value: gdk_content_serializer_get_value (serializer));
809 if (file)
810 {
811 uri = g_file_get_uri (file);
812 g_string_append (string: str, val: uri);
813 g_free (mem: uri);
814 }
815 else
816 {
817 g_string_append (string: str, val: "# GTK does not crash when copying a NULL GFile!");
818 }
819 g_string_append (string: str, val: "\r\n");
820 }
821 else if (G_VALUE_HOLDS (value, GDK_TYPE_FILE_LIST))
822 {
823 GSList *l;
824
825 for (l = g_value_get_boxed (value); l; l = l->next)
826 {
827 uri = g_file_get_uri (file: l->data);
828 g_string_append (string: str, val: uri);
829 g_free (mem: uri);
830 g_string_append (string: str, val: "\r\n");
831 }
832 }
833
834 g_output_stream_write_all_async (stream: gdk_content_serializer_get_output_stream (serializer),
835 buffer: str->str,
836 count: str->len,
837 io_priority: gdk_content_serializer_get_priority (serializer),
838 cancellable: gdk_content_serializer_get_cancellable (serializer),
839 callback: file_serializer_finish,
840 user_data: serializer);
841 gdk_content_serializer_set_task_data (serializer, data: g_string_free (string: str, FALSE), notify: g_free);
842}
843
844static void
845file_text_serializer (GdkContentSerializer *serializer)
846{
847 const GValue *value;
848 char *path = NULL;
849
850 value = gdk_content_serializer_get_value (serializer);
851
852 if (G_VALUE_HOLDS (value, G_TYPE_FILE))
853 {
854 GFile *file;
855
856 file = g_value_get_object (value);
857 if (file)
858 {
859 path = g_file_get_path (file);
860 if (path == NULL)
861 path = g_file_get_uri (file);
862 }
863 }
864 else if (G_VALUE_HOLDS (value, GDK_TYPE_FILE_LIST))
865 {
866 GString *str;
867 GSList *l;
868
869 str = g_string_new (NULL);
870
871 for (l = g_value_get_boxed (value); l; l = l->next)
872 {
873 path = g_file_get_path (file: l->data);
874 if (path == NULL)
875 path = g_file_get_uri (file: l->data);
876 g_string_append (string: str, val: path);
877 g_free (mem: path);
878 if (l->next)
879 g_string_append (string: str, val: " ");
880 }
881 path = g_string_free (string: str, FALSE);
882 }
883
884 g_assert (path != NULL);
885
886 g_output_stream_write_all_async (stream: gdk_content_serializer_get_output_stream (serializer),
887 buffer: path,
888 count: strlen (s: path),
889 io_priority: gdk_content_serializer_get_priority (serializer),
890 cancellable: gdk_content_serializer_get_cancellable (serializer),
891 callback: file_serializer_finish,
892 user_data: serializer);
893 gdk_content_serializer_set_task_data (serializer, data: path, notify: g_free);
894}
895
896static void
897color_serializer_finish (GObject *source,
898 GAsyncResult *result,
899 gpointer serializer)
900{
901 GOutputStream *stream = G_OUTPUT_STREAM (source);
902 GError *error = NULL;
903
904 if (!g_output_stream_write_all_finish (stream, result, NULL, error: &error))
905 gdk_content_serializer_return_error (serializer, error);
906 else
907 gdk_content_serializer_return_success (serializer);
908}
909
910static void
911color_serializer (GdkContentSerializer *serializer)
912{
913 const GValue *value;
914 GdkRGBA *rgba;
915 guint16 *data;
916
917 value = gdk_content_serializer_get_value (serializer);
918 rgba = g_value_get_boxed (value);
919 data = g_new0 (guint16, 4);
920 if (rgba)
921 {
922 data[0] = (guint16) (rgba->red * 65535);
923 data[1] = (guint16) (rgba->green * 65535);
924 data[2] = (guint16) (rgba->blue * 65535);
925 data[3] = (guint16) (rgba->alpha * 65535);
926 }
927
928 g_output_stream_write_all_async (stream: gdk_content_serializer_get_output_stream (serializer),
929 buffer: data,
930 count: 4 * sizeof (guint16),
931 io_priority: gdk_content_serializer_get_priority (serializer),
932 cancellable: gdk_content_serializer_get_cancellable (serializer),
933 callback: color_serializer_finish,
934 user_data: serializer);
935 gdk_content_serializer_set_task_data (serializer, data, notify: g_free);
936}
937
938static void
939init (void)
940{
941 static gboolean initialized = FALSE;
942 GSList *formats, *f;
943 const char *charset;
944
945 if (initialized)
946 return;
947
948 initialized = TRUE;
949
950 gdk_content_register_serializer (GDK_TYPE_TEXTURE,
951 mime_type: "image/png",
952 serialize: texture_serializer,
953 NULL, NULL);
954
955 gdk_content_register_serializer (GDK_TYPE_TEXTURE,
956 mime_type: "image/tiff",
957 serialize: texture_serializer,
958 NULL, NULL);
959
960 gdk_content_register_serializer (GDK_TYPE_TEXTURE,
961 mime_type: "image/jpeg",
962 serialize: texture_serializer,
963 NULL, NULL);
964
965 formats = gdk_pixbuf_get_formats ();
966
967 for (f = formats; f; f = f->next)
968 {
969 GdkPixbufFormat *fmt = f->data;
970 char **mimes, **m;
971 char *name;
972
973 if (!gdk_pixbuf_format_is_writable (format: fmt))
974 continue;
975
976 name = gdk_pixbuf_format_get_name (format: fmt);
977 mimes = gdk_pixbuf_format_get_mime_types (format: fmt);
978 for (m = mimes; *m; m++)
979 {
980 /* Turning textures into pngs, tiffs or jpegs is handled above */
981 if (!g_str_equal (v1: name, v2: "png") &&
982 !g_str_equal (v1: name, v2: "tiff") &&
983 !g_str_equal (v1: name, v2: "jpeg"))
984 gdk_content_register_serializer (GDK_TYPE_TEXTURE,
985 mime_type: *m,
986 serialize: pixbuf_serializer,
987 data: gdk_pixbuf_format_get_name (format: fmt),
988 notify: g_free);
989 gdk_content_register_serializer (GDK_TYPE_PIXBUF,
990 mime_type: *m,
991 serialize: pixbuf_serializer,
992 data: gdk_pixbuf_format_get_name (format: fmt),
993 notify: g_free);
994 }
995 g_strfreev (str_array: mimes);
996 g_free (mem: name);
997 }
998
999 g_slist_free (list: formats);
1000
1001#if defined(G_OS_UNIX) && !defined(__APPLE__)
1002 file_transfer_portal_register ();
1003#endif
1004
1005 gdk_content_register_serializer (G_TYPE_FILE,
1006 mime_type: "text/uri-list",
1007 serialize: file_uri_serializer,
1008 NULL,
1009 NULL);
1010 gdk_content_register_serializer (G_TYPE_FILE,
1011 mime_type: "text/plain;charset=utf-8",
1012 serialize: file_text_serializer,
1013 NULL,
1014 NULL);
1015
1016 gdk_content_register_serializer (GDK_TYPE_FILE_LIST,
1017 mime_type: "text/uri-list",
1018 serialize: file_uri_serializer,
1019 NULL,
1020 NULL);
1021 gdk_content_register_serializer (GDK_TYPE_FILE_LIST,
1022 mime_type: "text/plain;charset=utf-8",
1023 serialize: file_text_serializer,
1024 NULL,
1025 NULL);
1026
1027 gdk_content_register_serializer (G_TYPE_STRING,
1028 mime_type: "text/plain;charset=utf-8",
1029 serialize: string_serializer,
1030 data: (gpointer) "utf-8",
1031 NULL);
1032 if (!g_get_charset (charset: &charset))
1033 {
1034 char *mime = g_strdup_printf (format: "text/plain;charset=%s", charset);
1035 gdk_content_register_serializer (G_TYPE_STRING,
1036 mime_type: mime,
1037 serialize: string_serializer,
1038 data: (gpointer) charset,
1039 NULL);
1040 g_free (mem: mime);
1041 }
1042 gdk_content_register_serializer (G_TYPE_STRING,
1043 mime_type: "text/plain",
1044 serialize: string_serializer,
1045 data: (gpointer) "ASCII",
1046 NULL);
1047
1048 gdk_content_register_serializer (GDK_TYPE_RGBA,
1049 mime_type: "application/x-color",
1050 serialize: color_serializer,
1051 NULL,
1052 NULL);
1053}
1054
1055

source code of gtk/gdk/gdkcontentserializer.c