shithub: riscv

Download patch

ref: d2089f949edd71626e2391672b723aa2d9fbcb9b
parent: 670137bf0a75813df93d9b727ce70ddb099d11e1
author: cinap_lenrek <[email protected]>
date: Mon Aug 20 15:23:42 EDT 2018

vncs: support for desktop resize extension, update devdraw

--- a/sys/src/cmd/vnc/devcons.c
+++ b/sys/src/cmd/vnc/devcons.c
@@ -13,7 +13,6 @@
 	Qcons,
 	Qconsctl,
 	Qsnarf,
-	Qwinname,
 };
 
 static Dirtab consdir[]={
@@ -21,7 +20,6 @@
 	"cons",		{Qcons},	0,		0660,
 	"consctl",	{Qconsctl},	0,		0220,
 	"snarf",	{Qsnarf},	0,		0600,
-	"winname",	{Qwinname},	0,		0000,
 };
 
 static Chan*
--- a/sys/src/cmd/vnc/devdraw.c
+++ b/sys/src/cmd/vnc/devdraw.c
@@ -14,6 +14,7 @@
 {
 	Qtopdir		= 0,
 	Qnew,
+	Qwinname,
 	Q3rd,
 	Q2nd,
 	Qcolormap,
@@ -35,6 +36,7 @@
 
 #define	NHASH		(1<<5)
 #define	HASHMASK	(NHASH-1)
+#define	IOUNIT		(64*1024)
 
 typedef struct Client Client;
 typedef struct Draw Draw;
@@ -46,21 +48,15 @@
 typedef struct Refx Refx;
 typedef struct DName DName;
 
-ulong blanktime = 30;	/* in minutes; a half hour */
-
 struct Draw
 {
-	QLock;
 	int		clientid;
 	int		nclient;
 	Client**	client;
 	int		nname;
-	DName*	name;
+	DName*		name;
 	int		vers;
 	int		softscreen;
-	int		blanked;	/* screen turned off */
-	ulong		blanktime;	/* time of last operation */
-	ulong		savemap[3*256];
 };
 
 struct Client
@@ -70,6 +66,7 @@
 	CScreen*	cscreen;
 	Refresh*	refresh;
 	Rendez		refrend;
+	QLock		refq;
 	uchar*		readdata;
 	int		nreaddata;
 	int		busy;
@@ -95,10 +92,10 @@
 
 struct DName
 {
-	char			*name;
-	Client	*client;
+	char		*name;
+	Client		*client;
 	DImage*		dimage;
-	int			vers;
+	int		vers;
 };
 
 struct FChar
@@ -128,7 +125,7 @@
 	int		nfchar;
 	FChar*		fchar;
 	DScreen*	dscreen;	/* 0 if not a window */
-	DImage*	fromname;	/* image this one is derived from, by name */
+	DImage*		fromname;	/* image this one is derived from, by name */
 	DImage*		next;
 };
 
@@ -143,8 +140,8 @@
 	int		id;
 	int		public;
 	int		ref;
-	DImage	*dimage;
-	DImage	*dfill;
+	DImage		*dimage;
+	DImage		*dfill;
 	Memscreen*	screen;
 	Client*		owner;
 	DScreen*	next;
@@ -151,8 +148,13 @@
 };
 
 static	Draw		sdraw;
+	QLock	drawlock;
+
 static	Memimage	*screenimage;
-static	Memdata	screendata;
+static	DImage*	screendimage;
+static	char	screenname[40];
+static	int	screennameid;
+
 static	Rectangle	flushrect;
 static	int		waste;
 static	DScreen*	dscreen;
@@ -161,6 +163,7 @@
 	void		drawuninstall(Client*, int);
 	void		drawfreedimage(DImage*);
 	Client*		drawclientofpath(ulong);
+	DImage*	allocdimage(Memimage*);
 
 static	char Enodrawimage[] =	"unknown id for draw image";
 static	char Enodrawscreen[] =	"unknown id for draw screen";
@@ -174,7 +177,6 @@
 static	char Enotfont[] =	"image not a font";
 static	char Eindex[] =		"character index out of range";
 static	char Enoclient[] =	"no such draw client";
-static	char Edepth[] =	"image has bad depth";
 static	char Enameused[] =	"image name in use";
 static	char Enoname[] =	"no image with that name";
 static	char Eoldname[] =	"named image no longer valid";
@@ -181,22 +183,22 @@
 static	char Enamed[] = 	"image already has name";
 static	char Ewrongname[] = 	"wrong name for image";
 
-void
-drawlock(void)
+static void
+dlock(void)
 {
-	qlock(&sdraw);
+	qlock(&drawlock);
 }
 
-void
-drawunlock(void)
+static int
+candlock(void)
 {
-	qunlock(&sdraw);
+	return canqlock(&drawlock);
 }
 
-int
-candrawlock(void)
+static void
+dunlock(void)
 {
-	return canqlock(&sdraw);
+	qunlock(&drawlock);
 }
 
 static int
@@ -226,7 +228,7 @@
 			devdir(c, q, up->genbuf, 0, eve, 0500, dp);
 			break;
 		default:
-			panic("drawwalk %#llux", c->qid.path);
+			panic("drawwalk %llux", c->qid.path);
 		}
 		return 1;
 	}
@@ -235,15 +237,17 @@
 	 * Top level directory contains the name of the device.
 	 */
 	t = QID(c->qid);
