- Fixed a bug that crashed wmaker when selecting the "Start alternate window
[wmaker-crm.git] / WINGs / selection.c
bloba9908f3abb7d4effc7d83b7d5bf976b42179d9dc
3 #include <stdlib.h>
5 #include <X11/Xatom.h>
7 #include "WINGsP.h"
9 #define MAX_PROPERTY_SIZE 8*1024
12 char *WMSelectionOwnerDidChangeNotification = "WMSelectionOwnerDidChange";
15 typedef struct SelectionHandler {
16 WMView *view;
17 Atom selection;
18 Time timestamp;
19 WMSelectionProcs procs;
20 void *data;
22 struct {
23 unsigned delete_pending:1;
24 unsigned done_pending:1;
25 } flags;
26 } SelectionHandler;
29 typedef struct SelectionCallback {
30 WMView *view;
31 Atom selection;
32 Atom target;
33 Time timestamp;
34 WMSelectionCallback *callback;
35 void *data;
37 struct {
38 unsigned delete_pending:1;
39 unsigned done_pending:1;
40 } flags;
41 } SelectionCallback;
45 static WMArray *selCallbacks = NULL;
47 static WMArray *selHandlers = NULL;
49 static Bool gotXError = False;
54 void
55 WMDeleteSelectionHandler(WMView *view, Atom selection, Time timestamp)
57 SelectionHandler *handler;
58 Display *dpy = W_VIEW_SCREEN(view)->display;
59 Window win = W_VIEW_DRAWABLE(view);
60 WMArrayIterator iter;
62 if (!selHandlers)
63 return;
65 printf("deleting selection handler for %d", win);
67 WM_ITERATE_ARRAY(selHandlers, handler, iter) {
68 if (handler->view == view
69 && (handler->selection == selection || selection == None)
70 && (handler->timestamp == timestamp || timestamp == CurrentTime)) {
72 if (handler->flags.done_pending) {
73 handler->flags.delete_pending = 1;
74 puts(": postponed because still pending");
75 return;
77 printf(": found & removed");
78 WMRemoveFromArray(selHandlers, handler);
79 break;
83 printf("\n");
85 XGrabServer(dpy);
86 if (XGetSelectionOwner(dpy, selection) == win) {
87 XSetSelectionOwner(dpy, selection, None, timestamp);
89 XUngrabServer(dpy);
94 void
95 WMDeleteSelectionCallback(WMView *view, Atom selection, Time timestamp)
97 SelectionCallback *handler;
98 WMArrayIterator iter;
100 if (!selCallbacks)
101 return;
103 WM_ITERATE_ARRAY(selCallbacks, handler, iter) {
104 if (handler->view == view
105 && (handler->selection == selection || selection == None)
106 && (handler->timestamp == timestamp || timestamp == CurrentTime)) {
108 if (handler->flags.done_pending) {
109 handler->flags.delete_pending = 1;
110 return;
112 WMRemoveFromArray(selCallbacks, handler);
113 break;
119 static int
120 handleXError(Display *dpy, XErrorEvent *ev)
122 gotXError = True;
124 return 1;
128 static Bool
129 writeSelection(Display *dpy, Window requestor, Atom property, Atom type,
130 WMData *data)
132 static void *oldHandler;
133 int format, bpi;
135 format = WMGetDataFormat(data);
136 if (format == 0)
137 format = 8;
139 bpi = format/8;
141 /* printf("write to %x: %s\n", requestor, XGetAtomName(dpy, property)); */
143 oldHandler = XSetErrorHandler(handleXError);
145 gotXError = False;
147 XChangeProperty(dpy, requestor, property, type, format, PropModeReplace,
148 WMDataBytes(data), WMGetDataLength(data)/bpi);
150 XFlush(dpy);
152 XSetErrorHandler(oldHandler);
154 return !gotXError;
158 static void
159 notifySelection(XEvent *event, Atom prop)
161 XEvent ev;
163 /* printf("event to %x\n", event->xselectionrequest.requestor); */
165 ev.xselection.type = SelectionNotify;
166 ev.xselection.serial = 0;
167 ev.xselection.send_event = True;
168 ev.xselection.display = event->xselectionrequest.display;
169 ev.xselection.requestor = event->xselectionrequest.requestor;
170 ev.xselection.target = event->xselectionrequest.target;
171 ev.xselection.selection = event->xselectionrequest.selection;
172 ev.xselection.property = prop;
173 ev.xselection.time = event->xselectionrequest.time;
175 XSendEvent(event->xany.display, event->xselectionrequest.requestor,
176 False, 0, &ev);
177 XFlush(event->xany.display);
181 static void
182 handleRequestEvent(XEvent *event)
184 SelectionHandler *handler;
185 WMArrayIterator iter;
186 WMArray *copy;
187 Bool handledRequest;
189 WM_ITERATE_ARRAY(selHandlers, handler, iter) {
191 switch (event->type) {
192 case SelectionClear:
193 if (W_VIEW_DRAWABLE(handler->view)
194 != event->xselectionclear.window) {
195 break;
198 handler->flags.done_pending = 1;
199 if (handler->procs.selectionLost)
200 handler->procs.selectionLost(handler->view,
201 handler->selection,
202 handler->data);
203 handler->flags.done_pending = 0;
204 handler->flags.delete_pending = 1;
205 break;
207 case SelectionRequest:
208 if (W_VIEW_DRAWABLE(handler->view)!=event->xselectionrequest.owner) {
209 break;
212 if (handler->procs.convertSelection != NULL
213 && handler->selection == event->xselectionrequest.selection) {
214 Atom atom;
215 WMData *data;
216 Atom prop;
218 /* they're requesting for something old.. maybe another handler
219 * can handle it */
220 if (event->xselectionrequest.time < handler->timestamp
221 && event->xselectionrequest.time != CurrentTime) {
222 break;
225 handledRequest = False;
227 handler->flags.done_pending = 1;
229 data = handler->procs.convertSelection(handler->view,
230 handler->selection,
231 event->xselectionrequest.target,
232 handler->data,
233 &atom);
235 prop = event->xselectionrequest.property;
236 /* obsolete clients that don't set the property field */
237 if (prop == None)
238 prop = event->xselectionrequest.target;
240 if (data) {
241 if (writeSelection(event->xselectionrequest.display,
242 event->xselectionrequest.requestor,
243 prop, atom, data)) {
244 handledRequest = True;
246 WMReleaseData(data);
249 notifySelection(event, (handledRequest==True ? prop : None));
251 if (handler->procs.selectionDone != NULL) {
252 handler->procs.selectionDone(handler->view,
253 handler->selection,
254 event->xselectionrequest.target,
255 handler->data);
258 handler->flags.done_pending = 0;
260 break;
264 /* delete handlers */
265 copy = WMDuplicateArray(selHandlers);
266 WM_ITERATE_ARRAY(copy, handler, iter) {
267 if (handler && handler->flags.delete_pending) {
268 WMDeleteSelectionHandler(handler->view, handler->selection,
269 handler->timestamp);
272 WMFreeArray(copy);
276 static WMData*
277 getSelectionData(Display *dpy, Window win, Atom where)
279 WMData *wdata;
280 unsigned char *data;
281 Atom rtype;
282 unsigned bits, bpi;
283 unsigned long len, bytes;
286 if (XGetWindowProperty(dpy, win, where, 0, MAX_PROPERTY_SIZE,
287 False, AnyPropertyType, &rtype, &bits, &len,
288 &bytes, &data)!=Success) {
289 return NULL;
292 bpi = bits/8;
294 wdata = WMCreateDataWithBytesNoCopy(data, len*bpi, (WMFreeDataProc*)XFree);
295 WMSetDataFormat(wdata, bits);
297 return wdata;
301 static void
302 handleNotifyEvent(XEvent *event)
304 SelectionCallback *handler;
305 WMArrayIterator iter;
306 WMArray *copy;
307 WMData *data;
309 WM_ITERATE_ARRAY(selCallbacks, handler, iter) {
311 if (W_VIEW_DRAWABLE(handler->view) != event->xselection.requestor
312 || handler->selection != event->xselection.selection) {
313 continue;
315 handler->flags.done_pending = 1;
317 if (event->xselection.property == None) {
318 data = NULL;
319 } else {
320 data = getSelectionData(event->xselection.display,
321 event->xselection.requestor,
322 event->xselection.property);
325 (*handler->callback)(handler->view, handler->selection,
326 handler->target, handler->timestamp,
327 handler->data, data);
329 if (data != NULL) {
330 WMReleaseData(data);
332 handler->flags.done_pending = 0;
333 handler->flags.delete_pending = 1;
336 /* delete callbacks */
337 copy = WMDuplicateArray(selCallbacks);
338 WM_ITERATE_ARRAY(copy, handler, iter) {
339 if (handler && handler->flags.delete_pending) {
340 WMDeleteSelectionCallback(handler->view, handler->selection,
341 handler->timestamp);
344 WMFreeArray(copy);
349 void
350 W_HandleSelectionEvent(XEvent *event)
352 printf("%d received selection ", event->xany.window);
353 switch(event->type) {
354 case SelectionNotify:
355 puts("notify"); break;
356 case SelectionRequest:
357 puts("request"); break;
358 case SelectionClear:
359 puts("clear"); break;
360 default:
361 puts("unknown"); break;
364 if (event->type == SelectionNotify) {
365 handleNotifyEvent(event);
366 } else {
367 handleRequestEvent(event);
373 Bool
374 WMCreateSelectionHandler(WMView *view, Atom selection, Time timestamp,
375 WMSelectionProcs *procs, void *cdata)
377 SelectionHandler *handler;
378 Display *dpy = W_VIEW_SCREEN(view)->display;
380 XSetSelectionOwner(dpy, selection, W_VIEW_DRAWABLE(view), timestamp);
381 if (XGetSelectionOwner(dpy, selection) != W_VIEW_DRAWABLE(view)) {
382 return False;
385 WMPostNotificationName(WMSelectionOwnerDidChangeNotification,
386 (void*)selection, (void*)view);
388 printf("created selection handler for %d\n", W_VIEW_DRAWABLE(view));
390 handler = wmalloc(sizeof(SelectionHandler));
392 handler->view = view;
393 handler->selection = selection;
394 handler->timestamp = timestamp;
395 handler->procs = *procs;
396 handler->data = cdata;
397 memset(&handler->flags, 0, sizeof(handler->flags));
399 if (selHandlers == NULL) {
400 selHandlers = WMCreateArrayWithDestructor(4, wfree);
403 WMAddToArray(selHandlers, handler);
405 return True;
410 Bool
411 WMRequestSelection(WMView *view, Atom selection, Atom target, Time timestamp,
412 WMSelectionCallback *callback, void *cdata)
414 SelectionCallback *handler;
416 if (XGetSelectionOwner(W_VIEW_SCREEN(view)->display, selection) == None)
417 return False;
419 if (!XConvertSelection(W_VIEW_SCREEN(view)->display, selection, target,
420 W_VIEW_SCREEN(view)->clipboardAtom,
421 W_VIEW_DRAWABLE(view), timestamp)) {
422 return False;
425 handler = wmalloc(sizeof(SelectionCallback));
427 handler->view = view;
428 handler->selection = selection;
429 handler->target = target;
430 handler->timestamp = timestamp;
431 handler->callback = callback;
432 handler->data = cdata;
433 memset(&handler->flags, 0, sizeof(handler->flags));
435 if (selCallbacks == NULL) {
436 selCallbacks = WMCreateArrayWithDestructor(4, wfree);
439 WMAddToArray(selCallbacks, handler);
441 return True;