#include #include #include #include #include #include /* for support routines only */ #include #include "faces.h" enum { Facesep = 6, /* must be even to avoid damaging background stipple */ HhmmTime = 18*60*60, /* max age of face to display hh:mm time */ }; enum { Mainp, Timep, Mousep, Kbdp, Infop, NPROC }; int pids[NPROC]; char *procnames[] = { "main", "time", "mouse", "info", "keyboard", }; Rectangle updown = {0, 0, 16, 16}; uchar updata[] = { 0x01, 0x80, 0x03, 0xc0, 0x07, 0xe0, 0x0f, 0xf0, 0x1f, 0xf8, 0x3f, 0xfc, 0x7f, 0xfe, 0xff, 0xff, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0 }; uchar downdata[] = { 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0xff, 0xff, 0x7f, 0xfe, 0x3f, 0xfc, 0x1f, 0xf8, 0x0f, 0xf0, 0x07, 0xe0, 0x03, 0xc0, 0x01, 0x80 }; Image *blue; /* full arrow */ Image *bgrnd; /* background color */ Image *red; /* red mask */ Image *up; /* up-pointing arrow mask */ Image *down; /* down-pointing arrow mask */ Font *mediumfont; Font *datefont; int first, last; /* first and last visible face; last is first invisible */ int nfaces; int mousefd; int kbdfd; int pfd[2]; int ndown; char date[64]; Face **faces; char *maildir = "/mail/fs/mbox"; ulong now; Point datep = { 8, 6 }; Point facep = { 8, 6+0+4 }; /* 0 updated to datefont->height in init() */ Point enddate; /* where date ends on display; used to place arrows */ Rectangle upr; /* location of up arrow on display */ Rectangle downr; /* location of down arrow on display */ Rectangle infor; /* location of information */ Rectangle delrect; /* locatation of Del text */ char selstr[128]; /* selection string */ int selected = -1; /* selected face */ Rectangle facerect(int index); char* infoget(char *, int, int, char *); void setdate(void) { Tm *tm; now = time(nil); tm=localtime(now); /* strcpy(date, smprint("%.4d-%.2d-%.2d:%.2d:%.2d", tm->year+1900, tm->mon+1, tm->mday, tm->hour, tm->min)); */ strcpy(date, smprint("%.2d:%.2d", tm->hour, tm->min)); } void init(void) { int fd; kbdfd = open("/dev/cons", ORDWR|OCEXEC); fd = open("/dev/consctl", OWRITE|OCEXEC); if(kbdfd < 0 || fd < 0 || write(fd, "rawon", 5) < 0){ fprint(2, "inbox: can't open keyboard: %r\n"); exits("keyboard"); } if(pipe(pfd) < 0){ fprint(2, "inbox: pipe: %r\n"); exits("pipe"); } mousefd = open("/dev/mouse", OREAD); if(mousefd < 0){ fprint(2, "inbox: can't open mouse: %r\n"); exits("mouse"); } initplumb(); /* make background color */ bgrnd = allocimagemix(display, 0xBBBBBBBB, DWhite); blue = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x008888FF); /* blue-green */ red = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xff00007f); up = allocimage(display, updown, GREY1, 0, DWhite); down = allocimage(display, updown, GREY1, 0, DWhite); if(bgrnd==nil || blue==nil || red == nil || up==nil || down==nil){ fprint(2, "inbox: can't create images: %r\n"); exits("image"); } loadimage(up, updown, updata, sizeof updata); loadimage(down, updown, downdata, sizeof downdata); /* initialize little fonts */ // mediumfont = openfont(display, "/lib/font/bit/dejavu/DejaVuSans/DejaVuSans.13.font"); mediumfont = openfont(display, "/lib/font/bit/lucida/unicode.7.font"); if(mediumfont == nil) mediumfont = font; datefont = mediumfont; facep.y += datefont->height; if(datefont->height & 1) /* stipple parity */ facep.y++; faces = nil; } void drawtime(void) { Rectangle r; r.min = addpt(screen->r.min, datep); if(eqpt(enddate, ZP)){ enddate = r.min; enddate.x += stringwidth(datefont, "88:88"); enddate.x += Facesep; /* for safety */ } r.max.x = enddate.x; r.max.y = enddate.y+datefont->height; draw(screen, r, bgrnd, nil, ZP); string(screen, r.min, display->black, ZP, datefont, date); } void timeproc(void) { for(;;){ lockdisplay(display); drawtime(); flushimage(display, 1); unlockdisplay(display); now = time(nil); sleep(((60 - now%60) + 1)*1000); /* wait for minute to change */ setdate(); } } int alreadyseen(char *digest) { int i; Face *f; if(!digest) return 0; /* can do accurate check */ for(i=0; istr[Sdigest]!=nil && strcmp(digest, f->str[Sdigest])==0) return 1; } return 0; } int torune(Rune *r, char *s, int nr) { int i; for(i=0; ir.min, facep); r.min.y += index*(Facesize+Facesep); r.max = addpt(r.min, Pt(Facesize, Facesize)); r.max.x += (Dx(screen->r)-facep.x+Facesep); /* simple fix to avoid drawing off screen, allowing customers to use position */ if(index<0 || index>=ndown) r.max.x = r.min.x; return r; } static char *mon = "JanFebMarAprMayJunJulAugSepOctNovDec"; char* facetime(Face *f) { static char buf[30]; sprint(buf, "%04d-%02d-%02d %02d:%02d", f->tm.year+1900, f->tm.mon, f->tm.mday, f->tm.hour, f->tm.min); return buf; } void drawface(Face *f, int i, Image *bg) { char *tstr; Rectangle r, r0; char buf[80]; if(f == nil) return; if(i=last) return; r = facerect(i-first); r0 = r; draw(screen, r, bg, nil, ZP); draw(screen, r, f->bit, f->mask, ZP); r.min.x += Facesize+Facesep; // r.min.y -= 4; /* hack; manual adjustment for the font height/ascent */ r.min.y -= (mediumfont->height - mediumfont->ascent); /* also wrong. */ stringbg(screen, r.min, display->black, ZP, mediumfont, f->str[Suser], bgrnd, ZP); r.min.y += mediumfont->height; tstr = facetime(f); stringbg(screen, r.min, display->black, ZP, mediumfont, tstr, bgrnd, ZP); r.min.y += mediumfont->height; stringbg(screen, r.min, display->black, ZP, mediumfont, infoget(buf, sizeof buf, i, "subject"), bgrnd, ZP); if(i == selected){ border(screen, insetrect(r0, -2), -2, blue, ZP); stringbg(screen, infor.min, display->black, ZP, datefont, selstr, bgrnd, ZP); } } void setlast(void) { last = first+ndown; if(last > nfaces) last = nfaces; } void drawarrows(void) { Point p; p = enddate; p.x += Facesep; if(p.x & 1) p.x++; /* align background texture */ upr = rectaddpt(updown, p); p.x += Dy(updown) + Facesep; downr = rectaddpt(updown, p); draw(screen, upr, first>0? blue : bgrnd, up, updown.min); draw(screen, downr, last=0; y--){ /* move them along */ r0 = facerect(y+0); r1 = facerect(y+1); r = r1; r.max.x = r.min.x; draw(screen, r, screen, nil, r0.min); /* copy one down from row above */ if(y != 0){ r = facerect(y-1); draw(screen, r0, screen, nil, r.min); } } ofaces = faces; faces = emalloc((nfaces+1)*sizeof(Face*)); memmove(faces+1, ofaces, nfaces*(sizeof(Face*))); free(ofaces); nfaces++; setlast(); drawarrows(); faces[0] = f; drawface(f, 0, bgrnd); selected = sel; if(selected != -1) drawface(faces[selected], selected, bgrnd); flushimage(display, 1); unlockdisplay(display); } void loadmboxfaces(char *maildir) { int dirfd; Dir *d; int i, n; dirfd = open(maildir, OREAD); if(dirfd >= 0){ chdir(maildir); while((n = dirread(dirfd, &d)) > 0){ for(i=0; ifile!=nil && f->bit!=f->file->image) freeimage(f->bit); freefacefile(f->file); for(i=0; istr[i]); free(f); } void delface(int j) { Rectangle r0, r1, r; int ny, x, y; if(j < first) first--; else if(j < last){ ny = nfaces; x = 0; for(y=j-first; ystr[Sdigest]!=nil && strcmp(digest, f->str[Sdigest]) == 0){ dodelete(i); break; } }else{ if(f->str[Sshow] && strcmp(s, f->str[Sshow]) == 0){ dodelete(i); break; } } } unlockdisplay(display); } void faceproc(void) { for(;;) addface(nextface()); } void resized(void) { int i; for(ndown=1; rectinrect(facerect(ndown), screen->r); ndown++) ; setlast(); draw(screen, screen->r, bgrnd, nil, ZP); enddate = ZP; drawtime(); for(i=0; ibuttons = 0; return 0; } n = eatomouse(m, buf, n); if(n > 0) return 1; } } enum { Clicksize = 3, /* pixels */ }; int scroll(int but, Point p) { int delta; delta = 0; lockdisplay(display); if(ptinrect(p, upr) && first>0){ if(but == 2) delta = -first; else{ delta = ndown; if(delta > first) delta = first; delta = -delta; } }else if(ptinrect(p, downr) && last nfaces-last) delta = nfaces-last; } } first += delta; last += delta; unlockdisplay(display); if(delta) eresized(0); return delta; } enum {Fields = 4}; void infodel(char **f, int) { Point p; if(selected == -1) return; p = Pt(atoi(f[2]), atoi(f[3])); if(!ptinrect(p, delrect)) return; drawface(faces[selected], selected, red); flushimage(display, 1); remove(faces[selected]->str[Sshow]); } void infoclear(void) { if(selected != -1){ draw(screen, infor, bgrnd, nil, ZP); border(screen, insetrect(facerect(selected-first), -2), -2, bgrnd, ZP); // error!? } selected = -1; } char* infoget(char *s, int l, int i, char *field) { char buf[128]; int fd, n; snprint(buf, sizeof buf, "%s/%s", faces[i]->str[Sshow], field); fd = open(buf, OREAD); if(fd < 0){ fail: snprint(s, l, "%d", i); return s; } n = read(fd, s, l-1); close(fd); if(n < 0) goto fail; s[n] = 0; return s; } void infoset(char **f) { Point p, q; char buf[80]; int i; // hasn't been set yet. if(upr.max.y == 0) return; i = atoi(f[1]); infodel(f, i); infoclear(); if(i == -1) return; infoget(buf, sizeof buf, i, "subject"); snprint(selstr, sizeof selstr, "Del | %-.80s", buf); p = addpt(Pt(0, screen->r.min.y), Pt(upr.max.x, datep.y)); p = addpt(p, Pt(datefont->height*6, 0)); delrect = Rpt(p, addpt(p, Pt(stringwidth(datefont, "Del"), datefont->height))); q = addpt(p, Pt(stringwidth(datefont, selstr), datefont->height)); infor = Rpt(p, q); stringbg(screen, p, display->black, ZP, datefont, selstr, bgrnd, ZP); border(screen, insetrect(facerect(i-first), -2), -2, blue, ZP); // error!? selected = i; } void infoproc(void) { int n; char buf[128], *f[Fields]; // close(pfd[0]); while((n = read(pfd[1], buf, sizeof buf-1)) > 0){ buf[n] = 0; if(getfields(buf, f, Fields, 0, "\001") != Fields) continue; lockdisplay(display); infoset(f); flushimage(display, 1); unlockdisplay(display); } } void infosend(int i, Point p) { char buf[50]; if(i == -1 && selected == -1) return; snprint(buf, sizeof buf, "set\001%d\001%d\001%d\n", i, p.x, p.y); write(pfd[0], buf, strlen(buf)); } void click(int button, Mouse *m) { Point p; int i; static int lasti; p = m->xy; while(m->buttons == (1<<(button-1))) getmouse(m); if(m->buttons) return; if(abs(p.x-m->xy.x)>Clicksize || abs(p.y-m->xy.y)>Clicksize) return; switch(button){ case 1: if(scroll(1, p)) break; infosend(-1, ZP); for(i=first; istr[Sshow], "/XXXvwhois")){ delface(i); flushimage(display, 1); } break; case 2: scroll(2, p); for(i=first; i 0) for(i = 0; i < n; i++) if(strchr("q", buf[i])) return; } void killall(char *s) { int i, pid; pid = getpid(); for(i=0; i= 0) killall("process died"); exits(nil); } if(index >= 0) pids[index] = pid; } void usage(void) { fprint(2, "usage: inbox [-m maildir]\n"); exits("usage"); } void main(int argc, char *argv[]) { int i; ARGBEGIN{ case 'm': addmaildir(EARGF(usage())); maildir = nil; break; default: usage(); }ARGEND if(initdraw(nil, nil, "inbox") < 0){ fprint(2, "inbox: initdraw failed: %r\n"); exits("initdraw"); } if(maildir) addmaildir(maildir); init(); unlockdisplay(display); /* initdraw leaves it locked */ display->locking = 1; /* tell library we're using the display lock */ setdate(); eresized(0); pids[Mainp] = getpid(); startproc(timeproc, Timep); startproc(mouseproc, Mousep); startproc(kbdproc, Kbdp); startproc(infoproc, Infop); // close(pfd[1]); for(i = 0; i < nmaildirs; i++) loadmboxfaces(maildirs[i]); faceproc(); fprint(2, "inbox: %s process exits\n", procnames[Mainp]); killall(nil); }