shithub: winview

ref: 0729f86ea9233ed4e2e7881fe5ac8aee735c78ee
dir: /winview.c/

View raw version
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <memdraw.h>
#include <event.h>
#include <cursor.h>

void
usage(void)
{
	fprint(2, "usage: %s\n", argv0);
	exits("usage");
}

char *windir = "/dev/wsys";
char *wintitle = "winview";

typedef struct Win Win;
struct Win {
	char *name;
};

Win *windows = nil;
int numwindows = 0;

Point cellsize;
Point cells;
Point halfcell;
Point hovercell = {0, 0};

int smaller = 2;

void
reallocwindows(int num)
{
	int i;
	if (windows) {
		for (i = 0; i < numwindows; i++) {
			if (windows[i].name)
				free(windows[i].name);
		}
		free(windows);
	}
	numwindows = num;
	windows = mallocz(numwindows * sizeof(Win), 1);
}

int
getwid(void)
{
	int n;
	n = hovercell.y * cells.x + hovercell.x;
	if (n >= numwindows)
		return -1;
	return n;
}

Point
clampinrect(Point xy, Rectangle r)
{
	if (xy.x < r.min.x)
		xy.x = r.min.x;
	if (xy.y < r.min.y)
		xy.y = r.min.y;
	if (xy.x > r.max.x)
		xy.x = r.max.x;
	if (xy.y > r.max.y)
		xy.y = r.max.y;
	return xy;
}

uchar
ilerp(uchar A, uchar B, double α)
{
	ulong a = A;
	ulong b = B;
	return (b * α) + (a * (1. - α));
}

Memimage*
downsample(Memimage *i)
{
	Memimage *t;
	Rectangle r;
	Point xy;
	Point fxy;
	uchar *fp, *tp;
	int c, px;
	
	if (!i)
		sysfatal("%r");
	
	r = i->r;
	r.min.x /= 2;
	r.min.y /= 2;
	r.max.x /= 2;
	r.max.y /= 2;
	
	if (Dx(r) <= 0 || Dy(r) <= 0)
		return nil;
	
	t = allocmemimage(r, i->chan);
	if (!t)
		sysfatal("%r");
	
	for (xy.y = r.min.y; xy.y < r.max.y; xy.y++) {
		for (xy.x = r.min.x; xy.x < r.max.x; xy.x++) {
			tp = byteaddr(t, xy);
			fxy = mulpt(xy, 2);
			
			for (px = 0; px < 4; px++) {
				switch (px) {
				case 0:
					/* empty: top left */
					break;
				case 1:
					fxy.x++; /* top right */
					break;
				case 2:
					fxy.y++; /* bottom right */
					break;
				case 3:
					fxy.x--; /* bottom left */
					break;
				}
				for (c = 0; c < i->nchan; c++) {
					fp = byteaddr(i, clampinrect(fxy, i->r));
					if (c == 0) {
						tp[c] = fp[c];
						continue;
					}
					tp[c] = ilerp(tp[c], fp[c], 0.5);
				}
			}
		}
	}
	return t;
}

int
ishidden(char *win)
{
	char buf[6*12];
	char *tokens[6];
	char *s;
	int fd;
	
	s = smprint("%s/%s/wctl", windir, win);
	if (!s)
		sysfatal("%r");
	fd = open(s, OREAD);
	free(s);
	if (fd < 0)
		sysfatal("%r");
	
	if (read(fd, buf, sizeof(buf)) != 6*12) {
		close(fd);
		sysfatal("short read: %r");
	}
	close(fd);
	buf[6*12-1] = 0;
	if (tokenize(buf, tokens, 6) != 6)
		sysfatal("bad read of wctl file");
	return strcmp(tokens[5], "hidden") == 0;
}

char*
getwinname(char *win)
{
	char buf[128];
	char *s;
	int n;
	int fd;
	
	s = smprint("%s/%s/label", windir, win);
	if (!s)
		sysfatal("%r");
	fd = open(s, OREAD);
	free(s);
	if (fd < 0)
		return nil;
	if ((n = read(fd, buf, 127)) <= 0) {
		close(fd);
		return nil;
	}
	close(fd);
	buf[n] = 0;
	s = strdup(buf);
	return s;
}

int
issamewin(char *winname)
{
	return strcmp(winname, wintitle) == 0;
}

Memsubfont *memdefont = nil;
Memimage *contrast = nil;
Memimage *tback = nil;
Image *green = nil;
Image *red = nil;

