plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

tweak.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 }