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 | |
55 | typedef struct _Serializer Serializer; |
56 | |
57 | struct _Serializer |
58 | { |
59 | const char * mime_type; /* interned */ |
60 | GType type; |
61 | GdkContentSerializeFunc serialize; |
62 | gpointer data; |
63 | GDestroyNotify notify; |
64 | }; |
65 | |
66 | GQueue serializers = G_QUEUE_INIT; |
67 | |
68 | static 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 | |
74 | typedef struct _GdkContentSerializerClass GdkContentSerializerClass; |
75 | |
76 | struct _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 | |
96 | struct _GdkContentSerializerClass |
97 | { |
98 | GObjectClass parent_class; |
99 | }; |
100 | |
101 | static gpointer |
102 | gdk_content_serializer_async_result_get_user_data (GAsyncResult *res) |
103 | { |
104 | return GDK_CONTENT_SERIALIZER (res)->callback_data; |
105 | } |
106 | |
107 | static GObject * |
108 | gdk_content_serializer_async_result_get_source_object (GAsyncResult *res) |
109 | { |
110 | return NULL; |
111 | } |
112 | |
113 | static void |
114 | gdk_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 | |
120 | G_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 | |
123 | static void |
124 | gdk_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 | |
139 | static void |
140 | gdk_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 | |
147 | static void |
148 | gdk_content_serializer_init (GdkContentSerializer *content_serializer) |
149 | { |
150 | } |
151 | |
152 | static void |
153 | gdk_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 | */ |
189 | const char * |
190 | gdk_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 | */ |
205 | GType |
206 | gdk_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 | */ |
221 | const GValue * |
222 | gdk_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 | */ |
239 | GOutputStream * |
240 | gdk_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 | */ |
257 | int |
258 | gdk_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 | */ |
275 | GCancellable * |
276 | gdk_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 | */ |
291 | gpointer |
292 | gdk_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 | */ |
307 | void |
308 | gdk_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 | */ |
331 | gpointer |
332 | gdk_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 | |
339 | static gboolean |
340 | gdk_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 | */ |
358 | void |
359 | gdk_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 | */ |
381 | void |
382 | gdk_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 | */ |
404 | void |
405 | gdk_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 | |
427 | static Serializer * |
428 | lookup_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 | */ |
460 | GdkContentFormats * |
461 | gdk_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 | */ |
495 | GdkContentFormats * |
496 | gdk_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 | |
521 | static void |
522 | serialize_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 | */ |
550 | void |
551 | gdk_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 | */ |
588 | gboolean |
589 | gdk_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 | |
612 | static void |
613 | pixbuf_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 | |
625 | static void |
626 | pixbuf_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 | |
660 | static void |
661 | texture_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 | |
674 | static void |
675 | serialize_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 | |
718 | static void |
719 | texture_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 | |
731 | static void |
732 | string_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 | |
745 | static void |
746 | string_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 | |
781 | static void |
782 | file_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 | |
795 | static void |
796 | file_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 | |
844 | static void |
845 | file_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 | |
896 | static void |
897 | color_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 | |
910 | static void |
911 | color_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 | |
938 | static void |
939 | init (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 | |