Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

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