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