definable cursor code updates from Jim Knoble <jmknoble@pobox.com>
[wmaker-crm.git] / WINGs / selection.c
blob434ba7f484080f42a2940e179a3d13bf085aad39
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 WMArray *selCallbacks = NULL;
43 WMArray *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 WMArrayIterator iter;
54 if (!selHandlers)
55 return;
58 WM_ITERATE_ARRAY(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 WMRemoveFromArray(selHandlers, handler);
68 break;
72 XGrabServer(dpy);
73 if (XGetSelectionOwner(dpy, selection) == win) {
74 XSetSelectionOwner(dpy, selection, None, timestamp);
76 XUngrabServer(dpy);
81 void
82 WMDeleteSelectionCallback(WMView *view, Atom selection, Time timestamp)
84 SelectionCallback *handler;
85 WMArrayIterator iter;
87 if (!selCallbacks)
88 return;
90 WM_ITERATE_ARRAY(selCallbacks, handler, iter) {
91 if (handler->view == view
92 && (handler->selection == selection || selection == 0)
93 && (handler->timestamp == timestamp || timestamp == CurrentTime)) {
95 if (handler->flags.done_pending) {
96 handler->flags.delete_pending = 1;
97 return;
99 WMRemoveFromArray(selCallbacks, handler);
100 break;
107 static Bool gotError = 0;
109 static int
110 errorHandler(XErrorEvent *error)
112 return 0;
116 static Bool
117 writeSelection(Display *dpy, Window requestor, Atom property, Atom type,
118 WMData *data)
120 int format;
122 format = WMGetDataFormat(data);
123 if (format == 0)
124 format = 8;
127 printf("write to %x: %s\n", requestor, XGetAtomName(dpy, property));
129 gotError = False;
131 #ifndef __sgi
132 if (!XChangeProperty(dpy, requestor, property, type, format,
133 PropModeReplace, WMDataBytes(data),
134 WMGetDataLength(data)))
135 return False;
136 #else
137 /* in sgi seems this seems to return void */
138 XChangeProperty(dpy, requestor, property, type, format,
139 PropModeReplace, WMDataBytes(data), WMGetDataLength(data));
140 #endif
142 XFlush(dpy);
144 return !gotError;
148 static void
149 notifySelection(XEvent *event, Atom prop)
151 XEvent ev;
153 printf("envent to %x\n", event->xselectionrequest.requestor);
155 ev.xselection.type = SelectionNotify;
156 ev.xselection.serial = 0;
157 ev.xselection.send_event = True;
158 ev.xselection.display = event->xselectionrequest.display;
159 ev.xselection.requestor = event->xselectionrequest.requestor;
160 ev.xselection.target = event->xselectionrequest.target;
161 ev.xselection.selection = event->xselectionrequest.selection;
162 ev.xselection.property = prop;
163 ev.xselection.time = event->xselectionrequest.time;
165 XSendEvent(event->xany.display, event->xselectionrequest.requestor,
166 False, 0, &ev);
167 XFlush(event->xany.display);
171 static void
172 handleRequestEvent(XEvent *event)
174 SelectionHandler *handler;
175 WMArrayIterator iter;
176 WMArray *copy;
177 Bool handledRequest = False;
179 WM_ITERATE_ARRAY(selHandlers, handler, iter) {
181 switch (event->type) {
182 case SelectionClear:
183 if (W_VIEW_DRAWABLE(handler->view)
184 != event->xselectionclear.window) {
185 break;
188 handler->flags.done_pending = 1;
189 if (handler->procs.selectionLost)
190 handler->procs.selectionLost(handler->view,
191 handler->selection,
192 handler->data);
193 handler->flags.done_pending = 0;
194 handler->flags.delete_pending = 1;
195 break;
197 case SelectionRequest:
198 if (W_VIEW_DRAWABLE(handler->view)
199 != event->xselectionrequest.owner) {
200 break;
203 if (handler->procs.convertSelection != NULL
204 && handler->selection == event->xselectionrequest.selection) {
205 Atom atom;
206 WMData *data;
207 Atom prop;
209 /* they're requesting for something old.. maybe another handler
210 * can handle it */
211 if (event->xselectionrequest.time < handler->timestamp
212 && event->xselectionrequest.time != CurrentTime) {
213 break;
216 handler->flags.done_pending = 1;
218 data = handler->procs.convertSelection(handler->view,
219 handler->selection,
220 event->xselectionrequest.target,
221 handler->data,
222 &atom);
223 if (data == NULL) {
224 break;
227 handledRequest = True;
230 prop = event->xselectionrequest.property;
231 /* obsolete clients that don't set the property field */
232 if (prop == None)
233 prop = event->xselectionrequest.target;
235 if (!writeSelection(event->xselectionrequest.display,
236 event->xselectionrequest.requestor,
237 prop, atom, data)) {
238 WMReleaseData(data);
239 notifySelection(event, None);
240 break;
242 WMReleaseData(data);
244 notifySelection(event, prop);
246 if (handler->procs.selectionDone != NULL) {
247 handler->procs.selectionDone(handler->view,
248 handler->selection,
249 event->xselectionrequest.target,
250 handler->data);
253 handler->flags.done_pending = 0;
255 if (!handledRequest) {
256 notifySelection(event, None);
259 break;
263 /* delete handlers */
264 copy = WMDuplicateArray(selHandlers);
265 WM_ITERATE_ARRAY(copy, handler, iter) {
266 if (handler && handler->flags.delete_pending) {
267 WMDeleteSelectionHandler(handler->view, handler->selection,
268 handler->timestamp);
271 WMFreeArray(copy);
275 static WMData*
276 getSelectionData(Display *dpy, Window win, Atom where)
278 WMData *wdata;
279 unsigned char *data;
280 Atom rtype;
281 unsigned bits;
282 unsigned long len, bytes;
285 if (XGetWindowProperty(dpy, win, where, 0, MAX_PROPERTY_SIZE,
286 False, AnyPropertyType, &rtype, &bits, &len,
287 &bytes, &data)!=Success) {
288 return NULL;
291 wdata = WMCreateDataWithBytesNoCopy(data, len, (WMFreeDataProc*)XFree);
292 if (wdata == NULL) {
293 return NULL;
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 if (event->type == SelectionNotify) {
353 handleNotifyEvent(event);
354 } else {
355 handleRequestEvent(event);
362 Bool
363 WMCreateSelectionHandler(WMView *view, Atom selection, Time timestamp,
364 WMSelectionProcs *procs, void *cdata)
366 SelectionHandler *handler;
367 Display *dpy = W_VIEW_SCREEN(view)->display;
369 XSetSelectionOwner(dpy, selection, W_VIEW_DRAWABLE(view), timestamp);
370 if (XGetSelectionOwner(dpy, selection) != W_VIEW_DRAWABLE(view))
371 return False;
373 handler = malloc(sizeof(SelectionHandler));
374 if (handler == NULL)
375 return False;
377 handler->view = view;
378 handler->selection = selection;
379 handler->timestamp = timestamp;
380 handler->procs = *procs;
381 handler->data = cdata;
382 memset(&handler->flags, 0, sizeof(handler->flags));
384 if (selHandlers == NULL) {
385 selHandlers = WMCreateArrayWithDestructor(4, wfree);
388 WMAddToArray(selHandlers, handler);
390 return True;
395 Bool
396 WMRequestSelection(WMView *view, Atom selection, Atom target, Time timestamp,
397 WMSelectionCallback *callback, void *cdata)
399 SelectionCallback *handler;
401 if (XGetSelectionOwner(W_VIEW_SCREEN(view)->display, selection) == None)
402 return False;
404 handler = wmalloc(sizeof(SelectionCallback));
406 handler->view = view;
407 handler->selection = selection;
408 handler->target = target;
409 handler->timestamp = timestamp;
410 handler->callback = callback;
411 handler->data = cdata;
412 memset(&handler->flags, 0, sizeof(handler->flags));
414 if (selCallbacks == NULL) {
415 selCallbacks = WMCreateArrayWithDestructor(4, wfree);
418 WMAddToArray(selCallbacks, handler);
420 if (!XConvertSelection(W_VIEW_SCREEN(view)->display, selection, target,
421 W_VIEW_SCREEN(view)->clipboardAtom,
422 W_VIEW_DRAWABLE(view), timestamp)) {
423 return False;
426 return True;