ref: 4d0493cc927121ae117b0e3c5e5179902a134d37
parent: 87bb47145bb65c9fc656a71fdcbed62d6a7a6f81
parent: aca0d232c8c02c5bd688ea05fa95dcf259ca468d
author: qwx <[email protected]>
date: Tue Mar 26 22:04:11 EDT 2024
merge
--- /dev/null
+++ b/volctl.c
@@ -1,0 +1,308 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <event.h>
+
+// volctl.c: simple volume control for /dev/volume, using our shatup /dev/theme
+
+char *progname = "volctl";
+
+char *vf;
+int vfd;
+
+Font *font;
+
+// if set, write extra stuff to stderr
+int debug;
+
+enum {
+ Mono,
+ Stereo,
+};
+
+typedef struct Volume Volume;
+struct Volume {
+ char *name;
+ int type;
+ int llevel;
+ int rlevel;
+ Rectangle r;
+};
+
+Volume *voltab;
+int Nvolume;
+
+#pragma varargck type "V" Volume*
+static int
+volfmt(Fmt *fmt)
+{
+ Volume *v;
+ v = va_arg(fmt->args, Volume*);
+
+ if(v->type == Stereo)
+ return fmtprint(fmt, "%s %d %d", v->name, v->llevel, v->rlevel);
+
+ return fmtprint(fmt, "%s %d", v->name, v->llevel);
+}
+
+void
+getvol(void)
+{
+ char buf[256];
+ char *line[32], *elem[3];
+ int i, l, n;
+ Volume *v;
+
+ // read volume file
+ seek(vfd, 0, 0);
+ if((n = read(vfd, buf, sizeof buf)) <= 0) {
+ fprint(2, "cannot read volume file: %r\n");
+ exits("read");
+ }
+
+ if(n >= sizeof buf)
+ n = sizeof(buf)-1;
+ buf[n] = 0;
+
+ // break volume lines into fields
+ n = getfields(buf, line, nelem(line), 1, "\n");
+
+ voltab = calloc(n, sizeof(struct Volume));
+
+ // fill each Volume struct
+ for(i=0; i<n; i++) {
+ v = voltab+Nvolume;
+ l = tokenize(line[i], elem, nelem(elem));
+
+ v->name = strdup(elem[0]);
+
+ switch(l) {
+ case 2:
+ v->type = Mono;
+ v->llevel = v->rlevel = atoi(elem[1]);
+ break;
+ case 3:
+ v->type = Stereo;
+ v->llevel = atoi(elem[1]);
+ v->rlevel = atoi(elem[2]);
+ break;
+ default:
+ fprint(2, "bad volume line '%s'\n", line[i]);
+ exits(0);
+ }
+
+ // we dont care about these
+ if(strcmp("speed", elem[0]) == 0 || strcmp("delay", elem[0]) == 0) {
+ if(debug) fprint(2, "ignored: %V\n", v);
+ continue;
+ } else {
+ Nvolume++;
+ }
+
+ if(debug) fprint(2, "parsed: %V\n", v);
+ }
+}
+
+void
+setvol(Volume *v)
+{
+ seek(vfd, 0, 0);
+
+ if(debug)
+ fprint(2, "write '%V'\n", v);
+ if(fprint(vfd, "%V", v) < 0)
+ fprint(2, "write '%V' to %s: %r\n", v, vf);
+}
+
+int nvrect;
+int toprect;
+Image *slidercolor;
+Image *background;
+Image *bordertext;
+
+void
+drawvol(Volume *v, int new)
+{
+ int midlx, midrx, midy;
+
+ midlx = v->r.min.x+(Dx(v->r)*v->llevel)/100;
+ midrx = v->r.min.x+(Dx(v->r)*v->rlevel)/100;
+ midy = v->r.min.y+Dy(v->r)/2;
+
+ if(new) {
+ border(screen, v->r, -1, bordertext, ZP);
+ draw(screen, v->r, background, nil, ZP);
+ line(screen, Pt(v->r.min.x, midy), Pt(v->r.max.x-1, midy), 0, 0, 0, background, ZP);
+ }
+
+ if(v->type == Stereo) {
+ draw(screen, Rect(v->r.min.x, v->r.min.y, midlx, midy), slidercolor, nil, ZP);
+ draw(screen, Rect(v->r.min.x, midy+1, midrx, v->r.max.y), slidercolor, nil, ZP);
+ } else {
+ draw(screen, Rect(v->r.min.x, v->r.min.y, midlx, v->r.max.y), slidercolor, nil, ZP);
+ }
+
+ if(!new) {
+ if(v->type == Stereo) {
+ draw(screen, Rect(midlx, v->r.min.y, v->r.max.x, midy), background, nil, ZP);
+ draw(screen, Rect(midrx, midy+1, v->r.max.x, v->r.max.y), background, nil, ZP);
+ } else {
+ draw(screen, Rect(midlx, v->r.min.y, v->r.max.x, v->r.max.y), background, nil, ZP);
+ }
+ }
+
+ string(screen, Pt(v->r.max.x-stringwidth(font, v->name)-5, v->r.min.y+2),
+ bordertext, ZP, font, v->name);
+}
+
+void
+redraw(Image *screen)
+{
+ enum { PAD=3, MARGIN=5 };
+ Point p;
+ int i, ht, wid;
+ Volume *v;
+
+ p = stringsize(font, "0");
+ ht = p.y + 2*2;
+ nvrect = (Dy(screen->r)-2*MARGIN)/(ht+PAD)+1;
+ wid = Dx(screen->r)-2*MARGIN;
+ if(nvrect >= Nvolume) {
+ nvrect = Nvolume;
+ toprect = 0;
+ }
+
+ draw(screen, screen->r, background, nil, ZP);
+ p = addpt(screen->r.min, Pt(MARGIN, MARGIN));
+ for(i=0; i<nvrect; i++) {
+ v = &voltab[(i+toprect)%Nvolume];
+ v->r = Rpt(p, addpt(p, Pt(wid, ht)));
+ p.y += ht+PAD;
+ drawvol(v, 1);
+ }
+ for(; i<Nvolume; i++){
+ v = &voltab[(i+toprect)%Nvolume];
+ v->r = Rect(0,0,0,0);
+ }
+}
+
+void
+click(Mouse m)
+{
+ Volume *v, *ev;
+ int mid, δ, lev;
+ int what;
+
+ if(m.buttons & 1) {
+ if(nvrect < Nvolume) {
+ if(toprect-- == 0)
+ toprect = Nvolume-1;
+ redraw(screen);
+ }
+ do {
+ m = emouse();
+ } while(m.buttons);
+ return;
+ }
+
+ if(m.buttons & 6) {
+ what = m.buttons;
+ for(v=voltab, ev=v+Nvolume; v<ev; v++) {
+ if(ptinrect(m.xy, v->r))
+ break;
+ }
+ if(v >= ev)
+ return;
+ mid = v->r.min.y+Dy(v->r)/2;
+ δ = m.xy.y - mid;
+
+ do {
+ lev = ((m.xy.x - v->r.min.x)*100)/Dx(v->r);
+ if(lev < 0)
+ lev = 0;
+ if(lev > 100)
+ lev = 100;
+ // if(-5 < δ && δ < 5)
+ if(what & 2 || v->type == Mono)
+ v->rlevel = v->llevel = lev;
+ else if(δ > 0)
+ v->rlevel = lev;
+ else
+ v->llevel = lev;
+ setvol(v);
+ drawvol(v, 0);
+ m = emouse();
+ } while(m.buttons);
+ return;
+ }
+ exits(0);
+}
+
+void
+main(int argc, char **argv)
+{
+ Event e;
+
+ vf = "/dev/volume";
+ ARGBEGIN{
+ case 'f':
+ vf = ARGF();
+ break;
+ case 'd':
+ debug++;
+ break;
+ default:
+ goto Usage;
+ }ARGEND;
+
+ if(argc) {
+ Usage:
+ fprint(2, "usage: %s [-f volumefile]\n", progname);
+ exits("usage");
+ }
+
+ if((vfd = open(vf, ORDWR)) < 0) {
+ fprint(2, "cannot open '%s': %r\n", vf);
+ exits("open");
+ }
+
+ fmtinstall('V', volfmt);
+ getvol();
+ initdraw(0, 0, progname);
+
+ Theme th[] = {
+ { "back", DPaleyellow },
+ { "border", DYellowgreen },
+ { "text", DBlack },
+ };
+ readtheme(th, nelem(th), nil);
+ background = allocimage(display, Rect(0,0,1,1), screen->chan, 1, th[0].c);
+ slidercolor = allocimage(display, Rect(0,0,1,1), screen->chan, 1, th[1].c);
+ bordertext = allocimage(display, Rect(0,0,1,1), screen->chan, 1, th[2].c);
+
+ font = display->defaultfont;
+ redraw(screen);
+ einit(Emouse|Ekeyboard);
+
+ for(;;) {
+ switch(eread(Emouse|Ekeyboard, &e)) {
+ case Ekeyboard:
+ if(e.kbdc == 0x7F || e.kbdc == 'q')
+ exits(0);
+ break;
+ case Emouse:
+ if(e.mouse.buttons)
+ click(e.mouse);
+ break;
+ }
+ }
+}
+
+void
+eresized(int new)
+{
+ if(new && getwindow(display, Refmesg) < 0)
+ fprint(2,"can't reattach to window");
+ redraw(screen);
+}
+