~drgeo-developers/drgeo/trunk

« back to all changes in this revision

Viewing changes to VMs/iPad/source/unix/vm-display-X11/sqUnixXdnd.c

  • Committer: Hilaire Fernandes
  • Date: 2012-01-27 21:15:40 UTC
  • Revision ID: hilaire.fernandes@gmail.com-20120127211540-912spf97bhpx6mve
Initial additions

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* sqUnixXdnd.c -- drag-and-drop for the X Window System.       -*- C -*-
 
2
 * 
 
3
 *   Copyright (C) 1996-2007 by Ian Piumarta and other authors/contributors
 
4
 *                              listed elsewhere in this file.
 
5
 *   All rights reserved.
 
6
 *   
 
7
 *   This file is part of Unix Squeak.
 
8
 * 
 
9
 *   Permission is hereby granted, free of charge, to any person obtaining a copy
 
10
 *   of this software and associated documentation files (the "Software"), to deal
 
11
 *   in the Software without restriction, including without limitation the rights
 
12
 *   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 
13
 *   copies of the Software, and to permit persons to whom the Software is
 
14
 *   furnished to do so, subject to the following conditions:
 
15
 * 
 
16
 *   The above copyright notice and this permission notice shall be included in
 
17
 *   all copies or substantial portions of the Software.
 
18
 * 
 
19
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 
20
 *   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 
21
 *   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 
22
 *   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 
23
 *   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 
24
 *   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
25
 *   SOFTWARE.
 
26
 */
 
27
 
 
28
/* Author: Ian Piumarta <ian.piumarta@inria.fr>
 
29
 * 
 
30
 * Last edited: 2009-08-19 04:21:30 by piumarta on emilia-2.local
 
31
 * 
 
32
 * BUGS
 
33
 * 
 
34
 * - This only works with version 3 and higher of the XDND protocol.
 
35
 *   No attempt whatsoever is made to check for and deal with earlier
 
36
 *   versions.  Since version 3 is at least six years old now, I doubt
 
37
 *   this matters much.
 
38
 * 
 
39
 * - Some memory could be released between drop operations, but it's
 
40
 *   only a few tens of bytes so who cares?
 
41
 * 
 
42
 * - Only filenames (MIME type text/uri-list) are handled, although
 
43
 *   it would be trivial to extend the code to cope with dropping text
 
44
 *   selections into the Squeak clipboard.  I'm simply too lazy to be
 
45
 *   bothered.
 
46
 * 
 
47
 * - No attempt is made to verify that XDND protocol messages arrive
 
48
 *   in the correct order.  (If your WM or file manager is broken, you
 
49
 *   get to keep all the shrapnel that will be left behind after
 
50
 *   dragging something onto Squeak).
 
51
 */
 
52
 
 
53
 
 
54
#include <stdio.h>
 
55
#include <stdlib.h>
 
56
#include <stdarg.h>
 
57
#include <string.h>
 
58
#include <ctype.h>
 
59
 
 
60
#include <X11/Xlib.h>
 
61
#include <X11/Xatom.h>
 
62
 
 
63
 
 
64
#define DEBUG_XDND      0
 
65
 
 
66
 
 
67
static  Atom      XdndVersion= (Atom)3;
 
68
 
 
69
static  Atom      XdndAware;
 
70
static  Atom      XdndSelection;
 
71
static  Atom      XdndEnter;
 
72
static  Atom      XdndLeave;
 
73
static  Atom      XdndPosition;
 
74
static  Atom      XdndDrop;
 
75
static  Atom      XdndFinished;
 
76
static  Atom      XdndStatus;
 
77
static  Atom      XdndActionCopy;
 
78
static  Atom      XdndActionMove;
 
79
static  Atom      XdndActionLink;
 
80
static  Atom      XdndActionAsk;
 
81
static  Atom      XdndActionPrivate;
 
82
static  Atom      XdndTypeList;
 
83
static  Atom      XdndTextUriList;
 
84
static  Atom      XdndSelectionAtom;
 
85
 
 
86
static  Window    xdndSourceWindow= 0;
 
87
static  int       isUrlList= 0;
 
88
static  int       xdndWillAccept= 0;
 
89
 
 
90
/* To keep backword compatibitily, xdndWillAccept is always 1.
 
91
 * isUrlList is 1 only if the dropped type includes "text/uri-list".
 
92
 * 
 
93
 * case isUrlList == 1: Get url list and send dndFinished immediately.  Then record drag event.
 
94
 * case isUrlList == 0: Record drag event anyway (uxDropFileCount= 0).  The image will get the data and send dndFinished.
 
95
 */
 
96
 
 
97
static Atom      *xdndInTypes= 0;   /* all targets in clipboard */
 
98
 
 
99
static Window     xdndOutTarget= None;
 
100
static Atom      *xdndOutTypes= 0;  /* Types offered by source window */
 
101
 
 
102
static XSelectionRequestEvent xdndOutRequestEvent; /* RequestEvent from target */
 
103
 
 
104
enum XdndState {
 
105
  XdndStateIdle,
 
106
  XdndStateEntered,
 
107
  XdndStateTracking,
 
108
  XdndStateOutTracking,
 
109
  XdndStateOutAccepted
 
110
};
 
111
 
 
112
enum {
 
113
  DndOutStart   = -1,
 
114
  DndInFinished = -2
 
115
};
 
116
 
 
117
#define xdndEnter_sourceWindow(evt)             ( (evt)->data.l[0])
 
118
#define xdndEnter_version(evt)                  ( (evt)->data.l[1] >> 24)
 
119
#define xdndEnter_hasThreeTypes(evt)            (((evt)->data.l[1] & 0x1UL) == 0)
 
