Change to the linux kernel coding style
[wmaker-crm.git] / WINGs / selection.c
1
2 #include <stdlib.h>
3
4 #include <X11/Xatom.h>
5
6 #include "WINGsP.h"
7
8 #define MAX_PROPERTY_SIZE 8*1024
9
10 char *WMSelectionOwnerDidChangeNotification = "WMSelectionOwnerDidChange";
11
12 typedef struct SelectionHandler {
13         WMView *view;
14         Atom selection;
15         Time timestamp;
16         WMSelectionProcs procs;
17         void *data;
18
19         struct {
20                 unsigned delete_pending:1;
21                 unsigned done_pending:1;
22         } flags;
23 } SelectionHandler;
24
25 typedef struct SelectionCallback {
26         WMView *view;
27         Atom selection;
28         Atom target;
29         Time timestamp;
30         WMSelectionCallback *callback;
31         void *data;
32
33         struct {
34                 unsigned delete_pending:1;
35                 unsigned done_pending:1;
36         } flags;
37 } SelectionCallback;
38
39 static WMArray *selCallbacks = NULL;
40
41 static WMArray *selHandlers = NULL;
42
43 static Bool gotXError = False;
44
45 void WMDeleteSelectionHandler(WMView * view, Atom selection, Time timestamp)
46 {
47         SelectionHandler *handler;
48         Display *dpy = W_VIEW_SCREEN(view)->display;
49         Window win = W_VIEW_DRAWABLE(view);
50         WMArrayIterator iter;
51
52         if (!selHandlers)
53                 return;
54
55         /*//printf("deleting selection handler for %d", win); */
56
57         WM_ITERATE_ARRAY(selHandlers, handler, iter) {
58                 if (handler->view == view && (handler->selection == selection || selection == None)
59                     && (handler->timestamp == timestamp || timestamp == CurrentTime)) {
60
61                         if (handler->flags.done_pending) {
62                                 handler->flags.delete_pending = 1;
63                                 /*//puts(": postponed because still pending"); */
64                                 return;
65                         }
66                         /*//printf(": found & removed"); */
67                         WMRemoveFromArray(selHandlers, handler);
68                         break;
69                 }
70         }
71
72         /*//printf("\n"); */
73
74         XGrabServer(dpy);
75         if (XGetSelectionOwner(dpy, selection) == win) {
76                 XSetSelectionOwner(dpy, selection, None, timestamp);
77         }
78         XUngrabServer(dpy);
79 }
80
81 void WMDeleteSelectionCallback(WMView * view, Atom selection, Time timestamp)
82 {
83         SelectionCallback *handler;
84         WMArrayIterator iter;
85
86         if (!selCallbacks)
87                 return;
88
89         WM_ITERATE_ARRAY(selCallbacks, handler, iter) {
90                 if (handler->view == view && (handler->selection == selection || selection == None)
91                     && (handler->timestamp == timestamp || timestamp == CurrentTime)) {
92
93                         if (handler->flags.done_pending) {
94                                 handler->flags.delete_pending = 1;
95                                 return;
96                         }
97                         WMRemoveFromArray(selCallbacks, handler);
98                         break;
99                 }
100         }
101 }
102
103 static int handleXError(Display * dpy, XErrorEvent * ev)
104 {
105         gotXError = True;
106
107         return 1;
108 }
109
110 static Bool writeSelection(Display * dpy, Window requestor, Atom property, Atom type, WMData * data)
111 {
112         static void *oldHandler;
113         int format, bpi;
114
115         format = WMGetDataFormat(data);
116         if (format == 0)
117                 format = 8;
118
119         bpi = format / 8;
120
121         /* printf("write to %x: %s\n", requestor, XGetAtomName(dpy, property)); */
122
123         oldHandler = XSetErrorHandler(handleXError);
124
125         gotXError = False;
126
127         XChangeProperty(dpy, requestor, property, type, format, PropModeReplace,
128                         WMDataBytes(data), WMGetDataLength(data) / bpi);
129
130         XFlush(dpy);
131
132         XSetErrorHandler(oldHandler);
133
134         return !gotXError;
135 }
136
137 static void notifySelection(XEvent * event, Atom prop)
138 {
139         XEvent ev;
140
141         /* printf("event to %x\n", event->xselectionrequest.requestor); */
142
143         ev.xselection.type = SelectionNotify;
144         ev.xselection.serial = 0;
145         ev.xselection.send_event = True;
146         ev.xselection.display = event->xselectionrequest.display;
147         ev.xselection.requestor = event->xselectionrequest.requestor;
148         ev.xselection.target = event->xselectionrequest.target;
149         ev.xselection.selection = event->xselectionrequest.selection;
150         ev.xselection.property = prop;
151         ev.xselection.time = event->xselectionrequest.time;
152
153         XSendEvent(event->xany.display, event->xselectionrequest.requestor, False, 0, &ev);
154         XFlush(event->xany.display);
155 }
156
157 static void handleRequestEvent(XEvent * event)
158 {
159         SelectionHandler *handler;
160         WMArrayIterator iter;
161         WMArray *copy;
162         Bool handledRequest;
163
164         WM_ITERATE_ARRAY(selHandlers, handler, iter) {
165
166                 switch (event->type) {
167                 case SelectionClear:
168                         if (W_VIEW_DRAWABLE(handler->view)
169                             != event->xselectionclear.window) {
170                                 break;
171                         }
172
173                         handler->flags.done_pending = 1;
174                         if (handler->procs.selectionLost)
175                                 handler->procs.selectionLost(handler->view, handler->selection, handler->data);
176                         handler->flags.done_pending = 0;
177                         handler->flags.delete_pending = 1;
178                         break;
179
180                 case SelectionRequest:
181                         if (W_VIEW_DRAWABLE(handler->view) != event->xselectionrequest.owner) {
182                                 break;
183                         }
184
185                         if (handler->procs.convertSelection != NULL
186                             && handler->selection == event->xselectionrequest.selection) {
187                                 Atom atom;
188                                 WMData *data;
189                                 Atom prop;
190
191                                 /* they're requesting for something old.. maybe another handler
192                                  * can handle it */
193                                 if (event->xselectionrequest.time < handler->timestamp
194                                     && event->xselectionrequest.time != CurrentTime) {
195                                         break;
196                                 }
197
198                                 handledRequest = False;
199
200                                 handler->flags.done_pending = 1;
201
202                                 data = handler->procs.convertSelection(handler->view,
203                                                                        handler->selection,
204                                                                        event->xselectionrequest.target,
205                                                                        handler->data, &atom);
206
207                                 prop = event->xselectionrequest.property;
208                                 /* obsolete clients that don't set the property field */
209                                 if (prop == None)
210                                         prop = event->xselectionrequest.target;
211
212                                 if (data) {
213                                         if (writeSelection(event->xselectionrequest.display,
214                                                            event->xselectionrequest.requestor, prop, atom, data)) {
215                                                 handledRequest = True;
216                                         }
217                                         WMReleaseData(data);
218                                 }
219
220                                 notifySelection(event, (handledRequest == True ? prop : None));
221
222                                 if (handler->procs.selectionDone != NULL) {
223                                         handler->procs.selectionDone(handler->view,
224                                                                      handler->selection,
225                                                                      event->xselectionrequest.target,
226                                                                      handler->data);
227                                 }
228
229                                 handler->flags.done_pending = 0;
230                         }
231                         break;
232                 }
233         }
234
235         /* delete handlers */
236         copy = WMDuplicateArray(selHandlers);
237         WM_ITERATE_ARRAY(copy, handler, iter) {
238                 if (handler && handler->flags.delete_pending) {
239                         WMDeleteSelectionHandler(handler->view, handler->selection, handler->timestamp);
240                 }
241         }
242         WMFreeArray(copy);
243 }
244
245 static WMData *getSelectionData(Display * dpy, Window win, Atom where)
246 {
247         WMData *wdata;
248         unsigned char *data;
249         Atom rtype;
250         int bits, bpi;
251         unsigned long len, bytes;
252
253         if (XGetWindowProperty(dpy, win, where, 0, MAX_PROPERTY_SIZE,
254                                False, AnyPropertyType, &rtype, &bits, &len, &bytes, &data) != Success) {
255                 return NULL;
256         }
257
258         bpi = bits / 8;
259
260         wdata = WMCreateDataWithBytesNoCopy(data, len * bpi, (WMFreeDataProc *) XFree);
261         WMSetDataFormat(wdata, bits);
262
263         return wdata;
264 }
265
266 static void handleNotifyEvent(XEvent * event)
267 {
268         SelectionCallback *handler;
269         WMArrayIterator iter;
270         WMArray *copy;
271         WMData *data;
272
273         WM_ITERATE_ARRAY(selCallbacks, handler, iter) {
274
275                 if (W_VIEW_DRAWABLE(handler->view) != event->xselection.requestor
276                     || handler->selection != event->xselection.selection) {
277                         continue;
278                 }
279                 handler->flags.done_pending = 1;
280
281                 if (event->xselection.property == None) {
282                         data = NULL;
283                 } else {
284                         data = getSelectionData(event->xselection.display,
285                                                 event->xselection.requestor, event->xselection.property);
286                 }
287
288                 (*handler->callback) (handler->view, handler->selection,
289                                       handler->target, handler->timestamp, handler->data, data);
290
291                 if (data != NULL) {
292                         WMReleaseData(data);
293                 }
294                 handler->flags.done_pending = 0;
295                 handler->flags.delete_pending = 1;
296         }
297
298         /* delete callbacks */
299         copy = WMDuplicateArray(selCallbacks);
300         WM_ITERATE_ARRAY(copy, handler, iter) {
301                 if (handler && handler->flags.delete_pending) {
302                         WMDeleteSelectionCallback(handler->view, handler->selection, handler->timestamp);
303                 }
304         }
305         WMFreeArray(copy);
306 }
307
308 void W_HandleSelectionEvent(XEvent * event)
309 {
310         /*//printf("%d received selection ", event->xany.window); */
311         /*//switch(event->type) {
312            case SelectionNotify:
313            puts("notify"); break;
314            case SelectionRequest:
315            puts("request"); break;
316            case SelectionClear:
317            puts("clear"); break;
318            default:
319            puts("unknown"); break;
320            } */
321
322         if (event->type == SelectionNotify) {
323                 handleNotifyEvent(event);
324         } else {
325                 handleRequestEvent(event);
326         }
327 }
328
329 Bool WMCreateSelectionHandler(WMView * view, Atom selection, Time timestamp, WMSelectionProcs * procs, void *cdata)
330 {
331         SelectionHandler *handler;
332         Display *dpy = W_VIEW_SCREEN(view)->display;
333
334         XSetSelectionOwner(dpy, selection, W_VIEW_DRAWABLE(view), timestamp);
335         if (XGetSelectionOwner(dpy, selection) != W_VIEW_DRAWABLE(view)) {
336                 return False;
337         }
338
339         WMPostNotificationName(WMSelectionOwnerDidChangeNotification, (void *)selection, (void *)view);
340
341         /*//printf("created selection handler for %d\n", W_VIEW_DRAWABLE(view)); */
342
343         handler = wmalloc(sizeof(SelectionHandler));
344
345         handler->view = view;
346         handler->selection = selection;
347         handler->timestamp = timestamp;
348         handler->procs = *procs;
349         handler->data = cdata;
350         memset(&handler->flags, 0, sizeof(handler->flags));
351
352         if (selHandlers == NULL) {
353                 selHandlers = WMCreateArrayWithDestructor(4, wfree);
354         }
355
356         WMAddToArray(selHandlers, handler);
357
358         return True;
359 }
360
361 Bool
362 WMRequestSelection(WMView * view, Atom selection, Atom target, Time timestamp,
363                    WMSelectionCallback * callback, void *cdata)
364 {
365         SelectionCallback *handler;
366
367         if (XGetSelectionOwner(W_VIEW_SCREEN(view)->display, selection) == None)
368                 return False;
369
370         if (!XConvertSelection(W_VIEW_SCREEN(view)->display, selection, target,
371                                W_VIEW_SCREEN(view)->clipboardAtom, W_VIEW_DRAWABLE(view), timestamp)) {
372                 return False;
373         }
374
375         handler = wmalloc(sizeof(SelectionCallback));
376
377         handler->view = view;
378         handler->selection = selection;
379         handler->target = target;
380         handler->timestamp = timestamp;
381         handler->callback = callback;
382         handler->data = cdata;
383         memset(&handler->flags, 0, sizeof(handler->flags));
384
385         if (selCallbacks == NULL) {
386                 selCallbacks = WMCreateArrayWithDestructor(4, wfree);
387         }
388
389         WMAddToArray(selCallbacks, handler);
390
391         return True;
392 }