Commit Diff


commit - 933b98054f40bb224acda134d7bb77a023bcc57f
commit + 88ed92aa40ab5aa0f563624c488ba2a120990329
blob - a22dbe2bb4bd0bbe3f9e400b5ffde4c02b69e963
blob + dc0e0b72f9b7202654324c908c4e53a46397753f
--- include/memdraw.h
+++ include/memdraw.h
@@ -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)
 }
blob - 654ab4af6b3967ebd61931292b2c2176dc7c6b12
blob + e83f6f07bc4733dea0559faa404cc0b7f135895c
--- src/cmd/devdraw/devdraw.c
+++ src/cmd/devdraw/devdraw.c
@@ -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
@@ -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
@@ -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 */
@@ -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
@@ -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;
@@ -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;
@@ -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;
 		}
@@ -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;
@@ -466,9 +347,9 @@ drawinstallscreen(Client *client, DScreen *d, int id, 
 		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++;
@@ -479,18 +360,18 @@ drawinstallscreen(Client *client, DScreen *d, int id, 
 
 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;
 
@@ -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 = */
@@ -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;
@@ -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;
@@ -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);
@@ -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;
 		}
@@ -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;
 		}
 	}
@@ -622,14 +503,14 @@ drawaddname(Client *client, DImage *di, int n, char *s
 	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);
@@ -637,16 +518,16 @@ drawaddname(Client *client, DImage *di, int n, char *s
 		*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;
 }
 
@@ -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;
@@ -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;
@@ -790,7 +667,6 @@ _drawmsgwrite(void *v, int n)
 	a = v;
 	m = 0;
 	oldn = n;
-	client = client0;
 
 	while((n-=m) > 0){
 		a += m;
@@ -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){
@@ -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);
@@ -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] */
@@ -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] */
@@ -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;
 
@@ -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{
@@ -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;
 				}
 			}
@@ -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;
 
@@ -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);
@@ -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;
 
@@ -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);
 				}
@@ -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;
@@ -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);
@@ -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] */
@@ -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)){
@@ -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);
@@ -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] */
@@ -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;
 		}
blob - f768735f541fb5b392b84754a631af43b0b54dc3
blob + 40a2ed4e2d85e21de6c263a683b978ebb4b58333
--- src/cmd/devdraw/devdraw.h
+++ src/cmd/devdraw/devdraw.h
@@ -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;
blob - b5e3c70107ba82cd2020c2d9137d8d7950f3c519
blob + 7bc0920d8a8df1bfc7ce0191021a42fec1a7243d
--- src/cmd/devdraw/mac-screen.h
+++ src/cmd/devdraw/mac-screen.h
@@ -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*);
@@ -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);
blob - c115f86787879635bfcc9163d2d8ae5f476dd2cc
blob + b71f653d01faceeebe8271ebd89ec3277881a1f6
--- src/cmd/devdraw/mac-screen.m
+++ src/cmd/devdraw/mac-screen.m
@@ -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"
 
@@ -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)
@@ -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;
@@ -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{
@@ -113,7 +124,9 @@ callservep9p(void *v)
 {
 	USED(v);
 
-	servep9p();
+	client0->rfd = 3;
+	client0->wfd = 4;
+	servep9p(client0);
 	[NSApp terminate:myApp];
 }
 
@@ -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];
@@ -353,7 +367,7 @@ struct Cursors {
 - (void)windowDidResize:(NSNotification *)notification
 {
 	if(![myContent inLiveResize] && img) {
-		resizeimg();
+		resizeimg(myContent.client);
 	}
 }
 
@@ -463,7 +477,7 @@ struct Cursors {
 			b |= 4;
 		[self sendmouse:b];
 	}else if(m & ~omod & NSEventModifierFlagOption)
-		keystroke(Kalt);
+		keystroke(self.client, Kalt);
 
 	omod = m;
 }
@@ -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)
@@ -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];
 }
@@ -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
@@ -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,
@@ -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);
@@ -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);
@@ -731,7 +745,7 @@ struct Cursors {
 			k += Kcmd;
 	}
 	if(k>0)
-		keystroke(k);
+		keystroke(self.client, k);
 }
 
 // Helper for managing input rect approximately
@@ -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);
 	}
 }
 
@@ -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
@@ -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;
@@ -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)
@@ -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");
 
@@ -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];
 }
 
blob - b0925eee5d210ea3f97663f77ce1380f1f1edd4d
blob + 6727ef6318909fd8b1f28fcb801198665c24c086
--- src/cmd/devdraw/mac-srv.c
+++ src/cmd/devdraw/mac-srv.c
@@ -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;
+void runmsg(Client*, Wsysmsg*);
+void replymsg(Client*, Wsysmsg*);
+void matchkbd(Client*);
+void matchmouse(Client*);
 
-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);
-}
-
 int trace = 0;
 
 void
-servep9p(void)
+servep9p(Client *c)
 {
 	uchar buf[4], *mbuf;
 	int nmbuf, n, nn;
@@ -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);
@@ -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");
 
@@ -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);
 }
 
 /*
@@ -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;
@@ -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:
@@ -166,7 +119,7 @@ runmsg(Wsysmsg *m)
 			setcursor(nil, nil);
 		else
 			setcursor(&m->cursor, nil);
-		replymsg(m);
+		replymsg(c, m);
 		break;
 
 	case Tcursor2:
@@ -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;
 	}
 }
@@ -240,7 +193,7 @@ runmsg(Wsysmsg *m)
  */
 QLock replylock;
 void
-replymsg(Wsysmsg *m)
+replymsg(Client *c, Wsysmsg *m)
 {
 	int n;
 	static uchar *mbuf;
@@ -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);
 }
@@ -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);
 	}
 }
 
@@ -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;
 	}
blob - 27613239ca69b6293e525dc669a5388b4730caf3
blob + 6546b5903c3e332367d2a5c530097efd529d3487
--- src/cmd/devdraw/mkfile
+++ src/cmd/devdraw/mkfile
@@ -16,7 +16,11 @@ WSYSOFILES=\
 OFILES=$WSYSOFILES
 
 HFILES=\
+	bigarrow.h\
 	devdraw.h\
+	glendapng.h\
+	latin1.h\
+	$WSYSHFILES\
 
 <$PLAN9/src/mkone
 
@@ -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: 
blob - 8e9c30106ce89c276e3691bcabb6aae12e5893b0
blob + ec660bd12420aabf92d494086eae595edd016b2f
--- src/cmd/devdraw/mkwsysrules.sh
+++ src/cmd/devdraw/mkwsysrules.sh
@@ -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'
blob - 29f33b7bfbc53e90d61e390d4425e8fa76af9d4f
blob + d2dea666953793624f0393be0b04f5f3e137b8c8
--- src/cmd/devdraw/mouseswap.c
+++ src/cmd/devdraw/mouseswap.c
@@ -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
blob - 375401bf0812c59c5fca50e519953fcc811841d5
blob + 7d0a7b70ba76dafd132437f0c74821e00c6917b3
--- src/cmd/devdraw/winsize.c
+++ src/cmd/devdraw/winsize.c
@@ -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
blob - 8935c9d15a1ebad457a251fb41cddbd0d4d4bc92
blob + 4b5b570d240b272f51caa10e0ed88a78a2913a3c
--- src/cmd/devdraw/x11-init.c
+++ src/cmd/devdraw/x11-init.c
@@ -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>
@@ -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);