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