Plan 9 from Bell Labs’s /usr/web/sources/contrib/anothy/src/cmd/inbox/main.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


#include <u.h>
#include <libc.h>
#include <draw.h>
#include <plumb.h>
#include <regexp.h>
#include <event.h>	/* for support routines only */
#include <bio.h>
#include "faces.h"

enum
{
	Facesep = 6,	/* must be even to avoid damaging background stipple */
	HhmmTime = 18*60*60,	/* max age of face to display hh:mm time */
};

enum
{
	Mainp,
	Timep,
	Mousep,
	Kbdp,
	Infop,
	NPROC
};

int pids[NPROC];
char *procnames[] = {
	"main",
	"time",
	"mouse",
	"info",
	"keyboard",
};

Rectangle updown = {0, 0, 16, 16};

uchar updata[] = {
	0x01, 0x80,
	0x03, 0xc0,
	0x07, 0xe0,
	0x0f, 0xf0,
	0x1f, 0xf8,
	0x3f, 0xfc,
	0x7f, 0xfe,
	0xff, 0xff,
	0x03, 0xc0,
	0x03, 0xc0,
	0x03, 0xc0,
	0x03, 0xc0,
	0x03, 0xc0,
	0x03, 0xc0,
	0x03, 0xc0,
	0x03, 0xc0
};

uchar downdata[] = {
	0x03, 0xc0,
	0x03, 0xc0,
	0x03, 0xc0,
	0x03, 0xc0,
	0x03, 0xc0,
	0x03, 0xc0,
	0x03, 0xc0,
	0x03, 0xc0,
	0xff, 0xff,
	0x7f, 0xfe,
	0x3f, 0xfc,
	0x1f, 0xf8,
	0x0f, 0xf0,
	0x07, 0xe0,
	0x03, 0xc0,
	0x01, 0x80
};


Image	*blue;		/* full arrow */
Image	*bgrnd;		/* background color */
Image	*red;		/* red mask */
Image	*up;			/* up-pointing arrow mask */
Image	*down;		/* down-pointing arrow mask */
Font	*mediumfont;
Font	*datefont;
int	first, last;	/* first and last visible face; last is first invisible */
int	nfaces;
int	mousefd;
int	kbdfd;
int	pfd[2];
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	upr;			/* location of up arrow on display */
Rectangle	downr;		/* location of down arrow on display */
Rectangle	infor;		/* location of information */
Rectangle	delrect;		/* locatation of Del text */
char	selstr[128];			/* selection string */
int	 selected = -1;		/* selected face */

Rectangle facerect(int index);
char* infoget(char *, int, int, char *);

void
setdate(void)
{
	Tm *tm;
	now = time(nil);
	tm=localtime(now);
/*	strcpy(date, smprint("%.4d-%.2d-%.2d:%.2d:%.2d", tm->year+1900,
		tm->mon+1, tm->mday, tm->hour, tm->min));	*/
	strcpy(date, smprint("%.2d:%.2d", tm->hour, tm->min));
}

void
init(void)
{
	int fd;

	kbdfd = open("/dev/cons", 	ORDWR|OCEXEC);
	fd = open("/dev/consctl", OWRITE|OCEXEC);
	if(kbdfd < 0 || fd < 0 || write(fd, "rawon", 5) < 0){
		fprint(2, "inbox: can't open keyboard: %r\n");
		exits("keyboard");
	}

	if(pipe(pfd) < 0){
		fprint(2, "inbox: pipe: %r\n");
		exits("pipe");
	}

	mousefd = open("/dev/mouse", OREAD);
	if(mousefd < 0){
		fprint(2, "inbox: can't open mouse: %r\n");
		exits("mouse");
	}
	initplumb();

	/* make background color */
	bgrnd = allocimagemix(display, 0xBBBBBBBB, DWhite);
	blue = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x008888FF);	/* blue-green */
	red = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xff00007f);
	up = allocimage(display, updown, GREY1, 0, DWhite);
	down = allocimage(display, updown, GREY1, 0, DWhite);
	if(bgrnd==nil || blue==nil || red == nil || up==nil || down==nil){
		fprint(2, "inbox: can't create images: %r\n");
		exits("image");
	}

	loadimage(up, updown, updata, sizeof updata);
	loadimage(down, updown, downdata, sizeof downdata);


	/* initialize little fonts */