120
#define xdndEnter_typeAt(evt, idx)              ( (evt)->data.l[2 + (idx)])
 
121
#define xdndEnter_targets(evt)                  ( (evt)->data.l + 2)
 
122
 
 
123
#define xdndPosition_sourceWindow(evt)          ((Window)((evt)->data.l[0]))
 
124
#define xdndPosition_rootX(evt)                 ((evt)->data.l[2] >> 16)
 
125
#define xdndPosition_rootY(evt)                 ((evt)->data.l[2] & 0xffffUL)
 
126
#define xdndPosition_action(evt)                ((Atom)((evt)->data.l[4]))
 
127
 
 
128
#define xdndStatus_targetWindow(evt)            ((evt)->data.l[0])
 
129
#define xdndStatus_setWillAccept(evt, b)        ((evt)->data.l[1]= (((evt)->data.l[1] & ~1UL) | ((b) ? 1 : 0)))
 
130
#define xdndStatus_setWantPosition(evt, b)      ((evt)->data.l[1]= (((evt)->data.l[1] & ~2UL) | ((b) ? 2 : 0)))
 
131
#define xdndStatus_action(evt)                  ((evt)->data.l[4])
 
132
 
 
133
#define xdndDrop_sourceWindow(evt)              ((Window)((evt)->data.l[0]))
 
134
#define xdndDrop_time(evt)                      ((evt)->data.l[2])
 
135
 
 
136
#define xdndFinished_targetWindow(evt)          ((evt)->data.l[0])
 
137
 
 
138
 
 
139
#if (DEBUG_XDND)
 
140
# define fdebugf(ARGS) do { fprintf ARGS; } while (0)
 
141
#else
 
142
# define fdebugf(ARGS) do { } while (0)
 
143
#endif
 
144
 
 
145
static void updateCursor(int state);
 
146
 
 
147
void getMousePosition(void);
 
148
 
 
149
 
 
150
static void *xmalloc(size_t size)
 
151
{
 
152
  void *ptr= malloc(size);
 
153
  if (!ptr)
 
154
    {
 
155
      fprintf(stderr, "out of memory\n");
 
156
      exit(1);
 
157
    }
 
158
  return ptr;
 
159
}
 
160
 
 
161
 
 
162
static void *xcalloc(size_t nmemb, size_t size)
 
163
{
 
164
  void *ptr= calloc(nmemb, size);
 
165
  if (!ptr)
 
166
    {
 
167
      fprintf(stderr, "out of memory\n");
 
168
      exit(1);
 
169
    }
 
170
  return ptr;
 
171
}
 
172
 
 
173
 
 
174
static void *xrealloc(void *ptr, size_t size)
 
175
{
 
176
  ptr= realloc(ptr, size);
 
177
  if (!ptr)
 
178
    {
 
179
      fprintf(stderr, "out of memory\n");
 
180
      exit(1);
 
181
    }
 
182
  return ptr;
 
183
}
 
184
 
 
185
 
 
186
static int hexValue(const int c)
 
187
{
 
188
  if (c <  '0') return 0;
 
189
  if (c <= '9') return c - '0';
 
190
  if (c <  'A') return 0;
 
191
  if (c <= 'F') return c - 'A' + 10;
 
192
  if (c <  'a') return 0;
 
193
  if (c <= 'f') return c - 'a' + 10;
 
194
  return 0;
 
195
}
 
196
 
 
197
 
 
198
static char *uri2string(const char *uri)
 
199
{
 
200
  size_t len= strlen(uri);
 
201
  char *string= (char *)xmalloc(len + 3);
 
202
  /* whoever wrote the URL stuff in the the image was too damn stupid to understand file URIs */
 
203
  if (!strncmp(uri, "file:", 5))
 
204
    {
 
205
      char *in= string, *out= string;
 
206
      strncpy(string, uri + 5, len);
 
207
      while (*in)
 
208
        if ((in[0] == '%') && isxdigit(in[1]) && isxdigit(in[2]))
 
209
          {
 
210
            *out++= hexValue(in[1]) * 16 + hexValue(in[2]);
 
211
            in += 3;
 
212
          }
 
213
        else
 
214
          *out++= *in++;
 
215
      *out= '\0';
 
216
    }
 
217
  else
 
218
    {
 
219
      strncpy(string, uri, len);
 
220
    }
 
221
  fdebugf((stderr, "  uri2string: <%s>\n", string));
 
222
  return string;
 
223
}
 
224
 
 
225
 
 
226
/*** Handle DnD Output ***/
 
227
 
 
228
 
 
229
#define DndWindow stParent
 
230
 
 
231
/* Answer dndAware window under the cursor, or None if not found.
 
232
 */
 
233
static Window dndAwareWindow(Window root, Window child, int *versionReturn)
 
234
{
 
235
  Atom actualType;
 
236
  int actualFormat;
 
237
  unsigned long nitems, bytesAfter;
 
238
  unsigned char *data;
 
239
  Window rootReturn, childReturn;
 
240
  int rootX, rootY, winX, winY;
 
241
  unsigned int mask;
 
242
 
 
243
  if (None == child) return None;
 
244
  XGetWindowProperty(stDisplay, child, XdndAware,
 
245
                     0, 0x8000000L, False, XA_ATOM,
 
246
                     &actualType, &actualFormat, &nitems,
 
247
                     &bytesAfter, &data);
 
248
  if (nitems > 0)
 
249
    {
 
250
      *versionReturn= (int)*data;
 
251
      return child;
 
252
    }
 
253
  
 
254
  XQueryPointer(stDisplay, child, &rootReturn, &childReturn, &rootX, &rootY, &winX, &winY, &mask);
 
255
 
 
256
  if (childReturn == None) return None;
 
257
 
 
258
  return dndAwareWindow(root, childReturn, versionReturn);
 
259
}
 
