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