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 int 62 max(int a, int b) 63 { 64 return a > b ? a : b; 65 } 66 67 int 68 min(int a, int b) 69 { 70 return a < b ? a : b; 71 } 72 73 74 char* 75 menugen(int n) 76 { 77 static char menustr[32]; 78 char *p; 79 int len; 80 81 if(n == doc->npage) 82 return "exit"; 83 if(n > doc->npage) 84 return nil; 85 86 if(reverse) 87 n = doc->npage-1-n; 88 89 p = doc->pagename(doc, n); 90 len = (sizeof menustr)-2; 91 92 if(strlen(p) > len && strrchr(p, '/')) 93 p = strrchr(p, '/')+1; 94 if(strlen(p) > len) 95 p = p+strlen(p)-len; 96 97 strcpy(menustr+1, p); 98 if(page == n) 99 menustr[0] = '>'; 100 else 101 menustr[0] = ' '; 102 return menustr; 103 } 104 105 void 106 showpage(int page, Menu *m) 107 { 108 if(doc->fwdonly) 109 m->lasthit = 0; /* this page */ 110 else 111 m->lasthit = reverse ? doc->npage-1-page : page; 112 113 esetcursor(&reading); 114 freeimage(im); 115 if((page < 0 || page >= doc->npage) && !doc->fwdonly){ 116 im = nil; 117 return; 118 } 119 im = doc->drawpage(doc, page); 120 if(im == nil) { 121 if(doc->fwdonly) /* this is how we know we're out of pages */ 122 wexits(0); 123 124 im = xallocimage(display, Rect(0,0,50,50), GREY1, 1, DBlack); 125 if(im == nil) { 126 fprint(2, "out of memory: %r\n"); 127 wexits("memory"); 128 } 129 string(im, ZP, display->white, ZP, display->defaultfont, "?"); 130 }else if(resizing){ 131 resize(Dx(im->r), Dy(im->r)); 132 } 133 if(upside) 134 rot180(im); 135 136 esetcursor(nil); 137 if(showbottom){ 138 ul.y = screen->r.max.y - Dy(im->r); 139 showbottom = 0; 140 } 141 142 redraw(screen); 143 flushimage(display, 1); 144 } 145 146 char* 147 writebitmap(void) 148 { 149 char basename[64]; 150 char name[64+30]; 151 static char result[200]; 152 char *p, *q; 153 int fd; 154 155 if(im == nil) 156 return "no image"; 157 158 memset(basename, 0, sizeof basename); 159 if(doc->docname) 160 strncpy(basename, doc->docname, sizeof(basename)-1); 161 else if((p = menugen(page)) && p[0] != '\0') 162 strncpy(basename, p+1, sizeof(basename)-1); 163 164 if(basename[0]) { 165 if(q = strrchr(basename, '/')) 166 q++; 167 else 168 q = basename; 169 if(p = strchr(q, '.')) 170 *p = 0; 171 172 memset(name, 0, sizeof name); 173 snprint(name, sizeof(name)-1, "%s.%d.bit", q, page+1); 174 if(access(name, 0) >= 0) { 175 strcat(name, "XXXX"); 176 mktemp(name); 177 } 178 if(access(name, 0) >= 0) 179 return "couldn't think of a name for bitmap"; 180 } else { 181 strcpy(name, "bitXXXX"); 182 mktemp(name); 183 if(access(name, 0) >= 0) 184 return "couldn't think of a name for bitmap"; 185 } 186 187 if((fd = create(name, OWRITE, 0666)) < 0) { 188 snprint(result, sizeof result, "cannot create %s: %r", name); 189 return result; 190 } 191 192 if(writeimage(fd, im, 0) < 0) { 193 snprint(result, sizeof result, "cannot writeimage: %r"); 194 close(fd); 195 return result; 196 } 197 close(fd); 198 199 snprint(result, sizeof result, "wrote %s", name); 200 return result; 201 } 202 203 static void translate(Point); 204 205 static int 206 showdata(Plumbmsg *msg) 207 { 208 char *s; 209 210 s = plumblookup(msg->attr, "action"); 211 return s && strcmp(s, "showdata")==0; 212 } 213 214 void 215 viewer(Document *dd) 216 { 217 int i, fd, n, oldpage; 218 int nxt; 219 Menu menu; 220 Mouse m; 221 Event e; 222 Point dxy, oxy, xy0; 223 Rectangle r; 224 char *fwditems[] = { "this page", "next page", "exit", 0 }; 225 char *s; 226 enum { Eplumb = 4 }; 227 Plumbmsg *pm; 228 229 doc = dd; /* save global for menuhit */ 230 ul = screen->r.min; 231 einit(Emouse|Ekeyboard); 232 if(doc->addpage != nil) 233 eplumb(Eplumb, "image"); 234 235 esetcursor(&reading); 236 r.min = ZP; 237 238 /* 239 * im is a global pointer to the current image. 240 * eventually, i think we will have a layer between 241 * the display routines and the ps/pdf/whatever routines 242 * to perhaps cache and handle images of different 243 * sizes, etc. 244 */ 245 im = 0; 246 page = reverse ? doc->npage-1 : 0; 247 248 if(doc->fwdonly) { 249 menu.item = fwditems; 250 menu.gen = 0; 251 menu.lasthit = 0; 252 } else { 253 menu.item = 0; 254 menu.gen = menugen; 255 menu.lasthit = 0; 256 } 257 258 showpage(page, &menu); 259 esetcursor(nil); 260 261 nxt = 0; 262 for(;;) { 263 /* 264 * throughout, if doc->fwdonly is set, we restrict the functionality 265 * a fair amount. we don't care about doc->npage anymore, and 266 * all that can be done is select the next page. 267 */ 268 switch(eread(Emouse|Ekeyboard|Eplumb, &e)){ 269 case Ekeyboard: 270 if(e.kbdc <= 0xFF && isdigit(e.kbdc)) { 271 nxt = nxt*10+e.kbdc-'0'; 272 break; 273 } else if(e.kbdc != '\n') 274 nxt = 0; 275 switch(e.kbdc) { 276 case 'r': /* reverse page order */ 277 if(doc->fwdonly) 278 break; 279 reverse = !reverse; 280 menu.lasthit = doc->npage-1-menu.lasthit; 281 282 /* 283 * the theory is that if we are reversing the 284 * document order and are on the first or last 285 * page then we're just starting and really want 286 * to view the other end. maybe the if 287 * should be dropped and this should happen always. 288 */ 289 if(page == 0 || page == doc->npage-1) { 290 page = doc->npage-1-page; 291 showpage(page, &menu); 292 } 293 break; 294 case 'w': /* write bitmap of current screen */ 295 esetcursor(&reading); 296 s = writebitmap(); 297 if(s) 298 string(screen, addpt(screen->r.min, Pt(5,5)), display->black, ZP, 299 display->defaultfont, s); 300 esetcursor(nil); 301 flushimage(display, 1); 302 break; 303 case 'd': /* remove image from working set */ 304 if(doc->rmpage && page < doc->npage) { 305 if(doc->rmpage(doc, page) >= 0) { 306 if(doc->npage < 0) 307 wexits(0); 308 if(page >= doc->npage) 309 page = doc->npage-1; 310 showpage(page, &menu); 311 } 312 } 313 break; 314 case 'q': 315 case 0x04: /* ctrl-d */ 316 wexits(0); 317 case 'u': 318 if(im==nil) 319 break; 320 esetcursor(&reading); 321 rot180(im); 322 esetcursor(nil); 323 upside = !upside; 324 redraw(screen); 325 flushimage(display, 1); 326 break; 327 case '-': 328 case '\b': 329 case Kleft: 330 if(page > 0 && !doc->fwdonly) { 331 --page; 332 showpage(page, &menu); 333 } 334 break; 335 case '\n': 336 if(nxt) { 337 nxt--; 338 if(nxt >= 0 && nxt < doc->npage && !doc->fwdonly) 339 showpage(page=nxt, &menu); 340 nxt = 0; 341 break; 342 } 343 goto Gotonext; 344 case Kright: 345 case ' ': 346 Gotonext: 347 if(doc->npage && ++page >= doc->npage && !doc->fwdonly) 348 wexits(0); 349 showpage(page, &menu); 350 break; 351 352 /* 353 * The upper y coordinate of the image is at ul.y in screen->r. 354 * Panning up means moving the upper left corner down. If the 355 * upper left corner is currently visible, we need to go back a page. 356 */ 357 case Kup: 358 if(screen->r.min.y <= ul.y && ul.y < screen->r.max.y){ 359 if(page > 0 && !doc->fwdonly){ 360 --page; 361 showbottom = 1; 362 showpage(page, &menu); 363 } 364 } else { 365 i = Dy(screen->r)/2; 366 if(i > 10) 367 i -= 10; 368 if(i+ul.y > screen->r.min.y) 369 i = screen->r.min.y - ul.y; 370 translate(Pt(0, i)); 371 } 372 break; 373 374 /* 375 * If the lower y coordinate is on the screen, we go to the next page. 376 * The lower y coordinate is at ul.y + Dy(im->r). 377 */ 378 case Kdown: 379 i = ul.y + Dy(im->r); 380 if(screen->r.min.y <= i && i <= screen->r.max.y){ 381 ul.y = screen->r.min.y; 382 goto Gotonext; 383 } else { 384 i = -Dy(screen->r)/2; 385 if(i < -10) 386 i += 10; 387 if(i+ul.y+Dy(im->r) <= screen->r.max.y) 388 i = screen->r.max.y - Dy(im->r) - ul.y - 1; 389 translate(Pt(0, i)); 390 } 391 break; 392 default: 393 esetcursor(&query); 394 sleep(1000); 395 esetcursor(nil); 396 break; 397 } 398 break; 399 400 case Emouse: 401 m = e.mouse; 402 switch(m.buttons){ 403 case Left: 404 oxy = m.xy; 405 xy0 = oxy; 406 do { 407 dxy = subpt(m.xy, oxy); 408 oxy = m.xy; 409 translate(dxy); 410 m = emouse(); 411 } while(m.buttons == Left); 412 if(m.buttons) { 413 dxy = subpt(xy0, oxy); 414 translate(dxy); 415 } 416 break; 417 418 case Middle: 419 do 420 m = emouse(); 421 while(m.buttons == Middle); 422 if(m.buttons) 423 break; 424 425 if(doc->npage == 0) 426 break; 427 428 if(reverse) 429 page--; 430 else 431 page++; 432 433 if((page >= doc->npage || page < 0) && !doc->fwdonly) 434 return; 435 436 showpage(page, &menu); 437 nxt = 0; 438 break; 439 440 case Right: 441 if(doc->npage == 0) 442 break; 443 444 oldpage = page; 445 n = emenuhit(RMenu, &m, &menu); 446 if(n == -1) 447 break; 448 449 if(doc->fwdonly) { 450 switch(n){ 451 case 0: /* this page */ 452 break; 453 case 1: /* next page */ 454 showpage(++page, &menu); 455 break; 456 case 2: /* exit */ 457 return; 458 } 459 break; 460 } 461 462 if(n == doc->npage) 463 return; 464 else 465 page = reverse ? doc->npage-1-n : n; 466 467 if(oldpage != page) 468 showpage(page, &menu); 469 nxt = 0; 470 break; 471 } 472 break; 473 474 case Eplumb: 475 pm = e.v; 476 if(pm->ndata <= 0){ 477 plumbfree(pm); 478 break; 479 } 480 if(showdata(pm)) { 481 s = estrdup("/tmp/pageplumbXXXXXXX"); 482 fd = opentemp(s); 483 write(fd, pm->data, pm->ndata); 484 /* lose fd reference on purpose; the file is open ORCLOSE */ 485 } else if(pm->data[0] == '/') { 486 s = estrdup(pm->data); 487 } else { 488 s = emalloc(strlen(pm->wdir)+1+pm->ndata+1); 489 sprint(s, "%s/%s", pm->wdir, pm->data); 490 cleanname(s); 491 } 492 if((i = doc->addpage(doc, s)) >= 0) { 493 page = i; 494 showpage(page, &menu); 495 } 496 free(s); 497 plumbfree(pm); 498 break; 499 } 500 } 501 } 502 503 Image *gray; 504 505 /* 506 * A draw operation that touches only the area contained in bot but not in top. 507 * mp and sp get aligned with bot.min. 508 */ 509 static void 510 gendrawdiff(Image *dst, Rectangle bot, Rectangle top, 511 Image *src, Point sp, Image *mask, Point mp) 512 { 513 Rectangle r; 514 Point origin; 515 Point delta; 516 517 if(Dx(bot)*Dy(bot) == 0) 518 return; 519 520 /* no points in bot - top */ 521 if(rectinrect(bot, top)) 522 return; 523 524 /* bot - top ≡ bot */ 525 if(Dx(top)*Dy(top)==0 || rectXrect(bot, top)==0){ 526 gendraw(dst, bot, src, sp, mask, mp); 527 return; 528 } 529 530 origin = bot.min; 531 /* split bot into rectangles that don't intersect top */ 532 /* left side */ 533 if(bot.min.x < top.min.x){ 534 r = Rect(bot.min.x, bot.min.y, top.min.x, bot.max.y); 535 delta = subpt(r.min, origin); 536 gendraw(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta)); 537 bot.min.x = top.min.x; 538 } 539 540 /* right side */ 541 if(bot.max.x > top.max.x){ 542 r = Rect(top.max.x, bot.min.y, bot.max.x, bot.max.y); 543 delta = subpt(r.min, origin); 544 gendraw(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta)); 545 bot.max.x = top.max.x; 546 } 547 548 /* top */ 549 if(bot.min.y < top.min.y){ 550 r = Rect(bot.min.x, bot.min.y, bot.max.x, top.min.y); 551 delta = subpt(r.min, origin); 552 gendraw(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta)); 553 bot.min.y = top.min.y; 554 } 555 556 /* bottom */ 557 if(bot.max.y > top.max.y){ 558 r = Rect(bot.min.x, top.max.y, bot.max.x, bot.max.y); 559 delta = subpt(r.min, origin); 560 gendraw(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta)); 561 bot.max.y = top.max.y; 562 } 563 } 564 565 static void 566 drawdiff(Image *dst, Rectangle bot, Rectangle top, Image *src, Image *mask, Point p) 567 { 568 gendrawdiff(dst, bot, top, src, p, mask, p); 569 } 570 571 /* 572 * Translate the image in the window by delta. 573 */ 574 static void 575 translate(Point delta) 576 { 577 Point u; 578 Rectangle r, or; 579 580 if(im == nil) 581 return; 582 583 u = pclip(addpt(ul, delta), ulrange); 584 delta = subpt(u, ul); 585 if(delta.x == 0 && delta.y == 0) 586 return; 587 588 /* 589 * The upper left corner of the image is currently at ul. 590 * We want to move it to u. 591 */ 592 or = rectaddpt(Rpt(ZP, Pt(Dx(im->r), Dy(im->r))), ul); 593 r = rectaddpt(or, delta); 594 595 draw(screen, r, screen, nil, ul); 596 ul = u; 597 598 /* fill in gray where image used to be but isn't. */ 599 drawdiff(screen, insetrect(or, -2), insetrect(r, -2), gray, nil, ZP); 600 601 /* fill in black border */ 602 drawdiff(screen, insetrect(r, -2), r, display->black, nil, ZP); 603 604 /* fill in image where it used to be off the screen. */ 605 if(rectclip(&or, screen->r)) 606 drawdiff(screen, r, rectaddpt(or, delta), im, nil, im->r.min); 607 else 608 draw(screen, r, im, nil, im->r.min); 609 flushimage(display, 1); 610 } 611 612 void 613 redraw(Image *screen) 614 { 615 Rectangle r; 616 617 if(im == nil) 618 return; 619 620 ulrange.max = screen->r.max; 621 ulrange.min = subpt(screen->r.min, Pt(Dx(im->r), Dy(im->r))); 622 623 ul = pclip(ul, ulrange); 624 draw(screen, screen->r, im, nil, subpt(im->r.min, subpt(ul, screen->r.min))); 625 626 if(im->repl) 627 return; 628 629 /* fill in any outer edges */ 630 /* black border */ 631 r = rectaddpt(im->r, subpt(ul, im->r.min)); 632 border(screen, r, -2, display->black, ZP); 633 r.min = subpt(r.min, Pt(2,2)); 634 r.max = addpt(r.max, Pt(2,2)); 635 636 /* gray for the rest */ 637 if(gray == nil) { 638 gray = xallocimage(display, Rect(0,0,1,1), RGB24, 1, 0x888888FF); 639 if(gray == nil) { 640 fprint(2, "g out of memory: %r\n"); 641 wexits("mem"); 642 } 643 } 644 border(screen, r, -4000, gray, ZP); 645 // flushimage(display, 0); 646 } 647 648 void 649 eresized(int new) 650 { 651 Rectangle r; 652 r = screen->r; 653 if(new && getwindow(display, Refnone) < 0) 654 fprint(2,"can't reattach to window"); 655 ul = addpt(ul, subpt(screen->r.min, r.min)); 656 redraw(screen); 657 } 658 659 /* clip p to be in r */ 660 Point 661 pclip(Point p, Rectangle r) 662 { 663 if(p.x < r.min.x) 664 p.x = r.min.x; 665 else if(p.x >= r.max.x) 666 p.x = r.max.x-1; 667 668 if(p.y < r.min.y) 669 p.y = r.min.y; 670 else if(p.y >= r.max.y) 671 p.y = r.max.y-1; 672 673 return p; 674 } 675 676 /* 677 * resize is perhaps a misnomer. 678 * this really just grows the window to be at least dx across 679 * and dy high. if the window hits the bottom or right edge, 680 * it is backed up until it hits the top or left edge. 681 */ 682 void 683 resize(int dx, int dy) 684 { 685 static Rectangle sr; 686 Rectangle r, or; 687 688 dx += 2*Borderwidth; 689 dy += 2*Borderwidth; 690 if(wctlfd < 0){ 691 wctlfd = open("/dev/wctl", OWRITE); 692 if(wctlfd < 0) 693 return; 694 } 695 696 r = insetrect(screen->r, -Borderwidth); 697 if(Dx(r) >= dx && Dy(r) >= dy) 698 return; 699 700 if(Dx(sr)*Dy(sr) == 0) 701 sr = screenrect(); 702 703 or = r; 704 705 r.max.x = max(r.min.x+dx, r.max.x); 706 r.max.y = max(r.min.y+dy, r.max.y); 707 if(r.max.x > sr.max.x){ 708 if(Dx(r) > Dx(sr)){ 709 r.min.x = 0; 710 r.max.x = sr.max.x; 711 }else 712 r = rectaddpt(r, Pt(sr.max.x-r.max.x, 0)); 713 } 714 if(r.max.y > sr.max.y){ 715 if(Dy(r) > Dy(sr)){ 716 r.min.y = 0; 717 r.max.y = sr.max.y; 718 }else 719 r = rectaddpt(r, Pt(0, sr.max.y-r.max.y)); 720 } 721 722 /* 723 * Sometimes we can't actually grow the window big enough, 724 * and resizing it to the same shape makes it flash. 725 */ 726 if(Dx(r) == Dx(or) && Dy(r) == Dy(or)) 727 return; 728 729 fprint(wctlfd, "resize -minx %d -miny %d -maxx %d -maxy %d\n", 730 r.min.x, r.min.y, r.max.x, r.max.y); 731 } 732 733 /* 734 * If we allocimage after a resize but before flushing the draw buffer, 735 * we won't have seen the reshape event, and we won't have called 736 * getwindow, and allocimage will fail. So we flushimage before every alloc. 737 */ 738 Image* 739 xallocimage(Display *d, Rectangle r, ulong chan, int repl, ulong val) 740 { 741 flushimage(display, 0); 742 return allocimage(d, r, chan, repl, val); 743 } 744 745 /* all code below this line should be in the library, but is stolen from colors instead */ 746 static char* 747 rdenv(char *name) 748 { 749 char *v; 750 int fd, size; 751 752 fd = open(name, OREAD); 753 if(fd < 0) 754 return 0; 755 size = seek(fd, 0, 2); 756 v = malloc(size+1); 757 if(v == 0){ 758 fprint(2, "page: can't malloc: %r\n"); 759 wexits("no mem"); 760 } 761 seek(fd, 0, 0); 762 read(fd, v, size); 763 v[size] = 0; 764 close(fd); 765 return v; 766 } 767 768 void 769 newwin(void) 770 { 771 char *srv, *mntsrv; 772 char spec[100]; 773 int srvfd, cons, pid; 774 775 switch(rfork(RFFDG|RFPROC|RFNAMEG|RFENVG|RFNOTEG|RFNOWAIT)){ 776 case -1: 777 fprint(2, "page: can't fork: %r\n"); 778 wexits("no fork"); 779 case 0: 780 break; 781 default: 782 wexits(0); 783 } 784 785 srv = rdenv("/env/wsys"); 786 if(srv == 0){ 787 mntsrv = rdenv("/mnt/term/env/wsys"); 788 if(mntsrv == 0){ 789 fprint(2, "page: can't find $wsys\n"); 790 wexits("srv"); 791 } 792 srv = malloc(strlen(mntsrv)+10); 793 sprint(srv, "/mnt/term%s", mntsrv); 794 free(mntsrv); 795 pid = 0; /* can't send notes to remote processes! */ 796 }else 797 pid = getpid(); 798 srvfd = open(srv, ORDWR); 799 free(srv); 800 if(srvfd == -1){ 801 fprint(2, "page: can't open %s: %r\n", srv); 802 wexits("no srv"); 803 } 804 sprint(spec, "new -pid %d", pid); 805 if(mount(srvfd, "/mnt/wsys", 0, spec) == -1){ 806 fprint(2, "page: can't mount /mnt/wsys: %r (spec=%s)\n", spec); 807 wexits("no mount"); 808 } 809 close(srvfd); 810 unmount("/mnt/acme", "/dev"); 811 bind("/mnt/wsys", "/dev", MBEFORE); 812 cons = open("/dev/cons", OREAD); 813 if(cons==-1){ 814 NoCons: 815 fprint(2, "page: can't open /dev/cons: %r"); 816 wexits("no cons"); 817 } 818 dup(cons, 0); 819 close(cons); 820 cons = open("/dev/cons", OWRITE); 821 if(cons==-1) 822 goto NoCons; 823 dup(cons, 1); 824 dup(cons, 2); 825 close(cons); 826 // wctlfd = open("/dev/wctl", OWRITE); 827 } 828 829 Rectangle 830 screenrect(void) 831 { 832 int fd; 833 char buf[12*5]; 834 835 fd = open("/dev/screen", OREAD); 836 if(fd == -1) 837 fd=open("/mnt/term/dev/screen", OREAD); 838 if(fd == -1){ 839 fprint(2, "page: can't open /dev/screen: %r\n"); 840 wexits("window read"); 841 } 842 if(read(fd, buf, sizeof buf) != sizeof buf){ 843 fprint(2, "page: can't read /dev/screen: %r\n"); 844 wexits("screen read"); 845 } 846 close(fd); 847 return Rect(atoi(buf+12), atoi(buf+24), atoi(buf+36), atoi(buf+48)); 848 } 849 850