260
 
 
261
 
 
262
/* Send ClientMessage to drop target.
 
263
 *
 
264
 * long data[5] : event specific data
 
265
 * Window source : source window (stParent)
 
266
 * Window target : target window
 
267
 * char * type : event type name
 
268
 */
 
269
static void sendClientMessage(long *data, Window source, Window target, Atom type)
 
270
{
 
271
  XEvent e;
 
272
  XClientMessageEvent *evt= &e.xclient;
 
273
  if (None == target) return;
 
274
  evt->type= ClientMessage;
 
275
  evt->serial= 0;
 
276
  evt->send_event= 0;
 
277
  evt->display= stDisplay;
 
278
  evt->window= target;
 
279
  evt->message_type= type;
 
280
  evt->format= 32;
 
281
  evt->data.l[0]= source;
 
282
  evt->data.l[1]= data[1];
 
283
  evt->data.l[2]= data[2];
 
284
  evt->data.l[3]= data[3];
 
285
  evt->data.l[4]= data[4];
 
286
  XSendEvent(stDisplay, target, 0, 0, &e);
 
287
/*fdebugf((stderr, "Send %s to: 0x%lx\n", type, target));*/
 
288
}
 
289
 
 
290
static void sendEnter(Window target, Window source)
 
291
{
 
292
  long data[5]= { 0, 0, 0, 0, 0 };
 
293
  data[1] |= 0x0UL; /* just three data types */
 
294
  data[1] |= XdndVersion << 24; /* version num */
 
295
 
 
296
  if (0 != xdndOutTypes)
 
297
    {
 
298
      data[2]= xdndOutTypes[0];
 
299
      if (None != xdndOutTypes[1])
 
300
        {
 
301
          data[3]= xdndOutTypes[1];
 
302
          if (None != xdndOutTypes[2])
 
303
            {
 
304
              data[4]= xdndOutTypes[1];
 
305
            }
 
306
        }
 
307
    }
 
308
  fdebugf((stderr, "Send XdndEnter (output) source: 0x%lx target: 0x%lx\n", source, target));
 
309
  sendClientMessage(data, source, target, XdndEnter);
 
310
}
 
311
 
 
312
 
 
313
static void sendPosition(Window target, Window source, int rootX, int rootY, Time timestamp)
 
314
{
 
315
  long data[5]= { 0, 0, 0, 0, 0 };
 
316
  data[2]= (rootX << 16) | rootY;
 
317
  data[3]= timestamp;
 
318
  data[4]= XdndActionCopy;
 
319
  sendClientMessage(data, source, target, XdndPosition);
 
320
}
 
321
 
 
322
 
 
323
static void sendDrop(Window target, Window source, Time timestamp)
 
324
{
 
325
  long data[5]= { 0, 0, 0, 0, 0 };
 
326
  data[2]= timestamp;
 
327
  fdebugf((stderr, "Send XdndDrop (output) source: 0x%lx target: 0x%lx\n", source, target));
 
328
 
 
329
  sendClientMessage(data, source, target, XdndDrop);
 
330
}
 
331
 
 
332
 
 
333
static void sendLeave(Window target, Window source)
 
334
{
 
335
  long data[5]= { 0, 0, 0, 0, 0 };
 
336
  fdebugf((stderr, "Send XdndLeave (output) source: 0x%lx target: 0x%lx\n", source, target));
 
337
  sendClientMessage(data, source, target, XdndLeave);
 
338
}
 
339
 
 
340
 
 
341
static enum XdndState dndOutInitialize(enum XdndState state)
 
342
{
 
343
  fdebugf((stderr, "Internal signal DndOutStart (output)\n"));
 
344
  memset(&xdndOutRequestEvent, 0, sizeof(xdndOutRequestEvent));
 
345
  XSetSelectionOwner(stDisplay, XdndSelection, DndWindow, CurrentTime);
 
346
  updateCursor(-1);
 
347
  return XdndStateOutTracking;
 
348
}
 
349
 
 
350
 
 
351
/* Track the current mouse position.
 
352
 */
 
353
static enum XdndState dndOutMotion(enum XdndState state, XMotionEvent *evt)
 
354
{
 
355
  Window currentWindow= None;
 
356
  int versionReturn= 0;
 
357
 
 
358
  if ((XdndStateOutTracking != state) && (XdndStateOutAccepted != state)) return state;
 
359
 
 
360
  currentWindow= dndAwareWindow(evt->root, evt->root, &versionReturn);
 
361
  if (DndWindow == currentWindow) /* Cursor is on myself */
 
362
    {
 
363
      xdndOutTarget= None;
 
364
      return XdndStateOutTracking;
 
365
    }
 
366
  
 
367
  updateCursor(XdndStateOutAccepted == state);
 
368
 
 
369
  if ((XdndVersion > versionReturn)     /* Target's version is too low. */
 
370
      || (None == currentWindow))       /* I can't find XdndAware window. */
 
371
    {
 
372
      xdndOutTarget= None;
 
373
      return XdndStateOutTracking;
 
374
    }
 
375
  
 
376
  fdebugf((stderr, "Receive MotionNotify (output) root: 0x%lx awareWindow: 0x%lx\n", evt->root, currentWindow));
 
377
  if (currentWindow != xdndOutTarget)
 
378
    {
 
379
      sendLeave(xdndOutTarget, DndWindow);
 
380
      sendEnter(currentWindow, DndWindow);
 
381
    }
 
382
 
 
383
  sendPosition(currentWindow, DndWindow, evt->x_root, evt->y_root, evt->time);
 
384
  xdndOutTarget= currentWindow;
 
385
 
 
386
  return state;
 
387
}
 
