]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/port/devdraw.c
remove non standard COM3 (eia2) serial port from i8250 uart.
[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         i = name-sdraw.name;
611         memmove(name, name+1, (sdraw.nname-(i+1))*sizeof(DName));
612         sdraw.nname--;
613 }
614
615 void
616 drawfreedscreen(DScreen *this)
617 {
618         DScreen *ds, *next;
619
620         this->ref--;
621         if(this->ref < 0)
622                 print("negative ref in drawfreedscreen\n");
623         if(this->ref > 0)
624                 return;
625         ds = dscreen;
626         if(ds == this){
627                 dscreen = this->next;
628                 goto Found;
629         }
630         while(next = ds->next){ /* assign = */
631                 if(next == this){
632                         ds->next = this->next;
633                         goto Found;
634                 }
635                 ds = next;
636         }
637         error(Enodrawimage);
638
639     Found:
640         if(this->dimage)
641                 drawfreedimage(this->dimage);
642         if(this->dfill)
643                 drawfreedimage(this->dfill);
644         free(this->screen);
645         free(this);
646 }
647
648 void
649 drawfreedimage(DImage *dimage)
650 {
651         int i;
652         Memimage *l;
653         DScreen *ds;
654
655         dimage->ref--;
656         if(dimage->ref < 0)
657                 print("negative ref in drawfreedimage\n");
658         if(dimage->ref > 0)
659                 return;
660
661         /* any names? */
662         for(i=0; i<sdraw.nname; )
663                 if(sdraw.name[i].dimage == dimage)
664                         drawdelname(sdraw.name+i);
665                 else
666                         i++;
667         if(dimage->fromname){   /* acquired by name; owned by someone else*/
668                 drawfreedimage(dimage->fromname);
669                 goto Return;
670         }
671         ds = dimage->dscreen;
672         if(ds){
673                 l = dimage->image;
674                 if(screenimage && l->data == screenimage->data)
675                         addflush(l->layer->screenr);
676                 if(l->layer->refreshfn == drawrefresh)  /* else true owner will clean up */
677                         free(l->layer->refreshptr);
678                 l->layer->refreshptr = nil;
679                 if(drawgoodname(dimage))
680                         memldelete(l);
681                 else
682                         memlfree(l);
683                 drawfreedscreen(ds);
684         }else
685                 freememimage(dimage->image);
686     Return:
687         free(dimage->fchar);
688         free(dimage);
689 }
690
691 void
692 drawuninstallscreen(Client *client, CScreen *this)
693 {
694         CScreen *cs, *next;
695
696         cs = client->cscreen;
697         if(cs == this){
698                 client->cscreen = this->next;
699                 drawfreedscreen(this->dscreen);
700                 free(this);
701                 return;
702         }
703         while(next = cs->next){ /* assign = */
704                 if(next == this){
705                         cs->next = this->next;
706                         drawfreedscreen(this->dscreen);
707                         free(this);
708                         return;
709                 }
710                 cs = next;
711         }
712 }
713
714 void
715 drawuninstall(Client *client, int id)
716 {
717         DImage *d, *next;
718
719         d = client->dimage[id&HASHMASK];
720         if(d == 0)
721                 error(Enodrawimage);
722         if(d->id == id){
723                 client->dimage[id&HASHMASK] = d->next;
724                 drawfreedimage(d);
725                 return;
726         }
727         while(next = d->next){  /* assign = */
728                 if(next->id == id){
729                         d->next = next->next;
730                         drawfreedimage(next);
731                         return;
732                 }
733                 d = next;
734         }
735         error(Enodrawimage);
736 }
737
738 void
739 drawaddname(Client *client, DImage *di, int n, char *str)
740 {
741         DName *name, *ename, *new, *t;
742
743         name = sdraw.name;
744         ename = &name[sdraw.nname];
745         for(; name<ename; name++)
746                 if(drawcmp(name->name, str, n) == 0)
747                         error(Enameused);
748         t = smalloc((sdraw.nname+1)*sizeof(DName));
749         memmove(t, sdraw.name, sdraw.nname*sizeof(DName));
750         free(sdraw.name);
751         sdraw.name = t;
752         new = &sdraw.name[sdraw.nname++];
753         new->name = smalloc(n+1);
754         memmove(new->name, str, n);
755         new->name[n] = 0;
756         new->dimage = di;
757         new->client = client;
758         new->vers = ++sdraw.vers;
759 }
760
761 Client*
762 drawnewclient(void)
763 {
764         Client *cl, **cp;
765         int i;
766
767         for(i=0; i<sdraw.nclient; i++){
768                 cl = sdraw.client[i];
769                 if(cl == 0)
770                         break;
771         }
772         if(i == sdraw.nclient){
773                 cp = malloc((sdraw.nclient+1)*sizeof(Client*));
774                 if(cp == 0)
775                         return 0;
776                 memmove(cp, sdraw.client, sdraw.nclient*sizeof(Client*));
777                 free(sdraw.client);
778                 sdraw.client = cp;
779                 sdraw.nclient++;
780                 cp[i] = 0;
781         }
782         cl = malloc(sizeof(Client));
783         if(cl == 0)
784                 return 0;
785         memset(cl, 0, sizeof(Client));
786         cl->slot = i;
787         cl->clientid = ++sdraw.clientid;
788         cl->op = SoverD;
789         sdraw.client[i] = cl;
790         return cl;
791 }
792
793 static int
794 drawclientop(Client *cl)
795 {
796         int op;
797
798         op = cl->op;
799         cl->op = SoverD;
800         return op;
801 }
802
803 int
804 drawhasclients(void)
805 {
806         /*
807          * if draw has ever been used, we can't resize the frame buffer,
808          * even if all clients have exited (nclients is cumulative); it's too
809          * hard to make work.
810          */
811         return sdraw.nclient != 0;
812 }
813
814 Client*
815 drawclientofpath(ulong path)
816 {
817         Client *cl;
818         int slot;
819
820         slot = CLIENTPATH(path);
821         if(slot == 0)
822                 return nil;
823         cl = sdraw.client[slot-1];
824         if(cl==0 || cl->clientid==0)
825                 return nil;
826         return cl;
827 }
828
829
830 Client*
831 drawclient(Chan *c)
832 {
833         Client *client;
834
835         client = drawclientofpath(c->qid.path);
836         if(client == nil)
837                 error(Enoclient);
838         return client;
839 }
840
841 Memimage*
842 drawimage(Client *client, uchar *a)
843 {
844         DImage *d;
845
846         d = drawlookup(client, BGLONG(a), 1);
847         if(d == nil)
848                 error(Enodrawimage);
849         return d->image;
850 }
851
852 void
853 drawrectangle(Rectangle *r, uchar *a)
854 {
855         r->min.x = BGLONG(a+0*4);
856         r->min.y = BGLONG(a+1*4);
857         r->max.x = BGLONG(a+2*4);
858         r->max.y = BGLONG(a+3*4);
859 }
860
861 void
862 drawpoint(Point *p, uchar *a)
863 {
864         p->x = BGLONG(a+0*4);
865         p->y = BGLONG(a+1*4);
866 }
867
868 Point
869 drawchar(Memimage *dst, Memimage *rdst, Point p, Memimage *src, Point *sp, DImage *font, int index, int op)
870 {
871         FChar *fc;
872         Rectangle r;
873         Point sp1;
874         static Memimage *tmp;
875
876         fc = &font->fchar[index];
877         r.min.x = p.x+fc->left;
878         r.min.y = p.y-(font->ascent-fc->miny);
879         r.max.x = r.min.x+(fc->maxx-fc->minx);
880         r.max.y = r.min.y+(fc->maxy-fc->miny);
881         sp1.x = sp->x+fc->left;
882         sp1.y = sp->y+fc->miny;
883
884         /*
885          * If we're drawing greyscale fonts onto a VGA screen,
886          * it's very costly to read the screen memory to do the
887          * alpha blending inside memdraw.  If this is really a stringbg,
888          * then rdst is the bg image (in main memory) which we can
889          * refer to for the underlying dst pixels instead of reading dst
890          * directly.
891          */
892         if(ishwimage(dst) && !ishwimage(rdst) && font->image->depth > 1){
893                 if(tmp == nil || tmp->chan != dst->chan || Dx(tmp->r) < Dx(r) || Dy(tmp->r) < Dy(r)){
894                         if(tmp)
895                                 freememimage(tmp);
896                         tmp = allocmemimage(Rect(0,0,Dx(r),Dy(r)), dst->chan);
897                         if(tmp == nil)
898                                 goto fallback;
899                 }
900                 memdraw(tmp, Rect(0,0,Dx(r),Dy(r)), rdst, r.min, memopaque, ZP, S);
901                 memdraw(tmp, Rect(0,0,Dx(r),Dy(r)), src, sp1, font->image, Pt(fc->minx, fc->miny), op);
902                 memdraw(dst, r, tmp, ZP, memopaque, ZP, S);
903         }else{
904         fallback:
905                 memdraw(dst, r, src, sp1, font->image, Pt(fc->minx, fc->miny), op);
906         }
907
908         p.x += fc->width;
909         sp->x += fc->width;
910         return p;
911 }
912
913 static DImage*
914 makescreenimage(void)
915 {
916         int width, depth;
917         ulong chan;
918         DImage *di;
919         Memdata *md;
920         Memimage *i;
921         Rectangle r;
922         uchar *data;
923
924         if((data = attachscreen(&r, &chan, &depth, &width, &sdraw.softscreen)) == nil)
925                 return nil;
926         if(sdraw.softscreen == 0xa110c){
927                 /* hack: softscreen is memimage. */
928                 md = *((Memdata**)(data - sizeof(ulong) - sizeof(Memdata*)));
929
930                 assert(md->bdata == data);
931                 assert(md->ref > 1);
932                 assert(md->allocd);
933
934                 if((i = allocmemimaged(r, chan, md)) == nil){
935                         md->ref--;
936                         return nil;
937                 }
938         }else{
939                 if((md = malloc(sizeof *md)) == nil)
940                         return nil;
941                 md->allocd = 1;
942                 md->base = nil;
943                 md->bdata = data;
944                 md->ref = 1;
945                 if((i = allocmemimaged(r, chan, md)) == nil){
946                         free(md);
947                         return nil;
948                 }
949         }
950         i->width = width;
951         i->clipr = r;
952         di = allocdimage(i);
953         if(di == nil){
954                 freememimage(i);        /* frees md */
955                 return nil;
956         }
957         if(!waserror()){
958                 snprint(screenname, sizeof screenname, "noborder.screen.%d", ++screennameid);
959                 drawaddname(nil, di, strlen(screenname), screenname);
960                 poperror();
961         }
962         return di;
963 }
964
965 static int
966 initscreenimage(void)
967 {
968         if(screenimage != nil)
969                 return 1;
970
971         screendimage = makescreenimage();
972         if(screendimage == nil)
973                 return 0;
974         screenimage = screendimage->image;
975 // iprint("initscreenimage %p %p\n", screendimage, screenimage);
976         mouseresize();
977         return 1;
978 }
979
980 void
981 deletescreenimage(void)
982 {
983         dlock();
984         if(screenimage){
985                 /* will be freed via screendimage; disable */
986                 screenimage->clipr = ZR;
987                 screenimage = nil;
988         }
989         if(screendimage){
990                 drawfreedimage(screendimage);
991                 screendimage = nil;
992         }
993         dunlock();
994 }
995
996 void
997 resetscreenimage(void)
998 {
999         dlock();
1000         initscreenimage();
1001         dunlock();
1002 }
1003
1004 static Chan*
1005 drawattach(char *spec)
1006 {
1007         dlock();
1008         if(!initscreenimage()){
1009                 dunlock();
1010                 error("no frame buffer");
1011         }
1012         dunlock();
1013         return devattach('i', spec);
1014 }
1015
1016 static Walkqid*
1017 drawwalk(Chan *c, Chan *nc, char **name, int nname)
1018 {
1019         if(screenimage == nil)
1020                 error("no frame buffer");
1021         return devwalk(c, nc, name, nname, 0, 0, drawgen);
1022 }
1023
1024 static int
1025 drawstat(Chan *c, uchar *db, int n)
1026 {
1027         return devstat(c, db, n, 0, 0, drawgen);
1028 }
1029
1030 static Chan*
1031 drawopen(Chan *c, int omode)
1032 {
1033         Client *cl;
1034         DName *dn;
1035         DImage *di;
1036
1037         if(c->qid.type & QTDIR){
1038                 c = devopen(c, omode, 0, 0, drawgen);
1039                 c->iounit = IOUNIT;
1040         }
1041
1042         dlock();
1043         if(waserror()){
1044                 dunlock();
1045                 nexterror();
1046         }
1047
1048         if(QID(c->qid) == Qnew){
1049                 cl = drawnewclient();
1050                 if(cl == 0)
1051                         error(Enodev);
1052                 c->qid.path = Qctl|((cl->slot+1)<<QSHIFT);
1053         }
1054
1055         switch(QID(c->qid)){
1056         case Qwinname:
1057                 break;
1058
1059         case Qnew:
1060                 break;
1061
1062         case Qctl:
1063                 cl = drawclient(c);
1064                 if(cl->busy)
1065                         error(Einuse);
1066                 cl->busy = 1;
1067                 flushrect = Rect(10000, 10000, -10000, -10000);
1068                 dn = drawlookupname(strlen(screenname), screenname);
1069                 if(dn == 0)
1070                         error("draw: cannot happen 2");
1071                 if(drawinstall(cl, 0, dn->dimage->image, 0) == 0)
1072                         error(Edrawmem);
1073                 di = drawlookup(cl, 0, 0);
1074                 if(di == 0)
1075                         error("draw: cannot happen 1");
1076                 di->vers = dn->vers;
1077                 di->name = smalloc(strlen(screenname)+1);
1078                 strcpy(di->name, screenname);
1079                 di->fromname = dn->dimage;
1080                 di->fromname->ref++;
1081                 incref(&cl->r);
1082                 break;
1083
1084         case Qcolormap:
1085         case Qdata:
1086         case Qrefresh:
1087                 cl = drawclient(c);
1088                 incref(&cl->r);
1089                 break;
1090         }
1091         dunlock();
1092         poperror();
1093         c->mode = openmode(omode);
1094         c->flag |= COPEN;
1095         c->offset = 0;
1096         c->iounit = IOUNIT;
1097         return c;
1098 }
1099
1100 static void
1101 drawclose(Chan *c)
1102 {
1103         int i;
1104         DImage *d, **dp;
1105         Client *cl;
1106         Refresh *r;
1107
1108         if(QID(c->qid) < Qcolormap)     /* Qtopdir, Qnew, Q3rd, Q2nd have no client */
1109                 return;
1110         dlock();
1111         if(waserror()){
1112                 dunlock();
1113                 nexterror();
1114         }
1115
1116         cl = drawclient(c);
1117         if(QID(c->qid) == Qctl)
1118                 cl->busy = 0;
1119         if((c->flag&COPEN) && (decref(&cl->r)==0)){
1120                 while(r = cl->refresh){ /* assign = */
1121                         cl->refresh = r->next;
1122                         free(r);
1123                 }
1124                 /* free names */
1125                 for(i=0; i<sdraw.nname; )
1126                         if(sdraw.name[i].client == cl)
1127                                 drawdelname(sdraw.name+i);
1128                         else
1129                                 i++;
1130                 while(cl->cscreen)
1131                         drawuninstallscreen(cl, cl->cscreen);
1132                 /* all screens are freed, so now we can free images */
1133                 dp = cl->dimage;
1134                 for(i=0; i<NHASH; i++){
1135                         while((d = *dp) != nil){
1136                                 *dp = d->next;
1137                                 drawfreedimage(d);
1138                         }
1139                         dp++;
1140                 }
1141                 sdraw.client[cl->slot] = 0;
1142                 drawflush();    /* to erase visible, now dead windows */
1143                 free(cl);
1144         }
1145         dunlock();
1146         poperror();
1147 }
1148
1149 long
1150 drawread(Chan *c, void *a, long n, vlong off)
1151 {
1152         int index, m;
1153         ulong red, green, blue;
1154         Client *cl;
1155         uchar *p;
1156         Refresh *r;
1157         DImage *di;
1158         Memimage *i;
1159         ulong offset = off;
1160         char buf[16];
1161
1162         if(c->qid.type & QTDIR)
1163                 return devdirread(c, a, n, 0, 0, drawgen);
1164         if(QID(c->qid) == Qwinname)
1165                 return readstr(off, a, n, screenname);
1166
1167         cl = drawclient(c);
1168         dlock();
1169         if(waserror()){
1170                 dunlock();
1171                 nexterror();
1172         }
1173         switch(QID(c->qid)){
1174         case Qctl:
1175                 if(n < 12*12)
1176                         error(Eshortread);
1177                 if(cl->infoid < 0)
1178                         error(Enodrawimage);
1179                 if(cl->infoid == 0){
1180                         i = screenimage;
1181                         if(i == nil)
1182                                 error(Enodrawimage);
1183                 }else{
1184                         di = drawlookup(cl, cl->infoid, 1);
1185                         if(di == nil)
1186                                 error(Enodrawimage);
1187                         i = di->image;
1188                 }
1189                 n = sprint(a, "%11d %11d %11s %11d %11d %11d %11d %11d %11d %11d %11d %11d ",
1190                         cl->clientid, cl->infoid, chantostr(buf, i->chan), (i->flags&Frepl)==Frepl,
1191                         i->r.min.x, i->r.min.y, i->r.max.x, i->r.max.y,
1192                         i->clipr.min.x, i->clipr.min.y, i->clipr.max.x, i->clipr.max.y);
1193                 cl->infoid = -1;
1194                 break;
1195
1196         case Qcolormap:
1197                 drawactive(1);  /* to restore map from backup */
1198                 p = malloc(4*12*256+1);
1199                 if(p == 0)
1200                         error(Enomem);
1201                 m = 0;
1202                 for(index = 0; index < 256; index++){
1203                         getcolor(index, &red, &green, &blue);
1204                         m += sprint((char*)p+m, "%11d %11lud %11lud %11lud\n", index, red>>24, green>>24, blue>>24);
1205                 }
1206                 n = readstr(offset, a, n, (char*)p);
1207                 free(p);
1208                 break;
1209
1210         case Qdata:
1211                 if(cl->readdata == nil)
1212                         error("no draw data");
1213                 if(n < cl->nreaddata)
1214                         error(Eshortread);
1215                 n = cl->nreaddata;
1216                 memmove(a, cl->readdata, cl->nreaddata);
1217                 free(cl->readdata);
1218                 cl->readdata = nil;
1219                 break;
1220
1221         case Qrefresh:
1222                 if(n < 5*4)
1223                         error(Ebadarg);
1224                 for(;;){
1225                         if(cl->refreshme || cl->refresh)
1226                                 break;
1227                         dunlock();
1228                         if(waserror()){
1229                                 dlock();
1230                                 nexterror();
1231                         }
1232                         eqlock(&cl->refq);
1233                         if(waserror()){
1234                                 qunlock(&cl->refq);
1235                                 nexterror();
1236                         }
1237                         sleep(&cl->refrend, drawrefactive, cl);
1238                         poperror();
1239                         qunlock(&cl->refq);
1240                         poperror();
1241                         dlock();
1242                 }
1243                 p = a;
1244                 while(cl->refresh && n>=5*4){
1245                         r = cl->refresh;
1246                         BPLONG(p+0*4, r->dimage->id);
1247                         BPLONG(p+1*4, r->r.min.x);
1248                         BPLONG(p+2*4, r->r.min.y);
1249                         BPLONG(p+3*4, r->r.max.x);
1250                         BPLONG(p+4*4, r->r.max.y);
1251                         cl->refresh = r->next;
1252                         free(r);
1253                         p += 5*4;
1254                         n -= 5*4;
1255                 }
1256                 cl->refreshme = 0;
1257                 n = p-(uchar*)a;
1258                 break;
1259         }
1260         dunlock();
1261         poperror();
1262         return n;
1263 }
1264
1265 void
1266 drawwakeall(void)
1267 {
1268         Client *cl;
1269         int i;
1270
1271         for(i=0; i<sdraw.nclient; i++){
1272                 cl = sdraw.client[i];
1273                 if(cl && (cl->refreshme || cl->refresh))
1274                         wakeup(&cl->refrend);
1275         }
1276 }
1277
1278 static long
1279 drawwrite(Chan *c, void *a, long n, vlong)
1280 {
1281         char buf[128], *fields[4], *q;
1282         Client *cl;
1283         int i, m, red, green, blue, x;
1284
1285         if(c->qid.type & QTDIR)
1286                 error(Eisdir);
1287         cl = drawclient(c);
1288         dlock();
1289         if(waserror()){
1290                 drawwakeall();
1291                 dunlock();
1292                 nexterror();
1293         }
1294         switch(QID(c->qid)){
1295         case Qctl:
1296                 if(n != 4)
1297                         error("unknown draw control request");
1298                 cl->infoid = BGLONG((uchar*)a);
1299                 break;
1300
1301         case Qcolormap:
1302                 drawactive(1);  /* to restore map from backup */
1303                 m = n;
1304                 n = 0;
1305                 while(m > 0){
1306                         x = m;
1307                         if(x > sizeof(buf)-1)
1308                                 x = sizeof(buf)-1;
1309                         q = memccpy(buf, a, '\n', x);
1310                         if(q == 0)
1311                                 break;
1312                         i = q-buf;
1313                         n += i;
1314                         a = (char*)a + i;
1315                         m -= i;
1316                         *q = 0;
1317                         if(tokenize(buf, fields, nelem(fields)) != 4)
1318                                 error(Ebadarg);
1319                         i = strtoul(fields[0], 0, 0);
1320                         red = strtoul(fields[1], 0, 0);
1321                         green = strtoul(fields[2], 0, 0);
1322                         blue = strtoul(fields[3], &q, 0);
1323                         if(fields[3] == q)
1324                                 error(Ebadarg);
1325                         if(red>255 || green>255 || blue>255 || i<0 || i>255)
1326                                 error(Ebadarg);
1327                         red |= red<<8;
1328                         red |= red<<16;
1329                         green |= green<<8;
1330                         green |= green<<16;
1331                         blue |= blue<<8;
1332                         blue |= blue<<16;
1333                         setcolor(i, red, green, blue);
1334                 }
1335                 break;
1336
1337         case Qdata:
1338                 drawmesg(cl, a, n);
1339                 drawwakeall();
1340                 break;
1341
1342         default:
1343                 error(Ebadusefd);
1344         }
1345         dunlock();
1346         poperror();
1347         return n;
1348 }
1349
1350 uchar*
1351 drawcoord(uchar *p, uchar *maxp, int oldx, int *newx)
1352 {
1353         int b, x;
1354
1355         if(p >= maxp)
1356                 error(Eshortdraw);
1357         b = *p++;
1358         x = b & 0x7F;
1359         if(b & 0x80){
1360                 if(p+1 >= maxp)
1361                         error(Eshortdraw);
1362                 x |= *p++ << 7;
1363                 x |= *p++ << 15;
1364                 if(x & (1<<22))
1365                         x |= ~0<<23;
1366         }else{
1367                 if(b & 0x40)
1368                         x |= ~0<<7;
1369                 x += oldx;
1370         }
1371         *newx = x;
1372         return p;
1373 }
1374
1375 static void
1376 printmesg(char *fmt, uchar *a, int plsprnt)
1377 {
1378         char buf[256];
1379         char *p, *q;
1380         int s;
1381
1382         if(1|| plsprnt==0){
1383                 SET(s,q,p);
1384                 USED(fmt, a, buf, p, q, s);
1385                 return;
1386         }
1387         q = buf;
1388         *q++ = *a++;
1389         for(p=fmt; *p; p++){
1390                 switch(*p){
1391                 case 'l':
1392                         q += sprint(q, " %ld", (long)BGLONG(a));
1393                         a += 4;
1394                         break;
1395                 case 'L':
1396                         q += sprint(q, " %.8lux", (ulong)BGLONG(a));
1397                         a += 4;
1398                         break;
1399                 case 'R':
1400                         q += sprint(q, " [%d %d %d %d]", BGLONG(a), BGLONG(a+4), BGLONG(a+8), BGLONG(a+12));
1401                         a += 16;
1402                         break;
1403                 case 'P':
1404                         q += sprint(q, " [%d %d]", BGLONG(a), BGLONG(a+4));
1405                         a += 8;
1406                         break;
1407                 case 'b':
1408                         q += sprint(q, " %d", *a++);
1409                         break;
1410                 case 's':
1411                         q += sprint(q, " %d", BGSHORT(a));
1412                         a += 2;
1413                         break;
1414                 case 'S':
1415                         q += sprint(q, " %.4ux", BGSHORT(a));
1416                         a += 2;
1417                         break;
1418                 }
1419         }
1420         *q++ = '\n';
1421         *q = 0;
1422         iprint("%.*s", (int)(q-buf), buf);
1423 }
1424
1425 void
1426 drawmesg(Client *client, void *av, int n)
1427 {
1428         int c, repl, m, y, dstid, scrnid, ni, ci, j, nw, e0, e1, op, ox, oy, oesize, esize, doflush;
1429         uchar *u, *a, refresh;
1430         char *fmt;
1431         ulong value, chan;
1432         Rectangle r, clipr;
1433         Point p, q, *pp, sp;
1434         Memimage *i, *bg, *dst, *src, *mask;
1435         Memimage *l, **lp;
1436         Memscreen *scrn;
1437         DImage *font, *ll, *di, *ddst, *dsrc;
1438         DName *dn;
1439         DScreen *dscrn;
1440         FChar *fc;
1441         Refx *refx;
1442         CScreen *cs;
1443         Refreshfn reffn;
1444
1445         a = av;
1446         m = 0;
1447         fmt = nil;
1448         if(waserror()){
1449                 if(fmt) printmesg(fmt, a, 1);
1450         /*      iprint("error: %s\n", up->errstr);      */
1451                 nexterror();
1452         }
1453         while((n-=m) > 0){
1454                 USED(fmt);
1455                 a += m;
1456                 switch(*a){
1457                 default:
1458                         error("bad draw command");
1459                 /* new allocate: 'b' id[4] screenid[4] refresh[1] chan[4] repl[1] R[4*4] clipR[4*4] rrggbbaa[4] */
1460                 case 'b':
1461                         printmesg(fmt="LLbLbRRL", a, 0);
1462                         m = 1+4+4+1+4+1+4*4+4*4+4;
1463                         if(n < m)
1464                                 error(Eshortdraw);
1465                         dstid = BGLONG(a+1);
1466                         scrnid = BGSHORT(a+5);
1467                         refresh = a[9];
1468                         chan = BGLONG(a+10);
1469                         repl = a[14];
1470                         drawrectangle(&r, a+15);
1471                         drawrectangle(&clipr, a+31);
1472                         value = BGLONG(a+47);
1473                         if(drawlookup(client, dstid, 0))
1474                                 error(Eimageexists);
1475                         if(scrnid){
1476                                 dscrn = drawlookupscreen(client, scrnid, &cs);
1477                                 scrn = dscrn->screen;
1478                                 if(repl || chan!=scrn->image->chan)
1479                                         error("image parameters incompatible with screen");
1480                                 reffn = nil;
1481                                 switch(refresh){
1482                                 case Refbackup:
1483                                         break;
1484                                 case Refnone:
1485                                         reffn = memlnorefresh;
1486                                         break;
1487                                 case Refmesg:
1488                                         reffn = drawrefresh;
1489                                         break;
1490                                 default:
1491                                         error("unknown refresh method");
1492                                 }
1493                                 l = memlalloc(scrn, r, reffn, 0, value);
1494                                 if(l == 0)
1495                                         error(Edrawmem);
1496                                 addflush(l->layer->screenr);
1497                                 l->clipr = clipr;
1498                                 rectclip(&l->clipr, r);
1499                                 if(drawinstall(client, dstid, l, dscrn) == 0){
1500                                         memldelete(l);
1501                                         error(Edrawmem);
1502                                 }
1503                                 dscrn->ref++;
1504                                 if(reffn){
1505                                         refx = nil;
1506                                         if(reffn == drawrefresh){
1507                                                 refx = malloc(sizeof(Refx));
1508                                                 if(refx == 0){
1509                                                         drawuninstall(client, dstid);
1510                                                         error(Edrawmem);
1511                                                 }
1512                                                 refx->client = client;
1513                                                 refx->dimage = drawlookup(client, dstid, 1);
1514                                         }
1515                                         memlsetrefresh(l, reffn, refx);
1516                                 }
1517                                 continue;
1518                         }
1519                         i = allocmemimage(r, chan);
1520                         if(i == 0)
1521                                 error(Edrawmem);
1522                         if(repl)
1523                                 i->flags |= Frepl;
1524                         i->clipr = clipr;
1525                         if(!repl)
1526                                 rectclip(&i->clipr, r);
1527                         if(drawinstall(client, dstid, i, 0) == 0){
1528                                 freememimage(i);
1529                                 error(Edrawmem);
1530                         }
1531                         memfillcolor(i, value);
1532                         continue;
1533
1534                 /* allocate screen: 'A' id[4] imageid[4] fillid[4] public[1] */
1535                 case 'A':
1536                         printmesg(fmt="LLLb", a, 1);
1537                         m = 1+4+4+4+1;
1538                         if(n < m)
1539                                 error(Eshortdraw);
1540                         dstid = BGLONG(a+1);
1541                         if(dstid == 0)
1542                                 error(Ebadarg);
1543                         if(drawlookupdscreen(dstid))
1544                                 error(Escreenexists);
1545                         ddst = drawlookup(client, BGLONG(a+5), 1);
1546                         dsrc = drawlookup(client, BGLONG(a+9), 1);
1547                         if(ddst==0 || dsrc==0)
1548                                 error(Enodrawimage);
1549                         if(drawinstallscreen(client, 0, dstid, ddst, dsrc, a[13]) == 0)
1550                                 error(Edrawmem);
1551                         continue;
1552
1553                 /* set repl and clip: 'c' dstid[4] repl[1] clipR[4*4] */
1554                 case 'c':
1555                         printmesg(fmt="LbR", a, 0);
1556                         m = 1+4+1+4*4;
1557                         if(n < m)
1558                                 error(Eshortdraw);
1559                         ddst = drawlookup(client, BGLONG(a+1), 1);
1560                         if(ddst == nil)
1561                                 error(Enodrawimage);
1562                         if(ddst->name)
1563                                 error("cannot change repl/clipr of shared image");
1564                         dst = ddst->image;
1565                         if(a[5])
1566                                 dst->flags |= Frepl;
1567                         drawrectangle(&dst->clipr, a+6);
1568                         continue;
1569
1570                 /* draw: 'd' dstid[4] srcid[4] maskid[4] R[4*4] P[2*4] P[2*4] */
1571                 case 'd':
1572                         printmesg(fmt="LLLRPP", a, 0);
1573                         m = 1+4+4+4+4*4+2*4+2*4;
1574                         if(n < m)
1575                                 error(Eshortdraw);
1576                         dst = drawimage(client, a+1);
1577                         dstid = BGLONG(a+1);
1578                         src = drawimage(client, a+5);
1579                         mask = drawimage(client, a+9);
1580                         drawrectangle(&r, a+13);
1581                         drawpoint(&p, a+29);
1582                         drawpoint(&q, a+37);
1583                         op = drawclientop(client);
1584                         memdraw(dst, r, src, p, mask, q, op);
1585                         dstflush(dstid, dst, r);
1586                         continue;
1587
1588                 /* toggle debugging: 'D' val[1] */
1589                 case 'D':
1590                         printmesg(fmt="b", a, 0);
1591                         m = 1+1;
1592                         if(n < m)
1593                                 error(Eshortdraw);
1594                         drawdebug = a[1];
1595                         continue;
1596
1597                 /* ellipse: 'e' dstid[4] srcid[4] center[2*4] a[4] b[4] thick[4] sp[2*4] alpha[4] phi[4]*/
1598                 case 'e':
1599                 case 'E':
1600                         printmesg(fmt="LLPlllPll", a, 0);
1601                         m = 1+4+4+2*4+4+4+4+2*4+2*4;
1602                         if(n < m)
1603                                 error(Eshortdraw);
1604                         dst = drawimage(client, a+1);
1605                         dstid = BGLONG(a+1);
1606                         src = drawimage(client, a+5);
1607                         drawpoint(&p, a+9);
1608                         e0 = BGLONG(a+17);
1609                         e1 = BGLONG(a+21);
1610                         if(e0<0 || e1<0)
1611                                 error("invalid ellipse semidiameter");
1612                         j = BGLONG(a+25);
1613                         if(j < 0)
1614                                 error("negative ellipse thickness");
1615                         drawpoint(&sp, a+29);
1616                         c = j;
1617                         if(*a == 'E')
1618                                 c = -1;
1619                         ox = BGLONG(a+37);
1620                         oy = BGLONG(a+41);
1621                         op = drawclientop(client);
1622                         /* high bit indicates arc angles are present */
1623                         if(ox & (1<<31)){
1624                                 if((ox & (1<<30)) == 0)
1625                                         ox &= ~(1<<31);
1626                                 memarc(dst, p, e0, e1, c, src, sp, ox, oy, op);
1627                         }else
1628                                 memellipse(dst, p, e0, e1, c, src, sp, op);
1629                         dstflush(dstid, dst, Rect(p.x-e0-j, p.y-e1-j, p.x+e0+j+1, p.y+e1+j+1));
1630                         continue;
1631
1632                 /* free: 'f' id[4] */
1633                 case 'f':
1634                         printmesg(fmt="L", a, 1);
1635                         m = 1+4;
1636                         if(n < m)
1637                                 error(Eshortdraw);
1638                         ll = drawlookup(client, BGLONG(a+1), 0);
1639                         if(ll && ll->dscreen && ll->dscreen->owner != client)
1640                                 ll->dscreen->owner->refreshme = 1;
1641                         drawuninstall(client, BGLONG(a+1));
1642                         continue;
1643
1644                 /* free screen: 'F' id[4] */
1645                 case 'F':
1646                         printmesg(fmt="L", a, 1);
1647                         m = 1+4;
1648                         if(n < m)
1649                                 error(Eshortdraw);
1650                         drawlookupscreen(client, BGLONG(a+1), &cs);
1651                         drawuninstallscreen(client, cs);
1652                         continue;
1653
1654                 /* initialize font: 'i' fontid[4] nchars[4] ascent[1] */
1655                 case 'i':
1656                         printmesg(fmt="Llb", a, 1);
1657                         m = 1+4+4+1;
1658                         if(n < m)
1659                                 error(Eshortdraw);
1660                         dstid = BGLONG(a+1);
1661                         if(dstid == 0)
1662                                 error("cannot use display as font");
1663                         font = drawlookup(client, dstid, 1);
1664                         if(font == 0)
1665                                 error(Enodrawimage);
1666                         if(font->image->layer)
1667                                 error("cannot use window as font");
1668                         ni = BGLONG(a+5);
1669                         if(ni<=0 || ni>4096)
1670                                 error("bad font size (4096 chars max)");
1671                         free(font->fchar);      /* should we complain if non-zero? */
1672                         font->fchar = malloc(ni*sizeof(FChar));
1673                         if(font->fchar == 0)
1674                                 error("no memory for font");
1675                         memset(font->fchar, 0, ni*sizeof(FChar));
1676                         font->nfchar = ni;
1677                         font->ascent = a[9];
1678                         continue;
1679
1680                 /* load character: 'l' fontid[4] srcid[4] index[2] R[4*4] P[2*4] left[1] width[1] */
1681                 case 'l':
1682                         printmesg(fmt="LLSRPbb", a, 0);
1683                         m = 1+4+4+2+4*4+2*4+1+1;
1684                         if(n < m)
1685                                 error(Eshortdraw);
1686                         font = drawlookup(client, BGLONG(a+1), 1);
1687                         if(font == 0)
1688                                 error(Enodrawimage);
1689                         if(font->nfchar == 0)
1690                                 error(Enotfont);
1691                         src = drawimage(client, a+5);
1692                         ci = BGSHORT(a+9);
1693                         if(ci >= font->nfchar)
1694                                 error(Eindex);
1695                         drawrectangle(&r, a+11);
1696                         drawpoint(&p, a+27);
1697                         memdraw(font->image, r, src, p, memopaque, p, S);
1698                         fc = &font->fchar[ci];
1699                         fc->minx = r.min.x;
1700                         fc->maxx = r.max.x;
1701                         fc->miny = r.min.y;
1702                         fc->maxy = r.max.y;
1703                         fc->left = a[35];
1704                         fc->width = a[36];
1705                         continue;
1706
1707                 /* draw line: 'L' dstid[4] p0[2*4] p1[2*4] end0[4] end1[4] radius[4] srcid[4] sp[2*4] */
1708                 case 'L':
1709                         printmesg(fmt="LPPlllLP", a, 0);
1710                         m = 1+4+2*4+2*4+4+4+4+4+2*4;
1711                         if(n < m)
1712                                 error(Eshortdraw);
1713                         dst = drawimage(client, a+1);
1714                         dstid = BGLONG(a+1);
1715                         drawpoint(&p, a+5);
1716                         drawpoint(&q, a+13);
1717                         e0 = BGLONG(a+21);
1718                         e1 = BGLONG(a+25);
1719                         j = BGLONG(a+29);
1720                         if(j < 0)
1721                                 error("negative line width");
1722                         src = drawimage(client, a+33);
1723                         drawpoint(&sp, a+37);
1724                         op = drawclientop(client);
1725                         memline(dst, p, q, e0, e1, j, src, sp, op);
1726                         /* avoid memlinebbox if possible */
1727                         if(dstid==0 || dst->layer!=nil){
1728                                 /* BUG: this is terribly inefficient: update maximal containing rect*/
1729                                 r = memlinebbox(p, q, e0, e1, j);
1730                                 dstflush(dstid, dst, insetrect(r, -(1+1+j)));
1731                         }
1732                         continue;
1733
1734                 /* create image mask: 'm' newid[4] id[4] */
1735 /*
1736  *
1737                 case 'm':
1738                         printmesg("LL", a, 0);
1739                         m = 4+4;
1740                         if(n < m)
1741                                 error(Eshortdraw);
1742                         break;
1743  *
1744  */
1745
1746                 /* attach to a named image: 'n' dstid[4] j[1] name[j] */
1747                 case 'n':
1748                         printmesg(fmt="Lz", a, 0);
1749                         m = 1+4+1;
1750                         if(n < m)
1751                                 error(Eshortdraw);
1752                         j = a[5];
1753                         if(j == 0)      /* give me a non-empty name please */
1754                                 error(Eshortdraw);
1755                         m += j;
1756                         if(n < m)
1757                                 error(Eshortdraw);
1758                         dstid = BGLONG(a+1);
1759                         if(drawlookup(client, dstid, 0))
1760                                 error(Eimageexists);
1761                         dn = drawlookupname(j, (char*)a+6);
1762                         if(dn == nil)
1763                                 error(Enoname);
1764                         if(drawinstall(client, dstid, dn->dimage->image, 0) == 0)
1765                                 error(Edrawmem);
1766                         di = drawlookup(client, dstid, 0);
1767                         if(di == 0)
1768                                 error("draw: cannot happen");
1769                         di->vers = dn->vers;
1770                         di->name = smalloc(j+1);
1771                         di->fromname = dn->dimage;
1772                         di->fromname->ref++;
1773                         memmove(di->name, a+6, j);
1774                         di->name[j] = 0;
1775                         client->infoid = dstid;
1776                         continue;
1777
1778                 /* name an image: 'N' dstid[4] in[1] j[1] name[j] */
1779                 case 'N':
1780                         printmesg(fmt="Lbz", a, 0);
1781                         m = 1+4+1+1;
1782                         if(n < m)
1783                                 error(Eshortdraw);
1784                         c = a[5];
1785                         j = a[6];
1786                         if(j == 0)      /* give me a non-empty name please */
1787                                 error(Eshortdraw);
1788                         m += j;
1789                         if(n < m)
1790                                 error(Eshortdraw);
1791                         di = drawlookup(client, BGLONG(a+1), 0);
1792                         if(di == 0)
1793                                 error(Enodrawimage);
1794                         if(di->name)
1795                                 error(Enamed);
1796                         if(c)
1797                                 drawaddname(client, di, j, (char*)a+7);
1798                         else{
1799                                 dn = drawlookupname(j, (char*)a+7);
1800                                 if(dn == nil)
1801                                         error(Enoname);
1802                                 if(dn->dimage != di)
1803                                         error(Ewrongname);
1804                                 drawdelname(dn);
1805                         }
1806                         continue;
1807
1808                 /* position window: 'o' id[4] r.min [2*4] screenr.min [2*4] */
1809                 case 'o':
1810                         printmesg(fmt="LPP", a, 0);
1811                         m = 1+4+2*4+2*4;
1812                         if(n < m)
1813                                 error(Eshortdraw);
1814                         dst = drawimage(client, a+1);
1815                         if(dst->layer){
1816                                 drawpoint(&p, a+5);
1817                                 drawpoint(&q, a+13);
1818                                 r = dst->layer->screenr;
1819                                 ni = memlorigin(dst, p, q);
1820                                 if(ni < 0)
1821                                         error("image origin failed");
1822                                 if(ni > 0){
1823                                         addflush(r);
1824                                         addflush(dst->layer->screenr);
1825                                         ll = drawlookup(client, BGLONG(a+1), 1);
1826                                         drawrefreshscreen(ll, client);
1827                                 }
1828                         }
1829                         continue;
1830
1831                 /* set compositing operator for next draw operation: 'O' op */
1832                 case 'O':
1833                         printmesg(fmt="b", a, 0);
1834                         m = 1+1;
1835                         if(n < m)
1836                                 error(Eshortdraw);
1837                         client->op = a[1];
1838                         continue;
1839
1840                 /* filled polygon: 'P' dstid[4] n[2] wind[4] ignore[2*4] srcid[4] sp[2*4] p0[2*4] dp[2*2*n] */
1841                 /* polygon: 'p' dstid[4] n[2] end0[4] end1[4] radius[4] srcid[4] sp[2*4] p0[2*4] dp[2*2*n] */
1842                 case 'p':
1843                 case 'P':
1844                         printmesg(fmt="LslllLPP", a, 0);
1845                         m = 1+4+2+4+4+4+4+2*4;
1846                         if(n < m)
1847                                 error(Eshortdraw);
1848                         dstid = BGLONG(a+1);
1849                         dst = drawimage(client, a+1);
1850                         ni = BGSHORT(a+5);
1851                         if(ni < 0)
1852                                 error("negative count in polygon");
1853                         e0 = BGLONG(a+7);
1854                         e1 = BGLONG(a+11);
1855                         j = 0;
1856                         if(*a == 'p'){
1857                                 j = BGLONG(a+15);
1858                                 if(j < 0)
1859                                         error("negative polygon line width");
1860                         }
1861                         src = drawimage(client, a+19);
1862                         drawpoint(&sp, a+23);
1863                         drawpoint(&p, a+31);
1864                         ni++;
1865                         pp = malloc(ni*sizeof(Point));
1866                         if(pp == nil)
1867                                 error(Enomem);
1868                         doflush = 0;
1869                         if(dstid==0 || (screenimage && dst->layer && dst->layer->screen->image->data == screenimage->data))
1870                                 doflush = 1;    /* simplify test in loop */
1871                         ox = oy = 0;
1872                         esize = 0;
1873                         u = a+m;
1874                         for(y=0; y<ni; y++){
1875                                 q = p;
1876                                 oesize = esize;
1877                                 u = drawcoord(u, a+n, ox, &p.x);
1878                                 u = drawcoord(u, a+n, oy, &p.y);
1879                                 ox = p.x;
1880                                 oy = p.y;
1881                                 if(doflush){
1882                                         esize = j;
1883                                         if(*a == 'p'){
1884                                                 if(y == 0){
1885                                                         c = memlineendsize(e0);
1886                                                         if(c > esize)
1887                                                                 esize = c;
1888                                                 }
1889                                                 if(y == ni-1){
1890                                                         c = memlineendsize(e1);
1891                                                         if(c > esize)
1892                                                                 esize = c;
1893                                                 }
1894                                         }
1895                                         if(*a=='P' && e0!=1 && e0 !=~0)
1896                                                 r = dst->clipr;
1897                                         else if(y > 0){
1898                                                 r = Rect(q.x-oesize, q.y-oesize, q.x+oesize+1, q.y+oesize+1);
1899                                                 combinerect(&r, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1));
1900                                         }
1901                                         if(rectclip(&r, dst->clipr))            /* should perhaps be an arg to dstflush */
1902                                                 dstflush(dstid, dst, r);
1903                                 }
1904                                 pp[y] = p;
1905                         }
1906                         if(y == 1)
1907                                 dstflush(dstid, dst, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1));
1908                         op = drawclientop(client);
1909                         if(*a == 'p')
1910                                 mempoly(dst, pp, ni, e0, e1, j, src, sp, op);
1911                         else
1912                                 memfillpoly(dst, pp, ni, e0, src, sp, op);
1913                         free(pp);
1914                         m = u-a;
1915                         continue;
1916
1917                 /* read: 'r' id[4] R[4*4] */
1918                 case 'r':
1919                         printmesg(fmt="LR", a, 0);
1920                         m = 1+4+4*4;
1921                         if(n < m)
1922                                 error(Eshortdraw);
1923                         i = drawimage(client, a+1);
1924                         drawrectangle(&r, a+5);
1925                         if(!rectinrect(r, i->r))
1926                                 error(Ereadoutside);
1927                         c = bytesperline(r, i->depth);
1928                         c *= Dy(r);
1929                         free(client->readdata);
1930                         client->readdata = mallocz(c, 0);
1931                         if(client->readdata == nil)
1932                                 error("readimage malloc failed");
1933                         client->nreaddata = memunload(i, r, client->readdata, c);
1934                         if(client->nreaddata < 0){
1935                                 free(client->readdata);
1936                                 client->readdata = nil;
1937                                 error("bad readimage call");
1938                         }
1939                         continue;
1940
1941                 /* string: 's' dstid[4] srcid[4] fontid[4] P[2*4] clipr[4*4] sp[2*4] ni[2] ni*(index[2]) */
1942                 /* 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]) */
1943                 case 's':
1944                 case 'x':
1945                         printmesg(fmt="LLLPRPs", a, 0);
1946                         m = 1+4+4+4+2*4+4*4+2*4+2;
1947                         if(*a == 'x')
1948                                 m += 4+2*4;
1949                         if(n < m)
1950                                 error(Eshortdraw);
1951
1952                         dst = drawimage(client, a+1);
1953                         dstid = BGLONG(a+1);
1954                         src = drawimage(client, a+5);
1955                         font = drawlookup(client, BGLONG(a+9), 1);
1956                         if(font == 0)
1957                                 error(Enodrawimage);
1958                         if(font->nfchar == 0)
1959                                 error(Enotfont);
1960                         drawpoint(&p, a+13);
1961                         drawrectangle(&r, a+21);
1962                         drawpoint(&sp, a+37);
1963                         ni = BGSHORT(a+45);
1964                         u = a+m;
1965                         m += ni*2;
1966                         if(n < m)
1967                                 error(Eshortdraw);
1968                         clipr = dst->clipr;
1969                         dst->clipr = r;
1970                         op = drawclientop(client);
1971                         bg = dst;
1972                         if(*a == 'x'){
1973                                 /* paint background */
1974                                 bg = drawimage(client, a+47);
1975                                 drawpoint(&q, a+51);
1976                                 r.min.x = p.x;
1977                                 r.min.y = p.y-font->ascent;
1978                                 r.max.x = p.x;
1979                                 r.max.y = r.min.y+Dy(font->image->r);
1980                                 j = ni;
1981                                 while(--j >= 0){
1982                                         ci = BGSHORT(u);
1983                                         if(ci<0 || ci>=font->nfchar){
1984                                                 dst->clipr = clipr;
1985                                                 error(Eindex);
1986                                         }
1987                                         r.max.x += font->fchar[ci].width;
1988                                         u += 2;
1989                                 }
1990                                 memdraw(dst, r, bg, q, memopaque, ZP, op);
1991                                 u -= 2*ni;
1992                         }
1993                         q = p;
1994                         while(--ni >= 0){
1995                                 ci = BGSHORT(u);
1996                                 if(ci<0 || ci>=font->nfchar){
1997                                         dst->clipr = clipr;
1998                                         error(Eindex);
1999                                 }
2000                                 q = drawchar(dst, bg, q, src, &sp, font, ci, op);
2001                                 u += 2;
2002                         }
2003                         dst->clipr = clipr;
2004                         p.y -= font->ascent;
2005                         dstflush(dstid, dst, Rect(p.x, p.y, q.x, p.y+Dy(font->image->r)));
2006                         continue;
2007
2008                 /* use public screen: 'S' id[4] chan[4] */
2009                 case 'S':
2010                         printmesg(fmt="Ll", a, 0);
2011                         m = 1+4+4;
2012                         if(n < m)
2013                                 error(Eshortdraw);
2014                         dstid = BGLONG(a+1);
2015                         if(dstid == 0)
2016                                 error(Ebadarg);
2017                         dscrn = drawlookupdscreen(dstid);
2018                         if(dscrn==0 || (dscrn->public==0 && dscrn->owner!=client))
2019                                 error(Enodrawscreen);
2020                         if(dscrn->screen->image->chan != BGLONG(a+5))
2021                                 error("inconsistent chan");
2022                         if(drawinstallscreen(client, dscrn, 0, 0, 0, 0) == 0)
2023                                 error(Edrawmem);
2024                         continue;
2025
2026                 /* top or bottom windows: 't' top[1] nw[2] n*id[4] */
2027                 case 't':
2028                         printmesg(fmt="bsL", a, 0);
2029                         m = 1+1+2;
2030                         if(n < m)
2031                                 error(Eshortdraw);
2032                         nw = BGSHORT(a+2);
2033                         if(nw < 0)
2034                                 error(Ebadarg);
2035                         if(nw == 0)
2036                                 continue;
2037                         m += nw*4;
2038                         if(n < m)
2039                                 error(Eshortdraw);
2040                         lp = malloc(nw*sizeof(Memimage*));
2041                         if(lp == 0)
2042                                 error(Enomem);
2043                         if(waserror()){
2044                                 free(lp);
2045                                 nexterror();
2046                         }
2047                         for(j=0; j<nw; j++)
2048                                 lp[j] = drawimage(client, a+1+1+2+j*4);
2049                         if(lp[0]->layer == 0)
2050                                 error("images are not windows");
2051                         for(j=1; j<nw; j++)
2052                                 if(lp[j]->layer->screen != lp[0]->layer->screen)
2053                                         error("images not on same screen");
2054                         if(a[1])
2055                                 memltofrontn(lp, nw);
2056                         else
2057                                 memltorearn(lp, nw);
2058                         if(screenimage && lp[0]->layer->screen->image->data == screenimage->data)
2059                                 for(j=0; j<nw; j++)
2060                                         addflush(lp[j]->layer->screenr);
2061                         ll = drawlookup(client, BGLONG(a+1+1+2), 1);
2062                         drawrefreshscreen(ll, client);
2063                         poperror();
2064                         free(lp);
2065                         continue;
2066
2067                 /* visible: 'v' */
2068                 case 'v':
2069                         printmesg(fmt="", a, 0);
2070                         m = 1;
2071                         drawflush();
2072                         continue;
2073
2074                 /* write: 'y' id[4] R[4*4] data[x*1] */
2075                 /* write from compressed data: 'Y' id[4] R[4*4] data[x*1] */
2076                 case 'y':
2077                 case 'Y':
2078                         printmesg(fmt="LR", a, 0);
2079                 //      iprint("load %c\n", *a);
2080                         m = 1+4+4*4;
2081                         if(n < m)
2082                                 error(Eshortdraw);
2083                         dstid = BGLONG(a+1);
2084                         dst = drawimage(client, a+1);
2085                         drawrectangle(&r, a+5);
2086                         if(!rectinrect(r, dst->r))
2087                                 error(Ewriteoutside);
2088                         y = memload(dst, r, a+m, n-m, *a=='Y');
2089                         if(y < 0)
2090                                 error("bad writeimage call");
2091                         dstflush(dstid, dst, r);
2092                         m += y;
2093                         continue;
2094                 }
2095         }
2096         poperror();
2097 }
2098
2099 Dev drawdevtab = {
2100         'i',
2101         "draw",
2102
2103         devreset,
2104         devinit,
2105         devshutdown,
2106         drawattach,
2107         drawwalk,
2108         drawstat,
2109         drawopen,
2110         devcreate,
2111         drawclose,
2112         drawread,
2113         devbread,
2114         drawwrite,
2115         devbwrite,
2116         devremove,
2117         devwstat,
2118 };
2119
2120 /*
2121  * On 8 bit displays, load the default color map
2122  */
2123 void
2124 drawcmap(void)
2125 {
2126         int r, g, b, cr, cg, cb, v;
2127         int num, den;
2128         int i, j;
2129
2130         drawactive(1);  /* to restore map from backup */
2131         for(r=0,i=0; r!=4; r++)
2132             for(v=0; v!=4; v++,i+=16){
2133                 for(g=0,j=v-r; g!=4; g++)
2134                     for(b=0;b!=4;b++,j++){
2135                         den = r;
2136                         if(g > den)
2137                                 den = g;
2138                         if(b > den)
2139                                 den = b;
2140                         if(den == 0)    /* divide check -- pick grey shades */
2141                                 cr = cg = cb = v*17;
2142                         else{
2143                                 num = 17*(4*den+v);
2144                                 cr = r*num/den;
2145                                 cg = g*num/den;
2146                                 cb = b*num/den;
2147                         }
2148                         setcolor(i+(j&15),
2149                                 cr*0x01010101, cg*0x01010101, cb*0x01010101);
2150                     }
2151         }
2152 }
2153
2154 void
2155 drawblankscreen(int blank)
2156 {
2157         int i, nc;
2158         ulong *p;
2159
2160         if(blank == sdraw.blanked)
2161                 return;
2162         if(!candlock())
2163                 return;
2164         if(screenimage == nil){
2165                 dunlock();
2166                 return;
2167         }
2168         p = sdraw.savemap;
2169         nc = screenimage->depth > 8 ? 256 : 1<<screenimage->depth;
2170
2171         /*
2172          * blankscreen uses the hardware to blank the screen
2173          * when possible.  to help in cases when it is not possible,
2174          * we set the color map to be all black.
2175          */
2176         if(blank == 0){ /* turn screen on */
2177                 for(i=0; i<nc; i++, p+=3)
2178                         setcolor(i, p[0], p[1], p[2]);
2179                 blankscreen(0);
2180         }else{  /* turn screen off */
2181                 blankscreen(1);
2182                 for(i=0; i<nc; i++, p+=3){
2183                         getcolor(i, &p[0], &p[1], &p[2]);
2184                         setcolor(i, 0, 0, 0);
2185                 }
2186         }
2187         sdraw.blanked = blank;
2188         dunlock();
2189 }
2190
2191 /*
2192  * record activity on screen, changing blanking as appropriate
2193  */
2194 void
2195 drawactive(int active)
2196 {
2197         if(active){
2198                 drawblankscreen(0);
2199                 sdraw.blanktime = MACHP(0)->ticks;
2200         }else{
2201                 if(blanktime && sdraw.blanktime && TK2SEC(MACHP(0)->ticks - sdraw.blanktime)/60 >= blanktime)
2202                         drawblankscreen(1);
2203         }
2204 }
2205
2206 int
2207 drawidletime(void)
2208 {
2209         return TK2SEC(MACHP(0)->ticks - sdraw.blanktime)/60;
2210 }