#include #include #include #include #include #include /* for support routines only */ #include #include #include "faces.h" int history = 0; /* use old interface, showing history of mailbox rather than current state */ int initload = 0; /* initialize program with contents of mail box */ enum { Facesep = 6, /* must be even to avoid damaging background stipple */ Infolines = 9, HhmmTime = 18*60*60, /* max age of face to display hh:mm time */ }; enum { Mainp, Timep, Inputp, NPROC }; int pids[NPROC]; char *procnames[] = { "main", "time", "mouse" }; Rectangle leftright = {0, 0, 20, 15}; uchar leftdata[] = { 0x00, 0x80, 0x00, 0x01, 0x80, 0x00, 0x03, 0x80, 0x00, 0x07, 0x80, 0x00, 0x0f, 0x00, 0x00, 0x1f, 0xff, 0xf0, 0x3f, 0xff, 0xf0, 0xff, 0xff, 0xf0, 0x3f, 0xff, 0xf0, 0x1f, 0xff, 0xf0, 0x0f, 0x00, 0x00, 0x07, 0x80, 0x00, 0x03, 0x80, 0x00, 0x01, 0x80, 0x00, 0x00, 0x80, 0x00 }; uchar rightdata[] = { 0x00, 0x10, 0x00, 0x00, 0x18, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x0f, 0x00, 0xff, 0xff, 0x80, 0xff, 0xff, 0xc0, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xc0, 0xff, 0xff, 0x80, 0x00, 0x0f, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x18, 0x00, 0x00, 0x10, 0x00 }; Image *blue; /* full arrow */ Image *bgrnd; /* pale blue background color */ Image *left; /* left-pointing arrow mask */ Image *right; /* right-pointing arrow mask */ Font *tinyfont; Font *mediumfont; Font *datefont; int first, last; /* first and last visible face; last is first invisible */ int nfaces; int nacross; 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 leftr; /* location of left arrow on display */ Rectangle rightr; /* location of right arrow on display */ void updatetimes(void); void setdate(void) { now = time(nil); strcpy(date, ctime(now)); date[4+4+3+5] = '\0'; /* change from Thu Jul 22 14:28:43 EDT 1999\n to Thu Jul 22 14:28 */ } void init(void) { initplumb(); /* make background color */ bgrnd = allocimagemix(display, DPalebluegreen, DWhite); blue = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x008888FF); /* blue-green */ left = allocimage(display, leftright, GREY1, 0, DWhite); right = allocimage(display, leftright, GREY1, 0, DWhite); if(bgrnd==nil || blue==nil || left==nil || right==nil){ fprint(2, "faces: can't create images: %r\n"); exits("image"); } loadimage(left, leftright, leftdata, sizeof leftdata); loadimage(right, leftright, rightdata, sizeof rightdata); /* initialize little fonts */ tinyfont = openfont(display, "/lib/font/bit/misc/ascii.5x7.font"); if(tinyfont == nil) tinyfont = font; mediumfont = openfont(display, "/lib/font/bit/misc/unicode.6x13.font"); if(mediumfont == nil) mediumfont = font; datefont = font; 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, "Wed May 30 22:54"); /* nice wide string */ 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(); updatetimes(); 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; i Facesize){ n = torune(rbuf, s, nelem(rbuf)); for(i=0; i Facesize) break; } sprint(sbuf, "%.*S", i, rbuf); s = sbuf; dx = stringwidth(f, s); } p.x += (Facesize-dx)/2; string(screen, p, color, ZP, f, s); } Rectangle facerect(int index) /* index is geometric; 0 is always upper left face */ { Rectangle r; int x, y; x = index % nacross; y = index / nacross; r.min = addpt(screen->r.min, facep); r.min.x += x*(Facesize+Facesep); r.min.y += y*(Facesize+Facesep+2*mediumfont->height); r.max = addpt(r.min, Pt(Facesize, Facesize)); r.max.y += 2*mediumfont->height; /* simple fix to avoid drawing off screen, allowing customers to use position */ if(index<0 || index>=nacross*ndown) r.max.x = r.min.x; return r; } static char *mon = "JanFebMarAprMayJunJulAugSepOctNovDec"; char* facetime(Face *f, int *recent) { static char buf[30]; if((long)(now - f->time) > HhmmTime){ *recent = 0; sprint(buf, "%.3s %2d", mon+3*f->tm.mon, f->tm.mday); return buf; }else{ *recent = 1; sprint(buf, "%02d:%02d", f->tm.hour, f->tm.min); return buf; } } void drawface(Face *f, int i) { char *tstr; Rectangle r; Point p; if(f == nil) return; if(i=last) return; r = facerect(i-first); draw(screen, r, bgrnd, nil, ZP); draw(screen, r, f->bit, f->mask, ZP); r.min.y += Facesize; center(mediumfont, r.min, f->str[Suser], display->black); r.min.y += mediumfont->height; tstr = facetime(f, &f->recent); center(mediumfont, r.min, tstr, display->black); if(f->unknown){ r.min.y -= mediumfont->height + tinyfont->height + 2; for(p.x=-1; p.x<=1; p.x++) for(p.y=-1; p.y<=1; p.y++) center(tinyfont, addpt(r.min, p), f->str[Sdomain], display->white); center(tinyfont, r.min, f->str[Sdomain], display->black); } } void updatetimes(void) { int i; Face *f; for(i=0; itime) <= HhmmTime) != f->recent) drawface(f, i); } } void setlast(void) { last = first+nacross*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 */ leftr = rectaddpt(leftright, p); p.x += Dx(leftright) + Facesep; rightr = rectaddpt(leftright, p); draw(screen, leftr, first>0? blue : bgrnd, left, leftright.min); draw(screen, rightr, last=0; y--){ /* move them along */ r0 = facerect(y*nx+0); r1 = facerect(y*nx+1); r = r1; r.max.x = r.min.x + (nx - 1)*(Facesize+Facesep); draw(screen, r, screen, nil, r0.min); /* copy one down from row above */ if(y != 0){ r = facerect((y-1)*nx+nx-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); 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 nx, ny, x, y; if(j < first) first--; else if(j < last){ nx = nacross; ny = (nfaces+(nx-1)) / nx; x = (j-first)%nx; for(y=(j-first)/nx; ystr[Sshow]); f->str[Sshow] = estrdup(""); }else{ delface(i); flushimage(display, 1); } } void delete(char *s, char *digest) { int i; Face *f; lockdisplay(display); for(i=0; istr[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; nacross = (Dx(screen->r)-2*facep.x+Facesep)/(Facesize+Facesep); for(ndown=1; rectinrect(facerect(ndown*nacross), screen->r); ndown++) ; setlast(); draw(screen, screen->r, bgrnd, nil, ZP); enddate = ZP; drawtime(); for(i=0; i0){ if(but == 2) delta = -first; else{ delta = nacross; if(delta > first) delta = first; delta = -delta; } }else if(ptinrect(p, rightr) && last nfaces-last) delta = nfaces-last; } } first += delta; last += delta; unlockdisplay(display); if(delta) eresized(0); return delta; } void click(int button, Mouse *m) { Point p; int i; p = m->xy; while(m->buttons == (1<<(button-1))) ereadmouse(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; if(history){ /* click clears display */ lockdisplay(display); for(i=0; istr[Sshow], "/XXXvwhois")){ delface(i); flushimage(display, 1); } } break; case 2: scroll(2, p); break; case 3: scroll(3, p); lockdisplay(display); for(i=first; i= 0) killall("process died"); exits(nil); } if(index >= 0) pids[index] = pid; } void usage(void) { fprint(2, "usage: faces [-hi] [-m maildir]\n"); exits("usage"); } void main(int argc, char *argv[]) { int i; ARGBEGIN{ case 'h': history++; break; case 'i': initload++; break; case 'm': addmaildir(EARGF(usage())); maildir = nil; break; default: usage(); }ARGEND if(initdraw(nil, nil, "faces") < 0){ fprint(2, "faces: 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(inputproc, Inputp); if(initload) for(i = 0; i < nmaildirs; i++) loadmboxfaces(maildirs[i]); faceproc(); fprint(2, "faces: %s process exits\n", procnames[Mainp]); killall(nil); }