388
 
 
389
 
 
390
/* A status message to know accept or not is received.
 
391
 */
 
392
static enum XdndState dndOutStatus(enum XdndState state, XClientMessageEvent *evt)
 
393
{
 
394
  long *ldata= evt->data.l;
 
395
  fdebugf((stderr, "Receive XdndStatus (output) status: 0x%lx target: 0x%lx\n", ldata[1], ldata[0]));
 
396
 
 
397
  if ((XdndStateOutTracking != state) && (XdndStateOutAccepted != state))
 
398
    {
 
399
      /*printf("%i is not expected in XdndStatus\n", state);*/
 
400
      sendLeave(ldata[0], DndWindow);
 
401
      return state;
 
402
    }
 
403
  
 
404
  if (xdndOutTarget != ldata[0]) return state;
 
405
 
 
406
  if (ldata[1] && 0x1UL)
 
407
    return XdndStateOutAccepted;
 
408
  else
 
409
    return XdndStateOutTracking;
 
410
}
 
411
 
 
412
 
 
413
/* The mouse button was released.
 
414
*/
 
415
static enum XdndState dndOutRelease(enum XdndState state, XButtonEvent *evt)
 
416
{
 
417
  if (XdndStateIdle == state) return XdndStateIdle;
 
418
  fdebugf((stderr, "Receive ButtonRelease (output) window: 0x%lx\n", evt->window));
 
419
 
 
420
  if (XdndStateOutAccepted == state)
 
421
    {
 
422
      sendDrop(xdndOutTarget, DndWindow, evt->time);
 
423
      return XdndStateOutAccepted;
 
424
    }
 
425
  sendLeave(xdndOutTarget, DndWindow);
 
426
  return XdndStateIdle;
 
427
}
 
428
 
 
429
 
 
430
/* Another application is requesting the selection.
 
431
*/
 
432
static enum XdndState dndOutSelectionRequest(enum XdndState state, XSelectionRequestEvent *req)
 
433
{
 
434
  fdebugf((stderr, "Receive SelectionRequest for %s (output) owner: 0x%lx : requestor: 0x%lx\n",
 
435
           XGetAtomName(stDisplay, req->target), req->owner, req->requestor));
 
436
  if (XdndStateOutAccepted != state)
 
437
    {
 
438
      /*printf("%i is not expected in SelectionRequest\n", state);*/
 
439
      return state;
 
440
    }
 
441
  memcpy(&xdndOutRequestEvent, req, sizeof(xdndOutRequestEvent));
 
442
  recordDragEvent(DragRequest, 1);
 
443
  return state;
 
444
}
 
445
 
 
446
 
 
447
/* A finished message is received.
 
448
 */
 
449
static enum XdndState dndOutFinished(enum XdndState state, XClientMessageEvent *evt)
 
450
{
 
451
  fdebugf((stderr, "Receive XdndFinished (output) source: 0x%lx target: 0x%lx\n",
 
452
           DndWindow, xdndFinished_targetWindow(evt)));
 
453
  xdndOutTarget= None;
 
454
  return XdndStateIdle;
 
455
}
 
456
 
 
457
 
 
458
/* Change cursor
 
459
 * TODO: The cursor should be controlled by the image, so it should be removed finally.
 
460
 *
 
461
 * state = -1 : Cursor is on Squeak window.
 
462
 * state =  0 : Target window doesn't accept.
 
463
 * state =  1 : Target window accepts.
 
464
 */
 
465
static void updateCursor(int state)
 
466
{
 
467
  static int lastCursor= -1;
 
468
 
 
469
  if (lastCursor == state) return;
 
470
  fdebugf((stderr, "Cursor change (output) previous: %i new: %i\n", lastCursor, state));
 
471
  if (1 == state)
 
472
    {
 
473
      Cursor cursor;
 
474
      cursor= XCreateFontCursor(stDisplay, 90);
 
475
      XDefineCursor(stDisplay, stWindow, cursor);
 
476
    }
 
477
  else
 
478
    XDefineCursor(stDisplay, stWindow, None);
 
479
  
 
480
  lastCursor= state;
 
481
}
 
482
 
 
483
 
 
484
static void dndInDestroyTypes(void)
 
485
{
 
486
  if (xdndInTypes == NULL)
 
487
    return;
 
488
  free(xdndInTypes);
 
489
  xdndInTypes= NULL;
 
490
}
 
491
 
 
492
 
 
493
static void updateInTypes(Atom *newTargets, int targetSize)
 
494
{
 
495
  int i;
 
496
  dndInDestroyTypes();
 
497
  xdndInTypes= (Atom *)calloc(targetSize + 1, sizeof(Atom));
 
498
  for (i= 0;  i < targetSize;  ++i)
 
499
    xdndInTypes[i]= newTargets[i];
 
500
  xdndInTypes[targetSize]= None;
 
501
}
 
502
 
 
503
 
 
504
/* Answer non-zero if dnd input object is available.
 
505
*/
 
506
static int dndAvailable(void)
 
507
{
 
508
  return useXdnd && xdndInTypes;
 
509
}
 
510
 
 
511
 
 
512
/* Answer types for dropping object.
 
513
 * types - returned types (it should be copied by client), or NULL if unavailable.
 
514
 * count - number of types.
 
515
 */
 
516
static void dndGetTargets(Atom **types, int *count)
 
517
{
 
518
  int i;
 
519
  *types= 0;
 
520
  *count= 0;
 
521
  if (!xdndInTypes) return;
 
522
  for (i= 0;  None != xdndInTypes[i];  ++i);
 
523
  *count= i;
 
524
  *types= xdndInTypes;
 
525
}
 
