1 /*
2 * LessTif ID : $Id: xdnd.c,v 1.1 2004/08/28 19:22:46 dannybackx Exp $
3 *
4 * This file is maintained by Paul Sheer.
5 * Integration with LessTif should be outside this source, so Paul can
6 * maintain this file and guarantee that other users (GTK+, ...) use
7 * the same version.
8 *
9 * If the integration is not possible without changing this file,
10 * please contact Paul.
11 */
12 /* xdnd.c, xdnd.h - C program library for handling the Xdnd protocol
13 Copyright (C) 1998 Paul Sheer
14
15 This library is free software; you can redistribute it and/or
16 modify it under the terms of the GNU Library General Public
17 License as published by the Free Software Foundation; either
18 version 2 of the License, or (at your option) any later version.
19
20 This library is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 Library General Public License for more details.
24
25 You should have received a copy of the GNU Library General Public
26 License along with this library; if not, write to the Free
27 Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28
29 Further info can also be obtained by emailing the author at,
30 psheer@obsidian.co.za
31 */
32
33
34 /*
35 Released 1998-08-07
36 Changes:
37
38
39 */
40
41 /*
42 TODO:
43 - action_choose_dialog not yet supported (never called)
44 - widget_delete_selection not yet supported and DELETE requests are ignored
45 - not yet tested with applications that only supported XDND 0 or 1
46 - INCR protocol not yet implemented
47 */
48
49 #include <LTconfig.h>
50
51 #include <string.h>
52 #include <stdlib.h>
53 #include <stdio.h>
54
55 #include <X11/Xlib.h>
56 #include <X11/X.h>
57 #include <X11/Xatom.h>
58 #include <Xm/XmP.h>
59 #include <XmI/xdnd.h>
60
61 #include <XmI/DebugUtil.h>
62
63 /* #include "mad.h" */
64
65 /* just to remind us : */
66
67 #if 0
68 typedef struct {
69 int type;
70 unsigned long serial;
71 Bool send_event;
72 Display *display;
73 Window window;
74 Atom message_type;
75 int format;
76 union {
77 char b[20];
78 short s[10];
79 long l[5];
80 } data;
81 } XClientMessageEvent;
82 XClientMessageEvent xclient;
83 #endif
84
85 #define xdnd_xfree(x) {if (x) { free (x); x = 0; }}
86
87 #define dnd_version_at_least(a,b) ((a) <= (b))
88
89
90 static unsigned char dnd_copy_cursor_bits[] =
91 {
92 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x0f, 0x00, 0x02, 0x00, 0x08, 0x01,
93 0x02, 0x00, 0x08, 0x01, 0x02, 0x00, 0x08, 0x01, 0x02, 0x00, 0xe8, 0x0f,
94 0x02, 0x00, 0x08, 0x01, 0x02, 0x00, 0x08, 0x01, 0x02, 0x00, 0x08, 0x01,
95 0x02, 0x00, 0x08, 0x00, 0x02, 0x04, 0x08, 0x00, 0x02, 0x0c, 0x08, 0x00,
96 0x02, 0x1c, 0x08, 0x00, 0x02, 0x3c, 0x08, 0x00, 0x02, 0x7c, 0x08, 0x00,
97 0x02, 0xfc, 0x08, 0x00, 0x02, 0xfc, 0x09, 0x00, 0x02, 0xfc, 0x0b, 0x00,
98 0x02, 0x7c, 0x08, 0x00, 0xfe, 0x6d, 0x0f, 0x00, 0x00, 0xc4, 0x00, 0x00,
99 0x00, 0xc0, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00,
100 0x00, 0x00, 0x00, 0x00};
101
102 static unsigned char dnd_copy_mask_bits[] =
103 {
104 0xff, 0xff, 0x1f, 0x00, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xff, 0x1f,
105 0x07, 0x00, 0xfc, 0x1f, 0x07, 0x00, 0xfc, 0x1f, 0x07, 0x00, 0xfc, 0x1f,
106 0x07, 0x00, 0xfc, 0x1f, 0x07, 0x00, 0xfc, 0x1f, 0x07, 0x00, 0xfc, 0x1f,
107 0x07, 0x06, 0xfc, 0x1f, 0x07, 0x0e, 0xfc, 0x1f, 0x07, 0x1e, 0x1c, 0x00,
108 0x07, 0x3e, 0x1c, 0x00, 0x07, 0x7e, 0x1c, 0x00, 0x07, 0xfe, 0x1c, 0x00,
109 0x07, 0xfe, 0x1d, 0x00, 0x07, 0xfe, 0x1f, 0x00, 0x07, 0xfe, 0x1f, 0x00,
110 0xff, 0xff, 0x1f, 0x00, 0xff, 0xff, 0x1e, 0x00, 0xff, 0xef, 0x1f, 0x00,
111 0x00, 0xe6, 0x01, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0xc0, 0x03, 0x00,
112 0x00, 0x80, 0x01, 0x00};
113
114 static unsigned char dnd_move_cursor_bits[] =
115 {
116 0x00, 0x00, 0x00, 0xfe, 0xff, 0x0f, 0x02, 0x00, 0x08, 0x02, 0x00, 0x08,
117 0x02, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, 0x08,
118 0x02, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x04, 0x08, 0x02, 0x0c, 0x08,
119 0x02, 0x1c, 0x08, 0x02, 0x3c, 0x08, 0x02, 0x7c, 0x08, 0x02, 0xfc, 0x08,
120 0x02, 0xfc, 0x09, 0x02, 0xfc, 0x0b, 0x02, 0x7c, 0x08, 0xfe, 0x6d, 0x0f,
121 0x00, 0xc4, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x80, 0x01, 0x00, 0x80, 0x01,
122 0x00, 0x00, 0x00};
123
124 static unsigned char dnd_move_mask_bits[] =
125 {
126 0xff, 0xff, 0x1f, 0xff, 0xff, 0x1f, 0xff, 0xff, 0x1f, 0x07, 0x00, 0x1c,
127 0x07, 0x00, 0x1c, 0x07, 0x00, 0x1c, 0x07, 0x00, 0x1c, 0x07, 0x00, 0x1c,
128 0x07, 0x00, 0x1c, 0x07, 0x06, 0x1c, 0x07, 0x0e, 0x1c, 0x07, 0x1e, 0x1c,
129 0x07, 0x3e, 0x1c, 0x07, 0x7e, 0x1c, 0x07, 0xfe, 0x1c, 0x07, 0xfe, 0x1d,
130 0x07, 0xfe, 0x1f, 0x07, 0xfe, 0x1f, 0xff, 0xff, 0x1f, 0xff, 0xff, 0x1e,
131 0xff, 0xef, 0x1f, 0x00, 0xe6, 0x01, 0x00, 0xc0, 0x03, 0x00, 0xc0, 0x03,
132 0x00, 0x80, 0x01};
133
134 static unsigned char dnd_link_cursor_bits[] =
135 {
136 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x0f, 0x00, 0x02, 0x00, 0x08, 0x01,
137 0x02, 0x00, 0x88, 0x00, 0x02, 0x00, 0x48, 0x00, 0x02, 0x00, 0xe8, 0x0f,
138 0x02, 0x00, 0x48, 0x00, 0x02, 0x00, 0x88, 0x00, 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_link_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_ask_cursor_bits[] =
159 {
160 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x0f, 0x00, 0x02, 0x00, 0x88, 0x03,
161 0x02, 0x00, 0x48, 0x04, 0x02, 0x00, 0x08, 0x04, 0x02, 0x00, 0x08, 0x02,
162 0x02, 0x00, 0x08, 0x01, 0x02, 0x00, 0x08, 0x01, 0x02, 0x00, 0x08, 0x00,
163 0x02, 0x00, 0x08, 0x01, 0x02, 0x04, 0x08, 0x00, 0x02, 0x0c, 0x08, 0x00,
164 0x02, 0x1c, 0x08, 0x00, 0x02, 0x3c, 0x08, 0x00, 0x02, 0x7c, 0x08, 0x00,
165 0x02, 0xfc, 0x08, 0x00, 0x02, 0xfc, 0x09, 0x00, 0x02, 0xfc, 0x0b, 0x00,
166 0x02, 0x7c, 0x08, 0x00, 0xfe, 0x6d, 0x0f, 0x00, 0x00, 0xc4, 0x00, 0x00,
167 0x00, 0xc0, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00,
168 0x00, 0x00, 0x00, 0x00};
169
170 static unsigned char dnd_ask_mask_bits[] =
171 {
172 0xff, 0xff, 0x1f, 0x00, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xff, 0x1f,
173 0x07, 0x00, 0xfc, 0x1f, 0x07, 0x00, 0xfc, 0x1f, 0x07, 0x00, 0xfc, 0x1f,
174 0x07, 0x00, 0xfc, 0x1f, 0x07, 0x00, 0xfc, 0x1f, 0x07, 0x00, 0xfc, 0x1f,
175 0x07, 0x06, 0xfc, 0x1f, 0x07, 0x0e, 0xfc, 0x1f, 0x07, 0x1e, 0x1c, 0x00,
176 0x07, 0x3e, 0x1c, 0x00, 0x07, 0x7e, 0x1c, 0x00, 0x07, 0xfe, 0x1c, 0x00,
177 0x07, 0xfe, 0x1d, 0x00, 0x07, 0xfe, 0x1f, 0x00, 0x07, 0xfe, 0x1f, 0x00,
178 0xff, 0xff, 0x1f, 0x00, 0xff, 0xff, 0x1e, 0x00, 0xff, 0xef, 0x1f, 0x00,
179 0x00, 0xe6, 0x01, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0xc0, 0x03, 0x00,
180 0x00, 0x80, 0x01, 0x00};
181
182 static DndCursor dnd_cursors[] =
183 {
184 {29, 25, 10, 10, dnd_copy_cursor_bits, dnd_copy_mask_bits, "XdndActionCopy", 0, 0, 0, 0},
185 {21, 25, 10, 10, dnd_move_cursor_bits, dnd_move_mask_bits, "XdndActionMove", 0, 0, 0, 0},
186 {29, 25, 10, 10, dnd_link_cursor_bits, dnd_link_mask_bits, "XdndActionLink", 0, 0, 0, 0},
187 {29, 25, 10, 10, dnd_ask_cursor_bits, dnd_ask_mask_bits, "XdndActionAsk", 0, 0, 0, 0},
188 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
189 };
190
xdnd_reset(DndClass * dnd)191 void xdnd_reset (DndClass * dnd)
192 {
193 dnd->stage = XDND_DROP_STAGE_IDLE;
194 dnd->dragging_version = 0;
195 dnd->internal_drag = 0;
196 dnd->want_position = 0;
197 dnd->ready_to_drop = 0;
198 dnd->will_accept = 0;
199 dnd->rectangle.x = dnd->rectangle.y = 0;
200 dnd->rectangle.width = dnd->rectangle.height = 0;
201 dnd->dropper_window = 0;
202 dnd->dragger_window = 0;
203 dnd->dragger_typelist = 0;
204 dnd->desired_type = 0;
205 dnd->time = 0;
206 }
207
xdnd_init(DndClass * dnd,Display * display)208 void xdnd_init (DndClass * dnd, Display * display)
209 {
210 DndCursor *cursor;
211 XColor black, white;
212 memset (dnd, 0, sizeof (*dnd));
213
214 dnd->display = display;
215 dnd->root_window = DefaultRootWindow (display);
216 dnd->version = XDND_VERSION;
217
218 dnd->XdndAware = XInternAtom (dnd->display, "XdndAware", False);
219 dnd->XdndSelection = XInternAtom (dnd->display, "XdndSelection", False);
220 dnd->XdndEnter = XInternAtom (dnd->display, "XdndEnter", False);
221 dnd->XdndLeave = XInternAtom (dnd->display, "XdndLeave", False);
222 dnd->XdndPosition = XInternAtom (dnd->display, "XdndPosition", False);
223 dnd->XdndDrop = XInternAtom (dnd->display, "XdndDrop", False);
224 dnd->XdndFinished = XInternAtom (dnd->display, "XdndFinished", False);
225 dnd->XdndStatus = XInternAtom (dnd->display, "XdndStatus", False);
226 dnd->XdndActionCopy = XInternAtom (dnd->display, "XdndActionCopy", False);
227 dnd->XdndActionMove = XInternAtom (dnd->display, "XdndActionMove", False);
228 dnd->XdndActionLink = XInternAtom (dnd->display, "XdndActionLink", False);
229 dnd->XdndActionAsk = XInternAtom (dnd->display, "XdndActionAsk", False);
230 dnd->XdndActionPrivate = XInternAtom (dnd->display, "XdndActionPrivate", False);
231 dnd->XdndTypeList = XInternAtom (dnd->display, "XdndTypeList", False);
232 dnd->XdndActionList = XInternAtom (dnd->display, "XdndActionList", False);
233 dnd->XdndActionDescription = XInternAtom (dnd->display, "XdndActionDescription", False);
234
235 dnd->Xdnd_NON_PROTOCOL_ATOM = XInternAtom (dnd->display, "JXSelectionWindowProperty", False);
236
237 xdnd_reset (dnd);
238
239 dnd->cursors = dnd_cursors;
240
241 black.pixel = BlackPixel (dnd->display, DefaultScreen (dnd->display));
242 white.pixel = WhitePixel (dnd->display, DefaultScreen (dnd->display));
243
244 XQueryColor (dnd->display, DefaultColormap (dnd->display, DefaultScreen (dnd->display)), &black);
245 XQueryColor (dnd->display, DefaultColormap (dnd->display, DefaultScreen (dnd->display)), &white);
246
247 for (cursor = &dnd->cursors[0]; cursor->width; cursor++) {
248 cursor->image_pixmap = XCreateBitmapFromData \
249 (dnd->display, dnd->root_window, (char *) cursor->image_data, cursor->width, cursor->height);
250 cursor->mask_pixmap = XCreateBitmapFromData \
251 (dnd->display, dnd->root_window, (char *) cursor->mask_data, cursor->width, cursor->height);
252 cursor->cursor = XCreatePixmapCursor (dnd->display, cursor->image_pixmap,
253 cursor->mask_pixmap, &black, &white, cursor->x, cursor->y);
254 XFreePixmap (dnd->display, cursor->image_pixmap);
255 XFreePixmap (dnd->display, cursor->mask_pixmap);
256 cursor->action = XInternAtom (dnd->display, cursor->_action, False);
257 }
258 }
259
xdnd_shut(DndClass * dnd)260 void xdnd_shut (DndClass * dnd)
261 {
262 DndCursor *cursor;
263 for (cursor = &dnd->cursors[0]; cursor->width; cursor++)
264 XFreeCursor (dnd->display, cursor->cursor);
265 memset (dnd, 0, sizeof (*dnd));
266 return;
267 }
268
269
270 /* typelist is a null terminated array */
array_length(Atom * a)271 static int array_length (Atom * a)
272 {
273 int n;
274 for (n = 0; a[n]; n++);
275 return n;
276 }
277
xdnd_set_dnd_aware(DndClass * dnd,Window window,Atom * typelist)278 void xdnd_set_dnd_aware (DndClass * dnd, Window window, Atom * typelist)
279 {
280 XChangeProperty (dnd->display, window, dnd->XdndAware, XA_ATOM, 32, PropModeReplace, (unsigned char *) &dnd->version, 1);
281 if (typelist) {
282 int n;
283 n = array_length (typelist);
284 if (n)
285 XChangeProperty (dnd->display, window, dnd->XdndAware, XA_ATOM, 32, PropModeAppend, (unsigned char *) typelist, n);
286 }
287 }
288
xdnd_is_dnd_aware(DndClass * dnd,Window window,int * version,Atom * typelist)289 int xdnd_is_dnd_aware (DndClass * dnd, Window window, int *version, Atom * typelist)
290 {
291 Atom actual;
292 int format;
293 unsigned long count, remaining;
294 unsigned char *data = 0;
295 Atom *types, *t;
296 int result = 1;
297
298 *version = 0;
299 XGetWindowProperty (dnd->display, window, dnd->XdndAware,
300 0, 0x8000000L, False, XA_ATOM,
301 &actual, &format,
302 &count, &remaining, &data);
303
304 if (actual != XA_ATOM || format != 32 || count == 0 || !data) {
305 DEBUGOUT(_LtDebug(__FILE__, NULL, "XGetWindowProperty failed in xdnd_is_dnd_aware - XdndAware = %ld", dnd->XdndAware));
306
307 if (data)
308 XFree (data);
309 return 0;
310 }
311 types = (Atom *) data;
312 *version = dnd->version < types[0] ? dnd->version : types[0]; /* minimum */
313 DEBUGOUT(_LtDebug(__FILE__, NULL,"Using XDND version %d", *version));
314 if (count > 1) {
315 result = 0;
316 for (t = typelist; *t; t++) {
317 int j;
318 for (j = 1; j < count; j++) {
319 if (types[j] == *t) {
320 result = 1;
321 break;
322 }
323 }
324 if (result)
325 break;
326 }
327 }
328 XFree (data);
329 return result;
330 }
331
xdnd_set_type_list(DndClass * dnd,Window window,Atom * typelist)332 void xdnd_set_type_list (DndClass * dnd, Window window, Atom * typelist)
333 {
334 int n;
335 n = array_length (typelist);
336 XChangeProperty (dnd->display, window, dnd->XdndTypeList, XA_ATOM, 32,
337 PropModeReplace, (unsigned char *) typelist, n);
338 }
339
340 /* result must be free'd */
xdnd_get_type_list(DndClass * dnd,Window window,Atom ** typelist)341 void xdnd_get_type_list (DndClass * dnd, Window window, Atom ** typelist)
342 {
343 Atom type, *a;
344 int format, i;
345 unsigned long count, remaining;
346 unsigned char *data = NULL;
347
348 *typelist = 0;
349
350 XGetWindowProperty (dnd->display, window, dnd->XdndTypeList,
351 0, 0x8000000L, False, XA_ATOM,
352 &type, &format, &count, &remaining, &data);
353
354 if (type != XA_ATOM || format != 32 || count == 0 || !data) {
355 if (data)
356 XFree (data);
357 DEBUGOUT(_LtDebug(__FILE__, NULL, "XGetWindowProperty failed in xdnd_get_type_list - dnd->XdndTypeList = %ld", dnd->XdndTypeList));
358 return;
359 }
360 *typelist = malloc ((count + 1) * sizeof (Atom));
361 a = (Atom *) data;
362 for (i = 0; i < count; i++)
363 (*typelist)[i] = a[i];
364 (*typelist)[count] = 0;
365
366 XFree (data);
367 }
368
xdnd_get_three_types(DndClass * dnd,XEvent * xevent,Atom ** typelist)369 void xdnd_get_three_types (DndClass * dnd, XEvent * xevent, Atom ** typelist)
370 {
371 int i;
372 *typelist = malloc ((XDND_THREE + 1) * sizeof (Atom));
373 for (i = 0; i < XDND_THREE; i++)
374 (*typelist)[i] = XDND_ENTER_TYPE (xevent, i);
375 (*typelist)[XDND_THREE] = 0; /* although (*typelist)[1] or (*typelist)[2] may also be set to nill */
376 }
377
378 /* result must be free'd */
concat_string_list(char ** t,int * bytes)379 static char *concat_string_list (char **t, int *bytes)
380 {
381 int l, n;
382 char *s;
383 for (l = n = 0;; n++) {
384 if (!t[n])
385 break;
386 if (!t[n][0])
387 break;
388 l += strlen (t[n]) + 1;
389 }
390 s = malloc (l + 1);
391 for (l = n = 0;; n++) {
392 if (!t[n])
393 break;
394 if (!(t[n][0]))
395 break;
396 strcpy (s + l, t[n]);
397 l += strlen (t[n]) + 1;
398 }
399 *bytes = l;
400 s[l] = '\0';
401 return s;
402 }
403
xdnd_set_actions(DndClass * dnd,Window window,Atom * actions,char ** descriptions)404 void xdnd_set_actions (DndClass * dnd, Window window, Atom * actions, char **descriptions)
405 {
406 int n, l;
407 char *s;
408 n = array_length (actions);
409
410 XChangeProperty (dnd->display, window, dnd->XdndActionList, XA_ATOM, 32,
411 PropModeReplace, (unsigned char *) actions, n);
412
413 s = concat_string_list (descriptions, &l);
414 XChangeProperty (dnd->display, window, dnd->XdndActionList, XA_STRING, 8,
415 PropModeReplace, (unsigned char *) s, l);
416 xdnd_xfree (s);
417 }
418
419 /* returns 1 on error or no actions, otherwise result must be free'd
420 xdnd_get_actions (window, &actions, &descriptions);
421 free (actions); free (descriptions); */
xdnd_get_actions(DndClass * dnd,Window window,Atom ** actions,char *** descriptions)422 int xdnd_get_actions (DndClass * dnd, Window window, Atom ** actions, char ***descriptions)
423 {
424 Atom type, *a;
425 int format, i;
426 unsigned long count, dcount, remaining;
427 unsigned char *data = 0, *r;
428
429 *actions = 0;
430 *descriptions = 0;
431 XGetWindowProperty (dnd->display, window, dnd->XdndActionList,
432 0, 0x8000000L, False, XA_ATOM,
433 &type, &format, &count, &remaining, &data);
434
435 if (type != XA_ATOM || format != 32 || count == 0 || !data) {
436 if (data)
437 XFree (data);
438 return 1;
439 }
440 *actions = malloc ((count + 1) * sizeof (Atom));
441 a = (Atom *) data;
442 for (i = 0; i < count; i++)
443 (*actions)[i] = a[i];
444 (*actions)[count] = 0;
445
446 XFree (data);
447
448 data = 0;
449 XGetWindowProperty (dnd->display, window, dnd->XdndActionDescription,
450 0, 0x8000000L, False, XA_STRING, &type, &format,
451 &dcount, &remaining, &data);
452
453 if (type != XA_STRING || format != 8 || dcount == 0) {
454 if (data)
455 XFree (data);
456 *descriptions = malloc ((count + 1) * sizeof (char *));
457 _XmWarning(NULL, "XGetWindowProperty no property or wrong format for action descriptions");
458 for (i = 0; i < count; i++)
459 (*descriptions)[i] = "";
460 (*descriptions)[count] = 0;
461 } else {
462 int l;
463 l = (count + 1) * sizeof (char *);
464 *descriptions = malloc (l + dcount);
465 memcpy (*descriptions + l, data, dcount);
466 XFree (data);
467 data = (unsigned char *) *descriptions;
468 data += l;
469 l = 0;
470 for (i = 0, r = data;; r += l + 1, i++) {
471 l = strlen ((char *) r);
472 if (!l || i >= count)
473 break;
474 (*descriptions)[i] = (char *) r;
475 }
476 for (; i < count; i++) {
477 (*descriptions)[i] = "";
478 }
479 (*descriptions)[count] = 0;
480 }
481 return 0;
482 }
483
484 /* returns non-zero on cancel */
xdnd_choose_action_dialog(DndClass * dnd,Atom * actions,char ** descriptions,Atom * result)485 int xdnd_choose_action_dialog (DndClass * dnd, Atom * actions, char **descriptions, Atom * result)
486 {
487 if (!actions[0])
488 return 1;
489 if (!dnd->action_choose_dialog) { /* default to return the first action if no dialog set */
490 *result = actions[0];
491 return 0;
492 }
493 return (*dnd->action_choose_dialog) (dnd, descriptions, actions, result);
494 }
495
xdnd_send_event(DndClass * dnd,Window window,XEvent * xevent)496 static void xdnd_send_event (DndClass * dnd, Window window, XEvent * xevent)
497 {
498 XSendEvent (dnd->display, window, 0, 0, xevent);
499 }
500
xdnd_send_enter(DndClass * dnd,Window window,Window from,Atom * typelist)501 void xdnd_send_enter (DndClass * dnd, Window window, Window from, Atom * typelist)
502 {
503 XEvent xevent;
504 int n, i;
505 n = array_length (typelist);
506
507 memset (&xevent, 0, sizeof (xevent));
508
509 xevent.xany.type = ClientMessage;
510 xevent.xany.display = dnd->display;
511 xevent.xclient.window = window;
512 xevent.xclient.message_type = dnd->XdndEnter;
513 xevent.xclient.format = 32;
514
515 XDND_ENTER_SOURCE_WIN (&xevent) = from;
516 XDND_ENTER_THREE_TYPES_SET (&xevent, n > XDND_THREE);
517 XDND_ENTER_VERSION_SET (&xevent, dnd->version);
518 for (i = 0; i < n && i < XDND_THREE; i++)
519 XDND_ENTER_TYPE (&xevent, i) = typelist[i];
520 xdnd_send_event (dnd, window, &xevent);
521 }
522
xdnd_send_position(DndClass * dnd,Window window,Window from,Atom action,int x,int y,unsigned long time)523 void xdnd_send_position (DndClass * dnd, Window window, Window from, Atom action, int x, int y, unsigned long time)
524 {
525 XEvent xevent;
526
527 memset (&xevent, 0, sizeof (xevent));
528
529 xevent.xany.type = ClientMessage;
530 xevent.xany.display = dnd->display;
531 xevent.xclient.window = window;
532 xevent.xclient.message_type = dnd->XdndPosition;
533 xevent.xclient.format = 32;
534
535 XDND_POSITION_SOURCE_WIN (&xevent) = from;
536 XDND_POSITION_ROOT_SET (&xevent, x, y);
537 if (dnd_version_at_least (dnd->dragging_version, 1))
538 XDND_POSITION_TIME (&xevent) = time;
539 if (dnd_version_at_least (dnd->dragging_version, 2))
540 XDND_POSITION_ACTION (&xevent) = action;
541
542 xdnd_send_event (dnd, window, &xevent);
543 }
544
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)545 void xdnd_send_status (DndClass * dnd, Window window, Window from, int will_accept, \
546 int want_position, int x, int y, int w, int h, Atom action)
547 {
548 XEvent xevent;
549
550 memset (&xevent, 0, sizeof (xevent));
551
552 xevent.xany.type = ClientMessage;
553 xevent.xany.display = dnd->display;
554 xevent.xclient.window = window;
555 xevent.xclient.message_type = dnd->XdndStatus;
556 xevent.xclient.format = 32;
557
558 XDND_STATUS_TARGET_WIN (&xevent) = from;
559 XDND_STATUS_WILL_ACCEPT_SET (&xevent, will_accept);
560 if (will_accept)
561 XDND_STATUS_WANT_POSITION_SET (&xevent, want_position);
562 if (want_position)
563 XDND_STATUS_RECT_SET (&xevent, x, y, w, h);
564 if (dnd_version_at_least (dnd->dragging_version, 2))
565 if (will_accept)
566 XDND_STATUS_ACTION (&xevent) = action;
567
568 xdnd_send_event (dnd, window, &xevent);
569 }
570
xdnd_send_leave(DndClass * dnd,Window window,Window from)571 void xdnd_send_leave (DndClass * dnd, Window window, Window from)
572 {
573 XEvent xevent;
574
575 memset (&xevent, 0, sizeof (xevent));
576
577 xevent.xany.type = ClientMessage;
578 xevent.xany.display = dnd->display;
579 xevent.xclient.window = window;
580 xevent.xclient.message_type = dnd->XdndLeave;
581 xevent.xclient.format = 32;
582
583 XDND_LEAVE_SOURCE_WIN (&xevent) = from;
584
585 xdnd_send_event (dnd, window, &xevent);
586 }
587
xdnd_send_drop(DndClass * dnd,Window window,Window from,unsigned long time)588 void xdnd_send_drop (DndClass * dnd, Window window, Window from, unsigned long time)
589 {
590 XEvent xevent;
591
592 memset (&xevent, 0, sizeof (xevent));
593
594 xevent.xany.type = ClientMessage;
595 xevent.xany.display = dnd->display;
596 xevent.xclient.window = window;
597 xevent.xclient.message_type = dnd->XdndDrop;
598 xevent.xclient.format = 32;
599
600 XDND_DROP_SOURCE_WIN (&xevent) = from;
601 if (dnd_version_at_least (dnd->dragging_version, 1))
602 XDND_DROP_TIME (&xevent) = time;
603
604 xdnd_send_event (dnd, window, &xevent);
605 }
606
607 /* error is not actually used, i think future versions of the protocol should return an error status
608 to the calling window with the XdndFinished client message */
xdnd_send_finished(DndClass * dnd,Window window,Window from,int error)609 void xdnd_send_finished (DndClass * dnd, Window window, Window from, int error)
610 {
611 XEvent xevent;
612
613 memset (&xevent, 0, sizeof (xevent));
614
615 xevent.xany.type = ClientMessage;
616 xevent.xany.display = dnd->display;
617 xevent.xclient.window = window;
618 xevent.xclient.message_type = dnd->XdndFinished;
619 xevent.xclient.format = 32;
620
621 XDND_FINISHED_TARGET_WIN (&xevent) = from;
622
623 xdnd_send_event (dnd, window, &xevent);
624 }
625
626 /* 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)627 int xdnd_convert_selection (DndClass * dnd, Window window, Window requester, Atom type)
628 {
629 if (window != XGetSelectionOwner (dnd->display, dnd->XdndSelection)) {
630 DEBUGOUT(_LtDebug(__FILE__, NULL,"xdnd_convert_selection(): XGetSelectionOwner failed"));
631 return 1;
632 }
633 XConvertSelection (dnd->display, dnd->XdndSelection, type,
634 dnd->Xdnd_NON_PROTOCOL_ATOM, requester, CurrentTime);
635 return 0;
636 }
637
638 /* returns non-zero on error */
xdnd_set_selection_owner(DndClass * dnd,Window window,Atom type)639 int xdnd_set_selection_owner (DndClass * dnd, Window window, Atom type)
640 {
641 if (!XSetSelectionOwner (dnd->display, dnd->XdndSelection, window, CurrentTime)) {
642 DEBUGOUT(_LtDebug(__FILE__, NULL, "xdnd_set_selection_owner(): XSetSelectionOwner failed"));
643 return 1;
644 }
645 return 0;
646 }
647
xdnd_selection_send(DndClass * dnd,XSelectionRequestEvent * request,unsigned char * data,int length)648 void xdnd_selection_send (DndClass * dnd, XSelectionRequestEvent * request, unsigned char *data, int length)
649 {
650 XEvent xevent;
651 DEBUGOUT(_LtDebug(__FILE__, NULL, " requestor = %ld", request->requestor));
652 DEBUGOUT(_LtDebug(__FILE__, NULL, " property = %ld", request->property));
653 DEBUGOUT(_LtDebug(__FILE__, NULL, " length = %d", length));
654 XChangeProperty (dnd->display, request->requestor, request->property,
655 request->target, 8, PropModeReplace, data, length);
656 xevent.xselection.type = SelectionNotify;
657 xevent.xselection.property = request->property;
658 xevent.xselection.display = request->display;
659 xevent.xselection.requestor = request->requestor;
660 xevent.xselection.selection = request->selection;
661 xevent.xselection.target = request->target;
662 xevent.xselection.time = request->time;
663 xdnd_send_event (dnd, request->requestor, &xevent);
664 }
665
666 /* respond to a notification that a primary selection has been sent */
xdnd_get_selection(DndClass * dnd,Window from,Atom property,Window insert)667 int xdnd_get_selection (DndClass * dnd, Window from, Atom property, Window insert)
668 {
669 long read;
670 int error = 0;
671 unsigned long remaining;
672 if (!property)
673 return 1;
674 read = 0;
675 do {
676 unsigned char *s;
677 Atom actual;
678 int format;
679 unsigned long count;
680 if (XGetWindowProperty (dnd->display, insert, property, read / 4, 65536, 1,
681 AnyPropertyType, &actual, &format,
682 &count, &remaining,
683 &s) != Success) {
684 XFree (s);
685 return 1;
686 }
687 read += count;
688 if (dnd->widget_insert_drop && !error)
689 error = (*dnd->widget_insert_drop) (dnd, s, count, remaining, insert, from, actual);
690 XFree (s);
691 } while (remaining);
692 return error;
693 }
694
695
outside_rectangle(int x,int y,XRectangle * r)696 int outside_rectangle (int x, int y, XRectangle * r)
697 {
698 return (x < r->x || y < r->y || x >= r->x + r->width || y >= r->y + r->height);
699 }
700
701 /* avoids linking with the maths library */
xdnd_sqrt(float x)702 static float xdnd_sqrt (float x)
703 {
704 float last_ans, ans = 2, a;
705 if (x <= 0.0)
706 return 0.0;
707 do {
708 last_ans = ans;
709 ans = (ans + x / ans) / 2;
710 a = (ans - last_ans) / ans;
711 if (a < 0.0)
712 a = (-a);
713 } while (a > 0.001);
714 return ans;
715 }
716
717 /* returns action on success, 0 otherwise */
xdnd_drag(DndClass * dnd,Window from,Atom action,Atom * typelist)718 Atom xdnd_drag (DndClass * dnd, Window from, Atom action, Atom * typelist)
719 {
720 XEvent xevent, xevent_temp;
721 Window over_window = 0, last_window = 0;
722 int n;
723 DndCursor *cursor;
724 float x_mouse, y_mouse;
725 int result = 0, dnd_aware;
726
727 if (!typelist)
728 _XmWarning(NULL, "xdnd_drag() called with typelist = 0");
729
730 /* first wait until the mouse moves more than five pixels */
731 do {
732 XNextEvent (dnd->display, &xevent);
733 if (xevent.type == ButtonRelease) {
734 DEBUGOUT(_LtDebug(__FILE__, NULL, "button release - no motion"));
735 XSendEvent (dnd->display, xevent.xany.window, 0, ButtonReleaseMask, &xevent);
736 return 0;
737 }
738 } while (xevent.type != MotionNotify);
739
740 x_mouse = (float) xevent.xmotion.x_root;
741 y_mouse = (float) xevent.xmotion.y_root;
742
743 if (!dnd->drag_threshold)
744 dnd->drag_threshold = 4.0;
745 for (;;) {
746 XNextEvent (dnd->display, &xevent);
747 if (xevent.type == MotionNotify)
748 if (xdnd_sqrt ((x_mouse - xevent.xmotion.x_root) * (x_mouse - xevent.xmotion.x_root) +
749 (y_mouse - xevent.xmotion.y_root) * (y_mouse - xevent.xmotion.y_root)) > dnd->drag_threshold)
750 break;
751 if (xevent.type == ButtonRelease) {
752 XSendEvent (dnd->display, xevent.xany.window, 0, ButtonReleaseMask, &xevent);
753 return 0;
754 }
755 }
756
757 DEBUGOUT(_LtDebug(__FILE__, NULL, "moved 5 pixels - going to drag"));
758
759 n = array_length (typelist);
760 if (n > XDND_THREE)
761 xdnd_set_type_list (dnd, from, typelist);
762
763 xdnd_reset (dnd);
764
765 dnd->stage = XDND_DRAG_STAGE_DRAGGING;
766
767 for (cursor = &dnd->cursors[0]; cursor->width; cursor++)
768 if (cursor->action == action)
769 break;
770 if (!cursor->width)
771 cursor = &dnd->cursors[0];
772
773 /* the mouse has been dragged a little, so this is a drag proper */
774 if (XGrabPointer (dnd->display, dnd->root_window, False,
775 ButtonMotionMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask,
776 GrabModeAsync, GrabModeAsync, None,
777 cursor->cursor, CurrentTime) != GrabSuccess)
778 DEBUGOUT(_LtDebug(__FILE__, NULL, "Unable to grab pointer"));
779
780 while (xevent.xany.type != ButtonRelease) {
781 XAllowEvents (dnd->display, SyncPointer, CurrentTime);
782 XNextEvent (dnd->display, &xevent);
783 switch (xevent.type) {
784 case Expose:
785 if (dnd->handle_expose_events)
786 (*dnd->handle_expose_events) (dnd, &xevent);
787 break;
788 case EnterNotify:
789 /* this event is not actually reported, so we find out by ourselves from motion events */
790 break;
791 case LeaveNotify:
792 /* this event is not actually reported, so we find out by ourselves from motion events */
793 break;
794 case ButtonRelease:
795 /* done, but must send a leave event */
796 DEBUGOUT(_LtDebug(__FILE__, NULL, "ButtonRelease - exiting event loop"));
797 break;
798 case MotionNotify:
799 memcpy (&xevent_temp, &xevent, sizeof (xevent));
800 if (!xevent.xmotion.subwindow) {
801 xevent.xmotion.subwindow = xevent.xmotion.window;
802 } else {
803 Window root_return, child_return;
804 unsigned int mask_return;
805 while (XQueryPointer (dnd->display, xevent.xmotion.subwindow, &root_return, &child_return,
806 &xevent.xmotion.x_root, &xevent.xmotion.y_root, &xevent.xmotion.x,
807 &xevent.xmotion.y, &mask_return)) {
808 if (!child_return)
809 goto found_descendent;
810 xevent.xmotion.subwindow = child_return;
811 }
812 break;
813 }
814 found_descendent:
815 /* last_window is just for debug purposes */
816 if (last_window != xevent.xmotion.subwindow) {
817 DEBUGOUT(_LtDebug(__FILE__, NULL, "window crossing to %ld", xevent.xmotion.subwindow));
818 DEBUGOUT(_LtDebug(__FILE__, NULL, " current window is %ld", over_window));
819 }
820 dnd_aware = 0;
821 /* is the new window dnd aware? if not stay in the old window */
822 if (over_window != xevent.xmotion.subwindow &&
823 last_window != xevent.xmotion.subwindow &&
824 (
825 (dnd_aware = xdnd_is_dnd_aware (dnd, xevent.xmotion.subwindow, &dnd->dragging_version, typelist)) ||
826 (dnd->options & XDND_OPTION_NO_HYSTERESIS)
827 )) {
828 /* leaving window we were over */
829 xevent.xany.window = over_window;
830 if (over_window) {
831 if (dnd->stage == XDND_DRAG_STAGE_ENTERED) {
832 DEBUGOUT(_LtDebug(__FILE__, NULL, "got leave at right stage"));
833 dnd->stage = XDND_DRAG_STAGE_DRAGGING;
834 if (dnd->internal_drag) {
835 DEBUGOUT(_LtDebug(__FILE__, NULL, " our own widget"));
836 if (dnd->widget_apply_leave)
837 (*dnd->widget_apply_leave) (dnd, xevent.xany.window);
838 } else {
839 DEBUGOUT(_LtDebug(__FILE__, NULL, " not our widget - sending XdndLeave"));
840 xdnd_send_leave (dnd, xevent.xany.window, from);
841 }
842 dnd->internal_drag = 0;
843 dnd->dropper_window = 0;
844 dnd->ready_to_drop = 0;
845 } else {
846 DEBUGOUT(_LtDebug(__FILE__, NULL, "got leave at wrong stage - ignoring"));
847 }
848 }
849 /* entering window we are currently over */
850 over_window = xevent.xmotion.subwindow;
851 if (dnd_aware) {
852 DEBUGOUT(_LtDebug(__FILE__, NULL, " is dnd aware"));
853 dnd->stage = XDND_DRAG_STAGE_ENTERED;
854 if (dnd->widget_exists && (*dnd->widget_exists) (dnd, over_window))
855 dnd->internal_drag = 1;
856 if (dnd->internal_drag) {
857 DEBUGOUT(_LtDebug(__FILE__, NULL, " our own widget"));
858 } else {
859 DEBUGOUT(_LtDebug(__FILE__, NULL, " not our widget - sending XdndEnter"));
860 xdnd_send_enter (dnd, over_window, from, typelist);
861 }
862 dnd->want_position = 1;
863 dnd->ready_to_drop = 0;
864 dnd->rectangle.width = dnd->rectangle.height = 0;
865 dnd->dropper_window = over_window;
866 /* we want an additional motion event in case the pointer enters and then stops */
867 XSendEvent (dnd->display, from, 0, ButtonMotionMask, &xevent_temp);
868 XSync (dnd->display, 0);
869 }
870 /* we are now officially in a new window */
871 } else {
872 /* got here, so we are just moving `inside' the same window */
873 if (dnd->stage == XDND_DRAG_STAGE_ENTERED) {
874 dnd->supported_action = dnd->XdndActionCopy;
875 DEBUGOUT(_LtDebug(__FILE__, NULL, "got motion at right stage"));
876 dnd->x = xevent.xmotion.x_root;
877 dnd->y = xevent.xmotion.y_root;
878 if (dnd->want_position && outside_rectangle (dnd->x, dnd->y, &dnd->rectangle)) {
879 DEBUGOUT(_LtDebug(__FILE__, NULL, " want position and outside rectangle"));
880 if (dnd->internal_drag) {
881 DEBUGOUT(_LtDebug(__FILE__, NULL, " our own widget"));
882 dnd->ready_to_drop = (*dnd->widget_apply_position) (dnd, over_window, from,
883 action, dnd->x, dnd->y, xevent.xmotion.time, typelist,
884 &dnd->want_position, &dnd->supported_action, &dnd->desired_type, &dnd->rectangle);
885 /* if not ready, keep sending positions, this check is repeated below for XdndStatus from external widgets */
886 if (!dnd->ready_to_drop) {
887 dnd->want_position = 1;
888 dnd->rectangle.width = dnd->rectangle.height = 0;
889 }
890 DEBUGOUT(_LtDebug(__FILE__, NULL, " return action=%ld", dnd->supported_action));
891 } else {
892 DEBUGOUT(_LtDebug(__FILE__, NULL, " not our own widget - sending XdndPosition"));
893 xdnd_send_position (dnd, over_window, from, action, dnd->x, dnd->y, xevent.xmotion.time);
894 }
895 } else {
896 DEBUGOUT(_LtDebug(__FILE__, NULL, " doesn't want position or inside rectangle"));
897 DEBUGOUT(_LtDebug(__FILE__, NULL, " rectangle = (x=%d, y=%d, w=%d, h=%d), (x,y)=(%d,%d) want_position=%d\n", dnd->rectangle.x, dnd->rectangle.y, dnd->rectangle.width, dnd->rectangle.height, dnd->x, dnd->y, dnd->want_position));
898 }
899 }
900 }
901 last_window = xevent.xmotion.subwindow;
902 break;
903 case ClientMessage:
904 DEBUGOUT(_LtDebug(__FILE__, NULL, "ClientMessage recieved"));
905 if (xevent.xclient.message_type == dnd->XdndStatus && !dnd->internal_drag) {
906 DEBUGOUT(_LtDebug(__FILE__, NULL, " XdndStatus recieved"));
907 if (dnd->stage == XDND_DRAG_STAGE_ENTERED && XDND_STATUS_TARGET_WIN (&xevent) == dnd->dropper_window) {
908 DEBUGOUT(_LtDebug(__FILE__, NULL, " XdndStatus stage correct, dropper window correct"));
909 dnd->want_position = XDND_STATUS_WANT_POSITION (&xevent);
910 dnd->ready_to_drop = XDND_STATUS_WILL_ACCEPT (&xevent);
911 dnd->rectangle.x = XDND_STATUS_RECT_X (&xevent);
912 dnd->rectangle.y = XDND_STATUS_RECT_Y (&xevent);
913 dnd->rectangle.width = XDND_STATUS_RECT_WIDTH (&xevent);
914 dnd->rectangle.height = XDND_STATUS_RECT_HEIGHT (&xevent);
915 dnd->supported_action = dnd->XdndActionCopy;
916 if (dnd_version_at_least (dnd->dragging_version, 2))
917 dnd->supported_action = XDND_STATUS_ACTION (&xevent);
918 DEBUGOUT(_LtDebug(__FILE__, NULL, " return action=%ld", dnd->supported_action));
919 /* if not ready, keep sending positions, this check is repeated above for internal widgets */
920 if (!dnd->ready_to_drop) {
921 dnd->want_position = 1;
922 dnd->rectangle.width = dnd->rectangle.height = 0;
923 }
924 DEBUGOUT(_LtDebug(__FILE__, NULL, " rectangle = (x=%d, y=%d, w=%d, h=%d), want_position=%d\n", dnd->rectangle.x, dnd->rectangle.y, dnd->rectangle.width, dnd->rectangle.height, dnd->want_position));
925 } else {
926 DEBUGOUT(_LtDebug(__FILE__, NULL, " XdndStatus stage incorrect or dropper window incorrect"));
927 }
928 }
929 break;
930 case SelectionRequest:{
931 /* the target widget MAY request data, so wait for SelectionRequest */
932 int length = 0;
933 unsigned char *data = 0;
934 DEBUGOUT(_LtDebug(__FILE__, NULL, "SelectionRequest - getting widget data"));
935
936 (*dnd->widget_get_data) (dnd, from, &data, &length, xevent.xselectionrequest.target);
937 if (data) {
938 DEBUGOUT(_LtDebug(__FILE__, NULL, " sending selection"));
939 xdnd_selection_send (dnd, &xevent.xselectionrequest, data, length);
940 xdnd_xfree (data);
941 }
942 }
943 break;
944 }
945 }
946
947 if (dnd->ready_to_drop) {
948 Time time;
949 DEBUGOUT(_LtDebug(__FILE__, NULL, "ready_to_drop - sending XdndDrop"));
950 time = xevent.xbutton.time;
951 if (dnd->internal_drag) {
952 /* we are dealing with our own widget, no need to send drop events, just put the data straight */
953 int length = 0;
954 unsigned char *data = 0;
955 if (dnd->widget_insert_drop) {
956 (*dnd->widget_get_data) (dnd, from, &data, &length, dnd->desired_type);
957 if (data) {
958 if (!(*dnd->widget_insert_drop) (dnd, data, length, 0, dnd->dropper_window, from, dnd->desired_type)) {
959 result = dnd->supported_action; /* success - so return action to caller */
960 DEBUGOUT(_LtDebug(__FILE__, NULL, " inserted data into widget - success"));
961 } else {
962 DEBUGOUT(_LtDebug(__FILE__, NULL, " inserted data into widget - failed"));
963 }
964 xdnd_xfree (data);
965 } else {
966 DEBUGOUT(_LtDebug(__FILE__, NULL, " got data from widget, but data is null"));
967 }
968 }
969 } else {
970 xdnd_set_selection_owner (dnd, from, dnd->desired_type);
971 xdnd_send_drop (dnd, dnd->dropper_window, from, time);
972 }
973 if (!dnd->internal_drag)
974 for (;;) {
975 XAllowEvents (dnd->display, SyncPointer, CurrentTime);
976 XNextEvent (dnd->display, &xevent);
977 if (xevent.type == ClientMessage && xevent.xclient.message_type == dnd->XdndFinished) {
978 DEBUGOUT(_LtDebug(__FILE__, NULL, "XdndFinished"));
979 if (XDND_FINISHED_TARGET_WIN (&xevent) == dnd->dropper_window) {
980 DEBUGOUT(_LtDebug(__FILE__, NULL, " source correct - exiting event loop, action=%ld", dnd->supported_action));
981 result = dnd->supported_action; /* success - so return action to caller */
982 break;
983 }
984 } else if (xevent.type == Expose) {
985 if (dnd->handle_expose_events)
986 (*dnd->handle_expose_events) (dnd, &xevent);
987 } else if (xevent.type == MotionNotify) {
988 if (xevent.xmotion.time > time + (dnd->time_out ? dnd->time_out * 1000 : 10000)) { /* allow a ten second timeout as default */
989 DEBUGOUT(_LtDebug(__FILE__, NULL, "timeout - exiting event loop"));
990 break;
991 }
992 } else if (xevent.type == SelectionRequest && xevent.xselectionrequest.selection == dnd->XdndSelection) {
993 /* the target widget is going to request data, so check for SelectionRequest events */
994 int length = 0;
995 unsigned char *data = 0;
996
997 DEBUGOUT(_LtDebug(__FILE__, NULL, "SelectionRequest - getting widget data"));
998 (*dnd->widget_get_data) (dnd, from, &data, &length, xevent.xselectionrequest.target);
999 if (data) {
1000 DEBUGOUT(_LtDebug(__FILE__, NULL, " sending selection"));
1001 xdnd_selection_send (dnd, &xevent.xselectionrequest, data, length);
1002 xdnd_xfree (data);
1003 }
1004 /* don't wait for a XdndFinished event */
1005 if (!dnd_version_at_least (dnd->dragging_version, 2))
1006 break;
1007 }
1008 }
1009 } else {
1010 DEBUGOUT(_LtDebug(__FILE__, NULL, "not ready_to_drop - ungrabbing pointer"));
1011 }
1012 XUngrabPointer (dnd->display, CurrentTime);
1013 xdnd_reset (dnd);
1014 return result;
1015 }
1016
1017 /* returns non-zero if event is handled */
xdnd_handle_drop_events(DndClass * dnd,XEvent * xevent)1018 int xdnd_handle_drop_events (DndClass * dnd, XEvent * xevent)
1019 {
1020 int result = 0;
1021 if (xevent->type == SelectionNotify) {
1022 DEBUGOUT(_LtDebug(__FILE__, NULL, "got SelectionNotify"));
1023 if (xevent->xselection.property == dnd->Xdnd_NON_PROTOCOL_ATOM && dnd->stage == XDND_DROP_STAGE_CONVERTING) {
1024 int error;
1025 DEBUGOUT(_LtDebug(__FILE__, NULL, " property is Xdnd_NON_PROTOCOL_ATOM - getting selection"));
1026 error = xdnd_get_selection (dnd, dnd->dragger_window, xevent->xselection.property, xevent->xany.window);
1027 /* error is not actually used, i think future versions of the protocol maybe should return
1028 an error status to the calling window with the XdndFinished client message */
1029 if (dnd_version_at_least (dnd->dragging_version, 2))
1030 xdnd_send_finished (dnd, dnd->dragger_window, dnd->dropper_window, error);
1031 xdnd_xfree (dnd->dragger_typelist);
1032 xdnd_reset (dnd);
1033 dnd->stage = XDND_DROP_STAGE_IDLE;
1034 result = 1;
1035 } else {
1036 DEBUGOUT(_LtDebug(__FILE__, NULL, " property is not Xdnd_NON_PROTOCOL_ATOM - ignoring"));
1037 }
1038 } else if (xevent->type == ClientMessage) {
1039 DEBUGOUT(_LtDebug(__FILE__, NULL, "got ClientMessage"));
1040 if (xevent->xclient.message_type == dnd->XdndEnter) {
1041 DEBUGOUT(_LtDebug(__FILE__, NULL, " message_type is XdndEnter"));
1042 xdnd_reset (dnd);
1043 dnd->dragger_window = XDND_ENTER_SOURCE_WIN (xevent);
1044 dnd->dropper_window = xevent->xany.window;
1045 xdnd_xfree (dnd->dragger_typelist);
1046 if (XDND_ENTER_THREE_TYPES (xevent)) {
1047 DEBUGOUT(_LtDebug(__FILE__, NULL, " three types only"));
1048 xdnd_get_three_types (dnd, xevent, &dnd->dragger_typelist);
1049 } else {
1050 DEBUGOUT(_LtDebug(__FILE__, NULL, " more than three types - getting list"));
1051 xdnd_get_type_list (dnd, dnd->dragger_window, &dnd->dragger_typelist);
1052 }
1053 if (dnd->dragger_typelist)
1054 dnd->stage = XDND_DROP_STAGE_ENTERED;
1055 else
1056 DEBUGOUT(_LtDebug(__FILE__, NULL, " typelist returned as zero!"));
1057 dnd->dragging_version = XDND_ENTER_VERSION (xevent);
1058 result = 1;
1059 } else if (xevent->xclient.message_type == dnd->XdndLeave) {
1060 DEBUGOUT(_LtDebug(__FILE__, NULL, " message_type is XdndLeave"));
1061 if (dnd->dragger_window == XDND_LEAVE_SOURCE_WIN (xevent) && dnd->stage == XDND_DROP_STAGE_ENTERED) {
1062 DEBUGOUT(_LtDebug(__FILE__, NULL, " leaving"));
1063 if (dnd->widget_apply_leave)
1064 (*dnd->widget_apply_leave) (dnd, xevent->xany.window);
1065 dnd->stage = XDND_DROP_STAGE_IDLE;
1066 xdnd_xfree (dnd->dragger_typelist);
1067 result = 1;
1068 } else {
1069 DEBUGOUT(_LtDebug(__FILE__, NULL, " wrong stage or from wrong window"));
1070 }
1071 } else if (xevent->xclient.message_type == dnd->XdndPosition) {
1072 DEBUGOUT(_LtDebug(__FILE__, NULL, " message_type is XdndPosition"));
1073 if (dnd->dragger_window == XDND_POSITION_SOURCE_WIN (xevent) && dnd->stage == XDND_DROP_STAGE_ENTERED) {
1074 int want_position;
1075 Atom action;
1076 XRectangle rectangle;
1077 action = dnd->XdndActionCopy;
1078 dnd->supported_action = dnd->XdndActionCopy;
1079 dnd->x = XDND_POSITION_ROOT_X (xevent);
1080 dnd->y = XDND_POSITION_ROOT_Y (xevent);
1081 dnd->time = CurrentTime;
1082 if (dnd_version_at_least (dnd->dragging_version, 1))
1083 dnd->time = XDND_POSITION_TIME (xevent);
1084 if (dnd_version_at_least (dnd->dragging_version, 1))
1085 action = XDND_POSITION_ACTION (xevent);
1086 dnd->will_accept = (*dnd->widget_apply_position) (dnd, xevent->xany.window, dnd->dragger_window,
1087 action, dnd->x, dnd->y, dnd->time, dnd->dragger_typelist,
1088 &want_position, &dnd->supported_action, &dnd->desired_type, &rectangle);
1089 DEBUGOUT(_LtDebug(__FILE__, NULL, " will accept = %d", dnd->will_accept));
1090 DEBUGOUT(_LtDebug(__FILE__, NULL, " sending status"));
1091 DEBUGOUT(_LtDebug(__FILE__, NULL, " action=%ld, rectangle = (x=%d, y=%d, w=%d, h=%d), (x,y)=(%d,%d) want_position=%d\n", dnd->supported_action, rectangle.x, rectangle.y, rectangle.width, rectangle.height, dnd->x, dnd->y, want_position));
1092 xdnd_send_status (dnd, dnd->dragger_window, xevent->xany.window, dnd->will_accept,
1093 want_position, rectangle.x, rectangle.y, rectangle.width, rectangle.height, dnd->supported_action);
1094 result = 1;
1095 } else {
1096 DEBUGOUT(_LtDebug(__FILE__, NULL, " wrong stage or from wrong window"));
1097 }
1098 } else if (xevent->xclient.message_type == dnd->XdndDrop) {
1099 DEBUGOUT(_LtDebug(__FILE__, NULL, " message_type is XdndDrop"));
1100 if (dnd->dragger_window == XDND_DROP_SOURCE_WIN (xevent) && dnd->stage == XDND_DROP_STAGE_ENTERED) {
1101 dnd->time = CurrentTime;
1102 if (dnd_version_at_least (dnd->dragging_version, 1))
1103 dnd->time = XDND_DROP_TIME (xevent);
1104 if (dnd->will_accept) {
1105 DEBUGOUT(_LtDebug(__FILE__, NULL, " will_accept is true - converting selectiong"));
1106 DEBUGOUT(_LtDebug(__FILE__, NULL, " my window is %ld", dnd->dropper_window));
1107 DEBUGOUT(_LtDebug(__FILE__, NULL, " source window is %ld", dnd->dragger_window));
1108 xdnd_convert_selection (dnd, dnd->dragger_window, dnd->dropper_window, dnd->desired_type);
1109 dnd->stage = XDND_DROP_STAGE_CONVERTING;
1110 } else {
1111 DEBUGOUT(_LtDebug(__FILE__, NULL, " will_accept is false - sending finished"));
1112 if (dnd_version_at_least (dnd->dragging_version, 1))
1113 xdnd_send_finished (dnd, dnd->dragger_window, xevent->xany.window, 1);
1114 xdnd_xfree (dnd->dragger_typelist);
1115 xdnd_reset (dnd);
1116 dnd->stage = XDND_DROP_STAGE_IDLE;
1117 }
1118 result = 1;
1119 } else {
1120 DEBUGOUT(_LtDebug(__FILE__, NULL, " wrong stage or from wrong window"));
1121 }
1122 }
1123 }
1124 return result;
1125 }
1126
1127 /*
1128 Following here is a sample implementation: Suppose we want a window
1129 to recieve drops, but do not want to be concerned with setting up all
1130 the DndClass methods. All we then do is call xdnd_get_drop() whenever a
1131 ClientMessage is recieved. If the message has nothing to do with XDND,
1132 xdnd_get_drop quickly returns 0. If it is a XdndEnter message, then
1133 xdnd_get_drop enters its own XNextEvent loop and handles all XDND
1134 protocol messages internally, returning the action requested.
1135
1136 You should pass a desired typelist and actionlist to xdnd_get_type.
1137 These must be null terminated arrays of atoms, or a null pointer
1138 if you would like any action or type to be accepted. If typelist
1139 is null then the first type of the dragging widgets typelist will
1140 be the one used. If actionlist is null, then only XdndActionCopy will
1141 be accepted.
1142
1143 The result is stored in *data, length, type, x and y.
1144 *data must be free'd.
1145 */
1146
1147 struct xdnd_get_drop_info {
1148 unsigned char *drop_data;
1149 int drop_data_length;
1150 int x, y;
1151 Atom return_type;
1152 Atom return_action;
1153 Atom *typelist;
1154 Atom *actionlist;
1155 };
1156
widget_insert_drop(DndClass * dnd,unsigned char * data,int length,int remaining,Window into,Window from,Atom type)1157 static int widget_insert_drop (DndClass * dnd, unsigned char *data, int length, int remaining, Window into, Window from, Atom type)
1158 {
1159 struct xdnd_get_drop_info *i;
1160 i = (struct xdnd_get_drop_info *) dnd->user_hook1;
1161 if (!i->drop_data) {
1162 i->drop_data = malloc (length);
1163 if (!i->drop_data)
1164 return 1;
1165 memcpy (i->drop_data, data, length);
1166 i->drop_data_length = length;
1167 } else {
1168 unsigned char *t;
1169 t = malloc (i->drop_data_length + length);
1170 if (!t) {
1171 free (i->drop_data);
1172 i->drop_data = 0;
1173 return 1;
1174 }
1175 memcpy (t, i->drop_data, i->drop_data_length);
1176 memcpy (t + i->drop_data_length, data, length);
1177 free (i->drop_data);
1178 i->drop_data = t;
1179 i->drop_data_length += length;
1180 }
1181 return 0;
1182 }
1183
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)1184 static int widget_apply_position (DndClass * dnd, Window widgets_window, Window from,
1185 Atom action, int x, int y, Time t, Atom * typelist,
1186 int *want_position, Atom * supported_action_return, Atom * desired_type,
1187 XRectangle * rectangle)
1188 {
1189 int i, j;
1190 struct xdnd_get_drop_info *info;
1191 Atom *dropper_typelist, supported_type = 0;
1192 Atom *supported_actions, supported_action = 0;
1193
1194 info = (struct xdnd_get_drop_info *) dnd->user_hook1;
1195 dropper_typelist = info->typelist;
1196 supported_actions = info->actionlist;
1197
1198 if (dropper_typelist) {
1199 /* find a correlation: */
1200 for (j = 0; dropper_typelist[j]; j++) {
1201 for (i = 0; typelist[i]; i++) {
1202 if (typelist[i] == dropper_typelist[j]) {
1203 supported_type = typelist[i];
1204 break;
1205 }
1206 }
1207 if (supported_type)
1208 break;
1209 }
1210 } else {
1211 /* user did not specify, so return first type */
1212 supported_type = typelist[0];
1213 }
1214 /* not supported, so return false */
1215 if (!supported_type)
1216 return 0;
1217
1218 if (supported_actions) {
1219 for (j = 0; supported_actions[j]; j++) {
1220 if (action == supported_actions[j]) {
1221 supported_action = action;
1222 break;
1223 }
1224 }
1225 } else {
1226 /* user did not specify */
1227 if (action == dnd->XdndActionCopy)
1228 supported_action = action;
1229 }
1230 if (!supported_action)
1231 return 0;
1232
1233 *want_position = 1;
1234 rectangle->x = rectangle->y = 0;
1235 rectangle->width = rectangle->height = 0;
1236
1237 info->return_action = *supported_action_return = supported_action;
1238 info->return_type = *desired_type = supported_type;
1239 info->x = x;
1240 info->y = y;
1241
1242 return 1;
1243 }
1244
xdnd_get_drop(Display * display,XEvent * xevent,Atom * typelist,Atom * actionlist,unsigned char ** data,int * length,Atom * type,int * x,int * y)1245 Atom xdnd_get_drop (Display * display, XEvent * xevent, Atom * typelist, Atom * actionlist,
1246 unsigned char **data, int *length, Atom * type, int *x, int *y)
1247 {
1248 Atom action = 0;
1249 static int initialised = 0;
1250 static DndClass dnd;
1251 if (!initialised) {
1252 xdnd_init (&dnd, display);
1253 initialised = 1;
1254 }
1255 if (xevent->type != ClientMessage || xevent->xclient.message_type != dnd.XdndEnter) {
1256 return 0;
1257 } else {
1258 struct xdnd_get_drop_info i;
1259
1260 /* setup user structure */
1261 memset (&i, 0, sizeof (i));
1262 i.typelist = actionlist;
1263 i.typelist = typelist;
1264 dnd.user_hook1 = &i;
1265
1266 /* setup methods */
1267 dnd.widget_insert_drop = widget_insert_drop;
1268 dnd.widget_apply_position = widget_apply_position;
1269
1270 /* main loop */
1271 for (;;) {
1272 xdnd_handle_drop_events (&dnd, xevent);
1273 if (dnd.stage == XDND_DROP_STAGE_IDLE)
1274 break;
1275 XNextEvent (dnd.display, xevent);
1276 }
1277
1278 /* return results */
1279 if (i.drop_data) {
1280 *length = i.drop_data_length;
1281 *data = i.drop_data;
1282 action = i.return_action;
1283 *type = i.return_type;
1284 *x = i.x;
1285 *y = i.y;
1286 }
1287 }
1288 return action;
1289 }
1290
1291
1292