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