526
 
 
527
 
 
528
static void dndGetTypeList(XClientMessageEvent *evt)
 
529
{
 
530
  xdndWillAccept= 0;
 
531
  isUrlList= 0;
 
532
 
 
533
  if (xdndEnter_hasThreeTypes(evt))
 
534
    {
 
535
      fdebugf((stderr, "  3 types\n"));
 
536
      updateInTypes((Atom *) xdndEnter_targets(evt), 3);
 
537
    }
 
538
  else
 
539
    {
 
540
      Atom type;
 
541
      int format;
 
542
      unsigned long count, remaining;
 
543
      unsigned char *data= 0;
 
544
 
 
545
      XGetWindowProperty(stDisplay, xdndSourceWindow, XdndTypeList, 0, 0x8000000L, False, XA_ATOM,
 
546
                         &type, &format, &count, &remaining, &data);
 
547
 
 
548
      if ((type != XA_ATOM) || (format != 32) || (count == 0) || !data)
 
549
        {
 
550
          if (data) XFree(data);
 
551
          fprintf(stderr, "XGetWindowProperty failed in xdndGetTypeList\n");
 
552
          return;
 
553
        }
 
554
 
 
555
      updateInTypes((Atom *) data, count);
 
556
      XFree(data);
 
557
      fdebugf((stderr, "  %ld types\n", count));
 
558
    }
 
559
 
 
560
  /* We only accept filenames (MIME type "text/uri-list"). */
 
561
  {
 
562
    int i;
 
563
    for (i= 0;  xdndInTypes[i];  ++i)
 
564
      {
 
565
        fdebugf((stderr, "  type %d == %ld %s\n", i, xdndInTypes[i], XGetAtomName(stDisplay, xdndInTypes[i])));
 
566
        if (XdndTextUriList == xdndInTypes[i])
 
567
          {
 
568
            isUrlList= 1;
 
569
            xdndWillAccept= 1;
 
570
          }
 
571
      }
 
572
  }
 
573
  xdndWillAccept= 1;
 
574
}
 
575
 
 
576
static void dndSendStatus(int willAccept, Atom action)
 
577
{
 
578
  XClientMessageEvent evt;
 
579
  memset(&evt, 0, sizeof(evt));
 
580
 
 
581
  evt.type         = ClientMessage;
 
582
  evt.display      = stDisplay;
 
583
  evt.window       = xdndSourceWindow;
 
584
  evt.message_type = XdndStatus;
 
585
  evt.format       = 32;
 
586
 
 
587
  xdndStatus_targetWindow(&evt)= DndWindow;
 
588
  xdndStatus_setWillAccept(&evt, willAccept);
 
589
  xdndStatus_setWantPosition(&evt, 0);
 
590
  xdndStatus_action(&evt)= action;
 
591
 
 
592
  XSendEvent(stDisplay, xdndSourceWindow, 0, 0, (XEvent *)&evt);
 
593
 
 
594
  /* fdebugf((stderr, "  sent status to 0x%lx willAccept=%d data=%ld action=%s(%ld)\n",
 
595
             xdndSourceWindow, willAccept, evt.data.l[1], XGetAtomName(stDisplay, action), action)); */
 
596
}
 
597
 
 
598
static void dndSendFinished(void)
 
599
{
 
600
    XClientMessageEvent evt;
 
601
    memset(&evt, 0, sizeof(evt));
 
602
 
 
603
    evt.type         = ClientMessage;
 
604
    evt.display      = stDisplay;
 
605
    evt.window       = xdndSourceWindow;
 
606
    evt.message_type = XdndFinished;
 
607
    evt.format       = 32;
 
608
 
 
609
    xdndFinished_targetWindow(&evt)= DndWindow;
 
610
    XSendEvent(stDisplay, xdndSourceWindow, 0, 0, (XEvent *)&evt);
 
611
 
 
612
    fdebugf((stderr, "dndSendFinished target: 0x%lx source: 0x%lx\n", DndWindow, xdndSourceWindow));
 
613
}
 
614
 
 
615
 
 
616
static enum XdndState dndInEnter(enum XdndState state, XClientMessageEvent *evt)
 
617
{
 
618
  fdebugf((stderr, "Receive XdndEnter (input)\n"));
 
619
  if (xdndEnter_version(evt) < 3)
 
620
    {
 
621
      fprintf(stderr, "  xdnd: protocol version %ld not supported\n", xdndEnter_version(evt));
 
622
      return state;
 
623
    }
 
624
  xdndSourceWindow= xdndEnter_sourceWindow(evt);
 
625
  dndGetTypeList(evt);
 
626
 
 
627
  fdebugf((stderr, "  dndEnter target: 0x%lx source: 0x%lx\n", evt->window, xdndSourceWindow));
 
628
  return XdndStateEntered;
 
629
}
 
630
 
 
631
 
 
632
static enum XdndState dndInLeave(enum XdndState state)
 
633
{
 
634
  fdebugf((stderr, "Receive XdndLeave (input)\n"));
 
635
  recordDragEvent(DragLeave, 1);
 
636
  return XdndStateIdle;
 
637
}
 
638
 
 
639
 
 
640
static enum XdndState dndInPosition(enum XdndState state, XClientMessageEvent *evt)
 
