2 This is a modified DND 1.0 library which does not depend on Xt
4 Modifications Copyright (c) 1997 Oliver Graf <ograf@fga.de>
7 Copyright (C) 1996 César Crusius
9 This file is part of the DND Library. This library is free
10 software; you can redistribute it and/or modify it under the terms of
11 the GNU Library General Public License as published by the Free
12 Software Foundation; either version 2 of the License, or (at your
13 option) any later version. This library is distributed in the hope
14 that it will be useful, but WITHOUT ANY WARRANTY; without even the
15 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
16 PURPOSE. See the GNU Library General Public License for more details.
17 You should have received a copy of the GNU Library General Public
18 License along with this library; if not, write to the Free Software
19 Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
24 #include "ui/X11/offix.h"
25 #include <X11/cursorfont.h>
26 #include <X11/Xatom.h>
27 #include <X11/Xmu/WinUtil.h>
33 static Display *dpy; /* current display */
34 static int DragPrecision; /* minimum dx,dy to start drag */
35 static int Dragging; /* Drag state flag */
36 static int DataOK; /* Non-zero if data registered */
37 static Atom DndProtocol; /* ClientMessage identifier */
38 static Atom DndSelection; /* For the data transfers */
39 static Atom OldDndProtocol; /* Version 0 atom */
40 static Atom OldDndSelection; /* Version 0 atom */
41 static Atom WM_STATE; /* Needed for icon stuff */
42 static Window Target; /* Drop window */
43 static Widget MainWidget; /* Main widget of application */
44 static int DataType; /* Current drag data type */
45 static int RootFlag; /* Non-zero if dropped on root */
46 static XColor Black, White; /* For the cursors */
48 /*=========================================================================
49 * Data for the standard Dnd cursors
50 *=========================================================================*/
51 #include "ui/X11/offix-cursors.h"
53 /*=============================================================== CursorData
54 * CursorData contains all the data for the cursors bitmaps
55 *==========================================================================*/
58 unsigned char *ImageData, *MaskData;
59 int HotSpotX, HotSpotY;
60 Pixmap ImagePixmap, MaskPixmap;
64 static CursorData DndCursor[DndEND] = {
65 {0, 0, NULL, NULL, 0, 0, 0},
66 {grey_width, grey_height, grey_bits, grey_mask_bits,
67 grey_x_hot, grey_y_hot},
68 {file_width, file_height, file_bits, file_mask_bits,
69 file_x_hot, file_y_hot},
70 {files_width, files_height, files_bits, files_mask_bits,
71 files_x_hot, files_y_hot},
72 {text_width, text_height, text_bits, text_mask_bits,
73 text_x_hot, text_y_hot},
74 {dir_width, dir_height, dir_bits, dir_mask_bits,
75 dir_x_hot, dir_y_hot},
76 {link_width, link_height, link_bits, link_mask_bits,
77 link_x_hot, link_y_hot},
78 {app_width, app_height, app_bits, app_mask_bits,
79 app_x_hot, app_y_hot},
80 {url_width, url_height, url_bits, url_mask_bits,
81 url_x_hot, url_y_hot},
82 {mime_width, mime_height, mime_bits, mime_mask_bits,
83 mime_x_hot, mime_y_hot}
86 /* Local prototypes */
87 int DndIsDragging(void);
88 void DndStartAction(Widget widget, XtPointer data, XEvent * event, Boolean * p);
89 void DndPropertyHandler(Widget widget,
90 XtPointer data, XEvent * event, Boolean * p);
92 /*======================================================== DndHandleDragging
93 * Takes care of the drag and drop process. Wait until the pointer had moved
94 * a little. Then takes control over the pointer until the buttons are
95 * released. After that send a Drag And Drop ClientMessage event. Returns
96 * non-zero if a drop did take place.
97 *===========================================================================*/
98 int DndHandleDragging(Widget widget, XEvent * event)
101 Window root = RootWindowOfScreen(XtScreenOfObject(widget));
102 XtAppContext app = XtWidgetToApplicationContext(widget);
103 Window DispatchWindow;
109 XUngrabPointer(dpy, CurrentTime);
110 /* Take control over the pointer */
111 XGrabPointer(dpy, root, False,
112 ButtonMotionMask | ButtonPressMask | ButtonReleaseMask,
113 GrabModeSync, GrabModeAsync, root,
114 DndCursor[DataType].CursorID, CurrentTime);
116 /* Wait for button release */
120 XAllowEvents(dpy, SyncPointer, CurrentTime);
121 XtAppNextEvent(app, &Event);
122 switch (Event.type) {
124 if (Event.xbutton.subwindow)
131 XtDispatchEvent(&Event);
136 /* Now release the pointer */
137 XUngrabPointer(dpy, CurrentTime);
138 /* Try to guess if the drop occurred in the root window */
140 Target = XmuClientWindow(dpy, Event.xbutton.subwindow);
141 if (Target == Event.xbutton.subwindow)
142 DispatchWindow = Target;
144 DispatchWindow = PointerWindow;
146 Target = DispatchWindow = XtWindow(MainWidget);
148 /* Now build the event structure */
149 DropX = Event.xbutton.x_root;
150 DropY = Event.xbutton.y_root;
151 Event.xclient.type = ClientMessage;
152 Event.xclient.display = dpy;
153 Event.xclient.message_type = DndProtocol;
154 Event.xclient.format = 32;
155 Event.xclient.window = Target;
156 Event.xclient.data.l[0] = DataType;
157 Event.xclient.data.l[1] = (long)event->xbutton.state;
158 Event.xclient.data.l[2] = (long)XtWindow(widget);
159 Event.xclient.data.l[3] = DropX + 65536L * (long)DropY;
160 Event.xclient.data.l[4] = 1;
162 /* Send the drop message */
163 XSendEvent(dpy, DispatchWindow, True, NoEventMask, &Event);
164 /* Send an old style version of the message just in case */
165 Event.xclient.message_type = OldDndProtocol;
166 XSendEvent(dpy, DispatchWindow, True, NoEventMask, &Event);
169 fprintf(stderr, "ClientMessage sent to 0x%x(0x%x).\n",
170 DispatchWindow, Target);
171 fprintf(stderr, "The drop coordinates are (%d,%d).\n", DropX, DropY);
177 /*=============================================================== DndIsIcon
178 * Return non-zero if the application is iconic (widget=toplevel)
179 *========================================================================*/
180 int DndIsIcon(Widget widget)
184 unsigned long WinState, JunkLong;
185 unsigned char *Property;
187 XGetWindowProperty(dpy, XtWindow(widget), WM_STATE,
188 0L, 2L, False, AnyPropertyType,
189 &JunkAtom, &JunkInt, &WinState, &JunkLong,
191 WinState = (unsigned long)(*((long *)Property));
192 return (WinState == 3);
195 /*============================================================ DndInitialize
196 * Must be called anywhere before the top level widget creation and the
197 * main loop. Initialize global variables and bind the DndDispatch function
198 * to the top level widget. Creates the cursors to be used in drag actions.
199 *=========================================================================*/
200 void DndInitialize(Widget shell)
206 dpy = XtDisplayOfObject(shell);
207 screen = DefaultScreen(dpy);
208 colormap = DefaultColormap(dpy, screen);
209 root = DefaultRootWindow(dpy);
211 Black.pixel = BlackPixel(dpy, screen);
212 White.pixel = WhitePixel(dpy, screen);
213 XQueryColor(dpy, colormap, &Black);
214 XQueryColor(dpy, colormap, &White);
216 for (i = 1; i != DndEND; i++) {
217 DndCursor[i].ImagePixmap =
218 XCreateBitmapFromData(dpy, root,
219 (char *)DndCursor[i].ImageData,
221 DndCursor[i].Height);
222 DndCursor[i].MaskPixmap =
223 XCreateBitmapFromData(dpy, root,
224 (char *)DndCursor[i].MaskData,
226 DndCursor[i].Height);
227 DndCursor[i].CursorID =
228 XCreatePixmapCursor(dpy, DndCursor[i].ImagePixmap,
229 DndCursor[i].MaskPixmap,
231 DndCursor[i].HotSpotX,
232 DndCursor[i].HotSpotY);
235 DndCursor[0].CursorID = XCreateFontCursor(dpy, XC_question_arrow);
237 /* These two are for older versions */
238 OldDndProtocol = XInternAtom(dpy, "DndProtocol", FALSE);
239 OldDndSelection = XInternAtom(dpy, "DndSelection", FALSE);
240 /* Now the correct stuff */
241 DndProtocol = XInternAtom(dpy, "_DND_PROTOCOL", FALSE);
242 DndSelection = XInternAtom(dpy, "_DND_SELECTION", FALSE);
244 WM_STATE = XInternAtom(dpy, "WM_STATE", True);
251 int DndIsDragging(void)
256 /*================================================================= DndSetData
257 * Updates the selection data.
258 *===========================================================================*/
259 void DndSetData(int Type, unsigned char *Data, unsigned long Size)
261 Window root = DefaultRootWindow(dpy);
263 unsigned char *AuxData;
264 unsigned long BackSize = Size;
269 /* Set the data type -- allow any type */
274 AuxSize = (Size <= INT_MAX ? (int)Size : INT_MAX);
275 XChangeProperty(dpy, root, DndSelection, XA_STRING, 8,
276 PropModeReplace, Data, AuxSize);
277 for (Size -= (unsigned long)AuxSize; Size;
278 Size -= (unsigned long)AuxSize) {
280 AuxSize = ((Size <= (INT_MAX)) ? (int)Size : (INT_MAX));
281 XChangeProperty(dpy, root, DndSelection, XA_STRING, 8,
282 PropModeAppend, Data, AuxSize);
285 /* Set the data for old DND version */
288 AuxSize = (Size <= INT_MAX ? (int)Size : INT_MAX);
289 XChangeProperty(dpy, root, OldDndSelection, XA_STRING, 8,
290 PropModeReplace, Data, AuxSize);
291 for (Size -= (unsigned long)AuxSize; Size;
292 Size -= (unsigned long)AuxSize) {
294 AuxSize = ((Size <= (INT_MAX)) ? (int)Size : (INT_MAX));
295 XChangeProperty(dpy, root, OldDndSelection, XA_STRING, 8,
296 PropModeAppend, Data, AuxSize);
299 /* Everything is now ok */
303 /*================================================================== DndGetData
304 * Return a pointer to the current data. See HOWTO for more details.
305 *===========================================================================*/
306 void DndGetData(XEvent * event, unsigned char **Data, unsigned long *Size)
308 Window root = DefaultRootWindow(dpy);
310 Atom ActualType, ActualDndSelection;
312 unsigned long RemainingBytes;
314 ActualDndSelection = (DndProtocolVersion(event) == 0L ?
315 OldDndSelection : DndSelection);
317 XGetWindowProperty(dpy, root, ActualDndSelection,
319 FALSE, AnyPropertyType,
320 &ActualType, &ActualFormat,
321 Size, &RemainingBytes, Data);
324 /*================================== DndDataType DndDragButtons DndSourceWidget
326 * Return information about the Dnd event received. If a non-dnd event is
327 * passed, the function DndDataType returns DndNotDnd, and the others
329 *===========================================================================*/
330 int DndDataType(XEvent * event)
334 if (!DndIsDropMessage(event))
336 Type = (int)(event->xclient.data.l[0]);
342 unsigned int DndDragButtons(XEvent * event)
344 if (!DndIsDropMessage(event))
346 return (unsigned int)(event->xclient.data.l[1]);
349 Window DndSourceWindow(XEvent * event)
351 if (!DndIsDropMessage(event))
353 if (DndProtocolVersion(event) < __DragAndDropH__)
354 /* We will try to do something about it, but nothing is certain */
355 return XtWindow((Widget) (event->xclient.data.l[2]));
356 return (Window) (event->xclient.data.l[2]);
359 void DndDropRootCoordinates(XEvent * event, int *x, int *y)
361 if (!DndIsDropMessage(event)) {
367 /* If it is an old protocol version we try to get the coordinates
368 using the current pointer position. Of course, the pointer may have
369 moved since the drop, but there's nothing we can do about it.
371 if (DndProtocolVersion(event) < 1L) {
372 Window root_return, child_return;
373 int win_x_return, win_y_return;
374 unsigned int mask_return;
376 XQueryPointer(dpy, DefaultRootWindow(dpy),
377 &root_return, &child_return, x, y,
378 &win_x_return, &win_y_return, &mask_return);
381 /* Thanks god you are using a decent protocol version */
382 *x = (int)((long)(event->xclient.data.l[3]) & 0xffff);
383 *y = (int)((long)(event->xclient.data.l[3]) / 65536);
386 void DndDropCoordinates(Widget widget, XEvent * event, int *x, int *y)
391 DndDropRootCoordinates(event, &root_x, &root_y);
392 XTranslateCoordinates(dpy, DefaultRootWindow(dpy),
394 root_x, root_y, x, y, &child_return);
397 long DndProtocolVersion(XEvent * event)
399 if (!DndIsDropMessage(event))
401 return event->xclient.data.l[4];
404 int DndIsDropMessage(XEvent * event)
406 if (event->xclient.type != ClientMessage)
408 if (event->xclient.message_type == OldDndProtocol &&
409 event->xclient.data.l[4] == 0)
411 if (event->xclient.message_type == DndProtocol)
417 DndChangeCursor(int Type, int width, int height, char *image, char *mask,
418 int hot_x, int hot_y)
420 DndCursor[Type].ImagePixmap =
421 XCreateBitmapFromData(dpy, DefaultRootWindow(dpy),
422 image, width, height);
423 DndCursor[Type].MaskPixmap =
424 XCreateBitmapFromData(dpy, DefaultRootWindow(dpy),
425 mask, width, height);
426 DndCursor[Type].CursorID =
427 XCreatePixmapCursor(dpy, DndCursor[Type].ImagePixmap,
428 DndCursor[Type].MaskPixmap,
429 &Black, &White, hot_x, hot_y);