1/* GDK - The GIMP Drawing Kit
2 * Copyright (C) 1995-1999 Peter Mattis, Spencer Kimball and Josh MacDonald
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/*
19 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
20 * file for a list of people on the GTK+ Team. See the ChangeLog
21 * files for a list of changes. These files are distributed with
22 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
23 */
24
25#include "config.h"
26
27#include "gdkdropprivate.h"
28
29#include "gdk-private.h"
30#include "gdkasync.h"
31#include "gdkclipboardprivate.h"
32#include "gdkclipboard-x11.h"
33#include "gdkdeviceprivate.h"
34#include "gdkdisplay-x11.h"
35#include "gdkdragprivate.h"
36#include "gdkintl.h"
37#include "gdkprivate-x11.h"
38#include "gdkscreen-x11.h"
39#include "gdkselectioninputstream-x11.h"
40#include "gdkselectionoutputstream-x11.h"
41#include "gdktextlistconverter-x11.h"
42
43#include <X11/Xlib.h>
44#include <X11/Xutil.h>
45#include <X11/Xatom.h>
46#include <X11/extensions/shape.h>
47
48#include <string.h>
49
50#define GDK_TYPE_X11_DROP (gdk_x11_drop_get_type())
51G_DECLARE_FINAL_TYPE (GdkX11Drop, gdk_x11_drop, GDK, X11_DROP, GdkDrop)
52
53struct _GdkX11Drop
54{
55 GdkDrop parent_instance;
56
57 Window source_window;
58
59 guint16 last_x; /* Coordinates from last event */
60 guint16 last_y;
61 gulong timestamp; /* Timestamp we claimed the DND selection with */
62 guint version; /* Xdnd protocol version */
63
64 GdkDragAction xdnd_actions; /* What is currently set in XdndActionList */
65 GdkDragAction suggested_action;
66
67 guint xdnd_targets_set : 1; /* Whether we've already set XdndTypeList */
68 guint xdnd_have_actions : 1; /* Whether an XdndActionList was provided */
69 guint enter_emitted : 1; /* Set after gdk_drop_emit_enter() */
70};
71
72struct _GdkX11DropClass
73{
74 GdkDropClass parent_class;
75};
76
77/* Forward declarations */
78
79static gboolean xdnd_source_surface_filter (GdkDisplay *display,
80 const XEvent *xevent,
81 gpointer data);
82static gboolean xdnd_enter_filter (GdkSurface *surface,
83 const XEvent *xevent);
84static gboolean xdnd_leave_filter (GdkSurface *surface,
85 const XEvent *xevent);
86static gboolean xdnd_position_filter (GdkSurface *surface,
87 const XEvent *xevent);
88static gboolean xdnd_drop_filter (GdkSurface *surface,
89 const XEvent *xevent);
90
91static const struct {
92 const char *atom_name;
93 gboolean (* func) (GdkSurface *surface, const XEvent *event);
94} xdnd_filters[] = {
95 { "XdndEnter", xdnd_enter_filter },
96 { "XdndLeave", xdnd_leave_filter },
97 { "XdndPosition", xdnd_position_filter },
98 { "XdndDrop", xdnd_drop_filter },
99};
100
101G_DEFINE_TYPE (GdkX11Drop, gdk_x11_drop, GDK_TYPE_DROP)
102
103static GInputStream *
104text_list_convert (GdkDisplay *display,
105 GInputStream *stream,
106 const char *encoding,
107 int format)
108{
109 GInputStream *converter_stream;
110 GConverter *converter;
111
112 converter = gdk_x11_text_list_converter_to_utf8_new (display, encoding, format);
113 converter_stream = g_converter_input_stream_new (base_stream: stream, converter);
114
115 g_object_unref (object: converter);
116 g_object_unref (object: stream);
117
118 return converter_stream;
119}
120
121static GInputStream *
122no_convert (GdkDisplay *display,
123 GInputStream *stream,
124 const char *encoding,
125 int format)
126{
127 return stream;
128}
129
130static const struct {
131 const char *x_target;
132 const char *mime_type;
133 GInputStream * (* convert) (GdkDisplay *, GInputStream *, const char *, int);
134 const char *type;
135 int format;
136} special_targets[] = {
137 { "UTF8_STRING", "text/plain;charset=utf-8", no_convert, "UTF8_STRING", 8 },
138 { "COMPOUND_TEXT", "text/plain;charset=utf-8", text_list_convert, "COMPOUND_TEXT", 8 },
139 { "TEXT", "text/plain;charset=utf-8", text_list_convert, "STRING", 8 },
140 { "STRING", "text/plain;charset=utf-8", text_list_convert, "STRING", 8 },
141 { "TARGETS", NULL, NULL, "ATOM", 32 },
142 { "TIMESTAMP", NULL, NULL, "INTEGER", 32 },
143 { "SAVE_TARGETS", NULL, NULL, "NULL", 32 }
144};
145
146static void
147gdk_x11_drop_read_got_stream (GObject *source,
148 GAsyncResult *res,
149 gpointer data)
150{
151 GTask *task = data;
152 GError *error = NULL;
153 GInputStream *stream;
154 const char *type;
155 int format;
156
157 stream = gdk_x11_selection_input_stream_new_finish (result: res, type: &type, format: &format, error: &error);
158 if (stream == NULL)
159 {
160 GSList *targets, *next;
161
162 targets = g_task_get_task_data (task);
163 next = targets->next;
164 if (next)
165 {
166 GdkDrop *drop = GDK_DROP (g_task_get_source_object (task));
167
168 GDK_DISPLAY_NOTE (gdk_drop_get_display (drop), DND, g_printerr ("reading %s failed, trying %s next\n",
169 (char *) targets->data, (char *) next->data));
170 targets->next = NULL;
171 g_task_set_task_data (task, task_data: next, task_data_destroy: (GDestroyNotify) g_slist_free);
172 gdk_x11_selection_input_stream_new_async (display: gdk_drop_get_display (self: drop),
173 selection: "XdndSelection",
174 target: next->data,
175 CurrentTime,
176 io_priority: g_task_get_priority (task),
177 cancellable: g_task_get_cancellable (task),
178 callback: gdk_x11_drop_read_got_stream,
179 user_data: task);
180 g_error_free (error);
181 return;
182 }
183
184 g_task_return_error (task, error);
185 }
186 else
187 {
188 gsize i;
189 const char *mime_type = ((GSList *) g_task_get_task_data (task))->data;
190 GdkDrop *drop = GDK_DROP (g_task_get_source_object (task));
191
192 for (i = 0; i < G_N_ELEMENTS (special_targets); i++)
193 {
194 if (g_str_equal (v1: mime_type, v2: special_targets[i].x_target))
195 {
196 g_assert (special_targets[i].mime_type != NULL);
197
198 mime_type = g_intern_string (string: special_targets[i].mime_type);
199 g_task_set_task_data (task, task_data: g_slist_prepend (NULL, data: (gpointer) mime_type), task_data_destroy: (GDestroyNotify) g_slist_free);
200 stream = special_targets[i].convert (gdk_drop_get_display (self: drop), stream, type, format);
201 break;
202 }
203 }
204
205 GDK_NOTE (DND, g_printerr ("reading DND as %s now\n",
206 (const char *)((GSList *) g_task_get_task_data (task))->data));
207 g_task_return_pointer (task, result: stream, result_destroy: g_object_unref);
208 }
209
210 g_object_unref (object: task);
211}
212
213static void
214gdk_x11_drop_read_async (GdkDrop *drop,
215 GdkContentFormats *formats,
216 int io_priority,
217 GCancellable *cancellable,
218 GAsyncReadyCallback callback,
219 gpointer user_data)
220{
221 GSList *targets;
222 GTask *task;
223
224 task = g_task_new (source_object: drop, cancellable, callback, callback_data: user_data);
225 g_task_set_priority (task, priority: io_priority);
226 g_task_set_source_tag (task, gdk_x11_drop_read_async);
227
228 targets = gdk_x11_clipboard_formats_to_targets (formats);
229 g_task_set_task_data (task, task_data: targets, task_data_destroy: (GDestroyNotify) g_slist_free);
230 if (targets == NULL)
231 {
232 g_task_return_new_error (task, G_IO_ERROR, code: G_IO_ERROR_NOT_SUPPORTED,
233 _("No compatible transfer format found"));
234 return;
235 }
236
237 GDK_DISPLAY_NOTE (gdk_drop_get_display (drop), DND, g_printerr ("new read for %s (%u other options)\n",
238 (char *) targets->data, g_slist_length (targets->next)));
239 gdk_x11_selection_input_stream_new_async (display: gdk_drop_get_display (self: drop),
240 selection: "XdndSelection",
241 target: targets->data,
242 CurrentTime,
243 io_priority,
244 cancellable,
245 callback: gdk_x11_drop_read_got_stream,
246 user_data: task);
247}
248
249static GInputStream *
250gdk_x11_drop_read_finish (GdkDrop *drop,
251 GAsyncResult *result,
252 const char **out_mime_type,
253 GError **error)
254{
255 GTask *task;
256
257 g_return_val_if_fail (g_task_is_valid (result, G_OBJECT (drop)), NULL);
258 task = G_TASK (result);
259 g_return_val_if_fail (g_task_get_source_tag (task) == gdk_x11_drop_read_async, NULL);
260
261 if (out_mime_type)
262 {
263 GSList *targets;
264
265 targets = g_task_get_task_data (task);
266 *out_mime_type = targets ? targets->data : NULL;
267 }
268
269 return g_task_propagate_pointer (task, error);
270}
271
272static void
273gdk_x11_drop_finalize (GObject *object)
274{
275 GdkX11Drop *drop_x11 = GDK_X11_DROP (ptr: object);
276
277 if (gdk_drop_get_drag (GDK_DROP (drop_x11)) == NULL)
278 {
279 g_signal_handlers_disconnect_by_func (gdk_drop_get_display (GDK_DROP (drop_x11)),
280 xdnd_source_surface_filter,
281 drop_x11);
282 /* Should we remove the GDK_PROPERTY_NOTIFY mask?
283 * but we might want it for other reasons. (Like
284 * INCR selection transactions).
285 */
286 }
287
288 G_OBJECT_CLASS (gdk_x11_drop_parent_class)->finalize (object);
289}
290
291/* Utility functions */
292
293#ifdef G_ENABLE_DEBUG
294static void
295print_target_list (GdkContentFormats *formats)
296{
297 char *name = gdk_content_formats_to_string (formats);
298 g_message ("DND formats: %s", name);
299 g_free (mem: name);
300}
301#endif /* G_ENABLE_DEBUG */
302
303/*************************************************************
304 ***************************** XDND **************************
305 *************************************************************/
306
307/* Utility functions */
308
309static struct {
310 const char *name;
311 GdkDragAction action;
312} xdnd_actions_table[] = {
313 { "XdndActionCopy", GDK_ACTION_COPY },
314 { "XdndActionMove", GDK_ACTION_MOVE },
315 { "XdndActionLink", GDK_ACTION_LINK },
316 { "XdndActionAsk", GDK_ACTION_ASK },
317 { "XdndActionPrivate", GDK_ACTION_COPY },
318 };
319
320static const int xdnd_n_actions = G_N_ELEMENTS (xdnd_actions_table);
321
322static GdkDragAction
323xdnd_action_from_atom (GdkDisplay *display,
324 Atom xatom)
325{
326 const char *name;
327 int i;
328
329 if (xatom == None)
330 return 0;
331
332 name = gdk_x11_get_xatom_name_for_display (display, xatom);
333
334 for (i = 0; i < xdnd_n_actions; i++)
335 if (g_str_equal (v1: name, v2: xdnd_actions_table[i].name))
336 return xdnd_actions_table[i].action;
337
338 return 0;
339}
340
341static Atom
342xdnd_action_to_atom (GdkDisplay *display,
343 GdkDragAction action)
344{
345 int i;
346
347 for (i = 0; i < xdnd_n_actions; i++)
348 if (action == xdnd_actions_table[i].action)
349 return gdk_x11_get_xatom_by_name_for_display (display, atom_name: xdnd_actions_table[i].name);
350
351 return None;
352}
353
354/* Target side */
355
356static void
357gdk_x11_drop_update_actions (GdkX11Drop *drop_x11)
358{
359 GdkDragAction actions;
360
361 if (!drop_x11->xdnd_have_actions)
362 actions = drop_x11->suggested_action;
363 else if (drop_x11->suggested_action & GDK_ACTION_ASK)
364 actions = drop_x11->xdnd_actions | GDK_ACTION_ASK;
365 else
366 actions = drop_x11->xdnd_actions & GDK_ACTION_ALL;
367
368 gdk_drop_set_actions (GDK_DROP (drop_x11), actions);
369}
370
371void
372gdk_x11_drop_read_actions (GdkDrop *drop)
373{
374 GdkX11Drop *drop_x11 = GDK_X11_DROP (ptr: drop);
375 GdkDisplay *display = gdk_drop_get_display (self: drop);
376 GdkDrag *drag;
377 GdkDragAction actions = GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK | GDK_ACTION_ASK;
378 Atom type;
379 int format;
380 gulong nitems, after;
381 guchar *data;
382 Atom *atoms;
383 int i;
384
385 drag = gdk_drop_get_drag (self: drop);
386
387 drop_x11->xdnd_have_actions = FALSE;
388
389 if (drag == NULL)
390 {
391 /* Get the XdndActionList, if set */
392
393 gdk_x11_display_error_trap_push (display);
394 if (XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
395 drop_x11->source_window,
396 gdk_x11_get_xatom_by_name_for_display (display, atom_name: "XdndActionList"),
397 0, 65536,
398 False, XA_ATOM, &type, &format, &nitems,
399 &after, &data) == Success &&
400 type == XA_ATOM)
401 {
402 actions = 0;
403
404 atoms = (Atom *)data;
405
406 for (i = 0; i < nitems; i++)
407 actions |= xdnd_action_from_atom (display, xatom: atoms[i]);
408
409 drop_x11->xdnd_have_actions = TRUE;
410
411#ifdef G_ENABLE_DEBUG
412 if (GDK_DISPLAY_DEBUG_CHECK (display, DND))
413 {
414 GString *action_str = g_string_new (NULL);
415 GdkDragAction drop_actions = gdk_drop_get_actions (self: drop);
416 if (drop_actions & GDK_ACTION_MOVE)
417 g_string_append(string: action_str, val: "MOVE ");
418 if (drop_actions & GDK_ACTION_COPY)
419 g_string_append(string: action_str, val: "COPY ");
420 if (drop_actions & GDK_ACTION_LINK)
421 g_string_append(string: action_str, val: "LINK ");
422 if (drop_actions & GDK_ACTION_ASK)
423 g_string_append(string: action_str, val: "ASK ");
424
425 g_message("Xdnd actions = %s", action_str->str);
426 g_string_free (string: action_str, TRUE);
427 }
428#endif /* G_ENABLE_DEBUG */
429 }
430
431 if (data)
432 XFree (data);
433
434 gdk_x11_display_error_trap_pop_ignored (display);
435 }
436 else
437 {
438 actions = gdk_drag_get_actions (drag);
439 drop_x11->xdnd_have_actions = TRUE;
440 }
441
442 drop_x11->xdnd_actions = actions;
443 gdk_x11_drop_update_actions (drop_x11);
444}
445
446/* We have to make sure that the XdndActionList we keep internally
447 * is up to date with the XdndActionList on the source window
448 * because we get no notification, because Xdnd wasn’t meant
449 * to continually send actions. So we select on PropertyChangeMask
450 * and add this filter.
451 */
452static gboolean
453xdnd_source_surface_filter (GdkDisplay *display,
454 const XEvent *xevent,
455 gpointer data)
456{
457 GdkX11Drop *drop_x11 = data;
458
459 if ((xevent->xany.type == PropertyNotify) &&
460 (xevent->xany.window == drop_x11->source_window) &&
461 (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, atom_name: "XdndActionList")))
462 {
463 gdk_x11_drop_read_actions (GDK_DROP (drop_x11));
464 }
465
466 return FALSE;
467}
468
469static gboolean
470xdnd_enter_filter (GdkSurface *surface,
471 const XEvent *xevent)
472{
473 GdkDisplay *display;
474 GdkX11Display *display_x11;
475 GdkDrop *drop;
476 GdkX11Drop *drop_x11;
477 GdkDrag *drag;
478 GdkSeat *seat;
479 int i;
480 Atom type;
481 int format;
482 gulong nitems, after;
483 guchar *data;
484 Atom *atoms;
485 GdkContentFormats *content_formats;
486 GPtrArray *formats;
487 Window source_window;
488 gboolean get_types;
489 int version;
490
491 source_window = xevent->xclient.data.l[0];
492 get_types = ((xevent->xclient.data.l[1] & 1) != 0);
493 version = (xevent->xclient.data.l[1] & 0xff000000) >> 24;
494
495 display = gdk_surface_get_display (surface);
496 display_x11 = GDK_X11_DISPLAY (display);
497
498 GDK_DISPLAY_NOTE (display, DND,
499 g_message ("XdndEnter: source_window: %#lx, version: %#x",
500 source_window, version));
501
502 if (version < 3)
503 {
504 /* Old source ignore */
505 GDK_DISPLAY_NOTE (display, DND, g_message ("Ignored old XdndEnter message"));
506 return TRUE;
507 }
508
509 if (display_x11->current_drop)
510 {
511 if (GDK_X11_DROP (ptr: display_x11->current_drop)->enter_emitted)
512 gdk_drop_emit_leave_event (self: display_x11->current_drop, FALSE, GDK_CURRENT_TIME);
513 g_clear_object (&display_x11->current_drop);
514 }
515
516 seat = gdk_display_get_default_seat (display);
517
518 formats = g_ptr_array_new ();
519 if (get_types)
520 {
521 gdk_x11_display_error_trap_push (display);
522 XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
523 source_window,
524 gdk_x11_get_xatom_by_name_for_display (display, atom_name: "XdndTypeList"),
525 0, 65536,
526 False, XA_ATOM, &type, &format, &nitems,
527 &after, &data);
528
529 if (gdk_x11_display_error_trap_pop (display) || (format != 32) || (type != XA_ATOM))
530 {
531 if (data)
532 XFree (data);
533
534 return TRUE;
535 }
536
537 atoms = (Atom *)data;
538 for (i = 0; i < nitems; i++)
539 g_ptr_array_add (array: formats,
540 data: (gpointer) gdk_x11_get_xatom_name_for_display (display, xatom: atoms[i]));
541
542 XFree (atoms);
543 }
544 else
545 {
546 for (i = 0; i < 3; i++)
547 if (xevent->xclient.data.l[2 + i])
548 g_ptr_array_add (array: formats,
549 data: (gpointer) gdk_x11_get_xatom_name_for_display (display,
550 xatom: xevent->xclient.data.l[2 + i]));
551 }
552 content_formats = gdk_content_formats_new (mime_types: (const char **) formats->pdata, n_mime_types: formats->len);
553 g_ptr_array_unref (array: formats);
554
555#ifdef G_ENABLE_DEBUG
556 if (GDK_DISPLAY_DEBUG_CHECK (display, DND))
557 print_target_list (formats: content_formats);
558#endif /* G_ENABLE_DEBUG */
559
560 drag = gdk_x11_drag_find (display, source_xid: source_window, GDK_SURFACE_XID (surface));
561
562 drop_x11 = g_object_new (GDK_TYPE_X11_DROP,
563 first_property_name: "device", gdk_seat_get_pointer (seat),
564 "drag", drag,
565 "formats", content_formats,
566 "surface", surface,
567 NULL);
568 drop = GDK_DROP (drop_x11);
569
570 drop_x11->version = version;
571
572 /* FIXME: Should extend DnD protocol to have device info */
573
574 drop_x11->source_window = source_window;
575 if (drag == NULL)
576 {
577 Display *xdisplay = gdk_x11_display_get_xdisplay (display);
578 XWindowAttributes attrs;
579
580 gdk_x11_display_error_trap_push (display);
581 XGetWindowAttributes (xdisplay, source_window, &attrs);
582 if (!(attrs.your_event_mask & PropertyChangeMask))
583 {
584 XSelectInput (xdisplay, source_window, attrs.your_event_mask | PropertyChangeMask);
585 }
586 gdk_x11_display_error_trap_pop_ignored (display);
587
588 g_signal_connect (display, "xevent", G_CALLBACK (xdnd_source_surface_filter), drop);
589 }
590 gdk_x11_drop_read_actions (drop);
591
592 display_x11->current_drop = drop;
593
594 gdk_content_formats_unref (formats: content_formats);
595
596 return TRUE;
597}
598
599static gboolean
600xdnd_leave_filter (GdkSurface *surface,
601 const XEvent *xevent)
602{
603 Window source_window = xevent->xclient.data.l[0];
604 GdkDisplay *display;
605 GdkX11Display *display_x11;
606
607 display = gdk_surface_get_display (surface);
608 display_x11 = GDK_X11_DISPLAY (display);
609
610 GDK_DISPLAY_NOTE (display, DND,
611 g_message ("XdndLeave: source_window: %#lx",
612 source_window));
613
614 if ((display_x11->current_drop != NULL) &&
615 (GDK_X11_DROP (ptr: display_x11->current_drop)->source_window == source_window))
616 {
617 if (GDK_X11_DROP (ptr: display_x11->current_drop)->enter_emitted)
618 gdk_drop_emit_leave_event (self: display_x11->current_drop, FALSE, GDK_CURRENT_TIME);
619
620 g_clear_object (&display_x11->current_drop);
621 }
622
623 return TRUE;
624}
625
626static gboolean
627xdnd_position_filter (GdkSurface *surface,
628 const XEvent *xevent)
629{
630 GdkX11Surface *impl;
631 Window source_window = xevent->xclient.data.l[0];
632 gint16 x_root = xevent->xclient.data.l[2] >> 16;
633 gint16 y_root = xevent->xclient.data.l[2] & 0xffff;
634 guint32 time = xevent->xclient.data.l[3];
635 Atom action = xevent->xclient.data.l[4];
636 GdkDisplay *display;
637 GdkX11Display *display_x11;
638 GdkDrop *drop;
639 GdkX11Drop *drop_x11;
640
641 display = gdk_surface_get_display (surface);
642 display_x11 = GDK_X11_DISPLAY (display);
643
644 GDK_DISPLAY_NOTE (display, DND,
645 g_message ("XdndPosition: source_window: %#lx position: (%d, %d) time: %d action: %ld",
646 source_window, x_root, y_root, time, action));
647
648 drop = display_x11->current_drop;
649 drop_x11 = GDK_X11_DROP (ptr: drop);
650
651 if ((drop != NULL) &&
652 (drop_x11->source_window == source_window))
653 {
654 surface = gdk_drop_get_surface (self: drop);
655 impl = GDK_X11_SURFACE (surface);
656
657 drop_x11->suggested_action = xdnd_action_from_atom (display, xatom: action);
658 gdk_x11_drop_update_actions (drop_x11);
659
660 drop_x11->last_x = x_root / impl->surface_scale;
661 drop_x11->last_y = y_root / impl->surface_scale;
662
663 if (drop_x11->enter_emitted)
664 {
665 gdk_drop_emit_motion_event (self: drop, FALSE, x: drop_x11->last_x - impl->abs_x, y: drop_x11->last_y - impl->abs_y, time);
666 }
667 else
668 {
669 gdk_drop_emit_enter_event (self: drop, FALSE, x: drop_x11->last_x - impl->abs_x, y: drop_x11->last_y - impl->abs_y, time);
670 drop_x11->enter_emitted = TRUE;
671 }
672 }
673
674 return TRUE;
675}
676
677static gboolean
678xdnd_drop_filter (GdkSurface *surface,
679 const XEvent *xevent)
680{
681 Window source_window = xevent->xclient.data.l[0];
682 guint32 time = xevent->xclient.data.l[2];
683 GdkDisplay *display;
684 GdkX11Display *display_x11;
685 GdkDrop *drop;
686 GdkX11Drop *drop_x11;
687
688 display = gdk_surface_get_display (surface);
689 display_x11 = GDK_X11_DISPLAY (display);
690
691 GDK_DISPLAY_NOTE (display, DND,
692 g_message ("XdndDrop: source_window: %#lx time: %d",
693 source_window, time));
694
695 drop = display_x11->current_drop;
696 drop_x11 = GDK_X11_DROP (ptr: drop);
697
698 if ((drop != NULL) &&
699 (drop_x11->source_window == source_window))
700 {
701 GdkSurface *s = gdk_drop_get_surface (self: drop);
702 GdkX11Surface *si = GDK_X11_SURFACE (s);
703 gdk_x11_surface_set_user_time (surface: s, timestamp: time);
704
705 gdk_drop_emit_drop_event (self: drop, FALSE, x: drop_x11->last_x - si->abs_x, y: drop_x11->last_y - si->abs_y, time);
706 }
707
708 return TRUE;
709}
710
711gboolean
712gdk_x11_drop_filter (GdkSurface *surface,
713 const XEvent *xevent)
714
715{
716 GdkDisplay *display;
717 int i;
718
719 if (!GDK_IS_X11_SURFACE (surface))
720 return GDK_FILTER_CONTINUE;
721
722 if (xevent->type != ClientMessage)
723 return GDK_FILTER_CONTINUE;
724
725 display = GDK_SURFACE_DISPLAY (surface);
726
727 for (i = 0; i < G_N_ELEMENTS (xdnd_filters); i++)
728 {
729 if (xevent->xclient.message_type != gdk_x11_get_xatom_by_name_for_display (display, atom_name: xdnd_filters[i].atom_name))
730 continue;
731
732 if (xdnd_filters[i].func (surface, xevent))
733 return TRUE;
734 else
735 return FALSE;
736 }
737
738 return FALSE;
739}
740
741/* Destination side */
742
743static void
744gdk_x11_drop_do_nothing (Window window,
745 gboolean success,
746 gpointer data)
747{
748#ifdef G_ENABLE_DEBUG
749 GdkDisplay *display = data;
750
751 if (!success)
752 {
753 GDK_DISPLAY_NOTE (display, DND, g_message ("Send event to %lx failed", window));
754 }
755#endif
756}
757
758static void
759gdk_x11_drop_status (GdkDrop *drop,
760 GdkDragAction actions,
761 GdkDragAction preferred)
762{
763 GdkX11Drop *drop_x11 = GDK_X11_DROP (ptr: drop);
764 GdkDragAction possible_actions, suggested_action;
765 XEvent xev;
766 GdkDisplay *display;
767
768 display = gdk_drop_get_display (self: drop);
769
770 possible_actions = actions & gdk_drop_get_actions (self: drop);
771
772 if (preferred & possible_actions)
773 suggested_action = preferred;
774 else if (drop_x11->suggested_action & possible_actions)
775 suggested_action = drop_x11->suggested_action;
776 else if (possible_actions & GDK_ACTION_COPY)
777 suggested_action = GDK_ACTION_COPY;
778 else if (possible_actions & GDK_ACTION_MOVE)
779 suggested_action = GDK_ACTION_MOVE;
780 else if (possible_actions & GDK_ACTION_ASK)
781 suggested_action = GDK_ACTION_ASK;
782 else
783 suggested_action = 0;
784
785 xev.xclient.type = ClientMessage;
786 xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, atom_name: "XdndStatus");
787 xev.xclient.format = 32;
788 xev.xclient.window = drop_x11->source_window;
789
790 xev.xclient.data.l[0] = GDK_SURFACE_XID (gdk_drop_get_surface (drop));
791 xev.xclient.data.l[1] = (possible_actions != 0) ? (2 | 1) : 0;
792 xev.xclient.data.l[2] = 0;
793 xev.xclient.data.l[3] = 0;
794 xev.xclient.data.l[4] = xdnd_action_to_atom (display, action: suggested_action);
795
796 if (gdk_drop_get_drag (self: drop))
797 {
798 gdk_x11_drag_handle_status (display, xevent: &xev);
799 }
800 else
801 {
802 _gdk_x11_send_client_message_async (display,
803 window: drop_x11->source_window,
804 FALSE, event_mask: 0,
805 event_send: &xev.xclient,
806 callback: gdk_x11_drop_do_nothing,
807 data: display);
808 }
809}
810
811static void
812gdk_x11_drop_finish (GdkDrop *drop,
813 GdkDragAction action)
814{
815 GdkX11Drop *drop_x11 = GDK_X11_DROP (ptr: drop);
816 GdkDisplay *display = gdk_drop_get_display (self: drop);
817 XEvent xev;
818
819 if (action == GDK_ACTION_MOVE)
820 {
821 XConvertSelection (GDK_DISPLAY_XDISPLAY (display),
822 gdk_x11_get_xatom_by_name_for_display (display, atom_name: "XdndSelection"),
823 gdk_x11_get_xatom_by_name_for_display (display, atom_name: "DELETE"),
824 gdk_x11_get_xatom_by_name_for_display (display, atom_name: "GDK_SELECTION"),
825 drop_x11->source_window,
826 GDK_X11_DROP (ptr: drop)->timestamp);
827 /* XXX: Do we need to wait for a reply here before sending the next message? */
828 }
829
830 xev.xclient.type = ClientMessage;
831 xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, atom_name: "XdndFinished");
832 xev.xclient.format = 32;
833 xev.xclient.window = drop_x11->source_window;
834
835 xev.xclient.data.l[0] = GDK_SURFACE_XID (gdk_drop_get_surface (drop));
836 if (action != 0)
837 {
838 xev.xclient.data.l[1] = 1;
839 xev.xclient.data.l[2] = xdnd_action_to_atom (display, action);
840 }
841 else
842 {
843 xev.xclient.data.l[1] = 0;
844 xev.xclient.data.l[2] = None;
845 }
846 xev.xclient.data.l[3] = 0;
847 xev.xclient.data.l[4] = 0;
848
849 if (gdk_drop_get_drag (self: drop))
850 {
851 gdk_x11_drag_handle_finished (display, xevent: &xev);
852 }
853 else
854 {
855 _gdk_x11_send_client_message_async (display,
856 window: drop_x11->source_window,
857 FALSE, event_mask: 0,
858 event_send: &xev.xclient,
859 callback: gdk_x11_drop_do_nothing,
860 data: display);
861 }
862}
863
864static void
865gdk_x11_drop_class_init (GdkX11DropClass *klass)
866{
867 GObjectClass *object_class = G_OBJECT_CLASS (klass);
868 GdkDropClass *drop_class = GDK_DROP_CLASS (klass);
869
870 object_class->finalize = gdk_x11_drop_finalize;
871
872 drop_class->status = gdk_x11_drop_status;
873 drop_class->finish = gdk_x11_drop_finish;
874 drop_class->read_async = gdk_x11_drop_read_async;
875 drop_class->read_finish = gdk_x11_drop_read_finish;
876}
877
878static void
879gdk_x11_drop_init (GdkX11Drop *drag)
880{
881}
882
883

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