6 #include <event.h> /* for support routines only */
10 int history = 0; /* use old interface, showing history of mailbox rather than current state */
11 int initload = 0; /* initialize program with contents of mail box */
15 Facesep = 6, /* must be even to avoid damaging background stipple */
18 HhmmTime = 18*60*60, /* max age of face to display hh:mm time */
36 Rectangle leftright = {0, 0, 20, 15};
39 0x00, 0x80, 0x00, 0x01, 0x80, 0x00, 0x03, 0x80,
40 0x00, 0x07, 0x80, 0x00, 0x0f, 0x00, 0x00, 0x1f,
41 0xff, 0xf0, 0x3f, 0xff, 0xf0, 0xff, 0xff, 0xf0,
42 0x3f, 0xff, 0xf0, 0x1f, 0xff, 0xf0, 0x0f, 0x00,
43 0x00, 0x07, 0x80, 0x00, 0x03, 0x80, 0x00, 0x01,
44 0x80, 0x00, 0x00, 0x80, 0x00
48 0x00, 0x10, 0x00, 0x00, 0x18, 0x00, 0x00, 0x1c,
49 0x00, 0x00, 0x1e, 0x00, 0x00, 0x0f, 0x00, 0xff,
50 0xff, 0x80, 0xff, 0xff, 0xc0, 0xff, 0xff, 0xf0,
51 0xff, 0xff, 0xc0, 0xff, 0xff, 0x80, 0x00, 0x0f,
52 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1c, 0x00, 0x00,
53 0x18, 0x00, 0x00, 0x10, 0x00
56 Image *blue; /* full arrow */
57 Image *bgrnd; /* pale blue background color */
58 Image *left; /* left-pointing arrow mask */
59 Image *right; /* right-pointing arrow mask */
63 int first, last; /* first and last visible face; last is first invisible */
71 char *maildir = "/mail/fs/mbox";
74 Point datep = { 8, 6 };
75 Point facep = { 8, 6+0+4 }; /* 0 updated to datefont->height in init() */
76 Point enddate; /* where date ends on display; used to place arrows */
77 Rectangle leftr; /* location of left arrow on display */
78 Rectangle rightr; /* location of right arrow on display */
79 void updatetimes(void);
85 strcpy(date, ctime(now));
86 date[4+4+3+5] = '\0'; /* change from Thu Jul 22 14:28:43 EDT 1999\n to Thu Jul 22 14:28 */
92 mousefd = open("/dev/mouse", OREAD);
94 fprint(2, "faces: can't open mouse: %r\n");
99 /* make background color */
100 bgrnd = allocimagemix(display, DPalebluegreen, DWhite);
101 blue = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x008888FF); /* blue-green */
102 left = allocimage(display, leftright, GREY1, 0, DWhite);
103 right = allocimage(display, leftright, GREY1, 0, DWhite);
104 if(bgrnd==nil || blue==nil || left==nil || right==nil){
105 fprint(2, "faces: can't create images: %r\n");
109 loadimage(left, leftright, leftdata, sizeof leftdata);
110 loadimage(right, leftright, rightdata, sizeof rightdata);
112 /* initialize little fonts */
113 tinyfont = openfont(display, "/lib/font/bit/misc/ascii.5x7.font");
116 mediumfont = openfont(display, "/lib/font/bit/misc/unicode.6x13.font");
117 if(mediumfont == nil)
121 facep.y += datefont->height;
122 if(datefont->height & 1) /* stipple parity */
132 r.min = addpt(screen->r.min, datep);
133 if(eqpt(enddate, ZP)){
135 enddate.x += stringwidth(datefont, "Wed May 30 22:54"); /* nice wide string */
136 enddate.x += Facesep; /* for safety */
139 r.max.y = enddate.y+datefont->height;
140 draw(screen, r, bgrnd, nil, ZP);
141 string(screen, r.min, display->black, ZP, datefont, date);
148 lockdisplay(display);
151 flushimage(display, 1);
152 unlockdisplay(display);
154 sleep(((60 - now%60) + 1)*1000); /* wait for minute to change */
160 alreadyseen(char *digest)
168 /* can do accurate check */
169 for(i=0; i<nfaces; i++){
171 if(f->str[Sdigest]!=nil && strcmp(digest, f->str[Sdigest])==0)
178 torune(Rune *r, char *s, int nr)
182 for(i=0; i<nr-1 && *s!='\0'; i++)
183 s += chartorune(r+i, s);
189 center(Font *f, Point p, char *s, Image *color)
193 char sbuf[32*UTFmax+1];
195 dx = stringwidth(f, s);
197 n = torune(rbuf, s, nelem(rbuf));
199 dx = runestringnwidth(f, rbuf, i+1);
203 sprint(sbuf, "%.*S", i, rbuf);
205 dx = stringwidth(f, s);
207 p.x += (Facesize-dx)/2;
208 string(screen, p, color, ZP, f, s);
212 facerect(int index) /* index is geometric; 0 is always upper left face */
219 r.min = addpt(screen->r.min, facep);
220 r.min.x += x*(Facesize+Facesep);
221 r.min.y += y*(Facesize+Facesep+2*mediumfont->height);
222 r.max = addpt(r.min, Pt(Facesize, Facesize));
223 r.max.y += 2*mediumfont->height;
224 /* simple fix to avoid drawing off screen, allowing customers to use position */
225 if(index<0 || index>=nacross*ndown)
230 static char *mon = "JanFebMarAprMayJunJulAugSepOctNovDec";
232 facetime(Face *f, int *recent)
236 if((long)(now - f->time) > HhmmTime){
238 sprint(buf, "%.3s %2d", mon+3*f->tm.mon, f->tm.mday);
242 sprint(buf, "%02d:%02d", f->tm.hour, f->tm.min);
248 drawface(Face *f, int i)
256 if(i<first || i>=last)
258 r = facerect(i-first);
259 draw(screen, r, bgrnd, nil, ZP);
260 draw(screen, r, f->bit, f->mask, ZP);
262 center(mediumfont, r.min, f->str[Suser], display->black);
263 r.min.y += mediumfont->height;
264 tstr = facetime(f, &f->recent);
265 center(mediumfont, r.min, tstr, display->black);
267 r.min.y -= mediumfont->height + tinyfont->height + 2;
268 for(p.x=-1; p.x<=1; p.x++)
269 for(p.y=-1; p.y<=1; p.y++)
270 center(tinyfont, addpt(r.min, p), f->str[Sdomain], display->white);
271 center(tinyfont, r.min, f->str[Sdomain], display->black);
281 for(i=0; i<nfaces; i++){
285 if(((long)(now - f->time) <= HhmmTime) != f->recent)
293 last = first+nacross*ndown;
306 p.x++; /* align background texture */
307 leftr = rectaddpt(leftright, p);
308 p.x += Dx(leftright) + Facesep;
309 rightr = rectaddpt(leftright, p);
310 draw(screen, leftr, first>0? blue : bgrnd, left, leftright.min);
311 draw(screen, rightr, last<nfaces? blue : bgrnd, right, leftright.min);
315 addface(Face *f) /* always adds at 0 */
323 lockdisplay(display);
331 ny = (nfaces+(nx-1)) / nx;
333 for(y=ny; y>=0; y--){
334 /* move them along */
335 r0 = facerect(y*nx+0);
336 r1 = facerect(y*nx+1);
338 r.max.x = r.min.x + (nx - 1)*(Facesize+Facesep);
339 draw(screen, r, screen, nil, r0.min);
340 /* copy one down from row above */
342 r = facerect((y-1)*nx+nx-1);
343 draw(screen, r0, screen, nil, r.min);
348 faces = emalloc((nfaces+1)*sizeof(Face*));
349 memmove(faces+1, ofaces, nfaces*(sizeof(Face*)));
356 flushimage(display, 1);
357 unlockdisplay(display);
361 loadmboxfaces(char *maildir)
367 dirfd = open(maildir, OREAD);
370 while((n = dirread(dirfd, &d)) > 0){
372 addface(dirface(maildir, d[i].name));
384 if(f->file==nil || f->bit!=f->file->image)
386 freefacefile(f->file);
387 for(i=0; i<Nstring; i++)
402 ny = (nfaces+(nx-1)) / nx;
404 for(y=(j-first)/nx; y<ny; y++){
406 /* move them along */
407 r0 = facerect(y*nx+x);
408 r1 = facerect(y*nx+x+1);
410 r.max.x = r.min.x + (nx - x - 1)*(Facesize+Facesep);
411 draw(screen, r, screen, nil, r1.min);
414 /* copy one up from row below */
415 r = facerect((y+1)*nx);
416 draw(screen, facerect(y*nx+nx-1), screen, nil, r.min);
420 if(last < nfaces) /* first off-screen becomes visible */
421 drawface(faces[last], last-1);
423 /* clear final spot */
424 r = facerect(last-first-1);
425 draw(screen, r, bgrnd, nil, r.min);
429 memmove(faces+j, faces+j+1, (nfaces-(j+1))*sizeof(Face*));
443 f->str[Sshow] = estrdup("");
446 flushimage(display, 1);
451 delete(char *s, char *digest)
456 lockdisplay(display);
457 for(i=0; i<nfaces; i++){
460 if(f->str[Sdigest]!=nil && strcmp(digest, f->str[Sdigest]) == 0){
465 if(f->str[Sshow] && strcmp(s, f->str[Sshow]) == 0){
471 unlockdisplay(display);
486 nacross = (Dx(screen->r)-2*facep.x+Facesep)/(Facesize+Facesep);
487 for(ndown=1; rectinrect(facerect(ndown*nacross), screen->r); ndown++)
490 draw(screen, screen->r, bgrnd, nil, ZP);
493 for(i=0; i<nfaces; i++)
494 drawface(faces[i], i);
496 flushimage(display, 1);
502 lockdisplay(display);
503 if(new && getwindow(display, Refnone) < 0) {
504 fprint(2, "can't reattach to window\n");
508 unlockdisplay(display);
521 n = read(mousefd, buf, sizeof(buf));
523 /* so callers needn't check return value every time */
528 n = eatomouse(m, buf, n);
536 Clicksize = 3, /* pixels */
540 scroll(int but, Point p)
545 lockdisplay(display);
546 if(ptinrect(p, leftr) && first>0){
555 }else if(ptinrect(p, rightr) && last<nfaces){
557 delta = (nfaces-nacross*ndown) - first;
560 if(delta > nfaces-last)
566 unlockdisplay(display);
573 click(int button, Mouse *m)
579 while(m->buttons == (1<<(button-1)))
583 if(abs(p.x-m->xy.x)>Clicksize || abs(p.y-m->xy.y)>Clicksize)
590 /* click clears display */
591 lockdisplay(display);
592 for(i=0; i<nfaces; i++)
597 unlockdisplay(display);
601 for(i=first; i<last; i++) /* clear vwhois faces */
602 if(ptinrect(p, facerect(i-first))
603 && strstr(faces[i]->str[Sshow], "/XXXvwhois")){
605 flushimage(display, 1);
614 lockdisplay(display);
615 for(i=first; i<last; i++)
616 if(ptinrect(p, facerect(i-first))){
620 unlockdisplay(display);
630 while(getmouse(&mouse)){
631 if(mouse.buttons == 1)
633 else if(mouse.buttons == 2)
635 else if(mouse.buttons == 4)
649 for(i=0; i<NPROC; i++)
650 if(pids[i] && pids[i]!=pid)
651 postnote(PNPROC, pids[i], "kill");
656 startproc(void (*f)(void), int index)
660 switch(pid = rfork(RFPROC|RFMEM|RFNOWAIT)){
662 fprint(2, "faces: fork failed: %r\n");
663 killall("fork failed");
666 fprint(2, "faces: %s process exits\n", procnames[index]);
668 killall("process died");
678 fprint(2, "usage: faces [-hi] [-m maildir]\n");
683 main(int argc, char *argv[])
695 addmaildir(EARGF(usage()));
702 if(initdraw(nil, nil, "faces") < 0){
703 fprint(2, "faces: initdraw failed: %r\n");
709 unlockdisplay(display); /* initdraw leaves it locked */
710 display->locking = 1; /* tell library we're using the display lock */
714 pids[Mainp] = getpid();
715 startproc(timeproc, Timep);
716 startproc(mouseproc, Mousep);
718 for(i = 0; i < nmaildirs; i++)
719 loadmboxfaces(maildirs[i]);
721 fprint(2, "faces: %s process exits\n", procnames[Mainp]);