added extension for mouseoffset in dnd
[wmaker-crm.git] / WINGs / dragsource.c
blob357f3e8e46372274179407a9988f6649a6df4c09
2 #include "../src/config.h"
4 #include <X11/Xlib.h>
5 #include <X11/Xutil.h>
6 #include <X11/Xatom.h>
8 #ifdef SHAPE
9 #include <X11/extensions/shape.h>
10 #endif
12 #include <math.h>
15 #include "WINGsP.h"
20 #define SPIT(a)
23 #define IS_DROPPABLE(view) (view!=NULL && view->droppableTypes!=NULL && \
24 view->dragDestinationProcs!=NULL)
27 static Atom operationToAction(WMScreen *scr, WMDragOperationType operation);
28 static WMDragOperationType actionToOperation(WMScreen *scr, Atom action);
30 static Bool _XErrorOccured = False;
34 static unsigned defDraggingSourceOperation(WMView *self, Bool local)
36 return WDOperationCopy;
40 static void defBeganDragImage(WMView *self, WMPixmap *image, WMPoint point)
45 static void defEndedDragImage(WMView *self, WMPixmap *image, WMPoint point,
46 Bool deposited)
50 static WMData* defFetchDragData(WMView *self, char *type)
52 return NULL;
56 void
57 WMSetViewDragSourceProcs(WMView *view, WMDragSourceProcs *procs)
59 if (view->dragSourceProcs)
60 free(view->dragSourceProcs);
61 view->dragSourceProcs = wmalloc(sizeof(WMDragSourceProcs));
63 *view->dragSourceProcs = *procs;
65 if (procs->draggingSourceOperation == NULL) {
66 view->dragSourceProcs->draggingSourceOperation = defDraggingSourceOperation;
68 if (procs->beganDragImage == NULL) {
69 view->dragSourceProcs->beganDragImage = defBeganDragImage;
71 if (procs->endedDragImage == NULL) {
72 view->dragSourceProcs->endedDragImage = defEndedDragImage;
74 if (procs->fetchDragData == NULL) {
75 view->dragSourceProcs->fetchDragData = defFetchDragData;
80 /***********************************************************************/
83 static int
84 handleXError(Display *dpy, XErrorEvent *ev)
86 _XErrorOccured = True;
88 return 1;
92 static void
93 protectBlock(Bool begin)
95 static void *oldHandler = NULL;
97 if (begin) {
98 oldHandler = XSetErrorHandler(handleXError);
99 } else {
100 XSetErrorHandler(oldHandler);
107 static Window
108 makeDragIcon(WMScreen *scr, WMPixmap *pixmap)
110 Window window;
111 WMSize size;
112 unsigned long flags;
113 XSetWindowAttributes attribs;
114 Pixmap pix, mask;
116 if (!pixmap) {
117 pixmap = scr->defaultObjectIcon;
119 size = WMGetPixmapSize(pixmap);
120 pix = pixmap->pixmap;
121 mask = pixmap->mask;
123 flags = CWSaveUnder|CWBackPixmap|CWOverrideRedirect|CWColormap;
124 attribs.save_under = True;
125 attribs.background_pixmap = pix;
126 attribs.override_redirect = True;
127 attribs.colormap = scr->colormap;
128 window = XCreateWindow(scr->display, scr->rootWin, 0, 0, size.width,
129 size.height, 0, scr->depth, InputOutput,
130 scr->visual, flags, &attribs);
132 #ifdef SHAPE
133 if (mask) {
134 XShapeCombineMask(scr->display, window, ShapeBounding, 0, 0, mask,
135 ShapeSet);
137 #endif
139 return window;
143 static void
144 slideWindow(Display *dpy, Window win, int srcX, int srcY, int dstX, int dstY)
146 double x, y, dx, dy;
147 int i;
148 int iterations;
150 iterations = WMIN(25, WMAX(abs(dstX-srcX), abs(dstY-srcY)));
152 x = srcX;
153 y = srcY;
155 dx = (double)(dstX-srcX)/iterations;
156 dy = (double)(dstY-srcY)/iterations;
158 for (i = 0; i <= iterations; i++) {
159 XMoveWindow(dpy, win, x, y);
160 XFlush(dpy);
162 wusleep(800);
164 x += dx;
165 y += dy;
170 static Window
171 findChildInWindow(Display *dpy, Window toplevel, int x, int y)
173 Window foo, bar;
174 Window *children;
175 unsigned nchildren;
176 int i;
178 if (!XQueryTree(dpy, toplevel, &foo, &bar,
179 &children, &nchildren) || children == NULL) {
180 return None;
183 /* first window that contains the point is the one */
184 for (i = nchildren-1; i >= 0; i--) {
185 XWindowAttributes attr;
187 if (XGetWindowAttributes(dpy, children[i], &attr)
188 && attr.map_state == IsViewable
189 && x >= attr.x && y >= attr.y
190 && x < attr.x + attr.width && y < attr.y + attr.height) {
191 Window child, tmp;
193 tmp = children[i];
195 child = findChildInWindow(dpy, tmp, x - attr.x, y - attr.y);
197 XFree(children);
199 if (child == None)
200 return tmp;
201 else
202 return child;
206 XFree(children);
207 return None;
211 static WMView*
212 findViewInToplevel(Display *dpy, Window toplevel, int x, int y)
214 Window child;
216 child = findChildInWindow(dpy, toplevel, x, y);
218 if (child != None) {
219 return W_GetViewForXWindow(dpy, child);
220 } else {
221 return NULL;
227 static Window
228 lookForToplevel(WMScreen *scr, Window window, Bool *isAware)
230 Window toplevel = None;
231 Atom *atoms;
232 int j, count;
234 *isAware = False;
236 atoms = XListProperties(scr->display, window, &count);
237 for (j = 0; j < count; j++) {
238 if (atoms[j] == scr->wmStateAtom) {
239 toplevel = window;
240 } else if (atoms[j] == scr->xdndAwareAtom) {
241 *isAware = True;
244 if (atoms)
245 XFree(atoms);
247 if (toplevel == None) {
248 Window *children;
249 Window foo, bar;
250 unsigned nchildren;
252 if (!XQueryTree(scr->display, window, &foo, &bar,
253 &children, &nchildren) || children == NULL) {
254 return None;
257 for (j = 0; j < nchildren; j++) {
258 toplevel = lookForToplevel(scr, children[j], isAware);
259 if (toplevel != None)
260 break;
263 XFree(children);
268 return toplevel;
273 static Window
274 findToplevelUnderDragPointer(WMScreen *scr, int x, int y, Window iconWindow)
276 Window foo, bar;
277 Window *children;
278 unsigned nchildren;
279 Bool overSomething = False;
280 int i;
282 if (!XQueryTree(scr->display, scr->rootWin, &foo, &bar,
283 &children, &nchildren) || children == NULL) {
284 SPIT("couldnt query tree!");
285 return None;
288 /* try to find the window below the iconWindow by traversing
289 * the whole window list */
291 /* first find the position of the iconWindow */
292 for (i = nchildren-1; i >= 0; i--) {
293 if (children[i] == iconWindow) {
294 i--;
295 break;
298 if (i <= 0) {
299 XFree(children);
300 return scr->rootWin;
303 /* first window that contains the point is the one */
304 for (; i >= 0; i--) {
305 XWindowAttributes attr;
307 if (XGetWindowAttributes(scr->display, children[i], &attr)
308 && attr.map_state == IsViewable
309 && x >= attr.x && y >= attr.y
310 && x < attr.x + attr.width && y < attr.y + attr.height) {
311 Window toplevel;
312 Bool isaware;
314 overSomething = True;
316 toplevel = lookForToplevel(scr, children[i], &isaware);
318 XFree(children);
320 if (isaware)
321 return toplevel;
322 else
323 return None;
327 XFree(children);
328 if (!overSomething)
329 return scr->rootWin;
330 else
331 return None;
339 static void
340 sendClientMessage(Display *dpy, Window win, Atom message,
341 unsigned data1, unsigned data2, unsigned data3,
342 unsigned data4, unsigned data5)
344 XEvent ev;
346 ev.type = ClientMessage;
347 ev.xclient.message_type = message;
348 ev.xclient.format = 32;
349 ev.xclient.window = win;
350 ev.xclient.data.l[0] = data1;
351 ev.xclient.data.l[1] = data2;
352 ev.xclient.data.l[2] = data3;
353 ev.xclient.data.l[3] = data4;
354 ev.xclient.data.l[4] = data5;
356 XSendEvent(dpy, win, False, 0, &ev);
357 XFlush(dpy);
363 static unsigned
364 notifyPosition(WMScreen *scr, WMDraggingInfo *info)
366 Atom action = operationToAction(scr, info->sourceOperation);
368 sendClientMessage(scr->display, info->destinationWindow,
369 scr->xdndPositionAtom,
370 info->sourceWindow,
371 0, /* reserved */
372 info->location.x<<16|info->location.y,
373 info->timestamp,
374 action/* operation */);
376 return 0;
381 static void
382 notifyDrop(WMScreen *scr, WMDraggingInfo *info)
384 sendClientMessage(scr->display, info->destinationWindow,
385 scr->xdndDropAtom,
386 info->sourceWindow,
387 0, /* reserved */
388 info->timestamp,
389 0, 0);
394 static void
395 notifyDragLeave(WMScreen *scr, WMDraggingInfo *info)
397 sendClientMessage(scr->display, info->destinationWindow,
398 scr->xdndLeaveAtom,
399 info->sourceWindow, 0, 0, 0, 0);
404 static unsigned
405 notifyDragEnter(WMScreen *scr, WMDraggingInfo *info)
407 unsigned d;
409 d = XDND_VERSION << 24;
411 sendClientMessage(scr->display, info->destinationWindow,
412 scr->xdndEnterAtom,
413 info->sourceWindow, d, 0, 0, 0);
415 return 0;
419 static void
420 translateCoordinates(WMScreen *scr, Window target, int fromX, int fromY,
421 int *x, int *y)
423 Window child;
425 XTranslateCoordinates(scr->display, scr->rootWin, target,
426 fromX, fromY, x, y, &child);
430 static void
431 updateDraggingInfo(WMScreen *scr, WMDraggingInfo *info, WMSize offset,
432 XEvent *event, Window iconWindow)
434 Window toplevel;
436 if (event->type == MotionNotify) {
437 info->imageLocation.x = event->xmotion.x_root-offset.width;
438 info->imageLocation.y = event->xmotion.y_root-offset.height;
440 info->location.x = event->xmotion.x_root;
441 info->location.y = event->xmotion.y_root;
442 /* info->timestamp = event->xmotion.time;*/
444 } else if (event->type == ButtonRelease) {
445 info->imageLocation.x = event->xbutton.x_root-offset.width;
446 info->imageLocation.y = event->xbutton.y_root-offset.height;
448 info->location.x = event->xbutton.x_root;
449 info->location.y = event->xbutton.y_root;
450 /* info->timestamp = event->xbutton.time;*/
453 toplevel = findToplevelUnderDragPointer(scr,
454 info->location.x,
455 info->location.y,
456 iconWindow);
457 info->destinationWindow = toplevel;
463 static void
464 processMotion(WMScreen *scr, WMDraggingInfo *info, WMDraggingInfo *oldInfo,
465 WMRect *rect, unsigned currentAction)
467 unsigned action;
469 if (info->destinationWindow == None) { /* entered an unsupporeted window */
471 if (oldInfo->destinationWindow != None
472 && oldInfo->destinationWindow != scr->rootWin) {
473 SPIT("left window");
475 notifyDragLeave(scr, oldInfo);
478 } else if (info->destinationWindow == scr->rootWin) {
480 if (oldInfo->destinationWindow != None
481 && oldInfo->destinationWindow != scr->rootWin) {
482 SPIT("left window to root");
484 notifyDragLeave(scr, oldInfo);
485 } else {
486 /* nothing */
489 } else if (oldInfo->destinationWindow != info->destinationWindow) {
491 if (oldInfo->destinationWindow != None
492 && oldInfo->destinationWindow != scr->rootWin) {
493 notifyDragLeave(scr, oldInfo);
494 SPIT("crossed");
495 } else {
496 SPIT("entered window");
499 action = notifyDragEnter(scr, info);
501 } else {
503 #define LEFT_RECT(r, X, Y) (X < r->pos.x || Y < r->pos.y \
504 || X >= r->pos.x + r->size.width \
505 || Y >= r->pos.y + r->size.height)
507 if (rect->size.width == 0 ||
508 (LEFT_RECT(rect, info->location.x, info->location.y))) {
510 action = notifyPosition(scr, info);
512 rect->size.width = 0;
519 static WMData*
520 convertSelection(WMView *view, Atom selection, Atom target,
521 void *cdata, Atom *type)
523 WMScreen *scr = W_VIEW_SCREEN(view);
524 WMData *data;
525 char *typeName = XGetAtomName(scr->display, target);
527 *type = target;
529 data = view->dragSourceProcs->fetchDragData(view, typeName);
531 if (typeName != NULL)
532 XFree(typeName);
534 return data;
538 static void
539 selectionLost(WMView *view, Atom selection, void *cdata)
541 if (W_VIEW_SCREEN(view)->dragSourceView == view) {
542 wwarning("DND selection lost during drag operation...");
543 W_VIEW_SCREEN(view)->dragSourceView = NULL;
548 static void
549 selectionDone(WMView *view, Atom selection, Atom target, void *cdata)
558 static void
559 setMouseOffsetHint(WMView *view, WMSize mouseOffset)
561 WMScreen *scr = W_VIEW_SCREEN(view);
562 long hint[2];
565 * Tell the offset from the topleft corner of the icon being
566 * dragged. Not from XDND, but it's backwards compatible.
569 hint[0] = mouseOffset.width;
570 hint[1] = mouseOffset.height;
572 XChangeProperty(scr->display, W_VIEW_DRAWABLE(view),
573 scr->wmIconDragOffsetAtom, XA_INTEGER, 32,
574 PropModeReplace, (unsigned char*)hint, 2);
578 static Bool
579 getMouseOffsetHint(WMScreen *scr, Window source, WMSize *mouseOffset)
581 long *hint;
582 Atom type_ret;
583 int fmt_ret;
584 unsigned long nitems_ret, bytes_after_ret;
585 Bool ok = False;
588 hint = NULL;
590 XGetWindowProperty(scr->display, source,
591 scr->wmIconDragOffsetAtom, 0, 2, False, XA_INTEGER,
592 &type_ret, &fmt_ret, &nitems_ret, &bytes_after_ret,
593 (unsigned char **)&hint);
595 if (hint && nitems_ret == 2) {
596 mouseOffset->width = hint[0];
597 mouseOffset->height = hint[1];
598 ok = True;
600 if (hint)
601 XFree(hint);
603 return ok;
608 static void
609 timeoutCallback(void *data)
611 wwarning("drag & drop timed out while waiting for response from 0x%x\n",
612 (unsigned)data);
613 _XErrorOccured = 2;
617 * State Machine For Drag Source:
618 * ------------------------------
619 * Events
620 * State Call Mtn Ent Lea Crs BUp StA StR Fin TO
621 * 0) idle 1bu - - - - - - - - -
622 * 1) drag over target - 1au - 2cu 1cbu 5fu 3 4 1w -
623 * 2) drag over nothing - 2 1bu - - 0 - - 2w -
624 * 3) drag targ+accept - 3u - 2cu 1cbu 6f 3 4w 0z -
625 * 4) drag targ+reject - 4u - 2cu 1cbu 0 3w 4 0z -
626 * 5) waiting status - 5X 5X 5X 5X - 6f 0 0z 0w
627 * 6) dropped - - - - - - - - 0 0w
629 * Events:
630 * Call - called WMDragImageFromView()
631 * Mtn - Motion
632 * Ent - Enter droppable window
633 * Lea - Leave droppable window (or rectangle)
634 * Crs - Leave droppable window (or rectangle) and enter another
635 * BUp - Button Released
636 * StA - XdndStatus client msg with Accept drop
637 * StR - XdndStatus client msg with Reject drop
638 * Fin - XdndFinish client msg
639 * TO - timeout
641 * Actions:
642 * a - send update message
643 * b - send enter message
644 * c - send leave message
645 * d - release drag section info
646 * e - send drop message
647 * f - setup timeout
648 * u - update dragInfo
650 * X - ignore
651 * w - warn about unexpected reply
652 * z - abort operation.. unexpected reply
653 * - shouldnt happen
655 void
656 WMDragImageFromView(WMView *view, WMPixmap *image, char *dataTypes[],
657 WMPoint atLocation, WMSize mouseOffset, XEvent *event,
658 Bool slideBack)
660 WMScreen *scr = view->screen;
661 Display *dpy = scr->display;
662 WMView *toplevel = W_TopLevelOfView(view);
663 Window icon;
664 XEvent ev;
665 WMRect rect = {{0,0},{0,0}};
666 int ostate = -1;
667 int state;
668 int action = -1;
669 XColor black = {0, 0,0,0, DoRed|DoGreen|DoBlue};
670 XColor green = {0x0045b045, 0x4500,0xb000,0x4500, DoRed|DoGreen|DoBlue};
671 XColor back = {0, 0xffff,0xffff,0xffff, DoRed|DoGreen|DoBlue};
672 WMDraggingInfo dragInfo;
673 WMDraggingInfo oldDragInfo;
674 WMHandlerID timer = NULL;
675 static WMSelectionProcs handler = {
676 convertSelection,
677 selectionLost,
678 selectionDone
682 if (scr->dragSourceView != NULL)
683 return;
685 wassertr(view->dragSourceProcs != NULL);
688 /* prepare icon to be dragged */
689 if (image == NULL)
690 image = scr->defaultObjectIcon;
692 icon = makeDragIcon(scr, image);
694 XMoveWindow(dpy, icon, atLocation.x, atLocation.y);
695 XMapRaised(dpy, icon);
698 /* init dragging info */
700 scr->dragSourceView = view;
702 memset(&dragInfo, 0, sizeof(WMDraggingInfo));
703 memset(&oldDragInfo, 0, sizeof(WMDraggingInfo));
704 dragInfo.image = image;
705 dragInfo.sourceView = view;
706 dragInfo.sourceWindow = W_VIEW_DRAWABLE(toplevel);
708 dragInfo.destinationWindow = dragInfo.sourceWindow;
710 dragInfo.location.x = atLocation.x + mouseOffset.width;
711 dragInfo.location.y = atLocation.y + mouseOffset.height;
712 dragInfo.imageLocation = atLocation;
715 /* start pointer grab */
716 XGrabPointer(dpy, scr->rootWin, False,
717 ButtonPressMask|ButtonReleaseMask|ButtonMotionMask,
718 GrabModeSync, GrabModeAsync, None, scr->defaultCursor,
719 CurrentTime);
721 XFlush(dpy);
723 _XErrorOccured = False;
725 /* take ownership of XdndSelection */
726 if (!WMCreateSelectionHandler(view, scr->xdndSelectionAtom,
727 event->xmotion.time,
728 &handler, NULL)) {
729 wwarning("could not get ownership or DND selection");
730 return;
733 setMouseOffsetHint(toplevel, mouseOffset);
735 if (view->dragSourceProcs->beganDragImage != NULL) {
736 view->dragSourceProcs->beganDragImage(view, image, atLocation);
739 processMotion(scr, &dragInfo, &oldDragInfo, &rect, action);
741 state = 1;
743 while (state != 6 && state != 0 && !_XErrorOccured) {
744 XAllowEvents(dpy, SyncPointer, CurrentTime);
745 WMNextEvent(dpy, &ev);
747 switch (ev.type) {
748 case MotionNotify:
749 if (state >= 1 && state <= 4) {
750 while (XCheckTypedEvent(dpy, MotionNotify, &ev)) ;
752 protectBlock(True);
754 oldDragInfo = dragInfo;
756 updateDraggingInfo(scr, &dragInfo, mouseOffset, &ev, icon);
758 XMoveWindow(dpy, icon, dragInfo.imageLocation.x,
759 dragInfo.imageLocation.y);
761 processMotion(scr, &dragInfo, &oldDragInfo, &rect, action);
763 protectBlock(False);
765 /* XXXif entered a different destination, check the operation */
767 switch (state) {
768 case 1:
769 if (oldDragInfo.destinationWindow != None
770 && oldDragInfo.destinationWindow != scr->rootWin
771 && (dragInfo.destinationWindow == None
772 || dragInfo.destinationWindow == scr->rootWin)) {
773 /* left the droppable window */
774 state = 2;
775 action = -1;
777 break;
779 case 2:
780 if (dragInfo.destinationWindow != None
781 && dragInfo.destinationWindow != scr->rootWin) {
783 state = 1;
784 action = -1;
786 break;
788 case 3:
789 case 4:
790 if (oldDragInfo.destinationWindow != None
791 && oldDragInfo.destinationWindow != scr->rootWin
792 && (dragInfo.destinationWindow == None
793 || dragInfo.destinationWindow == scr->rootWin)) {
794 /* left the droppable window */
795 state = 2;
796 action = -1;
798 break;
801 break;
804 case ButtonRelease:
805 /* if (state >= 1 && state <= 4) */ {
807 protectBlock(True);
809 oldDragInfo = dragInfo;
811 updateDraggingInfo(scr, &dragInfo, mouseOffset, &ev, icon);
813 XMoveWindow(dpy, icon, dragInfo.imageLocation.x,
814 dragInfo.imageLocation.y);
816 if (state == 4 || state == 1) {
817 dragInfo.destinationWindow = None;
818 dragInfo.destView = NULL;
820 processMotion(scr, &dragInfo, &oldDragInfo, &rect, action);
822 dragInfo.timestamp = ev.xbutton.time;
824 protectBlock(False);
826 switch (state) {
827 case 1:
828 state = 5;
829 timer = WMAddTimerHandler(3000, timeoutCallback,
830 (void*)dragInfo.destinationWindow);
831 break;
832 case 2:
833 state = 0;
834 break;
835 case 3:
836 state = 6;
837 break;
838 case 4:
839 state = 0;
840 break;
843 break;
845 case ClientMessage:
846 if ((state == 1 || state == 3 || state == 4 || state == 5)
847 && ev.xclient.message_type == scr->xdndStatusAtom
848 && ev.xclient.data.l[0] == dragInfo.destinationWindow) {
850 if (ev.xclient.data.l[1] & 1) {
851 SPIT("got accept msg");
852 /* will accept drop */
853 switch (state) {
854 case 1:
855 case 3:
856 case 4:
857 state = 3;
858 break;
859 case 5:
860 if (timer) {
861 WMDeleteTimerHandler(timer);
862 timer = NULL;
864 state = 6;
865 break;
867 action = actionToOperation(scr, ev.xclient.data.l[4]);
868 } else {
869 SPIT("got reject msg");
870 switch (state) {
871 case 1:
872 case 3:
873 case 4:
874 state = 4;
875 break;
876 case 5:
877 state = 0;
878 if (timer) {
879 WMDeleteTimerHandler(timer);
880 timer = NULL;
882 break;
884 action = 0;
887 if (ev.xclient.data.l[1] & (1<<1)) {
888 rect.pos.x = ev.xclient.data.l[2] >> 16;
889 rect.pos.y = ev.xclient.data.l[2] & 0xffff;
890 rect.size.width = ev.xclient.data.l[3] >> 16;
891 rect.size.height = ev.xclient.data.l[3] & 0xffff;
892 } else {
893 rect.size.width = 0;
896 } else if ((state >= 1 && state <= 5)
897 && ev.xclient.message_type == scr->xdndFinishedAtom
898 && ev.xclient.window == dragInfo.destinationWindow) {
900 wwarning("drag source received unexpected XdndFinished message from %x",
901 (unsigned)dragInfo.destinationWindow);
903 if (state == 3 || state == 4 || state == 5) {
904 state = 0;
905 if (timer) {
906 WMDeleteTimerHandler(timer);
907 timer = NULL;
912 default:
913 WMHandleEvent(&ev);
914 break;
917 if (ostate != state) {
918 if (state == 3) {
919 XRecolorCursor(dpy, scr->defaultCursor, &green, &back);
920 } else if (ostate == 3) {
921 XRecolorCursor(dpy, scr->defaultCursor, &black, &back);
923 ostate = state;
927 if (timer) {
928 WMDeleteTimerHandler(timer);
929 timer = NULL;
930 } else if (_XErrorOccured) {
931 /* got a timeout, send leave */
932 notifyDragLeave(scr, &dragInfo);
935 XUngrabPointer(dpy, CurrentTime);
937 SPIT("exited main loop");
939 if (_XErrorOccured || state != 6) {
940 goto cancelled;
943 assert(dragInfo.destinationWindow != None);
945 protectBlock(True);
946 notifyDrop(scr, &dragInfo);
947 protectBlock(False);
949 if (_XErrorOccured)
950 goto cancelled;
953 SPIT("dropped");
956 XDestroyWindow(dpy, icon);
958 return;
960 cancelled:
961 scr->dragSourceView = NULL;
963 WMDeleteSelectionHandler(view, scr->xdndSelectionAtom,
964 event->xmotion.time);
966 if (slideBack) {
967 slideWindow(dpy, icon,
968 dragInfo.imageLocation.x, dragInfo.imageLocation.y,
969 atLocation.x, atLocation.y);
971 XDestroyWindow(dpy, icon);
972 if (view->dragSourceProcs->endedDragImage != NULL) {
973 view->dragSourceProcs->endedDragImage(view, image,
974 dragInfo.imageLocation,
975 False);
990 static Atom
991 operationToAction(WMScreen *scr, WMDragOperationType operation)
993 switch (operation) {
994 case WDOperationNone:
995 return None;
997 case WDOperationCopy:
998 return scr->xdndActionCopy;
1000 case WDOperationMove:
1001 return scr->xdndActionMove;
1003 case WDOperationLink:
1004 return scr->xdndActionLink;
1006 case WDOperationAsk:
1007 return scr->xdndActionAsk;
1009 case WDOperationPrivate:
1010 return scr->xdndActionPrivate;
1012 default:
1013 return None;
1018 static WMDragOperationType
1019 actionToOperation(WMScreen *scr, Atom action)
1021 if (action == scr->xdndActionCopy) {
1022 return WDOperationCopy;
1024 } else if (action == scr->xdndActionMove) {
1025 return WDOperationMove;
1027 } else if (action == scr->xdndActionLink) {
1028 return WDOperationLink;
1030 } else if (action == scr->xdndActionAsk) {
1031 return WDOperationAsk;
1033 } else if (action == scr->xdndActionPrivate) {
1034 return WDOperationPrivate;
1036 } else if (action == None) {
1038 return WDOperationCopy;
1039 } else {
1040 char *tmp = XGetAtomName(scr->display, action);
1042 wwarning("unknown XDND action %s from 0x%x", tmp,
1043 (unsigned)scr->dragInfo.sourceWindow);
1044 XFree(tmp);
1046 return WDOperationCopy;
1055 static Atom*
1056 getTypeList(Window window, XClientMessageEvent *event)
1058 int i = 0;
1059 Atom *types = NULL;
1061 if (event->data.l[1] & 1) { /* > 3 types */
1063 } else {
1064 types = wmalloc(4 * sizeof(Atom));
1065 if (event->data.l[2] != None)
1066 types[i++] = event->data.l[2];
1067 if (event->data.l[3] != None)
1068 types[i++] = event->data.l[3];
1069 if (event->data.l[4] != None)
1070 types[i++] = event->data.l[4];
1071 types[i] = 0;
1074 if (types[0] == 0) {
1075 wwarning("received invalid drag & drop type list");
1076 /*XXX return;*/
1079 return types;
1084 #define DISPATCH(view, func, info) (view)->dragDestinationProcs->func(view, info)
1088 static void
1089 receivedData(WMView *view, Atom selection, Atom target,
1090 Time timestamp, void *cdata, WMData *data)
1092 WMScreen *scr = W_VIEW_SCREEN(view);
1093 WMDraggingInfo *info = (WMDraggingInfo*)cdata;
1094 Bool res;
1096 res = view->dragDestinationProcs->performDragOperation(view, info, data);
1098 if (res) {
1099 DISPATCH(view, concludeDragOperation, info);
1102 /* send finished message */
1103 sendClientMessage(scr->display, info->sourceWindow,
1104 scr->xdndFinishedAtom,
1105 info->destinationWindow,
1106 0, 0, 0, 0);
1108 memset(&scr->dragInfo, 0, sizeof(WMDraggingInfo));
1113 void
1114 W_HandleDNDClientMessage(WMView *toplevel, XClientMessageEvent *event)
1116 WMScreen *scr = W_VIEW_SCREEN(toplevel);
1117 WMView *oldView = NULL;
1118 WMView *newView = NULL;
1119 unsigned operation = 0;
1120 int x, y;
1121 enum {
1122 WNothing,
1123 WEnter,
1124 WLeave,
1125 WCross, /* leave one and enter another */
1126 WUpdate,
1127 WDrop
1129 Window source;
1130 int what = WNothing;
1131 Bool sendStatus = False;
1134 source = scr->dragInfo.sourceWindow;
1135 oldView = scr->dragInfo.destView;
1137 if (event->message_type == scr->xdndFinishedAtom) {
1138 WMView *view = scr->dragSourceView;
1140 WMDeleteSelectionHandler(view, scr->xdndSelectionAtom,
1141 scr->dragInfo.timestamp);
1143 if (view->dragSourceProcs->endedDragImage != NULL) {
1144 view->dragSourceProcs->endedDragImage(view,
1145 scr->dragInfo.image,
1146 scr->dragInfo.imageLocation,
1147 True);
1150 scr->dragSourceView = NULL;
1152 return;
1157 if (event->message_type == scr->xdndEnterAtom) {
1158 Window foo, bar;
1159 int bla;
1160 unsigned ble;
1162 if (scr->dragInfo.sourceWindow != None) {
1163 puts("received Enter event in bad order");
1166 memset(&scr->dragInfo, 0, sizeof(WMDraggingInfo));
1169 if ((event->data.l[1] >> 24) > XDND_VERSION) {
1170 wwarning("received drag & drop request with unsupported version %i",
1171 (event->data.l[1] >> 24));
1172 return;
1175 scr->dragInfo.protocolVersion = event->data.l[1] >> 24;
1176 scr->dragInfo.sourceWindow = source = event->data.l[0];
1177 scr->dragInfo.destinationWindow = event->window;
1179 getMouseOffsetHint(scr, source, &scr->dragInfo.mouseOffset);
1181 /* XXX */
1182 scr->dragInfo.image = NULL;
1184 XQueryPointer(scr->display, scr->rootWin, &foo, &bar,
1185 &scr->dragInfo.location.x, &scr->dragInfo.location.y,
1186 &bla, &bla, &ble);
1188 scr->dragInfo.imageLocation = scr->dragInfo.location;
1189 scr->dragInfo.imageLocation.x -= scr->dragInfo.mouseOffset.width;
1190 scr->dragInfo.imageLocation.y -= scr->dragInfo.mouseOffset.height;
1192 translateCoordinates(scr, scr->dragInfo.destinationWindow,
1193 scr->dragInfo.location.x,
1194 scr->dragInfo.location.y, &x, &y);
1197 newView = findViewInToplevel(scr->display,
1198 scr->dragInfo.destinationWindow,
1199 x, y);
1201 } else if (event->message_type == scr->xdndPositionAtom
1202 && scr->dragInfo.sourceWindow == event->data.l[0]) {
1204 scr->dragInfo.location.x = event->data.l[2] >> 16;
1205 scr->dragInfo.location.y = event->data.l[2] & 0xffff;
1207 scr->dragInfo.imageLocation = scr->dragInfo.location;
1208 scr->dragInfo.imageLocation.x -= scr->dragInfo.mouseOffset.width;
1209 scr->dragInfo.imageLocation.y -= scr->dragInfo.mouseOffset.height;
1211 if (scr->dragInfo.protocolVersion >= 1) {
1212 scr->dragInfo.timestamp = event->data.l[3];
1213 scr->dragInfo.sourceOperation = actionToOperation(scr,
1214 event->data.l[4]);
1216 } else {
1217 scr->dragInfo.timestamp = CurrentTime;
1218 scr->dragInfo.sourceOperation = WDOperationCopy;
1221 translateCoordinates(scr, scr->dragInfo.destinationWindow,
1222 scr->dragInfo.location.x,
1223 scr->dragInfo.location.y, &x, &y);
1225 newView = findViewInToplevel(scr->display,
1226 scr->dragInfo.destinationWindow,
1227 x, y);
1229 } else if (event->message_type == scr->xdndLeaveAtom
1230 && scr->dragInfo.sourceWindow == event->data.l[0]) {
1232 memset(&scr->dragInfo, 0, sizeof(WMDraggingInfo));
1234 } else if (event->message_type == scr->xdndDropAtom
1235 && scr->dragInfo.sourceWindow == event->data.l[0]) {
1237 /* drop */
1238 if (oldView != NULL)
1239 what = WDrop;
1241 } else {
1242 return;
1247 * Now map the XDND events to WINGs events.
1250 if (what == WNothing) {
1251 if (IS_DROPPABLE(newView)) {
1252 if (!IS_DROPPABLE(oldView)) { /* entered */
1253 what = WEnter;
1254 } else if (oldView == newView) { /* updated */
1255 what = WUpdate;
1256 } else {
1257 what = WCross;
1259 } else {
1260 if (IS_DROPPABLE(oldView)) {
1261 what = WLeave;
1262 } else {
1263 /* just send rejection msg */
1264 sendStatus = True;
1271 switch (what) {
1273 case WEnter:
1274 scr->dragInfo.destView = newView;
1275 operation = DISPATCH(newView, draggingEntered, &scr->dragInfo);
1276 sendStatus = True;
1277 break;
1279 case WLeave:
1280 scr->dragInfo.destView = NULL;
1281 DISPATCH(oldView, draggingExited, &scr->dragInfo);
1282 sendStatus = True;
1283 operation = WDOperationNone;
1284 break;
1286 case WCross:
1287 DISPATCH(oldView, draggingExited, &scr->dragInfo);
1288 scr->dragInfo.destView = newView;
1289 operation = DISPATCH(newView, draggingEntered, &scr->dragInfo);
1290 sendStatus = True;
1291 break;
1293 case WUpdate:
1294 operation = DISPATCH(oldView, draggingUpdated, &scr->dragInfo);
1295 sendStatus = True;
1296 break;
1298 case WDrop:
1300 char *type;
1302 type = DISPATCH(oldView, prepareForDragOperation, &scr->dragInfo);
1304 if (type != NULL) {
1305 if (!WMRequestSelection(scr->dragInfo.destView,
1306 scr->xdndSelectionAtom,
1307 XInternAtom(scr->display, type, False),
1308 scr->dragInfo.timestamp,
1309 receivedData, &scr->dragInfo)) {
1310 wwarning("could not request data for dropped data");
1312 /* send finished message */
1313 sendClientMessage(scr->display, source,
1314 scr->xdndFinishedAtom,
1315 scr->dragInfo.destinationWindow,
1316 0, 0, 0, 0);
1318 } else {
1319 /* send finished message */
1320 sendClientMessage(scr->display, source,
1321 scr->xdndFinishedAtom,
1322 scr->dragInfo.destinationWindow,
1323 0, 0, 0, 0);
1326 break;
1328 default:
1329 break;
1334 if (sendStatus) {
1335 Atom action;
1337 action = operationToAction(scr, operation);
1339 sendClientMessage(scr->display, source,
1340 scr->xdndStatusAtom,
1341 scr->dragInfo.destinationWindow,
1342 action != None ? 1 : 0, 0, 0, action);