Blob


1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <plumb.h>
5 #include <regexp.h>
6 //jpc #include <event.h> /* for support routines only */
7 #include <bio.h>
8 #include <thread.h>
9 #include <mouse.h>
10 #include <cursor.h>
11 #include <9pclient.h>
12 #include "faces.h"
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 */
17 enum
18 {
19 Facesep = 6, /* must be even to avoid damaging background stipple */
20 Infolines = 9,
22 HhmmTime = 18*60*60, /* max age of face to display hh:mm time */
23 };
25 enum
26 {
27 Mainp,
28 Timep,
29 Mousep,
30 NPROC
31 };
33 int pids[NPROC];
34 char *procnames[] = {
35 "main",
36 "time",
37 "mouse"
38 };
40 Rectangle leftright = {0, 0, 20, 15};
42 uchar leftdata[] = {
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
49 };
51 uchar rightdata[] = {
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
58 };
60 CFsys *upasfs;
61 Mousectl *mousectl;
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 */
66 Font *tinyfont;
67 Font *mediumfont;
68 Font *datefont;
69 int first, last; /* first and last visible face; last is first invisible */
70 int nfaces;
71 int mousefd;
72 int nacross;
73 int ndown;
75 char date[64];
76 Face **faces;
77 char *maildir = "/mail/fs/mbox";
78 ulong now;
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);
87 void
88 setdate(void)
89 {
90 now = time(nil);
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 */
93 }
95 void
96 init(void)
97 {
98 #if 0
99 mousefd = open("/dev/mouse", OREAD);
100 if(mousefd < 0){
101 fprint(2, "faces: can't open mouse: %r\n");
102 threadexitsall("mouse");
104 #endif
105 upasfs = nsmount("upasfs",nil);
106 mousectl = initmouse(nil,screen);
107 initplumb();
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");
124 if(tinyfont == nil)
125 tinyfont = font;
126 mediumfont = openfont(display, "/lib/font/bit/pelm/latin1.8.font");
127 if(mediumfont == nil)
128 mediumfont = font;
129 datefont = font;
131 facep.y += datefont->height;
132 if(datefont->height & 1) /* stipple parity */
133 facep.y++;
134 faces = nil;
137 void
138 drawtime(void)
140 Rectangle r;
142 r.min = addpt(screen->r.min, datep);
143 if(eqpt(enddate, ZP)){
144 enddate = r.min;
145 enddate.x += stringwidth(datefont, "Wed May 30 22:54"); /* nice wide string */
146 enddate.x += Facesep; /* for safety */
148 r.max.x = enddate.x;
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);
154 void
155 timeproc(void *dummy)
157 for(;;){
158 lockdisplay(display);
159 drawtime();
160 updatetimes();
161 flushimage(display, 1);
162 unlockdisplay(display);
163 sleep(60000);
164 setdate();
168 int
169 alreadyseen(char *digest)
171 int i;
172 Face *f;
174 if(!digest)
175 return 0;
177 /* can do accurate check */
178 for(i=0; i<nfaces; i++){
179 f = faces[i];
180 if(f->str[Sdigest]!=nil && strcmp(digest, f->str[Sdigest])==0)
181 return 1;
183 return 0;
186 int
187 torune(Rune *r, char *s, int nr)
189 int i;
191 for(i=0; i<nr-1 && *s!='\0'; i++)
192 s += chartorune(r+i, s);
193 r[i] = L'\0';
194 return i;
197 void
198 center(Font *f, Point p, char *s, Image *color)
200 int i, n, dx;
201 Rune rbuf[32];
202 char sbuf[32*UTFmax+1];
204 dx = stringwidth(f, s);
205 if(dx > Facesize){
206 n = torune(rbuf, s, nelem(rbuf));
207 for(i=0; i<n; i++){
208 dx = runestringnwidth(f, rbuf, i+1);
209 if(dx > Facesize)
210 break;
212 sprint(sbuf, "%.*S", i, rbuf);
213 s = sbuf;
214 dx = stringwidth(f, s);
216 p.x += (Facesize-dx)/2;
217 string(screen, p, color, ZP, f, s);
220 Rectangle
221 facerect(int index) /* index is geometric; 0 is always upper left face */
223 Rectangle r;
224 int x, y;
226 x = index % nacross;
227 y = index / nacross;
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)
235 r.max.x = r.min.x;
236 return r;
239 static char *mon = "JanFebMarAprMayJunJulAugSepOctNovDec";
240 char*
241 facetime(Face *f, int *recent)
243 static char buf[30];
245 if((long)(now - f->time) > HhmmTime){
246 *recent = 0;
247 sprint(buf, "%.3s %2d", mon+3*f->tm.mon, f->tm.mday);
248 return buf;
249 }else{
250 *recent = 1;
251 sprint(buf, "%02d:%02d", f->tm.hour, f->tm.min);
252 return buf;
256 void
257 drawface(Face *f, int i)
259 char *tstr;
260 Rectangle r;
261 Point p;
263 if(f == nil)
264 return;
265 if(i<first || i>=last)
266 return;
267 r = facerect(i-first);
268 draw(screen, r, bgrnd, nil, ZP);
269 draw(screen, r, f->bit, f->mask, ZP);
270 r.min.y += Facesize;
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);
275 if(f->unknown){
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);
284 void
285 updatetimes(void)
287 int i;
288 Face *f;
290 for(i=0; i<nfaces; i++){
291 f = faces[i];
292 if(f == nil)
293 continue;
294 if(((long)(now - f->time) <= HhmmTime) != f->recent)
295 drawface(f, i);
299 void
300 setlast(void)
302 last = first+nacross*ndown;
303 if(last > nfaces)
304 last = nfaces;
307 void
308 drawarrows(void)
310 Point p;
312 p = enddate;
313 p.x += Facesep;
314 if(p.x & 1)
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);
323 void
324 addface(Face *f) /* always adds at 0 */
326 Face **ofaces;
327 Rectangle r0, r1, r;
328 int y, nx, ny;
330 if(f == nil)
331 return;
332 lockdisplay(display);
333 if(first != 0){
334 first = 0;
335 resized();
337 findbit(f);
339 nx = nacross;
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);
346 r = r1;
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 */
350 if(y != 0){
351 r = facerect((y-1)*nx+nx-1);
352 draw(screen, r0, screen, nil, r.min);
356 ofaces = faces;
357 faces = emalloc((nfaces+1)*sizeof(Face*));
358 memmove(faces+1, ofaces, nfaces*(sizeof(Face*)));
359 free(ofaces);
360 nfaces++;
361 setlast();
362 drawarrows();
363 faces[0] = f;
364 drawface(f, 0);
365 flushimage(display, 1);
366 unlockdisplay(display);
369 #if 0
370 void
371 loadmboxfaces(char *maildir)
373 int dirfd;
374 Dir *d;
375 int i, n;
377 dirfd = open(maildir, OREAD);
378 if(dirfd >= 0){
379 chdir(maildir);
380 while((n = dirread(dirfd, &d)) > 0){
381 for(i=0; i<n; i++)
382 addface(dirface(maildir, d[i].name));
383 free(d);
385 close(dirfd);
388 #endif
390 void
391 loadmboxfaces(char *maildir)
393 CFid *dirfd;
394 Dir *d;
395 int i, n;
397 dirfd = fsopen(upasfs,maildir, OREAD);
398 if(dirfd != nil){
399 //jpc chdir(maildir);
400 while((n = fsdirread(dirfd, &d)) > 0){
401 for(i=0; i<n; i++) {
402 addface(dirface(maildir, d[i].name));
404 free(d);
406 fsclose(dirfd);
408 else {
409 error("cannot open %s: %r",maildir);
413 void
414 freeface(Face *f)
416 int i;
418 if(f->file!=nil && f->bit!=f->file->image)
419 freeimage(f->bit);
420 freefacefile(f->file);
421 for(i=0; i<Nstring; i++)
422 free(f->str[i]);
423 free(f);
426 void
427 delface(int j)
429 Rectangle r0, r1, r;
430 int nx, ny, x, y;
432 if(j < first)
433 first--;
434 else if(j < last){
435 nx = nacross;
436 ny = (nfaces+(nx-1)) / nx;
437 x = (j-first)%nx;
438 for(y=(j-first)/nx; y<ny; y++){
439 if(x != nx-1){
440 /* move them along */
441 r0 = facerect(y*nx+x);
442 r1 = facerect(y*nx+x+1);
443 r = r0;
444 r.max.x = r.min.x + (nx - x - 1)*(Facesize+Facesep);
445 draw(screen, r, screen, nil, r1.min);
447 if(y != ny-1){
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);
452 x = 0;
454 if(last < nfaces) /* first off-screen becomes visible */
455 drawface(faces[last], last-1);
456 else{
457 /* clear final spot */
458 r = facerect(last-first-1);
459 draw(screen, r, bgrnd, nil, r.min);
462 freeface(faces[j]);
463 memmove(faces+j, faces+j+1, (nfaces-(j+1))*sizeof(Face*));
464 nfaces--;
465 setlast();
466 drawarrows();
469 void
470 dodelete(int i)
472 Face *f;
474 f = faces[i];
475 if(history){
476 free(f->str[Sshow]);
477 f->str[Sshow] = estrdup("");
478 }else{
479 delface(i);
480 flushimage(display, 1);
484 void
485 delete(char *s, char *digest)
487 int i;
488 Face *f;
490 lockdisplay(display);
491 for(i=0; i<nfaces; i++){
492 f = faces[i];
493 if(digest != nil){
494 if(f->str[Sdigest]!=nil && strcmp(digest, f->str[Sdigest]) == 0){
495 dodelete(i);
496 break;
498 }else{
499 if(f->str[Sshow] && strcmp(s, f->str[Sshow]) == 0){
500 dodelete(i);
501 break;
505 unlockdisplay(display);
508 void
509 faceproc(void)
511 for(;;)
512 addface(nextface());
515 void
516 resized(void)
518 int i;
520 nacross = (Dx(screen->r)-2*facep.x+Facesep)/(Facesize+Facesep);
521 for(ndown=1; rectinrect(facerect(ndown*nacross), screen->r); ndown++)
523 setlast();
524 draw(screen, screen->r, bgrnd, nil, ZP);
525 enddate = ZP;
526 drawtime();
527 for(i=0; i<nfaces; i++)
528 drawface(faces[i], i);
529 drawarrows();
530 flushimage(display, 1);
533 void
534 eresized(int new)
536 lockdisplay(display);
537 if(new && getwindow(display, Refnone) < 0) {
538 fprint(2, "can't reattach to window\n");
539 killall("reattach");
541 resized();
542 unlockdisplay(display);
545 #if 0
546 int
547 getmouse(Mouse *m)
549 int n;
550 static int eof;
551 char buf[128];
553 if(eof)
554 return 0;
555 for(;;){
556 n = read(mousefd, buf, sizeof(buf));
557 if(n <= 0){
558 /* so callers needn't check return value every time */
559 eof = 1;
560 m->buttons = 0;
561 return 0;
563 //jpc n = eatomouse(m, buf, n);
564 if(n > 0)
565 return 1;
568 #endif
569 int
570 getmouse(Mouse *m)
572 static int eof;
574 if(eof)
575 return 0;
576 if( readmouse(mousectl) < 0 ) {
577 eof = 1;
578 m->buttons = 0;
579 return 0;
581 else {
582 *m = mousectl->m;
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; */
587 return 1;
591 enum
593 Clicksize = 3, /* pixels */
594 };
596 int
597 scroll(int but, Point p)
599 int delta;
601 delta = 0;
602 lockdisplay(display);
603 if(ptinrect(p, leftr) && first>0){
604 if(but == 2)
605 delta = -first;
606 else{
607 delta = nacross;
608 if(delta > first)
609 delta = first;
610 delta = -delta;
612 }else if(ptinrect(p, rightr) && last<nfaces){
613 if(but == 2)
614 delta = (nfaces-nacross*ndown) - first;
615 else{
616 delta = nacross;
617 if(delta > nfaces-last)
618 delta = nfaces-last;
621 first += delta;
622 last += delta;
623 unlockdisplay(display);
624 if(delta)
625 eresized(0);
626 return delta;
629 void
630 click(int button, Mouse *m)
632 Point p;
633 int i;
635 p = m->xy;
636 while(m->buttons == (1<<(button-1)))
637 getmouse(m);
638 if(m->buttons)
639 return;
640 if(abs(p.x-m->xy.x)>Clicksize || abs(p.y-m->xy.y)>Clicksize)
641 return;
642 switch(button){
643 case 1:
644 if(scroll(1, p))
645 break;
646 if(history){
647 /* click clears display */
648 lockdisplay(display);
649 for(i=0; i<nfaces; i++)
650 freeface(faces[i]);
651 free(faces);
652 faces=nil;
653 nfaces = 0;
654 unlockdisplay(display);
655 eresized(0);
656 return;
657 }else{
658 for(i=first; i<last; i++) /* clear vwhois faces */
659 if(ptinrect(p, facerect(i-first))
660 && strstr(faces[i]->str[Sshow], "/XXXvwhois")){
661 delface(i);
662 flushimage(display, 1);
665 break;
666 case 2:
667 scroll(2, p);
668 break;
669 case 3:
670 scroll(3, p);
671 lockdisplay(display);
672 for(i=first; i<last; i++)
673 if(ptinrect(p, facerect(i-first))){
674 showmail(faces[i]);
675 break;
677 unlockdisplay(display);
678 break;
682 void
683 mouseproc(void *dummy)
685 Mouse mouse;
687 while(getmouse(&mouse)){
688 if(mouse.buttons == 1)
689 click(1, &mouse);
690 else if(mouse.buttons == 2)
691 click(2, &mouse);
692 else if(mouse.buttons == 4)
693 click(3, &mouse);
695 while(mouse.buttons)
696 getmouse(&mouse);
700 void
701 killall(char *s)
703 int i, pid;
705 pid = getpid();
706 for(i=0; i<NPROC; i++)
707 if(pids[i] && pids[i]!=pid)
708 postnote(PNPROC, pids[i], "kill");
709 threadexitsall(s);
712 void
713 startproc(void (*f)(void), int index)
715 int pid;
717 switch(pid = rfork(RFPROC|RFNOWAIT)){ //jpc removed |RFMEM
718 case -1:
719 fprint(2, "faces: fork failed: %r\n");
720 killall("fork failed");
721 case 0:
722 f();
723 fprint(2, "faces: %s process exits\n", procnames[index]);
724 if(index >= 0)
725 killall("process died");
726 threadexitsall(nil);
728 if(index >= 0)
729 pids[index] = pid;
732 void
733 usage(void)
735 fprint(2, "usage: faces [-hi] [-m maildir] -W winsize\n");
736 threadexitsall("usage");
739 void
740 threadmain(int argc, char *argv[])
742 int i;
744 ARGBEGIN{
745 case 'h':
746 history++;
747 break;
748 case 'i':
749 initload++;
750 break;
751 case 'm':
752 addmaildir(EARGF(usage()));
753 maildir = nil;
754 break;
755 case 'W':
756 winsize = EARGF(usage());
757 break;
758 default:
759 usage();
760 }ARGEND
762 if(initdraw(nil, nil, "faces") < 0){
763 fprint(2, "faces: initdraw failed: %r\n");
764 threadexitsall("initdraw");
766 if(maildir)
767 addmaildir(maildir);
768 init();
769 unlockdisplay(display); /* initdraw leaves it locked */
770 display->locking = 1; /* tell library we're using the display lock */
771 setdate();
772 eresized(0);
774 pids[Mainp] = getpid();
775 pids[Timep] = proccreate(timeproc, nil, 16000);
776 pids[Mousep] = proccreate(mouseproc, nil, 16000);
777 if(initload)
778 for(i = 0; i < nmaildirs; i++)
779 loadmboxfaces(maildirs[i]);
780 faceproc();
781 fprint(2, "faces: %s process exits\n", procnames[Mainp]);
782 killall(nil);