]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/tweak.c
9bootfat: rename open() to fileinit and make it static as its really a internal funct...
[plan9front.git] / sys / src / cmd / tweak.c
1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <cursor.h>
5 #include <event.h>
6 #include <bio.h>
7
8 typedef struct  Thing   Thing;
9
10 struct Thing
11 {
12         Image   *b;
13         Subfont         *s;
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 */
24         Thing   *next;
25 };
26
27 enum
28 {
29         Border  = 1,
30         Up              = 1,
31         Down    = 0,
32         Mag             = 4,
33         Maxmag  = 10,
34 };
35
36 enum
37 {
38         NORMAL  =0,
39         FACE    =1,
40         CURSOR  =2
41 };
42
43 enum
44 {
45         Mopen,
46         Mread,
47         Mwrite,
48         Mcopy,
49         Mchar,
50         Mpixels,
51         Mclose,
52         Mexit,
53 };
54
55 enum
56 {
57         Blue    = 54,
58 };
59
60 char    *menu3str[] = {
61         [Mopen] "open",
62         [Mread] "read",
63         [Mwrite]        "write",
64         [Mcopy] "copy",
65         [Mchar] "char",
66         [Mpixels]       "pixels",
67         [Mclose]        "close",
68         [Mexit] "exit",
69         0,
70 };
71
72 Menu    menu3 = {
73         menu3str
74 };
75
76 Cursor sweep0 = {
77         {-7, -7},
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}
86 };
87
88 Cursor box = {
89         {-7, -7},
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}
98 };
99
100 Cursor sight = {
101         {-7, -7},
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,}
110 };
111
112 Cursor pixel = {
113         {-7, -7},
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, }
122 };
123
124 Cursor busy = {
125         {-7, -7},
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,}
134 };
135
136 Cursor skull = {
137         {-7,-7},
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,}
146 };
147
148 Rectangle       cntlr;          /* control region */
149 Rectangle       editr;          /* editing region */
150 Rectangle       textr;          /* text region */
151 Thing           *thing;
152 Mouse           mouse;
153 char            hex[] = "0123456789abcdefABCDEF";
154 jmp_buf         err;
155 char            *file;
156 int             mag;
157 int             but1val = 0;
158 int             but2val = 255;
159 int             invert = 0;
160 Image           *values[256];
161 Image           *greyvalues[256];
162 uchar           data[8192];
163
164 Thing*  tget(char*);
165 void    mesg(char*, ...);
166 void    drawthing(Thing*, int);
167 void    select(void);
168 void    menu(void);
169 void    error(Display*, char*);
170 void    buttons(int);
171 void    drawall(void);
172 void    tclose1(Thing*);
173
174 void
175 main(int argc, char *argv[])
176 {
177         int i;
178         Event e;
179         Thing *t;
180
181         mag = Mag;
182         if(initdraw(error, 0, "tweak") < 0){
183                 fprint(2, "tweak: initdraw failed: %r\n");
184                 exits("initdraw");
185         }
186         for(i=0; i<256; i++){
187                 values[i] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, cmap2rgba(i));
188                 greyvalues[i] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, (i<<24)|(i<<16)|(i<<8)|0xFF);
189                 if(values[i] == 0 || greyvalues[i] == 0)
190                         drawerror(display, "can't allocate image");
191         }
192         einit(Emouse|Ekeyboard);
193         eresized(0);
194         i = 1;
195         setjmp(err);
196         for(; i<argc; i++){
197                 file = argv[i];
198                 t = tget(argv[i]);
199                 if(t)
200                         drawthing(t, 1);
201                 flushimage(display, 1);
202         }
203         file = 0;
204         setjmp(err);
205         for(;;)
206                 switch(event(&e)){
207                 case Ekeyboard:
208                         break;
209                 case Emouse:
210                         mouse = e.mouse;
211                         if(mouse.buttons & 3){
212                                 select();
213                                 break;
214                         }
215                         if(mouse.buttons & 4)
216                                 menu();
217                 }
218 }
219
220 void
221 error(Display*, char *s)
222 {
223         if(file)
224                 mesg("can't read %s: %s: %r", file, s);
225         else
226                 mesg("/dev/bitblt error: %s", s);
227         if(err[0])
228                 longjmp(err, 1);
229         exits(s);
230 }
231
232 void
233 redraw(Thing *t)
234 {
235         Thing *nt;
236         Point p;
237
238         if(thing==0 || thing==t)
239                 draw(screen, editr, display->white, nil, ZP);
240         if(thing == 0)
241                 return;
242         if(thing != t){
243                 for(nt=thing; nt->next!=t; nt=nt->next)
244                         ;
245                 draw(screen, Rect(screen->r.min.x, nt->er.max.y, editr.max.x, editr.max.y),
246                         display->white, nil, ZP);
247         }
248         for(nt=t; nt; nt=nt->next){
249                 drawthing(nt, 0);
250                 if(nt->next == 0){
251                         p = Pt(editr.min.x, nt->er.max.y);
252                         draw(screen, Rpt(p, editr.max), display->white, nil, ZP);
253                 }
254         }
255         mesg("");
256 }
257
258 void
259 eresized(int new)
260 {
261         if(new && getwindow(display, Refnone) < 0)
262                 error(display, "can't reattach to window");
263         cntlr = insetrect(screen->clipr, 1);
264         editr = cntlr;
265         textr = editr;
266         textr.min.y = textr.max.y - font->height;
267         cntlr.max.y = cntlr.min.y + font->height;
268         editr.min.y = cntlr.max.y+1;
269         editr.max.y = textr.min.y-1;
270         draw(screen, screen->clipr, display->white, nil, ZP);
271         draw(screen, Rect(editr.min.x, editr.max.y, editr.max.x+1, editr.max.y+1), display->black, nil, ZP);
272         replclipr(screen, 0, editr);
273         drawall();
274 }
275
276 void
277 mesgstr(Point p, int line, char *s)
278 {
279         Rectangle c, r;
280
281         r.min = p;
282         r.min.y += line*font->height;
283         r.max.y = r.min.y+font->height;
284         r.max.x = editr.max.x;
285         c = screen->clipr;
286         replclipr(screen, 0, r);
287         draw(screen, r, values[0xDD], nil, ZP);
288         r.min.x++;
289         string(screen, r.min, display->black, ZP, font, s);
290         replclipr(screen, 0, c);
291         flushimage(display, 1);
292 }
293
294 void
295 mesg(char *fmt, ...)
296 {
297         char buf[1024];
298         va_list arg;
299
300         va_start(arg, fmt);
301         vseprint(buf, buf+sizeof(buf), fmt, arg);
302         va_end(arg);
303         mesgstr(textr.min, 0, buf);
304 }
305
306 void
307 tmesg(Thing *t, int line, char *fmt, ...)
308 {
309         char buf[1024];
310         va_list arg;
311
312         va_start(arg, fmt);
313         vseprint(buf, buf+sizeof(buf), fmt, arg);
314         va_end(arg);
315         mesgstr(t->tr.min, line, buf);
316 }
317
318
319 void
320 scntl(char *l)
321 {
322         sprint(l, "mag: %d  but1: %d  but2: %d  invert-on-copy: %c", mag, but1val, but2val, "ny"[invert]);
323 }
324
325 void
326 cntl(void)
327 {
328         char buf[256];
329
330         scntl(buf);
331         mesgstr(cntlr.min, 0, buf);
332 }
333
334 void
335 stext(Thing *t, char *l0, char *l1)
336 {
337         Fontchar *fc;
338         char buf[256];
339
340         l1[0] = 0;
341         sprint(buf, "depth:%d r:%d %d  %d %d ", 
342                 t->b->depth, t->b->r.min.x, t->b->r.min.y,
343                 t->b->r.max.x, t->b->r.max.y);
344         if(t->parent)
345                 sprint(buf+strlen(buf), "mag: %d ", t->mag);
346         sprint(l0, "%s file: %s", buf, t->name);
347         if(t->c >= 0){
348                 fc = &t->parent->s->info[t->c];
349                 sprint(l1, "c(hex): %x c(char): %C x: %d "
350                            "top: %d bottom: %d left: %d width: %d iwidth: %d",
351                         (int)(t->c+t->parent->off), (int)(t->c+t->parent->off),
352                         fc->x, fc->top, fc->bottom, fc->left,
353                         fc->width, Dx(t->b->r));
354         }else if(t->s)
355                 sprint(l1, "offset(hex): %ux n:%d  height:%d  ascent:%d",
356                         t->off, t->s->n, t->s->height, t->s->ascent);
357 }
358
359 void
360 text(Thing *t)
361 {
362         char l0[256], l1[256];
363
364         stext(t, l0, l1);
365         tmesg(t, 0, l0);
366         if(l1[0])
367                 tmesg(t, 1, l1);
368 }
369
370 void
371 drawall(void)
372 {
373         Thing *t;
374
375         cntl();
376         for(t=thing; t; t=t->next)
377                 drawthing(t, 0);
378 }
379
380 /* imported from libdraw/arith.c to permit an extern log2 function */
381 static int log2[] = {
382         -1, 0, 1, -1, 2, -1, -1, -1, 3, -1, -1, -1, -1, -1, -1, -1, 4,
383         -1, -1, -1, -1, -1, -1, -1, 4 /* BUG */, -1, -1, -1, -1, -1, -1, -1, 5
384 };
385
386 int
387 value(Image *b, int x)
388 {
389         int v, l, w;
390         uchar mask;
391
392         w = b->depth;
393         if(w > 8){
394                 mesg("ldepth too large");
395                 return 0;
396         }
397         l = log2[w];
398         mask = (1<<w)-1;                /* ones at right end of word */
399         x -= b->r.min.x&~(7>>l);        /* adjust x relative to first pixel */
400         v = data[x>>(3-l)];
401         v >>= ((7>>l)<<l) - ((x&(7>>l))<<l);    /* pixel at right end of word */
402         v &= mask;                      /* pixel at right end of word */
403         return v;
404 }
405
406 int
407 bvalue(int v, int d)
408 {
409         v &= (1<<d)-1;
410         if(d > screen->depth)
411                 v >>= d - screen->depth;
412         else
413                 while(d < screen->depth && d < 8){
414                         v |= v << d;
415                         d <<= 1;
416                 }
417         if(v<0 || v>255){
418                 mesg("internal error: bad color");
419                 return Blue;
420         }
421         return v;
422 }
423
424 void
425 drawthing(Thing *nt, int link)
426 {
427         int nl, nf, i, x, y, sx, sy, fdx, dx, dy, v;
428         Thing *t;
429         Subfont *s;
430         Image *b, *col;
431         Point p, p1, p2;
432
433         if(link){
434                 nt->next = 0;
435                 if(thing == 0){
436                         thing = nt;
437                         y = editr.min.y;
438                 }else{
439                         for(t=thing; t->next; t=t->next)
440                                 ;
441                         t->next = nt;
442                         y = t->er.max.y;
443                 }
444         }else{
445                 if(thing == nt)
446                         y = editr.min.y;
447                 else{
448                         for(t=thing; t->next!=nt; t=t->next)
449                                 ;
450                         y = t->er.max.y;
451                 }
452         }
453         s = nt->s;
454         b = nt->b;
455         nl = font->height;
456         if(s || nt->c>=0)
457                 nl += font->height;
458         fdx = Dx(editr) - 2*Border;
459         dx = Dx(b->r);
460         dy = Dy(b->r);
461         if(nt->mag > 1){
462                 dx *= nt->mag;
463                 dy *= nt->mag;
464                 fdx -= fdx%nt->mag;
465         }
466         nf = 1 + dx/fdx;
467         nt->er.min.y = y;
468         nt->er.min.x = editr.min.x;
469         nt->er.max.x = nt->er.min.x + Border + dx + Border;
470         if(nt->er.max.x > editr.max.x)
471                 nt->er.max.x = editr.max.x;
472         nt->er.max.y = nt->er.min.y + Border + nf*(dy+Border);
473         nt->r = insetrect(nt->er, Border);
474         nt->er.max.x = editr.max.x;
475         draw(screen, nt->er, display->white, nil, ZP);
476         for(i=0; i<nf; i++){
477                 p1 = Pt(nt->r.min.x-1, nt->r.min.y+i*(Border+dy));
478                 /* draw portion of bitmap */
479                 p = Pt(p1.x+1, p1.y);
480                 if(nt->mag == 1)
481                         draw(screen, Rect(p.x, p.y, p.x+fdx+Dx(b->r), p.y+Dy(b->r)),
482                                 b, nil, Pt(b->r.min.x+i*fdx, b->r.min.y));
483                 else{
484                         for(y=b->r.min.y; y<b->r.max.y; y++){
485                                 sy = p.y+(y-b->r.min.y)*nt->mag;
486                                 unloadimage(b, Rect(b->r.min.x, y, b->r.max.x, y+1), data, sizeof data);
487                                 for(x=b->r.min.x+i*(fdx/nt->mag); x<b->r.max.x; x++){
488                                         sx = p.x+(x-i*(fdx/nt->mag)-b->r.min.x)*nt->mag;
489                                         if(sx >= nt->r.max.x)
490                                                 break;
491                                         v = bvalue(value(b, x), b->depth);
492                                         if(v == 255)
493                                                 continue;
494                                         if(b->chan == GREY8)
495                                                 draw(screen, Rect(sx, sy, sx+nt->mag, sy+nt->mag),
496                                                         greyvalues[v], nil, ZP);
497                                         else
498                                                 draw(screen, Rect(sx, sy, sx+nt->mag, sy+nt->mag),
499                                                         values[v], nil, ZP);
500                                 }
501
502                         }
503                 }
504                 /* line down left */
505                 if(i == 0)
506                         col = display->black;
507                 else
508                         col = display->white;
509                 draw(screen, Rect(p1.x, p1.y, p1.x+1, p1.y+dy+Border), col, nil, ZP);
510                 /* line across top */
511                 draw(screen, Rect(p1.x, p1.y-1, nt->r.max.x+Border, p1.y), display->black, nil, ZP);
512                 p2 = p1;
513                 if(i == nf-1){
514                         p2.x += 1 + dx%fdx;
515                         col = display->black;
516                 }else{
517                         p2.x = nt->r.max.x;
518                         col = display->white;
519                 }
520                 /* line down right */
521                 draw(screen, Rect(p2.x, p2.y, p2.x+1, p2.y+dy+Border), col, nil, ZP);
522                 /* line across bottom */
523                 if(i == nf-1){
524                         p1.y += Border+dy;
525                         draw(screen, Rect(p1.x, p1.y-1, p2.x,p1.y), display->black, nil, ZP);
526                 }
527         }
528         nt->tr.min.x = editr.min.x;
529         nt->tr.max.x = editr.max.x;
530         nt->tr.min.y = nt->er.max.y + Border;
531         nt->tr.max.y = nt->tr.min.y + nl;
532         nt->er.max.y = nt->tr.max.y + Border;
533         text(nt);
534 }
535
536 int
537 tohex(int c)
538 {
539         if('0'<=c && c<='9')
540                 return c - '0';
541         if('a'<=c && c<='f')
542                 return 10 + (c - 'a');
543         if('A'<=c && c<='F')
544                 return 10 + (c - 'A');
545         return 0;
546 }
547
548 Thing*
549 tget(char *file)
550 {
551         int i, j, fd, face, x, y, c, chan;
552         Image *b;
553         Subfont *s;
554         Thing *t;
555         Dir *d;
556         jmp_buf oerr;
557         uchar buf[256];
558         char *data;
559
560         buf[0] = '\0';
561         errstr((char*)buf, sizeof buf); /* flush pending error message */
562         memmove(oerr, err, sizeof err);
563         d = nil;
564         if(setjmp(err)){
565    Err:
566                 free(d);
567                 memmove(err, oerr, sizeof err);
568                 return 0;
569         }
570         fd = open(file, OREAD);
571         if(fd < 0){
572                 mesg("can't open %s: %r", file);
573                 goto Err;
574         }
575         d = dirfstat(fd);
576         if(d == nil){
577                 mesg("can't stat bitmap file %s: %r", file);
578                 close(fd);
579                 goto Err;
580         }
581         if(read(fd, buf, 11) != 11){
582                 mesg("can't read %s: %r", file);
583                 close(fd);
584                 goto Err;
585         }
586         seek(fd, 0, 0);
587         data = (char*)buf;
588         if(*data == '{')
589                 data++;
590         if(memcmp(data, "0x", 2)==0 && data[4]==','){
591                 /*
592                  * cursor file
593                  */
594                 face = CURSOR;
595                 s = 0;
596                 data = malloc(d->length+1);
597                 if(data == 0){
598                         mesg("can't malloc buffer: %r");
599                         close(fd);
600                         goto Err;
601                 }
602                 data[d->length] = 0;
603                 if(read(fd, data, d->length) != d->length){
604                         mesg("can't read cursor file %s: %r", file);
605                         close(fd);
606                         goto Err;
607                 }
608                 b = allocimage(display, Rect(0, 0, 16, 32), GREY1, 0, DNofill);
609                 if(b == 0){
610                         mesg("image alloc failed file %s: %r", file);
611                         free(data);
612                         close(fd);
613                         goto Err;
614                 }
615                 i = 0;
616                 for(x=0;x<64; ){
617                         if((c=data[i]) == '\0')
618                                 goto ill;
619                         if(c=='0' && data[i+1] == 'x'){
620                                 i += 2;
621                                 continue;
622                         }
623                         if(strchr(hex, c)){
624                                 buf[x++] = (tohex(c)<<4) | tohex(data[i+1]);
625                                 i += 2;
626                                 continue;
627                         }
628                         i++;
629                 }
630                 loadimage(b, Rect(0, 0, 16, 32), buf, sizeof buf);
631                 free(data);
632         }else if(memcmp(buf, "0x", 2)==0){
633                 /*
634                  * face file
635                  */
636                 face = FACE;
637                 s = 0;
638                 data = malloc(d->length+1);
639                 if(data == 0){
640                         mesg("can't malloc buffer: %r");
641                         close(fd);
642                         goto Err;
643                 }
644                 data[d->length] = 0;
645                 if(read(fd, data, d->length) != d->length){
646                         mesg("can't read bitmap file %s: %r", file);
647                         close(fd);
648                         goto Err;
649                 }
650                 for(y=0,i=0; i<d->length; i++)
651                         if(data[i] == '\n')
652                                 y++;
653                 if(y == 0){
654         ill:
655                         mesg("ill-formed face file %s", file);
656                         close(fd);
657                         free(data);
658                         goto Err;
659                 }
660                 for(x=0,i=0; (c=data[i])!='\n'; ){
661                         if(c==',' || c==' ' || c=='\t'){
662                                 i++;
663                                 continue;
664                         }
665                         if(c=='0' && data[i+1] == 'x'){
666                                 i += 2;
667                                 continue;
668                         }
669                         if(strchr(hex, c)){
670                                 x += 4;
671                                 i++;
672                                 continue;
673                         }
674                         goto ill;
675                 }
676                 if(x % y)
677                         goto ill;
678                 switch(x / y){
679                 default:
680                         goto ill;
681                 case 1:
682                         chan = GREY1;
683                         break;
684                 case 2:
685                         chan = GREY2;
686                         break;
687                 case 4:
688                         chan = GREY4;
689                         break;
690                 case 8:
691                         chan = CMAP8;
692                         break;
693                 }
694                 b = allocimage(display, Rect(0, 0, y, y), chan, 0, -1);
695                 if(b == 0){
696                         mesg("image alloc failed file %s: %r", file);
697                         free(data);
698                         close(fd);
699                         goto Err;
700                 }
701                 i = 0;
702                 for(j=0; j<y; j++){
703                         for(x=0; (c=data[i])!='\n'; ){
704                                 if(c=='0' && data[i+1] == 'x'){
705                                         i += 2;
706                                         continue;
707                                 }
708                                 if(strchr(hex, c)){
709                                         buf[x++] = ~((tohex(c)<<4) | tohex(data[i+1]));
710                                         i += 2;
711                                         continue;
712                                 }
713                                 i++;
714                         }
715                         i++;
716                         loadimage(b, Rect(0, j, y, j+1), buf, sizeof buf);
717                 }
718                 free(data);
719         }else{
720                 face = NORMAL;
721                 s = 0;
722                 b = readimage(display, fd, 0);
723                 if(b == 0){
724                         mesg("can't read bitmap file %s: %r", file);
725                         close(fd);
726                         goto Err;
727                 }
728                 if(seek(fd, 0, 1) < d->length)
729                         s = readsubfonti(display, file, fd, b, 0);
730         }
731         close(fd);
732         t = malloc(sizeof(Thing));
733         if(t == 0){
734    nomem:
735                 mesg("malloc failed: %r");
736                 if(s)
737                         freesubfont(s);
738                 else
739                         freeimage(b);
740                 goto Err;
741         }
742         t->name = strdup(file);
743         if(t->name == 0){
744                 free(t);
745                 goto nomem;
746         }
747         t->b = b;
748         t->s = s;
749         t->face = face;
750         t->mod = 0;
751         t->parent = 0;
752         t->c = -1;
753         t->mag = 1;
754         t->off = 0;
755         memmove(err, oerr, sizeof err);
756         return t;
757 }
758
759 int
760 atline(int x, Point p, char *line, char *buf)
761 {
762         char *s, *c, *word, *hit;
763         int w, wasblank;
764         Rune r;
765
766         wasblank = 1;
767         hit = 0;
768         word = 0;
769         for(s=line; *s; s+=w){
770                 w = chartorune(&r, s);
771                 x += runestringnwidth(font, &r, 1);
772                 if(wasblank && r!=' ')
773                         word = s;
774                 wasblank = 0;
775                 if(r == ' '){
776                         if(x >= p.x)
777                                 break;
778                         wasblank = 1;
779                 }
780                 if(r == ':')
781                         hit = word;
782         }
783         if(x < p.x)
784                 return 0;
785         c = utfrune(hit, ':');
786         strncpy(buf, hit, c-hit);
787         buf[c-hit] = 0;
788         return 1;
789 }
790
791 int
792 attext(Thing *t, Point p, char *buf)
793 {
794         char l0[256], l1[256];
795
796         if(!ptinrect(p, t->tr))
797                 return 0;
798         stext(t, l0, l1);
799         if(p.y < t->tr.min.y+font->height)
800                 return atline(t->r.min.x, p, l0, buf);
801         else
802                 return atline(t->r.min.x, p, l1, buf);
803 }
804
805 int
806 type(char *buf, char *tag)
807 {
808         Rune r;
809         char *p;
810
811         esetcursor(&busy);
812         p = buf;
813         for(;;){
814                 *p = 0;
815                 mesg("%s: %s", tag, buf);
816                 r = ekbd();
817                 switch(r){
818                 case '\n':
819                         mesg("");
820                         esetcursor(0);
821                         return p-buf;
822                 case 0x15:      /* control-U */
823                         p = buf;
824                         break;
825                 case '\b':
826                         if(p > buf)
827                                 --p;
828                         break;
829                 default:
830                         p += runetochar(p, &r);
831                 }
832         }
833 }
834
835 void
836 textedit(Thing *t, char *tag)
837 {
838         char buf[256];
839         char *s;
840         Image *b;
841         Subfont *f;
842         Fontchar *fc, *nfc;
843         Rectangle r;
844         ulong chan;
845         int i, ld, d, w, c, doredraw, fdx, x;
846         Thing *nt;
847
848         buttons(Up);
849         if(type(buf, tag) == 0)
850                 return;
851         if(strcmp(tag, "file") == 0){
852                 for(s=buf; *s; s++)
853                         if(*s <= ' '){
854                                 mesg("illegal file name");
855                                 return;
856                         }
857                 if(strcmp(t->name, buf) != 0){
858                         if(t->parent)
859                                 t->parent->mod = 1;
860                         else
861                                 t->mod = 1;
862                 }
863                 for(nt=thing; nt; nt=nt->next)
864                         if(t==nt || t->parent==nt || nt->parent==t){
865                                 free(nt->name);
866                                 nt->name = strdup(buf);
867                                 if(nt->name == 0){
868                                         mesg("malloc failed: %r");
869                                         return;
870                                 }
871                                 text(nt);
872                         }
873                 return;
874         }
875         if(strcmp(tag, "depth") == 0){
876                 if(buf[0]<'0' || '9'<buf[0] || (d=atoi(buf))<0 || d>8 || log2[d]<0){
877                         mesg("illegal ldepth");
878                         return;
879                 }
880                 if(d == t->b->depth)
881                         return;
882                 if(t->parent)
883                         t->parent->mod = 1;
884                 else
885                         t->mod = 1;
886                 if(d == 8)
887                         chan = CMAP8;
888                 else
889                         chan = CHAN1(CGrey, d);
890                 for(nt=thing; nt; nt=nt->next){
891                         if(nt!=t && nt!=t->parent && nt->parent!=t)
892                                 continue;
893                         b = allocimage(display, nt->b->r, chan, 0, 0);
894                         if(b == 0){
895         nobmem:
896                                 mesg("image alloc failed: %r");
897                                 return;
898                         }
899                         draw(b, b->r, nt->b, nil, nt->b->r.min);
900                         freeimage(nt->b);
901                         nt->b = b;
902                         if(nt->s){
903                                 b = allocimage(display, nt->b->r, chan, 0, -1);
904                                 if(b == 0)
905                                         goto nobmem;
906                                 draw(b, b->r, nt->b, nil, nt->b->r.min);
907                                 f = allocsubfont(t->name, nt->s->n, nt->s->height, nt->s->ascent, nt->s->info, b);
908                                 if(f == 0){
909         nofmem:
910                                         freeimage(b);
911                                         mesg("can't make subfont: %r");
912                                         return;
913                                 }
914                                 nt->s->info = 0;        /* prevent it being freed */
915                                 nt->s->bits = 0;
916                                 freesubfont(nt->s);
917                                 nt->s = f;
918                         }
919                         drawthing(nt, 0);
920                 }
921                 return;
922         }
923         if(strcmp(tag, "mag") == 0){
924                 if(buf[0]<'0' || '9'<buf[0] || (ld=atoi(buf))<=0 || ld>Maxmag){
925                         mesg("illegal magnification");
926                         return;
927                 }
928                 if(t->mag == ld)
929                         return;
930                 t->mag = ld;
931                 redraw(t);
932                 return;
933         }
934         if(strcmp(tag, "r") == 0){
935                 if(t->s){
936                         mesg("can't change rectangle of subfont\n");
937                         return;
938                 }
939                 s = buf;
940                 r.min.x = strtoul(s, &s, 0);
941                 r.min.y = strtoul(s, &s, 0);
942                 r.max.x = strtoul(s, &s, 0);
943                 r.max.y = strtoul(s, &s, 0);
944                 if(Dx(r)<=0 || Dy(r)<=0){
945                         mesg("illegal rectangle");
946                         return;
947                 }
948                 if(t->parent)
949                         t = t->parent;
950                 for(nt=thing; nt; nt=nt->next){
951                         if(nt->parent==t && !rectinrect(nt->b->r, r))
952                                 tclose1(nt);
953                 }
954                 b = allocimage(display, r, t->b->chan, 0, 0);
955                 if(b == 0)
956                         goto nobmem;
957                 draw(b, r, t->b, nil, r.min);
958                 freeimage(t->b);
959                 t->b = b;
960                 b = allocimage(display, r, t->b->chan, 0, 0);
961                 if(b == 0)
962                         goto nobmem;
963                 redraw(t);
964                 t->mod = 1;
965                 return;
966         }
967         if(strcmp(tag, "ascent") == 0){
968                 if(buf[0]<'0' || '9'<buf[0] || (ld=atoi(buf))<0 || ld>t->s->height){
969                         mesg("illegal ascent");
970                         return;
971                 }
972                 if(t->s->ascent == ld)
973                         return;
974                 t->s->ascent = ld;
975                 text(t);
976                 t->mod = 1;
977                 return;
978         }
979         if(strcmp(tag, "height") == 0){
980                 if(buf[0]<'0' || '9'<buf[0] || (ld=atoi(buf))<0){
981                         mesg("illegal height");
982                         return;
983                 }
984                 if(t->s->height == ld)
985                         return;
986                 t->s->height = ld;
987                 text(t);
988                 t->mod = 1;
989                 return;
990         }
991         if(strcmp(tag, "left")==0 || strcmp(tag, "width") == 0){
992                 if(buf[0]<'0' || '9'<buf[0] || (ld=atoi(buf))<0){
993                         mesg("illegal value");
994                         return;
995                 }
996                 fc = &t->parent->s->info[t->c];
997                 if(strcmp(tag, "left")==0){
998                         if(fc->left == ld)
999                                 return;
1000                         fc->left = ld;
1001                 }else{
1002                         if(fc->width == ld)
1003                                 return;
1004                         fc->width = ld;
1005                 }
1006                 text(t);
1007                 t->parent->mod = 1;
1008                 return;
1009         }
1010         if(strcmp(tag, "offset(hex)") == 0){
1011                 if(!strchr(hex, buf[0])){
1012         illoff:
1013                         mesg("illegal offset");
1014                         return;
1015                 }
1016                 s = 0;
1017                 ld = strtoul(buf, &s, 16);
1018                 if(*s)
1019                         goto illoff;
1020                 t->off = ld;
1021                 text(t);
1022                 for(nt=thing; nt; nt=nt->next)
1023                         if(nt->parent == t)
1024                                 text(nt);
1025                 return;
1026         }
1027         if(strcmp(tag, "n") == 0){
1028                 if(buf[0]<'0' || '9'<buf[0] || (w=atoi(buf))<=0){
1029                         mesg("illegal n");
1030                         return;
1031                 }
1032                 f = t->s;
1033                 if(w == f->n)
1034                         return;
1035                 doredraw = 0;
1036         again:
1037                 for(nt=thing; nt; nt=nt->next)
1038                         if(nt->parent == t){
1039                                 doredraw = 1;
1040                                 tclose1(nt);
1041                                 goto again;
1042                         }
1043                 r = t->b->r;
1044                 if(w < f->n)
1045                         r.max.x = f->info[w].x;
1046                 b = allocimage(display, r, t->b->chan, 0, 0);
1047                 if(b == 0)
1048                         goto nobmem;
1049                 draw(b, b->r, t->b, nil, r.min);
1050                 fdx = Dx(editr) - 2*Border;
1051                 if(Dx(t->b->r)/fdx != Dx(b->r)/fdx)
1052                         doredraw = 1;
1053                 freeimage(t->b);
1054                 t->b = b;
1055                 b = allocimage(display, r, t->b->chan, 0, 0);
1056                 if(b == 0)
1057                         goto nobmem;
1058                 draw(b, b->r, t->b, nil, r.min);
1059                 nfc = malloc((w+1)*sizeof(Fontchar));
1060                 if(nfc == 0){
1061                         mesg("malloc failed");
1062                         freeimage(b);
1063                         return;
1064                 }
1065                 fc = f->info;
1066                 for(i=0; i<=w && i<=f->n; i++)
1067                         nfc[i] = fc[i];
1068                 if(w+1 < i)
1069                         memset(nfc+i, 0, ((w+1)-i)*sizeof(Fontchar));
1070                 x = fc[f->n].x;
1071                 for(; i<=w; i++)
1072                         nfc[i].x = x;
1073                 f = allocsubfont(t->name, w, f->height, f->ascent, nfc, b);
1074                 if(f == 0)
1075                         goto nofmem;
1076                 t->s->bits = nil;       /* don't free it */
1077                 freesubfont(t->s);
1078                 f->info = nfc;
1079                 t->s = f;
1080                 if(doredraw)
1081                         redraw(thing);
1082                 else
1083                         drawthing(t, 0);
1084                 t->mod = 1;
1085                 return;
1086         }
1087         if(strcmp(tag, "iwidth") == 0){
1088                 if(buf[0]<'0' || '9'<buf[0] || (w=atoi(buf))<0){
1089                         mesg("illegal iwidth");
1090                         return;
1091                 }
1092                 w -= Dx(t->b->r);
1093                 if(w == 0)
1094                         return;
1095                 r = t->parent->b->r;
1096                 r.max.x += w;
1097                 c = t->c;
1098                 t = t->parent;
1099                 f = t->s;
1100                 b = allocimage(display, r, t->b->chan, 0, 0);
1101                 if(b == 0)
1102                         goto nobmem;
1103                 fc = &f->info[c];
1104                 draw(b, Rect(b->r.min.x, b->r.min.y,
1105                                 b->r.min.x+(fc[1].x-t->b->r.min.x), b->r.min.y+Dy(t->b->r)),
1106                                 t->b, nil, t->b->r.min);
1107                 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)),
1108                         t->b, nil, Pt(fc[1].x, t->b->r.min.y));
1109                 fdx = Dx(editr) - 2*Border;
1110                 doredraw = 0;
1111                 if(Dx(t->b->r)/fdx != Dx(b->r)/fdx)
1112                         doredraw = 1;
1113                 freeimage(t->b);
1114                 t->b = b;
1115                 b = allocimage(display, r, t->b->chan, 0, 0);
1116                 if(b == 0)
1117                         goto nobmem;
1118                 draw(b, b->r, t->b, nil, t->b->r.min);
1119                 fc = &f->info[c+1];
1120                 for(i=c+1; i<=f->n; i++, fc++)
1121                         fc->x += w;
1122                 f = allocsubfont(t->name, f->n, f->height, f->ascent,
1123                         f->info, b);
1124                 if(f == 0)
1125                         goto nofmem;
1126                 /* t->s and f share info; free carefully */
1127                 fc = f->info;
1128                 t->s->bits = nil;
1129                 t->s->info = 0;
1130                 freesubfont(t->s);
1131                 f->info = fc;
1132                 t->s = f;
1133                 if(doredraw)
1134                         redraw(t);
1135                 else
1136                         drawthing(t, 0);
1137                 /* redraw all affected chars */
1138                 for(nt=thing; nt; nt=nt->next){
1139                         if(nt->parent!=t || nt->c<c)
1140                                 continue;
1141                         fc = &f->info[nt->c];
1142                         r.min.x = fc[0].x;
1143                         r.min.y = nt->b->r.min.y;
1144                         r.max.x = fc[1].x;
1145                         r.max.y = nt->b->r.max.y;
1146                         b = allocimage(display, r, nt->b->chan, 0, 0);
1147                         if(b == 0)
1148                                 goto nobmem;
1149                         draw(b, r, t->b, nil, r.min);
1150                         doredraw = 0;
1151                         if(Dx(nt->b->r)/fdx != Dx(b->r)/fdx)
1152                                 doredraw = 1;
1153                         freeimage(nt->b);
1154                         nt->b = b;
1155                         if(c != nt->c)
1156                                 text(nt);
1157                         else{
1158                                 if(doredraw)
1159                                         redraw(nt);
1160                                 else
1161                                         drawthing(nt, 0);
1162                         }
1163                 }
1164                 t->mod = 1;
1165                 return;
1166         }
1167         mesg("cannot edit %s in file %s", tag, t->name);
1168 }
1169
1170 void
1171 cntledit(char *tag)
1172 {
1173         char buf[256];
1174         long l;
1175
1176         buttons(Up);
1177         if(type(buf, tag) == 0)
1178                 return;
1179         if(strcmp(tag, "mag") == 0){
1180                 if(buf[0]<'0' || '9'<buf[0] || (l=atoi(buf))<=0 || l>Maxmag){
1181                         mesg("illegal magnification");
1182                         return;
1183                 }
1184                 mag = l;
1185                 cntl();
1186                 return;
1187         }
1188         if(strcmp(tag, "but1")==0
1189         || strcmp(tag, "but2")==0){
1190                 if(buf[0]<'0' || '9'<buf[0] || (l=atoi(buf))<0 || l>255){
1191                         mesg("illegal value");
1192                         return;
1193                 }
1194                 if(strcmp(tag, "but1") == 0)
1195                         but1val = l;
1196                 else if(strcmp(tag, "but2") == 0)
1197                         but2val = l;
1198                 cntl();
1199                 return;
1200         }
1201         if(strcmp(tag, "invert-on-copy")==0){
1202                 if(buf[0]=='y' || buf[0]=='1')
1203                         invert = 1;
1204                 else if(buf[0]=='n' || buf[0]=='0')
1205                         invert = 0;
1206                 else{
1207                         mesg("illegal value");
1208                         return;
1209                 }
1210                 cntl();
1211                 return;
1212         }
1213         mesg("cannot edit %s", tag);
1214 }
1215
1216 void
1217 buttons(int ud)
1218 {
1219         while((mouse.buttons==0) != ud)
1220                 mouse = emouse();
1221 }
1222
1223 Point
1224 screenpt(Thing *t, Point realp)
1225 {
1226         int fdx, n;
1227         Point p;
1228
1229         fdx = Dx(editr)-2*Border;
1230         if(t->mag > 1)
1231                 fdx -= fdx%t->mag;
1232         p = mulpt(subpt(realp, t->b->r.min), t->mag);
1233         if(fdx < Dx(t->b->r)*t->mag){
1234                 n = p.x/fdx;
1235                 p.y += n * (Dy(t->b->r)*t->mag+Border);
1236                 p.x -= n * fdx;
1237         }
1238         p = addpt(p, t->r.min);
1239         return p;
1240 }
1241
1242 Point
1243 realpt(Thing *t, Point screenp)
1244 {
1245         int fdx, n, dy;
1246         Point p;
1247
1248         fdx = (Dx(editr)-2*Border);
1249         if(t->mag > 1)
1250                 fdx -= fdx%t->mag;
1251         p.y = screenp.y-t->r.min.y;
1252         p.x = 0;
1253         if(fdx < Dx(t->b->r)*t->mag){
1254                 dy = Dy(t->b->r)*t->mag+Border;
1255                 n = (p.y/dy);
1256                 p.x = n * fdx;
1257                 p.y -= n * dy;
1258         }
1259         p.x += screenp.x-t->r.min.x;
1260         p = addpt(divpt(p, t->mag), t->b->r.min);
1261         return p;
1262 }
1263
1264 int
1265 sweep(int but, Rectangle *r)
1266 {
1267         Thing *t;
1268         Point p, q, lastq;
1269
1270         esetcursor(&sweep0);
1271         buttons(Down);
1272         if(mouse.buttons != (1<<(but-1))){
1273                 buttons(Up);
1274                 esetcursor(0);
1275                 return 0;
1276         }
1277         p = mouse.xy;
1278         for(t=thing; t; t=t->next)
1279                 if(ptinrect(p, t->r))
1280                         break;
1281         if(t)
1282                 p = screenpt(t, realpt(t, p));
1283         r->min = p;
1284         r->max = p;
1285         esetcursor(&box);
1286         lastq = ZP;
1287         while(mouse.buttons == (1<<(but-1))){
1288                 edrawgetrect(insetrect(*r, -Borderwidth), 1);
1289                 mouse = emouse();
1290                 edrawgetrect(insetrect(*r, -Borderwidth), 0);
1291                 q = mouse.xy;
1292                 if(t)
1293                         q = screenpt(t, realpt(t, q));
1294                 if(eqpt(q, lastq))
1295                         continue;
1296                 *r = canonrect(Rpt(p, q));
1297                 lastq = q;
1298         }
1299         esetcursor(0);
1300         if(mouse.buttons){
1301                 buttons(Up);
1302                 return 0;
1303         }
1304         return 1;
1305 }
1306
1307 void
1308 openedit(Thing *t, Point pt, int c)
1309 {
1310         int x, y;
1311         Point p;
1312         Rectangle r;
1313         Rectangle br;
1314         Fontchar *fc;
1315         Thing *nt;
1316
1317         if(t->b->depth > 8){
1318                 mesg("image has depth %d; can't handle >8", t->b->depth);
1319                 return;
1320         }
1321         br = t->b->r;
1322         if(t->s == 0){
1323                 c = -1; 
1324                 /* if big enough to bother, sweep box */
1325                 if(Dx(br)<=16 && Dy(br)<=16)
1326                         r = br;
1327                 else{
1328                         if(!sweep(1, &r))
1329                                 return;
1330                         r = rectaddpt(r, subpt(br.min, t->r.min));
1331                         if(!rectclip(&r, br))
1332                                 return;
1333                         if(Dx(br) <= 8){
1334                                 r.min.x = br.min.x;
1335                                 r.max.x = br.max.x;
1336                         }else if(Dx(r) < 4){
1337             toosmall:
1338                                 mesg("rectangle too small");
1339                                 return;
1340                         }
1341                         if(Dy(br) <= 8){
1342                                 r.min.y = br.min.y;
1343                                 r.max.y = br.max.y;
1344                         }else if(Dy(r) < 4)
1345                                 goto toosmall;
1346                 }
1347         }else if(c >= 0){
1348                 fc = &t->s->info[c];
1349                 r.min.x = fc[0].x;
1350                 r.min.y = br.min.y;
1351                 r.max.x = fc[1].x;
1352                 r.max.y = br.min.y + Dy(br);
1353         }else{
1354                 /* just point at character */
1355                 fc = t->s->info;
1356                 p = addpt(pt, subpt(br.min, t->r.min));
1357                 x = br.min.x;
1358                 y = br.min.y;
1359                 for(c=0; c<t->s->n; c++,fc++){
1360             again:
1361                         r.min.x = x;
1362                         r.min.y = y;
1363                         r.max.x = x + fc[1].x - fc[0].x;
1364                         r.max.y = y + Dy(br);
1365                         if(ptinrect(p, r))
1366                                 goto found;
1367                         if(r.max.x >= br.min.x+Dx(t->r)){
1368                                 x -= Dx(t->r);
1369                                 y += t->s->height;
1370                                 if(fc[1].x > fc[0].x)
1371                                         goto again;
1372                         }
1373                         x += fc[1].x - fc[0].x;
1374                 }
1375                 return;
1376            found:
1377                 r = br;
1378                 r.min.x = fc[0].x;
1379                 r.max.x = fc[1].x;
1380         }
1381         nt = malloc(sizeof(Thing));
1382         if(nt == 0){
1383    nomem:
1384                 mesg("can't allocate: %r");
1385                 return;
1386         }
1387         memset(nt, 0, sizeof(Thing));
1388         nt->c = c;
1389         nt->b = allocimage(display, r, t->b->chan, 0, DNofill);
1390         if(nt->b == 0){
1391                 free(nt);
1392                 goto nomem;
1393         }
1394         draw(nt->b, r, t->b, nil, r.min);
1395         nt->name = strdup(t->name);
1396         if(nt->name == 0){
1397                 freeimage(nt->b);
1398                 free(nt);
1399                 goto nomem;
1400         }
1401         nt->parent = t;
1402         nt->mag = mag;
1403         drawthing(nt, 1);
1404 }
1405
1406 void
1407 ckinfo(Thing *t, Rectangle mod)
1408 {
1409         int i, j, k, top, bot, n, zero;
1410         Fontchar *fc;
1411         Rectangle r;
1412         Image *b;
1413         Thing *nt;
1414
1415         if(t->parent)
1416                 t = t->parent;
1417         if(t->s==0 || Dy(t->b->r)==0)
1418                 return;
1419         b = 0;
1420         /* check bounding boxes */
1421         fc = &t->s->info[0];
1422         r.min.y = t->b->r.min.y;
1423         r.max.y = t->b->r.max.y;
1424         for(i=0; i<t->s->n; i++, fc++){
1425                 r.min.x = fc[0].x;
1426                 r.max.x = fc[1].x;
1427                 if(!rectXrect(mod, r))
1428                         continue;
1429                 if(b==0 || Dx(b->r)<Dx(r)){
1430                         if(b)
1431                                 freeimage(b);
1432                         b = allocimage(display, rectsubpt(r, r.min), t->b->chan, 0, 0);
1433                         if(b == 0){
1434                                 mesg("can't alloc image");
1435                                 break;
1436                         }
1437                 }
1438                 draw(b, b->r, display->white, nil, ZP);
1439                 draw(b, b->r, t->b, nil, r.min);
1440                 top = 100000;
1441                 bot = 0;
1442                 n = 2+((Dx(r)/8)*t->b->depth);
1443                 for(j=0; j<b->r.max.y; j++){
1444                         memset(data, 0, n);
1445                         unloadimage(b, Rect(b->r.min.x, j, b->r.max.x, j+1), data, sizeof data);
1446                         zero = 1;
1447                         for(k=0; k<n; k++)
1448                                 if(data[k]){
1449                                         zero = 0;
1450                                         break;
1451                                 }
1452                         if(!zero){
1453                                 if(top > j)
1454                                         top = j;
1455                                 bot = j+1;
1456                         }
1457                 }
1458                 if(top > j)
1459                         top = 0;
1460                 if(top!=fc->top || bot!=fc->bottom){
1461                         fc->top = top;
1462                         fc->bottom = bot;
1463                         for(nt=thing; nt; nt=nt->next)
1464                                 if(nt->parent==t && nt->c==i)
1465                                         text(nt);
1466                 }
1467         }
1468         if(b)
1469                 freeimage(b);
1470 }
1471
1472 void
1473 twidpix(Thing *t, Point p, int set)
1474 {
1475         Image *b, *v;
1476         int c;
1477
1478         b = t->b;
1479         if(!ptinrect(p, b->r))
1480                 return;
1481         if(set)
1482                 c = but1val;
1483         else
1484                 c = but2val;
1485         if(b->chan == GREY8)
1486                 v = greyvalues[c];
1487         else
1488                 v = values[c];
1489         draw(b, Rect(p.x, p.y, p.x+1, p.y+1), v, nil, ZP);
1490         p = screenpt(t, p);
1491         draw(screen, Rect(p.x, p.y, p.x+t->mag, p.y+t->mag), v, nil, ZP);
1492 }
1493
1494 void
1495 twiddle(Thing *t)
1496 {
1497         int set;
1498         Point p, lastp;
1499         Image *b;
1500         Thing *nt;
1501         Rectangle mod;
1502
1503         if(mouse.buttons!=1 && mouse.buttons!=2){
1504                 buttons(Up);
1505                 return;
1506         }
1507         set = mouse.buttons==1;
1508         b = t->b;
1509         lastp = addpt(b->r.min, Pt(-1, -1));
1510         mod = Rpt(addpt(b->r.max, Pt(1, 1)), lastp);
1511         while(mouse.buttons){
1512                 p = realpt(t, mouse.xy);
1513                 if(!eqpt(p, lastp)){
1514                         lastp = p;
1515                         if(ptinrect(p, b->r)){
1516                                 for(nt=thing; nt; nt=nt->next)
1517                                         if(nt->parent==t->parent || nt==t->parent)
1518                                                 twidpix(nt, p, set);
1519                                 if(t->parent)
1520                                         t->parent->mod = 1;
1521                                 else
1522                                         t->mod = 1;
1523                                 if(p.x < mod.min.x)
1524                                         mod.min.x = p.x;
1525                                 if(p.y < mod.min.y)
1526                                         mod.min.y = p.y;
1527                                 if(p.x >= mod.max.x)
1528                                         mod.max.x = p.x+1;
1529                                 if(p.y >= mod.max.y)
1530                                         mod.max.y = p.y+1;
1531                         }
1532                 }
1533                 mouse = emouse();
1534         }
1535         ckinfo(t, mod);
1536 }
1537
1538 void
1539 select(void)
1540 {
1541         Thing *t;
1542         char line[128], buf[128];
1543         Point p;
1544
1545         if(ptinrect(mouse.xy, cntlr)){
1546                 scntl(line);
1547                 if(atline(cntlr.min.x, mouse.xy, line, buf)){
1548                         if(mouse.buttons == 1)
1549                                 cntledit(buf);
1550                         else
1551                                 buttons(Up);
1552                         return;
1553                 }
1554                 return;
1555         }
1556         for(t=thing; t; t=t->next){
1557                 if(attext(t, mouse.xy, buf)){
1558                         if(mouse.buttons == 1)
1559                                 textedit(t, buf);
1560                         else
1561                                 buttons(Up);
1562                         return;
1563                 }
1564                 if(ptinrect(mouse.xy, t->r)){
1565                         if(t->parent == 0){
1566                                 if(mouse.buttons == 1){
1567                                         p = mouse.xy;
1568                                         buttons(Up);
1569                                         openedit(t, p, -1);
1570                                 }else
1571                                         buttons(Up);
1572                                 return;
1573                         }
1574                         twiddle(t);
1575                         return;
1576                 }
1577         }
1578 }
1579
1580 void
1581 twrite(Thing *t)
1582 {
1583         int i, j, x, y, fd, ws, ld;
1584         Biobuf buf;
1585         Rectangle r;
1586
1587         if(t->parent)
1588                 t = t->parent;
1589         esetcursor(&busy);
1590         fd = create(t->name, OWRITE, 0666);
1591         if(fd < 0){
1592                 mesg("can't write %s: %r", t->name);
1593                 return;
1594         }
1595         if(t->face && t->b->depth <= 4){
1596                 r = t->b->r;
1597                 ld = log2[t->b->depth];
1598                 /* This heuristic reflects peculiarly different formats */
1599                 ws = 4;
1600                 if(t->face == 2)        /* cursor file */
1601                         ws = 1;
1602                 else if(Dx(r)<32 || ld==0)
1603                         ws = 2;
1604                 Binit(&buf, fd, OWRITE);
1605                 if(t->face == CURSOR)
1606                         Bprint(&buf, "{");
1607                 for(y=r.min.y; y<r.max.y; y++){
1608                         unloadimage(t->b, Rect(r.min.x, y, r.max.x, y+1), data, sizeof data);
1609                         j = 0;
1610                         for(x=r.min.x; x<r.max.x; j+=ws,x+=ws*8>>ld){
1611                                 Bprint(&buf, "0x");
1612                                 for(i=0; i<ws; i++)
1613                                         Bprint(&buf, "%.2x", data[i+j]);
1614                                 Bprint(&buf, ", ");
1615                         }
1616                         if(t->face == CURSOR){
1617                                 switch(y){
1618                                 case 3: case 7: case 11: case 19: case 23: case 27:
1619                                         Bprint(&buf, "\n ");
1620                                         break;
1621                                 case 15:
1622                                         Bprint(&buf, "},\n{");
1623                                         break;
1624                                 case 31:
1625                                         Bprint(&buf, "}\n");
1626                                         break;
1627                                 }
1628                         }else
1629                                 Bprint(&buf, "\n");
1630                 }
1631                 Bterm(&buf);
1632         }else
1633                 if(writeimage(fd, t->b, 0)<0 || (t->s && writesubfont(fd, t->s)<0)){
1634                         close(fd);
1635                         mesg("can't write %s: %r", t->name);
1636                 }
1637         t->mod = 0;
1638         close(fd);
1639         mesg("wrote %s", t->name);
1640 }
1641
1642 void
1643 tpixels(void)
1644 {
1645         Thing *t;
1646         Point p, lastp;
1647
1648         esetcursor(&pixel);
1649         for(;;){
1650                 buttons(Down);
1651                 if(mouse.buttons != 4)
1652                         break;
1653                 for(t=thing; t; t=t->next){
1654                         lastp = Pt(-1, -1);
1655                         if(ptinrect(mouse.xy, t->r)){
1656                                 while(ptinrect(mouse.xy, t->r) && mouse.buttons==4){
1657                                         p = realpt(t, mouse.xy);
1658                                         if(!eqpt(p, lastp)){
1659                                                 if(p.y != lastp.y)
1660                                                         unloadimage(t->b, Rect(t->b->r.min.x, p.y, t->b->r.max.x, p.y+1), data, sizeof data);
1661                                                 mesg("[%d,%d] = %d=0x%ux", p.x, p.y, value(t->b, p.x), value(t->b, p.x));
1662                                                 lastp = p;
1663                                         }
1664                                         mouse = emouse();
1665                                 }
1666                                 goto Continue;
1667                         }
1668                 }
1669                 mouse = emouse();
1670     Continue:;
1671         }
1672         buttons(Up);
1673         esetcursor(0);
1674 }
1675
1676 void
1677 tclose1(Thing *t)
1678 {
1679         Thing *nt;
1680
1681         if(t == thing)
1682                 thing = t->next;
1683         else{
1684                 for(nt=thing; nt->next!=t; nt=nt->next)
1685                         ;
1686                 nt->next = t->next;
1687         }
1688         do
1689                 for(nt=thing; nt; nt=nt->next)
1690                         if(nt->parent == t){
1691                                 tclose1(nt);
1692                                 break;
1693                         }
1694         while(nt);
1695         if(t->s)
1696                 freesubfont(t->s);
1697         else
1698                 freeimage(t->b);
1699         free(t->name);
1700         free(t);
1701 }
1702
1703 void
1704 tclose(Thing *t)
1705 {
1706         Thing *ct;
1707
1708         if(t->mod){
1709                 mesg("%s modified", t->name);
1710                 t->mod = 0;
1711                 return;
1712         }
1713         /* fiddle to save redrawing unmoved things */
1714         if(t == thing)
1715                 ct = 0;
1716         else
1717                 for(ct=thing; ct; ct=ct->next)
1718                         if(ct->next==t || ct->next->parent==t)
1719                                 break;
1720         tclose1(t);
1721         if(ct)
1722                 ct = ct->next;
1723         else
1724                 ct = thing;
1725         redraw(ct);
1726 }
1727
1728 void
1729 tread(Thing *t)
1730 {
1731         Thing *nt, *new;
1732         Fontchar *i;
1733         Rectangle r;
1734         int nclosed;
1735
1736         if(t->parent)
1737                 t = t->parent;
1738         new = tget(t->name);
1739         if(new == 0)
1740                 return;
1741         nclosed = 0;
1742     again:
1743         for(nt=thing; nt; nt=nt->next)
1744                 if(nt->parent == t){
1745                         if(!rectinrect(nt->b->r, new->b->r)
1746                         || new->b->depth!=nt->b->depth){
1747     closeit:
1748                                 nclosed++;
1749                                 nt->parent = 0;
1750                                 tclose1(nt);
1751                                 goto again;
1752                         }
1753                         if((t->s==0) != (new->s==0))
1754                                 goto closeit;
1755                         if((t->face==0) != (new->face==0))
1756                                 goto closeit;
1757                         if(t->s){       /* check same char */
1758                                 if(nt->c >= new->s->n)
1759                                         goto closeit;
1760                                 i = &new->s->info[nt->c];
1761                                 r.min.x = i[0].x;
1762                                 r.max.x = i[1].x;
1763                                 r.min.y = new->b->r.min.y;
1764                                 r.max.y = new->b->r.max.y;
1765                                 if(!eqrect(r, nt->b->r))
1766                                         goto closeit;
1767                         }
1768                         nt->parent = new;
1769                         draw(nt->b, nt->b->r, new->b, nil, nt->b->r.min);
1770                 }
1771         new->next = t->next;
1772         if(t == thing)
1773                 thing = new;
1774         else{
1775                 for(nt=thing; nt->next!=t; nt=nt->next)
1776                         ;
1777                 nt->next = new;
1778         }
1779         if(t->s)
1780                 freesubfont(t->s);
1781         else
1782                 freeimage(t->b);
1783         free(t->name);
1784         free(t);
1785         for(nt=thing; nt; nt=nt->next)
1786                 if(nt==new || nt->parent==new)
1787                         if(nclosed == 0)
1788                                 drawthing(nt, 0);       /* can draw in place */
1789                         else{
1790                                 redraw(nt);     /* must redraw all below */
1791                                 break;
1792                         }
1793 }
1794
1795 void
1796 tchar(Thing *t)
1797 {
1798         char buf[256], *p;
1799         Rune r;
1800         ulong c, d;
1801
1802         if(t->s == 0){
1803                 t = t->parent;
1804                 if(t==0 || t->s==0){
1805                         mesg("not a subfont");
1806                         return;
1807                 }
1808         }
1809         if(type(buf, "char (hex or character or hex-hex)") == 0)
1810                 return;
1811         if(utflen(buf) == 1){
1812                 chartorune(&r, buf);
1813                 c = r;
1814                 d = r;
1815         }else{
1816                 if(!strchr(hex, buf[0])){
1817                         mesg("illegal hex character");
1818                         return;
1819                 }
1820                 c = strtoul(buf, 0, 16);
1821                 d = c;
1822                 p = utfrune(buf, '-');
1823                 if(p){
1824                         d = strtoul(p+1, 0, 16);
1825                         if(d < c){
1826                                 mesg("invalid range");
1827                                 return;
1828                         }
1829                 }
1830         }
1831         c -= t->off;
1832         d -= t->off;
1833         while(c <= d){
1834                 if(c>=t->s->n){
1835                         mesg("0x%lux not in font %s", c+t->off, t->name);
1836                         return;
1837                 }
1838                 openedit(t, Pt(0, 0), c);
1839                 c++;
1840         }
1841 }
1842
1843 void
1844 apply(void (*f)(Thing*))
1845 {
1846         Thing *t;
1847
1848         esetcursor(&sight);
1849         buttons(Down);
1850         if(mouse.buttons == 4)
1851                 for(t=thing; t; t=t->next)
1852                         if(ptinrect(mouse.xy, t->er)){
1853                                 buttons(Up);
1854                                 f(t);
1855                                 break;
1856                         }
1857         buttons(Up);
1858         esetcursor(0);
1859 }
1860
1861 int
1862 complement(Image *t)
1863 {
1864         int i, n;
1865         uchar *buf;
1866
1867         n = Dy(t->r)*bytesperline(t->r, t->depth);
1868         buf = malloc(n);
1869         if(buf == 0)
1870                 return 0;
1871         unloadimage(t, t->r, buf, n);
1872         for(i=0; i<n; i++)
1873                 buf[i] = ~buf[i];
1874         loadimage(t, t->r, buf, n);
1875         free(buf);
1876         return 1;
1877 }
1878
1879 void
1880 copy(void)
1881 {
1882         Thing *st, *dt, *nt;
1883         Rectangle sr, dr, fr;
1884         Image *tmp;
1885         Point p1, p2;
1886         int but, up;
1887
1888         if(!sweep(3, &sr))
1889                 return;
1890         for(st=thing; st; st=st->next)
1891                 if(rectXrect(sr, st->r))
1892                         break;
1893         if(st == 0)
1894                 return;
1895         /* click gives full rectangle */
1896         if(Dx(sr)<4 && Dy(sr)<4)
1897                 sr = st->r;
1898         rectclip(&sr, st->r);
1899         p1 = realpt(st, sr.min);
1900         p2 = realpt(st, Pt(sr.min.x, sr.max.y));
1901         up = 0;
1902         if(p1.x != p2.x){       /* swept across a fold */
1903    onafold:
1904                 mesg("sweep spans a fold");
1905                 goto Return;
1906         }
1907         p2 = realpt(st, sr.max);
1908         sr.min = p1;
1909         sr.max = p2;
1910         fr.min = screenpt(st, sr.min);
1911         fr.max = screenpt(st, sr.max);
1912         p1 = subpt(p2, p1);     /* diagonal */
1913         if(p1.x==0 || p1.y==0)
1914                 return;
1915         border(screen, fr, -1, values[Blue], ZP);
1916         esetcursor(&box);
1917         for(; mouse.buttons==0; mouse=emouse()){
1918                 for(dt=thing; dt; dt=dt->next)
1919                         if(ptinrect(mouse.xy, dt->er))
1920                                 break;
1921                 if(up)
1922                         edrawgetrect(insetrect(dr, -Borderwidth), 0);
1923                 up = 0;
1924                 if(dt == 0)
1925                         continue;
1926                 dr.max = screenpt(dt, realpt(dt, mouse.xy));
1927                 dr.min = subpt(dr.max, mulpt(p1, dt->mag));
1928                 if(!rectXrect(dr, dt->r))
1929                         continue;
1930                 edrawgetrect(insetrect(dr, -Borderwidth), 1);
1931                 up = 1;
1932         }
1933         /* if up==1, we had a hit */
1934         esetcursor(0);
1935         if(up)
1936                 edrawgetrect(insetrect(dr, -Borderwidth), 0);
1937         but = mouse.buttons;
1938         buttons(Up);
1939         if(!up || but!=4)
1940                 goto Return;
1941         dt = 0;
1942         for(nt=thing; nt; nt=nt->next)
1943                 if(rectXrect(dr, nt->r)){
1944                         if(dt){
1945                                 mesg("ambiguous sweep");
1946                                 return;
1947                         }
1948                         dt = nt;
1949                 }
1950         if(dt == 0)
1951                 goto Return;
1952         p1 = realpt(dt, dr.min);
1953         p2 = realpt(dt, Pt(dr.min.x, dr.max.y));
1954         if(p1.x != p2.x)
1955                 goto onafold;
1956         p2 = realpt(dt, dr.max);
1957         dr.min = p1;
1958         dr.max = p2;
1959
1960         if(invert){
1961                 tmp = allocimage(display, dr, dt->b->chan, 0, 255);
1962                 if(tmp == 0){
1963     nomem:
1964                         mesg("can't allocate temporary");
1965                         goto Return;
1966                 }
1967                 draw(tmp, dr, st->b, nil, sr.min);
1968                 if(!complement(tmp))
1969                         goto nomem;
1970                 draw(dt->b, dr, tmp, nil, dr.min);
1971                 freeimage(tmp);
1972         }else
1973                 draw(dt->b, dr, st->b, nil, sr.min);
1974         if(dt->parent){
1975                 draw(dt->parent->b, dr, dt->b, nil, dr.min);
1976                 dt = dt->parent;
1977         }
1978         drawthing(dt, 0);
1979         for(nt=thing; nt; nt=nt->next)
1980                 if(nt->parent==dt && rectXrect(dr, nt->b->r)){
1981                         draw(nt->b, dr, dt->b, nil, dr.min);
1982                         drawthing(nt, 0);
1983                 }
1984         ckinfo(dt, dr);
1985         dt->mod = 1;
1986
1987 Return:
1988         /* clear blue box */
1989         drawthing(st, 0);
1990 }
1991
1992 void
1993 menu(void)
1994 {
1995         Thing *t;
1996         char *mod;
1997         int sel;
1998         char buf[256];
1999
2000         sel = emenuhit(3, &mouse, &menu3);
2001         switch(sel){
2002         case Mopen:
2003                 if(type(buf, "file")){
2004                         t = tget(buf);
2005                         if(t)
2006                                 drawthing(t, 1);
2007                 }
2008                 break;
2009         case Mwrite:
2010                 apply(twrite);
2011                 break;
2012         case Mread:
2013                 apply(tread);
2014                 break;
2015         case Mchar:
2016                 apply(tchar);
2017                 break;
2018         case Mcopy:
2019                 copy();
2020                 break;
2021         case Mpixels:
2022                 tpixels();
2023                 break;
2024         case Mclose:
2025                 apply(tclose);
2026                 break;
2027         case Mexit:
2028                 mod = 0;
2029                 for(t=thing; t; t=t->next)
2030                         if(t->mod){
2031                                 mod = t->name;
2032                                 t->mod = 0;
2033                         }
2034                 if(mod){
2035                         mesg("%s modified", mod);
2036                         break;
2037                 }
2038                 esetcursor(&skull);
2039                 buttons(Down);
2040                 if(mouse.buttons == 4){
2041                         buttons(Up);
2042                         exits(0);
2043                 }
2044                 buttons(Up);
2045                 esetcursor(0);
2046                 break;
2047         }
2048 }