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