diff options
author | cinap_lenrek <cinap_lenrek@centraldogma> | 2011-09-15 20:59:31 +0200 |
---|---|---|
committer | cinap_lenrek <cinap_lenrek@centraldogma> | 2011-09-15 20:59:31 +0200 |
commit | d9657f82748a397eed451a4cfd0bbc6f19551488 (patch) | |
tree | 920beb374f6476d81df66188119254acc0db7197 /sys/src/cmd/page.c | |
parent | d02a0b77666d94bc97bbf0412b3cbefeff12c462 (diff) |
replaceing page with npage
Diffstat (limited to 'sys/src/cmd/page.c')
-rw-r--r-- | sys/src/cmd/page.c | 1282 |
1 files changed, 1282 insertions, 0 deletions
diff --git a/sys/src/cmd/page.c b/sys/src/cmd/page.c new file mode 100644 index 000000000..a0751a5eb --- /dev/null +++ b/sys/src/cmd/page.c @@ -0,0 +1,1282 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <event.h> +#include <cursor.h> +#include <keyboard.h> +#include <plumb.h> + +typedef struct Page Page; +struct Page { + char *label; + + QLock; + void *data; + int (*open)(Page *); + + char *text; + Image *image; + int fd; + int gen; + + Page *up; + Page *next; + Page *down; + Page *tail; +}; + +int zoom = 1; +int ppi = 100; +int imode; +int newwin; +int rotate; +int viewgen; +int pagegen; +Point resize, pos; +Page *root, *current; +QLock pagelock; +int nullfd; + +char pagespool[] = "/tmp/pagespool."; + +enum { + NPROC = 4, + NAHEAD = 2, + NBUF = 8*1024, + NPATH = 1024, +}; + +char *pagemenugen(int i); + +char *menuitems[] = { + "orig size", + "rotate 90", + "upside down", + "", + "fit width", + "fit height", + "", + "zoom in", + "zoom out", + "", + "next", + "prev", + "zerox", + "", + "quit", + nil +}; + +Menu pagemenu = { + nil, + pagemenugen, + -1, +}; + +Menu menu = { + menuitems, + nil, + -1, +}; + +Cursor reading = { + {-1, -1}, + {0xff, 0x80, 0xff, 0x80, 0xff, 0x00, 0xfe, 0x00, + 0xff, 0x00, 0xff, 0x80, 0xff, 0xc0, 0xef, 0xe0, + 0xc7, 0xf0, 0x03, 0xf0, 0x01, 0xe0, 0x00, 0xc0, + 0x03, 0xff, 0x03, 0xff, 0x03, 0xff, 0x03, 0xff, }, + {0x00, 0x00, 0x7f, 0x00, 0x7e, 0x00, 0x7c, 0x00, + 0x7e, 0x00, 0x7f, 0x00, 0x6f, 0x80, 0x47, 0xc0, + 0x03, 0xe0, 0x01, 0xf0, 0x00, 0xe0, 0x00, 0x40, + 0x00, 0x00, 0x01, 0xb6, 0x01, 0xb6, 0x00, 0x00, } +}; + +void showpage(Page *); +void drawpage(Page *); +Point pagesize(Page *); + +Page* +addpage(Page *up, char *label, int (*popen)(Page *), void *pdata, int fd) +{ + Page *p; + + p = mallocz(sizeof(*p), 1); + p->label = strdup(label); + p->gen = pagegen; + p->text = nil; + p->image = nil; + p->data = pdata; + p->open = popen; + p->fd = fd; + + p->down = nil; + p->tail = nil; + p->next = nil; + + qlock(&pagelock); + if(p->up = up){ + if(up->tail == nil) + up->down = up->tail = p; + else { + up->tail->next = p; + up->tail = p; + } + } + qunlock(&pagelock); + + if(up && current == up) + showpage(p); + return p; +} + +void +resizewin(Point size) +{ + int wctl; + + if((wctl = open("/dev/wctl", OWRITE)) < 0) + return; + /* add rio border */ + size = addpt(size, Pt(Borderwidth*2, Borderwidth*2)); + fprint(wctl, "resize -dx %d -dy %d\n", size.x, size.y); + close(wctl); +} + +int +createtmp(ulong id, char *pfx) +{ + char nam[64]; + + snprint(nam, sizeof nam, "%s%s%.12d%.8lux", pagespool, pfx, getpid(), id ^ 0xcafebabe); + return create(nam, OEXCL|ORCLOSE|ORDWR, 0600); +} + +int +catchnote(void *, char *msg) +{ + if(strstr(msg, "sys: write on closed pipe")) + return 1; + if(strstr(msg, "hangup")) + return 1; + if(strstr(msg, "alarm")) + return 1; + return 0; +} + +void +pipeline(int fd, char *fmt, ...) +{ + char buf[128], *argv[4]; + va_list arg; + int pfd[2]; + + if(pipe(pfd) < 0){ + Err: + dup(nullfd, fd); + return; + } + switch(rfork(RFPROC|RFFDG|RFREND|RFNOWAIT)){ + case -1: + close(pfd[0]); + close(pfd[1]); + goto Err; + case 0: + if(dup(fd, 0)<0) + exits("dup"); + if(dup(pfd[1], 1)<0) + exits("dup"); + close(fd); + close(pfd[1]); + close(pfd[0]); + va_start(arg, fmt); + vsnprint(buf, sizeof buf, fmt, arg); + va_end(arg); + + argv[0] = "rc"; + argv[1] = "-c"; + argv[2] = buf; + argv[3] = nil; + exec("/bin/rc", argv); + sysfatal("exec: %r"); + } + close(pfd[1]); + dup(pfd[0], fd); + close(pfd[0]); +} + +int +popenfile(Page*); + +int +popenconv(Page *p) +{ + char nam[NPATH]; + int fd; + + if((fd = dup(p->fd, -1)) < 0){ + close(p->fd); + p->fd = -1; + return -1; + } + + seek(fd, 0, 0); + if(p->data) + pipeline(fd, "%s", (char*)p->data); + + /* + * dont keep the file descriptor arround if it can simply + * be reopened. + */ + fd2path(p->fd, nam, sizeof(nam)); + if(strncmp(nam, pagespool, strlen(pagespool))){ + close(p->fd); + p->fd = -1; + p->data = strdup(nam); + p->open = popenfile; + } + + return fd; +} + +typedef struct Ghost Ghost; +struct Ghost +{ + QLock; + + int pin; + int pout; + int pdat; +}; + +int +popenpdf(Page *p) +{ + char buf[NBUF]; + int n, pfd[2]; + Ghost *gs; + + if(pipe(pfd) < 0) + return -1; + switch(rfork(RFFDG|RFPROC|RFMEM|RFNOWAIT)){ + case -1: + close(pfd[0]); + close(pfd[1]); + return -1; + case 0: + close(pfd[0]); + gs = p->data; + qlock(gs); + fprint(gs->pin, "%s DoPDFPage\n" + "(/fd/3) (w) file " + "dup flushfile " + "dup (THIS IS NOT AN INFERNO BITMAP\\n) writestring " + "flushfile\n", p->label); + while((n = read(gs->pdat, buf, sizeof buf)) > 0){ + if(memcmp(buf, "THIS IS NOT AN INFERNO BITMAP\n", 30) == 0) + break; + if(pfd[1] < 0) + continue; + if(write(pfd[1], buf, n) != n){ + close(pfd[1]); + pfd[1]=-1; + } + } + qunlock(gs); + exits(nil); + } + close(pfd[1]); + return pfd[0]; +} + +int +infernobithdr(char *buf, int n) +{ + if(n >= 11){ + if(memcmp(buf, "compressed\n", 11) == 0) + return 1; + if(strtochan((char*)buf)) + return 1; + if(memcmp(buf, " ", 10) == 0 && + '0' <= buf[10] && buf[10] <= '9' && + buf[11] == ' ') + return 1; + } + return 0; +} + +int +popengs(Page *p) +{ + int n, i, pdf, ifd, ofd, pin[2], pout[2], pdat[2]; + char buf[NBUF], nam[32], *argv[16]; + + pdf = 0; + ifd = p->fd; + p->fd = -1; + seek(ifd, 0, 0); + if(read(ifd, buf, 5) != 5) + goto Err0; + seek(ifd, 0, 0); + if(memcmp(buf, "%PDF-", 5) == 0) + pdf = 1; + p->text = strdup(p->label); + if(pipe(pin) < 0){ + Err0: + close(ifd); + return -1; + } + if(pipe(pout) < 0){ + Err1: + close(pin[0]); + close(pin[1]); + goto Err0; + } + if(pipe(pdat) < 0){ + Err2: + close(pdat[0]); + close(pdat[1]); + goto Err1; + } + + switch(rfork(RFREND|RFPROC|RFFDG|RFNOWAIT)){ + case -1: + goto Err2; + case 0: + if(pdf){ + if(dup(pin[1], 0)<0) + exits("dup"); + if(dup(pout[1], 1)<0) + exits("dup"); + } else { + if(dup(nullfd, 0)<0) + exits("dup"); + if(dup(nullfd, 1)<0) + exits("dup"); + } + if(dup(nullfd, 2)<0) + exits("dup"); + if(dup(pdat[1], 3)<0) + exits("dup"); + if(dup(ifd, 4)<0) + exits("dup"); + + close(pin[0]); + close(pin[1]); + close(pout[0]); + close(pout[1]); + close(pdat[0]); + close(pdat[1]); + close(ifd); + + if(p->data) + pipeline(4, "%s", (char*)p->data); + + argv[0] = "gs"; + argv[1] = "-q"; + argv[2] = "-sDEVICE=plan9"; + argv[3] = "-sOutputFile=/fd/3"; + argv[4] = "-dBATCH"; + argv[5] = pdf ? "-dDELAYSAFER" : "-dSAFER"; + argv[6] = "-dQUIET"; + argv[7] = "-dTextAlphaBits=4"; + argv[8] = "-dGraphicsAlphaBits=4"; + snprint(buf, sizeof buf, "-r%d", ppi); + argv[9] = buf; + argv[10] = "-dDOINTERPOLATE"; + argv[11] = pdf ? "-" : "/fd/4"; + argv[12] = nil; + exec("/bin/gs", argv); + sysfatal("exec: %r"); + } + + close(pin[1]); + close(pout[1]); + close(pdat[1]); + close(ifd); + + if(pdf){ + Ghost *gs; + char *prolog = + "/PAGEOUT (/fd/1) (w) file def\n" + "/PAGE== { PAGEOUT exch write==only PAGEOUT (\\n) writestring PAGEOUT flushfile } def\n" + "\n" + "/Page null def\n" + "/Page# 0 def\n" + "/PDFSave null def\n" + "/DSCPageCount 0 def\n" + "/DoPDFPage {dup /Page# exch store pdfgetpage pdfshowpage } def\n" + "\n" + "GS_PDF_ProcSet begin\n" + "pdfdict begin\n" + "(/fd/4) (r) file { DELAYSAFER { .setsafe } if } stopped pop pdfopen begin\n" + "\n" + "pdfpagecount PAGE==\n"; + + n = strlen(prolog); + if(write(pin[0], prolog, n) != n) + goto Out; + if((n = read(pout[0], buf, sizeof(buf)-1)) < 0) + goto Out; + buf[n] = 0; + n = atoi(buf); + if(n <= 0){ + werrstr("no pages"); + goto Out; + } + gs = mallocz(sizeof(*gs), 1); + gs->pin = pin[0]; + gs->pout = pout[0]; + gs->pdat = pdat[0]; + for(i=1; i<=n; i++){ + snprint(nam, sizeof nam, "%d", i); + addpage(p, nam, popenpdf, gs, -1); + } + + /* keep ghostscript arround */ + return -1; + } else { + i = 0; + ofd = -1; + while((n = read(pdat[0], buf, sizeof(buf))) >= 0){ + if(ofd >= 0 && (n <= 0 || infernobithdr(buf, n))){ + snprint(nam, sizeof nam, "%d", i); + addpage(p, nam, popenconv, nil, ofd); + ofd = -1; + } + if(n <= 0) + break; + if(ofd < 0){ + snprint(nam, sizeof nam, "%.4d", ++i); + if((ofd = createtmp((ulong)p, nam)) < 0) + ofd = dup(nullfd, -1); + } + if(write(ofd, buf, n) != n) + break; + } + if(ofd >= 0) + close(ofd); + } +Out: + close(pin[0]); + close(pout[0]); + close(pdat[0]); + return -1; +} + +int +popenfile(Page *p) +{ + char buf[NBUF], *file; + int i, n, fd, tfd; + Dir *d; + + fd = p->fd; + p->fd = -1; + file = p->data; + if(fd < 0){ + if((fd = open(file, OREAD)) < 0){ + Err0: + p->data = nil; + free(file); + return -1; + } + } + seek(fd, 0, 0); + if((d = dirfstat(fd)) == nil){ + Err1: + close(fd); + goto Err0; + } + if(d->mode & DMDIR){ + free(d); + d = nil; + if((n = dirreadall(fd, &d)) < 0) + goto Err1; + for(i = 0; i<n; i++) + addpage(p, d[i].name, popenfile, smprint("%s/%s", file, d[i].name), -1); + free(d); + p->text = strdup(p->label); + goto Err1; + } + free(d); + + memset(buf, 0, 32+1); + if((n = read(fd, buf, 32)) <= 0) + goto Err1; + + p->fd = fd; + p->data = nil; + p->open = popenconv; + if(memcmp(buf, "%PDF-", 5) == 0 || strstr(buf, "%!")) + p->open = popengs; + else if(memcmp(buf, "x T ", 4) == 0){ + p->data = "lp -dstdout"; + p->open = popengs; + } + else if(memcmp(buf, "\xF7\x02\x01\x83\x92\xC0\x1C;", 8) == 0){ + p->data = "dvips -Pps -r0 -q1 -f1"; + p->open = popengs; + } + else if(memcmp(buf, "\x1F\x8B", 2) == 0){ + p->data = "gunzip"; + p->open = popengs; + } + else if(memcmp(buf, "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1", 8) == 0){ + p->data = "doc2ps"; + p->open = popengs; + } + else if(memcmp(buf, "GIF", 3) == 0) + p->data = "gif -t9"; + else if(memcmp(buf, "\111\111\052\000", 4) == 0) + p->data = "fb/tiff2pic | fb/3to1 rgbv | fb/pcp -tplan9"; + else if(memcmp(buf, "\115\115\000\052", 4) == 0) + p->data = "fb/tiff2pic | fb/3to1 rgbv | fb/pcp -tplan9"; + else if(memcmp(buf, "\377\330\377", 3) == 0) + p->data = "jpg -t9"; + else if(memcmp(buf, "\211PNG\r\n\032\n", 3) == 0) + p->data = "png -t9"; + else if(memcmp(buf, "\0PC Research, Inc", 17) == 0) + p->data = "aux/g3p9bit -g"; + else if(memcmp(buf, "TYPE=ccitt-g31", 14) == 0) + p->data = "aux/g3p9bit -g"; + else if(memcmp(buf, "II*", 3) == 0) + p->data = "aux/g3p9bit -g"; + else if(memcmp(buf, "TYPE=", 5) == 0) + p->data = "fb/3to1 rgbv |fb/pcp -tplan9"; + else if(buf[0] == 'P' && '0' <= buf[1] && buf[1] <= '9') + p->data = "ppm -t9"; + else if(memcmp(buf, "BM", 2) == 0) + p->data = "bmp -t9"; + else if(infernobithdr(buf, n)) + p->data = nil; + else { + werrstr("unknown image format"); + goto Err1; + } + + if(seek(fd, 0, 0) < 0) + goto Noseek; + if((i = read(fd, buf+n, n)) < 0) + goto Err1; + if(i != n || memcmp(buf, buf+n, i)){ + n += i; + Noseek: + if((tfd = createtmp((ulong)p, "file")) < 0) + goto Err1; + while(n > 0){ + if(write(tfd, buf, n) != n) + goto Err2; + if((n = read(fd, buf, sizeof(buf))) < 0) + goto Err2; + } + if(dup(tfd, fd) < 0){ + Err2: + close(tfd); + goto Err1; + } + close(tfd); + } + free(file); + return p->open(p); +} + +Page* +nextpage(Page *p) +{ + if(p){ + if(p->down) + return p->down; + if(p->next) + return p->next; + if(p->up) + return p->up->next; + } + return nil; +} + +Page* +prevpage(Page *x) +{ + Page *p, *t; + + if(x){ + for(p = root->down; p; p = t) + if((t = nextpage(p)) == x) + return p; + } + return nil; +} + +int +openpage(Page *p) +{ + int fd; + + fd = -1; + if(p->open == nil || (fd = p->open(p)) < 0) + p->open = nil; + else { + if(rotate) + pipeline(fd, "rotate -r %d", rotate); + if(resize.x) + pipeline(fd, "resize -x %d", resize.x); + else if(resize.y) + pipeline(fd, "resize -y %d", resize.y); + } + return fd; +} + +void +loadpage(Page *p) +{ + int fd; + + if(p->open && p->image == nil && p->text == nil){ + if((fd = openpage(p)) >= 0){ + pagegen++; + p->image = readimage(display, fd, 1); + close(fd); + } + if(p->image == nil && p->text == nil) + p->text = smprint("%s: %r", p->label); + } + p->gen = pagegen; +} + +void +unloadpage(Page *p) +{ + if(p->open){ + if(p->text) + free(p->text); + p->text = nil; + if(p->image){ + lockdisplay(display); + freeimage(p->image); + unlockdisplay(display); + } + p->image = nil; + } +} + +void +unloadpages(int age) +{ + Page *p; + + for(p = root->down; p; p = nextpage(p)){ + if(age == 0) /* synchronous flush */ + qlock(p); + else if(!canqlock(p)) + continue; + if((pagegen - p->gen) >= age) + unloadpage(p); + qunlock(p); + } +} + +void +loadpages(Page *p, int ahead, int oviewgen) +{ + int i; + + ahead++; /* load at least one */ + unloadpages(ahead*2); + for(i = 0; i < ahead && p; p = nextpage(p), i++){ + if(viewgen != oviewgen) + break; + if(canqlock(p)){ + loadpage(p); + if(viewgen != oviewgen){ + unloadpage(p); + qunlock(p); + break; + } + if(p == current){ + Point size; + + esetcursor(nil); + size = pagesize(p); + if(size.x && size.y && newwin){ + newwin = 0; + resizewin(size); + } + lockdisplay(display); + drawpage(p); + unlockdisplay(display); + } + qunlock(p); + } + } +} + +/* + * A draw operation that touches only the area contained in bot but not in top. + * mp and sp get aligned with bot.min. + */ +static void +gendrawdiff(Image *dst, Rectangle bot, Rectangle top, + Image *src, Point sp, Image *mask, Point mp, int op) +{ + Rectangle r; + Point origin; + Point delta; + + USED(op); + + if(Dx(bot)*Dy(bot) == 0) + return; + + /* no points in bot - top */ + if(rectinrect(bot, top)) + return; + + /* bot - top ≡ bot */ + if(Dx(top)*Dy(top)==0 || rectXrect(bot, top)==0){ + gendrawop(dst, bot, src, sp, mask, mp, op); + return; + } + + origin = bot.min; + /* split bot into rectangles that don't intersect top */ + /* left side */ + if(bot.min.x < top.min.x){ + r = Rect(bot.min.x, bot.min.y, top.min.x, bot.max.y); + delta = subpt(r.min, origin); + gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op); + bot.min.x = top.min.x; + } + + /* right side */ + if(bot.max.x > top.max.x){ + r = Rect(top.max.x, bot.min.y, bot.max.x, bot.max.y); + delta = subpt(r.min, origin); + gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op); + bot.max.x = top.max.x; + } + + /* top */ + if(bot.min.y < top.min.y){ + r = Rect(bot.min.x, bot.min.y, bot.max.x, top.min.y); + delta = subpt(r.min, origin); + gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op); + bot.min.y = top.min.y; + } + + /* bottom */ + if(bot.max.y > top.max.y){ + r = Rect(bot.min.x, top.max.y, bot.max.x, bot.max.y); + delta = subpt(r.min, origin); + gendrawop(dst, r, src, addpt(sp, delta), mask, addpt(mp, delta), op); + bot.max.y = top.max.y; + } +} + +void +zoomdraw(Image *d, Rectangle r, Rectangle top, Image *s, Point sp, int f) +{ + int w, x, y; + Image *t; + Point a; + + if(f <= 1){ + gendrawdiff(d, r, top, s, sp, nil, ZP, S); + return; + } + a = ZP; + if(r.min.x < d->r.min.x){ + sp.x += (d->r.min.x - r.min.x)/f; + a.x = (d->r.min.x - r.min.x)%f; + r.min.x = d->r.min.x; + } + if(r.min.y < d->r.min.y){ + sp.y += (d->r.min.y - r.min.y)/f; + a.y = (d->r.min.y - r.min.y)%f; + r.min.y = d->r.min.y; + } + rectclip(&r, d->r); + w = s->r.max.x - sp.x; + if(w > Dx(r)) + w = Dx(r); + t = allocimage(display, Rect(r.min.x, r.min.y, r.min.x+w, r.max.y), s->chan, 0, DNofill); + if(t == nil) + return; + for(y=r.min.y; y<r.max.y; y++){ + draw(t, Rect(r.min.x, y, r.min.x+w, y+1), s, nil, sp); + if(++a.y == zoom){ + a.y = 0; + sp.y++; + } + } + sp = r.min; + for(x=r.min.x; x<r.max.x; x++){ + gendrawdiff(d, Rect(x, r.min.y, x+1, r.max.y), top, t, sp, nil, ZP, S); + if(++a.x == f){ + a.x = 0; + sp.x++; + } + } + freeimage(t); +} + +Point +pagesize(Page *p) +{ + return p->image ? mulpt(subpt(p->image->r.max, p->image->r.min), zoom) : ZP; +} + +void +drawpage(Page *p) +{ + Rectangle r; + Image *i; + + if((i = p->image) == nil){ + char *s; + + if((s = p->text) == nil) + s = "..."; + r.min = ZP; + r.max = stringsize(font, p->text); + r = rectaddpt(r, addpt(subpt(divpt(subpt(screen->r.max, screen->r.min), 2), divpt(r.max, 2)), + screen->r.min)); + draw(screen, r, display->white, nil, ZP); + string(screen, r.min, display->black, ZP, font, s); + } else { + r = rectaddpt(Rpt(ZP, pagesize(p)), addpt(pos, screen->r.min)); + zoomdraw(screen, r, ZR, i, i->r.min, zoom); + } + gendrawdiff(screen, screen->r, r, display->white, ZP, nil, ZP, S); + border(screen, r, -Borderwidth, display->black, ZP); + flushimage(display, 1); +} + +void +translate(Page *p, Point d) +{ + Rectangle r, or, nr; + Image *i; + + i = p->image; + if((i==0) || (d.x==0 && d.y==0)) + return; + r = rectaddpt(Rpt(ZP, pagesize(p)), addpt(pos, screen->r.min)); + pos = addpt(pos, d); + nr = rectaddpt(r, d); + or = r; + rectclip(&or, screen->r); + draw(screen, rectaddpt(or, d), screen, nil, or.min); + zoomdraw(screen, nr, rectaddpt(or, d), i, i->r.min, zoom); + gendrawdiff(screen, screen->r, nr, display->white, ZP, nil, ZP, S); + border(screen, nr, -Borderwidth, display->black, ZP); + flushimage(display, 1); +} + +Page* +findpage(char *name) +{ + Page *p; + int n; + + n = strlen(name); + /* look in current document first */ + if(current && current->up){ + for(p = current->up->down; p; p = p->next) + if(cistrncmp(p->label, name, n) == 0) + return p; + } + /* look everywhere */ + for(p = root->down; p; p = nextpage(p)) + if(cistrncmp(p->label, name, n) == 0) + return p; + return nil; +} + +Page* +pageat(int i) +{ + Page *p; + + for(p = root->down; i > 0 && p; p = nextpage(p)) + i--; + return i ? nil : p; +} + +int +pageindex(Page *x) +{ + Page *p; + int i; + + for(i = 0, p = root->down; p && p != x; p = nextpage(p)) + i++; + return (p == x) ? i : -1; +} + +char* +pagemenugen(int i) +{ + Page *p; + if(p = pageat(i)) + return p->label; + return nil; +} + +void +showpage(Page *p) +{ + static int nproc; + int oviewgen; + + if(p == nil) + return; + esetcursor(&reading); + current = p; + oviewgen = viewgen; + if(++nproc > NPROC) + if(waitpid() > 0) + nproc--; + switch(rfork(RFPROC|RFMEM)){ + case -1: + sysfatal("rfork: %r"); + case 0: + loadpages(p, NAHEAD, oviewgen); + exits(nil); + } +} + +void +zerox(Page *p) +{ + char nam[64], *argv[4]; + int fd; + + if(p == nil) + return; + esetcursor(&reading); + qlock(p); + if((fd = openpage(p)) < 0) + goto Out; + if(rfork(RFREND|RFFDG|RFPROC|RFENVG|RFNOTEG|RFNOWAIT) == 0){ + dup(fd, 0); + close(fd); + + snprint(nam, sizeof nam, "/bin/%s", argv0); + argv[0] = argv0; + argv[1] = "-w"; + argv[2] = nil; + exec(nam, argv); + sysfatal("exec: %r"); + } + close(fd); +Out: + qunlock(p); + esetcursor(nil); +} + +void +eresized(int new) +{ + Page *p; + + lockdisplay(display); + if(new && getwindow(display, Refnone) == -1) + sysfatal("getwindow: %r"); + if(p = current){ + if(canqlock(p)){ + drawpage(p); + qunlock(p); + } + } + unlockdisplay(display); +} + +void killcohort(void) +{ + int i; + for(i=0;i!=3;i++){ /* It's a long way to the kitchen */ + postnote(PNGROUP, getpid(), "kill"); + sleep(1); + } +} + +void drawerr(Display *, char *msg) +{ + sysfatal("draw: %s", msg); +} + +char* +shortname(char *s) +{ + char *x; + if(x = strrchr(s, '/')) + if(x[1] != 0) + return x+1; + return s; +} + +void +usage(void) +{ + fprint(2, "usage: %s [ -iRw ] [ -p ppi ] [ file ... ]\n", argv0); + exits("usage"); +} + +void +main(int argc, char *argv[]) +{ + enum { Eplumb = 4 }; + char jump[32]; + Plumbmsg *pm; + Point o; + Mouse m; + Event e; + char *s; + int i; + + ARGBEGIN { + case 'a': + case 'v': + case 'V': + case 'P': + break; + case 'R': + newwin = -1; + break; + case 'w': + newwin = 1; + break; + case 'i': + imode = 1; + break; + case 'p': + ppi = atoi(EARGF(usage())); + break; + default: + usage(); + } ARGEND; + + /* + * so that we can stop all subprocesses with a note, + * and to isolate rendezvous from other processes + */ + rfork(RFNOTEG|RFNAMEG|RFREND); + atexit(killcohort); + atnotify(catchnote, 1); + if(newwin > 0){ + s = smprint("-pid %d", getpid()); + if(newwindow(s) < 0) + sysfatal("newwindow: %r"); + free(s); + } + initdraw(drawerr, nil, argv0); + display->locking = 1; + unlockdisplay(display); + einit(Ekeyboard|Emouse); + eplumb(Eplumb, "image"); + nullfd = open("/dev/null", ORDWR); + current = root = addpage(nil, "root", nil, nil, -1); + + if(*argv == nil && !imode) + addpage(root, "stdin", popenfile, strdup("/fd/0"), -1); + for(; *argv; argv++) + addpage(root, shortname(*argv), popenfile, strdup(*argv), -1); + + jump[0] = 0; + for(;;){ + i=event(&e); + switch(i){ + case Emouse: + lockdisplay(display); + m = e.mouse; + if(m.buttons & 1){ + if(current == nil || !canqlock(current)) + goto Unlock; + for(;;) { + o = m.xy; + m = emouse(); + if((m.buttons & 1) == 0) + break; + translate(current, subpt(m.xy, o)); + } + qunlock(current); + goto Unlock; + } + if(m.buttons & 2){ + i = emenuhit(2, &m, &menu); + if(i < 0 || i >= nelem(menuitems) || menuitems[i]==nil) + goto Unlock; + s = menuitems[i]; + if(strcmp(s, "orig size")==0){ + pos = ZP; + zoom = 1; + resize = ZP; + rotate = 0; + Unload: + viewgen++; + unlockdisplay(display); + esetcursor(&reading); + unloadpages(0); + showpage(current); + continue; + } + if(strncmp(s, "rotate ", 7)==0){ + rotate += atoi(s+7); + rotate %= 360; + goto Unload; + } + if(strcmp(s, "upside down")==0){ + rotate += 180; + goto Unload; + } + if(strcmp(s, "fit width")==0){ + pos = ZP; + zoom = 1; + resize = subpt(screen->r.max, screen->r.min); + resize.y = 0; + goto Unload; + } + if(strcmp(s, "fit height")==0){ + pos = ZP; + zoom = 1; + resize = subpt(screen->r.max, screen->r.min); + resize.x = 0; + goto Unload; + } + if(strncmp(s, "zoom", 4)==0){ + if(current && canqlock(current)){ + o = subpt(m.xy, screen->r.min); + if(strstr(s, "in")){ + if(zoom < 0x40000000){ + zoom *= 2; + pos = addpt(mulpt(subpt(pos, o), 2), o); + } + }else{ + if(zoom > 1){ + zoom /= 2; + pos = addpt(divpt(subpt(pos, o), 2), o); + } + } + drawpage(current); + qunlock(current); + } + } + unlockdisplay(display); + if(strcmp(s, "next")==0) + showpage(nextpage(current)); + if(strcmp(s, "prev")==0) + showpage(prevpage(current)); + if(strcmp(s, "zerox")==0) + zerox(current); + if(strcmp(s, "quit")==0) + exits(0); + continue; + } + if(m.buttons & 4){ + if(root->down == nil) + goto Unlock; + pagemenu.lasthit = pageindex(current); + i = emenuhit(3, &m, &pagemenu); + unlockdisplay(display); + if(i != -1) + showpage(pageat(i)); + continue; + } + Unlock: + unlockdisplay(display); + break; + case Ekeyboard: + switch(e.kbdc){ + case 'q': + case Kdel: + case Keof: + exits(0); + case Kup: + if(current == nil || !canqlock(current)) + break; + lockdisplay(display); + if(pos.y < 0){ + translate(current, Pt(0, Dy(screen->r)/2)); + unlockdisplay(display); + qunlock(current); + continue; + } + unlockdisplay(display); + qunlock(current); + if(prevpage(current)) + pos.y = 0; + case '-': + case Kbs: + case Kleft: + showpage(prevpage(current)); + break; + case Kdown: + if(current == nil || !canqlock(current)) + break; + o = addpt(pos, pagesize(current)); + lockdisplay(display); + if(o.y > Dy(screen->r)){ + translate(current, Pt(0, -Dy(screen->r)/2)); + unlockdisplay(display); + qunlock(current); + continue; + } + unlockdisplay(display); + qunlock(current); + if(nextpage(current)) + pos.y = 0; + case '\n': + if(jump[0]){ + showpage(findpage(jump)); + jump[0] = 0; + break; + } + case ' ': + case Kright: + showpage(nextpage(current)); + break; + default: + i = strlen(jump); + if(i+1 < sizeof(jump)){ + jump[i] = e.kbdc; + jump[i+1] = 0; + } + } + break; + case Eplumb: + pm = e.v; + if(pm && pm->ndata > 0){ + int fd; + + fd = -1; + s = plumblookup(pm->attr, "action"); + if(s && strcmp(s, "quit")==0) + exits(0); + if(s && strcmp(s, "showdata")==0){ + static ulong plumbid; + + if((fd = createtmp(plumbid++, "plumb")) < 0){ + fprint(2, "plumb: createtmp: %r\n"); + goto Plumbfree; + } + s = malloc(NPATH); + if(fd2path(fd, s, NPATH) < 0){ + close(fd); + goto Plumbfree; + } + write(fd, pm->data, pm->ndata); + }else if(pm->data[0] == '/'){ + s = strdup(pm->data); + }else{ + s = malloc(strlen(pm->wdir)+1+pm->ndata+1); + sprint(s, "%s/%s", pm->wdir, pm->data); + cleanname(s); + } + showpage(addpage(root, shortname(s), popenfile, s, fd)); + } + Plumbfree: + plumbfree(pm); + break; + } + } +} |