123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717 |
- #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 */
- 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
- };
- 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)
- {
- addmaildir(maildir);
- mousefd = open("/dev/mouse", OREAD);
- if(mousefd < 0){
- fprint(2, "faces: can't open mouse: %r\n");
- exits("mouse");
- }
- 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/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)
- {
- 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) > 23*60*60){
- *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);
- }
- 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, 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);
- }
- 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))
- && strstr(faces[i]->str[Sshow], "/mail/fs/mbox/XXX")){
- 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 [-hi]\n");
- exits("usage");
- }
- void
- main(int argc, char *argv[])
- {
- ARGBEGIN{
- case 'h':
- history++;
- break;
- case 'i':
- initload++;
- break;
- case 'm':
- addmaildir(EARGF(usage()));
- break;
- default:
- usage();
- }ARGEND
- if(initdraw(nil, nil, "faces") < 0){
- fprint(2, "faces: initdraw failed: %r\n");
- exits("initdraw");
- }
- 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)
- loadmboxfaces(maildir);
- faceproc();
- fprint(2, "faces: %s process exits\n", procnames[Mainp]);
- killall(nil);
- }
|