shithub: bar

ref: 8456690db7e4fc2f5f17c70233e29aec233fec60
dir: /bar.c/

View raw version
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <draw.h>
#include <keyboard.h>
#include <mouse.h>
#include <thread.h>
#include <tos.h>

#define MAX(a,b) ((a)>=(b)?(a):(b))

enum {
	Off = 3,
};

static int wctl, owidth, width, twidth, bottom, bat, minheight;
static Image *cback, *ctext;
static char sep[16], bats[16], *aux;
static char *pos = "rb";
static Tzone *local;
static Font *f;

#pragma varargck type "|" char*
static int
sepfmt(Fmt *f)
{
	return fmtstrcpy(f, va_arg(f->args, char*)[0] ? sep : "");
}

/*
 * nsec() is wallclock and can be adjusted by timesync
 * so need to use cycles() instead, but fall back to
 * nsec() in case we can't
 */
static uvlong
nanosec(void)
{
	static uvlong fasthz, xstart;
	uvlong x, div;

	if(fasthz == ~0ULL)
		return nsec() - xstart;

	if(fasthz == 0){
		fasthz = _tos->cyclefreq;
		if(fasthz == 0){
			fasthz = ~0ULL;
			xstart = nsec();
			return 0;
		}else{
			cycles(&xstart);
		}
	}
	cycles(&x);
	x -= xstart;

	/* this is ugly */
	for(div = 1000000000ULL; x < 0x1999999999999999ULL && div > 1 ; div /= 10ULL, x *= 10ULL);

	return x / (fasthz / div);
}

static void
place(void)
{
	int fd, n, w, h, minx, miny, maxx, maxy;
	char t[61], *a[5];
	static int ow, oh;

	if((fd = open("/dev/screen", OREAD)) < 0)
		return;
	n = read(fd, t, sizeof(t)-1);
	close(fd);
	t[sizeof(t)-1] = 0;
	if(n != sizeof(t)-1 || tokenize(t, a, 5) != 5)
		return;
	w = atoi(a[3]);
	h = atoi(a[4]);

	if(ow != w || oh != h || owidth != width){
		if(pos[0] == 't' || pos[1] == 't'){
			miny = 0;
			maxy = minheight;
		}else{
			miny = h - minheight;
			maxy = h;
		}
		if(pos[0] == 'l' || pos[1] == 'l'){
			minx = 0;
			maxx = MAX(100, Borderwidth+Off+width+Off+Borderwidth);
		}else if(pos[0] == 'r' || pos[1] == 'r'){
			minx = MAX(100, w-(Borderwidth+Off+width+Off+Borderwidth));
			maxx = w;
		}else{
			minx = (w-MAX(100, Borderwidth+Off+width+Off+Borderwidth))/2;
			maxx = (w+MAX(100, Borderwidth+Off+width+Off+Borderwidth))/2;
		}
		snprint(t, sizeof(t), "resize -r %d %d %d %d", minx, miny, maxx, maxy);
		if(fprint(wctl, "%s", t) < 0)
			fprint(2, "%s: %r\n", t);
		ow = w;
		oh = h;
		owidth = width;
	}
}

static void
redraw(void)
{
	char s[128];
	Rectangle r;
	Tmfmt tf;
	Point p;
	Tm tm;

	lockdisplay(display);
	if(screen == nil)
		return;
	r = screen->r;

	draw(screen, r, cback, nil, ZP);

	tf = tmfmt(tmnow(&tm, local), "YYYY/MM/DD WW hh:mm:ss");
	p.x = r.min.x + Off;
	p.y = (pos[0] == 't' || pos[1] == 't') ? r.max.y - (f->height + Off) : r.min.y + Off;
	if(pos[0] == 'l' || pos[1] == 'l'){
		snprint(s, sizeof(s), "%τ%|%s%|%s", tf, bats, bats, aux, aux);
	}else{
		snprint(s, sizeof(s), "%s%|%s%|%τ", aux, aux, bats, bats, tf);
		if(pos[0] == 'r' || pos[1] == 'r')
			p.x = r.max.x - (stringwidth(f, s) + Off);
	}
	string(screen, p, ctext, ZP, f, s);

	flushimage(display, 1);
	unlockdisplay(display);

	snprint(s, sizeof(s), "%τ", tf);
	twidth = MAX(twidth, stringwidth(f, s));
	snprint(s, sizeof(s), "%|%s%|%s", bats, bats[0] ? "100%" : "", aux, aux);
	width = twidth + stringwidth(f, s);
	if(owidth != width)
		place();
}

