]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/faces/main.c
cwfs: fix listen filedescriptor leaks
[plan9front.git] / sys / src / cmd / faces / main.c
1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <plumb.h>
5 #include <regexp.h>
6 #include <event.h>      /* for support routines only */
7 #include <bio.h>
8 #include "faces.h"
9
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 */
12
13 enum
14 {
15         Facesep = 6,    /* must be even to avoid damaging background stipple */
16         Infolines = 9,
17
18         HhmmTime = 18*60*60,    /* max age of face to display hh:mm time */
19 };
20
21 enum
22 {
23         Mainp,
24         Timep,
25         Mousep,
26         NPROC
27 };
28
29 int pids[NPROC];
30 char *procnames[] = {
31         "main",
32         "time",
33         "mouse"
34 };
35
36 Rectangle leftright = {0, 0, 20, 15};
37
38 uchar leftdata[] = {
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
45 };
46
47 uchar rightdata[] = {
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
54 };
55
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 */
60 Font    *tinyfont;
61 Font    *mediumfont;
62 Font    *datefont;
63 int     first, last;    /* first and last visible face; last is first invisible */
64 int     nfaces;
65 int     mousefd;
66 int     nacross;
67 int     ndown;
68
69 char    date[64];
70 Face    **faces;
71 char    *maildir = "/mail/fs/mbox";
72 ulong   now;
73
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);
80
81 void
82 setdate(void)
83 {
84         now = time(nil);
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 */
87 }
88
89 void
90 init(void)
91 {
92         mousefd = open("/dev/mouse", OREAD);
93         if(mousefd < 0){
94                 fprint(2, "faces: can't open mouse: %r\n");
95                 exits("mouse");
96         }
97         initplumb();
98
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");
106                 exits("image");
107         }
108
109         loadimage(left, leftright, leftdata, sizeof leftdata);
110         loadimage(right, leftright, rightdata, sizeof rightdata);
111
112         /* initialize little fonts */
113         tinyfont = openfont(display, "/lib/font/bit/misc/ascii.5x7.font");
114         if(tinyfont == nil)
115                 tinyfont = font;
116         mediumfont = openfont(display, "/lib/font/bit/misc/unicode.6x13.font");
117         if(mediumfont == nil)
118                 mediumfont = font;
119         datefont = font;
120
121         facep.y += datefont->height;
122         if(datefont->height & 1)        /* stipple parity */
123                 facep.y++;
124         faces = nil;
125 }
126
127 void
128 drawtime(void)
129 {
130         Rectangle r;
131
132         r.min = addpt(screen->r.min, datep);
133         if(eqpt(enddate, ZP)){
134                 enddate = r.min;
135                 enddate.x += stringwidth(datefont, "Wed May 30 22:54"); /* nice wide string */
136                 enddate.x += Facesep;   /* for safety */
137         }
138         r.max.x = enddate.x;
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);
142 }
143
144 void
145 timeproc(void)
146 {
147         for(;;){
148                 lockdisplay(display);
149                 drawtime();
150                 updatetimes();
151                 flushimage(display, 1);
152                 unlockdisplay(display);
153                 now = time(nil);
154                 sleep(((60 - now%60) + 1)*1000); /* wait for minute to change */
155                 setdate();
156         }
157 }
158
159 int
160 alreadyseen(char *digest)
161 {
162         int i;
163         Face *f;
164
165         if(!digest)
166                 return 0;
167
168         /* can do accurate check */
169         for(i=0; i<nfaces; i++){
170                 f = faces[i];
171                 if(f->str[Sdigest]!=nil && strcmp(digest, f->str[Sdigest])==0)
172                         return 1;
173         }
174         return 0;
175 }
176
177 int
178 torune(Rune *r, char *s, int nr)
179 {
180         int i;
181
182         for(i=0; i<nr-1 && *s!='\0'; i++)
183                 s += chartorune(r+i, s);
184         r[i] = L'\0';
185         return i;
186 }
187
188 void
189 center(Font *f, Point p, char *s, Image *color)
190 {
191         int i, n, dx;
192         Rune rbuf[32];
193         char sbuf[32*UTFmax+1];
194
195         dx = stringwidth(f, s);
196         if(dx > Facesize){
197                 n = torune(rbuf, s, nelem(rbuf));
198                 for(i=0; i<n; i++){
199                         dx = runestringnwidth(f, rbuf, i+1);
200                         if(dx > Facesize)
201                                 break;
202                 }
203                 sprint(sbuf, "%.*S", i, rbuf);
204                 s = sbuf;
205                 dx = stringwidth(f, s);
206         }
207         p.x += (Facesize-dx)/2;
208         string(screen, p, color, ZP, f, s);
209 }
210
211 Rectangle
212 facerect(int index)     /* index is geometric; 0 is always upper left face */
213 {
214         Rectangle r;
215         int x, y;
216
217         x = index % nacross;
218         y = index / nacross;
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)
226                 r.max.x = r.min.x;
227         return r;
228 }
229
230 static char *mon = "JanFebMarAprMayJunJulAugSepOctNovDec";
231 char*
232 facetime(Face *f, int *recent)
233 {
234         static char buf[30];
235
236         if((long)(now - f->time) > HhmmTime){
237                 *recent = 0;
238                 sprint(buf, "%.3s %2d", mon+3*f->tm.mon, f->tm.mday);
239                 return buf;
240         }else{
241                 *recent = 1;
242                 sprint(buf, "%02d:%02d", f->tm.hour, f->tm.min);
243                 return buf;
244         }
245 }
246
247 void
248 drawface(Face *f, int i)
249 {
250         char *tstr;
251         Rectangle r;
252         Point p;
253
254         if(f == nil)
255                 return;
256         if(i<first || i>=last)
257                 return;
258         r = facerect(i-first);
259         draw(screen, r, bgrnd, nil, ZP);
260         draw(screen, r, f->bit, f->mask, ZP);
261         r.min.y += Facesize;
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);
266         if(f->unknown){
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);
272         }
273 }
274
275 void
276 updatetimes(void)
277 {
278         int i;
279         Face *f;
280
281         for(i=0; i<nfaces; i++){
282                 f = faces[i];
283                 if(f == nil)
284                         continue;
285                 if(((long)(now - f->time) <= HhmmTime) != f->recent)
286                         drawface(f, i);
287         }       
288 }
289
290 void
291 setlast(void)
292 {
293         last = first+nacross*ndown;
294         if(last > nfaces)
295                 last = nfaces;
296 }
297
298 void
299 drawarrows(void)
300 {
301         Point p;
302
303         p = enddate;
304         p.x += Facesep;
305         if(p.x & 1)
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);
312 }
313
314 void
315 addface(Face *f)        /* always adds at 0 */
316 {
317         Face **ofaces;
318         Rectangle r0, r1, r;
319         int y, nx, ny;
320
321         if(f == nil)
322                 return;
323         lockdisplay(display);
324         if(first != 0){
325                 first = 0;
326                 resized();
327         }
328         findbit(f);
329
330         nx = nacross;
331         ny = (nfaces+(nx-1)) / nx;
332
333         for(y=ny; y>=0; y--){
334                 /* move them along */
335                 r0 = facerect(y*nx+0);
336                 r1 = facerect(y*nx+1);
337                 r = r1;
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 */
341                 if(y != 0){
342                         r = facerect((y-1)*nx+nx-1);
343                         draw(screen, r0, screen, nil, r.min);
344                 }
345         }
346
347         ofaces = faces;
348         faces = emalloc((nfaces+1)*sizeof(Face*));
349         memmove(faces+1, ofaces, nfaces*(sizeof(Face*)));
350         free(ofaces);
351         nfaces++;
352         setlast();
353         drawarrows();
354         faces[0] = f;
355         drawface(f, 0);
356         flushimage(display, 1);
357         unlockdisplay(display);
358 }
359
360 void
361 loadmboxfaces(char *maildir)
362 {
363         int dirfd;
364         Dir *d;
365         int i, n;
366
367         dirfd = open(maildir, OREAD);
368         if(dirfd >= 0){
369                 chdir(maildir);
370                 while((n = dirread(dirfd, &d)) > 0){
371                         for(i=0; i<n; i++)
372                                 addface(dirface(maildir, d[i].name));
373                         free(d);
374                 }
375                 close(dirfd);
376         }
377 }
378
379 void
380 freeface(Face *f)
381 {
382         int i;
383
384         if(f->file==nil || f->bit!=f->file->image)
385                 freeimage(f->bit);
386         freefacefile(f->file);
387         for(i=0; i<Nstring; i++)
388                 free(f->str[i]);
389         free(f);
390 }
391
392 void
393 delface(int j)
394 {
395         Rectangle r0, r1, r;
396         int nx, ny, x, y;
397
398         if(j < first)
399                 first--;
400         else if(j < last){
401                 nx = nacross;
402                 ny = (nfaces+(nx-1)) / nx;
403                 x = (j-first)%nx;
404                 for(y=(j-first)/nx; y<ny; y++){
405                         if(x != nx-1){
406                                 /* move them along */
407                                 r0 = facerect(y*nx+x);
408                                 r1 = facerect(y*nx+x+1);
409                                 r = r0;
410                                 r.max.x = r.min.x + (nx - x - 1)*(Facesize+Facesep);
411                                 draw(screen, r, screen, nil, r1.min);
412                         }
413                         if(y != ny-1){
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);
417                         }
418                         x = 0;
419                 }
420                 if(last < nfaces)       /* first off-screen becomes visible */
421                         drawface(faces[last], last-1);
422                 else{
423                         /* clear final spot */
424                         r = facerect(last-first-1);
425                         draw(screen, r, bgrnd, nil, r.min);
426                 }
427         }
428         freeface(faces[j]);
429         memmove(faces+j, faces+j+1, (nfaces-(j+1))*sizeof(Face*));
430         nfaces--;
431         setlast();
432         drawarrows();
433 }
434
435 void
436 dodelete(int i)
437 {
438         Face *f;
439
440         f = faces[i];
441         if(history){
442                 free(f->str[Sshow]);
443                 f->str[Sshow] = estrdup("");
444         }else{
445                 delface(i);
446                 flushimage(display, 1);
447         }
448 }
449
450 void
451 delete(char *s, char *digest)
452 {
453         int i;
454         Face *f;
455
456         lockdisplay(display);
457         for(i=0; i<nfaces; i++){
458                 f = faces[i];
459                 if(digest != nil){
460                         if(f->str[Sdigest]!=nil && strcmp(digest, f->str[Sdigest]) == 0){
461                                 dodelete(i);
462                                 break;
463                         }
464                 }else{
465                         if(f->str[Sshow] && strcmp(s, f->str[Sshow]) == 0){
466                                 dodelete(i);
467                                 break;
468                         }
469                 }
470         }
471         unlockdisplay(display);
472 }
473
474 void
475 faceproc(void)
476 {
477         for(;;)
478                 addface(nextface());
479 }
480
481 void
482 resized(void)
483 {
484         int i;
485
486         nacross = (Dx(screen->r)-2*facep.x+Facesep)/(Facesize+Facesep);
487         for(ndown=1; rectinrect(facerect(ndown*nacross), screen->r); ndown++)
488                 ;
489         setlast();
490         draw(screen, screen->r, bgrnd, nil, ZP);
491         enddate = ZP;
492         drawtime();
493         for(i=0; i<nfaces; i++)
494                 drawface(faces[i], i);
495         drawarrows();
496         flushimage(display, 1);
497 }
498
499 void
500 eresized(int new)
501 {
502         lockdisplay(display);
503         if(new && getwindow(display, Refnone) < 0) {
504                 fprint(2, "can't reattach to window\n");
505                 killall("reattach");
506         }
507         resized();
508         unlockdisplay(display);
509 }
510
511 int
512 getmouse(Mouse *m)
513 {
514         int n;
515         static int eof;
516         char buf[128];
517
518         if(eof)
519                 return 0;
520         for(;;){
521                 n = read(mousefd, buf, sizeof(buf));
522                 if(n <= 0){
523                         /* so callers needn't check return value every time */
524                         eof = 1;
525                         m->buttons = 0;
526                         return 0;
527                 }
528                 n = eatomouse(m, buf, n);
529                 if(n > 0)
530                         return 1;
531         }
532 }
533
534 enum
535 {
536         Clicksize       = 3,            /* pixels */
537 };
538
539 int
540 scroll(int but, Point p)
541 {
542         int delta;
543
544         delta = 0;
545         lockdisplay(display);
546         if(ptinrect(p, leftr) && first>0){
547                 if(but == 2)
548                         delta = -first;
549                 else{
550                         delta = nacross;
551                         if(delta > first)
552                                 delta = first;
553                         delta = -delta;
554                 }
555         }else if(ptinrect(p, rightr) && last<nfaces){
556                 if(but == 2)
557                         delta = (nfaces-nacross*ndown) - first;
558                 else{
559                         delta = nacross;
560                         if(delta > nfaces-last)
561                                 delta = nfaces-last;
562                 }
563         }
564         first += delta;
565         last += delta;
566         unlockdisplay(display);
567         if(delta)
568                 eresized(0);
569         return delta;
570 }
571
572 void
573 click(int button, Mouse *m)
574 {
575         Point p;
576         int i;
577
578         p = m->xy;
579         while(m->buttons == (1<<(button-1)))
580                 getmouse(m);
581         if(m->buttons)
582                 return;
583         if(abs(p.x-m->xy.x)>Clicksize || abs(p.y-m->xy.y)>Clicksize)
584                 return;
585         switch(button){
586         case 1:
587                 if(scroll(1, p))
588                         break;
589                 if(history){
590                         /* click clears display */
591                         lockdisplay(display);
592                         for(i=0; i<nfaces; i++)
593                                 freeface(faces[i]);
594                         free(faces);
595                         faces=nil;
596                         nfaces = 0;
597                         unlockdisplay(display);
598                         eresized(0);
599                         return;
600                 }else{
601                         for(i=first; i<last; i++)       /* clear vwhois faces */
602                                 if(ptinrect(p, facerect(i-first)) 
603                                 && strstr(faces[i]->str[Sshow], "/XXXvwhois")){
604                                         delface(i);
605                                         flushimage(display, 1);
606                                 }
607                 }
608                 break;
609         case 2:
610                 scroll(2, p);
611                 break;
612         case 3:
613                 scroll(3, p);
614                 lockdisplay(display);
615                 for(i=first; i<last; i++)
616                         if(ptinrect(p, facerect(i-first))){
617                                 showmail(faces[i]);
618                                 break;
619                         }
620                 unlockdisplay(display);
621                 break;
622         }
623 }
624
625 void
626 mouseproc(void)
627 {
628         Mouse mouse;
629
630         while(getmouse(&mouse)){
631                 if(mouse.buttons == 1)
632                         click(1, &mouse);
633                 else if(mouse.buttons == 2)
634                         click(2, &mouse);
635                 else if(mouse.buttons == 4)
636                         click(3, &mouse);
637
638                 while(mouse.buttons)
639                         getmouse(&mouse);
640         }
641 }
642
643 void
644 killall(char *s)
645 {
646         int i, pid;
647
648         pid = getpid();
649         for(i=0; i<NPROC; i++)
650                 if(pids[i] && pids[i]!=pid)
651                         postnote(PNPROC, pids[i], "kill");
652         exits(s);
653 }
654
655 void
656 startproc(void (*f)(void), int index)
657 {
658         int pid;
659
660         switch(pid = rfork(RFPROC|RFMEM|RFNOWAIT)){
661         case -1:
662                 fprint(2, "faces: fork failed: %r\n");
663                 killall("fork failed");
664         case 0:
665                 f();
666                 fprint(2, "faces: %s process exits\n", procnames[index]);
667                 if(index >= 0)
668                         killall("process died");
669                 exits(nil);
670         }
671         if(index >= 0)
672                 pids[index] = pid;
673 }
674
675 void
676 usage(void)
677 {
678         fprint(2, "usage: faces [-hi] [-m maildir]\n");
679         exits("usage");
680 }
681
682 void
683 main(int argc, char *argv[])
684 {
685         int i;
686
687         ARGBEGIN{
688         case 'h':
689                 history++;
690                 break;
691         case 'i':
692                 initload++;
693                 break;
694         case 'm':
695                 addmaildir(EARGF(usage()));
696                 maildir = nil;
697                 break;
698         default:
699                 usage();
700         }ARGEND
701
702         if(initdraw(nil, nil, "faces") < 0){
703                 fprint(2, "faces: initdraw failed: %r\n");
704                 exits("initdraw");
705         }
706         if(maildir)
707                 addmaildir(maildir);
708         init();
709         unlockdisplay(display); /* initdraw leaves it locked */
710         display->locking = 1;   /* tell library we're using the display lock */
711         setdate();
712         eresized(0);
713
714         pids[Mainp] = getpid();
715         startproc(timeproc, Timep);
716         startproc(mouseproc, Mousep);
717         if(initload)
718                 for(i = 0; i < nmaildirs; i++)
719                  loadmboxfaces(maildirs[i]);
720         faceproc();
721         fprint(2, "faces: %s process exits\n", procnames[Mainp]);
722         killall(nil);
723 }