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