-	if(t == Qtopdir){
-		switch(s){
-		case 0:
+	if(t == Qtopdir || t == Qwinname){
+		if(s == 1 || t == Qwinname){
+			mkqid(&q, Qwinname, 0, QTFILE);
+			devdir(c, q, "winname", 0, eve, 0444, dp);
+		}
+		else if(s == 0){
 			mkqid(&q, Q2nd, 0, QTDIR);
 			devdir(c, q, "draw", 0, eve, 0555, dp);
-			break;
-		default:
-			return -1;
 		}
+		else
+			return -1;
 		return 1;
 	}
 
@@ -272,7 +276,7 @@
 	/*
 	 * Third level.
 	 */
-	path = c->qid.path&~(((1<<QSHIFT)-1));	/* slot component */
+	path = c->qid.path&~((1<<QSHIFT)-1);	/* slot component */
 	q.vers = c->qid.vers;
 	q.type = QTFILE;
 	switch(s){
@@ -320,7 +324,7 @@
 
 static
 void
-drawrefresh(Memimage *l, Rectangle r, void *v)
+drawrefresh(Memimage*, Rectangle r, void *v)
 {
 	Refx *x;
 	DImage *d;
@@ -327,7 +331,6 @@
 	Client *c;
 	Refresh *ref;
 
-	USED(l);
 	if(v == 0)
 		return;
 	x = v;
@@ -353,9 +356,8 @@
 	int abb, ar, anbb;
 	Rectangle nbb;
 
-	if(sdraw.softscreen==0 || !rectclip(&r, screenimage->r))
+	if(sdraw.softscreen==0 || screenimage == nil || !rectclip(&r, screenimage->r))
 		return;
-
 	if(flushrect.min.x >= flushrect.max.x){
 		flushrect = r;
 		waste = 0;
@@ -393,7 +395,8 @@
 		return;
 	}
 	/* emit current state */
-	flushmemscreen(flushrect);
+	if(flushrect.min.x < flushrect.max.x)
+		flushmemscreen(flushrect);
 	flushrect = r;
 	waste = 0;
 }
@@ -409,8 +412,7 @@
 		addflush(r); // for VNC, see comments in addflush
 		return;
 	}
-	l = dst->layer;
-	if(l == nil)
+	if(screenimage == nil || dst == nil || (l = dst->layer) == nil)
 		return;
 	do{
 		if(l->screen->image->data != screenimage->data)
@@ -421,11 +423,11 @@
 	addflush(r);
 }
 
-static
 void
 drawflush(void)
 {
-	flushmemscreen(flushrect);
+	if(screenimage && flushrect.min.x < flushrect.max.x)
+		flushmemscreen(flushrect);
 	flushrect = Rect(10000, 10000, -10000, -10000);
 }
 
@@ -517,8 +519,8 @@
 	return 0;
 }
 
-Memimage*
-drawinstall(Client *client, int id, Memimage *i, DScreen *dscreen)
+DImage*
+allocdimage(Memimage *i)
 {
 	DImage *d;
 
@@ -525,14 +527,26 @@
 	d = malloc(sizeof(DImage));
 	if(d == 0)
 		return 0;
-	d->id = id;
 	d->ref = 1;
 	d->name = 0;
 	d->vers = 0;
 	d->image = i;
+	d->dscreen = 0;
 	d->nfchar = 0;
 	d->fchar = 0;
 	d->fromname = 0;
+	return d;
+}
+
+Memimage*
+drawinstall(Client *client, int id, Memimage *i, DScreen *dscreen)
+{
+	DImage *d;
+
+	d = allocdimage(i);
+	if(d == 0)
+		return 0;
+	d->id = id;
 	d->dscreen = dscreen;
 	d->next = client->dimage[id&HASHMASK];
 	client->dimage[id&HASHMASK] = d;
@@ -595,6 +609,7 @@
 {
 	int i;
 
+	free(name->name);
 	i = name-sdraw.name;
 	memmove(name, name+1, (sdraw.nname-(i+1))*sizeof(DName));
 	sdraw.nname--;
@@ -656,12 +671,10 @@
 		drawfreedimage(dimage->fromname);
 		goto Return;
 	}
-	if(dimage->image == screenimage)	/* don't free the display */
-		goto Return;
 	ds = dimage->dscreen;
 	if(ds){
 		l = dimage->image;
-		if(l->data == screenimage->data)
+		if(screenimage && l->data == screenimage->data)
 			addflush(l->layer->screenr);
 		if(l->layer->refreshfn == drawrefresh)	/* else true owner will clean up */
 			free(l->layer->refreshptr);
@@ -856,11 +869,12 @@
 }
 
 Point
-drawchar(Memimage *dst, Point p, Memimage *src, Point *sp, DImage *font, int index, int op)
+drawchar(Memimage *dst, Memimage *rdst, Point p, Memimage *src, Point *sp, DImage *font, int index, int op)
 {
 	FChar *fc;
 	Rectangle r;
 	Point sp1;
+	static Memimage *tmp;
 
 	fc = &font->fchar[index];
 	r.min.x = p.x+fc->left;
@@ -869,69 +883,143 @@
 	r.max.y = r.min.y+(fc->maxy-fc->miny);
 	sp1.x = sp->x+fc->left;
 	sp1.y = sp->y+fc->miny;
-	memdraw(dst, r, src, sp1, font->image, Pt(fc->minx, fc->miny), op);
+
+	/*
+	 * If we're drawing greyscale fonts onto a VGA screen,
+	 * it's very costly to read the screen memory to do the
+	 * alpha blending inside memdraw.  If this is really a stringbg,
+	 * then rdst is the bg image (in main memory) which we can
+	 * refer to for the underlying dst pixels instead of reading dst
+	 * directly.
+	 */
+	if(ishwimage(dst) && !ishwimage(rdst) && font->image->depth > 1){
+		if(tmp == nil || tmp->chan != dst->chan || Dx(tmp->r) < Dx(r) || Dy(tmp->r) < Dy(r)){
+			if(tmp)
+				freememimage(tmp);
+			tmp = allocmemimage(Rect(0,0,Dx(r),Dy(r)), dst->chan);
+			if(tmp == nil)
+				goto fallback;
+		}
+		memdraw(tmp, Rect(0,0,Dx(r),Dy(r)), rdst, r.min, memopaque, ZP, S);
+		memdraw(tmp, Rect(0,0,Dx(r),Dy(r)), src, sp1, font->image, Pt(fc->minx, fc->miny), op);
+		memdraw(dst, r, tmp, ZP, memopaque, ZP, S);
+	}else{
+	fallback:
+		memdraw(dst, r, src, sp1, font->image, Pt(fc->minx, fc->miny), op);
+	}
+
 	p.x += fc->width;
 	sp->x += fc->width;
 	return p;
 }
 
-static int
-initscreenimage(void)
+static DImage*
+makescreenimage(void)
 {
 	int width, depth;
 	ulong chan;
+	DImage *di;
+	Memdata *md;
+	Memimage *i;
 	Rectangle r;
+	uchar *data;
 
+	if((data = attachscreen(&r, &chan, &depth, &width, &sdraw.softscreen)) == nil)
+		return nil;
+	if(sdraw.softscreen == 0xa110c){
+		/* hack: softscreen is memimage. */
+		md = *((Memdata**)(data - sizeof(ulong) - sizeof(Memdata*)));
+
+		assert(md->bdata == data);
+		assert(md->ref > 1);
+		assert(md->allocd);
+
+		if((i = allocmemimaged(r, chan, md)) == nil){
+			md->ref--;
+			return nil;
+		}
+	}else{
+		if((md = malloc(sizeof *md)) == nil)
+			return nil;
+		md->allocd = 1;
+		md->base = nil;
+		md->bdata = data;
+		md->ref = 1;
+		if((i = allocmemimaged(r, chan, md)) == nil){
+			free(md);
+			return nil;
+		}
+	}
+	i->width = width;
+	i->clipr = r;
+	di = allocdimage(i);
+	if(di == nil){
+		freememimage(i);	/* frees md */
+		return nil;
+	}
+	if(!waserror()){
+		snprint(screenname, sizeof screenname, "noborder.screen.%d", ++screennameid);
+		drawaddname(nil, di, strlen(screenname), screenname);
+		poperror();
+	}
+	return di;
+}
+
+static int
+initscreenimage(void)
+{
 	if(screenimage != nil)
 		return 1;
 
-	screendata.base = nil;
-	screendata.bdata = attachscreen(&r, &chan, &depth, &width, &sdraw.softscreen);
-	if(screendata.bdata == nil)
-{fprint(2, "bad bdata\n");
+	screendimage = makescreenimage();
+	if(screendimage == nil)
 		return 0;
+	screenimage = screendimage->image;
+// iprint("initscreenimage %p %p\n", screendimage, screenimage);
+	mouseresize();
+	return 1;
 }
-	screendata.ref = 1;
 
-	screenimage = allocmemimaged(r, chan, &screendata);
-	if(screenimage == nil){
-fprint(2, "bad memimaged: %r\n");
-		/* RSC: BUG: detach screen */
-		return 0;
+void
+deletescreenimage(void)
+{
+	dlock();
+	if(screenimage){
+		/* will be freed via screendimage; disable */
+		screenimage->clipr = ZR;
+		screenimage = nil;
 	}
-
-	screenimage->width = width;
-	screenimage->clipr  = screenimage->r;
-	return 1;
+	if(screendimage){
+		drawfreedimage(screendimage);
+		screendimage = nil;
+	}
+	dunlock();
 }
 
 void
-deletescreenimage(void)
+resetscreenimage(void)
 {
-	qlock(&sdraw);
-	/* RSC: BUG: detach screen */
-	if(screenimage)
-		freememimage(screenimage);
-	screenimage = nil;
-	qunlock(&sdraw);
+	dlock();
+	initscreenimage();
+	dunlock();
 }
 
-Chan*
+static Chan*
 drawattach(char *spec)
 {
-	qlock(&sdraw);
+	dlock();
 	if(!initscreenimage()){
-		qunlock(&sdraw);
+		dunlock();
 		error("no frame buffer");
 	}
-	qunlock(&sdraw);
+	dunlock();
 	return devattach('i', spec);
 }
 
-Walkqid*
+static Walkqid*
 drawwalk(Chan *c, Chan *nc, char **name, int nname)
 {
-	if(screendata.bdata == nil)
+	if(screenimage == nil)
 		error("no frame buffer");
 	return devwalk(c, nc, name, nname, 0, 0, drawgen);
 }
@@ -946,13 +1034,17 @@
 drawopen(Chan *c, int omode)
 {
 	Client *cl;
+	DName *dn;
+	DImage *di;
 
-	if(c->qid.type & QTDIR)
-		return devopen(c, omode, 0, 0, drawgen);
+	if(c->qid.type & QTDIR){
+		c = devopen(c, omode, 0, 0, drawgen);
+		c->iounit = IOUNIT;
+	}
 
-	qlock(&sdraw);
+	dlock();
 	if(waserror()){
-		qunlock(&sdraw);
+		dunlock();
 		nexterror();
 	}
 
@@ -964,6 +1056,9 @@
 	}
 
 	switch(QID(c->qid)){
+	case Qwinname:
+		break;
+
 	case Qnew:
 		break;
 
@@ -973,9 +1068,22 @@
 			error(Einuse);
 		cl->busy = 1;
 		flushrect = Rect(10000, 10000, -10000, -10000);
-		drawinstall(cl, 0, screenimage, 0);
+		dn = drawlookupname(strlen(screenname), screenname);
+		if(dn == 0)
+			error("draw: cannot happen 2");
+		if(drawinstall(cl, 0, dn->dimage->image, 0) == 0)
+			error(Edrawmem);
+		di = drawlookup(cl, 0, 0);
+		if(di == 0)
+			error("draw: cannot happen 1");
+		di->vers = dn->vers;
+		di->name = smalloc(strlen(screenname)+1);
+		strcpy(di->name, screenname);
+		di->fromname = dn->dimage;
+		di->fromname->ref++;
 		incref(&cl->r);
 		break;
+
 	case Qcolormap:
 	case Qdata:
 	case Qrefresh:
@@ -983,11 +1091,12 @@
 		incref(&cl->r);
 		break;
 	}
-	qunlock(&sdraw);
+	dunlock();
 	poperror();
 	c->mode = openmode(omode);
 	c->flag |= COPEN;
 	c->offset = 0;
+	c->iounit = IOUNIT;
 	return c;
 }
 
@@ -999,11 +1108,11 @@
 	Client *cl;
 	Refresh *r;
 
-	if(c->qid.type & QTDIR)
+	if(QID(c->qid) < Qcolormap)	/* Qtopdir, Qnew, Q3rd, Q2nd have no client */
 		return;
-	qlock(&sdraw);
+	dlock();
 	if(waserror()){
-		qunlock(&sdraw);
+		dunlock();
 		nexterror();
 	}
 
@@ -1036,7 +1145,7 @@
 		drawflush();	/* to erase visible, now dead windows */
 		free(cl);
 	}
-	qunlock(&sdraw);
+	dunlock();
 	poperror();
 }
 
@@ -1053,13 +1162,15 @@
 	ulong offset = off;
 	char buf[16];
 
-	USED(offset);
 	if(c->qid.type & QTDIR)
 		return devdirread(c, a, n, 0, 0, drawgen);
+	if(QID(c->qid) == Qwinname)
+		return readstr(off, a, n, screenname);
+
 	cl = drawclient(c);
-	qlock(&sdraw);
+	dlock();
 	if(waserror()){
-		qunlock(&sdraw);
+		dunlock();
 		nexterror();
 	}
 	switch(QID(c->qid)){
@@ -1078,15 +1189,15 @@
 				error(Enodrawimage);
 			i = di->image;
 		}
-		n = sprint(a, "%11d %11d %11s %11d %11d %11d %11d %11d %11d %11d %11d %11d ",
+		n = sprint(a, "%11d %11d %11s %11d %11d %11d %11d %11d %11d %11d %11d %11d",
 			cl->clientid, cl->infoid, chantostr(buf, i->chan), (i->flags&Frepl)==Frepl,
 			i->r.min.x, i->r.min.y, i->r.max.x, i->r.max.y,
 			i->clipr.min.x, i->clipr.min.y, i->clipr.max.x, i->clipr.max.y);
+		((char*)a)[n++] = ' ';
 		cl->infoid = -1;
 		break;
 
 	case Qcolormap:
-		drawactive(1);	/* to restore map from backup */
 		p = malloc(4*12*256+1);
 		if(p == 0)
 			error(Enomem);
@@ -1116,14 +1227,21 @@
 		for(;;){
 			if(cl->refreshme || cl->refresh)
 				break;
-			qunlock(&sdraw);
+			dunlock();
 			if(waserror()){
-				qlock(&sdraw);	/* restore lock for waserror() above */
+				dlock();
 				nexterror();
 			}
+			qlock(&cl->refq);
+			if(waserror()){
+				qunlock(&cl->refq);
+				nexterror();
+			}
 			rendsleep(&cl->refrend, drawrefactive, cl);
 			poperror();
-			qlock(&sdraw);
+			qunlock(&cl->refq);
+			poperror();
+			dlock();
 		}
 		p = a;
 		while(cl->refresh && n>=5*4){
@@ -1140,8 +1258,9 @@
 		}
 		cl->refreshme = 0;
 		n = p-(uchar*)a;
+		break;
 	}
-	qunlock(&sdraw);
+	dunlock();
 	poperror();
 	return n;
 }
