1/* GIO - GLib Output, 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 "gdkselectionoutputstream-x11.h"
25
26#include "gdkclipboard-x11.h"
27#include "gdkdisplay-x11.h"
28#include "gdkintl.h"
29#include "gdktextlistconverter-x11.h"
30#include "gdkx11display.h"
31#include "gdkx11property.h"
32#include "gdkx11surface.h"
33
34typedef struct _GdkX11PendingSelectionNotify GdkX11PendingSelectionNotify;
35typedef struct _GdkX11SelectionOutputStreamPrivate GdkX11SelectionOutputStreamPrivate;
36
37struct _GdkX11SelectionOutputStreamPrivate {
38 GdkDisplay *display;
39 GdkX11PendingSelectionNotify *notify;
40 Window xwindow;
41 char *selection;
42 Atom xselection;
43 char *target;
44 Atom xtarget;
45 char *property;
46 Atom xproperty;
47 char *type;
48 Atom xtype;
49 int format;
50 gulong timestamp;
51
52 GMutex mutex;
53 GCond cond;
54 GByteArray *data;
55 guint flush_requested : 1;
56
57 GTask *pending_task;
58
59 guint incr : 1;
60 guint sent_end_of_stream : 1;
61 guint delete_pending : 1; /* owns a reference */
62};
63
64struct _GdkX11PendingSelectionNotify
65{
66 gsize n_pending;
67
68 XSelectionEvent xevent;
69};
70
71G_DEFINE_TYPE_WITH_PRIVATE (GdkX11SelectionOutputStream, gdk_x11_selection_output_stream, G_TYPE_OUTPUT_STREAM);
72
73static GdkX11PendingSelectionNotify *
74gdk_x11_pending_selection_notify_new (Window window,
75 Atom selection,
76 Atom target,
77 Atom property,
78 Time timestamp)
79{
80 GdkX11PendingSelectionNotify *pending;
81
82 pending = g_slice_new0 (GdkX11PendingSelectionNotify);
83 pending->n_pending = 1;
84
85 pending->xevent.type = SelectionNotify;
86 pending->xevent.serial = 0;
87 pending->xevent.send_event = True;
88 pending->xevent.requestor = window;
89 pending->xevent.selection = selection;
90 pending->xevent.target = target;
91 pending->xevent.property = property;
92 pending->xevent.time = timestamp;
93
94 return pending;
95}
96
97static void
98gdk_x11_pending_selection_notify_free (GdkX11PendingSelectionNotify *notify)
99{
100 g_slice_free (GdkX11PendingSelectionNotify, notify);
101}
102
103static void
104gdk_x11_pending_selection_notify_require (GdkX11PendingSelectionNotify *notify,
105 guint n_sends)
106{
107 notify->n_pending += n_sends;
108}
109
110static void
111gdk_x11_pending_selection_notify_send (GdkX11PendingSelectionNotify *notify,
112 GdkDisplay *display,
113 gboolean success)
114{
115 Display *xdisplay;
116 int error;
117
118 notify->n_pending--;
119 if (notify->n_pending)
120 {
121 GDK_DISPLAY_NOTE (display, SELECTION, g_printerr ("%s:%s: not sending SelectionNotify yet, %zu streams still pending\n",
122 gdk_x11_get_xatom_name_for_display (display, notify->xevent.selection),
123 gdk_x11_get_xatom_name_for_display (display, notify->xevent.target),
124 notify->n_pending));
125 return;
126 }
127
128 GDK_DISPLAY_NOTE (display, SELECTION, g_printerr ("%s:%s: sending SelectionNotify reporting %s\n",
129 gdk_x11_get_xatom_name_for_display (display, notify->xevent.selection),
130 gdk_x11_get_xatom_name_for_display (display, notify->xevent.target),
131 success ? "success" : "failure"));
132 if (!success)
133 notify->xevent.property = None;
134
135 xdisplay = gdk_x11_display_get_xdisplay (display);
136
137 gdk_x11_display_error_trap_push (display);
138
139 if (XSendEvent (xdisplay, notify->xevent.requestor, False, NoEventMask, (XEvent*) &notify->xevent) == 0)
140 {
141 GDK_DISPLAY_NOTE (display, SELECTION, g_printerr ("%s:%s: failed to XSendEvent()\n",
142 gdk_x11_get_xatom_name_for_display (display, notify->xevent.selection),
143 gdk_x11_get_xatom_name_for_display (display, notify->xevent.target)));
144 }
145 XSync (xdisplay, False);
146
147 error = gdk_x11_display_error_trap_pop (display);
148 if (error != Success)
149 {
150 GDK_DISPLAY_NOTE (display, SELECTION, g_printerr ("%s:%s: X error during write: %d\n",
151 gdk_x11_get_xatom_name_for_display (display, notify->xevent.selection),
152 gdk_x11_get_xatom_name_for_display (display, notify->xevent.target),
153 error));
154 }
155
156 gdk_x11_pending_selection_notify_free (notify);
157}
158
159static gboolean
160gdk_x11_selection_output_stream_xevent (GdkDisplay *display,
161 const XEvent *xevent,
162 gpointer data);
163
164static gboolean
165gdk_x11_selection_output_stream_can_flush (GdkX11SelectionOutputStream *stream)
166{
167 GdkX11SelectionOutputStreamPrivate *priv = gdk_x11_selection_output_stream_get_instance_private (self: stream);
168
169 if (priv->delete_pending)
170 return FALSE;
171
172 return TRUE;
173}
174
175static gboolean
176gdk_x11_selection_output_stream_needs_flush_unlocked (GdkX11SelectionOutputStream *stream)
177{
178 GdkX11SelectionOutputStreamPrivate *priv = gdk_x11_selection_output_stream_get_instance_private (self: stream);
179
180 if (priv->sent_end_of_stream)
181 return FALSE;
182
183 if (g_output_stream_is_closing (G_OUTPUT_STREAM (stream)) ||
184 g_output_stream_is_closed (G_OUTPUT_STREAM (stream)))
185 return TRUE;
186
187 if (priv->data->len == 0 && priv->notify == NULL)
188 return FALSE;
189
190 if (priv->flush_requested)
191 return TRUE;
192
193 return priv->data->len >= gdk_x11_display_get_max_request_size (display: priv->display);
194}
195
196static gboolean
197gdk_x11_selection_output_stream_needs_flush (GdkX11SelectionOutputStream *stream)
198{
199 GdkX11SelectionOutputStreamPrivate *priv = gdk_x11_selection_output_stream_get_instance_private (self: stream);
200
201 gboolean result;
202
203 g_mutex_lock (mutex: &priv->mutex);
204
205 result = gdk_x11_selection_output_stream_needs_flush_unlocked (stream);
206
207 g_mutex_unlock (mutex: &priv->mutex);
208
209 return result;
210}
211
212static gsize
213get_element_size (int format)
214{
215 switch (format)
216 {
217 case 8:
218 return 1;
219
220 case 16:
221 return sizeof (short);
222
223 case 32:
224 return sizeof (long);
225
226 default:
227 g_warning ("Unknown format %u", format);
228 return 1;
229 }
230}
231
232static void
233gdk_x11_selection_output_stream_perform_flush (GdkX11SelectionOutputStream *stream)
234{
235 GdkX11SelectionOutputStreamPrivate *priv = gdk_x11_selection_output_stream_get_instance_private (self: stream);
236 Display *xdisplay;
237 gsize element_size, n_elements;
238 int error;
239
240 g_assert (!priv->delete_pending);
241
242 xdisplay = gdk_x11_display_get_xdisplay (display: priv->display);
243
244 /* We operate on a foreign window, better guard against catastrophe */
245 gdk_x11_display_error_trap_push (display: priv->display);
246
247 g_mutex_lock (mutex: &priv->mutex);
248
249 element_size = get_element_size (format: priv->format);
250 n_elements = priv->data->len / element_size;
251
252 if (priv->notify && !g_output_stream_is_closing (G_OUTPUT_STREAM (stream)))
253 {
254 XWindowAttributes attrs;
255
256 priv->incr = TRUE;
257 GDK_DISPLAY_NOTE (priv->display, SELECTION, g_printerr ("%s:%s: initiating INCR transfer\n",
258 priv->selection, priv->target));
259
260 XGetWindowAttributes (xdisplay,
261 priv->xwindow,
262 &attrs);
263 if (!(attrs.your_event_mask & PropertyChangeMask))
264 {
265 XSelectInput (xdisplay, priv->xwindow, attrs.your_event_mask | PropertyChangeMask);
266 }
267
268 XChangeProperty (GDK_DISPLAY_XDISPLAY (priv->display),
269 priv->xwindow,
270 priv->xproperty,
271 gdk_x11_get_xatom_by_name_for_display (display: priv->display, atom_name: "INCR"),
272 32,
273 PropModeReplace,
274 (guchar *) &(long) { n_elements },
275 1);
276 }
277 else
278 {
279 XChangeProperty (GDK_DISPLAY_XDISPLAY (priv->display),
280 priv->xwindow,
281 priv->xproperty,
282 priv->xtype,
283 priv->format,
284 PropModeReplace,
285 priv->data->data,
286 n_elements);
287 GDK_DISPLAY_NOTE (priv->display, SELECTION, g_printerr ("%s:%s: wrote %zu/%u bytes\n",
288 priv->selection, priv->target, n_elements * element_size, priv->data->len));
289 g_byte_array_remove_range (array: priv->data, index_: 0, length: n_elements * element_size);
290 if (priv->data->len < element_size)
291 priv->flush_requested = FALSE;
292 if (!priv->incr || n_elements == 0)
293 priv->sent_end_of_stream = TRUE;
294 }
295
296 if (priv->notify)
297 {
298 gdk_x11_pending_selection_notify_send (notify: priv->notify, display: priv->display, TRUE);
299 priv->notify = NULL;
300 }
301
302 g_object_ref (stream);
303 priv->delete_pending = TRUE;
304 g_cond_broadcast (cond: &priv->cond);
305 g_mutex_unlock (mutex: &priv->mutex);
306
307 /* XXX: handle failure here and report EPIPE for future operations on the stream? */
308 error = gdk_x11_display_error_trap_pop (display: priv->display);
309 if (error != Success)
310 {
311 GDK_DISPLAY_NOTE (priv->display, SELECTION, g_printerr ("%s:%s: X error during write: %d\n",
312 priv->selection, priv->target, error));
313 }
314
315 if (priv->pending_task)
316 {
317 g_task_return_int (task: priv->pending_task, GPOINTER_TO_SIZE (g_task_get_task_data (priv->pending_task)));
318 g_object_unref (object: priv->pending_task);
319 priv->pending_task = NULL;
320 }
321}
322
323static gboolean
324gdk_x11_selection_output_stream_invoke_flush (gpointer data)
325{
326 GdkX11SelectionOutputStream *stream = GDK_X11_SELECTION_OUTPUT_STREAM (data);
327
328 if (gdk_x11_selection_output_stream_needs_flush (stream) &&
329 gdk_x11_selection_output_stream_can_flush (stream))
330 gdk_x11_selection_output_stream_perform_flush (stream);
331
332 return G_SOURCE_REMOVE;
333}
334
335static gssize
336gdk_x11_selection_output_stream_write (GOutputStream *output_stream,
337 const void *buffer,
338 gsize count,
339 GCancellable *cancellable,
340 GError **error)
341{
342 GdkX11SelectionOutputStream *stream = GDK_X11_SELECTION_OUTPUT_STREAM (output_stream);
343 GdkX11SelectionOutputStreamPrivate *priv = gdk_x11_selection_output_stream_get_instance_private (self: stream);
344
345 g_mutex_lock (mutex: &priv->mutex);
346 g_byte_array_append (array: priv->data, data: buffer, len: count);
347 GDK_NOTE (SELECTION, g_printerr ("%s:%s: wrote %zu bytes, %u total now\n",
348 priv->selection, priv->target, count, priv->data->len));
349 g_mutex_unlock (mutex: &priv->mutex);
350
351 g_main_context_invoke (NULL, function: gdk_x11_selection_output_stream_invoke_flush, data: stream);
352
353 g_mutex_lock (mutex: &priv->mutex);
354 if (gdk_x11_selection_output_stream_needs_flush_unlocked (stream))
355 g_cond_wait (cond: &priv->cond, mutex: &priv->mutex);
356 g_mutex_unlock (mutex: &priv->mutex);
357
358 return count;
359}
360
361static void
362gdk_x11_selection_output_stream_write_async (GOutputStream *output_stream,
363 const void *buffer,
364 gsize count,
365 int io_priority,
366 GCancellable *cancellable,
367 GAsyncReadyCallback callback,
368 gpointer user_data)
369{
370 GdkX11SelectionOutputStream *stream = GDK_X11_SELECTION_OUTPUT_STREAM (output_stream);
371 GdkX11SelectionOutputStreamPrivate *priv = gdk_x11_selection_output_stream_get_instance_private (self: stream);
372 GTask *task;
373
374 task = g_task_new (source_object: stream, cancellable, callback, callback_data: user_data);
375 g_task_set_source_tag (task, gdk_x11_selection_output_stream_write_async);
376 g_task_set_priority (task, priority: io_priority);
377
378 g_mutex_lock (mutex: &priv->mutex);
379 g_byte_array_append (array: priv->data, data: buffer, len: count);
380 GDK_NOTE (SELECTION, g_printerr ("%s:%s: async wrote %zu bytes, %u total now\n",
381 priv->selection, priv->target, count, priv->data->len));
382 g_mutex_unlock (mutex: &priv->mutex);
383
384 if (!gdk_x11_selection_output_stream_needs_flush (stream))
385 {
386 g_task_return_int (task, result: count);
387 g_object_unref (object: task);
388 return;
389 }
390 else if (!gdk_x11_selection_output_stream_can_flush (stream))
391 {
392 g_assert (priv->pending_task == NULL);
393 priv->pending_task = task;
394 g_task_set_task_data (task, GSIZE_TO_POINTER (count), NULL);
395 return;
396 }
397 else
398 {
399 gdk_x11_selection_output_stream_perform_flush (stream);
400 g_task_return_int (task, result: count);
401 g_object_unref (object: task);
402 return;
403 }
404}
405
406static gssize
407gdk_x11_selection_output_stream_write_finish (GOutputStream *stream,
408 GAsyncResult *result,
409 GError **error)
410{
411 g_return_val_if_fail (g_task_is_valid (result, stream), -1);
412 g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == gdk_x11_selection_output_stream_write_async, -1);
413
414 return g_task_propagate_int (G_TASK (result), error);
415}
416
417static gboolean
418gdk_x11_selection_output_request_flush (GdkX11SelectionOutputStream *stream)
419{
420 GdkX11SelectionOutputStreamPrivate *priv = gdk_x11_selection_output_stream_get_instance_private (self: stream);
421 gboolean needs_flush;
422
423 g_mutex_lock (mutex: &priv->mutex);
424
425 if (priv->data->len >= get_element_size (format: priv->format))
426 priv->flush_requested = TRUE;
427
428 needs_flush = gdk_x11_selection_output_stream_needs_flush_unlocked (stream);
429 g_mutex_unlock (mutex: &priv->mutex);
430
431 GDK_NOTE (SELECTION, g_printerr ("%s:%s: requested flush%s\n",
432 priv->selection, priv->target, needs_flush ?"" : ", but not needed"));
433 return needs_flush;
434}
435
436static gboolean
437gdk_x11_selection_output_stream_flush (GOutputStream *output_stream,
438 GCancellable *cancellable,
439 GError **error)
440{
441 GdkX11SelectionOutputStream *stream = GDK_X11_SELECTION_OUTPUT_STREAM (output_stream);
442 GdkX11SelectionOutputStreamPrivate *priv = gdk_x11_selection_output_stream_get_instance_private (self: stream);
443
444 if (!gdk_x11_selection_output_request_flush (stream))
445 return TRUE;
446
447 g_main_context_invoke (NULL, function: gdk_x11_selection_output_stream_invoke_flush, data: stream);
448
449 g_mutex_lock (mutex: &priv->mutex);
450 if (gdk_x11_selection_output_stream_needs_flush_unlocked (stream))
451 g_cond_wait (cond: &priv->cond, mutex: &priv->mutex);
452 g_mutex_unlock (mutex: &priv->mutex);
453
454 return TRUE;
455}
456
457static void
458gdk_x11_selection_output_stream_flush_async (GOutputStream *output_stream,
459 int io_priority,
460 GCancellable *cancellable,
461 GAsyncReadyCallback callback,
462 gpointer user_data)
463{
464 GdkX11SelectionOutputStream *stream = GDK_X11_SELECTION_OUTPUT_STREAM (output_stream);
465 GdkX11SelectionOutputStreamPrivate *priv = gdk_x11_selection_output_stream_get_instance_private (self: stream);
466 GTask *task;
467
468 task = g_task_new (source_object: stream, cancellable, callback, callback_data: user_data);
469 g_task_set_source_tag (task, gdk_x11_selection_output_stream_flush_async);
470 g_task_set_priority (task, priority: io_priority);
471
472 if (!gdk_x11_selection_output_stream_can_flush (stream))
473 {
474 if (gdk_x11_selection_output_request_flush (stream))
475 {
476 g_assert (priv->pending_task == NULL);
477 priv->pending_task = task;
478 return;
479 }
480 else
481 {
482 g_task_return_boolean (task, TRUE);
483 g_object_unref (object: task);
484 return;
485 }
486 }
487
488 gdk_x11_selection_output_stream_perform_flush (stream);
489 g_task_return_boolean (task, TRUE);
490 g_object_unref (object: task);
491 return;
492}
493
494static gboolean
495gdk_x11_selection_output_stream_flush_finish (GOutputStream *stream,
496 GAsyncResult *result,
497 GError **error)
498{
499 g_return_val_if_fail (g_task_is_valid (result, stream), FALSE);
500 g_return_val_if_fail (g_async_result_is_tagged (result, gdk_x11_selection_output_stream_flush_async), FALSE);
501
502 return g_task_propagate_boolean (G_TASK (result), error);
503}
504
505static void
506gdk_x11_selection_output_stream_finalize (GObject *object)
507{
508 GdkX11SelectionOutputStream *stream = GDK_X11_SELECTION_OUTPUT_STREAM (object);
509 GdkX11SelectionOutputStreamPrivate *priv = gdk_x11_selection_output_stream_get_instance_private (self: stream);
510
511 /* not sending a notify is terrible */
512 g_assert (priv->notify == NULL);
513
514 GDK_DISPLAY_NOTE (priv->display, SELECTION, g_printerr ("%s:%s: finalizing\n",
515 priv->selection, priv->target));
516 GDK_X11_DISPLAY (priv->display)->streams = g_slist_remove (GDK_X11_DISPLAY (priv->display)->streams, data: stream);
517 g_signal_handlers_disconnect_by_func (priv->display,
518 gdk_x11_selection_output_stream_xevent,
519 stream);
520
521 g_byte_array_unref (array: priv->data);
522 g_cond_clear (cond: &priv->cond);
523 g_mutex_clear (mutex: &priv->mutex);
524
525 g_free (mem: priv->selection);
526 g_free (mem: priv->target);
527 g_free (mem: priv->property);
528 g_free (mem: priv->type);
529
530 G_OBJECT_CLASS (gdk_x11_selection_output_stream_parent_class)->finalize (object);
531}
532
533static void
534gdk_x11_selection_output_stream_class_init (GdkX11SelectionOutputStreamClass *klass)
535{
536 GObjectClass *object_class = G_OBJECT_CLASS (klass);
537 GOutputStreamClass *output_stream_class = G_OUTPUT_STREAM_CLASS (klass);
538
539 object_class->finalize = gdk_x11_selection_output_stream_finalize;
540
541 output_stream_class->write_fn = gdk_x11_selection_output_stream_write;
542 output_stream_class->flush = gdk_x11_selection_output_stream_flush;
543
544 output_stream_class->write_async = gdk_x11_selection_output_stream_write_async;
545 output_stream_class->write_finish = gdk_x11_selection_output_stream_write_finish;
546 output_stream_class->flush_async = gdk_x11_selection_output_stream_flush_async;
547 output_stream_class->flush_finish = gdk_x11_selection_output_stream_flush_finish;
548}
549
550static void
551gdk_x11_selection_output_stream_init (GdkX11SelectionOutputStream *stream)
552{
553 GdkX11SelectionOutputStreamPrivate *priv = gdk_x11_selection_output_stream_get_instance_private (self: stream);
554
555 g_mutex_init (mutex: &priv->mutex);
556 g_cond_init (cond: &priv->cond);
557 priv->data = g_byte_array_new ();
558}
559
560static gboolean
561gdk_x11_selection_output_stream_xevent (GdkDisplay *display,
562 const XEvent *xevent,
563 gpointer data)
564{
565 GdkX11SelectionOutputStream *stream = GDK_X11_SELECTION_OUTPUT_STREAM (data);
566 GdkX11SelectionOutputStreamPrivate *priv = gdk_x11_selection_output_stream_get_instance_private (self: stream);
567 Display *xdisplay;
568
569 xdisplay = gdk_x11_display_get_xdisplay (display: priv->display);
570
571 if (xevent->xany.display != xdisplay ||
572 xevent->xany.window != priv->xwindow)
573 return FALSE;
574
575 switch (xevent->type)
576 {
577 case PropertyNotify:
578 if (!priv->incr ||
579 xevent->xproperty.atom != priv->xproperty ||
580 xevent->xproperty.state != PropertyDelete)
581 return FALSE;
582
583 GDK_DISPLAY_NOTE (display, SELECTION, g_printerr ("%s:%s: got PropertyNotify Delete during INCR\n",
584 priv->selection, priv->target));
585 priv->delete_pending = FALSE;
586 if (gdk_x11_selection_output_stream_needs_flush (stream) &&
587 gdk_x11_selection_output_stream_can_flush (stream))
588 gdk_x11_selection_output_stream_perform_flush (stream);
589 g_object_unref (object: stream); /* from unsetting the delete_pending */
590 return FALSE;
591
592 default:
593 return FALSE;
594 }
595}
596
597static GOutputStream *
598gdk_x11_selection_output_stream_new (GdkDisplay *display,
599 GdkX11PendingSelectionNotify *notify,
600 Window window,
601 const char *selection,
602 const char *target,
603 const char *property,
604 const char *type,
605 int format,
606 gulong timestamp)
607{
608 GdkX11SelectionOutputStream *stream;
609 GdkX11SelectionOutputStreamPrivate *priv;
610
611 stream = g_object_new (GDK_TYPE_X11_SELECTION_OUTPUT_STREAM, NULL);
612 priv = gdk_x11_selection_output_stream_get_instance_private (self: stream);
613
614 priv->display = display;
615 GDK_X11_DISPLAY (display)->streams = g_slist_prepend (GDK_X11_DISPLAY (display)->streams, data: stream);
616 priv->notify = notify;
617 priv->xwindow = window;
618 priv->selection = g_strdup (str: selection);
619 priv->xselection = gdk_x11_get_xatom_by_name_for_display (display, atom_name: priv->selection);
620 priv->target = g_strdup (str: target);
621 priv->xtarget = gdk_x11_get_xatom_by_name_for_display (display, atom_name: priv->target);
622 priv->property = g_strdup (str: property);
623 priv->xproperty = gdk_x11_get_xatom_by_name_for_display (display, atom_name: priv->property);
624 priv->type = g_strdup (str: type);
625 priv->xtype = gdk_x11_get_xatom_by_name_for_display (display, atom_name: priv->type);
626 priv->format = format;
627 priv->timestamp = timestamp;
628
629 g_signal_connect (display,
630 "xevent",
631 G_CALLBACK (gdk_x11_selection_output_stream_xevent),
632 stream);
633
634 return G_OUTPUT_STREAM (stream);
635}
636
637static void
638print_atoms (GdkDisplay *display,
639 const char *selection,
640 const char *prefix,
641 const Atom *atoms,
642 gsize n_atoms)
643{
644 GDK_DISPLAY_NOTE (display, CLIPBOARD,
645 gsize i;
646
647 g_printerr ("%s: %s [ ", selection, prefix);
648 for (i = 0; i < n_atoms; i++)
649 {
650 g_printerr ("%s%s", i > 0 ? ", " : "", gdk_x11_get_xatom_name_for_display (display , atoms[i]));
651 }
652 g_printerr (" ]\n");
653 );
654}
655
656static void
657handle_targets_done (GObject *stream,
658 GAsyncResult *result,
659 gpointer user_data)
660{
661 GError *error = NULL;
662 gsize bytes_written;
663
664 if (!g_output_stream_write_all_finish (G_OUTPUT_STREAM (stream), result, bytes_written: &bytes_written, error: &error))
665 {
666 GDK_NOTE (CLIPBOARD, g_printerr ("---: failed to send targets after %zu bytes: %s\n",
667 bytes_written, error->message));
668 g_error_free (error);
669 }
670
671 g_free (mem: user_data);
672}
673
674static void
675handle_targets (GOutputStream *stream,
676 GdkDisplay *display,
677 GdkContentFormats *formats,
678 const char *target,
679 const char *encoding,
680 int format,
681 gulong timestamp,
682 GdkX11SelectionOutputHandler handler,
683 gpointer user_data)
684{
685 Atom *atoms;
686 gsize n_atoms;
687
688 atoms = gdk_x11_clipboard_formats_to_atoms (display,
689 TRUE,
690 formats,
691 n_atoms: &n_atoms);
692 print_atoms (display, selection: "---", prefix: "sending targets", atoms, n_atoms);
693 g_output_stream_write_all_async (stream,
694 buffer: atoms,
695 count: n_atoms * sizeof (Atom),
696 G_PRIORITY_DEFAULT,
697 NULL,
698 callback: handle_targets_done,
699 user_data: atoms);
700 g_object_unref (object: stream);
701}
702
703static void
704handle_timestamp_done (GObject *stream,
705 GAsyncResult *result,
706 gpointer user_data)
707{
708 GError *error = NULL;
709 gsize bytes_written;
710
711 if (!g_output_stream_write_all_finish (G_OUTPUT_STREAM (stream), result, bytes_written: &bytes_written, error: &error))
712 {
713 GDK_NOTE (CLIPBOARD, g_printerr ("---: failed to send timestamp after %zu bytes: %s\n",
714 bytes_written, error->message));
715 g_error_free (error);
716 }
717
718 g_slice_free (gulong, user_data);
719}
720
721static void
722handle_timestamp (GOutputStream *stream,
723 GdkDisplay *display,
724 GdkContentFormats *formats,
725 const char *target,
726 const char *encoding,
727 int format,
728 gulong timestamp,
729 GdkX11SelectionOutputHandler handler,
730 gpointer user_data)
731{
732 gulong *time_;
733
734 time_ = g_slice_new (gulong);
735 *time_ = timestamp;
736
737 g_output_stream_write_all_async (stream,
738 buffer: time_,
739 count: sizeof (gulong),
740 G_PRIORITY_DEFAULT,
741 NULL,
742 callback: handle_timestamp_done,
743 user_data: time_);
744 g_object_unref (object: stream);
745}
746
747static void
748handle_save_targets (GOutputStream *stream,
749 GdkDisplay *display,
750 GdkContentFormats *formats,
751 const char *target,
752 const char *encoding,
753 int format,
754 gulong timestamp,
755 GdkX11SelectionOutputHandler handler,
756 gpointer user_data)
757{
758 /* Don't do anything */
759
760 g_object_unref (object: stream);
761}
762
763static void
764handle_text_list (GOutputStream *stream,
765 GdkDisplay *display,
766 GdkContentFormats *formats,
767 const char *target,
768 const char *encoding,
769 int format,
770 gulong timestamp,
771 GdkX11SelectionOutputHandler handler,
772 gpointer user_data)
773{
774 GOutputStream *converter_stream;
775 GConverter *converter;
776
777 converter = gdk_x11_text_list_converter_to_utf8_new (display,
778 encoding,
779 format);
780 converter_stream = g_converter_output_stream_new (base_stream: stream, converter);
781
782 g_object_unref (object: converter);
783 g_object_unref (object: stream);
784
785 handler (converter_stream, gdk_intern_mime_type (string: "text/plain;charset=utf-8"), user_data);
786}
787
788static void
789handle_utf8 (GOutputStream *stream,
790 GdkDisplay *display,
791 GdkContentFormats *formats,
792 const char *target,
793 const char *encoding,
794 int format,
795 gulong timestamp,
796 GdkX11SelectionOutputHandler handler,
797 gpointer user_data)
798{
799 handler (stream, gdk_intern_mime_type (string: "text/plain;charset=utf-8"), user_data);
800}
801
802typedef void (* MimeTypeHandleFunc) (GOutputStream *, GdkDisplay *, GdkContentFormats *, const char *, const char *, int, gulong, GdkX11SelectionOutputHandler, gpointer);
803
804static const struct {
805 const char *x_target;
806 const char *mime_type;
807 const char *type;
808 int format;
809 MimeTypeHandleFunc handler;
810} special_targets[] = {
811 { "UTF8_STRING", "text/plain;charset=utf-8", "UTF8_STRING", 8, handle_utf8 },
812 { "COMPOUND_TEXT", "text/plain;charset=utf-8", "COMPOUND_TEXT", 8, handle_text_list },
813 { "TEXT", "text/plain;charset=utf-8", "STRING", 8, handle_text_list },
814 { "STRING", "text/plain;charset=utf-8", "STRING", 8, handle_text_list },
815 { "TARGETS", NULL, "ATOM", 32, handle_targets },
816 { "TIMESTAMP", NULL, "INTEGER", 32, handle_timestamp },
817 { "SAVE_TARGETS", NULL, "NULL", 32, handle_save_targets }
818};
819
820static gboolean
821gdk_x11_selection_output_streams_request (GdkDisplay *display,
822 GdkX11PendingSelectionNotify *notify,
823 GdkContentFormats *formats,
824 Window requestor,
825 Atom xselection,
826 Atom xtarget,
827 Atom xproperty,
828 gulong timestamp,
829 GdkX11SelectionOutputHandler handler,
830 gpointer user_data)
831{
832 const char *mime_type, *selection, *target, *property;
833
834 selection = gdk_x11_get_xatom_name_for_display (display, xatom: xselection);
835 target = gdk_x11_get_xatom_name_for_display (display, xatom: xtarget);
836 property = gdk_x11_get_xatom_name_for_display (display, xatom: xproperty);
837 mime_type = gdk_intern_mime_type (string: target);
838
839 if (mime_type)
840 {
841 if (gdk_content_formats_contain_mime_type (formats, mime_type))
842 {
843 GOutputStream *stream;
844
845 stream = gdk_x11_selection_output_stream_new (display,
846 notify,
847 window: requestor,
848 selection,
849 target,
850 property,
851 type: target,
852 format: 8,
853 timestamp);
854 handler (stream, mime_type, user_data);
855 return TRUE;
856 }
857 }
858 else if (g_str_equal (v1: target, v2: "MULTIPLE"))
859 {
860 gulong n_atoms;
861 gulong nbytes;
862 Atom prop_type;
863 int prop_format;
864 Atom *atoms = NULL;
865 int error;
866
867 error = XGetWindowProperty (gdk_x11_display_get_xdisplay (display),
868 requestor,
869 xproperty,
870 0, 0x1FFFFFFF, False,
871 AnyPropertyType,
872 &prop_type, &prop_format,
873 &n_atoms, &nbytes, (guchar **) &atoms);
874 if (error != Success)
875 {
876 GDK_DISPLAY_NOTE (display, SELECTION, g_printerr ("%s: XGetProperty() during MULTIPLE failed with %d\n",
877 selection, error));
878 }
879 else if (prop_format != 32 ||
880 prop_type != gdk_x11_get_xatom_by_name_for_display (display, atom_name: "ATOM_PAIR"))
881 {
882 GDK_DISPLAY_NOTE (display, SELECTION, g_printerr ("%s: XGetProperty() type/format should be ATOM_PAIR/32 but is %s/%d\n",
883 selection, gdk_x11_get_xatom_name_for_display (display, prop_type), prop_format));
884 }
885 else if (n_atoms < 2)
886 {
887 print_atoms (display, selection, prefix: "ignoring MULTIPLE request with too little elements", atoms, n_atoms);
888 }
889 else
890 {
891 gulong i;
892
893 print_atoms (display, selection, prefix: "MULTIPLE request", atoms, n_atoms);
894 if (n_atoms % 2)
895 {
896 GDK_DISPLAY_NOTE (display, SELECTION, g_printerr ("%s: Number of atoms is uneven at %lu, ignoring last element\n",
897 selection, n_atoms));
898 n_atoms &= ~1;
899 }
900
901 gdk_x11_pending_selection_notify_require (notify, n_sends: n_atoms / 2);
902
903 for (i = 0; i < n_atoms / 2; i++)
904 {
905 gboolean success;
906
907 if (atoms[2 * i] == None || atoms[2 * i + 1] == None)
908 {
909 success = FALSE;
910 GDK_DISPLAY_NOTE (display, SELECTION, g_printerr ("%s: None not allowed as atom in MULTIPLE request\n",
911 selection));
912 gdk_x11_pending_selection_notify_send (notify, display, FALSE);
913 }
914 else if (atoms[2 * i] == gdk_x11_get_xatom_by_name_for_display (display, atom_name: "MULTIPLE"))
915 {
916 success = FALSE;
917 GDK_DISPLAY_NOTE (display, SELECTION, g_printerr ("%s: MULTIPLE as target in MULTIPLE request would cause recursion\n",
918 selection));
919 gdk_x11_pending_selection_notify_send (notify, display, FALSE);
920 }
921 else
922 {
923 success = gdk_x11_selection_output_streams_request (display,
924 notify,
925 formats,
926 requestor,
927 xselection,
928 xtarget: atoms[2 * i],
929 xproperty: atoms[2 * i + 1],
930 timestamp,
931 handler,
932 user_data);
933 }
934
935 if (!success)
936 atoms[2 * i + 1] = None;
937 }
938 }
939
940 XChangeProperty (gdk_x11_display_get_xdisplay (display),
941 requestor,
942 xproperty,
943 prop_type, 32,
944 PropModeReplace, (guchar *)atoms, n_atoms);
945
946 if (atoms)
947 XFree (atoms);
948
949 gdk_x11_pending_selection_notify_send (notify, display, TRUE);
950 return TRUE;
951 }
952 else
953 {
954 gsize i;
955
956 for (i = 0; i < G_N_ELEMENTS (special_targets); i++)
957 {
958 if (g_str_equal (v1: target, v2: special_targets[i].x_target) &&
959 special_targets[i].handler)
960 {
961 GOutputStream *stream;
962
963 if (special_targets[i].mime_type)
964 mime_type = gdk_intern_mime_type (string: special_targets[i].mime_type);
965 stream = gdk_x11_selection_output_stream_new (display,
966 notify,
967 window: requestor,
968 selection,
969 target,
970 property,
971 type: special_targets[i].type,
972 format: special_targets[i].format,
973 timestamp);
974 special_targets[i].handler (stream,
975 display,
976 formats,
977 target,
978 special_targets[i].type,
979 special_targets[i].format,
980 timestamp,
981 handler,
982 user_data);
983 return TRUE;
984 }
985 }
986 }
987
988 gdk_x11_pending_selection_notify_send (notify, display, FALSE);
989 return FALSE;
990}
991
992void
993gdk_x11_selection_output_streams_create (GdkDisplay *display,
994 GdkContentFormats *formats,
995 Window requestor,
996 Atom selection,
997 Atom target,
998 Atom property,
999 gulong timestamp,
1000 GdkX11SelectionOutputHandler handler,
1001 gpointer user_data)
1002{
1003 GdkX11PendingSelectionNotify *notify;
1004
1005 notify = gdk_x11_pending_selection_notify_new (window: requestor,
1006 selection,
1007 target,
1008 property,
1009 timestamp);
1010 gdk_x11_selection_output_streams_request (display,
1011 notify,
1012 formats,
1013 requestor,
1014 xselection: selection,
1015 xtarget: target,
1016 xproperty: property,
1017 timestamp,
1018 handler,
1019 user_data);
1020}
1021

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