mirror of
https://github.com/gryf/wmaker.git
synced 2025-12-19 04:20:27 +01:00
423 lines
8.7 KiB
C
423 lines
8.7 KiB
C
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <X11/Xatom.h>
|
|
|
|
#include "WINGsP.h"
|
|
|
|
#define MAX_PROPERTY_SIZE 8*1024
|
|
|
|
|
|
typedef struct SelectionHandler {
|
|
WMWidget *widget;
|
|
Atom selection;
|
|
Time timestamp;
|
|
WMConvertSelectionProc *convProc;
|
|
WMLoseSelectionProc *loseProc;
|
|
WMSelectionDoneProc *doneProc;
|
|
|
|
struct {
|
|
unsigned delete_pending:1;
|
|
unsigned done_pending:1;
|
|
} flags;
|
|
|
|
struct SelectionHandler *next;
|
|
} SelectionHandler;
|
|
|
|
|
|
static SelectionHandler *selHandlers = NULL;
|
|
|
|
|
|
void
|
|
WMDeleteSelectionHandler(WMWidget *widget, Atom selection)
|
|
{
|
|
SelectionHandler *handler, *tmp;
|
|
Display *dpy = WMWidgetScreen(widget)->display;
|
|
Window win = WMWidgetXID(widget);
|
|
Time timestamp;
|
|
|
|
if (!selHandlers)
|
|
return;
|
|
|
|
tmp = selHandlers;
|
|
|
|
if (tmp->widget == widget) {
|
|
|
|
if (tmp->flags.done_pending) {
|
|
tmp->flags.delete_pending = 1;
|
|
return;
|
|
}
|
|
selHandlers = tmp->next;
|
|
timestamp = tmp->timestamp;
|
|
wfree(tmp);
|
|
} else {
|
|
while (tmp->next) {
|
|
if (tmp->next->widget == widget) {
|
|
|
|
if (tmp->next->flags.done_pending) {
|
|
tmp->next->flags.delete_pending = 1;
|
|
return;
|
|
}
|
|
handler = tmp->next;
|
|
tmp->next = handler->next;
|
|
timestamp = handler->timestamp;
|
|
wfree(handler);
|
|
break;
|
|
}
|
|
tmp = tmp->next;
|
|
}
|
|
}
|
|
|
|
XGrabServer(dpy);
|
|
if (XGetSelectionOwner(dpy, selection) == win) {
|
|
XSetSelectionOwner(dpy, selection, None, timestamp);
|
|
}
|
|
XUngrabServer(dpy);
|
|
}
|
|
|
|
|
|
static Bool gotError = 0;
|
|
/*
|
|
static int
|
|
errorHandler(XErrorEvent *error)
|
|
{
|
|
return 0;
|
|
}
|
|
*/
|
|
|
|
static Bool
|
|
writeSelection(Display *dpy, Window requestor, Atom property, Atom type,
|
|
void *value, long length, int format)
|
|
{
|
|
/*
|
|
printf("write to %x: %s\n", requestor, XGetAtomName(dpy, property));
|
|
*/
|
|
gotError = False;
|
|
|
|
#ifndef __sgi
|
|
if (!XChangeProperty(dpy, requestor, property, type, format,
|
|
PropModeReplace, value, length))
|
|
return False;
|
|
#else
|
|
/* in sgi seems this seems to return void */
|
|
XChangeProperty(dpy, requestor, property, type, format,
|
|
PropModeReplace, value, length);
|
|
#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);
|
|
}
|
|
|
|
|
|
void
|
|
W_HandleSelectionEvent(XEvent *event)
|
|
{
|
|
SelectionHandler *handler;
|
|
|
|
handler = selHandlers;
|
|
|
|
while (handler) {
|
|
if (WMWidgetXID(handler->widget)==event->xany.window
|
|
/* && handler->selection == event->selection*/) {
|
|
|
|
switch (event->type) {
|
|
case SelectionClear:
|
|
if (handler->loseProc)
|
|
(*handler->loseProc)(handler->widget, handler->selection);
|
|
break;
|
|
|
|
case SelectionRequest:
|
|
if (handler->convProc) {
|
|
Atom atom;
|
|
void *data;
|
|
unsigned length;
|
|
int format;
|
|
Atom prop;
|
|
|
|
/* they're requesting for something old */
|
|
if (event->xselectionrequest.time < handler->timestamp
|
|
&& event->xselectionrequest.time != CurrentTime) {
|
|
|
|
notifySelection(event, None);
|
|
break;
|
|
}
|
|
|
|
handler->flags.done_pending = 1;
|
|
|
|
if (!(*handler->convProc)(handler->widget,
|
|
handler->selection,
|
|
event->xselectionrequest.target,
|
|
&atom, &data, &length, &format)) {
|
|
|
|
notifySelection(event, None);
|
|
break;
|
|
}
|
|
|
|
|
|
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, length, format)) {
|
|
|
|
wfree(data);
|
|
notifySelection(event, None);
|
|
break;
|
|
}
|
|
wfree(data);
|
|
|
|
notifySelection(event, prop);
|
|
|
|
if (handler->doneProc) {
|
|
(*handler->doneProc)(handler->widget,
|
|
handler->selection,
|
|
event->xselectionrequest.target);
|
|
}
|
|
|
|
handler->flags.done_pending = 0;
|
|
|
|
/* in case the handler was deleted from some
|
|
* callback */
|
|
if (handler->flags.delete_pending) {
|
|
WMDeleteSelectionHandler(handler->widget,
|
|
handler->selection);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case SelectionNotify:
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
handler = handler->next;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
Bool
|
|
WMCreateSelectionHandler(WMWidget *w, Atom selection, Time timestamp,
|
|
WMConvertSelectionProc *convProc,
|
|
WMLoseSelectionProc *loseProc,
|
|
WMSelectionDoneProc *doneProc)
|
|
{
|
|
SelectionHandler *handler, *tmp;
|
|
Display *dpy = WMWidgetScreen(w)->display;
|
|
|
|
XSetSelectionOwner(dpy, selection, WMWidgetXID(w), timestamp);
|
|
if (XGetSelectionOwner(dpy, selection) != WMWidgetXID(w))
|
|
return False;
|
|
|
|
handler = malloc(sizeof(SelectionHandler));
|
|
if (!handler)
|
|
return False;
|
|
|
|
handler->widget = w;
|
|
handler->selection = selection;
|
|
handler->timestamp = timestamp;
|
|
handler->convProc = convProc;
|
|
handler->loseProc = loseProc;
|
|
handler->doneProc = doneProc;
|
|
memset(&handler->flags, 0, sizeof(handler->flags));
|
|
|
|
if (!selHandlers) {
|
|
/* first in the queue */
|
|
handler->next = selHandlers;
|
|
selHandlers = handler;
|
|
} else {
|
|
tmp = selHandlers;
|
|
while (tmp->next) {
|
|
tmp = tmp->next;
|
|
}
|
|
handler->next = tmp->next;
|
|
tmp->next = handler;
|
|
}
|
|
|
|
return True;
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
timeoutHandler(void *data)
|
|
{
|
|
*(int*)data = 1;
|
|
}
|
|
|
|
|
|
static Bool
|
|
getInternalSelection(WMScreen *scr, Atom selection, Atom target,
|
|
void **data, unsigned *length)
|
|
{
|
|
Window owner;
|
|
SelectionHandler *handler;
|
|
|
|
/*
|
|
* Check if the selection is owned by this application and if so,
|
|
* do the conversion directly.
|
|
*/
|
|
|
|
*data = NULL;
|
|
|
|
owner = XGetSelectionOwner(scr->display, selection);
|
|
if (!owner)
|
|
return False;
|
|
|
|
handler = selHandlers;
|
|
|
|
while (handler) {
|
|
if (WMWidgetXID(handler->widget) == owner
|
|
/* && handler->selection == event->selection*/) {
|
|
break;
|
|
}
|
|
handler = handler->next;
|
|
}
|
|
|
|
if (!handler)
|
|
return False;
|
|
|
|
if (handler->convProc) {
|
|
Atom atom;
|
|
int format;
|
|
|
|
if (!(*handler->convProc)(handler->widget, handler->selection,
|
|
target, &atom, data, length, &format)) {
|
|
return True;
|
|
}
|
|
|
|
if (handler->doneProc) {
|
|
(*handler->doneProc)(handler->widget, handler->selection, target);
|
|
}
|
|
}
|
|
|
|
return True;
|
|
}
|
|
|
|
|
|
char*
|
|
W_GetTextSelection(WMScreen *scr, Atom selection)
|
|
{
|
|
int buffer = -1;
|
|
|
|
switch (selection) {
|
|
case XA_CUT_BUFFER0:
|
|
buffer = 0;
|
|
break;
|
|
case XA_CUT_BUFFER1:
|
|
buffer = 1;
|
|
break;
|
|
case XA_CUT_BUFFER2:
|
|
buffer = 2;
|
|
break;
|
|
case XA_CUT_BUFFER3:
|
|
buffer = 3;
|
|
break;
|
|
case XA_CUT_BUFFER4:
|
|
buffer = 4;
|
|
break;
|
|
case XA_CUT_BUFFER5:
|
|
buffer = 5;
|
|
break;
|
|
case XA_CUT_BUFFER6:
|
|
buffer = 6;
|
|
break;
|
|
case XA_CUT_BUFFER7:
|
|
buffer = 7;
|
|
break;
|
|
}
|
|
if (buffer >= 0) {
|
|
char *data;
|
|
int size;
|
|
|
|
data = XFetchBuffer(scr->display, &size, buffer);
|
|
|
|
return data;
|
|
} else {
|
|
char *data;
|
|
int bits;
|
|
Atom rtype;
|
|
unsigned long len, bytes;
|
|
WMHandlerID timer;
|
|
int timeout = 0;
|
|
XEvent ev;
|
|
unsigned length;
|
|
|
|
XDeleteProperty(scr->display, scr->groupLeader, scr->clipboardAtom);
|
|
|
|
if (getInternalSelection(scr, selection, XA_STRING, (void**)&data,
|
|
&length)) {
|
|
|
|
return data;
|
|
}
|
|
|
|
XConvertSelection(scr->display, selection, XA_STRING,
|
|
scr->clipboardAtom, scr->groupLeader,
|
|
scr->lastEventTime);
|
|
|
|
timer = WMAddTimerHandler(1000, timeoutHandler, &timeout);
|
|
|
|
while (!XCheckTypedWindowEvent(scr->display, scr->groupLeader,
|
|
SelectionNotify, &ev) && !timeout);
|
|
|
|
if (!timeout) {
|
|
WMDeleteTimerHandler(timer);
|
|
} else {
|
|
wwarning("selection retrieval timed out");
|
|
return NULL;
|
|
}
|
|
|
|
/* nobody owns the selection or the current owner has
|
|
* nothing to do with what we need */
|
|
if (ev.xselection.property == None) {
|
|
return NULL;
|
|
}
|
|
|
|
if (XGetWindowProperty(scr->display, scr->groupLeader,
|
|
scr->clipboardAtom, 0, MAX_PROPERTY_SIZE,
|
|
False, XA_STRING, &rtype, &bits, &len,
|
|
&bytes, (unsigned char**)&data)!=Success) {
|
|
return NULL;
|
|
}
|
|
if (rtype!=XA_STRING || bits!=8) {
|
|
wwarning("invalid data in text selection");
|
|
if (data)
|
|
XFree(data);
|
|
return NULL;
|
|
}
|
|
return data;
|
|
}
|
|
}
|
|
|