/*
* Warren - Plan 9 Collaboration Framework
*
*/
#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"
void scandir(void);
enum
{
Facesep = 6, /* must be even to avoid damaging background stipple */
Infolines = 9,
HhmmTime = 90, /* max age of face to display hh:mm time */
};
char path[255];
char myname[255];
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
};
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 nacross;
int ndown;
char date[64];
Face **faces;
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)
{
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");
exits("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
updateme(void)
{
Dir stbuff;
int fd, ret;
nulldir(&stbuff);
stbuff.mtime = time(nil);
if(dirwstat(myname, &stbuff) >= 0)
return;
if((fd = create(myname, OREAD|OEXCL, DMDIR|0666)) < 0)
return;
dirfwstat(fd, &stbuff);
close(fd);
}
void
timeproc(void)
{
for(;;){
updateme();
scandir();
lockdisplay(display);
drawtime();
flushimage(display, 1);
unlockdisplay(display);
now = time(nil);
sleep(10*1000);
setdate();
}
}
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;
}
void
drawface(Face *f, int i)
{
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;
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
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);
}
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)
{
delface(i);
flushimage(display, 1);
}
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);
}
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, mi;
enum Menu2
{
Mmail,
Mchat,
Mblog,
Msee,
Mfiles,
Nmenu2,
};
char *menu2str[Nmenu2+1] = {
"mail",
"chat",
"blog",
"see",
"files",
nil,
};
Menu menu2 = {menu2str, nil};
p = m->xy;
switch(button){
case 1:
scroll(1, p);
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]); */
mi = emenuhit(3, m, &menu2);
/* TODO: plumb m */
break;
}
unlockdisplay(display);
break;
}
}
void
mouseproc(void)
{
Mouse mouse;
einit(Emouse);
for(;;){
mouse = emouse();
if(mouse.buttons == 1)
click(1, &mouse);
else if(mouse.buttons == 2)
click(2, &mouse);
else if(mouse.buttons == 4)
click(3, &mouse);
}
}
void
scandir(void)
{
int dirfd;
Dir *d;
int i, n, x;
dirfd = open(".", OREAD);
if(dirfd >= 0){
while((n = dirread(dirfd, &d)) > 0){
for(i=0; i<n; i++) {
for(x=0;x<nfaces;x++) {
if(strstr(faces[x]->str[Sshow],d[i].name)) {
if((now - d[i].mtime) > HhmmTime)
delface(i);
break;
}
}
if((x==nfaces)&&((now-d[i].mtime) < HhmmTime))
addface(dirface(d[i].name, d[i].mtime));
}
free(d);
}
close(dirfd);
}
}
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, "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");
exits(nil);
}
if(index >= 0)
pids[index] = pid;
}
void
usage(void)
{
fprint(2, "usage: warren [-n face-name] [-p collabdir]\n");
exits("usage");
}
void
main(int argc, char *argv[])
{
ARGBEGIN{
case 'p':
strcpy(path, EARGF(usage()));
chdir(path);
break;
case 'n':
strcpy(myname, EARGF(usage()));
break;
default:
usage();
}ARGEND
print("Hello %s - %s\n", myname, path);
if(initdraw(nil, nil, "warren") < 0){
fprint(2, "warren: initdraw failed: %r\n");
exits("initdraw");
}
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(mouseproc, Mousep);
timeproc();
fprint(2, "warren: %s process exits\n", procnames[Mainp]);
killall(nil);
}
|