@@ -1160,21 +1279,19 @@
 }
 
 static long
-drawwrite(Chan *c, void *a, long n, vlong off)
+drawwrite(Chan *c, void *a, long n, vlong)
 {
 	char buf[128], *fields[4], *q;
 	Client *cl;
 	int i, m, red, green, blue, x;
-	ulong offset = off;
 
-	USED(offset);
 	if(c->qid.type & QTDIR)
 		error(Eisdir);
 	cl = drawclient(c);
-	qlock(&sdraw);
+	dlock();
 	if(waserror()){
 		drawwakeall();
-		qunlock(&sdraw);
+		dunlock();
 		nexterror();
 	}
 	switch(QID(c->qid)){
@@ -1185,7 +1302,6 @@
 		break;
 
 	case Qcolormap:
-		drawactive(1);	/* to restore map from backup */
 		m = n;
 		n = 0;
 		while(m > 0){
@@ -1200,7 +1316,7 @@
 			a = (char*)a + i;
 			m -= i;
 			*q = 0;
-			if(getfields(buf, fields, nelem(fields), 1, " ") != 4)
+			if(tokenize(buf, fields, nelem(fields)) != 4)
 				error(Ebadarg);
 			i = strtoul(fields[0], 0, 0);
 			red = strtoul(fields[1], 0, 0);
@@ -1228,7 +1344,7 @@
 	default:
 		error(Ebadusefd);
 	}
-	qunlock(&sdraw);
+	dunlock();
 	poperror();
 	return n;
 }
@@ -1265,7 +1381,7 @@
 	char *p, *q;
 	int s;
 
-	if(1||plsprnt==0){
+	if(1|| plsprnt==0){
 		SET(s,q,p);
 		USED(fmt, a, buf, p, q, s);
 		return;
@@ -1305,7 +1421,7 @@
 	}
 	*q++ = '\n';
 	*q = 0;
-	fprint(2, "%.*s", (int)(q-buf), buf);
+	// iprint("%.*s", (int)(q-buf), buf);
 }
 
 void
@@ -1317,7 +1433,7 @@
 	ulong value, chan;
 	Rectangle r, clipr;
 	Point p, q, *pp, sp;
-	Memimage *i, *dst, *src, *mask;
+	Memimage *i, *bg, *dst, *src, *mask;
 	Memimage *l, **lp;
 	Memscreen *scrn;
 	DImage *font, *ll, *di, *ddst, *dsrc;
@@ -1333,6 +1449,7 @@
 	fmt = nil;
 	if(waserror()){
 		if(fmt) printmesg(fmt, a, 1);
+	/*	iprint("error: %s\n", up->errstr);	*/
 		nexterror();
 	}
 	while((n-=m) > 0){
@@ -1445,7 +1562,7 @@
 			if(ddst == nil)
 				error(Enodrawimage);
 			if(ddst->name)
-				error("can't change repl/clipr of shared image");
+				error("cannot change repl/clipr of shared image");
 			dst = ddst->image;
 			if(a[5])
 				dst->flags |= Frepl;
@@ -1543,14 +1660,16 @@
 				error(Eshortdraw);
 			dstid = BGLONG(a+1);
 			if(dstid == 0)
-				error("can't use display as font");
+				error("cannot use display as font");
 			font = drawlookup(client, dstid, 1);
 			if(font == 0)
 				error(Enodrawimage);
 			if(font->image->layer)
-				error("can't use window as font");
-			free(font->fchar);	/* should we complain if non-zero? */
+				error("cannot use window as font");
 			ni = BGLONG(a+5);
+			if(ni<=0 || ni>4096)
+				error("bad font size (4096 chars max)");
+			free(font->fchar);	/* should we complain if non-zero? */
 			font->fchar = malloc(ni*sizeof(FChar));
 			if(font->fchar == 0)
 				error("no memory for font");
@@ -1647,7 +1766,7 @@
 				error(Edrawmem);
 			di = drawlookup(client, dstid, 0);
 			if(di == 0)
-				error("draw: can't happen");
+				error("draw: cannot happen");
 			di->vers = dn->vers;
 			di->name = smalloc(j+1);
 			di->fromname = dn->dimage;
@@ -1748,7 +1867,7 @@
 			if(pp == nil)
 				error(Enomem);
 			doflush = 0;
-			if(dstid==0 || (dst->layer && dst->layer->screen->image->data == screenimage->data))
+			if(dstid==0 || (screenimage && dst->layer && dst->layer->screen->image->data == screenimage->data))
 				doflush = 1;	/* simplify test in loop */
 			ox = oy = 0;
 			esize = 0;
@@ -1803,8 +1922,6 @@
 			if(n < m)
 				error(Eshortdraw);
 			i = drawimage(client, a+1);
-			if(0 && i->layer)
-				error("readimage from window unimplemented");
 			drawrectangle(&r, a+5);
 			if(!rectinrect(r, i->r))
 				error(Ereadoutside);
@@ -1814,7 +1931,7 @@
 			client->readdata = mallocz(c, 0);
 			if(client->readdata == nil)
 				error("readimage malloc failed");
-			client->nreaddata = unloadmemimage(i, r, client->readdata, c);
+			client->nreaddata = memunload(i, r, client->readdata, c);
 			if(client->nreaddata < 0){
 				free(client->readdata);
 				client->readdata = nil;
@@ -1852,9 +1969,10 @@
 			clipr = dst->clipr;
 			dst->clipr = r;
 			op = drawclientop(client);
+			bg = dst;
 			if(*a == 'x'){
 				/* paint background */
-				l = drawimage(client, a+47);
+				bg = drawimage(client, a+47);
 				drawpoint(&q, a+51);
 				r.min.x = p.x;
 				r.min.y = p.y-font->ascent;
@@ -1870,7 +1988,7 @@
 					r.max.x += font->fchar[ci].width;
 					u += 2;
 				}
-				memdraw(dst, r, l, q, memopaque, ZP, op);
+				memdraw(dst, r, bg, q, memopaque, ZP, op);
 				u -= 2*ni;
 			}
 			q = p;
@@ -1880,7 +1998,7 @@
 					dst->clipr = clipr;
 					error(Eindex);
 				}
-				q = drawchar(dst, q, src, &sp, font, ci, op);
+				q = drawchar(dst, bg, q, src, &sp, font, ci, op);
 				u += 2;
 			}
 			dst->clipr = clipr;
