ref: 053d3b16498c601e2af3023f2011e9fa6bf84843
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 fd; int ivalue; double value; double init; double min; double max; double step; int flags; char *group; char *name; char *unit; char *ctlpath; 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 initctl(UI *ui, char *path) { Biobuf b; char *s, *k; int i, fd; Dir *d; if ((fd = open(path, OREAD)) < 0) { fprint(2, "%r\n"); return -1; } if ((d = dirfstat(fd)) == nil) { fprint(2, "%r\n"); close(fd); return -1; } free(d); ui->ctlpath = path; ui->qidpath = d->qid.path; ui->type = -1; ui->show = 1; /* show everything by default */ Binit(&b, fd, 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: 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); close(fd); Bterm(&b); return 0; } static UI * newui(char *path) { UI *ui; Dir *dirs; 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->fd = -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) { if (initctl(ui, s) == 0) s = nil; } 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 int uiwrite(UI *ui, char *s, int n) { if (ui->ctlpath == nil) return -1; if (ui->fd < 0 && (ui->fd = open(ui->ctlpath, OWRITE)) < 0) { fprint(2, "%r\n"); return -1; } return write(ui->fd, s, n); } static void uidone(UI *ui) { if (ui->fd >= 0) close(ui->fd); ui->fd = -1; } 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 (uiwrite(c, "1", 1) > 0) c->value = 1; uidone(c); } 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 (uiwrite(c, "0", 1) > 0) c->value = 0; uidone(c); } } break; case UICheckBox: if (mu_checkbox(&c->ivalue, c->label) & MU_RES_CHANGE) { if (uiwrite(c, c->ivalue ? "1" : "0", 1) < 0) c->value = c->ivalue; uidone(c); } 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 (uiwrite(c, tmp, n) > 0) c->value = v; uidone(c); } 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, force; 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; force = 1; for (;;) { process_frame(); if (mu_render(force)) flushimage(display, 1); force = 0; 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); force = 1; 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); }