ref: afc5d2b7f964522e35ad9023eff4c6bb213114bd
parent: fec45947f05be694706f3d4d25e9c46a42bbbb93
author: Sigrid Solveig Haflínudóttir <sigrid@ftrv.se>
date: Wed Sep 14 19:17:47 EDT 2022
bar: a status bar program
--- /dev/null
+++ b/sys/man/1/bar
@@ -1,0 +1,93 @@
+.TH BAR 1
+.SH NAME
+bar \- display a bar
+.SH SYNOPSIS
+.B bar
+[
+.I -b
+]
+[
+.I -d dateformat
+]
+[
+.I -p position
+]
+[
+.I -s separator
+]
+.SH DESCRIPTION
+.I bar
+displays a small window with current battery charge, date and time.
+Additional data may be displayed by writing lines of text to its
+standard input.
+.PP
+.I Bar
+reacts to mouse clicks by writing the "clicked" portion of the text
+(enclosed by the separator) to standard output. This can be used to
+perform additional actions by a custom script. See
+.I Examples
+section.
+.PP
+By default,
+.I bar
+is placed at the bottom right corner of the screen. This can be
+changed by speicifying option
+.I -p
+with first letters of the required placement:
+.I l
+for "left",
+.I r
+for "right",
+.I t
+for "top" and
+.I b
+for "bottom". If neither left nor right is chosen, the window will be
+placed in either top or bottom center.
+.PP
+.I Bar
+keeps itself as a top window. To make it stay at the bottom, use
+.I -b .
+.PP
+Separator can be changed with
+.I -s
+option.
+.PP
+Date and time format may be set using
+.I -d
+option, see
+.I tmdate(2).
+.SH EXAMPLES
+An example of how
+.I bar
+can be used along with
+.I riow
+to control
+.I zuke
+playback via mouse clicks:
+.PP
+.EX
+#!/bin/rc
+rfork ne
+fn mybar {
+ sed -u 's/$/ │ ⏮ │ ⏯ │ ⏭/g' \\
+ | bar \\
+ | awk -v 'c=plumb -d audio ''key ' '
+ /⏮/{system(c"<''")}
+ /⏯/{system(c"p''")}
+ /⏭/{system(c">''")}
+ ' >[2]/dev/null
+}
+riow </dev/kbdtap >/dev/kbdtap |[3] mybar
+.EE
+.PP
+The script is used instead of executing
+.I bar
+directly.
+.SH SOURCE
+/sys/src/cmd/bar.c
+.SH SEE ALSO
+.IR riow (1)
+.SH HISTORY
+Bar first appeared in 9front (September, 2022).
+.SH BUGS
+Impossible.
--- /dev/null
+++ b/sys/src/cmd/bar.c
@@ -1,0 +1,384 @@
+#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 = -1, owidth, width, twidth, bottom, bat, minheight, seplen, sepw, hlitem;
+static char sep[16], bats[16], *aux;
+static char *pos = "rb", *dfmt = "YYYY/MM/DD WW hh:mm:ss", *items[64];
+static int itemw[64], nitems;
+static Image *cback, *ctext;
+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 w, h, minx, miny, maxx, maxy;
+ char t[64];
+ static int ow, oh;
+
+ if(wctl < 0 && (wctl = open("/dev/wctl", OWRITE)) < 0)
+ return;
+
+ fprint(wctl, bottom ? "bottom" : "top");
+ w = Dx(display->image->r);
+ h = Dy(display->image->r);
+
+ 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);
+ write(wctl, "current", 7);
+ if(fprint(wctl, "%s", t) < 0)
+ fprint(2, "%s: %r\n", t);
+ ow = w;
+ oh = h;
+ owidth = width;
+ }
+}
+
+static void
+split(char *s)
+{
+ char *i;
+
+ for(nitems = 0, i = s; nitems < nelem(items); s += seplen, i = s){
+ if((s = strstr(s, sep)) != nil)
+ *s = 0;
+ if(*i == 0)
+ continue;
+ items[nitems] = i;
+ itemw[nitems++] = stringwidth(f, i) + sepw;
+ if(s == nil)
+ break;
+ }
+
+}
+
+static void
+redraw(void)
+{
+ static char s[128];
+ char tmp[128];
+ Rectangle r;
+ Tmfmt tf;
+ Point p;
+ Tm tm;
+ int i;
+
+ r = screen->r;
+
+ tf = tmfmt(tmnow(&tm, local), dfmt);
+ 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);
+ }
+ draw(screen, r, cback, nil, ZP);
+ string(screen, p, ctext, ZP, f, s);
+ if(hlitem >= 0){
+ for(i = 0; i < hlitem; i++)
+ r.min.x += itemw[i];
+ r.max.x = r.min.x + itemw[i];
+ replclipr(screen, 0, r);
+ stringbg(screen, p, cback, ZP, f, s, ctext, ZP);
+ replclipr(screen, 0, screen->r);
+ }
+ split(s);
+
+ flushimage(display, 1);
+
+ snprint(tmp, sizeof(tmp), "%τ", tf);
+ twidth = MAX(twidth, stringwidth(f, tmp));
+ snprint(tmp, sizeof(tmp), "%|%s%|%s", bats, bats[0] ? "100%" : "", aux, aux);
+ width = twidth + stringwidth(f, tmp);
+ 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
+timerproc(void *c)
+{
+ threadsetname("timer");
+ for(;;){
+ sleep(990);
+ sendul(c, 0);
+ }
+}
+
+static void
+auxproc(void *c)
+{
+ Biobuf b;
+ char *s;
+
+ threadsetname("aux");
+ Binit(&b, 0, OREAD);
+ for(;;){
+ s = Brdstr(&b, '\n', 1);
+ sendp(c, s ? s : strdup(""));
+ if(s == nil)
+ break;
+ }
+ Bterm(&b);
+
+ threadexits(nil);
+}
+
+static void
+usage(void)
+{
+ fprint(2, "usage: %s [-b] [-d dateformat] [-p lt|t|rt|lb|b|rb] [-s separator]\n", argv0);
+ threadexitsall("usage");
+}
+
+static void
+clicked(int x, int buttons)
+{
+ int i, ix;
+
+ if(hlitem >= 0){
+ hlitem = -1;
+ return;
+ }
+
+ for(i = ix = 0; i < nitems; i++){
+ ix += itemw[i];
+ if(x <= ix){
+ fprint(1, "%d\t%s\n", buttons, items[i]);
+ hlitem = i;
+ break;
+ }
+ }
+}
+
+void
+threadmain(int argc, char **argv)
+{
+ Keyboardctl *kctl;
+ Mousectl *mctl;
+ uvlong t, oldt;
+ int oldbuttons;
+ char *s, *v[3];
+ u32int brgb;
+ Biobuf *b;
+ Rune key;
+ Mouse m;
+ enum {
+ Emouse,
+ Eresize,
+ Ekeyboard,
+ Eaux,
+ Etimer,
+ Eend,
+ };
+ Alt a[] = {
+ [Emouse] = { nil, &m, CHANRCV },
+ [Eresize] = { nil, nil, CHANRCV },
+ [Ekeyboard] = { nil, &key, CHANRCV },
+ [Eaux] = { nil, &s, CHANRCV },
+ [Etimer] = { nil, nil, CHANRCV },
+ [Eend] = { nil, nil, CHANEND },
+ };
+
+ strcpy(sep, " │ ");
+ ARGBEGIN{
+ case 'b':
+ bottom = 1;
+ break;
+ case 'd':
+ dfmt = EARGF(usage());
+ break;
+ case 'p':
+ pos = EARGF(usage());
+ break;
+ case 's':
+ snprint(sep, sizeof(sep), "%s", EARGF(usage()));
+ break;
+ default:
+ usage();
+ }ARGEND
+ seplen = strlen(sep);
+
+ fmtinstall('|', sepfmt);
+ tmfmtinstall();
+ if((local = tzload("local")) == nil)
+ sysfatal("zone: %r");
+
+ 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);
+ }
+
+ if((bat = open("/mnt/pm/battery", OREAD)) < 0)
+ bat = open("/dev/battery", OREAD);
+ if(initdraw(nil, nil, "bar") < 0)
+ sysfatal("initdraw: %r");
+ f = display->defaultfont;
+ minheight = 2*(Borderwidth+1) + f->height;
+ sepw = stringwidth(f, sep);
+ if((mctl = initmouse(nil, screen)) == nil)
+ sysfatal("initmouse: %r");
+ if((kctl = initkeyboard(nil)) == nil)
+ sysfatal("initkeyboard: %r");
+
+ cback = allocimage(display, Rect(0,0,1,1), RGB24, 1, brgb);
+ if(brgb == DPalegreygreen)
+ brgb = DBlack;
+ else{ /* dunno, just invert */
+ 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);
+
+ a[Emouse].c = mctl->c;
+ a[Eresize].c = mctl->resizec;
+ a[Ekeyboard].c = kctl->c;
+ a[Eaux].c = chancreate(sizeof(s), 0);
+ a[Etimer].c = chancreate(sizeof(ulong), 0);
+
+ hlitem = -1;
+ aux = strdup("");
+ readbattery();
+ redraw();
+ proccreate(timerproc, a[Etimer].c, 4096);
+ proccreate(auxproc, a[Eaux].c, 16384);
+
+ m.buttons = 0;
+ oldt = nanosec();
+ for(;;){
+ oldbuttons = m.buttons;
+
+ switch(alt(a)){
+ case Ekeyboard:
+ if(key == Kdel){
+ close(wctl);
+ threadexitsall(nil);
+ }
+ break;
+
+ case Emouse:
+ if(m.buttons == oldbuttons)
+ break;
+ clicked(m.xy.x-screen->r.min.x, m.buttons);
+ /* wet floor */
+
+ if(0){
+ case Eresize:
+ if(getwindow(display, Refnone) < 0)
+ threadexitsall(nil);
+ /* wet floor */
+ }
+
+ if(0){
+ case Eaux:
+ free(aux);
+ aux = s;
+ /* wet floor */
+ }
+
+ if(0){
+ case Etimer:
+ t = nanosec();
+ if(t - oldt >= 30000000000ULL){
+ readbattery();
+ oldt = t;
+ }
+ }
+ redraw();
+ place();
+ break;
+ }
+ }
+}