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