forked from vitrine/wmaker
This constructor was only needed in one particular place. We can duplidate the data instead of borrowing it. This ensures that WMData always owns its data segment, which simplifies porting to Rust significantly.
395 lines
9.5 KiB
C
395 lines
9.5 KiB
C
|
|
#include <stdlib.h>
|
|
|
|
#include <X11/Xatom.h>
|
|
|
|
#include "WINGsP.h"
|
|
|
|
#define MAX_PROPERTY_SIZE 8*1024
|
|
|
|
const char *WMSelectionOwnerDidChangeNotification = "WMSelectionOwnerDidChange";
|
|
|
|
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;
|
|
|
|
static WMArray *selCallbacks = NULL;
|
|
|
|
static WMArray *selHandlers = NULL;
|
|
|
|
static Bool gotXError = False;
|
|
|
|
void WMDeleteSelectionHandler(WMView * view, Atom selection, Time timestamp)
|
|
{
|
|
SelectionHandler *handler;
|
|
Display *dpy = W_VIEW_SCREEN(view)->display;
|
|
Window win = W_VIEW_DRAWABLE(view);
|
|
WMArrayIterator iter;
|
|
|
|
if (!selHandlers)
|
|
return;
|
|
|
|
/*//printf("deleting selection handler for %d", win); */
|
|
|
|
WM_ITERATE_ARRAY(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;
|
|
/*//puts(": postponed because still pending"); */
|
|
return;
|
|
}
|
|
/*//printf(": found & removed"); */
|
|
WMRemoveFromArray(selHandlers, handler);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*//printf("\n"); */
|
|
|
|
XGrabServer(dpy);
|
|
if (XGetSelectionOwner(dpy, selection) == win) {
|
|
XSetSelectionOwner(dpy, selection, None, timestamp);
|
|
}
|
|
XUngrabServer(dpy);
|
|
}
|
|
|
|
static void WMDeleteSelectionCallback(WMView * view, Atom selection, Time timestamp)
|
|
{
|
|
SelectionCallback *handler;
|
|
WMArrayIterator iter;
|
|
|
|
if (!selCallbacks)
|
|
return;
|
|
|
|
WM_ITERATE_ARRAY(selCallbacks, 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;
|
|
}
|
|
WMRemoveFromArray(selCallbacks, handler);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int handleXError(Display * dpy, XErrorEvent * ev)
|
|
{
|
|
/* Parameter not used, but tell the compiler that it is ok */
|
|
(void) dpy;
|
|
(void) ev;
|
|
|
|
gotXError = True;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static Bool writeSelection(Display * dpy, Window requestor, Atom property, Atom type, WMData * data)
|
|
{
|
|
static void *oldHandler;
|
|
int format, bpi;
|
|
|
|
format = WMGetDataFormat(data);
|
|
if (format == 0)
|
|
format = 8;
|
|
|
|
bpi = format / 8;
|
|
|
|
/* printf("write to %x: %s\n", requestor, XGetAtomName(dpy, property)); */
|
|
|
|
oldHandler = XSetErrorHandler(handleXError);
|
|
|
|
gotXError = False;
|
|
|
|
XChangeProperty(dpy, requestor, property, type, format, PropModeReplace,
|
|
WMDataBytes(data), WMGetDataLength(data) / bpi);
|
|
|
|
XFlush(dpy);
|
|
|
|
XSetErrorHandler(oldHandler);
|
|
|
|
return !gotXError;
|
|
}
|
|
|
|
static void notifySelection(XEvent * event, Atom prop)
|
|
{
|
|
XEvent ev;
|
|
|
|
/* printf("event 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 handleRequestEvent(XEvent * event)
|
|
{
|
|
SelectionHandler *handler;
|
|
WMArrayIterator iter;
|
|
WMArray *copy;
|
|
Bool handledRequest;
|
|
|
|
WM_ITERATE_ARRAY(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;
|
|
}
|
|
|
|
handledRequest = False;
|
|
|
|
handler->flags.done_pending = 1;
|
|
|
|
data = handler->procs.convertSelection(handler->view,
|
|
handler->selection,
|
|
event->xselectionrequest.target,
|
|
handler->data, &atom);
|
|
|
|
prop = event->xselectionrequest.property;
|
|
/* obsolete clients that don't set the property field */
|
|
if (prop == None)
|
|
prop = event->xselectionrequest.target;
|
|
|
|
if (data) {
|
|
if (writeSelection(event->xselectionrequest.display,
|
|
event->xselectionrequest.requestor, prop, atom, data)) {
|
|
handledRequest = True;
|
|
}
|
|
WMReleaseData(data);
|
|
}
|
|
|
|
notifySelection(event, (handledRequest == True ? prop : None));
|
|
|
|
if (handler->procs.selectionDone != NULL) {
|
|
handler->procs.selectionDone(handler->view,
|
|
handler->selection,
|
|
event->xselectionrequest.target,
|
|
handler->data);
|
|
}
|
|
|
|
handler->flags.done_pending = 0;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* delete handlers */
|
|
copy = WMCreateArrayWithArray(selHandlers);
|
|
WM_ITERATE_ARRAY(copy, handler, iter) {
|
|
if (handler && handler->flags.delete_pending) {
|
|
WMDeleteSelectionHandler(handler->view, handler->selection, handler->timestamp);
|
|
}
|
|
}
|
|
WMFreeArray(copy);
|
|
}
|
|
|
|
static WMData *getSelectionData(Display * dpy, Window win, Atom where)
|
|
{
|
|
WMData *wdata;
|
|
unsigned char *data;
|
|
Atom rtype;
|
|
int bits, bpi;
|
|
unsigned long len, bytes;
|
|
|
|
if (XGetWindowProperty(dpy, win, where, 0, MAX_PROPERTY_SIZE,
|
|
False, AnyPropertyType, &rtype, &bits, &len, &bytes, &data) != Success) {
|
|
return NULL;
|
|
}
|
|
|
|
bpi = bits / 8;
|
|
|
|
wdata = WMCreateDataWithBytes(data, len * bpi);
|
|
WMSetDataFormat(wdata, bits);
|
|
XFree(data);
|
|
|
|
return wdata;
|
|
}
|
|
|
|
static void handleNotifyEvent(XEvent * event)
|
|
{
|
|
SelectionCallback *handler;
|
|
WMArrayIterator iter;
|
|
WMArray *copy;
|
|
WMData *data;
|
|
|
|
WM_ITERATE_ARRAY(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;
|
|
}
|
|
|
|
/* delete callbacks */
|
|
copy = WMCreateArrayWithArray(selCallbacks);
|
|
WM_ITERATE_ARRAY(copy, handler, iter) {
|
|
if (handler && handler->flags.delete_pending) {
|
|
WMDeleteSelectionCallback(handler->view, handler->selection, handler->timestamp);
|
|
}
|
|
}
|
|
WMFreeArray(copy);
|
|
}
|
|
|
|
void W_HandleSelectionEvent(XEvent * event)
|
|
{
|
|
/*//printf("%d received selection ", event->xany.window); */
|
|
/*//switch(event->type) {
|
|
case SelectionNotify:
|
|
puts("notify"); break;
|
|
case SelectionRequest:
|
|
puts("request"); break;
|
|
case SelectionClear:
|
|
puts("clear"); break;
|
|
default:
|
|
puts("unknown"); break;
|
|
} */
|
|
|
|
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;
|
|
}
|
|
|
|
WMPostNotificationName(WMSelectionOwnerDidChangeNotification, (void *)selection, (void *)view);
|
|
|
|
/*//printf("created selection handler for %d\n", W_VIEW_DRAWABLE(view)); */
|
|
|
|
handler = wmalloc(sizeof(SelectionHandler));
|
|
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 = WMCreateArrayWithDestructor(4, wfree);
|
|
}
|
|
|
|
WMAddToArray(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;
|
|
|
|
if (!XConvertSelection(W_VIEW_SCREEN(view)->display, selection, target,
|
|
W_VIEW_SCREEN(view)->clipboardAtom, W_VIEW_DRAWABLE(view), timestamp)) {
|
|
return False;
|
|
}
|
|
|
|
handler = wmalloc(sizeof(SelectionCallback));
|
|
handler->view = view;
|
|
handler->selection = selection;
|
|
handler->target = target;
|
|
handler->timestamp = timestamp;
|
|
handler->callback = callback;
|
|
handler->data = cdata;
|
|
|
|
if (selCallbacks == NULL) {
|
|
selCallbacks = WMCreateArrayWithDestructor(4, wfree);
|
|
}
|
|
|
|
WMAddToArray(selCallbacks, handler);
|
|
|
|
return True;
|
|
}
|