main.c 13 KB

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