1/* GDK - The GIMP Drawing Kit
2 * Copyright (C) 2017 Red Hat, Inc.
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 "gdkclipboardprivate.h"
21#include "gdkclipboard-x11.h"
22
23#include "gdkintl.h"
24#include "gdkdisplay-x11.h"
25#include "gdkprivate-x11.h"
26#include "gdkselectioninputstream-x11.h"
27#include "gdkselectionoutputstream-x11.h"
28#include "gdktextlistconverter-x11.h"
29#include "gdk/gdk-private.h"
30
31#include <string.h>
32#include <X11/Xatom.h>
33
34#ifdef HAVE_XFIXES
35#include <X11/extensions/Xfixes.h>
36#endif
37
38#define IDLE_ABORT_TIME 30 /* seconds */
39
40typedef struct _GdkX11ClipboardClass GdkX11ClipboardClass;
41
42typedef struct _RetrievalInfo RetrievalInfo;
43
44struct _GdkX11Clipboard
45{
46 GdkClipboard parent;
47
48 char *selection;
49 Atom xselection;
50 gulong timestamp;
51
52 GTask *store_task;
53};
54
55struct _GdkX11ClipboardClass
56{
57 GdkClipboardClass parent_class;
58};
59
60G_DEFINE_TYPE (GdkX11Clipboard, gdk_x11_clipboard, GDK_TYPE_CLIPBOARD)
61
62static void
63print_atoms (GdkX11Clipboard *cb,
64 const char *prefix,
65 const Atom *atoms,
66 gsize n_atoms)
67{
68 GDK_DISPLAY_NOTE (gdk_clipboard_get_display (GDK_CLIPBOARD (cb)), CLIPBOARD, {
69 gsize i;
70 GdkDisplay *display = gdk_clipboard_get_display (GDK_CLIPBOARD (cb));
71
72 g_printerr ("%s: %s [ ", cb->selection, prefix);
73 for (i = 0; i < n_atoms; i++)
74 g_printerr ("%s%s", i > 0 ? ", " : "", gdk_x11_get_xatom_name_for_display (display , atoms[i]));
75 g_printerr (" ]\n");
76 });
77}
78
79static void
80gdk_x11_clipboard_default_output_closed (GObject *stream,
81 GAsyncResult *result,
82 gpointer user_data)
83{
84 GError *error = NULL;
85
86 if (!g_output_stream_close_finish (G_OUTPUT_STREAM (stream), result, error: &error))
87 {
88 GDK_NOTE (CLIPBOARD,
89 g_printerr ("-------: failed to close stream: %s\n",
90 error->message));
91 g_error_free (error);
92 }
93
94 g_object_unref (object: stream);
95}
96
97static void
98gdk_x11_clipboard_default_output_done (GObject *clipboard,
99 GAsyncResult *result,
100 gpointer user_data)
101{
102 GOutputStream *stream = user_data;
103 GError *error = NULL;
104
105 if (!gdk_clipboard_write_finish (GDK_CLIPBOARD (clipboard), result, error: &error))
106 {
107 GDK_DISPLAY_NOTE (gdk_clipboard_get_display (GDK_CLIPBOARD (clipboard)), CLIPBOARD,
108 g_printerr ("%s: failed to write stream: %s\n",
109 GDK_X11_CLIPBOARD (clipboard)->selection, error->message));
110 g_error_free (error);
111 }
112
113 g_output_stream_close_async (stream,
114 G_PRIORITY_DEFAULT,
115 NULL,
116 callback: gdk_x11_clipboard_default_output_closed,
117 NULL);
118}
119
120static void
121gdk_x11_clipboard_default_output_handler (GOutputStream *stream,
122 const char *mime_type,
123 gpointer user_data)
124{
125 gdk_clipboard_write_async (GDK_CLIPBOARD (user_data),
126 mime_type,
127 stream,
128 G_PRIORITY_DEFAULT,
129 NULL,
130 callback: gdk_x11_clipboard_default_output_done,
131 user_data: stream);
132}
133
134static GInputStream *
135text_list_convert (GdkX11Clipboard *cb,
136 GInputStream *stream,
137 const char *encoding,
138 int format)
139{
140 GInputStream *converter_stream;
141 GConverter *converter;
142
143 converter = gdk_x11_text_list_converter_to_utf8_new (display: gdk_clipboard_get_display (GDK_CLIPBOARD (cb)),
144 encoding,
145 format);
146 converter_stream = g_converter_input_stream_new (base_stream: stream, converter);
147
148 g_object_unref (object: converter);
149 g_object_unref (object: stream);
150
151 return converter_stream;
152}
153
154static GInputStream *
155no_convert (GdkX11Clipboard *cb,
156 GInputStream *stream,
157 const char *encoding,
158 int format)
159{
160 return stream;
161}
162
163static const struct {
164 const char *x_target;
165 const char *mime_type;
166 GInputStream * (* convert) (GdkX11Clipboard *, GInputStream *, const char *, int);
167 const char *type;
168 int format;
169} special_targets[] = {
170 { "UTF8_STRING", "text/plain;charset=utf-8", no_convert, "UTF8_STRING", 8 },
171 { "COMPOUND_TEXT", "text/plain;charset=utf-8", text_list_convert, "COMPOUND_TEXT", 8 },
172 { "TEXT", "text/plain;charset=utf-8", text_list_convert, "STRING", 8 },
173 { "STRING", "text/plain;charset=utf-8", text_list_convert, "STRING", 8 },
174 { "TARGETS", NULL, NULL, "ATOM", 32 },
175 { "TIMESTAMP", NULL, NULL, "INTEGER", 32 },
176 { "SAVE_TARGETS", NULL, NULL, "NULL", 32 }
177};
178
179GSList *
180gdk_x11_clipboard_formats_to_targets (GdkContentFormats *formats)
181{
182 GSList *targets;
183 const char * const *mime_types;
184 gsize i, j, n_mime_types;
185
186 targets = NULL;
187 mime_types = gdk_content_formats_get_mime_types (formats, n_mime_types: &n_mime_types);
188
189 for (i = 0; i < n_mime_types; i++)
190 {
191 for (j = 0; j < G_N_ELEMENTS (special_targets); j++)
192 {
193 if (special_targets[j].mime_type == NULL)
194 continue;
195
196 if (g_str_equal (v1: mime_types[i], v2: special_targets[j].mime_type))
197 targets = g_slist_prepend (list: targets, data: (gpointer) g_intern_string (string: special_targets[j].x_target));
198 }
199 targets = g_slist_prepend (list: targets, data: (gpointer) mime_types[i]);
200 }
201
202 return g_slist_reverse (list: targets);
203}
204
205Atom *
206gdk_x11_clipboard_formats_to_atoms (GdkDisplay *display,
207 gboolean include_special,
208 GdkContentFormats *formats,
209 gsize *n_atoms)
210{
211 GSList *l, *targets;
212 Atom *atoms;
213 gsize i;
214
215 targets = gdk_x11_clipboard_formats_to_targets (formats);
216
217 if (include_special)
218 {
219 for (i = 0; i < G_N_ELEMENTS (special_targets); i++)
220 {
221 if (special_targets[i].mime_type != NULL)
222 continue;
223
224 targets = g_slist_prepend (list: targets, data: (gpointer) g_intern_string (string: special_targets[i].x_target));
225 }
226 }
227
228 *n_atoms = g_slist_length (list: targets);
229 atoms = g_new (Atom, *n_atoms);
230 i = 0;
231 for (l = targets; l; l = l->next)
232 atoms[i++] = gdk_x11_get_xatom_by_name_for_display (display, atom_name: l->data);
233
234 return atoms;
235}
236
237static GdkContentFormats *
238gdk_x11_clipboard_formats_from_atoms (GdkDisplay *display,
239 const Atom *atoms,
240 gsize n_atoms)
241{
242 GdkContentFormatsBuilder *builder;
243 gsize i, j;
244
245 builder = gdk_content_formats_builder_new ();
246 for (i = 0; i < n_atoms; i++)
247 {
248 const char *name;
249
250 name = gdk_x11_get_xatom_name_for_display (display , xatom: atoms[i]);
251 if (!name)
252 {
253 continue;
254 }
255 if (strchr (s: name, c: '/'))
256 {
257 gdk_content_formats_builder_add_mime_type (builder, mime_type: name);
258 continue;
259 }
260
261 for (j = 0; j < G_N_ELEMENTS (special_targets); j++)
262 {
263 if (g_str_equal (v1: name, v2: special_targets[j].x_target))
264 {
265 if (special_targets[j].mime_type)
266 gdk_content_formats_builder_add_mime_type (builder, mime_type: special_targets[j].mime_type);
267 break;
268 }
269 }
270 }
271
272 return gdk_content_formats_builder_free_to_formats (builder);
273}
274
275static void
276gdk_x11_clipboard_request_targets_finish (GObject *source_object,
277 GAsyncResult *res,
278 gpointer user_data)
279{
280 GInputStream *stream = G_INPUT_STREAM (source_object);
281 GdkX11Clipboard *cb = user_data;
282 GdkDisplay *display;
283 GdkContentFormats *formats;
284 GBytes *bytes;
285 GError *error = NULL;
286
287 display = gdk_clipboard_get_display (GDK_CLIPBOARD (cb));
288
289 bytes = g_input_stream_read_bytes_finish (stream, result: res, error: &error);
290 if (bytes == NULL)
291 {
292 GDK_DISPLAY_NOTE (display, CLIPBOARD,
293 g_printerr ("%s: error reading TARGETS: %s\n", cb->selection, error->message));
294 g_error_free (error);
295 g_object_unref (object: stream);
296 g_object_unref (object: cb);
297 return;
298 }
299 else if (g_bytes_get_size (bytes) == 0)
300 {
301 g_bytes_unref (bytes);
302 g_object_unref (object: stream);
303 g_object_unref (object: cb);
304 return;
305 }
306 else if (gdk_clipboard_is_local (GDK_CLIPBOARD (cb)))
307 {
308 /* FIXME: Use a cancellable for this request, so that we don't do he brittle
309 * is_local() check */
310 g_bytes_unref (bytes);
311 g_object_unref (object: stream);
312 g_object_unref (object: cb);
313 return;
314 }
315
316 print_atoms (cb,
317 prefix: "received targets",
318 atoms: g_bytes_get_data (bytes, NULL),
319 n_atoms: g_bytes_get_size (bytes) / sizeof (Atom));
320
321 formats = gdk_x11_clipboard_formats_from_atoms (display,
322 atoms: g_bytes_get_data (bytes, NULL),
323 n_atoms: g_bytes_get_size (bytes) / sizeof (Atom));
324 GDK_DISPLAY_NOTE (display, CLIPBOARD, char *s = gdk_content_formats_to_string (formats); g_printerr ("%s: got formats: %s\n", cb->selection, s); g_free (s));
325
326 /* union with previously loaded formats */
327 formats = gdk_content_formats_union (first: formats, second: gdk_clipboard_get_formats (GDK_CLIPBOARD (cb)));
328 gdk_clipboard_claim_remote (GDK_CLIPBOARD (cb), formats);
329 gdk_content_formats_unref (formats);
330 g_bytes_unref (bytes);
331
332 g_input_stream_read_bytes_async (stream,
333 count: gdk_x11_display_get_max_request_size (display),
334 G_PRIORITY_DEFAULT,
335 NULL,
336 callback: gdk_x11_clipboard_request_targets_finish,
337 user_data: cb);
338}
339
340static void
341gdk_x11_clipboard_request_targets_got_stream (GObject *source,
342 GAsyncResult *result,
343 gpointer data)
344{
345 GdkX11Clipboard *cb = data;
346 GInputStream *stream;
347 GdkDisplay *display;
348 GError *error = NULL;
349 const char *type;
350 int format;
351
352 display = gdk_clipboard_get_display (GDK_CLIPBOARD (cb));
353 stream = gdk_x11_selection_input_stream_new_finish (result, type: &type, format: &format, error: &error);
354 if (stream == NULL)
355 {
356 GDK_DISPLAY_NOTE (display, CLIPBOARD, g_printerr ("%s: can't request TARGETS: %s\n", cb->selection, error->message));
357 g_object_unref (object: cb);
358 g_error_free (error);
359 return;
360 }
361 else if (!g_str_equal (v1: type, v2: "ATOM") || format != 32)
362 {
363 GDK_DISPLAY_NOTE (display, CLIPBOARD, g_printerr ("%s: Wrong reply type to TARGETS: type %s != ATOM or format %d != 32\n",
364 cb->selection, type, format));
365 g_input_stream_close (stream, NULL, NULL);
366 g_object_unref (object: stream);
367 g_object_unref (object: cb);
368 return;
369 }
370
371 g_input_stream_read_bytes_async (stream,
372 count: gdk_x11_display_get_max_request_size (display),
373 G_PRIORITY_DEFAULT,
374 NULL,
375 callback: gdk_x11_clipboard_request_targets_finish,
376 user_data: cb);
377}
378
379static void
380gdk_x11_clipboard_request_targets (GdkX11Clipboard *cb)
381{
382 gdk_x11_selection_input_stream_new_async (display: gdk_clipboard_get_display (GDK_CLIPBOARD (cb)),
383 selection: cb->selection,
384 target: "TARGETS",
385 timestamp: cb->timestamp,
386 G_PRIORITY_DEFAULT,
387 NULL,
388 callback: gdk_x11_clipboard_request_targets_got_stream,
389 g_object_ref (cb));
390}
391
392static void
393gdk_x11_clipboard_claim_remote (GdkX11Clipboard *cb,
394 guint32 timestamp)
395{
396 GdkContentFormats *empty;
397
398 empty = gdk_content_formats_new (NULL, n_mime_types: 0);
399 gdk_clipboard_claim_remote (GDK_CLIPBOARD (cb), formats: empty);
400 gdk_content_formats_unref (formats: empty);
401 cb->timestamp = timestamp;
402 gdk_x11_clipboard_request_targets (cb);
403}
404
405static gboolean
406gdk_x11_clipboard_xevent (GdkDisplay *display,
407 const XEvent *xevent,
408 gpointer data)
409{
410 GdkX11Clipboard *cb = GDK_X11_CLIPBOARD (data);
411 Window xwindow;
412
413 xwindow = GDK_X11_DISPLAY (display)->leader_window;
414
415 if (xevent->xany.window != xwindow)
416 return FALSE;
417
418 switch (xevent->type)
419 {
420 case SelectionClear:
421 if (xevent->xselectionclear.selection != cb->xselection)
422 return FALSE;
423
424 if (xevent->xselectionclear.time < cb->timestamp)
425 {
426 GDK_DISPLAY_NOTE (display, CLIPBOARD,
427 g_printerr ("%s: ignoring SelectionClear with too old timestamp (%lu vs %lu)\n",
428 cb->selection, xevent->xselectionclear.time, cb->timestamp));
429 return FALSE;
430 }
431
432 GDK_DISPLAY_NOTE (display, CLIPBOARD, g_printerr ("%s: got SelectionClear\n", cb->selection));
433 gdk_x11_clipboard_claim_remote (cb, timestamp: xevent->xselectionclear.time);
434 return TRUE;
435
436 case SelectionNotify:
437 /* This code only checks clipboard manager replies, so... */
438 if (!g_str_equal (v1: cb->selection, v2: "CLIPBOARD"))
439 return FALSE;
440
441 /* selection is not for us */
442 if (xevent->xselection.selection != gdk_x11_get_xatom_by_name_for_display (display, atom_name: "CLIPBOARD_MANAGER") ||
443 xevent->xselection.target != gdk_x11_get_xatom_by_name_for_display (display, atom_name: "SAVE_TARGETS"))
444 return FALSE;
445
446 /* We already received a selectionNotify before */
447 if (cb->store_task == NULL)
448 {
449 GDK_DISPLAY_NOTE (display, CLIPBOARD,
450 g_printerr ("%s: got SelectionNotify for nonexisting task?!\n", cb->selection));
451 return FALSE;
452 }
453
454 GDK_DISPLAY_NOTE (display, CLIPBOARD,
455 g_printerr ("%s: got SelectionNotify for store task\n", cb->selection));
456
457 if (xevent->xselection.property != None)
458 g_task_return_boolean (task: cb->store_task, TRUE);
459 else
460 g_task_return_new_error (task: cb->store_task, G_IO_ERROR, code: G_IO_ERROR_FAILED,
461 _("Clipboard manager could not store selection."));
462 g_clear_object (&cb->store_task);
463
464 return FALSE;
465
466 case SelectionRequest:
467 {
468#ifdef G_ENABLE_DEBUG
469 const char *target, *property;
470#endif
471
472 if (xevent->xselectionrequest.selection != cb->xselection)
473 return FALSE;
474
475#ifdef G_ENABLE_DEBUG
476 target = gdk_x11_get_xatom_name_for_display (display, xatom: xevent->xselectionrequest.target);
477 if (xevent->xselectionrequest.property == None)
478 property = target;
479 else
480 property = gdk_x11_get_xatom_name_for_display (display, xatom: xevent->xselectionrequest.property);
481#endif
482
483 if (!gdk_clipboard_is_local (GDK_CLIPBOARD (cb)))
484 {
485 GDK_DISPLAY_NOTE (display, CLIPBOARD,
486 g_printerr ("%s: got SelectionRequest for %s @ %s even though we don't own the selection, huh?\n",
487 cb->selection, target, property));
488 return TRUE;
489 }
490 if (xevent->xselectionrequest.requestor == None)
491 {
492 GDK_DISPLAY_NOTE (display, CLIPBOARD,
493 g_printerr ("%s: got SelectionRequest for %s @ %s with NULL window, ignoring\n",
494 cb->selection, target, property));
495 return TRUE;
496 }
497
498 GDK_DISPLAY_NOTE (display, CLIPBOARD,
499 g_printerr ("%s: got SelectionRequest for %s @ %s\n", cb->selection, target, property));
500
501 gdk_x11_selection_output_streams_create (display,
502 formats: gdk_clipboard_get_formats (GDK_CLIPBOARD (cb)),
503 requestor: xevent->xselectionrequest.requestor,
504 selection: xevent->xselectionrequest.selection,
505 target: xevent->xselectionrequest.target,
506 property: xevent->xselectionrequest.property ? xevent->xselectionrequest.property
507 : xevent->xselectionrequest.target,
508 timestamp: xevent->xselectionrequest.time,
509 handler: gdk_x11_clipboard_default_output_handler,
510 user_data: cb);
511 return TRUE;
512 }
513
514 default:
515#ifdef HAVE_XFIXES
516 if (xevent->type - GDK_X11_DISPLAY (display)->xfixes_event_base == XFixesSelectionNotify)
517 {
518 XFixesSelectionNotifyEvent *sn = (XFixesSelectionNotifyEvent *) xevent;
519
520 if (sn->selection != cb->xselection)
521 return FALSE;
522
523 if (sn->selection_timestamp < cb->timestamp)
524 {
525 GDK_DISPLAY_NOTE (display, CLIPBOARD,
526 g_printerr ("%s: Ignoring XFixesSelectionNotify with too old timestamp (%lu vs %lu)\n",
527 cb->selection, sn->selection_timestamp, cb->timestamp));
528 return FALSE;
529 }
530
531 if (sn->owner == GDK_X11_DISPLAY (display)->leader_window)
532 {
533 GDK_DISPLAY_NOTE (display, CLIPBOARD,
534 g_printerr ("%s: Ignoring XFixesSelectionNotify for ourselves\n", cb->selection));
535 return FALSE;
536 }
537
538 GDK_DISPLAY_NOTE (display, CLIPBOARD,
539 g_printerr ("%s: Received XFixesSelectionNotify, claiming selection\n", cb->selection));
540
541 gdk_x11_clipboard_claim_remote (cb, timestamp: sn->selection_timestamp);
542 }
543#endif
544 return FALSE;
545 }
546}
547
548static void
549gdk_x11_clipboard_finalize (GObject *object)
550{
551 GdkX11Clipboard *cb = GDK_X11_CLIPBOARD (object);
552
553 g_signal_handlers_disconnect_by_func (gdk_clipboard_get_display (GDK_CLIPBOARD (cb)),
554 gdk_x11_clipboard_xevent,
555 cb);
556 g_free (mem: cb->selection);
557
558 G_OBJECT_CLASS (gdk_x11_clipboard_parent_class)->finalize (object);
559}
560
561static gboolean
562gdk_x11_clipboard_claim (GdkClipboard *clipboard,
563 GdkContentFormats *formats,
564 gboolean local,
565 GdkContentProvider *content)
566{
567 if (local)
568 {
569 GdkX11Clipboard *cb = GDK_X11_CLIPBOARD (clipboard);
570 GdkDisplay *display = gdk_clipboard_get_display (GDK_CLIPBOARD (cb));
571 Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
572 Window xwindow = GDK_X11_DISPLAY (display)->leader_window;
573 guint32 time;
574
575 time = gdk_x11_get_server_time (GDK_X11_DISPLAY (display)->leader_gdk_surface);
576
577 if (content)
578 {
579 XSetSelectionOwner (xdisplay, cb->xselection, xwindow, time);
580
581 if (XGetSelectionOwner (xdisplay, cb->xselection) != xwindow)
582 {
583 GDK_DISPLAY_NOTE (display, CLIPBOARD, g_printerr ("%s: failed XSetSelectionOwner()\n", cb->selection));
584 return FALSE;
585 }
586 }
587 else
588 {
589 XSetSelectionOwner (xdisplay, cb->xselection, None, time);
590 }
591
592 cb->timestamp = time;
593 GDK_DISPLAY_NOTE (display, CLIPBOARD, g_printerr ("%s: claimed via XSetSelectionOwner()\n", cb->selection));
594 }
595
596 return GDK_CLIPBOARD_CLASS (gdk_x11_clipboard_parent_class)->claim (clipboard, formats, local, content);
597}
598
599static void
600gdk_x11_clipboard_store_async (GdkClipboard *clipboard,
601 int io_priority,
602 GCancellable *cancellable,
603 GAsyncReadyCallback callback,
604 gpointer user_data)
605{
606 GdkX11Clipboard *cb = GDK_X11_CLIPBOARD (clipboard);
607 GdkDisplay *display = gdk_clipboard_get_display (clipboard);
608 Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
609 Atom clipboard_manager, save_targets, property_name;
610 GdkContentProvider *content;
611 GdkContentFormats *formats;
612 Atom *atoms;
613 gsize n_atoms;
614 int error;
615
616 /* clipboard managers don't work on anything but the clipbpoard selection */
617 if (!g_str_equal (v1: cb->selection, v2: "CLIPBOARD"))
618 {
619 GDK_DISPLAY_NOTE (display, CLIPBOARD, g_printerr ("%s: can only store on CLIPBOARD\n", cb->selection));
620 GDK_CLIPBOARD_CLASS (gdk_x11_clipboard_parent_class)->store_async (clipboard,
621 io_priority,
622 cancellable,
623 callback,
624 user_data);
625 return;
626 }
627
628 cb->store_task = g_task_new (source_object: clipboard, cancellable, callback, callback_data: user_data);
629 g_task_set_priority (task: cb->store_task, priority: io_priority);
630 g_task_set_source_tag (cb->store_task, gdk_x11_clipboard_store_async);
631
632 clipboard_manager = gdk_x11_get_xatom_by_name_for_display (display, atom_name: "CLIPBOARD_MANAGER");
633 save_targets = gdk_x11_get_xatom_by_name_for_display (display, atom_name: "SAVE_TARGETS");
634
635 if (XGetSelectionOwner (xdisplay, clipboard_manager) == None)
636 {
637 GDK_DISPLAY_NOTE (display, CLIPBOARD,
638 g_printerr ("%s: XGetSelectionOwner (CLIPBOARD_MANAGER) returned None, aborting.\n",
639 cb->selection));
640 g_task_return_new_error (task: cb->store_task, G_IO_ERROR, code: G_IO_ERROR_NOT_SUPPORTED,
641 _("Cannot store clipboard. No clipboard manager is active."));
642 g_clear_object (&cb->store_task);
643 return;
644 }
645
646 content = gdk_clipboard_get_content (clipboard);
647 if (content == NULL)
648 {
649 GDK_DISPLAY_NOTE (display, CLIPBOARD, g_printerr ("%s: storing empty clipboard: SUCCESS!\n", cb->selection));
650 g_task_return_boolean (task: cb->store_task, TRUE);
651 g_clear_object (&cb->store_task);
652 return;
653 }
654
655 formats = gdk_content_provider_ref_storable_formats (provider: content);
656 formats = gdk_content_formats_union_serialize_mime_types (formats);
657 atoms = gdk_x11_clipboard_formats_to_atoms (display, FALSE, formats, n_atoms: &n_atoms);
658 print_atoms (cb, prefix: "requesting store from clipboard manager", atoms, n_atoms);
659 gdk_content_formats_unref (formats);
660
661 gdk_x11_display_error_trap_push (display);
662
663 if (n_atoms > 0)
664 {
665 property_name = gdk_x11_get_xatom_by_name_for_display (display, atom_name: "GDK_CLIPBOARD_SAVE_TARGETS");
666
667 XChangeProperty (xdisplay, GDK_X11_DISPLAY (display)->leader_window,
668 property_name, XA_ATOM,
669 32, PropModeReplace, (guchar *)atoms, n_atoms);
670 }
671 else
672 property_name = None;
673
674 XConvertSelection (xdisplay,
675 clipboard_manager, save_targets, property_name,
676 GDK_X11_DISPLAY (display)->leader_window, cb->timestamp);
677
678 error = gdk_x11_display_error_trap_pop (display);
679 if (error != Success)
680 {
681 GDK_DISPLAY_NOTE (display, CLIPBOARD,
682 g_printerr ("%s: X error during ConvertSelection() while storing selection: %d\n", cb->selection, error));
683 }
684
685 g_free (mem: atoms);
686}
687
688static gboolean
689gdk_x11_clipboard_store_finish (GdkClipboard *clipboard,
690 GAsyncResult *result,
691 GError **error)
692{
693 g_return_val_if_fail (g_task_is_valid (result, clipboard), FALSE);
694 g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == gdk_x11_clipboard_store_async, FALSE);
695
696 return g_task_propagate_boolean (G_TASK (result), error);
697}
698
699static void
700gdk_x11_clipboard_read_got_stream (GObject *source,
701 GAsyncResult *res,
702 gpointer data)
703{
704 GTask *task = data;
705 GError *error = NULL;
706 GInputStream *stream;
707 const char *type;
708 int format;
709
710 stream = gdk_x11_selection_input_stream_new_finish (result: res, type: &type, format: &format, error: &error);
711 if (stream == NULL)
712 {
713 GSList *targets, *next;
714
715 targets = g_task_get_task_data (task);
716 next = targets->next;
717 if (next)
718 {
719 GdkX11Clipboard *cb = GDK_X11_CLIPBOARD (g_task_get_source_object (task));
720
721 GDK_DISPLAY_NOTE (gdk_clipboard_get_display (GDK_CLIPBOARD(cb)), CLIPBOARD,
722 g_printerr ("%s: reading %s failed, trying %s next\n",
723 cb->selection, (char *) targets->data, (char *) next->data));
724 targets->next = NULL;
725 g_task_set_task_data (task, task_data: next, task_data_destroy: (GDestroyNotify) g_slist_free);
726 gdk_x11_selection_input_stream_new_async (display: gdk_clipboard_get_display (GDK_CLIPBOARD (cb)),
727 selection: cb->selection,
728 target: next->data,
729 timestamp: cb->timestamp,
730 io_priority: g_task_get_priority (task),
731 cancellable: g_task_get_cancellable (task),
732 callback: gdk_x11_clipboard_read_got_stream,
733 user_data: task);
734 g_error_free (error);
735 return;
736 }
737
738 g_task_return_error (task, error);
739 }
740 else
741 {
742 GdkX11Clipboard *cb = GDK_X11_CLIPBOARD (g_task_get_source_object (task));
743 const char *mime_type = ((GSList *) g_task_get_task_data (task))->data;
744 gsize i;
745
746 for (i = 0; i < G_N_ELEMENTS (special_targets); i++)
747 {
748 if (g_str_equal (v1: mime_type, v2: special_targets[i].x_target))
749 {
750 g_assert (special_targets[i].mime_type != NULL);
751
752 GDK_DISPLAY_NOTE (gdk_clipboard_get_display (GDK_CLIPBOARD (cb)), CLIPBOARD,
753 g_printerr ("%s: reading with converter from %s to %s\n",
754 cb->selection, mime_type, special_targets[i].mime_type));
755 mime_type = g_intern_string (string: special_targets[i].mime_type);
756 g_task_set_task_data (task, task_data: g_slist_prepend (NULL, data: (gpointer) mime_type), task_data_destroy: (GDestroyNotify) g_slist_free);
757 stream = special_targets[i].convert (cb, stream, type, format);
758 break;
759 }
760 }
761
762 GDK_DISPLAY_NOTE (gdk_clipboard_get_display (GDK_CLIPBOARD (cb)), CLIPBOARD,
763 g_printerr ("%s: reading clipboard as %s now\n", cb->selection, mime_type));
764 g_task_return_pointer (task, result: stream, result_destroy: g_object_unref);
765 }
766
767 g_object_unref (object: task);
768}
769
770static void
771gdk_x11_clipboard_read_async (GdkClipboard *clipboard,
772 GdkContentFormats *formats,
773 int io_priority,
774 GCancellable *cancellable,
775 GAsyncReadyCallback callback,
776 gpointer user_data)
777{
778 GdkX11Clipboard *cb = GDK_X11_CLIPBOARD (clipboard);
779 GSList *targets;
780 GTask *task;
781
782 task = g_task_new (source_object: clipboard, cancellable, callback, callback_data: user_data);
783 g_task_set_priority (task, priority: io_priority);
784 g_task_set_source_tag (task, gdk_x11_clipboard_read_async);
785
786 targets = gdk_x11_clipboard_formats_to_targets (formats);
787 g_task_set_task_data (task, task_data: targets, task_data_destroy: (GDestroyNotify) g_slist_free);
788 if (targets == NULL)
789 {
790 g_task_return_new_error (task, G_IO_ERROR, code: G_IO_ERROR_NOT_SUPPORTED,
791 _("No compatible transfer format found"));
792 return;
793 }
794
795 GDK_DISPLAY_NOTE (gdk_clipboard_get_display (clipboard), CLIPBOARD,
796 g_printerr ("%s: new read for %s (%u other options)\n",
797 cb->selection, (char *) targets->data, g_slist_length (targets->next)));
798 gdk_x11_selection_input_stream_new_async (display: gdk_clipboard_get_display (GDK_CLIPBOARD (cb)),
799 selection: cb->selection,
800 target: targets->data,
801 timestamp: cb->timestamp,
802 io_priority,
803 cancellable,
804 callback: gdk_x11_clipboard_read_got_stream,
805 user_data: task);
806}
807
808static GInputStream *
809gdk_x11_clipboard_read_finish (GdkClipboard *clipboard,
810 GAsyncResult *result,
811 const char **out_mime_type,
812 GError **error)
813{
814 GTask *task;
815
816 g_return_val_if_fail (g_task_is_valid (result, G_OBJECT (clipboard)), NULL);
817 task = G_TASK (result);
818 g_return_val_if_fail (g_task_get_source_tag (task) == gdk_x11_clipboard_read_async, NULL);
819
820 if (out_mime_type)
821 {
822 GSList *targets;
823
824 targets = g_task_get_task_data (task);
825 *out_mime_type = targets ? targets->data : NULL;
826 }
827
828 return g_task_propagate_pointer (task, error);
829}
830
831static void
832gdk_x11_clipboard_class_init (GdkX11ClipboardClass *class)
833{
834 GObjectClass *object_class = G_OBJECT_CLASS (class);
835 GdkClipboardClass *clipboard_class = GDK_CLIPBOARD_CLASS (class);
836
837 object_class->finalize = gdk_x11_clipboard_finalize;
838
839 clipboard_class->claim = gdk_x11_clipboard_claim;
840 clipboard_class->store_async = gdk_x11_clipboard_store_async;
841 clipboard_class->store_finish = gdk_x11_clipboard_store_finish;
842 clipboard_class->read_async = gdk_x11_clipboard_read_async;
843 clipboard_class->read_finish = gdk_x11_clipboard_read_finish;
844}
845
846static void
847gdk_x11_clipboard_init (GdkX11Clipboard *cb)
848{
849 cb->timestamp = CurrentTime;
850}
851
852GdkClipboard *
853gdk_x11_clipboard_new (GdkDisplay *display,
854 const char *selection)
855{
856 GdkX11Clipboard *cb;
857
858 cb = g_object_new (GDK_TYPE_X11_CLIPBOARD,
859 first_property_name: "display", display,
860 NULL);
861
862 cb->selection = g_strdup (str: selection);
863 cb->xselection = gdk_x11_get_xatom_by_name_for_display (display, atom_name: selection);
864
865 gdk_x11_display_request_selection_notification (display, selection);
866 g_signal_connect (display, "xevent", G_CALLBACK (gdk_x11_clipboard_xevent), cb);
867 gdk_x11_clipboard_claim_remote (cb, CurrentTime);
868
869 return GDK_CLIPBOARD (cb);
870}
871
872

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