Face* nextface(void) { int i; Face *f; Plumbmsg *m; char *t, *senderp, *showmailp, *digestp; ulong xtime; f = emalloc(sizeof(Face)); for(;;){ if(seefd >= 0){ m = plumbrecv(seefd); if(m == nil) killall("error on seemail plumb port"); t = value(m->attr, "mailtype", ""); if(strcmp(t, "delete") == 0) delete(m->data, value(m->attr, "digest", nil)); else if(strcmp(t, "new") != 0) fprint(2, "faces: unknown plumb message type %s\n", t); else for(i=0; i<nmaildirs; i++) if(strncmp(m->data, maildirs[i], strlen(maildirs[i])) == 0) goto Found; plumbfree(m); continue; Found: xtime = parsedate(value(m->attr, "date", date)); digestp = value(m->attr, "digest", nil); if(alreadyseen(digestp)){ /* duplicate upas/fs can send duplicate messages */ plumbfree(m); continue; } senderp = estrdup(value(m->attr, "sender", "???")); showmailp = estrdup(m->data); if(digestp) digestp = estrdup(digestp); plumbfree(m); }else{ if(logrecv(&senderp, &xtime) <= 0) killall("error reading log file"); showmailp = estrdup(""); digestp = nil; } setname(f, senderp); f->time = xtime; f->tm = *localtime(xtime); f->str[Sshow] = showmailp; f->str[Sdigest] = digestp; return f; } }
void plumb(Message *m, char *dir) { int i; char *port; Plumbmsg *pm; if(strlen(m->type) == 0) return; i = plumbport(m->type, m->filename); if(i < 0) fprint(2, "can't find destination for message subpart\n"); else{ port = ports[i].port; pm = emalloc(sizeof(Plumbmsg)); pm->src = estrdup("Mail"); if(port) pm->dst = estrdup(port); else pm->dst = nil; pm->wdir = nil; pm->type = estrdup("text"); pm->ndata = -1; pm->data = estrstrdup(dir, "body"); pm->data = eappend(pm->data, "", ports[i].suffix); if(plumbsend(plumbsendfd, pm) < 0) fprint(2, "error writing plumb message: %r\n"); plumbfree(pm); } }
int plumbformat(int i) { Plumbmsg *m; char *addr, *data, *act; int n; data = (char*)plumbbuf[i].data; m = plumbunpack(data, plumbbuf[i].n); if(m == nil) return 0; n = m->ndata; if(n == 0){ plumbfree(m); return 0; } act = plumblookup(m->attr, "action"); if(act!=nil && strcmp(act, "showfile")!=0){ /* can't handle other cases yet */ plumbfree(m); return 0; } addr = plumblookup(m->attr, "addr"); if(addr){ if(addr[0] == '\0') addr = nil; else addr = strdup(addr); /* copy to safe storage; we'll overwrite data */ } memmove(data, "B ", 2); /* we know there's enough room for this */ memmove(data+2, m->data, n); n += 2; if(data[n-1] != '\n') data[n++] = '\n'; if(addr != nil){ if(n+strlen(addr)+1+1 <= READBUFSIZE) n += sprint(data+n, "%s\n", addr); free(addr); } plumbbuf[i].n = n; plumbfree(m); return 1; }
void plumbsendthread(void *v) { Plumbmsg *m; USED(v); threadsetname("plumbsendthread"); while((m = recvp(cplumbsend)) != nil){ mkreply(nil, "Mail", m->data, m->attr, nil); plumbfree(m); } threadexits(nil); }
void plumbshowthread(void *v) { Plumbmsg *m; USED(v); threadsetname("plumbshowthread"); while((m = recvp(cplumbshow)) != nil){ showmesg(m->data, plumblookup(m->attr, "digest")); plumbfree(m); } threadexits(nil); }
static void plumbwebthread(void*) { char *base; Plumbmsg *m; for(;;){ m = recvp(plumbchan); if(m == nil) threadexits(nil); base = plumblookup(m->attr, "baseurl"); if(base == nil) base = m->wdir; plumburl(m->data, base); plumbfree(m); } }
void main(int argc, char *argv[]) { Plumbmsg *m; int fd; fd = plumbopen("audioplay", OREAD); if (fd < 0) sysfatal("port audioplay: %r"); for (;;) { m = plumbrecv(fd); if (m == nil) sysfatal("plumrecv: %r"); plumbfree(m); } }
int replytoaddr(Window *w, Message *m, Event *e, char *s) { int did; char *buf; Plumbmsg *pm; buf = nil; did = 0; if(e->flag & 2){ /* autoexpanded; use our own bigger expansion */ buf = expandaddr(w, e); if(buf == nil) return 0; s = buf; } if(isemail(s)){ did = 1; pm = emalloc(sizeof(Plumbmsg)); pm->src = estrdup("Mail"); pm->dst = estrdup("sendmail"); pm->data = estrdup(s); pm->ndata = -1; if(m->subject && m->subject[0]){ pm->attr = emalloc(sizeof(Plumbattr)); pm->attr->name = estrdup("Subject"); if(tolower(m->subject[0]) != 'r' || tolower(m->subject[1]) != 'e' || m->subject[2] != ':') pm->attr->value = estrstrdup("Re: ", m->subject); else pm->attr->value = estrdup(m->subject); pm->attr->next = nil; } if(plumbsend(plumbsendfd, pm) < 0) fprint(2, "error writing plumb message: %r\n"); plumbfree(pm); } free(buf); return did; }
void plumbthread(void) { Plumbmsg *m; Plumbattr *a; char *type, *digest; threadsetname("plumbthread"); while((m = recvp(cplumb)) != nil){ a = m->attr; digest = plumblookup(a, "digest"); type = plumblookup(a, "mailtype"); if(type == nil) fprint(2, "Mail: plumb message with no mailtype attribute\n"); else if(strcmp(type, "new") == 0) newmesg(m->data, digest); else if(strcmp(type, "delete") == 0) delmesg(m->data, digest, 0, nil); else fprint(2, "Mail: unknown plumb attribute %s\n", type); plumbfree(m); } threadexits(nil); }
int plumbrunestr(Runestr *rs, char *attr) { Plumbmsg *m; int i; i = -1; if(plumbsendfd >= 0) { m = emalloc(sizeof(Plumbmsg)); m->src = estrdup("abaco"); m->dst = nil; m->wdir = estrdup("/tmp"); m->type = estrdup("text"); if(attr) m->attr = plumbunpackattr(attr); else m->attr = nil; m->data = smprint("%.*S", rs->nr, rs->r); m->ndata = -1; i = plumbsend(plumbsendfd, m); plumbfree(m); } return i; }
int inmesg(Tmesg type) { Rune buf[1025]; char cbuf[64]; int i, m; short s; long l, l1; vlong v; File *f; Posn p0, p1, p; Range r; String *str; char *c, *wdir; Rune *rp; Plumbmsg *pm; if(type > TMAX) panic("inmesg"); journal(0, tname[type]); inp = indata; switch(type){ case -1: panic("rcv error"); default: fprint(2, "unknown type %d\n", type); panic("rcv unknown"); case Tversion: tversion = inshort(); journaln(0, tversion); break; case Tstartcmdfile: v = invlong(); /* for 64-bit pointers */ journaln(0, v); Strdupl(&genstr, samname); cmd = newfile(); cmd->unread = 0; outTsv(Hbindname, cmd->tag, v); outTs(Hcurrent, cmd->tag); logsetname(cmd, &genstr); cmd->rasp = listalloc('P'); cmd->mod = 0; if(cmdstr.n){ loginsert(cmd, 0L, cmdstr.s, cmdstr.n); Strdelete(&cmdstr, 0L, (Posn)cmdstr.n); } fileupdate(cmd, FALSE, TRUE); outT0(Hunlock); break; case Tcheck: /* go through whichfile to check the tag */ outTs(Hcheck, whichfile(inshort())->tag); break; case Trequest: f = whichfile(inshort()); p0 = inlong(); p1 = p0+inshort(); journaln(0, p0); journaln(0, p1-p0); if(f->unread) panic("Trequest: unread"); if(p1>f->b.nc) p1 = f->b.nc; if(p0>f->b.nc) /* can happen e.g. scrolling during command */ p0 = f->b.nc; if(p0 == p1){ i = 0; r.p1 = r.p2 = p0; }else{ r = rdata(f->rasp, p0, p1-p0); i = r.p2-r.p1; bufread(&f->b, r.p1, buf, i); } buf[i]=0; outTslS(Hdata, f->tag, r.p1, tmprstr(buf, i+1)); break; case Torigin: s = inshort(); l = inlong(); l1 = inlong(); journaln(0, l1); lookorigin(whichfile(s), l, l1); break; case Tstartfile: termlocked++; f = whichfile(inshort()); if(!f->rasp) /* this might be a duplicate message */ f->rasp = listalloc('P'); current(f); outTsv(Hbindname, f->tag, invlong()); /* for 64-bit pointers */ outTs(Hcurrent, f->tag); journaln(0, f->tag); if(f->unread) load(f); else{ if(f->b.nc>0){ rgrow(f->rasp, 0L, f->b.nc); outTsll(Hgrow, f->tag, 0L, f->b.nc); } outTs(Hcheck0, f->tag); moveto(f, f->dot.r); } break; case Tworkfile: i = inshort(); f = whichfile(i); current(f); f->dot.r.p1 = inlong(); f->dot.r.p2 = inlong(); f->tdot = f->dot.r; journaln(0, i); journaln(0, f->dot.r.p1); journaln(0, f->dot.r.p2); break; case Ttype: f = whichfile(inshort()); p0 = inlong(); journaln(0, p0); journal(0, (char*)inp); str = tmpcstr((char*)inp); i = str->n; loginsert(f, p0, str->s, str->n); if(fileupdate(f, FALSE, FALSE)) seq++; if(f==cmd && p0==f->b.nc-i && i>0 && str->s[i-1]=='\n'){ freetmpstr(str); termlocked++; termcommand(); }else freetmpstr(str); f->dot.r.p1 = f->dot.r.p2 = p0+i; /* terminal knows this already */ f->tdot = f->dot.r; break; case Tcut: f = whichfile(inshort()); p0 = inlong(); p1 = inlong(); journaln(0, p0); journaln(0, p1); logdelete(f, p0, p1); if(fileupdate(f, FALSE, FALSE)) seq++; f->dot.r.p1 = f->dot.r.p2 = p0; f->tdot = f->dot.r; /* terminal knows the value of dot already */ break; case Tpaste: f = whichfile(inshort()); p0 = inlong(); journaln(0, p0); for(l=0; l<snarfbuf.nc; l+=m){ m = snarfbuf.nc-l; if(m>BLOCKSIZE) m = BLOCKSIZE; bufread(&snarfbuf, l, genbuf, m); loginsert(f, p0, tmprstr(genbuf, m)->s, m); } if(fileupdate(f, FALSE, TRUE)) seq++; f->dot.r.p1 = p0; f->dot.r.p2 = p0+snarfbuf.nc; f->tdot.p1 = -1; /* force telldot to tell (arguably a BUG) */ telldot(f); outTs(Hunlockfile, f->tag); break; case Tsnarf: i = inshort(); p0 = inlong(); p1 = inlong(); snarf(whichfile(i), p0, p1, &snarfbuf, 0); break; case Tstartnewfile: v = invlong(); Strdupl(&genstr, empty); f = newfile(); f->rasp = listalloc('P'); outTsv(Hbindname, f->tag, v); logsetname(f, &genstr); outTs(Hcurrent, f->tag); current(f); load(f); break; case Twrite: termlocked++; i = inshort(); journaln(0, i); f = whichfile(i); addr.r.p1 = 0; addr.r.p2 = f->b.nc; if(f->name.s[0] == 0) error(Enoname); Strduplstr(&genstr, &f->name); writef(f); break; case Tclose: termlocked++; i = inshort(); journaln(0, i); f = whichfile(i); current(f); trytoclose(f); /* if trytoclose fails, will error out */ delete(f); break; case Tlook: f = whichfile(inshort()); termlocked++; p0 = inlong(); p1 = inlong(); journaln(0, p0); journaln(0, p1); setgenstr(f, p0, p1); for(l = 0; l<genstr.n; l++){ i = genstr.s[l]; if(utfrune(".*+?(|)\\[]^$", i)){ str = tmpcstr("\\"); Strinsert(&genstr, str, l++); freetmpstr(str); } } Straddc(&genstr, '\0'); nextmatch(f, &genstr, p1, 1); moveto(f, sel.p[0]); break; case Tsearch: termlocked++; if(curfile == 0) error(Enofile); if(lastpat.s[0] == 0) panic("Tsearch"); nextmatch(curfile, &lastpat, curfile->dot.r.p2, 1); moveto(curfile, sel.p[0]); break; case Tsend: termlocked++; inshort(); /* ignored */ p0 = inlong(); p1 = inlong(); setgenstr(cmd, p0, p1); bufreset(&snarfbuf); bufinsert(&snarfbuf, (Posn)0, genstr.s, genstr.n); outTl(Hsnarflen, genstr.n); if(genstr.s[genstr.n-1] != '\n') Straddc(&genstr, '\n'); loginsert(cmd, cmd->b.nc, genstr.s, genstr.n); fileupdate(cmd, FALSE, TRUE); cmd->dot.r.p1 = cmd->dot.r.p2 = cmd->b.nc; telldot(cmd); termcommand(); break; case Tdclick: f = whichfile(inshort()); p1 = inlong(); doubleclick(f, p1); f->tdot.p1 = f->tdot.p2 = p1; telldot(f); outTs(Hunlockfile, f->tag); break; case Tstartsnarf: if (snarfbuf.nc <= 0) { /* nothing to export */ outTs(Hsetsnarf, 0); break; } c = 0; i = 0; m = snarfbuf.nc; if(m > SNARFSIZE) { m = SNARFSIZE; dprint("?warning: snarf buffer truncated\n"); } rp = malloc(m*sizeof(Rune)); if(rp){ bufread(&snarfbuf, 0, rp, m); c = Strtoc(tmprstr(rp, m)); free(rp); i = strlen(c); } outTs(Hsetsnarf, i); if(c){ Write(1, c, i); free(c); } else dprint("snarf buffer too long\n"); break; case Tsetsnarf: m = inshort(); if(m > SNARFSIZE) error(Etoolong); c = malloc(m+1); if(c){ for(i=0; i<m; i++) c[i] = rcvchar(); c[m] = 0; str = tmpcstr(c); free(c); bufreset(&snarfbuf); bufinsert(&snarfbuf, (Posn)0, str->s, str->n); freetmpstr(str); outT0(Hunlock); } break; case Tack: waitack = 0; break; case Tplumb: f = whichfile(inshort()); p0 = inlong(); p1 = inlong(); pm = emalloc(sizeof(Plumbmsg)); pm->src = strdup("sam"); pm->dst = 0; /* construct current directory */ c = Strtoc(&f->name); if(c[0] == '/') pm->wdir = c; else{ wdir = emalloc(1024); getwd(wdir, 1024); pm->wdir = emalloc(1024); snprint(pm->wdir, 1024, "%s/%s", wdir, c); cleanname(pm->wdir); free(wdir); free(c); } c = strrchr(pm->wdir, '/'); if(c) *c = '\0'; pm->type = strdup("text"); if(p1 > p0) pm->attr = nil; else{ p = p0; while(p0>0 && (i=filereadc(f, p0 - 1))!=' ' && i!='\t' && i!='\n') p0--; while(p1<f->b.nc && (i=filereadc(f, p1))!=' ' && i!='\t' && i!='\n') p1++; sprint(cbuf, "click=%ld", p-p0); pm->attr = plumbunpackattr(cbuf); } if(p0==p1 || p1-p0>=BLOCKSIZE){ plumbfree(pm); break; } setgenstr(f, p0, p1); pm->data = Strtoc(&genstr); pm->ndata = strlen(pm->data); c = plumbpack(pm, &i); if(c != 0){ outTs(Hplumb, i); Write(1, c, i); free(c); } plumbfree(pm); break; case Texit: exits(0); } return TRUE; }
void mousethread(void *v) { Text *t, *argt; int but; uint q0, q1; Window *w; Plumbmsg *pm; Mouse m; char *act; enum { MResize, MMouse, MPlumb, MWarnings, NMALT }; static Alt alts[NMALT+1]; USED(v); threadsetname("mousethread"); alts[MResize].c = mousectl->resizec; alts[MResize].v = nil; alts[MResize].op = CHANRCV; alts[MMouse].c = mousectl->c; alts[MMouse].v = &mousectl->m; alts[MMouse].op = CHANRCV; alts[MPlumb].c = cplumb; alts[MPlumb].v = ± alts[MPlumb].op = CHANRCV; alts[MWarnings].c = cwarn; alts[MWarnings].v = nil; alts[MWarnings].op = CHANRCV; if(cplumb == nil) alts[MPlumb].op = CHANNOP; alts[NMALT].op = CHANEND; for(;;){ qlock(&row.lk); flushwarnings(); qunlock(&row.lk); flushimage(display, 1); switch(alt(alts)){ case MResize: if(getwindow(display, Refnone) < 0) error("attach to window"); draw(screen, screen->r, display->white, nil, ZP); iconinit(); scrlresize(); rowresize(&row, screen->clipr); break; case MPlumb: if(strcmp(pm->type, "text") == 0){ act = plumblookup(pm->attr, "action"); if(act==nil || strcmp(act, "showfile")==0) plumblook(pm); else if(strcmp(act, "showdata")==0) plumbshow(pm); } plumbfree(pm); break; case MWarnings: break; case MMouse: /* * Make a copy so decisions are consistent; mousectl changes * underfoot. Can't just receive into m because this introduces * another race; see /sys/src/libdraw/mouse.c. */ m = mousectl->m; qlock(&row.lk); t = rowwhich(&row, m.xy); if(t!=mousetext && mousetext!=nil && mousetext->w!=nil){ winlock(mousetext->w, 'M'); mousetext->eq0 = ~0; wincommit(mousetext->w, mousetext); winunlock(mousetext->w); } mousetext = t; if(t == nil) goto Continue; w = t->w; if(t==nil || m.buttons==0) goto Continue; but = 0; if(m.buttons == 1) but = 1; else if(m.buttons == 2) but = 2; else if(m.buttons == 4) but = 3; barttext = t; if(t->what==Body && ptinrect(m.xy, t->scrollr)){ if(but){ if(swapscrollbuttons){ if(but == 1) but = 3; else if(but == 3) but = 1; } winlock(w, 'M'); t->eq0 = ~0; textscroll(t, but); winunlock(w); } goto Continue; } /* scroll buttons, wheels, etc. */ if(w != nil && (m.buttons & (8|16))){ if(m.buttons & 8) but = Kscrolloneup; else but = Kscrollonedown; winlock(w, 'M'); t->eq0 = ~0; texttype(t, but); winunlock(w); goto Continue; } if(ptinrect(m.xy, t->scrollr)){ if(but){ if(t->what == Columntag) rowdragcol(&row, t->col, but); else if(t->what == Tag){ coldragwin(t->col, t->w, but); if(t->w) barttext = &t->w->body; } if(t->col) activecol = t->col; } goto Continue; } if(m.buttons){ if(w) winlock(w, 'M'); t->eq0 = ~0; if(w) wincommit(w, t); else textcommit(t, TRUE); if(m.buttons & 1){ textselect(t); if(w) winsettag(w); argtext = t; seltext = t; if(t->col) activecol = t->col; /* button 1 only */ if(t->w!=nil && t==&t->w->body) activewin = t->w; }else if(m.buttons & 2){ if(textselect2(t, &q0, &q1, &argt)) execute(t, q0, q1, FALSE, argt); }else if(m.buttons & 4){ if(textselect3(t, &q0, &q1)) look3(t, q0, q1, FALSE); } if(w) winunlock(w); goto Continue; } Continue: qunlock(&row.lk); break; } } }
void look3(Text *t, uint q0, uint q1, int external) { int n, c, f, expanded; Text *ct; Expand e; Rune *r; uint p; Plumbmsg *m; Runestr dir; char buf[32]; ct = seltext; if(ct == nil) seltext = t; expanded = expand(t, q0, q1, &e); if(!external && t->w!=nil && t->w->nopen[QWevent]>0){ /* send alphanumeric expansion to external client */ if(expanded == FALSE) return; f = 0; if((e.u.at!=nil && t->w!=nil) || (e.nname>0 && lookfile(e.name, e.nname)!=nil)) f = 1; /* acme can do it without loading a file */ if(q0!=e.q0 || q1!=e.q1) f |= 2; /* second (post-expand) message follows */ if(e.nname) f |= 4; /* it's a file name */ c = 'l'; if(t->what == Body) c = 'L'; n = q1-q0; if(n <= EVENTSIZE){ r = runemalloc(n); bufread(&t->file->b, q0, r, n); winevent(t->w, "%c%d %d %d %d %.*S\n", c, q0, q1, f, n, n, r); free(r); }else winevent(t->w, "%c%d %d %d 0 \n", c, q0, q1, f, n); if(q0==e.q0 && q1==e.q1) return; if(e.nname){ n = e.nname; if(e.a1 > e.a0) n += 1+(e.a1-e.a0); r = runemalloc(n); runemove(r, e.name, e.nname); if(e.a1 > e.a0){ r[e.nname] = ':'; bufread(&e.u.at->file->b, e.a0, r+e.nname+1, e.a1-e.a0); } }else{ n = e.q1 - e.q0; r = runemalloc(n); bufread(&t->file->b, e.q0, r, n); } f &= ~2; if(n <= EVENTSIZE) winevent(t->w, "%c%d %d %d %d %.*S\n", c, e.q0, e.q1, f, n, n, r); else winevent(t->w, "%c%d %d %d 0 \n", c, e.q0, e.q1, f, n); free(r); goto Return; } if(plumbsendfid != nil){ /* send whitespace-delimited word to plumber */ m = emalloc(sizeof(Plumbmsg)); m->src = estrdup("acme"); m->dst = nil; dir = dirname(t, nil, 0); if(dir.nr==1 && dir.r[0]=='.'){ /* sigh */ free(dir.r); dir.r = nil; dir.nr = 0; } if(dir.nr == 0) m->wdir = estrdup(wdir); else m->wdir = runetobyte(dir.r, dir.nr); free(dir.r); m->type = estrdup("text"); m->attr = nil; buf[0] = '\0'; if(q1 == q0){ if(t->q1>t->q0 && t->q0<=q0 && q0<=t->q1){ q0 = t->q0; q1 = t->q1; }else{ p = q0; while(q0>0 && (c=tgetc(t, q0-1))!=' ' && c!='\t' && c!='\n') q0--; while(q1<t->file->b.nc && (c=tgetc(t, q1))!=' ' && c!='\t' && c!='\n') q1++; if(q1 == q0){ plumbfree(m); goto Return; } sprint(buf, "click=%d", p-q0); m->attr = plumbunpackattr(buf); } } r = runemalloc(q1-q0); bufread(&t->file->b, q0, r, q1-q0); m->data = runetobyte(r, q1-q0); m->ndata = strlen(m->data); free(r); if(m->ndata<messagesize-1024 && plumbsendtofid(plumbsendfid, m) >= 0){ plumbfree(m); goto Return; } plumbfree(m); /* plumber failed to match; fall through */ } /* interpret alphanumeric string ourselves */ if(expanded == FALSE) return; if(e.name || e.u.at) openfile(t, &e); else{ if(t->w == nil) return; ct = &t->w->body; if(t->w != ct->w) winlock(ct->w, 'M'); if(t == ct) textsetselect(ct, e.q1, e.q1); n = e.q1 - e.q0; r = runemalloc(n); bufread(&t->file->b, e.q0, r, n); if(search(ct, r, n) && e.jump) moveto(mousectl, addpt(frptofchar(&ct->fr, ct->fr.p0), Pt(4, ct->fr.font->height-4))); if(t->w != ct->w) winunlock(ct->w); free(r); } Return: free(e.name); free(e.bname); }
void mousethread(void *) { Plumbmsg *pm; Mouse m; Text *t; int but; enum { MResize, MMouse, MPlumb, MRefresh, NMALT }; static Alt alts[NMALT+1]; threadsetname("mousethread"); alts[MResize].c = mousectl->resizec; alts[MResize].v = nil; alts[MResize].op = CHANRCV; alts[MMouse].c = mousectl->c; alts[MMouse].v = &mousectl->Mouse; alts[MMouse].op = CHANRCV; alts[MPlumb].c = cplumb; alts[MPlumb].v = ± alts[MPlumb].op = CHANRCV; alts[MRefresh].c = crefresh; alts[MRefresh].v = nil; alts[MRefresh].op = CHANRCV; if(cplumb == nil) alts[MPlumb].op = CHANNOP; alts[NMALT].op = CHANEND; for(;;){ qlock(&row); flushrefresh(); qunlock(&row); flushimage(display, 1); switch(alt(alts)){ case MResize: if(getwindow(display, Refnone) < 0) error("resized"); scrlresize(); tmpresize(); rowresize(&row, screen->clipr); break; case MPlumb: plumblook(pm); plumbfree(pm); break; case MRefresh: break; case MMouse: m = mousectl->Mouse; if(m.buttons == 0) continue; qlock(&row); but = 0; if(m.buttons == 1) but = 1; else if(m.buttons == 2) but = 2; else if(m.buttons == 4) but = 3; if(m.buttons & (8|16)){ if(m.buttons & 8) but = Kscrolloneup; else but = Kscrollonedown; rowwhich(&row, m.xy, but, TRUE); }else if(but){ t = rowwhich(&row, m.xy, but, FALSE); if(t) textmouse(t, m.xy, but); } qunlock(&row); break; } } }
void viewer(Document *dd) { int i, fd, n, oldpage; int nxt; Menu menu, midmenu; Mouse m; Event e; Point dxy, oxy, xy0; Image *tmp; static char *fwditems[] = { "this page", "next page", "exit", 0 }; static char *miditems[] = { "orig size", "zoom in", "fit window", "rotate 90", "upside down", "", "next", "prev", "zerox", "", "reverse", "discard", "write", "", "quit", 0 }; char *s; enum { Eplumb = 4 }; Plumbmsg *pm; doc = dd; /* save global for menuhit */ ul = screen->r.min; einit(Emouse|Ekeyboard); if(doc->addpage != nil) eplumb(Eplumb, "image"); esetcursor(&reading); /* * im is a global pointer to the current image. * eventually, i think we will have a layer between * the display routines and the ps/pdf/whatever routines * to perhaps cache and handle images of different * sizes, etc. */ im = 0; page = reverse ? doc->npage-1 : 0; if(doc->fwdonly) { menu.item = fwditems; menu.gen = 0; menu.lasthit = 0; } else { menu.item = 0; menu.gen = menugen; menu.lasthit = 0; } midmenu.item = miditems; midmenu.gen = 0; midmenu.lasthit = Next; if(doc->docname != nil) setlabel(doc->docname); showpage(page, &menu); esetcursor(nil); nxt = 0; for(;;) { /* * throughout, if doc->fwdonly is set, we restrict the functionality * a fair amount. we don't care about doc->npage anymore, and * all that can be done is select the next page. */ unlockdisplay(display); i = eread(Emouse|Ekeyboard|Eplumb, &e); lockdisplay(display); switch(i){ case Ekeyboard: if(e.kbdc <= 0xFF && isdigit(e.kbdc)) { nxt = nxt*10+e.kbdc-'0'; break; } else if(e.kbdc != '\n') nxt = 0; switch(e.kbdc) { case 'r': /* reverse page order */ if(doc->fwdonly) break; reverse = !reverse; menu.lasthit = doc->npage-1-menu.lasthit; /* * the theory is that if we are reversing the * document order and are on the first or last * page then we're just starting and really want * to view the other end. maybe the if * should be dropped and this should happen always. */ if(page == 0 || page == doc->npage-1) { page = doc->npage-1-page; showpage(page, &menu); } break; case 'w': /* write bitmap of current screen */ esetcursor(&reading); s = writebitmap(); if(s) string(screen, addpt(screen->r.min, Pt(5,5)), display->black, ZP, display->defaultfont, s); esetcursor(nil); flushimage(display, 1); break; case 'd': /* remove image from working set */ if(doc->rmpage && page < doc->npage) { if(doc->rmpage(doc, page) >= 0) { if(doc->npage < 0) wexits(0); if(page >= doc->npage) page = doc->npage-1; showpage(page, &menu); } } break; case 'q': case 0x04: /* ctrl-d */ wexits(0); case 'u': if(im==nil) break; angle = (angle+180) % 360; showpage(page, &menu); break; case '-': case '\b': case Kleft: if(page > 0 && !doc->fwdonly) { --page; showpage(page, &menu); } break; case '\n': if(nxt) { nxt--; if(nxt >= 0 && nxt < doc->npage && !doc->fwdonly) showpage(page=nxt, &menu); nxt = 0; break; } goto Gotonext; case Kright: case ' ': Gotonext: if(doc->npage && ++page >= doc->npage && !doc->fwdonly) wexits(0); showpage(page, &menu); break; /* * The upper y coordinate of the image is at ul.y in screen->r. * Panning up means moving the upper left corner down. If the * upper left corner is currently visible, we need to go back a page. */ case Kup: if(screen->r.min.y <= ul.y && ul.y < screen->r.max.y){ if(page > 0 && !doc->fwdonly){ --page; showbottom = 1; showpage(page, &menu); } } else { i = Dy(screen->r)/2; if(i > 10) i -= 10; if(i+ul.y > screen->r.min.y) i = screen->r.min.y - ul.y; translate(Pt(0, i)); } break; /* * If the lower y coordinate is on the screen, we go to the next page. * The lower y coordinate is at ul.y + Dy(im->r). */ case Kdown: i = ul.y + Dy(im->r); if(screen->r.min.y <= i && i <= screen->r.max.y){ ul.y = screen->r.min.y; goto Gotonext; } else { i = -Dy(screen->r)/2; if(i < -10) i += 10; if(i+ul.y+Dy(im->r) <= screen->r.max.y) i = screen->r.max.y - Dy(im->r) - ul.y - 1; translate(Pt(0, i)); } break; default: esetcursor(&query); sleep(1000); esetcursor(nil); break; } break; case Emouse: m = e.mouse; switch(m.buttons){ case Left: oxy = m.xy; xy0 = oxy; do { dxy = subpt(m.xy, oxy); oxy = m.xy; translate(dxy); unlockdisplay(display); m = emouse(); lockdisplay(display); } while(m.buttons == Left); if(m.buttons) { dxy = subpt(xy0, oxy); translate(dxy); } break; case Middle: if(doc->npage == 0) break; unlockdisplay(display); n = emenuhit(Middle, &m, &midmenu); lockdisplay(display); if(n == -1) break; switch(n){ case Next: /* next */ if(reverse) page--; else page++; if(page < 0) { if(reverse) return; else page = 0; } if((page >= doc->npage) && !doc->fwdonly) return; showpage(page, &menu); nxt = 0; break; case Prev: /* prev */ if(reverse) page++; else page--; if(page < 0) { if(reverse) return; else page = 0; } if((page >= doc->npage) && !doc->fwdonly && !reverse) return; showpage(page, &menu); nxt = 0; break; case Zerox: /* prev */ zerox(); break; case Zin: /* zoom in */ { double delta; Rectangle r; r = egetrect(Middle, &m); if((rectclip(&r, rectaddpt(im->r, ul)) == 0) || Dx(r) == 0 || Dy(r) == 0) break; /* use the smaller side to expand */ if(Dx(r) < Dy(r)) delta = (double)Dx(im->r)/(double)Dx(r); else delta = (double)Dy(im->r)/(double)Dy(r); esetcursor(&reading); tmp = xallocimage(display, Rect(0, 0, (int)((double)Dx(im->r)*delta), (int)((double)Dy(im->r)*delta)), im->chan, 0, DBlack); if(tmp == nil) { fprint(2, "out of memory during zoom: %r\n"); wexits("memory"); } resample(im, tmp); im = tmp; delayfreeimage(tmp); esetcursor(nil); ul = screen->r.min; redraw(screen); flushimage(display, 1); break; } case Fit: /* fit */ { double delta; Rectangle r; delta = (double)Dx(screen->r)/(double)Dx(im->r); if((double)Dy(im->r)*delta > Dy(screen->r)) delta = (double)Dy(screen->r)/(double)Dy(im->r); r = Rect(0, 0, (int)((double)Dx(im->r)*delta), (int)((double)Dy(im->r)*delta)); esetcursor(&reading); tmp = xallocimage(display, r, im->chan, 0, DBlack); if(tmp == nil) { fprint(2, "out of memory during fit: %r\n"); wexits("memory"); } resample(im, tmp); im = tmp; delayfreeimage(tmp); esetcursor(nil); ul = screen->r.min; redraw(screen); flushimage(display, 1); break; } case Rot: /* rotate 90 */ angle = (angle+90) % 360; showpage(page, &menu); break; case Upside: /* upside-down */ angle = (angle+180) % 360; showpage(page, &menu); break; case Restore: /* restore */ showpage(page, &menu); break; case Reverse: /* reverse */ if(doc->fwdonly) break; reverse = !reverse; menu.lasthit = doc->npage-1-menu.lasthit; if(page == 0 || page == doc->npage-1) { page = doc->npage-1-page; showpage(page, &menu); } break; case Write: /* write */ esetcursor(&reading); s = writebitmap(); if(s) string(screen, addpt(screen->r.min, Pt(5,5)), display->black, ZP, display->defaultfont, s); esetcursor(nil); flushimage(display, 1); break; case Del: /* delete */ if(doc->rmpage && page < doc->npage) { if(doc->rmpage(doc, page) >= 0) { if(doc->npage < 0) wexits(0); if(page >= doc->npage) page = doc->npage-1; showpage(page, &menu); } } break; case Exit: /* exit */ return; case Empty1: case Empty2: case Empty3: break; }; case Right: if(doc->npage == 0) break; oldpage = page; unlockdisplay(display); n = emenuhit(RMenu, &m, &menu); lockdisplay(display); if(n == -1) break; if(doc->fwdonly) { switch(n){ case 0: /* this page */ break; case 1: /* next page */ showpage(++page, &menu); break; case 2: /* exit */ return; } break; } if(n == doc->npage) return; else page = reverse ? doc->npage-1-n : n; if(oldpage != page) showpage(page, &menu); nxt = 0; break; } break; case Eplumb: pm = e.v; if(pm->ndata <= 0){ plumbfree(pm); break; } if(plumbquit(pm)) exits(nil); if(showdata(pm)) { s = estrdup("/tmp/pageplumbXXXXXXX"); fd = opentemp(s); write(fd, pm->data, pm->ndata); /* lose fd reference on purpose; the file is open ORCLOSE */ } else if(pm->data[0] == '/') { s = estrdup(pm->data); } else { s = emalloc(strlen(pm->wdir)+1+pm->ndata+1); sprint(s, "%s/%s", pm->wdir, pm->data); cleanname(s); } if((i = doc->addpage(doc, s)) >= 0) { page = i; unhide(); showpage(page, &menu); } free(s); plumbfree(pm); break; } } }