#include #include #include #include // volctl.c: simple volume control for /dev/volume 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; iname = 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; ir = Rpt(p, addpt(p, Pt(wid, ht))); p.y += ht+PAD; drawvol(v, 1); } for(; ir = 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; vr)) 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); slidercolor = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x555555FF); background = display->black; bordertext = display->white; 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); }