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
32typedef struct GdkX11SelectionInputStreamPrivate GdkX11SelectionInputStreamPrivate;
33
34struct 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
55G_DEFINE_TYPE_WITH_PRIVATE (GdkX11SelectionInputStream, gdk_x11_selection_input_stream, G_TYPE_INPUT_STREAM);
56
57static gboolean
58gdk_x11_selection_input_stream_xevent (GdkDisplay *display,
59 const XEvent *xevent,
60 gpointer data);
61
62static gboolean
63gdk_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 */
71static gsize
72gdk_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
125static void
126gdk_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
148static void
149gdk_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
171static gssize
172gdk_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
194static gboolean
195gdk_x11_selection_input_stream_close (GInputStream *stream,
196 GCancellable *cancellable,
197 GError **error)
198{
199 return TRUE;
200}
201
202static void
203gdk_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
241static gssize
242gdk_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
252static void
253gdk_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
267static gboolean
268gdk_x11_selection_input_stream_close_finish (GInputStream *stream,
269 GAsyncResult *result,
270 GError **error)
271{
272 return TRUE;
273}
274
275static void
276gdk_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
292static void
293gdk_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
309static void
310gdk_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
317static void
318XFree_without_return_value (gpointer data)
319{
320 XFree (data);
321}
322
323static GBytes *
324get_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
371err:
372 if (data)
373 XFree (data);
374
375 *ret_type = None;
376 *ret_format = 0;
377
378 return NULL;
379}
380
381
382static gboolean
383gdk_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
509void
510gdk_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
548GInputStream *
549gdk_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

source code of gtk/gdk/x11/gdkselectioninputstream-x11.c