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