| t@@ -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
+#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;
+
+ /*
+ * 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
+}
+
+ |