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