shithub: neindaw

Download patch

ref: f38288c2e207d8dc2a2fe433462462d57978da43
parent: 5d2c9a4de9139939b456ddb2ddd332365dfb3910
author: Sigrid Haflínudóttir <ftrvxmtrx@gmail.com>
date: Sun Jan 12 14:01:34 EST 2020

cfg: configuring DSPs through a GUI

--- a/cfg/cfg.c
+++ b/cfg/cfg.c
@@ -1,12 +1,17 @@
 #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"
-#include "nk.h"
 
+enum {
+	Hasclone = 1<<0,
+};
+
 typedef struct UI UI;
 
 struct UI {
@@ -21,23 +26,293 @@
 	double step;
 
 	int ctl;
+	int flags;
 
+	char *group;
+	char *name;
+	char *unit;
+
 	char *path;
-	UI *child;
+	UI **child;
 	int numchild;
+
+	/* µui stuff goes here */
+	mu_Container *win;
+	int show;
 };
 
 int mainstacksize = 32768;
 
-static Mousectl *mctl;
-static Keyboardctl *kctl;
-static Image *cola, *colb;
-static Font *f;
 static char **dirs;
-static UI topui;
-static struct nk_context nk;
+static UI **top;
+static int numtop;
 
+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:
+			break;
+		case UIButton:
+		case UICheckBox:
+			ui->value = atof(strtok(nil, "\t"));
+			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;
+	char *s, *name;
+	long i, n;
+	int f;
+
+	if ((f = open(path, OREAD)) < 0)
+		return nil;
+	ui = calloc(1, sizeof(*ui));
+	ui->path = strdup(path);
+	ui->ctl = -1;
+	if ((ui->label = strrchr(ui->path, '/')) == nil)
+		ui->label = ui->path;
+	else
+		ui->label++;
+
+	while ((n = dirread(f, &dirs)) > 0) {
+		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);
+				readctl(ui);
+			} 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);
+		}
+	}
+	close(f);
+
+	return ui;
+}
+
 static void
