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