Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
185
drawlock(void)
186
{
187
	qlock(&sdraw);
188
}
189
 
190
void
191
drawunlock(void)
192
{
193
	qunlock(&sdraw);
194
}
195
 
196
int
197
candrawlock(void)
198
{
199
	return canqlock(&sdraw);
200
}
201
 
202
static int
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
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
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
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
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
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
426
drawflush(void)
427
{
428
	flushmemscreen(flushrect);
429
	flushrect = Rect(10000, 10000, -10000, -10000);
430
}
431
 
432
static
433
int
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*
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
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*
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*
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*
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*
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*
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
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
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
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
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
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
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*
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
784
drawclientop(Client *cl)
785
{
786
	int op;
787
 
788
	op = cl->op;
789
	cl->op = SoverD;
790
	return op;
791
}
792
 
793
int
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*
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*
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*
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
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
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
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
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
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*
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*
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
940
drawstat(Chan *c, uchar *db, int n)
941
{
942
	return devstat(c, db, n, 0, 0, drawgen);
943
}
944
 
945
static Chan*
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
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
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
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
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*
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
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
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
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
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
2079
drawactive(int active)
2080
{
2081
	if(active){
2082
		drawblankscreen(0);
2083
		sdraw.blanktime = 0;
2084
	}else
2085
		sdraw.blanktime++;
2086
}