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);
381 updateDraggingInfo(WMScreen
*scr
, WMDraggingInfo
*info
,
382 XEvent
*event
, Window iconWindow
)
387 size
= WMGetPixmapSize(info
->image
);
389 if (event
->type
== MotionNotify
) {
390 info
->imageLocation
.x
= event
->xmotion
.x_root
-(int)size
.width
/2;
391 info
->imageLocation
.y
= event
->xmotion
.y_root
-(int)size
.height
/2;
393 info
->location
.x
= event
->xmotion
.x_root
;
394 info
->location
.y
= event
->xmotion
.y_root
;
395 info
->timestamp
= event
->xmotion
.time
;
397 } else if (event
->type
== ButtonRelease
) {
398 info
->imageLocation
.x
= event
->xbutton
.x_root
-(int)size
.width
/2;
399 info
->imageLocation
.y
= event
->xbutton
.y_root
-(int)size
.height
/2;
401 info
->location
.x
= event
->xbutton
.x_root
;
402 info
->location
.y
= event
->xbutton
.y_root
;
403 info
->timestamp
= event
->xbutton
.time
;
406 toplevel
= findToplevelUnderDragPointer(scr
,
410 info
->destinationWindow
= toplevel
;
412 if (toplevel == None) {
413 info->destinationWindow = None;
414 } else if (toplevel == scr->rootWin) {
415 info->destinationWindow = scr->rootWin;
420 XTranslateCoordinates(scr->display, scr->rootWin, toplevel,
421 info->location.x, info->location.y,
424 child = findChildInWindow(scr->display, toplevel, x, y);
427 info->destination = W_GetViewForXWindow(scr->display, child);
428 if (info->destination->droppableTypes == NULL) {
429 info->destination = NULL;
430 } else if (info->destination->dragDestinationProcs == NULL) {
431 info->destination = NULL;
434 info->destination = NULL;
436 info->destinationWindow = toplevel;
445 processMotion(WMScreen
*scr
, WMDraggingInfo
*info
, WMDraggingInfo
*oldInfo
,
446 WMRect
*rect
, unsigned currentAction
)
450 if (info
->destinationWindow
== None
) { /* entered an unsupporeted window */
452 if (oldInfo
->destinationWindow
!= None
453 && oldInfo
->destinationWindow
!= scr
->rootWin
) {
456 notifyDragLeave(scr
, oldInfo
);
459 } else if (info
->destinationWindow
== scr
->rootWin
) {
461 if (oldInfo
->destinationWindow
!= None
462 && oldInfo
->destinationWindow
!= scr
->rootWin
) {
463 SPIT("left window to root");
465 notifyDragLeave(scr
, oldInfo
);
470 } else if (oldInfo
->destinationWindow
!= info
->destinationWindow
) {
472 if (oldInfo
->destinationWindow
!= None
473 && oldInfo
->destinationWindow
!= scr
->rootWin
) {
474 notifyDragLeave(scr
, oldInfo
);
477 SPIT("entered window");
480 action
= notifyDragEnter(scr
, info
);
484 #define LEFT_RECT(r, X, Y) (X < r->pos.x || Y < r->pos.y \
485 || X >= r->pos.x + r->size.width \
486 || Y >= r->pos.y + r->size.height)
488 if (rect
->size
.width
== 0 ||
489 (LEFT_RECT(rect
, info
->location
.x
, info
->location
.y
))) {
491 action
= notifyPosition(scr
, info
);
493 rect
->size
.width
= 0;
497 /* little trick to simulate XdndStatus for local dnd */
499 if (bla && action != currentAction) {
502 ev.type = ClientMessage;
503 ev.xclient.display = scr->display;
504 ev.xclient.message_type = scr->xdndStatusAtom;
505 ev.xclient.format = 32;
506 ev.xclient.window = info->destinationWindow;
507 ev.xclient.data.l[0] = info->sourceWindow;
508 ev.xclient.data.l[1] = (action ? 1 : 0);
509 ev.xclient.data.l[2] = 0;
510 ev.xclient.data.l[3] = 0;
511 ev.xclient.data.l[4] = action;
513 XPutBackEvent(scr->display, &ev);
521 timeoutCallback(void *data
)
523 wwarning("drag & drop timed out while waiting for response from 0x%x\n",
529 * State Machine For Drag Source:
530 * ------------------------------
532 * State Call Mtn Ent Lea Crs BUp StA StR Fin TO
533 * 0) idle 1bu - - - - - - - - -
534 * 1) drag over target - 1au - 2cu 1cbu 5fu 3 4 1w -
535 * 2) drag over nothing - 2 1bu - - 0 - - 2w -
536 * 3) drag targ+accept - 3u - 2cu 1cbu 6f 3 4w 0z -
537 * 4) drag targ+reject - 4u - 2cu 1cbu 0 3w 4 0z -
538 * 5) waiting status - 5X 5X 5X 5X - 6f 0 0z 0w
539 * 6) dropped - - - - - - - - 0 0w
542 * Call - called WMDragImageFromView()
544 * Ent - Enter droppable window
545 * Lea - Leave droppable window (or rectangle)
546 * Crs - Leave droppable window (or rectangle) and enter another
547 * BUp - Button Released
548 * StA - XdndStatus client msg with Accept drop
549 * StR - XdndStatus client msg with Reject drop
550 * Fin - XdndFinish client msg
554 * a - send update message
555 * b - send enter message
556 * c - send leave message
557 * d - release drag section info
558 * e - send drop message
560 * u - update dragInfo
563 * w - warn about unexpected reply
564 * z - abort operation.. unexpected reply
568 WMDragImageFromView(WMView
*view
, WMPixmap
*image
, char *dataTypes
[],
569 WMPoint atLocation
, WMSize mouseOffset
, XEvent
*event
,
572 WMScreen
*scr
= view
->screen
;
573 Display
*dpy
= scr
->display
;
577 WMRect rect
= {{0,0},{0,0}};
581 XColor black
= {0, 0,0,0, DoRed
|DoGreen
|DoBlue
};
582 XColor green
= {0x0045b045, 0x4500,0xb000,0x4500, DoRed
|DoGreen
|DoBlue
};
583 XColor back
= {0, 0xffff,0xffff,0xffff, DoRed
|DoGreen
|DoBlue
};
584 WMDraggingInfo dragInfo
;
585 WMDraggingInfo oldDragInfo
;
586 WMHandlerID timer
= NULL
;
587 WMData
*draggedData
= NULL
;
590 wassertr(view
->dragSourceProcs
!= NULL
);
593 /* prepare icon to be dragged */
595 image
= scr
->defaultObjectIcon
;
597 size
= WMGetPixmapSize(image
);
599 icon
= makeDragIcon(scr
, image
);
601 XMoveWindow(dpy
, icon
, event
->xmotion
.x_root
-(int)size
.width
/2,
602 event
->xmotion
.y_root
-(int)size
.height
/2);
603 XMapRaised(dpy
, icon
);
606 /* init dragging info */
607 memset(&dragInfo
, 0, sizeof(WMDraggingInfo
));
608 memset(&oldDragInfo
, 0, sizeof(WMDraggingInfo
));
609 dragInfo
.image
= image
;
610 dragInfo
.sourceWindow
= W_VIEW_DRAWABLE(W_TopLevelOfView(view
));
612 dragInfo
.destinationWindow
= dragInfo
.sourceWindow
;
614 dragInfo
.location
.x
= event
->xmotion
.x_root
;
615 dragInfo
.location
.y
= event
->xmotion
.y_root
;
616 dragInfo
.imageLocation
= atLocation
;
619 /* start pointer grab */
620 XGrabPointer(dpy
, scr
->rootWin
, False
,
621 ButtonPressMask
|ButtonReleaseMask
|ButtonMotionMask
,
622 GrabModeSync
, GrabModeAsync
, None
, scr
->defaultCursor
,
627 _XErrorOccured
= False
;
629 /* XXX: take ownership of XdndSelection */
632 if (view
->dragSourceProcs
->beganDragImage
!= NULL
) {
633 view
->dragSourceProcs
->beganDragImage(view
, image
, atLocation
);
636 processMotion(scr
, &dragInfo
, &oldDragInfo
, &rect
, action
);
640 while (state
!= 6 && state
!= 0 && !_XErrorOccured
) {
641 XAllowEvents(dpy
, SyncPointer
, CurrentTime
);
642 WMNextEvent(dpy
, &ev
);
646 if (state
>= 1 && state
<= 4) {
647 while (XCheckTypedEvent(dpy
, MotionNotify
, &ev
)) ;
651 oldDragInfo
= dragInfo
;
653 updateDraggingInfo(scr
, &dragInfo
, &ev
, icon
);
655 XMoveWindow(dpy
, icon
, dragInfo
.imageLocation
.x
,
656 dragInfo
.imageLocation
.y
);
659 processMotion(scr
, &dragInfo
, &oldDragInfo
, &rect
, action
);
663 /* XXXif entered a different destination, check the operation */
667 if (oldDragInfo
.destinationWindow
!= None
668 && (dragInfo
.destinationWindow
== None
669 || dragInfo
.destinationWindow
== scr
->rootWin
)) {
670 /* left the droppable window */
677 if (dragInfo
.destinationWindow
!= None
) {
685 if (oldDragInfo
.destinationWindow
!= None
686 && (dragInfo
.destinationWindow
== None
687 || dragInfo
.destinationWindow
== scr
->rootWin
)) {
688 /* left the droppable window */
699 /* if (state >= 1 && state <= 4) */ {
703 oldDragInfo
= dragInfo
;
705 updateDraggingInfo(scr
, &dragInfo
, &ev
, icon
);
707 XMoveWindow(dpy
, icon
, dragInfo
.imageLocation
.x
,
708 dragInfo
.imageLocation
.y
);
710 processMotion(scr
, &dragInfo
, &oldDragInfo
, &rect
,
718 timer
= WMAddTimerHandler(3000, timeoutCallback
,
719 (void*)dragInfo
.destinationWindow
);
735 case SelectionRequest
:
742 if ((state
== 1 || state
== 3 || state
== 4 || state
== 5)
743 && ev
.xclient
.message_type
== scr
->xdndStatusAtom
744 && ev
.xclient
.window
== dragInfo
.destinationWindow
) {
746 if (ev
.xclient
.data
.l
[1] & 1) {
747 puts("got accept msg");
748 /* will accept drop */
757 WMDeleteTimerHandler(timer
);
763 if (ev
.xclient
.data
.l
[4] == None
) {
766 action
= ev
.xclient
.data
.l
[4];/*XXX*/
769 puts("got reject msg");
779 WMDeleteTimerHandler(timer
);
787 if (ev
.xclient
.data
.l
[1] & (1<<1)) {
788 rect
.pos
.x
= ev
.xclient
.data
.l
[2] >> 16;
789 rect
.pos
.y
= ev
.xclient
.data
.l
[2] & 0xffff;
790 rect
.size
.width
= ev
.xclient
.data
.l
[3] >> 16;
791 rect
.size
.height
= ev
.xclient
.data
.l
[3] & 0xffff;
796 } else if ((state
>= 1 && state
<= 5)
797 && ev
.xclient
.message_type
== scr
->xdndFinishedAtom
798 && ev
.xclient
.window
== dragInfo
.destinationWindow
) {
800 wwarning("drag source received unexpected XdndFinished message from %x",
801 (unsigned)dragInfo
.destinationWindow
);
803 if (state
== 3 || state
== 4 || state
== 5) {
806 WMDeleteTimerHandler(timer
);
817 if (ostate
!= state
) {
818 printf("state changed to %i\n", state
);
820 XRecolorCursor(dpy
, scr
->defaultCursor
, &green
, &back
);
821 } else if (ostate
== 3) {
822 XRecolorCursor(dpy
, scr
->defaultCursor
, &black
, &back
);
829 WMDeleteTimerHandler(timer
);
833 XUngrabPointer(dpy
, CurrentTime
);
835 SPIT("exited main loop");
837 if (_XErrorOccured
|| state
!= 6) {
841 assert(dragInfo
.destinationWindow
!= None
);
844 notifyDrop(scr
, &dragInfo
);
852 /* wait for Finished message and SelectionRequest if not a local drop */
856 XDestroyWindow(dpy
, icon
);
857 if (view
->dragSourceProcs
->endedDragImage
!= NULL
) {
858 view
->dragSourceProcs
->endedDragImage(view
, image
,
859 dragInfo
.imageLocation
,
866 WMReleaseData(draggedData
);
870 slideWindow(dpy
, icon
,
871 dragInfo
.imageLocation
.x
, dragInfo
.imageLocation
.y
,
872 atLocation
.x
, atLocation
.y
);
874 XDestroyWindow(dpy
, icon
);
875 if (view
->dragSourceProcs
->endedDragImage
!= NULL
) {
876 view
->dragSourceProcs
->endedDragImage(view
, image
,
877 dragInfo
.imageLocation
,
900 getTypeList(Window window
, XClientMessageEvent
*event
)
905 if (event
->data
.l
[1] & 1) { /* > 3 types */
908 types
= wmalloc(4 * sizeof(Atom
));
909 if (event
->data
.l
[2] != None
)
910 types
[i
++] = event
->data
.l
[2];
911 if (event
->data
.l
[3] != None
)
912 types
[i
++] = event
->data
.l
[3];
913 if (event
->data
.l
[4] != None
)
914 types
[i
++] = event
->data
.l
[4];
918 if (types
[0] == NULL
) {
919 wwarning("received invalid drag & drop type list");
929 W_HandleDNDClientMessage(WMView
*toplevel
, XClientMessageEvent
*event
)
931 WMScreen
*scr
= W_VIEW_SCREEN(toplevel
);
935 if (event
->message_type
== scr
->xdndEnterAtom
) {
942 if (scr
->dragInfo
.sourceWindow
!= None
) {
943 puts("received Enter event in bad order");
946 memset(&scr
->dragInfo
, 0, sizeof(WMDraggingInfo
));
949 if ((event
->data
.l
[0] >> 24) > XDND_VERSION
) {
950 wwarning("received drag & drop request with unsupported version %i",
951 (event
->data
.l
[0] >> 24));
955 info
.protocolVersion
= event
->data
.l
[0] >> 24;
957 info
.sourceWindow
= event
->data
.l
[0];
958 info
.destinationWindow
= event
->window
;
963 XQueryPointer(scr
->display
, scr
->rootWin
, &foo
, &bar
,
964 &event
->location
.x
, &event
->location
.y
,
967 view
= findViewInToplevel(scr
->display
,
968 scr
->dragInfo
.destinationWindow
,
969 scr
->dragInfo
.location
.x
,
970 scr
->dragInfo
.location
.y
);
972 if (IS_DROPPABLE(view
)) {
974 view
->dragDestinationProcs
->draggingEntered(view
,
981 } else if (event
->message_type
== scr
->xdndPositionAtom
982 && scr
->dragInfo
.sourceWindow
== event
->data
.l
[0]) {
983 unsigned operation
= 0;
985 scr
->dragInfo
.location
.x
= event
->data
.l
[2] >> 16;
986 scr
->dragInfo
.location
.y
= event
->data
.l
[2] 0xffff;
988 if (scr
->dragInfo
.protocolVersion
>= 1) {
989 scr
->dragInfo
.timestamp
= event
->data
.l
[3];
990 scr
->dragInfo
.sourceOperation
= event
->data
.l
[4];
992 scr
->dragInfo
.timestamp
= CurrentTime
;
993 scr
->dragInfo
.sourceOperation
= 0; /*XXX*/
996 child
= findChildInWindow(scr
->display
, scr
->dragInfo
.destinationWindow
,
997 scr
->dragInfo
.location
.x
,
998 scr
->dragInfo
.location
.y
);
1000 view
= W_GetViewForXWindow(scr
->display
, child
);
1005 if (IS_DROPPABLE(view
)) {
1007 view
->dragDestinationProcs
->draggingUpdated(view
,
1011 if (operation
== 0) {
1012 sendClientMessage(scr
->display
, scr
->xdndStatusAtom
,
1013 scr
->dragInfo
.sourceWindow
,
1014 scr
->dragInfo
.destinationWindow
,
1019 switch (operation
) {
1025 sendClientMessage(scr
->display
, scr
->xdndStatusAtom
,
1026 scr
->dragInfo
.sourceWindow
,
1027 scr
->dragInfo
.destinationWindow
,
1032 } else if (event
->message_type
== scr
->xdndLeaveAtom
1033 && scr
->dragInfo
.sourceWindow
== event
->data
.l
[0]) {
1036 } else if (event
->message_type
== scr
->xdndDropAtom
1037 && scr
->dragInfo
.sourceWindow
== event
->data
.l
[0]) {