641
{
 
642
  /*fdebugf((stderr, "Receive XdndPosition (input)\n"));*/
 
643
 
 
644
  if (xdndSourceWindow != xdndPosition_sourceWindow(evt))
 
645
    {
 
646
      fdebugf((stderr, "dndInPosition: wrong source window\n"));
 
647
      return XdndStateIdle;
 
648
    }
 
649
 
 
650
  getMousePosition();
 
651
 
 
652
  if ((state != XdndStateEntered) && (state != XdndStateTracking))
 
653
    {
 
654
      fdebugf((stderr, "dndInPosition: wrong state\n"));
 
655
      return XdndStateIdle;
 
656
    }
 
657
  
 
658
  if ((state == XdndStateEntered) && xdndWillAccept)
 
659
    recordDragEvent(DragEnter, 1);
 
660
  
 
661
  if (xdndWillAccept)
 
662
    {
 
663
      Atom action= xdndPosition_action(evt);
 
664
      /*fdebugf((stderr, "  dndInPosition: action = %ld %s\n", action, XGetAtomName(stDisplay, action)));*/
 
665
      xdndWillAccept= (action == XdndActionMove) | (action == XdndActionCopy)
 
666
        |             (action == XdndActionLink) | (action == XdndActionAsk);
 
667
    }
 
668
 
 
669
  if (xdndWillAccept)
 
670
    {
 
671
      /*fdebugf((stderr, "  dndInPosition: accepting\n"));*/
 
672
      dndSendStatus(1, XdndActionCopy);
 
673
      recordDragEvent(DragMove, 1);
 
674
    }
 
675
  else /* won't accept */
 
676
    {
 
677
      /*fdebugf((stderr, "  dndInPosition: not accepting\n"));*/
 
678
      dndSendStatus(0, XdndActionPrivate);
 
679
    }
 
680
  return XdndStateTracking;
 
681
}
 
682
 
 
683
 
 
684
enum XdndState dndInDrop(enum XdndState state, XClientMessageEvent *evt)
 
685
{
 
686
  fdebugf((stderr, "Receive XdndDrop (input)\n"));
 
687
 
 
688
  /* If there is "text/url-list" in xdndInTypes, the selection is
 
689
   * processed only in DropFilesEvent. But if none (file count == 0),
 
690
   * the selection is handled ClipboardExtendedPlugin.
 
691
   */
 
692
  if (isUrlList == 0)
 
693
    {
 
694
      fdebugf((stderr, "  dndInDrop: no url list\n"));
 
695
      recordDragEvent(DragDrop, 0);
 
696
      return state;
 
697
    }
 
698
  dndInDestroyTypes();
 
699
 
 
700
  if (xdndSourceWindow != xdndDrop_sourceWindow(evt))
 
701
    {
 
702
      fdebugf((stderr, "  dndInDrop: wrong source window\n"));
 
703
    }
 
704
  else if (xdndWillAccept)
 
705
    {
 
706
      Window owner;
 
707
      fdebugf((stderr, "  dndInDrop: converting selection\n"));
 
708
      if (!(owner= XGetSelectionOwner(stDisplay, XdndSelection)))
 
709
        fprintf(stderr, "  dndInDrop: XGetSelectionOwner failed\n");
 
710
      else
 
711
        XConvertSelection(stDisplay, XdndSelection, XdndTextUriList, XdndSelectionAtom, stWindow, xdndDrop_time(evt));
 
712
      if (uxDropFileCount)
 
713
        {
 
714
          int i;
 
715
          assert(uxDropFileNames);
 
716
          for (i= 0;  i < uxDropFileCount;  ++i)
 
717
            free(uxDropFileNames[i]);
 
718
          free(uxDropFileNames);
 
719
          uxDropFileCount= 0;
 
720
          uxDropFileNames= 0;
 
721
        }
 
722
    }
 
723
  else
 
724
    {
 
725
      fdebugf((stderr, "  dndInDrop: refusing selection -- finishing\n"));
 
726
    }
 
727
 
 
728
  dndSendFinished();
 
729
  recordDragEvent(DragLeave, 1);
 
730
 
 
731
  return XdndStateIdle;
 
732
}
 
733
 
 
734
 
 
735
static void dndGetSelection(Window owner, Atom property)
 
736
{
 
737
  unsigned long remaining;
 
738
  unsigned char *data= 0;
 
739
  Atom actual;
 
740
  int format;
 
741
  unsigned long count;
 
742
 
 
743
  if (Success != XGetWindowProperty(stDisplay, owner, property, 0, 65536, 1, AnyPropertyType,
 
744
                                    &actual, &format, &count, &remaining, &data))
 
745
    fprintf(stderr, "dndGetSelection: XGetWindowProperty failed\n");
 
746
  else if (remaining)
 
747
    /* a little violent perhaps */
 
748
    fprintf(stderr, "dndGetSelection: XGetWindowProperty has more than 64K (why?)\n");
 
749
  else
 
750
    {
 
751
      char *tokens= (char *)data;
 
752
      char *item= 0;
 
753
      while ((item= strtok(tokens, "\n\r")))
 
754
        {
 
755
          fdebugf((stderr, "  got URI <%s>\n", item));
 
756
          if (!strncmp(item, "file:", 5))               /*** xxx BOGUS -- just while image is broken ***/
 
757
            {
 
758
              if (uxDropFileCount)
 
759
                uxDropFileNames= (char **)xrealloc(uxDropFileNames, (uxDropFileCount + 1) * sizeof(char *));
 
760
              else
 
761
                uxDropFileNames= (char **)xcalloc(1, sizeof(char *));
 
762
              uxDropFileNames[uxDropFileCount++]= uri2string(item);
 
763
            }
 
764
          tokens= 0;
 
765
        }
 
766
      if (uxDropFileCount)
 
767
        recordDragEvent(DragDrop, uxDropFileCount);
 
768
      fdebugf((stderr, "  uxDropFileCount = %d\n", uxDropFileCount));
 
769
    }
 
770
  XFree(data);
 
771
}
 
