plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

main.c (13479B)


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