plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

snarfer.c (7227B)


      1 /*
      2  * This program is only intended for OS X, but the
      3  * ifdef __APPLE__ below lets us build it on all systems.
      4  * On non-OS X systems, you can use it to hold the snarf
      5  * buffer around after a program exits.
      6  */
      7 
      8 #include <u.h>
      9 #define Colormap	XColormap
     10 #define Cursor		XCursor
     11 #define Display		XDisplay
     12 #define Drawable	XDrawable
     13 #define Font		XFont
     14 #define GC		XGC
     15 #define Point		XPoint
     16 #define Rectangle	XRectangle
     17 #define Screen		XScreen
     18 #define Visual		XVisual
     19 #define Window		XWindow
     20 #include <X11/Xlib.h>
     21 #include <X11/Xatom.h>
     22 #include <X11/Xutil.h>
     23 #include <X11/keysym.h>
     24 #include <X11/IntrinsicP.h>
     25 #include <X11/StringDefs.h>
     26 #undef Colormap
     27 #undef Cursor
     28 #undef Display
     29 #undef Drawable
     30 #undef Font
     31 #undef GC
     32 #undef Point
     33 #undef Rectangle
     34 #undef Screen
     35 #undef Visual
     36 #undef Window
     37 AUTOLIB(X11);
     38 #ifdef __APPLE__
     39 #define APPLESNARF
     40 #define Boolean AppleBoolean
     41 #define Rect AppleRect
     42 #define EventMask AppleEventMask
     43 #define Point ApplePoint
     44 #define Cursor AppleCursor
     45 #include <Carbon/Carbon.h>
     46 AUTOFRAMEWORK(Carbon)
     47 #undef Boolean
     48 #undef Rect
     49 #undef EventMask
     50 #undef Point
     51 #undef Cursor
     52 #endif
     53 #include <libc.h>
     54 #undef time
     55 AUTOLIB(draw)	/* to cause link of X11 */
     56 
     57 enum {
     58 	SnarfSize = 65536
     59 };
     60 char snarf[3*SnarfSize+1];
     61 Rune rsnarf[SnarfSize+1];
     62 XDisplay *xdisplay;
     63 XWindow drawable;
     64 Atom xclipboard;
     65 Atom xutf8string;
     66 Atom xtargets;
     67 Atom xtext;
     68 Atom xcompoundtext;
     69 
     70 void xselectionrequest(XEvent*);
     71 char *xgetsnarf(void);
     72 void appleputsnarf(void);
     73 void xputsnarf(void);
     74 
     75 int verbose;
     76 
     77 #ifdef __APPLE__
     78 PasteboardRef appleclip;
     79 #endif
     80 
     81 void
     82 usage(void)
     83 {
     84 	fprint(2, "usage: snarfer [-v]\n");
     85 	exits("usage");
     86 }
     87 
     88 void
     89 main(int argc, char **argv)
     90 {
     91 	XEvent xevent;
     92 
     93 	ARGBEGIN{
     94 	default:
     95 		usage();
     96 	case 'v':
     97 		verbose = 1;
     98 		break;
     99 	}ARGEND
    100 
    101 	if((xdisplay = XOpenDisplay(nil)) == nil)
    102 		sysfatal("XOpenDisplay: %r");
    103 	drawable = XCreateWindow(xdisplay, DefaultRootWindow(xdisplay),
    104 		0, 0, 1, 1, 0, 0,
    105 		InputOutput, DefaultVisual(xdisplay, DefaultScreen(xdisplay)),
    106 		0, 0);
    107 	if(drawable == None)
    108 		sysfatal("XCreateWindow: %r");
    109 	XFlush(xdisplay);
    110 
    111 	xclipboard = XInternAtom(xdisplay, "CLIPBOARD", False);
    112 	xutf8string = XInternAtom(xdisplay, "UTF8_STRING", False);
    113 	xtargets = XInternAtom(xdisplay, "TARGETS", False);
    114 	xtext = XInternAtom(xdisplay, "TEXT", False);
    115 	xcompoundtext = XInternAtom(xdisplay, "COMPOUND_TEXT", False);
    116 
    117 #ifdef __APPLE__
    118 	if(PasteboardCreate(kPasteboardClipboard, &appleclip) != noErr)
    119 		sysfatal("pasteboard create failed");
    120 #endif
    121 
    122 	xgetsnarf();
    123 	appleputsnarf();
    124 	xputsnarf();
    125 
    126 	for(;;){
    127 		XNextEvent(xdisplay, &xevent);
    128 		switch(xevent.type){
    129 		case DestroyNotify:
    130 			exits(0);
    131 		case SelectionClear:
    132 			xgetsnarf();
    133 			appleputsnarf();
    134 			xputsnarf();
    135 			if(verbose)
    136 				print("snarf{%s}\n", snarf);
    137 			break;
    138 		case SelectionRequest:
    139 			xselectionrequest(&xevent);
    140 			break;
    141 		}
    142 	}
    143 }
    144 
    145 void
    146 xselectionrequest(XEvent *e)
    147 {
    148 	char *name;
    149 	Atom a[4];
    150 	XEvent r;
    151 	XSelectionRequestEvent *xe;
    152 	XDisplay *xd;
    153 
    154 	xd = xdisplay;
    155 
    156 	memset(&r, 0, sizeof r);
    157 	xe = (XSelectionRequestEvent*)e;
    158 if(0) fprint(2, "xselect target=%d requestor=%d property=%d selection=%d\n",
    159 	xe->target, xe->requestor, xe->property, xe->selection);
    160 	r.xselection.property = xe->property;
    161 	if(xe->target == xtargets){
    162 		a[0] = XA_STRING;
    163 		a[1] = xutf8string;
    164 		a[2] = xtext;
    165 		a[3] = xcompoundtext;
    166 
    167 		XChangeProperty(xd, xe->requestor, xe->property, xe->target,
    168 			8, PropModeReplace, (uchar*)a, sizeof a);
    169 	}else if(xe->target == XA_STRING || xe->target == xutf8string || xe->target == xtext || xe->target == xcompoundtext){
    170 		/* if the target is STRING we're supposed to reply with Latin1 XXX */
    171 		XChangeProperty(xd, xe->requestor, xe->property, xe->target,
    172 			8, PropModeReplace, (uchar*)snarf, strlen(snarf));
    173 	}else{
    174 		name = XGetAtomName(xd, xe->target);
    175 		if(strcmp(name, "TIMESTAMP") != 0)
    176 			fprint(2, "%s: cannot handle selection request for '%s' (%d)\n", argv0, name, (int)xe->target);
    177 		r.xselection.property = None;
    178 	}
    179 
    180 	r.xselection.display = xe->display;
    181 	/* r.xselection.property filled above */
    182 	r.xselection.target = xe->target;
    183 	r.xselection.type = SelectionNotify;
    184 	r.xselection.requestor = xe->requestor;
    185 	r.xselection.time = xe->time;
    186 	r.xselection.send_event = True;
    187 	r.xselection.selection = xe->selection;
    188 	XSendEvent(xd, xe->requestor, False, 0, &r);
    189 	XFlush(xd);
    190 }
    191 
    192 char*
    193 xgetsnarf(void)
    194 {
    195 	uchar *data, *xdata;
    196 	Atom clipboard, type, prop;
    197 	ulong len, lastlen, dummy;
    198 	int fmt, i;
    199 	XWindow w;
    200 	XDisplay *xd;
    201 
    202 	xd = xdisplay;
    203 
    204 	w = None;
    205 	clipboard = None;
    206 
    207 	/*
    208 	 * Is there a primary selection (highlighted text in an xterm)?
    209 	 */
    210 	if(0){
    211 		clipboard = XA_PRIMARY;
    212 		w = XGetSelectionOwner(xd, XA_PRIMARY);
    213 		if(w == drawable)
    214 			return snarf;
    215 	}
    216 
    217 	/*
    218 	 * If not, is there a clipboard selection?
    219 	 */
    220 	if(w == None && xclipboard != None){
    221 		clipboard = xclipboard;
    222 		w = XGetSelectionOwner(xd, xclipboard);
    223 		if(w == drawable)
    224 			return snarf;
    225 	}
    226 
    227 	/*
    228 	 * If not, give up.
    229 	 */
    230 	if(w == None)
    231 		return nil;
    232 
    233 	/*
    234 	 * We should be waiting for SelectionNotify here, but it might never
    235 	 * come, and we have no way to time out.  Instead, we will clear
    236 	 * local property #1, request our buddy to fill it in for us, and poll
    237 	 * until he's done or we get tired of waiting.
    238 	 *
    239 	 * We should try to go for _x.utf8string instead of XA_STRING,
    240 	 * but that would add to the polling.
    241 	 */
    242 	prop = 1;
    243 	XChangeProperty(xd, drawable, prop, XA_STRING, 8, PropModeReplace, (uchar*)"", 0);
    244 	XConvertSelection(xd, clipboard, XA_STRING, prop, drawable, CurrentTime);
    245 	XFlush(xd);
    246 	lastlen = 0;
    247 	for(i=0; i<10 || (lastlen!=0 && i<30); i++){
    248 		sleep(100);
    249 		XGetWindowProperty(xd, drawable, prop, 0, 0, 0, AnyPropertyType,
    250 			&type, &fmt, &dummy, &len, &data);
    251 		if(lastlen == len && len > 0)
    252 			break;
    253 		lastlen = len;
    254 	}
    255 	if(i == 10)
    256 		return nil;
    257 	/* get the property */
    258 	data = nil;
    259 	XGetWindowProperty(xd, drawable, prop, 0, SnarfSize/sizeof(ulong), 0,
    260 		AnyPropertyType, &type, &fmt, &len, &dummy, &xdata);
    261 	if(xdata == nil || (type != XA_STRING && type != xutf8string) || len == 0){
    262 		if(xdata)
    263 			XFree(xdata);
    264 		return nil;
    265 	}
    266 	if(strlen((char*)xdata) >= SnarfSize){
    267 		XFree(xdata);
    268 		return nil;
    269 	}
    270 	strcpy(snarf, (char*)xdata);
    271 	return snarf;
    272 }
    273 
    274 void
    275 xputsnarf(void)
    276 {
    277 	if(0) XSetSelectionOwner(xdisplay, XA_PRIMARY, drawable, CurrentTime);
    278 	if(xclipboard != None)
    279 		XSetSelectionOwner(xdisplay, xclipboard, drawable, CurrentTime);
    280 	XFlush(xdisplay);
    281 }
    282 
    283 void
    284 appleputsnarf(void)
    285 {
    286 #ifdef __APPLE__
    287 	CFDataRef cfdata;
    288 	PasteboardSyncFlags flags;
    289 
    290 	runesnprint(rsnarf, nelem(rsnarf), "%s", snarf);
    291 	if(PasteboardClear(appleclip) != noErr){
    292 		fprint(2, "apple pasteboard clear failed\n");
    293 		return;
    294 	}
    295 	flags = PasteboardSynchronize(appleclip);
    296 	if((flags&kPasteboardModified) || !(flags&kPasteboardClientIsOwner)){
    297 		fprint(2, "apple pasteboard cannot assert ownership\n");
    298 		return;
    299 	}
    300 	cfdata = CFDataCreate(kCFAllocatorDefault,
    301 		(uchar*)rsnarf, runestrlen(rsnarf)*2);
    302 	if(cfdata == nil){
    303 		fprint(2, "apple pasteboard cfdatacreate failed\n");
    304 		return;
    305 	}
    306 	if(PasteboardPutItemFlavor(appleclip, (PasteboardItemID)1,
    307 		CFSTR("public.utf16-plain-text"), cfdata, 0) != noErr){
    308 		fprint(2, "apple pasteboard putitem failed\n");
    309 		CFRelease(cfdata);
    310 		return;
    311 	}
    312 	CFRelease(cfdata);
    313 #endif
    314 }