1
/* xdnd.c, xdnd.h - C program library for handling the Xdnd protocol
2
Copyright (C) 1996-2000 Paul Sheer
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.
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.
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
25
2000-08-08: INCR protocol implemented.
31
- INCR protocol now implemented
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
41
#include <X11/Xatom.h>
46
#if TIME_WITH_SYS_TIME
47
# include <sys/time.h>
51
# include <sys/time.h>
56
#include <sys/types.h>
61
#ifdef HAVE_SYS_SELECT_H
62
# include <sys/select.h>
67
static void xdnd_send_enter (DndClass * dnd, Window window, Window from, Atom * typelist);
68
static void xdnd_send_position (DndClass * dnd, Window window, Window from, Atom action, int x, int y,
70
static void xdnd_send_status (DndClass * dnd, Window window, Window from, int will_accept, int want_position,
71
int x, int y, int w, int h, Atom action);
72
static void xdnd_send_leave (DndClass * dnd, Window window, Window from);
73
static void xdnd_send_drop (DndClass * dnd, Window window, Window from, unsigned long etime);
74
static void xdnd_send_finished (DndClass * dnd, Window window, Window from, int error);
75
static int xdnd_convert_selection (DndClass * dnd, Window window, Window requester, Atom type);
76
static void xdnd_selection_send (DndClass * dnd, XSelectionRequestEvent * request, unsigned char *data,
78
static int xdnd_get_selection (DndClass * dnd, Window from, Atom property, Window insert);
81
/* just to remind us : */
97
} XClientMessageEvent;
98
XClientMessageEvent xclient;
101
/* #define DND_DEBUG */
103
#define xdnd_xfree(x) {if (x) { free (x); x = 0; }}
107
#include <sys/time.h>
110
char *xdnd_debug_milliseconds (void)
114
gettimeofday (&tv, 0);
115
sprintf (r, "%.2ld.%.3ld", tv.tv_sec % 100L, tv.tv_usec / 1000L);
119
#define dnd_debug1(a) printf("%s: %d: %s: " a "\n", __FILE__, __LINE__, xdnd_debug_milliseconds ())
120
#define dnd_debug2(a,b) printf("%s: %d: %s: " a "\n", __FILE__, __LINE__, xdnd_debug_milliseconds (), b)
121
#define dnd_debug3(a,b,c) printf("%s: %d: %s: " a "\n", __FILE__, __LINE__, xdnd_debug_milliseconds (), b, c)
122
#define dnd_debug4(a,b,c,d) printf("%s: %d: %s: " a "\n", __FILE__, __LINE__, xdnd_debug_milliseconds (), b, c, d)
124
#define dnd_debug1(a)
125
#define dnd_debug2(a,b)
126
#define dnd_debug3(a,b,c)
127
#define dnd_debug4(a,b,c,d)
130
#define dnd_warning(a) fprintf (stderr, a)
132
#define dnd_version_at_least(a,b) ((a) >= (b))
134
static unsigned char dnd_copy_cursor_bits[] =
136
0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x0f, 0x00, 0x02, 0x00, 0x08, 0x01,
137
0x02, 0x00, 0x08, 0x01, 0x02, 0x00, 0x08, 0x01, 0x02, 0x00, 0xe8, 0x0f,
138
0x02, 0x00, 0x08, 0x01, 0x02, 0x00, 0x08, 0x01, 0x02, 0x00, 0x08, 0x01,
139
0x02, 0x00, 0x08, 0x00, 0x02, 0x04, 0x08, 0x00, 0x02, 0x0c, 0x08, 0x00,
140
0x02, 0x1c, 0x08, 0x00, 0x02, 0x3c, 0x08, 0x00, 0x02, 0x7c, 0x08, 0x00,
141
0x02, 0xfc, 0x08, 0x00, 0x02, 0xfc, 0x09, 0x00, 0x02, 0xfc, 0x0b, 0x00,
142
0x02, 0x7c, 0x08, 0x00, 0xfe, 0x6d, 0x0f, 0x00, 0x00, 0xc4, 0x00, 0x00,
143
0x00, 0xc0, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00,
144
0x00, 0x00, 0x00, 0x00};
146
static unsigned char dnd_copy_mask_bits[] =
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};
158
static unsigned char dnd_move_cursor_bits[] =
160
0x00, 0x00, 0x00, 0xfe, 0xff, 0x0f, 0x02, 0x00, 0x08, 0x02, 0x00, 0x08,
161
0x02, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x00, 0x08,
162
0x02, 0x00, 0x08, 0x02, 0x00, 0x08, 0x02, 0x04, 0x08, 0x02, 0x0c, 0x08,
163
0x02, 0x1c, 0x08, 0x02, 0x3c, 0x08, 0x02, 0x7c, 0x08, 0x02, 0xfc, 0x08,
164
0x02, 0xfc, 0x09, 0x02, 0xfc, 0x0b, 0x02, 0x7c, 0x08, 0xfe, 0x6d, 0x0f,
165
0x00, 0xc4, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x80, 0x01, 0x00, 0x80, 0x01,
168
static unsigned char dnd_move_mask_bits[] =
170
0xff, 0xff, 0x1f, 0xff, 0xff, 0x1f, 0xff, 0xff, 0x1f, 0x07, 0x00, 0x1c,
171
0x07, 0x00, 0x1c, 0x07, 0x00, 0x1c, 0x07, 0x00, 0x1c, 0x07, 0x00, 0x1c,
172
0x07, 0x00, 0x1c, 0x07, 0x06, 0x1c, 0x07, 0x0e, 0x1c, 0x07, 0x1e, 0x1c,
173
0x07, 0x3e, 0x1c, 0x07, 0x7e, 0x1c, 0x07, 0xfe, 0x1c, 0x07, 0xfe, 0x1d,
174
0x07, 0xfe, 0x1f, 0x07, 0xfe, 0x1f, 0xff, 0xff, 0x1f, 0xff, 0xff, 0x1e,
175
0xff, 0xef, 0x1f, 0x00, 0xe6, 0x01, 0x00, 0xc0, 0x03, 0x00, 0xc0, 0x03,
178
static unsigned char dnd_link_cursor_bits[] =
180
0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x0f, 0x00, 0x02, 0x00, 0x08, 0x01,
181
0x02, 0x00, 0x88, 0x00, 0x02, 0x00, 0x48, 0x00, 0x02, 0x00, 0xe8, 0x0f,
182
0x02, 0x00, 0x48, 0x00, 0x02, 0x00, 0x88, 0x00, 0x02, 0x00, 0x08, 0x01,
183
0x02, 0x00, 0x08, 0x00, 0x02, 0x04, 0x08, 0x00, 0x02, 0x0c, 0x08, 0x00,
184
0x02, 0x1c, 0x08, 0x00, 0x02, 0x3c, 0x08, 0x00, 0x02, 0x7c, 0x08, 0x00,
185
0x02, 0xfc, 0x08, 0x00, 0x02, 0xfc, 0x09, 0x00, 0x02, 0xfc, 0x0b, 0x00,
186
0x02, 0x7c, 0x08, 0x00, 0xfe, 0x6d, 0x0f, 0x00, 0x00, 0xc4, 0x00, 0x00,
187
0x00, 0xc0, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00,
188
0x00, 0x00, 0x00, 0x00};
190
static unsigned char dnd_link_mask_bits[] =
192
0xff, 0xff, 0x1f, 0x00, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xff, 0x1f,
193
0x07, 0x00, 0xfc, 0x1f, 0x07, 0x00, 0xfc, 0x1f, 0x07, 0x00, 0xfc, 0x1f,
194
0x07, 0x00, 0xfc, 0x1f, 0x07, 0x00, 0xfc, 0x1f, 0x07, 0x00, 0xfc, 0x1f,
195
0x07, 0x06, 0xfc, 0x1f, 0x07, 0x0e, 0xfc, 0x1f, 0x07, 0x1e, 0x1c, 0x00,
196
0x07, 0x3e, 0x1c, 0x00, 0x07, 0x7e, 0x1c, 0x00, 0x07, 0xfe, 0x1c, 0x00,
197
0x07, 0xfe, 0x1d, 0x00, 0x07, 0xfe, 0x1f, 0x00, 0x07, 0xfe, 0x1f, 0x00,
198
0xff, 0xff, 0x1f, 0x00, 0xff, 0xff, 0x1e, 0x00, 0xff, 0xef, 0x1f, 0x00,
199
0x00, 0xe6, 0x01, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0xc0, 0x03, 0x00,
200
0x00, 0x80, 0x01, 0x00};
202
static unsigned char dnd_ask_cursor_bits[] =
204
0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x0f, 0x00, 0x02, 0x00, 0x88, 0x03,
205
0x02, 0x00, 0x48, 0x04, 0x02, 0x00, 0x08, 0x04, 0x02, 0x00, 0x08, 0x02,
206
0x02, 0x00, 0x08, 0x01, 0x02, 0x00, 0x08, 0x01, 0x02, 0x00, 0x08, 0x00,
207
0x02, 0x00, 0x08, 0x01, 0x02, 0x04, 0x08, 0x00, 0x02, 0x0c, 0x08, 0x00,
208
0x02, 0x1c, 0x08, 0x00, 0x02, 0x3c, 0x08, 0x00, 0x02, 0x7c, 0x08, 0x00,
209
0x02, 0xfc, 0x08, 0x00, 0x02, 0xfc, 0x09, 0x00, 0x02, 0xfc, 0x0b, 0x00,
210
0x02, 0x7c, 0x08, 0x00, 0xfe, 0x6d, 0x0f, 0x00, 0x00, 0xc4, 0x00, 0x00,
211
0x00, 0xc0, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00,
212
0x00, 0x00, 0x00, 0x00};
214
static unsigned char dnd_ask_mask_bits[] =
216
0xff, 0xff, 0x1f, 0x00, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xff, 0x1f,
217
0x07, 0x00, 0xfc, 0x1f, 0x07, 0x00, 0xfc, 0x1f, 0x07, 0x00, 0xfc, 0x1f,
218
0x07, 0x00, 0xfc, 0x1f, 0x07, 0x00, 0xfc, 0x1f, 0x07, 0x00, 0xfc, 0x1f,
219
0x07, 0x06, 0xfc, 0x1f, 0x07, 0x0e, 0xfc, 0x1f, 0x07, 0x1e, 0x1c, 0x00,
220
0x07, 0x3e, 0x1c, 0x00, 0x07, 0x7e, 0x1c, 0x00, 0x07, 0xfe, 0x1c, 0x00,
221
0x07, 0xfe, 0x1d, 0x00, 0x07, 0xfe, 0x1f, 0x00, 0x07, 0xfe, 0x1f, 0x00,
222
0xff, 0xff, 0x1f, 0x00, 0xff, 0xff, 0x1e, 0x00, 0xff, 0xef, 0x1f, 0x00,
223
0x00, 0xe6, 0x01, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0xc0, 0x03, 0x00,
224
0x00, 0x80, 0x01, 0x00};
226
static DndCursor dnd_cursors[] =
228
{29, 25, 10, 10, dnd_copy_cursor_bits, dnd_copy_mask_bits, "XdndActionCopy", 0, 0, 0, 0},
229
{21, 25, 10, 10, dnd_move_cursor_bits, dnd_move_mask_bits, "XdndActionMove", 0, 0, 0, 0},
230
{29, 25, 10, 10, dnd_link_cursor_bits, dnd_link_mask_bits, "XdndActionLink", 0, 0, 0, 0},
231
{29, 25, 10, 10, dnd_ask_cursor_bits, dnd_ask_mask_bits, "XdndActionAsk", 0, 0, 0, 0},
232
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
235
void xdnd_reset (DndClass * dnd)
237
dnd->stage = XDND_DROP_STAGE_IDLE;
238
dnd->dragging_version = 0;
239
dnd->internal_drag = 0;
240
dnd->want_position = 0;
241
dnd->ready_to_drop = 0;
242
dnd->will_accept = 0;
243
dnd->rectangle.x = dnd->rectangle.y = 0;
244
dnd->rectangle.width = dnd->rectangle.height = 0;
245
dnd->dropper_window = 0;
246
dnd->dropper_toplevel = 0;
247
dnd->dragger_window = 0;
248
dnd->dragger_typelist = 0;
249
dnd->desired_type = 0;
253
void xdnd_init (DndClass * dnd, Display * display)
257
memset (dnd, 0, sizeof (*dnd));
259
dnd->display = display;
260
dnd->root_window = DefaultRootWindow (display);
261
dnd->version = XDND_VERSION;
263
dnd->XdndAware = XInternAtom (dnd->display, "XdndAware", False);
264
dnd->XdndSelection = XInternAtom (dnd->display, "XdndSelection", False);
265
dnd->XdndEnter = XInternAtom (dnd->display, "XdndEnter", False);
266
dnd->XdndLeave = XInternAtom (dnd->display, "XdndLeave", False);
267
dnd->XdndPosition = XInternAtom (dnd->display, "XdndPosition", False);
268
dnd->XdndDrop = XInternAtom (dnd->display, "XdndDrop", False);
269
dnd->XdndFinished = XInternAtom (dnd->display, "XdndFinished", False);
270
dnd->XdndStatus = XInternAtom (dnd->display, "XdndStatus", False);
271
dnd->XdndActionCopy = XInternAtom (dnd->display, "XdndActionCopy", False);
272
dnd->XdndActionMove = XInternAtom (dnd->display, "XdndActionMove", False);
273
dnd->XdndActionLink = XInternAtom (dnd->display, "XdndActionLink", False);
274
dnd->XdndActionAsk = XInternAtom (dnd->display, "XdndActionAsk", False);
275
dnd->XdndActionPrivate = XInternAtom (dnd->display, "XdndActionPrivate", False);
276
dnd->XdndTypeList = XInternAtom (dnd->display, "XdndTypeList", False);
277
dnd->XdndActionList = XInternAtom (dnd->display, "XdndActionList", False);
278
dnd->XdndActionDescription = XInternAtom (dnd->display, "XdndActionDescription", False);
280
dnd->Xdnd_NON_PROTOCOL_ATOM = XInternAtom (dnd->display, "JXSelectionWindowProperty", False);
284
dnd->cursors = dnd_cursors;
286
black.pixel = BlackPixel (dnd->display, DefaultScreen (dnd->display));
287
white.pixel = WhitePixel (dnd->display, DefaultScreen (dnd->display));
289
XQueryColor (dnd->display, DefaultColormap (dnd->display, DefaultScreen (dnd->display)), &black);
290
XQueryColor (dnd->display, DefaultColormap (dnd->display, DefaultScreen (dnd->display)), &white);
292
for (cursor = &dnd->cursors[0]; cursor->width; cursor++) {
293
cursor->image_pixmap = XCreateBitmapFromData \
294
(dnd->display, dnd->root_window, (char *) cursor->image_data, cursor->width, cursor->height);
295
cursor->mask_pixmap = XCreateBitmapFromData \
296
(dnd->display, dnd->root_window, (char *) cursor->mask_data, cursor->width, cursor->height);
297
cursor->cursor = XCreatePixmapCursor (dnd->display, cursor->image_pixmap,
298
cursor->mask_pixmap, &black, &white, cursor->x, cursor->y);
299
XFreePixmap (dnd->display, cursor->image_pixmap);
300
XFreePixmap (dnd->display, cursor->mask_pixmap);
301
cursor->action = XInternAtom (dnd->display, cursor->_action, False);
305
void xdnd_shut (DndClass * dnd)
308
for (cursor = &dnd->cursors[0]; cursor->width; cursor++)
309
XFreeCursor (dnd->display, cursor->cursor);
310
memset (dnd, 0, sizeof (*dnd));
315
/* typelist is a null terminated array */
316
static int array_length (Atom * a)
319
for (n = 0; a[n]; n++);
323
void xdnd_set_dnd_aware (DndClass * dnd, Window window, Atom * typelist)
325
Window root_return, parent;
326
unsigned int nchildren_return;
327
Window *children_return = 0;
330
if (dnd->widget_exists)
331
if (!(*dnd->widget_exists) (dnd, window))
333
s = XChangeProperty (dnd->display, window, dnd->XdndAware, XA_ATOM, 32, PropModeReplace,
334
(unsigned char *) &dnd->version, 1);
336
dnd_debug4 ("XChangeProperty() = %d, window = %ld, widget = %s", s, window, "<WIDGET>");
340
n = array_length (typelist);
342
s = XChangeProperty (dnd->display, window, dnd->XdndAware, XA_ATOM, 32, PropModeAppend,
343
(unsigned char *) typelist, n);
346
XQueryTree (dnd->display, window, &root_return, &parent, &children_return,
349
XFree (children_return);
351
xdnd_set_dnd_aware (dnd, parent, typelist);
354
int xdnd_is_dnd_aware (DndClass * dnd, Window window, int *version, Atom * typelist)
358
unsigned long count, remaining;
359
unsigned char *data = 0;
364
XGetWindowProperty (dnd->display, window, dnd->XdndAware,
365
0, 0x8000000L, False, XA_ATOM,
367
&count, &remaining, &data);
369
if (actual != XA_ATOM || format != 32 || count == 0 || !data) {
370
dnd_debug2 ("XGetWindowProperty failed in xdnd_is_dnd_aware - XdndAware = %ld", dnd->XdndAware);
375
types = (Atom *) data;
376
#if XDND_VERSION >= 3
383
*version = dnd->version < types[0] ? dnd->version : types[0]; /* minimum */
384
dnd_debug2 ("Using XDND version %d", *version);
387
for (t = typelist; *t; t++) {
389
for (j = 1; j < count; j++) {
390
if (types[j] == *t) {
403
void xdnd_set_type_list (DndClass * dnd, Window window, Atom * typelist)
406
n = array_length (typelist);
407
XChangeProperty (dnd->display, window, dnd->XdndTypeList, XA_ATOM, 32,
408
PropModeReplace, (unsigned char *) typelist, n);
411
/* result must be free'd */
412
void xdnd_get_type_list (DndClass * dnd, Window window, Atom ** typelist)
416
unsigned long count, remaining;
417
unsigned char *data = NULL;
421
XGetWindowProperty (dnd->display, window, dnd->XdndTypeList,
422
0, 0x8000000L, False, XA_ATOM,
423
&type, &format, &count, &remaining, &data);
425
if (type != XA_ATOM || format != 32 || count == 0 || !data) {
428
dnd_debug2 ("XGetWindowProperty failed in xdnd_get_type_list - dnd->XdndTypeList = %ld", dnd->XdndTypeList);
431
*typelist = malloc ((count + 1) * sizeof (Atom));
433
for (i = 0; i < count; i++)
434
(*typelist)[i] = a[i];
435
(*typelist)[count] = 0;
440
void xdnd_get_three_types (DndClass * dnd, XEvent * xevent, Atom ** typelist)
443
*typelist = malloc ((XDND_THREE + 1) * sizeof (Atom));
444
for (i = 0; i < XDND_THREE; i++)
445
(*typelist)[i] = XDND_ENTER_TYPE (xevent, i);
446
(*typelist)[XDND_THREE] = 0; /* although (*typelist)[1] or (*typelist)[2] may also be set to nill */
449
/* result must be free'd */
450
static char *concat_string_list (char **t, int *bytes)
454
for (l = n = 0;; n++) {
459
l += strlen (t[n]) + 1;
462
for (l = n = 0;; n++) {
467
strcpy (s + l, t[n]);
468
l += strlen (t[n]) + 1;
475
void xdnd_set_actions (DndClass * dnd, Window window, Atom * actions, char **descriptions)
479
n = array_length (actions);
481
XChangeProperty (dnd->display, window, dnd->XdndActionList, XA_ATOM, 32,
482
PropModeReplace, (unsigned char *) actions, n);
484
s = concat_string_list (descriptions, &l);
485
XChangeProperty (dnd->display, window, dnd->XdndActionList, XA_STRING, 8,
486
PropModeReplace, (unsigned char *) s, l);
490
/* returns 1 on error or no actions, otherwise result must be free'd
491
xdnd_get_actions (window, &actions, &descriptions);
492
free (actions); free (descriptions); */
493
int xdnd_get_actions (DndClass * dnd, Window window, Atom ** actions, char ***descriptions)
497
unsigned long count, dcount, remaining;
498
unsigned char *data = 0, *r;
502
XGetWindowProperty (dnd->display, window, dnd->XdndActionList,
503
0, 0x8000000L, False, XA_ATOM,
504
&type, &format, &count, &remaining, &data);
506
if (type != XA_ATOM || format != 32 || count == 0 || !data) {
511
*actions = malloc ((count + 1) * sizeof (Atom));
513
for (i = 0; i < count; i++)
514
(*actions)[i] = a[i];
515
(*actions)[count] = 0;
520
XGetWindowProperty (dnd->display, window, dnd->XdndActionDescription,
521
0, 0x8000000L, False, XA_STRING, &type, &format,
522
&dcount, &remaining, &data);
524
if (type != XA_STRING || format != 8 || dcount == 0) {
527
*descriptions = malloc ((count + 1) * sizeof (char *));
528
dnd_warning ("XGetWindowProperty no property or wrong format for action descriptions");
529
for (i = 0; i < count; i++)
530
(*descriptions)[i] = "";
531
(*descriptions)[count] = 0;
534
l = (count + 1) * sizeof (char *);
535
*descriptions = malloc (l + dcount);
536
memcpy (*descriptions + l, data, dcount);
538
data = (unsigned char *) *descriptions;
541
for (i = 0, r = data;; r += l + 1, i++) {
542
l = strlen ((char *) r);
543
if (!l || i >= count)
545
(*descriptions)[i] = (char *) r;
547
for (; i < count; i++) {
548
(*descriptions)[i] = "";
550
(*descriptions)[count] = 0;
555
/* returns non-zero on cancel */
556
int xdnd_choose_action_dialog (DndClass * dnd, Atom * actions, char **descriptions, Atom * result)
560
if (!dnd->action_choose_dialog) { /* default to return the first action if no dialog set */
561
*result = actions[0];
564
return (*dnd->action_choose_dialog) (dnd, descriptions, actions, result);
567
static void xdnd_send_event (DndClass * dnd, Window window, XEvent * xevent)
569
dnd_debug4 ("xdnd_send_event(), window = %ld, l[0] = %ld, l[4] = %ld",
570
window, xevent->xclient.data.l[0], xevent->xclient.data.l[4]);
571
dnd_debug2 ("xdnd_send_event(), from widget widget %s", (char *) "<WIDGET>");
572
XSendEvent (dnd->display, window, 0, 0, xevent);
575
static void xdnd_send_enter (DndClass * dnd, Window window, Window from, Atom * typelist)
579
n = array_length (typelist);
581
memset (&xevent, 0, sizeof (xevent));
583
xevent.xany.type = ClientMessage;
584
xevent.xany.display = dnd->display;
585
xevent.xclient.window = window;
586
xevent.xclient.message_type = dnd->XdndEnter;
587
xevent.xclient.format = 32;
589
XDND_ENTER_SOURCE_WIN (&xevent) = from;
590
XDND_ENTER_THREE_TYPES_SET (&xevent, n > XDND_THREE);
591
XDND_ENTER_VERSION_SET (&xevent, dnd->version);
592
for (i = 0; i < n && i < XDND_THREE; i++)
593
XDND_ENTER_TYPE (&xevent, i) = typelist[i];
594
xdnd_send_event (dnd, window, &xevent);
597
static void xdnd_send_position (DndClass * dnd, Window window, Window from, Atom action, int x, int y, unsigned long time)
601
memset (&xevent, 0, sizeof (xevent));
603
xevent.xany.type = ClientMessage;
604
xevent.xany.display = dnd->display;
605
xevent.xclient.window = window;
606
xevent.xclient.message_type = dnd->XdndPosition;
607
xevent.xclient.format = 32;
609
XDND_POSITION_SOURCE_WIN (&xevent) = from;
610
XDND_POSITION_ROOT_SET (&xevent, x, y);
611
if (dnd_version_at_least (dnd->dragging_version, 1))
612
XDND_POSITION_TIME (&xevent) = time;
613
if (dnd_version_at_least (dnd->dragging_version, 2))
614
XDND_POSITION_ACTION (&xevent) = action;
616
xdnd_send_event (dnd, window, &xevent);
619
static void xdnd_send_status (DndClass * dnd, Window window, Window from, int will_accept, \
620
int want_position, int x, int y, int w, int h, Atom action)
624
memset (&xevent, 0, sizeof (xevent));
626
xevent.xany.type = ClientMessage;
627
xevent.xany.display = dnd->display;
628
xevent.xclient.window = window;
629
xevent.xclient.message_type = dnd->XdndStatus;
630
xevent.xclient.format = 32;
632
XDND_STATUS_TARGET_WIN (&xevent) = from;
633
XDND_STATUS_WILL_ACCEPT_SET (&xevent, will_accept);
635
XDND_STATUS_WANT_POSITION_SET (&xevent, want_position);
637
XDND_STATUS_RECT_SET (&xevent, x, y, w, h);
638
if (dnd_version_at_least (dnd->dragging_version, 2))
640
XDND_STATUS_ACTION (&xevent) = action;
642
xdnd_send_event (dnd, window, &xevent);
645
static void xdnd_send_leave (DndClass * dnd, Window window, Window from)
649
memset (&xevent, 0, sizeof (xevent));
651
xevent.xany.type = ClientMessage;
652
xevent.xany.display = dnd->display;
653
xevent.xclient.window = window;
654
xevent.xclient.message_type = dnd->XdndLeave;
655
xevent.xclient.format = 32;
657
XDND_LEAVE_SOURCE_WIN (&xevent) = from;
659
xdnd_send_event (dnd, window, &xevent);
662
static void xdnd_send_drop (DndClass * dnd, Window window, Window from, unsigned long time)
666
memset (&xevent, 0, sizeof (xevent));
668
xevent.xany.type = ClientMessage;
669
xevent.xany.display = dnd->display;
670
xevent.xclient.window = window;
671
xevent.xclient.message_type = dnd->XdndDrop;
672
xevent.xclient.format = 32;
674
XDND_DROP_SOURCE_WIN (&xevent) = from;
675
if (dnd_version_at_least (dnd->dragging_version, 1))
676
XDND_DROP_TIME (&xevent) = time;
678
xdnd_send_event (dnd, window, &xevent);
681
/* error is not actually used, i think future versions of the protocol should return an error status
682
to the calling window with the XdndFinished client message */
683
static void xdnd_send_finished (DndClass * dnd, Window window, Window from, int error)
687
memset (&xevent, 0, sizeof (xevent));
689
xevent.xany.type = ClientMessage;
690
xevent.xany.display = dnd->display;
691
xevent.xclient.window = window;
692
xevent.xclient.message_type = dnd->XdndFinished;
693
xevent.xclient.format = 32;
695
XDND_FINISHED_TARGET_WIN (&xevent) = from;
697
xdnd_send_event (dnd, window, &xevent);
700
/* returns non-zero on error - i.e. no selection owner set. Type is of course the mime type */
701
static int xdnd_convert_selection (DndClass * dnd, Window window, Window requester, Atom type)
703
if (!(window = XGetSelectionOwner (dnd->display, dnd->XdndSelection))) {
704
dnd_debug1 ("xdnd_convert_selection(): XGetSelectionOwner failed");
707
XConvertSelection (dnd->display, dnd->XdndSelection, type,
708
dnd->Xdnd_NON_PROTOCOL_ATOM, requester, CurrentTime);
712
/* returns non-zero on error */
713
static int xdnd_set_selection_owner (DndClass * dnd, Window window, Atom type, Time time)
715
if (!XSetSelectionOwner (dnd->display, dnd->XdndSelection, window, time)) {
716
dnd_debug1 ("xdnd_set_selection_owner(): XSetSelectionOwner failed");
722
static void xdnd_selection_send (DndClass * dnd, XSelectionRequestEvent * request, unsigned char *data, int length)
725
dnd_debug2 (" requestor = %ld", request->requestor);
726
dnd_debug2 (" property = %ld", request->property);
727
dnd_debug2 (" length = %d", length);
728
XChangeProperty (dnd->display, request->requestor, request->property,
729
request->target, 8, PropModeReplace, data, length);
730
xevent.xselection.type = SelectionNotify;
731
xevent.xselection.property = request->property;
732
xevent.xselection.display = request->display;
733
xevent.xselection.requestor = request->requestor;
734
xevent.xselection.selection = request->selection;
735
xevent.xselection.target = request->target;
736
xevent.xselection.time = request->time;
737
xdnd_send_event (dnd, request->requestor, &xevent);
741
/* respond to a notification that a primary selection has been sent */
742
int xdnd_get_selection (DndClass * dnd, Window from, Atom property, Window insert)
746
unsigned long remaining;
755
if (XGetWindowProperty (dnd->display, insert, property, read / 4, 65536, 1,
756
AnyPropertyType, &actual, &format,
763
if (dnd->widget_insert_drop && !error)
764
error = (*dnd->widget_insert_drop) (dnd, s, count, remaining, insert, from, actual);
771
static int paste_prop_internal (DndClass * dnd, Window from, Window insert, unsigned long prop, int delete_prop)
774
unsigned long nitems;
775
unsigned long bytes_after;
780
unsigned char *s = 0;
781
if (XGetWindowProperty (dnd->display, insert, prop,
782
nread / 4, 65536, delete_prop,
783
AnyPropertyType, &actual_type, &actual_fmt,
784
&nitems, &bytes_after, &s) != Success) {
789
if (dnd->widget_insert_drop && !error)
790
error = (*dnd->widget_insert_drop) (dnd, s, nitems, bytes_after, insert, from, actual_fmt);
792
} while (bytes_after);
799
* Respond to a notification that a primary selection has been sent (supports INCR)
801
static int xdnd_get_selection (DndClass * dnd, Window from, Atom prop, Window insert)
803
struct timeval tv, tv_start;
804
unsigned long bytes_after;
807
unsigned long nitems;
808
unsigned char *s = 0;
811
if (XGetWindowProperty
812
(dnd->display, insert, prop, 0, 8, False, AnyPropertyType, &actual_type, &actual_fmt,
813
&nitems, &bytes_after, &s) != Success) {
818
if (actual_type != XInternAtom (dnd->display, "INCR", False))
819
return paste_prop_internal (dnd, from, insert, prop, True);
820
XDeleteProperty (dnd->display, insert, prop);
821
gettimeofday (&tv_start, 0);
826
if (XCheckMaskEvent (dnd->display, PropertyChangeMask, &xe)) {
827
if (xe.type == PropertyNotify && xe.xproperty.state == PropertyNewValue) {
828
/* time between arrivals of data */
829
gettimeofday (&tv_start, 0);
830
if (paste_prop_internal (dnd, from, insert, prop, True))
837
FD_SET (ConnectionNumber (dnd->display), &r);
838
select (ConnectionNumber (dnd->display) + 1, &r, 0, 0, &tv);
839
if (FD_ISSET (ConnectionNumber (dnd->display), &r))
842
gettimeofday (&tv, 0);
843
t = (tv.tv_sec - tv_start.tv_sec) * 1000000L + (tv.tv_usec - tv_start.tv_usec);
844
/* no data for five seconds, so quit */
852
int outside_rectangle (int x, int y, XRectangle * r)
854
return (x < r->x || y < r->y || x >= r->x + r->width || y >= r->y + r->height);
857
/* avoids linking with the maths library */
858
static float xdnd_sqrt (float x)
860
float last_ans, ans = 2, a;
865
ans = (ans + x / ans) / 2;
866
a = (ans - last_ans) / ans;
873
#define print_marks print_win_marks(from,__FILE__,__LINE__);
875
/* returns action on success, 0 otherwise */
876
Atom xdnd_drag (DndClass * dnd, Window from, Atom action, Atom * typelist)
878
XEvent xevent, xevent_temp;
879
Window over_window = 0, last_window = 0;
880
#if XDND_VERSION >= 3
881
Window last_dropper_toplevel = 0;
882
int internal_dropable = 1;
886
float x_mouse, y_mouse;
887
int result = 0, dnd_aware;
890
dnd_warning ("xdnd_drag() called with typelist = 0");
892
/* first wait until the mouse moves more than five pixels */
894
XNextEvent (dnd->display, &xevent);
895
if (xevent.type == ButtonRelease) {
896
dnd_debug1 ("button release - no motion");
897
XSendEvent (dnd->display, xevent.xany.window, 0, ButtonReleaseMask, &xevent);
900
} while (xevent.type != MotionNotify);
902
x_mouse = (float) xevent.xmotion.x_root;
903
y_mouse = (float) xevent.xmotion.y_root;
905
if (!dnd->drag_threshold)
906
dnd->drag_threshold = 4.0;
908
XNextEvent (dnd->display, &xevent);
909
if (xevent.type == MotionNotify)
910
if (xdnd_sqrt ((x_mouse - xevent.xmotion.x_root) * (x_mouse - xevent.xmotion.x_root) +
911
(y_mouse - xevent.xmotion.y_root) * (y_mouse - xevent.xmotion.y_root)) > dnd->drag_threshold)
913
if (xevent.type == ButtonRelease) {
914
XSendEvent (dnd->display, xevent.xany.window, 0, ButtonReleaseMask, &xevent);
919
dnd_debug1 ("moved 5 pixels - going to drag");
921
n = array_length (typelist);
923
xdnd_set_type_list (dnd, from, typelist);
927
dnd->stage = XDND_DRAG_STAGE_DRAGGING;
929
for (cursor = &dnd->cursors[0]; cursor->width; cursor++)
930
if (cursor->action == action)
933
cursor = &dnd->cursors[0];
935
/* the mouse has been dragged a little, so this is a drag proper */
936
if (XGrabPointer (dnd->display, dnd->root_window, False,
937
ButtonMotionMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask,
938
GrabModeAsync, GrabModeAsync, None,
939
cursor->cursor, CurrentTime) != GrabSuccess)
940
dnd_debug1 ("Unable to grab pointer");
943
while (xevent.xany.type != ButtonRelease) {
944
XAllowEvents (dnd->display, SyncPointer, CurrentTime);
945
XNextEvent (dnd->display, &xevent);
946
switch (xevent.type) {
948
if (dnd->handle_expose_events)
949
(*dnd->handle_expose_events) (dnd, &xevent);
952
/* this event is not actually reported, so we find out by ourselves from motion events */
955
/* this event is not actually reported, so we find out by ourselves from motion events */
958
/* done, but must send a leave event */
959
dnd_debug1 ("ButtonRelease - exiting event loop");
963
dnd->dropper_toplevel = 0;
964
memcpy (&xevent_temp, &xevent, sizeof (xevent));
965
xevent.xmotion.subwindow = xevent.xmotion.window;
967
Window root_return, child_return;
969
unsigned int mask_return;
970
while (XQueryPointer (dnd->display, xevent.xmotion.subwindow, &root_return, &child_return,
971
&x_temp, &y_temp, &xevent.xmotion.x,
972
&xevent.xmotion.y, &mask_return)) {
973
#if XDND_VERSION >= 3
975
if ((dnd_aware = xdnd_is_dnd_aware (dnd, xevent.xmotion.subwindow, &dnd->dragging_version, typelist))) {
976
dnd->dropper_toplevel = xevent.xmotion.subwindow;
977
xevent.xmotion.x_root = x_temp;
978
xevent.xmotion.y_root = y_temp;
982
xevent.xmotion.x_root = x_temp;
983
xevent.xmotion.y_root = y_temp;
986
goto found_descendent;
987
xevent.xmotion.subwindow = child_return;
993
/* last_window is just for debug purposes */
994
if (last_window != xevent.xmotion.subwindow) {
995
dnd_debug2 ("window crossing to %ld", xevent.xmotion.subwindow);
996
dnd_debug2 (" current window is %ld", over_window);
997
dnd_debug3 (" last_window = %ld, xmotion.subwindow = %ld", last_window, xevent.xmotion.subwindow);
998
#if XDND_VERSION >= 3
999
dnd_debug3 (" dropper_toplevel = %ld, last_dropper_toplevel.subwindow = %ld", dnd->dropper_toplevel, last_dropper_toplevel);
1001
dnd_debug3 (" dnd_aware = %d, dnd->options & XDND_OPTION_NO_HYSTERESIS = %ld", dnd_aware, (long) dnd->options & XDND_OPTION_NO_HYSTERESIS);
1004
#if XDND_VERSION < 3
1005
/* is the new window dnd aware? if not stay in the old window */
1006
if (over_window != xevent.xmotion.subwindow &&
1007
last_window != xevent.xmotion.subwindow &&
1009
(dnd_aware = xdnd_is_dnd_aware (dnd, xevent.xmotion.subwindow, &dnd->dragging_version, typelist))
1011
(dnd->options & XDND_OPTION_NO_HYSTERESIS)
1014
internal_dropable = 1;
1015
if (dnd->widget_exists && (*dnd->widget_exists) (dnd, xevent.xmotion.subwindow))
1016
if (!xdnd_is_dnd_aware (dnd, xevent.xmotion.subwindow, &dnd->dragging_version, typelist))
1017
internal_dropable = 0;
1018
dnd_debug3 ("dnd->dropper_toplevel = %ld, last_dropper_toplevel = %ld\n", dnd->dropper_toplevel, last_dropper_toplevel);
1019
if ((dnd->dropper_toplevel != last_dropper_toplevel ||
1020
last_window != xevent.xmotion.subwindow) && internal_dropable &&
1024
(dnd->options & XDND_OPTION_NO_HYSTERESIS)
1028
/* leaving window we were over */
1030
if (dnd->stage == XDND_DRAG_STAGE_ENTERED) {
1031
dnd_debug1 ("got leave at right stage");
1032
dnd->stage = XDND_DRAG_STAGE_DRAGGING;
1033
if (dnd->internal_drag) {
1034
dnd_debug1 (" our own widget");
1035
if (dnd->widget_apply_leave)
1036
(*dnd->widget_apply_leave) (dnd, over_window);
1038
dnd_debug1 (" not our widget - sending XdndLeave");
1039
#if XDND_VERSION < 3
1040
xdnd_send_leave (dnd, over_window, from);
1042
if (dnd->dropper_toplevel != last_dropper_toplevel) {
1043
xdnd_send_leave (dnd, last_dropper_toplevel, from);
1045
dnd_debug1 (" not sending leave --> dnd->dropper_toplevel == last_dropper_toplevel");
1049
dnd->internal_drag = 0;
1050
dnd->dropper_window = 0;
1051
dnd->ready_to_drop = 0;
1053
dnd_debug1 ("got leave at wrong stage - ignoring");
1056
/* entering window we are currently over */
1057
over_window = xevent.xmotion.subwindow;
1059
dnd_debug1 (" is dnd aware");
1060
dnd->stage = XDND_DRAG_STAGE_ENTERED;
1061
if (dnd->widget_exists && (*dnd->widget_exists) (dnd, over_window))
1062
dnd->internal_drag = 1;
1063
if (dnd->internal_drag) {
1064
dnd_debug1 (" our own widget");
1066
dnd_debug2 (" not our widget - sending XdndEnter to %ld", over_window);
1067
#if XDND_VERSION < 3
1068
xdnd_send_enter (dnd, over_window, from, typelist);
1070
if (dnd->dropper_toplevel != last_dropper_toplevel)
1071
xdnd_send_enter (dnd, dnd->dropper_toplevel, from, typelist);
1074
dnd->want_position = 1;
1075
dnd->ready_to_drop = 0;
1076
dnd->rectangle.width = dnd->rectangle.height = 0;
1077
dnd->dropper_window = over_window;
1078
/* we want an additional motion event in case the pointer enters and then stops */
1079
XSendEvent (dnd->display, from, 0, ButtonMotionMask, &xevent_temp);
1080
XSync (dnd->display, 0);
1082
#if XDND_VERSION >= 3
1083
last_dropper_toplevel = dnd->dropper_toplevel;
1085
/* we are now officially in a new window */
1087
/* got here, so we are just moving `inside' the same window */
1088
if (dnd->stage == XDND_DRAG_STAGE_ENTERED) {
1089
dnd->supported_action = dnd->XdndActionCopy;
1090
dnd_debug1 ("got motion at right stage");
1091
dnd->x = xevent.xmotion.x_root;
1092
dnd->y = xevent.xmotion.y_root;
1093
if (dnd->want_position || outside_rectangle (dnd->x, dnd->y, &dnd->rectangle)) {
1094
dnd_debug1 (" want position and outside rectangle");
1095
if (dnd->internal_drag) {
1096
dnd_debug1 (" our own widget");
1097
dnd->ready_to_drop = (*dnd->widget_apply_position) (dnd, over_window, from,
1098
action, dnd->x, dnd->y, xevent.xmotion.time, typelist,
1099
&dnd->want_position, &dnd->supported_action, &dnd->desired_type, &dnd->rectangle);
1100
/* if not ready, keep sending positions, this check is repeated below for XdndStatus from external widgets */
1101
if (!dnd->ready_to_drop) {
1102
dnd->want_position = 1;
1103
dnd->rectangle.width = dnd->rectangle.height = 0;
1105
dnd_debug2 (" return action=%ld", dnd->supported_action);
1107
#if XDND_VERSION < 3
1108
dnd_debug3 (" not our own widget - sending XdndPosition to %ld, action %ld", over_window, action);
1109
xdnd_send_position (dnd, over_window, from, action, dnd->x, dnd->y, xevent.xmotion.time);
1111
dnd_debug3 (" not our own widget - sending XdndPosition to %ld, action %ld", dnd->dropper_toplevel, action);
1112
xdnd_send_position (dnd, dnd->dropper_toplevel, from, action, dnd->x, dnd->y, xevent.xmotion.time);
1115
} else if (dnd->want_position) {
1116
dnd_debug1 (" inside rectangle");
1118
dnd_debug1 (" doesn't want position");
1122
last_window = xevent.xmotion.subwindow;
1125
dnd_debug1 ("ClientMessage recieved");
1126
if (xevent.xclient.message_type == dnd->XdndStatus && !dnd->internal_drag) {
1127
dnd_debug1 (" XdndStatus recieved");
1128
if (dnd->stage == XDND_DRAG_STAGE_ENTERED
1129
#if XDND_VERSION < 3
1130
&& XDND_STATUS_TARGET_WIN (&xevent) == dnd->dropper_window
1133
dnd_debug1 (" XdndStatus stage correct, dropper window correct");
1134
dnd->want_position = XDND_STATUS_WANT_POSITION (&xevent);
1135
dnd->ready_to_drop = XDND_STATUS_WILL_ACCEPT (&xevent);
1136
dnd->rectangle.x = XDND_STATUS_RECT_X (&xevent);
1137
dnd->rectangle.y = XDND_STATUS_RECT_Y (&xevent);
1138
dnd->rectangle.width = XDND_STATUS_RECT_WIDTH (&xevent);
1139
dnd->rectangle.height = XDND_STATUS_RECT_HEIGHT (&xevent);
1140
dnd->supported_action = dnd->XdndActionCopy;
1141
if (dnd_version_at_least (dnd->dragging_version, 2))
1142
dnd->supported_action = XDND_STATUS_ACTION (&xevent);
1143
dnd_debug3 (" return action=%ld, ready=%d", dnd->supported_action, dnd->ready_to_drop);
1144
/* if not ready, keep sending positions, this check is repeated above for internal widgets */
1145
if (!dnd->ready_to_drop) {
1146
dnd->want_position = 1;
1147
dnd->rectangle.width = dnd->rectangle.height = 0;
1149
dnd_debug3 (" rectangle = (x=%d, y=%d, ", dnd->rectangle.x, dnd->rectangle.y);
1150
dnd_debug4 ("w=%d, h=%d), want_position=%d\n", dnd->rectangle.width, dnd->rectangle.height, dnd->want_position);
1152
#if XDND_VERSION < 3
1153
else if (XDND_STATUS_TARGET_WIN (&xevent) != dnd->dropper_window) {
1154
dnd_debug3 (" XdndStatus XDND_STATUS_TARGET_WIN (&xevent) = %ld, dnd->dropper_window = %ld", XDND_STATUS_TARGET_WIN (&xevent), dnd->dropper_window);
1158
dnd_debug2 (" XdndStatus stage incorrect dnd->stage = %d", dnd->stage);
1162
case SelectionRequest:{
1163
/* the target widget MAY request data, so wait for SelectionRequest */
1165
unsigned char *data = 0;
1166
dnd_debug1 ("SelectionRequest - getting widget data");
1168
(*dnd->widget_get_data) (dnd, from, &data, &length, xevent.xselectionrequest.target);
1170
dnd_debug1 (" sending selection");
1171
xdnd_selection_send (dnd, &xevent.xselectionrequest, data, length);
1179
if (dnd->ready_to_drop) {
1181
dnd_debug1 ("ready_to_drop - sending XdndDrop");
1182
time = xevent.xbutton.time;
1183
if (dnd->internal_drag) {
1184
/* we are dealing with our own widget, no need to send drop events, just put the data straight */
1186
unsigned char *data = 0;
1187
if (dnd->widget_insert_drop) {
1188
(*dnd->widget_get_data) (dnd, from, &data, &length, dnd->desired_type);
1190
if (!(*dnd->widget_insert_drop) (dnd, data, length, 0, dnd->dropper_window, from, dnd->desired_type)) {
1191
result = dnd->supported_action; /* success - so return action to caller */
1192
dnd_debug1 (" inserted data into widget - success");
1194
dnd_debug1 (" inserted data into widget - failed");
1198
dnd_debug1 (" got data from widget, but data is null");
1202
xdnd_set_selection_owner (dnd, from, dnd->desired_type, time);
1203
#if XDND_VERSION < 3
1204
xdnd_send_drop (dnd, dnd->dropper_window, from, time);
1206
xdnd_send_drop (dnd, dnd->dropper_toplevel, from, time);
1209
if (!dnd->internal_drag)
1211
XAllowEvents (dnd->display, SyncPointer, CurrentTime);
1212
XNextEvent (dnd->display, &xevent);
1213
if (xevent.type == ClientMessage && xevent.xclient.message_type == dnd->XdndFinished) {
1214
dnd_debug1 ("XdndFinished");
1215
#if XDND_VERSION < 3
1216
if (XDND_FINISHED_TARGET_WIN (&xevent) == dnd->dropper_window) {
1218
dnd_debug2 (" source correct - exiting event loop, action=%ld", dnd->supported_action);
1219
result = dnd->supported_action; /* success - so return action to caller */
1221
#if XDND_VERSION < 3
1224
} else if (xevent.type == Expose) {
1225
if (dnd->handle_expose_events)
1226
(*dnd->handle_expose_events) (dnd, &xevent);
1227
} else if (xevent.type == MotionNotify) {
1228
if (xevent.xmotion.time > time + (dnd->time_out ? dnd->time_out * 1000 : 10000)) { /* allow a ten second timeout as default */
1229
dnd_debug1 ("timeout - exiting event loop");
1232
} else if (xevent.type == SelectionRequest && xevent.xselectionrequest.selection == dnd->XdndSelection) {
1233
/* the target widget is going to request data, so check for SelectionRequest events */
1235
unsigned char *data = 0;
1237
dnd_debug1 ("SelectionRequest - getting widget data");
1238
(*dnd->widget_get_data) (dnd, from, &data, &length, xevent.xselectionrequest.target);
1240
dnd_debug1 (" sending selection");
1241
xdnd_selection_send (dnd, &xevent.xselectionrequest, data, length);
1244
/* don't wait for a XdndFinished event */
1245
if (!dnd_version_at_least (dnd->dragging_version, 2))
1250
dnd_debug1 ("not ready_to_drop - ungrabbing pointer");
1252
XUngrabPointer (dnd->display, CurrentTime);
1257
/* returns non-zero if event is handled */
1258
int xdnd_handle_drop_events (DndClass * dnd, XEvent * xevent)
1261
if (xevent->type == SelectionNotify) {
1262
dnd_debug1 ("got SelectionNotify");
1263
if (xevent->xselection.property == dnd->Xdnd_NON_PROTOCOL_ATOM && dnd->stage == XDND_DROP_STAGE_CONVERTING) {
1265
dnd_debug1 (" property is Xdnd_NON_PROTOCOL_ATOM - getting selection");
1266
error = xdnd_get_selection (dnd, dnd->dragger_window, xevent->xselection.property, xevent->xany.window);
1267
/* error is not actually used, i think future versions of the protocol maybe should return
1268
an error status to the calling window with the XdndFinished client message */
1269
if (dnd_version_at_least (dnd->dragging_version, 2)) {
1270
#if XDND_VERSION >= 3
1271
xdnd_send_finished (dnd, dnd->dragger_window, dnd->dropper_toplevel, error);
1273
xdnd_send_finished (dnd, dnd->dragger_window, dnd->dropper_window, error);
1275
dnd_debug1 (" sending finished");
1277
xdnd_xfree (dnd->dragger_typelist);
1279
dnd->stage = XDND_DROP_STAGE_IDLE;
1282
dnd_debug1 (" property is not Xdnd_NON_PROTOCOL_ATOM - ignoring");
1284
} else if (xevent->type == ClientMessage) {
1285
dnd_debug2 ("got ClientMessage to xevent->xany.window = %ld", xevent->xany.window);
1286
if (xevent->xclient.message_type == dnd->XdndEnter) {
1287
dnd_debug2 (" message_type is XdndEnter, version = %ld", XDND_ENTER_VERSION (xevent));
1288
#if XDND_VERSION >= 3
1289
if (XDND_ENTER_VERSION (xevent) < 3)
1293
dnd->dragger_window = XDND_ENTER_SOURCE_WIN (xevent);
1294
#if XDND_VERSION >= 3
1295
dnd->dropper_toplevel = xevent->xany.window;
1296
dnd->dropper_window = 0; /* enter goes to the top level window only,
1297
so we don't really know what the
1298
sub window is yet */
1300
dnd->dropper_window = xevent->xany.window;
1302
xdnd_xfree (dnd->dragger_typelist);
1303
if (XDND_ENTER_THREE_TYPES (xevent)) {
1304
dnd_debug1 (" three types only");
1305
xdnd_get_three_types (dnd, xevent, &dnd->dragger_typelist);
1307
dnd_debug1 (" more than three types - getting list");
1308
xdnd_get_type_list (dnd, dnd->dragger_window, &dnd->dragger_typelist);
1310
if (dnd->dragger_typelist)
1311
dnd->stage = XDND_DROP_STAGE_ENTERED;
1313
dnd_debug1 (" typelist returned as zero!");
1314
dnd->dragging_version = XDND_ENTER_VERSION (xevent);
1316
} else if (xevent->xclient.message_type == dnd->XdndLeave) {
1317
#if XDND_VERSION >= 3
1318
if (xevent->xany.window == dnd->dropper_toplevel && dnd->dropper_window)
1319
xevent->xany.window = dnd->dropper_window;
1321
dnd_debug1 (" message_type is XdndLeave");
1322
if (dnd->dragger_window == XDND_LEAVE_SOURCE_WIN (xevent) && dnd->stage == XDND_DROP_STAGE_ENTERED) {
1323
dnd_debug1 (" leaving");
1324
if (dnd->widget_apply_leave)
1325
(*dnd->widget_apply_leave) (dnd, xevent->xany.window);
1326
dnd->stage = XDND_DROP_STAGE_IDLE;
1327
xdnd_xfree (dnd->dragger_typelist);
1329
dnd->dropper_toplevel = dnd->dropper_window = 0;
1331
dnd_debug1 (" wrong stage or from wrong window");
1333
} else if (xevent->xclient.message_type == dnd->XdndPosition) {
1334
dnd_debug2 (" message_type is XdndPosition to %ld", xevent->xany.window);
1335
if (dnd->dragger_window == XDND_POSITION_SOURCE_WIN (xevent) && dnd->stage == XDND_DROP_STAGE_ENTERED) {
1338
XRectangle rectangle;
1340
last_window = dnd->dropper_window;
1341
#if XDND_VERSION >= 3
1342
/* version 3 gives us the top-level window only. WE have to find the child that the pointer is over: */
1343
if (1 || xevent->xany.window != dnd->dropper_toplevel || !dnd->dropper_window) {
1344
Window parent, child, new_child = 0;
1345
dnd->dropper_toplevel = xevent->xany.window;
1346
parent = dnd->root_window;
1347
child = dnd->dropper_toplevel;
1351
if (!XTranslateCoordinates (dnd->display, parent, child,
1352
XDND_POSITION_ROOT_X (xevent), XDND_POSITION_ROOT_Y (xevent),
1353
&xd, &yd, &new_child))
1359
dnd->dropper_window = xevent->xany.window = child;
1360
dnd_debug2 (" child window translates to %ld", dnd->dropper_window);
1361
} else if (xevent->xany.window == dnd->dropper_toplevel && dnd->dropper_window) {
1362
xevent->xany.window = dnd->dropper_window;
1363
dnd_debug2 (" child window previously found: %ld", dnd->dropper_window);
1366
action = dnd->XdndActionCopy;
1367
dnd->supported_action = dnd->XdndActionCopy;
1368
dnd->x = XDND_POSITION_ROOT_X (xevent);
1369
dnd->y = XDND_POSITION_ROOT_Y (xevent);
1370
dnd->time = CurrentTime;
1371
if (dnd_version_at_least (dnd->dragging_version, 1))
1372
dnd->time = XDND_POSITION_TIME (xevent);
1373
if (dnd_version_at_least (dnd->dragging_version, 1))
1374
action = XDND_POSITION_ACTION (xevent);
1375
#if XDND_VERSION >= 3
1376
if (last_window && last_window != xevent->xany.window)
1377
if (dnd->widget_apply_leave)
1378
(*dnd->widget_apply_leave) (dnd, last_window);
1380
dnd->will_accept = (*dnd->widget_apply_position) (dnd, xevent->xany.window, dnd->dragger_window,
1381
action, dnd->x, dnd->y, dnd->time, dnd->dragger_typelist,
1382
&want_position, &dnd->supported_action, &dnd->desired_type, &rectangle);
1383
dnd_debug2 (" will accept = %d", dnd->will_accept);
1384
#if XDND_VERSION >= 3
1385
dnd_debug2 (" sending status of %ld", dnd->dropper_toplevel);
1386
xdnd_send_status (dnd, dnd->dragger_window, dnd->dropper_toplevel, dnd->will_accept,
1387
want_position, rectangle.x, rectangle.y, rectangle.width, rectangle.height, dnd->supported_action);
1389
dnd_debug2 (" sending status of %ld", xevent->xany.window);
1390
xdnd_send_status (dnd, dnd->dragger_window, xevent->xany.window, dnd->will_accept,
1391
want_position, rectangle.x, rectangle.y, rectangle.width, rectangle.height, dnd->supported_action);
1395
dnd_debug1 (" wrong stage or from wrong window");
1397
} else if (xevent->xclient.message_type == dnd->XdndDrop) {
1398
#if XDND_VERSION >= 3
1399
if (xevent->xany.window == dnd->dropper_toplevel && dnd->dropper_window)
1400
xevent->xany.window = dnd->dropper_window;
1402
dnd_debug1 (" message_type is XdndDrop");
1403
if (dnd->dragger_window == XDND_DROP_SOURCE_WIN (xevent) && dnd->stage == XDND_DROP_STAGE_ENTERED) {
1404
dnd->time = CurrentTime;
1405
if (dnd_version_at_least (dnd->dragging_version, 1))
1406
dnd->time = XDND_DROP_TIME (xevent);
1407
if (dnd->will_accept) {
1408
dnd_debug1 (" will_accept is true - converting selectiong");
1409
dnd_debug2 (" my window is %ld", dnd->dropper_window);
1410
dnd_debug2 (" source window is %ld", dnd->dragger_window);
1411
xdnd_convert_selection (dnd, dnd->dragger_window, dnd->dropper_window, dnd->desired_type);
1412
dnd->stage = XDND_DROP_STAGE_CONVERTING;
1414
dnd_debug1 (" will_accept is false - sending finished");
1415
if (dnd_version_at_least (dnd->dragging_version, 2)) {
1416
#if XDND_VERSION >= 3
1417
xdnd_send_finished (dnd, dnd->dragger_window, dnd->dropper_toplevel, 1);
1419
xdnd_send_finished (dnd, dnd->dragger_window, xevent->xany.window, 1);
1422
xdnd_xfree (dnd->dragger_typelist);
1424
dnd->stage = XDND_DROP_STAGE_IDLE;
1428
dnd_debug1 (" wrong stage or from wrong window");
1436
Following here is a sample implementation: Suppose we want a window
1437
to recieve drops, but do not want to be concerned with setting up all
1438
the DndClass methods. All we then do is call xdnd_get_drop() whenever a
1439
ClientMessage is recieved. If the message has nothing to do with XDND,
1440
xdnd_get_drop quickly returns 0. If it is a XdndEnter message, then
1441
xdnd_get_drop enters its own XNextEvent loop and handles all XDND
1442
protocol messages internally, returning the action requested.
1444
You should pass a desired typelist and actionlist to xdnd_get_type.
1445
These must be null terminated arrays of atoms, or a null pointer
1446
if you would like any action or type to be accepted. If typelist
1447
is null then the first type of the dragging widgets typelist will
1448
be the one used. If actionlist is null, then only XdndActionCopy will
1451
The result is stored in *data, length, type, x and y.
1452
*data must be free'd.
1455
struct xdnd_get_drop_info {
1456
unsigned char *drop_data;
1457
int drop_data_length;
1465
static int widget_insert_drop (DndClass * dnd, unsigned char *data, int length, int remaining, Window into, Window from, Atom type)
1467
struct xdnd_get_drop_info *i;
1468
i = (struct xdnd_get_drop_info *) dnd->user_hook1;
1469
if (!i->drop_data) {
1470
i->drop_data = malloc (length);
1473
memcpy (i->drop_data, data, length);
1474
i->drop_data_length = length;
1477
t = malloc (i->drop_data_length + length);
1479
free (i->drop_data);
1483
memcpy (t, i->drop_data, i->drop_data_length);
1484
memcpy (t + i->drop_data_length, data, length);
1485
free (i->drop_data);
1487
i->drop_data_length += length;
1492
static int widget_apply_position (DndClass * dnd, Window widgets_window, Window from,
1493
Atom action, int x, int y, Time t, Atom * typelist,
1494
int *want_position, Atom * supported_action_return, Atom * desired_type,
1495
XRectangle * rectangle)
1498
struct xdnd_get_drop_info *info;
1499
Atom *dropper_typelist, supported_type = 0;
1500
Atom *supported_actions, supported_action = 0;
1502
info = (struct xdnd_get_drop_info *) dnd->user_hook1;
1503
dropper_typelist = info->typelist;
1504
supported_actions = info->actionlist;
1506
if (dropper_typelist) {
1507
/* find a correlation: */
1508
for (j = 0; dropper_typelist[j]; j++) {
1509
for (i = 0; typelist[i]; i++) {
1510
if (typelist[i] == dropper_typelist[j]) {
1511
supported_type = typelist[i];
1519
/* user did not specify, so return first type */
1520
supported_type = typelist[0];
1522
/* not supported, so return false */
1523
if (!supported_type)
1526
if (supported_actions) {
1527
for (j = 0; supported_actions[j]; j++) {
1528
if (action == supported_actions[j]) {
1529
supported_action = action;
1534
/* user did not specify */
1535
if (action == dnd->XdndActionCopy)
1536
supported_action = action;
1538
if (!supported_action)
1542
rectangle->x = rectangle->y = 0;
1543
rectangle->width = rectangle->height = 0;
1545
info->return_action = *supported_action_return = supported_action;
1546
info->return_type = *desired_type = supported_type;
1553
Atom xdnd_get_drop (Display * display, XEvent * xevent, Atom * typelist, Atom * actionlist,
1554
unsigned char **data, int *length, Atom * type, int *x, int *y)
1557
static int initialised = 0;
1558
static DndClass dnd;
1560
xdnd_init (&dnd, display);
1563
if (xevent->type != ClientMessage || xevent->xclient.message_type != dnd.XdndEnter) {
1566
struct xdnd_get_drop_info i;
1568
/* setup user structure */
1569
memset (&i, 0, sizeof (i));
1570
i.actionlist = actionlist;
1571
i.typelist = typelist;
1572
dnd.user_hook1 = &i;
1575
dnd.widget_insert_drop = widget_insert_drop;
1576
dnd.widget_apply_position = widget_apply_position;
1580
xdnd_handle_drop_events (&dnd, xevent);
1581
if (dnd.stage == XDND_DROP_STAGE_IDLE)
1583
XNextEvent (dnd.display, xevent);
1586
/* return results */
1588
*length = i.drop_data_length;
1589
*data = i.drop_data;
1590
action = i.return_action;
1591
*type = i.return_type;