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