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