1 /*
   2  * Copyright (c) 2004, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /*
  25  * @summary Native window for TaskXDragDrop. This is taken from
  26  *          XDnD Tiger AWT testsuite.
  27  * @author Aruna Samji (aruna.samji@sun.com)
  28  */
  29 
  30 #include <stdio.h>
  31 #include <stdlib.h>
  32 
  33 #include <X11/Xmd.h>
  34 #include <X11/StringDefs.h>
  35 #include <X11/Intrinsic.h>
  36 #include <X11/Xatom.h>
  37 #include <X11/keysym.h>
  38 #include <X11/cursorfont.h>
  39 
  40 //#define TRACE
  41 
  42 /* XDnD atom names */
  43 static const char* XAtomName_XdndActionCopy = "XdndActionCopy";
  44 static const char* XAtomName_XdndAware = "XdndAware";
  45 static const char* XAtomName_XdndDrop = "XdndDrop";
  46 static const char* XAtomName_XdndEnter = "XdndEnter";
  47 static const char* XAtomName_XdndFinished = "XdndFinished";
  48 static const char* XAtomName_XdndLeave = "XdndLeave";
  49 static const char* XAtomName_XdndPosition = "XdndPosition";
  50 static const char* XAtomName_XdndProxy = "XdndProxy";
  51 static const char* XAtomName_XdndSelection = "XdndSelection";
  52 static const char* XAtomName_XdndStatus = "XdndStatus";
  53 
  54 static const char* XAtomName_XdndActionMove = "XdndActionMove";
  55 static const char* XAtomName_XdndActionLink = "XdndActionLink";
  56 
  57 static const char* XAtomName_TextPlain = "text/plain";
  58 
  59 /* XDnD atoms */
  60 static Atom _XA_XdndActionCopy = None;
  61 static Atom _XA_XdndAware = None;
  62 static Atom _XA_XdndDrop = None;
  63 static Atom _XA_XdndEnter = None;
  64 static Atom _XA_XdndFinished = None;
  65 static Atom _XA_XdndLeave = None;
  66 static Atom _XA_XdndPosition = None;
  67 static Atom _XA_XdndProxy = None;
  68 static Atom _XA_XdndSelection = None;
  69 static Atom _XA_XdndStatus = None;
  70 static Atom _XA_TextPlain = None;
  71 
  72 static Atom _XA_XdndActionMove = None;
  73 static Atom _XA_XdndActionLink = None;
  74 
  75 /* XDnD constants */
  76 static const Atom XDnDVersion = 4;
  77 static const unsigned int DND_VERSION_SHIFT = 24;
  78 static const unsigned int ACCEPT_DROP_FLAG = 1;
  79 
  80 /* shared constants */
  81 static const EventMask DRAG_MASK = ButtonMotionMask | PointerMotionMask |
  82     KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask;
  83 
  84 /* shared function prototypes */
  85 static void init();
  86 static void remove_dnd_grab(Display* dpy, Time time);
  87 static void cleanup_drag(Display* dpy);
  88 static void register_drag_source(Widget widget);
  89 
  90 /* XDnD support */
  91 static Boolean intern_xdnd_atoms(Display* dpy);
  92 static void update_dnd_version(int version);
  93 static int get_dnd_version();
  94 static void set_same_answer(int x, int y, int w, int h);
  95 static Boolean is_same_answer(int x, int y);
  96 static Boolean register_xdnd_drop_target(Widget widget);
  97 
  98 /* shared state variables */
  99 static Boolean drag_in_progress = False;
 100 
 101 static Window target_win = None;
 102 static Window target_proxy_win = None;
 103 
 104 static char* drop_data = NULL;
 105 
 106 int main(int argc, char **argv) {
 107     XtAppContext app;
 108     Widget widget = XtAppInitialize (&app, "XDnD_main",
 109                                      NULL, 0,
 110                                      &argc, argv,
 111                                      NULL, NULL, 0);
 112 
 113     XtVaSetValues(widget, XtNx, 550, XtNy, 220,
 114                   XtNwidth, 200, XtNheight, 150, NULL);
 115     XtRealizeWidget(widget);
 116 
 117     init();
 118     intern_xdnd_atoms(XtDisplay(widget));
 119 
 120     register_drag_source(widget);
 121     register_xdnd_drop_target(widget);
 122 
 123     XSetSelectionOwner(XtDisplay(widget), XA_PRIMARY, None, CurrentTime);
 124 
 125 #ifdef DEBUG
 126     fprintf(stderr,"Root=%X\n", XRootWindowOfScreen(XtScreen(widget)));
 127 #endif
 128     for (;;) {
 129         XEvent event;
 130         Window root_win = XRootWindowOfScreen(XtScreen(widget));
 131         XtAppNextEvent(app, &event);
 132 #ifdef DEBUG
 133         fprintf(stderr,"Event type=%d win=%X\n", event.type, event.xany.window);
 134         if (event.type == MotionNotify) {
 135             fprintf(stderr,"              subwin=%X\n", event.xmotion.subwindow);
 136         }
 137 #endif
 138         if (drag_in_progress && event.xany.window == root_win) {
 139             switch (event.type) {
 140             case ButtonPress:
 141             case ButtonRelease:
 142             case MotionNotify:
 143             case EnterNotify:
 144             case LeaveNotify:
 145                 {
 146                     Window child;
 147                     XTranslateCoordinates(XtDisplay(widget), root_win,
 148                                           XtWindow(widget),
 149                                           event.xbutton.x, event.xbutton.y,
 150                                           &event.xbutton.x, &event.xbutton.y,
 151                                           &child);
 152                 }
 153             case KeyPress:
 154             case KeyRelease:
 155                 event.xany.window = XtWindow(widget);
 156             }
 157         }
 158         XtDispatchEvent(&event);
 159     }
 160 
 161     return 0;
 162 }
 163 
 164 static Boolean bad_window = False;
 165 static int (*original_x_errhandler)(Display* dpy, XErrorEvent*);
 166 
 167 static int x_errhandler(Display *dpy, XErrorEvent *err) {
 168     if (err->error_code == BadWindow) {
 169             bad_window = True;
 170     }
 171 
 172     {
 173         char errstr[256];
 174         XGetErrorText( dpy, err->error_code, errstr, 256 );
 175         fprintf(stderr, "Error: %s %d\n  Major opcode:  %d\n  Resource id:  0x%lx\n",
 176             errstr, err->error_code, err->request_code, err->resourceid);
 177     }
 178     return 0;
 179 }
 180 
 181 static void init() {
 182     original_x_errhandler = XSetErrorHandler(x_errhandler);
 183 }
 184 
 185 static Boolean intern_xdnd_atoms(Display* dpy) {
 186     _XA_XdndActionCopy = XInternAtom(dpy, XAtomName_XdndActionCopy, False);
 187     if (_XA_XdndActionCopy == None) {
 188             return False;
 189     }
 190 
 191         _XA_XdndActionMove = XInternAtom(dpy, XAtomName_XdndActionMove, False);
 192     if (_XA_XdndActionMove == None) {
 193             return False;
 194     }
 195 
 196     _XA_XdndActionLink = XInternAtom(dpy, XAtomName_XdndActionLink, False);
 197     if (_XA_XdndActionLink == None) {
 198             return False;
 199     }
 200 
 201     _XA_XdndAware = XInternAtom(dpy, XAtomName_XdndAware, False);
 202     if (_XA_XdndAware == None) {
 203             return False;
 204     }
 205 
 206     _XA_XdndDrop = XInternAtom(dpy, XAtomName_XdndDrop, False);
 207     if (_XA_XdndDrop == None) {
 208             return False;
 209     }
 210 
 211     _XA_XdndEnter = XInternAtom(dpy, XAtomName_XdndEnter, False);
 212     if (_XA_XdndEnter == None) {
 213             return False;
 214     }
 215 
 216     _XA_XdndFinished = XInternAtom(dpy, XAtomName_XdndFinished, False);
 217     if (_XA_XdndFinished == None) {
 218             return False;
 219     }
 220 
 221     _XA_XdndLeave = XInternAtom(dpy, XAtomName_XdndLeave, False);
 222     if (_XA_XdndLeave == None) {
 223             return False;
 224     }
 225 
 226     _XA_XdndPosition = XInternAtom(dpy, XAtomName_XdndPosition, False);
 227     if (_XA_XdndPosition == None) {
 228             return False;
 229     }
 230 
 231     _XA_XdndProxy = XInternAtom(dpy, XAtomName_XdndProxy, False);
 232     if (_XA_XdndProxy == None) {
 233             return False;
 234     }
 235 
 236     _XA_XdndSelection = XInternAtom(dpy, XAtomName_XdndSelection, False);
 237     if (_XA_XdndSelection == None) {
 238             return False;
 239     }
 240 
 241     _XA_XdndStatus = XInternAtom(dpy, XAtomName_XdndStatus, False);
 242     if (_XA_XdndStatus == None) {
 243             return False;
 244     }
 245 
 246     _XA_TextPlain = XInternAtom(dpy, XAtomName_TextPlain, False);
 247     if (_XA_TextPlain == None) {
 248             return False;
 249     }
 250 
 251     return True;
 252 }
 253 
 254 /******************************* Drag source support **************************/
 255 
 256 static Boolean
 257 xdnd_convert_proc(Widget w, Atom * selection, Atom * target, Atom * type,
 258                   XtPointer * value, unsigned long *length, int32_t *format) {
 259 
 260 #ifdef DEBUG
 261     fprintf(stderr,"xdnd_convert_proc\n");
 262 #endif
 263     if (*target == _XA_TextPlain) {
 264         char* str = drop_data;
 265         int len = strlen(str);
 266 
 267         char* copy = (char*)XtMalloc(len);
 268         strcpy(copy, str);
 269 
 270             *type   = *target;
 271             *value  = (XtPointer)copy;
 272             *length = len;
 273             *format = 8;
 274 
 275         return True;
 276     }
 277     return False;
 278 }
 279 
 280 static Window find_receiver_window(Widget wid, int x, int y) {
 281     Display* dpy = XtDisplay(wid);
 282     Atom           type;
 283     int            format;
 284     unsigned long  nitems;
 285     unsigned long  after;
 286     unsigned char  *data;
 287 
 288     Window         src_w = XtWindow(wid);
 289     Window         dest_w = XRootWindowOfScreen(XtScreen(wid));
 290     Window         ret_w = None;
 291     Window         child_w = None;
 292     int            src_x = x;
 293     int            src_y = y;
 294 
 295     while (dest_w != None) {
 296         XTranslateCoordinates(dpy, src_w, dest_w,
 297                       src_x, src_y, &src_x, &src_y, &child_w);
 298 
 299         if (child_w == None) {
 300             break;
 301         }
 302 
 303         src_w = dest_w;
 304         dest_w = child_w;
 305 
 306         type = None;
 307         XGetWindowProperty(dpy, dest_w, _XA_XdndAware, 0, 1, False,
 308                    AnyPropertyType, &type, &format, &nitems,
 309                    &after, &data);
 310 
 311         if (type == XA_ATOM && data != NULL) {
 312             ret_w = dest_w;
 313         }
 314 
 315         XFree(data);
 316         data = NULL;
 317     }
 318 
 319     return ret_w;
 320 }
 321 
 322 static void update_receiver_window(Display* dpy, Window win) {
 323     Atom           type;
 324     int            format;
 325     unsigned long  nitems;
 326     unsigned long  after;
 327     unsigned char  *data;
 328 
 329     if (win == None) {
 330         target_win = None;
 331         target_proxy_win = None;
 332         return;
 333     }
 334 
 335     XGetWindowProperty(dpy, win, _XA_XdndAware, 0, 1, False,
 336                        AnyPropertyType, &type, &format, &nitems,
 337                        &after, &data);
 338 
 339     if (type == XA_ATOM && data != NULL) {
 340         Window proxy_win = None;
 341         int target_version = *((int*)data);
 342         update_dnd_version(target_version);
 343 
 344         XFree(data);
 345 
 346         XGetWindowProperty(dpy, win, _XA_XdndProxy, 0, 1, False,
 347                    XA_WINDOW, &type, &format, &nitems, &after, &data);
 348 
 349         if (type == XA_WINDOW && data != None) {
 350             proxy_win = *((Window*)data);
 351             XFree(data);
 352 
 353             XGetWindowProperty(dpy, proxy_win, _XA_XdndProxy, 0, 1, False,
 354                        XA_WINDOW, &type, &format, &nitems, &after, &data);
 355 
 356             if (type != XA_WINDOW || data == NULL ||
 357             *((Window*)data) != proxy_win) {
 358             proxy_win = None;
 359             }
 360 
 361             XFree(data);
 362 
 363             if (proxy_win == None) {
 364             proxy_win = win;
 365             }
 366         }
 367 
 368         target_win = win;
 369         target_proxy_win = proxy_win;
 370     } else {
 371             XFree(data);
 372     }
 373 
 374     if (target_proxy_win == None) {
 375             target_proxy_win = target_win;
 376     }
 377 
 378     {
 379         XWindowAttributes xwa;
 380 
 381         XGetWindowAttributes(dpy, win, &xwa);
 382         XSelectInput(dpy, win,
 383                  xwa.your_event_mask | EnterWindowMask | LeaveWindowMask);
 384     }
 385 }
 386 
 387 static void xdnd_send_enter(Display* dpy, Window source_win, XMotionEvent* event) {
 388     XClientMessageEvent enter;
 389 
 390     enter.type = ClientMessage;
 391     enter.window = target_win;
 392     enter.format = 32;
 393     enter.message_type = _XA_XdndEnter;
 394     enter.data.l[0] = source_win;
 395     enter.data.l[1] = get_dnd_version() << DND_VERSION_SHIFT;
 396     enter.data.l[2] = _XA_TextPlain;
 397     enter.data.l[3] = 0;
 398     enter.data.l[4] = 0;
 399 
 400     set_same_answer(event->x_root - 1,
 401                     event->y_root - 1, 3, 3);
 402 
 403 #ifdef DEBUG
 404     fprintf(stderr,"xdnd_send_enter win=%X\n", target_proxy_win);
 405 #endif
 406     XSendEvent(dpy, target_proxy_win, False, NoEventMask,
 407                (XEvent*)&enter);
 408 }
 409 
 410 static void
 411 send_enter(Display* dpy, Window source_win, XMotionEvent* event) {
 412     xdnd_send_enter(dpy, source_win, event);
 413 }
 414 
 415 
 416 static void
 417 xdnd_send_leave(Display* dpy, Window source_win, XMotionEvent* event) {
 418     XClientMessageEvent leave;
 419 
 420     leave.type = ClientMessage;
 421     leave.window = target_win;
 422     leave.format = 32;
 423     leave.message_type = _XA_XdndLeave;
 424     leave.data.l[0] = source_win;
 425     leave.data.l[1] = 0;
 426     leave.data.l[2] = 0;
 427     leave.data.l[3] = 0;
 428     leave.data.l[4] = 0;
 429 
 430 #ifdef DEBUG
 431     fprintf(stderr,"xdnd_send_leave win=%X\n", target_proxy_win);
 432 #endif
 433     XSendEvent(dpy, target_proxy_win, False, NoEventMask,
 434                (XEvent*)&leave);
 435 }
 436 
 437 static void
 438 send_leave(Display* dpy, Window source_win, XMotionEvent* event) {
 439     xdnd_send_leave(dpy, source_win, event);
 440 }
 441 
 442 static void
 443 xdnd_send_move(Display* dpy, Window source_win, XMotionEvent* event) {
 444     XClientMessageEvent move;
 445 
 446     move.type = ClientMessage;
 447     move.window = target_win;
 448     move.format = 32;
 449     move.message_type = _XA_XdndPosition;
 450     move.data.l[0] = source_win;
 451     move.data.l[1] = 0; /* flags */
 452     move.data.l[2] = event->x_root << 16 | event->y_root;
 453     move.data.l[3] = event->time;
 454     /*move.data.l[4] = _XA_XdndActionCopy;*/
 455     move.data.l[4] = _XA_XdndActionMove;
 456 
 457     if (event != NULL) {
 458                 switch (event->state & (ShiftMask | ControlMask)) {
 459             case 0 :                         move.data.l[4] = _XA_XdndActionCopy; break;
 460             case ShiftMask :                 move.data.l[4] = _XA_XdndActionMove; break;
 461             case ControlMask :               move.data.l[4] = _XA_XdndActionCopy; break;
 462             case (ShiftMask | ControlMask) : move.data.l[4] = _XA_XdndActionLink; break;
 463                 }
 464     }
 465 
 466 #ifdef DEBUG
 467     fprintf(stderr,"xdnd_send_move win=%X\n", target_proxy_win);
 468 #endif
 469     XSendEvent(dpy, target_proxy_win, False, NoEventMask,
 470                (XEvent*)&move);
 471 }
 472 
 473 static void send_move(Display* dpy, Window source_win, XMotionEvent* event) {
 474     xdnd_send_move(dpy, source_win, event);
 475 }
 476 
 477 static void handle_move(Widget w, XMotionEvent* event) {
 478     Display* dpy = XtDisplay(w);
 479     Window source_win = XtWindow(w);
 480     Window receiver_win = None;
 481     int flags = 2 | 3 << 4 | 2 << 8;
 482 
 483     receiver_win = find_receiver_window(w, event->x, event->y);
 484 #ifdef DEBUG
 485     fprintf(stderr,"handle_move receiver=%X\n", receiver_win);
 486     fprintf(stderr,"            target=%X proxy=%X\n", target_win, target_proxy_win);
 487 #endif
 488 
 489     if (target_win != receiver_win) {
 490         if (target_win != None) {
 491             send_leave(dpy, source_win, event);
 492         }
 493 
 494         update_receiver_window(dpy, receiver_win);
 495 
 496         if (target_win != None) {
 497             send_enter(dpy, source_win, event);
 498         }
 499     }
 500 
 501     if (target_win != None) {
 502             send_move(dpy, source_win, event);
 503     }
 504 }
 505 
 506 static void handle_xdnd_status(Widget w, XClientMessageEvent* event) {
 507 #ifdef TRACE
 508     fprintf(stderr,"[Source] dragOver {XDnD}\n");
 509 #endif
 510 
 511 #ifdef DEBUG
 512     fprintf(stderr,"handle_xdnd_status\n");
 513 #endif
 514 }
 515 
 516 static void
 517 handle_xdnd_finished(Widget w, XClientMessageEvent* event) {
 518 #ifdef TRACE
 519     fprintf(stderr,"[Source] dragDropEnd {XDnD}\n");
 520 #endif
 521 
 522 #ifdef DEBUG
 523     fprintf(stderr,"handle_xdnd_finished\n");
 524 #endif
 525     cleanup_drag(XtDisplay(w));
 526     fprintf(stderr,"finished\n");
 527     //exit(0);
 528 }
 529 
 530 static void
 531 xdnd_handle_client_message(Widget w, XClientMessageEvent* event) {
 532     if (event->message_type == _XA_XdndStatus) {
 533             handle_xdnd_status(w, event);
 534     } else if (event->message_type == _XA_XdndFinished) {
 535             handle_xdnd_finished(w, event);
 536     }
 537 }
 538 
 539 static void
 540 handle_client_message(Widget w, XClientMessageEvent* event) {
 541     xdnd_handle_client_message(w, event);
 542 }
 543 
 544 static void
 545 xdnd_send_drop(Display* dpy, Window source_win, XButtonEvent* event) {
 546     XClientMessageEvent drop;
 547     drop.type = ClientMessage;
 548     drop.window = target_win;
 549     drop.format = 32;
 550     drop.message_type = _XA_XdndDrop;
 551     drop.data.l[0] = source_win;
 552     drop.data.l[1] = 1 << 24; /* flags */
 553     drop.data.l[2] = 0; /* ### */
 554     drop.data.l[3] = event->time;
 555     drop.data.l[4] = 0;
 556 
 557     XSendEvent(dpy, target_proxy_win, False, NoEventMask, (XEvent*)&drop);
 558 }
 559 
 560 static void
 561 send_drop(Display* dpy, Window source_win, XButtonEvent* event) {
 562     xdnd_send_drop(dpy, source_win, event);
 563 }
 564 
 565 static void
 566 drag_event_handler(Widget w, XtPointer client_data,
 567                    XEvent * event, Boolean * cont) {
 568 #ifdef DEBUG
 569     fprintf(stderr,"drag_event_handler type=%d in_progress=%X\n", event->type, drag_in_progress);
 570 #endif
 571 
 572     if (!drag_in_progress) {
 573         XtRemoveEventHandler(w, DRAG_MASK, False, drag_event_handler, NULL);
 574         return;
 575     }
 576 
 577     switch (event->type) {
 578         case KeyRelease: {
 579             KeySym keysym = XKeycodeToKeysym(XtDisplay(w), event->xkey.keycode, 0);
 580         #ifdef DEBUG
 581             fprintf(stderr,"\tKeyRelease code=%X sym=%X\n", event->xkey.keycode);
 582         #endif
 583             if (keysym == XK_Escape) {
 584                 remove_dnd_grab(XtDisplay(w), event->xkey.time);
 585                 cleanup_drag(XtDisplay(w));
 586             }
 587             *cont = False;
 588             break;
 589         }
 590         case KeyPress:
 591         case ButtonPress:
 592             break;
 593         case MotionNotify: {
 594             handle_move(w, (XMotionEvent*)event);
 595             break;
 596         }
 597         case ButtonRelease:
 598         #ifdef DEBUG
 599             fprintf(stderr,"\tButtonRelease button=%d\n", event->xbutton.button);
 600         #endif
 601             if (event->xbutton.button == Button1) {
 602                 remove_dnd_grab(XtDisplay(w), event->xbutton.time);
 603                 if (target_win != None) {
 604                 send_drop(XtDisplay(w), XtWindow(w), &event->xbutton);
 605         #ifdef DEBUG
 606                 fprintf(stderr,"DROP_START sent\n");
 607         #endif
 608                 } else {
 609                 cleanup_drag(XtDisplay(w));
 610                 }
 611                 *cont = True;
 612             } else {
 613                 *cont = False;
 614             }
 615             break;
 616         case ClientMessage:
 617             handle_client_message(w, &event->xclient);
 618             break;
 619     }
 620 }
 621 
 622 static void initiate_drag(Widget w, XEvent* event) {
 623     Display* dpy = XtDisplay(w);
 624     int num_targets = 1;
 625     Atom target = XA_STRING;
 626     int index = -1;
 627     unsigned int event_mask =
 628         ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | EnterWindowMask | LeaveWindowMask;
 629     unsigned int root_event_mask =
 630         ButtonMotionMask | EnterWindowMask | LeaveWindowMask | KeyPressMask | KeyReleaseMask;
 631 
 632     if (XtOwnSelection(w, _XA_XdndSelection, event->xmotion.time,
 633                        xdnd_convert_proc, NULL, NULL) != True) {
 634 #ifdef DEBUG
 635         fprintf(stderr,"initiate_drag: cannot own selection\n");
 636 #endif
 637             return;
 638     }
 639 
 640     {
 641         Window root_win = RootWindowOfScreen(XtScreen(w));
 642         XWindowAttributes xwa;
 643         Cursor cursor = XCreateFontCursor(dpy, XC_hand2);
 644 
 645         XGetWindowAttributes(dpy, root_win, &xwa);
 646         XSelectInput(dpy, root_win, xwa.your_event_mask | root_event_mask);
 647 
 648         if (XGrabPointer(dpy, root_win,
 649                  True,
 650                  event_mask,
 651                  GrabModeSync,
 652                  GrabModeAsync,
 653                  None,
 654                  cursor,
 655                  event->xmotion.time) != GrabSuccess) {
 656     #ifdef DEBUG
 657             fprintf(stderr,"initiate_drag: XGrabPointer fails\n");
 658     #endif
 659             return;
 660         }
 661 
 662         if (XGrabKeyboard(dpy, root_win,
 663                   False,
 664                   GrabModeSync,
 665                   GrabModeAsync,
 666                   event->xmotion.time) != GrabSuccess) {
 667     #ifdef DEBUG
 668             fprintf(stderr,"initiate_drag: XGrabKeyboard fails\n");
 669     #endif
 670             return;
 671         }
 672         XAllowEvents(dpy, SyncPointer, event->xmotion.time);
 673     }
 674     XtInsertEventHandler(w, DRAG_MASK,
 675                          True, drag_event_handler, NULL, XtListHead);
 676 }
 677 
 678 static void
 679 drag_gesture_recognizer(Widget w, XtPointer client_data,
 680                         XEvent * event, Boolean * cont) {
 681 #ifdef DEBUG
 682     fprintf(stderr,"drag_gesture_recognizer: type=%d\n", event->type);
 683 #endif
 684     if (event->type != MotionNotify) {
 685         return;
 686     }
 687 
 688     if (drag_in_progress) {
 689 #ifdef DEBUG
 690             fprintf(stderr,"initiate_drag: in progress\n");
 691 #endif
 692             return;
 693     }
 694 
 695 #ifdef DEBUG
 696     fprintf(stderr,"drag_gesture_recognizer: start\n");
 697 #endif
 698     initiate_drag(w, event);
 699     drag_in_progress = True;
 700 }
 701 
 702 static void register_drag_source(Widget w) {
 703 #ifdef DEBUG
 704     fprintf(stderr,"register_drag_source: w=%X\n", w);
 705 #endif
 706     XtAddEventHandler(w, ButtonMotionMask | ButtonPressMask,
 707                       False, drag_gesture_recognizer, (XtPointer)NULL);
 708 }
 709 
 710 static void remove_dnd_grab(Display* dpy, Time time) {
 711     XUngrabPointer(dpy, time);
 712     XUngrabKeyboard(dpy, time);
 713 }
 714 
 715 static void cleanup_drag(Display* dpy) {
 716 #ifdef DEBUG
 717     fprintf(stderr,"cleanup_drag\n");
 718 #endif
 719 
 720     drag_in_progress = False;
 721     target_win = None;
 722     target_proxy_win = None;
 723 }
 724 
 725 /* XDnD state variables */
 726 static int dnd_version = 0;
 727 
 728 static int same_x = 0;
 729 static int same_y = 0;
 730 static int same_w = 0;
 731 static int same_h = 0;
 732 
 733 static void set_same_answer(int x, int y, int w, int h) {
 734     same_x = x;
 735     same_y = y;
 736     same_w = w;
 737     same_h = h;
 738 }
 739 
 740 static Boolean is_same_answer(int x, int y) {
 741     if (!drag_in_progress) {
 742             return False;
 743     }
 744 
 745     if (x >= same_x && x < same_x + same_w &&
 746         y >= same_y && y < same_y + same_h) {
 747         return True;
 748     }
 749 
 750     return False;
 751 }
 752 
 753 static void update_dnd_version(int version) {
 754     dnd_version = XDnDVersion < version ? XDnDVersion : version;
 755 }
 756 
 757 static int get_dnd_version() {
 758     return dnd_version;
 759 }
 760 
 761 /******************************* Drop target support **************************/
 762 
 763 static void handle_xdnd_enter(Widget w, XEvent* event) {
 764 #ifdef TRACE
 765     fprintf(stderr,"[Target] dragEnter {XDnD}\n");
 766 #endif
 767 
 768 #ifdef DEBUG
 769     fprintf(stderr,"handle_xdnd_enter\n");
 770 #endif
 771 }
 772 
 773 static void handle_xdnd_leave(Widget w, XEvent* event) {
 774 #ifdef TRACE
 775     fprintf(stderr,"[Target] dragExit {XDnD}\n");
 776 #endif
 777 #ifdef DEBUG
 778     fprintf(stderr,"handle_xdnd_leave\n");
 779 #endif
 780 }
 781 
 782 static void handle_xdnd_position(Widget w, XEvent* event) {
 783     Window drag_source = event->xclient.data.l[0];
 784     XClientMessageEvent response;
 785     response.type = ClientMessage;
 786     response.window = drag_source;
 787     response.format = 32;
 788     response.message_type = _XA_XdndStatus;
 789     response.data.l[0] = XtWindow(w);
 790     response.data.l[1] = 0; /* flags */
 791     response.data.l[2] = 0; /* x, y */
 792     response.data.l[3] = 0; /* w, h */
 793     response.data.l[4] = 0; /* action */
 794 
 795     response.data.l[1] |= ACCEPT_DROP_FLAG;
 796     /*response.data.l[4] = _XA_XdndActionCopy;*/
 797     response.data.l[4] = _XA_XdndActionMove;
 798 
 799 #ifdef TRACE
 800         fprintf(stderr,"[Target] dragOver {XDnD}\n");
 801 #endif
 802 #ifdef DEBUG
 803     fprintf(stderr,"handle_xdnd_position win=%ld\n", event->xclient.data.l[0]);
 804 #endif
 805     XSendEvent(XtDisplay(w), drag_source, False,
 806                NoEventMask, (XEvent*)&response);
 807 }
 808 
 809 static void
 810 selection_callback_proc(Widget w, XtPointer client_data, Atom* selection,
 811                         Atom* type, XtPointer value, unsigned long* length,
 812                         int* format) {
 813 
 814     if (*type == _XA_TextPlain) {
 815 #ifdef DEBUG
 816         fprintf(stderr,"selection_callback_proc data=%s\n", (char*)value);
 817 #endif
 818 #ifdef TRACE
 819             fprintf(stderr,"[Target] drop {XDnD} data=%s\n", (char*)value);
 820 #endif
 821     }
 822 
 823     if (value != NULL && *length != 0) {
 824         drop_data = calloc(1, 2*(*length));
 825         strcpy(drop_data, (char*)value);
 826         strcpy(drop_data + (*length), (char*)value);
 827 #ifdef TRACE
 828             fprintf(stderr,"[Target] drag data=%s\n", drop_data);
 829 #endif
 830     }
 831 
 832     if (value != NULL) {
 833             XtFree((char*)value);
 834     }
 835 }
 836 
 837 static void handle_xdnd_drop(Widget w, XEvent* event) {
 838 #ifdef TRACE
 839     fprintf(stderr,"[Target] drop {XDnD}\n");
 840 #endif
 841     XtGetSelectionValue(w, _XA_XdndSelection, _XA_TextPlain,
 842                         selection_callback_proc, NULL,
 843                         XtLastTimestampProcessed(XtDisplay(w)));
 844 #ifdef DEBUG
 845     fprintf(stderr,"handle_xdnd_drop\n");
 846 #endif
 847     {
 848         Window drag_source = event->xclient.data.l[0];
 849         XClientMessageEvent finished;
 850         finished.type = ClientMessage;
 851         finished.window = drag_source;
 852         finished.format = 32;
 853         finished.message_type = _XA_XdndFinished;
 854         finished.data.l[0] = XtWindow(w);
 855         finished.data.l[1] = 0; /* flags */
 856         XSendEvent(XtDisplay(w), drag_source, False,
 857                NoEventMask, (XEvent*)&finished);
 858     }
 859 }
 860 
 861 static void
 862 target_client_message_handler(Widget w, XtPointer client_data,
 863                               XEvent* event, Boolean* cont) {
 864 #ifdef DEBUG
 865     fprintf(stderr,"target_client_message_handler\n");
 866 #endif
 867 
 868     if (event->type != ClientMessage) {
 869             return;
 870     }
 871 
 872     if (event->xclient.message_type == _XA_XdndEnter) {
 873             handle_xdnd_enter(w, event);
 874     } else if (event->xclient.message_type == _XA_XdndPosition) {
 875             handle_xdnd_position(w, event);
 876     } else if (event->xclient.message_type == _XA_XdndLeave) {
 877             handle_xdnd_leave(w, event);
 878     } else if (event->xclient.message_type == _XA_XdndDrop) {
 879             handle_xdnd_drop(w, event);
 880     }
 881 }
 882 
 883 static Boolean register_xdnd_drop_target(Widget widget) {
 884     Widget w = NULL;
 885 
 886     for (w = widget; w != NULL && !XtIsShell(w); w = XtParent(w));
 887 
 888     if (w == NULL || XtWindow(w) == None) {
 889             return False;
 890     }
 891 
 892 #ifdef DEBUG
 893     fprintf(stderr,"register_xdnd_drop_target win=%X\n", XtWindow(w));
 894 #endif
 895     XChangeProperty(XtDisplay(w), XtWindow(w),
 896                     _XA_XdndAware, XA_ATOM, 32,
 897                     PropModeReplace,
 898                     (unsigned char*)&XDnDVersion, 1);
 899 
 900     XtAddEventHandler(w, EnterWindowMask | LeaveWindowMask,
 901                       True, target_client_message_handler, NULL);
 902 }
 903 /*************************** End drop target support **************************/