PageRenderTime 6ms CodeModel.GetById 2ms app.highlight 126ms RepoModel.GetById 1ms app.codeStats 1ms

/sys/src/cmd/vnc/devdraw.c

https://bitbucket.org/rminnich/sysfromiso
C | 2086 lines | 1835 code | 151 blank | 100 comment | 410 complexity | 513b7d8ea2f085442e39527556df3ff4 MD5 | raw file
   1#include	<u.h>
   2#include	<libc.h>
   3#include	"compat.h"
   4#include	"error.h"
   5
   6#define	Image	IMAGE
   7#include	<draw.h>
   8#include	<memdraw.h>
   9#include	<memlayer.h>
  10#include	<cursor.h>
  11#include	"screen.h"
  12
  13enum
  14{
  15	Qtopdir		= 0,
  16	Qnew,
  17	Q3rd,
  18	Q2nd,
  19	Qcolormap,
  20	Qctl,
  21	Qdata,
  22	Qrefresh,
  23};
  24
  25/*
  26 * Qid path is:
  27 *	 4 bits of file type (qids above)
  28 *	24 bits of mux slot number +1; 0 means not attached to client
  29 */
  30#define	QSHIFT	4	/* location in qid of client # */
  31
  32#define	QID(q)		((((ulong)(q).path)&0x0000000F)>>0)
  33#define	CLIENTPATH(q)	((((ulong)q)&0x7FFFFFF0)>>QSHIFT)
  34#define	CLIENT(q)	CLIENTPATH((q).path)
  35
  36#define	NHASH		(1<<5)
  37#define	HASHMASK	(NHASH-1)
  38
  39typedef struct Client Client;
  40typedef struct Draw Draw;
  41typedef struct DImage DImage;
  42typedef struct DScreen DScreen;
  43typedef struct CScreen CScreen;
  44typedef struct FChar FChar;
  45typedef struct Refresh Refresh;
  46typedef struct Refx Refx;
  47typedef struct DName DName;
  48
  49ulong blanktime = 30;	/* in minutes; a half hour */
  50
  51struct Draw
  52{
  53	QLock;
  54	int		clientid;
  55	int		nclient;
  56	Client**	client;
  57	int		nname;
  58	DName*	name;
  59	int		vers;
  60	int		softscreen;
  61	int		blanked;	/* screen turned off */
  62	ulong		blanktime;	/* time of last operation */
  63	ulong		savemap[3*256];
  64};
  65
  66struct Client
  67{
  68	Ref		r;
  69	DImage*		dimage[NHASH];
  70	CScreen*	cscreen;
  71	Refresh*	refresh;
  72	Rendez		refrend;
  73	uchar*		readdata;
  74	int		nreaddata;
  75	int		busy;
  76	int		clientid;
  77	int		slot;
  78	int		refreshme;
  79	int		infoid;
  80	int		op;
  81};
  82
  83struct Refresh
  84{
  85	DImage*		dimage;
  86	Rectangle	r;
  87	Refresh*	next;
  88};
  89
  90struct Refx
  91{
  92	Client*		client;
  93	DImage*		dimage;
  94};
  95
  96struct DName
  97{
  98	char			*name;
  99	Client	*client;
 100	DImage*		dimage;
 101	int			vers;
 102};
 103
 104struct FChar
 105{
 106	int		minx;	/* left edge of bits */
 107	int		maxx;	/* right edge of bits */
 108	uchar		miny;	/* first non-zero scan-line */
 109	uchar		maxy;	/* last non-zero scan-line + 1 */
 110	schar		left;	/* offset of baseline */
 111	uchar		width;	/* width of baseline */
 112};
 113
 114/*
 115 * Reference counts in DImages:
 116 *	one per open by original client
 117 *	one per screen image or fill
 118 * 	one per image derived from this one by name
 119 */
 120struct DImage
 121{
 122	int		id;
 123	int		ref;
 124	char		*name;
 125	int		vers;
 126	Memimage*	image;
 127	int		ascent;
 128	int		nfchar;
 129	FChar*		fchar;
 130	DScreen*	dscreen;	/* 0 if not a window */
 131	DImage*	fromname;	/* image this one is derived from, by name */
 132	DImage*		next;
 133};
 134
 135struct CScreen
 136{
 137	DScreen*	dscreen;
 138	CScreen*	next;
 139};
 140
 141struct DScreen
 142{
 143	int		id;
 144	int		public;
 145	int		ref;
 146	DImage	*dimage;
 147	DImage	*dfill;
 148	Memscreen*	screen;
 149	Client*		owner;
 150	DScreen*	next;
 151};
 152
 153static	Draw		sdraw;
 154static	Memimage	*screenimage;
 155static	Memdata	screendata;
 156static	Rectangle	flushrect;
 157static	int		waste;
 158static	DScreen*	dscreen;
 159extern	void		flushmemscreen(Rectangle);
 160	void		drawmesg(Client*, void*, int);
 161	void		drawuninstall(Client*, int);
 162	void		drawfreedimage(DImage*);
 163	Client*		drawclientofpath(ulong);
 164
 165static	char Enodrawimage[] =	"unknown id for draw image";
 166static	char Enodrawscreen[] =	"unknown id for draw screen";
 167static	char Eshortdraw[] =	"short draw message";
 168static	char Eshortread[] =	"draw read too short";
 169static	char Eimageexists[] =	"image id in use";
 170static	char Escreenexists[] =	"screen id in use";
 171static	char Edrawmem[] =	"image memory allocation failed";
 172static	char Ereadoutside[] =	"readimage outside image";
 173static	char Ewriteoutside[] =	"writeimage outside image";
 174static	char Enotfont[] =	"image not a font";
 175static	char Eindex[] =		"character index out of range";
 176static	char Enoclient[] =	"no such draw client";
 177static	char Edepth[] =	"image has bad depth";
 178static	char Enameused[] =	"image name in use";
 179static	char Enoname[] =	"no image with that name";
 180static	char Eoldname[] =	"named image no longer valid";
 181static	char Enamed[] = 	"image already has name";
 182static	char Ewrongname[] = 	"wrong name for image";
 183
 184void
 185drawlock(void)
 186{
 187	qlock(&sdraw);
 188}
 189
 190void
 191drawunlock(void)
 192{
 193	qunlock(&sdraw);
 194}
 195
 196int
 197candrawlock(void)
 198{
 199	return canqlock(&sdraw);
 200}
 201
 202static int
 203drawgen(Chan *c, Dirtab*, int, int s, Dir *dp)
 204{
 205	int t;
 206	Qid q;
 207	ulong path;
 208	Client *cl;
 209
 210	q.vers = 0;
 211
 212	if(s == DEVDOTDOT){
 213		switch(QID(c->qid)){
 214		case Qtopdir:
 215		case Q2nd:
 216			mkqid(&q, Qtopdir, 0, QTDIR);
 217			devdir(c, q, "#i", 0, eve, 0500, dp);
 218			break;
 219		case Q3rd:
 220			cl = drawclientofpath(c->qid.path);
 221			if(cl == nil)
 222				strcpy(up->genbuf, "??");
 223			else
 224				sprint(up->genbuf, "%d", cl->clientid);
 225			mkqid(&q, Q2nd, 0, QTDIR);
 226			devdir(c, q, up->genbuf, 0, eve, 0500, dp);
 227			break;
 228		default:
 229			panic("drawwalk %#llux", c->qid.path);
 230		}
 231		return 1;
 232	}
 233
 234	/*
 235	 * Top level directory contains the name of the device.
 236	 */
 237	t = QID(c->qid);
 238	if(t == Qtopdir){
 239		switch(s){
 240		case 0:
 241			mkqid(&q, Q2nd, 0, QTDIR);
 242			devdir(c, q, "draw", 0, eve, 0555, dp);
 243			break;
 244		default:
 245			return -1;
 246		}
 247		return 1;
 248	}
 249
 250	/*
 251	 * Second level contains "new" plus all the clients.
 252	 */
 253	if(t == Q2nd || t == Qnew){
 254		if(s == 0){
 255			mkqid(&q, Qnew, 0, QTFILE);
 256			devdir(c, q, "new", 0, eve, 0666, dp);
 257		}
 258		else if(s <= sdraw.nclient){
 259			cl = sdraw.client[s-1];
 260			if(cl == 0)
 261				return 0;
 262			sprint(up->genbuf, "%d", cl->clientid);
 263			mkqid(&q, (s<<QSHIFT)|Q3rd, 0, QTDIR);
 264			devdir(c, q, up->genbuf, 0, eve, 0555, dp);
 265			return 1;
 266		}
 267		else
 268			return -1;
 269		return 1;
 270	}
 271
 272	/*
 273	 * Third level.
 274	 */
 275	path = c->qid.path&~(((1<<QSHIFT)-1));	/* slot component */
 276	q.vers = c->qid.vers;
 277	q.type = QTFILE;
 278	switch(s){
 279	case 0:
 280		q.path = path|Qcolormap;
 281		devdir(c, q, "colormap", 0, eve, 0600, dp);
 282		break;
 283	case 1:
 284		q.path = path|Qctl;
 285		devdir(c, q, "ctl", 0, eve, 0600, dp);
 286		break;
 287	case 2:
 288		q.path = path|Qdata;
 289		devdir(c, q, "data", 0, eve, 0600, dp);
 290		break;
 291	case 3:
 292		q.path = path|Qrefresh;
 293		devdir(c, q, "refresh", 0, eve, 0400, dp);
 294		break;
 295	default:
 296		return -1;
 297	}
 298	return 1;
 299}
 300
 301static
 302int
 303drawrefactive(void *a)
 304{
 305	Client *c;
 306
 307	c = a;
 308	return c->refreshme || c->refresh!=0;
 309}
 310
 311static
 312void
 313drawrefreshscreen(DImage *l, Client *client)
 314{
 315	while(l != nil && l->dscreen == nil)
 316		l = l->fromname;
 317	if(l != nil && l->dscreen->owner != client)
 318		l->dscreen->owner->refreshme = 1;
 319}
 320
 321static
 322void
 323drawrefresh(Memimage *l, Rectangle r, void *v)
 324{
 325	Refx *x;
 326	DImage *d;
 327	Client *c;
 328	Refresh *ref;
 329
 330	USED(l);
 331	if(v == 0)
 332		return;
 333	x = v;
 334	c = x->client;
 335	d = x->dimage;
 336	for(ref=c->refresh; ref; ref=ref->next)
 337		if(ref->dimage == d){
 338			combinerect(&ref->r, r);
 339			return;
 340		}
 341	ref = malloc(sizeof(Refresh));
 342	if(ref){
 343		ref->dimage = d;
 344		ref->r = r;
 345		ref->next = c->refresh;
 346		c->refresh = ref;
 347	}
 348}
 349
 350static void
 351addflush(Rectangle r)
 352{
 353	int abb, ar, anbb;
 354	Rectangle nbb;
 355
 356	if(sdraw.softscreen==0 || !rectclip(&r, screenimage->r))
 357		return;
 358
 359	if(flushrect.min.x >= flushrect.max.x){
 360		flushrect = r;
 361		waste = 0;
 362		return;
 363	}
 364	/* VNC uses a region to compute the minimum bounding area.
 365	 * The waste is far less than that of a bounding box. see region.c
 366	 */
 367	if(1){
 368		flushmemscreen(flushrect);
 369		flushrect = r;
 370		return;
 371	}
 372	nbb = flushrect;
 373	combinerect(&nbb, r);
 374	ar = Dx(r)*Dy(r);
 375	abb = Dx(flushrect)*Dy(flushrect);
 376	anbb = Dx(nbb)*Dy(nbb);
 377	/*
 378	 * Area of new waste is area of new bb minus area of old bb,
 379	 * less the area of the new segment, which we assume is not waste.
 380	 * This could be negative, but that's OK.
 381	 */
 382	waste += anbb-abb - ar;
 383	if(waste < 0)
 384		waste = 0;
 385	/*
 386	 * absorb if:
 387	 *	total area is small
 388	 *	waste is less than half total area
 389	 * 	rectangles touch
 390	 */
 391	if(anbb<=1024 || waste*2<anbb || rectXrect(flushrect, r)){
 392		flushrect = nbb;
 393		return;
 394	}
 395	/* emit current state */
 396	flushmemscreen(flushrect);
 397	flushrect = r;
 398	waste = 0;
 399}
 400
 401static
 402void
 403dstflush(int dstid, Memimage *dst, Rectangle r)
 404{
 405	Memlayer *l;
 406
 407	if(dstid == 0){
 408		//combinerect(&flushrect, r);
 409		addflush(r); // for VNC, see comments in addflush
 410		return;
 411	}
 412	l = dst->layer;
 413	if(l == nil)
 414		return;
 415	do{
 416		if(l->screen->image->data != screenimage->data)
 417			return;
 418		r = rectaddpt(r, l->delta);
 419		l = l->screen->image->layer;
 420	}while(l);
 421	addflush(r);
 422}
 423
 424static
 425void
 426drawflush(void)
 427{
 428	flushmemscreen(flushrect);
 429	flushrect = Rect(10000, 10000, -10000, -10000);
 430}
 431
 432static
 433int
 434drawcmp(char *a, char *b, int n)
 435{
 436	if(strlen(a) != n)
 437		return 1;
 438	return memcmp(a, b, n);
 439}
 440
 441DName*
 442drawlookupname(int n, char *str)
 443{
 444	DName *name, *ename;
 445
 446	name = sdraw.name;
 447	ename = &name[sdraw.nname];
 448	for(; name<ename; name++)
 449		if(drawcmp(name->name, str, n) == 0)
 450			return name;
 451	return 0;
 452}
 453
 454int
 455drawgoodname(DImage *d)
 456{
 457	DName *n;
 458
 459	/* if window, validate the screen's own images */
 460	if(d->dscreen)
 461		if(drawgoodname(d->dscreen->dimage) == 0
 462		|| drawgoodname(d->dscreen->dfill) == 0)
 463			return 0;
 464	if(d->name == nil)
 465		return 1;
 466	n = drawlookupname(strlen(d->name), d->name);
 467	if(n==nil || n->vers!=d->vers)
 468		return 0;
 469	return 1;
 470}
 471
 472DImage*
 473drawlookup(Client *client, int id, int checkname)
 474{
 475	DImage *d;
 476
 477	d = client->dimage[id&HASHMASK];
 478	while(d){
 479		if(d->id == id){
 480			if(checkname && !drawgoodname(d))
 481				error(Eoldname);
 482			return d;
 483		}
 484		d = d->next;
 485	}
 486	return 0;
 487}
 488
 489DScreen*
 490drawlookupdscreen(int id)
 491{
 492	DScreen *s;
 493
 494	s = dscreen;
 495	while(s){
 496		if(s->id == id)
 497			return s;
 498		s = s->next;
 499	}
 500	return 0;
 501}
 502
 503DScreen*
 504drawlookupscreen(Client *client, int id, CScreen **cs)
 505{
 506	CScreen *s;
 507
 508	s = client->cscreen;
 509	while(s){
 510		if(s->dscreen->id == id){
 511			*cs = s;
 512			return s->dscreen;
 513		}
 514		s = s->next;
 515	}
 516	error(Enodrawscreen);
 517	return 0;
 518}
 519
 520Memimage*
 521drawinstall(Client *client, int id, Memimage *i, DScreen *dscreen)
 522{
 523	DImage *d;
 524
 525	d = malloc(sizeof(DImage));
 526	if(d == 0)
 527		return 0;
 528	d->id = id;
 529	d->ref = 1;
 530	d->name = 0;
 531	d->vers = 0;
 532	d->image = i;
 533	d->nfchar = 0;
 534	d->fchar = 0;
 535	d->fromname = 0;
 536	d->dscreen = dscreen;
 537	d->next = client->dimage[id&HASHMASK];
 538	client->dimage[id&HASHMASK] = d;
 539	return i;
 540}
 541
 542Memscreen*
 543drawinstallscreen(Client *client, DScreen *d, int id, DImage *dimage, DImage *dfill, int public)
 544{
 545	Memscreen *s;
 546	CScreen *c;
 547
 548	c = malloc(sizeof(CScreen));
 549	if(dimage && dimage->image && dimage->image->chan == 0)
 550		panic("bad image %p in drawinstallscreen", dimage->image);
 551
 552	if(c == 0)
 553		return 0;
 554	if(d == 0){
 555		d = malloc(sizeof(DScreen));
 556		if(d == 0){
 557			free(c);
 558			return 0;
 559		}
 560		s = malloc(sizeof(Memscreen));
 561		if(s == 0){
 562			free(c);
 563			free(d);
 564			return 0;
 565		}
 566		s->frontmost = 0;
 567		s->rearmost = 0;
 568		d->dimage = dimage;
 569		if(dimage){
 570			s->image = dimage->image;
 571			dimage->ref++;
 572		}
 573		d->dfill = dfill;
 574		if(dfill){
 575			s->fill = dfill->image;
 576			dfill->ref++;
 577		}
 578		d->ref = 0;
 579		d->id = id;
 580		d->screen = s;
 581		d->public = public;
 582		d->next = dscreen;
 583		d->owner = client;
 584		dscreen = d;
 585	}
 586	c->dscreen = d;
 587	d->ref++;
 588	c->next = client->cscreen;
 589	client->cscreen = c;
 590	return d->screen;
 591}
 592
 593void
 594drawdelname(DName *name)
 595{
 596	int i;
 597
 598	i = name-sdraw.name;
 599	memmove(name, name+1, (sdraw.nname-(i+1))*sizeof(DName));
 600	sdraw.nname--;
 601}
 602
 603void
 604drawfreedscreen(DScreen *this)
 605{
 606	DScreen *ds, *next;
 607
 608	this->ref--;
 609	if(this->ref < 0)
 610		print("negative ref in drawfreedscreen\n");
 611	if(this->ref > 0)
 612		return;
 613	ds = dscreen;
 614	if(ds == this){
 615		dscreen = this->next;
 616		goto Found;
 617	}
 618	while(next = ds->next){	/* assign = */
 619		if(next == this){
 620			ds->next = this->next;
 621			goto Found;
 622		}
 623		ds = next;
 624	}
 625	error(Enodrawimage);
 626
 627    Found:
 628	if(this->dimage)
 629		drawfreedimage(this->dimage);
 630	if(this->dfill)
 631		drawfreedimage(this->dfill);
 632	free(this->screen);
 633	free(this);
 634}
 635
 636void
 637drawfreedimage(DImage *dimage)
 638{
 639	int i;
 640	Memimage *l;
 641	DScreen *ds;
 642
 643	dimage->ref--;
 644	if(dimage->ref < 0)
 645		print("negative ref in drawfreedimage\n");
 646	if(dimage->ref > 0)
 647		return;
 648
 649	/* any names? */
 650	for(i=0; i<sdraw.nname; )
 651		if(sdraw.name[i].dimage == dimage)
 652			drawdelname(sdraw.name+i);
 653		else
 654			i++;
 655	if(dimage->fromname){	/* acquired by name; owned by someone else*/
 656		drawfreedimage(dimage->fromname);
 657		goto Return;
 658	}
 659	if(dimage->image == screenimage)	/* don't free the display */
 660		goto Return;
 661	ds = dimage->dscreen;
 662	if(ds){
 663		l = dimage->image;
 664		if(l->data == screenimage->data)
 665			addflush(l->layer->screenr);
 666		if(l->layer->refreshfn == drawrefresh)	/* else true owner will clean up */
 667			free(l->layer->refreshptr);
 668		l->layer->refreshptr = nil;
 669		if(drawgoodname(dimage))
 670			memldelete(l);
 671		else
 672			memlfree(l);
 673		drawfreedscreen(ds);
 674	}else
 675		freememimage(dimage->image);
 676    Return:
 677	free(dimage->fchar);
 678	free(dimage);
 679}
 680
 681void
 682drawuninstallscreen(Client *client, CScreen *this)
 683{
 684	CScreen *cs, *next;
 685
 686	cs = client->cscreen;
 687	if(cs == this){
 688		client->cscreen = this->next;
 689		drawfreedscreen(this->dscreen);
 690		free(this);
 691		return;
 692	}
 693	while(next = cs->next){	/* assign = */
 694		if(next == this){
 695			cs->next = this->next;
 696			drawfreedscreen(this->dscreen);
 697			free(this);
 698			return;
 699		}
 700		cs = next;
 701	}
 702}
 703
 704void
 705drawuninstall(Client *client, int id)
 706{
 707	DImage *d, *next;
 708
 709	d = client->dimage[id&HASHMASK];
 710	if(d == 0)
 711		error(Enodrawimage);
 712	if(d->id == id){
 713		client->dimage[id&HASHMASK] = d->next;
 714		drawfreedimage(d);
 715		return;
 716	}
 717	while(next = d->next){	/* assign = */
 718		if(next->id == id){
 719			d->next = next->next;
 720			drawfreedimage(next);
 721			return;
 722		}
 723		d = next;
 724	}
 725	error(Enodrawimage);
 726}
 727
 728void
 729drawaddname(Client *client, DImage *di, int n, char *str)
 730{
 731	DName *name, *ename, *new, *t;
 732
 733	name = sdraw.name;
 734	ename = &name[sdraw.nname];
 735	for(; name<ename; name++)
 736		if(drawcmp(name->name, str, n) == 0)
 737			error(Enameused);
 738	t = smalloc((sdraw.nname+1)*sizeof(DName));
 739	memmove(t, sdraw.name, sdraw.nname*sizeof(DName));
 740	free(sdraw.name);
 741	sdraw.name = t;
 742	new = &sdraw.name[sdraw.nname++];
 743	new->name = smalloc(n+1);
 744	memmove(new->name, str, n);
 745	new->name[n] = 0;
 746	new->dimage = di;
 747	new->client = client;
 748	new->vers = ++sdraw.vers;
 749}
 750
 751Client*
 752drawnewclient(void)
 753{
 754	Client *cl, **cp;
 755	int i;
 756
 757	for(i=0; i<sdraw.nclient; i++){
 758		cl = sdraw.client[i];
 759		if(cl == 0)
 760			break;
 761	}
 762	if(i == sdraw.nclient){
 763		cp = malloc((sdraw.nclient+1)*sizeof(Client*));
 764		if(cp == 0)
 765			return 0;
 766		memmove(cp, sdraw.client, sdraw.nclient*sizeof(Client*));
 767		free(sdraw.client);
 768		sdraw.client = cp;
 769		sdraw.nclient++;
 770		cp[i] = 0;
 771	}
 772	cl = malloc(sizeof(Client));
 773	if(cl == 0)
 774		return 0;
 775	memset(cl, 0, sizeof(Client));
 776	cl->slot = i;
 777	cl->clientid = ++sdraw.clientid;
 778	cl->op = SoverD;
 779	sdraw.client[i] = cl;
 780	return cl;
 781}
 782
 783static int
 784drawclientop(Client *cl)
 785{
 786	int op;
 787
 788	op = cl->op;
 789	cl->op = SoverD;
 790	return op;
 791}
 792
 793int
 794drawhasclients(void)
 795{
 796	/*
 797	 * if draw has ever been used, we can't resize the frame buffer,
 798	 * even if all clients have exited (nclients is cumulative); it's too
 799	 * hard to make work.
 800	 */
 801	return sdraw.nclient != 0;
 802}
 803
 804Client*
 805drawclientofpath(ulong path)
 806{
 807	Client *cl;
 808	int slot;
 809
 810	slot = CLIENTPATH(path);
 811	if(slot == 0)
 812		return nil;
 813	cl = sdraw.client[slot-1];
 814	if(cl==0 || cl->clientid==0)
 815		return nil;
 816	return cl;
 817}
 818
 819
 820Client*
 821drawclient(Chan *c)
 822{
 823	Client *client;
 824
 825	client = drawclientofpath(c->qid.path);
 826	if(client == nil)
 827		error(Enoclient);
 828	return client;
 829}
 830
 831Memimage*
 832drawimage(Client *client, uchar *a)
 833{
 834	DImage *d;
 835
 836	d = drawlookup(client, BGLONG(a), 1);
 837	if(d == nil)
 838		error(Enodrawimage);
 839	return d->image;
 840}
 841
 842void
 843drawrectangle(Rectangle *r, uchar *a)
 844{
 845	r->min.x = BGLONG(a+0*4);
 846	r->min.y = BGLONG(a+1*4);
 847	r->max.x = BGLONG(a+2*4);
 848	r->max.y = BGLONG(a+3*4);
 849}
 850
 851void
 852drawpoint(Point *p, uchar *a)
 853{
 854	p->x = BGLONG(a+0*4);
 855	p->y = BGLONG(a+1*4);
 856}
 857
 858Point
 859drawchar(Memimage *dst, Point p, Memimage *src, Point *sp, DImage *font, int index, int op)
 860{
 861	FChar *fc;
 862	Rectangle r;
 863	Point sp1;
 864
 865	fc = &font->fchar[index];
 866	r.min.x = p.x+fc->left;
 867	r.min.y = p.y-(font->ascent-fc->miny);
 868	r.max.x = r.min.x+(fc->maxx-fc->minx);
 869	r.max.y = r.min.y+(fc->maxy-fc->miny);
 870	sp1.x = sp->x+fc->left;
 871	sp1.y = sp->y+fc->miny;
 872	memdraw(dst, r, src, sp1, font->image, Pt(fc->minx, fc->miny), op);
 873	p.x += fc->width;
 874	sp->x += fc->width;
 875	return p;
 876}
 877
 878static int
 879initscreenimage(void)
 880{
 881	int width, depth;
 882	ulong chan;
 883	Rectangle r;
 884
 885	if(screenimage != nil)
 886		return 1;
 887
 888	screendata.base = nil;
 889	screendata.bdata = attachscreen(&r, &chan, &depth, &width, &sdraw.softscreen);
 890	if(screendata.bdata == nil)
 891{fprint(2, "bad bdata\n");
 892		return 0;
 893}
 894	screendata.ref = 1;
 895
 896	screenimage = allocmemimaged(r, chan, &screendata);
 897	if(screenimage == nil){
 898fprint(2, "bad memimaged: %r\n");
 899		/* RSC: BUG: detach screen */
 900		return 0;
 901	}
 902
 903	screenimage->width = width;
 904	screenimage->clipr  = screenimage->r;
 905	return 1;
 906}
 907
 908void
 909deletescreenimage(void)
 910{
 911	qlock(&sdraw);
 912	/* RSC: BUG: detach screen */
 913	if(screenimage)
 914		freememimage(screenimage);
 915	screenimage = nil;
 916	qunlock(&sdraw);
 917}
 918
 919Chan*
 920drawattach(char *spec)
 921{
 922	qlock(&sdraw);
 923	if(!initscreenimage()){
 924		qunlock(&sdraw);
 925		error("no frame buffer");
 926	}
 927	qunlock(&sdraw);
 928	return devattach('i', spec);
 929}
 930
 931Walkqid*
 932drawwalk(Chan *c, Chan *nc, char **name, int nname)
 933{
 934	if(screendata.bdata == nil)
 935		error("no frame buffer");
 936	return devwalk(c, nc, name, nname, 0, 0, drawgen);
 937}
 938
 939static int
 940drawstat(Chan *c, uchar *db, int n)
 941{
 942	return devstat(c, db, n, 0, 0, drawgen);
 943}
 944
 945static Chan*
 946drawopen(Chan *c, int omode)
 947{
 948	Client *cl;
 949
 950	if(c->qid.type & QTDIR)
 951		return devopen(c, omode, 0, 0, drawgen);
 952
 953	qlock(&sdraw);
 954	if(waserror()){
 955		qunlock(&sdraw);
 956		nexterror();
 957	}
 958
 959	if(QID(c->qid) == Qnew){
 960		cl = drawnewclient();
 961		if(cl == 0)
 962			error(Enodev);
 963		c->qid.path = Qctl|((cl->slot+1)<<QSHIFT);
 964	}
 965
 966	switch(QID(c->qid)){
 967	case Qnew:
 968		break;
 969
 970	case Qctl:
 971		cl = drawclient(c);
 972		if(cl->busy)
 973			error(Einuse);
 974		cl->busy = 1;
 975		flushrect = Rect(10000, 10000, -10000, -10000);
 976		drawinstall(cl, 0, screenimage, 0);
 977		incref(&cl->r);
 978		break;
 979	case Qcolormap:
 980	case Qdata:
 981	case Qrefresh:
 982		cl = drawclient(c);
 983		incref(&cl->r);
 984		break;
 985	}
 986	qunlock(&sdraw);
 987	poperror();
 988	c->mode = openmode(omode);
 989	c->flag |= COPEN;
 990	c->offset = 0;
 991	return c;
 992}
 993
 994static void
 995drawclose(Chan *c)
 996{
 997	int i;
 998	DImage *d, **dp;
 999	Client *cl;
1000	Refresh *r;
1001
1002	if(c->qid.type & QTDIR)
1003		return;
1004	qlock(&sdraw);
1005	if(waserror()){
1006		qunlock(&sdraw);
1007		nexterror();
1008	}
1009
1010	cl = drawclient(c);
1011	if(QID(c->qid) == Qctl)
1012		cl->busy = 0;
1013	if((c->flag&COPEN) && (decref(&cl->r)==0)){
1014		while(r = cl->refresh){	/* assign = */
1015			cl->refresh = r->next;
1016			free(r);
1017		}
1018		/* free names */
1019		for(i=0; i<sdraw.nname; )
1020			if(sdraw.name[i].client == cl)
1021				drawdelname(sdraw.name+i);
1022			else
1023				i++;
1024		while(cl->cscreen)
1025			drawuninstallscreen(cl, cl->cscreen);
1026		/* all screens are freed, so now we can free images */
1027		dp = cl->dimage;
1028		for(i=0; i<NHASH; i++){
1029			while((d = *dp) != nil){
1030				*dp = d->next;
1031				drawfreedimage(d);
1032			}
1033			dp++;
1034		}
1035		sdraw.client[cl->slot] = 0;
1036		drawflush();	/* to erase visible, now dead windows */
1037		free(cl);
1038	}
1039	qunlock(&sdraw);
1040	poperror();
1041}
1042
1043long
1044drawread(Chan *c, void *a, long n, vlong off)
1045{
1046	int index, m;
1047	ulong red, green, blue;
1048	Client *cl;
1049	uchar *p;
1050	Refresh *r;
1051	DImage *di;
1052	Memimage *i;
1053	ulong offset = off;
1054	char buf[16];
1055
1056	USED(offset);
1057	if(c->qid.type & QTDIR)
1058		return devdirread(c, a, n, 0, 0, drawgen);
1059	cl = drawclient(c);
1060	qlock(&sdraw);
1061	if(waserror()){
1062		qunlock(&sdraw);
1063		nexterror();
1064	}
1065	switch(QID(c->qid)){
1066	case Qctl:
1067		if(n < 12*12)
1068			error(Eshortread);
1069		if(cl->infoid < 0)
1070			error(Enodrawimage);
1071		if(cl->infoid == 0){
1072			i = screenimage;
1073			if(i == nil)
1074				error(Enodrawimage);
1075		}else{
1076			di = drawlookup(cl, cl->infoid, 1);
1077			if(di == nil)
1078				error(Enodrawimage);
1079			i = di->image;
1080		}
1081		n = sprint(a, "%11d %11d %11s %11d %11d %11d %11d %11d %11d %11d %11d %11d ",
1082			cl->clientid, cl->infoid, chantostr(buf, i->chan), (i->flags&Frepl)==Frepl,
1083			i->r.min.x, i->r.min.y, i->r.max.x, i->r.max.y,
1084			i->clipr.min.x, i->clipr.min.y, i->clipr.max.x, i->clipr.max.y);
1085		cl->infoid = -1;
1086		break;
1087
1088	case Qcolormap:
1089		drawactive(1);	/* to restore map from backup */
1090		p = malloc(4*12*256+1);
1091		if(p == 0)
1092			error(Enomem);
1093		m = 0;
1094		for(index = 0; index < 256; index++){
1095			getcolor(index, &red, &green, &blue);
1096			m += sprint((char*)p+m, "%11d %11lud %11lud %11lud\n", index, red>>24, green>>24, blue>>24);
1097		}
1098		n = readstr(offset, a, n, (char*)p);
1099		free(p);
1100		break;
1101
1102	case Qdata:
1103		if(cl->readdata == nil)
1104			error("no draw data");
1105		if(n < cl->nreaddata)
1106			error(Eshortread);
1107		n = cl->nreaddata;
1108		memmove(a, cl->readdata, cl->nreaddata);
1109		free(cl->readdata);
1110		cl->readdata = nil;
1111		break;
1112
1113	case Qrefresh:
1114		if(n < 5*4)
1115			error(Ebadarg);
1116		for(;;){
1117			if(cl->refreshme || cl->refresh)
1118				break;
1119			qunlock(&sdraw);
1120			if(waserror()){
1121				qlock(&sdraw);	/* restore lock for waserror() above */
1122				nexterror();
1123			}
1124			rendsleep(&cl->refrend, drawrefactive, cl);
1125			poperror();
1126			qlock(&sdraw);
1127		}
1128		p = a;
1129		while(cl->refresh && n>=5*4){
1130			r = cl->refresh;
1131			BPLONG(p+0*4, r->dimage->id);
1132			BPLONG(p+1*4, r->r.min.x);
1133			BPLONG(p+2*4, r->r.min.y);
1134			BPLONG(p+3*4, r->r.max.x);
1135			BPLONG(p+4*4, r->r.max.y);
1136			cl->refresh = r->next;
1137			free(r);
1138			p += 5*4;
1139			n -= 5*4;
1140		}
1141		cl->refreshme = 0;
1142		n = p-(uchar*)a;
1143	}
1144	qunlock(&sdraw);
1145	poperror();
1146	return n;
1147}
1148
1149void
1150drawwakeall(void)
1151{
1152	Client *cl;
1153	int i;
1154
1155	for(i=0; i<sdraw.nclient; i++){
1156		cl = sdraw.client[i];
1157		if(cl && (cl->refreshme || cl->refresh))
1158			rendwakeup(&cl->refrend);
1159	}
1160}
1161
1162static long
1163drawwrite(Chan *c, void *a, long n, vlong off)
1164{
1165	char buf[128], *fields[4], *q;
1166	Client *cl;
1167	int i, m, red, green, blue, x;
1168	ulong offset = off;
1169
1170	USED(offset);
1171	if(c->qid.type & QTDIR)
1172		error(Eisdir);
1173	cl = drawclient(c);
1174	qlock(&sdraw);
1175	if(waserror()){
1176		drawwakeall();
1177		qunlock(&sdraw);
1178		nexterror();
1179	}
1180	switch(QID(c->qid)){
1181	case Qctl:
1182		if(n != 4)
1183			error("unknown draw control request");
1184		cl->infoid = BGLONG((uchar*)a);
1185		break;
1186
1187	case Qcolormap:
1188		drawactive(1);	/* to restore map from backup */
1189		m = n;
1190		n = 0;
1191		while(m > 0){
1192			x = m;
1193			if(x > sizeof(buf)-1)
1194				x = sizeof(buf)-1;
1195			q = memccpy(buf, a, '\n', x);
1196			if(q == 0)
1197				break;
1198			i = q-buf;
1199			n += i;
1200			a = (char*)a + i;
1201			m -= i;
1202			*q = 0;
1203			if(getfields(buf, fields, nelem(fields), 1, " ") != 4)
1204				error(Ebadarg);
1205			i = strtoul(fields[0], 0, 0);
1206			red = strtoul(fields[1], 0, 0);
1207			green = strtoul(fields[2], 0, 0);
1208			blue = strtoul(fields[3], &q, 0);
1209			if(fields[3] == q)
1210				error(Ebadarg);
1211			if(red>255 || green>255 || blue>255 || i<0 || i>255)
1212				error(Ebadarg);
1213			red |= red<<8;
1214			red |= red<<16;
1215			green |= green<<8;
1216			green |= green<<16;
1217			blue |= blue<<8;
1218			blue |= blue<<16;
1219			setcolor(i, red, green, blue);
1220		}
1221		break;
1222
1223	case Qdata:
1224		drawmesg(cl, a, n);
1225		drawwakeall();
1226		break;
1227
1228	default:
1229		error(Ebadusefd);
1230	}
1231	qunlock(&sdraw);
1232	poperror();
1233	return n;
1234}
1235
1236uchar*
1237drawcoord(uchar *p, uchar *maxp, int oldx, int *newx)
1238{
1239	int b, x;
1240
1241	if(p >= maxp)
1242		error(Eshortdraw);
1243	b = *p++;
1244	x = b & 0x7F;
1245	if(b & 0x80){
1246		if(p+1 >= maxp)
1247			error(Eshortdraw);
1248		x |= *p++ << 7;
1249		x |= *p++ << 15;
1250		if(x & (1<<22))
1251			x |= ~0<<23;
1252	}else{
1253		if(b & 0x40)
1254			x |= ~0<<7;
1255		x += oldx;
1256	}
1257	*newx = x;
1258	return p;
1259}
1260
1261static void
1262printmesg(char *fmt, uchar *a, int plsprnt)
1263{
1264	char buf[256];
1265	char *p, *q;
1266	int s;
1267
1268	if(1|| plsprnt==0){
1269		SET(s,q,p);
1270		USED(fmt, a, buf, p, q, s);
1271		return;
1272	}
1273	q = buf;
1274	*q++ = *a++;
1275	for(p=fmt; *p; p++){
1276		switch(*p){
1277		case 'l':
1278			q += sprint(q, " %ld", (long)BGLONG(a));
1279			a += 4;
1280			break;
1281		case 'L':
1282			q += sprint(q, " %.8lux", (ulong)BGLONG(a));
1283			a += 4;
1284			break;
1285		case 'R':
1286			q += sprint(q, " [%d %d %d %d]", BGLONG(a), BGLONG(a+4), BGLONG(a+8), BGLONG(a+12));
1287			a += 16;
1288			break;
1289		case 'P':
1290			q += sprint(q, " [%d %d]", BGLONG(a), BGLONG(a+4));
1291			a += 8;
1292			break;
1293		case 'b':
1294			q += sprint(q, " %d", *a++);
1295			break;
1296		case 's':
1297			q += sprint(q, " %d", BGSHORT(a));
1298			a += 2;
1299			break;
1300		case 'S':
1301			q += sprint(q, " %.4ux", BGSHORT(a));
1302			a += 2;
1303			break;
1304		}
1305	}
1306	*q++ = '\n';
1307	*q = 0;
1308	iprint("%.*s", (int)(q-buf), buf);
1309}
1310
1311void
1312drawmesg(Client *client, void *av, int n)
1313{
1314	int c, repl, m, y, dstid, scrnid, ni, ci, j, nw, e0, e1, op, ox, oy, oesize, esize, doflush;
1315	uchar *u, *a, refresh;
1316	char *fmt;
1317	ulong value, chan;
1318	Rectangle r, clipr;
1319	Point p, q, *pp, sp;
1320	Memimage *i, *dst, *src, *mask;
1321	Memimage *l, **lp;
1322	Memscreen *scrn;
1323	DImage *font, *ll, *di, *ddst, *dsrc;
1324	DName *dn;
1325	DScreen *dscrn;
1326	FChar *fc;
1327	Refx *refx;
1328	CScreen *cs;
1329	Refreshfn reffn;
1330
1331	a = av;
1332	m = 0;
1333	fmt = nil;
1334	if(waserror()){
1335		if(fmt) printmesg(fmt, a, 1);
1336	/*	iprint("error: %s\n", up->error);	*/
1337		nexterror();
1338	}
1339	while((n-=m) > 0){
1340		USED(fmt);
1341		a += m;
1342		switch(*a){
1343		default:
1344			error("bad draw command");
1345		/* new allocate: 'b' id[4] screenid[4] refresh[1] chan[4] repl[1] R[4*4] clipR[4*4] rrggbbaa[4] */
1346		case 'b':
1347			printmesg(fmt="LLbLbRRL", a, 0);
1348			m = 1+4+4+1+4+1+4*4+4*4+4;
1349			if(n < m)
1350				error(Eshortdraw);
1351			dstid = BGLONG(a+1);
1352			scrnid = BGSHORT(a+5);
1353			refresh = a[9];
1354			chan = BGLONG(a+10);
1355			repl = a[14];
1356			drawrectangle(&r, a+15);
1357			drawrectangle(&clipr, a+31);
1358			value = BGLONG(a+47);
1359			if(drawlookup(client, dstid, 0))
1360				error(Eimageexists);
1361			if(scrnid){
1362				dscrn = drawlookupscreen(client, scrnid, &cs);
1363				scrn = dscrn->screen;
1364				if(repl || chan!=scrn->image->chan)
1365					error("image parameters incompatible with screen");
1366				reffn = nil;
1367				switch(refresh){
1368				case Refbackup:
1369					break;
1370				case Refnone:
1371					reffn = memlnorefresh;
1372					break;
1373				case Refmesg:
1374					reffn = drawrefresh;
1375					break;
1376				default:
1377					error("unknown refresh method");
1378				}
1379				l = memlalloc(scrn, r, reffn, 0, value);
1380				if(l == 0)
1381					error(Edrawmem);
1382				addflush(l->layer->screenr);
1383				l->clipr = clipr;
1384				rectclip(&l->clipr, r);
1385				if(drawinstall(client, dstid, l, dscrn) == 0){
1386					memldelete(l);
1387					error(Edrawmem);
1388				}
1389				dscrn->ref++;
1390				if(reffn){
1391					refx = nil;
1392					if(reffn == drawrefresh){
1393						refx = malloc(sizeof(Refx));
1394						if(refx == 0){
1395							drawuninstall(client, dstid);
1396							error(Edrawmem);
1397						}
1398						refx->client = client;
1399						refx->dimage = drawlookup(client, dstid, 1);
1400					}
1401					memlsetrefresh(l, reffn, refx);
1402				}
1403				continue;
1404			}
1405			i = allocmemimage(r, chan);
1406			if(i == 0)
1407				error(Edrawmem);
1408			if(repl)
1409				i->flags |= Frepl;
1410			i->clipr = clipr;
1411			if(!repl)
1412				rectclip(&i->clipr, r);
1413			if(drawinstall(client, dstid, i, 0) == 0){
1414				freememimage(i);
1415				error(Edrawmem);
1416			}
1417			memfillcolor(i, value);
1418			continue;
1419
1420		/* allocate screen: 'A' id[4] imageid[4] fillid[4] public[1] */
1421		case 'A':
1422			printmesg(fmt="LLLb", a, 1);
1423			m = 1+4+4+4+1;
1424			if(n < m)
1425				error(Eshortdraw);
1426			dstid = BGLONG(a+1);
1427			if(dstid == 0)
1428				error(Ebadarg);
1429			if(drawlookupdscreen(dstid))
1430				error(Escreenexists);
1431			ddst = drawlookup(client, BGLONG(a+5), 1);
1432			dsrc = drawlookup(client, BGLONG(a+9), 1);
1433			if(ddst==0 || dsrc==0)
1434				error(Enodrawimage);
1435			if(drawinstallscreen(client, 0, dstid, ddst, dsrc, a[13]) == 0)
1436				error(Edrawmem);
1437			continue;
1438
1439		/* set repl and clip: 'c' dstid[4] repl[1] clipR[4*4] */
1440		case 'c':
1441			printmesg(fmt="LbR", a, 0);
1442			m = 1+4+1+4*4;
1443			if(n < m)
1444				error(Eshortdraw);
1445			ddst = drawlookup(client, BGLONG(a+1), 1);
1446			if(ddst == nil)
1447				error(Enodrawimage);
1448			if(ddst->name)
1449				error("can't change repl/clipr of shared image");
1450			dst = ddst->image;
1451			if(a[5])
1452				dst->flags |= Frepl;
1453			drawrectangle(&dst->clipr, a+6);
1454			continue;
1455
1456		/* draw: 'd' dstid[4] srcid[4] maskid[4] R[4*4] P[2*4] P[2*4] */
1457		case 'd':
1458			printmesg(fmt="LLLRPP", a, 0);
1459			m = 1+4+4+4+4*4+2*4+2*4;
1460			if(n < m)
1461				error(Eshortdraw);
1462			dst = drawimage(client, a+1);
1463			dstid = BGLONG(a+1);
1464			src = drawimage(client, a+5);
1465			mask = drawimage(client, a+9);
1466			drawrectangle(&r, a+13);
1467			drawpoint(&p, a+29);
1468			drawpoint(&q, a+37);
1469			op = drawclientop(client);
1470			memdraw(dst, r, src, p, mask, q, op);
1471			dstflush(dstid, dst, r);
1472			continue;
1473
1474		/* toggle debugging: 'D' val[1] */
1475		case 'D':
1476			printmesg(fmt="b", a, 0);
1477			m = 1+1;
1478			if(n < m)
1479				error(Eshortdraw);
1480			drawdebug = a[1];
1481			continue;
1482
1483		/* ellipse: 'e' dstid[4] srcid[4] center[2*4] a[4] b[4] thick[4] sp[2*4] alpha[4] phi[4]*/
1484		case 'e':
1485		case 'E':
1486			printmesg(fmt="LLPlllPll", a, 0);
1487			m = 1+4+4+2*4+4+4+4+2*4+2*4;
1488			if(n < m)
1489				error(Eshortdraw);
1490			dst = drawimage(client, a+1);
1491			dstid = BGLONG(a+1);
1492			src = drawimage(client, a+5);
1493			drawpoint(&p, a+9);
1494			e0 = BGLONG(a+17);
1495			e1 = BGLONG(a+21);
1496			if(e0<0 || e1<0)
1497				error("invalid ellipse semidiameter");
1498			j = BGLONG(a+25);
1499			if(j < 0)
1500				error("negative ellipse thickness");
1501			drawpoint(&sp, a+29);
1502			c = j;
1503			if(*a == 'E')
1504				c = -1;
1505			ox = BGLONG(a+37);
1506			oy = BGLONG(a+41);
1507			op = drawclientop(client);
1508			/* high bit indicates arc angles are present */
1509			if(ox & (1<<31)){
1510				if((ox & (1<<30)) == 0)
1511					ox &= ~(1<<31);
1512				memarc(dst, p, e0, e1, c, src, sp, ox, oy, op);
1513			}else
1514				memellipse(dst, p, e0, e1, c, src, sp, op);
1515			dstflush(dstid, dst, Rect(p.x-e0-j, p.y-e1-j, p.x+e0+j+1, p.y+e1+j+1));
1516			continue;
1517
1518		/* free: 'f' id[4] */
1519		case 'f':
1520			printmesg(fmt="L", a, 1);
1521			m = 1+4;
1522			if(n < m)
1523				error(Eshortdraw);
1524			ll = drawlookup(client, BGLONG(a+1), 0);
1525			if(ll && ll->dscreen && ll->dscreen->owner != client)
1526				ll->dscreen->owner->refreshme = 1;
1527			drawuninstall(client, BGLONG(a+1));
1528			continue;
1529
1530		/* free screen: 'F' id[4] */
1531		case 'F':
1532			printmesg(fmt="L", a, 1);
1533			m = 1+4;
1534			if(n < m)
1535				error(Eshortdraw);
1536			drawlookupscreen(client, BGLONG(a+1), &cs);
1537			drawuninstallscreen(client, cs);
1538			continue;
1539
1540		/* initialize font: 'i' fontid[4] nchars[4] ascent[1] */
1541		case 'i':
1542			printmesg(fmt="Llb", a, 1);
1543			m = 1+4+4+1;
1544			if(n < m)
1545				error(Eshortdraw);
1546			dstid = BGLONG(a+1);
1547			if(dstid == 0)
1548				error("can't use display as font");
1549			font = drawlookup(client, dstid, 1);
1550			if(font == 0)
1551				error(Enodrawimage);
1552			if(font->image->layer)
1553				error("can't use window as font");
1554			free(font->fchar);	/* should we complain if non-zero? */
1555			ni = BGLONG(a+5);
1556			font->fchar = malloc(ni*sizeof(FChar));
1557			if(font->fchar == 0)
1558				error("no memory for font");
1559			memset(font->fchar, 0, ni*sizeof(FChar));
1560			font->nfchar = ni;
1561			font->ascent = a[9];
1562			continue;
1563
1564		/* load character: 'l' fontid[4] srcid[4] index[2] R[4*4] P[2*4] left[1] width[1] */
1565		case 'l':
1566			printmesg(fmt="LLSRPbb", a, 0);
1567			m = 1+4+4+2+4*4+2*4+1+1;
1568			if(n < m)
1569				error(Eshortdraw);
1570			font = drawlookup(client, BGLONG(a+1), 1);
1571			if(font == 0)
1572				error(Enodrawimage);
1573			if(font->nfchar == 0)
1574				error(Enotfont);
1575			src = drawimage(client, a+5);
1576			ci = BGSHORT(a+9);
1577			if(ci >= font->nfchar)
1578				error(Eindex);
1579			drawrectangle(&r, a+11);
1580			drawpoint(&p, a+27);
1581			memdraw(font->image, r, src, p, memopaque, p, S);
1582			fc = &font->fchar[ci];
1583			fc->minx = r.min.x;
1584			fc->maxx = r.max.x;
1585			fc->miny = r.min.y;
1586			fc->maxy = r.max.y;
1587			fc->left = a[35];
1588			fc->width = a[36];
1589			continue;
1590
1591		/* draw line: 'L' dstid[4] p0[2*4] p1[2*4] end0[4] end1[4] radius[4] srcid[4] sp[2*4] */
1592		case 'L':
1593			printmesg(fmt="LPPlllLP", a, 0);
1594			m = 1+4+2*4+2*4+4+4+4+4+2*4;
1595			if(n < m)
1596				error(Eshortdraw);
1597			dst = drawimage(client, a+1);
1598			dstid = BGLONG(a+1);
1599			drawpoint(&p, a+5);
1600			drawpoint(&q, a+13);
1601			e0 = BGLONG(a+21);
1602			e1 = BGLONG(a+25);
1603			j = BGLONG(a+29);
1604			if(j < 0)
1605				error("negative line width");
1606			src = drawimage(client, a+33);
1607			drawpoint(&sp, a+37);
1608			op = drawclientop(client);
1609			memline(dst, p, q, e0, e1, j, src, sp, op);
1610			/* avoid memlinebbox if possible */
1611			if(dstid==0 || dst->layer!=nil){
1612				/* BUG: this is terribly inefficient: update maximal containing rect*/
1613				r = memlinebbox(p, q, e0, e1, j);
1614				dstflush(dstid, dst, insetrect(r, -(1+1+j)));
1615			}
1616			continue;
1617
1618		/* create image mask: 'm' newid[4] id[4] */
1619/*
1620 *
1621		case 'm':
1622			printmesg("LL", a, 0);
1623			m = 4+4;
1624			if(n < m)
1625				error(Eshortdraw);
1626			break;
1627 *
1628 */
1629
1630		/* attach to a named image: 'n' dstid[4] j[1] name[j] */
1631		case 'n':
1632			printmesg(fmt="Lz", a, 0);
1633			m = 1+4+1;
1634			if(n < m)
1635				error(Eshortdraw);
1636			j = a[5];
1637			if(j == 0)	/* give me a non-empty name please */
1638				error(Eshortdraw);
1639			m += j;
1640			if(n < m)
1641				error(Eshortdraw);
1642			dstid = BGLONG(a+1);
1643			if(drawlookup(client, dstid, 0))
1644				error(Eimageexists);
1645			dn = drawlookupname(j, (char*)a+6);
1646			if(dn == nil)
1647				error(Enoname);
1648			if(drawinstall(client, dstid, dn->dimage->image, 0) == 0)
1649				error(Edrawmem);
1650			di = drawlookup(client, dstid, 0);
1651			if(di == 0)
1652				error("draw: can't happen");
1653			di->vers = dn->vers;
1654			di->name = smalloc(j+1);
1655			di->fromname = dn->dimage;
1656			di->fromname->ref++;
1657			memmove(di->name, a+6, j);
1658			di->name[j] = 0;
1659			client->infoid = dstid;
1660			continue;
1661
1662		/* name an image: 'N' dstid[4] in[1] j[1] name[j] */
1663		case 'N':
1664			printmesg(fmt="Lbz", a, 0);
1665			m = 1+4+1+1;
1666			if(n < m)
1667				error(Eshortdraw);
1668			c = a[5];
1669			j = a[6];
1670			if(j == 0)	/* give me a non-empty name please */
1671				error(Eshortdraw);
1672			m += j;
1673			if(n < m)
1674				error(Eshortdraw);
1675			di = drawlookup(client, BGLONG(a+1), 0);
1676			if(di == 0)
1677				error(Enodrawimage);
1678			if(di->name)
1679				error(Enamed);
1680			if(c)
1681				drawaddname(client, di, j, (char*)a+7);
1682			else{
1683				dn = drawlookupname(j, (char*)a+7);
1684				if(dn == nil)
1685					error(Enoname);
1686				if(dn->dimage != di)
1687					error(Ewrongname);
1688				drawdelname(dn);
1689			}
1690			continue;
1691
1692		/* position window: 'o' id[4] r.min [2*4] screenr.min [2*4] */
1693		case 'o':
1694			printmesg(fmt="LPP", a, 0);
1695			m = 1+4+2*4+2*4;
1696			if(n < m)
1697				error(Eshortdraw);
1698			dst = drawimage(client, a+1);
1699			if(dst->layer){
1700				drawpoint(&p, a+5);
1701				drawpoint(&q, a+13);
1702				r = dst->layer->screenr;
1703				ni = memlorigin(dst, p, q);
1704				if(ni < 0)
1705					error("image origin failed");
1706				if(ni > 0){
1707					addflush(r);
1708					addflush(dst->layer->screenr);
1709					ll = drawlookup(client, BGLONG(a+1), 1);
1710					drawrefreshscreen(ll, client);
1711				}
1712			}
1713			continue;
1714
1715		/* set compositing operator for next draw operation: 'O' op */
1716		case 'O':
1717			printmesg(fmt="b", a, 0);
1718			m = 1+1;
1719			if(n < m)
1720				error(Eshortdraw);
1721			client->op = a[1];
1722			continue;
1723
1724		/* filled polygon: 'P' dstid[4] n[2] wind[4] ignore[2*4] srcid[4] sp[2*4] p0[2*4] dp[2*2*n] */
1725		/* polygon: 'p' dstid[4] n[2] end0[4] end1[4] radius[4] srcid[4] sp[2*4] p0[2*4] dp[2*2*n] */
1726		case 'p':
1727		case 'P':
1728			printmesg(fmt="LslllLPP", a, 0);
1729			m = 1+4+2+4+4+4+4+2*4;
1730			if(n < m)
1731				error(Eshortdraw);
1732			dstid = BGLONG(a+1);
1733			dst = drawimage(client, a+1);
1734			ni = BGSHORT(a+5);
1735			if(ni < 0)
1736				error("negative count in polygon");
1737			e0 = BGLONG(a+7);
1738			e1 = BGLONG(a+11);
1739			j = 0;
1740			if(*a == 'p'){
1741				j = BGLONG(a+15);
1742				if(j < 0)
1743					error("negative polygon line width");
1744			}
1745			src = drawimage(client, a+19);
1746			drawpoint(&sp, a+23);
1747			drawpoint(&p, a+31);
1748			ni++;
1749			pp = malloc(ni*sizeof(Point));
1750			if(pp == nil)
1751				error(Enomem);
1752			doflush = 0;
1753			if(dstid==0 || (dst->layer && dst->layer->screen->image->data == screenimage->data))
1754				doflush = 1;	/* simplify test in loop */
1755			ox = oy = 0;
1756			esize = 0;
1757			u = a+m;
1758			for(y=0; y<ni; y++){
1759				q = p;
1760				oesize = esize;
1761				u = drawcoord(u, a+n, ox, &p.x);
1762				u = drawcoord(u, a+n, oy, &p.y);
1763				ox = p.x;
1764				oy = p.y;
1765				if(doflush){
1766					esize = j;
1767					if(*a == 'p'){
1768						if(y == 0){
1769							c = memlineendsize(e0);
1770							if(c > esize)
1771								esize = c;
1772						}
1773						if(y == ni-1){
1774							c = memlineendsize(e1);
1775							if(c > esize)
1776								esize = c;
1777						}
1778					}
1779					if(*a=='P' && e0!=1 && e0 !=~0)
1780						r = dst->clipr;
1781					else if(y > 0){
1782						r = Rect(q.x-oesize, q.y-oesize, q.x+oesize+1, q.y+oesize+1);
1783						combinerect(&r, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1));
1784					}
1785					if(rectclip(&r, dst->clipr))		/* should perhaps be an arg to dstflush */
1786						dstflush(dstid, dst, r);
1787				}
1788				pp[y] = p;
1789			}
1790			if(y == 1)
1791				dstflush(dstid, dst, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1));
1792			op = drawclientop(client);
1793			if(*a == 'p')
1794				mempoly(dst, pp, ni, e0, e1, j, src, sp, op);
1795			else
1796				memfillpoly(dst, pp, ni, e0, src, sp, op);
1797			free(pp);
1798			m = u-a;
1799			continue;
1800
1801		/* read: 'r' id[4] R[4*4] */
1802		case 'r':
1803			printmesg(fmt="LR", a, 0);
1804			m = 1+4+4*4;
1805			if(n < m)
1806				error(Eshortdraw);
1807			i = drawimage(client, a+1);
1808			if(0 && i->layer)
1809				error("readimage from window unimplemented");
1810			drawrectangle(&r, a+5);
1811			if(!rectinrect(r, i->r))
1812				error(Ereadoutside);
1813			c = bytesperline(r, i->depth);
1814			c *= Dy(r);
1815			free(client->readdata);
1816			client->readdata = mallocz(c, 0);
1817			if(client->readdata == nil)
1818				error("readimage malloc failed");
1819			client->nreaddata = unloadmemimage(i, r, client->readdata, c);
1820			if(client->nreaddata < 0){
1821				free(client->readdata);
1822				client->readdata = nil;
1823				error("bad readimage call");
1824			}
1825			continue;
1826
1827		/* string: 's' dstid[4] srcid[4] fontid[4] P[2*4] clipr[4*4] sp[2*4] ni[2] ni*(index[2]) */
1828		/* 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]) */
1829		case 's':
1830		case 'x':
1831			printmesg(fmt="LLLPRPs", a, 0);
1832			m = 1+4+4+4+2*4+4*4+2*4+2;
1833			if(*a == 'x')
1834				m += 4+2*4;
1835			if(n < m)
1836				error(Eshortdraw);
1837
1838			dst = drawimage(client, a+1);
1839			dstid = BGLONG(a+1);
1840			src = drawimage(client, a+5);
1841			font = drawlookup(client, BGLONG(a+9), 1);
1842			if(font == 0)
1843				error(Enodrawimage);
1844			if(font->nfchar == 0)
1845				error(Enotfont);
1846			drawpoint(&p, a+13);
1847			drawrectangle(&r, a+21);
1848			drawpoint(&sp, a+37);
1849			ni = BGSHORT(a+45);
1850			u = a+m;
1851			m += ni*2;
1852			if(n < m)
1853				error(Eshortdraw);
1854			clipr = dst->clipr;
1855			dst->clipr = r;
1856			op = drawclientop(client);
1857			if(*a == 'x'){
1858				/* paint background */
1859				l = drawimage(client, a+47);
1860				drawpoint(&q, a+51);
1861				r.min.x = p.x;
1862				r.min.y = p.y-font->ascent;
1863				r.max.x = p.x;
1864				r.max.y = r.min.y+Dy(font->image->r);
1865				j = ni;
1866				while(--j >= 0){
1867					ci = BGSHORT(u);
1868					if(ci<0 || ci>=font->nfchar){
1869						dst->clipr = clipr;
1870						error(Eindex);
1871					}
1872					r.max.x += font->fchar[ci].width;
1873					u += 2;
1874				}
1875				memdraw(dst, r, l, q, memopaque, ZP, op);
1876				u -= 2*ni;
1877			}
1878			q = p;
1879			while(--ni >= 0){
1880				ci = BGSHORT(u);
1881				if(ci<0 || ci>=font->nfchar){
1882					dst->clipr = clipr;
1883					error(Eindex);
1884				}
1885				q = drawchar(dst, q, src, &sp, font, ci, op);
1886				u += 2;
1887			}
1888			dst->clipr = clipr;
1889			p.y -= font->ascent;
1890			dstflush(dstid, dst, Rect(p.x, p.y, q.x, p.y+Dy(font->image->r)));
1891			continue;
1892
1893		/* use public screen: 'S' id[4] chan[4] */
1894		case 'S':
1895			printmesg(fmt="Ll", a, 0);
1896			m = 1+4+4;
1897			if(n < m)
1898				error(Eshortdraw);
1899			dstid = BGLONG(a+1);
1900			if(dstid == 0)
1901				error(Ebadarg);
1902			dscrn = drawlookupdscreen(dstid);
1903			if(dscrn==0 || (dscrn->public==0 && dscrn->owner!=client))
1904				error(Enodrawscreen);
1905			if(dscrn->screen->image->chan != BGLONG(a+5))
1906				error("inconsistent chan");
1907			if(drawinstallscreen(client, dscrn, 0, 0, 0, 0) == 0)
1908				error(Edrawmem);
1909			continue;
1910
1911		/* top or bottom windows: 't' top[1] nw[2] n*id[4] */
1912		case 't':
1913			printmesg(fmt="bsL", a, 0);
1914			m = 1+1+2;
1915			if(n < m)
1916				error(Eshortdraw);
1917			nw = BGSHORT(a+2);
1918			if(nw < 0)
1919				error(Ebadarg);
1920			if(nw == 0)
1921				continue;
1922			m += nw*4;
1923			if(n < m)
1924				error(Eshortdraw);
1925			lp = malloc(nw*sizeof(Memimage*));
1926			if(lp == 0)
1927				error(Enomem);
1928			if(waserror()){
1929				free(lp);
1930				nexterror();
1931			}
1932			for(j=0; j<nw; j++)
1933				lp[j] = drawimage(client, a+1+1+2+j*4);
1934			if(lp[0]->layer == 0)
1935				error("images are not windows");
1936			for(j=1; j<nw; j++)
1937				if(lp[j]->layer->screen != lp[0]->layer->screen)
1938					error("images not on same screen");
1939			if(a[1])
1940				memltofrontn(lp, nw);
1941			else
1942				memltorearn(lp, nw);
1943			if(lp[0]->layer->screen->image->data == screenimage->data)
1944				for(j=0; j<nw; j++)
1945					addflush(lp[j]->layer->screenr);
1946			ll = drawlookup(client, BGLONG(a+1+1+2), 1);
1947			drawrefreshscreen(ll, client);
1948			poperror();
1949			free(lp);
1950			continue;
1951
1952		/* visible: 'v' */
1953		case 'v':
1954			printmesg(fmt="", a, 0);
1955			m = 1;
1956			drawflush();
1957			continue;
1958
1959		/* write: 'y' id[4] R[4*4] data[x*1] */
1960		/* write from compressed data: 'Y' id[4] R[4*4] data[x*1] */
1961		case 'y':
1962		case 'Y':
1963			printmesg(fmt="LR", a, 0);
1964		//	iprint("load %c\n", *a);
1965			m = 1+4+4*4;
1966			if(n < m)
1967				error(Eshortdraw);
1968			dstid = BGLONG(a+1);
1969			dst = drawimage(client, a+1);
1970			drawrectangle(&r, a+5);
1971			if(!rectinrect(r, dst->r))
1972				error(Ewriteoutside);
1973			y = memload(dst, r, a+m, n-m, *a=='Y');
1974			if(y < 0)
1975				error("bad writeimage call");
1976			dstflush(dstid, dst, r);
1977			m += y;
1978			continue;
1979		}
1980	}
1981	poperror();
1982}
1983
1984Dev drawdevtab = {
1985	'i',
1986	"draw",
1987
1988	devreset,
1989	devinit,
1990	drawattach,
1991	drawwalk,
1992	drawstat,
1993	drawopen,
1994	devcreate,
1995	drawclose,
1996	drawread,
1997	devbread,
1998	drawwrite,
1999	devbwrite,
2000	devremove,
2001	devwstat,
2002};
2003
2004/*
2005 * On 8 bit displays, load the default color map
2006 */
2007void
2008drawcmap(void)
2009{
2010	int r, g, b, cr, cg, cb, v;
2011	int num, den;
2012	int i, j;
2013
2014	drawactive(1);	/* to restore map from backup */
2015	for(r=0,i=0; r!=4; r++)
2016	    for(v=0; v!=4; v++,i+=16){
2017		for(g=0,j=v-r; g!=4; g++)
2018		    for(b=0;b!=4;b++,j++){
2019			den = r;
2020			if(g > den)
2021				den = g;
2022			if(b > den)
2023				den = b;
2024			if(den == 0)	/* divide check -- pick grey shades */
2025				cr = cg = cb = v*17;
2026			else{
2027				num = 17*(4*den+v);
2028				cr = r*num/den;
2029				cg = g*num/den;
2030				cb = b*num/den;
2031			}
2032			setcolor(i+(j&15),
2033				cr*0x01010101, cg*0x01010101, cb*0x01010101);
2034		    }
2035	}
2036}
2037
2038void
2039drawblankscreen(int blank)
2040{
2041	int i, nc;
2042	ulong *p;
2043
2044	if(blank == sdraw.blanked)
2045		return;
2046	if(!canqlock(&sdraw))
2047		return;
2048	if(!initscreenimage()){
2049		qunlock(&sdraw);
2050		return;
2051	}
2052	p = sdraw.savemap;
2053	nc = screenimage->depth > 8 ? 256 : 1<<screenimage->depth;
2054
2055	/*
2056	 * blankscreen uses the hardware to blank the screen
2057	 * when possible.  to help in cases when it is not possible,
2058	 * we set the color map to be all black.
2059	 */
2060	if(blank == 0){	/* turn screen on */
2061		for(i=0; i<nc; i++, p+=3)
2062			setcolor(i, p[0], p[1], p[2]);
2063		blankscreen(0);
2064	}else{	/* turn screen off */
2065		blankscreen(1);
2066		for(i=0; i<nc; i++, p+=3){
2067			getcolor(i, &p[0], &p[1], &p[2]);
2068			setcolor(i, 0, 0, 0);
2069		}
2070	}
2071	sdraw.blanked = blank;
2072	qunlock(&sdraw);
2073}
2074
2075/*
2076 * record activity on screen, changing blanking as appropriate
2077 */
2078void
2079drawactive(int active)
2080{
2081	if(active){
2082		drawblankscreen(0);
2083		sdraw.blanktime = 0;
2084	}else
2085		sdraw.blanktime++;
2086}