shithub: neindaw

ref: 29415dfac1fb18f32d86433c00b4b4942c4c24d6
dir: /uiglue.c/

View raw version
#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
#include "common.h"
#include "uiglue.h"
#include "aux.h"

static struct {
	float *zone;
	Meta *meta;
	int nummeta;
}decl;

char *
ui_readstr(UI *ui, int auxtype, char *s, int sz)
{
	char *x, *t;
	int i;

	if (auxtype == Xuictl) {
		if (ui->type < 0 || ui->type >= UInum)
			sysfatal("unknown ui type %d", ui->type);
		t = uitypenames[ui->type];
		switch (ui->type) {
		case UITGroup:
		case UIHGroup:
		case UIVGroup:
			snprint(s, sz, "%s\n", t);
			return s;
		case UIButton:
		case UICheckBox:
			snprint(s, sz, "%s\t%g\n", t, *ui->zone);
			return s;
		case UIVSlider:
		case UIHSlider:
		case UINEntry:
			snprint(s, sz, "%s\t%g\t%g\t%g\t%g\t%g\n", t, *ui->zone, ui->init, ui->min, ui->max, ui->step);
			return s;
		case UIHBarGraph:
		case UIVBarGraph:
			snprint(s, sz, "%s\t%g\t%g\t%g\n", t, *ui->zone, ui->min, ui->max);
			return s;
		default:
			sysfatal("readstr not implemented for ui type %d", ui->type);
		}
	} else if (auxtype == Xuimeta) {
		x = s;
		*x = 0;
		for (i = 0; i < ui->nummeta; i++)
			x = seprint(x, s+sz-1, "%s\t%s\n", ui->meta[i].k, ui->meta[i].v);
		return s;
	} else {
		sysfatal("unsupported ui aux %d", auxtype);
	}

	return nil;
}

int
ui_writestr(UI *ui, int auxtype, char *s)
{
	char *e;
	int failoor;
	float v;

	if (auxtype != Xuictl)
		sysfatal("unsupported ui aux %d", auxtype);

	/* FIXME optional argument should specify at which frame to apply the change */

	v = 0.0f;
	failoor = 0;
	if (strncmp(s, "reset", 5) == 0) { /* FIXME reset for a box should reset ALL controls inside it */
		v = ui->init;
	} else if (strncmp(s, "add", 3) == 0) {
		if (ui->zone != nil)
			v = *ui->zone + atof(s+3);
	} else if (strncmp(s, "sub", 3) == 0) {
		if (ui->zone != nil)
			v = *ui->zone - atof(s+3);
	} else {
		v = strtod(s, &e);
		if (*e != 0 && *e != '\n')
			return -1;
		failoor = 1;
	}

	if (ui->zone != nil) {
		if (ui->type == UIButton || ui->type == UICheckBox) {
			*ui->zone = v > 0 ? 1 : 0;
		} else {
			if (v < ui->min) {
				if (failoor)
					return -1;
				v = ui->min;
			} else if (v > ui->max) {
				if (failoor)
					return -1;
				v = ui->max;
			}
			*ui->zone = v;
		}
	}

	return 0;
}

static UI *
newui(File *f, const char *label, int type)
{
	Aux *a;

	a = calloc(1, sizeof(*a) + sizeof(UI) + sizeof(Meta)*decl.nummeta);
	a->type = Xui;
	a->ui = (UI*)(a+1);
	a->ui->type = type;
	a->ui->meta = (Meta*)(a->ui+1);
	a->ui->nummeta = decl.nummeta;
	memmove(a->ui->meta, decl.meta, sizeof(Meta)*decl.nummeta);
	a->ctl = Xuictl;
	a->metadata = Xuimeta;
	a->ui->zone = decl.zone;
	a->ui->label = label;
	a->ui->readstr = uiglue.readstr != nil ? uiglue.readstr : ui_readstr;
	a->ui->writestr = uiglue.writestr != nil ? uiglue.writestr : ui_writestr;
	a->ui->userdata = uiglue.userdata;
	if ((uiglue.f = createfile(f, label, nil, DMDIR|0775, a)) == nil)
		sysfatal("failed to create ui: %r");
	if ((f = createfile(uiglue.f, "ctl", nil, 0664, &a->ctl)) == nil)
		sysfatal("failed to create ui ctl: %r");
	closefile(f);
	if ((f = createfile(uiglue.f, "metadata", nil, 0664, &a->metadata)) == nil)
		sysfatal("failed to create ui metadata: %r");
	closefile(f);
	closefile(uiglue.f);

	free(decl.meta);
	decl.zone = nil;
	decl.meta = nil;
	decl.nummeta = 0;

	return a->ui;
}

