shithub: misc

Download patch

ref: 87bb47145bb65c9fc656a71fdcbed62d6a7a6f81
parent: bcc3ca5d8b65c479a2323d548ae7be5425c5a074
author: qwx <[email protected]>
date: Tue Mar 26 22:03:24 EDT 2024

add vol(1): generic volume slider for all /dev/volume contents

--- a/mkfile
+++ b/mkfile
@@ -8,6 +8,7 @@
 	rfx\
 	spd\
 	tev\
+	vol\
 	wstat\
 
 HFILES=
--- /dev/null
+++ b/vol.c
@@ -1,0 +1,341 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <bio.h>
+#include <draw.h>
+#include <mouse.h>
+#include <keyboard.h>
+
+enum{
+	Cback,
+	Cbord,
+	Cvol,
+	Ctxt,
+	Cmute,
+	Ncols,
+
+	Bordsz = 3,
+};
+Image *cols[Ncols];
+
+typedef struct Vol Vol;
+struct Vol{
+	char name[128];
+	char value[128];
+	int text;
+	int stereo;
+	int left;
+	int right;
+	int muted;
+	Rectangle r;
+	Rectangle lr;
+	Rectangle rr;
+};
+Rectangle frame;
+Vol *voltab;
+int nvol, tabsz;
+char *dev = "/dev/volume";
+Mousectl *mctl;
+Keyboardctl *kctl;
+
+int
+writevol(Vol *v)
+{
+	Biobuf *bf;
+
+	if((bf = Bopen(dev, OWRITE)) == nil)
+		return -1;
+	if(v->text)
+		Bprint(bf, "%s %s\n", v->name, v->value);
+	else if(v->stereo)
+		Bprint(bf, "%s %d %d\n", v->name, v->left, v->right);
+	else
+		Bprint(bf, "%s %d\n", v->name, v->left);
+	Bterm(bf);
+	return 0;
+}
+
+int
+quickquiet(Vol *v)
+{
+	Biobuf *bf;
+
+	if((bf = Bopen(dev, OWRITE)) == nil)
+		return -1;
+	Bprint(bf, "%s 0\n", v->name);
+	v->left = v->right = 0;
+	Bterm(bf);
+	return 0;
+}
+
+int
+prompt(Vol *v)
+{
+	int r;
+	char buf[sizeof v->value] = {0};
+
+	strncpy(buf, v->value, sizeof buf);
+	if((r = enter(v->name, buf, sizeof(buf)-UTFmax, mctl, kctl, nil)) < 0){
+		fprint(2, "prompt: %r\n");
+		return -1;
+	}else if(r > 0)
+		strncpy(v->value, buf, sizeof buf);
+	return 0;
+}
+
+int
+readvol(char *path)
+{
+	int n;
+	Biobuf *bf;
+	char *s, *fld[4];
+	Vol *v;
+
+	if((bf = Bopen(path, OREAD)) == nil)
+		return -1;
+	nvol = 0;
+	for(;;){
+		if((s = Brdstr(bf, '\n', 1)) == nil)
+			break;
+		if((n = getfields(s, fld, nelem(fld), 1, " ")) < 1 || n > 3)
+			goto next;
+		if(nvol++ >= tabsz){
+			tabsz += 16;
+			if((voltab = realloc(voltab, tabsz * sizeof *voltab)) == nil)
+				sysfatal("realloc: %r");
+		}
+		v = voltab + nvol - 1;
+		memset(v, 0, sizeof *v);
+		strncpy(v->name, fld[0], sizeof(v->name)-1);
+		if(strcmp(fld[0], "delay") == 0
+		|| strcmp(fld[0], "speed") == 0
+		|| strcmp(fld[0], "dev") == 0){
+			strncpy(v->value, fld[1], sizeof(v->value)-1);
+			v->text = 1;
+			goto next;
+		}
+		v->text = 0;
+		v->stereo = n == 3;
+		v->left = strtol(fld[1], nil, 10);
+		if(n > 2){
+			v->right = strtol(fld[2], nil, 10);
+			if(v->right == 0.0)
+				v->stereo = 0;
+		}
+next:
+		free(s);
+	}
+	Bterm(bf);
+	return 0;
+}
+
+Vol *
+getcur(Point p, int *right)
+{
+	Vol *v;
+
+	for(v=voltab; v<voltab+nvol; v++){
+		if(ptinrect(p, v->r)){
+			if(ptinrect(p, v->rr))
+				*right = 1;
+			else
+				*right = 0;
+			break;
+		}
+	}
+	return (v == voltab + nvol ? nil : v);
+}
+
+void
+redraw(void)
+{
+	int h;
+	Vol *v;
+	Point p;
+	Rectangle r, rr;
+
+	draw(screen, screen->r, cols[Cback], nil, ZP);
+	h = (Dy(frame) - nvol) / nvol;
+	rr = frame;
+	rr.max.y = frame.min.y + h + 1;
+	for(v=voltab; v<voltab+nvol; v++){
+		border(screen, rr, 1, cols[Cbord], ZP);
+		r = v->lr;
+		if(v->text){
+			p = subpt(r.max, stringsize(font, v->value));
+			string(screen, p, cols[Ctxt], ZP, font, v->value);
+		}else{
+			r.max.x = r.min.x + Dx(r) * v->left / 100;
+			draw(screen, r, cols[v->muted ? Cmute : Cvol], nil, ZP);
+			if(v->stereo){
+				r = v->rr;
+				r.max.x = r.min.x + Dx(r) * v->right / 100;
+				draw(screen, r, cols[v->muted ? Cmute : Cvol], nil, ZP);
+			}
+		}
+		r = v->lr;
+		string(screen, addpt(r.min, Pt(2,2)), cols[Ctxt], ZP, font, v->name);
+		rr = rectaddpt(rr, Pt(0, h+1));
+	}
+	flushimage(display, 1);
+}
+
+void
+snarfgeom(void)
+{
+	int hh, h;
+	Rectangle r, rr;
+	Vol *v;
+
+	frame = insetrect(screen->r, Bordsz+1);
+	hh = (Dy(frame) - nvol) / nvol;
+	rr = frame;
+	rr.max.y = frame.min.y + hh + 1;
+	for(v=voltab; v<voltab+nvol; v++){
+		r = insetrect(rr, 1);
+		v->r = r;
+		h = Dy(r);
+		v->lr = r;
+		if(v->stereo){
+			r.max.y -= Dy(r) / 2 + 1;
+			v->lr.max.y = r.max.y;
+			r = rectaddpt(r, Pt(0, h/2+1));
+			v->rr = r;
+		}
+		else
+			v->rr = ZR;
+		rr = rectaddpt(rr, Pt(0, hh + 1));
+	}
+}
+
+void
+load(void)
+{
+	if(readvol(dev) < 0)
+		sysfatal("readvol: %r");
+	snarfgeom();
+}
+
+void
+reset(void)
+{
+	load();
+	redraw();
+}
+
+void
+usage(void)
+{
+	fprint(2, "usage: %s [-f dev]\n", argv0);
+	threadexits("usage");
+}
+
+void
+threadmain(int argc, char **argv)
+{
+	int i, right;
+	double Δ;
+	Rune r;
+	Mouse mold;
+	Vol *cur;
+
+	ARGBEGIN{
+	case 'f': dev = EARGF(usage()); break;
+	}ARGEND
+	if(initdraw(0, 0, "vol") < 0)
+		sysfatal("initdraw: %r");
+	if((mctl = initmouse(nil, screen)) == nil)
+		sysfatal("initmouse: %r");
+	if((kctl = initkeyboard(nil)) == nil)
+		sysfatal("initkeyboard: %r");
+	fmtinstall('P', Pfmt);
+	fmtinstall('R', Rfmt);
+	Theme th[nelem(cols)] = {
+		[Cback] { "back",	DBlack },
+		[Cbord] { "border",	DWhite },
+		[Cvol] { "paletext",	0x777777FF },
+		[Cmute] { "holdtext", DMedblue },
+		[Ctxt] { "text",	0x777777FF },
+	};
+	readtheme(th, nelem(th), nil);
+	for(i=0; i<nelem(cols); i++)
+ 		cols[i] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, th[i].c); 
+	if(readvol(dev) < 0)
+		sysfatal("readvol: %r");
+	reset();
+	enum{AMOUSE, ARESIZE, AKEY, AEND};
+	Alt a[AEND+1] = {
+		[AMOUSE] {mctl->c, &mctl->Mouse, CHANRCV},
+		[ARESIZE] {mctl->resizec, nil, CHANRCV},
+		[AKEY] {kctl->c, &r, CHANRCV},
+		[AEND] {nil, nil, CHANEND}
+	};
+	for(cur=nil;;){
+		switch(alt(a)){
+		case AMOUSE:
+			/* FIXME: maybe fsm should be flipped to work based on position
+			 * instead of button */
+			/* FIXME: device: choose from list of valid devices */
+			if(mctl->buttons != 0 && mctl->buttons != mold.buttons)
+				cur = nil;
+			if((mctl->buttons & 7) == 1){
+				if(cur == nil && (cur = getcur(mctl->xy, &right)) == nil)
+					break;
+				if(cur->text)
+					break;
+				Δ = 100.0 * (mctl->xy.x - cur->r.min.x) / Dx(cur->r);
+				cur->left = cur->right = Δ;
+				if(writevol(cur) < 0){
+					fprint(2, "writevol: %r\n");
+					load();
+				}
+				redraw();
+			}else if((mctl->buttons & 7) == 2){
+				if(cur == nil && (cur = getcur(mctl->xy, &right)) == nil)
+					break;
+				if(cur->text)
+					break;
+				else{
+					Δ = 100.0 * (mctl->xy.x - cur->r.min.x) / Dx(cur->r);
+					if(right)
+						cur->right = Δ;
+					else
+						cur->left = Δ;
+					if(writevol(cur) < 0){
+						fprint(2, "writevol: %r\n");
+						load();
+					}
+				}
+				redraw();
+			}else if((mctl->buttons & 7) == 4){
+				if(cur == nil && (cur = getcur(mctl->xy, &right)) == nil)
+					break;
+				if(cur->text)
+					prompt(cur);
+				/* FIXME */
+				//quickquiet(cur);
+				redraw();
+				cur = nil;
+			}else if(cur != nil){
+				if(writevol(cur) < 0)
+					sysfatal("writevol: %r");
+				reset();
+				cur = nil;
+			}else
+				cur = nil;
+			mold = mctl->Mouse;
+			break;
+		case ARESIZE:
+			if(getwindow(display, Refnone) < 0)
+				sysfatal("getwindow: %r");
+			reset();
+			break;
+		case AKEY:
+			switch(r){
+			case 'q':
+			case Kdel: threadexitsall(nil);
+			}
+			break;
+		}
+	}
+}