commit 892de7987409ccd209dd4f43fb7717408d4a04bd from: rsc date: Mon Apr 19 05:54:21 2004 UTC various little bug fixes commit - 6b53e2a4d0d41305efa4b8b0800d119a13478ded commit + 892de7987409ccd209dd4f43fb7717408d4a04bd blob - 789ce1ca7d8d6f9e2e27bc09dc978263cddf676f blob + b5c4eaf82e6fb47ab423e60e77ef15ec68b6ab92 --- src/cmd/acme/dat.h +++ src/cmd/acme/dat.h @@ -174,6 +174,9 @@ struct Text uint org; uint q0; uint q1; + uint oldorg; + uint oldq0; + uint oldq1; int what; int tabstop; Window *w; blob - 15b1467045b911a358ab3df19b7f1ae00906c4e1 blob + 835be5b895416d208bca7daeb25fd100a9419b9a --- src/cmd/acme/exec.c +++ src/cmd/acme/exec.c @@ -580,6 +580,9 @@ get(Text *et, Text *t, Text *argt, int flag1, int _0, r = bytetorune(name, &n); for(i=0; ifile->ntext; i++){ u = t->file->text[i]; + u->oldorg = u->org; + u->oldq0 = u->q0; + u->oldq1 = u->q1; /* second and subsequent calls with zero an already empty buffer, but OK */ textreset(u); windirfree(u->w); @@ -601,6 +604,14 @@ get(Text *et, Text *t, Text *argt, int flag1, int _0, t->file->unread = FALSE; for(i=0; ifile->ntext; i++){ u = t->file->text[i]; + if(u->oldorg > u->file->b.nc) + u->oldorg = u->file->b.nc; + if(u->oldq0 > u->file->b.nc) + u->oldq0 = u->file->b.nc; + if(u->oldq1 > u->file->b.nc) + u->oldq1 = u->file->b.nc; + u->org = u->oldorg; + textshow(u, u->oldq0, u->oldq1, 1); textsetselect(&u->w->tag, u->w->tag.file->b.nc, u->w->tag.file->b.nc); textscrdraw(u); } blob - 17c2d06da477cf69b6f7c8bd965ea10de272ec68 blob + e7ac9953cdc039c005a4af255d643c772c22c2f2 --- src/cmd/bzip2/bzip2.c +++ src/cmd/bzip2/bzip2.c @@ -23,7 +23,9 @@ void main(int argc, char **argv) { int i, ok, stdout; + char **oargv; + oargv = argv; level = 6; stdout = 0; ARGBEGIN{ @@ -36,6 +38,20 @@ main(int argc, char **argv) case 'c': stdout++; break; + case 'd': + /* + * gnu tar expects bzip2 -d to decompress + * humor it. ugh. + */ + /* remove -d from command line - magic! */ + if(strcmp(argv[0], "-d") == 0){ + while(*argv++) + *(argv-1) = *argv; + }else + memmove(_args-1, _args, strlen(_args)+1); + exec("bunzip2", oargv); + sysfatal("exec bunzip2 failed"); + break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': level = ARGC() - '0'; blob - /dev/null blob + e43731e5fa0f4694698af027e5244e4b22704bed (mode 644) --- /dev/null +++ src/cmd/fmt.c @@ -0,0 +1,241 @@ +#include +#include +#include +#include + +/* + * block up paragraphs, possibly with indentation + */ + +int extraindent = 0; /* how many spaces to indent all lines */ +int indent = 0; /* current value of indent, before extra indent */ +int length = 70; /* how many columns per output line */ +int join = 1; /* can lines be joined? */ +int maxtab = 8; +Biobuf bin; +Biobuf bout; + +typedef struct Word Word; +struct Word{ + int bol; + int indent; + char text[1]; +}; + +void fmt(void); + +void +usage(void) +{ + fprint(2, "usage: %s [-j] [-i indent] [-l length] [file...]\n", argv0); + exits("usage"); +} + +void +main(int argc, char **argv) +{ + int i, f; + char *s, *err; + + ARGBEGIN{ + case 'i': + extraindent = atoi(EARGF(usage())); + break; + case 'j': + join = 0; + break; + case 'w': + case 'l': + length = atoi(EARGF(usage())); + break; + default: + usage(); + }ARGEND + + if(length <= indent){ + fprint(2, "%s: line length<=indentation\n", argv0); + exits("length"); + } + + s=getenv("tabstop"); + if(s!=nil && atoi(s)>0) + maxtab=atoi(s); + err = nil; + Binit(&bout, 1, OWRITE); + if(argc <= 0){ + Binit(&bin, 0, OREAD); + fmt(); + }else{ + for(i=0; itext, s, l); + w->text[l] = '\0'; + w->indent = indent; + w->bol = bol; + words = realloc(words, (*nwordp+1)*sizeof(Word*)); + words[(*nwordp)++] = w; + return words; +} + +Word** +parseline(char *line, Word **words, int *nwordp) +{ + int ind, l, bol; + + ind = indentof(&line); + indent = ind; + bol = 1; + for(;;){ + /* find next word */ + while(*line==' ' || *line=='\t') + line++; + if(*line == '\0'){ + if(bol) + return addword(words, nwordp, "", 0, -1, bol); + break; + } + /* how long is this word? */ + for(l=0; line[l]; l++) + if(line[l]==' ' || line[l]=='\t') + break; + words = addword(words, nwordp, line, l, indent, bol); + bol = 0; + line += l; + } + return words; +} + +void +printindent(int w) +{ + while(w >= maxtab){ + Bputc(&bout, '\t'); + w -= maxtab; + } + while(w > 0){ + Bputc(&bout, ' '); + w--; + } +} + +/* give extra space if word ends with period, etc. */ +int +nspaceafter(char *s) +{ + int n; + + n = strlen(s); + if(n < 2) + return 1; + if(isupper(s[0]) && n < 4) + return 1; + if(strchr(".!?", s[n-1]) != nil) + return 2; + return 1; +} + + +void +printwords(Word **w, int nw) +{ + int i, j, n, col, nsp; + + /* one output line per loop */ + for(i=0; iindent == -1){ + Bputc(&bout, '\n'); + if(++i == nw) /* out of words */ + break; + } + /* emit leading indent */ + col = extraindent+w[i]->indent; + printindent(col); + /* emit words until overflow; always emit at least one word */ + for(n=0;; n++){ + Bprint(&bout, "%s", w[i]->text); + col += utflen(w[i]->text); + if(++i == nw) + break; /* out of words */ + if(w[i]->indent != w[i-1]->indent) + break; /* indent change */ + nsp = nspaceafter(w[i-1]->text); + if(col+nsp+utflen(w[i]->text) > extraindent+length) + break; /* fold line */ + if(!join && n != 0 && w[i]->bol) + break; + for(j=0; jwindow, 0); - if (c) { + if(c){ if (debug) fprintf(stderr, "but: e x=%d y=%d c x=%d y=%d dx=%d dy=%d BORDR %d\n", e->x, e->y, c->x, c->y, c->dx, c->dy, BORDER); - if (e->x <= BORDER || e->x > (c->dx + BORDER) || - e->y <= BORDER || e->y > (c->dy + BORDER)) { + if(borderorient(c, e->x, e->y) != BorderUnknown){ switch (e->button) { case Button1: case Button2: @@ -63,11 +62,10 @@ button(XButtonEvent *e) default: return; } - } + } e->x += c->x - BORDER; e->y += c->y - BORDER; - } - else if (e->window != e->root) { + } else if (e->window != e->root) { if (debug) fprintf(stderr, "but no client: e x=%d y=%d\n", e->x, e->y); XTranslateCoordinates(dpy, e->window, s->root, e->x, e->y, blob - /dev/null blob + 20b6ac923c6b8f3fd22b83c694ee12b5c38fdd88 (mode 644) --- /dev/null +++ src/cmd/tweak.c @@ -0,0 +1,2058 @@ +#include +#include +#include +#include +#include +#include + +typedef struct Thing Thing; + +struct Thing +{ + Image *b; + Subfont *s; + char *name; /* file name */ + int face; /* is 48x48 face file or cursor file*/ + Rectangle r; /* drawing region */ + Rectangle tr; /* text region */ + Rectangle er; /* entire region */ + long c; /* character number in subfont */ + int mod; /* modified */ + int mag; /* magnification */ + Rune off; /* offset for subfont indices */ + Thing *parent; /* thing of which i'm an edit */ + Thing *next; +}; + +enum +{ + Border = 1, + Up = 1, + Down = 0, + Mag = 4, + Maxmag = 10, +}; + +enum +{ + NORMAL =0, + FACE =1, + CURSOR =2 +}; + +enum +{ + Mopen, + Mread, + Mwrite, + Mcopy, + Mchar, + Mpixels, + Mclose, + Mexit, +}; + +enum +{ + Blue = 54, +}; + +char *menu3str[] = { + [Mopen] "open", + [Mread] "read", + [Mwrite] "write", + [Mcopy] "copy", + [Mchar] "char", + [Mpixels] "pixels", + [Mclose] "close", + [Mexit] "exit", + 0, +}; + +Menu menu3 = { + menu3str +}; + +Cursor sweep0 = { + {-7, -7}, + {0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, + 0x03, 0xC0, 0x03, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xC0, 0x03, 0xC0, + 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0}, + {0x00, 0x00, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, + 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x7F, 0xFE, + 0x7F, 0xFE, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, + 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x00, 0x00} +}; + +Cursor box = { + {-7, -7}, + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, + 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, + {0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, + 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, + 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, + 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x00, 0x00} +}; + +Cursor sight = { + {-7, -7}, + {0x1F, 0xF8, 0x3F, 0xFC, 0x7F, 0xFE, 0xFB, 0xDF, + 0xF3, 0xCF, 0xE3, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xC7, 0xF3, 0xCF, + 0x7B, 0xDF, 0x7F, 0xFE, 0x3F, 0xFC, 0x1F, 0xF8,}, + {0x00, 0x00, 0x0F, 0xF0, 0x31, 0x8C, 0x21, 0x84, + 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, 0x7F, 0xFE, + 0x7F, 0xFE, 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, + 0x21, 0x84, 0x31, 0x8C, 0x0F, 0xF0, 0x00, 0x00,} +}; + +Cursor pixel = { + {-7, -7}, + {0x1f, 0xf8, 0x3f, 0xfc, 0x7f, 0xfe, 0xf8, 0x1f, + 0xf0, 0x0f, 0xe0, 0x07, 0xe0, 0x07, 0xfe, 0x7f, + 0xfe, 0x7f, 0xe0, 0x07, 0xe0, 0x07, 0xf0, 0x0f, + 0x78, 0x1f, 0x7f, 0xfe, 0x3f, 0xfc, 0x1f, 0xf8, }, + {0x00, 0x00, 0x0f, 0xf0, 0x31, 0x8c, 0x21, 0x84, + 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, 0x40, 0x02, + 0x40, 0x02, 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, + 0x21, 0x84, 0x31, 0x8c, 0x0f, 0xf0, 0x00, 0x00, } +}; + +Cursor busy = { + {-7, -7}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0c, 0x00, 0x8e, 0x1d, 0xc7, + 0xff, 0xe3, 0xff, 0xf3, 0xff, 0xff, 0x7f, 0xfe, + 0x3f, 0xf8, 0x17, 0xf0, 0x03, 0xe0, 0x00, 0x00,}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x82, + 0x04, 0x41, 0xff, 0xe1, 0x5f, 0xf1, 0x3f, 0xfe, + 0x17, 0xf0, 0x03, 0xe0, 0x00, 0x00, 0x00, 0x00,} +}; + +Cursor skull = { + {-7,-7}, + {0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0xe7, 0xe7, + 0xff, 0xff, 0xff, 0xff, 0x3f, 0xfc, 0x1f, 0xf8, + 0x0f, 0xf0, 0x3f, 0xfc, 0xff, 0xff, 0xff, 0xff, + 0xef, 0xf7, 0xc7, 0xe3, 0x00, 0x00, 0x00, 0x00,}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x03, + 0xE7, 0xE7, 0x3F, 0xFC, 0x0F, 0xF0, 0x0D, 0xB0, + 0x07, 0xE0, 0x06, 0x60, 0x37, 0xEC, 0xE4, 0x27, + 0xC3, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,} +}; + +Rectangle cntlr; /* control region */ +Rectangle editr; /* editing region */ +Rectangle textr; /* text region */ +Thing *thing; +Mouse mouse; +char hex[] = "0123456789abcdefABCDEF"; +jmp_buf err; +char *file; +int mag; +int but1val = 0; +int but2val = 255; +int invert = 0; +Image *values[256]; +Image *greyvalues[256]; +uchar data[8192]; + +Thing* tget(char*); +void mesg(char*, ...); +void drawthing(Thing*, int); +void xselect(void); +void menu(void); +void error(Display*, char*); +void buttons(int); +void drawall(void); +void tclose1(Thing*); + +void +main(int argc, char *argv[]) +{ + int i; + Event e; + Thing *t; + + mag = Mag; + if(initdraw(error, 0, "tweak") < 0){ + fprint(2, "tweak: initdraw failed: %r\n"); + exits("initdraw"); + } + for(i=0; i<256; i++){ + values[i] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, cmap2rgba(i)); + greyvalues[i] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, (i<<24)|(i<<16)|(i<<8)|0xFF); + if(values[i] == 0 || greyvalues[i] == 0) + drawerror(display, "can't allocate image"); + } + einit(Emouse|Ekeyboard); + eresized(0); + i = 1; + setjmp(err); + for(; iwhite, nil, ZP); + if(thing == 0) + return; + if(thing != t){ + for(nt=thing; nt->next!=t; nt=nt->next) + ; + draw(screen, Rect(screen->r.min.x, nt->er.max.y, editr.max.x, editr.max.y), + display->white, nil, ZP); + } + for(nt=t; nt; nt=nt->next){ + drawthing(nt, 0); + if(nt->next == 0){ + p = Pt(editr.min.x, nt->er.max.y); + draw(screen, Rpt(p, editr.max), display->white, nil, ZP); + } + } + mesg(""); +} + +void +eresized(int new) +{ + if(new && getwindow(display, Refnone) < 0) + error(display, "can't reattach to window"); + cntlr = insetrect(screen->clipr, 1); + editr = cntlr; + textr = editr; + textr.min.y = textr.max.y - font->height; + cntlr.max.y = cntlr.min.y + font->height; + editr.min.y = cntlr.max.y+1; + editr.max.y = textr.min.y-1; + draw(screen, screen->clipr, display->white, nil, ZP); + draw(screen, Rect(editr.min.x, editr.max.y, editr.max.x+1, editr.max.y+1), display->black, nil, ZP); + replclipr(screen, 0, editr); + drawall(); +} + +void +mesgstr(Point p, int line, char *s) +{ + Rectangle c, r; + + r.min = p; + r.min.y += line*font->height; + r.max.y = r.min.y+font->height; + r.max.x = editr.max.x; + c = screen->clipr; + replclipr(screen, 0, r); + draw(screen, r, values[0xDD], nil, ZP); + r.min.x++; + string(screen, r.min, display->black, ZP, font, s); + replclipr(screen, 0, c); + flushimage(display, 1); +} + +void +mesg(char *fmt, ...) +{ + char buf[1024]; + va_list arg; + + va_start(arg, fmt); + vseprint(buf, buf+sizeof(buf), fmt, arg); + va_end(arg); + mesgstr(textr.min, 0, buf); +} + +void +tmesg(Thing *t, int line, char *fmt, ...) +{ + char buf[1024]; + va_list arg; + + va_start(arg, fmt); + vseprint(buf, buf+sizeof(buf), fmt, arg); + va_end(arg); + mesgstr(t->tr.min, line, buf); +} + + +void +scntl(char *l) +{ + sprint(l, "mag: %d but1: %d but2: %d invert-on-copy: %c", mag, but1val, but2val, "ny"[invert]); +} + +void +cntl(void) +{ + char buf[256]; + + scntl(buf); + mesgstr(cntlr.min, 0, buf); +} + +void +stext(Thing *t, char *l0, char *l1) +{ + Fontchar *fc; + char buf[256]; + + l1[0] = 0; + sprint(buf, "depth:%d r:%d %d %d %d ", + t->b->depth, t->b->r.min.x, t->b->r.min.y, + t->b->r.max.x, t->b->r.max.y); + if(t->parent) + sprint(buf+strlen(buf), "mag: %d ", t->mag); + sprint(l0, "%s file: %s", buf, t->name); + if(t->c >= 0){ + fc = &t->parent->s->info[t->c]; + sprint(l1, "c(hex): %x c(char): %C x: %d " + "top: %d bottom: %d left: %d width: %d iwidth: %d", + (int)(t->c+t->parent->off), (int)(t->c+t->parent->off), + fc->x, fc->top, fc->bottom, fc->left, + fc->width, Dx(t->b->r)); + }else if(t->s) + sprint(l1, "offset(hex): %ux n:%d height:%d ascent:%d", + t->off, t->s->n, t->s->height, t->s->ascent); +} + +void +text(Thing *t) +{ + char l0[256], l1[256]; + + stext(t, l0, l1); + tmesg(t, 0, l0); + if(l1[0]) + tmesg(t, 1, l1); +} + +void +drawall(void) +{ + Thing *t; + + cntl(); + for(t=thing; t; t=t->next) + drawthing(t, 0); +} + +int +value(Image *b, int x) +{ + int v, l, w; + uchar mask; + + w = b->depth; + if(w > 8){ + mesg("ldepth too large"); + return 0; + } + l = xlog2(w); + mask = (1<r.min.x&~(7>>l); /* adjust x relative to first pixel */ + v = data[x>>(3-l)]; + v >>= ((7>>l)<>l))< screen->depth) + v >>= d - screen->depth; + else + while(d < screen->depth && d < 8){ + v |= v << d; + d <<= 1; + } + if(v<0 || v>255){ + mesg("internal error: bad color"); + return Blue; + } + return v; +} + +void +drawthing(Thing *nt, int link) +{ + int n, nl, nf, i, x, y, sx, sy, fdx, dx, dy, v; + Thing *t; + Subfont *s; + Image *b, *col; + Point p, p1, p2; + + if(link){ + nt->next = 0; + if(thing == 0){ + thing = nt; + y = editr.min.y; + }else{ + for(t=thing; t->next; t=t->next) + ; + t->next = nt; + y = t->er.max.y; + } + }else{ + if(thing == nt) + y = editr.min.y; + else{ + for(t=thing; t->next!=nt; t=t->next) + ; + y = t->er.max.y; + } + } + s = nt->s; + b = nt->b; + nl = font->height; + if(s || nt->c>=0) + nl += font->height; + fdx = Dx(editr) - 2*Border; + dx = Dx(b->r); + dy = Dy(b->r); + if(nt->mag > 1){ + dx *= nt->mag; + dy *= nt->mag; + fdx -= fdx%nt->mag; + } + nf = 1 + dx/fdx; + nt->er.min.y = y; + nt->er.min.x = editr.min.x; + nt->er.max.x = nt->er.min.x + Border + dx + Border; + if(nt->er.max.x > editr.max.x) + nt->er.max.x = editr.max.x; + nt->er.max.y = nt->er.min.y + Border + nf*(dy+Border); + nt->r = insetrect(nt->er, Border); + nt->er.max.x = editr.max.x; + draw(screen, nt->er, display->white, nil, ZP); + for(i=0; ir.min.x-1, nt->r.min.y+i*(Border+dy)); + /* draw portion of bitmap */ + p = Pt(p1.x+1, p1.y); + if(nt->mag == 1) + draw(screen, Rect(p.x, p.y, p.x+fdx+Dx(b->r), p.y+Dy(b->r)), + b, nil, Pt(b->r.min.x+i*fdx, b->r.min.y)); + else{ + for(y=b->r.min.y; yr.max.y; y++){ + sy = p.y+(y-b->r.min.y)*nt->mag; + if((n=unloadimage(b, Rect(b->r.min.x, y, b->r.max.x, y+1), data, sizeof data)) < 0) + fprint(2, "unloadimage: %r\n"); + for(x=b->r.min.x+i*(fdx/nt->mag); xr.max.x; x++){ + sx = p.x+(x-i*(fdx/nt->mag)-b->r.min.x)*nt->mag; + if(sx >= nt->r.max.x) + break; + v = bvalue(value(b, x), b->depth); + if(v == 255) + continue; + if(b->chan == GREY8) + draw(screen, Rect(sx, sy, sx+nt->mag, sy+nt->mag), + greyvalues[v], nil, ZP); + else + draw(screen, Rect(sx, sy, sx+nt->mag, sy+nt->mag), + values[v], nil, ZP); + } + + } + } + /* line down left */ + if(i == 0) + col = display->black; + else + col = display->white; + draw(screen, Rect(p1.x, p1.y, p1.x+1, p1.y+dy+Border), col, nil, ZP); + /* line across top */ + draw(screen, Rect(p1.x, p1.y-1, nt->r.max.x+Border, p1.y), display->black, nil, ZP); + p2 = p1; + if(i == nf-1){ + p2.x += 1 + dx%fdx; + col = display->black; + }else{ + p2.x = nt->r.max.x; + col = display->white; + } + /* line down right */ + draw(screen, Rect(p2.x, p2.y, p2.x+1, p2.y+dy+Border), col, nil, ZP); + /* line across bottom */ + if(i == nf-1){ + p1.y += Border+dy; + draw(screen, Rect(p1.x, p1.y-1, p2.x,p1.y), display->black, nil, ZP); + } + } + nt->tr.min.x = editr.min.x; + nt->tr.max.x = editr.max.x; + nt->tr.min.y = nt->er.max.y + Border; + nt->tr.max.y = nt->tr.min.y + nl; + nt->er.max.y = nt->tr.max.y + Border; + text(nt); +} + +int +tohex(int c) +{ + if('0'<=c && c<='9') + return c - '0'; + if('a'<=c && c<='f') + return 10 + (c - 'a'); + if('A'<=c && c<='F') + return 10 + (c - 'A'); + return 0; +} + +Thing* +tget(char *file) +{ + int i, j, fd, face, x, y, c, chan; + Image *b; + Subfont *s; + Thing *t; + Dir *d; + jmp_buf oerr; + uchar buf[256]; + char *data; + + buf[0] = '\0'; + errstr((char*)buf, sizeof buf); /* flush pending error message */ + memmove(oerr, err, sizeof err); + d = nil; + if(setjmp(err)){ + Err: + free(d); + memmove(err, oerr, sizeof err); + return 0; + } + fd = open(file, OREAD); + if(fd < 0){ + mesg("can't open %s: %r", file); + goto Err; + } + d = dirfstat(fd); + if(d == nil){ + mesg("can't stat bitmap file %s: %r", file); + close(fd); + goto Err; + } + if(read(fd, buf, 11) != 11){ + mesg("can't read %s: %r", file); + close(fd); + goto Err; + } + seek(fd, 0, 0); + data = (char*)buf; + if(*data == '{') + data++; + if(memcmp(data, "0x", 2)==0 && data[4]==','){ + /* + * cursor file + */ + face = CURSOR; + s = 0; + data = malloc(d->length+1); + if(data == 0){ + mesg("can't malloc buffer: %r"); + close(fd); + goto Err; + } + data[d->length] = 0; + if(read(fd, data, d->length) != d->length){ + mesg("can't read cursor file %s: %r", file); + close(fd); + goto Err; + } + b = allocimage(display, Rect(0, 0, 16, 32), GREY1, 0, DNofill); + if(b == 0){ + mesg("image alloc failed file %s: %r", file); + free(data); + close(fd); + goto Err; + } + i = 0; + for(x=0;x<64; ){ + if((c=data[i]) == '\0') + goto ill; + if(c=='0' && data[i+1] == 'x'){ + i += 2; + continue; + } + if(strchr(hex, c)){ + buf[x++] = (tohex(c)<<4) | tohex(data[i+1]); + i += 2; + continue; + } + i++; + } + loadimage(b, Rect(0, 0, 16, 32), buf, sizeof buf); + free(data); + }else if(memcmp(buf, "0x", 2)==0){ + /* + * face file + */ + face = FACE; + s = 0; + data = malloc(d->length+1); + if(data == 0){ + mesg("can't malloc buffer: %r"); + close(fd); + goto Err; + } + data[d->length] = 0; + if(read(fd, data, d->length) != d->length){ + mesg("can't read bitmap file %s: %r", file); + close(fd); + goto Err; + } + for(y=0,i=0; ilength; i++) + if(data[i] == '\n') + y++; + if(y == 0){ + ill: + mesg("ill-formed face file %s", file); + close(fd); + free(data); + goto Err; + } + for(x=0,i=0; (c=data[i])!='\n'; ){ + if(c==',' || c==' ' || c=='\t'){ + i++; + continue; + } + if(c=='0' && data[i+1] == 'x'){ + i += 2; + continue; + } + if(strchr(hex, c)){ + x += 4; + i++; + continue; + } + goto ill; + } + if(x % y) + goto ill; + switch(x / y){ + default: + goto ill; + case 1: + chan = GREY1; + break; + case 2: + chan = GREY2; + break; + case 4: + chan = GREY4; + break; + case 8: + chan = CMAP8; + break; + } + b = allocimage(display, Rect(0, 0, y, y), chan, 0, -1); + if(b == 0){ + mesg("image alloc failed file %s: %r", file); + free(data); + close(fd); + goto Err; + } + i = 0; + for(j=0; jlength) + s = readsubfonti(display, file, fd, b, 0); + } + close(fd); + t = malloc(sizeof(Thing)); + if(t == 0){ + nomem: + mesg("malloc failed: %r"); + if(s) + freesubfont(s); + else + freeimage(b); + goto Err; + } + t->name = strdup(file); + if(t->name == 0){ + free(t); + goto nomem; + } + t->b = b; + t->s = s; + t->face = face; + t->mod = 0; + t->parent = 0; + t->c = -1; + t->mag = 1; + t->off = 0; + memmove(err, oerr, sizeof err); + return t; +} + +int +atline(int x, Point p, char *line, char *buf) +{ + char *s, *c, *word, *hit; + int w, wasblank; + Rune r; + + wasblank = 1; + hit = 0; + word = 0; + for(s=line; *s; s+=w){ + w = chartorune(&r, s); + x += runestringnwidth(font, &r, 1); + if(wasblank && r!=' ') + word = s; + wasblank = 0; + if(r == ' '){ + if(x >= p.x) + break; + wasblank = 1; + } + if(r == ':') + hit = word; + } + if(x < p.x) + return 0; + c = utfrune(hit, ':'); + strncpy(buf, hit, c-hit); + buf[c-hit] = 0; + return 1; +} + +int +attext(Thing *t, Point p, char *buf) +{ + char l0[256], l1[256]; + + if(!ptinrect(p, t->tr)) + return 0; + stext(t, l0, l1); + if(p.y < t->tr.min.y+font->height) + return atline(t->r.min.x, p, l0, buf); + else + return atline(t->r.min.x, p, l1, buf); +} + +int +type(char *buf, char *tag) +{ + Rune r; + char *p; + + esetcursor(&busy); + p = buf; + for(;;){ + *p = 0; + mesg("%s: %s", tag, buf); + r = ekbd(); + switch(r){ + case '\n': + mesg(""); + esetcursor(0); + return p-buf; + case 0x15: /* control-U */ + p = buf; + break; + case '\b': + if(p > buf) + --p; + break; + default: + p += runetochar(p, &r); + } + } + return 0; /* shut up compiler */ +} + +void +textedit(Thing *t, char *tag) +{ + char buf[256]; + char *s; + Image *b; + Subfont *f; + Fontchar *fc, *nfc; + Rectangle r; + ulong chan; + int i, ld, d, w, c, doredraw, fdx, x; + Thing *nt; + + buttons(Up); + if(type(buf, tag) == 0) + return; + if(strcmp(tag, "file") == 0){ + for(s=buf; *s; s++) + if(*s <= ' '){ + mesg("illegal file name"); + return; + } + if(strcmp(t->name, buf) != 0){ + if(t->parent) + t->parent->mod = 1; + else + t->mod = 1; + } + for(nt=thing; nt; nt=nt->next) + if(t==nt || t->parent==nt || nt->parent==t){ + free(nt->name); + nt->name = strdup(buf); + if(nt->name == 0){ + mesg("malloc failed: %r"); + return; + } + text(nt); + } + return; + } + if(strcmp(tag, "depth") == 0){ + if(buf[0]<'0' || '9'8 || xlog2(d)<0){ + mesg("illegal ldepth"); + return; + } + if(d == t->b->depth) + return; + if(t->parent) + t->parent->mod = 1; + else + t->mod = 1; + if(d == 8) + chan = CMAP8; + else + chan = CHAN1(CGrey, d); + for(nt=thing; nt; nt=nt->next){ + if(nt!=t && nt!=t->parent && nt->parent!=t) + continue; + b = allocimage(display, nt->b->r, chan, 0, 0); + if(b == 0){ + nobmem: + mesg("image alloc failed: %r"); + return; + } + draw(b, b->r, nt->b, nil, nt->b->r.min); + freeimage(nt->b); + nt->b = b; + if(nt->s){ + b = allocimage(display, nt->b->r, chan, 0, -1); + if(b == 0) + goto nobmem; + draw(b, b->r, nt->b, nil, nt->b->r.min); + f = allocsubfont(t->name, nt->s->n, nt->s->height, nt->s->ascent, nt->s->info, b); + if(f == 0){ + nofmem: + freeimage(b); + mesg("can't make subfont: %r"); + return; + } + nt->s->info = 0; /* prevent it being freed */ + nt->s->bits = 0; + freesubfont(nt->s); + nt->s = f; + } + drawthing(nt, 0); + } + return; + } + if(strcmp(tag, "mag") == 0){ + if(buf[0]<'0' || '9'Maxmag){ + mesg("illegal magnification"); + return; + } + if(t->mag == ld) + return; + t->mag = ld; + redraw(t); + return; + } + if(strcmp(tag, "r") == 0){ + if(t->s){ + mesg("can't change rectangle of subfont\n"); + return; + } + s = buf; + r.min.x = strtoul(s, &s, 0); + r.min.y = strtoul(s, &s, 0); + r.max.x = strtoul(s, &s, 0); + r.max.y = strtoul(s, &s, 0); + if(Dx(r)<=0 || Dy(r)<=0){ + mesg("illegal rectangle"); + return; + } + if(t->parent) + t = t->parent; + for(nt=thing; nt; nt=nt->next){ + if(nt->parent==t && !rectinrect(nt->b->r, r)) + tclose1(nt); + } + b = allocimage(display, r, t->b->chan, 0, 0); + if(b == 0) + goto nobmem; + draw(b, r, t->b, nil, r.min); + freeimage(t->b); + t->b = b; + b = allocimage(display, r, t->b->chan, 0, 0); + if(b == 0) + goto nobmem; + redraw(t); + t->mod = 1; + return; + } + if(strcmp(tag, "ascent") == 0){ + if(buf[0]<'0' || '9't->s->height){ + mesg("illegal ascent"); + return; + } + if(t->s->ascent == ld) + return; + t->s->ascent = ld; + text(t); + t->mod = 1; + return; + } + if(strcmp(tag, "height") == 0){ + if(buf[0]<'0' || '9's->height == ld) + return; + t->s->height = ld; + text(t); + t->mod = 1; + return; + } + if(strcmp(tag, "left")==0 || strcmp(tag, "width") == 0){ + if(buf[0]<'0' || '9'parent->s->info[t->c]; + if(strcmp(tag, "left")==0){ + if(fc->left == ld) + return; + fc->left = ld; + }else{ + if(fc->width == ld) + return; + fc->width = ld; + } + text(t); + t->parent->mod = 1; + return; + } + if(strcmp(tag, "offset(hex)") == 0){ + if(!strchr(hex, buf[0])){ + illoff: + mesg("illegal offset"); + return; + } + s = 0; + ld = strtoul(buf, &s, 16); + if(*s) + goto illoff; + t->off = ld; + text(t); + for(nt=thing; nt; nt=nt->next) + if(nt->parent == t) + text(nt); + return; + } + if(strcmp(tag, "n") == 0){ + if(buf[0]<'0' || '9's; + if(w == f->n) + return; + doredraw = 0; + again: + for(nt=thing; nt; nt=nt->next) + if(nt->parent == t){ + doredraw = 1; + tclose1(nt); + goto again; + } + r = t->b->r; + if(w < f->n) + r.max.x = f->info[w].x; + b = allocimage(display, r, t->b->chan, 0, 0); + if(b == 0) + goto nobmem; + draw(b, b->r, t->b, nil, r.min); + fdx = Dx(editr) - 2*Border; + if(Dx(t->b->r)/fdx != Dx(b->r)/fdx) + doredraw = 1; + freeimage(t->b); + t->b = b; + b = allocimage(display, r, t->b->chan, 0, 0); + if(b == 0) + goto nobmem; + draw(b, b->r, t->b, nil, r.min); + nfc = malloc((w+1)*sizeof(Fontchar)); + if(nfc == 0){ + mesg("malloc failed"); + freeimage(b); + return; + } + fc = f->info; + for(i=0; i<=w && i<=f->n; i++) + nfc[i] = fc[i]; + if(w+1 < i) + memset(nfc+i, 0, ((w+1)-i)*sizeof(Fontchar)); + x = fc[f->n].x; + for(; i<=w; i++) + nfc[i].x = x; + f = allocsubfont(t->name, w, f->height, f->ascent, nfc, b); + if(f == 0) + goto nofmem; + t->s->bits = nil; /* don't free it */ + freesubfont(t->s); + f->info = nfc; + t->s = f; + if(doredraw) + redraw(thing); + else + drawthing(t, 0); + t->mod = 1; + return; + } + if(strcmp(tag, "iwidth") == 0){ + if(buf[0]<'0' || '9'b->r); + if(w == 0) + return; + r = t->parent->b->r; + r.max.x += w; + c = t->c; + t = t->parent; + f = t->s; + b = allocimage(display, r, t->b->chan, 0, 0); + if(b == 0) + goto nobmem; + fc = &f->info[c]; + draw(b, Rect(b->r.min.x, b->r.min.y, + b->r.min.x+(fc[1].x-t->b->r.min.x), b->r.min.y+Dy(t->b->r)), + t->b, nil, t->b->r.min); + draw(b, Rect(fc[1].x+w, b->r.min.y, w+t->b->r.max.x, b->r.min.y+Dy(t->b->r)), + t->b, nil, Pt(fc[1].x, t->b->r.min.y)); + fdx = Dx(editr) - 2*Border; + doredraw = 0; + if(Dx(t->b->r)/fdx != Dx(b->r)/fdx) + doredraw = 1; + freeimage(t->b); + t->b = b; + b = allocimage(display, r, t->b->chan, 0, 0); + if(b == 0) + goto nobmem; + draw(b, b->r, t->b, nil, t->b->r.min); + fc = &f->info[c+1]; + for(i=c+1; i<=f->n; i++, fc++) + fc->x += w; + f = allocsubfont(t->name, f->n, f->height, f->ascent, + f->info, b); + if(f == 0) + goto nofmem; + /* t->s and f share info; free carefully */ + fc = f->info; + t->s->bits = nil; + t->s->info = 0; + freesubfont(t->s); + f->info = fc; + t->s = f; + if(doredraw) + redraw(t); + else + drawthing(t, 0); + /* redraw all affected chars */ + for(nt=thing; nt; nt=nt->next){ + if(nt->parent!=t || nt->cinfo[nt->c]; + r.min.x = fc[0].x; + r.min.y = nt->b->r.min.y; + r.max.x = fc[1].x; + r.max.y = nt->b->r.max.y; + b = allocimage(display, r, nt->b->chan, 0, 0); + if(b == 0) + goto nobmem; + draw(b, r, t->b, nil, r.min); + doredraw = 0; + if(Dx(nt->b->r)/fdx != Dx(b->r)/fdx) + doredraw = 1; + freeimage(nt->b); + nt->b = b; + if(c != nt->c) + text(nt); + else{ + if(doredraw) + redraw(nt); + else + drawthing(nt, 0); + } + } + t->mod = 1; + return; + } + mesg("cannot edit %s in file %s", tag, t->name); +} + +void +cntledit(char *tag) +{ + char buf[256]; + ulong l; + + buttons(Up); + if(type(buf, tag) == 0) + return; + if(strcmp(tag, "mag") == 0){ + if(buf[0]<'0' || '9'Maxmag){ + mesg("illegal magnification"); + return; + } + mag = l; + cntl(); + return; + } + if(strcmp(tag, "but1")==0 + || strcmp(tag, "but2")==0){ + if(buf[0]<'0' || '9'255){ + mesg("illegal value"); + return; + } + if(strcmp(tag, "but1") == 0) + but1val = l; + else if(strcmp(tag, "but2") == 0) + but2val = l; + cntl(); + return; + } + if(strcmp(tag, "invert-on-copy")==0){ + if(buf[0]=='y' || buf[0]=='1') + invert = 1; + else if(buf[0]=='n' || buf[0]=='0') + invert = 0; + else{ + mesg("illegal value"); + return; + } + cntl(); + return; + } + mesg("cannot edit %s", tag); +} + +void +buttons(int ud) +{ + while((mouse.buttons==0) != ud) + mouse = emouse(); +} + +Point +screenpt(Thing *t, Point realp) +{ + int fdx, n; + Point p; + + fdx = Dx(editr)-2*Border; + if(t->mag > 1) + fdx -= fdx%t->mag; + p = mulpt(subpt(realp, t->b->r.min), t->mag); + if(fdx < Dx(t->b->r)*t->mag){ + n = p.x/fdx; + p.y += n * (Dy(t->b->r)*t->mag+Border); + p.x -= n * fdx; + } + p = addpt(p, t->r.min); + return p; +} + +Point +realpt(Thing *t, Point screenp) +{ + int fdx, n, dy; + Point p; + + fdx = (Dx(editr)-2*Border); + if(t->mag > 1) + fdx -= fdx%t->mag; + p.y = screenp.y-t->r.min.y; + p.x = 0; + if(fdx < Dx(t->b->r)*t->mag){ + dy = Dy(t->b->r)*t->mag+Border; + n = (p.y/dy); + p.x = n * fdx; + p.y -= n * dy; + } + p.x += screenp.x-t->r.min.x; + p = addpt(divpt(p, t->mag), t->b->r.min); + return p; +} + +int +sweep(int but, Rectangle *r) +{ + Thing *t; + Point p, q, lastq; + + esetcursor(&sweep0); + buttons(Down); + if(mouse.buttons != (1<<(but-1))){ + buttons(Up); + esetcursor(0); + return 0; + } + p = mouse.xy; + for(t=thing; t; t=t->next) + if(ptinrect(p, t->r)) + break; + if(t) + p = screenpt(t, realpt(t, p)); + r->min = p; + r->max = p; + esetcursor(&box); + lastq = ZP; + while(mouse.buttons == (1<<(but-1))){ + edrawgetrect(insetrect(*r, -Borderwidth), 1); + mouse = emouse(); + edrawgetrect(insetrect(*r, -Borderwidth), 0); + q = mouse.xy; + if(t) + q = screenpt(t, realpt(t, q)); + if(eqpt(q, lastq)) + continue; + *r = canonrect(Rpt(p, q)); + lastq = q; + } + esetcursor(0); + if(mouse.buttons){ + buttons(Up); + return 0; + } + return 1; +} + +void +openedit(Thing *t, Point pt, int c) +{ + int x, y; + Point p; + Rectangle r; + Rectangle br; + Fontchar *fc; + Thing *nt; + + if(t->b->depth > 8){ + mesg("image has depth %d; can't handle >8", t->b->depth); + return; + } + br = t->b->r; + if(t->s == 0){ + c = -1; + /* if big enough to bother, sweep box */ + if(Dx(br)<=16 && Dy(br)<=16) + r = br; + else{ + if(!sweep(1, &r)) + return; + r = rectaddpt(r, subpt(br.min, t->r.min)); + if(!rectclip(&r, br)) + return; + if(Dx(br) <= 8){ + r.min.x = br.min.x; + r.max.x = br.max.x; + }else if(Dx(r) < 4){ + toosmall: + mesg("rectangle too small"); + return; + } + if(Dy(br) <= 8){ + r.min.y = br.min.y; + r.max.y = br.max.y; + }else if(Dy(r) < 4) + goto toosmall; + } + }else if(c >= 0){ + fc = &t->s->info[c]; + r.min.x = fc[0].x; + r.min.y = br.min.y; + r.max.x = fc[1].x; + r.max.y = br.min.y + Dy(br); + }else{ + /* just point at character */ + fc = t->s->info; + p = addpt(pt, subpt(br.min, t->r.min)); + x = br.min.x; + y = br.min.y; + for(c=0; cs->n; c++,fc++){ + again: + r.min.x = x; + r.min.y = y; + r.max.x = x + fc[1].x - fc[0].x; + r.max.y = y + Dy(br); + if(ptinrect(p, r)) + goto found; + if(r.max.x >= br.min.x+Dx(t->r)){ + x -= Dx(t->r); + y += t->s->height; + if(fc[1].x > fc[0].x) + goto again; + } + x += fc[1].x - fc[0].x; + } + return; + found: + r = br; + r.min.x = fc[0].x; + r.max.x = fc[1].x; + } + nt = malloc(sizeof(Thing)); + if(nt == 0){ + nomem: + mesg("can't allocate: %r"); + return; + } + memset(nt, 0, sizeof(Thing)); + nt->c = c; + nt->b = allocimage(display, r, t->b->chan, 0, DNofill); + if(nt->b == 0){ + free(nt); + goto nomem; + } + draw(nt->b, r, t->b, nil, r.min); + nt->name = strdup(t->name); + if(nt->name == 0){ + freeimage(nt->b); + free(nt); + goto nomem; + } + nt->parent = t; + nt->mag = mag; + drawthing(nt, 1); +} + +void +ckinfo(Thing *t, Rectangle mod) +{ + int i, j, k, top, bot, n, zero; + Fontchar *fc; + Rectangle r; + Image *b; + Thing *nt; + + if(t->parent) + t = t->parent; + if(t->s==0 || Dy(t->b->r)==0) + return; + b = 0; + /* check bounding boxes */ + fc = &t->s->info[0]; + r.min.y = t->b->r.min.y; + r.max.y = t->b->r.max.y; + for(i=0; is->n; i++, fc++){ + r.min.x = fc[0].x; + r.max.x = fc[1].x; + if(!rectXrect(mod, r)) + continue; + if(b==0 || Dx(b->r)b->chan, 0, 0); + if(b == 0){ + mesg("can't alloc image"); + break; + } + } + draw(b, b->r, display->white, nil, ZP); + draw(b, b->r, t->b, nil, r.min); + top = 100000; + bot = 0; + n = 2+((Dx(r)/8)*t->b->depth); + for(j=0; jr.max.y; j++){ + memset(data, 0, n); + unloadimage(b, Rect(b->r.min.x, j, b->r.max.x, j+1), data, sizeof data); + zero = 1; + for(k=0; k j) + top = j; + bot = j+1; + } + } + if(top > j) + top = 0; + if(top!=fc->top || bot!=fc->bottom){ + fc->top = top; + fc->bottom = bot; + for(nt=thing; nt; nt=nt->next) + if(nt->parent==t && nt->c==i) + text(nt); + } + } + if(b) + freeimage(b); +} + +void +twidpix(Thing *t, Point p, int set) +{ + Image *b, *v; + int c; + + b = t->b; + if(!ptinrect(p, b->r)) + return; + if(set) + c = but1val; + else + c = but2val; + if(b->chan == GREY8) + v = greyvalues[c]; + else + v = values[c]; + draw(b, Rect(p.x, p.y, p.x+1, p.y+1), v, nil, ZP); + p = screenpt(t, p); + draw(screen, Rect(p.x, p.y, p.x+t->mag, p.y+t->mag), v, nil, ZP); +} + +void +twiddle(Thing *t) +{ + int set; + Point p, lastp; + Image *b; + Thing *nt; + Rectangle mod; + + if(mouse.buttons!=1 && mouse.buttons!=2){ + buttons(Up); + return; + } + set = mouse.buttons==1; + b = t->b; + lastp = addpt(b->r.min, Pt(-1, -1)); + mod = Rpt(addpt(b->r.max, Pt(1, 1)), lastp); + while(mouse.buttons){ + p = realpt(t, mouse.xy); + if(!eqpt(p, lastp)){ + lastp = p; + if(ptinrect(p, b->r)){ + for(nt=thing; nt; nt=nt->next) + if(nt->parent==t->parent || nt==t->parent) + twidpix(nt, p, set); + if(t->parent) + t->parent->mod = 1; + else + t->mod = 1; + if(p.x < mod.min.x) + mod.min.x = p.x; + if(p.y < mod.min.y) + mod.min.y = p.y; + if(p.x >= mod.max.x) + mod.max.x = p.x+1; + if(p.y >= mod.max.y) + mod.max.y = p.y+1; + } + } + mouse = emouse(); + } + ckinfo(t, mod); +} + +void +xselect(void) +{ + Thing *t; + char line[128], buf[128]; + Point p; + + if(ptinrect(mouse.xy, cntlr)){ + scntl(line); + if(atline(cntlr.min.x, mouse.xy, line, buf)){ + if(mouse.buttons == 1) + cntledit(buf); + else + buttons(Up); + return; + } + return; + } + for(t=thing; t; t=t->next){ + if(attext(t, mouse.xy, buf)){ + if(mouse.buttons == 1) + textedit(t, buf); + else + buttons(Up); + return; + } + if(ptinrect(mouse.xy, t->r)){ + if(t->parent == 0){ + if(mouse.buttons == 1){ + p = mouse.xy; + buttons(Up); + openedit(t, p, -1); + }else + buttons(Up); + return; + } + twiddle(t); + return; + } + } +} + +void +twrite(Thing *t) +{ + int i, j, x, y, fd, ws, ld; + Biobuf buf; + Rectangle r; + + if(t->parent) + t = t->parent; + esetcursor(&busy); + fd = create(t->name, OWRITE, 0666); + if(fd < 0){ + mesg("can't write %s: %r", t->name); + return; + } + if(t->face && t->b->depth <= 4){ + r = t->b->r; + ld = xlog2(t->b->depth); + /* This heuristic reflects peculiarly different formats */ + ws = 4; + if(t->face == 2) /* cursor file */ + ws = 1; + else if(Dx(r)<32 || ld==0) + ws = 2; + Binit(&buf, fd, OWRITE); + if(t->face == CURSOR) + Bprint(&buf, "{"); + for(y=r.min.y; yb, Rect(r.min.x, y, r.max.x, y+1), data, sizeof data); + j = 0; + for(x=r.min.x; x>ld){ + Bprint(&buf, "0x"); + for(i=0; iface == CURSOR){ + switch(y){ + case 3: case 7: case 11: case 19: case 23: case 27: + Bprint(&buf, "\n "); + break; + case 15: + Bprint(&buf, "},\n{"); + break; + case 31: + Bprint(&buf, "}\n"); + break; + } + }else + Bprint(&buf, "\n"); + } + Bterm(&buf); + }else + if(writeimage(fd, t->b, 0)<0 || (t->s && writesubfont(fd, t->s)<0)){ + close(fd); + mesg("can't write %s: %r", t->name); + } + t->mod = 0; + close(fd); + mesg("wrote %s", t->name); +} + +void +tpixels(void) +{ + Thing *t; + Point p, lastp; + + esetcursor(&pixel); + for(;;){ + buttons(Down); + if(mouse.buttons != 4) + break; + for(t=thing; t; t=t->next){ + lastp = Pt(-1, -1); + if(ptinrect(mouse.xy, t->r)){ + while(ptinrect(mouse.xy, t->r) && mouse.buttons==4){ + p = realpt(t, mouse.xy); + if(!eqpt(p, lastp)){ + if(p.y != lastp.y) + unloadimage(t->b, Rect(t->b->r.min.x, p.y, t->b->r.max.x, p.y+1), data, sizeof data); + mesg("[%d,%d] = %d=0x%ux", p.x, p.y, value(t->b, p.x), value(t->b, p.x)); + lastp = p; + } + mouse = emouse(); + } + goto Continue; + } + } + mouse = emouse(); + Continue:; + } + buttons(Up); + esetcursor(0); +} + +void +tclose1(Thing *t) +{ + Thing *nt; + + if(t == thing) + thing = t->next; + else{ + for(nt=thing; nt->next!=t; nt=nt->next) + ; + nt->next = t->next; + } + do + for(nt=thing; nt; nt=nt->next) + if(nt->parent == t){ + tclose1(nt); + break; + } + while(nt); + if(t->s) + freesubfont(t->s); + else + freeimage(t->b); + free(t->name); + free(t); +} + +void +tclose(Thing *t) +{ + Thing *ct; + + if(t->mod){ + mesg("%s modified", t->name); + t->mod = 0; + return; + } + /* fiddle to save redrawing unmoved things */ + if(t == thing) + ct = 0; + else + for(ct=thing; ct; ct=ct->next) + if(ct->next==t || ct->next->parent==t) + break; + tclose1(t); + if(ct) + ct = ct->next; + else + ct = thing; + redraw(ct); +} + +void +tread(Thing *t) +{ + Thing *nt, *new; + Fontchar *i; + Rectangle r; + int nclosed; + + if(t->parent) + t = t->parent; + new = tget(t->name); + if(new == 0) + return; + nclosed = 0; + again: + for(nt=thing; nt; nt=nt->next) + if(nt->parent == t){ + if(!rectinrect(nt->b->r, new->b->r) + || new->b->depth!=nt->b->depth){ + closeit: + nclosed++; + nt->parent = 0; + tclose1(nt); + goto again; + } + if((t->s==0) != (new->s==0)) + goto closeit; + if((t->face==0) != (new->face==0)) + goto closeit; + if(t->s){ /* check same char */ + if(nt->c >= new->s->n) + goto closeit; + i = &new->s->info[nt->c]; + r.min.x = i[0].x; + r.max.x = i[1].x; + r.min.y = new->b->r.min.y; + r.max.y = new->b->r.max.y; + if(!eqrect(r, nt->b->r)) + goto closeit; + } + nt->parent = new; + draw(nt->b, nt->b->r, new->b, nil, nt->b->r.min); + } + new->next = t->next; + if(t == thing) + thing = new; + else{ + for(nt=thing; nt->next!=t; nt=nt->next) + ; + nt->next = new; + } + if(t->s) + freesubfont(t->s); + else + freeimage(t->b); + free(t->name); + free(t); + for(nt=thing; nt; nt=nt->next) + if(nt==new || nt->parent==new) + if(nclosed == 0) + drawthing(nt, 0); /* can draw in place */ + else{ + redraw(nt); /* must redraw all below */ + break; + } +} + +void +tchar(Thing *t) +{ + char buf[256], *p; + Rune r; + ulong c, d; + + if(t->s == 0){ + t = t->parent; + if(t==0 || t->s==0){ + mesg("not a subfont"); + return; + } + } + if(type(buf, "char (hex or character or hex-hex)") == 0) + return; + if(utflen(buf) == 1){ + chartorune(&r, buf); + c = r; + d = r; + }else{ + if(!strchr(hex, buf[0])){ + mesg("illegal hex character"); + return; + } + c = strtoul(buf, 0, 16); + d = c; + p = utfrune(buf, '-'); + if(p){ + d = strtoul(p+1, 0, 16); + if(d < c){ + mesg("invalid range"); + return; + } + } + } + c -= t->off; + d -= t->off; + while(c <= d){ + if(c<0 || c>=t->s->n){ + mesg("0x%lux not in font %s", c+t->off, t->name); + return; + } + openedit(t, Pt(0, 0), c); + c++; + } +} + +void +apply(void (*f)(Thing*)) +{ + Thing *t; + + esetcursor(&sight); + buttons(Down); + if(mouse.buttons == 4) + for(t=thing; t; t=t->next) + if(ptinrect(mouse.xy, t->er)){ + buttons(Up); + f(t); + break; + } + buttons(Up); + esetcursor(0); +} + +int +complement(Image *t) +{ + int i, n; + uchar *buf; + + n = Dy(t->r)*bytesperline(t->r, t->depth); + buf = malloc(n); + if(buf == 0) + return 0; + unloadimage(t, t->r, buf, n); + for(i=0; ir, buf, n); + free(buf); + return 1; +} + +void +copy(void) +{ + Thing *st, *dt, *nt; + Rectangle sr, dr, fr; + Image *tmp; + Point p1, p2; + int but, up; + + if(!sweep(3, &sr)) + return; + for(st=thing; st; st=st->next) + if(rectXrect(sr, st->r)) + break; + if(st == 0) + return; + /* click gives full rectangle */ + if(Dx(sr)<4 && Dy(sr)<4) + sr = st->r; + rectclip(&sr, st->r); + p1 = realpt(st, sr.min); + p2 = realpt(st, Pt(sr.min.x, sr.max.y)); + up = 0; + if(p1.x != p2.x){ /* swept across a fold */ + onafold: + mesg("sweep spans a fold"); + goto Return; + } + p2 = realpt(st, sr.max); + sr.min = p1; + sr.max = p2; + fr.min = screenpt(st, sr.min); + fr.max = screenpt(st, sr.max); + p1 = subpt(p2, p1); /* diagonal */ + if(p1.x==0 || p1.y==0) + return; + border(screen, fr, -1, values[Blue], ZP); + esetcursor(&box); + for(; mouse.buttons==0; mouse=emouse()){ + for(dt=thing; dt; dt=dt->next) + if(ptinrect(mouse.xy, dt->er)) + break; + if(up) + edrawgetrect(insetrect(dr, -Borderwidth), 0); + up = 0; + if(dt == 0) + continue; + dr.max = screenpt(dt, realpt(dt, mouse.xy)); + dr.min = subpt(dr.max, mulpt(p1, dt->mag)); + if(!rectXrect(dr, dt->r)) + continue; + edrawgetrect(insetrect(dr, -Borderwidth), 1); + up = 1; + } + /* if up==1, we had a hit */ + esetcursor(0); + if(up) + edrawgetrect(insetrect(dr, -Borderwidth), 0); + but = mouse.buttons; + buttons(Up); + if(!up || but!=4) + goto Return; + dt = 0; + for(nt=thing; nt; nt=nt->next) + if(rectXrect(dr, nt->r)){ + if(dt){ + mesg("ambiguous sweep"); + return; + } + dt = nt; + } + if(dt == 0) + goto Return; + p1 = realpt(dt, dr.min); + p2 = realpt(dt, Pt(dr.min.x, dr.max.y)); + if(p1.x != p2.x) + goto onafold; + p2 = realpt(dt, dr.max); + dr.min = p1; + dr.max = p2; + + if(invert){ + tmp = allocimage(display, dr, dt->b->chan, 0, 255); + if(tmp == 0){ + nomem: + mesg("can't allocate temporary"); + goto Return; + } + draw(tmp, dr, st->b, nil, sr.min); + if(!complement(tmp)) + goto nomem; + draw(dt->b, dr, tmp, nil, dr.min); + freeimage(tmp); + }else + draw(dt->b, dr, st->b, nil, sr.min); + if(dt->parent){ + draw(dt->parent->b, dr, dt->b, nil, dr.min); + dt = dt->parent; + } + drawthing(dt, 0); + for(nt=thing; nt; nt=nt->next) + if(nt->parent==dt && rectXrect(dr, nt->b->r)){ + draw(nt->b, dr, dt->b, nil, dr.min); + drawthing(nt, 0); + } + ckinfo(dt, dr); + dt->mod = 1; + +Return: + /* clear blue box */ + drawthing(st, 0); +} + +void +menu(void) +{ + Thing *t; + char *mod; + int sel; + char buf[256]; + + sel = emenuhit(3, &mouse, &menu3); + switch(sel){ + case Mopen: + if(type(buf, "file")){ + t = tget(buf); + if(t) + drawthing(t, 1); + } + break; + case Mwrite: + apply(twrite); + break; + case Mread: + apply(tread); + break; + case Mchar: + apply(tchar); + break; + case Mcopy: + copy(); + break; + case Mpixels: + tpixels(); + break; + case Mclose: + apply(tclose); + break; + case Mexit: + mod = 0; + for(t=thing; t; t=t->next) + if(t->mod){ + mod = t->name; + t->mod = 0; + } + if(mod){ + mesg("%s modified", mod); + break; + } + esetcursor(&skull); + buttons(Down); + if(mouse.buttons == 4){ + buttons(Up); + exits(0); + } + buttons(Up); + esetcursor(0); + break; + } +}