2 * the actual viewer that handles screen stuff
24 int showbottom = 0; /* on the next showpage, move the image so the bottom is visible. */
26 Rectangle ulrange; /* the upper left corner of the image must be in this rectangle */
27 Point ul; /* the upper left corner of the image is at this point on the screen */
29 Point pclip(Point, Rectangle);
30 Rectangle mkrange(Rectangle screenr, Rectangle imr);
32 void plumbproc(void*);
36 {0xff, 0x80, 0xff, 0x80, 0xff, 0x00, 0xfe, 0x00,
37 0xff, 0x00, 0xff, 0x80, 0xff, 0xc0, 0xef, 0xe0,
38 0xc7, 0xf0, 0x03, 0xf0, 0x01, 0xe0, 0x00, 0xc0,
39 0x03, 0xff, 0x03, 0xff, 0x03, 0xff, 0x03, 0xff, },
40 {0x00, 0x00, 0x7f, 0x00, 0x7e, 0x00, 0x7c, 0x00,
41 0x7e, 0x00, 0x7f, 0x00, 0x6f, 0x80, 0x47, 0xc0,
42 0x03, 0xe0, 0x01, 0xf0, 0x00, 0xe0, 0x00, 0x40,
43 0x00, 0x00, 0x01, 0xb6, 0x01, 0xb6, 0x00, 0x00, }
48 {0x0f, 0xf0, 0x1f, 0xf8, 0x3f, 0xfc, 0x7f, 0xfe,
49 0x7c, 0x7e, 0x78, 0x7e, 0x00, 0xfc, 0x01, 0xf8,
50 0x03, 0xf0, 0x07, 0xe0, 0x07, 0xc0, 0x07, 0xc0,
51 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, },
52 {0x00, 0x00, 0x0f, 0xf0, 0x1f, 0xf8, 0x3c, 0x3c,
53 0x38, 0x1c, 0x00, 0x3c, 0x00, 0x78, 0x00, 0xf0,
54 0x01, 0xe0, 0x03, 0xc0, 0x03, 0x80, 0x03, 0x80,
55 0x00, 0x00, 0x03, 0x80, 0x03, 0x80, 0x00, 0x00, }
67 delayfreeimage(Image *m)
98 static char menustr[32];
110 p = doc->pagename(doc, n);
111 len = (sizeof menustr)-2;
113 if(strlen(p) > len && strrchr(p, '/'))
114 p = strrchr(p, '/')+1;
118 strcpy(menustr+1, p);
127 showpage(int page, Menu *m)
130 m->lasthit = 0; /* this page */
132 m->lasthit = reverse ? doc->npage-1-page : page;
134 setcursor(mc, &reading);
136 im = cachedpage(doc, angle, page);
140 resize(Dx(im->r), Dy(im->r));
144 ul.y = screen->r.max.y - Dy(im->r);
148 if((doc->type == Tgfx) && fitwin)
152 flushimage(display, 1);
161 static char result[200];
168 memset(basename, 0, sizeof basename);
170 strncpy(basename, doc->docname, sizeof(basename)-1);
171 else if((p = menugen(page)) && p[0] != '\0')
172 strncpy(basename, p+1, sizeof(basename)-1);
175 if(q = strrchr(basename, '/'))
179 if(p = strchr(q, '.'))
182 memset(name, 0, sizeof name);
183 snprint(name, sizeof(name)-1, "%s.%d.bit", q, page+1);
184 if(access(name, 0) >= 0) {
185 strcat(name, "XXXX");
189 return "couldn't think of a name for bitmap";
191 strcpy(name, "bitXXXX");
194 return "couldn't think of a name for bitmap";
198 snprint(result, sizeof result, "cannot create %s: %r", name);
202 if(writeimage(fd, im, 0) < 0) {
203 snprint(result, sizeof result, "cannot writeimage: %r");
209 snprint(result, sizeof result, "wrote %s", name);
213 static void translate(Point);
216 showdata(Plumbmsg *msg)
220 s = plumblookup(msg->attr, "action");
221 return s && strcmp(s, "showdata")==0;
224 /* correspond to entries in miditems[] below,
225 * changing one means you need to change
248 int i, fd, n, oldpage;
261 static char *fwditems[] = { "this page", "next page", "exit", 0 };
262 static char *miditems[] = {
291 cp = chancreate(sizeof pm, 0);
294 doc = dd; /* save global for menuhit */
296 mc = initmouse(nil, screen);
297 kc = initkeyboard(nil);
298 alts[CMouse].c = mc->c;
300 alts[CMouse].op = CHANRCV;
301 alts[CResize].c = mc->resizec;
302 alts[CResize].v = &size;
303 alts[CResize].op = CHANRCV;
304 alts[CKeyboard].c = kc->c;
305 alts[CKeyboard].v = &run;
306 alts[CKeyboard].op = CHANRCV;
308 alts[CPlumb].v = ±
309 alts[CPlumb].op = CHANNOP;
310 alts[CN].op = CHANEND;
313 if(doc->addpage != nil) {
314 alts[CPlumb].op = CHANRCV;
315 proccreate(plumbproc, cp, 16384);
318 setcursor(mc, &reading);
322 * im is a global pointer to the current image.
323 * eventually, i think we will have a layer between
324 * the display routines and the ps/pdf/whatever routines
325 * to perhaps cache and handle images of different
329 page = reverse ? doc->npage-1 : 0;
332 menu.item = fwditems;
341 midmenu.item = miditems;
343 midmenu.lasthit = Next;
345 showpage(page, &menu);
351 * throughout, if doc->fwdonly is set, we restrict the functionality
352 * a fair amount. we don't care about doc->npage anymore, and
353 * all that can be done is select the next page.
355 unlockdisplay(display);
357 lockdisplay(display);
360 if(run <= 0xFF && isdigit(run)) {
361 nxt = nxt*10+run-'0';
363 } else if(run != '\n')
366 case 'r': /* reverse page order */
370 menu.lasthit = doc->npage-1-menu.lasthit;
373 * the theory is that if we are reversing the
374 * document order and are on the first or last
375 * page then we're just starting and really want
376 * to view the other end. maybe the if
377 * should be dropped and this should happen always.
379 if(page == 0 || page == doc->npage-1) {
380 page = doc->npage-1-page;
381 showpage(page, &menu);
384 case 'w': /* write bitmap of current screen */
385 setcursor(mc, &reading);
388 string(screen, addpt(screen->r.min, Pt(5,5)), display->black, ZP,
389 display->defaultfont, s);
391 flushimage(display, 1);
393 case 'd': /* remove image from working set */
394 if(doc->rmpage && page < doc->npage) {
395 if(doc->rmpage(doc, page) >= 0) {
398 if(page >= doc->npage)
400 showpage(page, &menu);
405 case 0x04: /* ctrl-d */
410 setcursor(mc, &reading);
413 angle = (angle+180) % 360;
415 flushimage(display, 1);
420 if(page > 0 && !doc->fwdonly) {
422 showpage(page, &menu);
428 if(nxt >= 0 && nxt < doc->npage && !doc->fwdonly)
429 showpage(page=nxt, &menu);
437 if(doc->npage && ++page >= doc->npage && !doc->fwdonly)
439 showpage(page, &menu);
443 * The upper y coordinate of the image is at ul.y in screen->r.
444 * Panning up means moving the upper left corner down. If the
445 * upper left corner is currently visible, we need to go back a page.
448 if(screen->r.min.y <= ul.y && ul.y < screen->r.max.y){
449 if(page > 0 && !doc->fwdonly){
452 showpage(page, &menu);
458 if(i+ul.y > screen->r.min.y)
459 i = screen->r.min.y - ul.y;
465 * If the lower y coordinate is on the screen, we go to the next page.
466 * The lower y coordinate is at ul.y + Dy(im->r).
469 i = ul.y + Dy(im->r);
470 if(screen->r.min.y <= i && i <= screen->r.max.y){
471 ul.y = screen->r.min.y;
474 i = -Dy(screen->r)/2;
477 if(i+ul.y+Dy(im->r) <= screen->r.max.y)
478 i = screen->r.max.y - Dy(im->r) - ul.y - 1;
483 setcursor(mc, &query);
496 dxy = subpt(m.xy, oxy);
500 } while(m.buttons == Left);
502 dxy = subpt(xy0, oxy);
511 n = menuhit(Middle, mc, &midmenu, nil);
515 case Next: /* next */
525 if((page >= doc->npage) && !doc->fwdonly)
528 showpage(page, &menu);
531 case Prev: /* prev */
541 if((page >= doc->npage) && !doc->fwdonly && !reverse)
544 showpage(page, &menu);
547 case Zerox: /* prev */
550 case Zin: /* zoom in */
551 if (dd->type == Tpdf){ /* pdf */
552 pdf = (PDFInfo *) dd->extra;
555 setdim(&pdf->gs, Rect(0,0,0,0), ppi, 0);
556 showpage(page, &menu);
560 if (dd->type == Tps){ /* ps */
561 ps = (PSInfo *) dd->extra;
564 setdim(&ps->gs, Rect(0,0,0,0), ppi, 0);
565 showpage(page, &menu);
573 r = getrect(Middle, mc);
574 if((rectclip(&r, rectaddpt(im->r, ul)) == 0) ||
575 Dx(r) == 0 || Dy(r) == 0)
577 /* use the smaller side to expand */
579 delta = (double)Dx(im->r)/(double)Dx(r);
581 delta = (double)Dy(im->r)/(double)Dy(r);
583 setcursor(mc, &reading);
584 tmp = xallocimage(display,
585 Rect(0, 0, (int)((double)Dx(im->r)*delta), (int)((double)Dy(im->r)*delta)),
586 im->chan, 0, DBlack);
588 fprint(2, "out of memory during zoom: %r\n");
597 flushimage(display, 1);
601 /* no op if pdf or ps*/
602 if (dd->type == Tgfx){
607 case Rot: /* rotate 90 */
608 angle = (angle+90) % 360;
609 showpage(page, &menu);
611 case Upside: /* upside-down */
612 angle = (angle+180) % 360;
613 showpage(page, &menu);
615 case Restore: /* restore */
616 if (dd->type == Tpdf){ /* pdf */
617 pdf = (PDFInfo *) dd->extra;
620 setdim(&pdf->gs, Rect(0,0,0,0), ppi, 0);
622 showpage(page, &menu);
625 if (dd->type == Tps){ /* ps */
626 ps = (PSInfo *) dd->extra;
629 setdim(&ps->gs, Rect(0,0,0,0), ppi, 0);
631 showpage(page, &menu);
635 showpage(page, &menu);
637 case Reverse: /* reverse */
641 menu.lasthit = doc->npage-1-menu.lasthit;
643 if(page == 0 || page == doc->npage-1) {
644 page = doc->npage-1-page;
645 showpage(page, &menu);
648 case Write: /* write */
649 setcursor(mc, &reading);
652 string(screen, addpt(screen->r.min, Pt(5,5)), display->black, ZP,
653 display->defaultfont, s);
655 flushimage(display, 1);
657 case Del: /* delete */
658 if(doc->rmpage && page < doc->npage) {
659 if(doc->rmpage(doc, page) >= 0) {
662 if(page >= doc->npage)
664 showpage(page, &menu);
668 case Exit: /* exit */
684 n = menuhit(RMenu, mc, &menu, nil);
690 case 0: /* this page */
692 case 1: /* next page */
693 showpage(++page, &menu);
704 page = reverse ? doc->npage-1-n : n;
707 showpage(page, &menu);
714 if(getwindow(display, Refnone) < 0)
715 fprint(2,"can't reattach to window");
716 ul = addpt(ul, subpt(screen->r.min, r.min));
718 flushimage(display, 1);
726 s = estrdup("/tmp/pageplumbXXXXXXX");
727 fd = opentemp(s, ORDWR|ORCLOSE);
728 write(fd, pm->data, pm->ndata);
729 /* lose fd reference on purpose; the file is open ORCLOSE */
730 } else if(pm->data[0] == '/') {
731 s = estrdup(pm->data);
733 s = emalloc(strlen(pm->wdir)+1+pm->ndata+1);
734 sprint(s, "%s/%s", pm->wdir, pm->data);
737 if((i = doc->addpage(doc, s)) >= 0) {
740 showpage(page, &menu);
752 * A draw operation that touches only the area contained in bot but not in top.
753 * mp and sp get aligned with bot.min.
756 gendrawdiff(Image *dst, Rectangle bot, Rectangle top,
757 Image *src, Point sp, Image *mask, Point mp, int op)
765 if(Dx(bot)*Dy(bot) == 0)
768 /* no points in bot - top */
769 if(rectinrect(bot, top))
772 /* bot - top ≡ bot */
773 if(Dx(top)*Dy(top)==0 || rectXrect(bot, top)==0){
774 gendrawop(dst, bot, src, sp, mask, mp, op);
779 /* split bot into rectangles that don't intersect top */
781 if(bot.min.x < top.min.x){
782 r = Rect(bot.min.x, bot.min.y, top.min.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.min.x = top.min.x;
789 if(bot.max.x > top.max.x){
790 r = Rect(top.max.x, bot.min.y, bot.max.x, bot.max.y);
791 delta = subpt(r.min, origin);
792 gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
793 bot.max.x = top.max.x;
797 if(bot.min.y < top.min.y){
798 r = Rect(bot.min.x, bot.min.y, bot.max.x, top.min.y);
799 delta = subpt(r.min, origin);
800 gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
801 bot.min.y = top.min.y;
805 if(bot.max.y > top.max.y){
806 r = Rect(bot.min.x, top.max.y, bot.max.x, bot.max.y);
807 delta = subpt(r.min, origin);
808 gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
809 bot.max.y = top.max.y;
814 drawdiff(Image *dst, Rectangle bot, Rectangle top, Image *src, Image *mask, Point p, int op)
816 gendrawdiff(dst, bot, top, src, p, mask, p, op);
820 * Translate the image in the window by delta.
823 translate(Point delta)
831 u = pclip(addpt(ul, delta), ulrange);
832 delta = subpt(u, ul);
833 if(delta.x == 0 && delta.y == 0)
837 * The upper left corner of the image is currently at ul.
838 * We want to move it to u.
840 or = rectaddpt(Rpt(ZP, Pt(Dx(im->r), Dy(im->r))), ul);
841 r = rectaddpt(or, delta);
843 drawop(screen, r, screen, nil, ul, S);
846 /* fill in gray where image used to be but isn't. */
847 drawdiff(screen, insetrect(or, -2), insetrect(r, -2), gray, nil, ZP, S);
849 /* fill in black border */
850 drawdiff(screen, insetrect(r, -2), r, display->black, nil, ZP, S);
852 /* fill in image where it used to be off the screen. */
853 if(rectclip(&or, screen->r))
854 drawdiff(screen, r, rectaddpt(or, delta), im, nil, im->r.min, S);
856 drawop(screen, r, im, nil, im->r.min, S);
857 flushimage(display, 1);
861 redraw(Image *screen)
868 ulrange.max = screen->r.max;
869 ulrange.min = subpt(screen->r.min, Pt(Dx(im->r), Dy(im->r)));
871 ul = pclip(ul, ulrange);
872 drawop(screen, screen->r, im, nil, subpt(im->r.min, subpt(ul, screen->r.min)), S);
877 /* fill in any outer edges */
879 r = rectaddpt(im->r, subpt(ul, im->r.min));
880 border(screen, r, -2, display->black, ZP);
881 r.min = subpt(r.min, Pt(2,2));
882 r.max = addpt(r.max, Pt(2,2));
884 /* gray for the rest */
886 gray = xallocimage(display, Rect(0,0,1,1), RGB24, 1, 0x888888FF);
888 fprint(2, "g out of memory: %r\n");
892 border(screen, r, -4000, gray, ZP);
893 // flushimage(display, 0);
896 /* clip p to be in r */
898 pclip(Point p, Rectangle r)
902 else if(p.x >= r.max.x)
907 else if(p.y >= r.max.y)
914 * resize is perhaps a misnomer.
915 * this really just grows the window to be at least dx across
916 * and dy high. if the window hits the bottom or right edge,
917 * it is backed up until it hits the top or left edge.
920 resize(int dx, int dy)
926 if(Dx(sr)*Dy(sr) == 0) {
928 /* Start with the size of the first image */
933 if(Dx(r) >= dx && Dy(r) >= dy)
938 r.max.x = max(r.min.x+dx, r.max.x);
939 r.max.y = max(r.min.y+dy, r.max.y);
940 if(r.max.x > sr.max.x){
945 r = rectaddpt(r, Pt(sr.max.x-r.max.x, 0));
947 if(r.max.y > sr.max.y){
952 r = rectaddpt(r, Pt(0, sr.max.y-r.max.y));
956 * Sometimes we can't actually grow the window big enough,
957 * and resizing it to the same shape makes it flash.
959 if(Dx(r) == Dx(or) && Dy(r) == Dy(or))
966 * If we allocimage after a resize but before flushing the draw buffer,
967 * we won't have seen the reshape event, and we won't have called
968 * getwindow, and allocimage will fail. So we flushimage before every alloc.
971 xallocimage(Display *d, Rectangle r, ulong chan, int repl, ulong val)
973 flushimage(display, 0);
974 return allocimage(d, r, chan, repl, val);
984 fd = plumbopenfid("image", OREAD|OCEXEC);
986 fprint(2, "Cannot connect to the plumber");
987 threadexits("plumber");
990 send(cp, plumbrecvfid(fd));
994 /* XXX: This function is ugly and hacky. There may be a better way... or not */
1004 wexits("pipe failed");
1006 fd[0] = open("/dev/null", OREAD);
1009 if(threadspawnl(fd, "rc", "rc", "-c", "xdpyinfo | grep 'dimensions:'", nil) == -1)
1010 wexits("threadspawnl failed");
1012 if((n = read(pfd[0], buf, 63)) <= 0)
1013 wexits("read xdpyinfo failed");
1017 for(p = buf; *p; p++)
1018 if(*p >= '0' && *p <= '9') break;
1020 wexits("xdpyinfo parse failed");
1022 w = strtoul(p, &pr, 10);
1023 if(p == pr || *pr == '\0' || *(++pr) == '\0')
1024 wexits("xdpyinfo parse failed");
1025 h = strtoul(pr, &p, 10);
1027 wexits("xdpyinfo parse failed");
1029 return Rect(0, 0, w, h);
1042 threadspawnl(fd, "page", "page", "-R", nil);
1044 writeimage(pfd[1], im, 0);
1055 delta = (double)Dx(screen->r)/(double)Dx(im->r);
1056 if((double)Dy(im->r)*delta > Dy(screen->r))
1057 delta = (double)Dy(screen->r)/(double)Dy(im->r);
1058 r = Rect(0, 0, (int)((double)Dx(im->r)*delta), (int)((double)Dy(im->r)*delta));
1059 setcursor(mc, &reading);
1060 tmp = xallocimage(display, r, im->chan, 0, DBlack);
1062 fprint(2, "out of memory during fit: %r\n");
1067 delayfreeimage(tmp);
1071 flushimage(display, 1);