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