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