28 * 4 bits of file type (qids above)
29 * 24 bits of mux slot number +1; 0 means not attached to client
31 #define QSHIFT 4 /* location in qid of client # */
33 #define QID(q) ((((ulong)(q).path)&0x0000000F)>>0)
34 #define CLIENTPATH(q) ((((ulong)q)&0x7FFFFFF0)>>QSHIFT)
35 #define CLIENT(q) CLIENTPATH((q).path)
38 #define HASHMASK (NHASH-1)
39 #define IOUNIT (64*1024)
41 typedef struct Client Client;
42 typedef struct Draw Draw;
43 typedef struct DImage DImage;
44 typedef struct DScreen DScreen;
45 typedef struct CScreen CScreen;
46 typedef struct FChar FChar;
47 typedef struct Refresh Refresh;
48 typedef struct Refx Refx;
49 typedef struct DName DName;
65 DImage* dimage[NHASH];
103 int minx; /* left edge of bits */
104 int maxx; /* right edge of bits */
105 uchar miny; /* first non-zero scan-line */
106 uchar maxy; /* last non-zero scan-line + 1 */
107 schar left; /* offset of baseline */
108 uchar width; /* width of baseline */
112 * Reference counts in DImages:
113 * one per open by original client
114 * one per screen image or fill
115 * one per image derived from this one by name
127 DScreen* dscreen; /* 0 if not a window */
128 DImage* fromname; /* image this one is derived from, by name */
153 static Memimage *screenimage;
154 static DImage* screendimage;
155 static char screenname[40];
156 static int screennameid;
158 static Rectangle flushrect;
160 static DScreen* dscreen;
161 extern void flushmemscreen(Rectangle);
162 void drawmesg(Client*, void*, int);
163 void drawuninstall(Client*, int);
164 void drawfreedimage(DImage*);
165 Client* drawclientofpath(ulong);
166 DImage* allocdimage(Memimage*);
168 static char Enodrawimage[] = "unknown id for draw image";
169 static char Enodrawscreen[] = "unknown id for draw screen";
170 static char Eshortdraw[] = "short draw message";
171 static char Eshortread[] = "draw read too short";
172 static char Eimageexists[] = "image id in use";
173 static char Escreenexists[] = "screen id in use";
174 static char Edrawmem[] = "image memory allocation failed";
175 static char Ereadoutside[] = "readimage outside image";
176 static char Ewriteoutside[] = "writeimage outside image";
177 static char Enotfont[] = "image not a font";
178 static char Eindex[] = "character index out of range";
179 static char Enoclient[] = "no such draw client";
180 static char Enameused[] = "image name in use";
181 static char Enoname[] = "no image with that name";
182 static char Eoldname[] = "named image no longer valid";
183 static char Enamed[] = "image already has name";
184 static char Ewrongname[] = "wrong name for image";
195 return canqlock(&drawlock);
205 drawgen(Chan *c, Dirtab*, int, int s, Dir *dp)
218 mkqid(&q, Qtopdir, 0, QTDIR);
219 devdir(c, q, "#i", 0, eve, 0500, dp);
222 cl = drawclientofpath(c->qid.path);
224 strcpy(up->genbuf, "??");
226 sprint(up->genbuf, "%d", cl->clientid);
227 mkqid(&q, Q2nd, 0, QTDIR);
228 devdir(c, q, up->genbuf, 0, eve, 0500, dp);
231 panic("drawwalk %llux", c->qid.path);
237 * Top level directory contains the name of the device.
243 mkqid(&q, Q2nd, 0, QTDIR);
244 devdir(c, q, "draw", 0, eve, 0555, dp);
249 mkqid(&q, Qwinname, 0, QTFILE);
250 devdir(c, q, "winname", 0, eve, 0444, dp);
257 * Second level contains "new" plus all the clients.
263 mkqid(&q, Qnew, 0, QTFILE);
264 devdir(c, q, "new", 0, eve, 0666, dp);
267 if(s <= sdraw.nclient){
268 cl = sdraw.client[s-1];
271 sprint(up->genbuf, "%d", cl->clientid);
272 mkqid(&q, (s<<QSHIFT)|Q3rd, 0, QTDIR);
273 devdir(c, q, up->genbuf, 0, eve, 0555, dp);
282 path = c->qid.path&~((1<<QSHIFT)-1); /* slot component */
283 q.vers = c->qid.vers;
287 q.path = path|Qcolormap;
288 devdir(c, q, "colormap", 0, eve, 0600, dp);
292 devdir(c, q, "ctl", 0, eve, 0600, dp);
296 devdir(c, q, "data", 0, eve, 0600, dp);
299 q.path = path|Qrefresh;
300 devdir(c, q, "refresh", 0, eve, 0400, dp);
310 drawrefactive(void *a)
315 return c->refreshme || c->refresh!=0;
320 drawrefreshscreen(DImage *l, Client *client)
322 while(l != nil && l->dscreen == nil)
324 if(l != nil && l->dscreen->owner != client)
325 l->dscreen->owner->refreshme = 1;
330 drawrefresh(Memimage*, Rectangle r, void *v)
342 for(ref=c->refresh; ref; ref=ref->next)
343 if(ref->dimage == d){
344 combinerect(&ref->r, r);
347 ref = malloc(sizeof(Refresh));
351 ref->next = c->refresh;
357 addflush(Rectangle r)
362 if(sdraw.softscreen==0 || screenimage == nil || !rectclip(&r, screenimage->r))
364 if(flushrect.min.x >= flushrect.max.x){
369 /* VNC uses a region to compute the minimum bounding area.
370 * The waste is far less than that of a bounding box. see region.c
373 flushmemscreen(flushrect);
378 combinerect(&nbb, r);
380 abb = Dx(flushrect)*Dy(flushrect);
381 anbb = Dx(nbb)*Dy(nbb);
383 * Area of new waste is area of new bb minus area of old bb,
384 * less the area of the new segment, which we assume is not waste.
385 * This could be negative, but that's OK.
387 waste += anbb-abb - ar;
392 * total area is small
393 * waste is less than half total area
396 if(anbb<=1024 || waste*2<anbb || rectXrect(flushrect, r)){
400 /* emit current state */
401 if(flushrect.min.x < flushrect.max.x)
402 flushmemscreen(flushrect);
409 dstflush(int dstid, Memimage *dst, Rectangle r)
414 //combinerect(&flushrect, r);
415 addflush(r); // for VNC, see comments in addflush
418 if(screenimage == nil || dst == nil || (l = dst->layer) == nil)
421 if(l->screen->image->data != screenimage->data)
423 r = rectaddpt(r, l->delta);
424 l = l->screen->image->layer;
432 if(screenimage && flushrect.min.x < flushrect.max.x)
433 flushmemscreen(flushrect);
434 flushrect = Rect(10000, 10000, -10000, -10000);
439 drawcmp(char *a, char *b, int n)
443 return memcmp(a, b, n);
447 drawlookupname(int n, char *str)
452 ename = &name[sdraw.nname];
453 for(; name<ename; name++)
454 if(drawcmp(name->name, str, n) == 0)
460 drawgoodname(DImage *d)
464 /* if window, validate the screen's own images */
466 if(drawgoodname(d->dscreen->dimage) == 0
467 || drawgoodname(d->dscreen->dfill) == 0)
471 n = drawlookupname(strlen(d->name), d->name);
472 if(n==nil || n->vers!=d->vers)
478 drawlookup(Client *client, int id, int checkname)
482 d = client->dimage[id&HASHMASK];
485 if(checkname && !drawgoodname(d))
495 drawlookupdscreen(int id)
509 drawlookupscreen(Client *client, int id, CScreen **cs)
515 if(s->dscreen->id == id){
521 error(Enodrawscreen);
526 allocdimage(Memimage *i)
530 d = malloc(sizeof(DImage));
545 drawinstall(Client *client, int id, Memimage *i, DScreen *dscreen)
553 d->dscreen = dscreen;
554 d->next = client->dimage[id&HASHMASK];
555 client->dimage[id&HASHMASK] = d;
560 drawinstallscreen(Client *client, DScreen *d, int id, DImage *dimage, DImage *dfill, int public)
565 c = malloc(sizeof(CScreen));
566 if(dimage && dimage->image && dimage->image->chan == 0)
567 panic("bad image %p in drawinstallscreen", dimage->image);
572 d = malloc(sizeof(DScreen));
577 s = malloc(sizeof(Memscreen));
587 s->image = dimage->image;
592 s->fill = dfill->image;
605 c->next = client->cscreen;
611 drawdelname(DName *name)
617 memmove(name, name+1, (sdraw.nname-(i+1))*sizeof(DName));
622 drawfreedscreen(DScreen *this)
628 print("negative ref in drawfreedscreen\n");
633 dscreen = this->next;
636 while(next = ds->next){ /* assign = */
638 ds->next = this->next;
647 drawfreedimage(this->dimage);
649 drawfreedimage(this->dfill);
655 drawfreedimage(DImage *dimage)
663 print("negative ref in drawfreedimage\n");
668 for(i=0; i<sdraw.nname; )
669 if(sdraw.name[i].dimage == dimage)
670 drawdelname(sdraw.name+i);
673 if(dimage->fromname){ /* acquired by name; owned by someone else*/
674 drawfreedimage(dimage->fromname);
677 ds = dimage->dscreen;
680 if(screenimage && l->data == screenimage->data)
681 addflush(l->layer->screenr);
682 if(l->layer->refreshfn == drawrefresh) /* else true owner will clean up */
683 free(l->layer->refreshptr);
684 l->layer->refreshptr = nil;
685 if(drawgoodname(dimage))
691 freememimage(dimage->image);
698 drawuninstallscreen(Client *client, CScreen *this)
702 cs = client->cscreen;
704 client->cscreen = this->next;
705 drawfreedscreen(this->dscreen);
709 while(next = cs->next){ /* assign = */
711 cs->next = this->next;
712 drawfreedscreen(this->dscreen);
721 drawuninstall(Client *client, int id)
725 d = client->dimage[id&HASHMASK];
729 client->dimage[id&HASHMASK] = d->next;
733 while(next = d->next){ /* assign = */
735 d->next = next->next;
736 drawfreedimage(next);
745 drawaddname(Client *client, DImage *di, int n, char *str)
747 DName *name, *ename, *new, *t;
750 ename = &name[sdraw.nname];
751 for(; name<ename; name++)
752 if(drawcmp(name->name, str, n) == 0)
754 t = smalloc((sdraw.nname+1)*sizeof(DName));
755 memmove(t, sdraw.name, sdraw.nname*sizeof(DName));
758 new = &sdraw.name[sdraw.nname++];
759 new->name = smalloc(n+1);
760 memmove(new->name, str, n);
763 new->client = client;
764 new->vers = ++sdraw.vers;
773 for(i=0; i<sdraw.nclient; i++){
774 cl = sdraw.client[i];
778 if(i == sdraw.nclient){
779 cp = malloc((sdraw.nclient+1)*sizeof(Client*));
782 memmove(cp, sdraw.client, sdraw.nclient*sizeof(Client*));
788 cl = malloc(sizeof(Client));
791 memset(cl, 0, sizeof(Client));
793 cl->clientid = ++sdraw.clientid;
795 sdraw.client[i] = cl;
800 drawclientop(Client *cl)
813 * if draw has ever been used, we can't resize the frame buffer,
814 * even if all clients have exited (nclients is cumulative); it's too
817 return sdraw.nclient != 0;
821 drawclientofpath(ulong path)
826 slot = CLIENTPATH(path);
829 cl = sdraw.client[slot-1];
830 if(cl==0 || cl->clientid==0)
841 client = drawclientofpath(c->qid.path);
848 drawimage(Client *client, uchar *a)
852 d = drawlookup(client, BGLONG(a), 1);
859 drawrectangle(Rectangle *r, uchar *a)
861 r->min.x = BGLONG(a+0*4);
862 r->min.y = BGLONG(a+1*4);
863 r->max.x = BGLONG(a+2*4);
864 r->max.y = BGLONG(a+3*4);
868 drawpoint(Point *p, uchar *a)
870 p->x = BGLONG(a+0*4);
871 p->y = BGLONG(a+1*4);
875 drawchar(Memimage *dst, Memimage *rdst, Point p, Memimage *src, Point *sp, DImage *font, int index, int op)
880 static Memimage *tmp;
882 fc = &font->fchar[index];
883 r.min.x = p.x+fc->left;
884 r.min.y = p.y-(font->ascent-fc->miny);
885 r.max.x = r.min.x+(fc->maxx-fc->minx);
886 r.max.y = r.min.y+(fc->maxy-fc->miny);
887 sp1.x = sp->x+fc->left;
888 sp1.y = sp->y+fc->miny;
891 * If we're drawing greyscale fonts onto a VGA screen,
892 * it's very costly to read the screen memory to do the
893 * alpha blending inside memdraw. If this is really a stringbg,
894 * then rdst is the bg image (in main memory) which we can
895 * refer to for the underlying dst pixels instead of reading dst
898 if(ishwimage(dst) && !ishwimage(rdst) && font->image->depth > 1){
899 if(tmp == nil || tmp->chan != dst->chan || Dx(tmp->r) < Dx(r) || Dy(tmp->r) < Dy(r)){
902 tmp = allocmemimage(Rect(0,0,Dx(r),Dy(r)), dst->chan);
906 memdraw(tmp, Rect(0,0,Dx(r),Dy(r)), rdst, r.min, memopaque, ZP, S);
907 memdraw(tmp, Rect(0,0,Dx(r),Dy(r)), src, sp1, font->image, Pt(fc->minx, fc->miny), op);
908 memdraw(dst, r, tmp, ZP, memopaque, ZP, S);
911 memdraw(dst, r, src, sp1, font->image, Pt(fc->minx, fc->miny), op);
920 makescreenimage(void)
929 if((md = attachscreen(&r, &chan, &depth, &width, &sdraw.softscreen)) == nil)
932 if((i = allocmemimaged(r, chan, md)) == nil){
933 if(--md->ref == 0 && md->allocd)
941 freememimage(i); /* frees md */
945 snprint(screenname, sizeof screenname, "noborder.screen.%d", ++screennameid);
946 drawaddname(nil, di, strlen(screenname), screenname);
953 initscreenimage(void)
955 if(screenimage != nil)
958 screendimage = makescreenimage();
959 if(screendimage == nil)
961 screenimage = screendimage->image;
962 // iprint("initscreenimage %p %p\n", screendimage, screenimage);
968 deletescreenimage(void)
972 /* will be freed via screendimage; disable */
973 screenimage->clipr = ZR;
977 drawfreedimage(screendimage);
984 resetscreenimage(void)
992 drawattach(char *spec)
995 if(!initscreenimage()){
997 error("no frame buffer");
1000 return devattach('i', spec);
1004 drawwalk(Chan *c, Chan *nc, char **name, int nname)
1006 if(screenimage == nil)
1007 error("no frame buffer");
1008 return devwalk(c, nc, name, nname, 0, 0, drawgen);
1012 drawstat(Chan *c, uchar *db, int n)
1014 return devstat(c, db, n, 0, 0, drawgen);
1018 drawopen(Chan *c, int omode)
1024 if(c->qid.type & QTDIR){
1025 c = devopen(c, omode, 0, 0, drawgen);
1035 if(QID(c->qid) == Qnew){
1036 cl = drawnewclient();
1039 c->qid.path = Qctl|((cl->slot+1)<<QSHIFT);
1042 switch(QID(c->qid)){
1054 flushrect = Rect(10000, 10000, -10000, -10000);
1055 dn = drawlookupname(strlen(screenname), screenname);
1057 error("draw: cannot happen 2");
1058 if(drawinstall(cl, 0, dn->dimage->image, 0) == 0)
1060 di = drawlookup(cl, 0, 0);
1062 error("draw: cannot happen 1");
1063 di->vers = dn->vers;
1064 di->name = smalloc(strlen(screenname)+1);
1065 strcpy(di->name, screenname);
1066 di->fromname = dn->dimage;
1067 di->fromname->ref++;
1080 c->mode = openmode(omode);
1095 if(QID(c->qid) < Qcolormap) /* Qtopdir, Qnew, Q3rd, Q2nd have no client */
1104 if(QID(c->qid) == Qctl)
1106 if((c->flag&COPEN) && (decref(&cl->r)==0)){
1107 while(r = cl->refresh){ /* assign = */
1108 cl->refresh = r->next;
1112 for(i=0; i<sdraw.nname; )
1113 if(sdraw.name[i].client == cl)
1114 drawdelname(sdraw.name+i);
1118 drawuninstallscreen(cl, cl->cscreen);
1119 /* all screens are freed, so now we can free images */
1121 for(i=0; i<NHASH; i++){
1122 while((d = *dp) != nil){
1128 sdraw.client[cl->slot] = 0;
1129 drawflush(); /* to erase visible, now dead windows */
1137 drawread(Chan *c, void *a, long n, vlong off)
1140 ulong red, green, blue;
1149 if(c->qid.type & QTDIR)
1150 return devdirread(c, a, n, 0, 0, drawgen);
1151 if(QID(c->qid) == Qwinname)
1152 return readstr(off, a, n, screenname);
1160 switch(QID(c->qid)){
1165 error(Enodrawimage);
1166 if(cl->infoid == 0){
1169 error(Enodrawimage);
1171 di = drawlookup(cl, cl->infoid, 1);
1173 error(Enodrawimage);
1176 n = sprint(a, "%11d %11d %11s %11d %11d %11d %11d %11d %11d %11d %11d %11d",
1177 cl->clientid, cl->infoid, chantostr(buf, i->chan), (i->flags&Frepl)==Frepl,
1178 i->r.min.x, i->r.min.y, i->r.max.x, i->r.max.y,
1179 i->clipr.min.x, i->clipr.min.y, i->clipr.max.x, i->clipr.max.y);
1180 ((char*)a)[n++] = ' ';
1185 p = malloc(4*12*256+1);
1189 for(index = 0; index < 256; index++){
1190 getcolor(index, &red, &green, &blue);
1191 m += sprint((char*)p+m, "%11d %11lud %11lud %11lud\n", index, red>>24, green>>24, blue>>24);
1193 n = readstr(offset, a, n, (char*)p);
1198 if(cl->readdata == nil)
1199 error("no draw data");
1200 if(n < cl->nreaddata)
1203 memmove(a, cl->readdata, cl->nreaddata);
1212 if(cl->refreshme || cl->refresh)
1224 rendsleep(&cl->refrend, drawrefactive, cl);
1231 while(cl->refresh && n>=5*4){
1233 BPLONG(p+0*4, r->dimage->id);
1234 BPLONG(p+1*4, r->r.min.x);
1235 BPLONG(p+2*4, r->r.min.y);
1236 BPLONG(p+3*4, r->r.max.x);
1237 BPLONG(p+4*4, r->r.max.y);
1238 cl->refresh = r->next;
1258 for(i=0; i<sdraw.nclient; i++){
1259 cl = sdraw.client[i];
1260 if(cl && (cl->refreshme || cl->refresh))
1261 rendwakeup(&cl->refrend);
1266 drawwrite(Chan *c, void *a, long n, vlong)
1268 char buf[128], *fields[4], *q;
1270 int i, m, red, green, blue, x;
1272 if(c->qid.type & QTDIR)
1281 switch(QID(c->qid)){
1284 error("unknown draw control request");
1285 cl->infoid = BGLONG((uchar*)a);
1293 if(x > sizeof(buf)-1)
1295 q = memccpy(buf, a, '\n', x);
1303 if(tokenize(buf, fields, nelem(fields)) != 4)
1305 i = strtoul(fields[0], 0, 0);
1306 red = strtoul(fields[1], 0, 0);
1307 green = strtoul(fields[2], 0, 0);
1308 blue = strtoul(fields[3], &q, 0);
1311 if(red>255 || green>255 || blue>255 || i<0 || i>255)
1319 setcolor(i, red, green, blue);
1337 drawcoord(uchar *p, uchar *maxp, int oldx, int *newx)
1362 printmesg(char *fmt, uchar *a, int plsprnt)
1370 USED(fmt, a, buf, p, q, s);
1375 for(p=fmt; *p; p++){
1378 q += sprint(q, " %ld", (long)BGLONG(a));
1382 q += sprint(q, " %.8lux", (ulong)BGLONG(a));
1386 q += sprint(q, " [%d %d %d %d]", BGLONG(a), BGLONG(a+4), BGLONG(a+8), BGLONG(a+12));
1390 q += sprint(q, " [%d %d]", BGLONG(a), BGLONG(a+4));
1394 q += sprint(q, " %d", *a++);
1397 q += sprint(q, " %d", BGSHORT(a));
1401 q += sprint(q, " %.4ux", BGSHORT(a));
1408 // iprint("%.*s", (int)(q-buf), buf);
1412 drawmesg(Client *client, void *av, int n)
1414 int c, repl, m, y, dstid, scrnid, ni, ci, j, nw, e0, e1, op, ox, oy, oesize, esize, doflush;
1415 uchar *u, *a, refresh;
1419 Point p, q, *pp, sp;
1420 Memimage *i, *bg, *dst, *src, *mask;
1423 DImage *font, *ll, *di, *ddst, *dsrc;
1435 if(fmt) printmesg(fmt, a, 1);
1436 /* iprint("error: %s\n", up->errstr); */
1444 error("bad draw command");
1445 /* new allocate: 'b' id[4] screenid[4] refresh[1] chan[4] repl[1] R[4*4] clipR[4*4] rrggbbaa[4] */
1447 printmesg(fmt="LLbLbRRL", a, 0);
1448 m = 1+4+4+1+4+1+4*4+4*4+4;
1451 dstid = BGLONG(a+1);
1452 scrnid = BGLONG(a+5);
1454 chan = BGLONG(a+10);
1456 drawrectangle(&r, a+15);
1457 drawrectangle(&clipr, a+31);
1458 value = BGLONG(a+47);
1459 if(drawlookup(client, dstid, 0))
1460 error(Eimageexists);
1462 dscrn = drawlookupscreen(client, scrnid, &cs);
1463 scrn = dscrn->screen;
1464 if(repl || chan!=scrn->image->chan)
1465 error("image parameters incompatible with screen");
1471 reffn = memlnorefresh;
1474 reffn = drawrefresh;
1477 error("unknown refresh method");
1479 l = memlalloc(scrn, r, reffn, 0, value);
1482 addflush(l->layer->screenr);
1484 rectclip(&l->clipr, r);
1485 if(drawinstall(client, dstid, l, dscrn) == 0){
1492 if(reffn == drawrefresh){
1493 refx = malloc(sizeof(Refx));
1495 drawuninstall(client, dstid);
1498 refx->client = client;
1499 refx->dimage = drawlookup(client, dstid, 1);
1501 memlsetrefresh(l, reffn, refx);
1505 i = allocmemimage(r, chan);
1512 rectclip(&i->clipr, r);
1513 if(drawinstall(client, dstid, i, 0) == 0){
1517 memfillcolor(i, value);
1520 /* allocate screen: 'A' id[4] imageid[4] fillid[4] public[1] */
1522 printmesg(fmt="LLLb", a, 1);
1526 dstid = BGLONG(a+1);
1529 if(drawlookupdscreen(dstid))
1530 error(Escreenexists);
1531 ddst = drawlookup(client, BGLONG(a+5), 1);
1532 dsrc = drawlookup(client, BGLONG(a+9), 1);
1533 if(ddst==0 || dsrc==0)
1534 error(Enodrawimage);
1535 if(drawinstallscreen(client, 0, dstid, ddst, dsrc, a[13]) == 0)
1539 /* set repl and clip: 'c' dstid[4] repl[1] clipR[4*4] */
1541 printmesg(fmt="LbR", a, 0);
1545 ddst = drawlookup(client, BGLONG(a+1), 1);
1547 error(Enodrawimage);
1549 error("cannot change repl/clipr of shared image");
1552 dst->flags |= Frepl;
1553 drawrectangle(&dst->clipr, a+6);
1556 /* draw: 'd' dstid[4] srcid[4] maskid[4] R[4*4] P[2*4] P[2*4] */
1558 printmesg(fmt="LLLRPP", a, 0);
1559 m = 1+4+4+4+4*4+2*4+2*4;
1562 dst = drawimage(client, a+1);
1563 dstid = BGLONG(a+1);
1564 src = drawimage(client, a+5);
1565 mask = drawimage(client, a+9);
1566 drawrectangle(&r, a+13);
1567 drawpoint(&p, a+29);
1568 drawpoint(&q, a+37);
1569 op = drawclientop(client);
1570 memdraw(dst, r, src, p, mask, q, op);
1571 dstflush(dstid, dst, r);
1574 /* toggle debugging: 'D' val[1] */
1576 printmesg(fmt="b", a, 0);
1582 /* ellipse: 'e' dstid[4] srcid[4] center[2*4] a[4] b[4] thick[4] sp[2*4] alpha[4] phi[4]*/
1585 printmesg(fmt="LLPlllPll", a, 0);
1586 m = 1+4+4+2*4+4+4+4+2*4+2*4;
1589 dst = drawimage(client, a+1);
1590 dstid = BGLONG(a+1);
1591 src = drawimage(client, a+5);
1596 error("invalid ellipse semidiameter");
1599 error("negative ellipse thickness");
1600 drawpoint(&sp, a+29);
1606 op = drawclientop(client);
1607 /* high bit indicates arc angles are present */
1609 if((ox & (1<<30)) == 0)
1611 memarc(dst, p, e0, e1, c, src, sp, ox, oy, op);
1613 memellipse(dst, p, e0, e1, c, src, sp, op);
1614 dstflush(dstid, dst, Rect(p.x-e0-j, p.y-e1-j, p.x+e0+j+1, p.y+e1+j+1));
1617 /* free: 'f' id[4] */
1619 printmesg(fmt="L", a, 1);
1623 ll = drawlookup(client, BGLONG(a+1), 0);
1624 if(ll && ll->dscreen && ll->dscreen->owner != client)
1625 ll->dscreen->owner->refreshme = 1;
1626 drawuninstall(client, BGLONG(a+1));
1629 /* free screen: 'F' id[4] */
1631 printmesg(fmt="L", a, 1);
1635 drawlookupscreen(client, BGLONG(a+1), &cs);
1636 drawuninstallscreen(client, cs);
1639 /* initialize font: 'i' fontid[4] nchars[4] ascent[1] */
1641 printmesg(fmt="Llb", a, 1);
1645 dstid = BGLONG(a+1);
1647 error("cannot use display as font");
1648 font = drawlookup(client, dstid, 1);
1650 error(Enodrawimage);
1651 if(font->image->layer)
1652 error("cannot use window as font");
1654 if(ni<=0 || ni>4096)
1655 error("bad font size (4096 chars max)");
1656 free(font->fchar); /* should we complain if non-zero? */
1657 font->fchar = malloc(ni*sizeof(FChar));
1658 if(font->fchar == 0)
1659 error("no memory for font");
1660 memset(font->fchar, 0, ni*sizeof(FChar));
1662 font->ascent = a[9];
1665 /* load character: 'l' fontid[4] srcid[4] index[2] R[4*4] P[2*4] left[1] width[1] */
1667 printmesg(fmt="LLSRPbb", a, 0);
1668 m = 1+4+4+2+4*4+2*4+1+1;
1671 font = drawlookup(client, BGLONG(a+1), 1);
1673 error(Enodrawimage);
1674 if(font->nfchar == 0)
1676 src = drawimage(client, a+5);
1678 if(ci >= font->nfchar)
1680 drawrectangle(&r, a+11);
1681 drawpoint(&p, a+27);
1682 memdraw(font->image, r, src, p, memopaque, p, S);
1683 fc = &font->fchar[ci];
1692 /* draw line: 'L' dstid[4] p0[2*4] p1[2*4] end0[4] end1[4] radius[4] srcid[4] sp[2*4] */
1694 printmesg(fmt="LPPlllLP", a, 0);
1695 m = 1+4+2*4+2*4+4+4+4+4+2*4;
1698 dst = drawimage(client, a+1);
1699 dstid = BGLONG(a+1);
1701 drawpoint(&q, a+13);
1706 error("negative line width");
1707 src = drawimage(client, a+33);
1708 drawpoint(&sp, a+37);
1709 op = drawclientop(client);
1710 memline(dst, p, q, e0, e1, j, src, sp, op);
1711 /* avoid memlinebbox if possible */
1712 if(dstid==0 || dst->layer!=nil){
1713 /* BUG: this is terribly inefficient: update maximal containing rect*/
1714 r = memlinebbox(p, q, e0, e1, j);
1715 dstflush(dstid, dst, insetrect(r, -(1+1+j)));
1719 /* create image mask: 'm' newid[4] id[4] */
1723 printmesg("LL", a, 0);
1731 /* attach to a named image: 'n' dstid[4] j[1] name[j] */
1733 printmesg(fmt="Lz", a, 0);
1738 if(j == 0) /* give me a non-empty name please */
1743 dstid = BGLONG(a+1);
1744 if(drawlookup(client, dstid, 0))
1745 error(Eimageexists);
1746 dn = drawlookupname(j, (char*)a+6);
1749 if(drawinstall(client, dstid, dn->dimage->image, 0) == 0)
1751 di = drawlookup(client, dstid, 0);
1753 error("draw: cannot happen");
1754 di->vers = dn->vers;
1755 di->name = smalloc(j+1);
1756 di->fromname = dn->dimage;
1757 di->fromname->ref++;
1758 memmove(di->name, a+6, j);
1760 client->infoid = dstid;
1763 /* name an image: 'N' dstid[4] in[1] j[1] name[j] */
1765 printmesg(fmt="Lbz", a, 0);
1771 if(j == 0) /* give me a non-empty name please */
1776 di = drawlookup(client, BGLONG(a+1), 0);
1778 error(Enodrawimage);
1782 drawaddname(client, di, j, (char*)a+7);
1784 dn = drawlookupname(j, (char*)a+7);
1787 if(dn->dimage != di)
1793 /* position window: 'o' id[4] r.min [2*4] screenr.min [2*4] */
1795 printmesg(fmt="LPP", a, 0);
1799 dst = drawimage(client, a+1);
1802 drawpoint(&q, a+13);
1803 r = dst->layer->screenr;
1804 ni = memlorigin(dst, p, q);
1806 error("image origin failed");
1809 addflush(dst->layer->screenr);
1810 ll = drawlookup(client, BGLONG(a+1), 1);
1811 drawrefreshscreen(ll, client);
1816 /* set compositing operator for next draw operation: 'O' op */
1818 printmesg(fmt="b", a, 0);
1825 /* filled polygon: 'P' dstid[4] n[2] wind[4] ignore[2*4] srcid[4] sp[2*4] p0[2*4] dp[2*2*n] */
1826 /* polygon: 'p' dstid[4] n[2] end0[4] end1[4] radius[4] srcid[4] sp[2*4] p0[2*4] dp[2*2*n] */
1829 printmesg(fmt="LslllLPP", a, 0);
1830 m = 1+4+2+4+4+4+4+2*4;
1833 dstid = BGLONG(a+1);
1834 dst = drawimage(client, a+1);
1837 error("negative count in polygon");
1844 error("negative polygon line width");
1846 src = drawimage(client, a+19);
1847 drawpoint(&sp, a+23);
1848 drawpoint(&p, a+31);
1850 pp = malloc(ni*sizeof(Point));
1854 if(dstid==0 || (screenimage && dst->layer && dst->layer->screen->image->data == screenimage->data))
1855 doflush = 1; /* simplify test in loop */
1859 for(y=0; y<ni; y++){
1862 u = drawcoord(u, a+n, ox, &p.x);
1863 u = drawcoord(u, a+n, oy, &p.y);
1870 c = memlineendsize(e0);
1875 c = memlineendsize(e1);
1880 if(*a=='P' && e0!=1 && e0 !=~0)
1883 r = Rect(q.x-oesize, q.y-oesize, q.x+oesize+1, q.y+oesize+1);
1884 combinerect(&r, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1));
1886 if(rectclip(&r, dst->clipr)) /* should perhaps be an arg to dstflush */
1887 dstflush(dstid, dst, r);
1892 dstflush(dstid, dst, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1));
1893 op = drawclientop(client);
1895 mempoly(dst, pp, ni, e0, e1, j, src, sp, op);
1897 memfillpoly(dst, pp, ni, e0, src, sp, op);
1902 /* read: 'r' id[4] R[4*4] */
1904 printmesg(fmt="LR", a, 0);
1908 i = drawimage(client, a+1);
1909 drawrectangle(&r, a+5);
1910 if(!rectinrect(r, i->r))
1911 error(Ereadoutside);
1912 c = bytesperline(r, i->depth);
1914 free(client->readdata);
1915 client->readdata = mallocz(c, 0);
1916 if(client->readdata == nil)
1917 error("readimage malloc failed");
1918 client->nreaddata = memunload(i, r, client->readdata, c);
1919 if(client->nreaddata < 0){
1920 free(client->readdata);
1921 client->readdata = nil;
1922 error("bad readimage call");
1926 /* string: 's' dstid[4] srcid[4] fontid[4] P[2*4] clipr[4*4] sp[2*4] ni[2] ni*(index[2]) */
1927 /* stringbg: 'x' dstid[4] srcid[4] fontid[4] P[2*4] clipr[4*4] sp[2*4] ni[2] bgid[4] bgpt[2*4] ni*(index[2]) */
1930 printmesg(fmt="LLLPRPs", a, 0);
1931 m = 1+4+4+4+2*4+4*4+2*4+2;
1937 dst = drawimage(client, a+1);
1938 dstid = BGLONG(a+1);
1939 src = drawimage(client, a+5);
1940 font = drawlookup(client, BGLONG(a+9), 1);
1942 error(Enodrawimage);
1943 if(font->nfchar == 0)
1945 drawpoint(&p, a+13);
1946 drawrectangle(&r, a+21);
1947 drawpoint(&sp, a+37);
1955 op = drawclientop(client);
1958 /* paint background */
1959 bg = drawimage(client, a+47);
1960 drawpoint(&q, a+51);
1962 r.min.y = p.y-font->ascent;
1964 r.max.y = r.min.y+Dy(font->image->r);
1968 if(ci<0 || ci>=font->nfchar){
1972 r.max.x += font->fchar[ci].width;
1975 memdraw(dst, r, bg, q, memopaque, ZP, op);
1981 if(ci<0 || ci>=font->nfchar){
1985 q = drawchar(dst, bg, q, src, &sp, font, ci, op);
1989 p.y -= font->ascent;
1990 dstflush(dstid, dst, Rect(p.x, p.y, q.x, p.y+Dy(font->image->r)));
1993 /* use public screen: 'S' id[4] chan[4] */
1995 printmesg(fmt="Ll", a, 0);
1999 dstid = BGLONG(a+1);
2002 dscrn = drawlookupdscreen(dstid);
2003 if(dscrn==0 || (dscrn->public==0 && dscrn->owner!=client))
2004 error(Enodrawscreen);
2005 if(dscrn->screen->image->chan != BGLONG(a+5))
2006 error("inconsistent chan");
2007 if(drawinstallscreen(client, dscrn, 0, 0, 0, 0) == 0)
2011 /* top or bottom windows: 't' top[1] nw[2] n*id[4] */
2013 printmesg(fmt="bsL", a, 0);
2025 lp = malloc(nw*sizeof(Memimage*));
2032 for(j=0; j<nw; j++){
2033 lp[j] = drawimage(client, a+1+1+2+j*4);
2034 if(lp[j]->layer == 0)
2035 error("images are not windows");
2036 if(lp[j]->layer->screen != lp[0]->layer->screen)
2037 error("images not on same screen");
2040 memltofrontn(lp, nw);
2042 memltorearn(lp, nw);
2043 if(screenimage && lp[0]->layer->screen->image->data == screenimage->data)
2045 addflush(lp[j]->layer->screenr);
2046 ll = drawlookup(client, BGLONG(a+1+1+2), 1);
2047 drawrefreshscreen(ll, client);
2054 printmesg(fmt="", a, 0);
2059 /* write: 'y' id[4] R[4*4] data[x*1] */
2060 /* write from compressed data: 'Y' id[4] R[4*4] data[x*1] */
2063 printmesg(fmt="LR", a, 0);
2064 // iprint("load %c\n", *a);
2068 dstid = BGLONG(a+1);
2069 dst = drawimage(client, a+1);
2070 drawrectangle(&r, a+5);
2071 if(!rectinrect(r, dst->r))
2072 error(Ewriteoutside);
2073 y = memload(dst, r, a+m, n-m, *a=='Y');
2075 error("bad writeimage call");
2076 dstflush(dstid, dst, r);
2105 * On 8 bit displays, load the default color map
2110 int r, g, b, cr, cg, cb, v;
2114 for(r=0,i=0; r!=4; r++)
2115 for(v=0; v!=4; v++,i+=16){
2116 for(g=0,j=v-r; g!=4; g++)
2117 for(b=0;b!=4;b++,j++){
2123 if(den == 0) /* divide check -- pick grey shades */
2124 cr = cg = cb = v*17;
2132 cr*0x01010101, cg*0x01010101, cb*0x01010101);