772
 
 
773
 
 
774
static enum XdndState dndInSelectionNotify(enum XdndState state, XSelectionEvent *evt)
 
775
{
 
776
  fdebugf((stderr, "Receive SelectionNotify (input)\n"));
 
777
  if (evt->property != XdndSelectionAtom) return state;
 
778
 
 
779
  dndGetSelection(evt->requestor, evt->property);
 
780
  dndSendFinished();
 
781
  recordDragEvent(DragLeave, 1);
 
782
  return XdndStateIdle;
 
783
}
 
784
 
 
785
 
 
786
static enum XdndState dndInFinished(enum XdndState state)
 
787
{
 
788
  fdebugf((stderr, "Internal signal DndInFinished (input)\n"));
 
789
  dndSendFinished();
 
790
  recordDragEvent(DragLeave, 1);
 
791
  dndInDestroyTypes();
 
792
  return XdndStateIdle;
 
793
}
 
794
 
 
795
 
 
796
/* DnD client event handler */
 
797
 
 
798
static enum XdndState dndHandleClientMessage(enum XdndState state, XClientMessageEvent *evt)
 
799
{
 
800
  Atom type= evt->message_type;
 
801
  if      (type == XdndStatus)   return dndOutStatus(state, evt);
 
802
  else if (type == XdndFinished) return dndOutFinished(state, evt);
 
803
  else if (type == XdndEnter)    return dndInEnter(state, evt);
 
804
  else if (type == XdndPosition) return dndInPosition(state, evt);
 
805
  else if (type == XdndDrop)     return dndInDrop(state, evt);
 
806
  else if (type == XdndLeave)    return dndInLeave(state);
 
807
  else                           return state;
 
808
}
 
809
 
 
810
 
 
811
/* DnD event handler */
 
812
 
 
813
static void dndHandleEvent(int type, XEvent *evt)
 
814
{
 
815
  static enum XdndState state= XdndStateIdle;
 
816
 
 
817
  switch(type)
 
818
    {
 
819
    case DndOutStart:      state= dndOutInitialize(state);                                      break;
 
820
    case MotionNotify:     state= dndOutMotion(state, &evt->xmotion);                           break;
 
821
    case ButtonRelease:    state= dndOutRelease(state, &evt->xbutton);                          break;
 
822
    case SelectionRequest: state= dndOutSelectionRequest(state, &evt->xselectionrequest);       break;
 
823
    case SelectionNotify:  state= dndInSelectionNotify(state, &evt->xselection);                break;
 
824
    case DndInFinished:    state= dndInFinished(state);                                         break;
 
825
    case ClientMessage:    state= dndHandleClientMessage(state, &evt->xclient);                 break;
 
826
    }
 
827
}
 
828
 
 
829
 
 
830
static sqInt display_dndOutStart(char *types, int ntypes)
 
831
{
 
832
  int pos, i;
 
833
  int typesSize= 0;
 
834
 
 
835
  if (xdndOutTypes != 0)
 
836
    {
 
837
      free(xdndOutTypes);
 
838
      xdndOutTypes= 0;
 
839
    }
 
840
 
 
841
  for (pos= 0; pos < ntypes; pos += strlen(types + pos) + 1)
 
842
    typesSize++;
 
843
 
 
844
  if (typesSize > 3) return 0; /* Supported types are up to 3 now */
 
845
 
 
846
  xdndOutTypes= xmalloc(sizeof(Atom) * (typesSize + 1));
 
847
  xdndOutTypes[typesSize]= None;
 
848
 
 
849
  for (pos= 0, i= 0; pos < ntypes; pos += strlen(types + pos) + 1, i++)
 
850
    xdndOutTypes[i]= XInternAtom(stDisplay, types + pos, False);
 
851
 
 
852
  for (i= 0; i < typesSize; i++)
 
853
    fdebugf((stderr, "dndOutStart: %s\n", XGetAtomName(stDisplay, xdndOutTypes[i])));
 
854
  dndHandleEvent(DndOutStart, 0);
 
855
 
 
856
  return 1;
 
857
}
 
858
 
 
859
static void display_dndOutSend (char *bytes, int nbytes)
 
860
{
 
861
  XEvent notify;
 
862
  XSelectionEvent *res= &notify.xselection;
 
863
  Atom targetProperty= ((None == xdndOutRequestEvent.property)
 
864
                        ? xdndOutRequestEvent.target
 
865
                        : xdndOutRequestEvent.property);
 
866
 
 
867
  res->type       = SelectionNotify;
 
868
  res->display    = xdndOutRequestEvent.display;
 
869
  res->requestor  = xdndOutRequestEvent.requestor;
 
870
  res->selection  = xdndOutRequestEvent.selection;
 
871
  res->target     = xdndOutRequestEvent.target;
 
872
  res->time       = xdndOutRequestEvent.time;
 
873
  res->send_event = True;
 
874
  res->property   = targetProperty; /* override later if error */
 
875
 
 
876
  XChangeProperty(stDisplay, res->requestor,
 
877
                  targetProperty, xdndOutRequestEvent.target,
 
878
                  8, PropModeReplace,
 
879
                  (unsigned char *)bytes,
 
880
                  nbytes);
 
881
 
 
882
  XSendEvent(stDisplay, res->requestor, False, 0, &notify);
 
883
  fdebugf((stderr, "Send data for %s (output) requestor: 0x%lx\n",
 
884
           XGetAtomName(stDisplay, res->target), res->requestor));
 
885
}
 
886
 
 
887
static sqInt display_dndOutAcceptedType(char * buf, int nbuf)
 
