main.c 13 KB

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