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 }