Blob


1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <cursor.h>
5 #include <event.h>
6 #include <bio.h>
8 typedef struct Thing Thing;
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 };
27 enum
28 {
29 Border = 1,
30 Up = 1,
31 Down = 0,
32 Mag = 4,
33 Maxmag = 20
34 };
36 enum
37 {
38 NORMAL =0,
39 FACE =1,
40 CURSOR =2
41 };
43 enum
44 {
45 Mopen,
46 Mread,
47 Mwrite,
48 Mcopy,
49 Mchar,
50 Mpixels,
51 Mclose,
52 Mexit
53 };
55 enum
56 {
57 Blue = 54
58 };
60 char *menu3str[] = {
61 "open",
62 "read",
63 "write",
64 "copy",
65 "char",
66 "pixels",
67 "close",
68 "exit",
69 0
70 };
72 Menu menu3 = {
73 menu3str
74 };
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 };
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 };
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 };
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 };
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 };
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 };
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];
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*);
174 void
175 usage(void)
177 fprint(2, "usage: tweak [-W winsize] file...\n");
178 exits("usage");
181 void
182 main(volatile int argc, char **volatile argv)
184 volatile int i;
185 Event e;
186 Thing *t;
187 Thing *nt;
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");
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");
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);
221 flushimage(display, 1);
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;
235 if(mouse.buttons & 4)
236 menu();
240 int
241 xlog2(int n)
243 int i;
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;
252 void
253 error(Display *d, char *s)
255 USED(d);
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);
266 void
267 redraw(Thing *t)
269 Thing *nt;
270 Point p;
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)
279 draw(screen, Rect(screen->r.min.x, nt->er.max.y, editr.max.x, editr.max.y),
280 display->white, nil, ZP);
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);
289 mesg("");
292 void
293 eresized(int new)
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();
310 void
311 mesgstr(Point p, int line, char *s)
313 Rectangle c, r;
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);
328 void
329 mesg(char *fmt, ...)
331 char buf[1024];
332 va_list arg;
334 va_start(arg, fmt);
335 vseprint(buf, buf+sizeof(buf), fmt, arg);
336 va_end(arg);
337 mesgstr(textr.min, 0, buf);
340 void
341 tmesg(Thing *t, int line, char *fmt, ...)
343 char buf[1024];
344 va_list arg;
346 va_start(arg, fmt);
347 vseprint(buf, buf+sizeof(buf), fmt, arg);
348 va_end(arg);
349 mesgstr(t->tr.min, line, buf);
353 void
354 scntl(char *l)
356 sprint(l, "mag: %d but1: %d but2: %d invert-on-copy: %c", mag, but1val, but2val, "ny"[invert]);
359 void
360 cntl(void)
362 char buf[256];
364 scntl(buf);
365 mesgstr(cntlr.min, 0, buf);
368 void
369 stext(Thing *t, char *l0, char *l1)
371 Fontchar *fc;
372 char buf[256];
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));
395 void
396 text(Thing *t)
398 char l0[256], l1[256];
400 stext(t, l0, l1);
401 tmesg(t, 0, l0);
402 if(l1[0])
403 tmesg(t, 1, l1);
406 void
407 drawall(void)
409 Thing *t;
411 cntl();
412 for(t=thing; t; t=t->next)
413 drawthing(t, 0);
416 int
417 value(Image *b, int x)
419 int v, l, w;
420 uchar mask;
422 w = b->depth;
423 if(w > 8){
424 mesg("ldepth too large");
425 return 0;
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;
436 int
437 bvalue(int v, int d)
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;
447 if(v<0 || v>255){
448 mesg("internal error: bad color");
449 return Blue;
451 return v;
454 void
455 drawthing(Thing *nt, int link)
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;
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)
471 t->next = nt;
472 y = t->er.max.y;
474 }else{
475 if(thing == nt)
476 y = editr.min.y;
477 else{
478 for(t=thing; t->next!=nt; t=t->next)
480 y = t->er.max.y;
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;
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);
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;
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);
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);
567 int
568 tohex(int c)
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;
579 Thing*
580 tget(char *file, int extra)
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;
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;
602 fd = open(file, OREAD);
603 if(fd < 0){
604 mesg("can't open %s: %r", file);
605 goto Err;
607 d = dirfstat(fd);
608 if(d == nil){
609 mesg("can't stat bitmap file %s: %r", file);
610 close(fd);
611 goto Err;
613 if(read(fd, buf, 11) != 11){
614 mesg("can't read %s: %r", file);
615 close(fd);
616 goto Err;
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;
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;
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;
649 if(c=='0' && data[i+1] == 'x'){
650 i += 2;
651 continue;
653 if(strchr(hex, c)){
654 buf[x++] = (tohex(c)<<4) | tohex(data[i+1]);
655 i += 2;
656 continue;
658 i++;
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;
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;
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;
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;
702 for(x=0,i=0; (c=data[i])!='\n'; ){
703 if(c==',' || c==' ' || c=='\t'){
704 i++;
705 continue;
707 if(c=='0' && data[i+1] == 'x'){
708 i += 2;
709 continue;
711 if(strchr(hex, c)){
712 x += 4;
713 i++;
714 continue;
716 goto ill;
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;
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;
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;
750 if(strchr(hex, c)){
751 buf[x++] = ~((tohex(c)<<4) | tohex(data[i+1]));
752 i += 2;
753 continue;
755 i++;
757 i++;
758 loadimage(b, Rect(0, j, y, j+1), buf, sizeof buf);
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;
770 if(seek(fd, 0, 1) < d->length)
771 s = readsubfonti(display, file, fd, b, 0);
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;
784 t->name = strdup(file);
785 if(t->name == 0){
786 free(t);
787 goto nomem;
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;
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;
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;
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;
831 memmove(err, oerr, sizeof err);
832 return t;
835 int
836 atline(int x, Point p, char *line, char *buf)
838 char *s, *c, *word, *hit;
839 int w, wasblank;
840 Rune r;
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;
856 if(r == ':')
857 hit = word;
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;
867 int
868 attext(Thing *t, Point p, char *buf)
870 char l0[256], l1[256];
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);
881 int
882 type(char *buf, char *tag)
884 Rune r;
885 char *p;
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);
909 /* return 0; shut up compiler */
912 void
913 textedit(Thing *t, char *tag)
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;
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;
934 if(strcmp(t->name, buf) != 0){
935 if(t->parent)
936 t->parent->mod = 1;
937 else
938 t->mod = 1;
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;
948 text(nt);
950 return;
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;
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;
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;
991 nt->s->info = 0; /* prevent it being freed */
992 nt->s->bits = 0;
993 freesubfont(nt->s);
994 nt->s = f;
996 drawthing(nt, 0);
998 return;
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;
1005 if(t->mag == ld)
1006 return;
1007 t->mag = ld;
1008 redraw(t);
1009 return;
1011 if(strcmp(tag, "r") == 0){
1012 if(t->s){
1013 mesg("can't change rectangle of subfont\n");
1014 return;
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;
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);
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;
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;
1049 if(t->s->ascent == ld)
1050 return;
1051 t->s->ascent = ld;
1052 text(t);
1053 t->mod = 1;
1054 return;
1056 if(strcmp(tag, "height") == 0){
1057 if(buf[0]<'0' || '9'<buf[0] || (ld=atoi(buf))<0){
1058 mesg("illegal height");
1059 return;
1061 if(t->s->height == ld)
1062 return;
1063 t->s->height = ld;
1064 text(t);
1065 t->mod = 1;
1066 return;
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;
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;
1083 text(t);
1084 t->parent->mod = 1;
1085 return;
1087 if(strcmp(tag, "offset(hex)") == 0){
1088 if(!strchr(hex, buf[0])){
1089 illoff:
1090 mesg("illegal offset");
1091 return;
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;
1104 if(strcmp(tag, "n") == 0){
1105 if(buf[0]<'0' || '9'<buf[0] || (w=atoi(buf))<=0){
1106 mesg("illegal n");
1107 return;
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;
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;
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;
1164 if(strcmp(tag, "iwidth") == 0){
1165 if(buf[0]<'0' || '9'<buf[0] || (w=atoi(buf))<0){
1166 mesg("illegal iwidth");
1167 return;
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);
1241 t->mod = 1;
1242 return;
1244 mesg("cannot edit %s in file %s", tag, t->name);
1247 void
1248 cntledit(char *tag)
1250 char buf[256];
1251 ulong l;
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;
1261 mag = l;
1262 cntl();
1263 return;
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;
1271 if(strcmp(tag, "but1") == 0)
1272 but1val = l;
1273 else if(strcmp(tag, "but2") == 0)
1274 but2val = l;
1275 cntl();
1276 return;
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;
1287 cntl();
1288 return;
1290 mesg("cannot edit %s", tag);
1293 void
1294 buttons(int ud)
1296 while((mouse.buttons==0) != ud)
1297 mouse = emouse();
1300 Point
1301 screenpt(Thing *t, Point realp)
1303 int fdx, n;
1304 Point p;
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;
1315 p = addpt(p, t->r.min);
1316 return p;
1319 Point
1320 realpt(Thing *t, Point screenp)
1322 int fdx, n, dy;
1323 Point p;
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;
1336 p.x += screenp.x-t->r.min.x;
1337 p = addpt(divpt(p, t->mag), t->b->r.min);
1338 return p;
1341 int
1342 sweep(int but, Rectangle *r)
1344 Thing *t;
1345 Point p, q, lastq;
1347 esetcursor(&sweep0);
1348 buttons(Down);
1349 if(mouse.buttons != (1<<(but-1))){
1350 buttons(Up);
1351 esetcursor(0);
1352 return 0;
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;
1376 esetcursor(0);
1377 if(mouse.buttons){
1378 buttons(Up);
1379 return 0;
1381 return 1;
1384 void
1385 openedit(Thing *t, Point pt, int c)
1387 int x, y;
1388 Point p;
1389 Rectangle r;
1390 Rectangle br;
1391 Fontchar *fc;
1392 Thing *nt;
1394 if(t->b->depth > 8){
1395 mesg("image has depth %d; can't handle >8", t->b->depth);
1396 return;
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;
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;
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;
1450 x += fc[1].x - fc[0].x;
1452 return;
1453 found:
1454 r = br;
1455 r.min.x = fc[0].x;
1456 r.max.x = fc[1].x;
1458 nt = malloc(sizeof(Thing));
1459 if(nt == 0){
1460 nomem:
1461 mesg("can't allocate: %r");
1462 return;
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;
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;
1478 nt->parent = t;
1479 nt->mag = mag;
1480 drawthing(nt, 1);
1483 void
1484 ckinfo(Thing *t, Rectangle mod)
1486 int i, j, k, top, bot, n, zero;
1487 Fontchar *fc;
1488 Rectangle r;
1489 Image *b;
1490 Thing *nt;
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;
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;
1529 if(!zero){
1530 if(top > j)
1531 top = j;
1532 bot = j+1;
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);
1545 if(b)
1546 freeimage(b);
1549 void
1550 twidpix(Thing *t, Point p, int set)
1552 Image *b, *v;
1553 int c;
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);
1571 void
1572 twiddle(Thing *t)
1574 int set;
1575 Point p, lastp;
1576 Image *b;
1577 Thing *nt;
1578 Rectangle mod;
1580 if(mouse.buttons!=1 && mouse.buttons!=2){
1581 buttons(Up);
1582 return;
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;
1610 mouse = emouse();
1612 ckinfo(t, mod);
1615 void
1616 xselect(void)
1618 Thing *t;
1619 char line[128], buf[128];
1620 Point p;
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;
1631 return;
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;
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;
1651 twiddle(t);
1652 return;
1657 void
1658 twrite(Thing *t)
1660 int i, j, x, y, fd, ws, ld;
1661 Biobuf buf;
1662 Rectangle r;
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;
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, ", ");
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");
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);
1709 t->mod = 0;
1710 close(fd);
1711 mesg("wrote %s", t->name);
1714 void
1715 tpixels(void)
1717 Thing *t;
1718 Point p, lastp;
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;
1736 mouse = emouse();
1738 goto Continue;
1741 mouse = emouse();
1742 Continue:;
1744 buttons(Up);
1745 esetcursor(0);
1748 void
1749 tclose1(Thing *t)
1751 Thing *nt;
1753 if(t == thing)
1754 thing = t->next;
1755 else{
1756 for(nt=thing; nt->next!=t; nt=nt->next)
1758 nt->next = t->next;
1761 for(nt=thing; nt; nt=nt->next)
1762 if(nt->parent == t){
1763 tclose1(nt);
1764 break;
1766 while(nt);
1767 if(t->s)
1768 freesubfont(t->s);
1769 else
1770 freeimage(t->b);
1771 free(t->name);
1772 free(t);
1775 void
1776 tclose(Thing *t)
1778 Thing *ct;
1780 if(t->mod){
1781 mesg("%s modified", t->name);
1782 t->mod = 0;
1783 return;
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);
1800 void
1801 tread(Thing *t)
1803 Thing *nt, *new;
1804 Fontchar *i;
1805 Rectangle r;
1806 int nclosed;
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;
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;
1840 nt->parent = new;
1841 draw(nt->b, nt->b->r, new->b, nil, nt->b->r.min);
1843 new->next = t->next;
1844 if(t == thing)
1845 thing = new;
1846 else{
1847 for(nt=thing; nt->next!=t; nt=nt->next)
1849 nt->next = new;
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;
1867 void
1868 tchar(Thing *t)
1870 char buf[256], *p;
1871 Rune r;
1872 ulong c, d;
1874 if(t->s == 0){
1875 t = t->parent;
1876 if(t==0 || t->s==0){
1877 mesg("not a subfont");
1878 return;
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;
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;
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;
1910 openedit(t, Pt(0, 0), c);
1911 c++;
1915 void
1916 apply(void (*f)(Thing*))
1918 Thing *t;
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;
1929 buttons(Up);
1930 esetcursor(0);
1933 int
1934 complement(Image *t)
1936 int i, n;
1937 uchar *buf;
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;
1951 void
1952 copy(void)
1954 Thing *st, *dt, *nt;
1955 Rectangle sr, dr, fr;
1956 Image *tmp;
1957 Point p1, p2;
1958 int but, up;
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;
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;
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;
2020 dt = nt;
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;
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;
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;
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);
2056 ckinfo(dt, dr);
2057 dt->mod = 1;
2059 Return:
2060 /* clear blue box */
2061 drawthing(st, 0);
2064 void
2065 menu(void)
2067 Thing *t;
2068 char *mod;
2069 int sel;
2070 char buf[256];
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);
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;
2106 if(mod){
2107 mesg("%s modified", mod);
2108 break;
2110 esetcursor(&skull);
2111 buttons(Down);
2112 if(mouse.buttons == 4){
2113 buttons(Up);
2114 exits(0);
2116 buttons(Up);
2117 esetcursor(0);
2118 break;