1 | /* GIO - GLib Input, Output and Streaming Library |
2 | * |
3 | * Copyright (C) 2017 Red Hat, Inc. |
4 | * |
5 | * This library is free software; you can redistribute it and/or |
6 | * modify it under the terms of the GNU Lesser General Public |
7 | * License as published by the Free Software Foundation; either |
8 | * version 2.1 of the License, or (at your option) any later version. |
9 | * |
10 | * This library is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | * Lesser General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU Lesser General |
16 | * Public License along with this library; if not, see <http://www.gnu.org/licenses/>. |
17 | * |
18 | * Author: Benjamin Otte <otte@gnome.org> |
19 | * Christian Kellner <gicmo@gnome.org> |
20 | */ |
21 | |
22 | #include "config.h" |
23 | |
24 | #include "gdkselectioninputstream-x11.h" |
25 | |
26 | #include "gdkdisplay-x11.h" |
27 | #include "gdkintl.h" |
28 | #include "gdkx11display.h" |
29 | #include "gdkx11property.h" |
30 | #include "gdkx11surface.h" |
31 | |
32 | typedef struct GdkX11SelectionInputStreamPrivate GdkX11SelectionInputStreamPrivate; |
33 | |
34 | struct GdkX11SelectionInputStreamPrivate { |
35 | GdkDisplay *display; |
36 | GAsyncQueue *chunks; |
37 | char *selection; |
38 | Atom xselection; |
39 | char *target; |
40 | Atom xtarget; |
41 | char *property; |
42 | Atom xproperty; |
43 | const char *type; |
44 | Atom xtype; |
45 | int format; |
46 | |
47 | GTask *pending_task; |
48 | guchar *pending_data; |
49 | gsize pending_size; |
50 | |
51 | guint complete : 1; |
52 | guint incr : 1; |
53 | }; |
54 | |
55 | G_DEFINE_TYPE_WITH_PRIVATE (GdkX11SelectionInputStream, gdk_x11_selection_input_stream, G_TYPE_INPUT_STREAM); |
56 | |
57 | static gboolean |
58 | gdk_x11_selection_input_stream_xevent (GdkDisplay *display, |
59 | const XEvent *xevent, |
60 | gpointer data); |
61 | |
62 | static gboolean |
63 | gdk_x11_selection_input_stream_has_data (GdkX11SelectionInputStream *stream) |
64 | { |
65 | GdkX11SelectionInputStreamPrivate *priv = gdk_x11_selection_input_stream_get_instance_private (self: stream); |
66 | |
67 | return g_async_queue_length (queue: priv->chunks) > 0 || priv->complete; |
68 | } |
69 | |
70 | /* NB: blocks when no data is in buffer */ |
71 | static gsize |
72 | gdk_x11_selection_input_stream_fill_buffer (GdkX11SelectionInputStream *stream, |
73 | guchar *buffer, |
74 | gsize count) |
75 | { |
76 | GdkX11SelectionInputStreamPrivate *priv = gdk_x11_selection_input_stream_get_instance_private (self: stream); |
77 | GBytes *bytes; |
78 | |
79 | gsize result = 0; |
80 | |
81 | g_async_queue_lock (queue: priv->chunks); |
82 | |
83 | for (bytes = g_async_queue_pop_unlocked (queue: priv->chunks); |
84 | bytes != NULL && count > 0; |
85 | bytes = g_async_queue_try_pop_unlocked (queue: priv->chunks)) |
86 | { |
87 | gsize size = g_bytes_get_size (bytes); |
88 | |
89 | if (size == 0) |
90 | { |
91 | /* EOF marker, put it back */ |
92 | g_async_queue_push_front_unlocked (queue: priv->chunks, item: bytes); |
93 | break; |
94 | } |
95 | else if (size > count) |
96 | { |
97 | GBytes *subbytes; |
98 | if (buffer) |
99 | memcpy (dest: buffer, src: g_bytes_get_data (bytes, NULL), n: count); |
100 | subbytes = g_bytes_new_from_bytes (bytes, offset: count, length: size - count); |
101 | g_async_queue_push_front_unlocked (queue: priv->chunks, item: subbytes); |
102 | size = count; |
103 | } |
104 | else |
105 | { |
106 | if (buffer) |
107 | memcpy (dest: buffer, src: g_bytes_get_data (bytes, NULL), n: size); |
108 | } |
109 | |
110 | g_bytes_unref (bytes); |
111 | result += size; |
112 | if (buffer) |
113 | buffer += size; |
114 | count -= size; |
115 | } |
116 | |
117 | if (bytes) |
118 | g_async_queue_push_front_unlocked (queue: priv->chunks, item: bytes); |
119 | |
120 | g_async_queue_unlock (queue: priv->chunks); |
121 | |
122 | return result; |
123 | } |
124 | |
125 | static void |
126 | gdk_x11_selection_input_stream_flush (GdkX11SelectionInputStream *stream) |
127 | { |
128 | GdkX11SelectionInputStreamPrivate *priv = gdk_x11_selection_input_stream_get_instance_private (self: stream); |
129 | gssize written; |
130 | |
131 | if (!gdk_x11_selection_input_stream_has_data (stream)) |
132 | return; |
133 | |
134 | if (priv->pending_task == NULL) |
135 | return; |
136 | |
137 | written = gdk_x11_selection_input_stream_fill_buffer (stream, buffer: priv->pending_data, count: priv->pending_size); |
138 | GDK_NOTE (SELECTION, g_printerr ("%s:%s: finishing read of %zd/%zu bytes\n" , |
139 | priv->selection, priv->target, |
140 | written, priv->pending_size)); |
141 | g_task_return_int (task: priv->pending_task, result: written); |
142 | |
143 | g_clear_object (&priv->pending_task); |
144 | priv->pending_data = NULL; |
145 | priv->pending_size = 0; |
146 | } |
147 | |
148 | static void |
149 | gdk_x11_selection_input_stream_complete (GdkX11SelectionInputStream *stream) |
150 | { |
151 | GdkX11SelectionInputStreamPrivate *priv = gdk_x11_selection_input_stream_get_instance_private (self: stream); |
152 | |
153 | if (priv->complete) |
154 | return; |
155 | |
156 | GDK_NOTE (SELECTION, g_printerr ("%s:%s: transfer complete\n" , |
157 | priv->selection, priv->target)); |
158 | priv->complete = TRUE; |
159 | |
160 | g_async_queue_push (queue: priv->chunks, data: g_bytes_new (NULL, size: 0)); |
161 | gdk_x11_selection_input_stream_flush (stream); |
162 | |
163 | GDK_X11_DISPLAY (priv->display)->streams = g_slist_remove (GDK_X11_DISPLAY (priv->display)->streams, data: stream); |
164 | g_signal_handlers_disconnect_by_func (priv->display, |
165 | gdk_x11_selection_input_stream_xevent, |
166 | stream); |
167 | |
168 | g_object_unref (object: stream); |
169 | } |
170 | |
171 | static gssize |
172 | gdk_x11_selection_input_stream_read (GInputStream *input_stream, |
173 | void *buffer, |
174 | gsize count, |
175 | GCancellable *cancellable, |
176 | GError **error) |
177 | { |
178 | GdkX11SelectionInputStream *stream = GDK_X11_SELECTION_INPUT_STREAM (input_stream); |
179 | #ifdef G_ENABLE_DEBUG |
180 | GdkX11SelectionInputStreamPrivate *priv = gdk_x11_selection_input_stream_get_instance_private (self: stream); |
181 | #endif |
182 | gssize written; |
183 | |
184 | GDK_NOTE (SELECTION, g_printerr ("%s:%s: starting sync read of %zu bytes\n" , |
185 | priv->selection, priv->target, |
186 | count)); |
187 | written = gdk_x11_selection_input_stream_fill_buffer (stream, buffer, count); |
188 | GDK_NOTE (SELECTION, g_printerr ("%s:%s: finishing sync read of %zd/%zu bytes\n" , |
189 | priv->selection, priv->target, |
190 | written, count)); |
191 | return written; |
192 | } |
193 | |
194 | static gboolean |
195 | gdk_x11_selection_input_stream_close (GInputStream *stream, |
196 | GCancellable *cancellable, |
197 | GError **error) |
198 | { |
199 | return TRUE; |
200 | } |
201 | |
202 | static void |
203 | gdk_x11_selection_input_stream_read_async (GInputStream *input_stream, |
204 | void *buffer, |
205 | gsize count, |
206 | int io_priority, |
207 | GCancellable *cancellable, |
208 | GAsyncReadyCallback callback, |
209 | gpointer user_data) |
210 | { |
211 | GdkX11SelectionInputStream *stream = GDK_X11_SELECTION_INPUT_STREAM (input_stream); |
212 | GdkX11SelectionInputStreamPrivate *priv = gdk_x11_selection_input_stream_get_instance_private (self: stream); |
213 | GTask *task; |
214 | |
215 | task = g_task_new (source_object: stream, cancellable, callback, callback_data: user_data); |
216 | g_task_set_source_tag (task, gdk_x11_selection_input_stream_read_async); |
217 | g_task_set_priority (task, priority: io_priority); |
218 | |
219 | if (gdk_x11_selection_input_stream_has_data (stream)) |
220 | { |
221 | gssize size; |
222 | |
223 | size = gdk_x11_selection_input_stream_fill_buffer (stream, buffer, count); |
224 | GDK_NOTE (SELECTION, g_printerr ("%s:%s: async read of %zd/%zu bytes\n" , |
225 | priv->selection, priv->target, |
226 | size, count)); |
227 | g_task_return_int (task, result: size); |
228 | g_object_unref (object: task); |
229 | } |
230 | else |
231 | { |
232 | priv->pending_data = buffer; |
233 | priv->pending_size = count; |
234 | priv->pending_task = task; |
235 | GDK_NOTE (SELECTION, g_printerr ("%s:%s: async read of %zu bytes pending\n" , |
236 | priv->selection, priv->target, |
237 | count)); |
238 | } |
239 | } |
240 | |
241 | static gssize |
242 | gdk_x11_selection_input_stream_read_finish (GInputStream *stream, |
243 | GAsyncResult *result, |
244 | GError **error) |
245 | { |
246 | g_return_val_if_fail (g_task_is_valid (result, stream), -1); |
247 | g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == gdk_x11_selection_input_stream_read_async, -1); |
248 | |
249 | return g_task_propagate_int (G_TASK (result), error); |
250 | } |
251 | |
252 | static void |
253 | gdk_x11_selection_input_stream_close_async (GInputStream *stream, |
254 | int io_priority, |
255 | GCancellable *cancellable, |
256 | GAsyncReadyCallback callback, |
257 | gpointer user_data) |
258 | { |
259 | GTask *task; |
260 | |
261 | task = g_task_new (source_object: stream, cancellable, callback, callback_data: user_data); |
262 | g_task_set_source_tag (task, gdk_x11_selection_input_stream_close_async); |
263 | g_task_return_boolean (task, TRUE); |
264 | g_object_unref (object: task); |
265 | } |
266 | |
267 | static gboolean |
268 | gdk_x11_selection_input_stream_close_finish (GInputStream *stream, |
269 | GAsyncResult *result, |
270 | GError **error) |
271 | { |
272 | return TRUE; |
273 | } |
274 | |
275 | static void |
276 | gdk_x11_selection_input_stream_finalize (GObject *object) |
277 | { |
278 | GdkX11SelectionInputStream *stream = GDK_X11_SELECTION_INPUT_STREAM (object); |
279 | GdkX11SelectionInputStreamPrivate *priv = gdk_x11_selection_input_stream_get_instance_private (self: stream); |
280 | |
281 | gdk_x11_selection_input_stream_complete (stream); |
282 | |
283 | g_async_queue_unref (queue: priv->chunks); |
284 | |
285 | g_free (mem: priv->selection); |
286 | g_free (mem: priv->target); |
287 | g_free (mem: priv->property); |
288 | |
289 | G_OBJECT_CLASS (gdk_x11_selection_input_stream_parent_class)->finalize (object); |
290 | } |
291 | |
292 | static void |
293 | gdk_x11_selection_input_stream_class_init (GdkX11SelectionInputStreamClass *klass) |
294 | { |
295 | GObjectClass *object_class = G_OBJECT_CLASS (klass); |
296 | GInputStreamClass *istream_class = G_INPUT_STREAM_CLASS (klass); |
297 | |
298 | object_class->finalize = gdk_x11_selection_input_stream_finalize; |
299 | |
300 | istream_class->read_fn = gdk_x11_selection_input_stream_read; |
301 | istream_class->close_fn = gdk_x11_selection_input_stream_close; |
302 | |
303 | istream_class->read_async = gdk_x11_selection_input_stream_read_async; |
304 | istream_class->read_finish = gdk_x11_selection_input_stream_read_finish; |
305 | istream_class->close_async = gdk_x11_selection_input_stream_close_async; |
306 | istream_class->close_finish = gdk_x11_selection_input_stream_close_finish; |
307 | } |
308 | |
309 | static void |
310 | gdk_x11_selection_input_stream_init (GdkX11SelectionInputStream *stream) |
311 | { |
312 | GdkX11SelectionInputStreamPrivate *priv = gdk_x11_selection_input_stream_get_instance_private (self: stream); |
313 | |
314 | priv->chunks = g_async_queue_new_full (item_free_func: (GDestroyNotify) g_bytes_unref); |
315 | } |
316 | |
317 | static void |
318 | XFree_without_return_value (gpointer data) |
319 | { |
320 | XFree (data); |
321 | } |
322 | |
323 | static GBytes * |
324 | get_selection_property (Display *display, |
325 | Window owner, |
326 | Atom property, |
327 | Atom *ret_type, |
328 | int *ret_format) |
329 | { |
330 | gulong nitems; |
331 | gulong nbytes; |
332 | Atom prop_type; |
333 | int prop_format; |
334 | guchar *data = NULL; |
335 | |
336 | if (XGetWindowProperty (display, owner, property, |
337 | 0, 0x1FFFFFFF, False, |
338 | AnyPropertyType, &prop_type, &prop_format, |
339 | &nitems, &nbytes, &data) != Success) |
340 | goto err; |
341 | |
342 | if (prop_type != None) |
343 | { |
344 | gsize length; |
345 | |
346 | switch (prop_format) |
347 | { |
348 | case 8: |
349 | length = nitems; |
350 | break; |
351 | case 16: |
352 | length = sizeof (short) * nitems; |
353 | break; |
354 | case 32: |
355 | length = sizeof (long) * nitems; |
356 | break; |
357 | default: |
358 | g_warning ("Unknown XGetWindowProperty() format %u" , prop_format); |
359 | goto err; |
360 | } |
361 | |
362 | *ret_type = prop_type; |
363 | *ret_format = prop_format; |
364 | |
365 | return g_bytes_new_with_free_func (data, |
366 | size: length, |
367 | free_func: XFree_without_return_value, |
368 | user_data: data); |
369 | } |
370 | |
371 | err: |
372 | if (data) |
373 | XFree (data); |
374 | |
375 | *ret_type = None; |
376 | *ret_format = 0; |
377 | |
378 | return NULL; |
379 | } |
380 | |
381 | |
382 | static gboolean |
383 | gdk_x11_selection_input_stream_xevent (GdkDisplay *display, |
384 | const XEvent *xevent, |
385 | gpointer data) |
386 | { |
387 | GdkX11SelectionInputStream *stream = GDK_X11_SELECTION_INPUT_STREAM (data); |
388 | GdkX11SelectionInputStreamPrivate *priv = gdk_x11_selection_input_stream_get_instance_private (self: stream); |
389 | Display *xdisplay; |
390 | Window xwindow; |
391 | GBytes *bytes; |
392 | Atom type; |
393 | int format; |
394 | |
395 | xdisplay = gdk_x11_display_get_xdisplay (display: priv->display); |
396 | xwindow = GDK_X11_DISPLAY (priv->display)->leader_window; |
397 | |
398 | if (xevent->xany.display != xdisplay || |
399 | xevent->xany.window != xwindow) |
400 | return FALSE; |
401 | |
402 | switch (xevent->type) |
403 | { |
404 | case PropertyNotify: |
405 | if (!priv->incr || |
406 | xevent->xproperty.atom != priv->xproperty || |
407 | xevent->xproperty.state != PropertyNewValue) |
408 | return FALSE; |
409 | |
410 | bytes = get_selection_property (display: xdisplay, owner: xwindow, property: xevent->xproperty.atom, ret_type: &type, ret_format: &format); |
411 | if (bytes == NULL) |
412 | { |
413 | GDK_DISPLAY_NOTE (display, SELECTION, g_printerr ("%s:%s: got PropertyNotify erroring out of INCR\n" , |
414 | priv->selection, priv->target)); |
415 | /* error, should we signal one? */ |
416 | gdk_x11_selection_input_stream_complete (stream); |
417 | } |
418 | else if (g_bytes_get_size (bytes) == 0 || type == None) |
419 | { |
420 | GDK_DISPLAY_NOTE (display, SELECTION, g_printerr ("%s:%s: got PropertyNotify ending INCR\n" , |
421 | priv->selection, priv->target)); |
422 | g_bytes_unref (bytes); |
423 | gdk_x11_selection_input_stream_complete (stream); |
424 | } |
425 | else |
426 | { |
427 | GDK_DISPLAY_NOTE (display, SELECTION, g_printerr ("%s:%s: got PropertyNotify during INCR with %zu bytes\n" , |
428 | priv->selection, priv->target, |
429 | g_bytes_get_size (bytes))); |
430 | g_async_queue_push (queue: priv->chunks, data: bytes); |
431 | gdk_x11_selection_input_stream_flush (stream); |
432 | } |
433 | |
434 | XDeleteProperty (xdisplay, xwindow, xevent->xproperty.atom); |
435 | |
436 | return FALSE; |
437 | |
438 | case SelectionNotify: |
439 | { |
440 | GTask *task; |
441 | |
442 | /* selection is not for us */ |
443 | if (priv->xselection != xevent->xselection.selection || |
444 | priv->xtarget != xevent->xselection.target) |
445 | return FALSE; |
446 | |
447 | /* We already received a selectionNotify before */ |
448 | if (priv->pending_task == NULL || |
449 | g_task_get_source_tag (task: priv->pending_task) != gdk_x11_selection_input_stream_new_async) |
450 | return FALSE; |
451 | |
452 | GDK_DISPLAY_NOTE (display, SELECTION, g_printerr ("%s:%s: got SelectionNotify\n" , priv->selection, priv->target)); |
453 | |
454 | task = priv->pending_task; |
455 | priv->pending_task = NULL; |
456 | |
457 | if (xevent->xselection.property == None) |
458 | { |
459 | g_task_return_new_error (task, |
460 | G_IO_ERROR, |
461 | code: G_IO_ERROR_NOT_FOUND, |
462 | _("Format %s not supported" ), priv->target); |
463 | gdk_x11_selection_input_stream_complete (stream); |
464 | } |
465 | else |
466 | { |
467 | bytes = get_selection_property (display: xdisplay, owner: xwindow, property: xevent->xselection.property, ret_type: &priv->xtype, ret_format: &priv->format); |
468 | priv->type = gdk_x11_get_xatom_name_for_display (display: priv->display, xatom: priv->xtype); |
469 | |
470 | g_task_return_pointer (task, g_object_ref (stream), result_destroy: g_object_unref); |
471 | |
472 | if (bytes == NULL) |
473 | { |
474 | gdk_x11_selection_input_stream_complete (stream); |
475 | } |
476 | else |
477 | { |
478 | if (priv->xtype == gdk_x11_get_xatom_by_name_for_display (display: priv->display, atom_name: "INCR" )) |
479 | { |
480 | /* The remainder of the selection will come through PropertyNotify |
481 | events on xwindow */ |
482 | GDK_DISPLAY_NOTE (display, SELECTION, g_printerr ("%s:%s: initiating INCR transfer\n" , priv->selection, priv->target)); |
483 | priv->incr = TRUE; |
484 | gdk_x11_selection_input_stream_flush (stream); |
485 | } |
486 | else |
487 | { |
488 | GDK_DISPLAY_NOTE (display, SELECTION, g_printerr ("%s:%s: reading %zu bytes\n" , |
489 | priv->selection, priv->target, |
490 | g_bytes_get_size (bytes))); |
491 | g_async_queue_push (queue: priv->chunks, data: bytes); |
492 | |
493 | gdk_x11_selection_input_stream_complete (stream); |
494 | } |
495 | } |
496 | |
497 | XDeleteProperty (xdisplay, xwindow, xevent->xselection.property); |
498 | } |
499 | |
500 | g_object_unref (object: task); |
501 | } |
502 | return TRUE; |
503 | |
504 | default: |
505 | return FALSE; |
506 | } |
507 | } |
508 | |
509 | void |
510 | gdk_x11_selection_input_stream_new_async (GdkDisplay *display, |
511 | const char *selection, |
512 | const char *target, |
513 | guint32 timestamp, |
514 | int io_priority, |
515 | GCancellable *cancellable, |
516 | GAsyncReadyCallback callback, |
517 | gpointer user_data) |
518 | { |
519 | GdkX11SelectionInputStream *stream; |
520 | GdkX11SelectionInputStreamPrivate *priv; |
521 | |
522 | stream = g_object_new (GDK_TYPE_X11_SELECTION_INPUT_STREAM, NULL); |
523 | priv = gdk_x11_selection_input_stream_get_instance_private (self: stream); |
524 | |
525 | priv->display = display; |
526 | GDK_X11_DISPLAY (display)->streams = g_slist_prepend (GDK_X11_DISPLAY (display)->streams, data: stream); |
527 | priv->selection = g_strdup (str: selection); |
528 | priv->xselection = gdk_x11_get_xatom_by_name_for_display (display, atom_name: priv->selection); |
529 | priv->target = g_strdup (str: target); |
530 | priv->xtarget = gdk_x11_get_xatom_by_name_for_display (display, atom_name: priv->target); |
531 | priv->property = g_strdup_printf (format: "GDK_SELECTION_%p" , stream); |
532 | priv->xproperty = gdk_x11_get_xatom_by_name_for_display (display, atom_name: priv->property); |
533 | |
534 | g_signal_connect (display, "xevent" , G_CALLBACK (gdk_x11_selection_input_stream_xevent), stream); |
535 | |
536 | XConvertSelection (GDK_DISPLAY_XDISPLAY (display), |
537 | priv->xselection, |
538 | priv->xtarget, |
539 | priv->xproperty, |
540 | GDK_X11_DISPLAY (display)->leader_window, |
541 | timestamp); |
542 | |
543 | priv->pending_task = g_task_new (NULL, cancellable, callback, callback_data: user_data); |
544 | g_task_set_source_tag (priv->pending_task, gdk_x11_selection_input_stream_new_async); |
545 | g_task_set_priority (task: priv->pending_task, priority: io_priority); |
546 | } |
547 | |
548 | GInputStream * |
549 | gdk_x11_selection_input_stream_new_finish (GAsyncResult *result, |
550 | const char **type, |
551 | int *format, |
552 | GError **error) |
553 | { |
554 | GdkX11SelectionInputStream *stream; |
555 | GTask *task; |
556 | |
557 | g_return_val_if_fail (g_task_is_valid (result, NULL), NULL); |
558 | task = G_TASK (result); |
559 | g_return_val_if_fail (g_task_get_source_tag (task) == gdk_x11_selection_input_stream_new_async, NULL); |
560 | |
561 | stream = g_task_propagate_pointer (task, error); |
562 | if (stream) |
563 | { |
564 | GdkX11SelectionInputStreamPrivate *priv = gdk_x11_selection_input_stream_get_instance_private (self: stream); |
565 | |
566 | if (type) |
567 | *type = priv->type; |
568 | if (format) |
569 | *format = priv->format; |
570 | g_object_ref (stream); |
571 | } |
572 | |
573 | return G_INPUT_STREAM (stream); |
574 | } |
575 | |
576 | |