debian: Update with version 0.95.9-2 packaging.
[wmaker-crm.git] / WINGs / selection.c
blobde0f9f7e403b24c264f05e3f84bce8d8c25241e9
2 #include <stdlib.h>
4 #include <X11/Xatom.h>
6 #include "WINGsP.h"
8 #define MAX_PROPERTY_SIZE 8*1024
10 char *WMSelectionOwnerDidChangeNotification = "WMSelectionOwnerDidChange";
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;
25 typedef struct SelectionCallback {
26 WMView *view;
27 Atom selection;
28 Atom target;
29 Time timestamp;
30 WMSelectionCallback *callback;
31 void *data;
33 struct {
34 unsigned delete_pending:1;
35 unsigned done_pending:1;
36 } flags;
37 } SelectionCallback;
39 static WMArray *selCallbacks = NULL;
41 static WMArray *selHandlers = NULL;
43 static Bool gotXError = False;
45 void WMDeleteSelectionHandler(WMView * view, Atom selection, Time timestamp)
47 SelectionHandler *handler;
48 Display *dpy = W_VIEW_SCREEN(view)->display;
49 Window win = W_VIEW_DRAWABLE(view);
50 WMArrayIterator iter;
52 if (!selHandlers)
53 return;
55 /*//printf("deleting selection handler for %d", win); */
57 WM_ITERATE_ARRAY(selHandlers, handler, iter) {
58 if (handler->view == view && (handler->selection == selection || selection == None)
59 && (handler->timestamp == timestamp || timestamp == CurrentTime)) {
61 if (handler->flags.done_pending) {
62 handler->flags.delete_pending = 1;
63 /*//puts(": postponed because still pending"); */
64 return;
66 /*//printf(": found & removed"); */
67 WMRemoveFromArray(selHandlers, handler);
68 break;
72 /*//printf("\n"); */
74 XGrabServer(dpy);
75 if (XGetSelectionOwner(dpy, selection) == win) {
76 XSetSelectionOwner(dpy, selection, None, timestamp);
78 XUngrabServer(dpy);
81 static void WMDeleteSelectionCallback(WMView * view, Atom selection, Time timestamp)
83 SelectionCallback *handler;
84 WMArrayIterator iter;
86 if (!selCallbacks)
87 return;
89 WM_ITERATE_ARRAY(selCallbacks, handler, iter) {
90 if (handler->view == view && (handler->selection == selection || selection == None)
91 && (handler->timestamp == timestamp || timestamp == CurrentTime)) {
93 if (handler->flags.done_pending) {
94 handler->flags.delete_pending = 1;
95 return;
97 WMRemoveFromArray(selCallbacks, handler);
98 break;
103 static int handleXError(Display * dpy, XErrorEvent * ev)
105 /* Parameter not used, but tell the compiler that it is ok */
106 (void) dpy;
107 (void) ev;
109 gotXError = True;
111 return 1;
114 static Bool writeSelection(Display * dpy, Window requestor, Atom property, Atom type, WMData * data)
116 static void *oldHandler;
117 int format, bpi;
119 format = WMGetDataFormat(data);
120 if (format == 0)
121 format = 8;
123 bpi = format / 8;
125 /* printf("write to %x: %s\n", requestor, XGetAtomName(dpy, property)); */
127 oldHandler = XSetErrorHandler(handleXError);
129 gotXError = False;
131 XChangeProperty(dpy, requestor, property, type, format, PropModeReplace,
132 WMDataBytes(data), WMGetDataLength(data) / bpi);
134 XFlush(dpy);
136 XSetErrorHandler(oldHandler);
138 return !gotXError;
141 static void notifySelection(XEvent * event, Atom prop)
143 XEvent ev;
145 /* printf("event to %x\n", event->xselectionrequest.requestor); */
147 ev.xselection.type = SelectionNotify;
148 ev.xselection.serial = 0;
149 ev.xselection.send_event = True;
150 ev.xselection.display = event->xselectionrequest.display;
151 ev.xselection.requestor = event->xselectionrequest.requestor;
152 ev.xselection.target = event->xselectionrequest.target;
153 ev.xselection.selection = event->xselectionrequest.selection;
154 ev.xselection.property = prop;
155 ev.xselection.time = event->xselectionrequest.time;
157 XSendEvent(event->xany.display, event->xselectionrequest.requestor, False, 0, &ev);
158 XFlush(event->xany.display);
161 static void handleRequestEvent(XEvent * event)
163 SelectionHandler *handler;
164 WMArrayIterator iter;
165 WMArray *copy;
166 Bool handledRequest;
168 WM_ITERATE_ARRAY(selHandlers, handler, iter) {
170 switch (event->type) {
171 case SelectionClear:
172 if (W_VIEW_DRAWABLE(handler->view)
173 != event->xselectionclear.window) {
174 break;
177 handler->flags.done_pending = 1;
178 if (handler->procs.selectionLost)
179 handler->procs.selectionLost(handler->view, handler->selection, handler->data);
180 handler->flags.done_pending = 0;
181 handler->flags.delete_pending = 1;
182 break;
184 case SelectionRequest:
185 if (W_VIEW_DRAWABLE(handler->view) != event->xselectionrequest.owner) {
186 break;
189 if (handler->procs.convertSelection != NULL
190 && handler->selection == event->xselectionrequest.selection) {
191 Atom atom;
192 WMData *data;
193 Atom prop;
195 /* they're requesting for something old.. maybe another handler
196 * can handle it */
197 if (event->xselectionrequest.time < handler->timestamp
198 && event->xselectionrequest.time != CurrentTime) {
199 break;
202 handledRequest = False;
204 handler->flags.done_pending = 1;
206 data = handler->procs.convertSelection(handler->view,
207 handler->selection,
208 event->xselectionrequest.target,
209 handler->data, &atom);
211 prop = event->xselectionrequest.property;
212 /* obsolete clients that don't set the property field */
213 if (prop == None)
214 prop = event->xselectionrequest.target;
216 if (data) {
217 if (writeSelection(event->xselectionrequest.display,
218 event->xselectionrequest.requestor, prop, atom, data)) {
219 handledRequest = True;
221 WMReleaseData(data);
224 notifySelection(event, (handledRequest == True ? prop : None));
226 if (handler->procs.selectionDone != NULL) {
227 handler->procs.selectionDone(handler->view,
228 handler->selection,
229 event->xselectionrequest.target,
230 handler->data);
233 handler->flags.done_pending = 0;
235 break;
239 /* delete handlers */
240 copy = WMDuplicateArray(selHandlers);
241 WM_ITERATE_ARRAY(copy, handler, iter) {
242 if (handler && handler->flags.delete_pending) {
243 WMDeleteSelectionHandler(handler->view, handler->selection, handler->timestamp);
246 WMFreeArray(copy);
249 static WMData *getSelectionData(Display * dpy, Window win, Atom where)
251 WMData *wdata;
252 unsigned char *data;
253 Atom rtype;
254 int bits, bpi;
255 unsigned long len, bytes;
257 if (XGetWindowProperty(dpy, win, where, 0, MAX_PROPERTY_SIZE,
258 False, AnyPropertyType, &rtype, &bits, &len, &bytes, &data) != Success) {
259 return NULL;
262 bpi = bits / 8;
264 wdata = WMCreateDataWithBytesNoCopy(data, len * bpi, (void *) XFree);
265 WMSetDataFormat(wdata, bits);
267 return wdata;
270 static void handleNotifyEvent(XEvent * event)
272 SelectionCallback *handler;
273 WMArrayIterator iter;
274 WMArray *copy;
275 WMData *data;
277 WM_ITERATE_ARRAY(selCallbacks, handler, iter) {
279 if (W_VIEW_DRAWABLE(handler->view) != event->xselection.requestor
280 || handler->selection != event->xselection.selection) {
281 continue;
283 handler->flags.done_pending = 1;
285 if (event->xselection.property == None) {
286 data = NULL;
287 } else {
288 data = getSelectionData(event->xselection.display,
289 event->xselection.requestor, event->xselection.property);
292 (*handler->callback) (handler->view, handler->selection,
293 handler->target, handler->timestamp, handler->data, data);
295 if (data != NULL) {
296 WMReleaseData(data);
298 handler->flags.done_pending = 0;
299 handler->flags.delete_pending = 1;
302 /* delete callbacks */
303 copy = WMDuplicateArray(selCallbacks);
304 WM_ITERATE_ARRAY(copy, handler, iter) {
305 if (handler && handler->flags.delete_pending) {
306 WMDeleteSelectionCallback(handler->view, handler->selection, handler->timestamp);
309 WMFreeArray(copy);
312 void W_HandleSelectionEvent(XEvent * event)
314 /*//printf("%d received selection ", event->xany.window); */
315 /*//switch(event->type) {
316 case SelectionNotify:
317 puts("notify"); break;
318 case SelectionRequest:
319 puts("request"); break;
320 case SelectionClear:
321 puts("clear"); break;
322 default:
323 puts("unknown"); break;
324 } */
326 if (event->type == SelectionNotify) {
327 handleNotifyEvent(event);
328 } else {
329 handleRequestEvent(event);
333 Bool WMCreateSelectionHandler(WMView * view, Atom selection, Time timestamp, WMSelectionProcs * procs, void *cdata)
335 SelectionHandler *handler;
336 Display *dpy = W_VIEW_SCREEN(view)->display;
338 XSetSelectionOwner(dpy, selection, W_VIEW_DRAWABLE(view), timestamp);
339 if (XGetSelectionOwner(dpy, selection) != W_VIEW_DRAWABLE(view)) {
340 return False;
343 WMPostNotificationName(WMSelectionOwnerDidChangeNotification, (void *)selection, (void *)view);
345 /*//printf("created selection handler for %d\n", W_VIEW_DRAWABLE(view)); */
347 handler = wmalloc(sizeof(SelectionHandler));
348 handler->view = view;
349 handler->selection = selection;
350 handler->timestamp = timestamp;
351 handler->procs = *procs;
352 handler->data = cdata;
353 memset(&handler->flags, 0, sizeof(handler->flags));
355 if (selHandlers == NULL) {
356 selHandlers = WMCreateArrayWithDestructor(4, wfree);
359 WMAddToArray(selHandlers, handler);
361 return True;
364 Bool
365 WMRequestSelection(WMView * view, Atom selection, Atom target, Time timestamp,
366 WMSelectionCallback * callback, void *cdata)
368 SelectionCallback *handler;
370 if (XGetSelectionOwner(W_VIEW_SCREEN(view)->display, selection) == None)
371 return False;
373 if (!XConvertSelection(W_VIEW_SCREEN(view)->display, selection, target,
374 W_VIEW_SCREEN(view)->clipboardAtom, W_VIEW_DRAWABLE(view), timestamp)) {
375 return False;
378 handler = wmalloc(sizeof(SelectionCallback));
379 handler->view = view;
380 handler->selection = selection;
381 handler->target = target;
382 handler->timestamp = timestamp;
383 handler->callback = callback;
384 handler->data = cdata;
386 if (selCallbacks == NULL) {
387 selCallbacks = WMCreateArrayWithDestructor(4, wfree);
390 WMAddToArray(selCallbacks, handler);
392 return True;