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