misc selection and textfield fixes
[wmaker-crm.git] / WINGs / selection.c
blobf094f2c417565436ddd240b4098c3370d9960ffb
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;
42 static WMArray *selCallbacks = NULL;
44 static WMArray *selHandlers = NULL;
46 static Bool gotXError = False;
51 void
52 WMDeleteSelectionHandler(WMView *view, Atom selection, Time timestamp)
54 SelectionHandler *handler;
55 Display *dpy = W_VIEW_SCREEN(view)->display;
56 Window win = W_VIEW_DRAWABLE(view);
57 WMArrayIterator iter;
59 if (!selHandlers)
60 return;
62 //printf("deleting selection handler for %d\n", win);
64 WM_ITERATE_ARRAY(selHandlers, handler, iter) {
65 if (handler->view == view
66 && (handler->selection == selection || selection == None)
67 && (handler->timestamp == timestamp || timestamp == CurrentTime)) {
69 if (handler->flags.done_pending) {
70 handler->flags.delete_pending = 1;
71 return;
73 //puts("found & removed");
74 WMRemoveFromArray(selHandlers, handler);
75 break;
79 XGrabServer(dpy);
80 if (XGetSelectionOwner(dpy, selection) == win) {
81 XSetSelectionOwner(dpy, selection, None, timestamp);
83 XUngrabServer(dpy);
88 void
89 WMDeleteSelectionCallback(WMView *view, Atom selection, Time timestamp)
91 SelectionCallback *handler;
92 WMArrayIterator iter;
94 if (!selCallbacks)
95 return;
97 WM_ITERATE_ARRAY(selCallbacks, handler, iter) {
98 if (handler->view == view
99 && (handler->selection == selection || selection == None)
100 && (handler->timestamp == timestamp || timestamp == CurrentTime)) {
102 if (handler->flags.done_pending) {
103 handler->flags.delete_pending = 1;
104 return;
106 WMRemoveFromArray(selCallbacks, handler);
107 break;
113 static int
114 handleXError(Display *dpy, XErrorEvent *ev)
116 gotXError = True;
118 return 1;
122 static Bool
123 writeSelection(Display *dpy, Window requestor, Atom property, Atom type,
124 WMData *data)
126 static void *oldHandler;
127 int format, bpi;
129 format = WMGetDataFormat(data);
130 if (format == 0)
131 format = 8;
133 bpi = format/8;
135 /* printf("write to %x: %s\n", requestor, XGetAtomName(dpy, property)); */
137 oldHandler = XSetErrorHandler(handleXError);
139 gotXError = False;
141 XChangeProperty(dpy, requestor, property, type, format, PropModeReplace,
142 WMDataBytes(data), WMGetDataLength(data)/bpi);
144 XFlush(dpy);
146 XSetErrorHandler(oldHandler);
148 return !gotXError;
152 static void
153 notifySelection(XEvent *event, Atom prop)
155 XEvent ev;
157 /* printf("event to %x\n", event->xselectionrequest.requestor); */
159 ev.xselection.type = SelectionNotify;
160 ev.xselection.serial = 0;
161 ev.xselection.send_event = True;
162 ev.xselection.display = event->xselectionrequest.display;
163 ev.xselection.requestor = event->xselectionrequest.requestor;
164 ev.xselection.target = event->xselectionrequest.target;
165 ev.xselection.selection = event->xselectionrequest.selection;
166 ev.xselection.property = prop;
167 ev.xselection.time = event->xselectionrequest.time;
169 XSendEvent(event->xany.display, event->xselectionrequest.requestor,
170 False, 0, &ev);
171 XFlush(event->xany.display);
175 static void
176 handleRequestEvent(XEvent *event)
178 SelectionHandler *handler;
179 WMArrayIterator iter;
180 WMArray *copy;
181 Bool handledRequest;
183 WM_ITERATE_ARRAY(selHandlers, handler, iter) {
185 switch (event->type) {
186 case SelectionClear:
187 if (W_VIEW_DRAWABLE(handler->view)
188 != event->xselectionclear.window) {
189 break;
192 handler->flags.done_pending = 1;
193 if (handler->procs.selectionLost)
194 handler->procs.selectionLost(handler->view,
195 handler->selection,
196 handler->data);
197 handler->flags.done_pending = 0;
198 handler->flags.delete_pending = 1;
199 break;
201 case SelectionRequest:
202 if (W_VIEW_DRAWABLE(handler->view)!=event->xselectionrequest.owner) {
203 break;
206 if (handler->procs.convertSelection != NULL
207 && handler->selection == event->xselectionrequest.selection) {
208 Atom atom;
209 WMData *data;
210 Atom prop;
212 /* they're requesting for something old.. maybe another handler
213 * can handle it */
214 if (event->xselectionrequest.time < handler->timestamp
215 && event->xselectionrequest.time != CurrentTime) {
216 break;
219 handledRequest = False;
221 handler->flags.done_pending = 1;
223 data = handler->procs.convertSelection(handler->view,
224 handler->selection,
225 event->xselectionrequest.target,
226 handler->data,
227 &atom);
229 prop = event->xselectionrequest.property;
230 /* obsolete clients that don't set the property field */
231 if (prop == None)
232 prop = event->xselectionrequest.target;
234 if (data) {
235 if (writeSelection(event->xselectionrequest.display,
236 event->xselectionrequest.requestor,
237 prop, atom, data)) {
238 handledRequest = True;
240 WMReleaseData(data);
243 notifySelection(event, (handledRequest==True ? prop : None));
245 if (handler->procs.selectionDone != NULL) {
246 handler->procs.selectionDone(handler->view,
247 handler->selection,
248 event->xselectionrequest.target,
249 handler->data);
252 handler->flags.done_pending = 0;
254 break;
258 /* delete handlers */
259 copy = WMDuplicateArray(selHandlers);
260 WM_ITERATE_ARRAY(copy, handler, iter) {
261 if (handler && handler->flags.delete_pending) {
262 WMDeleteSelectionHandler(handler->view, handler->selection,
263 handler->timestamp);
266 WMFreeArray(copy);
270 static WMData*
271 getSelectionData(Display *dpy, Window win, Atom where)
273 WMData *wdata;
274 unsigned char *data;
275 Atom rtype;
276 unsigned bits, bpi;
277 unsigned long len, bytes;
280 if (XGetWindowProperty(dpy, win, where, 0, MAX_PROPERTY_SIZE,
281 False, AnyPropertyType, &rtype, &bits, &len,
282 &bytes, &data)!=Success) {
283 return NULL;
286 bpi = bits/8;
288 wdata = WMCreateDataWithBytesNoCopy(data, len*bpi, (WMFreeDataProc*)XFree);
289 WMSetDataFormat(wdata, bits);
291 return wdata;
295 static void
296 handleNotifyEvent(XEvent *event)
298 SelectionCallback *handler;
299 WMArrayIterator iter;
300 WMArray *copy;
301 WMData *data;
303 WM_ITERATE_ARRAY(selCallbacks, handler, iter) {
305 if (W_VIEW_DRAWABLE(handler->view) != event->xselection.requestor
306 || handler->selection != event->xselection.selection) {
307 continue;
309 handler->flags.done_pending = 1;
311 if (event->xselection.property == None) {
312 data = NULL;
313 } else {
314 data = getSelectionData(event->xselection.display,
315 event->xselection.requestor,
316 event->xselection.property);
319 (*handler->callback)(handler->view, handler->selection,
320 handler->target, handler->timestamp,
321 handler->data, data);
323 if (data != NULL) {
324 WMReleaseData(data);
326 handler->flags.done_pending = 0;
327 handler->flags.delete_pending = 1;
330 /* delete callbacks */
331 copy = WMDuplicateArray(selCallbacks);
332 WM_ITERATE_ARRAY(copy, handler, iter) {
333 if (handler && handler->flags.delete_pending) {
334 WMDeleteSelectionCallback(handler->view, handler->selection,
335 handler->timestamp);
338 WMFreeArray(copy);
343 void
344 W_HandleSelectionEvent(XEvent *event)
346 //printf("%d received %d\n", event->xany.window, event->type);
347 if (event->type == SelectionNotify) {
348 handleNotifyEvent(event);
349 } else {
350 handleRequestEvent(event);
357 Bool
358 WMCreateSelectionHandler(WMView *view, Atom selection, Time timestamp,
359 WMSelectionProcs *procs, void *cdata)
361 SelectionHandler *handler;
362 Display *dpy = W_VIEW_SCREEN(view)->display;
364 XSetSelectionOwner(dpy, selection, W_VIEW_DRAWABLE(view), timestamp);
365 if (XGetSelectionOwner(dpy, selection) != W_VIEW_DRAWABLE(view)) {
366 return False;
369 //printf("created selection handler for %d\n", W_VIEW_DRAWABLE(view));
370 handler = malloc(sizeof(SelectionHandler));
371 if (handler == NULL)
372 return False;
374 handler->view = view;
375 handler->selection = selection;
376 handler->timestamp = timestamp;
377 handler->procs = *procs;
378 handler->data = cdata;
379 memset(&handler->flags, 0, sizeof(handler->flags));
381 if (selHandlers == NULL) {
382 selHandlers = WMCreateArrayWithDestructor(4, wfree);
385 WMAddToArray(selHandlers, handler);
387 return True;
392 Bool
393 WMRequestSelection(WMView *view, Atom selection, Atom target, Time timestamp,
394 WMSelectionCallback *callback, void *cdata)
396 SelectionCallback *handler;
398 if (XGetSelectionOwner(W_VIEW_SCREEN(view)->display, selection) == None)
399 return False;
401 handler = wmalloc(sizeof(SelectionCallback));
403 handler->view = view;
404 handler->selection = selection;
405 handler->target = target;
406 handler->timestamp = timestamp;
407 handler->callback = callback;
408 handler->data = cdata;
409 memset(&handler->flags, 0, sizeof(handler->flags));
411 if (selCallbacks == NULL) {
412 selCallbacks = WMCreateArrayWithDestructor(4, wfree);
415 WMAddToArray(selCallbacks, handler);
417 if (!XConvertSelection(W_VIEW_SCREEN(view)->display, selection, target,
418 W_VIEW_SCREEN(view)->clipboardAtom,
419 W_VIEW_DRAWABLE(view), timestamp)) {
420 return False;
423 return True;