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