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 | |
50 | typedef struct _Deserializer Deserializer; |
51 | |
52 | struct _Deserializer |
53 | { |
54 | const char * mime_type; /* interned */ |
55 | GType type; |
56 | GdkContentDeserializeFunc deserialize; |
57 | gpointer data; |
58 | GDestroyNotify notify; |
59 | }; |
60 | |
61 | GQueue deserializers = G_QUEUE_INIT; |
62 | |
63 | static 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 | |
69 | typedef struct _GdkContentDeserializerClass GdkContentDeserializerClass; |
70 | |
71 | struct _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 | |
91 | struct _GdkContentDeserializerClass |
92 | { |
93 | GObjectClass parent_class; |
94 | }; |
95 | |
96 | static gpointer |
97 | gdk_content_deserializer_async_result_get_user_data (GAsyncResult *res) |
98 | { |
99 | return GDK_CONTENT_DESERIALIZER (res)->callback_data; |
100 | } |
101 | |
102 | static GObject * |
103 | gdk_content_deserializer_async_result_get_source_object (GAsyncResult *res) |
104 | { |
105 | return NULL; |
106 | } |
107 | |
108 | static void |
109 | gdk_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 | |
115 | G_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 | |
118 | static void |
119 | gdk_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 | |
134 | static void |
135 | gdk_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 | |
142 | static void |
143 | gdk_content_deserializer_init (GdkContentDeserializer *content_deserializer) |
144 | { |
145 | } |
146 | |
147 | static void |
148 | gdk_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 | */ |
183 | const char * |
184 | gdk_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 | */ |
199 | GType |
200 | gdk_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 | */ |
215 | GValue * |
216 | gdk_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 | */ |
233 | GInputStream * |
234 | gdk_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 | */ |
251 | int |
252 | gdk_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 | */ |
269 | GCancellable * |
270 | gdk_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 | */ |
285 | gpointer |
286 | gdk_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 | */ |
301 | void |
302 | gdk_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 | */ |
325 | gpointer |
326 | gdk_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 | |
333 | static gboolean |
334 | gdk_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 | */ |
352 | void |
353 | gdk_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 | */ |
375 | void |
376 | gdk_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 | */ |
398 | void |
399 | gdk_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 | |
421 | static Deserializer * |
422 | lookup_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 | */ |
454 | GdkContentFormats * |
455 | gdk_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 | */ |
489 | GdkContentFormats * |
490 | gdk_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 | |
515 | static void |
516 | deserialize_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 | */ |
544 | void |
545 | gdk_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 | */ |
586 | gboolean |
587 | gdk_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 | |
614 | static void |
615 | pixbuf_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 | |
649 | static void |
650 | pixbuf_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 | |
658 | static void |
659 | texture_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 | |
690 | static void |
691 | texture_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 | |
708 | static void |
709 | string_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 | |
746 | static void |
747 | string_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 | |
778 | static void |
779 | file_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 | |
830 | static void |
831 | file_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 | |
847 | static void |
848 | color_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 | |
885 | static void |
886 | color_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 | |
904 | static void |
905 | init (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 | |