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