main.c 13 KB

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