tdevdraw: move Client into devdraw.h and move global state in - plan9port - [fork] Plan 9 from user space
 (HTM) git clone git://src.adamsgaard.dk/plan9port
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 88ed92aa40ab5aa0f563624c488ba2a120990329
 (DIR) parent 933b98054f40bb224acda134d7bb77a023bcc57f
 (HTM) Author: Russ Cox <rsc@swtch.com>
       Date:   Wed,  8 Jan 2020 20:28:17 -0500
       
       devdraw: move Client into devdraw.h and move global state in
       
       Diffstat:
         M include/memdraw.h                   |       1 -
         M src/cmd/devdraw/devdraw.c           |     340 ++++++++++---------------------
         M src/cmd/devdraw/devdraw.h           |     169 +++++++++++++++++++++++++++++--
         M src/cmd/devdraw/mac-screen.h        |      15 +++++----------
         M src/cmd/devdraw/mac-screen.m        |      80 +++++++++++++++++--------------
         M src/cmd/devdraw/mac-srv.c           |     309 +++++++++++++------------------
         M src/cmd/devdraw/mkfile              |       6 +++++-
         M src/cmd/devdraw/mkwsysrules.sh      |       2 ++
         M src/cmd/devdraw/mouseswap.c         |       5 +++++
         M src/cmd/devdraw/winsize.c           |       5 +++++
         M src/cmd/devdraw/x11-init.c          |       3 ++-
       
       11 files changed, 470 insertions(+), 465 deletions(-)
       ---
 (DIR) diff --git a/include/memdraw.h b/include/memdraw.h
       t@@ -216,7 +216,6 @@ extern Memdrawparam*        _memimagedrawsetup(Memimage*,
                                        Rectangle, Memimage*, Point, Memimage*,
                                        Point, int);
        extern void                _memimagedraw(Memdrawparam*);
       -extern void                _drawreplacescreenimage(Memimage*);
        
        #if defined(__cplusplus)
        }
 (DIR) diff --git a/src/cmd/devdraw/devdraw.c b/src/cmd/devdraw/devdraw.c
       t@@ -8,154 +8,32 @@
        #include <draw.h>
        #include <memdraw.h>
        #include <memlayer.h>
       +#include <mouse.h>
       +#include <cursor.h>
       +#include <keyboard.h>
       +#include <drawfcall.h>
        #include "devdraw.h"
        
        extern void _flushmemscreen(Rectangle);
       -int forcedpi = 0;
       -int displaydpi = 100;
       -
       -#define NHASH (1<<5)
       -#define HASHMASK (NHASH-1)
       -
       -typedef struct Client Client;
       -typedef struct Draw Draw;
       -typedef struct DImage DImage;
       -typedef struct DScreen DScreen;
       -typedef struct CScreen CScreen;
       -typedef struct FChar FChar;
       -typedef struct Refresh Refresh;
       -typedef struct Refx Refx;
       -typedef struct DName DName;
       -
       -struct Draw
       -{
       -        QLock                lk;
       -        int                clientid;
       -        int                nclient;
       -        Client*                client[1];
       -        int                nname;
       -        DName*                name;
       -        int                vers;
       -        int                softscreen;
       -};
       -
       -struct Client
       -{
       -        /*Ref                r;*/
       -        DImage*                dimage[NHASH];
       -        CScreen*        cscreen;
       -        Refresh*        refresh;
       -        Rendez                refrend;
       -        uchar*                readdata;
       -        int                nreaddata;
       -        int                busy;
       -        int                clientid;
       -        int                slot;
       -        int                refreshme;
       -        int                infoid;
       -        int                op;
       -};
       -
       -struct Refresh
       -{
       -        DImage*                dimage;
       -        Rectangle        r;
       -        Refresh*        next;
       -};
       -
       -struct Refx
       -{
       -        Client*                client;
       -        DImage*                dimage;
       -};
       -
       -struct DName
       -{
       -        char                        *name;
       -        Client        *client;
       -        DImage*                dimage;
       -        int                        vers;
       -};
       -
       -struct FChar
       -{
       -        int                minx;        /* left edge of bits */
       -        int                maxx;        /* right edge of bits */
       -        uchar                miny;        /* first non-zero scan-line */
       -        uchar                maxy;        /* last non-zero scan-line + 1 */
       -        schar                left;        /* offset of baseline */
       -        uchar                width;        /* width of baseline */
       -};
       -
       -/*
       - * Reference counts in DImages:
       - *        one per open by original client
       - *        one per screen image or fill
       - *         one per image derived from this one by name
       - */
       -struct DImage
       -{
       -        int                id;
       -        int                ref;
       -        char                *name;
       -        int                vers;
       -        Memimage*        image;
       -        int                ascent;
       -        int                nfchar;
       -        FChar*                fchar;
       -        DScreen*        dscreen;        /* 0 if not a window */
       -        DImage*        fromname;        /* image this one is derived from, by name */
       -        DImage*                next;
       -};
       -
       -struct CScreen
       -{
       -        DScreen*        dscreen;
       -        CScreen*        next;
       -};
       -
       -struct DScreen
       -{
       -        int                id;
       -        int                public;
       -        int                ref;
       -        DImage        *dimage;
       -        DImage        *dfill;
       -        Memscreen*        screen;
       -        Client*                owner;
       -        DScreen*        next;
       -};
        
        static        Draw                sdraw;
       -static        Client                *client0;
       -static        Memimage        *screenimage;
       -static        Rectangle        flushrect;
       -static        int                waste;
       -static        DScreen*        dscreen;
       +Client                *client0;
        static        int                drawuninstall(Client*, int);
        static        Memimage*        drawinstall(Client*, int, Memimage*, DScreen*);
       -static        void                drawfreedimage(DImage*);
       +static        void                drawfreedimage(Client*, DImage*);
        
        void
       -_initdisplaymemimage(Memimage *m)
       +_initdisplaymemimage(Client *c, Memimage *m)
        {
       -        screenimage = m;
       +        c->screenimage = m;
                m->screenref = 1;
       -        client0 = mallocz(sizeof(Client), 1);
       -        if(client0 == nil){
       -                fprint(2, "initdraw: allocating client0: out of memory");
       -                abort();
       -        }
       -        client0->slot = 0;
       -        client0->clientid = ++sdraw.clientid;
       -        client0->op = SoverD;
       -        sdraw.client[0] = client0;
       -        sdraw.nclient = 1;
       -        sdraw.softscreen = 1;
       +        c->slot = 0;
       +        c->clientid = 1;
       +        c->op = SoverD;
        }
        
        void
       -_drawreplacescreenimage(Memimage *m)
       +_drawreplacescreenimage(Client *c, Memimage *m)
        {
                /*
                 * Replace the screen image because the screen
       t@@ -171,14 +49,17 @@ _drawreplacescreenimage(Memimage *m)
                 */
                Memimage *om;
        
       +        qlock(&c->inputlk);
                qlock(&sdraw.lk);
       -        om = screenimage;
       -        screenimage = m;
       +        om = c->screenimage;
       +        c->screenimage = m;
                m->screenref = 1;
       +        c->mouse.resized = 1;
                if(om && --om->screenref == 0){
                        _freememimage(om);
                }
                qunlock(&sdraw.lk);
       +        qunlock(&c->inputlk);
        }
        
        static
       t@@ -222,57 +103,57 @@ drawrefresh(Memimage *m, Rectangle r, void *v)
        }
        
        static void
       -addflush(Rectangle r)
       +addflush(Client *c, Rectangle r)
        {
                int abb, ar, anbb;
                Rectangle nbb;
        
       -        if(sdraw.softscreen==0 || !rectclip(&r, screenimage->r))
       +        if(/*sdraw.softscreen==0 ||*/ !rectclip(&r, c->screenimage->r))
                        return;
        
       -        if(flushrect.min.x >= flushrect.max.x){
       -                flushrect = r;
       -                waste = 0;
       +        if(c->flushrect.min.x >= c->flushrect.max.x){
       +                c->flushrect = r;
       +                c->waste = 0;
                        return;
                }
       -        nbb = flushrect;
       +        nbb = c->flushrect;
                combinerect(&nbb, r);
                ar = Dx(r)*Dy(r);
       -        abb = Dx(flushrect)*Dy(flushrect);
       +        abb = Dx(c->flushrect)*Dy(c->flushrect);
                anbb = Dx(nbb)*Dy(nbb);
                /*
                 * Area of new waste is area of new bb minus area of old bb,
                 * less the area of the new segment, which we assume is not waste.
                 * This could be negative, but that's OK.
                 */
       -        waste += anbb-abb - ar;
       -        if(waste < 0)
       -                waste = 0;
       +        c->waste += anbb-abb - ar;
       +        if(c->waste < 0)
       +                c->waste = 0;
                /*
                 * absorb if:
                 *        total area is small
                 *        waste is less than half total area
                 *         rectangles touch
                 */
       -        if(anbb<=1024 || waste*2<anbb || rectXrect(flushrect, r)){
       -                flushrect = nbb;
       +        if(anbb<=1024 || c->waste*2<anbb || rectXrect(c->flushrect, r)){
       +                c->flushrect = nbb;
                        return;
                }
                /* emit current state */
       -        if(flushrect.min.x < flushrect.max.x)
       -                _flushmemscreen(flushrect);
       -        flushrect = r;
       -        waste = 0;
       +        if(c->flushrect.min.x < c->flushrect.max.x)
       +                _flushmemscreen(c->flushrect);
       +        c->flushrect = r;
       +        c->waste = 0;
        }
        
        static
        void
       -dstflush(int dstid, Memimage *dst, Rectangle r)
       +dstflush(Client *c, int dstid, Memimage *dst, Rectangle r)
        {
                Memlayer *l;
        
                if(dstid == 0){
       -                combinerect(&flushrect, r);
       +                combinerect(&c->flushrect, r);
                        return;
                }
                /* how can this happen? -rsc, dec 12 2002 */
       t@@ -284,21 +165,21 @@ dstflush(int dstid, Memimage *dst, Rectangle r)
                if(l == nil)
                        return;
                do{
       -                if(l->screen->image->data != screenimage->data)
       +                if(l->screen->image->data != c->screenimage->data)
                                return;
                        r = rectaddpt(r, l->delta);
                        l = l->screen->image->layer;
                }while(l);
       -        addflush(r);
       +        addflush(c, r);
        }
        
        static
        void
       -drawflush(void)
       +drawflush(Client *c)
        {
       -        if(flushrect.min.x < flushrect.max.x)
       -                _flushmemscreen(flushrect);
       -        flushrect = Rect(10000, 10000, -10000, -10000);
       +        if(c->flushrect.min.x < c->flushrect.max.x)
       +                _flushmemscreen(c->flushrect);
       +        c->flushrect = Rect(10000, 10000, -10000, -10000);
        }
        
        static
       t@@ -312,12 +193,12 @@ drawcmp(char *a, char *b, int n)
        
        static
        DName*
       -drawlookupname(int n, char *str)
       +drawlookupname(Client *client, int n, char *str)
        {
                DName *name, *ename;
        
       -        name = sdraw.name;
       -        ename = &name[sdraw.nname];
       +        name = client->name;
       +        ename = &name[client->nname];
                for(; name<ename; name++)
                        if(drawcmp(name->name, str, n) == 0)
                                return name;
       t@@ -326,18 +207,18 @@ drawlookupname(int n, char *str)
        
        static
        int
       -drawgoodname(DImage *d)
       +drawgoodname(Client *client, DImage *d)
        {
                DName *n;
        
                /* if window, validate the screen's own images */
                if(d->dscreen)
       -                if(drawgoodname(d->dscreen->dimage) == 0
       -                || drawgoodname(d->dscreen->dfill) == 0)
       +                if(drawgoodname(client, d->dscreen->dimage) == 0
       +                || drawgoodname(client, d->dscreen->dfill) == 0)
                                return 0;
                if(d->name == nil)
                        return 1;
       -        n = drawlookupname(strlen(d->name), d->name);
       +        n = drawlookupname(client, strlen(d->name), d->name);
                if(n==nil || n->vers!=d->vers)
                        return 0;
                return 1;
       t@@ -356,7 +237,7 @@ drawlookup(Client *client, int id, int checkname)
                                 * BUG: should error out but too hard.
                                 * Return 0 instead.
                                 */
       -                        if(checkname && !drawgoodname(d))
       +                        if(checkname && !drawgoodname(client, d))
                                        return 0;
                                return d;
                        }
       t@@ -367,11 +248,11 @@ drawlookup(Client *client, int id, int checkname)
        
        static
        DScreen*
       -drawlookupdscreen(int id)
       +drawlookupdscreen(Client *c, int id)
        {
                DScreen *s;
        
       -        s = dscreen;
       +        s = c->dscreen;
                while(s){
                        if(s->id == id)
                                return s;
       t@@ -466,9 +347,9 @@ drawinstallscreen(Client *client, DScreen *d, int id, DImage *dimage, DImage *df
                        d->id = id;
                        d->screen = s;
                        d->public = public;
       -                d->next = dscreen;
       +                d->next = client->dscreen;
                        d->owner = client;
       -                dscreen = d;
       +                client->dscreen = d;
                }
                c->dscreen = d;
                d->ref++;
       t@@ -479,18 +360,18 @@ drawinstallscreen(Client *client, DScreen *d, int id, DImage *dimage, DImage *df
        
        static
        void
       -drawdelname(DName *name)
       +drawdelname(Client *client, DName *name)
        {
                int i;
        
       -        i = name-sdraw.name;
       -        memmove(name, name+1, (sdraw.nname-(i+1))*sizeof(DName));
       -        sdraw.nname--;
       +        i = name-client->name;
       +        memmove(name, name+1, (client->nname-(i+1))*sizeof(DName));
       +        client->nname--;
        }
        
        static
        void
       -drawfreedscreen(DScreen *this)
       +drawfreedscreen(Client *client, DScreen *this)
        {
                DScreen *ds, *next;
        
       t@@ -499,9 +380,9 @@ drawfreedscreen(DScreen *this)
                        fprint(2, "negative ref in drawfreedscreen\n");
                if(this->ref > 0)
                        return;
       -        ds = dscreen;
       +        ds = client->dscreen;
                if(ds == this){
       -                dscreen = this->next;
       +                client->dscreen = this->next;
                        goto Found;
                }
                while(next = ds->next){        /* assign = */
       t@@ -518,16 +399,16 @@ drawfreedscreen(DScreen *this)
        
            Found:
                if(this->dimage)
       -                drawfreedimage(this->dimage);
       +                drawfreedimage(client, this->dimage);
                if(this->dfill)
       -                drawfreedimage(this->dfill);
       +                drawfreedimage(client, this->dfill);
                free(this->screen);
                free(this);
        }
        
        static
        void
       -drawfreedimage(DImage *dimage)
       +drawfreedimage(Client *client, DImage *dimage)
        {
                int i;
                Memimage *l;
       t@@ -540,13 +421,13 @@ drawfreedimage(DImage *dimage)
                        return;
        
                /* any names? */
       -        for(i=0; i<sdraw.nname; )
       -                if(sdraw.name[i].dimage == dimage)
       -                        drawdelname(sdraw.name+i);
       +        for(i=0; i<client->nname; )
       +                if(client->name[i].dimage == dimage)
       +                        drawdelname(client, client->name+i);
                        else
                                i++;
                if(dimage->fromname){        /* acquired by name; owned by someone else*/
       -                drawfreedimage(dimage->fromname);
       +                drawfreedimage(client, dimage->fromname);
                        goto Return;
                }
                ds = dimage->dscreen;
       t@@ -554,16 +435,16 @@ drawfreedimage(DImage *dimage)
                dimage->dscreen = nil;        /* paranoia */
                dimage->image = nil;
                if(ds){
       -                if(l->data == screenimage->data)
       -                        addflush(l->layer->screenr);
       +                if(l->data == client->screenimage->data)
       +                        addflush(client, l->layer->screenr);
                        if(l->layer->refreshfn == drawrefresh)        /* else true owner will clean up */
                                free(l->layer->refreshptr);
                        l->layer->refreshptr = nil;
       -                if(drawgoodname(dimage))
       +                if(drawgoodname(client, dimage))
                                memldelete(l);
                        else
                                memlfree(l);
       -                drawfreedscreen(ds);
       +                drawfreedscreen(client, ds);
                }else{
                        if(l->screenref==0)
                                freememimage(l);
       t@@ -584,14 +465,14 @@ drawuninstallscreen(Client *client, CScreen *this)
                cs = client->cscreen;
                if(cs == this){
                        client->cscreen = this->next;
       -                drawfreedscreen(this->dscreen);
       +                drawfreedscreen(client, this->dscreen);
                        free(this);
                        return;
                }
                while(next = cs->next){        /* assign = */
                        if(next == this){
                                cs->next = this->next;
       -                        drawfreedscreen(this->dscreen);
       +                        drawfreedscreen(client, this->dscreen);
                                free(this);
                                return;
                        }
       t@@ -608,7 +489,7 @@ drawuninstall(Client *client, int id)
                for(l=&client->dimage[id&HASHMASK]; (d=*l) != nil; l=&d->next){
                        if(d->id == id){
                                *l = d->next;
       -                        drawfreedimage(d);
       +                        drawfreedimage(client, d);
                                return 0;
                        }
                }
       t@@ -622,14 +503,14 @@ drawaddname(Client *client, DImage *di, int n, char *str, char **err)
                DName *name, *ename, *new, *t;
                char *ns;
        
       -        name = sdraw.name;
       -        ename = &name[sdraw.nname];
       +        name = client->name;
       +        ename = &name[client->nname];
                for(; name<ename; name++)
                        if(drawcmp(name->name, str, n) == 0){
                                *err = "image name in use";
                                return -1;
                        }
       -        t = mallocz((sdraw.nname+1)*sizeof(DName), 1);
       +        t = mallocz((client->nname+1)*sizeof(DName), 1);
                ns = malloc(n+1);
                if(t == nil || ns == nil){
                        free(t);
       t@@ -637,16 +518,16 @@ drawaddname(Client *client, DImage *di, int n, char *str, char **err)
                        *err = "out of memory";
                        return -1;
                }
       -        memmove(t, sdraw.name, sdraw.nname*sizeof(DName));
       -        free(sdraw.name);
       -        sdraw.name = t;
       -        new = &sdraw.name[sdraw.nname++];
       +        memmove(t, client->name, client->nname*sizeof(DName));
       +        free(client->name);
       +        client->name = t;
       +        new = &client->name[client->nname++];
                new->name = ns;
                memmove(new->name, str, n);
                new->name[n] = 0;
                new->dimage = di;
                new->client = client;
       -        new->vers = ++sdraw.vers;
       +        new->vers = ++client->namevers;
                return 0;
        }
        
       t@@ -738,12 +619,9 @@ drawcoord(uchar *p, uchar *maxp, int oldx, int *newx)
        }
        
        int
       -_drawmsgread(void *a, int n)
       +_drawmsgread(Client *cl, void *a, int n)
        {
       -        Client *cl;
       -
                qlock(&sdraw.lk);
       -        cl = client0;
                if(cl->readdata == nil){
                        werrstr("no draw data");
                        goto err;
       t@@ -765,14 +643,13 @@ err:
        }
        
        int
       -_drawmsgwrite(void *v, int n)
       +_drawmsgwrite(Client *client, void *v, int n)
        {
                char cbuf[40], *err, ibuf[12*12+1], *s;
                int c, ci, doflush, dstid, e0, e1, esize, j, m;
                int ni, nw, oesize, oldn, op, ox, oy, repl, scrnid, y;
                uchar *a, refresh, *u;
                u32int chan, value;
       -        Client *client;
                CScreen *cs;
                DImage *di, *ddst, *dsrc, *font, *ll;
                DName *dn;
       t@@ -790,7 +667,6 @@ _drawmsgwrite(void *v, int n)
                a = v;
                m = 0;
                oldn = n;
       -        client = client0;
        
                while((n-=m) > 0){
                        a += m;
       t@@ -844,7 +720,7 @@ _drawmsgwrite(void *v, int n)
                                        l = memlalloc(scrn, r, reffn, 0, value);
                                        if(l == 0)
                                                goto Edrawmem;
       -                                addflush(l->layer->screenr);
       +                                addflush(client, l->layer->screenr);
                                        l->clipr = clipr;
                                        rectclip(&l->clipr, r);
                                        if(drawinstall(client, dstid, l, dscrn) == 0){
       t@@ -891,7 +767,7 @@ _drawmsgwrite(void *v, int n)
                                dstid = BGLONG(a+1);
                                if(dstid == 0)
                                        goto Ebadarg;
       -                        if(drawlookupdscreen(dstid))
       +                        if(drawlookupdscreen(client, dstid))
                                        goto Escreenexists;
                                ddst = drawlookup(client, BGLONG(a+5), 1);
                                dsrc = drawlookup(client, BGLONG(a+9), 1);
       t@@ -935,7 +811,7 @@ _drawmsgwrite(void *v, int n)
                                drawpoint(&q, a+37);
                                op = drawclientop(client);
                                memdraw(dst, r, src, p, mask, q, op);
       -                        dstflush(dstid, dst, r);
       +                        dstflush(client, dstid, dst, r);
                                continue;
        
                        /* toggle debugging: 'D' val[1] */
       t@@ -984,7 +860,7 @@ _drawmsgwrite(void *v, int n)
                                        memarc(dst, p, e0, e1, c, src, sp, ox, oy, op);
                                }else
                                        memellipse(dst, p, e0, e1, c, src, sp, op);
       -                        dstflush(dstid, dst, Rect(p.x-e0-j, p.y-e1-j, p.x+e0+j+1, p.y+e1+j+1));
       +                        dstflush(client, dstid, dst, Rect(p.x-e0-j, p.y-e1-j, p.x+e0+j+1, p.y+e1+j+1));
                                continue;
        
                        /* free: 'f' id[4] */
       t@@ -1049,7 +925,7 @@ _drawmsgwrite(void *v, int n)
                                        goto Eshortdraw;
                                if(drawlookup(client, 0, 0))
                                        goto Eimageexists;
       -                        drawinstall(client, 0, screenimage, 0);
       +                        drawinstall(client, 0, client->screenimage, 0);
                                client->infoid = 0;
                                continue;
        
       t@@ -1061,7 +937,7 @@ _drawmsgwrite(void *v, int n)
                                if(client->infoid < 0)
                                        goto Enodrawimage;
                                if(client->infoid == 0){
       -                                i = screenimage;
       +                                i = client->screenimage;
                                        if(i == nil)
                                                goto Enodrawimage;
                                }else{
       t@@ -1102,10 +978,10 @@ _drawmsgwrite(void *v, int n)
                                                err = "unknown query";
                                                goto error;
                                        case 'd':        /* dpi */
       -                                        if(forcedpi)
       -                                                fmtprint(&fmt, "%11d ", forcedpi);
       +                                        if(client->forcedpi)
       +                                                fmtprint(&fmt, "%11d ", client->forcedpi);
                                                else
       -                                                fmtprint(&fmt, "%11d ", displaydpi);
       +                                                fmtprint(&fmt, "%11d ", client->displaydpi);
                                                break;
                                        }
                                }
       t@@ -1169,7 +1045,7 @@ _drawmsgwrite(void *v, int n)
                                if(dstid==0 || dst->layer!=nil){
                                        /* BUG: this is terribly inefficient: update maximal containing rect*/
                                        r = memlinebbox(p, q, e0, e1, j);
       -                                dstflush(dstid, dst, insetrect(r, -(1+1+j)));
       +                                dstflush(client, dstid, dst, insetrect(r, -(1+1+j)));
                                }
                                continue;
        
       t@@ -1198,7 +1074,7 @@ _drawmsgwrite(void *v, int n)
                                dstid = BGLONG(a+1);
                                if(drawlookup(client, dstid, 0))
                                        goto Eimageexists;
       -                        dn = drawlookupname(j, (char*)a+6);
       +                        dn = drawlookupname(client, j, (char*)a+6);
                                if(dn == nil)
                                        goto Enoname;
                                s = malloc(j+1);
       t@@ -1239,12 +1115,12 @@ _drawmsgwrite(void *v, int n)
                                        if(drawaddname(client, di, j, (char*)a+7, &err) < 0)
                                                goto error;
                                else{
       -                                dn = drawlookupname(j, (char*)a+7);
       +                                dn = drawlookupname(client, j, (char*)a+7);
                                        if(dn == nil)
                                                goto Enoname;
                                        if(dn->dimage != di)
                                                goto Ewrongname;
       -                                drawdelname(dn);
       +                                drawdelname(client, dn);
                                }
                                continue;
        
       t@@ -1266,8 +1142,8 @@ _drawmsgwrite(void *v, int n)
                                                goto error;
                                        }
                                        if(ni > 0){
       -                                        addflush(r);
       -                                        addflush(dst->layer->screenr);
       +                                        addflush(client, r);
       +                                        addflush(client, dst->layer->screenr);
                                                ll = drawlookup(client, BGLONG(a+1), 1);
                                                drawrefreshscreen(ll, client);
                                        }
       t@@ -1316,7 +1192,7 @@ _drawmsgwrite(void *v, int n)
                                if(pp == nil)
                                        goto Enomem;
                                doflush = 0;
       -                        if(dstid==0 || (dst->layer && dst->layer->screen->image->data == screenimage->data))
       +                        if(dstid==0 || (dst->layer && dst->layer->screen->image->data == client->screenimage->data))
                                        doflush = 1;        /* simplify test in loop */
                                ox = oy = 0;
                                esize = 0;
       t@@ -1353,12 +1229,12 @@ _drawmsgwrite(void *v, int n)
                                                        combinerect(&r, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1));
                                                }
                                                if(rectclip(&r, dst->clipr))                /* should perhaps be an arg to dstflush */
       -                                                dstflush(dstid, dst, r);
       +                                                dstflush(client, dstid, dst, r);
                                        }
                                        pp[y] = p;
                                }
                                if(y == 1)
       -                                dstflush(dstid, dst, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1));
       +                                dstflush(client, dstid, dst, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1));
                                op = drawclientop(client);
                                if(*a == 'p')
                                        mempoly(dst, pp, ni, e0, e1, j, src, sp, op);
       t@@ -1462,7 +1338,7 @@ _drawmsgwrite(void *v, int n)
                                }
                                dst->clipr = clipr;
                                p.y -= font->ascent;
       -                        dstflush(dstid, dst, Rect(p.x, p.y, q.x, p.y+Dy(font->image->r)));
       +                        dstflush(client, dstid, dst, Rect(p.x, p.y, q.x, p.y+Dy(font->image->r)));
                                continue;
        
                        /* use public screen: 'S' id[4] chan[4] */
       t@@ -1473,7 +1349,7 @@ _drawmsgwrite(void *v, int n)
                                dstid = BGLONG(a+1);
                                if(dstid == 0)
                                        goto Ebadarg;
       -                        dscrn = drawlookupdscreen(dstid);
       +                        dscrn = drawlookupdscreen(client, dstid);
                                if(dscrn==0 || (dscrn->public==0 && dscrn->owner!=client))
                                        goto Enodrawscreen;
                                if(dscrn->screen->image->chan != BGLONG(a+5)){
       t@@ -1522,9 +1398,9 @@ _drawmsgwrite(void *v, int n)
                                        memltofrontn(lp, nw);
                                else
                                        memltorearn(lp, nw);
       -                        if(lp[0]->layer->screen->image->data == screenimage->data)
       +                        if(lp[0]->layer->screen->image->data == client->screenimage->data)
                                        for(j=0; j<nw; j++)
       -                                        addflush(lp[j]->layer->screenr);
       +                                        addflush(client, lp[j]->layer->screenr);
                                free(lp);
                                ll = drawlookup(client, BGLONG(a+1+1+2), 1);
                                drawrefreshscreen(ll, client);
       t@@ -1533,7 +1409,7 @@ _drawmsgwrite(void *v, int n)
                        /* visible: 'v' */
                        case 'v':
                                m = 1;
       -                        drawflush();
       +                        drawflush(client);
                                continue;
        
                        /* write: 'y' id[4] R[4*4] data[x*1] */
       t@@ -1555,7 +1431,7 @@ _drawmsgwrite(void *v, int n)
                                        err = "bad writeimage call";
                                        goto error;
                                }
       -                        dstflush(dstid, dst, r);
       +                        dstflush(client, dstid, dst, r);
                                m += y;
                                continue;
                        }
 (DIR) diff --git a/src/cmd/devdraw/devdraw.h b/src/cmd/devdraw/devdraw.h
       t@@ -1,10 +1,167 @@
       -int _drawmsgread(void*, int);
       -int _drawmsgwrite(void*, int);
       -void _initdisplaymemimage(Memimage*);
       +
       +#define NHASH (1<<5)
       +#define HASHMASK (NHASH-1)
       +
       +typedef struct Kbdbuf Kbdbuf;
       +typedef struct Mousebuf Mousebuf;
       +typedef struct Tagbuf Tagbuf;
       +
       +typedef struct Client Client;
       +typedef struct Draw Draw;
       +typedef struct DImage DImage;
       +typedef struct DScreen DScreen;
       +typedef struct CScreen CScreen;
       +typedef struct FChar FChar;
       +typedef struct Refresh Refresh;
       +typedef struct Refx Refx;
       +typedef struct DName DName;
       +
       +struct Draw
       +{
       +        QLock                lk;
       +};
       +
       +struct Kbdbuf
       +{
       +        Rune r[256];
       +        int ri;
       +        int wi;
       +        int stall;
       +        int alting;
       +};
       +
       +struct Mousebuf
       +{
       +        Mouse m[256];
       +        Mouse last;
       +        int ri;
       +        int wi;
       +        int stall;
       +        int resized;
       +};
       +
       +struct Tagbuf
       +{
       +        int t[256];
       +        int ri;
       +        int wi;
       +};
       +
       +struct Client
       +{
       +        /*Ref                r;*/
       +        DImage*                dimage[NHASH];
       +        CScreen*        cscreen;
       +        Refresh*        refresh;
       +        Rendez                refrend;
       +        uchar*                readdata;
       +        int                nreaddata;
       +        int                busy;
       +        int                clientid;
       +        int                slot;
       +        int                refreshme;
       +        int                infoid;
       +        int                op;
       +
       +        int                displaydpi;
       +        int                forcedpi;
       +        int                waste;
       +        Rectangle        flushrect;
       +        Memimage        *screenimage;
       +        DScreen*        dscreen;
       +        int                nname;
       +        DName*                name;
       +        int                namevers;
       +
       +        int                rfd;
       +        int                wfd;
       +        
       +        QLock inputlk;
       +        Kbdbuf kbd;
       +        Mousebuf mouse;
       +        Tagbuf kbdtags;
       +        Tagbuf mousetags;
       +        Rectangle mouserect;
       +};
       +
       +struct Refresh
       +{
       +        DImage*                dimage;
       +        Rectangle        r;
       +        Refresh*        next;
       +};
       +
       +struct Refx
       +{
       +        Client*                client;
       +        DImage*                dimage;
       +};
       +
       +struct DName
       +{
       +        char                        *name;
       +        Client        *client;
       +        DImage*                dimage;
       +        int                        vers;
       +};
       +
       +struct FChar
       +{
       +        int                minx;        /* left edge of bits */
       +        int                maxx;        /* right edge of bits */
       +        uchar                miny;        /* first non-zero scan-line */
       +        uchar                maxy;        /* last non-zero scan-line + 1 */
       +        schar                left;        /* offset of baseline */
       +        uchar                width;        /* width of baseline */
       +};
       +
       +/*
       + * Reference counts in DImages:
       + *        one per open by original client
       + *        one per screen image or fill
       + *         one per image derived from this one by name
       + */
       +struct DImage
       +{
       +        int                id;
       +        int                ref;
       +        char                *name;
       +        int                vers;
       +        Memimage*        image;
       +        int                ascent;
       +        int                nfchar;
       +        FChar*                fchar;
       +        DScreen*        dscreen;        /* 0 if not a window */
       +        DImage*        fromname;        /* image this one is derived from, by name */
       +        DImage*                next;
       +};
       +
       +struct CScreen
       +{
       +        DScreen*        dscreen;
       +        CScreen*        next;
       +};
       +
       +struct DScreen
       +{
       +        int                id;
       +        int                public;
       +        int                ref;
       +        DImage        *dimage;
       +        DImage        *dfill;
       +        Memscreen*        screen;
       +        Client*                owner;
       +        DScreen*        next;
       +};
       +
       +int _drawmsgread(Client*, void*, int);
       +int _drawmsgwrite(Client*, void*, int);
       +void _initdisplaymemimage(Client*, Memimage*);
       +void        _drawreplacescreenimage(Client*, Memimage*);
       +
        int _latin1(Rune*, int);
        int parsewinsize(char*, Rectangle*, int*);
        int mouseswap(int);
       -void abortcompose(void);
       +void abortcompose(Client*);
        
       -extern int displaydpi;
       -extern int forcedpi;
       +extern Client *client0;
 (DIR) diff --git a/src/cmd/devdraw/mac-screen.h b/src/cmd/devdraw/mac-screen.h
       t@@ -1,6 +1,6 @@
        #define setcursor dsetcursor
        
       -Memimage *attachscreen(char*, char*);
       +Memimage *attachscreen(Client*, char*, char*);
        void        setmouse(Point);
        void        setcursor(Cursor*, Cursor2*);
        void        setlabel(char*);
       t@@ -8,17 +8,12 @@ char*        getsnarf(void);
        void        putsnarf(char*);
        void        topwin(void);
        
       -void        mousetrack(int, int, int, uint);
       -void        keystroke(int);
       +void        mousetrack(Client*, int, int, int, uint);
       +void        keystroke(Client*, int);
        void        kicklabel(char*);
        
       -void        servep9p(void);
       -void        zlock(void);
       -void        zunlock(void);
       +void        servep9p(Client*);
        
       -void resizeimg(void);
       +void resizeimg(Client*);
        
       -Rectangle mouserect;
       -
       -int mouseresized;
        void resizewindow(Rectangle);
 (DIR) diff --git a/src/cmd/devdraw/mac-screen.m b/src/cmd/devdraw/mac-screen.m
       t@@ -15,10 +15,13 @@
        #include <thread.h>
        #include <draw.h>
        #include <memdraw.h>
       -#include <keyboard.h>
       +#include <memlayer.h>
       +#include <mouse.h>
        #include <cursor.h>
       -#include "mac-screen.h"
       +#include <keyboard.h>
       +#include <drawfcall.h>
        #include "devdraw.h"
       +#include "mac-screen.h"
        #include "bigarrow.h"
        #include "glendapng.h"
        
       t@@ -31,7 +34,7 @@ AUTOFRAMEWORK(QuartzCore)
        static void setprocname(const char*);
        static uint keycvt(uint);
        static uint msec(void);
       -static Memimage* initimg(void);
       +static Memimage* initimg(Client*);
        
        void
        usage(void)
       t@@ -48,6 +51,7 @@ usage(void)
        + (void)callsetcursor:(NSValue *)v;
        @end
        @interface DevDrawView : NSView<NSTextInputClient>
       +@property (nonatomic) Client *client;
        - (void)clearInput;
        - (void)getmouse:(NSEvent *)e;
        - (void)sendmouse:(NSUInteger)b;
       t@@ -96,6 +100,13 @@ threadmain(int argc, char **argv)
                        usage();
                }ARGEND
        
       +        client0 = mallocz(sizeof(Client), 1);
       +        client0->displaydpi = 100;
       +        if(client0 == nil){
       +                fprint(2, "initdraw: allocating client0: out of memory");
       +                abort();
       +        }
       +
                setprocname(argv0);
        
                @autoreleasepool{
       t@@ -113,7 +124,9 @@ callservep9p(void *v)
        {
                USED(v);
        
       -        servep9p();
       +        client0->rfd = 3;
       +        client0->wfd = 4;
       +        servep9p(client0);
                [NSApp terminate:myApp];
        }
        
       t@@ -167,6 +180,7 @@ callservep9p(void *v)
                [win setDelegate:myApp];
        
                myContent = [DevDrawView new];
       +        myContent.client = client0;
                [win setContentView:myContent];
                [myContent setWantsLayer:YES];
                [myContent setLayerContentsRedrawPolicy:NSViewLayerContentsRedrawOnSetNeedsDisplay];
       t@@ -353,7 +367,7 @@ struct Cursors {
        - (void)windowDidResize:(NSNotification *)notification
        {
                if(![myContent inLiveResize] && img) {
       -                resizeimg();
       +                resizeimg(myContent.client);
                }
        }
        
       t@@ -463,7 +477,7 @@ struct Cursors {
                                b |= 4;
                        [self sendmouse:b];
                }else if(m & ~omod & NSEventModifierFlagOption)
       -                keystroke(Kalt);
       +                keystroke(self.client, Kalt);
        
                omod = m;
        }
       t@@ -520,7 +534,7 @@ struct Cursors {
                if(b == 1){
                        m = [e modifierFlags];
                        if(m & NSEventModifierFlagOption){
       -                        abortcompose();
       +                        abortcompose(self.client);
                                b = 2;
                        }else
                        if(m & NSEventModifierFlagCommand)
       t@@ -535,9 +549,9 @@ struct Cursors {
        
                p = [self.window convertPointToBacking:
                        [self.window mouseLocationOutsideOfEventStream]];
       -        p.y = Dy(mouserect) - p.y;
       +        p.y = Dy(self.client->mouserect) - p.y;
                // LOG(@"(%g, %g) <- sendmouse(%d)", p.x, p.y, (uint)b);
       -        mousetrack(p.x, p.y, b, msec());
       +        mousetrack(self.client, p.x, p.y, b, msec());
                if(b && _lastInputRect.size.width && _lastInputRect.size.height)
                        [self resetLastInputRect];
        }
       t@@ -551,14 +565,14 @@ struct Cursors {
        {
                [super viewDidEndLiveResize];
                if(img)
       -                resizeimg();
       +                resizeimg(self.client);
        }
        
        - (void)viewDidChangeBackingProperties
        {
                [super viewDidChangeBackingProperties];
                if(img)
       -                resizeimg();
       +                resizeimg(self.client);
        }
        
        // conforms to protocol NSTextInputClient
       t@@ -617,24 +631,24 @@ struct Cursors {
                        LOG(@"text length %ld", _tmpText.length);
                        for(i = 0; i <= _tmpText.length; ++i){
                                if(i == _markedRange.location)
       -                                keystroke('[');
       +                                keystroke(self.client, '[');
                                if(_selectedRange.length){
                                        if(i == _selectedRange.location)
       -                                        keystroke('{');
       +                                        keystroke(self.client, '{');
                                        if(i == NSMaxRange(_selectedRange))
       -                                        keystroke('}');
       +                                        keystroke(self.client, '}');
                                        }
                                if(i == NSMaxRange(_markedRange))
       -                                keystroke(']');
       +                                keystroke(self.client, ']');
                                if(i < _tmpText.length)
       -                                keystroke([_tmpText characterAtIndex:i]);
       +                                keystroke(self.client, [_tmpText characterAtIndex:i]);
                        }
                        int l;
                        l = 1 + _tmpText.length - NSMaxRange(_selectedRange)
                                + (_selectedRange.length > 0);
                        LOG(@"move left %d", l);
                        for(i = 0; i < l; ++i)
       -                        keystroke(Kleft);
       +                        keystroke(self.client, Kleft);
                }
        
                LOG(@"text: \"%@\"  (%ld,%ld)  (%ld,%ld)", _tmpText,
       t@@ -649,7 +663,7 @@ struct Cursors {
                LOG(@"unmarkText");
                len = [_tmpText length];
                //for(i = 0; i < len; ++i)
       -        //        keystroke([_tmpText characterAtIndex:i]);
       +        //        keystroke(self.client, [_tmpText characterAtIndex:i]);
                [_tmpText deleteCharactersInRange:NSMakeRange(0, len)];
                _markedRange = NSMakeRange(NSNotFound, 0);
                _selectedRange = NSMakeRange(0, 0);
       t@@ -691,7 +705,7 @@ struct Cursors {
        
                len = [s length];
                for(i = 0; i < len; ++i)
       -                keystroke([s characterAtIndex:i]);
       +                keystroke(self.client, [s characterAtIndex:i]);
                [_tmpText deleteCharactersInRange:NSMakeRange(0, _tmpText.length)];
                _markedRange = NSMakeRange(NSNotFound, 0);
                _selectedRange = NSMakeRange(0, 0);
       t@@ -731,7 +745,7 @@ struct Cursors {
                                k += Kcmd;
                }
                if(k>0)
       -                keystroke(k);
       +                keystroke(self.client, k);
        }
        
        // Helper for managing input rect approximately
       t@@ -762,11 +776,11 @@ struct Cursors {
                                + (_selectedRange.length > 0);
                        LOG(@"move right %d", l);
                        for(i = 0; i < l; ++i)
       -                        keystroke(Kright);
       +                        keystroke(self.client, Kright);
                        l = _tmpText.length+2+2*(_selectedRange.length > 0);
                        LOG(@"backspace %d", l);
                        for(uint i = 0; i < l; ++i)
       -                        keystroke(Kbs);
       +                        keystroke(self.client, Kbs);
                }
        }
        
       t@@ -915,7 +929,7 @@ keycvt(uint code)
        }
        
        Memimage*
       -attachscreen(char *label, char *winsize)
       +attachscreen(Client *c, char *label, char *winsize)
        {
                LOG(@"attachscreen(%s, %s)", label, winsize);
                [AppDelegate
       t@@ -924,12 +938,12 @@ attachscreen(char *label, char *winsize)
                        waitUntilDone:YES];
                kicklabel(label);
                setcursor(nil, nil);
       -        mouseresized = 0;
       -        return initimg();
       +        c->mouse.resized = 0;
       +        return initimg(c);
        }
        
        static Memimage*
       -initimg(void)
       +initimg(Client *c)
        {
        @autoreleasepool{
                CGFloat scale;
       t@@ -937,11 +951,11 @@ initimg(void)
                MTLTextureDescriptor *textureDesc;
        
                size = [myContent convertSizeToBacking:[myContent bounds].size];
       -        mouserect = Rect(0, 0, size.width, size.height);
       +        c->mouserect = Rect(0, 0, size.width, size.height);
        
                LOG(@"initimg %.0f %.0f", size.width, size.height);
        
       -        img = allocmemimage(mouserect, XRGB32);
       +        img = allocmemimage(c->mouserect, XRGB32);
                if(img == nil)
                        panic("allocmemimage: %r");
                if(img->data == nil)
       t@@ -966,7 +980,7 @@ initimg(void)
                // This formula gives us 220 for retina, 110 otherwise.
                // That's not quite right but it's close to correct.
                // https://en.wikipedia.org/wiki/Retina_display#Models
       -        displaydpi = scale * 110;
       +        c->displaydpi = scale * 110;
        }
                LOG(@"initimg return");
        
       t@@ -1109,13 +1123,9 @@ topwin(void)
        }
        
        void
       -resizeimg(void)
       +resizeimg(Client *c)
        {
       -        zlock();
       -        _drawreplacescreenimage(initimg());
       -
       -        mouseresized = 1;
       -        zunlock();
       +        _drawreplacescreenimage(c, initimg(c));
                [myContent sendmouse:0];
        }
        
 (DIR) diff --git a/src/cmd/devdraw/mac-srv.c b/src/cmd/devdraw/mac-srv.c
       t@@ -7,70 +7,23 @@
        #include <thread.h>
        #include <draw.h>
        #include <memdraw.h>
       +#include <memlayer.h>
        #include <keyboard.h>
        #include <mouse.h>
        #include <cursor.h>
        #include <drawfcall.h>
       -#include "mac-screen.h"
        #include "devdraw.h"
       +#include "mac-screen.h"
        
       -typedef struct Kbdbuf Kbdbuf;
       -typedef struct Mousebuf Mousebuf;
       -typedef struct Fdbuf Fdbuf;
       -typedef struct Tagbuf Tagbuf;
       -
       -struct Kbdbuf
       -{
       -        Rune r[256];
       -        int ri;
       -        int wi;
       -        int stall;
       -};
       -
       -struct Mousebuf
       -{
       -        Mouse m[256];
       -        Mouse last;
       -        int ri;
       -        int wi;
       -        int stall;
       -};
       -
       -struct Tagbuf
       -{
       -        int t[256];
       -        int ri;
       -        int wi;
       -};
       -
       -Kbdbuf kbd;
       -Mousebuf mouse;
       -Tagbuf kbdtags;
       -Tagbuf mousetags;
       -
       -void runmsg(Wsysmsg*);
       -void replymsg(Wsysmsg*);
       -void matchkbd(void);
       -void matchmouse(void);
       -
       -
       -QLock lk;
       -void
       -zlock(void)
       -{
       -        qlock(&lk);
       -}
       -
       -void
       -zunlock(void)
       -{
       -        qunlock(&lk);
       -}
       +void runmsg(Client*, Wsysmsg*);
       +void replymsg(Client*, Wsysmsg*);
       +void matchkbd(Client*);
       +void matchmouse(Client*);
        
        int trace = 0;
        
        void
       -servep9p(void)
       +servep9p(Client *c)
        {
                uchar buf[4], *mbuf;
                int nmbuf, n, nn;
       t@@ -80,7 +33,7 @@ servep9p(void)
        
                mbuf = nil;
                nmbuf = 0;
       -        while((n = read(3, buf, 4)) == 4){
       +        while((n = read(c->rfd, buf, 4)) == 4){
                        GET(buf, n);
                        if(n > nmbuf){
                                free(mbuf);
       t@@ -90,7 +43,7 @@ servep9p(void)
                                nmbuf = n;
                        }
                        memmove(mbuf, buf, 4);
       -                nn = readn(3, mbuf+4, n-4);
       +                nn = readn(c->rfd, mbuf+4, n-4);
                        if(nn != n-4)
                                sysfatal("eof during message");
        
       t@@ -98,19 +51,19 @@ servep9p(void)
                        if(convM2W(mbuf, nn+4, &m) <= 0)
                                sysfatal("cannot convert message");
                        if(trace) fprint(2, "%ud [%d] <- %W\n", nsec()/1000000, threadid(), &m);
       -                runmsg(&m);
       +                runmsg(c, &m);
                }
        }
        
        void
       -replyerror(Wsysmsg *m)
       +replyerror(Client *c, Wsysmsg *m)
        {
                char err[256];
        
                rerrstr(err, sizeof err);
                m->type = Rerror;
                m->error = err;
       -        replymsg(m);
       +        replymsg(c, m);
        }
        
        /*
       t@@ -118,7 +71,7 @@ replyerror(Wsysmsg *m)
         * Might queue for later (kbd, mouse read)
         */
        void
       -runmsg(Wsysmsg *m)
       +runmsg(Client *c, Wsysmsg *m)
        {
                static uchar buf[65536];
                int n;
       t@@ -127,38 +80,38 @@ runmsg(Wsysmsg *m)
                switch(m->type){
                case Tinit:
                        memimageinit();
       -                i = attachscreen(m->label, m->winsize);
       -                _initdisplaymemimage(i);
       -                replymsg(m);
       +                i = attachscreen(c, m->label, m->winsize);
       +                _initdisplaymemimage(c, i);
       +                replymsg(c, m);
                        break;
        
                case Trdmouse:
       -                zlock();
       -                mousetags.t[mousetags.wi++] = m->tag;
       -                if(mousetags.wi == nelem(mousetags.t))
       -                        mousetags.wi = 0;
       -                if(mousetags.wi == mousetags.ri)
       +                qlock(&c->inputlk);
       +                c->mousetags.t[c->mousetags.wi++] = m->tag;
       +                if(c->mousetags.wi == nelem(c->mousetags.t))
       +                        c->mousetags.wi = 0;
       +                if(c->mousetags.wi == c->mousetags.ri)
                                sysfatal("too many queued mouse reads");
       -                mouse.stall = 0;
       -                matchmouse();
       -                zunlock();
       +                c->mouse.stall = 0;
       +                matchmouse(c);
       +                qunlock(&c->inputlk);
                        break;
        
                case Trdkbd:
       -                zlock();
       -                kbdtags.t[kbdtags.wi++] = m->tag;
       -                if(kbdtags.wi == nelem(kbdtags.t))
       -                        kbdtags.wi = 0;
       -                if(kbdtags.wi == kbdtags.ri)
       +                qlock(&c->inputlk);
       +                c->kbdtags.t[c->kbdtags.wi++] = m->tag;
       +                if(c->kbdtags.wi == nelem(c->kbdtags.t))
       +                        c->kbdtags.wi = 0;
       +                if(c->kbdtags.wi == c->kbdtags.ri)
                                sysfatal("too many queued keyboard reads");
       -                kbd.stall = 0;
       -                matchkbd();
       -                zunlock();
       +                c->kbd.stall = 0;
       +                matchkbd(c);
       +                qunlock(&c->inputlk);
                        break;
        
                case Tmoveto:
                        setmouse(m->mouse.xy);
       -                replymsg(m);
       +                replymsg(c, m);
                        break;
        
                case Tcursor:
       t@@ -166,7 +119,7 @@ runmsg(Wsysmsg *m)
                                setcursor(nil, nil);
                        else
                                setcursor(&m->cursor, nil);
       -                replymsg(m);
       +                replymsg(c, m);
                        break;
        
                case Tcursor2:
       t@@ -174,63 +127,63 @@ runmsg(Wsysmsg *m)
                                setcursor(nil, nil);
                        else
                                setcursor(&m->cursor, &m->cursor2);
       -                replymsg(m);
       +                replymsg(c, m);
                        break;
        
                case Tbouncemouse:
                //        _xbouncemouse(&m->mouse);
       -                replymsg(m);
       +                replymsg(c, m);
                        break;
        
                case Tlabel:
                        kicklabel(m->label);
       -                replymsg(m);
       +                replymsg(c, m);
                        break;
        
                case Trdsnarf:
                        m->snarf = getsnarf();
       -                replymsg(m);
       +                replymsg(c, m);
                        free(m->snarf);
                        break;
        
                case Twrsnarf:
                        putsnarf(m->snarf);
       -                replymsg(m);
       +                replymsg(c, m);
                        break;
        
                case Trddraw:
       -                zlock();
       +                qlock(&c->inputlk);
                        n = m->count;
                        if(n > sizeof buf)
                                n = sizeof buf;
       -                n = _drawmsgread(buf, n);
       +                n = _drawmsgread(c, buf, n);
                        if(n < 0)
       -                        replyerror(m);
       +                        replyerror(c, m);
                        else{
                                m->count = n;
                                m->data = buf;
       -                        replymsg(m);
       +                        replymsg(c, m);
                        }
       -                zunlock();
       +                qunlock(&c->inputlk);
                        break;
        
                case Twrdraw:
       -                zlock();
       -                if(_drawmsgwrite(m->data, m->count) < 0)
       -                        replyerror(m);
       +                qlock(&c->inputlk);
       +                if(_drawmsgwrite(c, m->data, m->count) < 0)
       +                        replyerror(c, m);
                        else
       -                        replymsg(m);
       -                zunlock();
       +                        replymsg(c, m);
       +                qunlock(&c->inputlk);
                        break;
        
                case Ttop:
                        topwin();
       -                replymsg(m);
       +                replymsg(c, m);
                        break;
        
                case Tresize:
                        resizewindow(m->rect);
       -                replymsg(m);
       +                replymsg(c, m);
                        break;
                }
        }
       t@@ -240,7 +193,7 @@ runmsg(Wsysmsg *m)
         */
        QLock replylock;
        void
       -replymsg(Wsysmsg *m)
       +replymsg(Client *c, Wsysmsg *m)
        {
                int n;
                static uchar *mbuf;
       t@@ -263,7 +216,7 @@ replymsg(Wsysmsg *m)
                        nmbuf = n;
                }
                convW2M(m, mbuf, n);
       -        if(write(4, mbuf, n) != n)
       +        if(write(c->wfd, mbuf, n) != n)
                        sysfatal("write: %r");
                qunlock(&replylock);
        }
       t@@ -272,21 +225,21 @@ replymsg(Wsysmsg *m)
         * Match queued kbd reads with queued kbd characters.
         */
        void
       -matchkbd(void)
       +matchkbd(Client *c)
        {
                Wsysmsg m;
        
       -        if(kbd.stall)
       +        if(c->kbd.stall)
                        return;
       -        while(kbd.ri != kbd.wi && kbdtags.ri != kbdtags.wi){
       +        while(c->kbd.ri != c->kbd.wi && c->kbdtags.ri != c->kbdtags.wi){
                        m.type = Rrdkbd;
       -                m.tag = kbdtags.t[kbdtags.ri++];
       -                if(kbdtags.ri == nelem(kbdtags.t))
       -                        kbdtags.ri = 0;
       -                m.rune = kbd.r[kbd.ri++];
       -                if(kbd.ri == nelem(kbd.r))
       -                        kbd.ri = 0;
       -                replymsg(&m);
       +                m.tag = c->kbdtags.t[c->kbdtags.ri++];
       +                if(c->kbdtags.ri == nelem(c->kbdtags.t))
       +                        c->kbdtags.ri = 0;
       +                m.rune = c->kbd.r[c->kbd.ri++];
       +                if(c->kbd.ri == nelem(c->kbd.r))
       +                        c->kbd.ri = 0;
       +                replymsg(c, &m);
                }
        }
        
       t@@ -294,131 +247,129 @@ matchkbd(void)
         * Match queued mouse reads with queued mouse events.
         */
        void
       -matchmouse(void)
       +matchmouse(Client *c)
        {
                Wsysmsg m;
        
       -        while(mouse.ri != mouse.wi && mousetags.ri != mousetags.wi){
       +        while(c->mouse.ri != c->mouse.wi && c->mousetags.ri != c->mousetags.wi){
                        m.type = Rrdmouse;
       -                m.tag = mousetags.t[mousetags.ri++];
       -                if(mousetags.ri == nelem(mousetags.t))
       -                        mousetags.ri = 0;
       -                m.mouse = mouse.m[mouse.ri];
       -                m.resized = mouseresized;
       -                mouseresized = 0;
       +                m.tag = c->mousetags.t[c->mousetags.ri++];
       +                if(c->mousetags.ri == nelem(c->mousetags.t))
       +                        c->mousetags.ri = 0;
       +                m.mouse = c->mouse.m[c->mouse.ri];
       +                m.resized = c->mouse.resized;
       +                c->mouse.resized = 0;
                        /*
                        if(m.resized)
                                fprint(2, "sending resize\n");
                        */
       -                mouse.ri++;
       -                if(mouse.ri == nelem(mouse.m))
       -                        mouse.ri = 0;
       -                replymsg(&m);
       +                c->mouse.ri++;
       +                if(c->mouse.ri == nelem(c->mouse.m))
       +                        c->mouse.ri = 0;
       +                replymsg(c, &m);
                }
        }
        
        void
       -mousetrack(int x, int y, int b, uint ms)
       +mousetrack(Client *c, int x, int y, int b, uint ms)
        {
                Mouse *m;
        
       -        if(x < mouserect.min.x)
       -                x = mouserect.min.x;
       -        if(x > mouserect.max.x)
       -                x = mouserect.max.x;
       -        if(y < mouserect.min.y)
       -                y = mouserect.min.y;
       -        if(y > mouserect.max.y)
       -                y = mouserect.max.y;
       +        if(x < c->mouserect.min.x)
       +                x = c->mouserect.min.x;
       +        if(x > c->mouserect.max.x)
       +                x = c->mouserect.max.x;
       +        if(y < c->mouserect.min.y)
       +                y = c->mouserect.min.y;
       +        if(y > c->mouserect.max.y)
       +                y = c->mouserect.max.y;
        
       -        zlock();
       +        qlock(&c->inputlk);
                // If reader has stopped reading, don't bother.
                // If reader is completely caught up, definitely queue.
                // Otherwise, queue only button change events.
       -        if(!mouse.stall)
       -        if(mouse.wi == mouse.ri || mouse.last.buttons != b){
       -                m = &mouse.last;
       +        if(!c->mouse.stall)
       +        if(c->mouse.wi == c->mouse.ri || c->mouse.last.buttons != b){
       +                m = &c->mouse.last;
                        m->xy.x = x;
                        m->xy.y = y;
                        m->buttons = b;
                        m->msec = ms;
        
       -                mouse.m[mouse.wi] = *m;
       -                if(++mouse.wi == nelem(mouse.m))
       -                        mouse.wi = 0;
       -                if(mouse.wi == mouse.ri){
       -                        mouse.stall = 1;
       -                        mouse.ri = 0;
       -                        mouse.wi = 1;
       -                        mouse.m[0] = *m;
       +                c->mouse.m[c->mouse.wi] = *m;
       +                if(++c->mouse.wi == nelem(c->mouse.m))
       +                        c->mouse.wi = 0;
       +                if(c->mouse.wi == c->mouse.ri){
       +                        c->mouse.stall = 1;
       +                        c->mouse.ri = 0;
       +                        c->mouse.wi = 1;
       +                        c->mouse.m[0] = *m;
                        }
       -                matchmouse();
       +                matchmouse(c);
                }
       -        zunlock();
       +        qunlock(&c->inputlk);
        }
        
        void
       -kputc(int c)
       +kputc(Client *c, int ch)
        {
       -        zlock();
       -        kbd.r[kbd.wi++] = c;
       -        if(kbd.wi == nelem(kbd.r))
       -                kbd.wi = 0;
       -        if(kbd.ri == kbd.wi)
       -                kbd.stall = 1;
       -        matchkbd();
       -        zunlock();
       +        qlock(&c->inputlk);
       +        c->kbd.r[c->kbd.wi++] = ch;
       +        if(c->kbd.wi == nelem(c->kbd.r))
       +                c->kbd.wi = 0;
       +        if(c->kbd.ri == c->kbd.wi)
       +                c->kbd.stall = 1;
       +        matchkbd(c);
       +        qunlock(&c->inputlk);
        }
        
       -static int alting;
       -
        void
       -abortcompose(void)
       +abortcompose(Client *c)
        {
       -        if(alting)
       -                keystroke(Kalt);
       +        if(c->kbd.alting)
       +                keystroke(c, Kalt);
        }
        
        void
       -keystroke(int c)
       +keystroke(Client *c, int ch)
        {
                static Rune k[10];
                static int nk;
                int i;
        
       -        if(c == Kalt){
       -                alting = !alting;
       +        if(ch == Kalt){
       +                c->kbd.alting = !c->kbd.alting;
                        nk = 0;
                        return;
                }
       -        if(c == Kcmd+'r') {
       -                if(forcedpi)
       -                        forcedpi = 0;
       -                else if(displaydpi >= 200)
       -                        forcedpi = 100;
       +        if(ch == Kcmd+'r') {
       +                if(c->forcedpi)
       +                        c->forcedpi = 0;
       +                else if(c->displaydpi >= 200)
       +                        c->forcedpi = 100;
                        else
       -                        forcedpi = 225;
       -                resizeimg();
       +                        c->forcedpi = 225;
       +                resizeimg(c);
                        return;
                }
       -        if(!alting){
       -                kputc(c);
       +        if(!c->kbd.alting){
       +                kputc(c, ch);
                        return;
                }
                if(nk >= nelem(k))      // should not happen
                        nk = 0;
       -        k[nk++] = c;
       -        c = _latin1(k, nk);
       -        if(c > 0){
       -                alting = 0;
       -                kputc(c);
       +        k[nk++] = ch;
       +        ch = _latin1(k, nk);
       +        if(ch > 0){
       +                c->kbd.alting = 0;
       +                kputc(c, ch);
                        nk = 0;
                        return;
                }
       -        if(c == -1){
       -                alting = 0;
       +        if(ch == -1){
       +                c->kbd.alting = 0;
                        for(i=0; i<nk; i++)
       -                        kputc(k[i]);
       +                        kputc(c, k[i]);
                        nk = 0;
                        return;
                }
 (DIR) diff --git a/src/cmd/devdraw/mkfile b/src/cmd/devdraw/mkfile
       t@@ -16,7 +16,11 @@ WSYSOFILES=\
        OFILES=$WSYSOFILES
        
        HFILES=\
       +        bigarrow.h\
                devdraw.h\
       +        glendapng.h\
       +        latin1.h\
       +        $WSYSHFILES\
        
        <$PLAN9/src/mkone
        
       t@@ -40,7 +44,7 @@ $O.macargv: $MACARGV
        %.$O: %.m
                $CC $CFLAGS $OBJCFLAGS -o $target $stem.m
        
       -CLEANFILES=$O.devdraw $O.macargv $O.mklatinkbd latin1.h
       +CLEANFILES=$O.devdraw $O.macargv $O.drawclient $O.mklatinkbd latin1.h
        
        install: mklatinkbd.install
        install:Q: 
 (DIR) diff --git a/src/cmd/devdraw/mkwsysrules.sh b/src/cmd/devdraw/mkwsysrules.sh
       t@@ -51,9 +51,11 @@ if [ $WSYSTYPE = x11 ]; then
                echo 'HFILES=$HFILES $XHFILES'
                XO=`ls x11-*.c 2>/dev/null | sed 's/\.c$/.o/'`
                echo 'WSYSOFILES=$WSYSOFILES '$XO
       +        echo 'WSYSHFILES=x11-inc.h x11-keysym2ucs.h x11-memdraw.h'
        elif [ $WSYSTYPE = mac ]; then
                echo 'OBJCFLAGS=$OBJCFLAGS -fobjc-arc'
                echo 'WSYSOFILES=$WSYSOFILES mac-draw.o mac-screen.o mac-srv.o'
       +        echo 'WSYSHFILES=mac-screen.h'
                echo 'MACARGV=macargv.o'
        elif [ $WSYSTYPE = nowsys ]; then
                echo 'WSYSOFILES=nowsys.o'
 (DIR) diff --git a/src/cmd/devdraw/mouseswap.c b/src/cmd/devdraw/mouseswap.c
       t@@ -2,6 +2,11 @@
        #include <libc.h>
        #include <draw.h>
        #include <memdraw.h>
       +#include <memlayer.h>
       +#include <mouse.h>
       +#include <cursor.h>
       +#include <keyboard.h>
       +#include <drawfcall.h>
        #include "devdraw.h"
        
        enum
 (DIR) diff --git a/src/cmd/devdraw/winsize.c b/src/cmd/devdraw/winsize.c
       t@@ -2,6 +2,11 @@
        #include <libc.h>
        #include <draw.h>
        #include <memdraw.h>
       +#include <memlayer.h>
       +#include <mouse.h>
       +#include <cursor.h>
       +#include <keyboard.h>
       +#include <drawfcall.h>
        #include "devdraw.h"
        
        int
 (DIR) diff --git a/src/cmd/devdraw/x11-init.c b/src/cmd/devdraw/x11-init.c
       t@@ -6,6 +6,7 @@
        #include <libc.h>
        #include <draw.h>
        #include <memdraw.h>
       +#include <memlayer.h>
        #include <keyboard.h>
        #include <mouse.h>
        #include <cursor.h>
       t@@ -232,7 +233,7 @@ _xattach(char *label, char *winsize)
        
                        if (XrmGetResource(database, "Xft.dpi", "String", &dpitype, &dpires) == True) {
                                if (dpires.addr) {
       -                                displaydpi=atoi(dpires.addr);
       +                                client0->displaydpi = atoi(dpires.addr);
                                }
                        }
                        geom = smprint("%s.geometry", label);