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