main.c (13479B)
1 #include <u.h> 2 #include <libc.h> 3 #include <draw.h> 4 #include <plumb.h> 5 #include <regexp.h> 6 #include <bio.h> 7 #include <thread.h> 8 #include <mouse.h> 9 #include <cursor.h> 10 #include <9pclient.h> 11 #include "faces.h" 12 13 int history = 0; /* use old interface, showing history of mailbox rather than current state */ 14 int initload = 0; /* initialize program with contents of mail box */ 15 16 enum 17 { 18 Facesep = 6, /* must be even to avoid damaging background stipple */ 19 Infolines = 9, 20 21 HhmmTime = 18*60*60, /* max age of face to display hh:mm time */ 22 23 STACK = 32768 24 }; 25 26 enum 27 { 28 Mainp, 29 Timep, 30 Mousep, 31 Resizep, 32 NPROC 33 }; 34 35 char *procnames[] = { 36 "main", 37 "time", 38 "mouse", 39 "resize" 40 }; 41 42 Rectangle leftright = {0, 0, 20, 15}; 43 44 uchar leftdata[] = { 45 0x00, 0x80, 0x00, 0x01, 0x80, 0x00, 0x03, 0x80, 46 0x00, 0x07, 0x80, 0x00, 0x0f, 0x00, 0x00, 0x1f, 47 0xff, 0xf0, 0x3f, 0xff, 0xf0, 0xff, 0xff, 0xf0, 48 0x3f, 0xff, 0xf0, 0x1f, 0xff, 0xf0, 0x0f, 0x00, 49 0x00, 0x07, 0x80, 0x00, 0x03, 0x80, 0x00, 0x01, 50 0x80, 0x00, 0x00, 0x80, 0x00 51 }; 52 53 uchar rightdata[] = { 54 0x00, 0x10, 0x00, 0x00, 0x18, 0x00, 0x00, 0x1c, 55 0x00, 0x00, 0x1e, 0x00, 0x00, 0x0f, 0x00, 0xff, 56 0xff, 0x80, 0xff, 0xff, 0xc0, 0xff, 0xff, 0xf0, 57 0xff, 0xff, 0xc0, 0xff, 0xff, 0x80, 0x00, 0x0f, 58 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1c, 0x00, 0x00, 59 0x18, 0x00, 0x00, 0x10, 0x00 60 }; 61 62 CFsys *mailfs; 63 Mousectl *mousectl; 64 Image *blue; /* full arrow */ 65 Image *bgrnd; /* pale blue background color */ 66 Image *left; /* left-pointing arrow mask */ 67 Image *right; /* right-pointing arrow mask */ 68 Font *tinyfont; 69 Font *mediumfont; 70 Font *datefont; 71 int first, last; /* first and last visible face; last is first invisible */ 72 int nfaces; 73 int mousefd; 74 int nacross; 75 int ndown; 76 77 char date[64]; 78 Face **faces; 79 char *maildir = "mbox"; 80 ulong now; 81 82 Point datep = { 8, 6 }; 83 Point facep = { 8, 6+0+4 }; /* 0 updated to datefont->height in init() */ 84 Point enddate; /* where date ends on display; used to place arrows */ 85 Rectangle leftr; /* location of left arrow on display */ 86 Rectangle rightr; /* location of right arrow on display */ 87 void updatetimes(void); 88 void eresized(int); 89 90 void 91 setdate(void) 92 { 93 now = time(nil); 94 strcpy(date, ctime(now)); 95 date[4+4+3+5] = '\0'; /* change from Thu Jul 22 14:28:43 EDT 1999\n to Thu Jul 22 14:28 */ 96 } 97 98 void 99 init(void) 100 { 101 mailfs = nsmount("mail", nil); 102 if(mailfs == nil) 103 sysfatal("mount mail: %r"); 104 mousectl = initmouse(nil, screen); 105 if(mousectl == nil) 106 sysfatal("initmouse: %r"); 107 initplumb(); 108 109 /* make background color */ 110 bgrnd = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 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"); 117 } 118 119 loadimage(left, leftright, leftdata, sizeof leftdata); 120 loadimage(right, leftright, rightdata, sizeof rightdata); 121 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; 130 131 facep.y += datefont->height; 132 if(datefont->height & 1) /* stipple parity */ 133 facep.y++; 134 faces = nil; 135 } 136 137 void 138 drawtime(void) 139 { 140 Rectangle r; 141 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 */ 147 } 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); 152 } 153 154 void 155 timeproc(void *dummy) 156 { 157 for(;;){ 158 lockdisplay(display); 159 drawtime(); 160 updatetimes(); 161 flushimage(display, 1); 162 unlockdisplay(display); 163 sleep(60000); 164 setdate(); 165 } 166 } 167 168 int 169 alreadyseen(char *digest) 170 { 171 int i; 172 Face *f; 173 174 if(!digest) 175 return 0; 176 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; 182 } 183 return 0; 184 } 185 186 int 187 torune(Rune *r, char *s, int nr) 188 { 189 int i; 190 191 for(i=0; i<nr-1 && *s!='\0'; i++) 192 s += chartorune(r+i, s); 193 r[i] = L'\0'; 194 return i; 195 } 196 197 void 198 center(Font *f, Point p, char *s, Image *color) 199 { 200 int i, n, dx; 201 Rune rbuf[32]; 202 char sbuf[32*UTFmax+1]; 203 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; 211 } 212 sprint(sbuf, "%.*S", i, rbuf); 213 s = sbuf; 214 dx = stringwidth(f, s); 215 } 216 p.x += (Facesize-dx)/2; 217 string(screen, p, color, ZP, f, s); 218 } 219 220 Rectangle 221 facerect(int index) /* index is geometric; 0 is always upper left face */ 222 { 223 Rectangle r; 224 int x, y; 225 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; 237 } 238 239 static char *mon = "JanFebMarAprMayJunJulAugSepOctNovDec"; 240 char* 241 facetime(Face *f, int *recent) 242 { 243 static char buf[30]; 244 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; 253 } 254 } 255 256 void 257 drawface(Face *f, int i) 258 { 259 char *tstr; 260 Rectangle r; 261 Point p; 262 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); 281 } 282 } 283 284 void 285 updatetimes(void) 286 { 287 int i; 288 Face *f; 289 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); 296 } 297 } 298 299 void 300 setlast(void) 301 { 302 last = first+nacross*ndown; 303 if(last > nfaces) 304 last = nfaces; 305 } 306 307 void 308 drawarrows(void) 309 { 310 Point p; 311 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); 321 } 322 323 void 324 addface(Face *f) /* always adds at 0 */ 325 { 326 Face **ofaces; 327 Rectangle r0, r1, r; 328 int y, nx, ny; 329 330 if(f == nil) 331 return; 332 if(first != 0){ 333 first = 0; 334 eresized(0); 335 } 336 findbit(f); 337 338 nx = nacross; 339 ny = (nfaces+(nx-1)) / nx; 340 341 lockdisplay(display); 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); 353 } 354 } 355 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); 367 } 368 369 void 370 loadmboxfaces(char *maildir) 371 { 372 CFid *dirfd; 373 Dir *d; 374 int i, n; 375 376 dirfd = fsopen(mailfs, maildir, OREAD); 377 if(dirfd != nil){ 378 while((n = fsdirread(dirfd, &d)) > 0){ 379 for(i=0; i<n; i++) 380 addface(dirface(maildir, d[i].name)); 381 free(d); 382 } 383 fsclose(dirfd); 384 }else 385 sysfatal("open %s: %r", maildir); 386 } 387 388 void 389 freeface(Face *f) 390 { 391 int i; 392 393 if(f->file!=nil && f->bit!=f->file->image) 394 freeimage(f->bit); 395 freefacefile(f->file); 396 for(i=0; i<Nstring; i++) 397 free(f->str[i]); 398 free(f); 399 } 400 401 void 402 delface(int j) 403 { 404 Rectangle r0, r1, r; 405 int nx, ny, x, y; 406 407 if(j < first) 408 first--; 409 else if(j < last){ 410 nx = nacross; 411 ny = (nfaces+(nx-1)) / nx; 412 x = (j-first)%nx; 413 for(y=(j-first)/nx; y<ny; y++){ 414 if(x != nx-1){ 415 /* move them along */ 416 r0 = facerect(y*nx+x); 417 r1 = facerect(y*nx+x+1); 418 r = r0; 419 r.max.x = r.min.x + (nx - x - 1)*(Facesize+Facesep); 420 draw(screen, r, screen, nil, r1.min); 421 } 422 if(y != ny-1){ 423 /* copy one up from row below */ 424 r = facerect((y+1)*nx); 425 draw(screen, facerect(y*nx+nx-1), screen, nil, r.min); 426 } 427 x = 0; 428 } 429 if(last < nfaces) /* first off-screen becomes visible */ 430 drawface(faces[last], last-1); 431 else{ 432 /* clear final spot */ 433 r = facerect(last-first-1); 434 draw(screen, r, bgrnd, nil, r.min); 435 } 436 } 437 freeface(faces[j]); 438 memmove(faces+j, faces+j+1, (nfaces-(j+1))*sizeof(Face*)); 439 nfaces--; 440 setlast(); 441 drawarrows(); 442 } 443 444 void 445 dodelete(int i) 446 { 447 Face *f; 448 449 f = faces[i]; 450 if(history){ 451 free(f->str[Sshow]); 452 f->str[Sshow] = estrdup(""); 453 }else{ 454 delface(i); 455 flushimage(display, 1); 456 } 457 } 458 459 void 460 delete(char *s, char *digest) 461 { 462 int i; 463 Face *f; 464 465 lockdisplay(display); 466 for(i=0; i<nfaces; i++){ 467 f = faces[i]; 468 if(digest != nil){ 469 if(f->str[Sdigest]!=nil && strcmp(digest, f->str[Sdigest]) == 0){ 470 dodelete(i); 471 break; 472 } 473 }else{ 474 if(f->str[Sshow] && strcmp(s, f->str[Sshow]) == 0){ 475 dodelete(i); 476 break; 477 } 478 } 479 } 480 unlockdisplay(display); 481 } 482 483 void 484 faceproc(void) 485 { 486 for(;;) 487 addface(nextface()); 488 } 489 490 void 491 resized(void) 492 { 493 int i; 494 495 nacross = (Dx(screen->r)-2*facep.x+Facesep)/(Facesize+Facesep); 496 for(ndown=1; rectinrect(facerect(ndown*nacross), screen->r); ndown++) 497 ; 498 setlast(); 499 draw(screen, screen->r, bgrnd, nil, ZP); 500 enddate = ZP; 501 drawtime(); 502 for(i=0; i<nfaces; i++) 503 drawface(faces[i], i); 504 drawarrows(); 505 flushimage(display, 1); 506 } 507 508 void 509 eresized(int new) 510 { 511 lockdisplay(display); 512 if(new && getwindow(display, Refnone) < 0) { 513 fprint(2, "can't reattach to window\n"); 514 killall("reattach"); 515 } 516 resized(); 517 unlockdisplay(display); 518 } 519 520 void 521 resizeproc(void *v) 522 { 523 USED(v); 524 525 while(recv(mousectl->resizec, 0) == 1) 526 eresized(1); 527 } 528 529 int 530 getmouse(Mouse *m) 531 { 532 static int eof; 533 534 if(eof) 535 return 0; 536 if(readmouse(mousectl) < 0){ 537 eof = 1; 538 m->buttons = 0; 539 return 0; 540 } 541 *m = mousectl->m; 542 return 1; 543 } 544 545 enum 546 { 547 Clicksize = 3, /* pixels */ 548 }; 549 550 int 551 scroll(int but, Point p) 552 { 553 int delta; 554 555 delta = 0; 556 lockdisplay(display); 557 if(ptinrect(p, leftr) && first>0){ 558 if(but == 2) 559 delta = -first; 560 else{ 561 delta = nacross; 562 if(delta > first) 563 delta = first; 564 delta = -delta; 565 } 566 }else if(ptinrect(p, rightr) && last<nfaces){ 567 if(but == 2) 568 delta = (nfaces-nacross*ndown) - first; 569 else{ 570 delta = nacross; 571 if(delta > nfaces-last) 572 delta = nfaces-last; 573 } 574 } 575 first += delta; 576 last += delta; 577 unlockdisplay(display); 578 if(delta) 579 eresized(0); 580 return delta; 581 } 582 583 void 584 click(int button, Mouse *m) 585 { 586 Point p; 587 int i; 588 589 p = m->xy; 590 while(m->buttons == (1<<(button-1))) 591 getmouse(m); 592 if(m->buttons) 593 return; 594 if(abs(p.x-m->xy.x)>Clicksize || abs(p.y-m->xy.y)>Clicksize) 595 return; 596 switch(button){ 597 case 1: 598 if(scroll(1, p)) 599 break; 600 if(history){ 601 /* click clears display */ 602 lockdisplay(display); 603 for(i=0; i<nfaces; i++) 604 freeface(faces[i]); 605 free(faces); 606 faces=nil; 607 nfaces = 0; 608 unlockdisplay(display); 609 eresized(0); 610 return; 611 }else{ 612 for(i=first; i<last; i++) /* clear vwhois faces */ 613 if(ptinrect(p, facerect(i-first)) 614 && strstr(faces[i]->str[Sshow], "/XXXvwhois")){ 615 lockdisplay(display); 616 delface(i); 617 flushimage(display, 1); 618 unlockdisplay(display); 619 break; 620 } 621 } 622 break; 623 case 2: 624 scroll(2, p); 625 break; 626 case 3: 627 scroll(3, p); 628 lockdisplay(display); 629 for(i=first; i<last; i++) 630 if(ptinrect(p, facerect(i-first))){ 631 showmail(faces[i]); 632 break; 633 } 634 unlockdisplay(display); 635 break; 636 } 637 } 638 639 void 640 mouseproc(void *v) 641 { 642 Mouse mouse; 643 USED(v); 644 645 while(getmouse(&mouse)){ 646 if(mouse.buttons == 1) 647 click(1, &mouse); 648 else if(mouse.buttons == 2) 649 click(2, &mouse); 650 else if(mouse.buttons == 4) 651 click(3, &mouse); 652 653 while(mouse.buttons) 654 getmouse(&mouse); 655 } 656 } 657 658 void 659 killall(char *s) 660 { 661 threadexitsall(s); 662 } 663 664 void 665 usage(void) 666 { 667 fprint(2, "usage: faces [-hi] [-m maildir] -W winsize\n"); 668 threadexitsall("usage"); 669 } 670 671 void 672 threadmain(int argc, char *argv[]) 673 { 674 int i; 675 676 rfork(RFNOTEG); 677 678 ARGBEGIN{ 679 case 'h': 680 history++; 681 break; 682 case 'i': 683 initload++; 684 break; 685 case 'm': 686 addmaildir(EARGF(usage())); 687 maildir = nil; 688 break; 689 case 'W': 690 winsize = EARGF(usage()); 691 break; 692 default: 693 usage(); 694 }ARGEND 695 696 if(initdraw(nil, nil, "faces") < 0){ 697 fprint(2, "faces: initdraw failed: %r\n"); 698 threadexitsall("initdraw"); 699 } 700 if(maildir) 701 addmaildir(maildir); 702 init(); 703 unlockdisplay(display); /* initdraw leaves it locked */ 704 display->locking = 1; /* tell library we're using the display lock */ 705 setdate(); 706 eresized(0); 707 708 proccreate(timeproc, nil, STACK); 709 proccreate(mouseproc, nil, STACK); 710 proccreate(resizeproc, nil, STACK); 711 if(initload) 712 for(i = 0; i < nmaildirs; i++) 713 loadmboxfaces(maildirs[i]); 714 faceproc(); 715 killall(nil); 716 }