#include #include #include "WINGsP.h" #define MAX_PROPERTY_SIZE 8*1024 typedef struct SelectionHandler { WMView *view; Atom selection; Time timestamp; WMSelectionProcs procs; void *data; struct { unsigned delete_pending:1; unsigned done_pending:1; } flags; } SelectionHandler; typedef struct SelectionCallback { WMView *view; Atom selection; Atom target; Time timestamp; WMSelectionCallback *callback; void *data; struct { unsigned delete_pending:1; unsigned done_pending:1; } flags; } SelectionCallback; WMBag *selCallbacks = NULL; WMBag *selHandlers = NULL; void WMDeleteSelectionHandler(WMView *view, Atom selection, Time timestamp) { SelectionHandler *handler; Display *dpy = W_VIEW_SCREEN(view)->display; Window win = W_VIEW_DRAWABLE(view); WMBagIterator iter; if (!selHandlers) return; WM_ITERATE_BAG(selHandlers, handler, iter) { if (handler->view == view && (handler->selection == selection || selection == None) && (handler->timestamp == timestamp || timestamp == CurrentTime)) { if (handler->flags.done_pending) { handler->flags.delete_pending = 1; return; } WMRemoveFromBag(selHandlers, handler); wfree(handler); break; } } XGrabServer(dpy); if (XGetSelectionOwner(dpy, selection) == win) { XSetSelectionOwner(dpy, selection, None, timestamp); } XUngrabServer(dpy); } void WMDeleteSelectionCallback(WMView *view, Atom selection, Time timestamp) { SelectionCallback *handler; WMBagIterator iter; if (!selCallbacks) return; WM_ITERATE_BAG(selCallbacks, handler, iter) { if (handler->view == view && (handler->selection == selection || selection == 0) && (handler->timestamp == timestamp || timestamp == CurrentTime)) { if (handler->flags.done_pending) { handler->flags.delete_pending = 1; return; } WMRemoveFromBag(selCallbacks, handler); wfree(handler); break; } } } static Bool gotError = 0; /* static int errorHandler(XErrorEvent *error) { return 0; } */ static Bool writeSelection(Display *dpy, Window requestor, Atom property, Atom type, WMData *data) { int format; format = WMGetDataFormat(data); if (format == 0) format = 8; /* printf("write to %x: %s\n", requestor, XGetAtomName(dpy, property)); */ gotError = False; #ifndef __sgi if (!XChangeProperty(dpy, requestor, property, type, format, PropModeReplace, WMDataBytes(data), WMGetDataLength(data))) return False; #else /* in sgi seems this seems to return void */ XChangeProperty(dpy, requestor, property, type, format, PropModeReplace, WMDataBytes(data), WMGetDataLength(data)); #endif XFlush(dpy); return !gotError; } static void notifySelection(XEvent *event, Atom prop) { XEvent ev; /* printf("envent to %x\n", event->xselectionrequest.requestor); */ ev.xselection.type = SelectionNotify; ev.xselection.serial = 0; ev.xselection.send_event = True; ev.xselection.display = event->xselectionrequest.display; ev.xselection.requestor = event->xselectionrequest.requestor; ev.xselection.target = event->xselectionrequest.target; ev.xselection.selection = event->xselectionrequest.selection; ev.xselection.property = prop; ev.xselection.time = event->xselectionrequest.time; XSendEvent(event->xany.display, event->xselectionrequest.requestor, False, 0, &ev); XFlush(event->xany.display); } static void deleteHandlers(WMBagIterator iter) { SelectionHandler *handler; if (iter == NULL) handler = WMBagFirst(selHandlers, &iter); else handler = WMBagNext(selHandlers, &iter); if (handler == NULL) return; deleteHandlers(iter); if (handler->flags.delete_pending) { WMDeleteSelectionHandler(handler->view, handler->selection, handler->timestamp); } } static void handleRequestEvent(XEvent *event) { SelectionHandler *handler; WMBagIterator iter; Bool handledRequest = False; WM_ITERATE_BAG(selHandlers, handler, iter) { switch (event->type) { case SelectionClear: if (W_VIEW_DRAWABLE(handler->view) != event->xselectionclear.window) { break; } handler->flags.done_pending = 1; if (handler->procs.selectionLost) handler->procs.selectionLost(handler->view, handler->selection, handler->data); handler->flags.done_pending = 0; handler->flags.delete_pending = 1; break; case SelectionRequest: if (W_VIEW_DRAWABLE(handler->view) != event->xselectionrequest.owner) { break; } if (handler->procs.convertSelection != NULL && handler->selection == event->xselectionrequest.selection) { Atom atom; WMData *data; Atom prop; /* they're requesting for something old.. maybe another handler * can handle it */ if (event->xselectionrequest.time < handler->timestamp && event->xselectionrequest.time != CurrentTime) { break; } handler->flags.done_pending = 1; data = handler->procs.convertSelection(handler->view, handler->selection, event->xselectionrequest.target, handler->data, &atom); if (data == NULL) { break; } handledRequest = True; prop = event->xselectionrequest.property; /* obsolete clients that don't set the property field */ if (prop == None) prop = event->xselectionrequest.target; if (!writeSelection(event->xselectionrequest.display, event->xselectionrequest.requestor, prop, atom, data)) { WMReleaseData(data); notifySelection(event, None); break; } WMReleaseData(data); notifySelection(event, prop); if (handler->procs.selectionDone != NULL) { handler->procs.selectionDone(handler->view, handler->selection, event->xselectionrequest.target, handler->data); } handler->flags.done_pending = 0; if (!handledRequest) { notifySelection(event, None); } } break; } } deleteHandlers(NULL); } static void deleteCallbacks(WMBagIterator iter) { SelectionCallback *handler; if (iter == NULL) handler = WMBagFirst(selCallbacks, &iter); else handler = WMBagNext(selCallbacks, &iter); if (handler == NULL) return; deleteCallbacks(iter); if (handler->flags.delete_pending) { WMDeleteSelectionCallback(handler->view, handler->selection, handler->timestamp); } } static WMData* getSelectionData(Display *dpy, Window win, Atom where) { WMData *wdata; unsigned char *data; Atom rtype; unsigned bits; unsigned long len, bytes; if (XGetWindowProperty(dpy, win, where, 0, MAX_PROPERTY_SIZE, False, AnyPropertyType, &rtype, &bits, &len, &bytes, &data)!=Success) { return NULL; } wdata = WMCreateDataWithBytesAndDestructor(data, len, (WMFreeDataProc*)XFree); if (wdata == NULL) { return NULL; } WMSetDataFormat(wdata, bits); return wdata; } static void handleNotifyEvent(XEvent *event) { SelectionCallback *handler; WMBagIterator iter; WMData *data; WM_ITERATE_BAG(selCallbacks, handler, iter) { if (W_VIEW_DRAWABLE(handler->view) != event->xselection.requestor && handler->selection == event->xselection.selection) { continue; } handler->flags.done_pending = 1; if (event->xselection.property == None) { data = NULL; } else { data = getSelectionData(event->xselection.display, event->xselection.requestor, event->xselection.property); } (*handler->callback)(handler->view, handler->selection, handler->target, handler->timestamp, handler->data, data); if (data != NULL) { WMReleaseData(data); } handler->flags.done_pending = 0; handler->flags.delete_pending = 1; } deleteCallbacks(NULL); } void W_HandleSelectionEvent(XEvent *event) { if (event->type == SelectionNotify) { handleNotifyEvent(event); } else { handleRequestEvent(event); } } Bool WMCreateSelectionHandler(WMView *view, Atom selection, Time timestamp, WMSelectionProcs *procs, void *cdata) { SelectionHandler *handler; Display *dpy = W_VIEW_SCREEN(view)->display; XSetSelectionOwner(dpy, selection, W_VIEW_DRAWABLE(view), timestamp); if (XGetSelectionOwner(dpy, selection) != W_VIEW_DRAWABLE(view)) return False; handler = malloc(sizeof(SelectionHandler)); if (handler == NULL) return False; handler->view = view; handler->selection = selection; handler->timestamp = timestamp; handler->procs = *procs; handler->data = cdata; memset(&handler->flags, 0, sizeof(handler->flags)); if (selHandlers == NULL) { selHandlers = WMCreateTreeBag(); } WMPutInBag(selHandlers, handler); return True; } Bool WMRequestSelection(WMView *view, Atom selection, Atom target, Time timestamp, WMSelectionCallback *callback, void *cdata) { SelectionCallback *handler; if (XGetSelectionOwner(W_VIEW_SCREEN(view)->display, selection) == None) return False; handler = wmalloc(sizeof(SelectionCallback)); handler->view = view; handler->selection = selection; handler->target = target; handler->timestamp = timestamp; handler->callback = callback; handler->data = cdata; memset(&handler->flags, 0, sizeof(handler->flags)); if (selCallbacks == NULL) { selCallbacks = WMCreateTreeBag(); } WMPutInBag(selCallbacks, handler); if (!XConvertSelection(W_VIEW_SCREEN(view)->display, selection, target, W_VIEW_SCREEN(view)->clipboardAtom, W_VIEW_DRAWABLE(view), timestamp)) { return False; } return True; }