| #include <u.h> | 
 | #include <libc.h> | 
 | #include <draw.h> | 
 | #include <plumb.h> | 
 | #include <regexp.h> | 
 | #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 */ | 
 | 	 | 
 | 	STACK = 32768 | 
 | }; | 
 |  | 
 | enum | 
 | { | 
 | 	Mainp, | 
 | 	Timep, | 
 | 	Mousep, | 
 | 	Resizep, | 
 | 	NPROC | 
 | }; | 
 |  | 
 | char *procnames[] = { | 
 | 	"main", | 
 | 	"time", | 
 | 	"mouse", | 
 | 	"resize" | 
 | }; | 
 |  | 
 | 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	*mailfs; | 
 | 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 = "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 eresized(int); | 
 |  | 
 | 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) | 
 | { | 
 | 	mailfs = nsmount("mail", nil); | 
 | 	if(mailfs == nil) | 
 | 		sysfatal("mount mail: %r"); | 
 | 	mousectl = initmouse(nil, screen); | 
 | 	if(mousectl == nil) | 
 | 		sysfatal("initmouse: %r"); | 
 | 	initplumb(); | 
 |  | 
 | 	/* make background color */ | 
 | 	bgrnd = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 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; | 
 | 	if(first != 0){ | 
 | 		first = 0; | 
 | 		eresized(0); | 
 | 	} | 
 | 	findbit(f); | 
 |  | 
 | 	nx = nacross; | 
 | 	ny = (nfaces+(nx-1)) / nx; | 
 |  | 
 | 	lockdisplay(display); | 
 | 	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); | 
 | } | 
 |  | 
 | void | 
 | loadmboxfaces(char *maildir) | 
 | { | 
 | 	CFid *dirfd; | 
 | 	Dir *d; | 
 | 	int i, n; | 
 |  | 
 | 	dirfd = fsopen(mailfs, maildir, OREAD); | 
 | 	if(dirfd != nil){ | 
 | 		while((n = fsdirread(dirfd, &d)) > 0){ | 
 | 			for(i=0; i<n; i++) | 
 | 				addface(dirface(maildir, d[i].name)); | 
 | 			free(d); | 
 | 		} | 
 | 		fsclose(dirfd); | 
 | 	}else | 
 | 		sysfatal("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); | 
 | } | 
 |  | 
 | void | 
 | resizeproc(void *v) | 
 | { | 
 | 	USED(v); | 
 |  | 
 | 	while(recv(mousectl->resizec, 0) == 1) | 
 | 		eresized(1); | 
 | } | 
 |  | 
 | int | 
 | getmouse(Mouse *m) | 
 | { | 
 | 	static int eof; | 
 |  | 
 | 	if(eof) | 
 | 		return 0; | 
 | 	if(readmouse(mousectl) < 0){ | 
 | 		eof = 1; | 
 | 		m->buttons = 0; | 
 | 		return 0; | 
 | 	} | 
 | 	*m = mousectl->m; | 
 | 	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")){ | 
 | 					lockdisplay(display); | 
 | 					delface(i); | 
 | 					flushimage(display, 1); | 
 | 					unlockdisplay(display); | 
 | 					break; | 
 | 				} | 
 | 		} | 
 | 		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 *v) | 
 | { | 
 | 	Mouse mouse; | 
 | 	USED(v); | 
 |  | 
 | 	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) | 
 | { | 
 | 	threadexitsall(s); | 
 | } | 
 |  | 
 | void | 
 | usage(void) | 
 | { | 
 | 	fprint(2, "usage: faces [-hi] [-m maildir] -W winsize\n"); | 
 | 	threadexitsall("usage"); | 
 | } | 
 |  | 
 | void | 
 | threadmain(int argc, char *argv[]) | 
 | { | 
 | 	int i; | 
 |  | 
 | 	rfork(RFNOTEG); | 
 |  | 
 | 	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); | 
 |  | 
 | 	proccreate(timeproc, nil, STACK); | 
 | 	proccreate(mouseproc, nil, STACK); | 
 | 	proccreate(resizeproc, nil, STACK); | 
 | 	if(initload) | 
 | 		for(i = 0; i < nmaildirs; i++) | 
 | 			loadmboxfaces(maildirs[i]); | 
 | 	faceproc(); | 
 | 	killall(nil); | 
 | } |