URI: 
       tsnarfer.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tsnarfer.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 }