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