13 int history = 0; /* use old interface, showing history of mailbox rather than current state */
14 int initload = 0; /* initialize program with contents of mail box */
18 Facesep = 6, /* must be even to avoid damaging background stipple */
21 HhmmTime = 18*60*60, /* max age of face to display hh:mm time */
42 Rectangle leftright = {0, 0, 20, 15};
45 0x00, 0x80, 0x00, 0x01, 0x80, 0x00, 0x03, 0x80,
46 0x00, 0x07, 0x80, 0x00, 0x0f, 0x00, 0x00, 0x1f,
47 0xff, 0xf0, 0x3f, 0xff, 0xf0, 0xff, 0xff, 0xf0,
48 0x3f, 0xff, 0xf0, 0x1f, 0xff, 0xf0, 0x0f, 0x00,
49 0x00, 0x07, 0x80, 0x00, 0x03, 0x80, 0x00, 0x01,
50 0x80, 0x00, 0x00, 0x80, 0x00
54 0x00, 0x10, 0x00, 0x00, 0x18, 0x00, 0x00, 0x1c,
55 0x00, 0x00, 0x1e, 0x00, 0x00, 0x0f, 0x00, 0xff,
56 0xff, 0x80, 0xff, 0xff, 0xc0, 0xff, 0xff, 0xf0,
57 0xff, 0xff, 0xc0, 0xff, 0xff, 0x80, 0x00, 0x0f,
58 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1c, 0x00, 0x00,
59 0x18, 0x00, 0x00, 0x10, 0x00
64 Image *blue; /* full arrow */
65 Image *bgrnd; /* pale blue background color */
66 Image *left; /* left-pointing arrow mask */
67 Image *right; /* right-pointing arrow mask */
71 int first, last; /* first and last visible face; last is first invisible */
79 char *maildir = "mbox";
82 Point datep = { 8, 6 };
83 Point facep = { 8, 6+0+4 }; /* 0 updated to datefont->height in init() */
84 Point enddate; /* where date ends on display; used to place arrows */
85 Rectangle leftr; /* location of left arrow on display */
86 Rectangle rightr; /* location of right arrow on display */
87 void updatetimes(void);
94 strcpy(date, ctime(now));
95 date[4+4+3+5] = '\0'; /* change from Thu Jul 22 14:28:43 EDT 1999\n to Thu Jul 22 14:28 */
101 mailfs = nsmount("mail", nil);
103 sysfatal("mount mail: %r");
104 mousectl = initmouse(nil, screen);
106 sysfatal("initmouse: %r");
109 /* make background color */
110 bgrnd = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DWhite);
111 blue = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x008888FF); /* blue-green */
112 left = allocimage(display, leftright, GREY1, 0, DWhite);
113 right = allocimage(display, leftright, GREY1, 0, DWhite);
114 if(bgrnd==nil || blue==nil || left==nil || right==nil){
115 fprint(2, "faces: can't create images: %r\n");
116 threadexitsall("image");
119 loadimage(left, leftright, leftdata, sizeof leftdata);
120 loadimage(right, leftright, rightdata, sizeof rightdata);
122 /* initialize little fonts */
123 tinyfont = openfont(display, "/lib/font/bit/misc/ascii.5x7.font");
126 mediumfont = openfont(display, "/lib/font/bit/pelm/latin1.8.font");
127 if(mediumfont == nil)
131 facep.y += datefont->height;
132 if(datefont->height & 1) /* stipple parity */
142 r.min = addpt(screen->r.min, datep);
143 if(eqpt(enddate, ZP)){
145 enddate.x += stringwidth(datefont, "Wed May 30 22:54"); /* nice wide string */
146 enddate.x += Facesep; /* for safety */
149 r.max.y = enddate.y+datefont->height;
150 draw(screen, r, bgrnd, nil, ZP);
151 string(screen, r.min, display->black, ZP, datefont, date);
155 timeproc(void *dummy)
158 lockdisplay(display);
161 flushimage(display, 1);
162 unlockdisplay(display);
169 alreadyseen(char *digest)
177 /* can do accurate check */
178 for(i=0; i<nfaces; i++){
180 if(f->str[Sdigest]!=nil && strcmp(digest, f->str[Sdigest])==0)
187 torune(Rune *r, char *s, int nr)
191 for(i=0; i<nr-1 && *s!='\0'; i++)
192 s += chartorune(r+i, s);
198 center(Font *f, Point p, char *s, Image *color)
202 char sbuf[32*UTFmax+1];
204 dx = stringwidth(f, s);
206 n = torune(rbuf, s, nelem(rbuf));
208 dx = runestringnwidth(f, rbuf, i+1);
212 sprint(sbuf, "%.*S", i, rbuf);
214 dx = stringwidth(f, s);
216 p.x += (Facesize-dx)/2;
217 string(screen, p, color, ZP, f, s);
221 facerect(int index) /* index is geometric; 0 is always upper left face */
228 r.min = addpt(screen->r.min, facep);
229 r.min.x += x*(Facesize+Facesep);
230 r.min.y += y*(Facesize+Facesep+2*mediumfont->height);
231 r.max = addpt(r.min, Pt(Facesize, Facesize));
232 r.max.y += 2*mediumfont->height;
233 /* simple fix to avoid drawing off screen, allowing customers to use position */
234 if(index<0 || index>=nacross*ndown)
239 static char *mon = "JanFebMarAprMayJunJulAugSepOctNovDec";
241 facetime(Face *f, int *recent)
245 if((long)(now - f->time) > HhmmTime){
247 sprint(buf, "%.3s %2d", mon+3*f->tm.mon, f->tm.mday);
251 sprint(buf, "%02d:%02d", f->tm.hour, f->tm.min);
257 drawface(Face *f, int i)
265 if(i<first || i>=last)
267 r = facerect(i-first);
268 draw(screen, r, bgrnd, nil, ZP);
269 draw(screen, r, f->bit, f->mask, ZP);
271 center(mediumfont, r.min, f->str[Suser], display->black);
272 r.min.y += mediumfont->height;
273 tstr = facetime(f, &f->recent);
274 center(mediumfont, r.min, tstr, display->black);
276 r.min.y -= mediumfont->height + tinyfont->height + 2;
277 for(p.x=-1; p.x<=1; p.x++)
278 for(p.y=-1; p.y<=1; p.y++)
279 center(tinyfont, addpt(r.min, p), f->str[Sdomain], display->white);
280 center(tinyfont, r.min, f->str[Sdomain], display->black);
290 for(i=0; i<nfaces; i++){
294 if(((long)(now - f->time) <= HhmmTime) != f->recent)
302 last = first+nacross*ndown;
315 p.x++; /* align background texture */
316 leftr = rectaddpt(leftright, p);
317 p.x += Dx(leftright) + Facesep;
318 rightr = rectaddpt(leftright, p);
319 draw(screen, leftr, first>0? blue : bgrnd, left, leftright.min);
320 draw(screen, rightr, last<nfaces? blue : bgrnd, right, leftright.min);
324 addface(Face *f) /* always adds at 0 */
339 ny = (nfaces+(nx-1)) / nx;
341 lockdisplay(display);
342 for(y=ny; y>=0; y--){
343 /* move them along */
344 r0 = facerect(y*nx+0);
345 r1 = facerect(y*nx+1);
347 r.max.x = r.min.x + (nx - 1)*(Facesize+Facesep);
348 draw(screen, r, screen, nil, r0.min);
349 /* copy one down from row above */
351 r = facerect((y-1)*nx+nx-1);
352 draw(screen, r0, screen, nil, r.min);
357 faces = emalloc((nfaces+1)*sizeof(Face*));
358 memmove(faces+1, ofaces, nfaces*(sizeof(Face*)));
365 flushimage(display, 1);
366 unlockdisplay(display);
370 loadmboxfaces(char *maildir)
376 dirfd = fsopen(mailfs, maildir, OREAD);
378 while((n = fsdirread(dirfd, &d)) > 0){
380 addface(dirface(maildir, d[i].name));
385 sysfatal("open %s: %r", maildir);
393 if(f->file!=nil && f->bit!=f->file->image)
395 freefacefile(f->file);
396 for(i=0; i<Nstring; i++)
411 ny = (nfaces+(nx-1)) / nx;
413 for(y=(j-first)/nx; y<ny; y++){
415 /* move them along */
416 r0 = facerect(y*nx+x);
417 r1 = facerect(y*nx+x+1);
419 r.max.x = r.min.x + (nx - x - 1)*(Facesize+Facesep);
420 draw(screen, r, screen, nil, r1.min);
423 /* copy one up from row below */
424 r = facerect((y+1)*nx);
425 draw(screen, facerect(y*nx+nx-1), screen, nil, r.min);
429 if(last < nfaces) /* first off-screen becomes visible */
430 drawface(faces[last], last-1);
432 /* clear final spot */
433 r = facerect(last-first-1);
434 draw(screen, r, bgrnd, nil, r.min);
438 memmove(faces+j, faces+j+1, (nfaces-(j+1))*sizeof(Face*));
452 f->str[Sshow] = estrdup("");
455 flushimage(display, 1);
460 delete(char *s, char *digest)
465 lockdisplay(display);
466 for(i=0; i<nfaces; i++){
469 if(f->str[Sdigest]!=nil && strcmp(digest, f->str[Sdigest]) == 0){
474 if(f->str[Sshow] && strcmp(s, f->str[Sshow]) == 0){
480 unlockdisplay(display);
495 nacross = (Dx(screen->r)-2*facep.x+Facesep)/(Facesize+Facesep);
496 for(ndown=1; rectinrect(facerect(ndown*nacross), screen->r); ndown++)
499 draw(screen, screen->r, bgrnd, nil, ZP);
502 for(i=0; i<nfaces; i++)
503 drawface(faces[i], i);
505 flushimage(display, 1);
511 lockdisplay(display);
512 if(new && getwindow(display, Refnone) < 0) {
513 fprint(2, "can't reattach to window\n");
517 unlockdisplay(display);
525 while(recv(mousectl->resizec, 0) == 1)
536 if(readmouse(mousectl) < 0){
547 Clicksize = 3, /* pixels */
551 scroll(int but, Point p)
556 lockdisplay(display);
557 if(ptinrect(p, leftr) && first>0){
566 }else if(ptinrect(p, rightr) && last<nfaces){
568 delta = (nfaces-nacross*ndown) - first;
571 if(delta > nfaces-last)
577 unlockdisplay(display);
584 click(int button, Mouse *m)
590 while(m->buttons == (1<<(button-1)))
594 if(abs(p.x-m->xy.x)>Clicksize || abs(p.y-m->xy.y)>Clicksize)
601 /* click clears display */
602 lockdisplay(display);
603 for(i=0; i<nfaces; i++)
608 unlockdisplay(display);
612 for(i=first; i<last; i++) /* clear vwhois faces */
613 if(ptinrect(p, facerect(i-first))
614 && strstr(faces[i]->str[Sshow], "/XXXvwhois")){
615 lockdisplay(display);
617 flushimage(display, 1);
618 unlockdisplay(display);
628 lockdisplay(display);
629 for(i=first; i<last; i++)
630 if(ptinrect(p, facerect(i-first))){
634 unlockdisplay(display);
645 while(getmouse(&mouse)){
646 if(mouse.buttons == 1)
648 else if(mouse.buttons == 2)
650 else if(mouse.buttons == 4)
667 fprint(2, "usage: faces [-hi] [-m maildir] -W winsize\n");
668 threadexitsall("usage");
672 threadmain(int argc, char *argv[])
686 addmaildir(EARGF(usage()));
690 winsize = EARGF(usage());
696 if(initdraw(nil, nil, "faces") < 0){
697 fprint(2, "faces: initdraw failed: %r\n");
698 threadexitsall("initdraw");
703 unlockdisplay(display); /* initdraw leaves it locked */
704 display->locking = 1; /* tell library we're using the display lock */
708 proccreate(timeproc, nil, STACK);
709 proccreate(mouseproc, nil, STACK);
710 proccreate(resizeproc, nil, STACK);
712 for(i = 0; i < nmaildirs; i++)
713 loadmboxfaces(maildirs[i]);