/* * 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 #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 #include #include #include #include #include #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 AUTOFRAMEWORK(Carbon) #undef Boolean #undef Rect #undef EventMask #undef Point #undef Cursor #endif #include #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; w = None; clipboard = None; /* * Is there a primary selection (highlighted text in an xterm)? */ if(0){ 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 }