#include "u.h" #include "lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "errno.h" #include "devtab.h" #include "gnot.h" /* * Some fields in Bitmaps are overloaded: * ldepth = -1 means free. * base is next pointer when free. * Arena is a word containing N, followed by a pointer to its bitmap, * followed by N blocks. The bitmap pointer is zero if block is free. */ struct{ Ref; int bltuse; QLock blt; /* a group of bitblts in a single write is atomic */ Bitmap *map; /* arena */ Bitmap *free; /* free list */ ulong *words; /* storage */ ulong nwords; /* total in arena */ ulong *wfree; /* pointer to next free word */ int lastid; /* last allocated bitmap id */ }bit; #define FREE 0x80000000 void bitcompact(void); void bitfree(Bitmap*); extern Bitmap screen; enum{ Qdir, Qbitblt, }; Dirtab bitdir[]={ "bitblt", Qbitblt, 0, 0600, }; #define NBIT (sizeof bitdir/sizeof(Dirtab)) void bitreset(void) { int i; Bitmap *bp; bit.map = ialloc(conf.nbitmap * sizeof(Bitmap), 0); for(i=0,bp=bit.map; ildepth = -1; bp->base = (ulong*)(bp+1); } bp--; bp->base = 0; bit.map[0] = screen; /* bitmap 0 is screen */ bit.free = bit.map+1; bit.lastid = -1; bit.words = ialloc(conf.nbitbyte, 0); bit.nwords = conf.nbitbyte/sizeof(ulong); bit.wfree = bit.words; } void bitinit(void) { lock(&bit); bit.bltuse = 0; unlock(&bit); } Chan* bitattach(char *spec) { return devattach('b', spec); } Chan* bitclone(Chan *c, Chan *nc) { nc = devclone(c, nc); if(c->qid != CHDIR) incref(&bit); } int bitwalk(Chan *c, char *name) { return devwalk(c, name, bitdir, NBIT, devgen); } void bitstat(Chan *c, char *db) { devstat(c, db, bitdir, NBIT, devgen); } Chan * bitopen(Chan *c, int omode) { if(c->qid == CHDIR){ if(omode != OREAD) error(0, Eperm); }else{ lock(&bit); if(bit.bltuse){ unlock(&bit); error(0, Einuse); } bit.lastid = -1; unlock(&bit); incref(&bit); } c->mode = openmode(omode); c->flag |= COPEN; c->offset = 0; return c; } void bitcreate(Chan *c, char *name, int omode, ulong perm) { error(0, Eperm); } void bitremove(Chan *c) { error(0, Eperm); } void bitwstat(Chan *c, char *db) { error(0, Eperm); } void bitclose(Chan *c) { int i; Bitmap *bp; if(c->qid != CHDIR){ lock(&bit); /* FREE ALL THE BITMAPS: BUG */ if(--bit.ref == 0){ for(i=1,bp=&bit.map[1]; ildepth >= 0) bitfree(bp); bit.bltuse = 0; } unlock(&bit); } } #define GSHORT(p) (((p)[0]<<0) | ((p)[1]<<8)) #define GLONG(p) ((GSHORT(p)<<0) | (GSHORT(p+2)<<16)) long bitread(Chan *c, void *va, long n) { uchar *p; if(c->qid & CHDIR) return devdirread(c, va, n, bitdir, NBIT, devgen); if(c->qid != Qbitblt) error(0, Egreg); p = va; qlock(&bit.blt); if(waserror()){ qunlock(&bit.blt); nexterror(); } /* * Fuss about and figure out what to say. */ if(bit.lastid > 0){ if(n < 3) error(0, Ebadblt); p[0] = 'A'; p[1] = bit.lastid; p[2] = bit.lastid>>8; bit.lastid = -1; n = 3; goto done; } error(0, Ebadblt); done: qunlock(&bit.blt); return n; } long bitwrite(Chan *c, void *va, long n) { uchar *p; long m; long v; ulong l, nw, ws; Point pt; Rectangle rect; Bitmap *bp, *src, *dst; if(c->qid == CHDIR) error(0, Eisdir); if(c->qid != Qbitblt) error(0, Egreg); p = va; m = n; qlock(&bit.blt); if(waserror()){ qunlock(&bit.blt); nexterror(); } while(m > 0) switch(*p){ case 'a': /* * allocate: * 'a' 1 * ldepth 1 * Rectangle 16 * next read returns allocated bitmap id */ if(m < 18) error(0, Ebadblt); v = *(p+1); if(v != 0) /* BUG */ error(0, Ebadblt); ws = 1<<(5-v); /* pixels per word */ if(bit.free == 0) error(0, Enobitmap); rect.min.x = GLONG(p+2); rect.min.y = GLONG(p+6); rect.max.x = GLONG(p+10); rect.max.y = GLONG(p+14); if(rect.min.x >= 0) l = (rect.max.x+ws-1)/ws - rect.min.x/ws; else{ /* make positive before divide */ long t; t = (-rect.min.x)+ws-1; t = (t/ws)*ws; l = (t+rect.max.x+ws-1)/ws; } nw = l*Dy(rect); if(bit.wfree+l+2 > bit.words+bit.nwords){ bitcompact(); if(bit.wfree+l+1 > bit.words+bit.nwords) error(0, Enobitstore); } bp = bit.free; bit.free = (Bitmap*)(bp->base); *bit.wfree++ = nw; *bit.wfree++ = (ulong)bp; bp->base = bit.wfree; memset(bp->base, 0, nw*sizeof(ulong)); bit.wfree += nw; bp->zero = l*rect.min.y; if(rect.min.x >= 0) bp->zero += rect.min.x/ws; else bp->zero -= (-rect.min.x+ws-1)/ws; bp->zero = -bp->zero; bp->width = l; bp->ldepth = v; bp->rect = rect; bp->cache = 0; bit.lastid = bp-bit.map; m -= 18; p += 18; break; case 'b': /* * bitblt * 'b' 1 * dst id 2 * dst Point 8 * src id 2 * src Rectangle 16 * code 2 */ if(m < 31) error(0, Ebadblt); v = GSHORT(p+1); dst = &bit.map[v]; if(v<0 || v>=conf.nbitmap || dst->ldepth < 0) error(0, Ebadblt); pt.x = GLONG(p+3); pt.y = GLONG(p+7); v = GSHORT(p+11); src = &bit.map[v]; if(v<0 || v>=conf.nbitmap || src->ldepth < 0) error(0, Ebadblt); rect.min.x = GLONG(p+13); rect.min.y = GLONG(p+17); rect.max.x = GLONG(p+21); rect.max.y = GLONG(p+25); v = GSHORT(p+29); bitblt(dst, pt, src, rect, v); m -= 31; p += 31; break; case 'f': /* * free * 'f' 1 * id 2 */ if(m < 3) error(0, Ebadblt); v = GSHORT(p+1); dst = &bit.map[v]; if(v >= conf.nbitmap || dst->ldepth<0) error(0, Ebadblt); bitfree(dst); m -= 3; p += 3; break; } qunlock(&bit.blt); return n; } void bituserstr(Error *e, char *buf) { consuserstr(e, buf); } void biterrstr(Error *e, char *buf) { rooterrstr(e, buf); } void bitfree(Bitmap *bp) { bp->base[1] = 0; bp->ldepth = -1; bp->base = (ulong*)bit.free; bit.free = bp; } void bitcompact(void) { ulong *p1, *p2; print("bitcompact\n"); p1 = p2 = bit.words; while(p2 < bit.wfree){ if(p2[1] == 0){ p2 += 2 + p2[0]; continue; } if(p1 != p2){ memcpy(p1, p2, (2+p2[0])*sizeof(ulong)); ((Bitmap*)p1[1])->base = p1+2; } p2 += 2 + p1[0]; p1 += 2 + p1[0]; } bit.wfree = p1; print("bitcompact done\n"); }