1 /* xdnd.c, xdnd.h - C program library for handling the Xdnd protocol
2    Copyright (C) 1996-2017 Paul Sheer
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2 of the License, or
7    (at your option) any later version.
8 
9    This program 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
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software
16    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
17    02111-1307, USA.
18  */
19 
20 
21 /*
22    Released 1998-08-07
23    Changes:
24 
25    2000-08-08: INCR protocol implemented.
26 
27 */
28 
29 /*
30     DONE:
31      - INCR protocol now implemented
32 
33     TODO:
34      - action_choose_dialog not yet supported (never called)
35      - widget_delete_selection not yet supported and DELETE requests are ignored
36      - not yet tested with applications that only supported XDND 0 or 1
37 */
38 
39 #include <config.h>
40 #include <X11/Xlib.h>
41 #include <X11/X.h>
42 #include <X11/Xatom.h>
43 #include <string.h>
44 #include <stdlib.h>
45 #include <stdio.h>
46 
47 #if TIME_WITH_SYS_TIME
48 # include <sys/time.h>
49 # include <time.h>
50 #else
51 # if HAVE_SYS_TIME_H
52 #  include <sys/time.h>
53 # else
54 #  include <time.h>
55 # endif
56 #endif
57 #include <sys/types.h>
58 #ifdef HAVE_UNISTD_H
59 #   include <unistd.h>
60 #endif
61 
62 #ifdef HAVE_SYS_SELECT_H
63 #  include <sys/select.h>
64 #endif
65 
66 #include "xdnd.h"
67 #include "mad.h"
68 
69 
70 static void xdnd_send_enter (DndClass * dnd, Window window, Window from, Atom * typelist);
71 static void xdnd_send_position (DndClass * dnd, Window window, Window from, Atom action, int x, int y,
72 				unsigned long etime);
73 static void xdnd_send_status (DndClass * dnd, Window window, Window from, int will_accept, int want_position,
74 			      int x, int y, int w, int h, Atom action);
75 static void xdnd_send_leave (DndClass * dnd, Window window, Window from);
76 static void xdnd_send_drop (DndClass * dnd, Window window, Window from, unsigned long etime);
77 static void xdnd_send_finished (DndClass * dnd, Window window, Window from, int error);
78 static int xdnd_convert_selection (DndClass * dnd, Window window, Window requester, Atom type);
79 static void xdnd_selection_send (DndClass * dnd, XSelectionRequestEvent * request, unsigned char *data,
80 				 int length);
81 static int xdnd_get_selection (DndClass * dnd, Window from, Atom property, Window insert);
82 
83 
84 /* just to remind us : */
85 
86 #if 0
87 typedef struct {
88     int type;
89     unsigned long serial;
90     Bool send_event;
91     Display *display;
92     Window window;
93     Atom message_type;
94     int format;
95     union {
96 	char b[20];
97 	short s[10];
98 	long l[5];
99     } data;
100 } XClientMessageEvent;
101 XClientMessageEvent xclient;
102 #endif
103 
104 /* #define DND_DEBUG */
105 
106 #define xdnd_xfree(x) {if (x) { free (x); x = 0; }}
107 
108 #ifdef DND_DEBUG
109 
110 #include <sys/time.h>
111 #include <unistd.h>
112 
xdnd_debug_milliseconds(void)113 char *xdnd_debug_milliseconds (void)
114 {
115     struct timeval tv;
116     static char r[22];
117     gettimeofday (&tv, 0);
118     sprintf (r, "%.2ld.%.3ld", tv.tv_sec % 100L, tv.tv_usec / 1000L);
119     return r;
120 }
121 
122 #define dnd_debug1(a)       printf("%s: %d: %s: " a "\n", __FILE__, __LINE__, xdnd_debug_milliseconds ())
123 #define dnd_debug2(a,b)     printf("%s: %d: %s: " a "\n", __FILE__, __LINE__, xdnd_debug_milliseconds (), b)
124 #define dnd_debug3(a,b,c)   printf("%s: %d: %s: " a "\n", __FILE__, __LINE__, xdnd_debug_milliseconds (), b, c)
125 #define dnd_debug4(a,b,c,d) printf("%s: %d: %s: " a "\n", __FILE__, __LINE__, xdnd_debug_milliseconds (), b, c, d)
126 #else
127 #define dnd_debug1(a)
128 #define dnd_debug2(a,b)
129 #define dnd_debug3(a,b,c)
130 #define dnd_debug4(a,b,c,d)
131 #endif
132 
133 #define dnd_warning(a) fprintf (stderr, a)
134 
135 #define dnd_version_at_least(a,b) ((a) >= (b))
136 
137 static unsigned char dnd_copy_cursor_bits[] =
138 {
139   0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x0f, 0x00, 0x02, 0x00, 0x08, 0x01,
140   0x02, 0x00, 0x08, 0x01, 0x02, 0x00, 0x08, 0x01, 0x02, 0x00, 0xe8, 0x0f,
141   0x02, 0x00, 0x08, 0x01, 0x02, 0x00, 0x08, 0x01, 0x02, 0x00, 0x08, 0x01,
142   0x02, 0x00, 0x08, 0x00, 0x02, 0x04, 0x08, 0x00, 0x02, 0x0c, 0x08, 0x00,
143   0x02, 0x1c, 0x08, 0x00, 0x02, 0x3c, 0x08, 0x00, 0x02, 0x7c, 0x08, 0x00,
144   0x02, 0xfc, 0x08, 0x00, 0x02, 0xfc, 0x09, 0x00, 0x02, 0xfc, 0x0b, 0x00,
145   0x02, 0x7c, 0x08, 0x00, 0xfe, 0x6d, 0x0f, 0x00, 0x00, 0xc4, 0x00, 0x00,
146   0x00, 0xc0, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00,
147     0x00, 0x00, 0x00, 0x00};
148 
149 static unsigned char dnd_copy_mask_bits[] =
150 {
151   0xff, 0xff, 0x1f, 0x00, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xff, 0x1f,
152   0x07, 0x00, 0xfc, 0x1f, 0x07, 0x00, 0xfc, 0x1f, 0x07, 0x00, 0xfc, 0x1f,
153   0x07, 0x00, 0xfc, 0x1f, 0x07, 0x00, 0xfc, 0x1f, 0x07, 0x00, 0xfc, 0x1f,
154   0x07, 0x06, 0xfc, 0x1f, 0x07, 0x0e, 0xfc, 0x1f, 0x07, 0x1e, 0x1c, 0x00,
155   0x07, 0x3e, 0x1c, 0x00, 0x07, 0x7e, 0x1c, 0x00, 0x07, 0xfe, 0x1c, 0x00,
156   0x07, 0xfe, 0x1d, 0x00, 0x07, 0xfe, 0x1f, 0x00, 0x07, 0xfe, 0x1f, 0x00,
157   0xff, 0xff, 0x1f, 0x00, 0xff, 0xff, 0x1e, 0x00, 0xff, 0xef, 0x1f, 0x00,
158   0x00, 0xe6, 0x01, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0xc0, 0x03, 0x00,
159     0x00, 0x80, 0x01, 0x00};
160 
161 static unsigned char dnd_move_cursor_bits[] =
162 {
163   0x00, 0x00, 0x00, 0xfe, 0xff, 0x0f, 0x02, 0x00, 0x08, 0x02, 0x00, 0x08,
164   0x02, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, 0x08,
165   0x02, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x04, 0x08, 0x02, 0x0c, 0x08,
166   0x02, 0x1c, 0x08, 0x02, 0x3c, 0x08, 0x02, 0x7c, 0x08, 0x02, 0xfc, 0x08,
167   0x02, 0xfc, 0x09, 0x02, 0xfc, 0x0b, 0x02, 0x7c, 0x08, 0xfe, 0x6d, 0x0f,
168   0x00, 0xc4, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x80, 0x01, 0x00, 0x80, 0x01,
169     0x00, 0x00, 0x00};
170 
171 static unsigned char dnd_move_mask_bits[] =
172 {
173   0xff, 0xff, 0x1f, 0xff, 0xff, 0x1f, 0xff, 0xff, 0x1f, 0x07, 0x00, 0x1c,
174   0x07, 0x00, 0x1c, 0x07, 0x00, 0x1c, 0x07, 0x00, 0x1c, 0x07, 0x00, 0x1c,
175   0x07, 0x00, 0x1c, 0x07, 0x06, 0x1c, 0x07, 0x0e, 0x1c, 0x07, 0x1e, 0x1c,
176   0x07, 0x3e, 0x1c, 0x07, 0x7e, 0x1c, 0x07, 0xfe, 0x1c, 0x07, 0xfe, 0x1d,
177   0x07, 0xfe, 0x1f, 0x07, 0xfe, 0x1f, 0xff, 0xff, 0x1f, 0xff, 0xff, 0x1e,
178   0xff, 0xef, 0x1f, 0x00, 0xe6, 0x01, 0x00, 0xc0, 0x03, 0x00, 0xc0, 0x03,
179     0x00, 0x80, 0x01};
180 
181 static unsigned char dnd_link_cursor_bits[] =
182 {
183   0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x0f, 0x00, 0x02, 0x00, 0x08, 0x01,
184   0x02, 0x00, 0x88, 0x00, 0x02, 0x00, 0x48, 0x00, 0x02, 0x00, 0xe8, 0x0f,
185   0x02, 0x00, 0x48, 0x00, 0x02, 0x00, 0x88, 0x00, 0x02, 0x00, 0x08, 0x01,
186   0x02, 0x00, 0x08, 0x00, 0x02, 0x04, 0x08, 0x00, 0x02, 0x0c, 0x08, 0x00,
187   0x02, 0x1c, 0x08, 0x00, 0x02, 0x3c, 0x08, 0x00, 0x02, 0x7c, 0x08, 0x00,
188   0x02, 0xfc, 0x08, 0x00, 0x02, 0xfc, 0x09, 0x00, 0x02, 0xfc, 0x0b, 0x00,
189   0x02, 0x7c, 0x08, 0x00, 0xfe, 0x6d, 0x0f, 0x00, 0x00, 0xc4, 0x00, 0x00,
190   0x00, 0xc0, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00,
191     0x00, 0x00, 0x00, 0x00};
192 
193 static unsigned char dnd_link_mask_bits[] =
194 {
195   0xff, 0xff, 0x1f, 0x00, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xff, 0x1f,
196   0x07, 0x00, 0xfc, 0x1f, 0x07, 0x00, 0xfc, 0x1f, 0x07, 0x00, 0xfc, 0x1f,
197   0x07, 0x00, 0xfc, 0x1f, 0x07, 0x00, 0xfc, 0x1f, 0x07, 0x00, 0xfc, 0x1f,
198   0x07, 0x06, 0xfc, 0x1f, 0x07, 0x0e, 0xfc, 0x1f, 0x07, 0x1e, 0x1c, 0x00,
199   0x07, 0x3e, 0x1c, 0x00, 0x07, 0x7e, 0x1c, 0x00, 0x07, 0xfe, 0x1c, 0x00,
200   0x07, 0xfe, 0x1d, 0x00, 0x07, 0xfe, 0x1f, 0x00, 0x07, 0xfe, 0x1f, 0x00,
201   0xff, 0xff, 0x1f, 0x00, 0xff, 0xff, 0x1e, 0x00, 0xff, 0xef, 0x1f, 0x00,
202   0x00, 0xe6, 0x01, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0xc0, 0x03, 0x00,
203     0x00, 0x80, 0x01, 0x00};
204 
205 static unsigned char dnd_ask_cursor_bits[] =
206 {
207   0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x0f, 0x00, 0x02, 0x00, 0x88, 0x03,
208   0x02, 0x00, 0x48, 0x04, 0x02, 0x00, 0x08, 0x04, 0x02, 0x00, 0x08, 0x02,
209   0x02, 0x00, 0x08, 0x01, 0x02, 0x00, 0x08, 0x01, 0x02, 0x00, 0x08, 0x00,
210   0x02, 0x00, 0x08, 0x01, 0x02, 0x04, 0x08, 0x00, 0x02, 0x0c, 0x08, 0x00,
211   0x02, 0x1c, 0x08, 0x00, 0x02, 0x3c, 0x08, 0x00, 0x02, 0x7c, 0x08, 0x00,
212   0x02, 0xfc, 0x08, 0x00, 0x02, 0xfc, 0x09, 0x00, 0x02, 0xfc, 0x0b, 0x00,
213   0x02, 0x7c, 0x08, 0x00, 0xfe, 0x6d, 0x0f, 0x00, 0x00, 0xc4, 0x00, 0x00,
214   0x00, 0xc0, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00,
215     0x00, 0x00, 0x00, 0x00};
216 
217 static unsigned char dnd_ask_mask_bits[] =
218 {
219   0xff, 0xff, 0x1f, 0x00, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xff, 0x1f,
220   0x07, 0x00, 0xfc, 0x1f, 0x07, 0x00, 0xfc, 0x1f, 0x07, 0x00, 0xfc, 0x1f,
221   0x07, 0x00, 0xfc, 0x1f, 0x07, 0x00, 0xfc, 0x1f, 0x07, 0x00, 0xfc, 0x1f,
222   0x07, 0x06, 0xfc, 0x1f, 0x07, 0x0e, 0xfc, 0x1f, 0x07, 0x1e, 0x1c, 0x00,
223   0x07, 0x3e, 0x1c, 0x00, 0x07, 0x7e, 0x1c, 0x00, 0x07, 0xfe, 0x1c, 0x00,
224   0x07, 0xfe, 0x1d, 0x00, 0x07, 0xfe, 0x1f, 0x00, 0x07, 0xfe, 0x1f, 0x00,
225   0xff, 0xff, 0x1f, 0x00, 0xff, 0xff, 0x1e, 0x00, 0xff, 0xef, 0x1f, 0x00,
226   0x00, 0xe6, 0x01, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0xc0, 0x03, 0x00,
227     0x00, 0x80, 0x01, 0x00};
228 
229 static DndCursor dnd_cursors[] =
230 {
231     {29, 25, 10, 10, dnd_copy_cursor_bits, dnd_copy_mask_bits, "XdndActionCopy", 0, 0, 0, 0},
232     {21, 25, 10, 10, dnd_move_cursor_bits, dnd_move_mask_bits, "XdndActionMove", 0, 0, 0, 0},
233     {29, 25, 10, 10, dnd_link_cursor_bits, dnd_link_mask_bits, "XdndActionLink", 0, 0, 0, 0},
234     {29, 25, 10, 10, dnd_ask_cursor_bits, dnd_ask_mask_bits, "XdndActionAsk", 0, 0, 0, 0},
235     {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
236 };
237 
xdnd_reset(DndClass * dnd)238 void xdnd_reset (DndClass * dnd)
239 {
240     dnd->stage = XDND_DROP_STAGE_IDLE;
241     dnd->dragging_version = 0;
242     dnd->internal_drag = 0;
243     dnd->want_position = 0;
244     dnd->ready_to_drop = 0;
245     dnd->will_accept = 0;
246     dnd->rectangle.x = dnd->rectangle.y = 0;
247     dnd->rectangle.width = dnd->rectangle.height = 0;
248     dnd->dropper_window = 0;
249     dnd->dropper_toplevel = 0;
250     dnd->dragger_window = 0;
251     dnd->dragger_typelist = 0;
252     dnd->desired_type = 0;
253     dnd->time = 0;
254 }
255 
xdnd_init(DndClass * dnd,Display * display)256 void xdnd_init (DndClass * dnd, Display * display)
257 {
258     DndCursor *cursor;
259     XColor black, white;
260     memset (dnd, 0, sizeof (*dnd));
261 
262     dnd->display = display;
263     dnd->root_window = DefaultRootWindow (display);
264     dnd->version = XDND_VERSION;
265 
266     dnd->XdndAware = XInternAtom (dnd->display, "XdndAware", False);
267     dnd->XdndSelection = XInternAtom (dnd->display, "XdndSelection", False);
268     dnd->XdndEnter = XInternAtom (dnd->display, "XdndEnter", False);
269     dnd->XdndLeave = XInternAtom (dnd->display, "XdndLeave", False);
270     dnd->XdndPosition = XInternAtom (dnd->display, "XdndPosition", False);
271     dnd->XdndDrop = XInternAtom (dnd->display, "XdndDrop", False);
272     dnd->XdndFinished = XInternAtom (dnd->display, "XdndFinished", False);
273     dnd->XdndStatus = XInternAtom (dnd->display, "XdndStatus", False);
274     dnd->XdndActionCopy = XInternAtom (dnd->display, "XdndActionCopy", False);
275     dnd->XdndActionMove = XInternAtom (dnd->display, "XdndActionMove", False);
276     dnd->XdndActionLink = XInternAtom (dnd->display, "XdndActionLink", False);
277     dnd->XdndActionAsk = XInternAtom (dnd->display, "XdndActionAsk", False);
278     dnd->XdndActionPrivate = XInternAtom (dnd->display, "XdndActionPrivate", False);
279     dnd->XdndTypeList = XInternAtom (dnd->display, "XdndTypeList", False);
280     dnd->XdndActionList = XInternAtom (dnd->display, "XdndActionList", False);
281     dnd->XdndActionDescription = XInternAtom (dnd->display, "XdndActionDescription", False);
282 
283     dnd->Xdnd_NON_PROTOCOL_ATOM = XInternAtom (dnd->display, "JXSelectionWindowProperty", False);
284 
285     xdnd_reset (dnd);
286 
287     dnd->cursors = dnd_cursors;
288 
289     black.pixel = BlackPixel (dnd->display, DefaultScreen (dnd->display));
290     white.pixel = WhitePixel (dnd->display, DefaultScreen (dnd->display));
291 
292     XQueryColor (dnd->display, DefaultColormap (dnd->display, DefaultScreen (dnd->display)), &black);
293     XQueryColor (dnd->display, DefaultColormap (dnd->display, DefaultScreen (dnd->display)), &white);
294 
295     for (cursor = &dnd->cursors[0]; cursor->width; cursor++) {
296 	cursor->image_pixmap = XCreateBitmapFromData \
297 	    (dnd->display, dnd->root_window, (char *) cursor->image_data, cursor->width, cursor->height);
298 	cursor->mask_pixmap = XCreateBitmapFromData \
299 	    (dnd->display, dnd->root_window, (char *) cursor->mask_data, cursor->width, cursor->height);
300 	cursor->cursor = XCreatePixmapCursor (dnd->display, cursor->image_pixmap,
301 	      cursor->mask_pixmap, &black, &white, cursor->x, cursor->y);
302 	XFreePixmap (dnd->display, cursor->image_pixmap);
303 	XFreePixmap (dnd->display, cursor->mask_pixmap);
304 	cursor->action = XInternAtom (dnd->display, cursor->_action, False);
305     }
306 }
307 
xdnd_shut(DndClass * dnd)308 void xdnd_shut (DndClass * dnd)
309 {
310     DndCursor *cursor;
311     for (cursor = &dnd->cursors[0]; cursor->width; cursor++)
312 	XFreeCursor (dnd->display, cursor->cursor);
313     memset (dnd, 0, sizeof (*dnd));
314     return;
315 }
316 
317 
318 /* typelist is a null terminated array */
array_length(Atom * a)319 static int array_length (Atom * a)
320 {
321     int n;
322     for (n = 0; a[n]; n++);
323     return n;
324 }
325 
xdnd_set_dnd_aware(DndClass * dnd,Window window,Atom * typelist)326 void xdnd_set_dnd_aware (DndClass * dnd, Window window, Atom * typelist)
327 {
328     Window root_return, parent;
329     unsigned int nchildren_return;
330     Window *children_return = 0;
331     int r, s;
332     if (dnd->widget_exists)
333 	if (!(*dnd->widget_exists) (dnd, window))
334 	    return;
335     s = XChangeProperty (dnd->display, window, dnd->XdndAware, XA_ATOM, 32, PropModeReplace,
336 			 (unsigned char *) &dnd->version, 1);
337 #if 0
338     dnd_debug4 ("XChangeProperty() = %d, window = %ld, widget = %s", s, window,
339 		(char *) CWidgetOfWindow (window));
340 #endif
341     if (s && typelist) {
342 	int n;
343 	n = array_length (typelist);
344 	if (n)
345 	    s = XChangeProperty (dnd->display, window, dnd->XdndAware, XA_ATOM, 32, PropModeAppend,
346 				 (unsigned char *) typelist, n);
347     }
348     r =
349 	XQueryTree (dnd->display, window, &root_return, &parent, &children_return,
350 		    &nchildren_return);
351     if (children_return)
352 	XFree (children_return);
353     if (r)
354 	xdnd_set_dnd_aware (dnd, parent, typelist);
355 }
356 
xdnd_is_dnd_aware(DndClass * dnd,Window window,int * version,Atom * typelist)357 int xdnd_is_dnd_aware (DndClass * dnd, Window window, int *version, Atom * typelist)
358 {
359     Atom actual;
360     int format;
361     unsigned long count, remaining;
362     unsigned char *data = 0;
363     Atom *types, *t;
364     int result = 1;
365 
366     *version = 0;
367     XGetWindowProperty (dnd->display, window, dnd->XdndAware,
368 			0, 0x8000000L, False, XA_ATOM,
369 			&actual, &format,
370 			&count, &remaining, &data);
371 
372     if (actual != XA_ATOM || format != 32 || count == 0 || !data) {
373 	dnd_debug2 ("XGetWindowProperty failed in xdnd_is_dnd_aware - XdndAware = %ld", dnd->XdndAware);
374 	if (data)
375 	    XFree (data);
376 	return 0;
377     }
378     types = (Atom *) data;
379 #if XDND_VERSION >= 3
380     if (types[0] < 3) {
381 	if (data)
382 	    XFree (data);
383 	return 0;
384     }
385 #endif
386     *version = dnd->version < types[0] ? dnd->version : types[0];	/* minimum */
387     dnd_debug2 ("Using XDND version %d", *version);
388     if (count > 1) {
389 	result = 0;
390 	for (t = typelist; *t; t++) {
391 	    int j;
392 	    for (j = 1; j < count; j++) {
393 		if (types[j] == *t) {
394 		    result = 1;
395 		    break;
396 		}
397 	    }
398 	    if (result)
399 		break;
400 	}
401     }
402     XFree (data);
403     return result;
404 }
405 
xdnd_set_type_list(DndClass * dnd,Window window,Atom * typelist)406 void xdnd_set_type_list (DndClass * dnd, Window window, Atom * typelist)
407 {
408     int n;
409     n = array_length (typelist);
410     XChangeProperty (dnd->display, window, dnd->XdndTypeList, XA_ATOM, 32,
411 		     PropModeReplace, (unsigned char *) typelist, n);
412 }
413 
414 /* result must be free'd */
xdnd_get_type_list(DndClass * dnd,Window window,Atom ** typelist)415 void xdnd_get_type_list (DndClass * dnd, Window window, Atom ** typelist)
416 {
417     Atom type, *a;
418     int format, i;
419     unsigned long count, remaining;
420     unsigned char *data = NULL;
421 
422     *typelist = 0;
423 
424     XGetWindowProperty (dnd->display, window, dnd->XdndTypeList,
425 			0, 0x8000000L, False, XA_ATOM,
426 			&type, &format, &count, &remaining, &data);
427 
428     if (type != XA_ATOM || format != 32 || count == 0 || !data) {
429 	if (data)
430 	    XFree (data);
431 	dnd_debug2 ("XGetWindowProperty failed in xdnd_get_type_list - dnd->XdndTypeList = %ld", dnd->XdndTypeList);
432 	return;
433     }
434     *typelist = malloc ((count + 1) * sizeof (Atom));
435     a = (Atom *) data;
436     for (i = 0; i < count; i++)
437 	(*typelist)[i] = a[i];
438     (*typelist)[count] = 0;
439 
440     XFree (data);
441 }
442 
xdnd_get_three_types(DndClass * dnd,XEvent * xevent,Atom ** typelist)443 void xdnd_get_three_types (DndClass * dnd, XEvent * xevent, Atom ** typelist)
444 {
445     int i;
446     *typelist = malloc ((XDND_THREE + 1) * sizeof (Atom));
447     for (i = 0; i < XDND_THREE; i++)
448 	(*typelist)[i] = XDND_ENTER_TYPE (xevent, i);
449     (*typelist)[XDND_THREE] = 0;	/* although (*typelist)[1] or (*typelist)[2] may also be set to nill */
450 }
451 
452 /* result must be free'd */
concat_string_list(char ** t,int * bytes)453 static char *concat_string_list (char **t, int *bytes)
454 {
455     int l, n;
456     char *s;
457     for (l = n = 0;; n++) {
458 	if (!t[n])
459 	    break;
460 	if (!t[n][0])
461 	    break;
462 	l += strlen (t[n]) + 1;
463     }
464     s = malloc (l + 1);
465     for (l = n = 0;; n++) {
466 	if (!t[n])
467 	    break;
468 	if (!(t[n][0]))
469 	    break;
470 	strcpy (s + l, t[n]);
471 	l += strlen (t[n]) + 1;
472     }
473     *bytes = l;
474     s[l] = '\0';
475     return s;
476 }
477 
xdnd_set_actions(DndClass * dnd,Window window,Atom * actions,char ** descriptions)478 void xdnd_set_actions (DndClass * dnd, Window window, Atom * actions, char **descriptions)
479 {
480     int n, l;
481     char *s;
482     n = array_length (actions);
483 
484     XChangeProperty (dnd->display, window, dnd->XdndActionList, XA_ATOM, 32,
485 		     PropModeReplace, (unsigned char *) actions, n);
486 
487     s = concat_string_list (descriptions, &l);
488     XChangeProperty (dnd->display, window, dnd->XdndActionList, XA_STRING, 8,
489 		     PropModeReplace, (unsigned char *) s, l);
490     xdnd_xfree (s);
491 }
492 
493 /* returns 1 on error or no actions, otherwise result must be free'd
494    xdnd_get_actions (window, &actions, &descriptions);
495    free (actions); free (descriptions); */
xdnd_get_actions(DndClass * dnd,Window window,Atom ** actions,char *** descriptions)496 int xdnd_get_actions (DndClass * dnd, Window window, Atom ** actions, char ***descriptions)
497 {
498     Atom type, *a;
499     int format, i;
500     unsigned long count, dcount, remaining;
501     unsigned char *data = 0, *r;
502 
503     *actions = 0;
504     *descriptions = 0;
505     XGetWindowProperty (dnd->display, window, dnd->XdndActionList,
506 			0, 0x8000000L, False, XA_ATOM,
507 			&type, &format, &count, &remaining, &data);
508 
509     if (type != XA_ATOM || format != 32 || count == 0 || !data) {
510 	if (data)
511 	    XFree (data);
512 	return 1;
513     }
514     *actions = malloc ((count + 1) * sizeof (Atom));
515     a = (Atom *) data;
516     for (i = 0; i < count; i++)
517 	(*actions)[i] = a[i];
518     (*actions)[count] = 0;
519 
520     XFree (data);
521 
522     data = 0;
523     XGetWindowProperty (dnd->display, window, dnd->XdndActionDescription,
524 			0, 0x8000000L, False, XA_STRING, &type, &format,
525 			&dcount, &remaining, &data);
526 
527     if (type != XA_STRING || format != 8 || dcount == 0) {
528 	if (data)
529 	    XFree (data);
530 	*descriptions = malloc ((count + 1) * sizeof (char *));
531 	dnd_warning ("XGetWindowProperty no property or wrong format for action descriptions");
532 	for (i = 0; i < count; i++)
533 	    (*descriptions)[i] = "";
534 	(*descriptions)[count] = 0;
535     } else {
536 	int l;
537 	l = (count + 1) * sizeof (char *);
538 	*descriptions = malloc (l + dcount);
539 	memcpy (*descriptions + l, data, dcount);
540 	XFree (data);
541 	data = (unsigned char *) *descriptions;
542 	data += l;
543 	l = 0;
544 	for (i = 0, r = data;; r += l + 1, i++) {
545 	    l = strlen ((char *) r);
546 	    if (!l || i >= count)
547 		break;
548 	    (*descriptions)[i] = (char *) r;
549 	}
550 	for (; i < count; i++) {
551 	    (*descriptions)[i] = "";
552 	}
553 	(*descriptions)[count] = 0;
554     }
555     return 0;
556 }
557 
558 /* returns non-zero on cancel */
xdnd_choose_action_dialog(DndClass * dnd,Atom * actions,char ** descriptions,Atom * result)559 int xdnd_choose_action_dialog (DndClass * dnd, Atom * actions, char **descriptions, Atom * result)
560 {
561     if (!actions[0])
562 	return 1;
563     if (!dnd->action_choose_dialog) {	/* default to return the first action if no dialog set */
564 	*result = actions[0];
565 	return 0;
566     }
567     return (*dnd->action_choose_dialog) (dnd, descriptions, actions, result);
568 }
569 
xdnd_send_event(DndClass * dnd,Window window,XEvent * xevent)570 static void xdnd_send_event (DndClass * dnd, Window window, XEvent * xevent)
571 {
572     dnd_debug4 ("xdnd_send_event(), window = %ld, l[0] = %ld, l[4] = %ld",
573     window, xevent->xclient.data.l[0], xevent->xclient.data.l[4]);
574     dnd_debug2 ("xdnd_send_event(), from widget widget %s", (char *) CWidgetOfWindow (xevent->xclient.data.l[0]));
575     XSendEvent (dnd->display, window, 0, 0, xevent);
576 }
577 
xdnd_send_enter(DndClass * dnd,Window window,Window from,Atom * typelist)578 static void xdnd_send_enter (DndClass * dnd, Window window, Window from, Atom * typelist)
579 {
580     XEvent xevent;
581     int n, i;
582     n = array_length (typelist);
583 
584     memset (&xevent, 0, sizeof (xevent));
585 
586     xevent.xany.type = ClientMessage;
587     xevent.xany.display = dnd->display;
588     xevent.xclient.window = window;
589     xevent.xclient.message_type = dnd->XdndEnter;
590     xevent.xclient.format = 32;
591 
592     XDND_ENTER_SOURCE_WIN (&xevent) = from;
593     XDND_ENTER_THREE_TYPES_SET (&xevent, n > XDND_THREE);
594     XDND_ENTER_VERSION_SET (&xevent, dnd->version);
595     for (i = 0; i < n && i < XDND_THREE; i++)
596 	XDND_ENTER_TYPE (&xevent, i) = typelist[i];
597     xdnd_send_event (dnd, window, &xevent);
598 }
599 
xdnd_send_position(DndClass * dnd,Window window,Window from,Atom action,int x,int y,unsigned long time)600 static void xdnd_send_position (DndClass * dnd, Window window, Window from, Atom action, int x, int y, unsigned long time)
601 {
602     XEvent xevent;
603 
604     memset (&xevent, 0, sizeof (xevent));
605 
606     xevent.xany.type = ClientMessage;
607     xevent.xany.display = dnd->display;
608     xevent.xclient.window = window;
609     xevent.xclient.message_type = dnd->XdndPosition;
610     xevent.xclient.format = 32;
611 
612     XDND_POSITION_SOURCE_WIN (&xevent) = from;
613     XDND_POSITION_ROOT_SET (&xevent, x, y);
614     if (dnd_version_at_least (dnd->dragging_version, 1))
615 	XDND_POSITION_TIME (&xevent) = time;
616     if (dnd_version_at_least (dnd->dragging_version, 2))
617 	XDND_POSITION_ACTION (&xevent) = action;
618 
619     xdnd_send_event (dnd, window, &xevent);
620 }
621 
xdnd_send_status(DndClass * dnd,Window window,Window from,int will_accept,int want_position,int x,int y,int w,int h,Atom action)622 static void xdnd_send_status (DndClass * dnd, Window window, Window from, int will_accept, \
623 	      int want_position, int x, int y, int w, int h, Atom action)
624 {
625     XEvent xevent;
626 
627     memset (&xevent, 0, sizeof (xevent));
628 
629     xevent.xany.type = ClientMessage;
630     xevent.xany.display = dnd->display;
631     xevent.xclient.window = window;
632     xevent.xclient.message_type = dnd->XdndStatus;
633     xevent.xclient.format = 32;
634 
635     XDND_STATUS_TARGET_WIN (&xevent) = from;
636     XDND_STATUS_WILL_ACCEPT_SET (&xevent, will_accept);
637     if (will_accept)
638 	XDND_STATUS_WANT_POSITION_SET (&xevent, want_position);
639     if (want_position)
640 	XDND_STATUS_RECT_SET (&xevent, x, y, w, h);
641     if (dnd_version_at_least (dnd->dragging_version, 2))
642 	if (will_accept)
643 	    XDND_STATUS_ACTION (&xevent) = action;
644 
645     xdnd_send_event (dnd, window, &xevent);
646 }
647 
xdnd_send_leave(DndClass * dnd,Window window,Window from)648 static void xdnd_send_leave (DndClass * dnd, Window window, Window from)
649 {
650     XEvent xevent;
651 
652     memset (&xevent, 0, sizeof (xevent));
653 
654     xevent.xany.type = ClientMessage;
655     xevent.xany.display = dnd->display;
656     xevent.xclient.window = window;
657     xevent.xclient.message_type = dnd->XdndLeave;
658     xevent.xclient.format = 32;
659 
660     XDND_LEAVE_SOURCE_WIN (&xevent) = from;
661 
662     xdnd_send_event (dnd, window, &xevent);
663 }
664 
xdnd_send_drop(DndClass * dnd,Window window,Window from,unsigned long time)665 static void xdnd_send_drop (DndClass * dnd, Window window, Window from, unsigned long time)
666 {
667     XEvent xevent;
668 
669     memset (&xevent, 0, sizeof (xevent));
670 
671     xevent.xany.type = ClientMessage;
672     xevent.xany.display = dnd->display;
673     xevent.xclient.window = window;
674     xevent.xclient.message_type = dnd->XdndDrop;
675     xevent.xclient.format = 32;
676 
677     XDND_DROP_SOURCE_WIN (&xevent) = from;
678     if (dnd_version_at_least (dnd->dragging_version, 1))
679 	XDND_DROP_TIME (&xevent) = time;
680 
681     xdnd_send_event (dnd, window, &xevent);
682 }
683 
684 /* error is not actually used, i think future versions of the protocol should return an error status
685    to the calling window with the XdndFinished client message */
xdnd_send_finished(DndClass * dnd,Window window,Window from,int error)686 static void xdnd_send_finished (DndClass * dnd, Window window, Window from, int error)
687 {
688     XEvent xevent;
689 
690     memset (&xevent, 0, sizeof (xevent));
691 
692     xevent.xany.type = ClientMessage;
693     xevent.xany.display = dnd->display;
694     xevent.xclient.window = window;
695     xevent.xclient.message_type = dnd->XdndFinished;
696     xevent.xclient.format = 32;
697 
698     XDND_FINISHED_TARGET_WIN (&xevent) = from;
699 
700     xdnd_send_event (dnd, window, &xevent);
701 }
702 
703 /* returns non-zero on error - i.e. no selection owner set. Type is of course the mime type */
xdnd_convert_selection(DndClass * dnd,Window window,Window requester,Atom type)704 static int xdnd_convert_selection (DndClass * dnd, Window window, Window requester, Atom type)
705 {
706     if (!(window = XGetSelectionOwner (dnd->display, dnd->XdndSelection))) {
707 	dnd_debug1 ("xdnd_convert_selection(): XGetSelectionOwner failed");
708 	return 1;
709     }
710     XConvertSelection (dnd->display, dnd->XdndSelection, type,
711 		    dnd->Xdnd_NON_PROTOCOL_ATOM, requester, CurrentTime);
712     return 0;
713 }
714 
715 /* returns non-zero on error */
xdnd_set_selection_owner(DndClass * dnd,Window window,Atom type,Time time)716 static int xdnd_set_selection_owner (DndClass * dnd, Window window, Atom type, Time time)
717 {
718     if (!XSetSelectionOwner (dnd->display, dnd->XdndSelection, window, time)) {
719 	dnd_debug1 ("xdnd_set_selection_owner(): XSetSelectionOwner failed");
720 	return 1;
721     }
722     return 0;
723 }
724 
xdnd_selection_send(DndClass * dnd,XSelectionRequestEvent * request,unsigned char * data,int length)725 static void xdnd_selection_send (DndClass * dnd, XSelectionRequestEvent * request, unsigned char *data, int length)
726 {
727     XEvent xevent;
728     dnd_debug2 ("      requestor = %ld", request->requestor);
729     dnd_debug2 ("      property = %ld", request->property);
730     dnd_debug2 ("      length = %d", length);
731     XChangeProperty (dnd->display, request->requestor, request->property,
732 		     request->target, 8, PropModeReplace, data, length);
733     xevent.xselection.type = SelectionNotify;
734     xevent.xselection.property = request->property;
735     xevent.xselection.display = request->display;
736     xevent.xselection.requestor = request->requestor;
737     xevent.xselection.selection = request->selection;
738     xevent.xselection.target = request->target;
739     xevent.xselection.time = request->time;
740     xdnd_send_event (dnd, request->requestor, &xevent);
741 }
742 
743 #if 0
744 /* respond to a notification that a primary selection has been sent */
745 int xdnd_get_selection (DndClass * dnd, Window from, Atom property, Window insert)
746 {
747     long read;
748     int error = 0;
749     unsigned long remaining;
750     if (!property)
751 	return 1;
752     read = 0;
753     do {
754 	unsigned char *s;
755 	Atom actual;
756 	int format;
757 	unsigned long count;
758 	if (XGetWindowProperty (dnd->display, insert, property, read / 4, 65536, 1,
759 				AnyPropertyType, &actual, &format,
760 				&count, &remaining,
761 				&s) != Success) {
762 	    XFree (s);
763 	    return 1;
764 	}
765 	read += count;
766 	if (dnd->widget_insert_drop && !error)
767 	    error = (*dnd->widget_insert_drop) (dnd, s, count, remaining, insert, from, actual);
768 	XFree (s);
769     } while (remaining);
770     return error;
771 }
772 #endif
773 
paste_prop_internal(DndClass * dnd,Window from,Window insert,unsigned long prop,int delete_prop)774 static int paste_prop_internal (DndClass * dnd, Window from, Window insert, unsigned long prop, int delete_prop)
775 {
776     long nread = 0;
777     unsigned long nitems;
778     unsigned long bytes_after;
779     int error = 0;
780     do {
781 	Atom actual_type;
782 	int actual_fmt;
783 	unsigned char *s = 0;
784 	if (XGetWindowProperty (dnd->display, insert, prop,
785 				nread / 4, 65536, delete_prop,
786 				AnyPropertyType, &actual_type, &actual_fmt,
787 				&nitems, &bytes_after, &s) != Success) {
788 	    XFree (s);
789 	    return 1;
790 	}
791 	nread += nitems;
792 	if (dnd->widget_insert_drop && !error)
793 	    error = (*dnd->widget_insert_drop) (dnd, s, nitems, bytes_after, insert, from, actual_fmt);
794 	XFree (s);
795     } while (bytes_after);
796     if (!nread)
797 	return 1;
798     return 0;
799 }
800 
801 /*
802  * Respond to a notification that a primary selection has been sent (supports INCR)
803  */
xdnd_get_selection(DndClass * dnd,Window from,Atom prop,Window insert)804 static int xdnd_get_selection (DndClass * dnd, Window from, Atom prop, Window insert)
805 {
806     struct timeval tv, tv_start;
807     long nread;
808     unsigned long bytes_after;
809     Atom actual_type;
810     int actual_fmt;
811     unsigned long nitems;
812     unsigned char *s = 0;
813     if (prop == None)
814 	return 1;
815     nread = 0;
816     if (XGetWindowProperty
817 	(dnd->display, insert, prop, 0, 8, False, AnyPropertyType, &actual_type, &actual_fmt,
818 	 &nitems, &bytes_after, &s) != Success) {
819 	XFree (s);
820 	return 1;
821     }
822     XFree (s);
823     if (actual_type != XInternAtom (dnd->display, "INCR", False))
824 	return paste_prop_internal (dnd, from, insert, prop, True);
825     XDeleteProperty (dnd->display, insert, prop);
826     gettimeofday (&tv_start, 0);
827     for (;;) {
828 	long t;
829 	fd_set r;
830 	XEvent xe;
831 	if (XCheckMaskEvent (dnd->display, PropertyChangeMask, &xe)) {
832 	    if (xe.type == PropertyNotify && xe.xproperty.state == PropertyNewValue) {
833 /* time between arrivals of data */
834 		gettimeofday (&tv_start, 0);
835 		if (paste_prop_internal (dnd, from, insert, prop, True))
836 		    break;
837 	    }
838 	} else {
839 	    tv.tv_sec = 0;
840 	    tv.tv_usec = 10000;
841 	    FD_ZERO (&r);
842 	    FD_SET (ConnectionNumber (dnd->display), &r);
843 	    select (ConnectionNumber (dnd->display) + 1, &r, 0, 0, &tv);
844 	    if (FD_ISSET (ConnectionNumber (dnd->display), &r))
845 		continue;
846 	}
847 	gettimeofday (&tv, 0);
848 	t = (tv.tv_sec - tv_start.tv_sec) * 1000000L + (tv.tv_usec - tv_start.tv_usec);
849 /* no data for five seconds, so quit */
850 	if (t > 5000000L)
851 	    return 1;
852     }
853     return 0;
854 }
855 
856 
outside_rectangle(int x,int y,XRectangle * r)857 int outside_rectangle (int x, int y, XRectangle * r)
858 {
859     return (x < r->x || y < r->y || x >= r->x + r->width || y >= r->y + r->height);
860 }
861 
862 /* avoids linking with the maths library */
xdnd_sqrt(float x)863 static float xdnd_sqrt (float x)
864 {
865     float last_ans, ans = 2, a;
866     if (x <= 0.0)
867 	return 0.0;
868     do {
869 	last_ans = ans;
870 	ans = (ans + x / ans) / 2;
871 	a = (ans - last_ans) / ans;
872 	if (a < 0.0)
873 	    a = (-a);
874     } while (a > 0.001);
875     return ans;
876 }
877 
878 #define print_marks print_win_marks(from,__FILE__,__LINE__);
879 
880 /* returns action on success, 0 otherwise */
xdnd_drag(DndClass * dnd,Window from,Atom action,Atom * typelist)881 Atom xdnd_drag (DndClass * dnd, Window from, Atom action, Atom * typelist)
882 {
883     XEvent xevent, xevent_temp;
884     Window over_window = 0, last_window = 0;
885 #if XDND_VERSION >= 3
886     Window last_dropper_toplevel = 0;
887     int internal_dropable = 1;
888 #endif
889     int n;
890     DndCursor *cursor;
891     float x_mouse, y_mouse;
892     int result = 0, dnd_aware;
893 
894     if (!typelist)
895 	dnd_warning ("xdnd_drag() called with typelist = 0");
896 
897 /* first wait until the mouse moves more than five pixels */
898     do {
899 	XNextEvent (dnd->display, &xevent);
900 	if (xevent.type == ButtonRelease) {
901 	    dnd_debug1 ("button release - no motion");
902 	    XSendEvent (dnd->display, xevent.xany.window, 0, ButtonReleaseMask, &xevent);
903 	    return 0;
904 	}
905     } while (xevent.type != MotionNotify);
906 
907     x_mouse = (float) xevent.xmotion.x_root;
908     y_mouse = (float) xevent.xmotion.y_root;
909 
910     if (!dnd->drag_threshold)
911 	dnd->drag_threshold = 4.0;
912     for (;;) {
913 	XNextEvent (dnd->display, &xevent);
914 	if (xevent.type == MotionNotify)
915 	    if (xdnd_sqrt ((x_mouse - xevent.xmotion.x_root) * (x_mouse - xevent.xmotion.x_root) +
916 			   (y_mouse - xevent.xmotion.y_root) * (y_mouse - xevent.xmotion.y_root)) > dnd->drag_threshold)
917 		break;
918 	if (xevent.type == ButtonRelease) {
919 	    XSendEvent (dnd->display, xevent.xany.window, 0, ButtonReleaseMask, &xevent);
920 	    return 0;
921 	}
922     }
923 
924     dnd_debug1 ("moved 5 pixels - going to drag");
925 
926     n = array_length (typelist);
927     if (n > XDND_THREE)
928 	xdnd_set_type_list (dnd, from, typelist);
929 
930     xdnd_reset (dnd);
931 
932     dnd->stage = XDND_DRAG_STAGE_DRAGGING;
933 
934     for (cursor = &dnd->cursors[0]; cursor->width; cursor++)
935 	if (cursor->action == action)
936 	    break;
937     if (!cursor->width)
938 	cursor = &dnd->cursors[0];
939 
940 /* the mouse has been dragged a little, so this is a drag proper */
941     if (XGrabPointer (dnd->display, dnd->root_window, False,
942 		      ButtonMotionMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask,
943 		      GrabModeAsync, GrabModeAsync, None,
944 		      cursor->cursor, CurrentTime) != GrabSuccess)
945 	dnd_debug1 ("Unable to grab pointer");
946 
947 
948     while (xevent.xany.type != ButtonRelease) {
949 	XAllowEvents (dnd->display, SyncPointer, CurrentTime);
950 	XNextEvent (dnd->display, &xevent);
951 	switch (xevent.type) {
952 	case Expose:
953 	    if (dnd->handle_expose_events)
954 		(*dnd->handle_expose_events) (dnd, &xevent);
955 	    break;
956 	case EnterNotify:
957 /* this event is not actually reported, so we find out by ourselves from motion events */
958 	    break;
959 	case LeaveNotify:
960 /* this event is not actually reported, so we find out by ourselves from motion events */
961 	    break;
962 	case ButtonRelease:
963 /* done, but must send a leave event */
964 	    dnd_debug1 ("ButtonRelease - exiting event loop");
965 	    break;
966 	case MotionNotify:
967 	    dnd_aware = 0;
968 	    dnd->dropper_toplevel = 0;
969 	    memcpy (&xevent_temp, &xevent, sizeof (xevent));
970             xevent.xmotion.subwindow = xevent.xmotion.window;
971             {
972 		Window root_return, child_return;
973 		int x_temp, y_temp;
974 		unsigned int mask_return;
975 		while (XQueryPointer (dnd->display, xevent.xmotion.subwindow, &root_return, &child_return,
976 				      &x_temp, &y_temp, &xevent.xmotion.x,
977 				      &xevent.xmotion.y, &mask_return)) {
978 #if XDND_VERSION >= 3
979 		    if (!dnd_aware) {
980 			if ((dnd_aware = xdnd_is_dnd_aware (dnd, xevent.xmotion.subwindow, &dnd->dragging_version, typelist))) {
981 			    dnd->dropper_toplevel = xevent.xmotion.subwindow;
982 			    xevent.xmotion.x_root = x_temp;
983 			    xevent.xmotion.y_root = y_temp;
984 			}
985                     }
986 #else
987 		    xevent.xmotion.x_root = x_temp;
988 		    xevent.xmotion.y_root = y_temp;
989 #endif
990 		    if (!child_return)
991 			goto found_descendent;
992 		    xevent.xmotion.subwindow = child_return;
993 		}
994 		break;
995 	    }
996 	  found_descendent:
997 
998 /* last_window is just for debug purposes */
999 	    if (last_window != xevent.xmotion.subwindow) {
1000 		dnd_debug2 ("window crossing to %ld", xevent.xmotion.subwindow);
1001 		dnd_debug2 ("  current window is %ld", over_window);
1002 		dnd_debug3 ("     last_window = %ld, xmotion.subwindow = %ld", last_window, xevent.xmotion.subwindow);
1003 #if XDND_VERSION >= 3
1004 		dnd_debug3 ("     dropper_toplevel = %ld, last_dropper_toplevel.subwindow = %ld", dnd->dropper_toplevel, last_dropper_toplevel);
1005 #endif
1006 		dnd_debug3 ("     dnd_aware = %d, dnd->options & XDND_OPTION_NO_HYSTERESIS = %ld", dnd_aware, (long) dnd->options & XDND_OPTION_NO_HYSTERESIS);
1007 	    }
1008 
1009 #if XDND_VERSION < 3
1010 /* is the new window dnd aware? if not stay in the old window */
1011 	    if (over_window != xevent.xmotion.subwindow &&
1012 		last_window != xevent.xmotion.subwindow &&
1013 		(
1014 		    (dnd_aware = xdnd_is_dnd_aware (dnd, xevent.xmotion.subwindow, &dnd->dragging_version, typelist))
1015 		    ||
1016 		    (dnd->options & XDND_OPTION_NO_HYSTERESIS)
1017 		))
1018 #else
1019             internal_dropable = 1;
1020             if (dnd->widget_exists && (*dnd->widget_exists) (dnd, xevent.xmotion.subwindow))
1021                 if (!xdnd_is_dnd_aware (dnd, xevent.xmotion.subwindow, &dnd->dragging_version, typelist))
1022                     internal_dropable = 0;
1023 	    dnd_debug3 ("dnd->dropper_toplevel = %ld, last_dropper_toplevel = %ld\n", dnd->dropper_toplevel, last_dropper_toplevel);
1024 	    if ((dnd->dropper_toplevel != last_dropper_toplevel ||
1025 		last_window != xevent.xmotion.subwindow) && internal_dropable &&
1026 		(
1027 		    (dnd_aware)
1028 		    ||
1029 		    (dnd->options & XDND_OPTION_NO_HYSTERESIS)
1030 		))
1031 #endif
1032 	    {
1033 /* leaving window we were over */
1034 		if (over_window) {
1035 		    if (dnd->stage == XDND_DRAG_STAGE_ENTERED) {
1036 			dnd_debug1 ("got leave at right stage");
1037 			dnd->stage = XDND_DRAG_STAGE_DRAGGING;
1038 			if (dnd->internal_drag) {
1039 			    dnd_debug1 ("  our own widget");
1040 			    if (dnd->widget_apply_leave)
1041 				(*dnd->widget_apply_leave) (dnd, over_window);
1042 			} else {
1043 			    dnd_debug1 ("  not our widget - sending XdndLeave");
1044 #if XDND_VERSION < 3
1045 			    xdnd_send_leave (dnd, over_window, from);
1046 #else
1047                             if (dnd->dropper_toplevel != last_dropper_toplevel) {
1048 			        xdnd_send_leave (dnd, last_dropper_toplevel, from);
1049 			    } else {
1050 				dnd_debug1 ("    not sending leave --> dnd->dropper_toplevel == last_dropper_toplevel");
1051 			    }
1052 #endif
1053 			}
1054 			dnd->internal_drag = 0;
1055 			dnd->dropper_window = 0;
1056 			dnd->ready_to_drop = 0;
1057 		    } else {
1058 			dnd_debug1 ("got leave at wrong stage - ignoring");
1059 		    }
1060 		}
1061 /* entering window we are currently over */
1062 		over_window = xevent.xmotion.subwindow;
1063 		if (dnd_aware) {
1064 		    dnd_debug1 ("  is dnd aware");
1065 		    dnd->stage = XDND_DRAG_STAGE_ENTERED;
1066 		    if (dnd->widget_exists && (*dnd->widget_exists) (dnd, over_window))
1067 			dnd->internal_drag = 1;
1068 		    if (dnd->internal_drag) {
1069 			dnd_debug1 ("    our own widget");
1070 		    } else {
1071 			dnd_debug2 ("    not our widget - sending XdndEnter to %ld", over_window);
1072 #if XDND_VERSION < 3
1073 			xdnd_send_enter (dnd, over_window, from, typelist);
1074 #else
1075                         if (dnd->dropper_toplevel != last_dropper_toplevel)
1076 			    xdnd_send_enter (dnd, dnd->dropper_toplevel, from, typelist);
1077 #endif
1078 		    }
1079 		    dnd->want_position = 1;
1080 		    dnd->ready_to_drop = 0;
1081 		    dnd->rectangle.width = dnd->rectangle.height = 0;
1082 		    dnd->dropper_window = over_window;
1083 /* we want an additional motion event in case the pointer enters and then stops */
1084 		    XSendEvent (dnd->display, from, 0, ButtonMotionMask, &xevent_temp);
1085 		    XSync (dnd->display, 0);
1086 		}
1087 #if XDND_VERSION >= 3
1088 		last_dropper_toplevel = dnd->dropper_toplevel;
1089 #endif
1090 /* we are now officially in a new window */
1091 	    } else {
1092 /* got here, so we are just moving `inside' the same window */
1093 		if (dnd->stage == XDND_DRAG_STAGE_ENTERED) {
1094 		    dnd->supported_action = dnd->XdndActionCopy;
1095 		    dnd_debug1 ("got motion at right stage");
1096 		    dnd->x = xevent.xmotion.x_root;
1097 		    dnd->y = xevent.xmotion.y_root;
1098 		    if (dnd->want_position || outside_rectangle (dnd->x, dnd->y, &dnd->rectangle)) {
1099 			dnd_debug1 ("  want position and outside rectangle");
1100 			if (dnd->internal_drag) {
1101 			    dnd_debug1 ("    our own widget");
1102 			    dnd->ready_to_drop = (*dnd->widget_apply_position) (dnd, over_window, from,
1103 										action, dnd->x, dnd->y, xevent.xmotion.time, typelist,
1104 										&dnd->want_position, &dnd->supported_action, &dnd->desired_type, &dnd->rectangle);
1105 			    /* if not ready, keep sending positions, this check is repeated below for XdndStatus from external widgets */
1106 			    if (!dnd->ready_to_drop) {
1107 				dnd->want_position = 1;
1108 				dnd->rectangle.width = dnd->rectangle.height = 0;
1109 			    }
1110 			    dnd_debug2 ("      return action=%ld", dnd->supported_action);
1111 			} else {
1112 #if XDND_VERSION < 3
1113 			    dnd_debug3 ("    not our own widget - sending XdndPosition to %ld, action %ld", over_window, action);
1114 			    xdnd_send_position (dnd, over_window, from, action, dnd->x, dnd->y, xevent.xmotion.time);
1115 #else
1116 			    dnd_debug3 ("    not our own widget - sending XdndPosition to %ld, action %ld", dnd->dropper_toplevel, action);
1117 			    xdnd_send_position (dnd, dnd->dropper_toplevel, from, action, dnd->x, dnd->y, xevent.xmotion.time);
1118 #endif
1119 			}
1120 		    } else if (dnd->want_position) {
1121 			dnd_debug1 ("  inside rectangle");
1122 		    } else {
1123 			dnd_debug1 ("  doesn't want position");
1124 		    }
1125 		}
1126 	    }
1127 	    last_window = xevent.xmotion.subwindow;
1128 	    break;
1129 	case ClientMessage:
1130 	    dnd_debug1 ("ClientMessage recieved");
1131 	    if (xevent.xclient.message_type == dnd->XdndStatus && !dnd->internal_drag) {
1132 		dnd_debug1 ("  XdndStatus recieved");
1133 		if (dnd->stage == XDND_DRAG_STAGE_ENTERED
1134 #if XDND_VERSION < 3
1135                         && XDND_STATUS_TARGET_WIN (&xevent) == dnd->dropper_window
1136 #endif
1137                 ) {
1138 		    dnd_debug1 ("    XdndStatus stage correct, dropper window correct");
1139 		    dnd->want_position = XDND_STATUS_WANT_POSITION (&xevent);
1140 		    dnd->ready_to_drop = XDND_STATUS_WILL_ACCEPT (&xevent);
1141 		    dnd->rectangle.x = XDND_STATUS_RECT_X (&xevent);
1142 		    dnd->rectangle.y = XDND_STATUS_RECT_Y (&xevent);
1143 		    dnd->rectangle.width = XDND_STATUS_RECT_WIDTH (&xevent);
1144 		    dnd->rectangle.height = XDND_STATUS_RECT_HEIGHT (&xevent);
1145 		    dnd->supported_action = dnd->XdndActionCopy;
1146 		    if (dnd_version_at_least (dnd->dragging_version, 2))
1147 			dnd->supported_action = XDND_STATUS_ACTION (&xevent);
1148 		    dnd_debug3 ("      return action=%ld, ready=%d", dnd->supported_action, dnd->ready_to_drop);
1149 		    /* if not ready, keep sending positions, this check is repeated above for internal widgets */
1150 		    if (!dnd->ready_to_drop) {
1151 			dnd->want_position = 1;
1152 			dnd->rectangle.width = dnd->rectangle.height = 0;
1153 		    }
1154 		    dnd_debug3 ("      rectangle = (x=%d, y=%d, ", dnd->rectangle.x, dnd->rectangle.y);
1155 		    dnd_debug4                               ("w=%d, h=%d), want_position=%d\n", dnd->rectangle.width, dnd->rectangle.height, dnd->want_position);
1156 		}
1157 #if XDND_VERSION < 3
1158                 else if (XDND_STATUS_TARGET_WIN (&xevent) != dnd->dropper_window) {
1159 		    dnd_debug3 ("    XdndStatus XDND_STATUS_TARGET_WIN (&xevent) = %ld, dnd->dropper_window = %ld", XDND_STATUS_TARGET_WIN (&xevent), dnd->dropper_window);
1160 		}
1161 #endif
1162                 else {
1163 		    dnd_debug2 ("    XdndStatus stage incorrect dnd->stage = %d", dnd->stage);
1164                 }
1165 	    }
1166 	    break;
1167 	case SelectionRequest:{
1168 /* the target widget MAY request data, so wait for SelectionRequest */
1169 		int length = 0;
1170 		unsigned char *data = 0;
1171 		dnd_debug1 ("SelectionRequest - getting widget data");
1172 
1173 		(*dnd->widget_get_data) (dnd, from, &data, &length, xevent.xselectionrequest.target);
1174 		if (data) {
1175 		    dnd_debug1 ("  sending selection");
1176 		    xdnd_selection_send (dnd, &xevent.xselectionrequest, data, length);
1177 		    xdnd_xfree (data);
1178 		}
1179 	    }
1180 	    break;
1181 	}
1182     }
1183 
1184     if (dnd->ready_to_drop) {
1185 	Time time;
1186 	dnd_debug1 ("ready_to_drop - sending XdndDrop");
1187 	time = xevent.xbutton.time;
1188 	if (dnd->internal_drag) {
1189 /* we are dealing with our own widget, no need to send drop events, just put the data straight */
1190 	    int length = 0;
1191 	    unsigned char *data = 0;
1192 	    if (dnd->widget_insert_drop) {
1193 		(*dnd->widget_get_data) (dnd, from, &data, &length, dnd->desired_type);
1194 		if (data) {
1195 		    if (!(*dnd->widget_insert_drop) (dnd, data, length, 0, dnd->dropper_window, from, dnd->desired_type)) {
1196 			result = dnd->supported_action;		/* success - so return action to caller */
1197 			dnd_debug1 ("  inserted data into widget - success");
1198 		    } else {
1199 			dnd_debug1 ("  inserted data into widget - failed");
1200 		    }
1201 		    xdnd_xfree (data);
1202 		} else {
1203 		    dnd_debug1 ("  got data from widget, but data is null");
1204 		}
1205 	    }
1206 	} else {
1207 	    xdnd_set_selection_owner (dnd, from, dnd->desired_type, time);
1208 #if XDND_VERSION < 3
1209 	    xdnd_send_drop (dnd, dnd->dropper_window, from, time);
1210 #else
1211 	    xdnd_send_drop (dnd, dnd->dropper_toplevel, from, time);
1212 #endif
1213 	}
1214 	if (!dnd->internal_drag)
1215 	    for (;;) {
1216 		XAllowEvents (dnd->display, SyncPointer, CurrentTime);
1217 		XNextEvent (dnd->display, &xevent);
1218 		if (xevent.type == ClientMessage && xevent.xclient.message_type == dnd->XdndFinished) {
1219 		    dnd_debug1 ("XdndFinished");
1220 #if XDND_VERSION < 3
1221 		    if (XDND_FINISHED_TARGET_WIN (&xevent) == dnd->dropper_window) {
1222 #endif
1223 			dnd_debug2 ("  source correct - exiting event loop, action=%ld", dnd->supported_action);
1224 			result = dnd->supported_action;		/* success - so return action to caller */
1225 			break;
1226 #if XDND_VERSION < 3
1227 		    }
1228 #endif
1229 		} else if (xevent.type == Expose) {
1230 		    if (dnd->handle_expose_events)
1231 			(*dnd->handle_expose_events) (dnd, &xevent);
1232 		} else if (xevent.type == MotionNotify) {
1233 		    if (xevent.xmotion.time > time + (dnd->time_out ? dnd->time_out * 1000 : 10000)) {	/* allow a ten second timeout as default */
1234 			dnd_debug1 ("timeout - exiting event loop");
1235 			break;
1236 		    }
1237 		} else if (xevent.type == SelectionRequest && xevent.xselectionrequest.selection == dnd->XdndSelection) {
1238 /* the target widget is going to request data, so check for SelectionRequest events */
1239 		    int length = 0;
1240 		    unsigned char *data = 0;
1241 
1242 		    dnd_debug1 ("SelectionRequest - getting widget data");
1243 		    (*dnd->widget_get_data) (dnd, from, &data, &length, xevent.xselectionrequest.target);
1244 		    if (data) {
1245 			dnd_debug1 ("  sending selection");
1246 			xdnd_selection_send (dnd, &xevent.xselectionrequest, data, length);
1247 			xdnd_xfree (data);
1248 		    }
1249 /* don't wait for a XdndFinished event */
1250 		    if (!dnd_version_at_least (dnd->dragging_version, 2))
1251 			break;
1252 		}
1253 	    }
1254     } else {
1255 	dnd_debug1 ("not ready_to_drop - ungrabbing pointer");
1256     }
1257     XUngrabPointer (dnd->display, CurrentTime);
1258     xdnd_reset (dnd);
1259     return result;
1260 }
1261 
1262 /* returns non-zero if event is handled */
xdnd_handle_drop_events(DndClass * dnd,XEvent * xevent)1263 int xdnd_handle_drop_events (DndClass * dnd, XEvent * xevent)
1264 {
1265     int result = 0;
1266     if (xevent->type == SelectionNotify) {
1267 	dnd_debug1 ("got SelectionNotify");
1268 	if (xevent->xselection.property == dnd->Xdnd_NON_PROTOCOL_ATOM && dnd->stage == XDND_DROP_STAGE_CONVERTING) {
1269 	    int error;
1270 	    dnd_debug1 ("  property is Xdnd_NON_PROTOCOL_ATOM - getting selection");
1271 	    error = xdnd_get_selection (dnd, dnd->dragger_window, xevent->xselection.property, xevent->xany.window);
1272 /* error is not actually used, i think future versions of the protocol maybe should return
1273    an error status to the calling window with the XdndFinished client message */
1274 	    if (dnd_version_at_least (dnd->dragging_version, 2)) {
1275 #if XDND_VERSION >= 3
1276 		xdnd_send_finished (dnd, dnd->dragger_window, dnd->dropper_toplevel, error);
1277 #else
1278 		xdnd_send_finished (dnd, dnd->dragger_window, dnd->dropper_window, error);
1279 #endif
1280 	        dnd_debug1 ("    sending finished");
1281             }
1282 	    xdnd_xfree (dnd->dragger_typelist);
1283 	    xdnd_reset (dnd);
1284 	    dnd->stage = XDND_DROP_STAGE_IDLE;
1285 	    result = 1;
1286 	} else {
1287 	    dnd_debug1 ("  property is not Xdnd_NON_PROTOCOL_ATOM - ignoring");
1288 	}
1289     } else if (xevent->type == ClientMessage) {
1290 	dnd_debug2 ("got ClientMessage to xevent->xany.window = %ld", xevent->xany.window);
1291 	if (xevent->xclient.message_type == dnd->XdndEnter) {
1292 	    dnd_debug2 ("  message_type is XdndEnter, version = %ld", XDND_ENTER_VERSION (xevent));
1293 #if XDND_VERSION >= 3
1294 	    if (XDND_ENTER_VERSION (xevent) < 3)
1295 		return 0;
1296 #endif
1297 	    xdnd_reset (dnd);
1298 	    dnd->dragger_window = XDND_ENTER_SOURCE_WIN (xevent);
1299 #if XDND_VERSION >= 3
1300             dnd->dropper_toplevel = xevent->xany.window;
1301 	    dnd->dropper_window = 0;     /* enter goes to the top level window only,
1302                                             so we don't really know what the
1303                                             sub window is yet */
1304 #else
1305 	    dnd->dropper_window = xevent->xany.window;
1306 #endif
1307 	    xdnd_xfree (dnd->dragger_typelist);
1308 	    if (XDND_ENTER_THREE_TYPES (xevent)) {
1309 		dnd_debug1 ("    three types only");
1310 		xdnd_get_three_types (dnd, xevent, &dnd->dragger_typelist);
1311 	    } else {
1312 		dnd_debug1 ("    more than three types - getting list");
1313 		xdnd_get_type_list (dnd, dnd->dragger_window, &dnd->dragger_typelist);
1314 	    }
1315 	    if (dnd->dragger_typelist)
1316 		dnd->stage = XDND_DROP_STAGE_ENTERED;
1317 	    else
1318 		dnd_debug1 ("      typelist returned as zero!");
1319 	    dnd->dragging_version = XDND_ENTER_VERSION (xevent);
1320 	    result = 1;
1321 	} else if (xevent->xclient.message_type == dnd->XdndLeave) {
1322 #if XDND_VERSION >= 3
1323             if (xevent->xany.window == dnd->dropper_toplevel && dnd->dropper_window)
1324                 xevent->xany.window = dnd->dropper_window;
1325 #endif
1326 	    dnd_debug1 ("  message_type is XdndLeave");
1327 	    if (dnd->dragger_window == XDND_LEAVE_SOURCE_WIN (xevent) && dnd->stage == XDND_DROP_STAGE_ENTERED) {
1328 		dnd_debug1 ("    leaving");
1329 		if (dnd->widget_apply_leave)
1330 		    (*dnd->widget_apply_leave) (dnd, xevent->xany.window);
1331 		dnd->stage = XDND_DROP_STAGE_IDLE;
1332 		xdnd_xfree (dnd->dragger_typelist);
1333 		result = 1;
1334                 dnd->dropper_toplevel = dnd->dropper_window = 0;
1335 	    } else {
1336 		dnd_debug1 ("    wrong stage or from wrong window");
1337 	    }
1338 	} else if (xevent->xclient.message_type == dnd->XdndPosition) {
1339 	    dnd_debug2 ("  message_type is XdndPosition to %ld", xevent->xany.window);
1340 	    if (dnd->dragger_window == XDND_POSITION_SOURCE_WIN (xevent) && dnd->stage == XDND_DROP_STAGE_ENTERED) {
1341 		int want_position;
1342 		Atom action;
1343 		XRectangle rectangle;
1344                 Window last_window;
1345                 last_window = dnd->dropper_window;
1346 #if XDND_VERSION >= 3
1347 /* version 3 gives us the top-level window only. WE have to find the child that the pointer is over: */
1348                 if (1 || xevent->xany.window != dnd->dropper_toplevel || !dnd->dropper_window) {
1349                     Window parent, child, new_child = 0;
1350                     dnd->dropper_toplevel = xevent->xany.window;
1351                     parent = dnd->root_window;
1352                     child = dnd->dropper_toplevel;
1353                     for (;;) {
1354                         int xd, yd;
1355                         new_child = 0;
1356                         if (!XTranslateCoordinates (dnd->display, parent, child,
1357                                     XDND_POSITION_ROOT_X (xevent), XDND_POSITION_ROOT_Y (xevent),
1358                                     &xd, &yd, &new_child))
1359                             break;
1360                         if (!new_child)
1361                             break;
1362                         child = new_child;
1363                     }
1364                     dnd->dropper_window = xevent->xany.window = child;
1365 	            dnd_debug2 ("   child window translates to %ld", dnd->dropper_window);
1366                 } else if (xevent->xany.window == dnd->dropper_toplevel && dnd->dropper_window) {
1367                     xevent->xany.window = dnd->dropper_window;
1368 	            dnd_debug2 ("   child window previously found: %ld", dnd->dropper_window);
1369                 }
1370 #endif
1371 		action = dnd->XdndActionCopy;
1372 		dnd->supported_action = dnd->XdndActionCopy;
1373 		dnd->x = XDND_POSITION_ROOT_X (xevent);
1374 		dnd->y = XDND_POSITION_ROOT_Y (xevent);
1375 		dnd->time = CurrentTime;
1376 		if (dnd_version_at_least (dnd->dragging_version, 1))
1377 		    dnd->time = XDND_POSITION_TIME (xevent);
1378 		if (dnd_version_at_least (dnd->dragging_version, 1))
1379 		    action = XDND_POSITION_ACTION (xevent);
1380 #if XDND_VERSION >= 3
1381                 if (last_window && last_window != xevent->xany.window)
1382 		    if (dnd->widget_apply_leave)
1383 		        (*dnd->widget_apply_leave) (dnd, last_window);
1384 #endif
1385 		dnd->will_accept = (*dnd->widget_apply_position) (dnd, xevent->xany.window, dnd->dragger_window,
1386 		action, dnd->x, dnd->y, dnd->time, dnd->dragger_typelist,
1387 								  &want_position, &dnd->supported_action, &dnd->desired_type, &rectangle);
1388 		dnd_debug2 ("    will accept = %d", dnd->will_accept);
1389 #if XDND_VERSION >= 3
1390 		dnd_debug2 ("    sending status of %ld", dnd->dropper_toplevel);
1391 		xdnd_send_status (dnd, dnd->dragger_window, dnd->dropper_toplevel, dnd->will_accept,
1392 				  want_position, rectangle.x, rectangle.y, rectangle.width, rectangle.height, dnd->supported_action);
1393 #else
1394 		dnd_debug2 ("    sending status of %ld", xevent->xany.window);
1395 		xdnd_send_status (dnd, dnd->dragger_window, xevent->xany.window, dnd->will_accept,
1396 				  want_position, rectangle.x, rectangle.y, rectangle.width, rectangle.height, dnd->supported_action);
1397 #endif
1398 		result = 1;
1399 	    } else {
1400 		dnd_debug1 ("    wrong stage or from wrong window");
1401 	    }
1402 	} else if (xevent->xclient.message_type == dnd->XdndDrop) {
1403 #if XDND_VERSION >= 3
1404             if (xevent->xany.window == dnd->dropper_toplevel && dnd->dropper_window)
1405                 xevent->xany.window = dnd->dropper_window;
1406 #endif
1407 	    dnd_debug1 ("  message_type is XdndDrop");
1408 	    if (dnd->dragger_window == XDND_DROP_SOURCE_WIN (xevent) && dnd->stage == XDND_DROP_STAGE_ENTERED) {
1409 		dnd->time = CurrentTime;
1410 		if (dnd_version_at_least (dnd->dragging_version, 1))
1411 		    dnd->time = XDND_DROP_TIME (xevent);
1412 		if (dnd->will_accept) {
1413 		    dnd_debug1 ("    will_accept is true - converting selectiong");
1414 		    dnd_debug2 ("      my window is %ld", dnd->dropper_window);
1415 		    dnd_debug2 ("        source window is %ld", dnd->dragger_window);
1416 		    xdnd_convert_selection (dnd, dnd->dragger_window, dnd->dropper_window, dnd->desired_type);
1417 		    dnd->stage = XDND_DROP_STAGE_CONVERTING;
1418 		} else {
1419 		    dnd_debug1 ("    will_accept is false - sending finished");
1420 		    if (dnd_version_at_least (dnd->dragging_version, 2)) {
1421 #if XDND_VERSION >= 3
1422 			xdnd_send_finished (dnd, dnd->dragger_window, dnd->dropper_toplevel, 1);
1423 #else
1424 			xdnd_send_finished (dnd, dnd->dragger_window, xevent->xany.window, 1);
1425 #endif
1426                     }
1427 		    xdnd_xfree (dnd->dragger_typelist);
1428 		    xdnd_reset (dnd);
1429 		    dnd->stage = XDND_DROP_STAGE_IDLE;
1430 		}
1431 		result = 1;
1432 	    } else {
1433 		dnd_debug1 ("    wrong stage or from wrong window");
1434 	    }
1435 	}
1436     }
1437     return result;
1438 }
1439 
1440 /*
1441    Following here is a sample implementation: Suppose we want a window
1442    to recieve drops, but do not want to be concerned with setting up all
1443    the DndClass methods. All we then do is call xdnd_get_drop() whenever a
1444    ClientMessage is recieved. If the message has nothing to do with XDND,
1445    xdnd_get_drop quickly returns 0. If it is a XdndEnter message, then
1446    xdnd_get_drop enters its own XNextEvent loop and handles all XDND
1447    protocol messages internally, returning the action requested.
1448 
1449    You should pass a desired typelist and actionlist to xdnd_get_type.
1450    These must be null terminated arrays of atoms, or a null pointer
1451    if you would like any action or type to be accepted. If typelist
1452    is null then the first type of the dragging widgets typelist will
1453    be the one used. If actionlist is null, then only XdndActionCopy will
1454    be accepted.
1455 
1456    The result is stored in *data, length, type, x and y.
1457    *data must be free'd.
1458  */
1459 
1460 struct xdnd_get_drop_info {
1461     unsigned char *drop_data;
1462     int drop_data_length;
1463     int x, y;
1464     Atom return_type;
1465     Atom return_action;
1466     Atom *typelist;
1467     Atom *actionlist;
1468 };
1469 
widget_insert_drop(DndClass * dnd,unsigned char * data,int length,int remaining,Window into,Window from,Atom type)1470 static int widget_insert_drop (DndClass * dnd, unsigned char *data, int length, int remaining, Window into, Window from, Atom type)
1471 {
1472     struct xdnd_get_drop_info *i;
1473     i = (struct xdnd_get_drop_info *) dnd->user_hook1;
1474     if (!i->drop_data) {
1475 	i->drop_data = malloc (length);
1476 	if (!i->drop_data)
1477 	    return 1;
1478 	memcpy (i->drop_data, data, length);
1479 	i->drop_data_length = length;
1480     } else {
1481 	unsigned char *t;
1482 	t = malloc (i->drop_data_length + length);
1483 	if (!t) {
1484 	    free (i->drop_data);
1485 	    i->drop_data = 0;
1486 	    return 1;
1487 	}
1488 	memcpy (t, i->drop_data, i->drop_data_length);
1489 	memcpy (t + i->drop_data_length, data, length);
1490 	free (i->drop_data);
1491 	i->drop_data = t;
1492 	i->drop_data_length += length;
1493     }
1494     return 0;
1495 }
1496 
widget_apply_position(DndClass * dnd,Window widgets_window,Window from,Atom action,int x,int y,Time t,Atom * typelist,int * want_position,Atom * supported_action_return,Atom * desired_type,XRectangle * rectangle)1497 static int widget_apply_position (DndClass * dnd, Window widgets_window, Window from,
1498 		      Atom action, int x, int y, Time t, Atom * typelist,
1499  int *want_position, Atom * supported_action_return, Atom * desired_type,
1500 				  XRectangle * rectangle)
1501 {
1502     int i, j;
1503     struct xdnd_get_drop_info *info;
1504     Atom *dropper_typelist, supported_type = 0;
1505     Atom *supported_actions, supported_action = 0;
1506 
1507     info = (struct xdnd_get_drop_info *) dnd->user_hook1;
1508     dropper_typelist = info->typelist;
1509     supported_actions = info->actionlist;
1510 
1511     if (dropper_typelist) {
1512 /* find a correlation: */
1513 	for (j = 0; dropper_typelist[j]; j++) {
1514 	    for (i = 0; typelist[i]; i++) {
1515 		if (typelist[i] == dropper_typelist[j]) {
1516 		    supported_type = typelist[i];
1517 		    break;
1518 		}
1519 	    }
1520 	    if (supported_type)
1521 		break;
1522 	}
1523     } else {
1524 /* user did not specify, so return first type */
1525 	supported_type = typelist[0];
1526     }
1527 /* not supported, so return false */
1528     if (!supported_type)
1529 	return 0;
1530 
1531     if (supported_actions) {
1532 	for (j = 0; supported_actions[j]; j++) {
1533 	    if (action == supported_actions[j]) {
1534 		supported_action = action;
1535 		break;
1536 	    }
1537 	}
1538     } else {
1539 /* user did not specify */
1540 	if (action == dnd->XdndActionCopy)
1541 	    supported_action = action;
1542     }
1543     if (!supported_action)
1544 	return 0;
1545 
1546     *want_position = 1;
1547     rectangle->x = rectangle->y = 0;
1548     rectangle->width = rectangle->height = 0;
1549 
1550     info->return_action = *supported_action_return = supported_action;
1551     info->return_type = *desired_type = supported_type;
1552     info->x = x;
1553     info->y = y;
1554 
1555     return 1;
1556 }
1557 
xdnd_get_drop(Display * display,XEvent * xevent,Atom * typelist,Atom * actionlist,unsigned char ** data,int * length,Atom * type,int * x,int * y)1558 Atom xdnd_get_drop (Display * display, XEvent * xevent, Atom * typelist, Atom * actionlist,
1559 	  unsigned char **data, int *length, Atom * type, int *x, int *y)
1560 {
1561     Atom action = 0;
1562     static int initialised = 0;
1563     static DndClass dnd;
1564     if (!initialised) {
1565 	xdnd_init (&dnd, display);
1566 	initialised = 1;
1567     }
1568     if (xevent->type != ClientMessage || xevent->xclient.message_type != dnd.XdndEnter) {
1569 	return 0;
1570     } else {
1571 	struct xdnd_get_drop_info i;
1572 
1573 /* setup user structure */
1574 	memset (&i, 0, sizeof (i));
1575 	i.typelist = actionlist;
1576 	i.typelist = typelist;
1577 	dnd.user_hook1 = &i;
1578 
1579 /* setup methods */
1580 	dnd.widget_insert_drop = widget_insert_drop;
1581 	dnd.widget_apply_position = widget_apply_position;
1582 
1583 /* main loop */
1584 	for (;;) {
1585 	    xdnd_handle_drop_events (&dnd, xevent);
1586 	    if (dnd.stage == XDND_DROP_STAGE_IDLE)
1587 		break;
1588 	    XNextEvent (dnd.display, xevent);
1589 	}
1590 
1591 /* return results */
1592 	if (i.drop_data) {
1593 	    *length = i.drop_data_length;
1594 	    *data = i.drop_data;
1595 	    action = i.return_action;
1596 	    *type = i.return_type;
1597 	    *x = i.x;
1598 	    *y = i.y;
1599 	}
1600     }
1601     return action;
1602 }
1603 
1604 
1605