diff options
Diffstat (limited to 'src/cmd/faces/main.c')
-rw-r--r-- | src/cmd/faces/main.c | 783 |
1 files changed, 783 insertions, 0 deletions
diff --git a/src/cmd/faces/main.c b/src/cmd/faces/main.c new file mode 100644 index 00000000..4be56230 --- /dev/null +++ b/src/cmd/faces/main.c @@ -0,0 +1,783 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <plumb.h> +#include <regexp.h> +//jpc #include <event.h> /* for support routines only */ +#include <bio.h> +#include <thread.h> +#include <mouse.h> +#include <cursor.h> +#include <9pclient.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 */ + +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 +}; + +CFsys *upasfs; +Mousectl *mousectl; +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 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 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) +{ +#if 0 + mousefd = open("/dev/mouse", OREAD); + if(mousefd < 0){ + fprint(2, "faces: can't open mouse: %r\n"); + threadexitsall("mouse"); + } +#endif + upasfs = nsmount("upasfs",nil); + mousectl = initmouse(nil,screen); + 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"); + threadexitsall("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/pelm/latin1.8.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 *dummy) +{ + for(;;){ + lockdisplay(display); + drawtime(); + updatetimes(); + flushimage(display, 1); + unlockdisplay(display); + sleep(60000); + 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, 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; 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? blue : bgrnd, left, leftright.min); + draw(screen, rightr, last<nfaces? blue : bgrnd, 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); +} + +#if 0 +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); + } +} +#endif + +void +loadmboxfaces(char *maildir) +{ + CFid *dirfd; + Dir *d; + int i, n; + + dirfd = fsopen(upasfs,maildir, OREAD); + if(dirfd != nil){ + //jpc chdir(maildir); + while((n = fsdirread(dirfd, &d)) > 0){ + for(i=0; i<n; i++) { + addface(dirface(maildir, d[i].name)); + } + free(d); + } + fsclose(dirfd); + } + else { + error("cannot open %s: %r",maildir); + } +} + +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, bgrnd, 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, bgrnd, 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); +} + +#if 0 +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; + } + //jpc n = eatomouse(m, buf, n); + if(n > 0) + return 1; + } +} +#endif +int +getmouse(Mouse *m) +{ + static int eof; + + if(eof) + return 0; + if( readmouse(mousectl) < 0 ) { + eof = 1; + m->buttons = 0; + return 0; + } + else { + *m = mousectl->m; +/* m->buttons = mousectl->m.buttons; + m->xy.x = mousectl->m.xy.x; + m->xy.y = mousectl->m.xy.y; + m->msec = mousectl->m.msec; */ + 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)) + && 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 *dummy) +{ + 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"); + threadexitsall(s); +} + +void +startproc(void (*f)(void), int index) +{ + int pid; + + switch(pid = rfork(RFPROC|RFNOWAIT)){ //jpc removed |RFMEM + 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"); + threadexitsall(nil); + } + if(index >= 0) + pids[index] = pid; +} + +void +usage(void) +{ + fprint(2, "usage: faces [-hi] [-m maildir] -W winsize\n"); + threadexitsall("usage"); +} + +void +threadmain(int argc, char *argv[]) +{ + int i; + + ARGBEGIN{ + case 'h': + history++; + break; + case 'i': + initload++; + break; + case 'm': + addmaildir(EARGF(usage())); + maildir = nil; + break; + case 'W': + winsize = EARGF(usage()); + break; + default: + usage(); + }ARGEND + + if(initdraw(nil, nil, "faces") < 0){ + fprint(2, "faces: initdraw failed: %r\n"); + threadexitsall("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(); + pids[Timep] = proccreate(timeproc, nil, 16000); + pids[Mousep] = proccreate(mouseproc, nil, 16000); + if(initload) + for(i = 0; i < nmaildirs; i++) + loadmboxfaces(maildirs[i]); + faceproc(); + fprint(2, "faces: %s process exits\n", procnames[Mainp]); + killall(nil); +} |