diff options
-rw-r--r-- | src/cmd/snarfer/mkfile | 9 | ||||
-rw-r--r-- | src/cmd/snarfer/snarfer.c | 310 |
2 files changed, 319 insertions, 0 deletions
diff --git a/src/cmd/snarfer/mkfile b/src/cmd/snarfer/mkfile new file mode 100644 index 00000000..b56ca029 --- /dev/null +++ b/src/cmd/snarfer/mkfile @@ -0,0 +1,9 @@ +<$PLAN9/src/mkhdr + +TARG=snarfer +OFILES= +HFILES= + +<|sh ../../libdraw/mkwsysrules.sh +<$PLAN9/src/mkmany + diff --git a/src/cmd/snarfer/snarfer.c b/src/cmd/snarfer/snarfer.c new file mode 100644 index 00000000..16f9b2ba --- /dev/null +++ b/src/cmd/snarfer/snarfer.c @@ -0,0 +1,310 @@ +/* + * This program is only intended for OS X, but the + * ifdef __APPLE__ below lets us build it on all systems. + * On non-OS X systems, you can use it to hold the snarf + * buffer around after a program exits. + */ + +#include <u.h> +#define Colormap XColormap +#define Cursor XCursor +#define Display XDisplay +#define Drawable XDrawable +#define Font XFont +#define GC XGC +#define Point XPoint +#define Rectangle XRectangle +#define Screen XScreen +#define Visual XVisual +#define Window XWindow +#include <X11/Xlib.h> +#include <X11/Xatom.h> +#include <X11/Xutil.h> +#include <X11/keysym.h> +#include <X11/IntrinsicP.h> +#include <X11/StringDefs.h> +#undef Colormap +#undef Cursor +#undef Display +#undef Drawable +#undef Font +#undef GC +#undef Point +#undef Rectangle +#undef Screen +#undef Visual +#undef Window +#ifdef __APPLE__ +#define APPLESNARF +#define Boolean AppleBoolean +#define Rect AppleRect +#define EventMask AppleEventMask +#define Point ApplePoint +#define Cursor AppleCursor +#include <Carbon/Carbon.h> +AUTOFRAMEWORK(Carbon) +#undef Boolean +#undef Rect +#undef EventMask +#undef Point +#undef Cursor +#endif +#include <libc.h> +#undef time +AUTOLIB(draw) /* to cause link of X11 */ + +enum { + SnarfSize = 65536, +}; +char snarf[3*SnarfSize+1]; +Rune rsnarf[SnarfSize+1]; +XDisplay *xdisplay; +XWindow drawable; +Atom xclipboard; +Atom xutf8string; +Atom xtargets; +Atom xtext; +Atom xcompoundtext; + +void xselectionrequest(XEvent*); +char *xgetsnarf(void); +void appleputsnarf(void); +void xputsnarf(void); + +int verbose; + +#ifdef __APPLE__ +PasteboardRef appleclip; +#endif + +void +usage(void) +{ + fprint(2, "usage: snarfer [-v]\n"); + exits("usage"); +} + +void +main(int argc, char **argv) +{ + XEvent xevent; + + ARGBEGIN{ + default: + usage(); + case 'v': + verbose = 1; + break; + }ARGEND + + if((xdisplay = XOpenDisplay(nil)) == nil) + sysfatal("XOpenDisplay: %r"); + drawable = XCreateWindow(xdisplay, DefaultRootWindow(xdisplay), + 0, 0, 1, 1, 0, 0, + InputOutput, DefaultVisual(xdisplay, DefaultScreen(xdisplay)), + 0, 0); + if(drawable == None) + sysfatal("XCreateWindow: %r"); + XFlush(xdisplay); + + xclipboard = XInternAtom(xdisplay, "CLIPBOARD", False); + xutf8string = XInternAtom(xdisplay, "UTF8_STRING", False); + xtargets = XInternAtom(xdisplay, "TARGETS", False); + xtext = XInternAtom(xdisplay, "TEXT", False); + xcompoundtext = XInternAtom(xdisplay, "COMPOUND_TEXT", False); + +#ifdef __APPLE__ + if(PasteboardCreate(kPasteboardClipboard, &appleclip) != noErr) + sysfatal("pasteboard create failed"); +#endif + + xgetsnarf(); + appleputsnarf(); + xputsnarf(); + + for(;;){ + XNextEvent(xdisplay, &xevent); + switch(xevent.type){ + case DestroyNotify: + exits(0); + case SelectionClear: + xgetsnarf(); + appleputsnarf(); + xputsnarf(); + if(verbose) + print("snarf{%s}\n", snarf); + break; + case SelectionRequest: + xselectionrequest(&xevent); + break; + } + } +} + +void +xselectionrequest(XEvent *e) +{ + char *name; + Atom a[4]; + XEvent r; + XSelectionRequestEvent *xe; + XDisplay *xd; + + xd = xdisplay; + + memset(&r, 0, sizeof r); + xe = (XSelectionRequestEvent*)e; +if(0) fprint(2, "xselect target=%d requestor=%d property=%d selection=%d\n", + xe->target, xe->requestor, xe->property, xe->selection); + r.xselection.property = xe->property; + if(xe->target == xtargets){ + a[0] = XA_STRING; + a[1] = xutf8string; + a[2] = xtext; + a[3] = xcompoundtext; + + XChangeProperty(xd, xe->requestor, xe->property, xe->target, + 8, PropModeReplace, (uchar*)a, sizeof a); + }else if(xe->target == XA_STRING || xe->target == xutf8string || xe->target == xtext || xe->target == xcompoundtext){ + /* if the target is STRING we're supposed to reply with Latin1 XXX */ + XChangeProperty(xd, xe->requestor, xe->property, xe->target, + 8, PropModeReplace, (uchar*)snarf, strlen(snarf)); + }else{ + name = XGetAtomName(xd, xe->target); + if(strcmp(name, "TIMESTAMP") != 0) + fprint(2, "%s: cannot handle selection request for '%s' (%d)\n", argv0, name, (int)xe->target); + r.xselection.property = None; + } + + r.xselection.display = xe->display; + /* r.xselection.property filled above */ + r.xselection.target = xe->target; + r.xselection.type = SelectionNotify; + r.xselection.requestor = xe->requestor; + r.xselection.time = xe->time; + r.xselection.send_event = True; + r.xselection.selection = xe->selection; + XSendEvent(xd, xe->requestor, False, 0, &r); + XFlush(xd); +} + +char* +xgetsnarf(void) +{ + uchar *data, *xdata; + Atom clipboard, type, prop; + ulong len, lastlen, dummy; + int fmt, i; + XWindow w; + XDisplay *xd; + + xd = xdisplay; + + /* + * Is there a primary selection (highlighted text in an xterm)? + */ + clipboard = XA_PRIMARY; + w = XGetSelectionOwner(xd, XA_PRIMARY); + if(w == drawable) + return snarf; + + /* + * If not, is there a clipboard selection? + */ + if(w == None && xclipboard != None){ + clipboard = xclipboard; + w = XGetSelectionOwner(xd, xclipboard); + if(w == drawable) + return snarf; + } + + /* + * If not, give up. + */ + if(w == None) + return nil; + + /* + * We should be waiting for SelectionNotify here, but it might never + * come, and we have no way to time out. Instead, we will clear + * local property #1, request our buddy to fill it in for us, and poll + * until he's done or we get tired of waiting. + * + * We should try to go for _x.utf8string instead of XA_STRING, + * but that would add to the polling. + */ + prop = 1; + XChangeProperty(xd, drawable, prop, XA_STRING, 8, PropModeReplace, (uchar*)"", 0); + XConvertSelection(xd, clipboard, XA_STRING, prop, drawable, CurrentTime); + XFlush(xd); + lastlen = 0; + for(i=0; i<10 || (lastlen!=0 && i<30); i++){ + sleep(100); + XGetWindowProperty(xd, drawable, prop, 0, 0, 0, AnyPropertyType, + &type, &fmt, &dummy, &len, &data); + if(lastlen == len && len > 0) + break; + lastlen = len; + } + if(i == 10) + return nil; + /* get the property */ + data = nil; + XGetWindowProperty(xd, drawable, prop, 0, SnarfSize/sizeof(ulong), 0, + AnyPropertyType, &type, &fmt, &len, &dummy, &xdata); + if(xdata == nil || (type != XA_STRING && type != xutf8string) || len == 0){ + if(xdata) + XFree(xdata); + return nil; + } + if(strlen((char*)xdata) >= SnarfSize){ + XFree(xdata); + return nil; + } + strcpy(snarf, (char*)xdata); + return snarf; +} + +void +xputsnarf(void) +{ + XSetSelectionOwner(xdisplay, XA_PRIMARY, drawable, CurrentTime); + if(xclipboard != None) + XSetSelectionOwner(xdisplay, xclipboard, drawable, CurrentTime); + XFlush(xdisplay); +} + +void +appleputsnarf(void) +{ +#ifdef __APPLE__ + CFDataRef cfdata; + PasteboardSyncFlags flags; + + runesnprint(rsnarf, nelem(rsnarf), "%s", snarf); + if(PasteboardClear(appleclip) != noErr){ + fprint(2, "apple pasteboard clear failed\n"); + return; + } + flags = PasteboardSynchronize(appleclip); + if((flags&kPasteboardModified) || !(flags&kPasteboardClientIsOwner)){ + fprint(2, "apple pasteboard cannot assert ownership\n"); + return; + } + cfdata = CFDataCreate(kCFAllocatorDefault, + (uchar*)rsnarf, runestrlen(rsnarf)*2); + if(cfdata == nil){ + fprint(2, "apple pasteboard cfdatacreate failed\n"); + return; + } + if(PasteboardPutItemFlavor(appleclip, (PasteboardItemID)1, + CFSTR("public.utf16-plain-text"), cfdata, 0) != noErr){ + fprint(2, "apple pasteboard putitem failed\n"); + CFRelease(cfdata); + return; + } + CFRelease(cfdata); +#endif +} + + |