Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

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