@@ -1938,7 +2056,7 @@
 				memltofrontn(lp, nw);
 			else
 				memltorearn(lp, nw);
-			if(lp[0]->layer->screen->image->data == screenimage->data)
+			if(screenimage && lp[0]->layer->screen->image->data == screenimage->data)
 				for(j=0; j<nw; j++)
 					addflush(lp[j]->layer->screenr);
 			ll = drawlookup(client, BGLONG(a+1+1+2), 1);
@@ -2009,7 +2127,6 @@
 	int num, den;
 	int i, j;
 
-	drawactive(1);	/* to restore map from backup */
 	for(r=0,i=0; r!=4; r++)
 	    for(v=0; v!=4; v++,i+=16){
 		for(g=0,j=v-r; g!=4; g++)
@@ -2031,54 +2148,4 @@
 				cr*0x01010101, cg*0x01010101, cb*0x01010101);
 		    }
 	}
-}
-
-void
-drawblankscreen(int blank)
-{
-	int i, nc;
-	ulong *p;
-
-	if(blank == sdraw.blanked)
-		return;
-	if(!canqlock(&sdraw))
-		return;
-	if(!initscreenimage()){
-		qunlock(&sdraw);
-		return;
-	}
-	p = sdraw.savemap;
-	nc = screenimage->depth > 8 ? 256 : 1<<screenimage->depth;
-
-	/*
-	 * blankscreen uses the hardware to blank the screen
-	 * when possible.  to help in cases when it is not possible,
-	 * we set the color map to be all black.
-	 */
-	if(blank == 0){	/* turn screen on */
-		for(i=0; i<nc; i++, p+=3)
-			setcolor(i, p[0], p[1], p[2]);
-		blankscreen(0);
-	}else{	/* turn screen off */
-		blankscreen(1);
-		for(i=0; i<nc; i++, p+=3){
-			getcolor(i, &p[0], &p[1], &p[2]);
-			setcolor(i, 0, 0, 0);
-		}
-	}
-	sdraw.blanked = blank;
-	qunlock(&sdraw);
-}
-
-/*
- * record activity on screen, changing blanking as appropriate
- */
-void
-drawactive(int active)
-{
-	if(active){
-		drawblankscreen(0);
-		sdraw.blanktime = 0;
-	}else
-		sdraw.blanktime++;
 }
