tweak.c (40316B)
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 175 usage(void) 176 { 177 fprint(2, "usage: tweak [-W winsize] file...\n"); 178 exits("usage"); 179 } 180 181 void 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 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 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 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 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 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 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 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 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 360 cntl(void) 361 { 362 char buf[256]; 363 364 scntl(buf); 365 mesgstr(cntlr.min, 0, buf); 366 } 367 368 void 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 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 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 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 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 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 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* 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 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 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 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 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 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 1294 buttons(int ud) 1295 { 1296 while((mouse.buttons==0) != ud) 1297 mouse = emouse(); 1298 } 1299 1300 Point 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 }