1 /* 2 * the actual viewer that handles screen stuff 3 */ 4 5 #include <u.h> 6 #include <libc.h> 7 #include <draw.h> 8 #include <cursor.h> 9 #include <event.h> 10 #include <bio.h> 11 #include <plumb.h> 12 #include <ctype.h> 13 #include <keyboard.h> 14 #include "page.h" 15 16 Document *doc; 17 Image *im; 18 int page; 19 int angle = 0; 20 int showbottom = 0; /* on the next showpage, move the image so the bottom is visible. */ 21 22 Rectangle ulrange; /* the upper left corner of the image must be in this rectangle */ 23 Point ul; /* the upper left corner of the image is at this point on the screen */ 24 25 Point pclip(Point, Rectangle); 26 Rectangle mkrange(Rectangle screenr, Rectangle imr); 27 void redraw(Image*); 28 29 Cursor reading={ 30 {-1, -1}, 31 {0xff, 0x80, 0xff, 0x80, 0xff, 0x00, 0xfe, 0x00, 32 0xff, 0x00, 0xff, 0x80, 0xff, 0xc0, 0xef, 0xe0, 33 0xc7, 0xf0, 0x03, 0xf0, 0x01, 0xe0, 0x00, 0xc0, 34 0x03, 0xff, 0x03, 0xff, 0x03, 0xff, 0x03, 0xff, }, 35 {0x00, 0x00, 0x7f, 0x00, 0x7e, 0x00, 0x7c, 0x00, 36 0x7e, 0x00, 0x7f, 0x00, 0x6f, 0x80, 0x47, 0xc0, 37 0x03, 0xe0, 0x01, 0xf0, 0x00, 0xe0, 0x00, 0x40, 38 0x00, 0x00, 0x01, 0xb6, 0x01, 0xb6, 0x00, 0x00, } 39 }; 40 41 Cursor query = { 42 {-7,-7}, 43 {0x0f, 0xf0, 0x1f, 0xf8, 0x3f, 0xfc, 0x7f, 0xfe, 44 0x7c, 0x7e, 0x78, 0x7e, 0x00, 0xfc, 0x01, 0xf8, 45 0x03, 0xf0, 0x07, 0xe0, 0x07, 0xc0, 0x07, 0xc0, 46 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, }, 47 {0x00, 0x00, 0x0f, 0xf0, 0x1f, 0xf8, 0x3c, 0x3c, 48 0x38, 0x1c, 0x00, 0x3c, 0x00, 0x78, 0x00, 0xf0, 49 0x01, 0xe0, 0x03, 0xc0, 0x03, 0x80, 0x03, 0x80, 50 0x00, 0x00, 0x03, 0x80, 0x03, 0x80, 0x00, 0x00, } 51 }; 52 53 enum { 54 Left = 1, 55 Middle = 2, 56 Right = 4, 57 58 RMenu = 3, 59 }; 60 61 void 62 unhide(void) 63 { 64 static int wctl = -1; 65 66 if(wctl < 0) 67 wctl = open("/dev/wctl", OWRITE); 68 if(wctl < 0) 69 return; 70 71 write(wctl, "unhide", 6); 72 } 73 74 int 75 max(int a, int b) 76 { 77 return a > b ? a : b; 78 } 79 80 int 81 min(int a, int b) 82 { 83 return a < b ? a : b; 84 } 85 86 87 char* 88 menugen(int n) 89 { 90 static char menustr[32]; 91 char *p; 92 int len; 93 94 if(n == doc->npage) 95 return "exit"; 96 if(n > doc->npage) 97 return nil; 98 99 if(reverse) 100 n = doc->npage-1-n; 101 102 p = doc->pagename(doc, n); 103 len = (sizeof menustr)-2; 104 105 if(strlen(p) > len && strrchr(p, '/')) 106 p = strrchr(p, '/')+1; 107 if(strlen(p) > len) 108 p = p+strlen(p)-len; 109 110 strcpy(menustr+1, p); 111 if(page == n) 112 menustr[0] = '>'; 113 else 114 menustr[0] = ' '; 115 return menustr; 116 } 117 118 void 119 showpage(int page, Menu *m) 120 { 121 Image *tmp; 122 123 if(doc->fwdonly) 124 m->lasthit = 0; /* this page */ 125 else 126 m->lasthit = reverse ? doc->npage-1-page : page; 127 128 esetcursor(&reading); 129 freeimage(im); 130 if((page < 0 || page >= doc->npage) && !doc->fwdonly){ 131 im = nil; 132 return; 133 } 134 im = doc->drawpage(doc, page); 135 if(im == nil) { 136 if(doc->fwdonly) /* this is how we know we're out of pages */ 137 wexits(0); 138 139 im = xallocimage(display, Rect(0,0,50,50), GREY1, 1, DBlack); 140 if(im == nil) { 141 fprint(2, "out of memory: %r\n"); 142 wexits("memory"); 143 } 144 string(im, ZP, display->white, ZP, display->defaultfont, "?"); 145 }else if(resizing){ 146 resize(Dx(im->r), Dy(im->r)); 147 } 148 if(im->r.min.x > 0 || im->r.min.y > 0) { 149 tmp = xallocimage(display, Rect(0, 0, Dx(im->r), Dy(im->r)), im->chan, 0, DNofill); 150 if(tmp == nil) { 151 fprint(2, "out of memory during showpage: %r\n"); 152 wexits("memory"); 153 } 154 drawop(tmp, tmp->r, im, nil, im->r.min, S); 155 freeimage(im); 156 im = tmp; 157 } 158 159 switch(angle){ 160 case 90: 161 im = rot90(im); 162 break; 163 case 180: 164 rot180(im); 165 break; 166 case 270: 167 im = rot270(im); 168 break; 169 } 170 171 esetcursor(nil); 172 if(showbottom){ 173 ul.y = screen->r.max.y - Dy(im->r); 174 showbottom = 0; 175 } 176 177 redraw(screen); 178 flushimage(display, 1); 179 } 180 181 char* 182 writebitmap(void) 183 { 184 char basename[64]; 185 char name[64+30]; 186 static char result[200]; 187 char *p, *q; 188 int fd; 189 190 if(im == nil) 191 return "no image"; 192 193 memset(basename, 0, sizeof basename); 194 if(doc->docname) 195 strncpy(basename, doc->docname, sizeof(basename)-1); 196 else if((p = menugen(page)) && p[0] != '\0') 197 strncpy(basename, p+1, sizeof(basename)-1); 198 199 if(basename[0]) { 200 if(q = strrchr(basename, '/')) 201 q++; 202 else 203 q = basename; 204 if(p = strchr(q, '.')) 205 *p = 0; 206 207 memset(name, 0, sizeof name); 208 snprint(name, sizeof(name)-1, "%s.%d.bit", q, page+1); 209 if(access(name, 0) >= 0) { 210 strcat(name, "XXXX"); 211 mktemp(name); 212 } 213 if(access(name, 0) >= 0) 214 return "couldn't think of a name for bitmap"; 215 } else { 216 strcpy(name, "bitXXXX"); 217 mktemp(name); 218 if(access(name, 0) >= 0) 219 return "couldn't think of a name for bitmap"; 220 } 221 222 if((fd = create(name, OWRITE, 0666)) < 0) { 223 snprint(result, sizeof result, "cannot create %s: %r", name); 224 return result; 225 } 226 227 if(writeimage(fd, im, 0) < 0) { 228 snprint(result, sizeof result, "cannot writeimage: %r"); 229 close(fd); 230 return result; 231 } 232 close(fd); 233 234 snprint(result, sizeof result, "wrote %s", name); 235 return result; 236 } 237 238 static void translate(Point); 239 240 static int 241 showdata(Plumbmsg *msg) 242 { 243 char *s; 244 245 s = plumblookup(msg->attr, "action"); 246 return s && strcmp(s, "showdata")==0; 247 } 248 249 /* correspond to entries in miditems[] below, 250 * changing one means you need to change 251 */ 252 enum{ 253 Restore = 0, 254 Zin, 255 Fit, 256 Rot, 257 Upside, 258 Empty1, 259 Next, 260 Prev, 261 Zerox, 262 Empty2, 263 Reverse, 264 Del, 265 Write, 266 Empty3, 267 Exit, 268 }; 269 270 void 271 viewer(Document *dd) 272 { 273 int i, fd, n, oldpage; 274 int nxt; 275 Menu menu, midmenu; 276 Mouse m; 277 Event e; 278 Point dxy, oxy, xy0; 279 Rectangle r; 280 Image *tmp; 281 static char *fwditems[] = { "this page", "next page", "exit", 0 }; 282 static char *miditems[] = { 283 "orig size", 284 "zoom in", 285 "fit window", 286 "rotate 90", 287 "upside down", 288 "", 289 "next", 290 "prev", 291 "zerox", 292 "", 293 "reverse", 294 "discard", 295 "write", 296 "", 297 "quit", 298 0 299 }; 300 char *s; 301 enum { Eplumb = 4 }; 302 Plumbmsg *pm; 303 304 doc = dd; /* save global for menuhit */ 305 ul = screen->r.min; 306 einit(Emouse|Ekeyboard); 307 if(doc->addpage != nil) 308 eplumb(Eplumb, "image"); 309 310 esetcursor(&reading); 311 r.min = ZP; 312 313 /* 314 * im is a global pointer to the current image. 315 * eventually, i think we will have a layer between 316 * the display routines and the ps/pdf/whatever routines 317 * to perhaps cache and handle images of different 318 * sizes, etc. 319 */ 320 im = 0; 321 page = reverse ? doc->npage-1 : 0; 322 323 if(doc->fwdonly) { 324 menu.item = fwditems; 325 menu.gen = 0; 326 menu.lasthit = 0; 327 } else { 328 menu.item = 0; 329 menu.gen = menugen; 330 menu.lasthit = 0; 331 } 332 333 midmenu.item = miditems; 334 midmenu.gen = 0; 335 midmenu.lasthit = Next; 336 337 showpage(page, &menu); 338 esetcursor(nil); 339 340 nxt = 0; 341 for(;;) { 342 /* 343 * throughout, if doc->fwdonly is set, we restrict the functionality 344 * a fair amount. we don't care about doc->npage anymore, and 345 * all that can be done is select the next page. 346 */ 347 switch(eread(Emouse|Ekeyboard|Eplumb, &e)){ 348 case Ekeyboard: 349 if(e.kbdc <= 0xFF && isdigit(e.kbdc)) { 350 nxt = nxt*10+e.kbdc-'0'; 351 break; 352 } else if(e.kbdc != '\n') 353 nxt = 0; 354 switch(e.kbdc) { 355 case 'r': /* reverse page order */ 356 if(doc->fwdonly) 357 break; 358 reverse = !reverse; 359 menu.lasthit = doc->npage-1-menu.lasthit; 360 361 /* 362 * the theory is that if we are reversing the 363 * document order and are on the first or last 364 * page then we're just starting and really want 365 * to view the other end. maybe the if 366 * should be dropped and this should happen always. 367 */ 368 if(page == 0 || page == doc->npage-1) { 369 page = doc->npage-1-page; 370 showpage(page, &menu); 371 } 372 break; 373 case 'w': /* write bitmap of current screen */ 374 esetcursor(&reading); 375 s = writebitmap(); 376 if(s) 377 string(screen, addpt(screen->r.min, Pt(5,5)), display->black, ZP, 378 display->defaultfont, s); 379 esetcursor(nil); 380 flushimage(display, 1); 381 break; 382 case 'd': /* remove image from working set */ 383 if(doc->rmpage && page < doc->npage) { 384 if(doc->rmpage(doc, page) >= 0) { 385 if(doc->npage < 0) 386 wexits(0); 387 if(page >= doc->npage) 388 page = doc->npage-1; 389 showpage(page, &menu); 390 } 391 } 392 break; 393 case 'q': 394 case 0x04: /* ctrl-d */ 395 wexits(0); 396 case 'u': 397 if(im==nil) 398 break; 399 esetcursor(&reading); 400 rot180(im); 401 esetcursor(nil); 402 angle = (angle+180) % 360; 403 redraw(screen); 404 flushimage(display, 1); 405 break; 406 case '-': 407 case '\b': 408 case Kleft: 409 if(page > 0 && !doc->fwdonly) { 410 --page; 411 showpage(page, &menu); 412 } 413 break; 414 case '\n': 415 if(nxt) { 416 nxt--; 417 if(nxt >= 0 && nxt < doc->npage && !doc->fwdonly) 418 showpage(page=nxt, &menu); 419 nxt = 0; 420 break; 421 } 422 goto Gotonext; 423 case Kright: 424 case ' ': 425 Gotonext: 426 if(doc->npage && ++page >= doc->npage && !doc->fwdonly) 427 wexits(0); 428 showpage(page, &menu); 429 break; 430 431 /* 432 * The upper y coordinate of the image is at ul.y in screen->r. 433 * Panning up means moving the upper left corner down. If the 434 * upper left corner is currently visible, we need to go back a page. 435 */ 436 case Kup: 437 if(screen->r.min.y <= ul.y && ul.y < screen->r.max.y){ 438 if(page > 0 && !doc->fwdonly){ 439 --page; 440 showbottom = 1; 441 showpage(page, &menu); 442 } 443 } else { 444 i = Dy(screen->r)/2; 445 if(i > 10) 446 i -= 10; 447 if(i+ul.y > screen->r.min.y) 448 i = screen->r.min.y - ul.y; 449 translate(Pt(0, i)); 450 } 451 break; 452 453 /* 454 * If the lower y coordinate is on the screen, we go to the next page. 455 * The lower y coordinate is at ul.y + Dy(im->r). 456 */ 457 case Kdown: 458 i = ul.y + Dy(im->r); 459 if(screen->r.min.y <= i && i <= screen->r.max.y){ 460 ul.y = screen->r.min.y; 461 goto Gotonext; 462 } else { 463 i = -Dy(screen->r)/2; 464 if(i < -10) 465 i += 10; 466 if(i+ul.y+Dy(im->r) <= screen->r.max.y) 467 i = screen->r.max.y - Dy(im->r) - ul.y - 1; 468 translate(Pt(0, i)); 469 } 470 break; 471 default: 472 esetcursor(&query); 473 sleep(1000); 474 esetcursor(nil); 475 break; 476 } 477 break; 478 479 case Emouse: 480 m = e.mouse; 481 switch(m.buttons){ 482 case Left: 483 oxy = m.xy; 484 xy0 = oxy; 485 do { 486 dxy = subpt(m.xy, oxy); 487 oxy = m.xy; 488 translate(dxy); 489 m = emouse(); 490 } while(m.buttons == Left); 491 if(m.buttons) { 492 dxy = subpt(xy0, oxy); 493 translate(dxy); 494 } 495 break; 496 497 case Middle: 498 if(doc->npage == 0) 499 break; 500 501 n = emenuhit(Middle, &m, &midmenu); 502 if(n == -1) 503 break; 504 switch(n){ 505 case Next: /* next */ 506 if(reverse) 507 page--; 508 else 509 page++; 510 if(page < 0) { 511 if(reverse) return; 512 else page = 0; 513 } 514 515 if((page >= doc->npage) && !doc->fwdonly) 516 return; 517 518 showpage(page, &menu); 519 nxt = 0; 520 break; 521 case Prev: /* prev */ 522 if(reverse) 523 page++; 524 else 525 page--; 526 if(page < 0) { 527 if(reverse) return; 528 else page = 0; 529 } 530 531 if((page >= doc->npage) && !doc->fwdonly && !reverse) 532 return; 533 534 showpage(page, &menu); 535 nxt = 0; 536 break; 537 case Zerox: /* prev */ 538 zerox(); 539 break; 540 case Zin: /* zoom in */ 541 { 542 double delta; 543 Rectangle r; 544 545 r = egetrect(Middle, &m); 546 if((rectclip(&r, rectaddpt(im->r, ul)) == 0) || 547 Dx(r) == 0 || Dy(r) == 0) 548 break; 549 /* use the smaller side to expand */ 550 if(Dx(r) < Dy(r)) 551 delta = (double)Dx(im->r)/(double)Dx(r); 552 else 553 delta = (double)Dy(im->r)/(double)Dy(r); 554 555 esetcursor(&reading); 556 tmp = xallocimage(display, 557 Rect(0, 0, (int)((double)Dx(im->r)*delta), (int)((double)Dy(im->r)*delta)), 558 im->chan, 0, DBlack); 559 if(tmp == nil) { 560 fprint(2, "out of memory during zoom: %r\n"); 561 wexits("memory"); 562 } 563 resample(im, tmp); 564 freeimage(im); 565 im = tmp; 566 esetcursor(nil); 567 ul = screen->r.min; 568 redraw(screen); 569 flushimage(display, 1); 570 break; 571 } 572 case Fit: /* fit */ 573 { 574 double delta; 575 Rectangle r; 576 577 delta = (double)Dx(screen->r)/(double)Dx(im->r); 578 if((double)Dy(im->r)*delta > Dy(screen->r)) 579 delta = (double)Dy(screen->r)/(double)Dy(im->r); 580 581 r = Rect(0, 0, (int)((double)Dx(im->r)*delta), (int)((double)Dy(im->r)*delta)); 582 esetcursor(&reading); 583 tmp = xallocimage(display, r, im->chan, 0, DBlack); 584 if(tmp == nil) { 585 fprint(2, "out of memory during fit: %r\n"); 586 wexits("memory"); 587 } 588 resample(im, tmp); 589 freeimage(im); 590 im = tmp; 591 esetcursor(nil); 592 ul = screen->r.min; 593 redraw(screen); 594 flushimage(display, 1); 595 break; 596 } 597 case Rot: /* rotate 90 */ 598 esetcursor(&reading); 599 im = rot90(im); 600 esetcursor(nil); 601 angle = (angle+90) % 360; 602 redraw(screen); 603 flushimage(display, 1); 604 break; 605 case Upside: /* upside-down */ 606 if(im==nil) 607 break; 608 esetcursor(&reading); 609 rot180(im); 610 esetcursor(nil); 611 angle = (angle+180) % 360; 612 redraw(screen); 613 flushimage(display, 1); 614 break; 615 case Restore: /* restore */ 616 showpage(page, &menu); 617 break; 618 case Reverse: /* reverse */ 619 if(doc->fwdonly) 620 break; 621 reverse = !reverse; 622 menu.lasthit = doc->npage-1-menu.lasthit; 623 624 if(page == 0 || page == doc->npage-1) { 625 page = doc->npage-1-page; 626 showpage(page, &menu); 627 } 628 break; 629 case Write: /* write */ 630 esetcursor(&reading); 631 s = writebitmap(); 632 if(s) 633 string(screen, addpt(screen->r.min, Pt(5,5)), display->black, ZP, 634 display->defaultfont, s); 635 esetcursor(nil); 636 flushimage(display, 1); 637 break; 638 case Del: /* delete */ 639 if(doc->rmpage && page < doc->npage) { 640 if(doc->rmpage(doc, page) >= 0) { 641 if(doc->npage < 0) 642 wexits(0); 643 if(page >= doc->npage) 644 page = doc->npage-1; 645 showpage(page, &menu); 646 } 647 } 648 break; 649 case Exit: /* exit */ 650 return; 651 case Empty1: 652 case Empty2: 653 case Empty3: 654 break; 655 656 }; 657 658 659 660 case Right: 661 if(doc->npage == 0) 662 break; 663 664 oldpage = page; 665 n = emenuhit(RMenu, &m, &menu); 666 if(n == -1) 667 break; 668 669 if(doc->fwdonly) { 670 switch(n){ 671 case 0: /* this page */ 672 break; 673 case 1: /* next page */ 674 showpage(++page, &menu); 675 break; 676 case 2: /* exit */ 677 return; 678 } 679 break; 680 } 681 682 if(n == doc->npage) 683 return; 684 else 685 page = reverse ? doc->npage-1-n : n; 686 687 if(oldpage != page) 688 showpage(page, &menu); 689 nxt = 0; 690 break; 691 } 692 break; 693 694 case Eplumb: 695 pm = e.v; 696 if(pm->ndata <= 0){ 697 plumbfree(pm); 698 break; 699 } 700 if(showdata(pm)) { 701 s = estrdup("/tmp/pageplumbXXXXXXX"); 702 fd = opentemp(s); 703 write(fd, pm->data, pm->ndata); 704 /* lose fd reference on purpose; the file is open ORCLOSE */ 705 } else if(pm->data[0] == '/') { 706 s = estrdup(pm->data); 707 } else { 708 s = emalloc(strlen(pm->wdir)+1+pm->ndata+1); 709 sprint(s, "%s/%s", pm->wdir, pm->data); 710 cleanname(s); 711 } 712 if((i = doc->addpage(doc, s)) >= 0) { 713 page = i; 714 unhide(); 715 showpage(page, &menu); 716 } 717 free(s); 718 plumbfree(pm); 719 break; 720 } 721 } 722 } 723 724 Image *gray; 725 726 /* 727 * A draw operation that touches only the area contained in bot but not in top. 728 * mp and sp get aligned with bot.min. 729 */ 730 static void 731 gendrawdiff(Image *dst, Rectangle bot, Rectangle top, 732 Image *src, Point sp, Image *mask, Point mp, int op) 733 { 734 Rectangle r; 735 Point origin; 736 Point delta; 737 738 USED(op); 739 740 if(Dx(bot)*Dy(bot) == 0) 741 return; 742 743 /* no points in bot - top */ 744 if(rectinrect(bot, top)) 745 return; 746 747 /* bot - top ≡ bot */ 748 if(Dx(top)*Dy(top)==0 || rectXrect(bot, top)==0){ 749 gendrawop(dst, bot, src, sp, mask, mp, op); 750 return; 751 } 752 753 origin = bot.min; 754 /* split bot into rectangles that don't intersect top */ 755 /* left side */ 756 if(bot.min.x < top.min.x){ 757 r = Rect(bot.min.x, bot.min.y, top.min.x, bot.max.y); 758 delta = subpt(r.min, origin); 759 gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op); 760 bot.min.x = top.min.x; 761 } 762 763 /* right side */ 764 if(bot.max.x > top.max.x){ 765 r = Rect(top.max.x, bot.min.y, bot.max.x, bot.max.y); 766 delta = subpt(r.min, origin); 767 gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op); 768 bot.max.x = top.max.x; 769 } 770 771 /* top */ 772 if(bot.min.y < top.min.y){ 773 r = Rect(bot.min.x, bot.min.y, bot.max.x, top.min.y); 774 delta = subpt(r.min, origin); 775 gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op); 776 bot.min.y = top.min.y; 777 } 778 779 /* bottom */ 780 if(bot.max.y > top.max.y){ 781 r = Rect(bot.min.x, top.max.y, bot.max.x, bot.max.y); 782 delta = subpt(r.min, origin); 783 gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op); 784 bot.max.y = top.max.y; 785 } 786 } 787 788 static void 789 drawdiff(Image *dst, Rectangle bot, Rectangle top, Image *src, Image *mask, Point p, int op) 790 { 791 gendrawdiff(dst, bot, top, src, p, mask, p, op); 792 } 793 794 /* 795 * Translate the image in the window by delta. 796 */ 797 static void 798 translate(Point delta) 799 { 800 Point u; 801 Rectangle r, or; 802 803 if(im == nil) 804 return; 805 806 u = pclip(addpt(ul, delta), ulrange); 807 delta = subpt(u, ul); 808 if(delta.x == 0 && delta.y == 0) 809 return; 810 811 /* 812 * The upper left corner of the image is currently at ul. 813 * We want to move it to u. 814 */ 815 or = rectaddpt(Rpt(ZP, Pt(Dx(im->r), Dy(im->r))), ul); 816 r = rectaddpt(or, delta); 817 818 drawop(screen, r, screen, nil, ul, S); 819 ul = u; 820 821 /* fill in gray where image used to be but isn't. */ 822 drawdiff(screen, insetrect(or, -2), insetrect(r, -2), gray, nil, ZP, S); 823 824 /* fill in black border */ 825 drawdiff(screen, insetrect(r, -2), r, display->black, nil, ZP, S); 826 827 /* fill in image where it used to be off the screen. */ 828 if(rectclip(&or, screen->r)) 829 drawdiff(screen, r, rectaddpt(or, delta), im, nil, im->r.min, S); 830 else 831 drawop(screen, r, im, nil, im->r.min, S); 832 flushimage(display, 1); 833 } 834 835 void 836 redraw(Image *screen) 837 { 838 Rectangle r; 839 840 if(im == nil) 841 return; 842 843 ulrange.max = screen->r.max; 844 ulrange.min = subpt(screen->r.min, Pt(Dx(im->r), Dy(im->r))); 845 846 ul = pclip(ul, ulrange); 847 drawop(screen, screen->r, im, nil, subpt(im->r.min, subpt(ul, screen->r.min)), S); 848 849 if(im->repl) 850 return; 851 852 /* fill in any outer edges */ 853 /* black border */ 854 r = rectaddpt(im->r, subpt(ul, im->r.min)); 855 border(screen, r, -2, display->black, ZP); 856 r.min = subpt(r.min, Pt(2,2)); 857 r.max = addpt(r.max, Pt(2,2)); 858 859 /* gray for the rest */ 860 if(gray == nil) { 861 gray = xallocimage(display, Rect(0,0,1,1), RGB24, 1, 0x888888FF); 862 if(gray == nil) { 863 fprint(2, "g out of memory: %r\n"); 864 wexits("mem"); 865 } 866 } 867 border(screen, r, -4000, gray, ZP); 868 // flushimage(display, 0); 869 } 870 871 void 872 eresized(int new) 873 { 874 Rectangle r; 875 r = screen->r; 876 if(new && getwindow(display, Refnone) < 0) 877 fprint(2,"can't reattach to window"); 878 ul = addpt(ul, subpt(screen->r.min, r.min)); 879 redraw(screen); 880 } 881 882 /* clip p to be in r */ 883 Point 884 pclip(Point p, Rectangle r) 885 { 886 if(p.x < r.min.x) 887 p.x = r.min.x; 888 else if(p.x >= r.max.x) 889 p.x = r.max.x-1; 890 891 if(p.y < r.min.y) 892 p.y = r.min.y; 893 else if(p.y >= r.max.y) 894 p.y = r.max.y-1; 895 896 return p; 897 } 898 899 /* 900 * resize is perhaps a misnomer. 901 * this really just grows the window to be at least dx across 902 * and dy high. if the window hits the bottom or right edge, 903 * it is backed up until it hits the top or left edge. 904 */ 905 void 906 resize(int dx, int dy) 907 { 908 static Rectangle sr; 909 Rectangle r, or; 910 911 dx += 2*Borderwidth; 912 dy += 2*Borderwidth; 913 if(wctlfd < 0){ 914 wctlfd = open("/dev/wctl", OWRITE); 915 if(wctlfd < 0) 916 return; 917 } 918 919 r = insetrect(screen->r, -Borderwidth); 920 if(Dx(r) >= dx && Dy(r) >= dy) 921 return; 922 923 if(Dx(sr)*Dy(sr) == 0) 924 sr = screenrect(); 925 926 or = r; 927 928 r.max.x = max(r.min.x+dx, r.max.x); 929 r.max.y = max(r.min.y+dy, r.max.y); 930 if(r.max.x > sr.max.x){ 931 if(Dx(r) > Dx(sr)){ 932 r.min.x = 0; 933 r.max.x = sr.max.x; 934 }else 935 r = rectaddpt(r, Pt(sr.max.x-r.max.x, 0)); 936 } 937 if(r.max.y > sr.max.y){ 938 if(Dy(r) > Dy(sr)){ 939 r.min.y = 0; 940 r.max.y = sr.max.y; 941 }else 942 r = rectaddpt(r, Pt(0, sr.max.y-r.max.y)); 943 } 944 945 /* 946 * Sometimes we can't actually grow the window big enough, 947 * and resizing it to the same shape makes it flash. 948 */ 949 if(Dx(r) == Dx(or) && Dy(r) == Dy(or)) 950 return; 951 952 fprint(wctlfd, "resize -minx %d -miny %d -maxx %d -maxy %d\n", 953 r.min.x, r.min.y, r.max.x, r.max.y); 954 } 955 956 /* 957 * If we allocimage after a resize but before flushing the draw buffer, 958 * we won't have seen the reshape event, and we won't have called 959 * getwindow, and allocimage will fail. So we flushimage before every alloc. 960 */ 961 Image* 962 xallocimage(Display *d, Rectangle r, ulong chan, int repl, ulong val) 963 { 964 flushimage(display, 0); 965 return allocimage(d, r, chan, repl, val); 966 } 967 968 /* all code below this line should be in the library, but is stolen from colors instead */ 969 static char* 970 rdenv(char *name) 971 { 972 char *v; 973 int fd, size; 974 975 fd = open(name, OREAD); 976 if(fd < 0) 977 return 0; 978 size = seek(fd, 0, 2); 979 v = malloc(size+1); 980 if(v == 0){ 981 fprint(2, "page: can't malloc: %r\n"); 982 wexits("no mem"); 983 } 984 seek(fd, 0, 0); 985 read(fd, v, size); 986 v[size] = 0; 987 close(fd); 988 return v; 989 } 990 991 void 992 newwin(void) 993 { 994 char *srv, *mntsrv; 995 char spec[100]; 996 int srvfd, cons, pid; 997 998 switch(rfork(RFFDG|RFPROC|RFNAMEG|RFENVG|RFNOTEG|RFNOWAIT)){ 999 case -1: 1000 fprint(2, "page: can't fork: %r\n"); 1001 wexits("no fork"); 1002 case 0: 1003 break; 1004 default: 1005 wexits(0); 1006 } 1007 1008 srv = rdenv("/env/wsys"); 1009 if(srv == 0){ 1010 mntsrv = rdenv("/mnt/term/env/wsys"); 1011 if(mntsrv == 0){ 1012 fprint(2, "page: can't find $wsys\n"); 1013 wexits("srv"); 1014 } 1015 srv = malloc(strlen(mntsrv)+10); 1016 sprint(srv, "/mnt/term%s", mntsrv); 1017 free(mntsrv); 1018 pid = 0; /* can't send notes to remote processes! */ 1019 }else 1020 pid = getpid(); 1021 srvfd = open(srv, ORDWR); 1022 free(srv); 1023 if(srvfd == -1){ 1024 fprint(2, "page: can't open %s: %r\n", srv); 1025 wexits("no srv"); 1026 } 1027 sprint(spec, "new -pid %d", pid); 1028 if(mount(srvfd, -1, "/mnt/wsys", 0, spec) == -1){ 1029 fprint(2, "page: can't mount /mnt/wsys: %r (spec=%s)\n", spec); 1030 wexits("no mount"); 1031 } 1032 close(srvfd); 1033 unmount("/mnt/acme", "/dev"); 1034 bind("/mnt/wsys", "/dev", MBEFORE); 1035 cons = open("/dev/cons", OREAD); 1036 if(cons==-1){ 1037 NoCons: 1038 fprint(2, "page: can't open /dev/cons: %r"); 1039 wexits("no cons"); 1040 } 1041 dup(cons, 0); 1042 close(cons); 1043 cons = open("/dev/cons", OWRITE); 1044 if(cons==-1) 1045 goto NoCons; 1046 dup(cons, 1); 1047 dup(cons, 2); 1048 close(cons); 1049 // wctlfd = open("/dev/wctl", OWRITE); 1050 } 1051 1052 Rectangle 1053 screenrect(void) 1054 { 1055 int fd; 1056 char buf[12*5]; 1057 1058 fd = open("/dev/screen", OREAD); 1059 if(fd == -1) 1060 fd=open("/mnt/term/dev/screen", OREAD); 1061 if(fd == -1){ 1062 fprint(2, "page: can't open /dev/screen: %r\n"); 1063 wexits("window read"); 1064 } 1065 if(read(fd, buf, sizeof buf) != sizeof buf){ 1066 fprint(2, "page: can't read /dev/screen: %r\n"); 1067 wexits("screen read"); 1068 } 1069 close(fd); 1070 return Rect(atoi(buf+12), atoi(buf+24), atoi(buf+36), atoi(buf+48)); 1071 } 1072 1073 void 1074 zerox(void) 1075 { 1076 int pfd[2]; 1077 1078 pipe(pfd); 1079 switch(rfork(RFFDG|RFREND|RFPROC)) { 1080 case -1: 1081 wexits("cannot fork in zerox: %r"); 1082 case 0: 1083 dup(pfd[1], 0); 1084 close(pfd[0]); 1085 execl("/bin/page", "page", "-w", nil); 1086 wexits("cannot exec in zerox: %r\n"); 1087 default: 1088 close(pfd[1]); 1089 writeimage(pfd[0], im, 0); 1090 close(pfd[0]); 1091 break; 1092 } 1093 } 1094