shithub: misc

ref: aca0d232c8c02c5bd688ea05fa95dcf259ca468d
dir: /volctl.c/

View raw version
#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);
}