+printui(UI *ui, int indent)
+{
+	static char *tabs = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
+	int i;
+
+	print("%.*sui (%s)\n", indent, tabs, ui->path);
+	if (ui->group != nil)
+		print("%.*sgroup: %s\n", indent+1, tabs, ui->group);
+	if (ui->name != nil)
+		print("%.*sname: %s\n", indent+1, tabs, ui->name);
+	print("%.*slabel: %s\n", indent+1, tabs, ui->label);
+	print("%.*sindex: %d\n", indent+1, tabs, ui->index);
+	if (ui->type >= 0)
+		print("%.*stype: %s\n", indent+1, tabs, uitypenames[ui->type]);
+	for (i = 0; i < ui->numchild; i++)
+		printui(ui->child[i], indent+1);
+}
+
+static void
+process_ui(UI *w)
+{
+	UI *c, *slider;
+	double v;
+	int i, state, n, widths[32], maxwidth, x;
+	char tmp[256];
+
+	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 ((state = mu_button(c->label)) != !!c->value) {
+				if (write(c->ctl, state ? "1" : "0", 1) > 0)
+					c->value = state;
+			}
+			break;
+		case UICheckBox:
+			state = !!c->value;
+			if (mu_checkbox(&state, c->label) & MU_RES_CHANGE) {
+				if (write(c->ctl, state ? "1" : "0", 1) > 0)
+					c->value = state;
+			}
+			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 + stringwidth(mu_style.font, ": ");
+				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;
+			if (mu_slider_ex(&v, c->min, c->max, c->step, "%g", MU_OPT_ALIGNCENTER) & MU_RES_CHANGE) {
+				if (fabs(fabs(v) - fabs(c->value)) >= c->step) {
+					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 *ui, *ch;
+	char tmp[128], *s;
+	int i, j;
+
+	mu_begin();
+	for (i = 0; i < numtop; i++) {
+		ui = top[i];
+		for (j = 0; j < ui->numchild; j++) {
+			ch = ui->child[j]->child[0];
+
+			s = ui->child[j]->path;
+			/* remove the annoying "./" if current dir was used */
+			/* FIXME perhaps a prefix common for all dirs should be removed too */
+			if (s[0] == '.' && s[1] == '/')
+				s += 2;
+			snprint(tmp, sizeof(tmp), "[%s] %s", s, ch->label);
+
+			if (mu_begin_window(ch->win, tmp)) {
+				process_ui(ch);
+				mu_end_window();
+			}
+		}
+	}
+	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;
+	for (i = 0; i < ui->numchild; i++) {
+		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);
@@ -47,6 +322,10 @@
 void
 threadmain(int argc, char **argv)
 {
+	char *s;
+	Biobuf *snarf;
+	Mousectl *mctl;
+	Keyboardctl *kctl;
 	Rune key;
 	Mouse m;
 	Alt a[] = {
@@ -56,7 +335,8 @@
 		{ nil, nil, CHANEND },
 	};
 	char *curdir[] = {".", nil};
-	int oldbuttons, x, entering;
+	int oldbuttons, b, nkey, gotevent, i;
+	char text[5];
 
 	ARGBEGIN{
 	default:
@@ -64,8 +344,9 @@
 	}ARGEND
 
 	dirs = *argv == nil ? curdir : argv;
-	memset(&topui, 0, sizeof(topui));
-	topui.type = -1; /* that one is special */
+	top = nil;
+	for (i = 0; dirs[i] != nil; i++)
+		diradd(dirs[i]);
 
 	if (initdraw(nil, nil, "daw/cfg") < 0)
 		sysfatal("initdraw: %r");
@@ -78,87 +359,81 @@
 	a[1].c = mctl->resizec;
 	a[2].c = kctl->c;
 
-	f = display->defaultfont;
-	cola = display->white;
-	colb = display->black;
 	srand(time(0));
 	threadsetname("daw/cfg");
 
-	if (!nk_init_p9(&nk))
-		sysfatal("nk_init_p9 failed");
+	mu_init();
+	mu_style.font = font;
+	mu_style.size.y = font->height;
+	mu_style.title_height = mu_style.size.y + 6;
+	process_frame();
 
 	oldbuttons = 0;
-	entering = 1;
 	for (;;) {
-		nk_glyph g;
-		enum nk_keys nkey;
+		process_frame();
+		if (mu_render())
+			flushimage(display, 1);
 
-		nk_input_begin(&nk);
+		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;
 
-		if (entering == 0) {
-			switch (alt(a)) {
-			case 0: /* mouse */
-				m.xy.x -= screen->r.min.x;
-				m.xy.y -= screen->r.min.y;
-				nk_input_motion(&nk, m.xy.x, m.xy.y);
-				if ((x = (m.buttons & 1)) != (oldbuttons & 1))
-					nk_input_button(&nk, NK_BUTTON_LEFT, m.xy.x, m.xy.y, x);
-				else if ((x = (m.buttons & 2)) != (oldbuttons & 2))
-					nk_input_button(&nk, NK_BUTTON_MIDDLE, m.xy.x, m.xy.y, x);
-				else if ((x = (m.buttons & 4)) != (oldbuttons & 4))
-					nk_input_button(&nk, NK_BUTTON_RIGHT, m.xy.x, m.xy.y, x);
-				oldbuttons = m.buttons;
-				break;
+		case 1: /* resize */
+			getwindow(display, Refnone);
+			break;
 
-			case 1: /* resize */
-				getwindow(display, Refnone);
-				break;
-
-			case 2: /* keyboard */
-				nkey = -1;
-				switch (key) {
-				case 'q': goto end;
-				case Kbs: nkey = NK_KEY_BACKSPACE; break;
-				case Kdel: nkey = NK_KEY_DEL; break;
-				case Kleft: nkey = NK_KEY_LEFT; break;
-				case Kright: nkey = NK_KEY_RIGHT; break;
-				case '\n': nkey = NK_KEY_ENTER; break;
-				default:
-					if (runetochar(g, &key) > 0)
-						nk_input_glyph(&nk, g);
-					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);
 				}
-				if (nkey >= 0) {
-					nk_input_key(&nk, nkey, 1);
-					nk_input_key(&nk, nkey, 0);
-				}
+				break;
 			}
-		} else {
-			entering = 0;
-		}
+			if (nkey >= 0) {
+				mu_input_keydown(nkey);
+				mu_input_keyup(nkey);
+			}
+			break;
 
-		nk_input_end(&nk);
-
-		/* that's just a test */
-		if (nk_begin_titled(&nk, "daw/cfg", "daw/cfg", nk_rect(0, 0, Dx(screen->r), Dy(screen->r)), 0)) {
-			static int d = 20;
-			nk_layout_row_template_begin(&nk, 0);
-			nk_layout_row_template_push_static(&nk, stringwidth(display->defaultfont, "Some kind of slider?"));
-			nk_layout_row_template_push_variable(&nk, 100);
-			nk_layout_row_template_push_static(&nk, stringwidth(display->defaultfont, "Button")*2);
-			nk_layout_row_template_end(&nk);
-			nk_label(&nk, "Some kind of slider?", NK_TEXT_LEFT);
-			nk_slider_int(&nk, 0, &d, 100, 1);
-			nk_button_label(&nk, "Button");
+		default:
+			gotevent = 0;
+			break;
 		}
-		nk_end(&nk);
 
-		nk_redraw_p9(&nk, screen);
-		nk_clear(&nk);
+		if (gotevent)
+			process_frame();
 	}
 
 end:
-	closemouse(mctl);
-	closekeyboard(kctl);
 	threadexitsall(nil);
 }
--- a/cfg/mkfile
+++ b/cfg/mkfile
@@ -4,11 +4,10 @@
 	cfg\
 
 BIN=/$objtype/bin/daw
-CFLAGS=$CFLAGS -I.. -p
+CFLAGS=$CFLAGS -I..
 OFILES=\
 	common.$O\
 	cfg.$O\
-	nk.$O\
 
 default:V:	all