ref: 530bc158c8015ef54da4cd3524da83516fd2a786
dir: /sys/src/cmd/faces/main.c/
#include <u.h> #include <libc.h> #include <draw.h> #include <plumb.h> #include <regexp.h> #include <event.h> /* for support routines only */ #include <bio.h> #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 */ int clickrm = 0; /* allows removing mail faces by left clicking */ 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, Mousep, 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 }; enum{ Cbgrnd, Carrow, Csmallfg, Cfacebg, Ctext, Csmalltext, Ncols, }; Image *cols[Ncols]; Image *left; /* left-pointing cols[Carrow] mask */ Image *right; /* right-pointing cols[Carrow] mask */ Font *tinyfont; Font *mediumfont; Font *datefont; int first, last; /* first and last visible face; last is first invisible */ int nfaces; int mousefd; 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 cols[Carrow]s */ Rectangle leftr; /* location of left cols[Carrow] on display */ Rectangle rightr; /* location of right cols[Carrow] 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) { int i; mousefd = open("/dev/mouse", OREAD); if(mousefd < 0){ fprint(2, "faces: can't open mouse: %r\n"); exits("mouse"); } initplumb(); Theme th[nelem(cols)] = { [Cbgrnd] { "rioback", DBlack }, [Carrow] { "palehold", 0x008888FF }, [Csmallfg] { "paletext", DBlack }, [Cfacebg] { "white", DWhite }, [Ctext] { "hold", DWhite }, [Csmalltext] { "white", DWhite }, }; readtheme(th, nelem(th), nil); for(i=0; i<nelem(cols); i++) cols[i] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, th[i].c); left = allocimage(display, leftright, GREY1, 0, DWhite); right = allocimage(display, leftright, GREY1, 0, DWhite); if(cols[Cbgrnd]==nil || cols[Carrow]==nil || cols[Csmallfg] == nil || cols[Cfacebg] == nil || cols[Ctext] == 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/vga/unicode.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, cols[Cbgrnd], nil, ZP); string(screen, r.min, cols[Csmallfg], 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; i<nfaces; i++){ f = faces[i]; if(f->str[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<nr-1 && *s!='\0'; i++) s += chartorune(r+i, s); r[i] = L'\0'; return i; } void center(Font *f, Point p, char *s, Image *color) { int i, n, dx; Rune rbuf[32]; char sbuf[32*UTFmax+1]; dx = stringwidth(f, s); if(dx > Facesize){ n = torune(rbuf, s, nelem(rbuf)); for(i=0; i<n; i++){ dx = runestringnwidth(f, rbuf, i+1); if(dx > 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<first || i>=last) return; r = facerect(i-first); draw(screen, r, cols[Cbgrnd], nil, ZP); draw(screen, Rpt(r.min, addpt(r.min, Pt(Facesize, Facesize))), cols[Cfacebg], nil, ZP); draw(screen, r, f->bit, f->mask, ZP); r.min.y += Facesize; center(mediumfont, r.min, f->str[Suser], cols[Ctext]); r.min.y += mediumfont->height; tstr = facetime(f, &f->recent); center(mediumfont, r.min, tstr, cols[Ctext]); 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], cols[Cbgrnd]); center(tinyfont, r.min, f->str[Sdomain], cols[Csmalltext]); } } void updatetimes(void) { int i; Face *f; for(i=0; i<nfaces; i++){ f = faces[i]; if(f == nil) continue; if(((long)(now - f->time) <= 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? cols[Carrow] : cols[Cbgrnd], left, leftright.min); draw(screen, rightr, last<nfaces? cols[Carrow] : cols[Cbgrnd], right, leftright.min); } void addface(Face *f) /* always adds at 0 */ { Face **ofaces; Rectangle r0, r1, r; int y, nx, ny; if(f == nil) return; lockdisplay(display); if(first != 0){ first = 0; resized(); } findbit(f); nx = nacross; ny = (nfaces+(nx-1)) / nx; for(y=ny; y>=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; i<n; i++) addface(dirface(maildir, d[i].name)); free(d); } close(dirfd); } } void freeface(Face *f) { int i; if(f->file==nil || f->bit!=f->file->image) freeimage(f->bit); freefacefile(f->file); for(i=0; i<Nstring; i++) free(f->str[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; y<ny; y++){ if(x != nx-1){ /* move them along */ r0 = facerect(y*nx+x); r1 = facerect(y*nx+x+1); r = r0; r.max.x = r.min.x + (nx - x - 1)*(Facesize+Facesep); draw(screen, r, screen, nil, r1.min); } if(y != ny-1){ /* copy one up from row below */ r = facerect((y+1)*nx); draw(screen, facerect(y*nx+nx-1), screen, nil, r.min); } x = 0; } if(last < nfaces) /* first off-screen becomes visible */ drawface(faces[last], last-1); else{ /* clear final spot */ r = facerect(last-first-1); draw(screen, r, cols[Cbgrnd], nil, r.min); } } freeface(faces[j]); memmove(faces+j, faces+j+1, (nfaces-(j+1))*sizeof(Face*)); nfaces--; setlast(); drawarrows(); } void dodelete(int i) { Face *f; f = faces[i]; if(history){ free(f->str[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; i<nfaces; i++){ f = faces[i]; if(digest != nil){ if(f->str[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, cols[Cbgrnd], nil, ZP); enddate = ZP; drawtime(); for(i=0; i<nfaces; i++) drawface(faces[i], i); drawarrows(); flushimage(display, 1); } void eresized(int new) { lockdisplay(display); if(new && getwindow(display, Refnone) < 0) { fprint(2, "can't reattach to window\n"); killall("reattach"); } resized(); unlockdisplay(display); } int getmouse(Mouse *m) { int n; static int eof; char buf[128]; if(eof) return 0; for(;;){ n = read(mousefd, buf, sizeof(buf)); if(n <= 0){ /* so callers needn't check return value every time */ eof = 1; m->buttons = 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, leftr) && first>0){ if(but == 2) delta = -first; else{ delta = nacross; if(delta > first) delta = first; delta = -delta; } }else if(ptinrect(p, rightr) && last<nfaces){ if(but == 2) delta = (nfaces-nacross*ndown) - first; else{ delta = nacross; if(delta > 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))) 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; if(history){ /* click clears display */ lockdisplay(display); for(i=0; i<nfaces; i++) freeface(faces[i]); free(faces); faces=nil; nfaces = 0; unlockdisplay(display); eresized(0); return; }else{ for(i=first; i<last; i++) /* clear vwhois faces */ if(ptinrect(p, facerect(i-first)) && (clickrm || strstr(faces[i]->str[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<last; i++) if(ptinrect(p, facerect(i-first))){ showmail(faces[i]); break; } unlockdisplay(display); break; } } void mouseproc(void) { Mouse mouse; while(getmouse(&mouse)){ if(mouse.buttons == 1) click(1, &mouse); else if(mouse.buttons == 2) click(2, &mouse); else if(mouse.buttons == 4) click(3, &mouse); while(mouse.buttons) getmouse(&mouse); } } void killall(char *s) { int i, pid; pid = getpid(); for(i=0; i<NPROC; i++) if(pids[i] && pids[i]!=pid) postnote(PNPROC, pids[i], "kill"); exits(s); } void startproc(void (*f)(void), int index) { int pid; switch(pid = rfork(RFPROC|RFMEM|RFNOWAIT)){ case -1: fprint(2, "faces: fork failed: %r\n"); killall("fork failed"); case 0: f(); fprint(2, "faces: %s process exits\n", procnames[index]); if(index >= 0) killall("process died"); exits(nil); } if(index >= 0) pids[index] = pid; } void usage(void) { fprint(2, "usage: faces [-chi] [-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; case 'c': clickrm++; 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(mouseproc, Mousep); if(initload) for(i = 0; i < nmaildirs; i++) loadmboxfaces(maildirs[i]); faceproc(); fprint(2, "faces: %s process exits\n", procnames[Mainp]); killall(nil); }