- Fixed a bug that crashed wmaker when selecting the "Start alternate window
[wmaker-crm.git] / WINGs / dragsource.c
blob4eeb4413eb843a580b64f7ab3a8efa4f91192a09
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
35 defDraggingSourceOperation(WMView *self, Bool local)
37 return WDOperationCopy;
41 static void
42 defBeganDragImage(WMView *self, WMPixmap *image, WMPoint point)
47 static void
48 defEndedDragImage(WMView *self, WMPixmap *image, WMPoint point, Bool deposited)
53 static WMData*
54 defFetchDragData(WMView *self, char *type)
56 return NULL;
60 void
61 WMSetViewDragSourceProcs(WMView *view, WMDragSourceProcs *procs)
63 if (view->dragSourceProcs)
64 wfree(view->dragSourceProcs);
65 view->dragSourceProcs = wmalloc(sizeof(WMDragSourceProcs));
67 *view->dragSourceProcs = *procs;
69 if (procs->draggingSourceOperation == NULL) {
70 view->dragSourceProcs->draggingSourceOperation = defDraggingSourceOperation;
72 if (procs->beganDragImage == NULL) {
73 view->dragSourceProcs->beganDragImage = defBeganDragImage;
75 if (procs->endedDragImage == NULL) {
76 view->dragSourceProcs->endedDragImage = defEndedDragImage;
78 if (procs->fetchDragData == NULL) {
79 view->dragSourceProcs->fetchDragData = defFetchDragData;
84 /***********************************************************************/
87 static int
88 handleXError(Display *dpy, XErrorEvent *ev)
90 _XErrorOccured = True;
92 return 1;
96 static void
97 protectBlock(Bool begin)
99 static void *oldHandler = NULL;
101 if (begin) {
102 oldHandler = XSetErrorHandler(handleXError);
103 } else {
104 XSetErrorHandler(oldHandler);
111 static Window
112 makeDragIcon(WMScreen *scr, WMPixmap *pixmap)
114 Window window;
115 WMSize size;
116 unsigned long flags;
117 XSetWindowAttributes attribs;
118 Pixmap pix, mask;
120 if (!pixmap) {
121 pixmap = scr->defaultObjectIcon;
123 size = WMGetPixmapSize(pixmap);
124 pix = pixmap->pixmap;
125 mask = pixmap->mask;
127 flags = CWSaveUnder|CWBackPixmap|CWOverrideRedirect|CWColormap;
128 attribs.save_under = True;
129 attribs.background_pixmap = pix;
130 attribs.override_redirect = True;
131 attribs.colormap = scr->colormap;
132 window = XCreateWindow(scr->display, scr->rootWin, 0, 0, size.width,
133 size.height, 0, scr->depth, InputOutput,
134 scr->visual, flags, &attribs);
136 #ifdef SHAPE
137 if (mask) {
138 XShapeCombineMask(scr->display, window, ShapeBounding, 0, 0, mask,
139 ShapeSet);
141 #endif
143 return window;
147 static void
148 slideWindow(Display *dpy, Window win, int srcX, int srcY, int dstX, int dstY)
150 double x, y, dx, dy;
151 int i;
152 int iterations;
154 iterations = WMIN(25, WMAX(abs(dstX-srcX), abs(dstY-srcY)));
156 x = srcX;
157 y = srcY;
159 dx = (double)(dstX-srcX)/iterations;
160 dy = (double)(dstY-srcY)/iterations;
162 for (i = 0; i <= iterations; i++) {
163 XMoveWindow(dpy, win, x, y);
164 XFlush(dpy);
166 wusleep(800);
168 x += dx;
169 y += dy;
174 static Window
175 findChildInWindow(Display *dpy, Window toplevel, int x, int y)
177 Window foo, bar;
178 Window *children;
179 unsigned nchildren;
180 int i;
182 if (!XQueryTree(dpy, toplevel, &foo, &bar,
183 &children, &nchildren) || children == NULL) {
184 return None;
187 /* first window that contains the point is the one */
188 for (i = nchildren-1; i >= 0; i--) {
189 XWindowAttributes attr;
191 if (XGetWindowAttributes(dpy, children[i], &attr)
192 && attr.map_state == IsViewable
193 && x >= attr.x && y >= attr.y
194 && x < attr.x + attr.width && y < attr.y + attr.height) {
195 Window child, tmp;
197 tmp = children[i];
199 child = findChildInWindow(dpy, tmp, x - attr.x, y - attr.y);
201 XFree(children);
203 if (child == None)
204 return tmp;
205 else
206 return child;
210 XFree(children);
211 return None;
215 static WMView*
216 findViewInToplevel(Display *dpy, Window toplevel, int x, int y)
218 Window child;
220 child = findChildInWindow(dpy, toplevel, x, y);
222 if (child != None) {
223 return W_GetViewForXWindow(dpy, child);
224 } else {
225 return NULL;
231 static Window
232 lookForToplevel(WMScreen *scr, Window window, Bool *isAware)
234 Window toplevel = None;
235 Atom *atoms;
236 int j, count;
238 *isAware = False;
240 atoms = XListProperties(scr->display, window, &count);
241 for (j = 0; j < count; j++) {
242 if (atoms[j] == scr->wmStateAtom) {
243 toplevel = window;
244 } else if (atoms[j] == scr->xdndAwareAtom) {
245 *isAware = True;
248 if (atoms)
249 XFree(atoms);
251 if (toplevel == None) {
252 Window *children;
253 Window foo, bar;
254 unsigned nchildren;
256 if (!XQueryTree(scr->display, window, &foo, &bar,
257 &children, &nchildren) || children == NULL) {
258 return None;
261 for (j = 0; j < nchildren; j++) {
262 toplevel = lookForToplevel(scr, children[j], isAware);
263 if (toplevel != None)
264 break;
267 XFree(children);
270 return toplevel;
275 static Window
276 findToplevelUnderDragPointer(WMScreen *scr, int x, int y, Window iconWindow)
278 Window foo, bar;
279 Window *children;
280 unsigned nchildren;
281 Bool overSomething = False;
282 int i;
284 if (!XQueryTree(scr->display, scr->rootWin, &foo, &bar,
285 &children, &nchildren) || children == NULL) {
286 SPIT("couldnt query tree!");
287 return None;
290 /* try to find the window below the iconWindow by traversing
291 * the whole window list */
293 /* first find the position of the iconWindow */
294 for (i = nchildren-1; i >= 0; i--) {
295 if (children[i] == iconWindow) {
296 i--;
297 break;
300 if (i <= 0) {
301 XFree(children);
302 return scr->rootWin;
305 /* first window that contains the point is the one */
306 for (; i >= 0; i--) {
307 XWindowAttributes attr;
309 if (XGetWindowAttributes(scr->display, children[i], &attr)
310 && attr.map_state == IsViewable
311 && x >= attr.x && y >= attr.y
312 && x < attr.x + attr.width && y < attr.y + attr.height) {
313 Window toplevel;
314 Bool isaware;
316 overSomething = True;
318 toplevel = lookForToplevel(scr, children[i], &isaware);
320 XFree(children);
322 if (isaware)
323 return toplevel;
324 else
325 return None;
329 XFree(children);
330 if (!overSomething)
331 return scr->rootWin;
332 else
333 return None;
341 static void
342 sendClientMessage(Display *dpy, Window win, Atom message,
343 unsigned data1, unsigned data2, unsigned data3,
344 unsigned data4, unsigned data5)
346 XEvent ev;
348 ev.type = ClientMessage;
349 ev.xclient.message_type = message;
350 ev.xclient.format = 32;
351 ev.xclient.window = win;
352 ev.xclient.data.l[0] = data1;
353 ev.xclient.data.l[1] = data2;
354 ev.xclient.data.l[2] = data3;
355 ev.xclient.data.l[3] = data4;
356 ev.xclient.data.l[4] = data5;
358 XSendEvent(dpy, win, False, 0, &ev);
359 XFlush(dpy);
365 static unsigned
366 notifyPosition(WMScreen *scr, WMDraggingInfo *info)
368 Atom action = operationToAction(scr, info->sourceOperation);
370 sendClientMessage(scr->display, info->destinationWindow,
371 scr->xdndPositionAtom,
372 info->sourceWindow,
373 0, /* reserved */
374 info->location.x<<16|info->location.y,
375 info->timestamp,
376 action/* operation */);
378 return 0;
383 static void
384 notifyDrop(WMScreen *scr, WMDraggingInfo *info)
386 sendClientMessage(scr->display, info->destinationWindow,
387 scr->xdndDropAtom,
388 info->sourceWindow,
389 0, /* reserved */
390 info->timestamp,
391 0, 0);
396 static void
397 notifyDragLeave(WMScreen *scr, WMDraggingInfo *info)
399 sendClientMessage(scr->display, info->destinationWindow,
400 scr->xdndLeaveAtom,
401 info->sourceWindow, 0, 0, 0, 0);
406 static unsigned
407 notifyDragEnter(WMScreen *scr, WMDraggingInfo *info)
409 unsigned d;
411 d = XDND_VERSION << 24;
413 sendClientMessage(scr->display, info->destinationWindow,
414 scr->xdndEnterAtom,
415 info->sourceWindow, d, 0, 0, 0);
417 return 0;
421 static void
422 translateCoordinates(WMScreen *scr, Window target, int fromX, int fromY,
423 int *x, int *y)
425 Window child;
427 XTranslateCoordinates(scr->display, scr->rootWin, target,
428 fromX, fromY, x, y, &child);
432 static void
433 updateDraggingInfo(WMScreen *scr, WMDraggingInfo *info, WMSize offset,
434 XEvent *event, Window iconWindow)
436 Window toplevel;
438 if (event->type == MotionNotify) {
439 info->imageLocation.x = event->xmotion.x_root-offset.width;
440 info->imageLocation.y = event->xmotion.y_root-offset.height;
442 info->location.x = event->xmotion.x_root;
443 info->location.y = event->xmotion.y_root;
444 /* info->timestamp = event->xmotion.time;*/
446 } else if (event->type == ButtonRelease) {
447 info->imageLocation.x = event->xbutton.x_root-offset.width;
448 info->imageLocation.y = event->xbutton.y_root-offset.height;
450 info->location.x = event->xbutton.x_root;
451 info->location.y = event->xbutton.y_root;
452 /* info->timestamp = event->xbutton.time;*/
455 toplevel = findToplevelUnderDragPointer(scr,
456 info->location.x,
457 info->location.y,
458 iconWindow);
459 info->destinationWindow = toplevel;
465 static void
466 processMotion(WMScreen *scr, WMDraggingInfo *info, WMDraggingInfo *oldInfo,
467 WMRect *rect, unsigned currentAction)
469 unsigned action;
471 if (info->destinationWindow == None) { /* entered an unsupporeted window */
473 if (oldInfo->destinationWindow != None
474 && oldInfo->destinationWindow != scr->rootWin) {
475 SPIT("left window");
477 notifyDragLeave(scr, oldInfo);
480 } else if (info->destinationWindow == scr->rootWin) {
482 if (oldInfo->destinationWindow != None
483 && oldInfo->destinationWindow != scr->rootWin) {
484 SPIT("left window to root");
486 notifyDragLeave(scr, oldInfo);
487 } else {
488 /* nothing */
491 } else if (oldInfo->destinationWindow != info->destinationWindow) {
493 if (oldInfo->destinationWindow != None
494 && oldInfo->destinationWindow != scr->rootWin) {
495 notifyDragLeave(scr, oldInfo);
496 SPIT("crossed");
497 } else {
498 SPIT("entered window");
501 action = notifyDragEnter(scr, info);
503 } else {
505 #define LEFT_RECT(r, X, Y) (X < r->pos.x || Y < r->pos.y \
506 || X >= r->pos.x + r->size.width \
507 || Y >= r->pos.y + r->size.height)
509 if (rect->size.width == 0 ||
510 (LEFT_RECT(rect, info->location.x, info->location.y))) {
512 action = notifyPosition(scr, info);
514 rect->size.width = 0;
521 static WMData*
522 convertSelection(WMView *view, Atom selection, Atom target,
523 void *cdata, Atom *type)
525 WMScreen *scr = W_VIEW_SCREEN(view);
526 WMData *data;
527 char *typeName = XGetAtomName(scr->display, target);
529 *type = target;
531 data = view->dragSourceProcs->fetchDragData(view, typeName);
533 if (typeName != NULL)
534 XFree(typeName);
536 return data;
540 static void
541 selectionLost(WMView *view, Atom selection, void *cdata)
543 if (W_VIEW_SCREEN(view)->dragSourceView == view) {
544 wwarning("DND selection lost during drag operation...");
545 W_VIEW_SCREEN(view)->dragSourceView = NULL;
550 static void
551 selectionDone(WMView *view, Atom selection, Atom target, void *cdata)
560 static void
561 setMouseOffsetHint(WMView *view, WMSize mouseOffset)
563 WMScreen *scr = W_VIEW_SCREEN(view);
564 long hint[2];
567 * Tell the offset from the topleft corner of the icon being
568 * dragged. Not from XDND, but it's backwards compatible.
571 hint[0] = mouseOffset.width;
572 hint[1] = mouseOffset.height;
574 XChangeProperty(scr->display, W_VIEW_DRAWABLE(view),
575 scr->wmIconDragOffsetAtom, XA_INTEGER, 32,
576 PropModeReplace, (unsigned char*)hint, 2);
580 static Bool
581 getMouseOffsetHint(WMScreen *scr, Window source, WMSize *mouseOffset)
583 long *hint;
584 Atom type_ret;
585 int fmt_ret;
586 unsigned long nitems_ret, bytes_after_ret;
587 Bool ok = False;
590 hint = NULL;
592 XGetWindowProperty(scr->display, source,
593 scr->wmIconDragOffsetAtom, 0, 2, False, XA_INTEGER,
594 &type_ret, &fmt_ret, &nitems_ret, &bytes_after_ret,
595 (unsigned char **)&hint);
597 if (hint && nitems_ret == 2) {
598 mouseOffset->width = hint[0];
599 mouseOffset->height = hint[1];
600 ok = True;
602 if (hint)
603 XFree(hint);
605 return ok;
610 static void
611 timeoutCallback(void *data)
613 wwarning("drag & drop timed out while waiting for response from 0x%x\n",
614 (unsigned)data);
615 _XErrorOccured = 2;
619 * State Machine For Drag Source:
620 * ------------------------------
621 * Events
622 * State Call Mtn Ent Lea Crs BUp StA StR Fin TO
623 * 0) idle 1bu - - - - - - - - -
624 * 1) drag over target - 1au - 2cu 1cbu 5fu 3 4 1w -
625 * 2) drag over nothing - 2 1bu - - 0 - - 2w -
626 * 3) drag targ+accept - 3u - 2cu 1cbu 6f 3 4w 0z -
627 * 4) drag targ+reject - 4u - 2cu 1cbu 0 3w 4 0z -
628 * 5) waiting status - 5X 5X 5X 5X - 6f 0 0z 0w
629 * 6) dropped - - - - - - - - 0 0w
631 * Events:
632 * Call - called WMDragImageFromView()
633 * Mtn - Motion
634 * Ent - Enter droppable window
635 * Lea - Leave droppable window (or rectangle)
636 * Crs - Leave droppable window (or rectangle) and enter another
637 * BUp - Button Released
638 * StA - XdndStatus client msg with Accept drop
639 * StR - XdndStatus client msg with Reject drop
640 * Fin - XdndFinish client msg
641 * TO - timeout
643 * Actions:
644 * a - send update message
645 * b - send enter message
646 * c - send leave message
647 * d - release drag section info
648 * e - send drop message
649 * f - setup timeout
650 * u - update dragInfo
652 * X - ignore
653 * w - warn about unexpected reply
654 * z - abort operation.. unexpected reply
655 * - shouldnt happen
657 void
658 WMDragImageFromView(WMView *view, WMPixmap *image, char *dataTypes[],
659 WMPoint atLocation, WMSize mouseOffset, XEvent *event,
660 Bool slideBack)
662 WMScreen *scr = view->screen;
663 Display *dpy = scr->display;
664 WMView *toplevel = W_TopLevelOfView(view);
665 Window icon;
666 XEvent ev;
667 WMRect rect = {{0,0},{0,0}};
668 int ostate = -1;
669 int state;
670 int action = -1;
671 XColor black = {0, 0,0,0, DoRed|DoGreen|DoBlue};
672 XColor green = {0x0045b045, 0x4500,0xb000,0x4500, DoRed|DoGreen|DoBlue};
673 XColor back = {0, 0xffff,0xffff,0xffff, DoRed|DoGreen|DoBlue};
674 WMDraggingInfo dragInfo;
675 WMDraggingInfo oldDragInfo;
676 WMHandlerID timer = NULL;
677 static WMSelectionProcs handler = {
678 convertSelection,
679 selectionLost,
680 selectionDone
684 if (scr->dragSourceView != NULL)
685 return;
687 wassertr(view->dragSourceProcs != NULL);
690 /* prepare icon to be dragged */
691 if (image == NULL)
692 image = scr->defaultObjectIcon;
694 icon = makeDragIcon(scr, image);
696 XMoveWindow(dpy, icon, atLocation.x, atLocation.y);
697 XMapRaised(dpy, icon);
700 /* init dragging info */
702 scr->dragSourceView = view;
704 memset(&dragInfo, 0, sizeof(WMDraggingInfo));
705 memset(&oldDragInfo, 0, sizeof(WMDraggingInfo));
706 dragInfo.image = image;
707 dragInfo.sourceView = view;
708 dragInfo.sourceWindow = W_VIEW_DRAWABLE(toplevel);
710 dragInfo.destinationWindow = dragInfo.sourceWindow;
712 dragInfo.location.x = atLocation.x + mouseOffset.width;
713 dragInfo.location.y = atLocation.y + mouseOffset.height;
714 dragInfo.imageLocation = atLocation;
717 /* start pointer grab */
718 XGrabPointer(dpy, scr->rootWin, False,
719 ButtonPressMask|ButtonReleaseMask|ButtonMotionMask,
720 GrabModeAsync, GrabModeAsync, None, scr->defaultCursor,
721 CurrentTime);
723 XFlush(dpy);
725 _XErrorOccured = False;
727 /* take ownership of XdndSelection */
728 if (!WMCreateSelectionHandler(view, scr->xdndSelectionAtom,
729 event->xmotion.time,
730 &handler, NULL)) {
731 wwarning("could not get ownership or DND selection");
732 return;
735 setMouseOffsetHint(toplevel, mouseOffset);
737 if (view->dragSourceProcs->beganDragImage != NULL) {
738 view->dragSourceProcs->beganDragImage(view, image, atLocation);
741 processMotion(scr, &dragInfo, &oldDragInfo, &rect, action);
743 state = 1;
745 while (state != 6 && state != 0 && !_XErrorOccured) {
746 WMNextEvent(dpy, &ev);
748 switch (ev.type) {
749 case MotionNotify:
750 if (state >= 1 && state <= 4) {
751 while (XCheckTypedEvent(dpy, MotionNotify, &ev)) ;
753 protectBlock(True);
755 oldDragInfo = dragInfo;
757 updateDraggingInfo(scr, &dragInfo, mouseOffset, &ev, icon);
759 XMoveWindow(dpy, icon, dragInfo.imageLocation.x,
760 dragInfo.imageLocation.y);
762 processMotion(scr, &dragInfo, &oldDragInfo, &rect, action);
764 protectBlock(False);
766 /* XXXif entered a different destination, check the operation */
768 switch (state) {
769 case 1:
770 if (oldDragInfo.destinationWindow != None
771 && oldDragInfo.destinationWindow != scr->rootWin
772 && (dragInfo.destinationWindow == None
773 || dragInfo.destinationWindow == scr->rootWin)) {
774 /* left the droppable window */
775 state = 2;
776 action = -1;
778 break;
780 case 2:
781 if (dragInfo.destinationWindow != None
782 && dragInfo.destinationWindow != scr->rootWin) {
784 state = 1;
785 action = -1;
787 break;
789 case 3:
790 case 4:
791 if (oldDragInfo.destinationWindow != None
792 && oldDragInfo.destinationWindow != scr->rootWin
793 && (dragInfo.destinationWindow == None
794 || dragInfo.destinationWindow == scr->rootWin)) {
795 /* left the droppable window */
796 state = 2;
797 action = -1;
799 break;
802 break;
805 case ButtonRelease:
806 /* if (state >= 1 && state <= 4) */ {
808 protectBlock(True);
810 oldDragInfo = dragInfo;
812 updateDraggingInfo(scr, &dragInfo, mouseOffset, &ev, icon);
814 XMoveWindow(dpy, icon, dragInfo.imageLocation.x,
815 dragInfo.imageLocation.y);
817 if (state == 4 || state == 1) {
818 dragInfo.destinationWindow = None;
819 dragInfo.destView = NULL;
821 processMotion(scr, &dragInfo, &oldDragInfo, &rect, action);
823 dragInfo.timestamp = ev.xbutton.time;
825 protectBlock(False);
827 switch (state) {
828 case 1:
829 state = 5;
830 timer = WMAddTimerHandler(3000, timeoutCallback,
831 (void*)dragInfo.destinationWindow);
832 break;
833 case 2:
834 state = 0;
835 break;
836 case 3:
837 state = 6;
838 break;
839 case 4:
840 state = 0;
841 break;
844 break;
846 case ClientMessage:
847 if ((state == 1 || state == 3 || state == 4 || state == 5)
848 && ev.xclient.message_type == scr->xdndStatusAtom
849 && ev.xclient.data.l[0] == dragInfo.destinationWindow) {
851 if (ev.xclient.data.l[1] & 1) {
852 SPIT("got accept msg");
853 /* will accept drop */
854 switch (state) {
855 case 1:
856 case 3:
857 case 4:
858 state = 3;
859 break;
860 case 5:
861 if (timer) {
862 WMDeleteTimerHandler(timer);
863 timer = NULL;
865 state = 6;
866 break;
868 action = actionToOperation(scr, ev.xclient.data.l[4]);
869 } else {
870 SPIT("got reject msg");
871 switch (state) {
872 case 1:
873 case 3:
874 case 4:
875 state = 4;
876 break;
877 case 5:
878 state = 0;
879 if (timer) {
880 WMDeleteTimerHandler(timer);
881 timer = NULL;
883 break;
885 action = 0;
888 if (ev.xclient.data.l[1] & (1<<1)) {
889 rect.pos.x = ev.xclient.data.l[2] >> 16;
890 rect.pos.y = ev.xclient.data.l[2] & 0xffff;
891 rect.size.width = ev.xclient.data.l[3] >> 16;
892 rect.size.height = ev.xclient.data.l[3] & 0xffff;
893 } else {
894 rect.size.width = 0;
897 } else if ((state >= 1 && state <= 5)
898 && ev.xclient.message_type == scr->xdndFinishedAtom
899 && ev.xclient.window == dragInfo.destinationWindow) {
901 wwarning("drag source received unexpected XdndFinished message from %x",
902 (unsigned)dragInfo.destinationWindow);
904 if (state == 3 || state == 4 || state == 5) {
905 state = 0;
906 if (timer) {
907 WMDeleteTimerHandler(timer);
908 timer = NULL;
913 default:
914 WMHandleEvent(&ev);
915 break;
918 if (ostate != state) {
919 if (state == 3) {
920 XRecolorCursor(dpy, scr->defaultCursor, &green, &back);
921 } else if (ostate == 3) {
922 XRecolorCursor(dpy, scr->defaultCursor, &black, &back);
924 ostate = state;
928 if (timer) {
929 WMDeleteTimerHandler(timer);
930 timer = NULL;
931 } else if (_XErrorOccured) {
932 /* got a timeout, send leave */
933 notifyDragLeave(scr, &dragInfo);
936 XUngrabPointer(dpy, CurrentTime);
938 SPIT("exited main loop");
940 if (_XErrorOccured || state != 6) {
941 goto cancelled;
944 assert(dragInfo.destinationWindow != None);
946 protectBlock(True);
947 notifyDrop(scr, &dragInfo);
948 protectBlock(False);
950 if (_XErrorOccured)
951 goto cancelled;
954 SPIT("dropped");
957 XDestroyWindow(dpy, icon);
959 return;
961 cancelled:
962 scr->dragSourceView = NULL;
964 WMDeleteSelectionHandler(view, scr->xdndSelectionAtom,
965 event->xmotion.time);
967 if (slideBack) {
968 slideWindow(dpy, icon,
969 dragInfo.imageLocation.x, dragInfo.imageLocation.y,
970 atLocation.x, atLocation.y);
972 XDestroyWindow(dpy, icon);
973 if (view->dragSourceProcs->endedDragImage != NULL) {
974 view->dragSourceProcs->endedDragImage(view, image,
975 dragInfo.imageLocation,
976 False);
991 static Atom
992 operationToAction(WMScreen *scr, WMDragOperationType operation)
994 switch (operation) {
995 case WDOperationNone:
996 return None;
998 case WDOperationCopy:
999 return scr->xdndActionCopy;
1001 case WDOperationMove:
1002 return scr->xdndActionMove;
1004 case WDOperationLink:
1005 return scr->xdndActionLink;
1007 case WDOperationAsk:
1008 return scr->xdndActionAsk;
1010 case WDOperationPrivate:
1011 return scr->xdndActionPrivate;
1013 default:
1014 return None;
1019 static WMDragOperationType
1020 actionToOperation(WMScreen *scr, Atom action)
1022 if (action == scr->xdndActionCopy) {
1023 return WDOperationCopy;
1025 } else if (action == scr->xdndActionMove) {
1026 return WDOperationMove;
1028 } else if (action == scr->xdndActionLink) {
1029 return WDOperationLink;
1031 } else if (action == scr->xdndActionAsk) {
1032 return WDOperationAsk;
1034 } else if (action == scr->xdndActionPrivate) {
1035 return WDOperationPrivate;
1037 } else if (action == None) {
1039 return WDOperationCopy;
1040 } else {
1041 char *tmp = XGetAtomName(scr->display, action);
1043 wwarning("unknown XDND action %s from 0x%x", tmp,
1044 (unsigned)scr->dragInfo.sourceWindow);
1045 XFree(tmp);
1047 return WDOperationCopy;
1056 static Atom*
1057 getTypeList(Window window, XClientMessageEvent *event)
1059 int i = 0;
1060 Atom *types = NULL;
1062 if (event->data.l[1] & 1) { /* > 3 types */
1064 } else {
1065 types = wmalloc(4 * sizeof(Atom));
1066 if (event->data.l[2] != None)
1067 types[i++] = event->data.l[2];
1068 if (event->data.l[3] != None)
1069 types[i++] = event->data.l[3];
1070 if (event->data.l[4] != None)
1071 types[i++] = event->data.l[4];
1072 types[i] = 0;
1075 if (types[0] == 0) {
1076 wwarning("received invalid drag & drop type list");
1077 /*XXX return;*/
1080 return types;
1085 #define DISPATCH(view, func, info) (view)->dragDestinationProcs->func(view, info)
1089 static void
1090 receivedData(WMView *view, Atom selection, Atom target,
1091 Time timestamp, void *cdata, WMData *data)
1093 WMScreen *scr = W_VIEW_SCREEN(view);
1094 WMDraggingInfo *info = (WMDraggingInfo*)cdata;
1095 Bool res;
1097 res = view->dragDestinationProcs->performDragOperation(view, info);
1099 if (res) {
1100 DISPATCH(view, concludeDragOperation, info);
1103 /* send finished message */
1104 sendClientMessage(scr->display, info->sourceWindow,
1105 scr->xdndFinishedAtom,
1106 info->destinationWindow,
1107 0, 0, 0, 0);
1109 memset(&scr->dragInfo, 0, sizeof(WMDraggingInfo));
1114 void
1115 W_HandleDNDClientMessage(WMView *toplevel, XClientMessageEvent *event)
1117 WMScreen *scr = W_VIEW_SCREEN(toplevel);
1118 WMView *oldView = NULL;
1119 WMView *newView = NULL;
1120 unsigned operation = 0;
1121 int x, y;
1122 enum {
1123 WNothing,
1124 WEnter,
1125 WLeave,
1126 WCross, /* leave one and enter another */
1127 WUpdate,
1128 WDrop
1130 Window source;
1131 int what = WNothing;
1132 Bool sendStatus = False;
1135 source = scr->dragInfo.sourceWindow;
1136 oldView = scr->dragInfo.destView;
1138 if (event->message_type == scr->xdndFinishedAtom) {
1139 WMView *view = scr->dragSourceView;
1141 WMDeleteSelectionHandler(view, scr->xdndSelectionAtom,
1142 scr->dragInfo.timestamp);
1144 if (view->dragSourceProcs->endedDragImage != NULL) {
1145 view->dragSourceProcs->endedDragImage(view,
1146 scr->dragInfo.image,
1147 scr->dragInfo.imageLocation,
1148 True);
1151 scr->dragSourceView = NULL;
1153 return;
1158 if (event->message_type == scr->xdndEnterAtom) {
1159 Window foo, bar;
1160 int bla;
1161 unsigned ble;
1163 if (scr->dragInfo.sourceWindow != None) {
1164 puts("received Enter event in bad order");
1167 memset(&scr->dragInfo, 0, sizeof(WMDraggingInfo));
1170 if ((event->data.l[1] >> 24) > XDND_VERSION) {
1171 wwarning("received drag & drop request with unsupported version %i",
1172 (event->data.l[1] >> 24));
1173 return;
1176 scr->dragInfo.protocolVersion = event->data.l[1] >> 24;
1177 scr->dragInfo.sourceWindow = source = event->data.l[0];
1178 scr->dragInfo.destinationWindow = event->window;
1180 getMouseOffsetHint(scr, source, &scr->dragInfo.mouseOffset);
1182 /* XXX */
1183 scr->dragInfo.image = NULL;
1185 XQueryPointer(scr->display, scr->rootWin, &foo, &bar,
1186 &scr->dragInfo.location.x, &scr->dragInfo.location.y,
1187 &bla, &bla, &ble);
1189 scr->dragInfo.imageLocation = scr->dragInfo.location;
1190 scr->dragInfo.imageLocation.x -= scr->dragInfo.mouseOffset.width;
1191 scr->dragInfo.imageLocation.y -= scr->dragInfo.mouseOffset.height;
1193 translateCoordinates(scr, scr->dragInfo.destinationWindow,
1194 scr->dragInfo.location.x,
1195 scr->dragInfo.location.y, &x, &y);
1198 newView = findViewInToplevel(scr->display,
1199 scr->dragInfo.destinationWindow,
1200 x, y);
1202 } else if (event->message_type == scr->xdndPositionAtom
1203 && scr->dragInfo.sourceWindow == event->data.l[0]) {
1205 scr->dragInfo.location.x = event->data.l[2] >> 16;
1206 scr->dragInfo.location.y = event->data.l[2] & 0xffff;
1208 scr->dragInfo.imageLocation = scr->dragInfo.location;
1209 scr->dragInfo.imageLocation.x -= scr->dragInfo.mouseOffset.width;
1210 scr->dragInfo.imageLocation.y -= scr->dragInfo.mouseOffset.height;
1212 if (scr->dragInfo.protocolVersion >= 1) {
1213 scr->dragInfo.timestamp = event->data.l[3];
1214 scr->dragInfo.sourceOperation = actionToOperation(scr,
1215 event->data.l[4]);
1217 } else {
1218 scr->dragInfo.timestamp = CurrentTime;
1219 scr->dragInfo.sourceOperation = WDOperationCopy;
1222 translateCoordinates(scr, scr->dragInfo.destinationWindow,
1223 scr->dragInfo.location.x,
1224 scr->dragInfo.location.y, &x, &y);
1226 newView = findViewInToplevel(scr->display,
1227 scr->dragInfo.destinationWindow,
1228 x, y);
1230 } else if (event->message_type == scr->xdndLeaveAtom
1231 && scr->dragInfo.sourceWindow == event->data.l[0]) {
1233 memset(&scr->dragInfo, 0, sizeof(WMDraggingInfo));
1235 } else if (event->message_type == scr->xdndDropAtom
1236 && scr->dragInfo.sourceWindow == event->data.l[0]) {
1238 /* drop */
1239 if (oldView != NULL)
1240 what = WDrop;
1242 } else {
1243 return;
1248 * Now map the XDND events to WINGs events.
1251 if (what == WNothing) {
1252 if (IS_DROPPABLE(newView)) {
1253 if (!IS_DROPPABLE(oldView)) { /* entered */
1254 what = WEnter;
1255 } else if (oldView == newView) { /* updated */
1256 what = WUpdate;
1257 } else {
1258 what = WCross;
1260 } else {
1261 if (IS_DROPPABLE(oldView)) {
1262 what = WLeave;
1263 } else {
1264 /* just send rejection msg */
1265 sendStatus = True;
1272 switch (what) {
1274 case WEnter:
1275 scr->dragInfo.destView = newView;
1276 operation = DISPATCH(newView, draggingEntered, &scr->dragInfo);
1277 sendStatus = True;
1278 break;
1280 case WLeave:
1281 scr->dragInfo.destView = NULL;
1282 DISPATCH(oldView, draggingExited, &scr->dragInfo);
1283 sendStatus = True;
1284 operation = WDOperationNone;
1285 break;
1287 case WCross:
1288 DISPATCH(oldView, draggingExited, &scr->dragInfo);
1289 scr->dragInfo.destView = newView;
1290 operation = DISPATCH(newView, draggingEntered, &scr->dragInfo);
1291 sendStatus = True;
1292 break;
1294 case WUpdate:
1295 operation = DISPATCH(oldView, draggingUpdated, &scr->dragInfo);
1296 sendStatus = True;
1297 break;
1299 case WDrop:
1301 Bool res;
1303 res = DISPATCH(oldView, prepareForDragOperation, &scr->dragInfo);
1305 if (res) {
1306 res = DISPATCH(oldView, performDragOperation, &scr->dragInfo);
1309 if (res) {
1313 break;
1315 default:
1316 break;
1321 if (sendStatus) {
1322 Atom action;
1324 action = operationToAction(scr, operation);
1326 sendClientMessage(scr->display, source,
1327 scr->xdndStatusAtom,
1328 scr->dragInfo.destinationWindow,
1329 action != None ? 1 : 0, 0, 0, action);