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