int
isselected(Point xy)
{
	return xy.x == hovercell.x && xy.y == hovercell.y;
}

Image*
getbordercolor(int isselected, int x, int y)
{
	int n;
	char *w;
	
	if (isselected)
		return green;
	
	n = y * cells.x + x;
	if (n >= numwindows)
		return display->white;
	w = windows[n].name;
	if (!w)
		return display->white;
	return ishidden(w) ? red : display->white;
}

void
drawgrid(void)
{
	int x, y;
	Rectangle r;
	int i;
	
	for (x = 0; x < cells.x; x++) {
		for (y = 0; y < cells.y; y++) {
			r.min.x = x * cellsize.x;
			r.min.y = y * cellsize.y;
			r.max.x = r.min.x + cellsize.x;
			r.max.y = r.min.y + cellsize.y;
			i = isselected(Pt(x, y));
			border(screen, rectaddpt(r, screen->r.min),
			i ? 2 : 2,
			getbordercolor(i, x, y), ZP);
		}
	}
}

int
redrawwin(Dir *dir, Memimage *target, int num)
{
	int fd, i;
	char *s;
	char *winname;
	Memimage *m, *mn;
	Point coords, txt, ts;
	Rectangle textbox;
	
	winname = getwinname(dir->name);
	if (!winname)
		return 0;
	if (issamewin(winname)) {
		free(winname);
		return 0;
	}
	
	s = smprint("%s/%s/window", windir, dir->name);
	if (!s)
		sysfatal("%r");
	fd = open(s, OREAD);
	free(s);
	if (fd < 0)
		return 0;
	m = readmemimage(fd);
	close(fd);
	if (!m)
		return 0;
	
	for (i = 0; i < smaller; i++) {
		mn = downsample(m);
		freememimage(m);
		m = mn;
	}
	
	if (!m) {
		fprint(2, "ignoring window %s\n", dir->name);
		return 0;
	}
	
	coords.x = num % cells.x;
	coords.y = num / cells.x;
	coords.x *= cellsize.x;
	coords.y *= cellsize.y;
	coords.x += target->r.min.x;
	coords.y += target->r.min.y;
	
	memimagedraw(target, rectaddpt(m->r, coords),
		m, m->r.min, nil, ZP, S);
	
	ts = stringsize(font, winname);
	txt = addpt(coords, halfcell);
	txt = subpt(txt, divpt(ts, 2));
	textbox.min = txt;
	textbox.max = addpt(textbox.min, ts);
	textbox = insetrect(textbox, -5);
	
	memimagedraw(target, textbox, tback, ZP, nil, ZP, SoverD);
	memimagestring(target, txt,
		contrast, ZP, memdefont, winname);
	
	free(winname);
	
	if (!windows[num].name) {
		windows[num].name = strdup(dir->name);
	} else if (strcmp(windows[num].name, dir->name) != 0) {
		free(windows[num].name);
		windows[num].name = strdup(dir->name);
	}
	return 1;
}

Memimage *bufimg = nil;

void
update(void)
{
	Dir *dirs;
	int fd;
	long num, l;
	int i;
	
	fd = open(windir, OREAD);
	if (fd < 0)
		sysfatal("%r");
	
	num = dirreadall(fd, &dirs);
	close(fd);
	if (num < 0)
		goto Out;
	
	reallocwindows(num);
	
	if (!bufimg) {
		bufimg = allocmemimage(screen->r, screen->chan);
		if (!bufimg)
			sysfatal("%r");
	}
	
	memfillcolor(bufimg, DBlack);
	i = 0;
	for (l = 0; l < num; l++)
		i += redrawwin(&dirs[l], bufimg, i);
	
	l = Dy(bufimg->r) * Dx(bufimg->r) * bufimg->depth;
	loadimage(screen, screen->r, bufimg->data->bdata, l);
	drawgrid();

Out:
	free(dirs);
}

void
eresized(int new)
{
	int i, n;
	
	if (new && getwindow(display, Refnone) < 0)
		sysfatal("%r");
	
	if (bufimg) {
		freememimage(bufimg);
		bufimg = nil;
	}
	
	n = 2;
	for (i = 1; i < smaller; i++)
		n *= 2;
	
	cellsize.x = Dx(display->image->r) / n;
	cellsize.y = Dy(display->image->r) / n;
	cells.x = Dx(screen->r) / cellsize.x;
	cells.y = Dy(screen->r) / cellsize.y;
	halfcell = divpt(cellsize, 2);
	
	draw(screen, screen->r, display->black, nil, ZP);
	update();
}

