Blob


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 */
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 */
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;
70 void xselectionrequest(XEvent*);
71 char *xgetsnarf(void);
72 void appleputsnarf(void);
73 void xputsnarf(void);
75 int verbose;
77 #ifdef __APPLE__
78 PasteboardRef appleclip;
79 #endif
81 void
82 usage(void)
83 {
84 fprint(2, "usage: snarfer [-v]\n");
85 exits("usage");
86 }
88 void
89 main(int argc, char **argv)
90 {
91 XEvent xevent;
93 ARGBEGIN{
94 default:
95 usage();
96 case 'v':
97 verbose = 1;
98 break;
99 }ARGEND
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);
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);
117 #ifdef __APPLE__
118 if(PasteboardCreate(kPasteboardClipboard, &appleclip) != noErr)
119 sysfatal("pasteboard create failed");
120 #endif
122 xgetsnarf();
123 appleputsnarf();
124 xputsnarf();
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;
145 void
146 xselectionrequest(XEvent *e)
148 char *name;
149 Atom a[4];
150 XEvent r;
151 XSelectionRequestEvent *xe;
152 XDisplay *xd;
154 xd = xdisplay;
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;
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;
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);
192 char*
193 xgetsnarf(void)
195 uchar *data, *xdata;
196 Atom clipboard, type, prop;
197 ulong len, lastlen, dummy;
198 int fmt, i;
199 XWindow w;
200 XDisplay *xd;
202 xd = xdisplay;
204 w = None;
205 clipboard = None;
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;
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;
227 /*
228 * If not, give up.
229 */
230 if(w == None)
231 return nil;
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.
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;
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;
266 if(strlen((char*)xdata) >= SnarfSize){
267 XFree(xdata);
268 return nil;
270 strcpy(snarf, (char*)xdata);
271 return snarf;
274 void
275 xputsnarf(void)
277 if(0) XSetSelectionOwner(xdisplay, XA_PRIMARY, drawable, CurrentTime);
278 if(xclipboard != None)
279 XSetSelectionOwner(xdisplay, xclipboard, drawable, CurrentTime);
280 XFlush(xdisplay);
283 void
284 appleputsnarf(void)
286 #ifdef __APPLE__
287 CFDataRef cfdata;
288 PasteboardSyncFlags flags;
290 runesnprint(rsnarf, nelem(rsnarf), "%s", snarf);
291 if(PasteboardClear(appleclip) != noErr){
292 fprint(2, "apple pasteboard clear failed\n");
293 return;
295 flags = PasteboardSynchronize(appleclip);
296 if((flags&kPasteboardModified) || !(flags&kPasteboardClientIsOwner)){
297 fprint(2, "apple pasteboard cannot assert ownership\n");
298 return;
300 cfdata = CFDataCreate(kCFAllocatorDefault,
301 (uchar*)rsnarf, runestrlen(rsnarf)*2);
302 if(cfdata == nil){
303 fprint(2, "apple pasteboard cfdatacreate failed\n");
304 return;
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;
312 CFRelease(cfdata);
313 #endif