paint.c (17531B)
1 /* 2 This code was taken from 9front repository (https://code.9front.org/hg/plan9front). 3 It is subject to license from 9front, below is a reproduction of the license. 4 5 Copyright (c) 20XX 9front 6 7 Permission is hereby granted, free of charge, to any person obtaining a copy 8 of this software and associated documentation files (the "Software"), to deal 9 in the Software without restriction, including without limitation the rights 10 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 copies of the Software, and to permit persons to whom the Software is 12 furnished to do so, subject to the following conditions: 13 14 The above copyright notice and this permission notice shall be included in all 15 copies or substantial portions of the Software. 16 17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 SOFTWARE. 24 */ 25 #include <u.h> 26 #include <libc.h> 27 #include <draw.h> 28 #include <event.h> 29 #include <keyboard.h> 30 31 /* additional libdraw function needed - defined here to avoid API change */ 32 extern int eenter(char*, char*, int, Mouse*); 33 34 char *filename; 35 int zoom = 1; 36 int brush = 1; 37 Point spos; /* position on screen */ 38 Point cpos; /* position on canvas */ 39 Image *canvas; 40 Image *ink; 41 Image *back; 42 Image *pal[16]; /* palette */ 43 Rectangle palr; /* palette rect on screen */ 44 Rectangle penr; /* pen size rect on screen */ 45 46 enum { 47 NBRUSH = 10+1, 48 }; 49 50 int nundo = 0; 51 Image *undo[1024]; 52 53 int c64[] = { /* c64 color palette */ 54 0x000000, 55 0xFFFFFF, 56 0x68372B, 57 0x70A4B2, 58 0x6F3D86, 59 0x588D43, 60 0x352879, 61 0xB8C76F, 62 0x6F4F25, 63 0x433900, 64 0x9A6759, 65 0x444444, 66 0x6C6C6C, 67 0x9AD284, 68 0x6C5EB5, 69 0x959595, 70 }; 71 72 /* 73 * get bounding rectnagle for stroke from r.min to r.max with 74 * specified brush (size). 75 */ 76 static Rectangle 77 strokerect(Rectangle r, int brush) 78 { 79 r = canonrect(r); 80 return Rect(r.min.x-brush, r.min.y-brush, r.max.x+brush+1, r.max.y+brush+1); 81 } 82 83 /* 84 * draw stroke from r.min to r.max to dst with color ink and 85 * brush (size). 86 */ 87 static void 88 strokedraw(Image *dst, Rectangle r, Image *ink, int brush) 89 { 90 if(!eqpt(r.min, r.max)) 91 line(dst, r.min, r.max, Enddisc, Enddisc, brush, ink, ZP); 92 fillellipse(dst, r.max, brush, brush, ink, ZP); 93 } 94 95 /* 96 * A draw operation that touches only the area contained in bot but not in top. 97 * mp and sp get aligned with bot.min. 98 */ 99 static void 100 gendrawdiff(Image *dst, Rectangle bot, Rectangle top, 101 Image *src, Point sp, Image *mask, Point mp, int op) 102 { 103 Rectangle r; 104 Point origin; 105 Point delta; 106 107 if(Dx(bot)*Dy(bot) == 0) 108 return; 109 110 /* no points in bot - top */ 111 if(rectinrect(bot, top)) 112 return; 113 114 /* bot - top ≡ bot */ 115 if(Dx(top)*Dy(top)==0 || rectXrect(bot, top)==0){ 116 gendrawop(dst, bot, src, sp, mask, mp, op); 117 return; 118 } 119 120 origin = bot.min; 121 /* split bot into rectangles that don't intersect top */ 122 /* left side */ 123 if(bot.min.x < top.min.x){ 124 r = Rect(bot.min.x, bot.min.y, top.min.x, bot.max.y); 125 delta = subpt(r.min, origin); 126 gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op); 127 bot.min.x = top.min.x; 128 } 129 130 /* right side */ 131 if(bot.max.x > top.max.x){ 132 r = Rect(top.max.x, bot.min.y, bot.max.x, bot.max.y); 133 delta = subpt(r.min, origin); 134 gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op); 135 bot.max.x = top.max.x; 136 } 137 138 /* top */ 139 if(bot.min.y < top.min.y){ 140 r = Rect(bot.min.x, bot.min.y, bot.max.x, top.min.y); 141 delta = subpt(r.min, origin); 142 gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op); 143 bot.min.y = top.min.y; 144 } 145 146 /* bottom */ 147 if(bot.max.y > top.max.y){ 148 r = Rect(bot.min.x, top.max.y, bot.max.x, bot.max.y); 149 delta = subpt(r.min, origin); 150 gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op); 151 bot.max.y = top.max.y; 152 } 153 } 154 155 int 156 alphachan(ulong chan) 157 { 158 for(; chan; chan >>= 8) 159 if(TYPE(chan) == CAlpha) 160 return 1; 161 return 0; 162 } 163 164 void 165 zoomdraw(Image *d, Rectangle r, Rectangle top, Image *b, Image *s, Point sp, int f) 166 { 167 Rectangle dr; 168 Image *t; 169 Point a; 170 int w; 171 172 a = ZP; 173 if(r.min.x < d->r.min.x){ 174 sp.x += (d->r.min.x - r.min.x)/f; 175 a.x = (d->r.min.x - r.min.x)%f; 176 r.min.x = d->r.min.x; 177 } 178 if(r.min.y < d->r.min.y){ 179 sp.y += (d->r.min.y - r.min.y)/f; 180 a.y = (d->r.min.y - r.min.y)%f; 181 r.min.y = d->r.min.y; 182 } 183 rectclip(&r, d->r); 184 w = s->r.max.x - sp.x; 185 if(w > Dx(r)) 186 w = Dx(r); 187 dr = r; 188 dr.max.x = dr.min.x+w; 189 if(!alphachan(s->chan)) 190 b = nil; 191 if(f <= 1){ 192 if(b) gendrawdiff(d, dr, top, b, sp, nil, ZP, SoverD); 193 gendrawdiff(d, dr, top, s, sp, nil, ZP, SoverD); 194 return; 195 } 196 if((t = allocimage(display, dr, s->chan, 0, 0)) == nil) 197 return; 198 for(; dr.min.y < r.max.y; dr.min.y++){ 199 dr.max.y = dr.min.y+1; 200 draw(t, dr, s, nil, sp); 201 if(++a.y == f){ 202 a.y = 0; 203 sp.y++; 204 } 205 } 206 dr = r; 207 for(sp=dr.min; dr.min.x < r.max.x; sp.x++){ 208 dr.max.x = dr.min.x+1; 209 if(b) gendrawdiff(d, dr, top, b, sp, nil, ZP, SoverD); 210 gendrawdiff(d, dr, top, t, sp, nil, ZP, SoverD); 211 for(dr.min.x++; ++a.x < f && dr.min.x < r.max.x; dr.min.x++){ 212 dr.max.x = dr.min.x+1; 213 gendrawdiff(d, dr, top, d, Pt(dr.min.x-1, dr.min.y), nil, ZP, SoverD); 214 } 215 a.x = 0; 216 } 217 freeimage(t); 218 } 219 220 Point 221 s2c(Point p){ 222 p = subpt(p, spos); 223 if(p.x < 0) p.x -= zoom-1; 224 if(p.y < 0) p.y -= zoom-1; 225 return addpt(divpt(p, zoom), cpos); 226 } 227 228 Point 229 c2s(Point p){ 230 return addpt(mulpt(subpt(p, cpos), zoom), spos); 231 } 232 233 Rectangle 234 c2sr(Rectangle r){ 235 return Rpt(c2s(r.min), c2s(r.max)); 236 } 237 238 void 239 update(Rectangle *rp){ 240 if(canvas==nil) 241 draw(screen, screen->r, back, nil, ZP); 242 else { 243 if(rp == nil) 244 rp = &canvas->r; 245 gendrawdiff(screen, screen->r, c2sr(canvas->r), back, ZP, nil, ZP, SoverD); 246 zoomdraw(screen, c2sr(*rp), ZR, back, canvas, rp->min, zoom); 247 } 248 flushimage(display, 1); 249 } 250 251 void 252 expand(Rectangle r) 253 { 254 Rectangle nr; 255 Image *tmp; 256 257 if(canvas==nil){ 258 if((canvas = allocimage(display, r, screen->chan, 0, DNofill)) == nil) 259 sysfatal("allocimage: %r"); 260 draw(canvas, canvas->r, back, nil, ZP); 261 return; 262 } 263 nr = canvas->r; 264 combinerect(&nr, r); 265 if(eqrect(nr, canvas->r)) 266 return; 267 if((tmp = allocimage(display, nr, canvas->chan, 0, DNofill)) == nil) 268 return; 269 draw(tmp, canvas->r, canvas, nil, canvas->r.min); 270 gendrawdiff(tmp, tmp->r, canvas->r, back, ZP, nil, ZP, SoverD); 271 freeimage(canvas); 272 canvas = tmp; 273 } 274 275 void 276 save(Rectangle r, int mark) 277 { 278 Image *tmp; 279 int x; 280 281 if(mark){ 282 x = nundo++ % nelem(undo); 283 if(undo[x]) 284 freeimage(undo[x]); 285 undo[x] = nil; 286 } 287 if(canvas==nil || nundo<0) 288 return; 289 if(!rectclip(&r, canvas->r)) 290 return; 291 if((tmp = allocimage(display, r, canvas->chan, 0, DNofill)) == nil) 292 return; 293 draw(tmp, r, canvas, nil, r.min); 294 x = nundo++ % nelem(undo); 295 if(undo[x]) 296 freeimage(undo[x]); 297 undo[x] = tmp; 298 } 299 300 void 301 restore(int n) 302 { 303 Image *tmp; 304 int x; 305 306 while(nundo > 0){ 307 if(n-- == 0) 308 return; 309 x = --nundo % nelem(undo); 310 if((tmp = undo[x]) == nil) 311 return; 312 undo[x] = nil; 313 if(canvas == nil || canvas->chan != tmp->chan){ 314 freeimage(canvas); 315 canvas = tmp; 316 update(nil); 317 } else { 318 expand(tmp->r); 319 draw(canvas, tmp->r, tmp, nil, tmp->r.min); 320 update(&tmp->r); 321 freeimage(tmp); 322 } 323 } 324 } 325 326 typedef struct { 327 Rectangle r; 328 Rectangle r0; 329 Image* dst; 330 331 int yscan; /* current scanline */ 332 int wscan; /* bscan width in bytes */ 333 Image* iscan; /* scanline image */ 334 uchar* bscan; /* scanline buffer */ 335 336 int nmask; /* size of bmask in bytes */ 337 int wmask; /* width of bmask in bytes */ 338 Image* imask; /* mask image */ 339 uchar* bmask; /* mask buffer */ 340 341 int ncmp; 342 uchar bcmp[4]; 343 } Filldata; 344 345 void 346 fillscan(Filldata *f, Point p0) 347 { 348 int x, y; 349 uchar *b; 350 351 x = p0.x; 352 y = p0.y; 353 b = f->bmask + y*f->wmask; 354 if(b[x/8] & 0x80>>(x%8)) 355 return; 356 357 if(f->yscan != y){ 358 draw(f->iscan, f->iscan->r, f->dst, nil, Pt(f->r.min.x, f->r.min.y+y)); 359 if(unloadimage(f->iscan, f->iscan->r, f->bscan, f->wscan) < 0) 360 return; 361 f->yscan = y; 362 } 363 364 for(x = p0.x; x >= 0; x--){ 365 if(memcmp(f->bscan + x*f->ncmp, f->bcmp, f->ncmp)) 366 break; 367 b[x/8] |= 0x80>>(x%8); 368 } 369 for(x = p0.x+1; x < f->r0.max.x; x++){ 370 if(memcmp(f->bscan + x*f->ncmp, f->bcmp, f->ncmp)) 371 break; 372 b[x/8] |= 0x80>>(x%8); 373 } 374 375 y = p0.y-1; 376 if(y >= 0){ 377 for(x = p0.x; x >= 0; x--){ 378 if((b[x/8] & 0x80>>(x%8)) == 0) 379 break; 380 fillscan(f, Pt(x, y)); 381 } 382 for(x = p0.x+1; x < f->r0.max.x; x++){ 383 if((b[x/8] & 0x80>>(x%8)) == 0) 384 break; 385 fillscan(f, Pt(x, y)); 386 } 387 } 388 389 y = p0.y+1; 390 if(y < f->r0.max.y){ 391 for(x = p0.x; x >= 0; x--){ 392 if((b[x/8] & 0x80>>(x%8)) == 0) 393 break; 394 fillscan(f, Pt(x, y)); 395 } 396 for(x = p0.x+1; x < f->r0.max.x; x++){ 397 if((b[x/8] & 0x80>>(x%8)) == 0) 398 break; 399 fillscan(f, Pt(x, y)); 400 } 401 } 402 } 403 404 void 405 floodfill(Image *dst, Rectangle r, Point p, Image *src) 406 { 407 Filldata f; 408 409 if(!rectclip(&r, dst->r)) 410 return; 411 if(!ptinrect(p, r)) 412 return; 413 memset(&f, 0, sizeof(f)); 414 f.dst = dst; 415 f.r = r; 416 f.r0 = rectsubpt(r, r.min); 417 f.wmask = bytesperline(f.r0, 1); 418 f.nmask = f.wmask*f.r0.max.y; 419 if((f.bmask = mallocz(f.nmask, 1)) == nil) 420 goto out; 421 if((f.imask = allocimage(display, f.r0, GREY1, 0, DNofill)) == nil) 422 goto out; 423 424 r = f.r0; 425 r.max.y = 1; 426 if((f.iscan = allocimage(display, r, RGB24, 0, DNofill)) == nil) 427 goto out; 428 f.yscan = -1; 429 f.wscan = bytesperline(f.iscan->r, f.iscan->depth); 430 if((f.bscan = mallocz(f.wscan, 0)) == nil) 431 goto out; 432 433 r = Rect(0,0,1,1); 434 f.ncmp = (f.iscan->depth+7) / 8; 435 draw(f.iscan, r, dst, nil, p); 436 if(unloadimage(f.iscan, r, f.bcmp, sizeof(f.bcmp)) < 0) 437 goto out; 438 439 fillscan(&f, subpt(p, f.r.min)); 440 441 loadimage(f.imask, f.imask->r, f.bmask, f.nmask); 442 draw(f.dst, f.r, src, f.imask, f.imask->r.min); 443 out: 444 free(f.bmask); 445 free(f.bscan); 446 if(f.iscan) 447 freeimage(f.iscan); 448 if(f.imask) 449 freeimage(f.imask); 450 } 451 452 void 453 translate(Point d) 454 { 455 Rectangle r, nr; 456 457 if(canvas==nil || d.x==0 && d.y==0) 458 return; 459 r = c2sr(canvas->r); 460 nr = rectaddpt(r, d); 461 rectclip(&r, screen->clipr); 462 draw(screen, rectaddpt(r, d), screen, nil, r.min); 463 zoomdraw(screen, nr, rectaddpt(r, d), back, canvas, canvas->r.min, zoom); 464 gendrawdiff(screen, screen->r, nr, back, ZP, nil, ZP, SoverD); 465 spos = addpt(spos, d); 466 flushimage(display, 1); 467 } 468 469 void 470 setzoom(Point o, int z) 471 { 472 if(z < 1) 473 return; 474 cpos = s2c(o); 475 spos = o; 476 zoom = z; 477 update(nil); 478 } 479 480 void 481 center(void) 482 { 483 cpos = ZP; 484 if(canvas) 485 cpos = addpt(canvas->r.min, 486 divpt(subpt(canvas->r.max, canvas->r.min), 2)); 487 spos = addpt(screen->r.min, 488 divpt(subpt(screen->r.max, screen->r.min), 2)); 489 update(nil); 490 } 491 492 void 493 drawpal(void) 494 { 495 Rectangle r, rr; 496 int i; 497 498 r = screen->r; 499 r.min.y = r.max.y - 20; 500 replclipr(screen, 0, r); 501 502 penr = r; 503 penr.min.x = r.max.x - NBRUSH*Dy(r); 504 505 palr = r; 506 palr.max.x = penr.min.x; 507 508 r = penr; 509 draw(screen, r, back, nil, ZP); 510 for(i=0; i<NBRUSH; i++){ 511 r.max.x = penr.min.x + (i+1)*Dx(penr) / NBRUSH; 512 rr = r; 513 if(i == brush) 514 rr.min.y += Dy(r)/3; 515 if(i == NBRUSH-1){ 516 /* last is special brush for fill draw */ 517 draw(screen, rr, ink, nil, ZP); 518 } else { 519 rr.min = addpt(rr.min, divpt(subpt(rr.max, rr.min), 2)); 520 rr.max = rr.min; 521 strokedraw(screen, rr, ink, i); 522 } 523 r.min.x = r.max.x; 524 } 525 526 r = palr; 527 for(i=1; i<=nelem(pal); i++){ 528 r.max.x = palr.min.x + i*Dx(palr) / nelem(pal); 529 rr = r; 530 if(ink == pal[i-1]) 531 rr.min.y += Dy(r)/3; 532 draw(screen, rr, pal[i-1], nil, ZP); 533 gendrawdiff(screen, r, rr, back, ZP, nil, ZP, SoverD); 534 r.min.x = r.max.x; 535 } 536 537 r = screen->r; 538 r.max.y -= Dy(palr); 539 replclipr(screen, 0, r); 540 } 541 542 int 543 hitpal(Mouse m) 544 { 545 if(ptinrect(m.xy, penr)){ 546 if(m.buttons & 7){ 547 brush = ((m.xy.x - penr.min.x) * NBRUSH) / Dx(penr); 548 drawpal(); 549 } 550 return 1; 551 } 552 if(ptinrect(m.xy, palr)){ 553 Image *col; 554 555 col = pal[(m.xy.x - palr.min.x) * nelem(pal) / Dx(palr)]; 556 switch(m.buttons & 7){ 557 case 1: 558 ink = col; 559 drawpal(); 560 break; 561 case 2: 562 back = col; 563 drawpal(); 564 update(nil); 565 break; 566 } 567 return 1; 568 } 569 return 0; 570 } 571 572 void 573 catch(void * _, char *msg) 574 { 575 USED(_); 576 if(strstr(msg, "closed pipe")) 577 noted(NCONT); 578 noted(NDFLT); 579 } 580 581 int 582 pipeline(char *fmt, ...) 583 { 584 char buf[1024]; 585 va_list a; 586 int p[2]; 587 588 va_start(a, fmt); 589 vsnprint(buf, sizeof(buf), fmt, a); 590 va_end(a); 591 if(pipe(p) < 0) 592 return -1; 593 switch(rfork(RFPROC|RFMEM|RFFDG|RFNOTEG)){ // RFEND not available in libc port 594 case -1: 595 close(p[0]); 596 close(p[1]); 597 return -1; 598 case 0: 599 close(p[1]); 600 dup(p[0], 0); 601 dup(p[0], 1); 602 close(p[0]); 603 execl("/bin/rc", "rc", "-c", buf, nil); 604 exits("exec"); 605 } 606 close(p[0]); 607 return p[1]; 608 } 609 610 void 611 usage(void) 612 { 613 fprint(2, "usage: %s [ file ]\n", argv0); 614 exits("usage"); 615 } 616 617 void 618 main(int argc, char *argv[]) 619 { 620 char *s, buf[1024]; 621 Rectangle r; 622 Image *img; 623 int i, fd; 624 Event e; 625 Mouse m; 626 Point p, d; 627 628 ARGBEGIN { 629 default: 630 usage(); 631 } ARGEND; 632 633 if(argc == 1) 634 filename = strdup(argv[0]); 635 else if(argc != 0) 636 usage(); 637 638 if(initdraw(0, 0, "paint") < 0) 639 sysfatal("initdraw: %r"); 640 641 if(filename){ 642 if((fd = open(filename, OREAD)) < 0) 643 sysfatal("open: %r"); 644 if((canvas = readimage(display, fd, 0)) == nil) 645 sysfatal("readimage: %r"); 646 close(fd); 647 } 648 649 /* palette initialization */ 650 for(i=0; i<nelem(pal); i++){ 651 pal[i] = allocimage(display, Rect(0, 0, 1, 1), RGB24, 1, 652 c64[i % nelem(c64)]<<8 | 0xFF); 653 if(pal[i] == nil) 654 sysfatal("allocimage: %r"); 655 } 656 ink = pal[0]; 657 back = pal[1]; 658 drawpal(); 659 center(); 660 661 einit(Emouse | Ekeyboard); 662 663 notify(catch); 664 for(;;) { 665 switch(event(&e)){ 666 case Emouse: 667 if(hitpal(e.mouse)) 668 continue; 669 670 img = ink; 671 switch(e.mouse.buttons & 7){ 672 case 2: 673 img = back; 674 /* no break */ 675 case 1: 676 p = s2c(e.mouse.xy); 677 if(brush == NBRUSH-1){ 678 /* flood fill brush */ 679 if(canvas == nil || !ptinrect(p, canvas->r)){ 680 back = img; 681 drawpal(); 682 update(nil); 683 break; 684 } 685 r = canvas->r; 686 save(r, 1); 687 floodfill(canvas, r, p, img); 688 update(&r); 689 690 /* wait for mouse release */ 691 while(event(&e) == Emouse && (e.mouse.buttons & 7) != 0) 692 ; 693 break; 694 } 695 r = strokerect(Rpt(p, p), brush); 696 expand(r); 697 save(r, 1); 698 strokedraw(canvas, Rpt(p, p), img, brush); 699 update(&r); 700 for(;;){ 701 m = e.mouse; 702 if(event(&e) != Emouse) 703 break; 704 if((e.mouse.buttons ^ m.buttons) & 7) 705 break; 706 d = s2c(e.mouse.xy); 707 if(eqpt(d, p)) 708 continue; 709 r = strokerect(Rpt(p, d), brush); 710 expand(r); 711 save(r, 0); 712 strokedraw(canvas, Rpt(p, d), img, brush); 713 update(&r); 714 p = d; 715 } 716 break; 717 case 4: 718 for(;;){ 719 m = e.mouse; 720 if(event(&e) != Emouse) 721 break; 722 if((e.mouse.buttons & 7) != 4) 723 break; 724 translate(subpt(e.mouse.xy, m.xy)); 725 } 726 break; 727 } 728 break; 729 case Ekeyboard: 730 switch(e.kbdc){ 731 case Kesc: 732 zoom = 1; 733 center(); 734 break; 735 case '+': 736 if(zoom < 0x1000) 737 setzoom(e.mouse.xy, zoom*2); 738 break; 739 case '-': 740 if(zoom > 1) 741 setzoom(e.mouse.xy, zoom/2); 742 break; 743 case 'c': 744 if(canvas == nil) 745 break; 746 save(canvas->r, 1); 747 freeimage(canvas); 748 canvas = nil; 749 update(nil); 750 break; 751 case 'u': 752 restore(16); 753 break; 754 case 'f': 755 brush = NBRUSH-1; 756 drawpal(); 757 break; 758 case '0': case '1': case '2': case '3': case '4': 759 case '5': case '6': case '7': case '8': case '9': 760 brush = e.kbdc - '0'; 761 drawpal(); 762 break; 763 default: 764 if(e.kbdc == Kdel) 765 e.kbdc = 'q'; 766 buf[0] = 0; 767 if(filename && (e.kbdc == 'r' || e.kbdc == 'w')) 768 snprint(buf, sizeof(buf), "%C %s", e.kbdc, filename); 769 else if(e.kbdc > 0x20 && e.kbdc < 0x7f) 770 snprint(buf, sizeof(buf), "%C", e.kbdc); 771 if(eenter("Cmd", buf, sizeof(buf), &e.mouse) <= 0) 772 break; 773 if(strcmp(buf, "q") == 0) 774 exits(nil); 775 s = buf+1; 776 while(*s == ' ' || *s == '\t') 777 s++; 778 if(*s == 0) 779 break; 780 switch(buf[0]){ 781 case 'r': 782 if((fd = open(s, OREAD)) < 0){ 783 Error: 784 snprint(buf, sizeof(buf), "%r"); 785 eenter(buf, nil, 0, &e.mouse); 786 break; 787 } 788 free(filename); 789 filename = strdup(s); 790 Readimage: 791 unlockdisplay(display); 792 img = readimage(display, fd, 1); 793 close(fd); 794 lockdisplay(display); 795 if(img == nil){ 796 werrstr("readimage: %r"); 797 goto Error; 798 } 799 if(canvas){ 800 save(canvas->r, 1); 801 freeimage(canvas); 802 } 803 canvas = img; 804 center(); 805 break; 806 case 'w': 807 if((fd = create(s, OWRITE, 0660)) < 0) 808 goto Error; 809 free(filename); 810 filename = strdup(s); 811 Writeimage: 812 if(canvas) 813 if(writeimage(fd, canvas, 0) < 0){ 814 close(fd); 815 werrstr("writeimage: %r"); 816 goto Error; 817 } 818 close(fd); 819 break; 820 case '<': 821 if((fd = pipeline("%s", s)) < 0) 822 goto Error; 823 goto Readimage; 824 case '>': 825 if((fd = pipeline("%s", s)) < 0) 826 goto Error; 827 goto Writeimage; 828 case '|': 829 if(canvas == nil) 830 break; 831 if((fd = pipeline("%s", s)) < 0) 832 goto Error; 833 switch(rfork(RFMEM|RFPROC|RFFDG)){ 834 case -1: 835 close(fd); 836 werrstr("rfork: %r"); 837 goto Error; 838 case 0: 839 writeimage(fd, canvas, 1); 840 exits(nil); 841 } 842 goto Readimage; 843 } 844 break; 845 } 846 break; 847 } 848 } 849 } 850 851 void 852 eresized(int _) 853 { 854 USED(_); 855 if(getwindow(display, Refnone) < 0) 856 sysfatal("resize failed"); 857 drawpal(); 858 update(nil); 859 }