int
getwctl(void)
{
	char *w, *s;
	int n;
	
	n = getwid();
	if (n < 0)
		return -1;
	
	w = windows[n].name;
	if (!w)
		return -1;
	
	s = smprint("%s/%s/wctl", windir, w);
	if (!s)
		sysfatal("%r");
	
	n = open(s, OWRITE);
	free(s);
	return n;
}

void
activatewin(void)
{
	int fd;
	
	fd = getwctl();
	if (fd < 0)
		return;
	
	fprint(fd, "unhide");
	fprint(fd, "top");
	fprint(fd, "current");
	close(fd);
}

void
hidewin(void)
{
	int fd;
	int h;
	
	fd = getwctl();
	if (fd < 0)
		return;
	
	h = ishidden(windows[getwid()].name);
	
	fprint(fd, h ? "unhide" : "hide");
	close(fd);
}

void
renamewin(Mouse *m)
{
	int fd, n;
	char *w, *s;
	char buf[128];
	
	n = getwid();
	if (n < 0)
		return;
	
	w = windows[n].name;
	if (!w)
		return;
	
	s = smprint("%s/%s/label", windir, w);
	if (!s)
		sysfatal("%r");
	fd = open(s, ORDWR);
	free(s);
	if (fd < 0)
		return;
	
	if ((n = read(fd, buf, 127)) <= 0) {
		close(n);
		return;
	}
	buf[n] = 0;
	if (!eenter("label", buf, sizeof(buf), m)) {
		close(fd);
		return;
	}
	
	fprint(fd, "%s", buf);
	close(fd);
}

void
mouseinput(Mouse m)
{
	if (m.buttons == 0) {
		m.xy.x -= screen->r.min.x;
		m.xy.y -= screen->r.min.y;
		hovercell.x = m.xy.x / cellsize.x;
		hovercell.y = m.xy.y / cellsize.y;
		drawgrid();
		return;
	}
	
	switch (m.buttons) {
	case 4: /* right button */
		hidewin();
		break;
	case 2: /* middle button */
		renamewin(&m);
		break;
	case 1: /* left button */
		activatewin();
		break;
	}
}

void
keyinput(int k)
{
	switch (k) {
	case '.':
		smaller--;
		if (smaller < 1)
			smaller = 1;
		goto Redraw;
	case ',':
		smaller++;
		if (smaller > 5)
			smaller = 5;
		goto Redraw;
	}
	return;
Redraw:
	eresized(0);
}

void
main(int argc, char **argv)
{
	int e, timer;
	int ms = 1000;
	Event ev;
	
	ARGBEGIN{
	case 'w':
		windir = EARGF(usage());
		break;
	case 'x':
		smaller = atoi(EARGF(usage()));
		break;
	case 't':
		ms = atoi(EARGF(usage()));
		break;
	case 'h':
		usage();
		break;
	}ARGEND;
	
	if (smaller < 1)
		smaller = 1;
	if (smaller > 5)
		smaller = 5;
	
	if (ms < 100)
		ms = 100;
	
	if (initdraw(nil, nil, wintitle) < 0)
		sysfatal("%r");
	
	if (memimageinit() < 0)
		sysfatal("%r");
	
	memdefont = getmemdefont();
	
	contrast = allocmemimage(Rect(0, 0, 1, 1), screen->chan);
	contrast->flags |= Frepl;
	contrast->clipr = Rect(-0x3FFFFFF, -0x3FFFFFF, 0x3FFFFFF, 0x3FFFFFF);
	memfillcolor(contrast, DWhite);
	
	tback = allocmemimage(Rect(0, 0, 1, 1), RGBA32);
	tback->flags |= Frepl;
	tback->clipr = Rect(-0x3FFFFFF, -0x3FFFFFF, 0x3FFFFFF, 0x3FFFFFF);
	memfillcolor(tback, 0x00000077);
	
	green = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0x00FF00FF);
	red = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xFF0000FF);
	
	einit(Emouse|Ekeyboard);
	timer = etimer(0, ms);
	
	eresized(0);
	
	for (;;) {
		e = event(&ev);
		
		switch (e) {
		case Emouse:
			mouseinput(ev.mouse);
			break;
		case Ekeyboard:
			if (ev.kbdc == 'q')
				exits(nil);
			keyinput(ev.kbdc);
			break;
		}
		if (e == timer)
			update();
	}
}