--- a/sys/src/cmd/vnc/devmouse.c
+++ b/sys/src/cmd/vnc/devmouse.c
@@ -27,6 +27,7 @@
 	ulong	lastcounter;	/* value when /dev/mouse read */
 	Rendez	r;
 	Ref;
+	int	resize;
 	int	open;
 	Mousestate	queue[16];	/* circular buffer of click events */
 	ulong	ri;		/* read index into queue */
@@ -195,6 +196,10 @@
 			m.xy.x, m.xy.y, m.buttons, m.msec);
 
 		mouse.lastcounter = m.counter;
+		if(mouse.resize){
+			mouse.resize = 0;
+			buf[0] = 'r';
+		}
 
 		if(n > 1+4*12)
 			n = 1+4*12;
@@ -321,7 +326,7 @@
 int
 mousechanged(void*)
 {
-	return mouse.lastcounter != mouse.counter;
+	return mouse.lastcounter != mouse.counter || mouse.resize != 0;
 }
 
 Point
@@ -328,4 +333,14 @@
 mousexy(void)
 {
 	return mouse.xy;
+}
+
+/*
+ * notify reader that screen has been resized
+ */
+void
+mouseresize(void)
+{
+	mouse.resize = 1;
+	rendwakeup(&mouse.r);
 }
