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 #ifdef __APPLE__
38 #define APPLESNARF
39 #define Boolean AppleBoolean
40 #define Rect AppleRect
41 #define EventMask AppleEventMask
42 #define Point ApplePoint
43 #define Cursor AppleCursor
44 #include <Carbon/Carbon.h>
45 AUTOFRAMEWORK(Carbon)
46 #undef Boolean
47 #undef Rect
48 #undef EventMask
49 #undef Point
50 #undef Cursor
51 #endif
52 #include <libc.h>
53 #undef time
54 AUTOLIB(draw) /* to cause link of X11 */
56 enum {
57 SnarfSize = 65536
58 };
59 char snarf[3*SnarfSize+1];
60 Rune rsnarf[SnarfSize+1];
61 XDisplay *xdisplay;
62 XWindow drawable;
63 Atom xclipboard;
64 Atom xutf8string;
65 Atom xtargets;
66 Atom xtext;
67 Atom xcompoundtext;
69 void xselectionrequest(XEvent*);
70 char *xgetsnarf(void);
71 void appleputsnarf(void);
72 void xputsnarf(void);
74 int verbose;
76 #ifdef __APPLE__
77 PasteboardRef appleclip;
78 #endif
80 void
81 usage(void)
82 {
83 fprint(2, "usage: snarfer [-v]\n");
84 exits("usage");
85 }
87 void
88 main(int argc, char **argv)
89 {
90 XEvent xevent;
92 ARGBEGIN{
93 default:
94 usage();
95 case 'v':
96 verbose = 1;
97 break;
98 }ARGEND
100 if((xdisplay = XOpenDisplay(nil)) == nil)
101 sysfatal("XOpenDisplay: %r");
102 drawable = XCreateWindow(xdisplay, DefaultRootWindow(xdisplay),
103 0, 0, 1, 1, 0, 0,
104 InputOutput, DefaultVisual(xdisplay, DefaultScreen(xdisplay)),
105 0, 0);
106 if(drawable == None)
107 sysfatal("XCreateWindow: %r");
108 XFlush(xdisplay);
110 xclipboard = XInternAtom(xdisplay, "CLIPBOARD", False);
111 xutf8string = XInternAtom(xdisplay, "UTF8_STRING", False);
112 xtargets = XInternAtom(xdisplay, "TARGETS", False);
113 xtext = XInternAtom(xdisplay, "TEXT", False);
114 xcompoundtext = XInternAtom(xdisplay, "COMPOUND_TEXT", False);
116 #ifdef __APPLE__
117 if(PasteboardCreate(kPasteboardClipboard, &appleclip) != noErr)
118 sysfatal("pasteboard create failed");
119 #endif
121 xgetsnarf();
122 appleputsnarf();
123 xputsnarf();
125 for(;;){
126 XNextEvent(xdisplay, &xevent);
127 switch(xevent.type){
128 case DestroyNotify:
129 exits(0);
130 case SelectionClear:
131 xgetsnarf();
132 appleputsnarf();
133 xputsnarf();
134 if(verbose)
135 print("snarf{%s}\n", snarf);
136 break;
137 case SelectionRequest:
138 xselectionrequest(&xevent);
139 break;
144 void
145 xselectionrequest(XEvent *e)
147 char *name;
148 Atom a[4];
149 XEvent r;
150 XSelectionRequestEvent *xe;
151 XDisplay *xd;
153 xd = xdisplay;
155 memset(&r, 0, sizeof r);
156 xe = (XSelectionRequestEvent*)e;
157 if(0) fprint(2, "xselect target=%d requestor=%d property=%d selection=%d\n",
158 xe->target, xe->requestor, xe->property, xe->selection);
159 r.xselection.property = xe->property;
160 if(xe->target == xtargets){
161 a[0] = XA_STRING;
162 a[1] = xutf8string;
163 a[2] = xtext;
164 a[3] = xcompoundtext;
166 XChangeProperty(xd, xe->requestor, xe->property, xe->target,
167 8, PropModeReplace, (uchar*)a, sizeof a);
168 }else if(xe->target == XA_STRING || xe->target == xutf8string || xe->target == xtext || xe->target == xcompoundtext){
169 /* if the target is STRING we're supposed to reply with Latin1 XXX */
170 XChangeProperty(xd, xe->requestor, xe->property, xe->target,
171 8, PropModeReplace, (uchar*)snarf, strlen(snarf));
172 }else{
173 name = XGetAtomName(xd, xe->target);
174 if(strcmp(name, "TIMESTAMP") != 0)
175 fprint(2, "%s: cannot handle selection request for '%s' (%d)\n", argv0, name, (int)xe->target);
176 r.xselection.property = None;
179 r.xselection.display = xe->display;
180 /* r.xselection.property filled above */
181 r.xselection.target = xe->target;
182 r.xselection.type = SelectionNotify;
183 r.xselection.requestor = xe->requestor;
184 r.xselection.time = xe->time;
185 r.xselection.send_event = True;
186 r.xselection.selection = xe->selection;
187 XSendEvent(xd, xe->requestor, False, 0, &r);
188 XFlush(xd);
191 char*
192 xgetsnarf(void)
194 uchar *data, *xdata;
195 Atom clipboard, type, prop;
196 ulong len, lastlen, dummy;
197 int fmt, i;
198 XWindow w;
199 XDisplay *xd;
201 xd = xdisplay;
203 w = None;
204 clipboard = None;
206 /*
207 * Is there a primary selection (highlighted text in an xterm)?
208 */
209 if(0){
210 clipboard = XA_PRIMARY;
211 w = XGetSelectionOwner(xd, XA_PRIMARY);
212 if(w == drawable)
213 return snarf;
216 /*
217 * If not, is there a clipboard selection?
218 */
219 if(w == None && xclipboard != None){
220 clipboard = xclipboard;
221 w = XGetSelectionOwner(xd, xclipboard);
222 if(w == drawable)
223 return snarf;
226 /*
227 * If not, give up.
228 */
229 if(w == None)
230 return nil;
232 /*
233 * We should be waiting for SelectionNotify here, but it might never
234 * come, and we have no way to time out. Instead, we will clear
235 * local property #1, request our buddy to fill it in for us, and poll
236 * until he's done or we get tired of waiting.
238 * We should try to go for _x.utf8string instead of XA_STRING,
239 * but that would add to the polling.
240 */
241 prop = 1;
242 XChangeProperty(xd, drawable, prop, XA_STRING, 8, PropModeReplace, (uchar*)"", 0);
243 XConvertSelection(xd, clipboard, XA_STRING, prop, drawable, CurrentTime);
244 XFlush(xd);
245 lastlen = 0;
246 for(i=0; i<10 || (lastlen!=0 && i<30); i++){
247 sleep(100);
248 XGetWindowProperty(xd, drawable, prop, 0, 0, 0, AnyPropertyType,
249 &type, &fmt, &dummy, &len, &data);
250 if(lastlen == len && len > 0)
251 break;
252 lastlen = len;
254 if(i == 10)
255 return nil;
256 /* get the property */
257 data = nil;
258 XGetWindowProperty(xd, drawable, prop, 0, SnarfSize/sizeof(ulong), 0,
259 AnyPropertyType, &type, &fmt, &len, &dummy, &xdata);
260 if(xdata == nil || (type != XA_STRING && type != xutf8string) || len == 0){
261 if(xdata)
262 XFree(xdata);
263 return nil;
265 if(strlen((char*)xdata) >= SnarfSize){
266 XFree(xdata);
267 return nil;
269 strcpy(snarf, (char*)xdata);
270 return snarf;
273 void
274 xputsnarf(void)
276 if(0) XSetSelectionOwner(xdisplay, XA_PRIMARY, drawable, CurrentTime);
277 if(xclipboard != None)
278 XSetSelectionOwner(xdisplay, xclipboard, drawable, CurrentTime);
279 XFlush(xdisplay);
282 void
283 appleputsnarf(void)
285 #ifdef __APPLE__
286 CFDataRef cfdata;
287 PasteboardSyncFlags flags;
289 runesnprint(rsnarf, nelem(rsnarf), "%s", snarf);
290 if(PasteboardClear(appleclip) != noErr){
291 fprint(2, "apple pasteboard clear failed\n");
292 return;
294 flags = PasteboardSynchronize(appleclip);
295 if((flags&kPasteboardModified) || !(flags&kPasteboardClientIsOwner)){
296 fprint(2, "apple pasteboard cannot assert ownership\n");
297 return;
299 cfdata = CFDataCreate(kCFAllocatorDefault,
300 (uchar*)rsnarf, runestrlen(rsnarf)*2);
301 if(cfdata == nil){
302 fprint(2, "apple pasteboard cfdatacreate failed\n");
303 return;
305 if(PasteboardPutItemFlavor(appleclip, (PasteboardItemID)1,
306 CFSTR("public.utf16-plain-text"), cfdata, 0) != noErr){
307 fprint(2, "apple pasteboard putitem failed\n");
308 CFRelease(cfdata);
309 return;
311 CFRelease(cfdata);
312 #endif