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