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