shithub: winview

Download patch

ref: 11948e5234a86097c64e59fdf0a7507e834ad362
author: sirjofri <[email protected]>
date: Sun Sep 1 09:39:51 EDT 2024

adds files

--- /dev/null
+++ b/Readme.txt
@@ -1,0 +1,41 @@
+
+     WINVIEW(1)                                             WINVIEW(1)
+
+     NAME
+          winview - visual winwatch
+
+     SYNOPSIS
+          winview [ -w windir ] [ -x amount ] [ -t milliseconds ]
+
+     DESCRIPTION
+          Winview behaves like winwatch(1), but it displays downscaled
+          screenshots of the windows.
+
+          The windir parameter is used to specify the rio filesystem,
+          default is /dev/wsys. The amount parameter determines how
+          much to downscale the windows.  Windows will be halved
+          amount times, for example a value of 4 halves the windows 4
+          times.  Valid values for amount are from 1 to 5.
+          Milliseconds is the amount of milliseconds to wait for each
+          update.
+
+          General usage is very similar to winwatch:
+
+          •    Left mouse button to activate the window.
+
+          •    Middle mouse button to set the window label.
+
+          •    Right mouse button to (un)hide the window.
+
+          The amount value can also be adjusted at runtime by using
+          the , and . keys.
+
+     SOURCE
+          winview.c
+
+     SEE ALSO
+          winwatch(1)
+
+     BUGS
+          Sure.
+
--- /dev/null
+++ b/mkfile
@@ -1,0 +1,15 @@
+</$objtype/mkfile
+
+TARG=winview
+OFILES=winview.$O
+
+</sys/src/cmd/mkone
+
+man:V: Readme.txt
+
+Readme.txt: winview1
+	@{
+		rfork n
+		bind . /sys/man/1
+		man 1 $prereq > $target
+	}
--- /dev/null
+++ b/winview.c
@@ -1,0 +1,589 @@
+#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;
+	return buf;
+}
+
+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;
+	Rectangle textbox;
+	
+	winname = strdup(getwinname(dir->name));
+	if (!winname)
+		return 0;
+	if (issamewin(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);
+	
+	textbox.min = addpt(coords, halfcell);
+	textbox.max = addpt(textbox.min, stringsize(font, winname));
+	textbox = insetrect(textbox, -5);
+	
+	memimagedraw(target, textbox, tback, ZP, nil, ZP, SoverD);
+	memimagestring(target, addpt(coords, halfcell),
+		contrast, ZP, memdefont, 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();
+	}
+}
--- /dev/null
+++ b/winview1
@@ -1,0 +1,62 @@
+.TH WINVIEW 1
+.SH NAME
+winview \- visual winwatch
+.SH SYNOPSIS
+.B winview
+[
+.B -w
+.I windir
+]
+[
+.B -x
+.I amount
+]
+[
+.B -t
+.I milliseconds
+]
+.SH DESCRIPTION
+.I Winview
+behaves like
+.IR winwatch (1),
+but it displays downscaled screenshots of the windows.
+.PP
+The
+.I windir
+parameter is used to specify the rio filesystem, default is
+.IR /dev/wsys .
+The
+.I amount
+parameter determines how much to downscale the windows.
+Windows will be halved
+.I amount
+times, for example a value of
+.I 4
+halves the windows 4 times.
+Valid values for
+.I amount
+are from 1 to 5.
+.I Milliseconds
+is the amount of milliseconds to wait for each update.
+.PP
+General usage is very similar to winwatch:
+.IP •
+Left mouse button to activate the window.
+.IP •
+Middle mouse button to set the window label.
+.IP •
+Right mouse button to (un)hide the window.
+.PP
+The
+.I amount
+value can also be adjusted at runtime by using the
+.I ,
+and
+.I .
+keys.
+.SH SOURCE
+.B winview.c
+.SH SEE ALSO
+.IR winwatch (1)
+.SH BUGS
+Sure.