--- a/sys/src/cmd/vnc/draw.c
+++ b/sys/src/cmd/vnc/draw.c
@@ -260,9 +260,12 @@
 	case EncMouseWarp:
 		mousewarp(r.min);
 		return;
-
+	case EncDesktopSize:
+		v->canresize |= 1;
+		vncsetdim(v, r);
+		return;
 	case EncXDesktopSize:
-		v->canresize = 1;
+		v->canresize |= 2;
 		n = vncrdlong(v)>>24;
 		if(n <= 0)
 			break;
@@ -274,9 +277,7 @@
 			vncrdrect(v);
 			vncrdlong(v);
 		}
-		/* wet floor */
-	case EncDesktopSize:
-		vncsetdim(v, r);
+		vncsetdim(v, v->screen[0].rect);
 		return;
 	}
 
--- a/sys/src/cmd/vnc/screen.c
+++ b/sys/src/cmd/vnc/screen.c
@@ -42,10 +42,7 @@
 void
 screeninit(int x, int y, char *chanstr)
 {
-	Point p, q;
-	char *greet;
 	char buf[128];
-	Memimage *grey;
 	Rectangle r;
 	int chan;
 
@@ -78,16 +75,6 @@
 		error(buf);
 	}
 
-	drawlock();
-
-	/*
-	 * set up goo for screenputs
-	 */
-	memdefont = getmemdefont();
-
-	back = memwhite;
-	conscol = memblack;
-
 	/* a lot of work to get a grey color */
 	curscol = allocmemimage(Rect(0,0,1,1), RGBA32);
 	curscol->flags |= Frepl;
@@ -94,15 +81,27 @@
 	curscol->clipr = gscreen->r;
 	memfillcolor(curscol, 0xff0000ff);
 
-	memfillcolor(gscreen, 0x444488FF);
+	screenwin();
 
-	w = memdefont->info[' '].width;
-	h = memdefont->height;
+	setcursor(&arrow);
+}
 
-	window.min = addpt(gscreen->r.min, Pt(20,20));
-	window.max.x = window.min.x + Dx(gscreen->r)*3/4-40;
-	window.max.y = window.min.y + Dy(gscreen->r)*3/4-100;
+void
+screenwin(void)
+{
+	Point p;
+	char *greet;
+	Memimage *grey;
 
+	qlock(&drawlock);
+	back = memwhite;
+	conscol = memblack;
+	memfillcolor(gscreen, 0x888844FF);
+	
+	memdefont = getmemdefont();
+	h = memdefont->height;
+
+	window = insetrect(gscreen->clipr, 20);
 	memimagedraw(gscreen, window, memblack, ZP, memopaque, ZP, S);
 	window = insetrect(window, 4);
 	memimagedraw(gscreen, window, memwhite, ZP, memopaque, ZP, S);
@@ -119,22 +118,18 @@
 
 	greet = " Plan 9 Console ";
 	p = addpt(window.min, Pt(10, 0));
-	q = memsubfontwidth(memdefont, greet);
 	memimagestring(gscreen, p, conscol, ZP, memdefont, greet);
 	window.min.y += h+6;
 	curpos = window.min;
 	window.max.y = window.min.y+((window.max.y-window.min.y)/h)*h;
 	flushmemscreen(gscreen->r);
-
-	drawunlock();
-
-	setcursor(&arrow);
+	qunlock(&drawlock);
 }
 
 uchar*
 attachscreen(Rectangle* r, ulong* chan, int* d, int* width, int *softscreen)
 {
-	*r = gscreen->r;
+	*r = gscreen->clipr;
 	*d = gscreen->depth;
 	*chan = gscreen->chan;
 	*width = gscreen->width;
@@ -263,7 +258,7 @@
 	memimagedraw(gscreen, r, gscreen, p, nil, p, S);
 	r = Rpt(Pt(window.min.x, window.max.y-o), window.max);
 	memimagedraw(gscreen, r, back, ZP, nil, ZP, S);
-	flushmemscreen(gscreen->r);
+	flushmemscreen(gscreen->clipr);
 
 	curpos.y -= o;
 }
@@ -336,7 +331,7 @@
 	static int nrb;
 	char *e;
 
-	drawlock();
+	qlock(&drawlock);
 	e = s + n;
 	while(s < e){
 		rb[nrb++] = *s++;
@@ -347,5 +342,5 @@
 		}
 	}
 	screenflush();
-	drawunlock();
+	qunlock(&drawlock);
 }
--- a/sys/src/cmd/vnc/screen.h
+++ b/sys/src/cmd/vnc/screen.h
@@ -11,6 +11,7 @@
 extern int		cursorver;
 extern Point		cursorpos;
 
+void		mouseresize(void);
 Point 		mousexy(void);
 void		cursoron(void);
 void		cursoroff(void);
@@ -19,16 +20,18 @@
 Rectangle	cursorrect(void);
 void		cursordraw(Memimage *dst, Rectangle r);
 
+extern QLock	drawlock;
 void		drawactive(int);
-void		drawlock(void);
-void		drawunlock(void);
-int		candrawlock(void);
 void		getcolor(ulong, ulong*, ulong*, ulong*);
 int		setcolor(ulong, ulong, ulong, ulong);
 #define		TK2SEC(x)	0
 extern void	blankscreen(int);
 void		screeninit(int x, int y, char *chanstr);
