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