888
{
 
889
  char *type;
 
890
  if (xdndOutRequestEvent.target == None) return 0;
 
891
  type= XGetAtomName(stDisplay, xdndOutRequestEvent.target);
 
892
  strncpy(buf, type, nbuf);
 
893
  XFree(type);
 
894
  return 1;
 
895
}
 
896
 
 
897
static void dndInitialise(void)
 
898
{
 
899
  XdndAware=             XInternAtom(stDisplay, "XdndAware", False);
 
900
  XdndSelection=         XInternAtom(stDisplay, "XdndSelection", False);
 
901
  XdndEnter=             XInternAtom(stDisplay, "XdndEnter", False);
 
902
  XdndLeave=             XInternAtom(stDisplay, "XdndLeave", False);
 
903
  XdndPosition=          XInternAtom(stDisplay, "XdndPosition", False);
 
904
  XdndDrop=              XInternAtom(stDisplay, "XdndDrop", False);
 
905
  XdndFinished=          XInternAtom(stDisplay, "XdndFinished", False);
 
906
  XdndStatus=            XInternAtom(stDisplay, "XdndStatus", False);
 
907
  XdndActionCopy=        XInternAtom(stDisplay, "XdndActionCopy", False);
 
908
  XdndActionMove=        XInternAtom(stDisplay, "XdndActionMove", False);
 
909
  XdndActionLink=        XInternAtom(stDisplay, "XdndActionLink", False);
 
910
  XdndActionAsk=         XInternAtom(stDisplay, "XdndActionAsk", False);
 
911
  XdndActionPrivate=     XInternAtom(stDisplay, "XdndActionPrivate", False);
 
912
  XdndTypeList=          XInternAtom(stDisplay, "XdndTypeList", False);
 
913
  XdndTextUriList=       XInternAtom(stDisplay, "text/uri-list", False);
 
914
  XdndSelectionAtom=     XInternAtom(stDisplay, "XdndSqueakSelection", False);
 
915
 
 
916
  XChangeProperty(stDisplay, DndWindow, XdndAware, XA_ATOM, 32, PropModeReplace, (unsigned char *)&XdndVersion, 1);
 
917
}
 
918
 
 
919
 
 
920
#if (TEST_XDND)
 
921
 
 
922
 
 
923
#define fail(why)       do { fprintf(stderr, "%s\n", why);  exit(1); } while (0)
 
924
 
 
925
 
 
926
static void run(void)
 
927
{
 
928
  for (;;)
 
929
    {
 
930
      XEvent evt;
 
931
      XNextEvent(stDisplay, &evt);
 
932
      switch (evt.type)
 
933
        {
 
934
        case MotionNotify:      printf("MotionNotify\n");                       break;
 
935
        case EnterNotify:       printf("EnterNotify\n");                        break;
 
936
        case LeaveNotify:       printf("LeaveNotify\n");                        break;
 
937
        case ButtonPress:       printf("ButtonPress\n");                        break;
 
938
        case ButtonRelease:     printf("ButtonRelease\n");                      break;
 
939
        case KeyPress:          printf("KeyPress\n");                           break;
 
940
        case KeyRelease:        printf("KeyRelease\n");                         break;
 
941
        case SelectionClear:    printf("SelectionClear\n");                     break;
 
942
        case SelectionRequest:  printf("SelectionRequest\n");                   break;
 
943
        case PropertyNotify:    printf("PropertyNotify\n");                     break;
 
944
        case Expose:            printf("Expose\n");                             break;
 
945
        case MapNotify:         printf("MapNotify\n");                          break;
 
946
        case UnmapNotify:       printf("UnmapNotify\n");                        break;
 
947
        case ConfigureNotify:   printf("ConfigureNotify\n");                    break;
 
948
        case MappingNotify:     printf("MappingNotify\n");                      break;
 
949
        case ClientMessage:     dndHandleClientMessage(&evt.xclient);           break;
 
950
        case SelectionNotify:   dndHandleSelectionNotify(&evt.xselection);      break;
 
951
        default:                printf("unknown event type %d\n", evt.type);    break;
 
952
        }
 
953
    }
 
954
}
 
955
 
 
956
 
 
957
int main(int argc, char **argv)
 
958
{
 
959
  stDisplay= XOpenDisplay(0);
 
960
  if (!stDisplay) fail("cannot open display");
 
961
 
 
962
  dndInitialise();
 
963
 
 
964
  {
 
965
    XSetWindowAttributes attributes;
 
966
    unsigned long valuemask= 0;
 
967
 
 
968
    attributes.event_mask= ButtonPressMask | ButtonReleaseMask
 
969
      |                    KeyPressMask | KeyReleaseMask
 
970
      |                    PointerMotionMask
 
971
      |                    EnterWindowMask | LeaveWindowMask | ExposureMask;
 
972
    valuemask |= CWEventMask;
 
973
 
 
974
    win= XCreateWindow(stDisplay, DefaultRootWindow(stDisplay),
 
975
                       100, 100, 100, 100,      /* geom */
 
976
                       0,                       /* border */
 
977
                       CopyFromParent,          /* depth */
 
978
                       CopyFromParent,          /* class */
 
979
                       CopyFromParent,          /* visual */
 
980
                       valuemask,
 
981
                       &attributes);
 
982
  }
 
983
  if (!win) fail("cannot create window");
 
984
 
 
985
  XChangeProperty (stDisplay, win, XdndAware, XA_ATOM, 32, PropModeReplace, (unsigned char *)&XdndVersion, 3);
 
986
  XMapWindow(stDisplay, stWindow);
 
987
  run();
 
988
  XCloseDisplay(stDisplay);
 
989
 
 
990
  return 0;
 
991
}
 
992
 
 
993
 
 
994
#endif /* TEST_XDND */