//	mediumfont = openfont(display, "/lib/font/bit/dejavu/DejaVuSans/DejaVuSans.13.font");
	mediumfont = openfont(display, "/lib/font/bit/lucida/unicode.7.font");
	if(mediumfont == nil)
		mediumfont = font;
	datefont = mediumfont;

	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, "88:88");
		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)
{
	for(;;){
		lockdisplay(display);
		drawtime();
		flushimage(display, 1);
		unlockdisplay(display);
		now = time(nil);
		sleep(((60 - now%60) + 1)*1000); /* wait for minute to change */
		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;
}

Rectangle
facerect(int index)	/* index is geometric; 0 is always upper top face */
{
	Rectangle r;

	r.min = addpt(screen->r.min, facep);
	r.min.y += index*(Facesize+Facesep);
	r.max = addpt(r.min, Pt(Facesize, Facesize));
	r.max.x += (Dx(screen->r)-facep.x+Facesep);
	/* simple fix to avoid drawing off screen, allowing customers to use position */
	if(index<0 || index>=ndown)
		r.max.x = r.min.x;
	return r;
}

static char *mon = "JanFebMarAprMayJunJulAugSepOctNovDec";
char*
facetime(Face *f)
{
	static char buf[30];

	sprint(buf, "%04d-%02d-%02d %02d:%02d",
		f->tm.year+1900, f->tm.mon, f->tm.mday, f->tm.hour, f->tm.min);
	return buf;
}

void
drawface(Face *f, int i, Image *bg)
{
	char *tstr;
	Rectangle r, r0;
	char buf[80];

	if(f == nil)
		return;
	if(i<first || i>=last)
		return;
	r = facerect(i-first);
	r0 = r;
	draw(screen, r, bg, nil, ZP);
	draw(screen, r, f->bit, f->mask, ZP);
	r.min.x += Facesize+Facesep;
//	r.min.y -= 4; /* hack; manual adjustment for the font height/ascent */
	r.min.y -= (mediumfont->height - mediumfont->ascent);	/* also wrong. */
	stringbg(screen, r.min, display->black, ZP, mediumfont, f->str[Suser], bgrnd, ZP);
	r.min.y += mediumfont->height;
	tstr = facetime(f);
	stringbg(screen, r.min, display->black, ZP, mediumfont, tstr, bgrnd, ZP);
	r.min.y += mediumfont->height;
	stringbg(screen, r.min, display->black, ZP, mediumfont,
		infoget(buf, sizeof buf, i, "subject"), bgrnd, ZP);
	if(i == selected){
		border(screen, insetrect(r0, -2), -2, blue, ZP);
		stringbg(screen, infor.min, display->black, ZP, datefont, selstr, bgrnd, ZP);
	}
}

void
setlast(void)
{
	last = first+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 */
	upr = rectaddpt(updown, p);
	p.x += Dy(updown) + Facesep;
	downr = rectaddpt(updown, p);
	draw(screen, upr, first>0? blue : bgrnd, up, updown.min);
	draw(screen, downr, last<nfaces? blue : bgrnd, down, updown.min);
}

void
addface(Face *f)	/* always adds at 0 */
{
	Face **ofaces;
	Rectangle r0, r1, r;
	int y, ny, sel;

	if(f == nil)
		return;
	lockdisplay(display);
	if(first != 0){
		first = 0;
		resized();
	}
	sel = -1;
	if(selected != -1){
		sel = selected+1;
		draw(screen, infor, bgrnd, nil, ZP);
		border(screen, insetrect(facerect(selected-first), -2), -2, bgrnd, ZP);
		selected = -1;
	}
	findbit(f);

	ny = nfaces;

	for(y=ny; y>=0; y--){
		/* move them along */
		r0 = facerect(y+0);
		r1 = facerect(y+1);
		r = r1;
		r.max.x = r.min.x;
		draw(screen, r, screen, nil, r0.min);
		/* copy one down from row above */
		if(y != 0){
			r = facerect(y-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, bgrnd);
	selected = sel;
	if(selected != -1)
		drawface(faces[selected], selected, bgrnd);
	flushimage(display, 1);
	unlockdisplay(display);
}

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);
	}
}

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 ny, x, y;

	if(j < first)
		first--;
	else if(j < last){
		ny = nfaces;
		x = 0;
		for(y=j-first; y<ny; y++){
			if(x != 0){
				/* move them along */
				r0 = facerect(y+x);
				r1 = facerect(y+x+1);
				r = r0;
				r.max.x = r.min.x;
				draw(screen, r, screen, nil, r1.min);
			}
			if(y != ny-1){
				/* copy one up from row below */
				r = facerect(y+1);
				draw(screen, facerect(y), screen, nil, r.min);
			}
			x = 0;
		}
		if(last < nfaces)	/* first off-screen becomes visible */
			drawface(faces[last], last-1, bgrnd);
		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)
{
	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;

	for(ndown=1; rectinrect(facerect(ndown), screen->r); ndown++)
		;
	setlast();
	draw(screen, screen->r, bgrnd, nil, ZP);
	enddate = ZP;
	drawtime();
	for(i=0; i<nfaces; i++)
		drawface(faces[i], i, bgrnd);
	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);
}

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;
		}
		n = eatomouse(m, buf, n);
		if(n > 0)
			return 1;
	}
}

enum
{
	Clicksize	= 3,		/* pixels */
};

int
scroll(int but, Point p)
{
	int delta;

	delta = 0;
	lockdisplay(display);
	if(ptinrect(p, upr) && first>0){
		if(but == 2)
			delta = -first;
		else{
			delta = ndown;
			if(delta > first)
				delta = first;
			delta = -delta;
		}
	}else if(ptinrect(p, downr) && last<nfaces){
		if(but == 2)
			delta = (nfaces-ndown) - first;
		else{
			delta = ndown;
			if(delta > nfaces-last)
				delta = nfaces-last;
		}
	}
	first += delta;
	last += delta;
	unlockdisplay(display);
	if(delta)
		eresized(0);
	return delta;
}

