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