ref: c67e37a64ddd883ebe55720b83285317f66f3034
dir: /cfg/cfg.c/
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <draw.h>
#include <mouse.h>
#include <keyboard.h>
#include <thread.h>
#include <microui.h>
#include "common.h"
enum {
Hasclone = 1<<0,
};
typedef struct UI UI;
struct UI {
int type;
const char *label;
int index;
uvlong qidpath;
int ivalue;
double value;
double init;
double min;
double max;
double step;
int ctl;
int flags;
char *group;
char *name;
char *unit;
char *path;
UI **child;
int numchild;
/* µui stuff goes here */
mu_Container *win;
int show;
};
int mainstacksize = 32768;
static char **dirs;
static UI **top;
static int numtop;
static int winopt;
static int
readmeta(UI *ui, char *path)
{
Biobuf *b;
char *s, *k;
int index;
if ((b = Bopen(path, OREAD)) == nil)
return -1;
ui->group = ui->name = ui->unit = nil;
ui->index = -1;
for (; (s = Brdstr(b, '\n', 1)) != nil; free(s)) {
if ((k = strtok(s, "\t")) == nil)
continue;
if (strcmp(k, "group") == 0) /* FIXME more than one group? */
ui->group = strdup(strtok(nil, "\t"));
else if (strcmp(k, "name") == 0)
ui->name = strdup(strtok(nil, "\t"));
else if (strcmp(k, "unit") == 0)
ui->unit = strdup(strtok(nil, "\t"));
else if ((index = strtol(k, &k, 10)) >= 0 && *k == 0)
ui->index = index;
}
Bterm(b);
return 0;
}
static int
readctl(UI *ui)
{
Biobuf b;
char *s, *k;
int i;
ui->type = -1;
ui->show = 1; /* show everything by default */
seek(ui->ctl, 0, 0);
Binit(&b, ui->ctl, OREAD);
if ((s = Brdstr(&b, '\n', 1)) != nil && (k = strtok(s, "\t")) != nil) {
for (i = 0; i < nelem(uitypenames); i++) {
if (strcmp(k, uitypenames[i]) == 0) {
ui->type = i;
break;
}
}
switch (ui->type) {
case UITGroup:
case UIHGroup:
case UIVGroup:
close(ui->ctl);
ui->ctl = -1;
break;
case UIButton:
case UICheckBox:
ui->value = atof(strtok(nil, "\t"));
ui->ivalue = !!ui->value;
break;
case UIVSlider:
case UIHSlider:
case UINEntry:
ui->value = atof(strtok(nil, "\t"));
ui->init = atof(strtok(nil, "\t"));
ui->min = atof(strtok(nil, "\t"));
ui->max = atof(strtok(nil, "\t"));
ui->step = atof(strtok(nil, "\t"));
break;
case UIHBarGraph:
case UIVBarGraph:
ui->min = atof(strtok(nil, "\t"));
ui->max = atof(strtok(nil, "\t"));
break;
}
}
free(s);
Bterm(&b);
return 0;
}
static UI *
newui(char *path)
{
UI *ui;
Dir *dirs, *d;
char *s, *name, tmp[64];
long i, n;
int f;
/* FIXME perhaps a prefix common for all dirs should be removed too */
path = cleanname(path);
if ((f = open(path, OREAD)) < 0)
return nil;
ui = calloc(1, sizeof(*ui));
ui->path = strdup(path);
ui->ctl = -1;
if ((s = strrchr(ui->path, '/')) == nil && fd2path(f, tmp, sizeof(tmp)) == 0) {
if ((s = strrchr(tmp, '/')) == nil)
s = path;
}
ui->label = strdup(s != nil ? s + 1 : ui->path);
n = dirreadall(f, &dirs);
close(f);
if (n < 0)
fprint(2, "newui: %s: %r", s);
for (i = 0; i < n; i++) {
name = dirs[i].name;
s = smprint("%s/%s", path, name);
if (strcmp(name, "metadata") == 0) {
readmeta(ui, s);
} else if (strcmp(name, "clone") == 0) {
ui->flags |= Hasclone;
} else if (strcmp(name, "ctl") == 0) {
ui->ctl = open(s, ORDWR);
if (readctl(ui) == 0 && ui->ctl >= 0 && (d = dirfstat(ui->ctl)) != nil) {
ui->qidpath = d->qid.path;
free(d);
}
} else if (dirs[i].mode & DMDIR) {
ui->child = realloc(ui->child, (ui->numchild+1) * sizeof(*ui->child));
ui->child[ui->numchild++] = newui(s);
}
free(s);
}
free(dirs);
return ui;
}
static void
process_ui(UI *w)
{
UI *c, *slider;
double v;
mu_Id id;
int i, n, widths[32], maxwidth, x, dt;
char tmp[256];
dt = stringwidth(mu_style.font, ": ");
slider = nil;
for (i = 0; i < w->numchild; i++) {
c = w->child[i];
if (slider != nil && c->type != UIVSlider && c->type != UIHSlider) {
widths[0] = -1;
mu_layout_row(1, widths, 0);
slider = nil;
}
switch (c->type) {
case UIHGroup: /* FIXME use mu_layout_row/mu_layout_begin_column here */
case UITGroup: /* FIXME no tgroup in µui yet */
case UIVGroup:
if (mu_header(&c->show, c->label))
process_ui(c);
break;
case UIButton:
if (mu_button(c->label) & MU_RES_SUBMIT) {
if (write(c->ctl, "1", 1) > 0)
c->value = 1;
} else if (c->value) {
id = mu_get_id(c->label, strlen(c->label));
if (mu_ctx.focus != id || mu_ctx.mouse_down != MU_MOUSE_LEFT) {
if (write(c->ctl, "0", 1) > 0)
c->value = 0;
}
}
break;
case UICheckBox:
if (mu_checkbox(&c->ivalue, c->label) & MU_RES_CHANGE && write(c->ctl, c->ivalue ? "1" : "0", 1) < 0)
c->value = c->ivalue;
break;
case UIVSlider: /* FIXME no vslider in µui yet */
case UIHSlider:
if (slider == nil) {
slider = c;
maxwidth = 0;
for (n = i; n < w->numchild; n++) {
if (w->child[n]->type != UIVSlider && w->child[n]->type != UIHSlider)
break;
if ((x = stringwidth(mu_style.font, w->child[n]->label)) > maxwidth)
maxwidth = x;
}
widths[0] = maxwidth + dt;
widths[1] = -1;
mu_layout_row(2, widths, 0);
}
snprint(tmp, sizeof(tmp), "%s: ", c->label);
mu_label(tmp);
mu_push_id(&c, sizeof(c));
v = c->value;
snprint(tmp, sizeof(tmp), "%%g%s", c->unit == nil ? "" : c->unit);
if (mu_slider_ex(&v, c->min, c->max, c->step, tmp, MU_OPT_ALIGNCENTER) & MU_RES_CHANGE) {
n = snprint(tmp, sizeof(tmp), "%g", v);
if (write(c->ctl, tmp, n) > 0)
c->value = v;
}
mu_pop_id();
case UINEntry:
break;
case UIHBarGraph:
case UIVBarGraph:
/* FIXME */
break;
}
}
}
static void
process_frame(void)
{
UI *prev, *ui, *ch;
char tmp[128], *s;
int i, j;
mu_begin();
for (i = 0, prev = nil; i < numtop; i++) {
ui = top[i];
if ((ui->flags & Hasclone) == 0) {
if (mu_begin_window_ex(ui->win, ui->label, winopt)) {
if (prev != nil && (winopt & MU_OPT_AUTOSIZE))
ui->win->rect.x = prev->win->rect.x + prev->win->rect.w;
prev = ui;
process_ui(ui);
mu_end_window();
continue;
}
}
for (j = 0; j < ui->numchild; j++) {
s = ui->child[j]->path;
ch = ui->child[j]->child[0];
snprint(tmp, sizeof(tmp), "[%s] %s", s, ch->label);
if (mu_begin_window_ex(ch->win, tmp, winopt)) {
if (prev != nil && (winopt & MU_OPT_AUTOSIZE))
ch->win->rect.x = prev->win->rect.x + prev->win->rect.w;
process_ui(ch);
mu_end_window();
}
prev = ch;
}
}
mu_end();
}
static void
diradd(char *path)
{
UI *ui, *ch;
mu_Container *w;
int i;
top = realloc(top, (numtop+1) * sizeof(*top));
ui = newui(path);
top[numtop++] = ui;
if ((ui->flags & Hasclone) == 0) {
ui->win = w = calloc(1, sizeof(*w));
mu_init_window(w, 0);
w->rect = mu_rect(10 + 210, 10, 200, 400);
return;
}
for (i = 0; i < ui->numchild; i++) {
if (ui->child[i]->numchild < 1)
continue;
ch = ui->child[i]->child[0];
ch->win = w = calloc(1, sizeof(*w));
mu_init_window(w, 0);
w->rect = mu_rect(10 + i*210, 10, 200, 400);
}
}
static void
usage(void)
{
print("usage: %s [DIR]...\n", argv0);
threadexitsall("usage");
}
void
threadmain(int argc, char **argv)
{
char *s;
Biobuf *snarf;
Mousectl *mctl;
Keyboardctl *kctl;
Rune key;
Mouse m;
Alt a[] = {
{ nil, &m, CHANRCV },
{ nil, nil, CHANRCV },
{ nil, &key, CHANRCV },
{ nil, nil, CHANEND },
};
char *curdir[] = {".", nil};
int oldbuttons, b, nkey, gotevent, i;
char text[5];
ARGBEGIN{
default:
usage();
}ARGEND
dirs = *argv == nil ? curdir : argv;
top = nil;
for (i = 0; dirs[i] != nil; i++)
diradd(dirs[i]);
if (initdraw(nil, nil, "daw/cfg") < 0)
sysfatal("initdraw: %r");
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;
srand(time(0));
threadsetname("daw/cfg");
mu_init();
mu_style.font = font;
mu_style.size.y = font->height - 6;
mu_style.title_height = mu_style.size.y + 12;
winopt = MU_OPT_AUTOSIZE_H;
/* FIXME that's how much it takes for the sizing to kick in :( */
for (i = 0; i < 3; i++)
process_frame();
winopt &= ~MU_OPT_AUTOSIZE_H;
oldbuttons = 0;
for (;;) {
process_frame();
if (mu_render())
flushimage(display, 1);
gotevent = 1;
switch (alt(a)) {
case 0: /* mouse */
m.xy.x -= screen->r.min.x;
m.xy.y -= screen->r.min.y;
mu_input_mousemove(m.xy.x, m.xy.y);
if ((b = (m.buttons & 1)) != (oldbuttons & 1))
(b ? mu_input_mousedown : mu_input_mouseup)(m.xy.x, m.xy.y, MU_MOUSE_LEFT);
else if ((b = (m.buttons & 2)) != (oldbuttons & 2))
(b ? mu_input_mousedown : mu_input_mouseup)(m.xy.x, m.xy.y, MU_MOUSE_MIDDLE);
else if ((b = (m.buttons & 4)) != (oldbuttons & 4))
(b ? mu_input_mousedown : mu_input_mouseup)(m.xy.x, m.xy.y, MU_MOUSE_RIGHT);
if (m.buttons == 5 && (snarf = Bopen("/dev/snarf", OREAD)) != nil) {
if ((s = Brdstr(snarf, 0, 1)) != nil) {
mu_input_text(s);
free(s);
}
Bterm(snarf);
}
oldbuttons = m.buttons;
break;
case 1: /* resize */
getwindow(display, Refnone);
break;
case 2: /* keyboard */
nkey = -1;
switch (key) {
case Kdel: goto end;
case Kshift: nkey = MU_KEY_SHIFT; break;
case Kbs: nkey = MU_KEY_BACKSPACE; break;
case '\n': nkey = MU_KEY_RETURN; break;
case Knack: nkey = MU_KEY_NACK; break;
case Kleft: nkey = MU_KEY_LEFT; break;
case Kright: nkey = MU_KEY_RIGHT; break;
case Kesc: mu_set_focus(0); break;
default:
if (key < 0xf000 || key > 0xffff) {
memset(text, 0, sizeof(text));
if (runetochar(text, &key) > 0)
mu_input_text(text);
}
break;
}
if (nkey >= 0) {
mu_input_keydown(nkey);
mu_input_keyup(nkey);
}
break;
default:
gotevent = 0;
break;
}
if (gotevent)
process_frame();
}
end:
threadexitsall(nil);
}