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