static void
ui_tgroup(void *f, const char *label)
{
	newui(f, label, UITGroup);
}

static void
ui_hgroup(void *f, const char *label)
{
	newui(f, label, UIHGroup);
}

static void
ui_vgroup(void *f, const char *label)
{
	newui(f, label, UIVGroup);
}

static void
ui_close_group(void *file)
{
	File *f;

	f = file;
	uiglue.f = f->parent;
}

static UI *
ui_define(File *f, int type, const char *label, float *zone)
{
	UI *ui;

	if (zone != decl.zone) { /* no "declare" called before */
		decl.zone = zone;
		free(decl.meta);
		decl.meta = nil;
		decl.nummeta = 0;
	}
	ui = newui(f, label, type);
	uiglue.f = f;

	return ui;
}

static void
ui_button(void *f, const char *label, float *zone)
{
	UI *ui;

	ui = ui_define(f, UIButton, label, zone);
	*zone = 0;
	ui->writestr(ui, Xuictl, "reset");
}

static void
ui_checkbox(void *f, const char *label, float *zone)
{
	UI *ui;

	ui = ui_define(f, UICheckBox, label, zone);
	*zone = 0;
	ui->writestr(ui, Xuictl, "reset");
}

static void
ui_vslider(void *f, const char *label, float *zone, float init, float min, float max, float step)
{
	UI *ui;

	ui = ui_define(f, UIVSlider, label, zone);
	ui->init = *zone = init;
	ui->min = min;
	ui->max = max;
	ui->step = step;
	ui->writestr(ui, Xuictl, "reset");
}

static void
ui_hslider(void *f, const char *label, float *zone, float init, float min, float max, float step)
{
	UI *ui;

	ui = ui_define(f, UIHSlider, label, zone);
	ui->init = *zone = init;
	ui->min = min;
	ui->max = max;
	ui->step = step;
	ui->writestr(ui, Xuictl, "reset");
}

static void
ui_nentry(void *f, const char *label, float *zone, float init, float min, float max, float step)
{
	UI *ui;

	ui = ui_define(f, UINEntry, label, zone);
	ui->init = *zone = init;
	ui->min = min;
	ui->max = max;
	ui->step = step;
	ui->writestr(ui, Xuictl, "reset");
}

static void
ui_hbargraph(void *f, const char *label, float *zone, float min, float max)
{
	UI *ui;

	ui = ui_define(f, UIHBarGraph, label, zone);
	ui->min = min;
	ui->max = max;
	*zone = 0;
	ui->writestr(ui, Xuictl, "reset");
}

static void
ui_vbargraph(void *f, const char *label, float *zone, float min, float max)
{
	UI *ui;

	ui = ui_define(f, UIVBarGraph, label, zone);
	ui->min = min;
	ui->max = max;
	*zone = 0;
	ui->writestr(ui, Xuictl, "reset");
}

static void
ui_declare(void *f, float *zone, const char *key, const char *value)
{
	USED(f);

	if (decl.zone != nil && decl.zone != zone)
		sysfatal("%s=\"%s\": zone mismatch during declaration (%p != %p)", key, value, decl.zone, zone);
	decl.zone = zone;
	decl.meta = realloc(decl.meta, sizeof(Meta)*(decl.nummeta+1));
	decl.meta[decl.nummeta].k = key;
	decl.meta[decl.nummeta].v = value;
	decl.nummeta++;
}

UIGlue uiglue = {
	.openTabBox = ui_tgroup,
	.openHorizontalBox = ui_hgroup,
	.openVerticalBox = ui_vgroup,
	.closeBox = ui_close_group,
	.addButton = ui_button,
	.addCheckButton = ui_checkbox,
	.addVerticalSlider = ui_vslider,
	.addHorizontalSlider = ui_hslider,
	.addNumEntry = ui_nentry,
	.addHorizontalBargraph = ui_hbargraph,
	.addVerticalBargraph = ui_vbargraph,
	.declare = ui_declare,
};