PageRenderTime 139ms CodeModel.GetById 2ms app.highlight 118ms RepoModel.GetById 4ms app.codeStats 0ms

/sys/src/9/port/devdraw.c

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