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