~ubuntu-branches/ubuntu/utopic/blender/utopic-proposed

« back to all changes in this revision

Viewing changes to extern/xdnd/xdnd.c

  • Committer: Package Import Robot
  • Author(s): Matteo F. Vescovi
  • Date: 2012-04-28 12:11:12 UTC
  • mto: (14.1.6 experimental) (1.5.1)
  • mto: This revision was merged to the branch mainline in revision 34.
  • Revision ID: package-import@ubuntu.com-20120428121112-2zi0vp8b6vejda8i
Tags: upstream-2.63
ImportĀ upstreamĀ versionĀ 2.63

Show diffs side-by-side

added added

removed removed

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