plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

devdraw.c (32173B)


      1 /*
      2  * /dev/draw simulator -- handles the messages prepared by the draw library.
      3  * Doesn't simulate the file system part, just the messages.
      4  */
      5 
      6 #include <u.h>
      7 #include <libc.h>
      8 #include <draw.h>
      9 #include <memdraw.h>
     10 #include <memlayer.h>
     11 #include <mouse.h>
     12 #include <cursor.h>
     13 #include <keyboard.h>
     14 #include <drawfcall.h>
     15 #include "devdraw.h"
     16 
     17 QLock	drawlk;
     18 
     19 static	int		drawuninstall(Client*, int);
     20 static	Memimage*	drawinstall(Client*, int, Memimage*, DScreen*);
     21 static	void		drawfreedimage(Client*, DImage*);
     22 
     23 void
     24 draw_initdisplaymemimage(Client *c, Memimage *m)
     25 {
     26 	c->screenimage = m;
     27 	m->screenref = 1;
     28 	c->slot = 0;
     29 	c->clientid = 1;
     30 	c->op = SoverD;
     31 }
     32 
     33 // gfx_replacescreenimage replaces c's screen image with m.
     34 // It is called by the host driver on the main host thread.
     35 void
     36 gfx_replacescreenimage(Client *c, Memimage *m)
     37 {
     38 	/*
     39 	 * Replace the screen image because the screen
     40 	 * was resized.
     41 	 *
     42 	 * In theory there should only be one reference
     43 	 * to the current screen image, and that's through
     44 	 * client0's image 0, installed a few lines above.
     45 	 * Once the client drops the image, the underlying backing
     46 	 * store freed properly.  The client is being notified
     47 	 * about the resize through external means, so all we
     48 	 * need to do is this assignment.
     49 	 */
     50 	Memimage *om;
     51 
     52 	qlock(&drawlk);
     53 	om = c->screenimage;
     54 	c->screenimage = m;
     55 	m->screenref = 1;
     56 	if(om && --om->screenref == 0){
     57 		_freememimage(om);
     58 	}
     59 	qunlock(&drawlk);
     60 	gfx_mouseresized(c);
     61 }
     62 
     63 static void
     64 drawrefreshscreen(DImage *l, Client *client)
     65 {
     66 	while(l != nil && l->dscreen == nil)
     67 		l = l->fromname;
     68 	if(l != nil && l->dscreen->owner != client)
     69 		l->dscreen->owner->refreshme = 1;
     70 }
     71 
     72 static void
     73 drawrefresh(Memimage *m, Rectangle r, void *v)
     74 {
     75 	Refx *x;
     76 	DImage *d;
     77 	Client *c;
     78 	Refresh *ref;
     79 
     80 	USED(m);
     81 
     82 	if(v == 0)
     83 		return;
     84 	x = v;
     85 	c = x->client;
     86 	d = x->dimage;
     87 	for(ref=c->refresh; ref; ref=ref->next)
     88 		if(ref->dimage == d){
     89 			combinerect(&ref->r, r);
     90 			return;
     91 		}
     92 	ref = mallocz(sizeof(Refresh), 1);
     93 	if(ref){
     94 		ref->dimage = d;
     95 		ref->r = r;
     96 		ref->next = c->refresh;
     97 		c->refresh = ref;
     98 	}
     99 }
    100 
    101 static void
    102 addflush(Client *c, Rectangle r)
    103 {
    104 	int abb, ar, anbb;
    105 	Rectangle nbb, fr;
    106 
    107 	if(/*sdraw.softscreen==0 ||*/ !rectclip(&r, c->screenimage->r))
    108 		return;
    109 
    110 	if(c->flushrect.min.x >= c->flushrect.max.x){
    111 		c->flushrect = r;
    112 		c->waste = 0;
    113 		return;
    114 	}
    115 	nbb = c->flushrect;
    116 	combinerect(&nbb, r);
    117 	ar = Dx(r)*Dy(r);
    118 	abb = Dx(c->flushrect)*Dy(c->flushrect);
    119 	anbb = Dx(nbb)*Dy(nbb);
    120 	/*
    121 	 * Area of new waste is area of new bb minus area of old bb,
    122 	 * less the area of the new segment, which we assume is not waste.
    123 	 * This could be negative, but that's OK.
    124 	 */
    125 	c->waste += anbb-abb - ar;
    126 	if(c->waste < 0)
    127 		c->waste = 0;
    128 	/*
    129 	 * absorb if:
    130 	 *	total area is small
    131 	 *	waste is less than half total area
    132 	 * 	rectangles touch
    133 	 */
    134 	if(anbb<=1024 || c->waste*2<anbb || rectXrect(c->flushrect, r)){
    135 		c->flushrect = nbb;
    136 		return;
    137 	}
    138 	/* emit current state */
    139 	fr = c->flushrect;
    140 	c->flushrect = r;
    141 	c->waste = 0;
    142 	if(fr.min.x < fr.max.x) {
    143 		// Unlock drawlk because rpc_flush may want to run on gfx thread,
    144 		// and gfx thread might be blocked on drawlk trying to install a new screen
    145 		// during a resize.
    146 		rpc_gfxdrawunlock();
    147 		qunlock(&drawlk);
    148 		c->impl->rpc_flush(c, fr);
    149 		qlock(&drawlk);
    150 		rpc_gfxdrawlock();
    151 	}
    152 }
    153 
    154 static void
    155 dstflush(Client *c, int dstid, Memimage *dst, Rectangle r)
    156 {
    157 	Memlayer *l;
    158 
    159 	if(dstid == 0){
    160 		combinerect(&c->flushrect, r);
    161 		return;
    162 	}
    163 	/* how can this happen? -rsc, dec 12 2002 */
    164 	if(dst == 0){
    165 		fprint(2, "nil dstflush\n");
    166 		return;
    167 	}
    168 	l = dst->layer;
    169 	if(l == nil)
    170 		return;
    171 	do{
    172 		if(l->screen->image->data != c->screenimage->data)
    173 			return;
    174 		r = rectaddpt(r, l->delta);
    175 		l = l->screen->image->layer;
    176 	}while(l);
    177 	addflush(c, r);
    178 }
    179 
    180 static void
    181 drawflush(Client *c)
    182 {
    183 	Rectangle r;
    184 
    185 	r = c->flushrect;
    186 	c->flushrect = Rect(10000, 10000, -10000, -10000);
    187 	if(r.min.x < r.max.x) {
    188 		// Unlock drawlk because rpc_flush may want to run on gfx thread,
    189 		// and gfx thread might be blocked on drawlk trying to install a new screen
    190 		// during a resize.
    191 		rpc_gfxdrawunlock();
    192 		qunlock(&drawlk);
    193 		c->impl->rpc_flush(c, r);
    194 		qlock(&drawlk);
    195 		rpc_gfxdrawlock();
    196 	}
    197 }
    198 
    199 static int
    200 drawcmp(char *a, char *b, int n)
    201 {
    202 	if(strlen(a) != n)
    203 		return 1;
    204 	return memcmp(a, b, n);
    205 }
    206 
    207 static DName*
    208 drawlookupname(Client *client, int n, char *str)
    209 {
    210 	DName *name, *ename;
    211 
    212 	name = client->name;
    213 	ename = &name[client->nname];
    214 	for(; name<ename; name++)
    215 		if(drawcmp(name->name, str, n) == 0)
    216 			return name;
    217 	return 0;
    218 }
    219 
    220 static int
    221 drawgoodname(Client *client, DImage *d)
    222 {
    223 	DName *n;
    224 
    225 	/* if window, validate the screen's own images */
    226 	if(d->dscreen)
    227 		if(drawgoodname(client, d->dscreen->dimage) == 0
    228 		|| drawgoodname(client, d->dscreen->dfill) == 0)
    229 			return 0;
    230 	if(d->name == nil)
    231 		return 1;
    232 	n = drawlookupname(client, strlen(d->name), d->name);
    233 	if(n==nil || n->vers!=d->vers)
    234 		return 0;
    235 	return 1;
    236 }
    237 
    238 static DImage*
    239 drawlookup(Client *client, int id, int checkname)
    240 {
    241 	DImage *d;
    242 
    243 	d = client->dimage[id&HASHMASK];
    244 	while(d){
    245 		if(d->id == id){
    246 			/*
    247 			 * BUG: should error out but too hard.
    248 			 * Return 0 instead.
    249 			 */
    250 			if(checkname && !drawgoodname(client, d))
    251 				return 0;
    252 			return d;
    253 		}
    254 		d = d->next;
    255 	}
    256 	return 0;
    257 }
    258 
    259 static DScreen*
    260 drawlookupdscreen(Client *c, int id)
    261 {
    262 	DScreen *s;
    263 
    264 	s = c->dscreen;
    265 	while(s){
    266 		if(s->id == id)
    267 			return s;
    268 		s = s->next;
    269 	}
    270 	return 0;
    271 }
    272 
    273 static DScreen*
    274 drawlookupscreen(Client *client, int id, CScreen **cs)
    275 {
    276 	CScreen *s;
    277 
    278 	s = client->cscreen;
    279 	while(s){
    280 		if(s->dscreen->id == id){
    281 			*cs = s;
    282 			return s->dscreen;
    283 		}
    284 		s = s->next;
    285 	}
    286 	/* caller must check! */
    287 	return 0;
    288 }
    289 
    290 static Memimage*
    291 drawinstall(Client *client, int id, Memimage *i, DScreen *dscreen)
    292 {
    293 	DImage *d;
    294 
    295 	d = mallocz(sizeof(DImage), 1);
    296 	if(d == 0)
    297 		return 0;
    298 	d->id = id;
    299 	d->ref = 1;
    300 	d->name = 0;
    301 	d->vers = 0;
    302 	d->image = i;
    303 	if(i->screenref)
    304 		++i->screenref;
    305 	d->nfchar = 0;
    306 	d->fchar = 0;
    307 	d->fromname = 0;
    308 	d->dscreen = dscreen;
    309 	d->next = client->dimage[id&HASHMASK];
    310 	client->dimage[id&HASHMASK] = d;
    311 	return i;
    312 }
    313 
    314 static Memscreen*
    315 drawinstallscreen(Client *client, DScreen *d, int id, DImage *dimage, DImage *dfill, int public)
    316 {
    317 	Memscreen *s;
    318 	CScreen *c;
    319 
    320 	c = mallocz(sizeof(CScreen), 1);
    321 	if(dimage && dimage->image && dimage->image->chan == 0){
    322 		fprint(2, "bad image %p in drawinstallscreen", dimage->image);
    323 		abort();
    324 	}
    325 
    326 	if(c == 0)
    327 		return 0;
    328 	if(d == 0){
    329 		d = mallocz(sizeof(DScreen), 1);
    330 		if(d == 0){
    331 			free(c);
    332 			return 0;
    333 		}
    334 		s = mallocz(sizeof(Memscreen), 1);
    335 		if(s == 0){
    336 			free(c);
    337 			free(d);
    338 			return 0;
    339 		}
    340 		s->frontmost = 0;
    341 		s->rearmost = 0;
    342 		d->dimage = dimage;
    343 		if(dimage){
    344 			s->image = dimage->image;
    345 			dimage->ref++;
    346 		}
    347 		d->dfill = dfill;
    348 		if(dfill){
    349 			s->fill = dfill->image;
    350 			dfill->ref++;
    351 		}
    352 		d->ref = 0;
    353 		d->id = id;
    354 		d->screen = s;
    355 		d->public = public;
    356 		d->next = client->dscreen;
    357 		d->owner = client;
    358 		client->dscreen = d;
    359 	}
    360 	c->dscreen = d;
    361 	d->ref++;
    362 	c->next = client->cscreen;
    363 	client->cscreen = c;
    364 	return d->screen;
    365 }
    366 
    367 static void
    368 drawdelname(Client *client, DName *name)
    369 {
    370 	int i;
    371 
    372 	i = name-client->name;
    373 	memmove(name, name+1, (client->nname-(i+1))*sizeof(DName));
    374 	client->nname--;
    375 }
    376 
    377 static void
    378 drawfreedscreen(Client *client, DScreen *this)
    379 {
    380 	DScreen *ds, *next;
    381 
    382 	this->ref--;
    383 	if(this->ref < 0)
    384 		fprint(2, "negative ref in drawfreedscreen\n");
    385 	if(this->ref > 0)
    386 		return;
    387 	ds = client->dscreen;
    388 	if(ds == this){
    389 		client->dscreen = this->next;
    390 		goto Found;
    391 	}
    392 	while(next = ds->next){	/* assign = */
    393 		if(next == this){
    394 			ds->next = this->next;
    395 			goto Found;
    396 		}
    397 		ds = next;
    398 	}
    399 	/*
    400 	 * Should signal Enodrawimage, but too hard.
    401 	 */
    402 	return;
    403 
    404     Found:
    405 	if(this->dimage)
    406 		drawfreedimage(client, this->dimage);
    407 	if(this->dfill)
    408 		drawfreedimage(client, this->dfill);
    409 	free(this->screen);
    410 	free(this);
    411 }
    412 
    413 static void
    414 drawfreedimage(Client *client, DImage *dimage)
    415 {
    416 	int i;
    417 	Memimage *l;
    418 	DScreen *ds;
    419 
    420 	dimage->ref--;
    421 	if(dimage->ref < 0)
    422 		fprint(2, "negative ref in drawfreedimage\n");
    423 	if(dimage->ref > 0)
    424 		return;
    425 
    426 	/* any names? */
    427 	for(i=0; i<client->nname; )
    428 		if(client->name[i].dimage == dimage)
    429 			drawdelname(client, client->name+i);
    430 		else
    431 			i++;
    432 	if(dimage->fromname){	/* acquired by name; owned by someone else*/
    433 		drawfreedimage(client, dimage->fromname);
    434 		goto Return;
    435 	}
    436 	ds = dimage->dscreen;
    437 	l = dimage->image;
    438 	dimage->dscreen = nil;	/* paranoia */
    439 	dimage->image = nil;
    440 	if(ds){
    441 		if(l->data == client->screenimage->data)
    442 			addflush(client, l->layer->screenr);
    443 		if(l->layer->refreshfn == drawrefresh)	/* else true owner will clean up */
    444 			free(l->layer->refreshptr);
    445 		l->layer->refreshptr = nil;
    446 		if(drawgoodname(client, dimage))
    447 			memldelete(l);
    448 		else
    449 			memlfree(l);
    450 		drawfreedscreen(client, ds);
    451 	}else{
    452 		if(l->screenref==0)
    453 			freememimage(l);
    454 		else if(--l->screenref==0)
    455 			_freememimage(l);
    456 	}
    457     Return:
    458 	free(dimage->fchar);
    459 	free(dimage);
    460 }
    461 
    462 static void
    463 drawuninstallscreen(Client *client, CScreen *this)
    464 {
    465 	CScreen *cs, *next;
    466 
    467 	cs = client->cscreen;
    468 	if(cs == this){
    469 		client->cscreen = this->next;
    470 		drawfreedscreen(client, this->dscreen);
    471 		free(this);
    472 		return;
    473 	}
    474 	while(next = cs->next){	/* assign = */
    475 		if(next == this){
    476 			cs->next = this->next;
    477 			drawfreedscreen(client, this->dscreen);
    478 			free(this);
    479 			return;
    480 		}
    481 		cs = next;
    482 	}
    483 }
    484 
    485 static int
    486 drawuninstall(Client *client, int id)
    487 {
    488 	DImage *d, **l;
    489 
    490 	for(l=&client->dimage[id&HASHMASK]; (d=*l) != nil; l=&d->next){
    491 		if(d->id == id){
    492 			*l = d->next;
    493 			drawfreedimage(client, d);
    494 			return 0;
    495 		}
    496 	}
    497 	return -1;
    498 }
    499 
    500 static int
    501 drawaddname(Client *client, DImage *di, int n, char *str, char **err)
    502 {
    503 	DName *name, *ename, *new, *t;
    504 	char *ns;
    505 
    506 	name = client->name;
    507 	ename = &name[client->nname];
    508 	for(; name<ename; name++)
    509 		if(drawcmp(name->name, str, n) == 0){
    510 			*err = "image name in use";
    511 			return -1;
    512 		}
    513 	t = mallocz((client->nname+1)*sizeof(DName), 1);
    514 	ns = malloc(n+1);
    515 	if(t == nil || ns == nil){
    516 		free(t);
    517 		free(ns);
    518 		*err = "out of memory";
    519 		return -1;
    520 	}
    521 	memmove(t, client->name, client->nname*sizeof(DName));
    522 	free(client->name);
    523 	client->name = t;
    524 	new = &client->name[client->nname++];
    525 	new->name = ns;
    526 	memmove(new->name, str, n);
    527 	new->name[n] = 0;
    528 	new->dimage = di;
    529 	new->client = client;
    530 	new->vers = ++client->namevers;
    531 	return 0;
    532 }
    533 
    534 static int
    535 drawclientop(Client *cl)
    536 {
    537 	int op;
    538 
    539 	op = cl->op;
    540 	cl->op = SoverD;
    541 	return op;
    542 }
    543 
    544 static Memimage*
    545 drawimage(Client *client, uchar *a)
    546 {
    547 	DImage *d;
    548 
    549 	d = drawlookup(client, BGLONG(a), 1);
    550 	if(d == nil)
    551 		return nil;	/* caller must check! */
    552 	return d->image;
    553 }
    554 
    555 static void
    556 drawrectangle(Rectangle *r, uchar *a)
    557 {
    558 	r->min.x = BGLONG(a+0*4);
    559 	r->min.y = BGLONG(a+1*4);
    560 	r->max.x = BGLONG(a+2*4);
    561 	r->max.y = BGLONG(a+3*4);
    562 }
    563 
    564 static void
    565 drawpoint(Point *p, uchar *a)
    566 {
    567 	p->x = BGLONG(a+0*4);
    568 	p->y = BGLONG(a+1*4);
    569 }
    570 
    571 static Point
    572 drawchar(Memimage *dst, Point p, Memimage *src, Point *sp, DImage *font, int index, int op)
    573 {
    574 	FChar *fc;
    575 	Rectangle r;
    576 	Point sp1;
    577 
    578 	fc = &font->fchar[index];
    579 	r.min.x = p.x+fc->left;
    580 	r.min.y = p.y-(font->ascent-fc->miny);
    581 	r.max.x = r.min.x+(fc->maxx-fc->minx);
    582 	r.max.y = r.min.y+(fc->maxy-fc->miny);
    583 	sp1.x = sp->x+fc->left;
    584 	sp1.y = sp->y+fc->miny;
    585 	memdraw(dst, r, src, sp1, font->image, Pt(fc->minx, fc->miny), op);
    586 	p.x += fc->width;
    587 	sp->x += fc->width;
    588 	return p;
    589 }
    590 
    591 static uchar*
    592 drawcoord(uchar *p, uchar *maxp, int oldx, int *newx)
    593 {
    594 	int b, x;
    595 
    596 	if(p >= maxp)
    597 		return nil;
    598 	b = *p++;
    599 	x = b & 0x7F;
    600 	if(b & 0x80){
    601 		if(p+1 >= maxp)
    602 			return nil;
    603 		x |= *p++ << 7;
    604 		x |= *p++ << 15;
    605 		if(x & (1<<22))
    606 			x |= ~0U<<23;
    607 	}else{
    608 		if(b & 0x40)
    609 			x |= ~0U<<7;
    610 		x += oldx;
    611 	}
    612 	*newx = x;
    613 	return p;
    614 }
    615 
    616 int
    617 draw_dataread(Client *cl, void *a, int n)
    618 {
    619 	qlock(&drawlk);
    620 	if(cl->readdata == nil){
    621 		werrstr("no draw data");
    622 		goto err;
    623 	}
    624 	if(n < cl->nreaddata){
    625 		werrstr("short read");
    626 		goto err;
    627 	}
    628 	n = cl->nreaddata;
    629 	memmove(a, cl->readdata, cl->nreaddata);
    630 	free(cl->readdata);
    631 	cl->readdata = nil;
    632 	qunlock(&drawlk);
    633 	return n;
    634 
    635 err:
    636 	qunlock(&drawlk);
    637 	return -1;
    638 }
    639 
    640 int
    641 draw_datawrite(Client *client, void *v, int n)
    642 {
    643 	char cbuf[40], *err, ibuf[12*12+1], *s;
    644 	int c, ci, doflush, dstid, e0, e1, esize, j, m;
    645 	int ni, nw, oesize, oldn, op, ox, oy, repl, scrnid, y;
    646 	uchar *a, refresh, *u;
    647 	u32int chan, value;
    648 	CScreen *cs;
    649 	DImage *di, *ddst, *dsrc, *font, *ll;
    650 	DName *dn;
    651 	DScreen *dscrn;
    652 	FChar *fc;
    653 	Fmt fmt;
    654 	Memimage *dst, *i, *l, **lp, *mask, *src;
    655 	Memscreen *scrn;
    656 	Point p, *pp, q, sp;
    657 	Rectangle clipr, r;
    658 	Refreshfn reffn;
    659 	Refx *refx;
    660 
    661 	qlock(&drawlk);
    662 	rpc_gfxdrawlock();
    663 	a = v;
    664 	m = 0;
    665 	oldn = n;
    666 
    667 	while((n-=m) > 0){
    668 		a += m;
    669 /*fprint(2, "msgwrite %d(%d)...", n, *a); */
    670 		switch(*a){
    671 		default:
    672 /*fprint(2, "bad command %d\n", *a); */
    673 			err = "bad draw command";
    674 			goto error;
    675 
    676 		/* allocate: 'b' id[4] screenid[4] refresh[1] chan[4] repl[1]
    677 			R[4*4] clipR[4*4] rrggbbaa[4]
    678 		 */
    679 		case 'b':
    680 			m = 1+4+4+1+4+1+4*4+4*4+4;
    681 			if(n < m)
    682 				goto Eshortdraw;
    683 			dstid = BGLONG(a+1);
    684 			scrnid = BGSHORT(a+5);
    685 			refresh = a[9];
    686 			chan = BGLONG(a+10);
    687 			repl = a[14];
    688 			drawrectangle(&r, a+15);
    689 			drawrectangle(&clipr, a+31);
    690 			value = BGLONG(a+47);
    691 			if(drawlookup(client, dstid, 0))
    692 				goto Eimageexists;
    693 			if(scrnid){
    694 				dscrn = drawlookupscreen(client, scrnid, &cs);
    695 				if(!dscrn)
    696 					goto Enodrawscreen;
    697 				scrn = dscrn->screen;
    698 				if(repl || chan!=scrn->image->chan){
    699 					err = "image parameters incompatibile with screen";
    700 					goto error;
    701 				}
    702 				reffn = 0;
    703 				switch(refresh){
    704 				case Refbackup:
    705 					break;
    706 				case Refnone:
    707 					reffn = memlnorefresh;
    708 					break;
    709 				case Refmesg:
    710 					reffn = drawrefresh;
    711 					break;
    712 				default:
    713 					err = "unknown refresh method";
    714 					goto error;
    715 				}
    716 				l = memlalloc(scrn, r, reffn, 0, value);
    717 				if(l == 0)
    718 					goto Edrawmem;
    719 				addflush(client, l->layer->screenr);
    720 				l->clipr = clipr;
    721 				rectclip(&l->clipr, r);
    722 				if(drawinstall(client, dstid, l, dscrn) == 0){
    723 					memldelete(l);
    724 					goto Edrawmem;
    725 				}
    726 				dscrn->ref++;
    727 				if(reffn){
    728 					refx = nil;
    729 					if(reffn == drawrefresh){
    730 						refx = mallocz(sizeof(Refx), 1);
    731 						if(refx == 0){
    732 							if(drawuninstall(client, dstid) < 0)
    733 								goto Enodrawimage;
    734 							goto Edrawmem;
    735 						}
    736 						refx->client = client;
    737 						refx->dimage = drawlookup(client, dstid, 1);
    738 					}
    739 					memlsetrefresh(l, reffn, refx);
    740 				}
    741 				continue;
    742 			}
    743 			i = allocmemimage(r, chan);
    744 			if(i == 0)
    745 				goto Edrawmem;
    746 			if(repl)
    747 				i->flags |= Frepl;
    748 			i->clipr = clipr;
    749 			if(!repl)
    750 				rectclip(&i->clipr, r);
    751 			if(drawinstall(client, dstid, i, 0) == 0){
    752 				freememimage(i);
    753 				goto Edrawmem;
    754 			}
    755 			memfillcolor(i, value);
    756 			continue;
    757 
    758 		/* allocate screen: 'A' id[4] imageid[4] fillid[4] public[1] */
    759 		case 'A':
    760 			m = 1+4+4+4+1;
    761 			if(n < m)
    762 				goto Eshortdraw;
    763 			dstid = BGLONG(a+1);
    764 			if(dstid == 0)
    765 				goto Ebadarg;
    766 			if(drawlookupdscreen(client, dstid))
    767 				goto Escreenexists;
    768 			ddst = drawlookup(client, BGLONG(a+5), 1);
    769 			dsrc = drawlookup(client, BGLONG(a+9), 1);
    770 			if(ddst==0 || dsrc==0)
    771 				goto Enodrawimage;
    772 			if(drawinstallscreen(client, 0, dstid, ddst, dsrc, a[13]) == 0)
    773 				goto Edrawmem;
    774 			continue;
    775 
    776 		/* set repl and clip: 'c' dstid[4] repl[1] clipR[4*4] */
    777 		case 'c':
    778 			m = 1+4+1+4*4;
    779 			if(n < m)
    780 				goto Eshortdraw;
    781 			ddst = drawlookup(client, BGLONG(a+1), 1);
    782 			if(ddst == nil)
    783 				goto Enodrawimage;
    784 			if(ddst->name){
    785 				err = "can't change repl/clipr of shared image";
    786 				goto error;
    787 			}
    788 			dst = ddst->image;
    789 			if(a[5])
    790 				dst->flags |= Frepl;
    791 			drawrectangle(&dst->clipr, a+6);
    792 			continue;
    793 
    794 		/* draw: 'd' dstid[4] srcid[4] maskid[4] R[4*4] P[2*4] P[2*4] */
    795 		case 'd':
    796 			m = 1+4+4+4+4*4+2*4+2*4;
    797 			if(n < m)
    798 				goto Eshortdraw;
    799 			dst = drawimage(client, a+1);
    800 			dstid = BGLONG(a+1);
    801 			src = drawimage(client, a+5);
    802 			mask = drawimage(client, a+9);
    803 			if(!dst || !src || !mask)
    804 				goto Enodrawimage;
    805 			drawrectangle(&r, a+13);
    806 			drawpoint(&p, a+29);
    807 			drawpoint(&q, a+37);
    808 			op = drawclientop(client);
    809 			memdraw(dst, r, src, p, mask, q, op);
    810 			dstflush(client, dstid, dst, r);
    811 			continue;
    812 
    813 		/* toggle debugging: 'D' val[1] */
    814 		case 'D':
    815 			m = 1+1;
    816 			if(n < m)
    817 				goto Eshortdraw;
    818 			drawdebug = a[1];
    819 			continue;
    820 
    821 		/* ellipse: 'e' dstid[4] srcid[4] center[2*4] a[4] b[4] thick[4] sp[2*4] alpha[4] phi[4]*/
    822 		case 'e':
    823 		case 'E':
    824 			m = 1+4+4+2*4+4+4+4+2*4+2*4;
    825 			if(n < m)
    826 				goto Eshortdraw;
    827 			dst = drawimage(client, a+1);
    828 			dstid = BGLONG(a+1);
    829 			src = drawimage(client, a+5);
    830 			if(!dst || !src)
    831 				goto Enodrawimage;
    832 			drawpoint(&p, a+9);
    833 			e0 = BGLONG(a+17);
    834 			e1 = BGLONG(a+21);
    835 			if(e0<0 || e1<0){
    836 				err = "invalid ellipse semidiameter";
    837 				goto error;
    838 			}
    839 			j = BGLONG(a+25);
    840 			if(j < 0){
    841 				err = "negative ellipse thickness";
    842 				goto error;
    843 			}
    844 
    845 			drawpoint(&sp, a+29);
    846 			c = j;
    847 			if(*a == 'E')
    848 				c = -1;
    849 			ox = BGLONG(a+37);
    850 			oy = BGLONG(a+41);
    851 			op = drawclientop(client);
    852 			/* high bit indicates arc angles are present */
    853 			if(ox & ((ulong)1<<31)){
    854 				if((ox & ((ulong)1<<30)) == 0)
    855 					ox &= ~((ulong)1<<31);
    856 				memarc(dst, p, e0, e1, c, src, sp, ox, oy, op);
    857 			}else
    858 				memellipse(dst, p, e0, e1, c, src, sp, op);
    859 			dstflush(client, dstid, dst, Rect(p.x-e0-j, p.y-e1-j, p.x+e0+j+1, p.y+e1+j+1));
    860 			continue;
    861 
    862 		/* free: 'f' id[4] */
    863 		case 'f':
    864 			m = 1+4;
    865 			if(n < m)
    866 				goto Eshortdraw;
    867 			ll = drawlookup(client, BGLONG(a+1), 0);
    868 			if(ll && ll->dscreen && ll->dscreen->owner != client)
    869 				ll->dscreen->owner->refreshme = 1;
    870 			if(drawuninstall(client, BGLONG(a+1)) < 0)
    871 				goto Enodrawimage;
    872 			continue;
    873 
    874 		/* free screen: 'F' id[4] */
    875 		case 'F':
    876 			m = 1+4;
    877 			if(n < m)
    878 				goto Eshortdraw;
    879 			if(!drawlookupscreen(client, BGLONG(a+1), &cs))
    880 				goto Enodrawscreen;
    881 			drawuninstallscreen(client, cs);
    882 			continue;
    883 
    884 		/* initialize font: 'i' fontid[4] nchars[4] ascent[1] */
    885 		case 'i':
    886 			m = 1+4+4+1;
    887 			if(n < m)
    888 				goto Eshortdraw;
    889 			dstid = BGLONG(a+1);
    890 			if(dstid == 0){
    891 				err = "can't use display as font";
    892 				goto error;
    893 			}
    894 			font = drawlookup(client, dstid, 1);
    895 			if(font == 0)
    896 				goto Enodrawimage;
    897 			if(font->image->layer){
    898 				err = "can't use window as font";
    899 				goto error;
    900 			}
    901 			ni = BGLONG(a+5);
    902 			if(ni<=0 || ni>4096){
    903 				err = "bad font size (4096 chars max)";
    904 				goto error;
    905 			}
    906 			free(font->fchar);	/* should we complain if non-zero? */
    907 			font->fchar = mallocz(ni*sizeof(FChar), 1);
    908 			if(font->fchar == 0){
    909 				err = "no memory for font";
    910 				goto error;
    911 			}
    912 			memset(font->fchar, 0, ni*sizeof(FChar));
    913 			font->nfchar = ni;
    914 			font->ascent = a[9];
    915 			continue;
    916 
    917 		/* set image 0 to screen image */
    918 		case 'J':
    919 			m = 1;
    920 			if(n < m)
    921 				goto Eshortdraw;
    922 			if(drawlookup(client, 0, 0))
    923 				goto Eimageexists;
    924 			drawinstall(client, 0, client->screenimage, 0);
    925 			client->infoid = 0;
    926 			continue;
    927 
    928 		/* get image info: 'I' */
    929 		case 'I':
    930 			m = 1;
    931 			if(n < m)
    932 				goto Eshortdraw;
    933 			if(client->infoid < 0)
    934 				goto Enodrawimage;
    935 			if(client->infoid == 0){
    936 				i = client->screenimage;
    937 				if(i == nil)
    938 					goto Enodrawimage;
    939 			}else{
    940 				di = drawlookup(client, client->infoid, 1);
    941 				if(di == nil)
    942 					goto Enodrawimage;
    943 				i = di->image;
    944 			}
    945 			ni = sprint(ibuf, "%11d %11d %11s %11d %11d %11d %11d %11d"
    946 					" %11d %11d %11d %11d ",
    947 					client->clientid,
    948 					client->infoid,
    949 					chantostr(cbuf, i->chan),
    950 					(i->flags&Frepl)==Frepl,
    951 					i->r.min.x, i->r.min.y, i->r.max.x, i->r.max.y,
    952 					i->clipr.min.x, i->clipr.min.y,
    953 					i->clipr.max.x, i->clipr.max.y);
    954 			free(client->readdata);
    955 			client->readdata = malloc(ni);
    956 			if(client->readdata == nil)
    957 				goto Enomem;
    958 			memmove(client->readdata, ibuf, ni);
    959 			client->nreaddata = ni;
    960 			client->infoid = -1;
    961 			continue;
    962 
    963 		/* query: 'Q' n[1] queryspec[n] */
    964 		case 'q':
    965 			if(n < 2)
    966 				goto Eshortdraw;
    967 			m = 1+1+a[1];
    968 			if(n < m)
    969 				goto Eshortdraw;
    970 			fmtstrinit(&fmt);
    971 			for(c=0; c<a[1]; c++) {
    972 				switch(a[2+c]) {
    973 				default:
    974 					err = "unknown query";
    975 					goto error;
    976 				case 'd':	/* dpi */
    977 					if(client->forcedpi)
    978 						fmtprint(&fmt, "%11d ", client->forcedpi);
    979 					else
    980 						fmtprint(&fmt, "%11d ", client->displaydpi);
    981 					break;
    982 				}
    983 			}
    984 			client->readdata = (uchar*)fmtstrflush(&fmt);
    985 			if(client->readdata == nil)
    986 				goto Enomem;
    987 			client->nreaddata = strlen((char*)client->readdata);
    988 			continue;
    989 
    990 		/* load character: 'l' fontid[4] srcid[4] index[2] R[4*4] P[2*4] left[1] width[1] */
    991 		case 'l':
    992 			m = 1+4+4+2+4*4+2*4+1+1;
    993 			if(n < m)
    994 				goto Eshortdraw;
    995 			font = drawlookup(client, BGLONG(a+1), 1);
    996 			if(font == 0)
    997 				goto Enodrawimage;
    998 			if(font->nfchar == 0)
    999 				goto Enotfont;
   1000 			src = drawimage(client, a+5);
   1001 			if(!src)
   1002 				goto Enodrawimage;
   1003 			ci = BGSHORT(a+9);
   1004 			if(ci >= font->nfchar)
   1005 				goto Eindex;
   1006 			drawrectangle(&r, a+11);
   1007 			drawpoint(&p, a+27);
   1008 			memdraw(font->image, r, src, p, memopaque, p, S);
   1009 			fc = &font->fchar[ci];
   1010 			fc->minx = r.min.x;
   1011 			fc->maxx = r.max.x;
   1012 			fc->miny = r.min.y;
   1013 			fc->maxy = r.max.y;
   1014 			fc->left = a[35];
   1015 			fc->width = a[36];
   1016 			continue;
   1017 
   1018 		/* draw line: 'L' dstid[4] p0[2*4] p1[2*4] end0[4] end1[4] radius[4] srcid[4] sp[2*4] */
   1019 		case 'L':
   1020 			m = 1+4+2*4+2*4+4+4+4+4+2*4;
   1021 			if(n < m)
   1022 				goto Eshortdraw;
   1023 			dst = drawimage(client, a+1);
   1024 			dstid = BGLONG(a+1);
   1025 			drawpoint(&p, a+5);
   1026 			drawpoint(&q, a+13);
   1027 			e0 = BGLONG(a+21);
   1028 			e1 = BGLONG(a+25);
   1029 			j = BGLONG(a+29);
   1030 			if(j < 0){
   1031 				err = "negative line width";
   1032 				goto error;
   1033 			}
   1034 			src = drawimage(client, a+33);
   1035 			if(!dst || !src)
   1036 				goto Enodrawimage;
   1037 			drawpoint(&sp, a+37);
   1038 			op = drawclientop(client);
   1039 			memline(dst, p, q, e0, e1, j, src, sp, op);
   1040 			/* avoid memlinebbox if possible */
   1041 			if(dstid==0 || dst->layer!=nil){
   1042 				/* BUG: this is terribly inefficient: update maximal containing rect*/
   1043 				r = memlinebbox(p, q, e0, e1, j);
   1044 				dstflush(client, dstid, dst, insetrect(r, -(1+1+j)));
   1045 			}
   1046 			continue;
   1047 
   1048 		/* create image mask: 'm' newid[4] id[4] */
   1049 /*
   1050  *
   1051 		case 'm':
   1052 			m = 4+4;
   1053 			if(n < m)
   1054 				goto Eshortdraw;
   1055 			break;
   1056  *
   1057  */
   1058 
   1059 		/* attach to a named image: 'n' dstid[4] j[1] name[j] */
   1060 		case 'n':
   1061 			m = 1+4+1;
   1062 			if(n < m)
   1063 				goto Eshortdraw;
   1064 			j = a[5];
   1065 			if(j == 0)	/* give me a non-empty name please */
   1066 				goto Eshortdraw;
   1067 			m += j;
   1068 			if(n < m)
   1069 				goto Eshortdraw;
   1070 			dstid = BGLONG(a+1);
   1071 			if(drawlookup(client, dstid, 0))
   1072 				goto Eimageexists;
   1073 			dn = drawlookupname(client, j, (char*)a+6);
   1074 			if(dn == nil)
   1075 				goto Enoname;
   1076 			s = malloc(j+1);
   1077 			if(s == nil)
   1078 				goto Enomem;
   1079 			if(drawinstall(client, dstid, dn->dimage->image, 0) == 0)
   1080 				goto Edrawmem;
   1081 			di = drawlookup(client, dstid, 0);
   1082 			if(di == 0)
   1083 				goto Eoldname;
   1084 			di->vers = dn->vers;
   1085 			di->name = s;
   1086 			di->fromname = dn->dimage;
   1087 			di->fromname->ref++;
   1088 			memmove(di->name, a+6, j);
   1089 			di->name[j] = 0;
   1090 			client->infoid = dstid;
   1091 			continue;
   1092 
   1093 		/* name an image: 'N' dstid[4] in[1] j[1] name[j] */
   1094 		case 'N':
   1095 			m = 1+4+1+1;
   1096 			if(n < m)
   1097 				goto Eshortdraw;
   1098 			c = a[5];
   1099 			j = a[6];
   1100 			if(j == 0)	/* give me a non-empty name please */
   1101 				goto Eshortdraw;
   1102 			m += j;
   1103 			if(n < m)
   1104 				goto Eshortdraw;
   1105 			di = drawlookup(client, BGLONG(a+1), 0);
   1106 			if(di == 0)
   1107 				goto Enodrawimage;
   1108 			if(di->name)
   1109 				goto Enamed;
   1110 			if(c)
   1111 				if(drawaddname(client, di, j, (char*)a+7, &err) < 0)
   1112 					goto error;
   1113 			else{
   1114 				dn = drawlookupname(client, j, (char*)a+7);
   1115 				if(dn == nil)
   1116 					goto Enoname;
   1117 				if(dn->dimage != di)
   1118 					goto Ewrongname;
   1119 				drawdelname(client, dn);
   1120 			}
   1121 			continue;
   1122 
   1123 		/* position window: 'o' id[4] r.min [2*4] screenr.min [2*4] */
   1124 		case 'o':
   1125 			m = 1+4+2*4+2*4;
   1126 			if(n < m)
   1127 				goto Eshortdraw;
   1128 			dst = drawimage(client, a+1);
   1129 			if(!dst)
   1130 				goto Enodrawimage;
   1131 			if(dst->layer){
   1132 				drawpoint(&p, a+5);
   1133 				drawpoint(&q, a+13);
   1134 				r = dst->layer->screenr;
   1135 				ni = memlorigin(dst, p, q);
   1136 				if(ni < 0){
   1137 					err = "image origin failed";
   1138 					goto error;
   1139 				}
   1140 				if(ni > 0){
   1141 					addflush(client, r);
   1142 					addflush(client, dst->layer->screenr);
   1143 					ll = drawlookup(client, BGLONG(a+1), 1);
   1144 					drawrefreshscreen(ll, client);
   1145 				}
   1146 			}
   1147 			continue;
   1148 
   1149 		/* set compositing operator for next draw operation: 'O' op */
   1150 		case 'O':
   1151 			m = 1+1;
   1152 			if(n < m)
   1153 				goto Eshortdraw;
   1154 			client->op = a[1];
   1155 			continue;
   1156 
   1157 		/* filled polygon: 'P' dstid[4] n[2] wind[4] ignore[2*4] srcid[4] sp[2*4] p0[2*4] dp[2*2*n] */
   1158 		/* polygon: 'p' dstid[4] n[2] end0[4] end1[4] radius[4] srcid[4] sp[2*4] p0[2*4] dp[2*2*n] */
   1159 		case 'p':
   1160 		case 'P':
   1161 			m = 1+4+2+4+4+4+4+2*4;
   1162 			if(n < m)
   1163 				goto Eshortdraw;
   1164 			dstid = BGLONG(a+1);
   1165 			dst = drawimage(client, a+1);
   1166 			ni = BGSHORT(a+5);
   1167 			if(ni < 0){
   1168 				err = "negative cout in polygon";
   1169 				goto error;
   1170 			}
   1171 			e0 = BGLONG(a+7);
   1172 			e1 = BGLONG(a+11);
   1173 			j = 0;
   1174 			if(*a == 'p'){
   1175 				j = BGLONG(a+15);
   1176 				if(j < 0){
   1177 					err = "negative polygon line width";
   1178 					goto error;
   1179 				}
   1180 			}
   1181 			src = drawimage(client, a+19);
   1182 			if(!dst || !src)
   1183 				goto Enodrawimage;
   1184 			drawpoint(&sp, a+23);
   1185 			drawpoint(&p, a+31);
   1186 			ni++;
   1187 			pp = mallocz(ni*sizeof(Point), 1);
   1188 			if(pp == nil)
   1189 				goto Enomem;
   1190 			doflush = 0;
   1191 			if(dstid==0 || (dst->layer && dst->layer->screen->image->data == client->screenimage->data))
   1192 				doflush = 1;	/* simplify test in loop */
   1193 			ox = oy = 0;
   1194 			esize = 0;
   1195 			u = a+m;
   1196 			for(y=0; y<ni; y++){
   1197 				q = p;
   1198 				oesize = esize;
   1199 				u = drawcoord(u, a+n, ox, &p.x);
   1200 				if(!u)
   1201 					goto Eshortdraw;
   1202 				u = drawcoord(u, a+n, oy, &p.y);
   1203 				if(!u)
   1204 					goto Eshortdraw;
   1205 				ox = p.x;
   1206 				oy = p.y;
   1207 				if(doflush){
   1208 					esize = j;
   1209 					if(*a == 'p'){
   1210 						if(y == 0){
   1211 							c = memlineendsize(e0);
   1212 							if(c > esize)
   1213 								esize = c;
   1214 						}
   1215 						if(y == ni-1){
   1216 							c = memlineendsize(e1);
   1217 							if(c > esize)
   1218 								esize = c;
   1219 						}
   1220 					}
   1221 					if(*a=='P' && e0!=1 && e0 !=~0)
   1222 						r = dst->clipr;
   1223 					else if(y > 0){
   1224 						r = Rect(q.x-oesize, q.y-oesize, q.x+oesize+1, q.y+oesize+1);
   1225 						combinerect(&r, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1));
   1226 					}
   1227 					if(rectclip(&r, dst->clipr))		/* should perhaps be an arg to dstflush */
   1228 						dstflush(client, dstid, dst, r);
   1229 				}
   1230 				pp[y] = p;
   1231 			}
   1232 			if(y == 1)
   1233 				dstflush(client, dstid, dst, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1));
   1234 			op = drawclientop(client);
   1235 			if(*a == 'p')
   1236 				mempoly(dst, pp, ni, e0, e1, j, src, sp, op);
   1237 			else
   1238 				memfillpoly(dst, pp, ni, e0, src, sp, op);
   1239 			free(pp);
   1240 			m = u-a;
   1241 			continue;
   1242 
   1243 		/* read: 'r' id[4] R[4*4] */
   1244 		case 'r':
   1245 			m = 1+4+4*4;
   1246 			if(n < m)
   1247 				goto Eshortdraw;
   1248 			i = drawimage(client, a+1);
   1249 			if(!i)
   1250 				goto Enodrawimage;
   1251 			drawrectangle(&r, a+5);
   1252 			if(!rectinrect(r, i->r))
   1253 				goto Ereadoutside;
   1254 			c = bytesperline(r, i->depth);
   1255 			c *= Dy(r);
   1256 			free(client->readdata);
   1257 			client->readdata = mallocz(c, 0);
   1258 			if(client->readdata == nil){
   1259 				err = "readimage malloc failed";
   1260 				goto error;
   1261 			}
   1262 			client->nreaddata = memunload(i, r, client->readdata, c);
   1263 			if(client->nreaddata < 0){
   1264 				free(client->readdata);
   1265 				client->readdata = nil;
   1266 				err = "bad readimage call";
   1267 				goto error;
   1268 			}
   1269 			continue;
   1270 
   1271 		/* string: 's' dstid[4] srcid[4] fontid[4] P[2*4] clipr[4*4] sp[2*4] ni[2] ni*(index[2]) */
   1272 		/* stringbg: 'x' dstid[4] srcid[4] fontid[4] P[2*4] clipr[4*4] sp[2*4] ni[2] bgid[4] bgpt[2*4] ni*(index[2]) */
   1273 		case 's':
   1274 		case 'x':
   1275 			m = 1+4+4+4+2*4+4*4+2*4+2;
   1276 			if(*a == 'x')
   1277 				m += 4+2*4;
   1278 			if(n < m)
   1279 				goto Eshortdraw;
   1280 
   1281 			dst = drawimage(client, a+1);
   1282 			dstid = BGLONG(a+1);
   1283 			src = drawimage(client, a+5);
   1284 			if(!dst || !src)
   1285 				goto Enodrawimage;
   1286 			font = drawlookup(client, BGLONG(a+9), 1);
   1287 			if(font == 0)
   1288 				goto Enodrawimage;
   1289 			if(font->nfchar == 0)
   1290 				goto Enotfont;
   1291 			drawpoint(&p, a+13);
   1292 			drawrectangle(&r, a+21);
   1293 			drawpoint(&sp, a+37);
   1294 			ni = BGSHORT(a+45);
   1295 			u = a+m;
   1296 			m += ni*2;
   1297 			if(n < m)
   1298 				goto Eshortdraw;
   1299 			clipr = dst->clipr;
   1300 			dst->clipr = r;
   1301 			op = drawclientop(client);
   1302 			if(*a == 'x'){
   1303 				/* paint background */
   1304 				l = drawimage(client, a+47);
   1305 				if(!l)
   1306 					goto Enodrawimage;
   1307 				drawpoint(&q, a+51);
   1308 				r.min.x = p.x;
   1309 				r.min.y = p.y-font->ascent;
   1310 				r.max.x = p.x;
   1311 				r.max.y = r.min.y+Dy(font->image->r);
   1312 				j = ni;
   1313 				while(--j >= 0){
   1314 					ci = BGSHORT(u);
   1315 					if(ci<0 || ci>=font->nfchar){
   1316 						dst->clipr = clipr;
   1317 						goto Eindex;
   1318 					}
   1319 					r.max.x += font->fchar[ci].width;
   1320 					u += 2;
   1321 				}
   1322 				memdraw(dst, r, l, q, memopaque, ZP, op);
   1323 				u -= 2*ni;
   1324 			}
   1325 			q = p;
   1326 			while(--ni >= 0){
   1327 				ci = BGSHORT(u);
   1328 				if(ci<0 || ci>=font->nfchar){
   1329 					dst->clipr = clipr;
   1330 					goto Eindex;
   1331 				}
   1332 				q = drawchar(dst, q, src, &sp, font, ci, op);
   1333 				u += 2;
   1334 			}
   1335 			dst->clipr = clipr;
   1336 			p.y -= font->ascent;
   1337 			dstflush(client, dstid, dst, Rect(p.x, p.y, q.x, p.y+Dy(font->image->r)));
   1338 			continue;
   1339 
   1340 		/* use public screen: 'S' id[4] chan[4] */
   1341 		case 'S':
   1342 			m = 1+4+4;
   1343 			if(n < m)
   1344 				goto Eshortdraw;
   1345 			dstid = BGLONG(a+1);
   1346 			if(dstid == 0)
   1347 				goto Ebadarg;
   1348 			dscrn = drawlookupdscreen(client, dstid);
   1349 			if(dscrn==0 || (dscrn->public==0 && dscrn->owner!=client))
   1350 				goto Enodrawscreen;
   1351 			if(dscrn->screen->image->chan != BGLONG(a+5)){
   1352 				err = "inconsistent chan";
   1353 				goto error;
   1354 			}
   1355 			if(drawinstallscreen(client, dscrn, 0, 0, 0, 0) == 0)
   1356 				goto Edrawmem;
   1357 			continue;
   1358 
   1359 		/* top or bottom windows: 't' top[1] nw[2] n*id[4] */
   1360 		case 't':
   1361 			m = 1+1+2;
   1362 			if(n < m)
   1363 				goto Eshortdraw;
   1364 			nw = BGSHORT(a+2);
   1365 			if(nw < 0)
   1366 				goto Ebadarg;
   1367 			if(nw == 0)
   1368 				continue;
   1369 			m += nw*4;
   1370 			if(n < m)
   1371 				goto Eshortdraw;
   1372 			lp = mallocz(nw*sizeof(Memimage*), 1);
   1373 			if(lp == 0)
   1374 				goto Enomem;
   1375 			for(j=0; j<nw; j++){
   1376 				lp[j] = drawimage(client, a+1+1+2+j*4);
   1377 				if(lp[j] == nil){
   1378 					free(lp);
   1379 					goto Enodrawimage;
   1380 				}
   1381 			}
   1382 			if(lp[0]->layer == 0){
   1383 				err = "images are not windows";
   1384 				free(lp);
   1385 				goto error;
   1386 			}
   1387 			for(j=1; j<nw; j++)
   1388 				if(lp[j]->layer->screen != lp[0]->layer->screen){
   1389 					err = "images not on same screen";
   1390 					free(lp);
   1391 					goto error;
   1392 				}
   1393 			if(a[1])
   1394 				memltofrontn(lp, nw);
   1395 			else
   1396 				memltorearn(lp, nw);
   1397 			if(lp[0]->layer->screen->image->data == client->screenimage->data)
   1398 				for(j=0; j<nw; j++)
   1399 					addflush(client, lp[j]->layer->screenr);
   1400 			free(lp);
   1401 			ll = drawlookup(client, BGLONG(a+1+1+2), 1);
   1402 			drawrefreshscreen(ll, client);
   1403 			continue;
   1404 
   1405 		/* visible: 'v' */
   1406 		case 'v':
   1407 			m = 1;
   1408 			drawflush(client);
   1409 			continue;
   1410 
   1411 		/* write: 'y' id[4] R[4*4] data[x*1] */
   1412 		/* write from compressed data: 'Y' id[4] R[4*4] data[x*1] */
   1413 		case 'y':
   1414 		case 'Y':
   1415 			m = 1+4+4*4;
   1416 			if(n < m)
   1417 				goto Eshortdraw;
   1418 			dstid = BGLONG(a+1);
   1419 			dst = drawimage(client, a+1);
   1420 			if(!dst)
   1421 				goto Enodrawimage;
   1422 			drawrectangle(&r, a+5);
   1423 			if(!rectinrect(r, dst->r))
   1424 				goto Ewriteoutside;
   1425 			y = memload(dst, r, a+m, n-m, *a=='Y');
   1426 			if(y < 0){
   1427 				err = "bad writeimage call";
   1428 				goto error;
   1429 			}
   1430 			dstflush(client, dstid, dst, r);
   1431 			m += y;
   1432 			continue;
   1433 		}
   1434 	}
   1435 	rpc_gfxdrawunlock();
   1436 	qunlock(&drawlk);
   1437 	return oldn - n;
   1438 
   1439 Enodrawimage:
   1440 	err = "unknown id for draw image";
   1441 	goto error;
   1442 Enodrawscreen:
   1443 	err = "unknown id for draw screen";
   1444 	goto error;
   1445 Eshortdraw:
   1446 	err = "short draw message";
   1447 	goto error;
   1448 /*
   1449 Eshortread:
   1450 	err = "draw read too short";
   1451 	goto error;
   1452 */
   1453 Eimageexists:
   1454 	err = "image id in use";
   1455 	goto error;
   1456 Escreenexists:
   1457 	err = "screen id in use";
   1458 	goto error;
   1459 Edrawmem:
   1460 	err = "image memory allocation failed";
   1461 	goto error;
   1462 Ereadoutside:
   1463 	err = "readimage outside image";
   1464 	goto error;
   1465 Ewriteoutside:
   1466 	err = "writeimage outside image";
   1467 	goto error;
   1468 Enotfont:
   1469 	err = "image not a font";
   1470 	goto error;
   1471 Eindex:
   1472 	err = "character index out of range";
   1473 	goto error;
   1474 /*
   1475 Enoclient:
   1476 	err = "no such draw client";
   1477 	goto error;
   1478 Edepth:
   1479 	err = "image has bad depth";
   1480 	goto error;
   1481 Enameused:
   1482 	err = "image name in use";
   1483 	goto error;
   1484 */
   1485 Enoname:
   1486 	err = "no image with that name";
   1487 	goto error;
   1488 Eoldname:
   1489 	err = "named image no longer valid";
   1490 	goto error;
   1491 Enamed:
   1492 	err = "image already has name";
   1493 	goto error;
   1494 Ewrongname:
   1495 	err = "wrong name for image";
   1496 	goto error;
   1497 Enomem:
   1498 	err = "out of memory";
   1499 	goto error;
   1500 Ebadarg:
   1501 	err = "bad argument in draw message";
   1502 	goto error;
   1503 
   1504 error:
   1505 	werrstr("%s", err);
   1506 	rpc_gfxdrawunlock();
   1507 	qunlock(&drawlk);
   1508 	return -1;
   1509 }