]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/tweak.c
merge
[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, int nbuf, char *tag)
807 {
808         Rune r;
809         char *p, *e;
810
811         esetcursor(&busy);
812         p = buf;
813         e = buf + nbuf-UTFmax-1;
814         for(;;){
815                 *p = 0;
816                 mesg("%s: %s", tag, buf);
817                 r = ekbd();
818                 switch(r){
819                 case '\n':
820                         mesg("");
821                         esetcursor(0);
822                         return p-buf;
823                 case 0x15:      /* control-U */
824                         p = buf;
825                         break;
826                 case '\b':
827                         if(p > buf)
828                                 --p;
829                         break;
830                 default:
831                         if(p < e)
832                                 p += runetochar(p, &r);
833                 }
834         }
835 }
836
837 void
838 textedit(Thing *t, char *tag)
839 {
840         char buf[256];
841         char *s;
842         Image *b;
843         Subfont *f;
844         Fontchar *fc, *nfc;
845         Rectangle r;
846         ulong chan;
847         int i, ld, d, w, c, doredraw, fdx, x;
848         Thing *nt;
849
850         buttons(Up);
851         if(type(buf, sizeof(buf), tag) == 0)
852                 return;
853         if(strcmp(tag, "file") == 0){
854                 for(s=buf; *s; s++)
855                         if(*s <= ' '){
856                                 mesg("illegal file name");
857                                 return;
858                         }
859                 if(strcmp(t->name, buf) != 0){
860                         if(t->parent)
861                                 t->parent->mod = 1;
862                         else
863                                 t->mod = 1;
864                 }
865                 for(nt=thing; nt; nt=nt->next)
866                         if(t==nt || t->parent==nt || nt->parent==t){
867                                 free(nt->name);
868                                 nt->name = strdup(buf);
869                                 if(nt->name == 0){
870                                         mesg("malloc failed: %r");
871                                         return;
872                                 }
873                                 text(nt);
874                         }
875                 return;
876         }
877         if(strcmp(tag, "depth") == 0){
878                 if(buf[0]<'0' || '9'<buf[0] || (d=atoi(buf))<0 || d>8 || log2[d]<0){
879                         mesg("illegal ldepth");
880                         return;
881                 }
882                 if(d == t->b->depth)
883                         return;
884                 if(t->parent)
885                         t->parent->mod = 1;
886                 else
887                         t->mod = 1;
888                 if(d == 8)
889                         chan = CMAP8;
890                 else
891                         chan = CHAN1(CGrey, d);
892                 for(nt=thing; nt; nt=nt->next){
893                         if(nt!=t && nt!=t->parent && nt->parent!=t)
894                                 continue;
895                         b = allocimage(display, nt->b->r, chan, 0, 0);
896                         if(b == 0){
897         nobmem:
898                                 mesg("image alloc failed: %r");
899                                 return;
900                         }
901                         draw(b, b->r, nt->b, nil, nt->b->r.min);
902                         freeimage(nt->b);
903                         nt->b = b;
904                         if(nt->s){
905                                 b = allocimage(display, nt->b->r, chan, 0, -1);
906                                 if(b == 0)
907                                         goto nobmem;
908                                 draw(b, b->r, nt->b, nil, nt->b->r.min);
909                                 f = allocsubfont(t->name, nt->s->n, nt->s->height, nt->s->ascent, nt->s->info, b);
910                                 if(f == 0){
911         nofmem:
912                                         freeimage(b);
913                                         mesg("can't make subfont: %r");
914                                         return;
915                                 }
916                                 nt->s->info = 0;        /* prevent it being freed */
917                                 nt->s->bits = 0;
918                                 freesubfont(nt->s);
919                                 nt->s = f;
920                         }
921                         drawthing(nt, 0);
922                 }
923                 return;
924         }
925         if(strcmp(tag, "mag") == 0){
926                 if(buf[0]<'0' || '9'<buf[0] || (ld=atoi(buf))<=0 || ld>Maxmag){
927                         mesg("illegal magnification");
928                         return;
929                 }
930                 if(t->mag == ld)
931                         return;
932                 t->mag = ld;
933                 redraw(t);
934                 return;
935         }
936         if(strcmp(tag, "r") == 0){
937                 if(t->s){
938                         mesg("can't change rectangle of subfont\n");
939                         return;
940                 }
941                 s = buf;
942                 r.min.x = strtoul(s, &s, 0);
943                 r.min.y = strtoul(s, &s, 0);
944                 r.max.x = strtoul(s, &s, 0);
945                 r.max.y = strtoul(s, &s, 0);
946                 if(Dx(r)<=0 || Dy(r)<=0){
947                         mesg("illegal rectangle");
948                         return;
949                 }
950                 if(t->parent)
951                         t = t->parent;
952                 for(nt=thing; nt; nt=nt->next){
953                         if(nt->parent==t && !rectinrect(nt->b->r, r))
954                                 tclose1(nt);
955                 }
956                 b = allocimage(display, r, t->b->chan, 0, 0);
957                 if(b == 0)
958                         goto nobmem;
959                 draw(b, r, t->b, nil, r.min);
960                 freeimage(t->b);
961                 t->b = b;
962                 b = allocimage(display, r, t->b->chan, 0, 0);
963                 if(b == 0)
964                         goto nobmem;
965                 redraw(t);
966                 t->mod = 1;
967                 return;
968         }
969         if(strcmp(tag, "ascent") == 0){
970                 if(buf[0]<'0' || '9'<buf[0] || (ld=atoi(buf))<0 || ld>t->s->height){
971                         mesg("illegal ascent");
972                         return;
973                 }
974                 if(t->s->ascent == ld)
975                         return;
976                 t->s->ascent = ld;
977                 text(t);
978                 t->mod = 1;
979                 return;
980         }
981         if(strcmp(tag, "height") == 0){
982                 if(buf[0]<'0' || '9'<buf[0] || (ld=atoi(buf))<0){
983                         mesg("illegal height");
984                         return;
985                 }
986                 if(t->s->height == ld)
987                         return;
988                 t->s->height = ld;
989                 text(t);
990                 t->mod = 1;
991                 return;
992         }
993         if(strcmp(tag, "left")==0 || strcmp(tag, "width") == 0){
994                 if(buf[0]<'0' || '9'<buf[0] || (ld=atoi(buf))<0){
995                         mesg("illegal value");
996                         return;
997                 }
998                 fc = &t->parent->s->info[t->c];
999                 if(strcmp(tag, "left")==0){
1000                         if(fc->left == ld)
1001                                 return;
1002                         fc->left = ld;
1003                 }else{
1004                         if(fc->width == ld)
1005                                 return;
1006                         fc->width = ld;
1007                 }
1008                 text(t);
1009                 t->parent->mod = 1;
1010                 return;
1011         }
1012         if(strcmp(tag, "offset(hex)") == 0){
1013                 if(!strchr(hex, buf[0])){
1014         illoff:
1015                         mesg("illegal offset");
1016                         return;
1017                 }
1018                 s = 0;
1019                 ld = strtoul(buf, &s, 16);
1020                 if(*s)
1021                         goto illoff;
1022                 t->off = ld;
1023                 text(t);
1024                 for(nt=thing; nt; nt=nt->next)
1025                         if(nt->parent == t)
1026                                 text(nt);
1027                 return;
1028         }
1029         if(strcmp(tag, "n") == 0){
1030                 if(buf[0]<'0' || '9'<buf[0] || (w=atoi(buf))<=0){
1031                         mesg("illegal n");
1032                         return;
1033                 }
1034                 f = t->s;
1035                 if(w == f->n)
1036                         return;
1037                 doredraw = 0;
1038         again:
1039                 for(nt=thing; nt; nt=nt->next)
1040                         if(nt->parent == t){
1041                                 doredraw = 1;
1042                                 tclose1(nt);
1043                                 goto again;
1044                         }
1045                 r = t->b->r;
1046                 if(w < f->n)
1047                         r.max.x = f->info[w].x;
1048                 b = allocimage(display, r, t->b->chan, 0, 0);
1049                 if(b == 0)
1050                         goto nobmem;
1051                 draw(b, b->r, t->b, nil, r.min);
1052                 fdx = Dx(editr) - 2*Border;
1053                 if(Dx(t->b->r)/fdx != Dx(b->r)/fdx)
1054                         doredraw = 1;
1055                 freeimage(t->b);
1056                 t->b = b;
1057                 b = allocimage(display, r, t->b->chan, 0, 0);
1058                 if(b == 0)
1059                         goto nobmem;
1060                 draw(b, b->r, t->b, nil, r.min);
1061                 nfc = malloc((w+1)*sizeof(Fontchar));
1062                 if(nfc == 0){
1063                         mesg("malloc failed");
1064                         freeimage(b);
1065                         return;
1066                 }
1067                 fc = f->info;
1068                 for(i=0; i<=w && i<=f->n; i++)
1069                         nfc[i] = fc[i];
1070                 if(w+1 < i)
1071                         memset(nfc+i, 0, ((w+1)-i)*sizeof(Fontchar));
1072                 x = fc[f->n].x;
1073                 for(; i<=w; i++)
1074                         nfc[i].x = x;
1075                 f = allocsubfont(t->name, w, f->height, f->ascent, nfc, b);
1076                 if(f == 0)
1077                         goto nofmem;
1078                 t->s->bits = nil;       /* don't free it */
1079                 freesubfont(t->s);
1080                 f->info = nfc;
1081                 t->s = f;
1082                 if(doredraw)
1083                         redraw(thing);
1084                 else
1085                         drawthing(t, 0);
1086                 t->mod = 1;
1087                 return;
1088         }
1089         if(strcmp(tag, "iwidth") == 0){
1090                 if(buf[0]<'0' || '9'<buf[0] || (w=atoi(buf))<0){
1091                         mesg("illegal iwidth");
1092                         return;
1093                 }
1094                 w -= Dx(t->b->r);
1095                 if(w == 0)
1096                         return;
1097                 r = t->parent->b->r;
1098                 r.max.x += w;
1099                 c = t->c;
1100                 t = t->parent;
1101                 f = t->s;
1102                 b = allocimage(display, r, t->b->chan, 0, 0);
1103                 if(b == 0)
1104                         goto nobmem;
1105                 fc = &f->info[c];
1106                 draw(b, Rect(b->r.min.x, b->r.min.y,
1107                                 b->r.min.x+(fc[1].x-t->b->r.min.x), b->r.min.y+Dy(t->b->r)),
1108                                 t->b, nil, t->b->r.min);
1109                 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)),
1110                         t->b, nil, Pt(fc[1].x, t->b->r.min.y));
1111                 fdx = Dx(editr) - 2*Border;
1112                 doredraw = 0;
1113                 if(Dx(t->b->r)/fdx != Dx(b->r)/fdx)
1114                         doredraw = 1;
1115                 freeimage(t->b);
1116                 t->b = b;
1117                 b = allocimage(display, r, t->b->chan, 0, 0);
1118                 if(b == 0)
1119                         goto nobmem;
1120                 draw(b, b->r, t->b, nil, t->b->r.min);
1121                 fc = &f->info[c+1];
1122                 for(i=c+1; i<=f->n; i++, fc++)
1123                         fc->x += w;
1124                 f = allocsubfont(t->name, f->n, f->height, f->ascent,
1125                         f->info, b);
1126                 if(f == 0)
1127                         goto nofmem;
1128                 /* t->s and f share info; free carefully */
1129                 fc = f->info;
1130                 t->s->bits = nil;
1131                 t->s->info = 0;
1132                 freesubfont(t->s);
1133                 f->info = fc;
1134                 t->s = f;
1135                 if(doredraw)
1136                         redraw(t);
1137                 else
1138                         drawthing(t, 0);
1139                 /* redraw all affected chars */
1140                 for(nt=thing; nt; nt=nt->next){
1141                         if(nt->parent!=t || nt->c<c)
1142                                 continue;
1143                         fc = &f->info[nt->c];
1144                         r.min.x = fc[0].x;
1145                         r.min.y = nt->b->r.min.y;
1146                         r.max.x = fc[1].x;
1147                         r.max.y = nt->b->r.max.y;
1148                         b = allocimage(display, r, nt->b->chan, 0, 0);
1149                         if(b == 0)
1150                                 goto nobmem;
1151                         draw(b, r, t->b, nil, r.min);
1152                         doredraw = 0;
1153                         if(Dx(nt->b->r)/fdx != Dx(b->r)/fdx)
1154                                 doredraw = 1;
1155                         freeimage(nt->b);
1156                         nt->b = b;
1157                         if(c != nt->c)
1158                                 text(nt);
1159                         else{
1160                                 if(doredraw)
1161                                         redraw(nt);
1162                                 else
1163                                         drawthing(nt, 0);
1164                         }
1165                 }
1166                 t->mod = 1;
1167                 return;
1168         }
1169         mesg("cannot edit %s in file %s", tag, t->name);
1170 }
1171
1172 void
1173 cntledit(char *tag)
1174 {
1175         char buf[256];
1176         long l;
1177
1178         buttons(Up);
1179         if(type(buf, sizeof(buf), tag) == 0)
1180                 return;
1181         if(strcmp(tag, "mag") == 0){
1182                 if(buf[0]<'0' || '9'<buf[0] || (l=atoi(buf))<=0 || l>Maxmag){
1183                         mesg("illegal magnification");
1184                         return;
1185                 }
1186                 mag = l;
1187                 cntl();
1188                 return;
1189         }
1190         if(strcmp(tag, "but1")==0
1191         || strcmp(tag, "but2")==0){
1192                 if(buf[0]<'0' || '9'<buf[0] || (l=atoi(buf))<0 || l>255){
1193                         mesg("illegal value");
1194                         return;
1195                 }
1196                 if(strcmp(tag, "but1") == 0)
1197                         but1val = l;
1198                 else if(strcmp(tag, "but2") == 0)
1199                         but2val = l;
1200                 cntl();
1201                 return;
1202         }
1203         if(strcmp(tag, "invert-on-copy")==0){
1204                 if(buf[0]=='y' || buf[0]=='1')
1205                         invert = 1;
1206                 else if(buf[0]=='n' || buf[0]=='0')
1207                         invert = 0;
1208                 else{
1209                         mesg("illegal value");
1210                         return;
1211                 }
1212                 cntl();
1213                 return;
1214         }
1215         mesg("cannot edit %s", tag);
1216 }
1217
1218 void
1219 buttons(int ud)
1220 {
1221         while((mouse.buttons==0) != ud)
1222                 mouse = emouse();
1223 }
1224
1225 Point
1226 screenpt(Thing *t, Point realp)
1227 {
1228         int fdx, n;
1229         Point p;
1230
1231         fdx = Dx(editr)-2*Border;
1232         if(t->mag > 1)
1233                 fdx -= fdx%t->mag;
1234         p = mulpt(subpt(realp, t->b->r.min), t->mag);
1235         if(fdx < Dx(t->b->r)*t->mag){
1236                 n = p.x/fdx;
1237                 p.y += n * (Dy(t->b->r)*t->mag+Border);
1238                 p.x -= n * fdx;
1239         }
1240         p = addpt(p, t->r.min);
1241         return p;
1242 }
1243
1244 Point
1245 realpt(Thing *t, Point screenp)
1246 {
1247         int fdx, n, dy;
1248         Point p;
1249
1250         fdx = (Dx(editr)-2*Border);
1251         if(t->mag > 1)
1252                 fdx -= fdx%t->mag;
1253         p.y = screenp.y-t->r.min.y;
1254         p.x = 0;
1255         if(fdx < Dx(t->b->r)*t->mag){
1256                 dy = Dy(t->b->r)*t->mag+Border;
1257                 n = (p.y/dy);
1258                 p.x = n * fdx;
1259                 p.y -= n * dy;
1260         }
1261         p.x += screenp.x-t->r.min.x;
1262         p = addpt(divpt(p, t->mag), t->b->r.min);
1263         return p;
1264 }
1265
1266 int
1267 sweep(int but, Rectangle *r)
1268 {
1269         Thing *t;
1270         Point p, q, lastq;
1271
1272         esetcursor(&sweep0);
1273         buttons(Down);
1274         if(mouse.buttons != (1<<(but-1))){
1275                 buttons(Up);
1276                 esetcursor(0);
1277                 return 0;
1278         }
1279         p = mouse.xy;
1280         for(t=thing; t; t=t->next)
1281                 if(ptinrect(p, t->r))
1282                         break;
1283         if(t)
1284                 p = screenpt(t, realpt(t, p));
1285         r->min = p;
1286         r->max = p;
1287         esetcursor(&box);
1288         lastq = ZP;
1289         while(mouse.buttons == (1<<(but-1))){
1290                 edrawgetrect(insetrect(*r, -Borderwidth), 1);
1291                 mouse = emouse();
1292                 edrawgetrect(insetrect(*r, -Borderwidth), 0);
1293                 q = mouse.xy;
1294                 if(t)
1295                         q = screenpt(t, realpt(t, q));
1296                 if(eqpt(q, lastq))
1297                         continue;
1298                 *r = canonrect(Rpt(p, q));
1299                 lastq = q;
1300         }
1301         esetcursor(0);
1302         if(mouse.buttons){
1303                 buttons(Up);
1304                 return 0;
1305         }
1306         return 1;
1307 }
1308
1309 void
1310 openedit(Thing *t, Point pt, int c)
1311 {
1312         int x, y;
1313         Point p;
1314         Rectangle r;
1315         Rectangle br;
1316         Fontchar *fc;
1317         Thing *nt;
1318
1319         if(t->b->depth > 8){
1320                 mesg("image has depth %d; can't handle >8", t->b->depth);
1321                 return;
1322         }
1323         br = t->b->r;
1324         if(t->s == 0){
1325                 c = -1; 
1326                 /* if big enough to bother, sweep box */
1327                 if(Dx(br)<=16 && Dy(br)<=16)
1328                         r = br;
1329                 else{
1330                         if(!sweep(1, &r))
1331                                 return;
1332                         r = rectaddpt(r, subpt(br.min, t->r.min));
1333                         if(!rectclip(&r, br))
1334                                 return;
1335                         if(Dx(br) <= 8){
1336                                 r.min.x = br.min.x;
1337                                 r.max.x = br.max.x;
1338                         }else if(Dx(r) < 4){
1339             toosmall:
1340                                 mesg("rectangle too small");
1341                                 return;
1342                         }
1343                         if(Dy(br) <= 8){
1344                                 r.min.y = br.min.y;
1345                                 r.max.y = br.max.y;
1346                         }else if(Dy(r) < 4)
1347                                 goto toosmall;
1348                 }
1349         }else if(c >= 0){
1350                 fc = &t->s->info[c];
1351                 r.min.x = fc[0].x;
1352                 r.min.y = br.min.y;
1353                 r.max.x = fc[1].x;
1354                 r.max.y = br.min.y + Dy(br);
1355         }else{
1356                 /* just point at character */
1357                 fc = t->s->info;
1358                 p = addpt(pt, subpt(br.min, t->r.min));
1359                 x = br.min.x;
1360                 y = br.min.y;
1361                 for(c=0; c<t->s->n; c++,fc++){
1362             again:
1363                         r.min.x = x;
1364                         r.min.y = y;
1365                         r.max.x = x + fc[1].x - fc[0].x;
1366                         r.max.y = y + Dy(br);
1367                         if(ptinrect(p, r))
1368                                 goto found;
1369                         if(r.max.x >= br.min.x+Dx(t->r)){
1370                                 x -= Dx(t->r);
1371                                 y += t->s->height;
1372                                 if(fc[1].x > fc[0].x)
1373                                         goto again;
1374                         }
1375                         x += fc[1].x - fc[0].x;
1376                 }
1377                 return;
1378            found:
1379                 r = br;
1380                 r.min.x = fc[0].x;
1381                 r.max.x = fc[1].x;
1382         }
1383         nt = malloc(sizeof(Thing));
1384         if(nt == 0){
1385    nomem:
1386                 mesg("can't allocate: %r");
1387                 return;
1388         }
1389         memset(nt, 0, sizeof(Thing));
1390         nt->c = c;
1391         nt->b = allocimage(display, r, t->b->chan, 0, DNofill);
1392         if(nt->b == 0){
1393                 free(nt);
1394                 goto nomem;
1395         }
1396         draw(nt->b, r, t->b, nil, r.min);
1397         nt->name = strdup(t->name);
1398         if(nt->name == 0){
1399                 freeimage(nt->b);
1400                 free(nt);
1401                 goto nomem;
1402         }
1403         nt->parent = t;
1404         nt->mag = mag;
1405         drawthing(nt, 1);
1406 }
1407
1408 void
1409 ckinfo(Thing *t, Rectangle mod)
1410 {
1411         int i, j, k, top, bot, n, zero;
1412         Fontchar *fc;
1413         Rectangle r;
1414         Image *b;
1415         Thing *nt;
1416
1417         if(t->parent)
1418                 t = t->parent;
1419         if(t->s==0 || Dy(t->b->r)==0)
1420                 return;
1421         b = 0;
1422         /* check bounding boxes */
1423         fc = &t->s->info[0];
1424         r.min.y = t->b->r.min.y;
1425         r.max.y = t->b->r.max.y;
1426         for(i=0; i<t->s->n; i++, fc++){
1427                 r.min.x = fc[0].x;
1428                 r.max.x = fc[1].x;
1429                 if(!rectXrect(mod, r))
1430                         continue;
1431                 if(b==0 || Dx(b->r)<Dx(r)){
1432                         if(b)
1433                                 freeimage(b);
1434                         b = allocimage(display, rectsubpt(r, r.min), t->b->chan, 0, 0);
1435                         if(b == 0){
1436                                 mesg("can't alloc image");
1437                                 break;
1438                         }
1439                 }
1440                 draw(b, b->r, display->white, nil, ZP);
1441                 draw(b, b->r, t->b, nil, r.min);
1442                 top = 100000;
1443                 bot = 0;
1444                 n = 2+((Dx(r)/8)*t->b->depth);
1445                 for(j=0; j<b->r.max.y; j++){
1446                         memset(data, 0, n);
1447                         unloadimage(b, Rect(b->r.min.x, j, b->r.max.x, j+1), data, sizeof data);
1448                         zero = 1;
1449                         for(k=0; k<n; k++)
1450                                 if(data[k]){
1451                                         zero = 0;
1452                                         break;
1453                                 }
1454                         if(!zero){
1455                                 if(top > j)
1456                                         top = j;
1457                                 bot = j+1;
1458                         }
1459                 }
1460                 if(top > j)
1461                         top = 0;
1462                 if(top!=fc->top || bot!=fc->bottom){
1463                         fc->top = top;
1464                         fc->bottom = bot;
1465                         for(nt=thing; nt; nt=nt->next)
1466                                 if(nt->parent==t && nt->c==i)
1467                                         text(nt);
1468                 }
1469         }
1470         if(b)
1471                 freeimage(b);
1472 }
1473
1474 void
1475 twidpix(Thing *t, Point p, int set)
1476 {
1477         Image *b, *v;
1478         int c;
1479
1480         b = t->b;
1481         if(!ptinrect(p, b->r))
1482                 return;
1483         if(set)
1484                 c = but1val;
1485         else
1486                 c = but2val;
1487         if(b->chan == GREY8)
1488                 v = greyvalues[c];
1489         else
1490                 v = values[c];
1491         draw(b, Rect(p.x, p.y, p.x+1, p.y+1), v, nil, ZP);
1492         p = screenpt(t, p);
1493         draw(screen, Rect(p.x, p.y, p.x+t->mag, p.y+t->mag), v, nil, ZP);
1494 }
1495
1496 void
1497 twiddle(Thing *t)
1498 {
1499         int set;
1500         Point p, lastp;
1501         Image *b;
1502         Thing *nt;
1503         Rectangle mod;
1504
1505         if(mouse.buttons!=1 && mouse.buttons!=2){
1506                 buttons(Up);
1507                 return;
1508         }
1509         set = mouse.buttons==1;
1510         b = t->b;
1511         lastp = addpt(b->r.min, Pt(-1, -1));
1512         mod = Rpt(addpt(b->r.max, Pt(1, 1)), lastp);
1513         while(mouse.buttons){
1514                 p = realpt(t, mouse.xy);
1515                 if(!eqpt(p, lastp)){
1516                         lastp = p;
1517                         if(ptinrect(p, b->r)){
1518                                 for(nt=thing; nt; nt=nt->next)
1519                                         if(nt->parent==t->parent || nt==t->parent)
1520                                                 twidpix(nt, p, set);
1521                                 if(t->parent)
1522                                         t->parent->mod = 1;
1523                                 else
1524                                         t->mod = 1;
1525                                 if(p.x < mod.min.x)
1526                                         mod.min.x = p.x;
1527                                 if(p.y < mod.min.y)
1528                                         mod.min.y = p.y;
1529                                 if(p.x >= mod.max.x)
1530                                         mod.max.x = p.x+1;
1531                                 if(p.y >= mod.max.y)
1532                                         mod.max.y = p.y+1;
1533                         }
1534                 }
1535                 mouse = emouse();
1536         }
1537         ckinfo(t, mod);
1538 }
1539
1540 void
1541 select(void)
1542 {
1543         Thing *t;
1544         char line[128], buf[128];
1545         Point p;
1546
1547         if(ptinrect(mouse.xy, cntlr)){
1548                 scntl(line);
1549                 if(atline(cntlr.min.x, mouse.xy, line, buf)){
1550                         if(mouse.buttons == 1)
1551                                 cntledit(buf);
1552                         else
1553                                 buttons(Up);
1554                         return;
1555                 }
1556                 return;
1557         }
1558         for(t=thing; t; t=t->next){
1559                 if(attext(t, mouse.xy, buf)){
1560                         if(mouse.buttons == 1)
1561                                 textedit(t, buf);
1562                         else
1563                                 buttons(Up);
1564                         return;
1565                 }
1566                 if(ptinrect(mouse.xy, t->r)){
1567                         if(t->parent == 0){
1568                                 if(mouse.buttons == 1){
1569                                         p = mouse.xy;
1570                                         buttons(Up);
1571                                         openedit(t, p, -1);
1572                                 }else
1573                                         buttons(Up);
1574                                 return;
1575                         }
1576                         twiddle(t);
1577                         return;
1578                 }
1579         }
1580 }
1581
1582 void
1583 twrite(Thing *t)
1584 {
1585         int i, j, x, y, fd, ws, ld;
1586         Biobuf buf;
1587         Rectangle r;
1588
1589         if(t->parent)
1590                 t = t->parent;
1591         esetcursor(&busy);
1592         fd = create(t->name, OWRITE, 0666);
1593         if(fd < 0){
1594                 mesg("can't write %s: %r", t->name);
1595                 return;
1596         }
1597         if(t->face && t->b->depth <= 4){
1598                 r = t->b->r;
1599                 ld = log2[t->b->depth];
1600                 /* This heuristic reflects peculiarly different formats */
1601                 ws = 4;
1602                 if(t->face == 2)        /* cursor file */
1603                         ws = 1;
1604                 else if(Dx(r)<32 || ld==0)
1605                         ws = 2;
1606                 Binit(&buf, fd, OWRITE);
1607                 if(t->face == CURSOR)
1608                         Bprint(&buf, "{");
1609                 for(y=r.min.y; y<r.max.y; y++){
1610                         unloadimage(t->b, Rect(r.min.x, y, r.max.x, y+1), data, sizeof data);
1611                         j = 0;
1612                         for(x=r.min.x; x<r.max.x; j+=ws,x+=ws*8>>ld){
1613                                 Bprint(&buf, "0x");
1614                                 for(i=0; i<ws; i++)
1615                                         Bprint(&buf, "%.2x", data[i+j]);
1616                                 Bprint(&buf, ", ");
1617                         }
1618                         if(t->face == CURSOR){
1619                                 switch(y){
1620                                 case 3: case 7: case 11: case 19: case 23: case 27:
1621                                         Bprint(&buf, "\n ");
1622                                         break;
1623                                 case 15:
1624                                         Bprint(&buf, "},\n{");
1625                                         break;
1626                                 case 31:
1627                                         Bprint(&buf, "}\n");
1628                                         break;
1629                                 }
1630                         }else
1631                                 Bprint(&buf, "\n");
1632                 }
1633                 Bterm(&buf);
1634         }else
1635                 if(writeimage(fd, t->b, 0)<0 || (t->s && writesubfont(fd, t->s)<0)){
1636                         close(fd);
1637                         mesg("can't write %s: %r", t->name);
1638                 }
1639         t->mod = 0;
1640         close(fd);
1641         mesg("wrote %s", t->name);
1642 }
1643
1644 void
1645 tpixels(void)
1646 {
1647         Thing *t;
1648         Point p, lastp;
1649
1650         esetcursor(&pixel);
1651         for(;;){
1652                 buttons(Down);
1653                 if(mouse.buttons != 4)
1654                         break;
1655                 for(t=thing; t; t=t->next){
1656                         lastp = Pt(-1, -1);
1657                         if(ptinrect(mouse.xy, t->r)){
1658                                 while(ptinrect(mouse.xy, t->r) && mouse.buttons==4){
1659                                         p = realpt(t, mouse.xy);
1660                                         if(!eqpt(p, lastp)){
1661                                                 if(p.y != lastp.y)
1662                                                         unloadimage(t->b, Rect(t->b->r.min.x, p.y, t->b->r.max.x, p.y+1), data, sizeof data);
1663                                                 mesg("[%d,%d] = %d=0x%ux", p.x, p.y, value(t->b, p.x), value(t->b, p.x));
1664                                                 lastp = p;
1665                                         }
1666                                         mouse = emouse();
1667                                 }
1668                                 goto Continue;
1669                         }
1670                 }
1671                 mouse = emouse();
1672     Continue:;
1673         }
1674         buttons(Up);
1675         esetcursor(0);
1676 }
1677
1678 void
1679 tclose1(Thing *t)
1680 {
1681         Thing *nt;
1682
1683         if(t == thing)
1684                 thing = t->next;
1685         else{
1686                 for(nt=thing; nt->next!=t; nt=nt->next)
1687                         ;
1688                 nt->next = t->next;
1689         }
1690         do
1691                 for(nt=thing; nt; nt=nt->next)
1692                         if(nt->parent == t){
1693                                 tclose1(nt);
1694                                 break;
1695                         }
1696         while(nt);
1697         if(t->s)
1698                 freesubfont(t->s);
1699         else
1700                 freeimage(t->b);
1701         free(t->name);
1702         free(t);
1703 }
1704
1705 void
1706 tclose(Thing *t)
1707 {
1708         Thing *ct;
1709
1710         if(t->mod){
1711                 mesg("%s modified", t->name);
1712                 t->mod = 0;
1713                 return;
1714         }
1715         /* fiddle to save redrawing unmoved things */
1716         if(t == thing)
1717                 ct = 0;
1718         else
1719                 for(ct=thing; ct; ct=ct->next)
1720                         if(ct->next==t || ct->next->parent==t)
1721                                 break;
1722         tclose1(t);
1723         if(ct)
1724                 ct = ct->next;
1725         else
1726                 ct = thing;
1727         redraw(ct);
1728 }
1729
1730 void
1731 tread(Thing *t)
1732 {
1733         Thing *nt, *new;
1734         Fontchar *i;
1735         Rectangle r;
1736         int nclosed;
1737
1738         if(t->parent)
1739                 t = t->parent;
1740         new = tget(t->name);
1741         if(new == 0)
1742                 return;
1743         nclosed = 0;
1744     again:
1745         for(nt=thing; nt; nt=nt->next)
1746                 if(nt->parent == t){
1747                         if(!rectinrect(nt->b->r, new->b->r)
1748                         || new->b->depth!=nt->b->depth){
1749     closeit:
1750                                 nclosed++;
1751                                 nt->parent = 0;
1752                                 tclose1(nt);
1753                                 goto again;
1754                         }
1755                         if((t->s==0) != (new->s==0))
1756                                 goto closeit;
1757                         if((t->face==0) != (new->face==0))
1758                                 goto closeit;
1759                         if(t->s){       /* check same char */
1760                                 if(nt->c >= new->s->n)
1761                                         goto closeit;
1762                                 i = &new->s->info[nt->c];
1763                                 r.min.x = i[0].x;
1764                                 r.max.x = i[1].x;
1765                                 r.min.y = new->b->r.min.y;
1766                                 r.max.y = new->b->r.max.y;
1767                                 if(!eqrect(r, nt->b->r))
1768                                         goto closeit;
1769                         }
1770                         nt->parent = new;
1771                         draw(nt->b, nt->b->r, new->b, nil, nt->b->r.min);
1772                 }
1773         new->next = t->next;
1774         if(t == thing)
1775                 thing = new;
1776         else{
1777                 for(nt=thing; nt->next!=t; nt=nt->next)
1778                         ;
1779                 nt->next = new;
1780         }
1781         if(t->s)
1782                 freesubfont(t->s);
1783         else
1784                 freeimage(t->b);
1785         free(t->name);
1786         free(t);
1787         for(nt=thing; nt; nt=nt->next)
1788                 if(nt==new || nt->parent==new)
1789                         if(nclosed == 0)
1790                                 drawthing(nt, 0);       /* can draw in place */
1791                         else{
1792                                 redraw(nt);     /* must redraw all below */
1793                                 break;
1794                         }
1795 }
1796
1797 void
1798 tchar(Thing *t)
1799 {
1800         char buf[256], *p;
1801         Rune r;
1802         ulong c, d;
1803
1804         if(t->s == 0){
1805                 t = t->parent;
1806                 if(t==0 || t->s==0){
1807                         mesg("not a subfont");
1808                         return;
1809                 }
1810         }
1811         if(type(buf, sizeof(buf), "char (hex or character or hex-hex)") == 0)
1812                 return;
1813         if(utflen(buf) == 1){
1814                 chartorune(&r, buf);
1815                 c = r;
1816                 d = r;
1817         }else{
1818                 if(!strchr(hex, buf[0])){
1819                         mesg("illegal hex character");
1820                         return;
1821                 }
1822                 c = strtoul(buf, 0, 16);
1823                 d = c;
1824                 p = utfrune(buf, '-');
1825                 if(p){
1826                         d = strtoul(p+1, 0, 16);
1827                         if(d < c){
1828                                 mesg("invalid range");
1829                                 return;
1830                         }
1831                 }
1832         }
1833         c -= t->off;
1834         d -= t->off;
1835         while(c <= d){
1836                 if(c>=t->s->n){
1837                         mesg("0x%lux not in font %s", c+t->off, t->name);
1838                         return;
1839                 }
1840                 openedit(t, Pt(0, 0), c);
1841                 c++;
1842         }
1843 }
1844
1845 void
1846 apply(void (*f)(Thing*))
1847 {
1848         Thing *t;
1849
1850         esetcursor(&sight);
1851         buttons(Down);
1852         if(mouse.buttons == 4)
1853                 for(t=thing; t; t=t->next)
1854                         if(ptinrect(mouse.xy, t->er)){
1855                                 buttons(Up);
1856                                 f(t);
1857                                 break;
1858                         }
1859         buttons(Up);
1860         esetcursor(0);
1861 }
1862
1863 int
1864 complement(Image *t)
1865 {
1866         int i, n;
1867         uchar *buf;
1868
1869         n = Dy(t->r)*bytesperline(t->r, t->depth);
1870         buf = malloc(n);
1871         if(buf == 0)
1872                 return 0;
1873         unloadimage(t, t->r, buf, n);
1874         for(i=0; i<n; i++)
1875                 buf[i] = ~buf[i];
1876         loadimage(t, t->r, buf, n);
1877         free(buf);
1878         return 1;
1879 }
1880
1881 void
1882 copy(void)
1883 {
1884         Thing *st, *dt, *nt;
1885         Rectangle sr, dr, fr;
1886         Image *tmp;
1887         Point p1, p2;
1888         int but, up;
1889
1890         if(!sweep(3, &sr))
1891                 return;
1892         for(st=thing; st; st=st->next)
1893                 if(rectXrect(sr, st->r))
1894                         break;
1895         if(st == 0)
1896                 return;
1897         /* click gives full rectangle */
1898         if(Dx(sr)<4 && Dy(sr)<4)
1899                 sr = st->r;
1900         rectclip(&sr, st->r);
1901         p1 = realpt(st, sr.min);
1902         p2 = realpt(st, Pt(sr.min.x, sr.max.y));
1903         up = 0;
1904         if(p1.x != p2.x){       /* swept across a fold */
1905    onafold:
1906                 mesg("sweep spans a fold");
1907                 goto Return;
1908         }
1909         p2 = realpt(st, sr.max);
1910         sr.min = p1;
1911         sr.max = p2;
1912         fr.min = screenpt(st, sr.min);
1913         fr.max = screenpt(st, sr.max);
1914         p1 = subpt(p2, p1);     /* diagonal */
1915         if(p1.x==0 || p1.y==0)
1916                 return;
1917         border(screen, fr, -1, values[Blue], ZP);
1918         esetcursor(&box);
1919         for(; mouse.buttons==0; mouse=emouse()){
1920                 for(dt=thing; dt; dt=dt->next)
1921                         if(ptinrect(mouse.xy, dt->er))
1922                                 break;
1923                 if(up)
1924                         edrawgetrect(insetrect(dr, -Borderwidth), 0);
1925                 up = 0;
1926                 if(dt == 0)
1927                         continue;
1928                 dr.max = screenpt(dt, realpt(dt, mouse.xy));
1929                 dr.min = subpt(dr.max, mulpt(p1, dt->mag));
1930                 if(!rectXrect(dr, dt->r))
1931                         continue;
1932                 edrawgetrect(insetrect(dr, -Borderwidth), 1);
1933                 up = 1;
1934         }
1935         /* if up==1, we had a hit */
1936         esetcursor(0);
1937         if(up)
1938                 edrawgetrect(insetrect(dr, -Borderwidth), 0);
1939         but = mouse.buttons;
1940         buttons(Up);
1941         if(!up || but!=4)
1942                 goto Return;
1943         dt = 0;
1944         for(nt=thing; nt; nt=nt->next)
1945                 if(rectXrect(dr, nt->r)){
1946                         if(dt){
1947                                 mesg("ambiguous sweep");
1948                                 return;
1949                         }
1950                         dt = nt;
1951                 }
1952         if(dt == 0)
1953                 goto Return;
1954         p1 = realpt(dt, dr.min);
1955         p2 = realpt(dt, Pt(dr.min.x, dr.max.y));
1956         if(p1.x != p2.x)
1957                 goto onafold;
1958         p2 = realpt(dt, dr.max);
1959         dr.min = p1;
1960         dr.max = p2;
1961
1962         if(invert){
1963                 tmp = allocimage(display, dr, dt->b->chan, 0, 255);
1964                 if(tmp == 0){
1965     nomem:
1966                         mesg("can't allocate temporary");
1967                         goto Return;
1968                 }
1969                 draw(tmp, dr, st->b, nil, sr.min);
1970                 if(!complement(tmp))
1971                         goto nomem;
1972                 draw(dt->b, dr, tmp, nil, dr.min);
1973                 freeimage(tmp);
1974         }else
1975                 draw(dt->b, dr, st->b, nil, sr.min);
1976         if(dt->parent){
1977                 draw(dt->parent->b, dr, dt->b, nil, dr.min);
1978                 dt = dt->parent;
1979         }
1980         drawthing(dt, 0);
1981         for(nt=thing; nt; nt=nt->next)
1982                 if(nt->parent==dt && rectXrect(dr, nt->b->r)){
1983                         draw(nt->b, dr, dt->b, nil, dr.min);
1984                         drawthing(nt, 0);
1985                 }
1986         ckinfo(dt, dr);
1987         dt->mod = 1;
1988
1989 Return:
1990         /* clear blue box */
1991         drawthing(st, 0);
1992 }
1993
1994 void
1995 menu(void)
1996 {
1997         Thing *t;
1998         char *mod;
1999         int sel;
2000         char buf[256];
2001
2002         sel = emenuhit(3, &mouse, &menu3);
2003         switch(sel){
2004         case Mopen:
2005                 if(type(buf, sizeof(buf), "file")){
2006                         t = tget(buf);
2007                         if(t)
2008                                 drawthing(t, 1);
2009                 }
2010                 break;
2011         case Mwrite:
2012                 apply(twrite);
2013                 break;
2014         case Mread:
2015                 apply(tread);
2016                 break;
2017         case Mchar:
2018                 apply(tchar);
2019                 break;
2020         case Mcopy:
2021                 copy();
2022                 break;
2023         case Mpixels:
2024                 tpixels();
2025                 break;
2026         case Mclose:
2027                 apply(tclose);
2028                 break;
2029         case Mexit:
2030                 mod = 0;
2031                 for(t=thing; t; t=t->next)
2032                         if(t->mod){
2033                                 mod = t->name;
2034                                 t->mod = 0;
2035                         }
2036                 if(mod){
2037                         mesg("%s modified", mod);
2038                         break;
2039                 }
2040                 esetcursor(&skull);
2041                 buttons(Down);
2042                 if(mouse.buttons == 4){
2043                         buttons(Up);
2044                         exits(0);
2045                 }
2046                 buttons(Up);
2047                 esetcursor(0);
2048                 break;
2049         }
2050 }