+void		screenwin(void);
 void		absmousetrack(int x, int y, int b, ulong msec);
 uchar		*attachscreen(Rectangle*, ulong*, int*, int*, int*);
+void		deletescreenimage(void);
+void		resetscreenimage(void);
 
 void		fsinit(char *mntpt, int x, int y, char *chanstr);
+#define		ishwimage(i)	0
--- a/sys/src/cmd/vnc/vncs.c
+++ b/sys/src/cmd/vnc/vncs.c
@@ -571,7 +571,7 @@
 	if(!shared)
 		killclients(v);
 
-	v->dim = rectsubpt(gscreen->r, gscreen->r.min);
+	v->dim = rectsubpt(gscreen->clipr, gscreen->clipr.min);
 	vncwrpoint(v, v->dim.max);
 	if(verbose)
 		fprint(2, "%V: send screen size %R\n", v, v->dim);
@@ -592,6 +592,8 @@
 	if(verbose)
 		fprint(2, "%V: handshaking done\n", v);
 
+	v->updatereq = 0;
+
 	switch(rfork(RFPROC|RFMEM)){
 	case -1:
 		fprint(2, "%V: cannot fork: %r; hanging up\n", v);
@@ -674,7 +676,10 @@
 			v->canwarp = 1;
 			continue;
 		case EncDesktopSize:
+			v->canresize |= 1;
+			continue;
 		case EncXDesktopSize:
+			v->canresize |= 2;
 			continue;
 		}
 		if(v->countrect != nil)
@@ -710,9 +715,10 @@
 	}
 
 	if(verbose)
-		fprint(2, "Encoding with %s%s%s\n", v->encname,
+		fprint(2, "Encoding with %s%s%s%s\n", v->encname,
 			v->copyrect ? ", copyrect" : "",
-			v->canwarp ? ", canwarp" : "");
+			v->canwarp ? ", canwarp" : "",
+			v->canresize ? ", resize" : "");
 }
 
 /*
@@ -755,20 +761,42 @@
 		case MFrameReq:
 			incremental = vncrdchar(v);
 			r = vncrdrect(v);
-			if(incremental){
-				vnclock(v);
-				v->updaterequest = 1;
-				vncunlock(v);
-			}else{
-				drawlock();	/* protects rlist */
-				vnclock(v);	/* protects updaterequest */
-				v->updaterequest = 1;
+			if(!incremental){
+				qlock(&drawlock);	/* protects rlist */
 				addtorlist(&v->rlist, r);
-				vncunlock(v);
-				drawunlock();
+				qunlock(&drawlock);
 			}
+			v->updatereq++;
 			break;
 
+		case MSetDesktopSize:
+			vncrdchar(v);
+			vncrdpoint(v);	// desktop size
+			n = vncrdchar(v);
+			vncrdchar(v);
+			if(n == 0)
+				break;
+			vncrdlong(v);	// id
+			r = vncrdrect(v);
+			vncrdlong(v);	// flags
+			while(--n > 0){
+				vncrdlong(v);
+				vncrdrect(v);
+				vncrdlong(v);
+			}
+			qlock(&drawlock);
+			if(!rectclip(&r, gscreen->r)){
+				qunlock(&drawlock);
+				break;
+			}
+			gscreen->clipr = r;
+			qunlock(&drawlock);
+
+			screenwin();
+			deletescreenimage();
+			resetscreenimage();
+			break;
+
 		/* send keystroke */
 		case MKey:
 			keydown = vncrdchar(v);
