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