6 //jpc #include <event.h> /* for support routines only */
14 int history = 0; /* use old interface, showing history of mailbox rather than current state */
15 int initload = 0; /* initialize program with contents of mail box */
19 Facesep = 6, /* must be even to avoid damaging background stipple */
22 HhmmTime = 18*60*60, /* max age of face to display hh:mm time */
40 Rectangle leftright = {0, 0, 20, 15};
43 0x00, 0x80, 0x00, 0x01, 0x80, 0x00, 0x03, 0x80,
44 0x00, 0x07, 0x80, 0x00, 0x0f, 0x00, 0x00, 0x1f,
45 0xff, 0xf0, 0x3f, 0xff, 0xf0, 0xff, 0xff, 0xf0,
46 0x3f, 0xff, 0xf0, 0x1f, 0xff, 0xf0, 0x0f, 0x00,
47 0x00, 0x07, 0x80, 0x00, 0x03, 0x80, 0x00, 0x01,
48 0x80, 0x00, 0x00, 0x80, 0x00
52 0x00, 0x10, 0x00, 0x00, 0x18, 0x00, 0x00, 0x1c,
53 0x00, 0x00, 0x1e, 0x00, 0x00, 0x0f, 0x00, 0xff,
54 0xff, 0x80, 0xff, 0xff, 0xc0, 0xff, 0xff, 0xf0,
55 0xff, 0xff, 0xc0, 0xff, 0xff, 0x80, 0x00, 0x0f,
56 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1c, 0x00, 0x00,
57 0x18, 0x00, 0x00, 0x10, 0x00
62 Image *blue; /* full arrow */
63 Image *bgrnd; /* pale blue background color */
64 Image *left; /* left-pointing arrow mask */
65 Image *right; /* right-pointing arrow mask */
69 int first, last; /* first and last visible face; last is first invisible */
77 char *maildir = "/mail/fs/mbox";
80 Point datep = { 8, 6 };
81 Point facep = { 8, 6+0+4 }; /* 0 updated to datefont->height in init() */
82 Point enddate; /* where date ends on display; used to place arrows */
83 Rectangle leftr; /* location of left arrow on display */
84 Rectangle rightr; /* location of right arrow on display */
85 void updatetimes(void);
91 strcpy(date, ctime(now));
92 date[4+4+3+5] = '\0'; /* change from Thu Jul 22 14:28:43 EDT 1999\n to Thu Jul 22 14:28 */
99 mousefd = open("/dev/mouse", OREAD);
101 fprint(2, "faces: can't open mouse: %r\n");
102 threadexitsall("mouse");
105 upasfs = nsmount("upasfs",nil);
106 mousectl = initmouse(nil,screen);
109 /* make background color */
110 bgrnd = allocimagemix(display, DPalebluegreen, 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 */
332 lockdisplay(display);
340 ny = (nfaces+(nx-1)) / nx;
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);
371 loadmboxfaces(char *maildir)
377 dirfd = open(maildir, OREAD);
380 while((n = dirread(dirfd, &d)) > 0){
382 addface(dirface(maildir, d[i].name));
391 loadmboxfaces(char *maildir)
397 dirfd = fsopen(upasfs,maildir, OREAD);
399 //jpc chdir(maildir);
400 while((n = fsdirread(dirfd, &d)) > 0){
402 addface(dirface(maildir, d[i].name));
409 error("cannot open %s: %r",maildir);
418 if(f->file!=nil && f->bit!=f->file->image)
420 freefacefile(f->file);
421 for(i=0; i<Nstring; i++)
436 ny = (nfaces+(nx-1)) / nx;
438 for(y=(j-first)/nx; y<ny; y++){
440 /* move them along */
441 r0 = facerect(y*nx+x);
442 r1 = facerect(y*nx+x+1);
444 r.max.x = r.min.x + (nx - x - 1)*(Facesize+Facesep);
445 draw(screen, r, screen, nil, r1.min);
448 /* copy one up from row below */
449 r = facerect((y+1)*nx);
450 draw(screen, facerect(y*nx+nx-1), screen, nil, r.min);
454 if(last < nfaces) /* first off-screen becomes visible */
455 drawface(faces[last], last-1);
457 /* clear final spot */
458 r = facerect(last-first-1);
459 draw(screen, r, bgrnd, nil, r.min);
463 memmove(faces+j, faces+j+1, (nfaces-(j+1))*sizeof(Face*));
477 f->str[Sshow] = estrdup("");
480 flushimage(display, 1);
485 delete(char *s, char *digest)
490 lockdisplay(display);
491 for(i=0; i<nfaces; i++){
494 if(f->str[Sdigest]!=nil && strcmp(digest, f->str[Sdigest]) == 0){
499 if(f->str[Sshow] && strcmp(s, f->str[Sshow]) == 0){
505 unlockdisplay(display);
520 nacross = (Dx(screen->r)-2*facep.x+Facesep)/(Facesize+Facesep);
521 for(ndown=1; rectinrect(facerect(ndown*nacross), screen->r); ndown++)
524 draw(screen, screen->r, bgrnd, nil, ZP);
527 for(i=0; i<nfaces; i++)
528 drawface(faces[i], i);
530 flushimage(display, 1);
536 lockdisplay(display);
537 if(new && getwindow(display, Refnone) < 0) {
538 fprint(2, "can't reattach to window\n");
542 unlockdisplay(display);
556 n = read(mousefd, buf, sizeof(buf));
558 /* so callers needn't check return value every time */
563 //jpc n = eatomouse(m, buf, n);
576 if( readmouse(mousectl) < 0 ) {
583 /* m->buttons = mousectl->m.buttons;
584 m->xy.x = mousectl->m.xy.x;
585 m->xy.y = mousectl->m.xy.y;
586 m->msec = mousectl->m.msec; */
593 Clicksize = 3, /* pixels */
597 scroll(int but, Point p)
602 lockdisplay(display);
603 if(ptinrect(p, leftr) && first>0){
612 }else if(ptinrect(p, rightr) && last<nfaces){
614 delta = (nfaces-nacross*ndown) - first;
617 if(delta > nfaces-last)
623 unlockdisplay(display);
630 click(int button, Mouse *m)
636 while(m->buttons == (1<<(button-1)))
640 if(abs(p.x-m->xy.x)>Clicksize || abs(p.y-m->xy.y)>Clicksize)
647 /* click clears display */
648 lockdisplay(display);
649 for(i=0; i<nfaces; i++)
654 unlockdisplay(display);
658 for(i=first; i<last; i++) /* clear vwhois faces */
659 if(ptinrect(p, facerect(i-first))
660 && strstr(faces[i]->str[Sshow], "/XXXvwhois")){
662 flushimage(display, 1);
671 lockdisplay(display);
672 for(i=first; i<last; i++)
673 if(ptinrect(p, facerect(i-first))){
677 unlockdisplay(display);
683 mouseproc(void *dummy)
687 while(getmouse(&mouse)){
688 if(mouse.buttons == 1)
690 else if(mouse.buttons == 2)
692 else if(mouse.buttons == 4)
706 for(i=0; i<NPROC; i++)
707 if(pids[i] && pids[i]!=pid)
708 postnote(PNPROC, pids[i], "kill");
713 startproc(void (*f)(void), int index)
717 switch(pid = rfork(RFPROC|RFNOWAIT)){ //jpc removed |RFMEM
719 fprint(2, "faces: fork failed: %r\n");
720 killall("fork failed");
723 fprint(2, "faces: %s process exits\n", procnames[index]);
725 killall("process died");
735 fprint(2, "usage: faces [-hi] [-m maildir] -W winsize\n");
736 threadexitsall("usage");
740 threadmain(int argc, char *argv[])
752 addmaildir(EARGF(usage()));
756 winsize = EARGF(usage());
762 if(initdraw(nil, nil, "faces") < 0){
763 fprint(2, "faces: initdraw failed: %r\n");
764 threadexitsall("initdraw");
769 unlockdisplay(display); /* initdraw leaves it locked */
770 display->locking = 1; /* tell library we're using the display lock */
774 pids[Mainp] = getpid();
775 pids[Timep] = proccreate(timeproc, nil, 16000);
776 pids[Mousep] = proccreate(mouseproc, nil, 16000);
778 for(i = 0; i < nmaildirs; i++)
779 loadmboxfaces(maildirs[i]);
781 fprint(2, "faces: %s process exits\n", procnames[Mainp]);