aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/faces/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/faces/main.c')
-rw-r--r--src/cmd/faces/main.c783
1 files changed, 783 insertions, 0 deletions
diff --git a/src/cmd/faces/main.c b/src/cmd/faces/main.c
new file mode 100644
index 00000000..4be56230
--- /dev/null
+++ b/src/cmd/faces/main.c
@@ -0,0 +1,783 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <plumb.h>
+#include <regexp.h>
+//jpc #include <event.h> /* for support routines only */
+#include <bio.h>
+#include <thread.h>
+#include <mouse.h>
+#include <cursor.h>
+#include <9pclient.h>
+#include "faces.h"
+
+int history = 0; /* use old interface, showing history of mailbox rather than current state */
+int initload = 0; /* initialize program with contents of mail box */
+
+enum
+{
+ Facesep = 6, /* must be even to avoid damaging background stipple */
+ Infolines = 9,
+
+ HhmmTime = 18*60*60, /* max age of face to display hh:mm time */
+};
+
+enum
+{
+ Mainp,
+ Timep,
+ Mousep,
+ NPROC
+};
+
+int pids[NPROC];
+char *procnames[] = {
+ "main",
+ "time",
+ "mouse"
+};
+
+Rectangle leftright = {0, 0, 20, 15};
+
+uchar leftdata[] = {
+ 0x00, 0x80, 0x00, 0x01, 0x80, 0x00, 0x03, 0x80,
+ 0x00, 0x07, 0x80, 0x00, 0x0f, 0x00, 0x00, 0x1f,
+ 0xff, 0xf0, 0x3f, 0xff, 0xf0, 0xff, 0xff, 0xf0,
+ 0x3f, 0xff, 0xf0, 0x1f, 0xff, 0xf0, 0x0f, 0x00,
+ 0x00, 0x07, 0x80, 0x00, 0x03, 0x80, 0x00, 0x01,
+ 0x80, 0x00, 0x00, 0x80, 0x00
+};
+
+uchar rightdata[] = {
+ 0x00, 0x10, 0x00, 0x00, 0x18, 0x00, 0x00, 0x1c,
+ 0x00, 0x00, 0x1e, 0x00, 0x00, 0x0f, 0x00, 0xff,
+ 0xff, 0x80, 0xff, 0xff, 0xc0, 0xff, 0xff, 0xf0,
+ 0xff, 0xff, 0xc0, 0xff, 0xff, 0x80, 0x00, 0x0f,
+ 0x00, 0x00, 0x1e, 0x00, 0x00, 0x1c, 0x00, 0x00,
+ 0x18, 0x00, 0x00, 0x10, 0x00
+};
+
+CFsys *upasfs;
+Mousectl *mousectl;
+Image *blue; /* full arrow */
+Image *bgrnd; /* pale blue background color */
+Image *left; /* left-pointing arrow mask */
+Image *right; /* right-pointing arrow mask */
+Font *tinyfont;
+Font *mediumfont;
+Font *datefont;
+int first, last; /* first and last visible face; last is first invisible */
+int nfaces;
+int mousefd;
+int nacross;
+int ndown;
+
+char date[64];
+Face **faces;
+char *maildir = "/mail/fs/mbox";
+ulong now;
+
+Point datep = { 8, 6 };
+Point facep = { 8, 6+0+4 }; /* 0 updated to datefont->height in init() */
+Point enddate; /* where date ends on display; used to place arrows */
+Rectangle leftr; /* location of left arrow on display */
+Rectangle rightr; /* location of right arrow on display */
+void updatetimes(void);
+
+void
+setdate(void)
+{
+ now = time(nil);
+ strcpy(date, ctime(now));
+ date[4+4+3+5] = '\0'; /* change from Thu Jul 22 14:28:43 EDT 1999\n to Thu Jul 22 14:28 */
+}
+
+void
+init(void)
+{
+#if 0
+ mousefd = open("/dev/mouse", OREAD);
+ if(mousefd < 0){
+ fprint(2, "faces: can't open mouse: %r\n");
+ threadexitsall("mouse");
+ }
+#endif
+ upasfs = nsmount("upasfs",nil);
+ mousectl = initmouse(nil,screen);
+ initplumb();
+
+ /* make background color */
+ bgrnd = allocimagemix(display, DPalebluegreen, DWhite);
+ blue = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x008888FF); /* blue-green */
+ left = allocimage(display, leftright, GREY1, 0, DWhite);
+ right = allocimage(display, leftright, GREY1, 0, DWhite);
+ if(bgrnd==nil || blue==nil || left==nil || right==nil){
+ fprint(2, "faces: can't create images: %r\n");
+ threadexitsall("image");
+ }
+
+ loadimage(left, leftright, leftdata, sizeof leftdata);
+ loadimage(right, leftright, rightdata, sizeof rightdata);
+
+ /* initialize little fonts */
+ tinyfont = openfont(display, "/lib/font/bit/misc/ascii.5x7.font");
+ if(tinyfont == nil)
+ tinyfont = font;
+ mediumfont = openfont(display, "/lib/font/bit/pelm/latin1.8.font");
+ if(mediumfont == nil)
+ mediumfont = font;
+ datefont = font;
+
+ facep.y += datefont->height;
+ if(datefont->height & 1) /* stipple parity */
+ facep.y++;
+ faces = nil;
+}
+
+void
+drawtime(void)
+{
+ Rectangle r;
+
+ r.min = addpt(screen->r.min, datep);
+ if(eqpt(enddate, ZP)){
+ enddate = r.min;
+ enddate.x += stringwidth(datefont, "Wed May 30 22:54"); /* nice wide string */
+ enddate.x += Facesep; /* for safety */
+ }
+ r.max.x = enddate.x;
+ r.max.y = enddate.y+datefont->height;
+ draw(screen, r, bgrnd, nil, ZP);
+ string(screen, r.min, display->black, ZP, datefont, date);
+}
+
+void
+timeproc(void *dummy)
+{
+ for(;;){
+ lockdisplay(display);
+ drawtime();
+ updatetimes();
+ flushimage(display, 1);
+ unlockdisplay(display);
+ sleep(60000);
+ setdate();
+ }
+}
+
+int
+alreadyseen(char *digest)
+{
+ int i;
+ Face *f;
+
+ if(!digest)
+ return 0;
+
+ /* can do accurate check */
+ for(i=0; i<nfaces; i++){
+ f = faces[i];
+ if(f->str[Sdigest]!=nil && strcmp(digest, f->str[Sdigest])==0)
+ return 1;
+ }
+ return 0;
+}
+
+int
+torune(Rune *r, char *s, int nr)
+{
+ int i;
+
+ for(i=0; i<nr-1 && *s!='\0'; i++)
+ s += chartorune(r+i, s);
+ r[i] = L'\0';
+ return i;
+}
+
+void
+center(Font *f, Point p, char *s, Image *color)
+{
+ int i, n, dx;
+ Rune rbuf[32];
+ char sbuf[32*UTFmax+1];
+
+ dx = stringwidth(f, s);
+ if(dx > Facesize){
+ n = torune(rbuf, s, nelem(rbuf));
+ for(i=0; i<n; i++){
+ dx = runestringnwidth(f, rbuf, i+1);
+ if(dx > Facesize)
+ break;
+ }
+ sprint(sbuf, "%.*S", i, rbuf);
+ s = sbuf;
+ dx = stringwidth(f, s);
+ }
+ p.x += (Facesize-dx)/2;
+ string(screen, p, color, ZP, f, s);
+}
+
+Rectangle
+facerect(int index) /* index is geometric; 0 is always upper left face */
+{
+ Rectangle r;
+ int x, y;
+
+ x = index % nacross;
+ y = index / nacross;
+ r.min = addpt(screen->r.min, facep);
+ r.min.x += x*(Facesize+Facesep);
+ r.min.y += y*(Facesize+Facesep+2*mediumfont->height);
+ r.max = addpt(r.min, Pt(Facesize, Facesize));
+ r.max.y += 2*mediumfont->height;
+ /* simple fix to avoid drawing off screen, allowing customers to use position */
+ if(index<0 || index>=nacross*ndown)
+ r.max.x = r.min.x;
+ return r;
+}
+
+static char *mon = "JanFebMarAprMayJunJulAugSepOctNovDec";
+char*
+facetime(Face *f, int *recent)
+{
+ static char buf[30];
+
+ if((long)(now - f->time) > HhmmTime){
+ *recent = 0;
+ sprint(buf, "%.3s %2d", mon+3*f->tm.mon, f->tm.mday);
+ return buf;
+ }else{
+ *recent = 1;
+ sprint(buf, "%02d:%02d", f->tm.hour, f->tm.min);
+ return buf;
+ }
+}
+
+void
+drawface(Face *f, int i)
+{
+ char *tstr;
+ Rectangle r;
+ Point p;
+
+ if(f == nil)
+ return;
+ if(i<first || i>=last)
+ return;
+ r = facerect(i-first);
+ draw(screen, r, bgrnd, nil, ZP);
+ draw(screen, r, f->bit, f->mask, ZP);
+ r.min.y += Facesize;
+ center(mediumfont, r.min, f->str[Suser], display->black);
+ r.min.y += mediumfont->height;
+ tstr = facetime(f, &f->recent);
+ center(mediumfont, r.min, tstr, display->black);
+ if(f->unknown){
+ r.min.y -= mediumfont->height + tinyfont->height + 2;
+ for(p.x=-1; p.x<=1; p.x++)
+ for(p.y=-1; p.y<=1; p.y++)
+ center(tinyfont, addpt(r.min, p), f->str[Sdomain], display->white);
+ center(tinyfont, r.min, f->str[Sdomain], display->black);
+ }
+}
+
+void
+updatetimes(void)
+{
+ int i;
+ Face *f;
+
+ for(i=0; i<nfaces; i++){
+ f = faces[i];
+ if(f == nil)
+ continue;
+ if(((long)(now - f->time) <= HhmmTime) != f->recent)
+ drawface(f, i);
+ }
+}
+
+void
+setlast(void)
+{
+ last = first+nacross*ndown;
+ if(last > nfaces)
+ last = nfaces;
+}
+
+void
+drawarrows(void)
+{
+ Point p;
+
+ p = enddate;
+ p.x += Facesep;
+ if(p.x & 1)
+ p.x++; /* align background texture */
+ leftr = rectaddpt(leftright, p);
+ p.x += Dx(leftright) + Facesep;
+ rightr = rectaddpt(leftright, p);
+ draw(screen, leftr, first>0? blue : bgrnd, left, leftright.min);
+ draw(screen, rightr, last<nfaces? blue : bgrnd, right, leftright.min);
+}
+
+void
+addface(Face *f) /* always adds at 0 */
+{
+ Face **ofaces;
+ Rectangle r0, r1, r;
+ int y, nx, ny;
+
+ if(f == nil)
+ return;
+ lockdisplay(display);
+ if(first != 0){
+ first = 0;
+ resized();
+ }
+ findbit(f);
+
+ nx = nacross;
+ ny = (nfaces+(nx-1)) / nx;
+
+ for(y=ny; y>=0; y--){
+ /* move them along */
+ r0 = facerect(y*nx+0);
+ r1 = facerect(y*nx+1);
+ r = r1;
+ r.max.x = r.min.x + (nx - 1)*(Facesize+Facesep);
+ draw(screen, r, screen, nil, r0.min);
+ /* copy one down from row above */
+ if(y != 0){
+ r = facerect((y-1)*nx+nx-1);
+ draw(screen, r0, screen, nil, r.min);
+ }
+ }
+
+ ofaces = faces;
+ faces = emalloc((nfaces+1)*sizeof(Face*));
+ memmove(faces+1, ofaces, nfaces*(sizeof(Face*)));
+ free(ofaces);
+ nfaces++;
+ setlast();
+ drawarrows();
+ faces[0] = f;
+ drawface(f, 0);
+ flushimage(display, 1);
+ unlockdisplay(display);
+}
+
+#if 0
+void
+loadmboxfaces(char *maildir)
+{
+ int dirfd;
+ Dir *d;
+ int i, n;
+
+ dirfd = open(maildir, OREAD);
+ if(dirfd >= 0){
+ chdir(maildir);
+ while((n = dirread(dirfd, &d)) > 0){
+ for(i=0; i<n; i++)
+ addface(dirface(maildir, d[i].name));
+ free(d);
+ }
+ close(dirfd);
+ }
+}
+#endif
+
+void
+loadmboxfaces(char *maildir)
+{
+ CFid *dirfd;
+ Dir *d;
+ int i, n;
+
+ dirfd = fsopen(upasfs,maildir, OREAD);
+ if(dirfd != nil){
+ //jpc chdir(maildir);
+ while((n = fsdirread(dirfd, &d)) > 0){
+ for(i=0; i<n; i++) {
+ addface(dirface(maildir, d[i].name));
+ }
+ free(d);
+ }
+ fsclose(dirfd);
+ }
+ else {
+ error("cannot open %s: %r",maildir);
+ }
+}
+
+void
+freeface(Face *f)
+{
+ int i;
+
+ if(f->file!=nil && f->bit!=f->file->image)
+ freeimage(f->bit);
+ freefacefile(f->file);
+ for(i=0; i<Nstring; i++)
+ free(f->str[i]);
+ free(f);
+}
+
+void
+delface(int j)
+{
+ Rectangle r0, r1, r;
+ int nx, ny, x, y;
+
+ if(j < first)
+ first--;
+ else if(j < last){
+ nx = nacross;
+ ny = (nfaces+(nx-1)) / nx;
+ x = (j-first)%nx;
+ for(y=(j-first)/nx; y<ny; y++){
+ if(x != nx-1){
+ /* move them along */
+ r0 = facerect(y*nx+x);
+ r1 = facerect(y*nx+x+1);
+ r = r0;
+ r.max.x = r.min.x + (nx - x - 1)*(Facesize+Facesep);
+ draw(screen, r, screen, nil, r1.min);
+ }
+ if(y != ny-1){
+ /* copy one up from row below */
+ r = facerect((y+1)*nx);
+ draw(screen, facerect(y*nx+nx-1), screen, nil, r.min);
+ }
+ x = 0;
+ }
+ if(last < nfaces) /* first off-screen becomes visible */
+ drawface(faces[last], last-1);
+ else{
+ /* clear final spot */
+ r = facerect(last-first-1);
+ draw(screen, r, bgrnd, nil, r.min);
+ }
+ }
+ freeface(faces[j]);
+ memmove(faces+j, faces+j+1, (nfaces-(j+1))*sizeof(Face*));
+ nfaces--;
+ setlast();
+ drawarrows();
+}
+
+void
+dodelete(int i)
+{
+ Face *f;
+
+ f = faces[i];
+ if(history){
+ free(f->str[Sshow]);
+ f->str[Sshow] = estrdup("");
+ }else{
+ delface(i);
+ flushimage(display, 1);
+ }
+}
+
+void
+delete(char *s, char *digest)
+{
+ int i;
+ Face *f;
+
+ lockdisplay(display);
+ for(i=0; i<nfaces; i++){
+ f = faces[i];
+ if(digest != nil){
+ if(f->str[Sdigest]!=nil && strcmp(digest, f->str[Sdigest]) == 0){
+ dodelete(i);
+ break;
+ }
+ }else{
+ if(f->str[Sshow] && strcmp(s, f->str[Sshow]) == 0){
+ dodelete(i);
+ break;
+ }
+ }
+ }
+ unlockdisplay(display);
+}
+
+void
+faceproc(void)
+{
+ for(;;)
+ addface(nextface());
+}
+
+void
+resized(void)
+{
+ int i;
+
+ nacross = (Dx(screen->r)-2*facep.x+Facesep)/(Facesize+Facesep);
+ for(ndown=1; rectinrect(facerect(ndown*nacross), screen->r); ndown++)
+ ;
+ setlast();
+ draw(screen, screen->r, bgrnd, nil, ZP);
+ enddate = ZP;
+ drawtime();
+ for(i=0; i<nfaces; i++)
+ drawface(faces[i], i);
+ drawarrows();
+ flushimage(display, 1);
+}
+
+void
+eresized(int new)
+{
+ lockdisplay(display);
+ if(new && getwindow(display, Refnone) < 0) {
+ fprint(2, "can't reattach to window\n");
+ killall("reattach");
+ }
+ resized();
+ unlockdisplay(display);
+}
+
+#if 0
+int
+getmouse(Mouse *m)
+{
+ int n;
+ static int eof;
+ char buf[128];
+
+ if(eof)
+ return 0;
+ for(;;){
+ n = read(mousefd, buf, sizeof(buf));
+ if(n <= 0){
+ /* so callers needn't check return value every time */
+ eof = 1;
+ m->buttons = 0;
+ return 0;
+ }
+ //jpc n = eatomouse(m, buf, n);
+ if(n > 0)
+ return 1;
+ }
+}
+#endif
+int
+getmouse(Mouse *m)
+{
+ static int eof;
+
+ if(eof)
+ return 0;
+ if( readmouse(mousectl) < 0 ) {
+ eof = 1;
+ m->buttons = 0;
+ return 0;
+ }
+ else {
+ *m = mousectl->m;
+/* m->buttons = mousectl->m.buttons;
+ m->xy.x = mousectl->m.xy.x;
+ m->xy.y = mousectl->m.xy.y;
+ m->msec = mousectl->m.msec; */
+ return 1;
+ }
+}
+
+enum
+{
+ Clicksize = 3, /* pixels */
+};
+
+int
+scroll(int but, Point p)
+{
+ int delta;
+
+ delta = 0;
+ lockdisplay(display);
+ if(ptinrect(p, leftr) && first>0){
+ if(but == 2)
+ delta = -first;
+ else{
+ delta = nacross;
+ if(delta > first)
+ delta = first;
+ delta = -delta;
+ }
+ }else if(ptinrect(p, rightr) && last<nfaces){
+ if(but == 2)
+ delta = (nfaces-nacross*ndown) - first;
+ else{
+ delta = nacross;
+ if(delta > nfaces-last)
+ delta = nfaces-last;
+ }
+ }
+ first += delta;
+ last += delta;
+ unlockdisplay(display);
+ if(delta)
+ eresized(0);
+ return delta;
+}
+
+void
+click(int button, Mouse *m)
+{
+ Point p;
+ int i;
+
+ p = m->xy;
+ while(m->buttons == (1<<(button-1)))
+ getmouse(m);
+ if(m->buttons)
+ return;
+ if(abs(p.x-m->xy.x)>Clicksize || abs(p.y-m->xy.y)>Clicksize)
+ return;
+ switch(button){
+ case 1:
+ if(scroll(1, p))
+ break;
+ if(history){
+ /* click clears display */
+ lockdisplay(display);
+ for(i=0; i<nfaces; i++)
+ freeface(faces[i]);
+ free(faces);
+ faces=nil;
+ nfaces = 0;
+ unlockdisplay(display);
+ eresized(0);
+ return;
+ }else{
+ for(i=first; i<last; i++) /* clear vwhois faces */
+ if(ptinrect(p, facerect(i-first))
+ && strstr(faces[i]->str[Sshow], "/XXXvwhois")){
+ delface(i);
+ flushimage(display, 1);
+ }
+ }
+ break;
+ case 2:
+ scroll(2, p);
+ break;
+ case 3:
+ scroll(3, p);
+ lockdisplay(display);
+ for(i=first; i<last; i++)
+ if(ptinrect(p, facerect(i-first))){
+ showmail(faces[i]);
+ break;
+ }
+ unlockdisplay(display);
+ break;
+ }
+}
+
+void
+mouseproc(void *dummy)
+{
+ Mouse mouse;
+
+ while(getmouse(&mouse)){
+ if(mouse.buttons == 1)
+ click(1, &mouse);
+ else if(mouse.buttons == 2)
+ click(2, &mouse);
+ else if(mouse.buttons == 4)
+ click(3, &mouse);
+
+ while(mouse.buttons)
+ getmouse(&mouse);
+ }
+}
+
+void
+killall(char *s)
+{
+ int i, pid;
+
+ pid = getpid();
+ for(i=0; i<NPROC; i++)
+ if(pids[i] && pids[i]!=pid)
+ postnote(PNPROC, pids[i], "kill");
+ threadexitsall(s);
+}
+
+void
+startproc(void (*f)(void), int index)
+{
+ int pid;
+
+ switch(pid = rfork(RFPROC|RFNOWAIT)){ //jpc removed |RFMEM
+ case -1:
+ fprint(2, "faces: fork failed: %r\n");
+ killall("fork failed");
+ case 0:
+ f();
+ fprint(2, "faces: %s process exits\n", procnames[index]);
+ if(index >= 0)
+ killall("process died");
+ threadexitsall(nil);
+ }
+ if(index >= 0)
+ pids[index] = pid;
+}
+
+void
+usage(void)
+{
+ fprint(2, "usage: faces [-hi] [-m maildir] -W winsize\n");
+ threadexitsall("usage");
+}
+
+void
+threadmain(int argc, char *argv[])
+{
+ int i;
+
+ ARGBEGIN{
+ case 'h':
+ history++;
+ break;
+ case 'i':
+ initload++;
+ break;
+ case 'm':
+ addmaildir(EARGF(usage()));
+ maildir = nil;
+ break;
+ case 'W':
+ winsize = EARGF(usage());
+ break;
+ default:
+ usage();
+ }ARGEND
+
+ if(initdraw(nil, nil, "faces") < 0){
+ fprint(2, "faces: initdraw failed: %r\n");
+ threadexitsall("initdraw");
+ }
+ if(maildir)
+ addmaildir(maildir);
+ init();
+ unlockdisplay(display); /* initdraw leaves it locked */
+ display->locking = 1; /* tell library we're using the display lock */
+ setdate();
+ eresized(0);
+
+ pids[Mainp] = getpid();
+ pids[Timep] = proccreate(timeproc, nil, 16000);
+ pids[Mousep] = proccreate(mouseproc, nil, 16000);
+ if(initload)
+ for(i = 0; i < nmaildirs; i++)
+ loadmboxfaces(maildirs[i]);
+ faceproc();
+ fprint(2, "faces: %s process exits\n", procnames[Mainp]);
+ killall(nil);
+}