8 typedef struct Thing Thing;
14 char *name; /* file name */
15 int face; /* is 48x48 face file or cursor file*/
16 Rectangle r; /* drawing region */
17 Rectangle tr; /* text region */
18 Rectangle er; /* entire region */
19 long c; /* character number in subfont */
20 int mod; /* modified */
21 int mag; /* magnification */
22 Rune off; /* offset for subfont indices */
23 Thing *parent; /* thing of which i'm an edit */
78 {0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0,
79 0x03, 0xC0, 0x03, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF,
80 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xC0, 0x03, 0xC0,
81 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0},
82 {0x00, 0x00, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80,
83 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x7F, 0xFE,
84 0x7F, 0xFE, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80,
85 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x00, 0x00}
90 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
91 0xFF, 0xFF, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F,
92 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xFF, 0xFF,
93 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
94 {0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE,
95 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
96 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
97 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x00, 0x00}
102 {0x1F, 0xF8, 0x3F, 0xFC, 0x7F, 0xFE, 0xFB, 0xDF,
103 0xF3, 0xCF, 0xE3, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF,
104 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xC7, 0xF3, 0xCF,
105 0x7B, 0xDF, 0x7F, 0xFE, 0x3F, 0xFC, 0x1F, 0xF8,},
106 {0x00, 0x00, 0x0F, 0xF0, 0x31, 0x8C, 0x21, 0x84,
107 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, 0x7F, 0xFE,
108 0x7F, 0xFE, 0x41, 0x82, 0x41, 0x82, 0x41, 0x82,
109 0x21, 0x84, 0x31, 0x8C, 0x0F, 0xF0, 0x00, 0x00,}
114 {0x1f, 0xf8, 0x3f, 0xfc, 0x7f, 0xfe, 0xf8, 0x1f,
115 0xf0, 0x0f, 0xe0, 0x07, 0xe0, 0x07, 0xfe, 0x7f,
116 0xfe, 0x7f, 0xe0, 0x07, 0xe0, 0x07, 0xf0, 0x0f,
117 0x78, 0x1f, 0x7f, 0xfe, 0x3f, 0xfc, 0x1f, 0xf8, },
118 {0x00, 0x00, 0x0f, 0xf0, 0x31, 0x8c, 0x21, 0x84,
119 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, 0x40, 0x02,
120 0x40, 0x02, 0x41, 0x82, 0x41, 0x82, 0x41, 0x82,
121 0x21, 0x84, 0x31, 0x8c, 0x0f, 0xf0, 0x00, 0x00, }
126 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
127 0x00, 0x00, 0x00, 0x0c, 0x00, 0x8e, 0x1d, 0xc7,
128 0xff, 0xe3, 0xff, 0xf3, 0xff, 0xff, 0x7f, 0xfe,
129 0x3f, 0xf8, 0x17, 0xf0, 0x03, 0xe0, 0x00, 0x00,},
130 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
131 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x82,
132 0x04, 0x41, 0xff, 0xe1, 0x5f, 0xf1, 0x3f, 0xfe,
133 0x17, 0xf0, 0x03, 0xe0, 0x00, 0x00, 0x00, 0x00,}
138 {0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0xe7, 0xe7,
139 0xff, 0xff, 0xff, 0xff, 0x3f, 0xfc, 0x1f, 0xf8,
140 0x0f, 0xf0, 0x3f, 0xfc, 0xff, 0xff, 0xff, 0xff,
141 0xef, 0xf7, 0xc7, 0xe3, 0x00, 0x00, 0x00, 0x00,},
142 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x03,
143 0xE7, 0xE7, 0x3F, 0xFC, 0x0F, 0xF0, 0x0D, 0xB0,
144 0x07, 0xE0, 0x06, 0x60, 0x37, 0xEC, 0xE4, 0x27,
145 0xC3, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,}
148 Rectangle cntlr; /* control region */
149 Rectangle editr; /* editing region */
150 Rectangle textr; /* text region */
153 char hex[] = "0123456789abcdefABCDEF";
161 Image *greyvalues[256];
164 Thing* tget(char*, int);
165 void mesg(char*, ...);
166 void drawthing(Thing*, int);
169 void error(Display*, char*);
172 void tclose1(Thing*);
177 fprint(2, "usage: tweak [-W winsize] file...\n");
182 main(volatile int argc, char **volatile argv)
191 winsize = EARGF(usage());
197 if(initdraw(error, 0, "tweak") < 0){
198 fprint(2, "tweak: initdraw failed: %r\n");
201 for(i=0; i<256; i++){
202 values[i] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, cmap2rgba(i));
203 greyvalues[i] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, (i<<24)|(i<<16)|(i<<8)|0xFF);
204 if(values[i] == 0 || greyvalues[i] == 0)
205 drawerror(display, "can't allocate image");
207 einit(Emouse|Ekeyboard);
213 t = tget(argv[i], 1);
221 flushimage(display, 1);
231 if(mouse.buttons & 3){
235 if(mouse.buttons & 4)
245 for(i=0; (1<<i) <= n; i++)
248 fprint(2, "log2 %d = 0\n", n);
253 error(Display *d, char *s)
258 mesg("can't read %s: %s: %r", file, s);
260 mesg("/dev/bitblt error: %s", s);
272 if(thing==0 || thing==t)
273 draw(screen, editr, display->white, nil, ZP);
277 for(nt=thing; nt->next!=t; nt=nt->next)
279 draw(screen, Rect(screen->r.min.x, nt->er.max.y, editr.max.x, editr.max.y),
280 display->white, nil, ZP);
282 for(nt=t; nt; nt=nt->next){
285 p = Pt(editr.min.x, nt->er.max.y);
286 draw(screen, Rpt(p, editr.max), display->white, nil, ZP);
295 if(new && getwindow(display, Refnone) < 0)
296 error(display, "can't reattach to window");
297 cntlr = insetrect(screen->clipr, 1);
300 textr.min.y = textr.max.y - font->height;
301 cntlr.max.y = cntlr.min.y + font->height;
302 editr.min.y = cntlr.max.y+1;
303 editr.max.y = textr.min.y-1;
304 draw(screen, screen->clipr, display->white, nil, ZP);
305 draw(screen, Rect(editr.min.x, editr.max.y, editr.max.x+1, editr.max.y+1), display->black, nil, ZP);
306 replclipr(screen, 0, editr);
311 mesgstr(Point p, int line, char *s)
316 r.min.y += line*font->height;
317 r.max.y = r.min.y+font->height;
318 r.max.x = editr.max.x;
320 replclipr(screen, 0, r);
321 draw(screen, r, values[0xDD], nil, ZP);
323 string(screen, r.min, display->black, ZP, font, s);
324 replclipr(screen, 0, c);
325 flushimage(display, 1);
335 vseprint(buf, buf+sizeof(buf), fmt, arg);
337 mesgstr(textr.min, 0, buf);
341 tmesg(Thing *t, int line, char *fmt, ...)
347 vseprint(buf, buf+sizeof(buf), fmt, arg);
349 mesgstr(t->tr.min, line, buf);
356 sprint(l, "mag: %d but1: %d but2: %d invert-on-copy: %c", mag, but1val, but2val, "ny"[invert]);
365 mesgstr(cntlr.min, 0, buf);
369 stext(Thing *t, char *l0, char *l1)
375 sprint(buf, "depth:%d r:%d %d %d %d ",
376 t->b->depth, t->b->r.min.x, t->b->r.min.y,
377 t->b->r.max.x, t->b->r.max.y);
379 sprint(buf+strlen(buf), "mag: %d ", t->mag);
380 sprint(l0, "%s file: %s", buf, t->name);
382 fc = &t->parent->s->info[t->c];
383 sprint(l1, "c(hex): %x c(char): %C x: %d "
384 "top: %d bottom: %d left: %d width: %d iwidth: %d",
385 (int)(t->c+t->parent->off), (int)(t->c+t->parent->off),
386 fc->x, fc->top, fc->bottom, fc->left,
387 fc->width, Dx(t->b->r));
389 sprint(l1, "offset(hex): %ux n:%d height:%d ascent:%d",
390 t->off, t->s->n, t->s->height, t->s->ascent);
391 else if(t->face == CURSOR)
392 sprint(l0+strlen(l0), " cursor:%d", Dx(t->b->r));
398 char l0[256], l1[256];
412 for(t=thing; t; t=t->next)
417 value(Image *b, int x)
424 mesg("ldepth too large");
428 mask = (1<<w)-1; /* ones at right end of word */
429 x -= b->r.min.x&~(7>>l); /* adjust x relative to first pixel */
431 v >>= ((7>>l)<<l) - ((x&(7>>l))<<l); /* pixel at right end of word */
432 v &= mask; /* pixel at right end of word */
440 if(d > screen->depth)
441 v >>= d - screen->depth;
443 while(d < screen->depth && d < 8){
448 mesg("internal error: bad color");
455 drawthing(Thing *nt, int link)
457 int n, nl, nf, i, x, y, sx, sy, fdx, dx, dy, v;
469 for(t=thing; t->next; t=t->next)
478 for(t=thing; t->next!=nt; t=t->next)
488 fdx = Dx(editr) - 2*Border;
498 nt->er.min.x = editr.min.x;
499 nt->er.max.x = nt->er.min.x + Border + dx + Border;
500 if(nt->er.max.x > editr.max.x)
501 nt->er.max.x = editr.max.x;
502 nt->er.max.y = nt->er.min.y + Border + nf*(dy+Border);
503 nt->r = insetrect(nt->er, Border);
504 nt->er.max.x = editr.max.x;
505 draw(screen, nt->er, display->white, nil, ZP);
507 p1 = Pt(nt->r.min.x-1, nt->r.min.y+i*(Border+dy));
508 /* draw portion of bitmap */
509 p = Pt(p1.x+1, p1.y);
511 draw(screen, Rect(p.x, p.y, p.x+fdx+Dx(b->r), p.y+Dy(b->r)),
512 b, nil, Pt(b->r.min.x+i*fdx, b->r.min.y));
514 for(y=b->r.min.y; y<b->r.max.y; y++){
515 sy = p.y+(y-b->r.min.y)*nt->mag;
516 if((n=unloadimage(b, Rect(b->r.min.x, y, b->r.max.x, y+1), data, sizeof data)) < 0)
517 fprint(2, "unloadimage: %r\n");
518 for(x=b->r.min.x+i*(fdx/nt->mag); x<b->r.max.x; x++){
519 sx = p.x+(x-i*(fdx/nt->mag)-b->r.min.x)*nt->mag;
520 if(sx >= nt->r.max.x)
522 v = bvalue(value(b, x), b->depth);
526 draw(screen, Rect(sx, sy, sx+nt->mag, sy+nt->mag),
527 greyvalues[v], nil, ZP);
529 draw(screen, Rect(sx, sy, sx+nt->mag, sy+nt->mag),
537 col = display->black;
539 col = display->white;
540 draw(screen, Rect(p1.x, p1.y, p1.x+1, p1.y+dy+Border), col, nil, ZP);
541 /* line across top */
542 draw(screen, Rect(p1.x, p1.y-1, nt->r.max.x+Border, p1.y), display->black, nil, ZP);
546 col = display->black;
549 col = display->white;
551 /* line down right */
552 draw(screen, Rect(p2.x, p2.y, p2.x+1, p2.y+dy+Border), col, nil, ZP);
553 /* line across bottom */
556 draw(screen, Rect(p1.x, p1.y-1, p2.x,p1.y), display->black, nil, ZP);
559 nt->tr.min.x = editr.min.x;
560 nt->tr.max.x = editr.max.x;
561 nt->tr.min.y = nt->er.max.y + Border;
562 nt->tr.max.y = nt->tr.min.y + nl;
563 nt->er.max.y = nt->tr.max.y + Border;
573 return 10 + (c - 'a');
575 return 10 + (c - 'A');
580 tget(char *file, int extra)
582 int i, j, fd, face, x, y, c, chan;
593 errstr((char*)buf, sizeof buf); /* flush pending error message */
594 memmove(oerr, err, sizeof err);
599 memmove(err, oerr, sizeof err);
602 fd = open(file, OREAD);
604 mesg("can't open %s: %r", file);
609 mesg("can't stat bitmap file %s: %r", file);
613 if(read(fd, buf, 11) != 11){
614 mesg("can't read %s: %r", file);
622 if(memcmp(data, "0x", 2)==0 && data[4]==','){
628 data = malloc(d->length+1);
630 mesg("can't malloc buffer: %r");
635 if(read(fd, data, d->length) != d->length){
636 mesg("can't read cursor file %s: %r", file);
642 if((c=data[i]) == '\0' || x > 256) {
643 if(x == 64 || x == 256)
645 mesg("ill-formed cursor file %s", file);
649 if(c=='0' && data[i+1] == 'x'){
654 buf[x++] = (tohex(c)<<4) | tohex(data[i+1]);
662 r = Rect(0, 0, 16, 32);
664 r = Rect(0, 0, 32, 64);
665 b = allocimage(display, r, GREY1, 0, DNofill);
667 mesg("image alloc failed file %s: %r", file);
672 loadimage(b, r, buf, sizeof buf);
674 }else if(memcmp(buf, "0x", 2)==0){
680 data = malloc(d->length+1);
682 mesg("can't malloc buffer: %r");
687 if(read(fd, data, d->length) != d->length){
688 mesg("can't read bitmap file %s: %r", file);
692 for(y=0,i=0; i<d->length; i++)
697 mesg("ill-formed face file %s", file);
702 for(x=0,i=0; (c=data[i])!='\n'; ){
703 if(c==',' || c==' ' || c=='\t'){
707 if(c=='0' && data[i+1] == 'x'){
736 b = allocimage(display, Rect(0, 0, y, y), chan, 0, -1);
738 mesg("image alloc failed file %s: %r", file);
745 for(x=0; (c=data[i])!='\n'; ){
746 if(c=='0' && data[i+1] == 'x'){
751 buf[x++] = ~((tohex(c)<<4) | tohex(data[i+1]));
758 loadimage(b, Rect(0, j, y, j+1), buf, sizeof buf);
764 b = readimage(display, fd, 0);
766 mesg("can't read bitmap file %s: %r", file);
770 if(seek(fd, 0, 1) < d->length)
771 s = readsubfonti(display, file, fd, b, 0);
774 t = mallocz(sizeof(Thing), 1);
777 mesg("malloc failed: %r");
784 t->name = strdup(file);
797 if(face == CURSOR && extra && Dx(t->b->r) == 16) {
798 // Make 32x32 cursor as second image.
803 nt = mallocz(sizeof(Thing), 1);
806 nt->name = strdup("");
811 b = allocimage(display, Rect(0, 0, 32, 64), GREY1, 0, DNofill);
817 memmove(c.clr, buf, 64);
818 scalecursor(&c2, &c);
819 memmove(buf, c2.clr, 256);
820 loadimage(b, b->r, buf, sizeof buf);
831 memmove(err, oerr, sizeof err);
836 atline(int x, Point p, char *line, char *buf)
838 char *s, *c, *word, *hit;
845 for(s=line; *s; s+=w){
846 w = chartorune(&r, s);
847 x += runestringnwidth(font, &r, 1);
848 if(wasblank && r!=' ')
861 c = utfrune(hit, ':');
862 strncpy(buf, hit, c-hit);
868 attext(Thing *t, Point p, char *buf)
870 char l0[256], l1[256];
872 if(!ptinrect(p, t->tr))
875 if(p.y < t->tr.min.y+font->height)
876 return atline(t->r.min.x, p, l0, buf);
878 return atline(t->r.min.x, p, l1, buf);
882 type(char *buf, char *tag)
891 mesg("%s: %s", tag, buf);
898 case 0x15: /* control-U */
906 p += runetochar(p, &r);
909 /* return 0; shut up compiler */
913 textedit(Thing *t, char *tag)
922 int i, ld, d, w, c, doredraw, fdx, x;
926 if(type(buf, tag) == 0)
928 if(strcmp(tag, "file") == 0){
931 mesg("illegal file name");
934 if(strcmp(t->name, buf) != 0){
940 for(nt=thing; nt; nt=nt->next)
941 if(t==nt || t->parent==nt || nt->parent==t){
943 nt->name = strdup(buf);
945 mesg("malloc failed: %r");
952 if(strcmp(tag, "depth") == 0){
953 if(buf[0]<'0' || '9'<buf[0] || (d=atoi(buf))<0 || d>8 || xlog2(d)<0){
954 mesg("illegal ldepth");
966 chan = CHAN1(CGrey, d);
967 for(nt=thing; nt; nt=nt->next){
968 if(nt!=t && nt!=t->parent && nt->parent!=t)
970 b = allocimage(display, nt->b->r, chan, 0, 0);
973 mesg("image alloc failed: %r");
976 draw(b, b->r, nt->b, nil, nt->b->r.min);
980 b = allocimage(display, nt->b->r, chan, 0, -1);
983 draw(b, b->r, nt->b, nil, nt->b->r.min);
984 f = allocsubfont(t->name, nt->s->n, nt->s->height, nt->s->ascent, nt->s->info, b);
988 mesg("can't make subfont: %r");
991 nt->s->info = 0; /* prevent it being freed */
1000 if(strcmp(tag, "mag") == 0){
1001 if(buf[0]<'0' || '9'<buf[0] || (ld=atoi(buf))<=0 || ld>Maxmag){
1002 mesg("illegal magnification");
1011 if(strcmp(tag, "r") == 0){
1013 mesg("can't change rectangle of subfont\n");
1017 r.min.x = strtoul(s, &s, 0);
1018 r.min.y = strtoul(s, &s, 0);
1019 r.max.x = strtoul(s, &s, 0);
1020 r.max.y = strtoul(s, &s, 0);
1021 if(Dx(r)<=0 || Dy(r)<=0){
1022 mesg("illegal rectangle");
1027 for(nt=thing; nt; nt=nt->next){
1028 if(nt->parent==t && !rectinrect(nt->b->r, r))
1031 b = allocimage(display, r, t->b->chan, 0, 0);
1034 draw(b, r, t->b, nil, r.min);
1037 b = allocimage(display, r, t->b->chan, 0, 0);
1044 if(strcmp(tag, "ascent") == 0){
1045 if(buf[0]<'0' || '9'<buf[0] || (ld=atoi(buf))<0 || ld>t->s->height){
1046 mesg("illegal ascent");
1049 if(t->s->ascent == ld)
1056 if(strcmp(tag, "height") == 0){
1057 if(buf[0]<'0' || '9'<buf[0] || (ld=atoi(buf))<0){
1058 mesg("illegal height");
1061 if(t->s->height == ld)
1068 if(strcmp(tag, "left")==0 || strcmp(tag, "width") == 0){
1069 if(buf[0]<'0' || '9'<buf[0] || (ld=atoi(buf))<0){
1070 mesg("illegal value");
1073 fc = &t->parent->s->info[t->c];
1074 if(strcmp(tag, "left")==0){
1087 if(strcmp(tag, "offset(hex)") == 0){
1088 if(!strchr(hex, buf[0])){
1090 mesg("illegal offset");
1094 ld = strtoul(buf, &s, 16);
1099 for(nt=thing; nt; nt=nt->next)
1104 if(strcmp(tag, "n") == 0){
1105 if(buf[0]<'0' || '9'<buf[0] || (w=atoi(buf))<=0){
1114 for(nt=thing; nt; nt=nt->next)
1115 if(nt->parent == t){
1122 r.max.x = f->info[w].x;
1123 b = allocimage(display, r, t->b->chan, 0, 0);
1126 draw(b, b->r, t->b, nil, r.min);
1127 fdx = Dx(editr) - 2*Border;
1128 if(Dx(t->b->r)/fdx != Dx(b->r)/fdx)
1132 b = allocimage(display, r, t->b->chan, 0, 0);
1135 draw(b, b->r, t->b, nil, r.min);
1136 nfc = malloc((w+1)*sizeof(Fontchar));
1138 mesg("malloc failed");
1143 for(i=0; i<=w && i<=f->n; i++)
1146 memset(nfc+i, 0, ((w+1)-i)*sizeof(Fontchar));
1150 f = allocsubfont(t->name, w, f->height, f->ascent, nfc, b);
1153 t->s->bits = nil; /* don't free it */
1164 if(strcmp(tag, "iwidth") == 0){
1165 if(buf[0]<'0' || '9'<buf[0] || (w=atoi(buf))<0){
1166 mesg("illegal iwidth");
1172 r = t->parent->b->r;
1177 b = allocimage(display, r, t->b->chan, 0, 0);
1181 draw(b, Rect(b->r.min.x, b->r.min.y,
1182 b->r.min.x+(fc[1].x-t->b->r.min.x), b->r.min.y+Dy(t->b->r)),
1183 t->b, nil, t->b->r.min);
1184 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)),
1185 t->b, nil, Pt(fc[1].x, t->b->r.min.y));
1186 fdx = Dx(editr) - 2*Border;
1188 if(Dx(t->b->r)/fdx != Dx(b->r)/fdx)
1192 b = allocimage(display, r, t->b->chan, 0, 0);
1195 draw(b, b->r, t->b, nil, t->b->r.min);
1197 for(i=c+1; i<=f->n; i++, fc++)
1199 f = allocsubfont(t->name, f->n, f->height, f->ascent,
1203 /* t->s and f share info; free carefully */
1214 /* redraw all affected chars */
1215 for(nt=thing; nt; nt=nt->next){
1216 if(nt->parent!=t || nt->c<c)
1218 fc = &f->info[nt->c];
1220 r.min.y = nt->b->r.min.y;
1222 r.max.y = nt->b->r.max.y;
1223 b = allocimage(display, r, nt->b->chan, 0, 0);
1226 draw(b, r, t->b, nil, r.min);
1228 if(Dx(nt->b->r)/fdx != Dx(b->r)/fdx)
1244 mesg("cannot edit %s in file %s", tag, t->name);
1254 if(type(buf, tag) == 0)
1256 if(strcmp(tag, "mag") == 0){
1257 if(buf[0]<'0' || '9'<buf[0] || (l=atoi(buf))<=0 || l>Maxmag){
1258 mesg("illegal magnification");
1265 if(strcmp(tag, "but1")==0
1266 || strcmp(tag, "but2")==0){
1267 if(buf[0]<'0' || '9'<buf[0] || (long)(l=atoi(buf))<0 || l>255){
1268 mesg("illegal value");
1271 if(strcmp(tag, "but1") == 0)
1273 else if(strcmp(tag, "but2") == 0)
1278 if(strcmp(tag, "invert-on-copy")==0){
1279 if(buf[0]=='y' || buf[0]=='1')
1281 else if(buf[0]=='n' || buf[0]=='0')
1284 mesg("illegal value");
1290 mesg("cannot edit %s", tag);
1296 while((mouse.buttons==0) != ud)
1301 screenpt(Thing *t, Point realp)
1306 fdx = Dx(editr)-2*Border;
1309 p = mulpt(subpt(realp, t->b->r.min), t->mag);
1310 if(fdx < Dx(t->b->r)*t->mag){
1312 p.y += n * (Dy(t->b->r)*t->mag+Border);
1315 p = addpt(p, t->r.min);
1320 realpt(Thing *t, Point screenp)
1325 fdx = (Dx(editr)-2*Border);
1328 p.y = screenp.y-t->r.min.y;
1330 if(fdx < Dx(t->b->r)*t->mag){
1331 dy = Dy(t->b->r)*t->mag+Border;
1336 p.x += screenp.x-t->r.min.x;
1337 p = addpt(divpt(p, t->mag), t->b->r.min);
1342 sweep(int but, Rectangle *r)
1347 esetcursor(&sweep0);
1349 if(mouse.buttons != (1<<(but-1))){
1355 for(t=thing; t; t=t->next)
1356 if(ptinrect(p, t->r))
1359 p = screenpt(t, realpt(t, p));
1364 while(mouse.buttons == (1<<(but-1))){
1365 edrawgetrect(insetrect(*r, -Borderwidth), 1);
1367 edrawgetrect(insetrect(*r, -Borderwidth), 0);
1370 q = screenpt(t, realpt(t, q));
1373 *r = canonrect(Rpt(p, q));
1385 openedit(Thing *t, Point pt, int c)
1394 if(t->b->depth > 8){
1395 mesg("image has depth %d; can't handle >8", t->b->depth);
1401 /* if big enough to bother, sweep box */
1402 if(Dx(br)<=16 && Dy(br)<=16)
1407 r = rectaddpt(r, subpt(br.min, t->r.min));
1408 if(!rectclip(&r, br))
1413 }else if(Dx(r) < 4){
1415 mesg("rectangle too small");
1425 fc = &t->s->info[c];
1429 r.max.y = br.min.y + Dy(br);
1431 /* just point at character */
1433 p = addpt(pt, subpt(br.min, t->r.min));
1436 for(c=0; c<t->s->n; c++,fc++){
1440 r.max.x = x + fc[1].x - fc[0].x;
1441 r.max.y = y + Dy(br);
1444 if(r.max.x >= br.min.x+Dx(t->r)){
1447 if(fc[1].x > fc[0].x)
1450 x += fc[1].x - fc[0].x;
1458 nt = malloc(sizeof(Thing));
1461 mesg("can't allocate: %r");
1464 memset(nt, 0, sizeof(Thing));
1466 nt->b = allocimage(display, r, t->b->chan, 0, DNofill);
1471 draw(nt->b, r, t->b, nil, r.min);
1472 nt->name = strdup(t->name);
1484 ckinfo(Thing *t, Rectangle mod)
1486 int i, j, k, top, bot, n, zero;
1494 if(t->s==0 || Dy(t->b->r)==0)
1497 /* check bounding boxes */
1498 fc = &t->s->info[0];
1499 r.min.y = t->b->r.min.y;
1500 r.max.y = t->b->r.max.y;
1501 for(i=0; i<t->s->n; i++, fc++){
1504 if(!rectXrect(mod, r))
1506 if(b==0 || Dx(b->r)<Dx(r)){
1509 b = allocimage(display, rectsubpt(r, r.min), t->b->chan, 0, 0);
1511 mesg("can't alloc image");
1515 draw(b, b->r, display->white, nil, ZP);
1516 draw(b, b->r, t->b, nil, r.min);
1519 n = 2+((Dx(r)/8)*t->b->depth);
1520 for(j=0; j<b->r.max.y; j++){
1522 unloadimage(b, Rect(b->r.min.x, j, b->r.max.x, j+1), data, sizeof data);
1537 if(top!=fc->top || bot!=fc->bottom){
1540 for(nt=thing; nt; nt=nt->next)
1541 if(nt->parent==t && nt->c==i)
1550 twidpix(Thing *t, Point p, int set)
1556 if(!ptinrect(p, b->r))
1562 if(b->chan == GREY8)
1566 draw(b, Rect(p.x, p.y, p.x+1, p.y+1), v, nil, ZP);
1568 draw(screen, Rect(p.x, p.y, p.x+t->mag, p.y+t->mag), v, nil, ZP);
1580 if(mouse.buttons!=1 && mouse.buttons!=2){
1584 set = mouse.buttons==1;
1586 lastp = addpt(b->r.min, Pt(-1, -1));
1587 mod = Rpt(addpt(b->r.max, Pt(1, 1)), lastp);
1588 while(mouse.buttons){
1589 p = realpt(t, mouse.xy);
1590 if(!eqpt(p, lastp)){
1592 if(ptinrect(p, b->r)){
1593 for(nt=thing; nt; nt=nt->next)
1594 if(nt->parent==t->parent || nt==t->parent)
1595 twidpix(nt, p, set);
1604 if(p.x >= mod.max.x)
1606 if(p.y >= mod.max.y)
1619 char line[128], buf[128];
1622 if(ptinrect(mouse.xy, cntlr)){
1624 if(atline(cntlr.min.x, mouse.xy, line, buf)){
1625 if(mouse.buttons == 1)
1633 for(t=thing; t; t=t->next){
1634 if(attext(t, mouse.xy, buf)){
1635 if(mouse.buttons == 1)
1641 if(ptinrect(mouse.xy, t->r)){
1643 if(mouse.buttons == 1){
1660 int i, j, x, y, fd, ws, ld;
1667 fd = create(t->name, OWRITE, 0666);
1669 mesg("can't write %s: %r", t->name);
1672 if(t->face && t->b->depth <= 4){
1674 ld = xlog2(t->b->depth);
1675 /* This heuristic reflects peculiarly different formats */
1677 if(t->face == 2) /* cursor file */
1679 else if(Dx(r)<32 || ld==0)
1681 Binit(&buf, fd, OWRITE);
1682 if(t->face == CURSOR)
1684 for(y=r.min.y; y<r.max.y; y++){
1685 unloadimage(t->b, Rect(r.min.x, y, r.max.x, y+1), data, sizeof data);
1687 for(x=r.min.x; x<r.max.x; j+=ws,x+=ws*8>>ld){
1690 Bprint(&buf, "%.2x", data[i+j]);
1693 if(t->face == CURSOR) {
1695 Bprint(&buf, "},\n{");
1696 else if(y == Dy(r)-1)
1697 Bprint(&buf, "}\n");
1699 Bprint(&buf, "\n\t");
1705 if(writeimage(fd, t->b, 0)<0 || (t->s && writesubfont(fd, t->s)<0)){
1707 mesg("can't write %s: %r", t->name);
1711 mesg("wrote %s", t->name);
1723 if(mouse.buttons != 4)
1725 for(t=thing; t; t=t->next){
1727 if(ptinrect(mouse.xy, t->r)){
1728 while(ptinrect(mouse.xy, t->r) && mouse.buttons==4){
1729 p = realpt(t, mouse.xy);
1730 if(!eqpt(p, lastp)){
1732 unloadimage(t->b, Rect(t->b->r.min.x, p.y, t->b->r.max.x, p.y+1), data, sizeof data);
1733 mesg("[%d,%d] = %d=0x%ux", p.x, p.y, value(t->b, p.x), value(t->b, p.x));
1756 for(nt=thing; nt->next!=t; nt=nt->next)
1761 for(nt=thing; nt; nt=nt->next)
1762 if(nt->parent == t){
1781 mesg("%s modified", t->name);
1785 /* fiddle to save redrawing unmoved things */
1789 for(ct=thing; ct; ct=ct->next)
1790 if(ct->next==t || ct->next->parent==t)
1810 new = tget(t->name, 0);
1815 for(nt=thing; nt; nt=nt->next)
1816 if(nt->parent == t){
1817 if(!rectinrect(nt->b->r, new->b->r)
1818 || new->b->depth!=nt->b->depth){
1825 if((t->s==0) != (new->s==0))
1827 if((t->face==0) != (new->face==0))
1829 if(t->s){ /* check same char */
1830 if(nt->c >= new->s->n)
1832 i = &new->s->info[nt->c];
1835 r.min.y = new->b->r.min.y;
1836 r.max.y = new->b->r.max.y;
1837 if(!eqrect(r, nt->b->r))
1841 draw(nt->b, nt->b->r, new->b, nil, nt->b->r.min);
1843 new->next = t->next;
1847 for(nt=thing; nt->next!=t; nt=nt->next)
1857 for(nt=thing; nt; nt=nt->next)
1858 if(nt==new || nt->parent==new)
1860 drawthing(nt, 0); /* can draw in place */
1862 redraw(nt); /* must redraw all below */
1876 if(t==0 || t->s==0){
1877 mesg("not a subfont");
1881 if(type(buf, "char (hex or character or hex-hex)") == 0)
1883 if(utflen(buf) == 1){
1884 chartorune(&r, buf);
1888 if(!strchr(hex, buf[0])){
1889 mesg("illegal hex character");
1892 c = strtoul(buf, 0, 16);
1894 p = utfrune(buf, '-');
1896 d = strtoul(p+1, 0, 16);
1898 mesg("invalid range");
1906 if((long)c<0 || c>=t->s->n){
1907 mesg("0x%lux not in font %s", c+t->off, t->name);
1910 openedit(t, Pt(0, 0), c);
1916 apply(void (*f)(Thing*))
1922 if(mouse.buttons == 4)
1923 for(t=thing; t; t=t->next)
1924 if(ptinrect(mouse.xy, t->er)){
1934 complement(Image *t)
1939 n = Dy(t->r)*bytesperline(t->r, t->depth);
1943 unloadimage(t, t->r, buf, n);
1946 loadimage(t, t->r, buf, n);
1954 Thing *st, *dt, *nt;
1955 Rectangle sr, dr, fr;
1962 for(st=thing; st; st=st->next)
1963 if(rectXrect(sr, st->r))
1967 /* click gives full rectangle */
1968 if(Dx(sr)<4 && Dy(sr)<4)
1970 rectclip(&sr, st->r);
1971 p1 = realpt(st, sr.min);
1972 p2 = realpt(st, Pt(sr.min.x, sr.max.y));
1974 if(p1.x != p2.x){ /* swept across a fold */
1976 mesg("sweep spans a fold");
1979 p2 = realpt(st, sr.max);
1982 fr.min = screenpt(st, sr.min);
1983 fr.max = screenpt(st, sr.max);
1984 p1 = subpt(p2, p1); /* diagonal */
1985 if(p1.x==0 || p1.y==0)
1987 border(screen, fr, -1, values[Blue], ZP);
1989 for(; mouse.buttons==0; mouse=emouse()){
1990 for(dt=thing; dt; dt=dt->next)
1991 if(ptinrect(mouse.xy, dt->er))
1994 edrawgetrect(insetrect(dr, -Borderwidth), 0);
1998 dr.max = screenpt(dt, realpt(dt, mouse.xy));
1999 dr.min = subpt(dr.max, mulpt(p1, dt->mag));
2000 if(!rectXrect(dr, dt->r))
2002 edrawgetrect(insetrect(dr, -Borderwidth), 1);
2005 /* if up==1, we had a hit */
2008 edrawgetrect(insetrect(dr, -Borderwidth), 0);
2009 but = mouse.buttons;
2014 for(nt=thing; nt; nt=nt->next)
2015 if(rectXrect(dr, nt->r)){
2017 mesg("ambiguous sweep");
2024 p1 = realpt(dt, dr.min);
2025 p2 = realpt(dt, Pt(dr.min.x, dr.max.y));
2028 p2 = realpt(dt, dr.max);
2033 tmp = allocimage(display, dr, dt->b->chan, 0, 255);
2036 mesg("can't allocate temporary");
2039 draw(tmp, dr, st->b, nil, sr.min);
2040 if(!complement(tmp))
2042 draw(dt->b, dr, tmp, nil, dr.min);
2045 draw(dt->b, dr, st->b, nil, sr.min);
2047 draw(dt->parent->b, dr, dt->b, nil, dr.min);
2051 for(nt=thing; nt; nt=nt->next)
2052 if(nt->parent==dt && rectXrect(dr, nt->b->r)){
2053 draw(nt->b, dr, dt->b, nil, dr.min);
2060 /* clear blue box */
2072 sel = emenuhit(3, &mouse, &menu3);
2075 if(type(buf, "file")){
2101 for(t=thing; t; t=t->next)
2107 mesg("%s modified", mod);
2112 if(mouse.buttons == 4){