URI: 
       tx11-screen.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
       ---
       tx11-screen.c (39053B)
       ---
            1 #include <u.h>
            2 #include "x11-inc.h"
            3 #include "x11-keysym2ucs.h"
            4 #include <errno.h>
            5 #include <libc.h>
            6 #include <draw.h>
            7 #include <memdraw.h>
            8 #include <memlayer.h>
            9 #include <keyboard.h>
           10 #include <mouse.h>
           11 #include <cursor.h>
           12 #include <thread.h>
           13 #include "x11-memdraw.h"
           14 #include "devdraw.h"
           15 
           16 #undef time
           17 
           18 static void        plan9cmap(void);
           19 static int        setupcmap(XWindow);
           20 static XGC        xgc(XDrawable, int, int);
           21 #define Mask MouseMask|ExposureMask|StructureNotifyMask|KeyPressMask|KeyReleaseMask|EnterWindowMask|LeaveWindowMask|FocusChangeMask
           22 
           23 #define MouseMask (\
           24         ButtonPressMask|\
           25         ButtonReleaseMask|\
           26         PointerMotionMask|\
           27         Button1MotionMask|\
           28         Button2MotionMask|\
           29         Button3MotionMask)
           30 
           31 Xprivate _x;
           32 
           33 static void runxevent(XEvent *xev);
           34 static int        _xconfigure(Xwin *w, XEvent *e);
           35 static int        _xdestroy(Xwin *w, XEvent *e);
           36 static void        _xexpose(Xwin *w, XEvent *e);
           37 static int _xreplacescreenimage(Client *client);
           38 static int _xtoplan9mouse(Xwin *w, XEvent *e, Mouse *m);
           39 static void _xmovewindow(Xwin *w, Rectangle r);
           40 static int _xtoplan9kbd(XEvent *e);
           41 static int _xselect(XEvent *e);
           42 
           43 static void        rpc_resizeimg(Client*);
           44 static void        rpc_resizewindow(Client*, Rectangle);
           45 static void        rpc_setcursor(Client*, Cursor*, Cursor2*);
           46 static void        rpc_setlabel(Client*, char*);
           47 static void        rpc_setmouse(Client*, Point);
           48 static void        rpc_topwin(Client*);
           49 static void        rpc_bouncemouse(Client*, Mouse);
           50 static void        rpc_flush(Client*, Rectangle);
           51 
           52 static ClientImpl x11impl = {
           53         rpc_resizeimg,
           54         rpc_resizewindow,
           55         rpc_setcursor,
           56         rpc_setlabel,
           57         rpc_setmouse,
           58         rpc_topwin,
           59         rpc_bouncemouse,
           60         rpc_flush
           61 };
           62 
           63 static Xwin*
           64 newxwin(Client *c)
           65 {
           66         Xwin *w;
           67 
           68         w = mallocz(sizeof *w, 1);
           69         if(w == nil)
           70                 sysfatal("out of memory");
           71         w->client = c;
           72         w->next = _x.windows;
           73         _x.windows = w;
           74         c->impl = &x11impl;
           75         c->view = w;
           76         return w;
           77 }
           78 
           79 static Xwin*
           80 findxwin(XDrawable d)
           81 {
           82         Xwin *w, **l;
           83 
           84         for(l=&_x.windows; (w=*l) != nil; l=&w->next) {
           85                 if(w->drawable == d) {
           86                         /* move to front */
           87                         *l = w->next;
           88                         w->next = _x.windows;
           89                         _x.windows = w;
           90                         return w;
           91                 }
           92         }
           93         return nil;
           94 }
           95 
           96 static int
           97 xerror(XDisplay *d, XErrorEvent *e)
           98 {
           99         char buf[200];
          100 
          101         if(e->request_code == 42) /* XSetInputFocus */
          102                 return 0;
          103         if(e->request_code == 18) /* XChangeProperty */
          104                 return 0;
          105         /*
          106          * BadDrawable happens in apps that get resized a LOT,
          107          * e.g. when KDE is configured to resize continuously
          108          * during a window drag.
          109          */
          110         if(e->error_code == 9) /* BadDrawable */
          111                 return 0;
          112 
          113         fprint(2, "X error: error_code=%d, request_code=%d, minor=%d disp=%p\n",
          114                 e->error_code, e->request_code, e->minor_code, d);
          115         XGetErrorText(d, e->error_code, buf, sizeof buf);
          116         fprint(2, "%s\n", buf);
          117         return 0;
          118 }
          119 
          120 static int
          121 xioerror(XDisplay *d)
          122 {
          123         /*print("X I/O error\n"); */
          124         exit(0);
          125         /*sysfatal("X I/O error\n");*/
          126         abort();
          127         return -1;
          128 }
          129 
          130 static void xloop(void);
          131 
          132 static QLock xlk;
          133 
          134 void
          135 xlock(void)
          136 {
          137         qlock(&xlk);
          138 }
          139 
          140 void
          141 xunlock(void)
          142 {
          143         qunlock(&xlk);
          144 }
          145 
          146 void
          147 gfx_main(void)
          148 {
          149         char *disp;
          150         int i, n, xrootid;
          151         XPixmapFormatValues *pfmt;
          152         XScreen *xscreen;
          153         XVisualInfo xvi;
          154         XWindow xrootwin;
          155 
          156         /*
          157         if(XInitThreads() == 0)
          158                 sysfatal("XInitThread: %r");
          159         */
          160 
          161         /*
          162          * Connect to X server.
          163          */
          164         _x.display = XOpenDisplay(NULL);
          165         if(_x.display == nil){
          166                 disp = getenv("DISPLAY");
          167                 werrstr("XOpenDisplay %s: %r", disp ? disp : ":0");
          168                 free(disp);
          169                 sysfatal("%r");
          170         }
          171         _x.fd = ConnectionNumber(_x.display);
          172         XSetErrorHandler(xerror);
          173         XSetIOErrorHandler(xioerror);
          174         xrootid = DefaultScreen(_x.display);
          175         xrootwin = DefaultRootWindow(_x.display);
          176 
          177         /*
          178          * Figure out underlying screen format.
          179          */
          180         if(XMatchVisualInfo(_x.display, xrootid, 24, TrueColor, &xvi)
          181         || XMatchVisualInfo(_x.display, xrootid, 24, DirectColor, &xvi)){
          182                 _x.vis = xvi.visual;
          183                 _x.depth = 24;
          184         }
          185         else
          186         if(XMatchVisualInfo(_x.display, xrootid, 16, TrueColor, &xvi)
          187         || XMatchVisualInfo(_x.display, xrootid, 16, DirectColor, &xvi)){
          188                 _x.vis = xvi.visual;
          189                 _x.depth = 16;
          190         }
          191         else
          192         if(XMatchVisualInfo(_x.display, xrootid, 15, TrueColor, &xvi)
          193         || XMatchVisualInfo(_x.display, xrootid, 15, DirectColor, &xvi)){
          194                 _x.vis = xvi.visual;
          195                 _x.depth = 15;
          196         }
          197         else
          198         if(XMatchVisualInfo(_x.display, xrootid, 8, PseudoColor, &xvi)
          199         || XMatchVisualInfo(_x.display, xrootid, 8, StaticColor, &xvi)){
          200                 if(_x.depth > 8){
          201                         werrstr("can't deal with colormapped depth %d screens",
          202                                 _x.depth);
          203                         goto err0;
          204                 }
          205                 _x.vis = xvi.visual;
          206                 _x.depth = 8;
          207         }
          208         else{
          209                 _x.depth = DefaultDepth(_x.display, xrootid);
          210                 if(_x.depth != 8){
          211                         werrstr("can't understand depth %d screen", _x.depth);
          212                         goto err0;
          213                 }
          214                 _x.vis = DefaultVisual(_x.display, xrootid);
          215         }
          216 
          217         if(DefaultDepth(_x.display, xrootid) == _x.depth)
          218                 _x.usetable = 1;
          219 
          220         /*
          221          * _x.depth is only the number of significant pixel bits,
          222          * not the total number of pixel bits.  We need to walk the
          223          * display list to find how many actual bits are used
          224          * per pixel.
          225          */
          226         _x.chan = 0;
          227         pfmt = XListPixmapFormats(_x.display, &n);
          228         for(i=0; i<n; i++){
          229                 if(pfmt[i].depth == _x.depth){
          230                         switch(pfmt[i].bits_per_pixel){
          231                         case 1:        /* untested */
          232                                 _x.chan = GREY1;
          233                                 break;
          234                         case 2:        /* untested */
          235                                 _x.chan = GREY2;
          236                                 break;
          237                         case 4:        /* untested */
          238                                 _x.chan = GREY4;
          239                                 break;
          240                         case 8:
          241                                 _x.chan = CMAP8;
          242                                 break;
          243                         case 15:
          244                                 _x.chan = RGB15;
          245                                 break;
          246                         case 16: /* how to tell RGB15? */
          247                                 _x.chan = RGB16;
          248                                 break;
          249                         case 24: /* untested (impossible?) */
          250                                 _x.chan = RGB24;
          251                                 break;
          252                         case 32:
          253                                 _x.chan = XRGB32;
          254                                 break;
          255                         }
          256                 }
          257         }
          258         XFree(pfmt);
          259         if(_x.chan == 0){
          260                 werrstr("could not determine screen pixel format");
          261                 goto err0;
          262         }
          263 
          264         /*
          265          * Set up color map if necessary.
          266          */
          267         xscreen = DefaultScreenOfDisplay(_x.display);
          268         _x.cmap = DefaultColormapOfScreen(xscreen);
          269         if(_x.vis->class != StaticColor){
          270                 plan9cmap();
          271                 setupcmap(xrootwin);
          272         }
          273         gfx_started();
          274         xloop();
          275 
          276 err0:
          277         XCloseDisplay(_x.display);
          278         sysfatal("%r");
          279 }
          280 
          281 static void
          282 xloop(void)
          283 {
          284         fd_set rd, wr, xx;
          285         XEvent event;
          286 
          287         xlock();
          288         _x.fd = ConnectionNumber(_x.display);
          289         for(;;) {
          290                 FD_ZERO(&rd);
          291                 FD_ZERO(&wr);
          292                 FD_ZERO(&xx);
          293                 FD_SET(_x.fd, &rd);
          294                 FD_SET(_x.fd, &xx);
          295                 if(_x.windows != nil)
          296                         XSelectInput(_x.display, _x.windows->drawable, Mask); // TODO: when is this needed?
          297                 XFlush(_x.display);
          298                 xunlock();
          299 
          300         again:
          301                 if(select(_x.fd+1, &rd, &wr, &xx, nil) < 0) {
          302                         if(errno == EINTR)
          303                                 goto again;
          304                         sysfatal("select: %r"); // TODO: quiet exit?
          305                 }
          306 
          307                 xlock();
          308                 while(XPending(_x.display)) {
          309                         XNextEvent(_x.display, &event);
          310                         runxevent(&event);
          311                 }
          312         }
          313 }
          314 
          315 /*
          316  * Handle an incoming X event.
          317  */
          318 static void
          319 runxevent(XEvent *xev)
          320 {
          321         int c;
          322         KeySym k;
          323         static Mouse m;
          324         XButtonEvent *be;
          325         XKeyEvent *ke;
          326         Xwin *w;
          327 
          328 #ifdef SHOWEVENT
          329         static int first = 1;
          330         if(first){
          331                 dup(create("/tmp/devdraw.out", OWRITE, 0666), 1);
          332                 setbuf(stdout, 0);
          333                 first = 0;
          334         }
          335 #endif
          336 
          337         if(xev == 0)
          338                 return;
          339 
          340 #ifdef SHOWEVENT
          341         print("\n");
          342         ShowEvent(xev);
          343 #endif
          344 
          345         w = nil;
          346         switch(xev->type){
          347         case Expose:
          348                 w = findxwin(((XExposeEvent*)xev)->window);
          349                 break;
          350         case DestroyNotify:
          351                 w = findxwin(((XDestroyWindowEvent*)xev)->window);
          352                 break;
          353         case ConfigureNotify:
          354                 w = findxwin(((XConfigureEvent*)xev)->window);
          355                 break;
          356         case ButtonPress:
          357         case ButtonRelease:
          358                 w = findxwin(((XButtonEvent*)xev)->window);
          359                 break;
          360         case MotionNotify:
          361                 w = findxwin(((XMotionEvent*)xev)->window);
          362                 break;
          363         case KeyRelease:
          364         case KeyPress:
          365                 w = findxwin(((XKeyEvent*)xev)->window);
          366                 break;
          367         case FocusOut:
          368                 w = findxwin(((XFocusChangeEvent*)xev)->window);
          369                 break;
          370         }
          371         if(w == nil)
          372                 w = _x.windows;
          373 
          374         switch(xev->type){
          375         case Expose:
          376                 _xexpose(w, xev);
          377                 break;
          378 
          379         case DestroyNotify:
          380                 if(_xdestroy(w, xev))
          381                         threadexitsall(nil);
          382                 break;
          383 
          384         case ConfigureNotify:
          385                 if(_xconfigure(w, xev))
          386                         _xreplacescreenimage(w->client);
          387                 break;
          388 
          389         case ButtonPress:
          390                 be = (XButtonEvent*)xev;
          391                 if(be->button == 1) {
          392                         if(_x.kstate & ControlMask)
          393                                 be->button = 2;
          394                         else if(_x.kstate & Mod1Mask)
          395                                 be->button = 3;
          396                 }
          397                 // fall through
          398         case ButtonRelease:
          399                 _x.altdown = 0;
          400                 // fall through
          401         case MotionNotify:
          402                 if(_xtoplan9mouse(w, xev, &m) < 0)
          403                         return;
          404                 gfx_mousetrack(w->client, m.xy.x, m.xy.y, m.buttons|_x.kbuttons, m.msec);
          405                 break;
          406 
          407         case KeyRelease:
          408         case KeyPress:
          409                 ke = (XKeyEvent*)xev;
          410                 XLookupString(ke, NULL, 0, &k, NULL);
          411                 c = ke->state;
          412                 switch(k) {
          413                 case XK_Alt_L:
          414                 case XK_Meta_L:        /* Shift Alt on PCs */
          415                 case XK_Alt_R:
          416                 case XK_Meta_R:        /* Shift Alt on PCs */
          417                 case XK_Multi_key:
          418                         if(xev->type == KeyPress)
          419                                 _x.altdown = 1;
          420                         else if(_x.altdown) {
          421                                 _x.altdown = 0;
          422                                 gfx_keystroke(w->client, Kalt);
          423                         }
          424                         break;
          425                 }
          426 
          427                 switch(k) {
          428                 case XK_Control_L:
          429                         if(xev->type == KeyPress)
          430                                 c |= ControlMask;
          431                         else
          432                                 c &= ~ControlMask;
          433                         goto kbutton;
          434                 case XK_Alt_L:
          435                 case XK_Shift_L:
          436                         if(xev->type == KeyPress)
          437                                 c |= Mod1Mask;
          438                         else
          439                                 c &= ~Mod1Mask;
          440                 kbutton:
          441                         _x.kstate = c;
          442                         if(m.buttons || _x.kbuttons) {
          443                                 _x.altdown = 0; // used alt
          444                                 _x.kbuttons = 0;
          445                                 if(c & ControlMask)
          446                                         _x.kbuttons |= 2;
          447                                 if(c & Mod1Mask)
          448                                         _x.kbuttons |= 4;
          449                                 gfx_mousetrack(w->client, m.xy.x, m.xy.y, m.buttons|_x.kbuttons, m.msec);
          450                                 break;
          451                         }
          452                 }
          453 
          454                 if(xev->type != KeyPress)
          455                         break;
          456                 if(k == XK_F11){
          457                         w->fullscreen = !w->fullscreen;
          458                         _xmovewindow(w, w->fullscreen ? w->screenrect : w->windowrect);
          459                         return;
          460                 }
          461                 if((c = _xtoplan9kbd(xev)) < 0)
          462                         return;
          463                 gfx_keystroke(w->client, c);
          464                 break;
          465 
          466         case FocusOut:
          467                 /*
          468                  * Some key combinations (e.g. Alt-Tab) can cause us
          469                  * to see the key down event without the key up event,
          470                  * so clear out the keyboard state when we lose the focus.
          471                  */
          472                 _x.kstate = 0;
          473                 _x.altdown = 0;
          474                 gfx_abortcompose(w->client);
          475                 break;
          476 
          477         case SelectionRequest:
          478                 _xselect(xev);
          479                 break;
          480         }
          481 }
          482 
          483 
          484 static Memimage*
          485 xattach(Client *client, char *label, char *winsize)
          486 {
          487         char *argv[2];
          488         int havemin, height, mask, width, x, y;
          489         Rectangle r;
          490         XClassHint classhint;
          491         XDrawable pmid;
          492         XScreen *xscreen;
          493         XSetWindowAttributes attr;
          494         XSizeHints normalhint;
          495         XTextProperty name;
          496         XWindow xrootwin;
          497         XWindowAttributes wattr;
          498         XWMHints hint;
          499         Atom atoms[2];
          500         Xwin *w;
          501 
          502         USED(client);
          503         xscreen = DefaultScreenOfDisplay(_x.display);
          504         xrootwin = DefaultRootWindow(_x.display);
          505 
          506         /*
          507          * We get to choose the initial rectangle size.
          508          * This is arbitrary.  In theory we should read the
          509          * command line and allow the traditional X options.
          510          */
          511         mask = 0;
          512         x = 0;
          513         y = 0;
          514         if(winsize && winsize[0]){
          515                 if(parsewinsize(winsize, &r, &havemin) < 0)
          516                         sysfatal("%r");
          517         }else{
          518                 /*
          519                  * Parse the various X resources.  Thanks to Peter Canning.
          520                  */
          521                 char *screen_resources, *display_resources, *geom,
          522                         *geomrestype, *home, *file, *dpitype;
          523                 XrmDatabase database;
          524                 XrmValue geomres, dpires;
          525 
          526                 database = XrmGetDatabase(_x.display);
          527                 screen_resources = XScreenResourceString(xscreen);
          528                 if(screen_resources != nil){
          529                         XrmCombineDatabase(XrmGetStringDatabase(screen_resources), &database, False);
          530                         XFree(screen_resources);
          531                 }
          532 
          533                 display_resources = XResourceManagerString(_x.display);
          534                 if(display_resources == nil){
          535                         home = getenv("HOME");
          536                         if(home!=nil && (file=smprint("%s/.Xdefaults", home)) != nil){
          537                                 XrmCombineFileDatabase(file, &database, False);
          538                                 free(file);
          539                         }
          540                         free(home);
          541                 }else
          542                         XrmCombineDatabase(XrmGetStringDatabase(display_resources), &database, False);
          543 
          544                 if (XrmGetResource(database, "Xft.dpi", "String", &dpitype, &dpires) == True) {
          545                         if (dpires.addr) {
          546                                 client->displaydpi = atoi(dpires.addr);
          547                         }
          548                 }
          549                 geom = smprint("%s.geometry", label);
          550                 if(geom && XrmGetResource(database, geom, nil, &geomrestype, &geomres))
          551                         mask = XParseGeometry(geomres.addr, &x, &y, (uint*)&width, (uint*)&height);
          552                 XrmDestroyDatabase(database);
          553                 free(geom);
          554 
          555                 if((mask & WidthValue) && (mask & HeightValue)){
          556                         r = Rect(0, 0, width, height);
          557                 }else{
          558                         r = Rect(0, 0, WidthOfScreen(xscreen)*3/4,
          559                                         HeightOfScreen(xscreen)*3/4);
          560                         if(Dx(r) > Dy(r)*3/2)
          561                                 r.max.x = r.min.x + Dy(r)*3/2;
          562                         if(Dy(r) > Dx(r)*3/2)
          563                                 r.max.y = r.min.y + Dx(r)*3/2;
          564                 }
          565                 if(mask & XNegative){
          566                         x += WidthOfScreen(xscreen);
          567                 }
          568                 if(mask & YNegative){
          569                         y += HeightOfScreen(xscreen);
          570                 }
          571                 havemin = 0;
          572         }
          573         w = newxwin(client);
          574 
          575         memset(&attr, 0, sizeof attr);
          576         attr.colormap = _x.cmap;
          577         attr.background_pixel = ~0;
          578         attr.border_pixel = 0;
          579         w->drawable = XCreateWindow(
          580                 _x.display,        /* display */
          581                 xrootwin,        /* parent */
          582                 x,                /* x */
          583                 y,                /* y */
          584                 Dx(r),                /* width */
          585                  Dy(r),                /* height */
          586                 0,                /* border width */
          587                 _x.depth,        /* depth */
          588                 InputOutput,        /* class */
          589                 _x.vis,                /* visual */
          590                                 /* valuemask */
          591                 CWBackPixel|CWBorderPixel|CWColormap,
          592                 &attr                /* attributes (the above aren't?!) */
          593         );
          594 
          595         /*
          596          * Label and other properties required by ICCCCM.
          597          */
          598         memset(&name, 0, sizeof name);
          599         if(label == nil)
          600                 label = "pjw-face-here";
          601         name.value = (uchar*)label;
          602         name.encoding = XA_STRING;
          603         name.format = 8;
          604         name.nitems = strlen((char*)name.value);
          605 
          606         memset(&normalhint, 0, sizeof normalhint);
          607         normalhint.flags = PSize|PMaxSize;
          608         if(winsize && winsize[0]){
          609                 normalhint.flags &= ~PSize;
          610                 normalhint.flags |= USSize;
          611                 normalhint.width = Dx(r);
          612                 normalhint.height = Dy(r);
          613         }else{
          614                 if((mask & WidthValue) && (mask & HeightValue)){
          615                         normalhint.flags &= ~PSize;
          616                         normalhint.flags |= USSize;
          617                         normalhint.width = width;
          618                         normalhint.height = height;
          619                 }
          620                 if((mask & WidthValue) && (mask & HeightValue)){
          621                         normalhint.flags |= USPosition;
          622                         normalhint.x = x;
          623                         normalhint.y = y;
          624                 }
          625         }
          626 
          627         normalhint.max_width = WidthOfScreen(xscreen);
          628         normalhint.max_height = HeightOfScreen(xscreen);
          629 
          630         memset(&hint, 0, sizeof hint);
          631         hint.flags = InputHint|StateHint;
          632         hint.input = 1;
          633         hint.initial_state = NormalState;
          634 
          635         memset(&classhint, 0, sizeof classhint);
          636         classhint.res_name = label;
          637         classhint.res_class = label;
          638 
          639         argv[0] = label;
          640         argv[1] = nil;
          641 
          642         XSetWMProperties(
          643                 _x.display,        /* display */
          644                 w->drawable,        /* window */
          645                 &name,                /* XA_WM_NAME property */
          646                 &name,                /* XA_WM_ICON_NAME property */
          647                 argv,                /* XA_WM_COMMAND */
          648                 1,                /* argc */
          649                 &normalhint,        /* XA_WM_NORMAL_HINTS */
          650                 &hint,                /* XA_WM_HINTS */
          651                 &classhint        /* XA_WM_CLASSHINTS */
          652         );
          653         XFlush(_x.display);
          654 
          655         if(havemin){
          656                 XWindowChanges ch;
          657 
          658                 memset(&ch, 0, sizeof ch);
          659                 ch.x = r.min.x;
          660                 ch.y = r.min.y;
          661                 XConfigureWindow(_x.display, w->drawable, CWX|CWY, &ch);
          662                 /*
          663                  * Must pretend origin is 0,0 for X.
          664                  */
          665                 r = Rect(0,0,Dx(r),Dy(r));
          666         }
          667         /*
          668          * Look up clipboard atom.
          669          */
          670          if(_x.clipboard == 0) {
          671                 _x.clipboard = XInternAtom(_x.display, "CLIPBOARD", False);
          672                 _x.utf8string = XInternAtom(_x.display, "UTF8_STRING", False);
          673                 _x.targets = XInternAtom(_x.display, "TARGETS", False);
          674                 _x.text = XInternAtom(_x.display, "TEXT", False);
          675                 _x.compoundtext = XInternAtom(_x.display, "COMPOUND_TEXT", False);
          676                 _x.takefocus = XInternAtom(_x.display, "WM_TAKE_FOCUS", False);
          677                 _x.losefocus = XInternAtom(_x.display, "_9WM_LOSE_FOCUS", False);
          678                 _x.wmprotos = XInternAtom(_x.display, "WM_PROTOCOLS", False);
          679         }
          680 
          681         atoms[0] = _x.takefocus;
          682         atoms[1] = _x.losefocus;
          683         XChangeProperty(_x.display, w->drawable, _x.wmprotos, XA_ATOM, 32,
          684                 PropModeReplace, (uchar*)atoms, 2);
          685 
          686         /*
          687          * Put the window on the screen, check to see what size we actually got.
          688          */
          689         XMapWindow(_x.display, w->drawable);
          690         XSync(_x.display, False);
          691 
          692         if(!XGetWindowAttributes(_x.display, w->drawable, &wattr))
          693                 fprint(2, "XGetWindowAttributes failed\n");
          694         else if(wattr.width && wattr.height){
          695                 if(wattr.width != Dx(r) || wattr.height != Dy(r)){
          696                         r.max.x = wattr.width;
          697                         r.max.y = wattr.height;
          698                 }
          699         }else
          700                 fprint(2, "XGetWindowAttributes: bad attrs\n");
          701         w->screenrect = Rect(0, 0, WidthOfScreen(xscreen), HeightOfScreen(xscreen));
          702         w->windowrect = r;
          703 
          704         /*
          705          * Allocate our local backing store.
          706          */
          707         w->screenr = r;
          708         w->screenpm = XCreatePixmap(_x.display, w->drawable, Dx(r), Dy(r), _x.depth);
          709         w->nextscreenpm = w->screenpm;
          710         w->screenimage = _xallocmemimage(r, _x.chan, w->screenpm);
          711         client->mouserect = r;
          712 
          713         /*
          714          * Allocate some useful graphics contexts for the future.
          715          * These can be used with any drawable matching w->drawable's
          716          * pixel format (which is all the drawables we create).
          717          */
          718          if(_x.gcfill == 0) {
          719                 _x.gcfill        = xgc(w->screenpm, FillSolid, -1);
          720                 _x.gccopy        = xgc(w->screenpm, -1, -1);
          721                 _x.gcsimplesrc         = xgc(w->screenpm, FillStippled, -1);
          722                 _x.gczero        = xgc(w->screenpm, -1, -1);
          723                 _x.gcreplsrc        = xgc(w->screenpm, FillTiled, -1);
          724 
          725                 pmid = XCreatePixmap(_x.display, w->drawable, 1, 1, 1);
          726                 _x.gcfill0        = xgc(pmid, FillSolid, 0);
          727                 _x.gccopy0        = xgc(pmid, -1, -1);
          728                 _x.gcsimplesrc0        = xgc(pmid, FillStippled, -1);
          729                 _x.gczero0        = xgc(pmid, -1, -1);
          730                 _x.gcreplsrc0        = xgc(pmid, FillTiled, -1);
          731                 XFreePixmap(_x.display, pmid);
          732         }
          733 
          734         return w->screenimage;
          735 }
          736 
          737 Memimage*
          738 rpc_attach(Client *client, char *label, char *winsize)
          739 {
          740         Memimage *m;
          741 
          742         xlock();
          743         m = xattach(client, label, winsize);
          744         xunlock();
          745         return m;
          746 }
          747 
          748 void
          749 rpc_setlabel(Client *client, char *label)
          750 {
          751         Xwin *w = (Xwin*)client->view;
          752         XTextProperty name;
          753 
          754         /*
          755          * Label and other properties required by ICCCCM.
          756          */
          757         xlock();
          758         memset(&name, 0, sizeof name);
          759         if(label == nil)
          760                 label = "pjw-face-here";
          761         name.value = (uchar*)label;
          762         name.encoding = XA_STRING;
          763         name.format = 8;
          764         name.nitems = strlen((char*)name.value);
          765 
          766         XSetWMProperties(
          767                 _x.display,        /* display */
          768                 w->drawable,        /* window */
          769                 &name,                /* XA_WM_NAME property */
          770                 &name,                /* XA_WM_ICON_NAME property */
          771                 nil,                /* XA_WM_COMMAND */
          772                 0,                /* argc */
          773                 nil,                /* XA_WM_NORMAL_HINTS */
          774                 nil,                /* XA_WM_HINTS */
          775                 nil        /* XA_WM_CLASSHINTS */
          776         );
          777         XFlush(_x.display);
          778         xunlock();
          779 }
          780 
          781 /*
          782  * Create a GC with a particular fill style and XXX.
          783  * Disable generation of GraphicsExpose/NoExpose events in the GC.
          784  */
          785 static XGC
          786 xgc(XDrawable d, int fillstyle, int foreground)
          787 {
          788         XGC gc;
          789         XGCValues v;
          790 
          791         memset(&v, 0, sizeof v);
          792         v.function = GXcopy;
          793         v.graphics_exposures = False;
          794         gc = XCreateGC(_x.display, d, GCFunction|GCGraphicsExposures, &v);
          795         if(fillstyle != -1)
          796                 XSetFillStyle(_x.display, gc, fillstyle);
          797         if(foreground != -1)
          798                 XSetForeground(_x.display, gc, 0);
          799         return gc;
          800 }
          801 
          802 
          803 /*
          804  * Initialize map with the Plan 9 rgbv color map.
          805  */
          806 static void
          807 plan9cmap(void)
          808 {
          809         int r, g, b, cr, cg, cb, v, num, den, idx, v7, idx7;
          810         static int once;
          811 
          812         if(once)
          813                 return;
          814         once = 1;
          815 
          816         for(r=0; r!=4; r++)
          817         for(g = 0; g != 4; g++)
          818         for(b = 0; b!=4; b++)
          819         for(v = 0; v!=4; v++){
          820                 den=r;
          821                 if(g > den)
          822                         den=g;
          823                 if(b > den)
          824                         den=b;
          825                 /* divide check -- pick grey shades */
          826                 if(den==0)
          827                         cr=cg=cb=v*17;
          828                 else {
          829                         num=17*(4*den+v);
          830                         cr=r*num/den;
          831                         cg=g*num/den;
          832                         cb=b*num/den;
          833                 }
          834                 idx = r*64 + v*16 + ((g*4 + b + v - r) & 15);
          835                 _x.map[idx].red = cr*0x0101;
          836                 _x.map[idx].green = cg*0x0101;
          837                 _x.map[idx].blue = cb*0x0101;
          838                 _x.map[idx].pixel = idx;
          839                 _x.map[idx].flags = DoRed|DoGreen|DoBlue;
          840 
          841                 v7 = v >> 1;
          842                 idx7 = r*32 + v7*16 + g*4 + b;
          843                 if((v & 1) == v7){
          844                         _x.map7to8[idx7][0] = idx;
          845                         if(den == 0) {                 /* divide check -- pick grey shades */
          846                                 cr = ((255.0/7.0)*v7)+0.5;
          847                                 cg = cr;
          848                                 cb = cr;
          849                         }
          850                         else {
          851                                 num=17*15*(4*den+v7*2)/14;
          852                                 cr=r*num/den;
          853                                 cg=g*num/den;
          854                                 cb=b*num/den;
          855                         }
          856                         _x.map7[idx7].red = cr*0x0101;
          857                         _x.map7[idx7].green = cg*0x0101;
          858                         _x.map7[idx7].blue = cb*0x0101;
          859                         _x.map7[idx7].pixel = idx7;
          860                         _x.map7[idx7].flags = DoRed|DoGreen|DoBlue;
          861                 }
          862                 else
          863                         _x.map7to8[idx7][1] = idx;
          864         }
          865 }
          866 
          867 /*
          868  * Initialize and install the rgbv color map as a private color map
          869  * for this application.  It gets the best colors when it has the
          870  * cursor focus.
          871  *
          872  * We always choose the best depth possible, but that might not
          873  * be the default depth.  On such "suboptimal" systems, we have to allocate an
          874  * empty color map anyway, according to Axel Belinfante.
          875  */
          876 static int
          877 setupcmap(XWindow w)
          878 {
          879         char buf[30];
          880         int i;
          881         u32int p, pp;
          882         XColor c;
          883 
          884         if(_x.depth <= 1)
          885                 return 0;
          886 
          887         if(_x.depth >= 24) {
          888                 if(_x.usetable == 0)
          889                         _x.cmap = XCreateColormap(_x.display, w, _x.vis, AllocNone);
          890 
          891                 /*
          892                  * The pixel value returned from XGetPixel needs to
          893                  * be converted to RGB so we can call rgb2cmap()
          894                  * to translate between 24 bit X and our color. Unfortunately,
          895                  * the return value appears to be display server endian
          896                  * dependant. Therefore, we run some heuristics to later
          897                  * determine how to mask the int value correctly.
          898                  * Yeah, I know we can look at _x.vis->byte_order but
          899                  * some displays say MSB even though they run on LSB.
          900                  * Besides, this is more anal.
          901                  */
          902                 c = _x.map[19];        /* known to have different R, G, B values */
          903                 if(!XAllocColor(_x.display, _x.cmap, &c)){
          904                         werrstr("XAllocColor: %r");
          905                         return -1;
          906                 }
          907                 p  = c.pixel;
          908                 pp = rgb2cmap((p>>16)&0xff,(p>>8)&0xff,p&0xff);
          909                 if(pp != _x.map[19].pixel) {
          910                         /* check if endian is other way */
          911                         pp = rgb2cmap(p&0xff,(p>>8)&0xff,(p>>16)&0xff);
          912                         if(pp != _x.map[19].pixel){
          913                                 werrstr("cannot detect X server byte order");
          914                                 return -1;
          915                         }
          916 
          917                         switch(_x.chan){
          918                         case RGB24:
          919                                 _x.chan = BGR24;
          920                                 break;
          921                         case XRGB32:
          922                                 _x.chan = XBGR32;
          923                                 break;
          924                         default:
          925                                 werrstr("cannot byteswap channel %s",
          926                                         chantostr(buf, _x.chan));
          927                                 break;
          928                         }
          929                 }
          930         }else if(_x.vis->class == TrueColor || _x.vis->class == DirectColor){
          931                 /*
          932                  * Do nothing.  We have no way to express a
          933                  * mixed-endian 16-bit screen, so pretend they don't exist.
          934                  */
          935                 if(_x.usetable == 0)
          936                         _x.cmap = XCreateColormap(_x.display, w, _x.vis, AllocNone);
          937         }else if(_x.vis->class == PseudoColor){
          938                 if(_x.usetable == 0){
          939                         _x.cmap = XCreateColormap(_x.display, w, _x.vis, AllocAll);
          940                         XStoreColors(_x.display, _x.cmap, _x.map, 256);
          941                         for(i = 0; i < 256; i++){
          942                                 _x.tox11[i] = i;
          943                                 _x.toplan9[i] = i;
          944                         }
          945                 }else{
          946                         for(i = 0; i < 128; i++){
          947                                 c = _x.map7[i];
          948                                 if(!XAllocColor(_x.display, _x.cmap, &c)){
          949                                         werrstr("can't allocate colors in 7-bit map");
          950                                         return -1;
          951                                 }
          952                                 _x.tox11[_x.map7to8[i][0]] = c.pixel;
          953                                 _x.tox11[_x.map7to8[i][1]] = c.pixel;
          954                                 _x.toplan9[c.pixel] = _x.map7to8[i][0];
          955                         }
          956                 }
          957         }else{
          958                 werrstr("unsupported visual class %d", _x.vis->class);
          959                 return -1;
          960         }
          961         return 0;
          962 }
          963 
          964 void
          965 rpc_shutdown(void)
          966 {
          967 }
          968 
          969 void
          970 rpc_flush(Client *client, Rectangle r)
          971 {
          972         Xwin *w = (Xwin*)client->view;
          973 
          974         xlock();
          975         if(w->nextscreenpm != w->screenpm){
          976                 XSync(_x.display, False);
          977                 XFreePixmap(_x.display, w->screenpm);
          978                 w->screenpm = w->nextscreenpm;
          979         }
          980 
          981         if(r.min.x >= r.max.x || r.min.y >= r.max.y) {
          982                 xunlock();
          983                 return;
          984         }
          985 
          986         XCopyArea(_x.display, w->screenpm, w->drawable, _x.gccopy, r.min.x, r.min.y,
          987                 Dx(r), Dy(r), r.min.x, r.min.y);
          988         XFlush(_x.display);
          989         xunlock();
          990 }
          991 
          992 static void
          993 _xexpose(Xwin *w, XEvent *e)
          994 {
          995         XExposeEvent *xe;
          996         Rectangle r;
          997 
          998         if(w->screenpm != w->nextscreenpm)
          999                 return;
         1000         xe = (XExposeEvent*)e;
         1001         r.min.x = xe->x;
         1002         r.min.y = xe->y;
         1003         r.max.x = xe->x+xe->width;
         1004         r.max.y = xe->y+xe->height;
         1005         XCopyArea(_x.display, w->screenpm, w->drawable, _x.gccopy, r.min.x, r.min.y,
         1006                 Dx(r), Dy(r), r.min.x, r.min.y);
         1007         XSync(_x.display, False);
         1008 }
         1009 
         1010 static int
         1011 _xdestroy(Xwin *w, XEvent *e)
         1012 {
         1013         XDestroyWindowEvent *xe;
         1014 
         1015         xe = (XDestroyWindowEvent*)e;
         1016         if(xe->window == w->drawable){
         1017                 w->destroyed = 1;
         1018                 return 1;
         1019         }
         1020         return 0;
         1021 }
         1022 
         1023 static int
         1024 _xconfigure(Xwin *w, XEvent *e)
         1025 {
         1026         Rectangle r;
         1027         XConfigureEvent *xe = (XConfigureEvent*)e;
         1028 
         1029         if(!w->fullscreen){
         1030                 int rx, ry;
         1031                 XWindow xw;
         1032                 if(XTranslateCoordinates(_x.display, w->drawable, DefaultRootWindow(_x.display), 0, 0, &rx, &ry, &xw))
         1033                         w->windowrect = Rect(rx, ry, rx+xe->width, ry+xe->height);
         1034         }
         1035 
         1036         if(xe->width == Dx(w->screenr) && xe->height == Dy(w->screenr))
         1037                 return 0;
         1038         r = Rect(0, 0, xe->width, xe->height);
         1039 
         1040         if(w->screenpm != w->nextscreenpm){
         1041                 XCopyArea(_x.display, w->screenpm, w->drawable, _x.gccopy, r.min.x, r.min.y,
         1042                         Dx(r), Dy(r), r.min.x, r.min.y);
         1043                 XSync(_x.display, False);
         1044         }
         1045         w->newscreenr = r;
         1046         return 1;
         1047 }
         1048 
         1049 static int
         1050 _xreplacescreenimage(Client *client)
         1051 {
         1052         Memimage *m;
         1053         XDrawable pixmap;
         1054         Rectangle r;
         1055         Xwin *w;
         1056 
         1057         w = (Xwin*)client->view;
         1058         r = w->newscreenr;
         1059         pixmap = XCreatePixmap(_x.display, w->drawable, Dx(r), Dy(r), _x.depth);
         1060         m = _xallocmemimage(r, _x.chan, pixmap);
         1061         if(w->nextscreenpm != w->screenpm)
         1062                 XFreePixmap(_x.display, w->nextscreenpm);
         1063         w->nextscreenpm = pixmap;
         1064         w->screenr = r;
         1065         client->mouserect = r;
         1066         xunlock();
         1067         gfx_replacescreenimage(client, m);
         1068         xlock();
         1069         return 1;
         1070 }
         1071 
         1072 void
         1073 rpc_resizeimg(Client *client)
         1074 {
         1075         xlock();
         1076         _xreplacescreenimage(client);
         1077         xunlock();
         1078 }
         1079 
         1080 void
         1081 rpc_gfxdrawlock(void)
         1082 {
         1083         xlock();
         1084 }
         1085 
         1086 void
         1087 rpc_gfxdrawunlock(void)
         1088 {
         1089         xunlock();
         1090 }
         1091 void
         1092 rpc_topwin(Client *client)
         1093 {
         1094         Xwin *w = (Xwin*)client->view;
         1095 
         1096         xlock();
         1097         XMapRaised(_x.display, w->drawable);
         1098         XSetInputFocus(_x.display, w->drawable, RevertToPointerRoot,
         1099                 CurrentTime);
         1100         XFlush(_x.display);
         1101         xunlock();
         1102 }
         1103 
         1104 void
         1105 rpc_resizewindow(Client *client, Rectangle r)
         1106 {
         1107         Xwin *w = (Xwin*)client->view;
         1108         XWindowChanges e;
         1109         int value_mask;
         1110 
         1111         xlock();
         1112         memset(&e, 0, sizeof e);
         1113         value_mask = CWX|CWY|CWWidth|CWHeight;
         1114         e.width = Dx(r);
         1115         e.height = Dy(r);
         1116         XConfigureWindow(_x.display, w->drawable, value_mask, &e);
         1117         XFlush(_x.display);
         1118         xunlock();
         1119 }
         1120 
         1121 static void
         1122 _xmovewindow(Xwin *w, Rectangle r)
         1123 {
         1124         XWindowChanges e;
         1125         int value_mask;
         1126 
         1127         memset(&e, 0, sizeof e);
         1128         value_mask = CWX|CWY|CWWidth|CWHeight;
         1129         e.x = r.min.x;
         1130         e.y = r.min.y;
         1131         e.width = Dx(r);
         1132         e.height = Dy(r);
         1133         XConfigureWindow(_x.display, w->drawable, value_mask, &e);
         1134         XFlush(_x.display);
         1135 }
         1136 
         1137 static int
         1138 _xtoplan9kbd(XEvent *e)
         1139 {
         1140         KeySym k;
         1141 
         1142         if(e->xany.type != KeyPress)
         1143                 return -1;
         1144         needstack(64*1024);        /* X has some *huge* buffers in openobject */
         1145                 /* and they're even bigger on SuSE */
         1146         XLookupString((XKeyEvent*)e,NULL,0,&k,NULL);
         1147         if(k == NoSymbol)
         1148                 return -1;
         1149 
         1150         if(k&0xFF00){
         1151                 switch(k){
         1152                 case XK_BackSpace:
         1153                 case XK_Tab:
         1154                 case XK_Escape:
         1155                 case XK_Delete:
         1156                 case XK_KP_0:
         1157                 case XK_KP_1:
         1158                 case XK_KP_2:
         1159                 case XK_KP_3:
         1160                 case XK_KP_4:
         1161                 case XK_KP_5:
         1162                 case XK_KP_6:
         1163                 case XK_KP_7:
         1164                 case XK_KP_8:
         1165                 case XK_KP_9:
         1166                 case XK_KP_Divide:
         1167                 case XK_KP_Multiply:
         1168                 case XK_KP_Subtract:
         1169                 case XK_KP_Add:
         1170                 case XK_KP_Decimal:
         1171                         k &= 0x7F;
         1172                         break;
         1173                 case XK_Linefeed:
         1174                         k = '\r';
         1175                         break;
         1176                 case XK_KP_Space:
         1177                         k = ' ';
         1178                         break;
         1179                 case XK_Home:
         1180                 case XK_KP_Home:
         1181                         k = Khome;
         1182                         break;
         1183                 case XK_Left:
         1184                 case XK_KP_Left:
         1185                         k = Kleft;
         1186                         break;
         1187                 case XK_Up:
         1188                 case XK_KP_Up:
         1189                         k = Kup;
         1190                         break;
         1191                 case XK_Down:
         1192                 case XK_KP_Down:
         1193                         k = Kdown;
         1194                         break;
         1195                 case XK_Right:
         1196                 case XK_KP_Right:
         1197                         k = Kright;
         1198                         break;
         1199                 case XK_Page_Down:
         1200                 case XK_KP_Page_Down:
         1201                         k = Kpgdown;
         1202                         break;
         1203                 case XK_End:
         1204                 case XK_KP_End:
         1205                         k = Kend;
         1206                         break;
         1207                 case XK_Page_Up:
         1208                 case XK_KP_Page_Up:
         1209                         k = Kpgup;
         1210                         break;
         1211                 case XK_Insert:
         1212                 case XK_KP_Insert:
         1213                         k = Kins;
         1214                         break;
         1215                 case XK_KP_Enter:
         1216                 case XK_Return:
         1217                         k = '\n';
         1218                         break;
         1219                 case XK_Alt_L:
         1220                 case XK_Meta_L:        /* Shift Alt on PCs */
         1221                 case XK_Alt_R:
         1222                 case XK_Meta_R:        /* Shift Alt on PCs */
         1223                 case XK_Multi_key:
         1224                         return -1;
         1225                 default:                /* not ISO-1 or tty control */
         1226                         if(k>0xff) {
         1227                                 k = _p9keysym2ucs(k);
         1228                                 if(k==-1) return -1;
         1229                         }
         1230                 }
         1231         }
         1232 
         1233         /* Compensate for servers that call a minus a hyphen */
         1234         if(k == XK_hyphen)
         1235                 k = XK_minus;
         1236         /* Do control mapping ourselves if translator doesn't */
         1237         if(e->xkey.state&ControlMask)
         1238                 k &= 0x9f;
         1239         if(k == NoSymbol) {
         1240                 return -1;
         1241         }
         1242 
         1243         return k+0;
         1244 }
         1245 
         1246 static int
         1247 _xtoplan9mouse(Xwin *w, XEvent *e, Mouse *m)
         1248 {
         1249         int s;
         1250         XButtonEvent *be;
         1251         XMotionEvent *me;
         1252 
         1253         if(_x.putsnarf != _x.assertsnarf){
         1254                 _x.assertsnarf = _x.putsnarf;
         1255                 XSetSelectionOwner(_x.display, XA_PRIMARY, w->drawable, CurrentTime);
         1256                 if(_x.clipboard != None)
         1257                         XSetSelectionOwner(_x.display, _x.clipboard, w->drawable, CurrentTime);
         1258                 XFlush(_x.display);
         1259         }
         1260 
         1261         switch(e->type){
         1262         case ButtonPress:
         1263                 be = (XButtonEvent*)e;
         1264 
         1265                 /*
         1266                  * Fake message, just sent to make us announce snarf.
         1267                  * Apparently state and button are 16 and 8 bits on
         1268                  * the wire, since they are truncated by the time they
         1269                  * get to us.
         1270                  */
         1271                 if(be->send_event
         1272                 && (~be->state&0xFFFF)==0
         1273                 && (~be->button&0xFF)==0)
         1274                         return -1;
         1275                 /* BUG? on mac need to inherit these from elsewhere? */
         1276                 m->xy.x = be->x;
         1277                 m->xy.y = be->y;
         1278                 s = be->state;
         1279                 m->msec = be->time;
         1280                 switch(be->button){
         1281                 case 1:
         1282                         s |= Button1Mask;
         1283                         break;
         1284                 case 2:
         1285                         s |= Button2Mask;
         1286                         break;
         1287                 case 3:
         1288                         s |= Button3Mask;
         1289                         break;
         1290                 case 4:
         1291                         s |= Button4Mask;
         1292                         break;
         1293                 case 5:
         1294                         s |= Button5Mask;
         1295                         break;
         1296                 }
         1297                 break;
         1298         case ButtonRelease:
         1299                 be = (XButtonEvent*)e;
         1300                 m->xy.x = be->x;
         1301                 m->xy.y = be->y;
         1302                 s = be->state;
         1303                 m->msec = be->time;
         1304                 switch(be->button){
         1305                 case 1:
         1306                         s &= ~Button1Mask;
         1307                         break;
         1308                 case 2:
         1309                         s &= ~Button2Mask;
         1310                         break;
         1311                 case 3:
         1312                         s &= ~Button3Mask;
         1313                         break;
         1314                 case 4:
         1315                         s &= ~Button4Mask;
         1316                         break;
         1317                 case 5:
         1318                         s &= ~Button5Mask;
         1319                         break;
         1320                 }
         1321                 break;
         1322 
         1323         case MotionNotify:
         1324                 me = (XMotionEvent*)e;
         1325                 s = me->state;
         1326                 m->xy.x = me->x;
         1327                 m->xy.y = me->y;
         1328                 m->msec = me->time;
         1329                 return 0; // do not set buttons
         1330 
         1331         default:
         1332                 return -1;
         1333         }
         1334 
         1335         m->buttons = 0;
         1336         if(s & Button1Mask)
         1337                 m->buttons |= 1;
         1338         if(s & Button2Mask)
         1339                 m->buttons |= 2;
         1340         if(s & Button3Mask)
         1341                 m->buttons |= 4;
         1342         if(s & Button4Mask)
         1343                 m->buttons |= 8;
         1344         if(s & Button5Mask)
         1345                 m->buttons |= 16;
         1346         return 0;
         1347 }
         1348 
         1349 void
         1350 rpc_setmouse(Client *client, Point p)
         1351 {
         1352         Xwin *w = (Xwin*)client->view;
         1353 
         1354         xlock();
         1355         XWarpPointer(_x.display, None, w->drawable, 0, 0, 0, 0, p.x, p.y);
         1356         XFlush(_x.display);
         1357         xunlock();
         1358 }
         1359 
         1360 static int
         1361 revbyte(int b)
         1362 {
         1363         int r;
         1364 
         1365         r = 0;
         1366         r |= (b&0x01) << 7;
         1367         r |= (b&0x02) << 5;
         1368         r |= (b&0x04) << 3;
         1369         r |= (b&0x08) << 1;
         1370         r |= (b&0x10) >> 1;
         1371         r |= (b&0x20) >> 3;
         1372         r |= (b&0x40) >> 5;
         1373         r |= (b&0x80) >> 7;
         1374         return r;
         1375 }
         1376 
         1377 static void
         1378 xcursorarrow(Xwin *w)
         1379 {
         1380         if(_x.cursor != 0){
         1381                 XFreeCursor(_x.display, _x.cursor);
         1382                 _x.cursor = 0;
         1383         }
         1384         XUndefineCursor(_x.display, w->drawable);
         1385         XFlush(_x.display);
         1386 }
         1387 
         1388 
         1389 void
         1390 rpc_setcursor(Client *client, Cursor *c, Cursor2 *c2)
         1391 {
         1392         Xwin *w = (Xwin*)client->view;
         1393         XColor fg, bg;
         1394         XCursor xc;
         1395         Pixmap xsrc, xmask;
         1396         int i;
         1397         uchar src[2*16], mask[2*16];
         1398 
         1399         USED(c2);
         1400 
         1401         xlock();
         1402         if(c == nil){
         1403                 xcursorarrow(w);
         1404                 xunlock();
         1405                 return;
         1406         }
         1407         for(i=0; i<2*16; i++){
         1408                 src[i] = revbyte(c->set[i]);
         1409                 mask[i] = revbyte(c->set[i] | c->clr[i]);
         1410         }
         1411 
         1412         fg = _x.map[0];
         1413         bg = _x.map[255];
         1414         xsrc = XCreateBitmapFromData(_x.display, w->drawable, (char*)src, 16, 16);
         1415         xmask = XCreateBitmapFromData(_x.display, w->drawable, (char*)mask, 16, 16);
         1416         xc = XCreatePixmapCursor(_x.display, xsrc, xmask, &fg, &bg, -c->offset.x, -c->offset.y);
         1417         if(xc != 0) {
         1418                 XDefineCursor(_x.display, w->drawable, xc);
         1419                 if(_x.cursor != 0)
         1420                         XFreeCursor(_x.display, _x.cursor);
         1421                 _x.cursor = xc;
         1422         }
         1423         XFreePixmap(_x.display, xsrc);
         1424         XFreePixmap(_x.display, xmask);
         1425         XFlush(_x.display);
         1426         xunlock();
         1427 }
         1428 
         1429 struct {
         1430         QLock lk;
         1431         char buf[SnarfSize];
         1432 #ifdef APPLESNARF
         1433         Rune rbuf[SnarfSize];
         1434         PasteboardRef apple;
         1435 #endif
         1436 } clip;
         1437 
         1438 static uchar*
         1439 _xgetsnarffrom(Xwin *w, XWindow xw, Atom clipboard, Atom target, int timeout0, int timeout)
         1440 {
         1441         Atom prop, type;
         1442         ulong len, lastlen, dummy;
         1443         int fmt, i;
         1444         uchar *data, *xdata;
         1445 
         1446         /*
         1447          * We should be waiting for SelectionNotify here, but it might never
         1448          * come, and we have no way to time out.  Instead, we will clear
         1449          * local property #1, request our buddy to fill it in for us, and poll
         1450          * until he's done or we get tired of waiting.
         1451          */
         1452         prop = 1;
         1453         XChangeProperty(_x.display, w->drawable, prop, target, 8, PropModeReplace, (uchar*)"", 0);
         1454         XConvertSelection(_x.display, clipboard, target, prop, w->drawable, CurrentTime);
         1455         XFlush(_x.display);
         1456         lastlen = 0;
         1457         timeout0 = (timeout0 + 9)/10;
         1458         timeout = (timeout + 9)/10;
         1459         for(i=0; i<timeout0 || (lastlen!=0 && i<timeout); i++){
         1460                 usleep(10*1000);
         1461                 XGetWindowProperty(_x.display, w->drawable, prop, 0, 0, 0, AnyPropertyType,
         1462                         &type, &fmt, &dummy, &len, &xdata);
         1463                 if(lastlen == len && len > 0){
         1464                         XFree(xdata);
         1465                         break;
         1466                 }
         1467                 lastlen = len;
         1468                 XFree(xdata);
         1469         }
         1470         if(len == 0)
         1471                 return nil;
         1472 
         1473         /* get the property */
         1474         xdata = nil;
         1475         XGetWindowProperty(_x.display, w->drawable, prop, 0, SnarfSize/sizeof(ulong), 0,
         1476                 AnyPropertyType, &type, &fmt, &len, &dummy, &xdata);
         1477         if((type != target && type != XA_STRING && type != _x.utf8string) || len == 0){
         1478                 if(xdata)
         1479                         XFree(xdata);
         1480                 return nil;
         1481         }
         1482         if(xdata){
         1483                 data = (uchar*)strdup((char*)xdata);
         1484                 XFree(xdata);
         1485                 return data;
         1486         }
         1487         return nil;
         1488 }
         1489 
         1490 char*
         1491 rpc_getsnarf(void)
         1492 {
         1493         uchar *data;
         1494         Atom clipboard;
         1495         XWindow xw;
         1496         Xwin *w;
         1497 
         1498         qlock(&clip.lk);
         1499         xlock();
         1500         w = _x.windows;
         1501         /*
         1502          * Have we snarfed recently and the X server hasn't caught up?
         1503          */
         1504         if(_x.putsnarf != _x.assertsnarf)
         1505                 goto mine;
         1506 
         1507         /*
         1508          * Is there a primary selection (highlighted text in an xterm)?
         1509          */
         1510         clipboard = XA_PRIMARY;
         1511         xw = XGetSelectionOwner(_x.display, XA_PRIMARY);
         1512         // TODO check more
         1513         if(xw == w->drawable){
         1514         mine:
         1515                 data = (uchar*)strdup(clip.buf);
         1516                 goto out;
         1517         }
         1518 
         1519         /*
         1520          * If not, is there a clipboard selection?
         1521          */
         1522         if(xw == None && _x.clipboard != None){
         1523                 clipboard = _x.clipboard;
         1524                 xw = XGetSelectionOwner(_x.display, _x.clipboard);
         1525                 if(xw == w->drawable)
         1526                         goto mine;
         1527         }
         1528 
         1529         /*
         1530          * If not, give up.
         1531          */
         1532         if(xw == None){
         1533                 data = nil;
         1534                 goto out;
         1535         }
         1536 
         1537         if((data = _xgetsnarffrom(w, xw, clipboard, _x.utf8string, 10, 100)) == nil)
         1538         if((data = _xgetsnarffrom(w, xw, clipboard, XA_STRING, 10, 100)) == nil){
         1539                 /* nothing left to do */
         1540         }
         1541 
         1542 out:
         1543         xunlock();
         1544         qunlock(&clip.lk);
         1545         return (char*)data;
         1546 }
         1547 
         1548 void
         1549 __xputsnarf(char *data)
         1550 {
         1551         XButtonEvent e;
         1552         Xwin *w;
         1553 
         1554         if(strlen(data) >= SnarfSize)
         1555                 return;
         1556         qlock(&clip.lk);
         1557         xlock();
         1558         w = _x.windows;
         1559         strcpy(clip.buf, data);
         1560         /* leave note for mouse proc to assert selection ownership */
         1561         _x.putsnarf++;
         1562 
         1563         /* send mouse a fake event so snarf is announced */
         1564         memset(&e, 0, sizeof e);
         1565         e.type = ButtonPress;
         1566         e.window = w->drawable;
         1567         e.state = ~0;
         1568         e.button = ~0;
         1569         XSendEvent(_x.display, w->drawable, True, ButtonPressMask, (XEvent*)&e);
         1570         XFlush(_x.display);
         1571         xunlock();
         1572         qunlock(&clip.lk);
         1573 }
         1574 
         1575 static int
         1576 _xselect(XEvent *e)
         1577 {
         1578         char *name;
         1579         XEvent r;
         1580         XSelectionRequestEvent *xe;
         1581         Atom a[4];
         1582 
         1583         memset(&r, 0, sizeof r);
         1584         xe = (XSelectionRequestEvent*)e;
         1585 if(0) fprint(2, "xselect target=%d requestor=%d property=%d selection=%d (sizeof atom=%d)\n",
         1586         xe->target, xe->requestor, xe->property, xe->selection, sizeof a[0]);
         1587         r.xselection.property = xe->property;
         1588         if(xe->target == _x.targets){
         1589                 a[0] = _x.utf8string;
         1590                 a[1] = XA_STRING;
         1591                 a[2] = _x.text;
         1592                 a[3] = _x.compoundtext;
         1593                 XChangeProperty(_x.display, xe->requestor, xe->property, XA_ATOM,
         1594                         32, PropModeReplace, (uchar*)a, nelem(a));
         1595         }else if(xe->target == XA_STRING
         1596         || xe->target == _x.utf8string
         1597         || xe->target == _x.text
         1598         || xe->target == _x.compoundtext
         1599         || ((name = XGetAtomName(_x.display, xe->target)) && strcmp(name, "text/plain;charset=UTF-8") == 0)){
         1600                 /* text/plain;charset=UTF-8 seems nonstandard but is used by Synergy */
         1601                 /* if the target is STRING we're supposed to reply with Latin1 XXX */
         1602                 qlock(&clip.lk);
         1603                 XChangeProperty(_x.display, xe->requestor, xe->property, xe->target,
         1604                         8, PropModeReplace, (uchar*)clip.buf, strlen(clip.buf));
         1605                 qunlock(&clip.lk);
         1606         }else{
         1607                 if(strcmp(name, "TIMESTAMP") != 0)
         1608                         fprint(2, "%s: cannot handle selection request for '%s' (%d)\n", argv0, name, (int)xe->target);
         1609                 r.xselection.property = None;
         1610         }
         1611 
         1612         r.xselection.display = xe->display;
         1613         /* r.xselection.property filled above */
         1614         r.xselection.target = xe->target;
         1615         r.xselection.type = SelectionNotify;
         1616         r.xselection.requestor = xe->requestor;
         1617         r.xselection.time = xe->time;
         1618         r.xselection.send_event = True;
         1619         r.xselection.selection = xe->selection;
         1620         XSendEvent(_x.display, xe->requestor, False, 0, &r);
         1621         XFlush(_x.display);
         1622         return 0;
         1623 }
         1624 
         1625 #ifdef APPLESNARF
         1626 char*
         1627 _applegetsnarf(void)
         1628 {
         1629         char *s, *t;
         1630         CFArrayRef flavors;
         1631         CFDataRef data;
         1632         CFIndex nflavor, ndata, j;
         1633         CFStringRef type;
         1634         ItemCount nitem;
         1635         PasteboardItemID id;
         1636         PasteboardSyncFlags flags;
         1637         UInt32 i;
         1638 
         1639 /*        fprint(2, "applegetsnarf\n"); */
         1640         qlock(&clip.lk);
         1641         if(clip.apple == nil){
         1642                 if(PasteboardCreate(kPasteboardClipboard, &clip.apple) != noErr){
         1643                         fprint(2, "apple pasteboard create failed\n");
         1644                         qunlock(&clip.lk);
         1645                         return nil;
         1646                 }
         1647         }
         1648         flags = PasteboardSynchronize(clip.apple);
         1649         if(flags&kPasteboardClientIsOwner){
         1650                 s = strdup(clip.buf);
         1651                 qunlock(&clip.lk);
         1652                 return s;
         1653         }
         1654         if(PasteboardGetItemCount(clip.apple, &nitem) != noErr){
         1655                 fprint(2, "apple pasteboard get item count failed\n");
         1656                 qunlock(&clip.lk);
         1657                 return nil;
         1658         }
         1659         for(i=1; i<=nitem; i++){
         1660                 if(PasteboardGetItemIdentifier(clip.apple, i, &id) != noErr)
         1661                         continue;
         1662                 if(PasteboardCopyItemFlavors(clip.apple, id, &flavors) != noErr)
         1663                         continue;
         1664                 nflavor = CFArrayGetCount(flavors);
         1665                 for(j=0; j<nflavor; j++){
         1666                         type = (CFStringRef)CFArrayGetValueAtIndex(flavors, j);
         1667                         if(!UTTypeConformsTo(type, CFSTR("public.utf16-plain-text")))
         1668                                 continue;
         1669                         if(PasteboardCopyItemFlavorData(clip.apple, id, type, &data) != noErr)
         1670                                 continue;
         1671                         ndata = CFDataGetLength(data);
         1672                         qunlock(&clip.lk);
         1673                         s = smprint("%.*S", ndata/2, (Rune*)CFDataGetBytePtr(data));
         1674                         CFRelease(flavors);
         1675                         CFRelease(data);
         1676                         for(t=s; *t; t++)
         1677                                 if(*t == '\r')
         1678                                         *t = '\n';
         1679                         return s;
         1680                 }
         1681                 CFRelease(flavors);
         1682         }
         1683         qunlock(&clip.lk);
         1684         return nil;
         1685 }
         1686 
         1687 void
         1688 _appleputsnarf(char *s)
         1689 {
         1690         CFDataRef cfdata;
         1691         PasteboardSyncFlags flags;
         1692 
         1693 /*        fprint(2, "appleputsnarf\n"); */
         1694 
         1695         if(strlen(s) >= SnarfSize)
         1696                 return;
         1697         qlock(&clip.lk);
         1698         strcpy(clip.buf, s);
         1699         runesnprint(clip.rbuf, nelem(clip.rbuf), "%s", s);
         1700         if(clip.apple == nil){
         1701                 if(PasteboardCreate(kPasteboardClipboard, &clip.apple) != noErr){
         1702                         fprint(2, "apple pasteboard create failed\n");
         1703                         qunlock(&clip.lk);
         1704                         return;
         1705                 }
         1706         }
         1707         if(PasteboardClear(clip.apple) != noErr){
         1708                 fprint(2, "apple pasteboard clear failed\n");
         1709                 qunlock(&clip.lk);
         1710                 return;
         1711         }
         1712         flags = PasteboardSynchronize(clip.apple);
         1713         if((flags&kPasteboardModified) || !(flags&kPasteboardClientIsOwner)){
         1714                 fprint(2, "apple pasteboard cannot assert ownership\n");
         1715                 qunlock(&clip.lk);
         1716                 return;
         1717         }
         1718         cfdata = CFDataCreate(kCFAllocatorDefault,
         1719                 (uchar*)clip.rbuf, runestrlen(clip.rbuf)*2);
         1720         if(cfdata == nil){
         1721                 fprint(2, "apple pasteboard cfdatacreate failed\n");
         1722                 qunlock(&clip.lk);
         1723                 return;
         1724         }
         1725         if(PasteboardPutItemFlavor(clip.apple, (PasteboardItemID)1,
         1726                 CFSTR("public.utf16-plain-text"), cfdata, 0) != noErr){
         1727                 fprint(2, "apple pasteboard putitem failed\n");
         1728                 CFRelease(cfdata);
         1729                 qunlock(&clip.lk);
         1730                 return;
         1731         }
         1732         /* CFRelease(cfdata); ??? */
         1733         qunlock(&clip.lk);
         1734 }
         1735 #endif        /* APPLESNARF */
         1736 
         1737 void
         1738 rpc_putsnarf(char *data)
         1739 {
         1740 #ifdef APPLESNARF
         1741         _appleputsnarf(data);
         1742 #endif
         1743         __xputsnarf(data);
         1744 }
         1745 
         1746 /*
         1747  * Send the mouse event back to the window manager.
         1748  * So that 9term can tell rio to pop up its button3 menu.
         1749  */
         1750 void
         1751 rpc_bouncemouse(Client *c, Mouse m)
         1752 {
         1753         Xwin *w = (Xwin*)c->view;
         1754         XButtonEvent e;
         1755         XWindow dw;
         1756 
         1757         xlock();
         1758         e.type = ButtonPress;
         1759         e.state = 0;
         1760         e.button = 0;
         1761         if(m.buttons&1)
         1762                 e.button = 1;
         1763         else if(m.buttons&2)
         1764                 e.button = 2;
         1765         else if(m.buttons&4)
         1766                 e.button = 3;
         1767         e.same_screen = 1;
         1768         XTranslateCoordinates(_x.display, w->drawable,
         1769                 DefaultRootWindow(_x.display),
         1770                 m.xy.x, m.xy.y, &e.x_root, &e.y_root, &dw);
         1771         e.root = DefaultRootWindow(_x.display);
         1772         e.window = e.root;
         1773         e.subwindow = None;
         1774         e.x = e.x_root;
         1775         e.y = e.y_root;
         1776 #undef time
         1777         e.time = CurrentTime;
         1778         XUngrabPointer(_x.display, m.msec);
         1779         XSendEvent(_x.display, e.root, True, ButtonPressMask, (XEvent*)&e);
         1780         XFlush(_x.display);
         1781         xunlock();
         1782 }