373 lines
6.2 KiB
C
373 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, ncols, z;
|
|
Rectangle r;
|
|
|
|
z = 0;
|
|
rows = (Dy(screen->r)-2*MARGIN+PAD)/(font->height+PAD);
|
|
if(rows <= 0)
|
|
rows = 1;
|
|
if(rows*cols < nwin || rows*cols >= nwin*2){
|
|
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;
|
|
}
|
|
}
|
|
}
|