New wkdemenu.pl from Malcolm Cowe
[wmaker-crm.git] / WINGs / selection.c
blob61f6cee2f298a01dad72af5e852d0a59b5ed8fcb
3 #include <stdlib.h>
5 #include <X11/Xatom.h>
7 #include "WINGsP.h"
9 #define MAX_PROPERTY_SIZE 8*1024
12 typedef struct SelectionHandler {
13 WMView *view;
14 Atom selection;
15 Time timestamp;
16 WMSelectionProcs procs;
17 void *data;
19 struct {
20 unsigned delete_pending:1;
21 unsigned done_pending:1;
22 } flags;
23 } SelectionHandler;
26 typedef struct SelectionCallback {
27 WMView *view;
28 Atom selection;
29 Atom target;
30 Time timestamp;
31 WMSelectionCallback *callback;
32 void *data;
34 struct {
35 unsigned delete_pending:1;
36 unsigned done_pending:1;
37 } flags;
38 } SelectionCallback;
41 WMBag *selCallbacks = NULL;
43 WMBag *selHandlers = NULL;
46 void
47 WMDeleteSelectionHandler(WMView *view, Atom selection, Time timestamp)
49 SelectionHandler *handler;
50 Display *dpy = W_VIEW_SCREEN(view)->display;
51 Window win = W_VIEW_DRAWABLE(view);
52 WMBagIterator iter;
54 if (!selHandlers)
55 return;
58 WM_ITERATE_BAG(selHandlers, handler, iter) {
59 if (handler->view == view
60 && (handler->selection == selection || selection == None)
61 && (handler->timestamp == timestamp || timestamp == CurrentTime)) {
63 if (handler->flags.done_pending) {
64 handler->flags.delete_pending = 1;
65 return;
67 WMRemoveFromBag(selHandlers, handler);
68 wfree(handler);
69 break;
73 XGrabServer(dpy);
74 if (XGetSelectionOwner(dpy, selection) == win) {
75 XSetSelectionOwner(dpy, selection, None, timestamp);
77 XUngrabServer(dpy);
82 void
83 WMDeleteSelectionCallback(WMView *view, Atom selection, Time timestamp)
85 SelectionCallback *handler;
86 WMBagIterator iter;
88 if (!selCallbacks)
89 return;
91 WM_ITERATE_BAG(selCallbacks, handler, iter) {
92 if (handler->view == view
93 && (handler->selection == selection || selection == 0)
94 && (handler->timestamp == timestamp || timestamp == CurrentTime)) {
96 if (handler->flags.done_pending) {
97 handler->flags.delete_pending = 1;
98 return;
100 WMRemoveFromBag(selCallbacks, handler);
101 wfree(handler);
102 break;
109 static Bool gotError = 0;
111 static int
112 errorHandler(XErrorEvent *error)
114 return 0;
118 static Bool
119 writeSelection(Display *dpy, Window requestor, Atom property, Atom type,
120 WMData *data)
122 int format;
124 format = WMGetDataFormat(data);
125 if (format == 0)
126 format = 8;
129 printf("write to %x: %s\n", requestor, XGetAtomName(dpy, property));
131 gotError = False;
133 #ifndef __sgi
134 if (!XChangeProperty(dpy, requestor, property, type, format,
135 PropModeReplace, WMDataBytes(data),
136 WMGetDataLength(data)))
137 return False;
138 #else
139 /* in sgi seems this seems to return void */
140 XChangeProperty(dpy, requestor, property, type, format,
141 PropModeReplace, WMDataBytes(data), WMGetDataLength(data));
142 #endif
144 XFlush(dpy);
146 return !gotError;
150 static void
151 notifySelection(XEvent *event, Atom prop)
153 XEvent ev;
155 printf("envent to %x\n", event->xselectionrequest.requestor);
157 ev.xselection.type = SelectionNotify;
158 ev.xselection.serial = 0;
159 ev.xselection.send_event = True;
160 ev.xselection.display = event->xselectionrequest.display;
161 ev.xselection.requestor = event->xselectionrequest.requestor;
162 ev.xselection.target = event->xselectionrequest.target;
163 ev.xselection.selection = event->xselectionrequest.selection;
164 ev.xselection.property = prop;
165 ev.xselection.time = event->xselectionrequest.time;
167 XSendEvent(event->xany.display, event->xselectionrequest.requestor,
168 False, 0, &ev);
169 XFlush(event->xany.display);
174 static void
175 deleteHandlers(WMBagIterator iter)
177 SelectionHandler *handler;
179 if (iter == NULL)
180 handler = WMBagFirst(selHandlers, &iter);
181 else
182 handler = WMBagNext(selHandlers, &iter);
184 if (handler == NULL)
185 return;
187 deleteHandlers(iter);
189 if (handler->flags.delete_pending) {
190 WMDeleteSelectionHandler(handler->view, handler->selection,
191 handler->timestamp);
197 static void
198 handleRequestEvent(XEvent *event)
200 SelectionHandler *handler;
201 WMBagIterator iter;
202 Bool handledRequest = False;
204 WM_ITERATE_BAG(selHandlers, handler, iter) {
206 switch (event->type) {
207 case SelectionClear:
208 if (W_VIEW_DRAWABLE(handler->view)
209 != event->xselectionclear.window) {
210 break;
213 handler->flags.done_pending = 1;
214 if (handler->procs.selectionLost)
215 handler->procs.selectionLost(handler->view,
216 handler->selection,
217 handler->data);
218 handler->flags.done_pending = 0;
219 handler->flags.delete_pending = 1;
220 break;
222 case SelectionRequest:
223 if (W_VIEW_DRAWABLE(handler->view)
224 != event->xselectionrequest.owner) {
225 break;
228 if (handler->procs.convertSelection != NULL
229 && handler->selection == event->xselectionrequest.selection) {
230 Atom atom;
231 WMData *data;
232 Atom prop;
234 /* they're requesting for something old.. maybe another handler
235 * can handle it */
236 if (event->xselectionrequest.time < handler->timestamp
237 && event->xselectionrequest.time != CurrentTime) {
238 break;
241 handler->flags.done_pending = 1;
243 data = handler->procs.convertSelection(handler->view,
244 handler->selection,
245 event->xselectionrequest.target,
246 handler->data,
247 &atom);
248 if (data == NULL) {
249 break;
252 handledRequest = True;
255 prop = event->xselectionrequest.property;
256 /* obsolete clients that don't set the property field */
257 if (prop == None)
258 prop = event->xselectionrequest.target;
260 if (!writeSelection(event->xselectionrequest.display,
261 event->xselectionrequest.requestor,
262 prop, atom, data)) {
263 WMReleaseData(data);
264 notifySelection(event, None);
265 break;
267 WMReleaseData(data);
269 notifySelection(event, prop);
271 if (handler->procs.selectionDone != NULL) {
272 handler->procs.selectionDone(handler->view,
273 handler->selection,
274 event->xselectionrequest.target,
275 handler->data);
278 handler->flags.done_pending = 0;
280 if (!handledRequest) {
281 notifySelection(event, None);
284 break;
288 deleteHandlers(NULL);
293 static void
294 deleteCallbacks(WMBagIterator iter)
296 SelectionCallback *handler;
298 if (iter == NULL)
299 handler = WMBagFirst(selCallbacks, &iter);
300 else
301 handler = WMBagNext(selCallbacks, &iter);
303 if (handler == NULL)
304 return;
306 deleteCallbacks(iter);
308 if (handler->flags.delete_pending) {
309 WMDeleteSelectionCallback(handler->view, handler->selection,
310 handler->timestamp);
317 static WMData*
318 getSelectionData(Display *dpy, Window win, Atom where)
320 WMData *wdata;
321 unsigned char *data;
322 Atom rtype;
323 unsigned bits;
324 unsigned long len, bytes;
327 if (XGetWindowProperty(dpy, win, where, 0, MAX_PROPERTY_SIZE,
328 False, AnyPropertyType, &rtype, &bits, &len,
329 &bytes, &data)!=Success) {
330 return NULL;
333 wdata = WMCreateDataWithBytesNoCopy(data, len, (WMFreeDataProc*)XFree);
334 if (wdata == NULL) {
335 return NULL;
337 WMSetDataFormat(wdata, bits);
339 return wdata;
343 static void
344 handleNotifyEvent(XEvent *event)
346 SelectionCallback *handler;
347 WMBagIterator iter;
348 WMData *data;
350 WM_ITERATE_BAG(selCallbacks, handler, iter) {
352 if (W_VIEW_DRAWABLE(handler->view) != event->xselection.requestor
353 && handler->selection == event->xselection.selection) {
354 continue;
356 handler->flags.done_pending = 1;
358 if (event->xselection.property == None) {
359 data = NULL;
360 } else {
361 data = getSelectionData(event->xselection.display,
362 event->xselection.requestor,
363 event->xselection.property);
366 (*handler->callback)(handler->view, handler->selection,
367 handler->target, handler->timestamp,
368 handler->data, data);
370 if (data != NULL) {
371 WMReleaseData(data);
373 handler->flags.done_pending = 0;
374 handler->flags.delete_pending = 1;
376 deleteCallbacks(NULL);
381 void
382 W_HandleSelectionEvent(XEvent *event)
384 if (event->type == SelectionNotify) {
385 handleNotifyEvent(event);
386 } else {
387 handleRequestEvent(event);
394 Bool
395 WMCreateSelectionHandler(WMView *view, Atom selection, Time timestamp,
396 WMSelectionProcs *procs, void *cdata)
398 SelectionHandler *handler;
399 Display *dpy = W_VIEW_SCREEN(view)->display;
401 XSetSelectionOwner(dpy, selection, W_VIEW_DRAWABLE(view), timestamp);
402 if (XGetSelectionOwner(dpy, selection) != W_VIEW_DRAWABLE(view))
403 return False;
405 handler = malloc(sizeof(SelectionHandler));
406 if (handler == NULL)
407 return False;
409 handler->view = view;
410 handler->selection = selection;
411 handler->timestamp = timestamp;
412 handler->procs = *procs;
413 handler->data = cdata;
414 memset(&handler->flags, 0, sizeof(handler->flags));
416 if (selHandlers == NULL) {
417 selHandlers = WMCreateTreeBag();
420 WMPutInBag(selHandlers, handler);
422 return True;
427 Bool
428 WMRequestSelection(WMView *view, Atom selection, Atom target, Time timestamp,
429 WMSelectionCallback *callback, void *cdata)
431 SelectionCallback *handler;
433 if (XGetSelectionOwner(W_VIEW_SCREEN(view)->display, selection) == None)
434 return False;
436 handler = wmalloc(sizeof(SelectionCallback));
438 handler->view = view;
439 handler->selection = selection;
440 handler->target = target;
441 handler->timestamp = timestamp;
442 handler->callback = callback;
443 handler->data = cdata;
444 memset(&handler->flags, 0, sizeof(handler->flags));
446 if (selCallbacks == NULL) {
447 selCallbacks = WMCreateTreeBag();
450 WMPutInBag(selCallbacks, handler);
452 if (!XConvertSelection(W_VIEW_SCREEN(view)->display, selection, target,
453 W_VIEW_SCREEN(view)->clipboardAtom,
454 W_VIEW_DRAWABLE(view), timestamp)) {
455 return False;
458 return True;