@@ -905,7 +933,7 @@
 {
 	Vncs *v;
 
-	if(!rectclip(&r, gscreen->r))
+	if(!rectclip(&r, gscreen->clipr))
 		return;
 	qlock(&clients);
 	for(v=clients.head; v; v=v->next)
@@ -925,7 +953,7 @@
 	for(v=clients.head; v; v=v->next){
 		if(v->canwarp){
 			vnclock(v);
-			v->needwarp = 1;
+			v->dowarp = 1;
 			v->warppt = p;
 			vncunlock(v);
 		}
@@ -940,7 +968,7 @@
 static int
 updateimage(Vncs *v)
 {
-	int i, ncount, nsend, docursor, needwarp;
+	int i, j, ncount, nsend, docursor, dowarp, doresize;
 	vlong ooffset;
 	Point warppt;
 	Rectangle cr;
@@ -949,20 +977,37 @@
 	int (*count)(Vncs*, Rectangle);
 	int (*send)(Vncs*, Rectangle);
 
-	if(v->image == nil)
-		return 0;
-
-	/* warping info and unlock v so that updates can proceed */
-	needwarp = v->canwarp && v->needwarp;
+	vnclock(v);
+	dowarp = v->canwarp && v->dowarp;
 	warppt = v->warppt;
-	v->needwarp = 0;
+	v->dowarp = 0;
 	vncunlock(v);
 
 	/* copy the screen bits and then unlock the screen so updates can proceed */
-	drawlock();
+	qlock(&drawlock);
 	rlist = v->rlist;
 	memset(&v->rlist, 0, sizeof v->rlist);
 
+	if(v->canresize && !eqrect(v->screen[0].rect, gscreen->clipr)){
+		v->screen[0].rect = gscreen->clipr;
+		v->dim = rectsubpt(gscreen->clipr, gscreen->clipr.min);
+		doresize = 1;
+	} else
+		doresize = 0;
+
+	if(doresize
+	|| (v->image == nil && v->imagechan != 0)
+	|| (v->image != nil && v->image->chan != v->imagechan)){
+		if(v->image)
+			freememimage(v->image);
+		v->image = allocmemimage(v->dim, v->imagechan);
+		if(v->image == nil){
+			fprint(2, "%V: allocmemimage: %r; hanging up\n", v);
+			qlock(&drawlock);
+			vnchungup(v);
+		}
+	}
+
 	/* if the cursor has moved or changed shape, we need to redraw its square */
 	lock(&cursor);
 	if(v->cursorver != cursorver || !eqpt(v->cursorpos, cursorpos)){
@@ -978,7 +1023,7 @@
 
 	if(docursor){
 		addtorlist(&rlist, v->cursorr);
-		if(!rectclip(&cr, gscreen->r))
+		if(!rectclip(&cr, gscreen->clipr))
 			cr.max = cr.min;
 		addtorlist(&rlist, cr);
 	}
@@ -996,16 +1041,8 @@
 		v->cursorr = cr;
 	}
 
-	drawunlock();
+	qunlock(&drawlock);
 
-	ooffset = Boffset(&v->out);
-	/* no more locks are held; talk to the client */
-
-	if(rlist.nrect == 0 && needwarp == 0){
-		vnclock(v);
-		return 0;
-	}
-
 	count = v->countrect;
 	send = v->sendrect;
 	if(count == nil || send == nil){
@@ -1014,17 +1051,46 @@
 	}
 
 	ncount = 0;
-	for(i=0; i<rlist.nrect; i++)
-		ncount += (*count)(v, rlist.rect[i]);
+	for(i=j=0; i<rlist.nrect; i++){
+		if(j < i)
+			rlist.rect[j] = rlist.rect[i];
+		if(rectclip(&rlist.rect[j], v->dim))
+			ncount += (*count)(v, rlist.rect[j++]);
+	}
+	rlist.nrect = j;
 
-	if(verbose > 1)
-		fprint(2, "sendupdate: rlist.nrect=%d, ncount=%d", rlist.nrect, ncount);
+	if(doresize == 0 && ncount == 0 && dowarp == 0)
+		return 0;
 
-	t1 = nsec();
+	if(verbose > 1){
+		fprint(2, "sendupdate: rlist.nrect=%d, ncount=%d\n", rlist.nrect, ncount);
+		t1 = nsec();
+		ooffset = Boffset(&v->out);
+	}
+
+	if(doresize && v->canresize == 1){
+		doresize = 0;
+
+		vncwrchar(v, MFrameUpdate);
+		vncwrchar(v, 0);
+		vncwrshort(v, 1);
+		vncwrrect(v, v->dim);
+		vncwrlong(v, EncDesktopSize);
+	}
+
 	vncwrchar(v, MFrameUpdate);
 	vncwrchar(v, 0);
-	vncwrshort(v, ncount+needwarp);
+	vncwrshort(v, doresize+ncount+dowarp);
 
+	if(doresize){
+		vncwrrect(v, gscreen->r);
+		vncwrlong(v, EncXDesktopSize);
+		vncwrlong(v, 1<<24);
+		vncwrlong(v, v->screen[0].id);
+		vncwrrect(v, v->screen[0].rect);
+		vncwrlong(v, v->screen[0].flags);
+	}
+
 	nsend = 0;
 	for(i=0; i<rlist.nrect; i++)
 		nsend += (*send)(v, rlist.rect[i]);
@@ -1034,17 +1100,17 @@
 		vnchungup(v);
 	}
 
-	if(needwarp){
+	if(dowarp){
 		vncwrrect(v, Rect(warppt.x, warppt.y, warppt.x+1, warppt.y+1));
 		vncwrlong(v, EncMouseWarp);
 	}
 
-	t1 = nsec() - t1;
-	if(verbose > 1)
+	if(verbose > 1){
+		t1 = nsec() - t1;
 		fprint(2, " in %lldms, %lld bytes\n", t1/1000000, Boffset(&v->out) - ooffset);
+	}
 
 	freerlist(&rlist);
-	vnclock(v);
 	return 1;
 }
 
@@ -1059,13 +1125,11 @@
 
 	if(v->snarfvers == snarf.vers)
 		return;
-	vncunlock(v);
 	qlock(&snarf);
 	len = snarf.n;
 	buf = malloc(len);
 	if(buf == nil){
 		qunlock(&snarf);
-		vnclock(v);
 		return;
 	}
 	memmove(buf, snarf.buf, len);
@@ -1077,7 +1141,6 @@
 	vncwrlong(v, len);
 	vncwrbytes(v, buf, len);
 	free(buf);
-	vnclock(v);
 }
 
 /*
@@ -1086,41 +1149,16 @@
 static void
 clientwriteproc(Vncs *v)
 {
-	char buf[32], buf2[32];
-	int sent;
+	ulong last = 0;
 
 	vncname("write %V", v);
-	for(;;){
-		vnclock(v);
-		if(v->ndead)
-			break;
-		if((v->image == nil && v->imagechan!=0)
-		|| (v->image && v->image->chan != v->imagechan)){
-			if(v->image)
-				freememimage(v->image);
-			v->image = allocmemimage(v->dim, v->imagechan);
-			if(v->image == nil){
-				fprint(2, "%V: allocmemimage: %r; hanging up\n", v);
-				vnchungup(v);
-			}
-			if(verbose)
-				fprint(2, "%V: translating image from chan=%s to chan=%s\n",
-					v, chantostr(buf, gscreen->chan), chantostr(buf2, v->imagechan));
-		}
-		sent = 0;
-		if(v->updaterequest){
-			v->updaterequest = 0;
-			updatesnarf(v);
-			sent = updateimage(v);
-			if(!sent)
-				v->updaterequest = 1;
-		}
-		vncunlock(v);
+	while(!v->ndead){
+		sleep(sleeptime);
+		updatesnarf(v);
+		if(v->updatereq != last && updateimage(v))
+			last++;
 		vncflush(v);
-		if(!sent)
-			sleep(sleeptime);
 	}
-	vncunlock(v);
 	vnchungup(v);
 }
 
--- a/sys/src/cmd/vnc/vncs.h
+++ b/sys/src/cmd/vnc/vncs.h
@@ -22,10 +22,11 @@
 	int		(*sendrect)(Vncs*, Rectangle);
 	int		copyrect;
 	int		canwarp;
-	int		needwarp;
+	int		dowarp;
 	Point		warppt;
 
-	int		updaterequest;
+	ulong		updatereq;
+
 	Rlist		rlist;
 	int		ndead;
 	int		nproc;