8 #define MAX_PROPERTY_SIZE 8*1024
10 char *WMSelectionOwnerDidChangeNotification = "WMSelectionOwnerDidChange";
12 typedef struct SelectionHandler {
16 WMSelectionProcs procs;
20 unsigned delete_pending:1;
21 unsigned done_pending:1;
25 typedef struct SelectionCallback {
30 WMSelectionCallback *callback;
34 unsigned delete_pending:1;
35 unsigned done_pending:1;
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);
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"); */
66 /*//printf(": found & removed"); */
67 WMRemoveFromArray(selHandlers, handler);
75 if (XGetSelectionOwner(dpy, selection) == win) {
76 XSetSelectionOwner(dpy, selection, None, timestamp);
81 void WMDeleteSelectionCallback(WMView * view, Atom selection, Time timestamp)
83 SelectionCallback *handler;
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;
97 WMRemoveFromArray(selCallbacks, handler);
103 static int handleXError(Display * dpy, XErrorEvent * ev)
110 static Bool writeSelection(Display * dpy, Window requestor, Atom property, Atom type, WMData * data)
112 static void *oldHandler;
115 format = WMGetDataFormat(data);
121 /* printf("write to %x: %s\n", requestor, XGetAtomName(dpy, property)); */
123 oldHandler = XSetErrorHandler(handleXError);
127 XChangeProperty(dpy, requestor, property, type, format, PropModeReplace,
128 WMDataBytes(data), WMGetDataLength(data) / bpi);
132 XSetErrorHandler(oldHandler);
137 static void notifySelection(XEvent * event, Atom prop)
141 /* printf("event to %x\n", event->xselectionrequest.requestor); */
143 ev.xselection.type = SelectionNotify;
144 ev.xselection.serial = 0;
145 ev.xselection.send_event = True;
146 ev.xselection.display = event->xselectionrequest.display;
147 ev.xselection.requestor = event->xselectionrequest.requestor;
148 ev.xselection.target = event->xselectionrequest.target;
149 ev.xselection.selection = event->xselectionrequest.selection;
150 ev.xselection.property = prop;
151 ev.xselection.time = event->xselectionrequest.time;
153 XSendEvent(event->xany.display, event->xselectionrequest.requestor, False, 0, &ev);
154 XFlush(event->xany.display);
157 static void handleRequestEvent(XEvent * event)
159 SelectionHandler *handler;
160 WMArrayIterator iter;
164 WM_ITERATE_ARRAY(selHandlers, handler, iter) {
166 switch (event->type) {
168 if (W_VIEW_DRAWABLE(handler->view)
169 != event->xselectionclear.window) {
173 handler->flags.done_pending = 1;
174 if (handler->procs.selectionLost)
175 handler->procs.selectionLost(handler->view, handler->selection, handler->data);
176 handler->flags.done_pending = 0;
177 handler->flags.delete_pending = 1;
180 case SelectionRequest:
181 if (W_VIEW_DRAWABLE(handler->view) != event->xselectionrequest.owner) {
185 if (handler->procs.convertSelection != NULL
186 && handler->selection == event->xselectionrequest.selection) {
191 /* they're requesting for something old.. maybe another handler
193 if (event->xselectionrequest.time < handler->timestamp
194 && event->xselectionrequest.time != CurrentTime) {
198 handledRequest = False;
200 handler->flags.done_pending = 1;
202 data = handler->procs.convertSelection(handler->view,
204 event->xselectionrequest.target,
205 handler->data, &atom);
207 prop = event->xselectionrequest.property;
208 /* obsolete clients that don't set the property field */
210 prop = event->xselectionrequest.target;
213 if (writeSelection(event->xselectionrequest.display,
214 event->xselectionrequest.requestor, prop, atom, data)) {
215 handledRequest = True;
220 notifySelection(event, (handledRequest == True ? prop : None));
222 if (handler->procs.selectionDone != NULL) {
223 handler->procs.selectionDone(handler->view,
225 event->xselectionrequest.target,
229 handler->flags.done_pending = 0;
235 /* delete handlers */
236 copy = WMDuplicateArray(selHandlers);
237 WM_ITERATE_ARRAY(copy, handler, iter) {
238 if (handler && handler->flags.delete_pending) {
239 WMDeleteSelectionHandler(handler->view, handler->selection, handler->timestamp);
245 static WMData *getSelectionData(Display * dpy, Window win, Atom where)
251 unsigned long len, bytes;
253 if (XGetWindowProperty(dpy, win, where, 0, MAX_PROPERTY_SIZE,
254 False, AnyPropertyType, &rtype, &bits, &len, &bytes, &data) != Success) {
260 wdata = WMCreateDataWithBytesNoCopy(data, len * bpi, (WMFreeDataProc *) XFree);
261 WMSetDataFormat(wdata, bits);
266 static void handleNotifyEvent(XEvent * event)
268 SelectionCallback *handler;
269 WMArrayIterator iter;
273 WM_ITERATE_ARRAY(selCallbacks, handler, iter) {
275 if (W_VIEW_DRAWABLE(handler->view) != event->xselection.requestor
276 || handler->selection != event->xselection.selection) {
279 handler->flags.done_pending = 1;
281 if (event->xselection.property == None) {
284 data = getSelectionData(event->xselection.display,
285 event->xselection.requestor, event->xselection.property);
288 (*handler->callback) (handler->view, handler->selection,
289 handler->target, handler->timestamp, handler->data, data);
294 handler->flags.done_pending = 0;
295 handler->flags.delete_pending = 1;
298 /* delete callbacks */
299 copy = WMDuplicateArray(selCallbacks);
300 WM_ITERATE_ARRAY(copy, handler, iter) {
301 if (handler && handler->flags.delete_pending) {
302 WMDeleteSelectionCallback(handler->view, handler->selection, handler->timestamp);
308 void W_HandleSelectionEvent(XEvent * event)
310 /*//printf("%d received selection ", event->xany.window); */
311 /*//switch(event->type) {
312 case SelectionNotify:
313 puts("notify"); break;
314 case SelectionRequest:
315 puts("request"); break;
317 puts("clear"); break;
319 puts("unknown"); break;
322 if (event->type == SelectionNotify) {
323 handleNotifyEvent(event);
325 handleRequestEvent(event);
329 Bool WMCreateSelectionHandler(WMView * view, Atom selection, Time timestamp, WMSelectionProcs * procs, void *cdata)
331 SelectionHandler *handler;
332 Display *dpy = W_VIEW_SCREEN(view)->display;
334 XSetSelectionOwner(dpy, selection, W_VIEW_DRAWABLE(view), timestamp);
335 if (XGetSelectionOwner(dpy, selection) != W_VIEW_DRAWABLE(view)) {
339 WMPostNotificationName(WMSelectionOwnerDidChangeNotification, (void *)selection, (void *)view);
341 /*//printf("created selection handler for %d\n", W_VIEW_DRAWABLE(view)); */
343 handler = wmalloc(sizeof(SelectionHandler));
345 handler->view = view;
346 handler->selection = selection;
347 handler->timestamp = timestamp;
348 handler->procs = *procs;
349 handler->data = cdata;
350 memset(&handler->flags, 0, sizeof(handler->flags));
352 if (selHandlers == NULL) {
353 selHandlers = WMCreateArrayWithDestructor(4, wfree);
356 WMAddToArray(selHandlers, handler);
362 WMRequestSelection(WMView * view, Atom selection, Atom target, Time timestamp,
363 WMSelectionCallback * callback, void *cdata)
365 SelectionCallback *handler;
367 if (XGetSelectionOwner(W_VIEW_SCREEN(view)->display, selection) == None)
370 if (!XConvertSelection(W_VIEW_SCREEN(view)->display, selection, target,
371 W_VIEW_SCREEN(view)->clipboardAtom, W_VIEW_DRAWABLE(view), timestamp)) {
375 handler = wmalloc(sizeof(SelectionCallback));
377 handler->view = view;
378 handler->selection = selection;
379 handler->target = target;
380 handler->timestamp = timestamp;
381 handler->callback = callback;
382 handler->data = cdata;
383 memset(&handler->flags, 0, sizeof(handler->flags));
385 if (selCallbacks == NULL) {
386 selCallbacks = WMCreateArrayWithDestructor(4, wfree);
389 WMAddToArray(selCallbacks, handler);