9 typedef struct Page Page;
37 int forward; /* read ahead direction: >= 0 forwards, < 0 backwards */
49 ulong imemlimit = 16*MiB;
52 Image *frame, *paper, *ground;
54 char pagespool[] = "/tmp/pagespool.";
87 [Corigsize] "orig size", 'o', Kesc, 0,
88 [Czoomin] "zoom in", '+', 0, 0,
89 [Czoomout] "zoom out", '-', 0, 0,
90 [Cfitwidth] "fit width", 'f', 0, 0,
91 [Cfitheight] "fit height", 'h', 0, 0,
92 [Crotate90] "rotate 90", 'r', 0, 0,
93 [Cupsidedown] "upside down", 'u', 0, 0,
94 [Cdummy1] "", 0, 0, 0,
95 [Cnext] "next", Kright, ' ', '\n',
96 [Cprev] "prev", Kleft, Kbs, 0,
97 [Csnarf] "snarf", 's', 0, 0,
98 [Czerox] "zerox", 'z', 0, 0,
99 [Cwrite] "write", 'w', 0, 0,
100 [Cext] "ext", 'x', 0, 0,
101 [Cdummy2] "", 0, 0, 0,
102 [Cquit] "quit", 'q', Kdel, Keof,
105 char *pagemenugen(int i);
106 char *cmdmenugen(int i);
122 {0xff, 0x80, 0xff, 0x80, 0xff, 0x00, 0xfe, 0x00,
123 0xff, 0x00, 0xff, 0x80, 0xff, 0xc0, 0xef, 0xe0,
124 0xc7, 0xf0, 0x03, 0xf0, 0x01, 0xe0, 0x00, 0xc0,
125 0x03, 0xff, 0x03, 0xff, 0x03, 0xff, 0x03, 0xff, },
126 {0x00, 0x00, 0x7f, 0x00, 0x7e, 0x00, 0x7c, 0x00,
127 0x7e, 0x00, 0x7f, 0x00, 0x6f, 0x80, 0x47, 0xc0,
128 0x03, 0xe0, 0x01, 0xf0, 0x00, 0xe0, 0x00, 0x40,
129 0x00, 0x00, 0x01, 0xb6, 0x01, 0xb6, 0x00, 0x00, }
132 int pagewalk1(Page *p);
133 void showpage1(Page *);
134 void showpage(Page *);
135 void drawpage(Page *);
136 Point pagesize(Page *);
139 addpage(Page *up, char *name, int (*popen)(Page *), void *pdata, int fd)
143 p = mallocz(sizeof(*p), 1);
144 p->name = strdup(name);
154 up->down = up->tail = p;
162 if(up && current == up){
171 resizewin(Point size)
175 if((wctl = open("/dev/wctl", OWRITE)) < 0)
178 size = addpt(size, Pt(Borderwidth*2, Borderwidth*2));
179 if(display->image != nil){
180 Point dsize = subpt(display->image->r.max, display->image->r.min);
185 /* can't just conver whole display */
186 if(eqpt(size, dsize))
189 fprint(wctl, "resize -dx %d -dy %d\n", size.x, size.y);
198 snprint(nam, sizeof nam, "%s%s%.12d%.8lux", pagespool, pfx, getpid(), id++);
199 return create(nam, OEXCL|ORCLOSE|ORDWR, 0600);
203 catchnote(void *, char *msg)
205 if(strstr(msg, "sys: write on closed pipe"))
207 if(strstr(msg, "hangup"))
209 if(strstr(msg, "alarm"))
211 if(strstr(msg, "interrupt"))
213 if(strstr(msg, "kill"))
226 for(mfd = 0; fd >= 0; fd = va_arg(arg, int), mfd++)
231 if((fd = open("/fd", OREAD)) < 0)
232 sysfatal("open: %r");
233 n = dirreadall(fd, &dir);
235 if(strstr(dir[i].name, "ctl"))
237 fd = atoi(dir[i].name);
245 pipeline(int fd, char *fmt, ...)
247 char buf[NPATH], *argv[4];
257 vsnprint(buf, sizeof buf, fmt, arg);
259 switch(rfork(RFPROC|RFMEM|RFFDG|RFREND|RFNOWAIT)){
265 dupfds(fd, pfd[1], 2, -1);
270 exec("/bin/rc", argv);
271 sysfatal("exec: %r");
282 static char buf[NR*UTFmax];
291 k += chartorune(&r, s+k);
295 strcpy(buf+k, "...");
296 while((l-i) >= NR/2-4){
297 k += chartorune(&r, s+k);
305 pageaddr1(Page *p, char *s, char *e)
307 if(p == nil || p == root)
309 return seprint(pageaddr1(p->up, s, e), e, "%s%s", p->up->delim, p->name);
313 * returns address string of a page in the form:
314 * /dir/filename!page!subpage!...
317 pageaddr(Page *p, char *buf, int nbuf)
320 pageaddr1(p, buf, buf+nbuf);
333 if((fd = dup(p->fd, -1)) < 0){
342 if(strcmp(p->ext, "ico") == 0)
343 pipeline(fd, "exec %s -c", p->ext);
345 pipeline(fd, "exec %s -t9", p->ext);
349 * dont keep the file descriptor arround if it can simply
352 fd2path(p->fd, nam, sizeof(nam));
353 if(strncmp(nam, pagespool, strlen(pagespool))){
356 p->data = strdup(nam);
368 pipeline(p->fd, "exec %s", (char*)p->data);
378 char mnt[32], cmd[64], *argv[4];
381 snprint(mnt, sizeof(mnt), "/n/tapefs.%.12d%.8lux", getpid(), (ulong)(uintptr)p);
382 snprint(cmd, sizeof(cmd), "exec %s -m %s /fd/0", (char*)p->data, mnt);
383 switch(rfork(RFPROC|RFMEM|RFFDG|RFREND)){
389 dupfds(p->fd, 1, 2, -1);
394 exec("/bin/rc", argv);
395 sysfatal("exec: %r");
400 p->data = strdup(mnt);
408 char buf[NPATH], *s, *e;
414 e = buf+sizeof(buf)-1;
415 s += snprint(s, e-s, "%s/", (char*)p->data);
418 pipeline(fd, "awk '/\\<rootfile/{"
419 "if(match($0, /full\\-path\\=\\\"([^\\\"]+)\\\"/)){"
420 "print substr($0, RSTART+11,RLENGTH-12);exit}}'");
421 n = read(fd, s, e - s);
425 while(n > 0 && s[n-1] == '\n')
429 if((fd = open(buf, OREAD)) < 0)
431 pipeline(fd, "awk '/\\<item/{"
432 "if(match($0, /id\\=\\\"([^\\\"]+)\\\"/)){"
433 "id=substr($0, RSTART+4, RLENGTH-5);"
434 "if(match($0, /href\\=\\\"([^\\\"]+)\\\"/)){"
435 "item[id]=substr($0, RSTART+6, RLENGTH-7)}}};"
437 "if(match($0, /idref\\=\\\"([^\\\"]+)\\\"/)){"
438 "ref=substr($0, RSTART+7, RLENGTH-8);"
439 "print item[ref]; fflush}}'");
440 s = strrchr(buf, '/')+1;
441 while((n = read(fd, s, e-s)) > 0){
442 while(n > 0 && s[n-1] == '\n')
445 addpage(p, s, popenfile, strdup(buf), -1);
451 typedef struct Ghost Ghost;
470 switch(rfork(RFPROC|RFMEM|RFFDG|RFNOWAIT)){
478 dupfds(gs->pdat, gs->pin, pfd[1], -1);
479 fprint(1, "%s DoPDFPage\n"
482 "dup (THIS IS NOT AN INFERNO BITMAP\\n) writestring "
483 "flushfile\n", p->name);
484 while((n = read(0, buf, sizeof buf)) > 0){
485 if(memcmp(buf, "THIS IS NOT AN INFERNO BITMAP\n", 30) == 0)
497 infernobithdr(char *buf, int n)
500 if(memcmp(buf, "compressed\n", 11) == 0)
502 if(strtochan((char*)buf))
504 if(memcmp(buf, " ", 10) == 0 &&
505 '0' <= buf[10] && buf[10] <= '9' &&
515 int n, i, pdf, ifd, ofd, pin[2], pout[2], pdat[2];
516 char buf[NBUF], nam[32], *argv[16];
523 if(read(ifd, buf, 5) != 5)
526 if(memcmp(buf, "%PDF-", 5) == 0)
546 argv[0] = (char*)p->data;
547 switch(rfork(RFPROC|RFMEM|RFFDG|RFREND|RFNOWAIT)){
552 dupfds(pin[1], pout[1], 2, pdat[1], ifd, -1);
554 dupfds(nullfd, nullfd, 2, pdat[1], ifd, -1);
556 pipeline(4, "%s", argv[0]);
559 argv[2] = "-sDEVICE=plan9";
560 argv[3] = "-sOutputFile=/fd/3";
562 argv[5] = pdf ? "-dDELAYSAFER" : "-dSAFER";
564 argv[7] = "-dTextAlphaBits=4";
565 argv[8] = "-dGraphicsAlphaBits=4";
566 snprint(buf, sizeof buf, "-r%d", ppi);
568 argv[10] = "-dDOINTERPOLATE";
569 argv[11] = pdf ? "-" : "/fd/4";
571 exec("/bin/gs", argv);
572 sysfatal("exec: %r");
583 "/PAGEOUT (/fd/1) (w) file def\n"
584 "/PAGE== { PAGEOUT exch write==only PAGEOUT (\\n) writestring PAGEOUT flushfile } def\n"
588 "/PDFSave null def\n"
589 "/DSCPageCount 0 def\n"
590 "/DoPDFPage {dup /Page# exch store pdfgetpage pdfshowpage } def\n"
592 "GS_PDF_ProcSet begin\n"
594 "(/fd/4) (r) file { DELAYSAFER { .setsafe } if } stopped pop pdfopen begin\n"
596 "pdfpagecount PAGE==\n";
599 if(write(pin[0], prolog, n) != n)
601 if((n = read(pout[0], buf, sizeof(buf)-1)) < 0)
609 gs = mallocz(sizeof(*gs), 1);
614 snprint(nam, sizeof nam, "%d", i);
615 addpage(p, nam, popenpdf, gs, -1);
618 /* keep ghostscript arround */
623 while((n = read(pdat[0], buf, sizeof(buf))) >= 0){
624 if(ofd >= 0 && (n <= 0 || infernobithdr(buf, n))){
625 snprint(nam, sizeof nam, "%d", i);
626 addpage(p, nam, popenimg, nil, ofd);
632 snprint(nam, sizeof nam, "%.4d", ++i);
633 if((ofd = createtmp(nam)) < 0)
634 ofd = dup(nullfd, -1);
636 if(write(ofd, buf, n) != n)
650 filetype(char *buf, int nbuf, char *typ, int ntyp)
652 int n, ifd[2], ofd[2];
655 if(infernobithdr(buf, nbuf)){
656 strncpy(typ, "image/p9bit", ntyp);
668 if(rfork(RFPROC|RFMEM|RFFDG|RFREND|RFNOWAIT) == 0){
669 dupfds(ifd[1], ofd[1], 2, -1);
673 exec("/bin/file", argv);
677 if(rfork(RFPROC|RFMEM|RFFDG|RFNOWAIT) == 0){
683 if((n = readn(ofd[0], typ, ntyp-1)) < 0)
686 while(n > 0 && typ[n-1] == '\n')
693 dircmp(void *p1, void *p2)
700 return strcmp(d1->name, d2->name);
711 "application/pdf", popengs, nil,
712 "application/postscript", popengs, nil,
713 "application/troff", popengs, "lp -dstdout",
714 "text/plain", popengs, "lp -dstdout",
715 "text/html", popengs, "uhtml | html2ms | tbl | troff -ms | lp -dstdout",
716 "application/dvi", popengs, "dvips -Pps -r0 -q1 -f1",
717 "application/doc", popengs, "doc2ps",
718 "application/zip", popentape, "fs/zipfs",
719 "application/x-tar", popentape, "fs/tarfs",
720 "application/x-ustar", popentape, "fs/tarfs",
721 "application/x-compress", popenfilter, "uncompress",
722 "application/x-gzip", popenfilter, "gunzip",
723 "application/x-bzip2", popenfilter, "bunzip2",
724 "image/gif", popenimg, "gif",
725 "image/jpeg", popenimg, "jpg",
726 "image/png", popenimg, "png",
727 "image/tiff", popenimg, "tif",
728 "image/ppm", popenimg, "ppm",
729 "image/bmp", popenimg, "bmp",
730 "image/tga", popenimg, "tga",
731 "image/x-icon", popenimg, "ico",
732 "image/p9bit", popenimg, nil,
735 char buf[NBUF], typ[128], *file;
746 if((fd = open(file, OREAD)) < 0){
753 if((d = dirfstat(fd)) == nil){
762 snprint(buf, sizeof(buf), "%s/META-INF/container.xml", file);
763 if((tfd = open(buf, OREAD)) >= 0){
770 if(strcmp(pageaddr(p, buf, sizeof(buf)), file) == 0)
772 if((n = dirreadall(fd, &d)) < 0)
774 qsort(d, n, sizeof d[0], dircmp);
776 addpage(p, d[i].name, popenfile, smprint("%s/%s", file, d[i].name), -1);
782 memset(buf, 0, NBUF/2);
783 if((n = readn(fd, buf, NBUF/2)) <= 0)
785 filetype(buf, n, typ, sizeof(typ));
786 for(i=0; i<nelem(tab); i++)
787 if(strncmp(typ, tab[i].typ, strlen(tab[i].typ)) == 0)
790 werrstr("unknown image format: %s", typ);
794 p->data = tab[i].data;
795 p->open = tab[i].open;
796 if(seek(fd, 0, 0) < 0)
798 if((i = readn(fd, buf+n, n)) < 0)
800 if(i != n || memcmp(buf, buf+n, i)){
803 if((tfd = createtmp("file")) < 0)
806 if(write(tfd, buf, n) != n)
808 if((n = read(fd, buf, sizeof(buf))) < 0)
811 if(dup(tfd, fd) < 0){
825 if(p != nil && p->down != nil)
841 for(p = root->down; p != nil; p = t)
842 if((t = nextpage(p)) == x)
854 if(p->open == nil || (fd = p->open(p)) < 0)
858 pipeline(fd, "exec rotate -r %d", rotate);
860 pipeline(fd, "exec resize -x %d", resize.x);
862 pipeline(fd, "exec resize -y %d", resize.y);
872 return Dy(i->r)*bytesperline(i->r, i->depth);
878 if(p->lnext == nil || p->lnext == p)
880 p->lnext->lprev = p->lprev;
881 p->lprev->lnext = p->lnext;
890 p->lnext = lru.lnext;
905 if(p->open != nil && p->image == nil){
908 if((p->image = readimage(display, fd, 1)) == nil)
909 fprint(2, "readimage: %r\n");
915 lockdisplay(display);
916 imemsize += imagesize(p->image);
917 unlockdisplay(display);
929 if(p->open == nil || p->image == nil)
931 lockdisplay(display);
932 imemsize -= imagesize(p->image);
934 unlockdisplay(display);
939 unloadpages(ulong limit)
943 while(imemsize >= limit && (p = lru.lprev) != &lru){
951 loadpages(Page *p, int oviewgen)
953 while(p != nil && viewgen == oviewgen){
956 if(viewgen != oviewgen){
966 if(size.x && size.y && newwin){
970 lockdisplay(display);
972 unlockdisplay(display);
975 if(p != current && imemsize >= imemlimit)
976 break; /* only one page ahead once we reach the limit */
978 if(p->up == nil || p->up->down == p)
990 * A draw operation that touches only the area contained in bot but not in top.
991 * mp and sp get aligned with bot.min.
994 gendrawdiff(Image *dst, Rectangle bot, Rectangle top,
995 Image *src, Point sp, Image *mask, Point mp, int op)
1001 if(Dx(bot)*Dy(bot) == 0)
1004 /* no points in bot - top */
1005 if(rectinrect(bot, top))
1008 /* bot - top ≡ bot */
1009 if(Dx(top)*Dy(top)==0 || rectXrect(bot, top)==0){
1010 gendrawop(dst, bot, src, sp, mask, mp, op);
1015 /* split bot into rectangles that don't intersect top */
1017 if(bot.min.x < top.min.x){
1018 r = Rect(bot.min.x, bot.min.y, top.min.x, bot.max.y);
1019 delta = subpt(r.min, origin);
1020 gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
1021 bot.min.x = top.min.x;
1025 if(bot.max.x > top.max.x){
1026 r = Rect(top.max.x, bot.min.y, bot.max.x, bot.max.y);
1027 delta = subpt(r.min, origin);
1028 gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
1029 bot.max.x = top.max.x;
1033 if(bot.min.y < top.min.y){
1034 r = Rect(bot.min.x, bot.min.y, bot.max.x, top.min.y);
1035 delta = subpt(r.min, origin);
1036 gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
1037 bot.min.y = top.min.y;
1041 if(bot.max.y > top.max.y){
1042 r = Rect(bot.min.x, top.max.y, bot.max.x, bot.max.y);
1043 delta = subpt(r.min, origin);
1044 gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op);
1045 bot.max.y = top.max.y;
1050 alphachan(ulong chan)
1052 for(; chan; chan >>= 8)
1053 if(TYPE(chan) == CAlpha)
1059 zoomdraw(Image *d, Rectangle r, Rectangle top, Image *b, Image *s, Point sp, int f)
1067 if(r.min.x < d->r.min.x){
1068 sp.x += (d->r.min.x - r.min.x)/f;
1069 a.x = (d->r.min.x - r.min.x)%f;
1070 r.min.x = d->r.min.x;
1072 if(r.min.y < d->r.min.y){
1073 sp.y += (d->r.min.y - r.min.y)/f;
1074 a.y = (d->r.min.y - r.min.y)%f;
1075 r.min.y = d->r.min.y;
1078 w = s->r.max.x - sp.x;
1082 dr.max.x = dr.min.x+w;
1083 if(!alphachan(s->chan))
1086 if(b) gendrawdiff(d, dr, top, b, sp, nil, ZP, SoverD);
1087 gendrawdiff(d, dr, top, s, sp, nil, ZP, SoverD);
1090 if((t = allocimage(display, dr, s->chan, 0, 0)) == nil)
1092 for(; dr.min.y < r.max.y; dr.min.y++){
1093 dr.max.y = dr.min.y+1;
1094 draw(t, dr, s, nil, sp);
1101 for(sp=dr.min; dr.min.x < r.max.x; sp.x++){
1102 dr.max.x = dr.min.x+1;
1103 if(b != nil) gendrawdiff(d, dr, top, b, sp, nil, ZP, SoverD);
1104 gendrawdiff(d, dr, top, t, sp, nil, ZP, SoverD);
1105 for(dr.min.x++; ++a.x < f && dr.min.x < r.max.x; dr.min.x++){
1106 dr.max.x = dr.min.x+1;
1107 gendrawdiff(d, dr, top, d, Pt(dr.min.x-1, dr.min.y), nil, ZP, SoverD);
1117 return p->image != nil ? mulpt(subpt(p->image->r.max, p->image->r.min), zoom) : ZP;
1121 drawframe(Rectangle r)
1123 border(screen, r, -Borderwidth, frame, ZP);
1124 gendrawdiff(screen, screen->r, insetrect(r, -Borderwidth), ground, ZP, nil, ZP, SoverD);
1125 flushimage(display, 1);
1134 if((i = p->image) != nil){
1135 r = rectaddpt(Rpt(ZP, pagesize(p)), addpt(pos, screen->r.min));
1136 zoomdraw(screen, r, ZR, paper, i, i->r.min, zoom);
1138 r = Rpt(ZP, stringsize(font, p->name));
1139 r = rectaddpt(r, addpt(subpt(divpt(subpt(screen->r.max, screen->r.min), 2),
1140 divpt(r.max, 2)), screen->r.min));
1141 draw(screen, r, paper, nil, ZP);
1142 string(screen, r.min, display->black, ZP, font, p->name);
1148 translate(Page *p, Point d)
1154 if(i==nil || d.x==0 && d.y==0)
1156 r = rectaddpt(Rpt(ZP, pagesize(p)), addpt(pos, screen->r.min));
1157 pos = addpt(pos, d);
1158 nr = rectaddpt(r, d);
1159 if(rectclip(&r, screen->r))
1160 draw(screen, rectaddpt(r, d), screen, nil, r.min);
1163 zoomdraw(screen, nr, rectaddpt(r, d), paper, i, i->r.min, zoom);
1173 if((s = pagewalk) == nil || *s == 0)
1175 n = strlen(p->name);
1176 if(n == 0 || strncmp(s, p->name, n) != 0)
1182 if(s[n] == '/' || s[n] == '!'){
1190 trywalk(char *name, char *addr)
1192 static char buf[NPATH];
1196 memset(buf, 0, sizeof(buf));
1197 snprint(buf, sizeof(buf), "%s%s%s",
1198 name != nil ? name : "",
1199 (name != nil && addr != nil) ? "!" : "",
1200 addr != nil ? addr : "");
1207 for(; p != nil; p = p->next)
1218 findpage(char *name)
1227 /* look in current document */
1228 if(current != nil && current->up != nil){
1229 for(p = current->up->down; p != nil; p = p->next)
1230 if(cistrncmp(p->name, name, n) == 0)
1233 /* look everywhere */
1235 for(p = root->down; p != nil; p = nextpage(p))
1236 if(cistrncmp(p->name, name, n) == 0)
1240 return trywalk(name, nil);
1244 writeaddr(Page *p, char *file)
1246 char buf[NPATH], *s;
1249 s = pageaddr(p, buf, sizeof(buf));
1250 if((fd = open(file, OWRITE)) >= 0){
1251 write(fd, s, strlen(s));
1261 for(p = root->down; i > 0 && p != nil; p = nextpage(p))
1272 for(i = 0, p = root->down; p != nil && p != x; p = nextpage(p))
1274 return (p == x) ? i : -1;
1282 if((p = pageat(i)) != nil)
1283 return shortlabel(p->name);
1290 if(i < 0 || i >= nelem(cmds))
1296 * spawn new proc to load a run of pages starting with p
1297 * the display should *not* be locked as it gets called
1298 * from recursive page load.
1308 esetcursor(&reading);
1309 writeaddr(p, "/dev/label");
1312 switch(rfork(RFPROC|RFMEM)){
1314 sysfatal("rfork: %r");
1316 loadpages(p, oviewgen);
1319 if(++nproc >= NPROC)
1324 /* recursive display lock, called from main proc only */
1326 drawlock(int dolock){
1330 lockdisplay(display);
1333 unlockdisplay(display);
1344 unloadpages(imemlimit);
1352 char nam[64], *argv[4];
1359 if((fd = openpage(p)) < 0)
1361 if(rfork(RFPROC|RFMEM|RFFDG|RFENVG|RFNOTEG|RFNOWAIT) == 0){
1362 dupfds(fd, 1, 2, -1);
1363 snprint(nam, sizeof nam, "/bin/%s", argv0);
1368 sysfatal("exec: %r");
1379 char label[64], *argv[4];
1385 snprint(label, sizeof(label), "%s %s", p->ext, p->name);
1388 ps = addpt(subpt(p->image->r.max, p->image->r.min), Pt(24, 24));
1390 if((fd = p->fd) < 0){
1391 if(p->open != popenfile)
1393 fd = open((char*)p->data, OREAD);
1398 if(rfork(RFPROC|RFMEM|RFFDG|RFNOTEG|RFREND|RFNOWAIT) == 0){
1399 if(newwindow(nil) != -1){
1400 dupfds(fd, open("/dev/cons", OWRITE), open("/dev/cons", OWRITE), -1);
1401 if((fd = open("/dev/label", OWRITE)) >= 0){
1402 write(fd, label, strlen(label));
1411 exec("/bin/rc", argv);
1425 if(new && getwindow(display, Refnone) == -1)
1426 sysfatal("getwindow: %r");
1427 if((p = current) != nil){
1437 void killcohort(void)
1440 for(i=0;i!=3;i++){ /* It's a long way to the kitchen */
1441 postnote(PNGROUP, cohort, "kill");
1446 void drawerr(Display *, char *msg)
1448 sysfatal("draw: %s", msg);
1454 fprint(2, "usage: %s [ -iRw ] [ -m mb ] [ -p ppi ] [ -j addr ] [ file ... ]\n", argv0);
1459 docmd(int i, Mouse *m)
1461 char buf[NPATH], *s;
1487 resize = subpt(screen->r.max, screen->r.min);
1493 resize = subpt(screen->r.max, screen->r.min);
1498 if(current == nil || !canqlock(current))
1500 o = subpt(m->xy, screen->r.min);
1504 pos = addpt(mulpt(subpt(pos, o), 2), o);
1509 pos = addpt(divpt(subpt(pos, o), 2), o);
1516 if(current == nil || !canqlock(current))
1518 if(current->image != nil){
1520 if(current->up != nil && current->up != root)
1521 s = current->up->name;
1522 snprint(buf, sizeof(buf), "%s%s%s.bit",
1524 s != nil ? "." : "",
1526 if(eenter("Write", buf, sizeof(buf), m) > 0){
1527 if((fd = create(buf, OWRITE, 0666)) < 0){
1528 errstr(buf, sizeof(buf));
1529 eenter(buf, 0, 0, m);
1531 esetcursor(&reading);
1532 writeimage(fd, current->image, 0);
1541 if(current == nil || !canqlock(current))
1547 writeaddr(current, "/dev/snarf");
1551 showpage(nextpage(current));
1555 showpage(prevpage(current));
1571 if(current == nil || !canqlock(current))
1575 p = prevpage(current);
1584 z.y = Dy(screen->r);
1585 if(pos.y+z.y > Dy(screen->r))
1586 pos.y = Dy(screen->r) - z.y;
1594 z = pagesize(current);
1595 if(pos.y+z.y <= Dy(screen->r)){
1596 p = nextpage(current);
1608 translate(current, Pt(0, -y));
1613 main(int argc, char *argv[])
1615 enum { Eplumb = 4 };
1643 trywalk(EARGF(usage()), nil);
1646 imemlimit = atol(EARGF(usage()))*MiB;
1649 ppi = atoi(EARGF(usage()));
1656 if(newwindow(nil) < 0)
1657 sysfatal("newwindow: %r");
1661 * so that we can stop all subprocesses with a note,
1662 * and to isolate rendezvous from other processes
1664 atnotify(catchnote, 1);
1665 if(cohort = rfork(RFPROC|RFNOTEG|RFNAMEG|RFREND)){
1672 if(initdraw(drawerr, nil, argv0) < 0)
1673 sysfatal("initdraw: %r");
1674 paper = display->white;
1675 frame = display->black;
1676 ground = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x777777FF);
1677 display->locking = 1;
1678 unlockdisplay(display);
1680 einit(Ekeyboard|Emouse);
1681 eplumb(Eplumb, "image");
1682 memset(&m, 0, sizeof(m));
1683 if((nullfd = open("/dev/null", ORDWR)) < 0)
1684 sysfatal("open: %r");
1688 current = root = addpage(nil, "", nil, nil, -1);
1690 if(*argv == nil && !imode)
1691 addpage(root, "stdin", popenfile, strdup("/fd/0"), -1);
1692 for(; *argv; argv++)
1693 addpage(root, *argv, popenfile, strdup(*argv), -1);
1705 if(current && canqlock(current)){
1709 if((m.buttons & 1) == 0)
1711 translate(current, subpt(m.xy, o));
1715 } else if(m.buttons & 2){
1717 i = emenuhit(2, &m, &cmdmenu);
1720 } else if(m.buttons & 4){
1725 pagemenu.lasthit = pageindex(current);
1726 x = pageat(emenuhit(3, &m, &pagemenu));
1731 } else if(m.buttons & 8){
1732 scroll(screen->r.min.y - m.xy.y);
1733 } else if(m.buttons & 16){
1734 scroll(m.xy.y - screen->r.min.y);
1740 scroll(-Dy(screen->r)/3);
1743 scroll(-Dy(screen->r)/2);
1746 scroll(Dy(screen->r)/3);
1749 scroll(Dy(screen->r)/2);
1752 for(i = 0; i<nelem(cmds); i++)
1753 if((cmds[i].k1 == e.kbdc) ||
1754 (cmds[i].k2 == e.kbdc) ||
1755 (cmds[i].k3 == e.kbdc))
1757 if(i < nelem(cmds)){
1761 if((e.kbdc < 0x20) ||
1762 (e.kbdc & 0xFF00) == KF ||
1763 (e.kbdc & 0xFF00) == Spec)
1765 snprint(buf, sizeof(buf), "%C", (Rune)e.kbdc);
1766 if(eenter("Go to", buf, sizeof(buf), &m) > 0){
1768 showpage(findpage(buf));
1774 if(pm && pm->ndata > 0){
1779 s = plumblookup(pm->attr, "action");
1780 if(s && strcmp(s, "quit")==0)
1782 if(s && strcmp(s, "showdata")==0){
1783 if((fd = createtmp("plumb")) < 0){
1784 fprint(2, "plumb: createtmp: %r\n");
1788 if(fd2path(fd, s, NPATH) < 0){
1792 write(fd, pm->data, pm->ndata);
1793 }else if(pm->data[0] == '/'){
1794 s = strdup(pm->data);
1796 s = malloc(strlen(pm->wdir)+1+pm->ndata+1);
1797 sprint(s, "%s/%s", pm->wdir, pm->data);
1800 j = trywalk(s, plumblookup(pm->attr, "addr"));
1804 j = addpage(root, s, popenfile, s, fd);