Introduction
Introduction Statistics Contact Development Disclaimer Help
ctrlsel.c - ledit - Text editor (WIP)
git clone git://lumidify.org/ledit.git (fast, but not encrypted)
git clone https://lumidify.org/ledit.git (encrypted, but very slow)
git clone git://4kcetb7mo7hj6grozzybxtotsub5bempzo4lirzc3437amof2c2impyd.onion/…
Log
Files
Refs
README
LICENSE
---
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 …
23 #define XDND_VERSION 5 /* XDND protocol version */
24 #define NCLIENTMSG_DATA 5 /* number of members on a the .d…
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…
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->transf…
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, …
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, Windo…
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 pro…
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 MULTIP…
463 if ((supported = calloc(nsupported, sizeof(*supported)))…
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, pr…
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, Fal…
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, Fa…
698 .format = 32,
699 .data.l[0] = time, /* timestamp */
700 .data.l[1] = selection, /* manager selec…
701 .data.l[2] = window, /* window owning…
702 .data.l[3] = 0, /* manager-speci…
703 .data.l[4] = 0, /* manager-speci…
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…
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->reque…
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 …
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 =…
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 =…
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.wind…
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]…
1130 case CURSOR_PIRATE:
1131 case CURSOR_NODROP:
1132 return SOME(cursors[CURSOR_NODROP], cursors[CURSOR_PIRAT…
1133 case CURSOR_COPY:
1134 return SOME(cursors[CURSOR_COPY], cursors[CURSOR_DRAG], …
1135 case CURSOR_MOVE:
1136 return SOME(cursors[CURSOR_MOVE], cursors[CURSOR_DRAG], …
1137 case CURSOR_LINK:
1138 return SOME(cursors[CURSOR_LINK], cursors[CURSOR_DRAG], …
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-no…
1149 cursors[CURSOR_COPY] = XcursorLibraryLoadCursor(display, "dnd-co…
1150 cursors[CURSOR_MOVE] = XcursorLibraryLoadCursor(display, "dnd-mo…
1151 cursors[CURSOR_LINK] = XcursorLibraryLoadCursor(display, "dnd-li…
1152 cursors[CURSOR_NODROP] = XcursorLibraryLoadCursor(display, "forb…
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, Wind…
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 *versi…
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…
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], …
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…
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, F…
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->dndwind…
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->dndwind…
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->dndwind…
1324 return CTRLSEL_NONE;
1325 if (((Atom)event->xclient.data.l[4] == atoms[XDND_ACTION…
1326 context->dndactions & CTRLSEL_COPY) ||
1327 ((Atom)event->xclient.data.l[4] == atoms[XDND_ACTION…
1328 context->dndactions & CTRLSEL_MOVE) ||
1329 ((Atom)event->xclient.data.l[4] == atoms[XDND_ACTION…
1330 context->dndactions & CTRLSEL_LINK) ||
1331 ((Atom)event->xclient.data.l[4] == atoms[XDND_ACTION…
1332 context->dndactions & CTRLSEL_ASK) ||
1333 ((Atom)event->xclient.data.l[4] == atoms[XDND_ACTION…
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 s…
1342 d[3] = 0xFFFFFFFF; /* so we do not get lots of mess…
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…
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, ato…
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(displ…
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_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_DISTA…
1524 event.xmotion.y_root + DND_DISTA…
1525 );
1526 }
1527 inside = between(event.xmotion.x, event.xmotion.…
1528 if ((lastaction != action || sendposition || !in…
1529 && lastwin != None) {
1530 if (lastaction != None)
1531 d[4] = lastaction;
1532 else if (FLAG(event.xmotion.state, Contr…
1533 d[4] = atoms[XDND_ACTION_LINK];
1534 else if (FLAG(event.xmotion.state, Shift…
1535 d[4] = atoms[XDND_ACTION_MOVE];
1536 else if (FLAG(event.xmotion.state, Contr…
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_P…
1545 sendposition = 1;
1546 }
1547 time = event.xmotion.time;
1548 lastaction = action;
1549 winbelow = getdndwindowbelow(display, wattr.root…
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_L…
1559 }
1560 if (winbelow != None && winbelow != window) {
1561 d[1] = version;
1562 d[1] <<= 24;
1563 d[2] = ntargets > 0 ? targets[0].target …
1564 d[3] = ntargets > 1 ? targets[1].target …
1565 d[4] = ntargets > 2 ? targets[2].target …
1566 clientmsg(display, winbelow, atoms[XDND_…
1567 }
1568 if (winbelow == None)
1569 cursor = getcursor(cursors, CURSOR_NODRO…
1570 else if (FLAG(event.xmotion.state, ControlMask|S…
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…
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 }
You are viewing proxied material from lumidify.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.