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