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*) ¬ify->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