enum {Fields = 4};

void
infodel(char **f, int)
{
	Point p;

	if(selected == -1)
		return;
	p = Pt(atoi(f[2]), atoi(f[3]));
	if(!ptinrect(p, delrect))
		return;
	drawface(faces[selected], selected, red);
	flushimage(display, 1);
  	remove(faces[selected]->str[Sshow]);
}

void
infoclear(void)
{
	if(selected != -1){
		draw(screen, infor, bgrnd, nil, ZP);
		border(screen, insetrect(facerect(selected-first), -2), -2, bgrnd, ZP);	// error!? 
	}
	selected = -1;
}

char*
infoget(char *s, int l, int i, char *field)
{
	char buf[128];
	int fd, n;

	snprint(buf, sizeof buf, "%s/%s", faces[i]->str[Sshow], field);
	fd = open(buf, OREAD);
	if(fd < 0){
fail:		snprint(s, l, "%d", i);
		return s;
	}
	n = read(fd, s, l-1);
	close(fd);
	if(n < 0)
		goto fail;
	s[n] = 0;
	return s;
}

void
infoset(char **f)
{
	Point p, q;
	char buf[80];
	int i;

	// hasn't been set yet.
	if(upr.max.y == 0)
		return;
	i = atoi(f[1]);
	infodel(f, i);
	infoclear();
	if(i == -1)
		return;
	infoget(buf, sizeof buf, i, "subject");
	snprint(selstr, sizeof selstr, "Del | %-.80s", buf);
	p = addpt(Pt(0, screen->r.min.y), Pt(upr.max.x, datep.y));
	p = addpt(p, Pt(datefont->height*6, 0));
	delrect = Rpt(p, addpt(p, Pt(stringwidth(datefont, "Del"), datefont->height)));
	q = addpt(p, Pt(stringwidth(datefont, selstr), datefont->height));
	infor = Rpt(p, q);
	stringbg(screen, p, display->black, ZP, datefont, selstr, bgrnd, ZP);
	border(screen, insetrect(facerect(i-first), -2), -2, blue, ZP);	// error!? 
	selected = i;
}

void
infoproc(void)
{
	int n;
	char buf[128], *f[Fields];

//	close(pfd[0]);
	while((n = read(pfd[1], buf, sizeof buf-1)) > 0){
		buf[n] = 0;
		if(getfields(buf, f, Fields, 0, "\001") != Fields)
			continue;
		lockdisplay(display);
		infoset(f);
		flushimage(display, 1);
		unlockdisplay(display);
	}
}

void
infosend(int i, Point p)
{
	char buf[50];

	if(i == -1 && selected == -1)
		return;
	snprint(buf, sizeof buf, "set\001%d\001%d\001%d\n", i, p.x, p.y);
	write(pfd[0], buf, strlen(buf));
}

void
click(int button, Mouse *m)
{
	Point p;
	int i;
	static int lasti;

	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;
		infosend(-1, ZP);
		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);
		for(i=first; i<last; i++)
			if(ptinrect(p, facerect(i-first)))
				break;
		if(i == last || lasti == i)
			i = -1;
		infosend(i, p);
		lasti = i;
		break;
	case 3:
		infosend(-1, ZP);
		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)
{
	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
kbdproc(void)
{
	int n, i;
	char buf[20];

	while((n = read(kbdfd, buf, sizeof buf)) > 0)
		for(i = 0; i < n; i++)
			if(strchr("q", buf[i]))
				return;
}

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");
	exits(s);
}

void
startproc(void (*f)(void), int index)
{
	int pid;

	switch(pid = rfork(RFPROC|RFMEM|RFNOWAIT)){
	case -1:
		fprint(2, "inbox: fork failed: %r\n");
		killall("fork failed");
	case 0:
		f();
		if(index != Kbdp)
			fprint(2, "inbox: %s process exits\n", procnames[index]);
		if(index >= 0)
			killall("process died");
		exits(nil);
	}
	if(index >= 0)
		pids[index] = pid;
}

void
usage(void)
{
	fprint(2, "usage: inbox [-m maildir]\n");
	exits("usage");
}

void
main(int argc, char *argv[])
{
	int i;

	ARGBEGIN{
	case 'm':
		addmaildir(EARGF(usage()));
		maildir = nil;
		break;
	default:
		usage();
	}ARGEND

	if(initdraw(nil, nil, "inbox") < 0){
		fprint(2, "inbox: initdraw failed: %r\n");
		exits("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();
	startproc(timeproc, Timep);
	startproc(mouseproc, Mousep);
	startproc(kbdproc, Kbdp);
	startproc(infoproc, Infop);
//	close(pfd[1]);
	for(i = 0; i < nmaildirs; i++)
		loadmboxfaces(maildirs[i]);
	faceproc();
	fprint(2, "inbox: %s process exits\n", procnames[Mainp]);
	killall(nil);
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to [email protected].