2 * the actual viewer that handles screen stuff
21 int showbottom = 0; /* on the next showpage, move the image so the bottom is visible. */
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 */
26 Point pclip(Point, Rectangle);
27 Rectangle mkrange(Rectangle screenr, Rectangle imr);
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, }
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, }
68 wctl = open("/dev/wctl", OWRITE);
72 write(wctl, "unhide", 6);
91 static char menustr[32];
103 p = doc->pagename(doc, n);
104 len = (sizeof menustr)-2;
106 if(strlen(p) > len && strrchr(p, '/'))
107 p = strrchr(p, '/')+1;
111 strcpy(menustr+1, p);
120 showpage(int page, Menu *m)
125 m->lasthit = 0; /* this page */
127 m->lasthit = reverse ? doc->npage-1-page : page;
129 esetcursor(&reading);
131 if((page < 0 || page >= doc->npage) && !doc->fwdonly){
135 im = doc->drawpage(doc, page);
137 if(doc->fwdonly) /* this is how we know we're out of pages */
140 im = xallocimage(display, Rect(0,0,50,50), GREY1, 1, DBlack);
142 fprint(2, "out of memory: %r\n");
145 string(im, ZP, display->white, ZP, display->defaultfont, "?");
147 resize(Dx(im->r), Dy(im->r));
149 if(im->r.min.x > 0 || im->r.min.y > 0) {
150 tmp = xallocimage(display, Rect(0, 0, Dx(im->r), Dy(im->r)), im->chan, 0, DNofill);
152 fprint(2, "out of memory during showpage: %r\n");
155 drawop(tmp, tmp->r, im, nil, im->r.min, S);
174 ul.y = screen->r.max.y - Dy(im->r);
179 flushimage(display, 1);
187 static char result[200];
194 memset(basename, 0, sizeof basename);
196 strncpy(basename, doc->docname, sizeof(basename)-1);
197 else if((p = menugen(page)) && p[0] != '\0')
198 strncpy(basename, p+1, sizeof(basename)-1);
201 if(q = strrchr(basename, '/'))
205 if(p = strchr(q, '.'))
208 memset(name, 0, sizeof name);
209 snprint(name, sizeof(name)-1, "%s.%d.bit", q, page+1);
210 if(access(name, 0) >= 0) {
211 strcat(name, "XXXX");
214 if(access(name, 0) >= 0)
215 return "couldn't think of a name for bitmap";
217 strcpy(name, "bitXXXX");
219 if(access(name, 0) >= 0)
220 return "couldn't think of a name for bitmap";
223 if((fd = create(name, OWRITE, 0666)) < 0) {
224 snprint(result, sizeof result, "cannot create %s: %r", name);
228 if(writeimage(fd, im, 0) < 0) {
229 snprint(result, sizeof result, "cannot writeimage: %r");
235 snprint(result, sizeof result, "wrote %s", name);
239 static void translate(Point);
242 showdata(Plumbmsg *msg)
246 s = plumblookup(msg->attr, "action");
247 return s && strcmp(s, "showdata")==0;
250 /* correspond to entries in miditems[] below,
251 * changing one means you need to change
274 int i, fd, n, oldpage;
282 static char *fwditems[] = { "this page", "next page", "exit", 0 };
283 static char *miditems[] = {
305 doc = dd; /* save global for menuhit */
307 einit(Emouse|Ekeyboard);
308 if(doc->addpage != nil)
309 eplumb(Eplumb, "image");
311 esetcursor(&reading);
315 * im is a global pointer to the current image.
316 * eventually, i think we will have a layer between
317 * the display routines and the ps/pdf/whatever routines
318 * to perhaps cache and handle images of different
322 page = reverse ? doc->npage-1 : 0;
325 menu.item = fwditems;
334 midmenu.item = miditems;
336 midmenu.lasthit = Next;
338 showpage(page, &menu);
344 * throughout, if doc->fwdonly is set, we restrict the functionality
345 * a fair amount. we don't care about doc->npage anymore, and
346 * all that can be done is select the next page.
348 switch(eread(Emouse|Ekeyboard|Eplumb, &e)){
350 if(e.kbdc <= 0xFF && isdigit(e.kbdc)) {
351 nxt = nxt*10+e.kbdc-'0';
353 } else if(e.kbdc != '\n')
356 case 'r': /* reverse page order */
360 menu.lasthit = doc->npage-1-menu.lasthit;
363 * the theory is that if we are reversing the
364 * document order and are on the first or last
365 * page then we're just starting and really want
366 * to view the other end. maybe the if
367 * should be dropped and this should happen always.
369 if(page == 0 || page == doc->npage-1) {
370 page = doc->npage-1-page;
371 showpage(page, &menu);
374 case 'w': /* write bitmap of current screen */
375 esetcursor(&reading);
378 string(screen, addpt(screen->r.min, Pt(5,5)), display->black, ZP,
379 display->defaultfont, s);
381 flushimage(display, 1);
383 case 'd': /* remove image from working set */
384 if(doc->rmpage && page < doc->npage) {
385 if(doc->rmpage(doc, page) >= 0) {
388 if(page >= doc->npage)
390 showpage(page, &menu);
395 case 0x04: /* ctrl-d */
400 esetcursor(&reading);
403 angle = (angle+180) % 360;
405 flushimage(display, 1);
410 if(page > 0 && !doc->fwdonly) {
412 showpage(page, &menu);
418 if(nxt >= 0 && nxt < doc->npage && !doc->fwdonly)
419 showpage(page=nxt, &menu);
427 if(doc->npage && ++page >= doc->npage && !doc->fwdonly)
429 showpage(page, &menu);
433 * The upper y coordinate of the image is at ul.y in screen->r.
434 * Panning up means moving the upper left corner down. If the
435 * upper left corner is currently visible, we need to go back a page.
438 if(screen->r.min.y <= ul.y && ul.y < screen->r.max.y){
439 if(page > 0 && !doc->fwdonly){
442 showpage(page, &menu);
448 if(i+ul.y > screen->r.min.y)
449 i = screen->r.min.y - ul.y;
455 * If the lower y coordinate is on the screen, we go to the next page.
456 * The lower y coordinate is at ul.y + Dy(im->r).
459 i = ul.y + Dy(im->r);
460 if(screen->r.min.y <= i && i <= screen->r.max.y){
461 ul.y = screen->r.min.y;
464 i = -Dy(screen->r)/2;
467 if(i+ul.y+Dy(im->r) <= screen->r.max.y)
468 i = screen->r.max.y - Dy(im->r) - ul.y - 1;
487 dxy = subpt(m.xy, oxy);
491 } while(m.buttons == Left);
493 dxy = subpt(xy0, oxy);
502 n = emenuhit(Middle, &m, &midmenu);
506 case Next: /* next */
516 if((page >= doc->npage) && !doc->fwdonly)
519 showpage(page, &menu);
522 case Prev: /* prev */
532 if((page >= doc->npage) && !doc->fwdonly && !reverse)
535 showpage(page, &menu);
538 case Zerox: /* prev */
541 case Zin: /* zoom in */
546 r = egetrect(Middle, &m);
547 if((rectclip(&r, rectaddpt(im->r, ul)) == 0) ||
548 Dx(r) == 0 || Dy(r) == 0)
550 /* use the smaller side to expand */
552 delta = (double)Dx(im->r)/(double)Dx(r);
554 delta = (double)Dy(im->r)/(double)Dy(r);
556 esetcursor(&reading);
557 tmp = xallocimage(display,
558 Rect(0, 0, (int)((double)Dx(im->r)*delta), (int)((double)Dy(im->r)*delta)),
559 im->chan, 0, DBlack);
561 fprint(2, "out of memory during zoom: %r\n");
570 flushimage(display, 1);
578 delta = (double)Dx(screen->r)/(double)Dx(im->r);
579 if((double)Dy(im->r)*delta > Dy(screen->r))
580 delta = (double)Dy(screen->r)/(double)Dy(im->r);
582 r = Rect(0, 0, (int)((double)Dx(im->r)*delta), (int)((double)Dy(im->r)*delta));
583 esetcursor(&reading);
584 tmp = xallocimage(display, r, im->chan, 0, DBlack);
586 fprint(2, "out of memory during fit: %r\n");
595 flushimage(display, 1);
598 case Rot: /* rotate 90 */
599 esetcursor(&reading);
602 angle = (angle+90) % 360;
604 flushimage(display, 1);
606 case Upside: /* upside-down */
609 esetcursor(&reading);
612 angle = (angle+180) % 360;
614 flushimage(display, 1);
616 case Restore: /* restore */
617 showpage(page, &menu);
619 case Reverse: /* reverse */
623 menu.lasthit = doc->npage-1-menu.lasthit;
625 if(page == 0 || page == doc->npage-1) {
626 page = doc->npage-1-page;
627 showpage(page, &menu);
630 case Write: /* write */
631 esetcursor(&reading);
634 string(screen, addpt(screen->r.min, Pt(5,5)), display->black, ZP,
635 display->defaultfont, s);
637 flushimage(display, 1);
639 case Del: /* delete */
640 if(doc->rmpage && page < doc->npage) {
641 if(doc->rmpage(doc, page) >= 0) {
644 if(page >= doc->npage)
646 showpage(page, &menu);
650 case Exit: /* exit */
666 n = emenuhit(RMenu, &m, &menu);
672 case 0: /* this page */
674 case 1: /* next page */
675 showpage(++page, &menu);
686 page = reverse ? doc->npage-1-n : n;
689 showpage(page, &menu);
702 s = estrdup("/tmp/pageplumbXXXXXXX");
704 write(fd, pm->data, pm->ndata);
705 /* lose fd reference on purpose; the file is open ORCLOSE */
706 } else if(pm->data[0] == '/') {
707 s = estrdup(pm->data);
709 s = emalloc(strlen(pm->wdir)+1+pm->ndata+1);
710 sprint(s, "%s/%s", pm->wdir, pm->data);
713 if((i = doc->addpage(doc, s)) >= 0) {
716 showpage(page, &menu);
728 * A draw operation that touches only the area contained in bot but not in top.
729 * mp and sp get aligned with bot.min.
732 gendrawdiff(Image *dst, Rectangle bot, Rectangle top,
733 Image *src, Point sp, Image *mask, Point mp, int op)
741 if(Dx(bot)*Dy(bot) == 0)
744 /* no points in bot - top */
745 if(rectinrect(bot, top))
748 /* bot - top ≡ bot */
749 if(Dx(top)*Dy(top)==0 || rectXrect(bot, top)==0){
750 gendrawop(dst, bot, src, sp, mask, mp, op);
755 /* split bot into rectangles that don't intersect top */
757 if(bot.min.x < top.min.x){
758 r = Rect(bot.min.x, bot.min.y, top.min.x, bot.max.y);
759 delta = subpt(r.min, origin);
760 gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
761 bot.min.x = top.min.x;
765 if(bot.max.x > top.max.x){
766 r = Rect(top.max.x, bot.min.y, bot.max.x, bot.max.y);
767 delta = subpt(r.min, origin);
768 gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
769 bot.max.x = top.max.x;
773 if(bot.min.y < top.min.y){
774 r = Rect(bot.min.x, bot.min.y, bot.max.x, top.min.y);
775 delta = subpt(r.min, origin);
776 gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
777 bot.min.y = top.min.y;
781 if(bot.max.y > top.max.y){
782 r = Rect(bot.min.x, top.max.y, bot.max.x, bot.max.y);
783 delta = subpt(r.min, origin);
784 gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
785 bot.max.y = top.max.y;
790 drawdiff(Image *dst, Rectangle bot, Rectangle top, Image *src, Image *mask, Point p, int op)
792 gendrawdiff(dst, bot, top, src, p, mask, p, op);
796 * Translate the image in the window by delta.
799 translate(Point delta)
807 u = pclip(addpt(ul, delta), ulrange);
808 delta = subpt(u, ul);
809 if(delta.x == 0 && delta.y == 0)
813 * The upper left corner of the image is currently at ul.
814 * We want to move it to u.
816 or = rectaddpt(Rpt(ZP, Pt(Dx(im->r), Dy(im->r))), ul);
817 r = rectaddpt(or, delta);
819 drawop(screen, r, screen, nil, ul, S);
822 /* fill in gray where image used to be but isn't. */
823 drawdiff(screen, insetrect(or, -2), insetrect(r, -2), gray, nil, ZP, S);
825 /* fill in black border */
826 drawdiff(screen, insetrect(r, -2), r, display->black, nil, ZP, S);
828 /* fill in image where it used to be off the screen. */
829 if(rectclip(&or, screen->r))
830 drawdiff(screen, r, rectaddpt(or, delta), im, nil, im->r.min, S);
832 drawop(screen, r, im, nil, im->r.min, S);
833 flushimage(display, 1);
837 redraw(Image *screen)
844 ulrange.max = screen->r.max;
845 ulrange.min = subpt(screen->r.min, Pt(Dx(im->r), Dy(im->r)));
847 ul = pclip(ul, ulrange);
848 drawop(screen, screen->r, im, nil, subpt(im->r.min, subpt(ul, screen->r.min)), S);
853 /* fill in any outer edges */
855 r = rectaddpt(im->r, subpt(ul, im->r.min));
856 border(screen, r, -2, display->black, ZP);
857 r.min = subpt(r.min, Pt(2,2));
858 r.max = addpt(r.max, Pt(2,2));
860 /* gray for the rest */
862 gray = xallocimage(display, Rect(0,0,1,1), RGB24, 1, 0x888888FF);
864 fprint(2, "g out of memory: %r\n");
868 border(screen, r, -4000, gray, ZP);
869 /* flushimage(display, 0); */
877 if(new && getwindow(display, Refnone) < 0)
878 fprint(2,"can't reattach to window");
879 ul = addpt(ul, subpt(screen->r.min, r.min));
883 /* clip p to be in r */
885 pclip(Point p, Rectangle r)
889 else if(p.x >= r.max.x)
894 else if(p.y >= r.max.y)
901 * resize is perhaps a misnomer.
902 * this really just grows the window to be at least dx across
903 * and dy high. if the window hits the bottom or right edge,
904 * it is backed up until it hits the top or left edge.
907 resize(int dx, int dy)
915 wctlfd = open("/dev/wctl", OWRITE);
920 r = insetrect(screen->r, -Borderwidth);
921 if(Dx(r) >= dx && Dy(r) >= dy)
924 if(Dx(sr)*Dy(sr) == 0)
929 r.max.x = max(r.min.x+dx, r.max.x);
930 r.max.y = max(r.min.y+dy, r.max.y);
931 if(r.max.x > sr.max.x){
936 r = rectaddpt(r, Pt(sr.max.x-r.max.x, 0));
938 if(r.max.y > sr.max.y){
943 r = rectaddpt(r, Pt(0, sr.max.y-r.max.y));
947 * Sometimes we can't actually grow the window big enough,
948 * and resizing it to the same shape makes it flash.
950 if(Dx(r) == Dx(or) && Dy(r) == Dy(or))
953 fprint(wctlfd, "resize -minx %d -miny %d -maxx %d -maxy %d\n",
954 r.min.x, r.min.y, r.max.x, r.max.y);
958 * If we allocimage after a resize but before flushing the draw buffer,
959 * we won't have seen the reshape event, and we won't have called
960 * getwindow, and allocimage will fail. So we flushimage before every alloc.
963 xallocimage(Display *d, Rectangle r, ulong chan, int repl, ulong val)
965 flushimage(display, 0);
966 return allocimage(d, r, chan, repl, val);
969 /* all code below this line should be in the library, but is stolen from colors instead */
976 fd = open(name, OREAD);
979 size = seek(fd, 0, 2);
982 fprint(2, "page: can't malloc: %r\n");
997 int srvfd, cons, pid;
999 switch(rfork(RFFDG|RFPROC|RFNAMEG|RFENVG|RFNOTEG|RFNOWAIT)){
1001 fprint(2, "page: can't fork: %r\n");
1009 srv = rdenv("/env/wsys");
1011 mntsrv = rdenv("/mnt/term/env/wsys");
1013 fprint(2, "page: can't find $wsys\n");
1016 srv = malloc(strlen(mntsrv)+10);
1017 sprint(srv, "/mnt/term%s", mntsrv);
1019 pid = 0; /* can't send notes to remote processes! */
1022 srvfd = open(srv, ORDWR);
1025 fprint(2, "page: can't open %s: %r\n", srv);
1028 sprint(spec, "new -pid %d", pid);
1029 if(mount(srvfd, -1, "/mnt/wsys", 0, spec) == -1){
1030 fprint(2, "page: can't mount /mnt/wsys: %r (spec=%s)\n", spec);
1034 unmount("/mnt/acme", "/dev");
1035 bind("/mnt/wsys", "/dev", MBEFORE);
1036 cons = open("/dev/cons", OREAD);
1039 fprint(2, "page: can't open /dev/cons: %r");
1044 cons = open("/dev/cons", OWRITE);
1050 /* wctlfd = open("/dev/wctl", OWRITE); */
1059 fd = open("/dev/screen", OREAD);
1061 fd=open("/mnt/term/dev/screen", OREAD);
1063 fprint(2, "page: can't open /dev/screen: %r\n");
1064 wexits("window read");
1066 if(read(fd, buf, sizeof buf) != sizeof buf){
1067 fprint(2, "page: can't read /dev/screen: %r\n");
1068 wexits("screen read");
1071 return Rect(atoi(buf+12), atoi(buf+24), atoi(buf+36), atoi(buf+48));
1080 switch(rfork(RFFDG|RFPROC)) {
1082 wexits("cannot fork in zerox: %r");
1086 execl("/bin/page", "page", "-w", nil);
1087 wexits("cannot exec in zerox: %r\n");
1090 writeimage(pfd[0], im, 0);