ctrlsel.c (39384B)
1 #include <stdlib.h> 2 #include <string.h> 3 4 #include <X11/Xlib.h> 5 #include <X11/Xatom.h> 6 #include <X11/keysym.h> 7 #include <X11/cursorfont.h> 8 #include <X11/Xcursor/Xcursor.h> 9 10 #include "ctrlsel.h" 11 12 #define _TIMESTAMP_PROP "_TIMESTAMP_PROP" 13 #define TIMESTAMP "TIMESTAMP" 14 #define ATOM_PAIR "ATOM_PAIR" 15 #define MULTIPLE "MULTIPLE" 16 #define MANAGER "MANAGER" 17 #define TARGETS "TARGETS" 18 #define INCR "INCR" 19 #define SELDEFSIZE 0x4000 20 #define FLAG(f, b) (((f) & (b)) == (b)) 21 #define MOTION_TIME 32 22 #define DND_DISTANCE 8 /* distance from pointer to dnd miniwindow */ 23 #define XDND_VERSION 5 /* XDND protocol version */ 24 #define NCLIENTMSG_DATA 5 /* number of members on a the .data.l[] array of a XClientMessageEvent */ 25 26 enum { 27 CONTENT_INCR, 28 CONTENT_ZERO, 29 CONTENT_ERROR, 30 CONTENT_SUCCESS, 31 }; 32 33 enum { 34 PAIR_TARGET, 35 PAIR_PROPERTY, 36 PAIR_LAST 37 }; 38 39 enum { 40 /* xdnd window properties */ 41 XDND_AWARE, 42 43 /* xdnd selections */ 44 XDND_SELECTION, 45 46 /* xdnd client messages */ 47 XDND_ENTER, 48 XDND_POSITION, 49 XDND_STATUS, 50 XDND_LEAVE, 51 XDND_DROP, 52 XDND_FINISHED, 53 54 /* xdnd actions */ 55 XDND_ACTION_COPY, 56 XDND_ACTION_MOVE, 57 XDND_ACTION_LINK, 58 XDND_ACTION_ASK, 59 XDND_ACTION_PRIVATE, 60 61 XDND_ATOM_LAST, 62 }; 63 64 enum { 65 CURSOR_TARGET, 66 CURSOR_PIRATE, 67 CURSOR_DRAG, 68 CURSOR_COPY, 69 CURSOR_MOVE, 70 CURSOR_LINK, 71 CURSOR_NODROP, 72 CURSOR_LAST, 73 }; 74 75 struct Transfer { 76 /* 77 * When a client request the clipboard but its content is too 78 * large, we perform incremental transfer. We keep track of 79 * each incremental transfer in a list of transfers. 80 */ 81 struct Transfer *prev, *next; 82 struct CtrlSelTarget *target; 83 Window requestor; 84 Atom property; 85 unsigned long size; /* how much have we transferred */ 86 }; 87 88 struct PredArg { 89 CtrlSelContext *context; 90 Window window; 91 Atom message_type; 92 }; 93 94 struct CtrlSelContext { 95 Display *display; 96 Window window; 97 Atom selection; 98 Time time; 99 unsigned long ntargets; 100 struct CtrlSelTarget *targets; 101 102 /* 103 * Items below are used internally to keep track of any 104 * incremental transference in progress. 105 */ 106 unsigned long selmaxsize; 107 unsigned long ndone; 108 void *transfers; 109 110 /* 111 * Items below are used internally for drag-and-dropping. 112 */ 113 Window dndwindow; 114 unsigned int dndactions, dndresult; 115 }; 116 117 static char *atomnames[XDND_ATOM_LAST] = { 118 [XDND_AWARE] = "XdndAware", 119 [XDND_SELECTION] = "XdndSelection", 120 [XDND_ENTER] = "XdndEnter", 121 [XDND_POSITION] = "XdndPosition", 122 [XDND_STATUS] = "XdndStatus", 123 [XDND_LEAVE] = "XdndLeave", 124 [XDND_DROP] = "XdndDrop", 125 [XDND_FINISHED] = "XdndFinished", 126 [XDND_ACTION_COPY] = "XdndActionCopy", 127 [XDND_ACTION_MOVE] = "XdndActionMove", 128 [XDND_ACTION_LINK] = "XdndActionLink", 129 [XDND_ACTION_ASK] = "XdndActionAsk", 130 [XDND_ACTION_PRIVATE] = "XdndActionPrivate", 131 }; 132 133 static int 134 between(int x, int y, int x0, int y0, int w0, int h0) 135 { 136 return x >= x0 && x < x0 + w0 && y >= y0 && y < y0 + h0; 137 } 138 139 static void 140 clientmsg(Display *dpy, Window win, Atom atom, long d[5]) 141 { 142 XEvent ev; 143 144 ev.xclient.type = ClientMessage; 145 ev.xclient.display = dpy; 146 ev.xclient.serial = 0; 147 ev.xclient.send_event = True; 148 ev.xclient.message_type = atom; 149 ev.xclient.window = win; 150 ev.xclient.format = 32; 151 ev.xclient.data.l[0] = d[0]; 152 ev.xclient.data.l[1] = d[1]; 153 ev.xclient.data.l[2] = d[2]; 154 ev.xclient.data.l[3] = d[3]; 155 ev.xclient.data.l[4] = d[4]; 156 (void)XSendEvent(dpy, win, False, 0x0, &ev); 157 } 158 159 static unsigned long 160 getselmaxsize(Display *display) 161 { 162 unsigned long n; 163 164 if ((n = XExtendedMaxRequestSize(display)) > 0) 165 return n; 166 if ((n = XMaxRequestSize(display)) > 0) 167 return n; 168 return SELDEFSIZE; 169 } 170 171 static int 172 getservertime(Display *display, Time *time) 173 { 174 XEvent xev; 175 Window window; 176 Atom timeprop; 177 178 /* 179 * According to ICCCM, a client wishing to acquire ownership of 180 * a selection should set the specfied time to some time between 181 * the current last-change time of the selection concerned and 182 * the current server time. 183 * 184 * Those clients should not set the time value to `CurrentTime`, 185 * because if they do so, they have no way of finding when they 186 * gained ownership of the selection. 187 * 188 * In the case that an event triggers the acquisition of the 189 * selection, this time value can be obtained from the event 190 * itself. 191 * 192 * In the case that the client must unconditionally acquire the 193 * ownership of a selection (which is our case), a zero-length 194 * append to a property is a way to obtain a timestamp for this 195 * purpose. The timestamp is in the corresponding 196 * `PropertyNotify` event. 197 */ 198 199 if (time != CurrentTime) 200 return 1; 201 timeprop = XInternAtom(display, _TIMESTAMP_PROP, False); 202 if (timeprop == None) 203 goto error; 204 window = XCreateWindow( 205 display, 206 DefaultRootWindow(display), 207 0, 0, 1, 1, 0, 208 CopyFromParent, CopyFromParent, CopyFromParent, 209 CWEventMask, 210 &(XSetWindowAttributes){ 211 .event_mask = PropertyChangeMask, 212 } 213 ); 214 if (window == None) 215 goto error; 216 XChangeProperty( 217 display, window, 218 timeprop, timeprop, 219 8L, PropModeAppend, NULL, 0 220 ); 221 while (!XWindowEvent(display, window, PropertyChangeMask, &xev)) { 222 if (xev.type == PropertyNotify && 223 xev.xproperty.window == window && 224 xev.xproperty.atom == timeprop) { 225 *time = xev.xproperty.time; 226 break; 227 } 228 } 229 (void)XDestroyWindow(display, window); 230 return 1; 231 error: 232 return 0; 233 } 234 235 static int 236 nbytes(int format) 237 { 238 switch (format) { 239 default: return sizeof(char); 240 case 16: return sizeof(short); 241 case 32: return sizeof(long); 242 } 243 } 244 245 static int 246 getcontent(struct CtrlSelTarget *target, Display *display, Window window, Atom property) 247 { 248 unsigned char *p, *q; 249 unsigned long len, addsize, size; 250 unsigned long dl; /* dummy variable */ 251 int status; 252 Atom incr; 253 254 incr = XInternAtom(display, INCR, False), 255 status = XGetWindowProperty( 256 display, 257 window, 258 property, 259 0L, 0x1FFFFFFF, 260 True, 261 AnyPropertyType, 262 &target->type, 263 &target->format, 264 &len, &dl, &p 265 ); 266 if (target->format != 32 && target->format != 16) 267 target->format = 8; 268 if (target->type == incr) { 269 XFree(p); 270 return CONTENT_INCR; 271 } 272 if (len == 0) { 273 XFree(p); 274 return CONTENT_ZERO; 275 } 276 if (status != Success) { 277 XFree(p); 278 return CONTENT_ERROR; 279 } 280 if (p == NULL) { 281 XFree(p); 282 return CONTENT_ERROR; 283 } 284 addsize = len * nbytes(target->format); 285 size = addsize; 286 if (target->buffer != NULL) { 287 /* append buffer */ 288 size += target->bufsize; 289 if ((q = realloc(target->buffer, size + 1)) == NULL) { 290 XFree(p); 291 return CONTENT_ERROR; 292 } 293 memcpy(q + target->bufsize, p, addsize); 294 target->buffer = q; 295 target->bufsize = size; 296 target->nitems += len; 297 } else { 298 /* new buffer */ 299 if ((q = malloc(size + 1)) == NULL) { 300 XFree(p); 301 return CONTENT_ERROR; 302 } 303 memcpy(q, p, addsize); 304 target->buffer = q; 305 target->bufsize = size; 306 target->nitems = len; 307 } 308 target->buffer[size] = '\0'; 309 XFree(p); 310 return CONTENT_SUCCESS; 311 } 312 313 static void 314 deltransfer(CtrlSelContext *context, struct Transfer *transfer) 315 { 316 if (transfer->prev != NULL) { 317 transfer->prev->next = transfer->next; 318 } else { 319 context->transfers = transfer->next; 320 } 321 if (transfer->next != NULL) { 322 transfer->next->prev = transfer->prev; 323 } 324 } 325 326 static void 327 freetransferences(CtrlSelContext *context) 328 { 329 struct Transfer *transfer; 330 331 while (context->transfers != NULL) { 332 transfer = (struct Transfer *)context->transfers; 333 context->transfers = ((struct Transfer *)context->transfers)->next; 334 XDeleteProperty( 335 context->display, 336 transfer->requestor, 337 transfer->property 338 ); 339 free(transfer); 340 } 341 context->transfers = NULL; 342 } 343 344 static void 345 freebuffers(CtrlSelContext *context) 346 { 347 unsigned long i; 348 349 for (i = 0; i < context->ntargets; i++) { 350 free(context->targets[i].buffer); 351 context->targets[i].buffer = NULL; 352 context->targets[i].nitems = 0; 353 context->targets[i].bufsize = 0; 354 } 355 } 356 357 static unsigned long 358 getatomsprop(Display *display, Window window, Atom property, Atom type, Atom **atoms) 359 { 360 unsigned char *p; 361 unsigned long len; 362 unsigned long dl; /* dummy variable */ 363 int format; 364 Atom gottype; 365 unsigned long size; 366 int success; 367 368 success = XGetWindowProperty( 369 display, 370 window, 371 property, 372 0L, 0x1FFFFFFF, 373 False, 374 type, &gottype, 375 &format, &len, 376 &dl, &p 377 ); 378 if (success != Success || len == 0 || p == NULL || format != 32) 379 goto error; 380 if (type != AnyPropertyType && type != gottype) 381 goto error; 382 size = len * sizeof(**atoms); 383 if ((*atoms = malloc(size)) == NULL) 384 goto error; 385 memcpy(*atoms, p, size); 386 XFree(p); 387 return len; 388 error: 389 XFree(p); 390 *atoms = NULL; 391 return 0; 392 } 393 394 static int 395 newtransfer(CtrlSelContext *context, struct CtrlSelTarget *target, Window requestor, Atom property) 396 { 397 struct Transfer *transfer; 398 399 transfer = malloc(sizeof(*transfer)); 400 if (transfer == NULL) 401 return 0; 402 *transfer = (struct Transfer){ 403 .prev = NULL, 404 .next = (struct Transfer *)context->transfers, 405 .requestor = requestor, 406 .property = property, 407 .target = target, 408 .size = 0, 409 }; 410 if (context->transfers != NULL) 411 ((struct Transfer *)context->transfers)->prev = transfer; 412 context->transfers = transfer; 413 return 1; 414 } 415 416 static Bool 417 convert(CtrlSelContext *context, Window requestor, Atom target, Atom property) 418 { 419 Atom multiple, timestamp, targets, incr; 420 Atom *supported; 421 unsigned long i; 422 int nsupported; 423 424 incr = XInternAtom(context->display, INCR, False); 425 targets = XInternAtom(context->display, TARGETS, False); 426 multiple = XInternAtom(context->display, MULTIPLE, False); 427 timestamp = XInternAtom(context->display, TIMESTAMP, False); 428 if (target == multiple) { 429 /* A MULTIPLE should be handled when processing a 430 * SelectionRequest event. We do not support nested 431 * MULTIPLE targets. 432 */ 433 return False; 434 } 435 if (target == timestamp) { 436 /* 437 * According to ICCCM, to avoid some race conditions, it 438 * is important that requestors be able to discover the 439 * timestamp the owner used to acquire ownership. 440 * Requestors do that by requesting selection owners to 441 * convert the `TIMESTAMP` target. Selection owners 442 * must return the timestamp as an `XA_INTEGER`. 443 */ 444 XChangeProperty( 445 context->display, 446 requestor, 447 property, 448 XA_INTEGER, 32, 449 PropModeReplace, 450 (unsigned char *)&context->time, 451 1 452 ); 453 return True; 454 } 455 if (target == targets) { 456 /* 457 * According to ICCCM, when requested for the `TARGETS` 458 * target, the selection owner should return a list of 459 * atoms representing the targets for which an attempt 460 * to convert the selection will (hopefully) succeed. 461 */ 462 nsupported = context->ntargets + 2; /* +2 for MULTIPLE + TIMESTAMP */ 463 if ((supported = calloc(nsupported, sizeof(*supported))) == NULL) 464 return False; 465 for (i = 0; i < context->ntargets; i++) { 466 supported[i] = context->targets[i].target; 467 } 468 supported[i++] = multiple; 469 supported[i++] = timestamp; 470 XChangeProperty( 471 context->display, 472 requestor, 473 property, 474 XA_ATOM, 32, 475 PropModeReplace, 476 (unsigned char *)supported, 477 nsupported 478 ); 479 free(supported); 480 return True; 481 } 482 for (i = 0; i < context->ntargets; i++) { 483 if (target == context->targets[i].target) 484 goto found; 485 } 486 return False; 487 found: 488 if (context->targets[i].bufsize > context->selmaxsize) { 489 XSelectInput( 490 context->display, 491 requestor, 492 StructureNotifyMask | PropertyChangeMask 493 ); 494 XChangeProperty( 495 context->display, 496 requestor, 497 property, 498 incr, 499 32L, 500 PropModeReplace, 501 (unsigned char *)context->targets[i].buffer, 502 1 503 ); 504 newtransfer(context, &context->targets[i], requestor, property); 505 } else { 506 XChangeProperty( 507 context->display, 508 requestor, 509 property, 510 target, 511 context->targets[i].format, 512 PropModeReplace, 513 context->targets[i].buffer, 514 context->targets[i].nitems 515 ); 516 } 517 return True; 518 } 519 520 static int 521 request(CtrlSelContext *context) 522 { 523 Atom multiple, atom_pair; 524 Atom *pairs; 525 unsigned long i, size; 526 527 for (i = 0; i < context->ntargets; i++) { 528 context->targets[i].nitems = 0; 529 context->targets[i].bufsize = 0; 530 context->targets[i].buffer = NULL; 531 } 532 if (context->ntargets == 1) { 533 (void)XConvertSelection( 534 context->display, 535 context->selection, 536 context->targets[0].target, 537 context->targets[0].target, 538 context->window, 539 context->time 540 ); 541 } else if (context->ntargets > 1) { 542 multiple = XInternAtom(context->display, MULTIPLE, False); 543 atom_pair = XInternAtom(context->display, ATOM_PAIR, False); 544 size = 2 * context->ntargets; 545 pairs = calloc(size, sizeof(*pairs)); 546 if (pairs == NULL) 547 return 0; 548 for (i = 0; i < context->ntargets; i++) { 549 pairs[i * 2 + 0] = context->targets[i].target; 550 pairs[i * 2 + 1] = context->targets[i].target; 551 } 552 (void)XChangeProperty( 553 context->display, 554 context->window, 555 multiple, 556 atom_pair, 557 32, 558 PropModeReplace, 559 (unsigned char *)pairs, 560 size 561 ); 562 (void)XConvertSelection( 563 context->display, 564 context->selection, 565 multiple, 566 multiple, 567 context->window, 568 context->time 569 ); 570 free(pairs); 571 } 572 return 1; 573 } 574 575 void 576 ctrlsel_filltarget( 577 Atom target, 578 Atom type, 579 int format, 580 unsigned char *buffer, 581 unsigned long size, 582 struct CtrlSelTarget *fill 583 ) { 584 if (fill == NULL) 585 return; 586 if (format != 32 && format != 16) 587 format = 8; 588 *fill = (struct CtrlSelTarget){ 589 .target = target, 590 .type = type, 591 .action = None, 592 .format = format, 593 .nitems = size / nbytes(format), 594 .buffer = buffer, 595 .bufsize = size, 596 }; 597 } 598 599 CtrlSelContext * 600 ctrlsel_request( 601 Display *display, 602 Window window, 603 Atom selection, 604 Time time, 605 struct CtrlSelTarget targets[], 606 unsigned long ntargets 607 ) { 608 CtrlSelContext *context; 609 610 if (!getservertime(display, &time)) 611 return NULL; 612 if ((context = malloc(sizeof(*context))) == NULL) 613 return NULL; 614 *context = (CtrlSelContext){ 615 .display = display, 616 .window = window, 617 .selection = selection, 618 .time = time, 619 .targets = targets, 620 .ntargets = ntargets, 621 .selmaxsize = getselmaxsize(display), 622 .ndone = 0, 623 .transfers = NULL, 624 .dndwindow = None, 625 .dndactions = 0x00, 626 .dndresult = 0x00, 627 }; 628 if (ntargets == 0) 629 return context; 630 if (request(context)) 631 return context; 632 free(context); 633 return NULL; 634 } 635 636 CtrlSelContext * 637 ctrlsel_setowner( 638 Display *display, 639 Window window, 640 Atom selection, 641 Time time, 642 int ismanager, 643 struct CtrlSelTarget targets[], 644 unsigned long ntargets 645 ) { 646 CtrlSelContext *context; 647 Window root; 648 649 root = DefaultRootWindow(display); 650 if (!getservertime(display, &time)) 651 return NULL; 652 if ((context = malloc(sizeof(*context))) == NULL) 653 return NULL; 654 *context = (CtrlSelContext){ 655 .display = display, 656 .window = window, 657 .selection = selection, 658 .time = time, 659 .targets = targets, 660 .ntargets = ntargets, 661 .selmaxsize = getselmaxsize(display), 662 .ndone = 0, 663 .transfers = NULL, 664 .dndwindow = None, 665 .dndactions = 0x00, 666 .dndresult = 0x00, 667 }; 668 (void)XSetSelectionOwner(display, selection, window, time); 669 if (XGetSelectionOwner(display, selection) != window) { 670 free(context); 671 return NULL; 672 } 673 if (!ismanager) 674 return context; 675 676 /* 677 * According to ICCCM, a manager client (that is, a client 678 * responsible for managing shared resources) should take 679 * ownership of an appropriate selection. 680 * 681 * Immediately after a manager successfully acquires ownership 682 * of a manager selection, it should announce its arrival by 683 * sending a `ClientMessage` event. (That is necessary for 684 * clients to be able to know when a specific manager has 685 * started: any client that wish to do so should select for 686 * `StructureNotify` on the root window and should watch for 687 * the appropriate `MANAGER` `ClientMessage`). 688 */ 689 (void)XSendEvent( 690 display, 691 root, 692 False, 693 StructureNotifyMask, 694 (XEvent *)&(XClientMessageEvent){ 695 .type = ClientMessage, 696 .window = root, 697 .message_type = XInternAtom(display, MANAGER, False), 698 .format = 32, 699 .data.l[0] = time, /* timestamp */ 700 .data.l[1] = selection, /* manager selection atom */ 701 .data.l[2] = window, /* window owning the selection */ 702 .data.l[3] = 0, /* manager-specific data */ 703 .data.l[4] = 0, /* manager-specific data */ 704 } 705 ); 706 return context; 707 } 708 709 static int 710 receiveinit(CtrlSelContext *context, XEvent *xev) 711 { 712 struct CtrlSelTarget *targetp; 713 XSelectionEvent *xselev; 714 Atom multiple, atom_pair; 715 Atom *pairs; 716 Atom pair[PAIR_LAST]; 717 unsigned long j, natoms; 718 unsigned long i; 719 int status, success; 720 721 multiple = XInternAtom(context->display, MULTIPLE, False); 722 atom_pair = XInternAtom(context->display, ATOM_PAIR, False); 723 xselev = &xev->xselection; 724 if (xselev->selection != context->selection) 725 return CTRLSEL_NONE; 726 if (xselev->requestor != context->window) 727 return CTRLSEL_NONE; 728 if (xselev->property == None) 729 return CTRLSEL_ERROR; 730 if (xselev->target == multiple) { 731 natoms = getatomsprop( 732 xselev->display, 733 xselev->requestor, 734 xselev->property, 735 atom_pair, 736 &pairs 737 ); 738 if (natoms == 0 || pairs == NULL) { 739 free(pairs); 740 return CTRLSEL_ERROR; 741 } 742 } else { 743 pair[PAIR_TARGET] = xselev->target; 744 pair[PAIR_PROPERTY] = xselev->property; 745 pairs = pair; 746 natoms = 2; 747 } 748 success = 1; 749 for (j = 0; j < natoms; j += 2) { 750 targetp = NULL; 751 for (i = 0; i < context->ntargets; i++) { 752 if (pairs[j + PAIR_TARGET] == context->targets[i].target) { 753 targetp = &context->targets[i]; 754 break; 755 } 756 } 757 if (pairs[j + PAIR_PROPERTY] == None) 758 pairs[j + PAIR_PROPERTY] = pairs[j + PAIR_TARGET]; 759 if (targetp == NULL) { 760 success = 0; 761 continue; 762 } 763 status = getcontent( 764 targetp, 765 xselev->display, 766 xselev->requestor, 767 pairs[j + PAIR_PROPERTY] 768 ); 769 switch (status) { 770 case CONTENT_ERROR: 771 success = 0; 772 break; 773 case CONTENT_SUCCESS: 774 /* fallthrough */ 775 case CONTENT_ZERO: 776 context->ndone++; 777 break; 778 case CONTENT_INCR: 779 if (!newtransfer(context, targetp, xselev->requestor, pairs[j + PAIR_PROPERTY])) 780 success = 0; 781 break; 782 } 783 } 784 if (xselev->target == multiple) 785 free(pairs); 786 return success ? CTRLSEL_INTERNAL : CTRLSEL_ERROR; 787 } 788 789 static int 790 receiveincr(CtrlSelContext *context, XEvent *xev) 791 { 792 struct Transfer *transfer; 793 XPropertyEvent *xpropev; 794 int status; 795 796 xpropev = &xev->xproperty; 797 if (xpropev->state != PropertyNewValue) 798 return CTRLSEL_NONE; 799 if (xpropev->window != context->window) 800 return CTRLSEL_NONE; 801 for (transfer = (struct Transfer *)context->transfers; transfer != NULL; transfer = transfer->next) 802 if (transfer->property == xpropev->atom) 803 goto found; 804 return CTRLSEL_NONE; 805 found: 806 status = getcontent( 807 transfer->target, 808 xpropev->display, 809 xpropev->window, 810 xpropev->atom 811 ); 812 switch (status) { 813 case CONTENT_ERROR: 814 case CONTENT_INCR: 815 return CTRLSEL_ERROR; 816 case CONTENT_SUCCESS: 817 return CTRLSEL_INTERNAL; 818 case CONTENT_ZERO: 819 context->ndone++; 820 deltransfer(context, transfer); 821 break; 822 } 823 return CTRLSEL_INTERNAL; 824 } 825 826 int 827 ctrlsel_receive(CtrlSelContext *context, XEvent *xev) 828 { 829 int status; 830 831 if (xev->type == SelectionNotify) 832 status = receiveinit(context, xev); 833 else if (xev->type == PropertyNotify) 834 status = receiveincr(context, xev); 835 else 836 return CTRLSEL_NONE; 837 if (status == CTRLSEL_INTERNAL) { 838 if (context->ndone >= context->ntargets) { 839 status = CTRLSEL_RECEIVED; 840 goto done; 841 } 842 } else if (status == CTRLSEL_ERROR) { 843 freebuffers(context); 844 freetransferences(context); 845 } 846 done: 847 if (status == CTRLSEL_RECEIVED) 848 freetransferences(context); 849 return status; 850 } 851 852 static int 853 sendinit(CtrlSelContext *context, XEvent *xev) 854 { 855 XSelectionRequestEvent *xreqev; 856 XSelectionEvent xselev; 857 unsigned long natoms, i; 858 Atom *pairs; 859 Atom pair[PAIR_LAST]; 860 Atom multiple, atom_pair; 861 Bool success; 862 863 xreqev = &xev->xselectionrequest; 864 if (xreqev->selection != context->selection) 865 return CTRLSEL_NONE; 866 multiple = XInternAtom(context->display, MULTIPLE, False); 867 atom_pair = XInternAtom(context->display, ATOM_PAIR, False); 868 xselev = (XSelectionEvent){ 869 .type = SelectionNotify, 870 .display = xreqev->display, 871 .requestor = xreqev->requestor, 872 .selection = xreqev->selection, 873 .time = xreqev->time, 874 .target = xreqev->target, 875 .property = None, 876 }; 877 if (xreqev->time != CurrentTime && xreqev->time < context->time) { 878 /* 879 * According to ICCCM, the selection owner 880 * should compare the timestamp with the period 881 * it has owned the selection and, if the time 882 * is outside, refuse the `SelectionRequest` by 883 * sending the requestor window a 884 * `SelectionNotify` event with the property set 885 * to `None` (by means of a `SendEvent` request 886 * with an empty event mask). 887 */ 888 goto done; 889 } 890 if (xreqev->target == multiple) { 891 if (xreqev->property == None) 892 goto done; 893 natoms = getatomsprop( 894 xreqev->display, 895 xreqev->requestor, 896 xreqev->property, 897 atom_pair, 898 &pairs 899 ); 900 } else { 901 pair[PAIR_TARGET] = xreqev->target; 902 pair[PAIR_PROPERTY] = xreqev->property; 903 pairs = pair; 904 natoms = 2; 905 } 906 success = True; 907 for (i = 0; i < natoms; i += 2) { 908 if (!convert(context, xreqev->requestor, 909 pairs[i + PAIR_TARGET], 910 pairs[i + PAIR_PROPERTY])) { 911 success = False; 912 pairs[i + PAIR_PROPERTY] = None; 913 } 914 } 915 if (xreqev->target == multiple) { 916 XChangeProperty( 917 xreqev->display, 918 xreqev->requestor, 919 xreqev->property, 920 atom_pair, 921 32, PropModeReplace, 922 (unsigned char *)pairs, 923 natoms 924 ); 925 free(pairs); 926 } 927 if (success) { 928 if (xreqev->property == None) { 929 xselev.property = xreqev->target; 930 } else { 931 xselev.property = xreqev->property; 932 } 933 } 934 done: 935 XSendEvent( 936 xreqev->display, 937 xreqev->requestor, 938 False, 939 NoEventMask, 940 (XEvent *)&xselev 941 ); 942 return CTRLSEL_INTERNAL; 943 } 944 945 static int 946 sendlost(CtrlSelContext *context, XEvent *xev) 947 { 948 XSelectionClearEvent *xclearev; 949 950 xclearev = &xev->xselectionclear; 951 if (xclearev->selection == context->selection && 952 xclearev->window == context->window) { 953 return CTRLSEL_LOST; 954 } 955 return CTRLSEL_NONE; 956 } 957 958 static int 959 senddestroy(CtrlSelContext *context, XEvent *xev) 960 { 961 struct Transfer *transfer; 962 XDestroyWindowEvent *xdestroyev; 963 964 xdestroyev = &xev->xdestroywindow; 965 for (transfer = context->transfers; transfer != NULL; transfer = transfer->next) 966 if (transfer->requestor == xdestroyev->window) 967 deltransfer(context, transfer); 968 return CTRLSEL_NONE; 969 } 970 971 static int 972 sendincr(CtrlSelContext *context, XEvent *xev) 973 { 974 struct Transfer *transfer; 975 XPropertyEvent *xpropev; 976 unsigned long size; 977 978 xpropev = &xev->xproperty; 979 if (xpropev->state != PropertyDelete) 980 return CTRLSEL_NONE; 981 for (transfer = context->transfers; transfer != NULL; transfer = transfer->next) 982 if (transfer->property == xpropev->atom && 983 transfer->requestor == xpropev->window) 984 goto found; 985 return CTRLSEL_NONE; 986 found: 987 if (transfer->size >= transfer->target->bufsize) 988 transfer->size = transfer->target->bufsize; 989 size = transfer->target->bufsize - transfer->size; 990 if (size > context->selmaxsize) 991 size = context->selmaxsize; 992 XChangeProperty( 993 xpropev->display, 994 xpropev->window, 995 xpropev->atom, 996 transfer->target->target, 997 transfer->target->format, 998 PropModeReplace, 999 transfer->target->buffer + transfer->size, 1000 size / nbytes(transfer->target->format) 1001 ); 1002 if (transfer->size >= transfer->target->bufsize) { 1003 deltransfer(context, transfer); 1004 } else { 1005 transfer->size += size; 1006 } 1007 return CTRLSEL_INTERNAL; 1008 } 1009 1010 int 1011 ctrlsel_send(CtrlSelContext *context, XEvent *xev) 1012 { 1013 int status; 1014 1015 if (xev->type == SelectionRequest) 1016 status = sendinit(context, xev); 1017 else if (xev->type == SelectionClear) 1018 status = sendlost(context, xev); 1019 else if (xev->type == DestroyNotify) 1020 status = senddestroy(context, xev); 1021 else if (xev->type == PropertyNotify) 1022 status = sendincr(context, xev); 1023 else 1024 return CTRLSEL_NONE; 1025 if (status == CTRLSEL_LOST || status == CTRLSEL_ERROR) { 1026 status = CTRLSEL_LOST; 1027 freetransferences(context); 1028 } 1029 return status; 1030 } 1031 1032 void 1033 ctrlsel_cancel(CtrlSelContext *context) 1034 { 1035 if (context == NULL) 1036 return; 1037 freebuffers(context); 1038 freetransferences(context); 1039 free(context); 1040 } 1041 1042 void 1043 ctrlsel_disown(CtrlSelContext *context) 1044 { 1045 if (context == NULL) 1046 return; 1047 freetransferences(context); 1048 free(context); 1049 } 1050 1051 static Bool 1052 dndpred(Display *display, XEvent *event, XPointer p) 1053 { 1054 struct PredArg *arg; 1055 struct Transfer *transfer; 1056 1057 arg = (struct PredArg *)p; 1058 switch (event->type) { 1059 case KeyPress: 1060 case KeyRelease: 1061 if (event->xkey.display == display && 1062 event->xkey.window == arg->window) 1063 return True; 1064 break; 1065 case ButtonPress: 1066 case ButtonRelease: 1067 if (event->xbutton.display == display && 1068 event->xbutton.window == arg->window) 1069 return True; 1070 break; 1071 case MotionNotify: 1072 if (event->xmotion.display == display && 1073 event->xmotion.window == arg->window) 1074 return True; 1075 break; 1076 case DestroyNotify: 1077 if (event->xdestroywindow.display == display && 1078 event->xdestroywindow.window == arg->window) 1079 return True; 1080 break; 1081 case UnmapNotify: 1082 if (event->xunmap.display == display && 1083 event->xunmap.window == arg->window) 1084 return True; 1085 break; 1086 case SelectionClear: 1087 if (event->xselectionclear.display == display && 1088 event->xselectionclear.window == arg->window) 1089 return True; 1090 break; 1091 case SelectionRequest: 1092 if (event->xselectionrequest.display == display && 1093 event->xselectionrequest.owner == arg->window) 1094 return True; 1095 break; 1096 case ClientMessage: 1097 if (event->xclient.display == display && 1098 event->xclient.window == arg->window && 1099 event->xclient.message_type == arg->message_type) 1100 return True; 1101 break; 1102 case PropertyNotify: 1103 if (event->xproperty.display != display || 1104 event->xproperty.state != PropertyDelete) 1105 return False; 1106 for (transfer = arg->context->transfers; 1107 transfer != NULL; 1108 transfer = transfer->next) { 1109 if (transfer->property == event->xproperty.atom && 1110 transfer->requestor == event->xproperty.window) { 1111 return True; 1112 } 1113 } 1114 break; 1115 default: 1116 break; 1117 } 1118 return False; 1119 } 1120 1121 #define SOME(a, b, c) ((a) != None ? (a) : ((b) != None ? (b) : (c))) 1122 1123 static Cursor 1124 getcursor(Cursor cursors[CURSOR_LAST], int type) 1125 { 1126 switch (type) { 1127 case CURSOR_TARGET: 1128 case CURSOR_DRAG: 1129 return SOME(cursors[CURSOR_DRAG], cursors[CURSOR_TARGET], None); 1130 case CURSOR_PIRATE: 1131 case CURSOR_NODROP: 1132 return SOME(cursors[CURSOR_NODROP], cursors[CURSOR_PIRATE], None); 1133 case CURSOR_COPY: 1134 return SOME(cursors[CURSOR_COPY], cursors[CURSOR_DRAG], cursors[CURSOR_TARGET]); 1135 case CURSOR_MOVE: 1136 return SOME(cursors[CURSOR_MOVE], cursors[CURSOR_DRAG], cursors[CURSOR_TARGET]); 1137 case CURSOR_LINK: 1138 return SOME(cursors[CURSOR_LINK], cursors[CURSOR_DRAG], cursors[CURSOR_TARGET]); 1139 }; 1140 return None; 1141 } 1142 1143 static void 1144 initcursors(Display *display, Cursor cursors[CURSOR_LAST]) 1145 { 1146 cursors[CURSOR_TARGET] = XCreateFontCursor(display, XC_target); 1147 cursors[CURSOR_PIRATE] = XCreateFontCursor(display, XC_pirate); 1148 cursors[CURSOR_DRAG] = XcursorLibraryLoadCursor(display, "dnd-none"); 1149 cursors[CURSOR_COPY] = XcursorLibraryLoadCursor(display, "dnd-copy"); 1150 cursors[CURSOR_MOVE] = XcursorLibraryLoadCursor(display, "dnd-move"); 1151 cursors[CURSOR_LINK] = XcursorLibraryLoadCursor(display, "dnd-link"); 1152 cursors[CURSOR_NODROP] = XcursorLibraryLoadCursor(display, "forbidden"); 1153 } 1154 1155 static void 1156 freecursors(Display *display, Cursor cursors[CURSOR_LAST]) 1157 { 1158 int i; 1159 1160 for (i = 0; i < CURSOR_LAST; i++) { 1161 if (cursors[i] != None) { 1162 XFreeCursor(display, cursors[i]); 1163 } 1164 } 1165 } 1166 1167 static int 1168 querypointer(Display *display, Window window, int *retx, int *rety, Window *retwin) 1169 { 1170 Window root, child; 1171 unsigned int mask; 1172 int rootx, rooty; 1173 int x, y; 1174 int retval; 1175 1176 retval = XQueryPointer( 1177 display, 1178 window, 1179 &root, &child, 1180 &rootx, &rooty, 1181 &x, &y, 1182 &mask 1183 ); 1184 if (retwin != NULL) 1185 *retwin = child; 1186 if (retx != NULL) 1187 *retx = x; 1188 if (rety != NULL) 1189 *rety = y; 1190 return retval; 1191 } 1192 1193 static Window 1194 getdndwindowbelow(Display *display, Window root, Atom aware, Atom *version) 1195 { 1196 Atom *p; 1197 Window window; 1198 1199 /* 1200 * Query pointer location and return the window below it, 1201 * and the version of the XDND protocol it uses. 1202 */ 1203 *version = None; 1204 window = root; 1205 p = NULL; 1206 while (querypointer(display, window, NULL, NULL, &window)) { 1207 if (window == None) 1208 break; 1209 p = NULL; 1210 if (getatomsprop(display, window, aware, AnyPropertyType, &p) > 0) { 1211 *version = *p; 1212 XFree(p); 1213 return window; 1214 } 1215 } 1216 XFree(p); 1217 return None; 1218 } 1219 1220 CtrlSelContext * 1221 ctrlsel_dndwatch( 1222 Display *display, 1223 Window window, 1224 unsigned int actions, 1225 struct CtrlSelTarget targets[], 1226 unsigned long ntargets 1227 ) { 1228 CtrlSelContext *context; 1229 Atom version = XDND_VERSION; /* yes, version is an Atom */ 1230 Atom xdndaware, xdndselection; 1231 1232 xdndaware = XInternAtom(display, atomnames[XDND_AWARE], False); 1233 if (xdndaware == None) 1234 return NULL; 1235 xdndselection = XInternAtom(display, atomnames[XDND_SELECTION], False); 1236 if (xdndselection == None) 1237 return NULL; 1238 if ((context = malloc(sizeof(*context))) == NULL) 1239 return NULL; 1240 *context = (CtrlSelContext){ 1241 .display = display, 1242 .window = window, 1243 .selection = xdndselection, 1244 .time = CurrentTime, 1245 .targets = targets, 1246 .ntargets = ntargets, 1247 .selmaxsize = getselmaxsize(display), 1248 .ndone = 0, 1249 .transfers = NULL, 1250 .dndwindow = None, 1251 .dndactions = actions, 1252 .dndresult = 0x00, 1253 }; 1254 (void)XChangeProperty( 1255 display, 1256 window, 1257 xdndaware, 1258 XA_ATOM, 32, 1259 PropModeReplace, 1260 (unsigned char *)&version, 1261 1 1262 ); 1263 return context; 1264 } 1265 1266 static void 1267 finishdrop(CtrlSelContext *context) 1268 { 1269 long d[NCLIENTMSG_DATA]; 1270 unsigned long i; 1271 Atom finished; 1272 1273 if (context->dndwindow == None) 1274 return; 1275 finished = XInternAtom(context->display, atomnames[XDND_FINISHED], False); 1276 if (finished == None) 1277 return; 1278 for (i = 0; i < context->ntargets; i++) 1279 context->targets[i].action = context->dndresult; 1280 d[0] = context->window; 1281 d[1] = d[2] = d[3] = d[4] = 0; 1282 clientmsg(context->display, context->dndwindow, finished, d); 1283 context->dndwindow = None; 1284 } 1285 1286 int 1287 ctrlsel_dndreceive(CtrlSelContext *context, XEvent *event) 1288 { 1289 Atom atoms[XDND_ATOM_LAST]; 1290 Atom action; 1291 long d[NCLIENTMSG_DATA]; 1292 1293 if (!XInternAtoms(context->display, atomnames, XDND_ATOM_LAST, False, atoms)) 1294 return CTRLSEL_NONE; 1295 switch (ctrlsel_receive(context, event)) { 1296 case CTRLSEL_RECEIVED: 1297 finishdrop(context); 1298 return CTRLSEL_RECEIVED; 1299 case CTRLSEL_INTERNAL: 1300 case CTRLSEL_ERROR: 1301 return CTRLSEL_INTERNAL; 1302 default: 1303 break; 1304 } 1305 if (event->type != ClientMessage) 1306 return CTRLSEL_NONE; 1307 if (event->xclient.message_type == atoms[XDND_ENTER]) { 1308 context->dndwindow = (Window)event->xclient.data.l[0]; 1309 context->dndresult = 0x00; 1310 } else if (event->xclient.message_type == atoms[XDND_LEAVE]) { 1311 if ((Window)event->xclient.data.l[0] == None || 1312 (Window)event->xclient.data.l[0] != context->dndwindow) 1313 return CTRLSEL_NONE; 1314 context->dndwindow = None; 1315 } else if (event->xclient.message_type == atoms[XDND_DROP]) { 1316 if ((Window)event->xclient.data.l[0] == None || 1317 (Window)event->xclient.data.l[0] != context->dndwindow) 1318 return CTRLSEL_NONE; 1319 context->time = (Time)event->xclient.data.l[2]; 1320 (void)request(context); 1321 } else if (event->xclient.message_type == atoms[XDND_POSITION]) { 1322 if ((Window)event->xclient.data.l[0] == None || 1323 (Window)event->xclient.data.l[0] != context->dndwindow) 1324 return CTRLSEL_NONE; 1325 if (((Atom)event->xclient.data.l[4] == atoms[XDND_ACTION_COPY] && 1326 context->dndactions & CTRLSEL_COPY) || 1327 ((Atom)event->xclient.data.l[4] == atoms[XDND_ACTION_MOVE] && 1328 context->dndactions & CTRLSEL_MOVE) || 1329 ((Atom)event->xclient.data.l[4] == atoms[XDND_ACTION_LINK] && 1330 context->dndactions & CTRLSEL_LINK) || 1331 ((Atom)event->xclient.data.l[4] == atoms[XDND_ACTION_ASK] && 1332 context->dndactions & CTRLSEL_ASK) || 1333 ((Atom)event->xclient.data.l[4] == atoms[XDND_ACTION_PRIVATE] && 1334 context->dndactions & CTRLSEL_PRIVATE)) { 1335 action = (Atom)event->xclient.data.l[4]; 1336 } else { 1337 action = atoms[XDND_ACTION_COPY]; 1338 } 1339 d[0] = context->window; 1340 d[1] = 0x1; 1341 d[2] = 0; /* our rectangle is the entire screen */ 1342 d[3] = 0xFFFFFFFF; /* so we do not get lots of messages */ 1343 d[4] = action; 1344 if (action == atoms[XDND_ACTION_PRIVATE]) 1345 context->dndresult = CTRLSEL_PRIVATE; 1346 else if (action == atoms[XDND_ACTION_ASK]) 1347 context->dndresult = CTRLSEL_ASK; 1348 else if (action == atoms[XDND_ACTION_LINK]) 1349 context->dndresult = CTRLSEL_LINK; 1350 else if (action == atoms[XDND_ACTION_MOVE]) 1351 context->dndresult = CTRLSEL_MOVE; 1352 else 1353 context->dndresult = CTRLSEL_COPY; 1354 clientmsg( 1355 context->display, 1356 (Window)event->xclient.data.l[0], 1357 atoms[XDND_STATUS], 1358 d 1359 ); 1360 } else { 1361 return CTRLSEL_NONE; 1362 } 1363 return CTRLSEL_INTERNAL; 1364 } 1365 1366 void 1367 ctrlsel_dndclose(CtrlSelContext *context) 1368 { 1369 if (context == NULL) 1370 return; 1371 finishdrop(context); 1372 freebuffers(context); 1373 freetransferences(context); 1374 free(context); 1375 } 1376 1377 void 1378 ctrlsel_dnddisown(CtrlSelContext *context) 1379 { 1380 ctrlsel_disown(context); 1381 } 1382 1383 int 1384 ctrlsel_dndsend(CtrlSelContext *context, XEvent *event) 1385 { 1386 Atom finished; 1387 1388 finished = XInternAtom(context->display, atomnames[XDND_FINISHED], False); 1389 if (event->type == ClientMessage && 1390 event->xclient.message_type == finished && 1391 (Window)event->xclient.data.l[0] == context->dndwindow) { 1392 ctrlsel_dnddisown(context); 1393 return CTRLSEL_SENT; 1394 } 1395 return ctrlsel_send(context, event); 1396 } 1397 1398 int 1399 ctrlsel_dndown( 1400 Display *display, 1401 Window window, 1402 Window miniature, 1403 Time time, 1404 struct CtrlSelTarget targets[], 1405 unsigned long ntargets, 1406 CtrlSelContext **context_ret 1407 ) { 1408 CtrlSelContext *context; 1409 struct PredArg arg; 1410 XWindowAttributes wattr; 1411 XEvent event; 1412 Atom atoms[XDND_ATOM_LAST]; 1413 Cursor cursors[CURSOR_LAST] = { None, None }; 1414 Cursor cursor; 1415 Window lastwin, winbelow; 1416 Atom lastaction, action, version; 1417 long d[NCLIENTMSG_DATA]; 1418 int sendposition, retval, status, inside; 1419 int x, y, w, h; 1420 1421 *context_ret = NULL; 1422 if (display == NULL || window == None) 1423 return CTRLSEL_ERROR; 1424 if (!XGetWindowAttributes(display, window, &wattr)) 1425 return CTRLSEL_ERROR; 1426 if ((wattr.your_event_mask & StructureNotifyMask) == 0x00) 1427 return CTRLSEL_ERROR; 1428 if (wattr.map_state != IsViewable) 1429 return CTRLSEL_ERROR; 1430 if (!XInternAtoms(display, atomnames, XDND_ATOM_LAST, False, atoms)) 1431 return CTRLSEL_ERROR; 1432 context = ctrlsel_setowner( 1433 display, 1434 window, 1435 atoms[XDND_SELECTION], 1436 time, 1437 0, 1438 targets, 1439 ntargets 1440 ); 1441 if (context == NULL) 1442 return CTRLSEL_ERROR; 1443 d[0] = window; 1444 sendposition = 1; 1445 x = y = w = h = 0; 1446 retval = CTRLSEL_ERROR; 1447 lastaction = action = None; 1448 lastwin = None; 1449 arg = (struct PredArg){ 1450 .context = context, 1451 .window = window, 1452 .message_type = atoms[XDND_STATUS], 1453 }; 1454 initcursors(display, cursors); 1455 status = XGrabPointer( 1456 display, 1457 window, 1458 True, 1459 ButtonPressMask | ButtonMotionMask | 1460 ButtonReleaseMask | PointerMotionMask, 1461 GrabModeAsync, 1462 GrabModeAsync, 1463 None, 1464 None, 1465 time 1466 ); 1467 if (status != GrabSuccess) 1468 goto done; 1469 status = XGrabKeyboard( 1470 display, 1471 window, 1472 True, 1473 GrabModeAsync, 1474 GrabModeAsync, 1475 time 1476 ); 1477 if (status != GrabSuccess) 1478 goto done; 1479 if (miniature != None) 1480 XMapRaised(display, miniature); 1481 cursor = getcursor(cursors, CURSOR_DRAG); 1482 for (;;) { 1483 (void)XIfEvent(display, &event, &dndpred, (XPointer)&arg); 1484 switch (ctrlsel_send(context, &event)) { 1485 case CTRLSEL_LOST: 1486 retval = CTRLSEL_NONE; 1487 goto done; 1488 case CTRLSEL_INTERNAL: 1489 continue; 1490 default: 1491 break; 1492 } 1493 switch (event.type) { 1494 case KeyPress: 1495 case KeyRelease: 1496 if (event.xkey.keycode != 0 && 1497 event.xkey.keycode == XKeysymToKeycode(display, XK_Escape)) { 1498 retval = CTRLSEL_NONE; 1499 goto done; 1500 } 1501 break; 1502 case ButtonPress: 1503 case ButtonRelease: 1504 if (lastwin == None) { 1505 retval = CTRLSEL_NONE; 1506 } else if (lastwin == window) { 1507 retval = CTRLSEL_DROPSELF; 1508 } else { 1509 retval = CTRLSEL_DROPOTHER; 1510 d[1] = d[3] = d[4] = 0; 1511 d[2] = event.xbutton.time; 1512 clientmsg(display, lastwin, atoms[XDND_DROP], d); 1513 context->dndwindow = lastwin; 1514 } 1515 goto done; 1516 case MotionNotify: 1517 if (event.xmotion.time - time < MOTION_TIME) 1518 break; 1519 if (miniature != None) { 1520 XMoveWindow( 1521 display, 1522 miniature, 1523 event.xmotion.x_root + DND_DISTANCE, 1524 event.xmotion.y_root + DND_DISTANCE 1525 ); 1526 } 1527 inside = between(event.xmotion.x, event.xmotion.y, x, y, w, h); 1528 if ((lastaction != action || sendposition || !inside) 1529 && lastwin != None) { 1530 if (lastaction != None) 1531 d[4] = lastaction; 1532 else if (FLAG(event.xmotion.state, ControlMask|ShiftMask)) 1533 d[4] = atoms[XDND_ACTION_LINK]; 1534 else if (FLAG(event.xmotion.state, ShiftMask)) 1535 d[4] = atoms[XDND_ACTION_MOVE]; 1536 else if (FLAG(event.xmotion.state, ControlMask)) 1537 d[4] = atoms[XDND_ACTION_COPY]; 1538 else 1539 d[4] = atoms[XDND_ACTION_ASK]; 1540 d[1] = 0; 1541 d[2] = event.xmotion.x_root << 16; 1542 d[2] |= event.xmotion.y_root & 0xFFFF; 1543 d[3] = event.xmotion.time; 1544 clientmsg(display, lastwin, atoms[XDND_POSITION], d); 1545 sendposition = 1; 1546 } 1547 time = event.xmotion.time; 1548 lastaction = action; 1549 winbelow = getdndwindowbelow(display, wattr.root, atoms[XDND_AWARE], &version); 1550 if (winbelow == lastwin) 1551 break; 1552 sendposition = 1; 1553 x = y = w = h = 0; 1554 if (version > XDND_VERSION) 1555 version = XDND_VERSION; 1556 if (lastwin != None && lastwin != window) { 1557 d[1] = d[2] = d[3] = d[4] = 0; 1558 clientmsg(display, lastwin, atoms[XDND_LEAVE], d); 1559 } 1560 if (winbelow != None && winbelow != window) { 1561 d[1] = version; 1562 d[1] <<= 24; 1563 d[2] = ntargets > 0 ? targets[0].target : None; 1564 d[3] = ntargets > 1 ? targets[1].target : None; 1565 d[4] = ntargets > 2 ? targets[2].target : None; 1566 clientmsg(display, winbelow, atoms[XDND_ENTER], d); 1567 } 1568 if (winbelow == None) 1569 cursor = getcursor(cursors, CURSOR_NODROP); 1570 else if (FLAG(event.xmotion.state, ControlMask|ShiftMask)) 1571 cursor = getcursor(cursors, CURSOR_LINK); 1572 else if (FLAG(event.xmotion.state, ShiftMask)) 1573 cursor = getcursor(cursors, CURSOR_MOVE); 1574 else if (FLAG(event.xmotion.state, ControlMask)) 1575 cursor = getcursor(cursors, CURSOR_COPY); 1576 else 1577 cursor = getcursor(cursors, CURSOR_DRAG); 1578 XDefineCursor(display, window, cursor); 1579 lastwin = winbelow; 1580 lastaction = action = None; 1581 break; 1582 case ClientMessage: 1583 if ((Window)event.xclient.data.l[0] != lastwin) 1584 break; 1585 sendposition = (event.xclient.data.l[1] & 0x02); 1586 if (event.xclient.data.l[1] & 0x01) 1587 XDefineCursor(display, window, cursor); 1588 else 1589 XDefineCursor(display, window, getcursor(cursors, CURSOR_NODROP)); 1590 x = event.xclient.data.l[2] >> 16; 1591 y = event.xclient.data.l[2] & 0xFFF; 1592 w = event.xclient.data.l[3] >> 16; 1593 h = event.xclient.data.l[3] & 0xFFF; 1594 if ((Atom)event.xclient.data.l[4] != None) 1595 action = (Atom)event.xclient.data.l[4]; 1596 else 1597 action = atoms[XDND_ACTION_COPY]; 1598 break; 1599 case DestroyNotify: 1600 case UnmapNotify: 1601 XPutBackEvent(display, &event); 1602 retval = CTRLSEL_ERROR; 1603 goto done; 1604 default: 1605 break; 1606 } 1607 } 1608 done: 1609 XUndefineCursor(display, window); 1610 if (miniature != None) 1611 XUnmapWindow(display, miniature); 1612 XUngrabPointer(display, CurrentTime); 1613 XUngrabKeyboard(display, CurrentTime); 1614 freecursors(display, cursors); 1615 if (retval != CTRLSEL_DROPOTHER) { 1616 ctrlsel_dnddisown(context); 1617 context = NULL; 1618 } 1619 *context_ret = context; 1620 return retval; 1621 }