Cleanup some jack server interactions
[sxemacs] / src / offix.c
1 /*
2 This is a modified DND 1.0 library which does not depend on Xt
3 event handling.
4 Modifications Copyright (c) 1997 Oliver Graf <ograf@fga.de>
5
6 Original DND lib
7 Copyright (C) 1996 César Crusius
8
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.
20 */
21
22 /* #define DEBUG */
23
24 #include "ui/X11/offix.h"
25 #include <X11/cursorfont.h>
26 #include <X11/Xatom.h>
27 #include <X11/Xmu/WinUtil.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <limits.h>
31
32 /* Local variables */
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              */
47
48 /*=========================================================================
49  * Data for the standard Dnd cursors
50  *=========================================================================*/
51 #include "ui/X11/offix-cursors.h"
52
53 /*=============================================================== CursorData
54  * CursorData contains all the data for the cursors bitmaps
55  *==========================================================================*/
56 typedef struct {
57         int Width, Height;
58         unsigned char *ImageData, *MaskData;
59         int HotSpotX, HotSpotY;
60         Pixmap ImagePixmap, MaskPixmap;
61         Cursor CursorID;
62 } CursorData;
63
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}
84 };
85
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);
91
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)
99 {
100         XEvent Event;
101         Window root = RootWindowOfScreen(XtScreenOfObject(widget));
102         XtAppContext app = XtWidgetToApplicationContext(widget);
103         Window DispatchWindow;
104         int DropX, DropY;
105
106         if (Dragging)
107                 return 0;
108
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);
115
116         /* Wait for button release */
117         Dragging = 1;
118         RootFlag = 0;
119         while (Dragging) {
120                 XAllowEvents(dpy, SyncPointer, CurrentTime);
121                 XtAppNextEvent(app, &Event);
122                 switch (Event.type) {
123                 case ButtonRelease:
124                         if (Event.xbutton.subwindow)
125                                 RootFlag = 0;
126                         else
127                                 RootFlag = 1;
128                         Dragging = 0;
129                         break;
130                 default:
131                         XtDispatchEvent(&Event);
132                         break;
133                 }
134         }
135         DataOK = 0;
136         /* Now release the pointer */
137         XUngrabPointer(dpy, CurrentTime);
138         /* Try to guess if the drop occurred in the root window */
139         if (!RootFlag) {
140                 Target = XmuClientWindow(dpy, Event.xbutton.subwindow);
141                 if (Target == Event.xbutton.subwindow)
142                         DispatchWindow = Target;
143                 else
144                         DispatchWindow = PointerWindow;
145         } else
146                 Target = DispatchWindow = XtWindow(MainWidget);
147
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;
161
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);
167
168 #ifdef DEBUG
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);
172 #endif
173
174         return 1;
175 }
176
177 /*=============================================================== DndIsIcon
178  * Return non-zero if the application is iconic (widget=toplevel)
179  *========================================================================*/
180 int DndIsIcon(Widget widget)
181 {
182         Atom JunkAtom;
183         int JunkInt;
184         unsigned long WinState, JunkLong;
185         unsigned char *Property;
186
187         XGetWindowProperty(dpy, XtWindow(widget), WM_STATE,
188                            0L, 2L, False, AnyPropertyType,
189                            &JunkAtom, &JunkInt, &WinState, &JunkLong,
190                            &Property);
191         WinState = (unsigned long)(*((long *)Property));
192         return (WinState == 3);
193 }
194
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)
201 {
202         int screen, i;
203         Colormap colormap;
204         Window root;
205
206         dpy = XtDisplayOfObject(shell);
207         screen = DefaultScreen(dpy);
208         colormap = DefaultColormap(dpy, screen);
209         root = DefaultRootWindow(dpy);
210
211         Black.pixel = BlackPixel(dpy, screen);
212         White.pixel = WhitePixel(dpy, screen);
213         XQueryColor(dpy, colormap, &Black);
214         XQueryColor(dpy, colormap, &White);
215
216         for (i = 1; i != DndEND; i++) {
217                 DndCursor[i].ImagePixmap =
218                     XCreateBitmapFromData(dpy, root,
219                                           (char *)DndCursor[i].ImageData,
220                                           DndCursor[i].Width,
221                                           DndCursor[i].Height);
222                 DndCursor[i].MaskPixmap =
223                     XCreateBitmapFromData(dpy, root,
224                                           (char *)DndCursor[i].MaskData,
225                                           DndCursor[i].Width,
226                                           DndCursor[i].Height);
227                 DndCursor[i].CursorID =
228                     XCreatePixmapCursor(dpy, DndCursor[i].ImagePixmap,
229                                         DndCursor[i].MaskPixmap,
230                                         &Black, &White,
231                                         DndCursor[i].HotSpotX,
232                                         DndCursor[i].HotSpotY);
233         }
234
235         DndCursor[0].CursorID = XCreateFontCursor(dpy, XC_question_arrow);
236
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);
243
244         WM_STATE = XInternAtom(dpy, "WM_STATE", True);
245         Dragging = 0;
246         DragPrecision = 10;
247         RootFlag = 0;
248         MainWidget = shell;
249 }
250
251 int DndIsDragging(void)
252 {
253         return Dragging;
254 }
255
256 /*================================================================= DndSetData
257  * Updates the selection data.
258  *===========================================================================*/
259 void DndSetData(int Type, unsigned char *Data, unsigned long Size)
260 {
261         Window root = DefaultRootWindow(dpy);
262         int AuxSize;
263         unsigned char *AuxData;
264         unsigned long BackSize = Size;
265
266         if (DataOK)
267                 return;
268
269         /* Set the data type -- allow any type */
270         DataType = Type;
271
272         /* Set the data */
273         AuxData = Data;
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) {
279                 Data += AuxSize;
280                 AuxSize = ((Size <= (INT_MAX)) ? (int)Size : (INT_MAX));
281                 XChangeProperty(dpy, root, DndSelection, XA_STRING, 8,
282                                 PropModeAppend, Data, AuxSize);
283         }
284
285         /* Set the data for old DND version */
286         Size = BackSize;
287         AuxData = Data;
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) {
293                 Data += AuxSize;
294                 AuxSize = ((Size <= (INT_MAX)) ? (int)Size : (INT_MAX));
295                 XChangeProperty(dpy, root, OldDndSelection, XA_STRING, 8,
296                                 PropModeAppend, Data, AuxSize);
297         }
298
299         /* Everything is now ok */
300         DataOK = 1;
301 }
302
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)
307 {
308         Window root = DefaultRootWindow(dpy);
309
310         Atom ActualType, ActualDndSelection;
311         int ActualFormat;
312         unsigned long RemainingBytes;
313
314         ActualDndSelection = (DndProtocolVersion(event) == 0L ?
315                               OldDndSelection : DndSelection);
316
317         XGetWindowProperty(dpy, root, ActualDndSelection,
318                            0L, 1000000L,
319                            FALSE, AnyPropertyType,
320                            &ActualType, &ActualFormat,
321                            Size, &RemainingBytes, Data);
322 }
323
324 /*================================== DndDataType DndDragButtons DndSourceWidget
325  *
326  * Return information about the Dnd event received. If a non-dnd event is
327  * passed, the function DndDataType returns DndNotDnd, and the others
328  * return zero.
329  *===========================================================================*/
330 int DndDataType(XEvent * event)
331 {
332         int Type;
333
334         if (!DndIsDropMessage(event))
335                 return DndNotDnd;
336         Type = (int)(event->xclient.data.l[0]);
337         if (Type >= DndEND)
338                 Type = DndUnknown;
339         return Type;
340 }
341
342 unsigned int DndDragButtons(XEvent * event)
343 {
344         if (!DndIsDropMessage(event))
345                 return 0;
346         return (unsigned int)(event->xclient.data.l[1]);
347 }
348
349 Window DndSourceWindow(XEvent * event)
350 {
351         if (!DndIsDropMessage(event))
352                 return 0;
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]);
357 }
358
359 void DndDropRootCoordinates(XEvent * event, int *x, int *y)
360 {
361         if (!DndIsDropMessage(event)) {
362                 *x = 0;
363                 *y = 0;
364                 return;
365         }
366
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.
370          */
371         if (DndProtocolVersion(event) < 1L) {
372                 Window root_return, child_return;
373                 int win_x_return, win_y_return;
374                 unsigned int mask_return;
375
376                 XQueryPointer(dpy, DefaultRootWindow(dpy),
377                               &root_return, &child_return, x, y,
378                               &win_x_return, &win_y_return, &mask_return);
379                 return;
380         }
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);
384 }
385
386 void DndDropCoordinates(Widget widget, XEvent * event, int *x, int *y)
387 {
388         int root_x, root_y;
389         Window child_return;
390
391         DndDropRootCoordinates(event, &root_x, &root_y);
392         XTranslateCoordinates(dpy, DefaultRootWindow(dpy),
393                               XtWindow(widget),
394                               root_x, root_y, x, y, &child_return);
395 }
396
397 long DndProtocolVersion(XEvent * event)
398 {
399         if (!DndIsDropMessage(event))
400                 return -1L;
401         return event->xclient.data.l[4];
402 }
403
404 int DndIsDropMessage(XEvent * event)
405 {
406         if (event->xclient.type != ClientMessage)
407                 return 0;
408         if (event->xclient.message_type == OldDndProtocol &&
409             event->xclient.data.l[4] == 0)
410                 return 1;
411         if (event->xclient.message_type == DndProtocol)
412                 return 1;
413         return 0;
414 }
415
416 void
417 DndChangeCursor(int Type, int width, int height, char *image, char *mask,
418                 int hot_x, int hot_y)
419 {
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);
430 }