plan9fox/sys/src/cmd/winwatch.c

375 lines
6.2 KiB
C

#include <u.h>
#include <libc.h>
#include <draw.h>
#include <cursor.h>
#include <event.h>
#include <regexp.h>
#include <keyboard.h>
enum {
VISIBLE = 1,
CURRENT = 2,
};
typedef struct Win Win;
struct Win {
int n;
int dirty;
int state;
char *label;
Rectangle r;
};
Reprog *exclude = nil;
Win *win;
int nwin;
int mwin;
int onwin;
int rows, cols;
Image *lightblue;
Image *statecol[4];
enum {
PAD = 3,
MARGIN = 5
};
void*
erealloc(void *v, ulong n)
{
v = realloc(v, n);
if(v == nil)
sysfatal("out of memory reallocating %lud", n);
return v;
}
void*
emalloc(ulong n)
{
void *v;
v = malloc(n);
if(v == nil)
sysfatal("out of memory allocating %lud", n);
memset(v, 0, n);
return v;
}
char*
estrdup(char *s)
{
int l;
char *t;
if (s == nil)
return nil;
l = strlen(s)+1;
t = emalloc(l);
memcpy(t, s, l);
return t;
}
int
readfile(char *buf, int nbuf, char *file, ...)
{
va_list arg;
int n, fd;
va_start(arg, file);
vsnprint(buf, nbuf, file, arg);
va_end(arg);
if((fd = open(buf, OREAD)) < 0){
buf[0] = 0;
return -1;
}
n = read(fd, buf, nbuf-1);
close(fd);
if(n < 0){
buf[0] = 0;
return -1;
}
buf[n] = 0;
return n;
}
void
refreshwin(void)
{
char label[128], wctl[128], *tok[8];
int i, fd, n, nr, nw, state;
static int mywinid = -1;
Dir *pd;
if(mywinid < 0){
if(readfile(wctl, sizeof(wctl), "/dev/winid") > 0)
mywinid = atoi(wctl);
}
if((fd = open("/dev/wsys", OREAD)) < 0)
return;
nw = 0;
/* i'd rather read one at a time but rio won't let me */
while((nr=dirread(fd, &pd)) > 0){
for(i=0; i<nr; i++){
n = atoi(pd[i].name);
if(n == mywinid)
continue;
if(readfile(label, sizeof(label), "/dev/wsys/%d/label", n) < 0)
continue;
if(exclude != nil && regexec(exclude,label,nil,0))
continue;
if(readfile(wctl, sizeof(wctl), "/dev/wsys/%d/wctl", n) <= 0)
continue;
if(tokenize(wctl, tok, nelem(tok)) != 6)
continue;
state = 0;
if(strcmp(tok[4], "current") == 0)
state |= CURRENT;
if(strcmp(tok[5], "visible") == 0)
state |= VISIBLE;
if(nw < nwin && win[nw].n == n && win[nw].state == state &&
strcmp(win[nw].label, label)==0){
nw++;
continue;
}
if(nw < nwin){
free(win[nw].label);
win[nw].label = nil;
}
if(nw >= mwin){
mwin += 8;
win = erealloc(win, mwin*sizeof(win[0]));
}
win[nw].n = n;
win[nw].label = estrdup(label);
win[nw].state = state;
win[nw].dirty = 1;
win[nw].r = Rect(0,0,0,0);
nw++;
}
free(pd);
}
while(nwin > nw)
free(win[--nwin].label);
nwin = nw;
close(fd);
}
void
drawnowin(int i)
{
Rectangle r;
r = Rect(0,0,(Dx(screen->r)-2*MARGIN+PAD)/cols-PAD, font->height);
r = rectaddpt(rectaddpt(r, Pt(MARGIN+(PAD+Dx(r))*(i/rows),
MARGIN+(PAD+Dy(r))*(i%rows))), screen->r.min);
draw(screen, insetrect(r, -1), lightblue, nil, ZP);
}
void
drawwin(int i)
{
draw(screen, win[i].r, statecol[win[i].state], nil, ZP);
_string(screen, addpt(win[i].r.min, Pt(2,0)), display->black, ZP,
font, win[i].label, nil, strlen(win[i].label),
win[i].r, nil, ZP, SoverD);
border(screen, win[i].r, 1, display->black, ZP);
win[i].dirty = 0;
}
int
geometry(void)
{
int i, nrows, ncols, z;
Rectangle r;
z = 0;
nrows = (Dy(screen->r)-2*MARGIN+PAD)/(font->height+PAD);
if(nrows <= 0)
nrows = 1;
if(nrows != rows){
rows = nrows;
z = 1;
}
ncols = nwin <= 0 ? 1 : (nwin+rows-1)/rows;
if(ncols != cols){
cols = ncols;
z = 1;
}
r = Rect(0,0,(Dx(screen->r)-2*MARGIN+PAD)/cols-PAD, font->height);
for(i=0; i<nwin; i++)
win[i].r = rectaddpt(rectaddpt(r, Pt(MARGIN+(PAD+Dx(r))*(i/rows),
MARGIN+(PAD+Dy(r))*(i%rows))), screen->r.min);
return z;
}
void
redraw(Image *screen, int all)
{
int i;
all |= geometry();
if(all)
draw(screen, screen->r, lightblue, nil, ZP);
for(i=0; i<nwin; i++)
if(all || win[i].dirty)
drawwin(i);
if(!all)
for(; i<onwin; i++)
drawnowin(i);
onwin = nwin;
}
void
eresized(int new)
{
if(new && getwindow(display, Refmesg) < 0)
fprint(2,"can't reattach to window");
geometry();
redraw(screen, 1);
}
int
label(Win w, Mouse m)
{
char buf[512], fname[128];
int n, fd;
snprint(buf, sizeof(buf), "%s", w.label);
n = eenter(nil, buf, sizeof(buf), &m);
if(n <= 0)
return 0;
sprint(fname, "/dev/wsys/%d/label", w.n);
if((fd = open(fname, OWRITE)) < 0)
return 0;
write(fd, buf, n);
close(fd);
refreshwin();
redraw(screen, 1);
return 1;
}
int
unhide(Win w)
{
char buf[128];
int fd;
sprint(buf, "/dev/wsys/%d/wctl", w.n);
if((fd = open(buf, OWRITE)) < 0)
return 0;
if(w.state == (CURRENT|VISIBLE))
write(fd, "hide\n", 5);
else {
write(fd, "unhide\n", 7);
write(fd, "top\n", 4);
write(fd, "current\n", 8);
}
close(fd);
return 1;
}
int
click(Mouse m)
{
int i, b;
b = m.buttons & 7;
if(b != 2 && b != 4)
return 0;
for(i=0; i<nwin; i++)
if(ptinrect(m.xy, win[i].r))
break;
if(i == nwin)
return 0;
do
m = emouse();
while((m.buttons & 7) == b);
if((m.buttons & 7) || !ptinrect(m.xy, win[i].r))
return 0;
switch(b) {
case 2:
return label(win[i], m);
case 4:
return unhide(win[i]);
default:
return 0;
}
}
void
usage(void)
{
fprint(2, "usage: winwatch [-e exclude] [-f font]\n");
exits("usage");
}
void
main(int argc, char **argv)
{
char *fontname = nil;
int Etimer;
Event e;
int i;
ARGBEGIN{
case 'f':
fontname = EARGF(usage());
break;
case 'e':
exclude = regcomp(EARGF(usage()));
if(exclude == nil)
sysfatal("Bad regexp");
break;
default:
usage();
}ARGEND
if(argc)
usage();
if(initdraw(0, fontname, "winwatch") < 0)
sysfatal("initdraw: %r");
lightblue = allocimagemix(display, DPalebluegreen, DWhite);
statecol[0] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xCCCCCCFF);
statecol[1] = lightblue;
statecol[2] = lightblue;
statecol[3] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPalegreygreen);
for(i=0; i<nelem(statecol); i++)
if(statecol[i] == nil)
sysfatal("allocimage: %r");
refreshwin();
redraw(screen, 1);
einit(Emouse|Ekeyboard);
Etimer = etimer(0, 2500);
for(;;){
switch(eread(Emouse|Ekeyboard|Etimer, &e)){
case Ekeyboard:
if(e.kbdc==Kdel || e.kbdc=='q')
exits(0);
break;
case Emouse:
if(click(e.mouse) == 0)
continue;
/* fall through */
default: /* Etimer */
refreshwin();
redraw(screen, 0);
break;
}
}
}