static void
readbattery(void)
{
	char *s, tmp[16];

	s = bat < 0 || pread(bat, tmp, 4, 0) < 4 ? nil : strchr(tmp, ' ');
	if(s != nil){
		*s = 0;
		snprint(bats, sizeof(bats), "%s%%", tmp);
	}else{
		bats[0] = 0;
	}
}

static void
updateproc(void *)
{
	uvlong t1, t2, n;

	t1 = nanosec();
	t2 = t1;
	for(;;){
		sleep(250);
		if(wctl < 0)
			break;
		fprint(wctl, bottom ? "bottom" : "top");

		n = nanosec();

		if(n - t1 >= 10000000000ULL){
			readbattery();
			t1 = n;
		}
		if(n - t2 >= 1000000000ULL){
			redraw();
			t2 = n;
		}
	}

	threadexits(nil);
}

static void
auxproc(void *)
{
	Biobuf b;
	char *s;

	Binit(&b, 0, OREAD);
	for(;;){
		s = Brdstr(&b, '\n', 1);
		lockdisplay(display);
		free(aux);
		aux = s ? s : strdup("");
		unlockdisplay(display);
		redraw();
		if(wctl >= 0)
			fprint(wctl, bottom ? "bottom" : "top");
		if(s == nil)
			break;
	}
	Bterm(&b);

	threadexits(nil);
}

static void
usage(void)
{
	fprint(2, "usage: %s [-b] [-p lt|t|rt|lb|b|rb] [-s separator]\n", argv0);
	threadexitsall("usage");
}

void
threadmain(int argc, char **argv)
{
	Keyboardctl *kctl;
	Mousectl *mctl;
	char *s, *v[3];
	u32int brgb;
	Biobuf *b;
	Rune key;
	Mouse m;
	enum {
		Emouse,
		Eresize,
		Ekeyboard,
		Eend,
	};
	Alt a[] = {
		[Emouse] = { nil, &m, CHANRCV },
		[Eresize] = { nil, nil, CHANRCV },
		[Ekeyboard] = { nil, &key, CHANRCV },
		[Eend] = { nil, nil, CHANEND },
	};

	strcpy(sep, " │ ");
	ARGBEGIN{
	case 'b':
		bottom = 1;
		break;
	case 'p':
		pos = EARGF(usage());
		break;
	case 's':
		snprint(sep, sizeof(sep), "%s", EARGF(usage()));
		break;
	default:
		usage();
	}ARGEND

	fmtinstall('|', sepfmt);
	tmfmtinstall();
	if((local = tzload("local")) == nil)
		sysfatal("zone: %r");

	if((wctl = open("/dev/wctl", ORDWR)) < 0)
		sysfatal("%r");
	bat = open("/mnt/acpi/battery", OREAD);
	if(initdraw(nil, nil, "bar") < 0)
		sysfatal("initdraw: %r");
	f = display->defaultfont;
	minheight = 2*(Borderwidth+1) + f->height;
	unlockdisplay(display);
	if((mctl = initmouse(nil, screen)) == nil)
		sysfatal("initmouse: %r");
	if((kctl = initkeyboard(nil)) == nil)
		sysfatal("initkeyboard: %r");

	a[0].c = mctl->c;
	a[1].c = mctl->resizec;
	a[2].c = kctl->c;

	brgb = DPalegreygreen;
	if((b = Bopen("/dev/theme", OREAD)) != nil){
		while((s = Brdline(b, '\n')) != nil){
			s[Blinelen(b)-1] = 0;
			if(tokenize(s, v, nelem(v)) > 1 && strcmp(v[0], "ltitle") == 0){
				brgb = strtoul(v[1], nil, 16)<<8 | 0xff;
				break;
			}
		}
		Bterm(b);
	}
	cback = allocimage(display, Rect(0,0,1,1), RGB24, 1, brgb);
	brgb = ~(brgb>>8 | brgb>>16 | brgb>>24);
	brgb = brgb<<8 | brgb<<16 | brgb<<24 | 0xff;
	ctext = allocimage(display, Rect(0,0,1,1), RGB24, 1, brgb);

	aux = strdup("");
	readbattery();
	redraw();
	proccreate(updateproc, nil, 4096);
	proccreate(auxproc, nil, 16384);

	for(;;){
		switch(alt(a)){
		case Emouse:
			break;

		case Eresize:
			if(getwindow(display, Refnone) < 0)
				threadexitsall(nil);
			redraw();
			place();
			break;

		case Ekeyboard:
			if(key == Kdel){
				close(wctl);
				wctl = -1;
				threadexitsall(nil);
			}
			break;
		}
	}
}