11 #define SPIT(a) puts(a)
14 #define IS_DROPPABLE(view) (view!=NULL && view->droppableTypes!=NULL && \
15 view->dragDestinationProcs!=NULL)
18 static Bool _XErrorOccured
= False
;
24 WMSetViewDragSourceProcs(WMView
*view
, WMDragSourceProcs
*procs
)
26 if (view
->dragSourceProcs
)
27 free(view
->dragSourceProcs
);
28 view
->dragSourceProcs
= wmalloc(sizeof(WMDragSourceProcs
));
30 *view
->dragSourceProcs
= *procs
;
32 /* XXX fill in non-implemented stuffs */
36 /***********************************************************************/
40 handleXError(Display
*dpy
, XErrorEvent
*ev
)
42 _XErrorOccured
= True
;
49 protectBlock(Bool begin
)
51 static void *oldHandler
= NULL
;
54 oldHandler
= XSetErrorHandler(handleXError
);
56 XSetErrorHandler(oldHandler
);
64 makeDragIcon(WMScreen
*scr
, WMPixmap
*pixmap
)
69 XSetWindowAttributes attribs
;
73 pixmap
= scr
->defaultObjectIcon
;
75 size
= WMGetPixmapSize(pixmap
);
79 flags
= CWSaveUnder
|CWBackPixmap
|CWOverrideRedirect
|CWColormap
;
80 attribs
.save_under
= True
;
81 attribs
.background_pixmap
= pix
;
82 attribs
.override_redirect
= True
;
83 attribs
.colormap
= scr
->colormap
;
84 window
= XCreateWindow(scr
->display
, scr
->rootWin
, 0, 0, size
.width
,
85 size
.height
, 0, scr
->depth
, InputOutput
,
86 scr
->visual
, flags
, &attribs
);
90 XShapeCombineMask(dpy
, scr
->balloon
->window
, ShapeBounding
, 0, 0, mask
,
100 slideWindow(Display
*dpy
, Window win
, int srcX
, int srcY
, int dstX
, int dstY
)
106 iterations
= WMIN(25, WMAX(abs(dstX
-srcX
), abs(dstY
-srcY
)));
111 dx
= (double)(dstX
-srcX
)/iterations
;
112 dy
= (double)(dstY
-srcY
)/iterations
;
114 for (i
= 0; i
<= iterations
; i
++) {
115 XMoveWindow(dpy
, win
, x
, y
);
127 findChildInWindow(Display
*dpy
, Window toplevel
, int x
, int y
)
134 if (!XQueryTree(dpy
, toplevel
, &foo
, &bar
,
135 &children
, &nchildren
) || children
== NULL
) {
139 /* first window that contains the point is the one */
140 for (i
= nchildren
-1; i
>= 0; i
--) {
141 XWindowAttributes attr
;
143 if (XGetWindowAttributes(dpy
, children
[i
], &attr
)
144 && attr
.map_state
== IsViewable
145 && x
>= attr
.x
&& y
>= attr
.y
146 && x
< attr
.x
+ attr
.width
&& y
< attr
.y
+ attr
.height
) {
149 child
= findChildInWindow(dpy
, children
[i
],
150 x
- attr
.x
, y
- attr
.y
);
167 findViewInToplevel(Display
*dpy
, Window toplevel
, int x
, int y
)
171 child
= findChildInWindow(dpy
, toplevel
, x
, y
);
174 return W_GetViewForXWindow(dpy
, child
);
182 lookForToplevel(WMScreen
*scr
, Window window
, Bool
*isAware
)
184 Window toplevel
= None
;
190 atoms
= XListProperties(scr
->display
, window
, &count
);
191 for (j
= 0; j
< count
; j
++) {
192 if (atoms
[j
] == scr
->wmStateAtom
) {
194 } else if (atoms
[j
] == scr
->xdndAwareAtom
) {
201 if (toplevel
== None
) {
206 if (!XQueryTree(scr
->display
, window
, &foo
, &bar
,
207 &children
, &nchildren
) || children
== NULL
) {
211 for (j
= 0; j
< nchildren
; j
++) {
212 toplevel
= lookForToplevel(scr
, children
[j
], isAware
);
213 if (toplevel
!= None
)
228 findToplevelUnderDragPointer(WMScreen
*scr
, int x
, int y
, Window iconWindow
)
233 Bool overSomething
= False
;
236 if (!XQueryTree(scr
->display
, scr
->rootWin
, &foo
, &bar
,
237 &children
, &nchildren
) || children
== NULL
) {
238 SPIT("couldnt query tree!");
242 /* try to find the window below the iconWindow by traversing
243 * the whole window list */
245 /* first find the position of the iconWindow */
246 for (i
= nchildren
-1; i
>= 0; i
--) {
247 if (children
[i
] == iconWindow
) {
257 /* first window that contains the point is the one */
258 for (; i
>= 0; i
--) {
259 XWindowAttributes attr
;
261 if (XGetWindowAttributes(scr
->display
, children
[i
], &attr
)
262 && attr
.map_state
== IsViewable
263 && x
>= attr
.x
&& y
>= attr
.y
264 && x
< attr
.x
+ attr
.width
&& y
< attr
.y
+ attr
.height
) {
268 overSomething
= True
;
270 toplevel
= lookForToplevel(scr
, children
[i
], &isaware
);
294 sendClientMessage(Display
*dpy
, Window win
, Atom message
,
295 unsigned data1
, unsigned data2
, unsigned data3
,
296 unsigned data4
, unsigned data5
)
300 ev
.type
= ClientMessage
;
301 ev
.xclient
.message_type
= message
;
302 ev
.xclient
.format
= 32;
303 ev
.xclient
.window
= win
;
304 ev
.xclient
.data
.l
[0] = data1
;
305 ev
.xclient
.data
.l
[1] = data2
;
306 ev
.xclient
.data
.l
[2] = data3
;
307 ev
.xclient
.data
.l
[3] = data4
;
308 ev
.xclient
.data
.l
[4] = data5
;
310 XSendEvent(dpy
, win
, False
, 0, &ev
);
318 notifyPosition(WMScreen
*scr
, WMDraggingInfo
*info
)
322 switch (info
->sourceOperation
) {
328 sendClientMessage(scr
->display
, info
->destinationWindow
,
329 scr
->xdndPositionAtom
,
332 info
->location
.x
<<16|info
->location
.y
,
334 operation
/* operation */);
342 notifyDrop(WMScreen
*scr
, WMDraggingInfo
*info
)
344 sendClientMessage(scr
->display
, info
->destinationWindow
,
355 notifyDragLeave(WMScreen
*scr
, WMDraggingInfo
*info
)
357 sendClientMessage(scr
->display
, info
->destinationWindow
,
359 info
->sourceWindow
, 0, 0, 0, 0);
365 notifyDragEnter(WMScreen
*scr
, WMDraggingInfo
*info
)
369 d
= XDND_VERSION
<< 24;
371 sendClientMessage(scr
->display
, info
->destinationWindow
,
373 info
->sourceWindow
, d
, 0, 0, 0);
380 translateCoordinates(WMScreen
*scr
, Window target
, int fromX
, int fromY
,
385 XTranslateCoordinates(scr
->display
, scr
->rootWin
, target
,
386 fromX
, fromY
, x
, y
, &child
);
391 updateDraggingInfo(WMScreen
*scr
, WMDraggingInfo
*info
,
392 XEvent
*event
, Window iconWindow
)
397 size
= WMGetPixmapSize(info
->image
);
399 if (event
->type
== MotionNotify
) {
400 info
->imageLocation
.x
= event
->xmotion
.x_root
-(int)size
.width
/2;
401 info
->imageLocation
.y
= event
->xmotion
.y_root
-(int)size
.height
/2;
403 info
->location
.x
= event
->xmotion
.x_root
;
404 info
->location
.y
= event
->xmotion
.y_root
;
405 info
->timestamp
= event
->xmotion
.time
;
407 } else if (event
->type
== ButtonRelease
) {
408 info
->imageLocation
.x
= event
->xbutton
.x_root
-(int)size
.width
/2;
409 info
->imageLocation
.y
= event
->xbutton
.y_root
-(int)size
.height
/2;
411 info
->location
.x
= event
->xbutton
.x_root
;
412 info
->location
.y
= event
->xbutton
.y_root
;
413 info
->timestamp
= event
->xbutton
.time
;
416 toplevel
= findToplevelUnderDragPointer(scr
,
420 info
->destinationWindow
= toplevel
;
422 if (toplevel == None) {
423 info->destinationWindow = None;
424 } else if (toplevel == scr->rootWin) {
425 info->destinationWindow = scr->rootWin;
430 XTranslateCoordinates(scr->display, scr->rootWin, toplevel,
431 info->location.x, info->location.y,
434 child = findChildInWindow(scr->display, toplevel, x, y);
437 info->destination = W_GetViewForXWindow(scr->display, child);
438 if (info->destination->droppableTypes == NULL) {
439 info->destination = NULL;
440 } else if (info->destination->dragDestinationProcs == NULL) {
441 info->destination = NULL;
444 info->destination = NULL;
446 info->destinationWindow = toplevel;
455 processMotion(WMScreen
*scr
, WMDraggingInfo
*info
, WMDraggingInfo
*oldInfo
,
456 WMRect
*rect
, unsigned currentAction
)
460 if (info
->destinationWindow
== None
) { /* entered an unsupporeted window */
462 if (oldInfo
->destinationWindow
!= None
463 && oldInfo
->destinationWindow
!= scr
->rootWin
) {
466 notifyDragLeave(scr
, oldInfo
);
469 } else if (info
->destinationWindow
== scr
->rootWin
) {
471 if (oldInfo
->destinationWindow
!= None
472 && oldInfo
->destinationWindow
!= scr
->rootWin
) {
473 SPIT("left window to root");
475 notifyDragLeave(scr
, oldInfo
);
480 } else if (oldInfo
->destinationWindow
!= info
->destinationWindow
) {
482 if (oldInfo
->destinationWindow
!= None
483 && oldInfo
->destinationWindow
!= scr
->rootWin
) {
484 notifyDragLeave(scr
, oldInfo
);
487 SPIT("entered window");
490 action
= notifyDragEnter(scr
, info
);
494 #define LEFT_RECT(r, X, Y) (X < r->pos.x || Y < r->pos.y \
495 || X >= r->pos.x + r->size.width \
496 || Y >= r->pos.y + r->size.height)
498 if (rect
->size
.width
== 0 ||
499 (LEFT_RECT(rect
, info
->location
.x
, info
->location
.y
))) {
501 action
= notifyPosition(scr
, info
);
503 rect
->size
.width
= 0;
507 /* little trick to simulate XdndStatus for local dnd */
509 if (bla && action != currentAction) {
512 ev.type = ClientMessage;
513 ev.xclient.display = scr->display;
514 ev.xclient.message_type = scr->xdndStatusAtom;
515 ev.xclient.format = 32;
516 ev.xclient.window = info->destinationWindow;
517 ev.xclient.data.l[0] = info->sourceWindow;
518 ev.xclient.data.l[1] = (action ? 1 : 0);
519 ev.xclient.data.l[2] = 0;
520 ev.xclient.data.l[3] = 0;
521 ev.xclient.data.l[4] = action;
523 XPutBackEvent(scr->display, &ev);
531 timeoutCallback(void *data
)
533 wwarning("drag & drop timed out while waiting for response from 0x%x\n",
539 * State Machine For Drag Source:
540 * ------------------------------
542 * State Call Mtn Ent Lea Crs BUp StA StR Fin TO
543 * 0) idle 1bu - - - - - - - - -
544 * 1) drag over target - 1au - 2cu 1cbu 5fu 3 4 1w -
545 * 2) drag over nothing - 2 1bu - - 0 - - 2w -
546 * 3) drag targ+accept - 3u - 2cu 1cbu 6f 3 4w 0z -
547 * 4) drag targ+reject - 4u - 2cu 1cbu 0 3w 4 0z -
548 * 5) waiting status - 5X 5X 5X 5X - 6f 0 0z 0w
549 * 6) dropped - - - - - - - - 0 0w
552 * Call - called WMDragImageFromView()
554 * Ent - Enter droppable window
555 * Lea - Leave droppable window (or rectangle)
556 * Crs - Leave droppable window (or rectangle) and enter another
557 * BUp - Button Released
558 * StA - XdndStatus client msg with Accept drop
559 * StR - XdndStatus client msg with Reject drop
560 * Fin - XdndFinish client msg
564 * a - send update message
565 * b - send enter message
566 * c - send leave message
567 * d - release drag section info
568 * e - send drop message
570 * u - update dragInfo
573 * w - warn about unexpected reply
574 * z - abort operation.. unexpected reply
578 WMDragImageFromView(WMView
*view
, WMPixmap
*image
, char *dataTypes
[],
579 WMPoint atLocation
, WMSize mouseOffset
, XEvent
*event
,
582 WMScreen
*scr
= view
->screen
;
583 Display
*dpy
= scr
->display
;
587 WMRect rect
= {{0,0},{0,0}};
591 XColor black
= {0, 0,0,0, DoRed
|DoGreen
|DoBlue
};
592 XColor green
= {0x0045b045, 0x4500,0xb000,0x4500, DoRed
|DoGreen
|DoBlue
};
593 XColor back
= {0, 0xffff,0xffff,0xffff, DoRed
|DoGreen
|DoBlue
};
594 WMDraggingInfo dragInfo
;
595 WMDraggingInfo oldDragInfo
;
596 WMHandlerID timer
= NULL
;
597 WMData
*draggedData
= NULL
;
600 wassertr(view
->dragSourceProcs
!= NULL
);
603 /* prepare icon to be dragged */
605 image
= scr
->defaultObjectIcon
;
607 size
= WMGetPixmapSize(image
);
609 icon
= makeDragIcon(scr
, image
);
611 XMoveWindow(dpy
, icon
, event
->xmotion
.x_root
-(int)size
.width
/2,
612 event
->xmotion
.y_root
-(int)size
.height
/2);
613 XMapRaised(dpy
, icon
);
616 /* init dragging info */
617 memset(&dragInfo
, 0, sizeof(WMDraggingInfo
));
618 memset(&oldDragInfo
, 0, sizeof(WMDraggingInfo
));
619 dragInfo
.image
= image
;
620 dragInfo
.sourceWindow
= W_VIEW_DRAWABLE(W_TopLevelOfView(view
));
622 dragInfo
.destinationWindow
= dragInfo
.sourceWindow
;
624 dragInfo
.location
.x
= event
->xmotion
.x_root
;
625 dragInfo
.location
.y
= event
->xmotion
.y_root
;
626 dragInfo
.imageLocation
= atLocation
;
629 /* start pointer grab */
630 XGrabPointer(dpy
, scr
->rootWin
, False
,
631 ButtonPressMask
|ButtonReleaseMask
|ButtonMotionMask
,
632 GrabModeSync
, GrabModeAsync
, None
, scr
->defaultCursor
,
637 _XErrorOccured
= False
;
639 /* XXX: take ownership of XdndSelection */
642 if (view
->dragSourceProcs
->beganDragImage
!= NULL
) {
643 view
->dragSourceProcs
->beganDragImage(view
, image
, atLocation
);
646 processMotion(scr
, &dragInfo
, &oldDragInfo
, &rect
, action
);
650 while (state
!= 6 && state
!= 0 && !_XErrorOccured
) {
651 XAllowEvents(dpy
, SyncPointer
, CurrentTime
);
652 WMNextEvent(dpy
, &ev
);
656 if (state
>= 1 && state
<= 4) {
657 while (XCheckTypedEvent(dpy
, MotionNotify
, &ev
)) ;
661 oldDragInfo
= dragInfo
;
663 updateDraggingInfo(scr
, &dragInfo
, &ev
, icon
);
665 XMoveWindow(dpy
, icon
, dragInfo
.imageLocation
.x
,
666 dragInfo
.imageLocation
.y
);
669 processMotion(scr
, &dragInfo
, &oldDragInfo
, &rect
, action
);
673 /* XXXif entered a different destination, check the operation */
677 if (oldDragInfo
.destinationWindow
!= None
678 && (dragInfo
.destinationWindow
== None
679 || dragInfo
.destinationWindow
== scr
->rootWin
)) {
680 /* left the droppable window */
687 if (dragInfo
.destinationWindow
!= None
) {
695 if (oldDragInfo
.destinationWindow
!= None
696 && (dragInfo
.destinationWindow
== None
697 || dragInfo
.destinationWindow
== scr
->rootWin
)) {
698 /* left the droppable window */
709 /* if (state >= 1 && state <= 4) */ {
713 oldDragInfo
= dragInfo
;
715 updateDraggingInfo(scr
, &dragInfo
, &ev
, icon
);
717 XMoveWindow(dpy
, icon
, dragInfo
.imageLocation
.x
,
718 dragInfo
.imageLocation
.y
);
720 processMotion(scr
, &dragInfo
, &oldDragInfo
, &rect
,
728 timer
= WMAddTimerHandler(3000, timeoutCallback
,
729 (void*)dragInfo
.destinationWindow
);
745 case SelectionRequest
:
752 if ((state
== 1 || state
== 3 || state
== 4 || state
== 5)
753 && ev
.xclient
.message_type
== scr
->xdndStatusAtom
754 && ev
.xclient
.window
== dragInfo
.destinationWindow
) {
756 if (ev
.xclient
.data
.l
[1] & 1) {
757 puts("got accept msg");
758 /* will accept drop */
767 WMDeleteTimerHandler(timer
);
773 if (ev
.xclient
.data
.l
[4] == None
) {
776 action
= ev
.xclient
.data
.l
[4];/*XXX*/
779 puts("got reject msg");
789 WMDeleteTimerHandler(timer
);
797 if (ev
.xclient
.data
.l
[1] & (1<<1)) {
798 rect
.pos
.x
= ev
.xclient
.data
.l
[2] >> 16;
799 rect
.pos
.y
= ev
.xclient
.data
.l
[2] & 0xffff;
800 rect
.size
.width
= ev
.xclient
.data
.l
[3] >> 16;
801 rect
.size
.height
= ev
.xclient
.data
.l
[3] & 0xffff;
806 } else if ((state
>= 1 && state
<= 5)
807 && ev
.xclient
.message_type
== scr
->xdndFinishedAtom
808 && ev
.xclient
.window
== dragInfo
.destinationWindow
) {
810 wwarning("drag source received unexpected XdndFinished message from %x",
811 (unsigned)dragInfo
.destinationWindow
);
813 if (state
== 3 || state
== 4 || state
== 5) {
816 WMDeleteTimerHandler(timer
);
827 if (ostate
!= state
) {
828 printf("state changed to %i\n", state
);
830 XRecolorCursor(dpy
, scr
->defaultCursor
, &green
, &back
);
831 } else if (ostate
== 3) {
832 XRecolorCursor(dpy
, scr
->defaultCursor
, &black
, &back
);
839 WMDeleteTimerHandler(timer
);
843 XUngrabPointer(dpy
, CurrentTime
);
845 SPIT("exited main loop");
847 if (_XErrorOccured
|| state
!= 6) {
851 assert(dragInfo
.destinationWindow
!= None
);
854 notifyDrop(scr
, &dragInfo
);
862 /* wait for Finished message and SelectionRequest if not a local drop */
866 XDestroyWindow(dpy
, icon
);
867 if (view
->dragSourceProcs
->endedDragImage
!= NULL
) {
868 view
->dragSourceProcs
->endedDragImage(view
, image
,
869 dragInfo
.imageLocation
,
876 WMReleaseData(draggedData
);
880 slideWindow(dpy
, icon
,
881 dragInfo
.imageLocation
.x
, dragInfo
.imageLocation
.y
,
882 atLocation
.x
, atLocation
.y
);
884 XDestroyWindow(dpy
, icon
);
885 if (view
->dragSourceProcs
->endedDragImage
!= NULL
) {
886 view
->dragSourceProcs
->endedDragImage(view
, image
,
887 dragInfo
.imageLocation
,
910 getTypeList(Window window
, XClientMessageEvent
*event
)
915 if (event
->data
.l
[1] & 1) { /* > 3 types */
918 types
= wmalloc(4 * sizeof(Atom
));
919 if (event
->data
.l
[2] != None
)
920 types
[i
++] = event
->data
.l
[2];
921 if (event
->data
.l
[3] != None
)
922 types
[i
++] = event
->data
.l
[3];
923 if (event
->data
.l
[4] != None
)
924 types
[i
++] = event
->data
.l
[4];
929 wwarning("received invalid drag & drop type list");
938 W_HandleDNDClientMessage(WMView
*toplevel
, XClientMessageEvent
*event
)
941 WMScreen
*scr
= W_VIEW_SCREEN(toplevel
);
942 WMView
*oldView
= NULL
, *newView
= NULL
;
943 unsigned operation
= 0;
946 if (event
->message_type
== scr
->xdndEnterAtom
) {
952 if (scr
->dragInfo
.sourceWindow
!= None
) {
953 puts("received Enter event in bad order");
956 memset(&scr
->dragInfo
, 0, sizeof(WMDraggingInfo
));
959 if ((event
->data
.l
[0] >> 24) > XDND_VERSION
) {
960 wwarning("received drag & drop request with unsupported version %i",
961 (event
->data
.l
[0] >> 24));
965 scr
->dragInfo
.protocolVersion
= event
->data
.l
[1] >> 24;
966 scr
->dragInfo
.sourceWindow
= event
->data
.l
[0];
967 scr
->dragInfo
.destinationWindow
= event
->window
;
970 scr
->dragInfo
.image
= NULL
;
972 XQueryPointer(scr
->display
, scr
->rootWin
, &foo
, &bar
,
973 &scr
->dragInfo
.location
.x
, &scr
->dragInfo
.location
.y
,
976 translateCoordinates(scr
, scr
->dragInfo
.destinationWindow
,
977 scr
->dragInfo
.location
.x
,
978 scr
->dragInfo
.location
.y
, &x
, &y
);
981 newView
= findViewInToplevel(scr
->display
,
982 scr
->dragInfo
.destinationWindow
,
985 if (IS_DROPPABLE(view
)) {
987 scr
->dragInfo
.destinationView
= view
;
990 view
->dragDestinationProcs
->draggingEntered(view
,
1002 sendClientMessage(scr
->display
, scr
->dragInfo
.sourceWindow
,
1003 scr
->xdndStatusAtom
,
1004 scr
->dragInfo
.destinationWindow
,
1008 } else if (event
->message_type
== scr
->xdndPositionAtom
1009 && scr
->dragInfo
.sourceWindow
== event
->data
.l
[0]) {
1011 scr
->dragInfo
.location
.x
= event
->data
.l
[2] >> 16;
1012 scr
->dragInfo
.location
.y
= event
->data
.l
[2] & 0xffff;
1014 if (scr
->dragInfo
.protocolVersion
>= 1) {
1015 scr
->dragInfo
.timestamp
= event
->data
.l
[3];
1016 scr
->dragInfo
.sourceOperation
= event
->data
.l
[4];
1018 scr
->dragInfo
.timestamp
= CurrentTime
;
1019 scr
->dragInfo
.sourceOperation
= 0; /*XXX*/
1022 translateCoordinates(scr
, scr
->dragInfo
.destinationWindow
,
1023 scr
->dragInfo
.location
.x
,
1024 scr
->dragInfo
.location
.y
, &x
, &y
);
1026 view
= findViewInToplevel(scr
->display
,
1027 scr
->dragInfo
.destinationWindow
,
1030 if (scr
->dragInfo
.destinationView
!= view
) {
1031 WMView
*oldVIew
= scr
->dragInfo
.destinationView
;
1033 oldView
->dragDestinationProcs
->draggingExited(oldView
,
1036 scr
->dragInfo
.destinationView
= NULL
;
1040 if (IS_DROPPABLE(view
)) {
1044 view
->dragDestinationProcs
->draggingUpdated(view
,
1048 if (operation
== 0) {
1049 sendClientMessage(scr
->display
, scr
->dragInfo
.sourceWindow
,
1050 scr
->xdndStatusAtom
,
1051 scr
->dragInfo
.destinationWindow
,
1056 switch (operation
) {
1062 sendClientMessage(scr
->display
, scr
->dragInfo
.sourceWindow
,
1063 scr
->xdndStatusAtom
,
1064 scr
->dragInfo
.destinationWindow
,
1068 } else if (event
->message_type
== scr
->xdndLeaveAtom
1069 && scr
->dragInfo
.sourceWindow
== event
->data
.l
[0]) {
1071 void (*draggingExited
)(WMView
*self
, WMDraggingInfo
*info
);
1074 } else if (event
->message_type
== scr
->xdndDropAtom
1075 && scr
->dragInfo
.sourceWindow
== event
->data
.l
[0]) {