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