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
drawcanqlock(void)187 drawcanqlock(void)
188 {
189 	return canqlock(&sdraw.lk);
190 }
191 
192 void
drawqlock(void)193 drawqlock(void)
194 {
195 	qlock(&sdraw.lk);
196 }
197 
198 void
drawqunlock(void)199 drawqunlock(void)
200 {
201 	qunlock(&sdraw.lk);
202 }
203 
204 static int
drawgen(Chan * c,char * name,Dirtab * dt,int ndt,int s,Dir * dp)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
drawrefactive(void * a)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
drawrefreshscreen(DImage * l,Client * client)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
drawrefresh(Memimage * m,Rectangle r,void * v)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
addflush(Rectangle r)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
dstflush(int dstid,Memimage * dst,Rectangle r)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
drawflush(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
drawflushr(Rectangle r)437 drawflushr(Rectangle r)
438 {
439 	qlock(&sdraw.lk);
440 	flushmemscreen(r);
441 	qunlock(&sdraw.lk);
442 }
443 
444 static
445 int
drawcmp(char * a,char * b,int n)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*
drawlookupname(int n,char * str)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
drawgoodname(DImage * d)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*
drawlookup(Client * client,int id,int checkname)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*
drawlookupdscreen(int id)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*
drawlookupscreen(Client * client,int id,CScreen ** cs)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*
drawinstall(Client * client,int id,Memimage * i,DScreen * dscreen)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*
drawinstallscreen(Client * client,DScreen * d,int id,DImage * dimage,DImage * dfill,int public)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
drawdelname(DName * name)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
drawfreedscreen(DScreen * this)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
drawfreedimage(DImage * dimage)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
drawuninstallscreen(Client * client,CScreen * this)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
drawuninstall(Client * client,int id)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
drawaddname(Client * client,DImage * di,int n,char * str)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*
drawnewclient(void)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
drawclientop(Client * cl)796 drawclientop(Client *cl)
797 {
798 	int op;
799 
800 	op = cl->op;
801 	cl->op = SoverD;
802 	return op;
803 }
804 
805 int
drawhasclients(void)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*
drawclientofpath(ulong path)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*
drawclient(Chan * c)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*
drawimage(Client * client,uchar * a)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
drawrectangle(Rectangle * r,uchar * a)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
drawpoint(Point * p,uchar * a)864 drawpoint(Point *p, uchar *a)
865 {
866 	p->x = BGLONG(a+0*4);
867 	p->y = BGLONG(a+1*4);
868 }
869 
870 #define isvgascreen(dst) 1
871 
872 
873 Point
drawchar(Memimage * dst,Memimage * rdst,Point p,Memimage * src,Point * sp,DImage * font,int index,int op)874 drawchar(Memimage *dst, Memimage *rdst, Point p,
875 	Memimage *src, Point *sp, DImage *font, int index, int op)
876 {
877 	FChar *fc;
878 	Rectangle r;
879 	Point sp1;
880 	static Memimage *tmp;
881 
882 	fc = &font->fchar[index];
883 	r.min.x = p.x+fc->left;
884 	r.min.y = p.y-(font->ascent-fc->miny);
885 	r.max.x = r.min.x+(fc->maxx-fc->minx);
886 	r.max.y = r.min.y+(fc->maxy-fc->miny);
887 	sp1.x = sp->x+fc->left;
888 	sp1.y = sp->y+fc->miny;
889 
890 	/*
891 	 * If we're drawing greyscale fonts onto a VGA screen,
892 	 * it's very costly to read the screen memory to do the
893 	 * alpha blending inside memdraw.  If this is really a stringbg,
894 	 * then rdst is the bg image (in main memory) which we can
895 	 * refer to for the underlying dst pixels instead of reading dst
896 	 * directly.
897 	 */
898 	if(1 || (isvgascreen(dst) && !isvgascreen(rdst) /*&& font->image->depth > 1*/)){
899 		if(tmp == nil || tmp->chan != dst->chan || Dx(tmp->r) < Dx(r) || Dy(tmp->r) < Dy(r)){
900 			if(tmp)
901 				freememimage(tmp);
902 			tmp = allocmemimage(Rect(0,0,Dx(r),Dy(r)), dst->chan);
903 			if(tmp == nil)
904 				goto fallback;
905 		}
906 		memdraw(tmp, Rect(0,0,Dx(r),Dy(r)), rdst, r.min, memopaque, ZP, S);
907 		memdraw(tmp, Rect(0,0,Dx(r),Dy(r)), src, sp1, font->image, Pt(fc->minx, fc->miny), op);
908 		memdraw(dst, r, tmp, ZP, memopaque, ZP, S);
909 	}else{
910 	fallback:
911 		memdraw(dst, r, src, sp1, font->image, Pt(fc->minx, fc->miny), op);
912 	}
913 
914 	p.x += fc->width;
915 	sp->x += fc->width;
916 	return p;
917 }
918 
919 static int
initscreenimage(void)920 initscreenimage(void)
921 {
922 	int width, depth;
923 	ulong chan;
924 	void *X;
925 	Rectangle r;
926 
927 	if(screenimage != nil)
928 		return 1;
929 
930 	screendata.base = nil;
931 	screendata.bdata = attachscreen(&r, &chan, &depth, &width, &sdraw.softscreen, &X);
932 	if(screendata.bdata == nil && X == nil)
933 		return 0;
934 	screendata.ref = 1;
935 
936 	screenimage = allocmemimaged(r, chan, &screendata, X);
937 	if(screenimage == nil){
938 		/* RSC: BUG: detach screen */
939 		return 0;
940 	}
941 
942 	screenimage->width = width;
943 	screenimage->clipr = r;
944 	return 1;
945 }
946 
947 void
deletescreenimage(void)948 deletescreenimage(void)
949 {
950 	qlock(&sdraw.lk);
951 	/* RSC: BUG: detach screen */
952 	if(screenimage)
953 		freememimage(screenimage);
954 	screenimage = nil;
955 	qunlock(&sdraw.lk);
956 }
957 
958 static Chan*
drawattach(char * spec)959 drawattach(char *spec)
960 {
961 	qlock(&sdraw.lk);
962 	if(!initscreenimage()){
963 		qunlock(&sdraw.lk);
964 		error("no frame buffer");
965 	}
966 	qunlock(&sdraw.lk);
967 	return devattach('i', spec);
968 }
969 
970 static Walkqid*
drawwalk(Chan * c,Chan * nc,char ** name,int nname)971 drawwalk(Chan *c, Chan *nc, char **name, int nname)
972 {
973 	if(screendata.bdata == nil)
974 		error("no frame buffer");
975 	return devwalk(c, nc, name, nname, 0, 0, drawgen);
976 }
977 
978 static int
drawstat(Chan * c,uchar * db,int n)979 drawstat(Chan *c, uchar *db, int n)
980 {
981 	return devstat(c, db, n, 0, 0, drawgen);
982 }
983 
984 static Chan*
drawopen(Chan * c,int omode)985 drawopen(Chan *c, int omode)
986 {
987 	Client *cl;
988 
989 	if(c->qid.type & QTDIR){
990 		c = devopen(c, omode, 0, 0, drawgen);
991 		c->iounit = IOUNIT;
992 	}
993 
994 	qlock(&sdraw.lk);
995 	if(waserror()){
996 		qunlock(&sdraw.lk);
997 		nexterror();
998 	}
999 
1000 	if(QID(c->qid) == Qnew){
1001 		cl = drawnewclient();
1002 		if(cl == 0)
1003 			error(Enodev);
1004 		c->qid.path = Qctl|((cl->slot+1)<<QSHIFT);
1005 	}
1006 
1007 	switch(QID(c->qid)){
1008 	case Qnew:
1009 		break;
1010 
1011 	case Qctl:
1012 		cl = drawclient(c);
1013 		if(cl->busy)
1014 			error(Einuse);
1015 		cl->busy = 1;
1016 		flushrect = Rect(10000, 10000, -10000, -10000);
1017 		drawinstall(cl, 0, screenimage, 0);
1018 		incref(&cl->r);
1019 		break;
1020 	case Qcolormap:
1021 	case Qdata:
1022 	case Qrefresh:
1023 		cl = drawclient(c);
1024 		incref(&cl->r);
1025 		break;
1026 	}
1027 	qunlock(&sdraw.lk);
1028 	poperror();
1029 	c->mode = openmode(omode);
1030 	c->flag |= COPEN;
1031 	c->offset = 0;
1032 	c->iounit = IOUNIT;
1033 	return c;
1034 }
1035 
1036 static void
drawclose(Chan * c)1037 drawclose(Chan *c)
1038 {
1039 	int i;
1040 	DImage *d, **dp;
1041 	Client *cl;
1042 	Refresh *r;
1043 
1044 	if(QID(c->qid) < Qcolormap)	/* Qtopdir, Qnew, Q3rd, Q2nd have no client */
1045 		return;
1046 	qlock(&sdraw.lk);
1047 	if(waserror()){
1048 		qunlock(&sdraw.lk);
1049 		nexterror();
1050 	}
1051 
1052 	cl = drawclient(c);
1053 	if(QID(c->qid) == Qctl)
1054 		cl->busy = 0;
1055 	if((c->flag&COPEN) && (decref(&cl->r)==0)){
1056 		while((r = cl->refresh)){	/* assign = */
1057 			cl->refresh = r->next;
1058 			free(r);
1059 		}
1060 		/* free names */
1061 		for(i=0; i<sdraw.nname; )
1062 			if(sdraw.name[i].client == cl)
1063 				drawdelname(sdraw.name+i);
1064 			else
1065 				i++;
1066 		while(cl->cscreen)
1067 			drawuninstallscreen(cl, cl->cscreen);
1068 		/* all screens are freed, so now we can free images */
1069 		dp = cl->dimage;
1070 		for(i=0; i<NHASH; i++){
1071 			while((d = *dp) != nil){
1072 				*dp = d->next;
1073 				drawfreedimage(d);
1074 			}
1075 			dp++;
1076 		}
1077 		sdraw.client[cl->slot] = 0;
1078 		drawflush();	/* to erase visible, now dead windows */
1079 		free(cl);
1080 	}
1081 	qunlock(&sdraw.lk);
1082 	poperror();
1083 }
1084 
1085 long
drawread(Chan * c,void * a,long n,vlong off)1086 drawread(Chan *c, void *a, long n, vlong off)
1087 {
1088 	int index, m;
1089 	ulong red, green, blue;
1090 	Client *cl;
1091 	uchar *p;
1092 	Refresh *r;
1093 	DImage *di;
1094 	Memimage *i;
1095 	ulong offset = off;
1096 	char buf[16];
1097 
1098 	if(c->qid.type & QTDIR)
1099 		return devdirread(c, a, n, 0, 0, drawgen);
1100 	cl = drawclient(c);
1101 	qlock(&sdraw.lk);
1102 	if(waserror()){
1103 		qunlock(&sdraw.lk);
1104 		nexterror();
1105 	}
1106 	switch(QID(c->qid)){
1107 	case Qctl:
1108 		if(n < 12*12)
1109 			error(Eshortread);
1110 		if(cl->infoid < 0)
1111 			error(Enodrawimage);
1112 		if(cl->infoid == 0){
1113 			i = screenimage;
1114 			if(i == nil)
1115 				error(Enodrawimage);
1116 		}else{
1117 			di = drawlookup(cl, cl->infoid, 1);
1118 			if(di == nil)
1119 				error(Enodrawimage);
1120 			i = di->image;
1121 		}
1122 		n = sprint(a, "%11d %11d %11s %11d %11d %11d %11d %11d %11d %11d %11d %11d ",
1123 			cl->clientid, cl->infoid, chantostr(buf, i->chan), (i->flags&Frepl)==Frepl,
1124 			i->r.min.x, i->r.min.y, i->r.max.x, i->r.max.y,
1125 			i->clipr.min.x, i->clipr.min.y, i->clipr.max.x, i->clipr.max.y);
1126 		cl->infoid = -1;
1127 		break;
1128 
1129 	case Qcolormap:
1130 		drawactive(1);	/* to restore map from backup */
1131 		p = malloc(4*12*256+1);
1132 		if(p == 0)
1133 			error(Enomem);
1134 		m = 0;
1135 		for(index = 0; index < 256; index++){
1136 			getcolor(index, &red, &green, &blue);
1137 			m += sprint((char*)p+m, "%11d %11lud %11lud %11lud\n", index, red>>24, green>>24, blue>>24);
1138 		}
1139 		n = readstr(offset, a, n, (char*)p);
1140 		free(p);
1141 		break;
1142 
1143 	case Qdata:
1144 		if(cl->readdata == nil)
1145 			error("no draw data");
1146 		if(n < cl->nreaddata)
1147 			error(Eshortread);
1148 		n = cl->nreaddata;
1149 		memmove(a, cl->readdata, cl->nreaddata);
1150 		free(cl->readdata);
1151 		cl->readdata = nil;
1152 		break;
1153 
1154 	case Qrefresh:
1155 		if(n < 5*4)
1156 			error(Ebadarg);
1157 		for(;;){
1158 			if(cl->refreshme || cl->refresh)
1159 				break;
1160 			qunlock(&sdraw.lk);
1161 			if(waserror()){
1162 				qlock(&sdraw.lk);	/* restore lock for waserror() above */
1163 				nexterror();
1164 			}
1165 			sleep(&cl->refrend, drawrefactive, cl);
1166 			poperror();
1167 			qlock(&sdraw.lk);
1168 		}
1169 		p = a;
1170 		while(cl->refresh && n>=5*4){
1171 			r = cl->refresh;
1172 			BPLONG(p+0*4, r->dimage->id);
1173 			BPLONG(p+1*4, r->r.min.x);
1174 			BPLONG(p+2*4, r->r.min.y);
1175 			BPLONG(p+3*4, r->r.max.x);
1176 			BPLONG(p+4*4, r->r.max.y);
1177 			cl->refresh = r->next;
1178 			free(r);
1179 			p += 5*4;
1180 			n -= 5*4;
1181 		}
1182 		cl->refreshme = 0;
1183 		n = p-(uchar*)a;
1184 	}
1185 	qunlock(&sdraw.lk);
1186 	poperror();
1187 	return n;
1188 }
1189 
1190 void
drawwakeall(void)1191 drawwakeall(void)
1192 {
1193 	Client *cl;
1194 	int i;
1195 
1196 	for(i=0; i<sdraw.nclient; i++){
1197 		cl = sdraw.client[i];
1198 		if(cl && (cl->refreshme || cl->refresh))
1199 			wakeup(&cl->refrend);
1200 	}
1201 }
1202 
1203 static long
drawwrite(Chan * c,void * a,long n,vlong offset)1204 drawwrite(Chan *c, void *a, long n, vlong offset)
1205 {
1206 	char buf[128], *fields[4], *q;
1207 	Client *cl;
1208 	int i, m, red, green, blue, x;
1209 
1210 	USED(offset);
1211 
1212 	if(c->qid.type & QTDIR)
1213 		error(Eisdir);
1214 	cl = drawclient(c);
1215 	qlock(&sdraw.lk);
1216 	if(waserror()){
1217 		drawwakeall();
1218 		qunlock(&sdraw.lk);
1219 		nexterror();
1220 	}
1221 	switch(QID(c->qid)){
1222 	case Qctl:
1223 		if(n != 4)
1224 			error("unknown draw control request");
1225 		cl->infoid = BGLONG((uchar*)a);
1226 		break;
1227 
1228 	case Qcolormap:
1229 		drawactive(1);	/* to restore map from backup */
1230 		m = n;
1231 		n = 0;
1232 		while(m > 0){
1233 			x = m;
1234 			if(x > sizeof(buf)-1)
1235 				x = sizeof(buf)-1;
1236 			q = memccpy(buf, a, '\n', x);
1237 			if(q == 0)
1238 				break;
1239 			i = q-buf;
1240 			n += i;
1241 			a = (char*)a + i;
1242 			m -= i;
1243 			*q = 0;
1244 			if(tokenize(buf, fields, nelem(fields)) != 4)
1245 				error(Ebadarg);
1246 			i = strtoul(fields[0], 0, 0);
1247 			red = strtoul(fields[1], 0, 0);
1248 			green = strtoul(fields[2], 0, 0);
1249 			blue = strtoul(fields[3], &q, 0);
1250 			if(fields[3] == q)
1251 				error(Ebadarg);
1252 			if(red>255 || green>255 || blue>255 || i<0 || i>255)
1253 				error(Ebadarg);
1254 			red |= red<<8;
1255 			red |= red<<16;
1256 			green |= green<<8;
1257 			green |= green<<16;
1258 			blue |= blue<<8;
1259 			blue |= blue<<16;
1260 			setcolor(i, red, green, blue);
1261 		}
1262 		break;
1263 
1264 	case Qdata:
1265 		drawmesg(cl, a, n);
1266 		drawwakeall();
1267 		break;
1268 
1269 	default:
1270 		error(Ebadusefd);
1271 	}
1272 	qunlock(&sdraw.lk);
1273 	poperror();
1274 	return n;
1275 }
1276 
1277 uchar*
drawcoord(uchar * p,uchar * maxp,int oldx,int * newx)1278 drawcoord(uchar *p, uchar *maxp, int oldx, int *newx)
1279 {
1280 	int b, x;
1281 
1282 	if(p >= maxp)
1283 		error(Eshortdraw);
1284 	b = *p++;
1285 	x = b & 0x7F;
1286 	if(b & 0x80){
1287 		if(p+1 >= maxp)
1288 			error(Eshortdraw);
1289 		x |= *p++ << 7;
1290 		x |= *p++ << 15;
1291 		if(x & (1<<22))
1292 			x |= ~0<<23;
1293 	}else{
1294 		if(b & 0x40)
1295 			x |= ~0<<7;
1296 		x += oldx;
1297 	}
1298 	*newx = x;
1299 	return p;
1300 }
1301 
1302 static void
printmesg(char * fmt,uchar * a,int plsprnt)1303 printmesg(char *fmt, uchar *a, int plsprnt)
1304 {
1305 	char buf[256];
1306 	char *p, *q;
1307 	int s;
1308 
1309 	if(1|| plsprnt==0){
1310 		SET(s);
1311 		SET(q);
1312 		SET(p);
1313 		USED(fmt);
1314 		USED(a);
1315 		p = buf;
1316 		USED(p);
1317 		USED(q);
1318 		USED(s);
1319 		return;
1320 	}
1321 	q = buf;
1322 	*q++ = *a++;
1323 	for(p=fmt; *p; p++){
1324 		switch(*p){
1325 		case 'l':
1326 			q += sprint(q, " %ld", (long)BGLONG(a));
1327 			a += 4;
1328 			break;
1329 		case 'L':
1330 			q += sprint(q, " %.8lux", (ulong)BGLONG(a));
1331 			a += 4;
1332 			break;
1333 		case 'R':
1334 			q += sprint(q, " [%d %d %d %d]", BGLONG(a), BGLONG(a+4), BGLONG(a+8), BGLONG(a+12));
1335 			a += 16;
1336 			break;
1337 		case 'P':
1338 			q += sprint(q, " [%d %d]", BGLONG(a), BGLONG(a+4));
1339 			a += 8;
1340 			break;
1341 		case 'b':
1342 			q += sprint(q, " %d", *a++);
1343 			break;
1344 		case 's':
1345 			q += sprint(q, " %d", BGSHORT(a));
1346 			a += 2;
1347 			break;
1348 		case 'S':
1349 			q += sprint(q, " %.4ux", BGSHORT(a));
1350 			a += 2;
1351 			break;
1352 		}
1353 	}
1354 	*q++ = '\n';
1355 	*q = 0;
1356 	iprint("%.*s", (int)(q-buf), buf);
1357 }
1358 
1359 void
drawmesg(Client * client,void * av,int n)1360 drawmesg(Client *client, void *av, int n)
1361 {
1362 	int c, repl, m, y, dstid, scrnid, ni, ci, j, nw, e0, e1, op, ox, oy, oesize, esize, doflush;
1363 	uchar *u, *a, refresh;
1364 	char *fmt;
1365 	ulong value, chan;
1366 	Rectangle r, clipr;
1367 	Point p, q, *pp, sp;
1368 	Memimage *i, *dst, *src, *mask;
1369 	Memimage *l, **lp;
1370 	Memscreen *scrn;
1371 	DImage *font, *ll, *di, *ddst, *dsrc;
1372 	DName *dn;
1373 	DScreen *dscrn;
1374 	FChar *fc;
1375 	Refx *refx;
1376 	CScreen *cs;
1377 	Refreshfn reffn;
1378 
1379 	a = av;
1380 	m = 0;
1381 	fmt = nil;
1382 	if(waserror()){
1383 		if(fmt) printmesg(fmt, a, 1);
1384 	/*	iprint("error: %s\n", up->errstr);	*/
1385 		nexterror();
1386 	}
1387 	while((n-=m) > 0){
1388 		USED(fmt);
1389 		a += m;
1390 		switch(*a){
1391 		default:
1392 			error("bad draw command");
1393 		/* new allocate: 'b' id[4] screenid[4] refresh[1] chan[4] repl[1] R[4*4] clipR[4*4] rrggbbaa[4] */
1394 		case 'b':
1395 			printmesg(fmt="LLbLbRRL", a, 0);
1396 			m = 1+4+4+1+4+1+4*4+4*4+4;
1397 			if(n < m)
1398 				error(Eshortdraw);
1399 			dstid = BGLONG(a+1);
1400 			scrnid = BGSHORT(a+5);
1401 			refresh = a[9];
1402 			chan = BGLONG(a+10);
1403 			repl = a[14];
1404 			drawrectangle(&r, a+15);
1405 			drawrectangle(&clipr, a+31);
1406 			value = BGLONG(a+47);
1407 			if(drawlookup(client, dstid, 0))
1408 				error(Eimageexists);
1409 			if(scrnid){
1410 				dscrn = drawlookupscreen(client, scrnid, &cs);
1411 				scrn = dscrn->screen;
1412 				if(repl || chan!=scrn->image->chan)
1413 					error("image parameters incompatible with screen");
1414 				reffn = 0;
1415 				switch(refresh){
1416 				case Refbackup:
1417 					break;
1418 				case Refnone:
1419 					reffn = memlnorefresh;
1420 					break;
1421 				case Refmesg:
1422 					reffn = drawrefresh;
1423 					break;
1424 				default:
1425 					error("unknown refresh method");
1426 				}
1427 				l = memlalloc(scrn, r, reffn, 0, value);
1428 				if(l == 0)
1429 					error(Edrawmem);
1430 				addflush(l->layer->screenr);
1431 				l->clipr = clipr;
1432 				rectclip(&l->clipr, r);
1433 				if(drawinstall(client, dstid, l, dscrn) == 0){
1434 					memldelete(l);
1435 					error(Edrawmem);
1436 				}
1437 				dscrn->ref++;
1438 				if(reffn){
1439 					refx = nil;
1440 					if(reffn == drawrefresh){
1441 						refx = malloc(sizeof(Refx));
1442 						if(refx == 0){
1443 							drawuninstall(client, dstid);
1444 							error(Edrawmem);
1445 						}
1446 						refx->client = client;
1447 						refx->dimage = drawlookup(client, dstid, 1);
1448 					}
1449 					memlsetrefresh(l, reffn, refx);
1450 				}
1451 				continue;
1452 			}
1453 			i = allocmemimage(r, chan);
1454 			if(i == 0)
1455 				error(Edrawmem);
1456 			if(repl)
1457 				i->flags |= Frepl;
1458 			i->clipr = clipr;
1459 			if(!repl)
1460 				rectclip(&i->clipr, r);
1461 			if(drawinstall(client, dstid, i, 0) == 0){
1462 				freememimage(i);
1463 				error(Edrawmem);
1464 			}
1465 			memfillcolor(i, value);
1466 			continue;
1467 
1468 		/* allocate screen: 'A' id[4] imageid[4] fillid[4] public[1] */
1469 		case 'A':
1470 			printmesg(fmt="LLLb", a, 1);
1471 			m = 1+4+4+4+1;
1472 			if(n < m)
1473 				error(Eshortdraw);
1474 			dstid = BGLONG(a+1);
1475 			if(dstid == 0)
1476 				error(Ebadarg);
1477 			if(drawlookupdscreen(dstid))
1478 				error(Escreenexists);
1479 			ddst = drawlookup(client, BGLONG(a+5), 1);
1480 			dsrc = drawlookup(client, BGLONG(a+9), 1);
1481 			if(ddst==0 || dsrc==0)
1482 				error(Enodrawimage);
1483 			if(drawinstallscreen(client, 0, dstid, ddst, dsrc, a[13]) == 0)
1484 				error(Edrawmem);
1485 			continue;
1486 
1487 		/* set repl and clip: 'c' dstid[4] repl[1] clipR[4*4] */
1488 		case 'c':
1489 			printmesg(fmt="LbR", a, 0);
1490 			m = 1+4+1+4*4;
1491 			if(n < m)
1492 				error(Eshortdraw);
1493 			ddst = drawlookup(client, BGLONG(a+1), 1);
1494 			if(ddst == nil)
1495 				error(Enodrawimage);
1496 			if(ddst->name)
1497 				error("can't change repl/clipr of shared image");
1498 			dst = ddst->image;
1499 			if(a[5])
1500 				dst->flags |= Frepl;
1501 			drawrectangle(&dst->clipr, a+6);
1502 			continue;
1503 
1504 		/* draw: 'd' dstid[4] srcid[4] maskid[4] R[4*4] P[2*4] P[2*4] */
1505 		case 'd':
1506 			printmesg(fmt="LLLRPP", a, 0);
1507 			m = 1+4+4+4+4*4+2*4+2*4;
1508 			if(n < m)
1509 				error(Eshortdraw);
1510 			dst = drawimage(client, a+1);
1511 			dstid = BGLONG(a+1);
1512 			src = drawimage(client, a+5);
1513 			mask = drawimage(client, a+9);
1514 			drawrectangle(&r, a+13);
1515 			drawpoint(&p, a+29);
1516 			drawpoint(&q, a+37);
1517 			op = drawclientop(client);
1518 			memdraw(dst, r, src, p, mask, q, op);
1519 			dstflush(dstid, dst, r);
1520 			continue;
1521 
1522 		/* toggle debugging: 'D' val[1] */
1523 		case 'D':
1524 			printmesg(fmt="b", a, 0);
1525 			m = 1+1;
1526 			if(n < m)
1527 				error(Eshortdraw);
1528 			drawdebug = a[1];
1529 			continue;
1530 
1531 		/* ellipse: 'e' dstid[4] srcid[4] center[2*4] a[4] b[4] thick[4] sp[2*4] alpha[4] phi[4]*/
1532 		case 'e':
1533 		case 'E':
1534 			printmesg(fmt="LLPlllPll", a, 0);
1535 			m = 1+4+4+2*4+4+4+4+2*4+2*4;
1536 			if(n < m)
1537 				error(Eshortdraw);
1538 			dst = drawimage(client, a+1);
1539 			dstid = BGLONG(a+1);
1540 			src = drawimage(client, a+5);
1541 			drawpoint(&p, a+9);
1542 			e0 = BGLONG(a+17);
1543 			e1 = BGLONG(a+21);
1544 			if(e0<0 || e1<0)
1545 				error("invalid ellipse semidiameter");
1546 			j = BGLONG(a+25);
1547 			if(j < 0)
1548 				error("negative ellipse thickness");
1549 			drawpoint(&sp, a+29);
1550 			c = j;
1551 			if(*a == 'E')
1552 				c = -1;
1553 			ox = BGLONG(a+37);
1554 			oy = BGLONG(a+41);
1555 			op = drawclientop(client);
1556 			/* high bit indicates arc angles are present */
1557 			if(ox & (1U<<31)){
1558 				if((ox & (1<<30)) == 0)
1559 					ox &= ~(1U<<31);
1560 				memarc(dst, p, e0, e1, c, src, sp, ox, oy, op);
1561 			}else
1562 				memellipse(dst, p, e0, e1, c, src, sp, op);
1563 			dstflush(dstid, dst, Rect(p.x-e0-j, p.y-e1-j, p.x+e0+j+1, p.y+e1+j+1));
1564 			continue;
1565 
1566 		/* free: 'f' id[4] */
1567 		case 'f':
1568 			printmesg(fmt="L", a, 1);
1569 			m = 1+4;
1570 			if(n < m)
1571 				error(Eshortdraw);
1572 			ll = drawlookup(client, BGLONG(a+1), 0);
1573 			if(ll && ll->dscreen && ll->dscreen->owner != client)
1574 				ll->dscreen->owner->refreshme = 1;
1575 			drawuninstall(client, BGLONG(a+1));
1576 			continue;
1577 
1578 		/* free screen: 'F' id[4] */
1579 		case 'F':
1580 			printmesg(fmt="L", a, 1);
1581 			m = 1+4;
1582 			if(n < m)
1583 				error(Eshortdraw);
1584 			drawlookupscreen(client, BGLONG(a+1), &cs);
1585 			drawuninstallscreen(client, cs);
1586 			continue;
1587 
1588 		/* initialize font: 'i' fontid[4] nchars[4] ascent[1] */
1589 		case 'i':
1590 			printmesg(fmt="Llb", a, 1);
1591 			m = 1+4+4+1;
1592 			if(n < m)
1593 				error(Eshortdraw);
1594 			dstid = BGLONG(a+1);
1595 			if(dstid == 0)
1596 				error("can't use display as font");
1597 			font = drawlookup(client, dstid, 1);
1598 			if(font == 0)
1599 				error(Enodrawimage);
1600 			if(font->image->layer)
1601 				error("can't use window as font");
1602 			ni = BGLONG(a+5);
1603 			if(ni<=0 || ni>4096)
1604 				error("bad font size (4096 chars max)");
1605 			free(font->fchar);	/* should we complain if non-zero? */
1606 			font->fchar = malloc(ni*sizeof(FChar));
1607 			if(font->fchar == 0)
1608 				error("no memory for font");
1609 			memset(font->fchar, 0, ni*sizeof(FChar));
1610 			font->nfchar = ni;
1611 			font->ascent = a[9];
1612 			continue;
1613 
1614 		/* load character: 'l' fontid[4] srcid[4] index[2] R[4*4] P[2*4] left[1] width[1] */
1615 		case 'l':
1616 			printmesg(fmt="LLSRPbb", a, 0);
1617 			m = 1+4+4+2+4*4+2*4+1+1;
1618 			if(n < m)
1619 				error(Eshortdraw);
1620 			font = drawlookup(client, BGLONG(a+1), 1);
1621 			if(font == 0)
1622 				error(Enodrawimage);
1623 			if(font->nfchar == 0)
1624 				error(Enotfont);
1625 			src = drawimage(client, a+5);
1626 			ci = BGSHORT(a+9);
1627 			if(ci >= font->nfchar)
1628 				error(Eindex);
1629 			drawrectangle(&r, a+11);
1630 			drawpoint(&p, a+27);
1631 			memdraw(font->image, r, src, p, memopaque, p, S);
1632 			fc = &font->fchar[ci];
1633 			fc->minx = r.min.x;
1634 			fc->maxx = r.max.x;
1635 			fc->miny = r.min.y;
1636 			fc->maxy = r.max.y;
1637 			fc->left = a[35];
1638 			fc->width = a[36];
1639 			continue;
1640 
1641 		/* draw line: 'L' dstid[4] p0[2*4] p1[2*4] end0[4] end1[4] radius[4] srcid[4] sp[2*4] */
1642 		case 'L':
1643 			printmesg(fmt="LPPlllLP", a, 0);
1644 			m = 1+4+2*4+2*4+4+4+4+4+2*4;
1645 			if(n < m)
1646 				error(Eshortdraw);
1647 			dst = drawimage(client, a+1);
1648 			dstid = BGLONG(a+1);
1649 			drawpoint(&p, a+5);
1650 			drawpoint(&q, a+13);
1651 			e0 = BGLONG(a+21);
1652 			e1 = BGLONG(a+25);
1653 			j = BGLONG(a+29);
1654 			if(j < 0)
1655 				error("negative line width");
1656 			src = drawimage(client, a+33);
1657 			drawpoint(&sp, a+37);
1658 			op = drawclientop(client);
1659 			memline(dst, p, q, e0, e1, j, src, sp, op);
1660 			/* avoid memlinebbox if possible */
1661 			if(dstid==0 || dst->layer!=nil){
1662 				/* BUG: this is terribly inefficient: update maximal containing rect*/
1663 				r = memlinebbox(p, q, e0, e1, j);
1664 				dstflush(dstid, dst, insetrect(r, -(1+1+j)));
1665 			}
1666 			continue;
1667 
1668 		/* create image mask: 'm' newid[4] id[4] */
1669 /*
1670  *
1671 		case 'm':
1672 			printmesg("LL", a, 0);
1673 			m = 4+4;
1674 			if(n < m)
1675 				error(Eshortdraw);
1676 			break;
1677  *
1678  */
1679 
1680 		/* attach to a named image: 'n' dstid[4] j[1] name[j] */
1681 		case 'n':
1682 			printmesg(fmt="Lz", a, 0);
1683 			m = 1+4+1;
1684 			if(n < m)
1685 				error(Eshortdraw);
1686 			j = a[5];
1687 			if(j == 0)	/* give me a non-empty name please */
1688 				error(Eshortdraw);
1689 			m += j;
1690 			if(n < m)
1691 				error(Eshortdraw);
1692 			dstid = BGLONG(a+1);
1693 			if(drawlookup(client, dstid, 0))
1694 				error(Eimageexists);
1695 			dn = drawlookupname(j, (char*)a+6);
1696 			if(dn == nil)
1697 				error(Enoname);
1698 			if(drawinstall(client, dstid, dn->dimage->image, 0) == 0)
1699 				error(Edrawmem);
1700 			di = drawlookup(client, dstid, 0);
1701 			if(di == 0)
1702 				error("draw: can't happen");
1703 			di->vers = dn->vers;
1704 			di->name = smalloc(j+1);
1705 			di->fromname = dn->dimage;
1706 			di->fromname->ref++;
1707 			memmove(di->name, a+6, j);
1708 			di->name[j] = 0;
1709 			client->infoid = dstid;
1710 			continue;
1711 
1712 		/* name an image: 'N' dstid[4] in[1] j[1] name[j] */
1713 		case 'N':
1714 			printmesg(fmt="Lbz", a, 0);
1715 			m = 1+4+1+1;
1716 			if(n < m)
1717 				error(Eshortdraw);
1718 			c = a[5];
1719 			j = a[6];
1720 			if(j == 0)	/* give me a non-empty name please */
1721 				error(Eshortdraw);
1722 			m += j;
1723 			if(n < m)
1724 				error(Eshortdraw);
1725 			di = drawlookup(client, BGLONG(a+1), 0);
1726 			if(di == 0)
1727 				error(Enodrawimage);
1728 			if(di->name)
1729 				error(Enamed);
1730 			if(c)
1731 				drawaddname(client, di, j, (char*)a+7);
1732 			else{
1733 				dn = drawlookupname(j, (char*)a+7);
1734 				if(dn == nil)
1735 					error(Enoname);
1736 				if(dn->dimage != di)
1737 					error(Ewrongname);
1738 				drawdelname(dn);
1739 			}
1740 			continue;
1741 
1742 		/* position window: 'o' id[4] r.min [2*4] screenr.min [2*4] */
1743 		case 'o':
1744 			printmesg(fmt="LPP", a, 0);
1745 			m = 1+4+2*4+2*4;
1746 			if(n < m)
1747 				error(Eshortdraw);
1748 			dst = drawimage(client, a+1);
1749 			if(dst->layer){
1750 				drawpoint(&p, a+5);
1751 				drawpoint(&q, a+13);
1752 				r = dst->layer->screenr;
1753 				ni = memlorigin(dst, p, q);
1754 				if(ni < 0)
1755 					error("image origin failed");
1756 				if(ni > 0){
1757 					addflush(r);
1758 					addflush(dst->layer->screenr);
1759 					ll = drawlookup(client, BGLONG(a+1), 1);
1760 					drawrefreshscreen(ll, client);
1761 				}
1762 			}
1763 			continue;
1764 
1765 		/* set compositing operator for next draw operation: 'O' op */
1766 		case 'O':
1767 			printmesg(fmt="b", a, 0);
1768 			m = 1+1;
1769 			if(n < m)
1770 				error(Eshortdraw);
1771 			client->op = a[1];
1772 			continue;
1773 
1774 		/* filled polygon: 'P' dstid[4] n[2] wind[4] ignore[2*4] srcid[4] sp[2*4] p0[2*4] dp[2*2*n] */
1775 		/* polygon: 'p' dstid[4] n[2] end0[4] end1[4] radius[4] srcid[4] sp[2*4] p0[2*4] dp[2*2*n] */
1776 		case 'p':
1777 		case 'P':
1778 			printmesg(fmt="LslllLPP", a, 0);
1779 			m = 1+4+2+4+4+4+4+2*4;
1780 			if(n < m)
1781 				error(Eshortdraw);
1782 			dstid = BGLONG(a+1);
1783 			dst = drawimage(client, a+1);
1784 			ni = BGSHORT(a+5);
1785 			if(ni < 0)
1786 				error("negative count in polygon");
1787 			e0 = BGLONG(a+7);
1788 			e1 = BGLONG(a+11);
1789 			j = 0;
1790 			if(*a == 'p'){
1791 				j = BGLONG(a+15);
1792 				if(j < 0)
1793 					error("negative polygon line width");
1794 			}
1795 			src = drawimage(client, a+19);
1796 			drawpoint(&sp, a+23);
1797 			drawpoint(&p, a+31);
1798 			ni++;
1799 			pp = malloc(ni*sizeof(Point));
1800 			if(pp == nil)
1801 				error(Enomem);
1802 			doflush = 0;
1803 			if(dstid==0 || (dst->layer && dst->layer->screen->image->data == screenimage->data))
1804 				doflush = 1;	/* simplify test in loop */
1805 			ox = oy = 0;
1806 			esize = 0;
1807 			u = a+m;
1808 			for(y=0; y<ni; y++){
1809 				q = p;
1810 				oesize = esize;
1811 				u = drawcoord(u, a+n, ox, &p.x);
1812 				u = drawcoord(u, a+n, oy, &p.y);
1813 				ox = p.x;
1814 				oy = p.y;
1815 				if(doflush){
1816 					esize = j;
1817 					if(*a == 'p'){
1818 						if(y == 0){
1819 							c = memlineendsize(e0);
1820 							if(c > esize)
1821 								esize = c;
1822 						}
1823 						if(y == ni-1){
1824 							c = memlineendsize(e1);
1825 							if(c > esize)
1826 								esize = c;
1827 						}
1828 					}
1829 					if(*a=='P' && e0!=1 && e0 !=~0)
1830 						r = dst->clipr;
1831 					else if(y > 0){
1832 						r = Rect(q.x-oesize, q.y-oesize, q.x+oesize+1, q.y+oesize+1);
1833 						combinerect(&r, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1));
1834 					}
1835 					if(rectclip(&r, dst->clipr))		/* should perhaps be an arg to dstflush */
1836 						dstflush(dstid, dst, r);
1837 				}
1838 				pp[y] = p;
1839 			}
1840 			if(y == 1)
1841 				dstflush(dstid, dst, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1));
1842 			op = drawclientop(client);
1843 			if(*a == 'p')
1844 				mempoly(dst, pp, ni, e0, e1, j, src, sp, op);
1845 			else
1846 				memfillpoly(dst, pp, ni, e0, src, sp, op);
1847 			free(pp);
1848 			m = u-a;
1849 			continue;
1850 
1851 		/* read: 'r' id[4] R[4*4] */
1852 		case 'r':
1853 			printmesg(fmt="LR", a, 0);
1854 			m = 1+4+4*4;
1855 			if(n < m)
1856 				error(Eshortdraw);
1857 			i = drawimage(client, a+1);
1858 			drawrectangle(&r, a+5);
1859 			if(!rectinrect(r, i->r))
1860 				error(Ereadoutside);
1861 			c = bytesperline(r, i->depth);
1862 			c *= Dy(r);
1863 			free(client->readdata);
1864 			client->readdata = mallocz(c, 0);
1865 			if(client->readdata == nil)
1866 				error("readimage malloc failed");
1867 			client->nreaddata = memunload(i, r, client->readdata, c);
1868 			if(client->nreaddata < 0){
1869 				free(client->readdata);
1870 				client->readdata = nil;
1871 				error("bad readimage call");
1872 			}
1873 			continue;
1874 
1875 		/* string: 's' dstid[4] srcid[4] fontid[4] P[2*4] clipr[4*4] sp[2*4] ni[2] ni*(index[2]) */
1876 		/* 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]) */
1877 		case 's':
1878 		case 'x':
1879 			printmesg(fmt="LLLPRPs", a, 0);
1880 			m = 1+4+4+4+2*4+4*4+2*4+2;
1881 			if(*a == 'x')
1882 				m += 4+2*4;
1883 			if(n < m)
1884 				error(Eshortdraw);
1885 
1886 			dst = drawimage(client, a+1);
1887 			dstid = BGLONG(a+1);
1888 			src = drawimage(client, a+5);
1889 			font = drawlookup(client, BGLONG(a+9), 1);
1890 			if(font == 0)
1891 				error(Enodrawimage);
1892 			if(font->nfchar == 0)
1893 				error(Enotfont);
1894 			drawpoint(&p, a+13);
1895 			drawrectangle(&r, a+21);
1896 			drawpoint(&sp, a+37);
1897 			ni = BGSHORT(a+45);
1898 			u = a+m;
1899 			m += ni*2;
1900 			if(n < m)
1901 				error(Eshortdraw);
1902 			clipr = dst->clipr;
1903 			dst->clipr = r;
1904 			op = drawclientop(client);
1905 			l = dst;
1906 			if(*a == 'x'){
1907 				/* paint background */
1908 				l = drawimage(client, a+47);
1909 				drawpoint(&q, a+51);
1910 				r.min.x = p.x;
1911 				r.min.y = p.y-font->ascent;
1912 				r.max.x = p.x;
1913 				r.max.y = r.min.y+Dy(font->image->r);
1914 				j = ni;
1915 				while(--j >= 0){
1916 					ci = BGSHORT(u);
1917 					if(ci<0 || ci>=font->nfchar){
1918 						dst->clipr = clipr;
1919 						error(Eindex);
1920 					}
1921 					r.max.x += font->fchar[ci].width;
1922 					u += 2;
1923 				}
1924 				memdraw(dst, r, l, q, memopaque, ZP, op);
1925 				u -= 2*ni;
1926 			}
1927 			q = p;
1928 			while(--ni >= 0){
1929 				ci = BGSHORT(u);
1930 				if(ci<0 || ci>=font->nfchar){
1931 					dst->clipr = clipr;
1932 					error(Eindex);
1933 				}
1934 				q = drawchar(dst, l, q, src, &sp, font, ci, op);
1935 				u += 2;
1936 			}
1937 			dst->clipr = clipr;
1938 			p.y -= font->ascent;
1939 			dstflush(dstid, dst, Rect(p.x, p.y, q.x, p.y+Dy(font->image->r)));
1940 			continue;
1941 
1942 		/* use public screen: 'S' id[4] chan[4] */
1943 		case 'S':
1944 			printmesg(fmt="Ll", a, 0);
1945 			m = 1+4+4;
1946 			if(n < m)
1947 				error(Eshortdraw);
1948 			dstid = BGLONG(a+1);
1949 			if(dstid == 0)
1950 				error(Ebadarg);
1951 			dscrn = drawlookupdscreen(dstid);
1952 			if(dscrn==0 || (dscrn->public==0 && dscrn->owner!=client))
1953 				error(Enodrawscreen);
1954 			if(dscrn->screen->image->chan != BGLONG(a+5))
1955 				error("inconsistent chan");
1956 			if(drawinstallscreen(client, dscrn, 0, 0, 0, 0) == 0)
1957 				error(Edrawmem);
1958 			continue;
1959 
1960 		/* top or bottom windows: 't' top[1] nw[2] n*id[4] */
1961 		case 't':
1962 			printmesg(fmt="bsL", a, 0);
1963 			m = 1+1+2;
1964 			if(n < m)
1965 				error(Eshortdraw);
1966 			nw = BGSHORT(a+2);
1967 			if(nw < 0)
1968 				error(Ebadarg);
1969 			if(nw == 0)
1970 				continue;
1971 			m += nw*4;
1972 			if(n < m)
1973 				error(Eshortdraw);
1974 			lp = malloc(nw*sizeof(Memimage*));
1975 			if(lp == 0)
1976 				error(Enomem);
1977 			if(waserror()){
1978 				free(lp);
1979 				nexterror();
1980 			}
1981 			for(j=0; j<nw; j++)
1982 				lp[j] = drawimage(client, a+1+1+2+j*4);
1983 			if(lp[0]->layer == 0)
1984 				error("images are not windows");
1985 			for(j=1; j<nw; j++)
1986 				if(lp[j]->layer->screen != lp[0]->layer->screen)
1987 					error("images not on same screen");
1988 			if(a[1])
1989 				memltofrontn(lp, nw);
1990 			else
1991 				memltorearn(lp, nw);
1992 			if(lp[0]->layer->screen->image->data == screenimage->data)
1993 				for(j=0; j<nw; j++)
1994 					addflush(lp[j]->layer->screenr);
1995 			ll = drawlookup(client, BGLONG(a+1+1+2), 1);
1996 			drawrefreshscreen(ll, client);
1997 			poperror();
1998 			free(lp);
1999 			continue;
2000 
2001 		/* visible: 'v' */
2002 		case 'v':
2003 			printmesg(fmt="", a, 0);
2004 			m = 1;
2005 			drawflush();
2006 			continue;
2007 
2008 		/* write: 'y' id[4] R[4*4] data[x*1] */
2009 		/* write from compressed data: 'Y' id[4] R[4*4] data[x*1] */
2010 		case 'y':
2011 		case 'Y':
2012 			printmesg(fmt="LR", a, 0);
2013 		//	iprint("load %c\n", *a);
2014 			m = 1+4+4*4;
2015 			if(n < m)
2016 				error(Eshortdraw);
2017 			dstid = BGLONG(a+1);
2018 			dst = drawimage(client, a+1);
2019 			drawrectangle(&r, a+5);
2020 			if(!rectinrect(r, dst->r))
2021 				error(Ewriteoutside);
2022 			y = memload(dst, r, a+m, n-m, *a=='Y');
2023 			if(y < 0)
2024 				error("bad writeimage call");
2025 			dstflush(dstid, dst, r);
2026 			m += y;
2027 			continue;
2028 		}
2029 	}
2030 	poperror();
2031 }
2032 
2033 Dev drawdevtab = {
2034 	'i',
2035 	"draw",
2036 
2037 	devreset,
2038 	devinit,
2039 	devshutdown,
2040 	drawattach,
2041 	drawwalk,
2042 	drawstat,
2043 	drawopen,
2044 	devcreate,
2045 	drawclose,
2046 	drawread,
2047 	devbread,
2048 	drawwrite,
2049 	devbwrite,
2050 	devremove,
2051 	devwstat,
2052 };
2053 
2054 /*
2055  * On 8 bit displays, load the default color map
2056  */
2057 void
drawcmap(void)2058 drawcmap(void)
2059 {
2060 	int r, g, b, cr, cg, cb, v;
2061 	int num, den;
2062 	int i, j;
2063 
2064 	drawactive(1);	/* to restore map from backup */
2065 	for(r=0,i=0; r!=4; r++)
2066 	    for(v=0; v!=4; v++,i+=16){
2067 		for(g=0,j=v-r; g!=4; g++)
2068 		    for(b=0;b!=4;b++,j++){
2069 			den = r;
2070 			if(g > den)
2071 				den = g;
2072 			if(b > den)
2073 				den = b;
2074 			if(den == 0)	/* divide check -- pick grey shades */
2075 				cr = cg = cb = v*17;
2076 			else{
2077 				num = 17*(4*den+v);
2078 				cr = r*num/den;
2079 				cg = g*num/den;
2080 				cb = b*num/den;
2081 			}
2082 			setcolor(i+(j&15),
2083 				cr*0x01010101, cg*0x01010101, cb*0x01010101);
2084 		    }
2085 	}
2086 }
2087 
2088 void
drawblankscreen(int blank)2089 drawblankscreen(int blank)
2090 {
2091 	int i, nc;
2092 	ulong *p;
2093 
2094 	if(blank == sdraw.blanked)
2095 		return;
2096 	if(!canqlock(&sdraw.lk))
2097 		return;
2098 	if(!initscreenimage()){
2099 		qunlock(&sdraw.lk);
2100 		return;
2101 	}
2102 	p = sdraw.savemap;
2103 	nc = screenimage->depth > 8 ? 256 : 1<<screenimage->depth;
2104 
2105 	/*
2106 	 * blankscreen uses the hardware to blank the screen
2107 	 * when possible.  to help in cases when it is not possible,
2108 	 * we set the color map to be all black.
2109 	 */
2110 	if(blank == 0){	/* turn screen on */
2111 		for(i=0; i<nc; i++, p+=3)
2112 			setcolor(i, p[0], p[1], p[2]);
2113 	//	blankscreen(0);
2114 	}else{	/* turn screen off */
2115 	//	blankscreen(1);
2116 		for(i=0; i<nc; i++, p+=3){
2117 			getcolor(i, &p[0], &p[1], &p[2]);
2118 			setcolor(i, 0, 0, 0);
2119 		}
2120 	}
2121 	sdraw.blanked = blank;
2122 	qunlock(&sdraw.lk);
2123 }
2124 
2125 /*
2126  * record activity on screen, changing blanking as appropriate
2127  */
2128 void
drawactive(int active)2129 drawactive(int active)
2130 {
2131 /*
2132 	if(active){
2133 		drawblankscreen(0);
2134 		sdraw.blanktime = MACHP(0)->ticks;
2135 	}else{
2136 		if(blanktime && sdraw.blanktime && TK2SEC(MACHP(0)->ticks - sdraw.blanktime)/60 >= blanktime)
2137 			drawblankscreen(1);
2138 	}
2139 */
2140 }
2141 
2142 int
drawidletime(void)2143 drawidletime(void)
2144 {
2145 	return 0;
2146 /*	return TK2SEC(MACHP(0)->ticks - sdraw.blanktime)/60; */
2147 }
2148 
2149