From 63a915404efa32677460fd1d3388a1aae3924d2b Mon Sep 17 00:00:00 2001 From: Vito Caputo Date: Sun, 8 Jan 2023 22:01:45 -0800 Subject: til_tap: first stab at a tap interface The idea here is for modules to bind variables to names @ context create time w/til_tap_new(). Pseudo-code sample: ``` typedef struct foo_context_t { struct { til_tap_t *position; } taps; struct { v2f_t position; } vars; v2f_t *position; } foo_context_t; foo_context_t * foo_create_context(void) { foo_context_t *foo = malloc(sizeof(foo_context_t)); /* This creates an isolated (pipe-)tap binding our local position variable and pointer * to a name for later "tapping" onto a stream. */ foo->taps.position = til_tap_new_v2f(&foo->position, "position", 1, &foo->vars.position); return foo; } foo_render(foo_context_t *foo, til_fb_fragment_t *fragment) { if (!til_stream_tap(fragment->stream, &foo->pipes.position)) { /* got nothing, we're driving */ foo->position->x = cosf(ticks); foo->position->y = sinf(ticks); } /* else { got something, just use foo->position as-is */ draw_stuff_using_position(foo->position); } ``` Note til_stream_tap() doesn't exist yet, this commit only adds the tap (til_tap_new()). The stream will probably implement a hash table for looking up the tap by name, verifying its type and nelems match if found, and update the pointer to point at the instance actually driving for the name. (in the example that's the foo_context_t.position pointer which draw_stuff_using_position() then dereferences) Also note that in the example, "position" alone is too simplistic for handling complex real-life compositions where a given module may recur in a given stream. That identifier would need to be derived from the module's context/setup producing a distinctly unique path to the tap. i.e. "/compose/layers/checkers/fill_module/foo:position" or something, to be dynamically generated. And the foo:position syntax isn't set in stone either. Maybe foo/position would suffice, the whole heirarchical syntax needs to be thought through and defined yet. Since the absolute path to the tap would be setup-dependent, there will have to be some glue tying together the setup used by the context and the tap within that context. The stream may be the natural place where that occurs. This also currently is barebones in terms of the tap types supported. The only higher-order types are rudimentary 2-4d vectors and 4x4 matrices. There are no semantics associated with the types, and it's likely in the future either the tap types themselves will expand to be semantic. Think things like a camera type, composed both a point and direction vector. As-is the few higher-order types in til_tap.h are simply forward declared, and at least in terms of the taps alone further type visibility isn't necessary. It may make more sense to build upon these bare taps with another semantic layer bringing the higher-order types to the table in a more concrete form. All those higher-order types would then be composed from the bare taps. There's some conceptual overlap with the knobs stubbed out in til_knobs.h as well. I think this likely at least partially replaces what's there, and what it doesn't will probably end up somewhere else. --- src/Makefile.am | 2 +- src/til_tap.c | 70 +++++++++++++++++++++++++++++++++ src/til_tap.h | 117 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 188 insertions(+), 1 deletion(-) create mode 100644 src/til_tap.c create mode 100644 src/til_tap.h (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 88cd43d..3b6c0f0 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,7 +1,7 @@ SUBDIRS = libs modules noinst_LTLIBRARIES = libtil.la -libtil_la_SOURCES = til_args.c til_args.h til_fb.c til_fb.h til_knobs.h til.c til.h til_module_context.c til_module_context.h til_settings.h til_settings.c til_setup.c til_setup.h til_threads.c til_threads.h til_util.c til_util.h +libtil_la_SOURCES = til_args.c til_args.h til_fb.c til_fb.h til_knobs.h til.c til.h til_module_context.c til_module_context.h til_settings.h til_settings.c til_setup.c til_setup.h til_tap.c til_tap.h til_threads.c til_threads.h til_util.c til_util.h libtil_la_CPPFLAGS = -I@top_srcdir@/src libtil_la_LIBADD = modules/blinds/libblinds.la modules/checkers/libcheckers.la modules/compose/libcompose.la modules/drizzle/libdrizzle.la modules/flui2d/libflui2d.la modules/julia/libjulia.la modules/meta2d/libmeta2d.la modules/moire/libmoire.la modules/montage/libmontage.la modules/pixbounce/libpixbounce.la modules/plasma/libplasma.la modules/plato/libplato.la modules/ray/libray.la modules/roto/libroto.la modules/rtv/librtv.la modules/shapes/libshapes.la modules/snow/libsnow.la modules/sparkler/libsparkler.la modules/spiro/libspiro.la modules/stars/libstars.la modules/strobe/libstrobe.la modules/submit/libsubmit.la modules/swab/libswab.la modules/swarm/libswarm.la modules/voronoi/libvoronoi.la libs/grid/libgrid.la libs/puddle/libpuddle.la libs/ray/libray.la libs/sig/libsig.la libs/txt/libtxt.la libs/ascii/libascii.la libs/din/libdin.la diff --git a/src/til_tap.c b/src/til_tap.c new file mode 100644 index 0000000..44550b6 --- /dev/null +++ b/src/til_tap.c @@ -0,0 +1,70 @@ +#include +#include +#include +#include + +#include "til_tap.h" + +/* A "tap" is a named binding of a local variable+pointer to that variable. + * + * It's purpose is to facilitate exposing local variables controlling rendering + * to potential external influence. + * + * While the tap alone does get named, this is mostly for ergonomic reasons since + * it's convenient and natural to name the tap while specifying its variable and + * pointer by name as well. Putting them all in once place. + * + * The tap itself is not a registry or otherwise discoverable entity by itself. + * This is strictly just the local glue, with a name. Other pieces must tie taps + * into streams or settings stuff for addressing them by name at a path or other + * means. + * + * Note the intended way for taps to work is that the caller will always access their + * local variables indirectly via the pointers they provided when creating the taps. + * There will be a function for managing the tap the caller must call before accessing + * the variable indirectly as well. It's that function which will update the indirection + * pointer to potentially point elsewhere if another tap is driving the variable. + */ + +struct til_tap_t { + til_tap_type_t type; + void *ptr; /* points at the caller-provided tap-managed indirection pointer */ + size_t n_elems; /* when > 1, *ptr is an array of n_elems elements. Otherwise individual variable. */ + void *elems; /* points at the first element of type type, may or may not be an array of them */ + char name[]; +}; + + +/* This is the raw tap creator but use the type-checked wrappers in the header and add one if one's missing */ +til_tap_t * til_tap_new(til_tap_type_t type, void *ptr, const char *name, size_t n_elems, void *elems) +{ + til_tap_t *tap; + + assert(name); + assert(type < TIL_TAP_TYPE_MAX); + assert(ptr); + assert(n_elems); + assert(elems); + + tap = calloc(1, sizeof(til_tap_t) + strlen(name) + 1); + if (!tap) + return NULL; + + strcpy(tap->name, name); + tap->type = type; + tap->ptr = ptr; + tap->n_elems = n_elems; + tap->elems = elems; + + *((void **)tap->ptr) = elems; + + return tap; +} + + +til_tap_t * til_tap_free(til_tap_t *tap) +{ + free(tap); + + return NULL; +} diff --git a/src/til_tap.h b/src/til_tap.h new file mode 100644 index 0000000..b9de6d9 --- /dev/null +++ b/src/til_tap.h @@ -0,0 +1,117 @@ +#ifndef _TIL_TAP_H +#define _TIL_TAP_H + +#include + +/* These are all the supported tap types, nothing is set in stone this just + * seemed like the likely stuff to need. Feel free to add anything as needed. + */ +typedef enum til_tap_type_t { + TIL_TAP_TYPE_I8, + TIL_TAP_TYPE_I16, + TIL_TAP_TYPE_I32, + TIL_TAP_TYPE_I64, + TIL_TAP_TYPE_U8, + TIL_TAP_TYPE_U16, + TIL_TAP_TYPE_U32, + TIL_TAP_TYPE_U64, + TIL_TAP_TYPE_FLOAT, + TIL_TAP_TYPE_DOUBLE, + TIL_TAP_TYPE_V2F, /* 2D vector of floats */ + TIL_TAP_TYPE_V3F, /* 3D vector of floats */ + TIL_TAP_TYPE_V4F, /* 4D vector of floats */ + TIL_TAP_TYPE_M4F, /* 4x4 float matrix */ + TIL_TAP_TYPE_VOIDP, /* escape hatch for when you're getting exotic and want to bypass type checking */ + TIL_TAP_TYPE_MAX, +} til_tap_type_t; + +typedef struct til_tap_t til_tap_t; + +til_tap_t * til_tap_new(til_tap_type_t type, void *ptr, const char *name, size_t n_elems, void *elems); +til_tap_t * til_tap_free(til_tap_t *tap); + +/* just some forward declared higher-order vector and matrix types for the wrappers */ +typedef struct v2f_t v2f_t; +typedef struct v3f_t v3f_t; +typedef struct v4f_t v4f_t; +typedef struct m4f_t m4f_t; + +/* typed wrappers, just supply n_elems=1 for individual variables - note n_elems is just a defensive + * programming sanity check to catch callers mismatching array sizes + */ +static inline til_tap_t * til_tap_new_i8(int8_t **ptr, const char *name, size_t n_elems, int8_t *elems) +{ + return til_tap_new(TIL_TAP_TYPE_I8, ptr, name, n_elems, elems); +} + +static inline til_tap_t * til_tap_new_i16(int16_t **ptr, const char *name, size_t n_elems, int16_t *elems) +{ + return til_tap_new(TIL_TAP_TYPE_I16, ptr, name, n_elems, elems); +} + +static inline til_tap_t * til_tap_new_i32(int32_t **ptr, const char *name, size_t n_elems, int32_t *elems) +{ + return til_tap_new(TIL_TAP_TYPE_I32, ptr, name, n_elems, elems); +} + +static inline til_tap_t * til_tap_new_i64(int64_t **ptr, const char *name, size_t n_elems, int64_t *elems) +{ + return til_tap_new(TIL_TAP_TYPE_I64, ptr, name, n_elems, elems); +} + +static inline til_tap_t * til_tap_new_u8(uint8_t **ptr, const char *name, size_t n_elems, uint8_t *elems) +{ + return til_tap_new(TIL_TAP_TYPE_U8, ptr, name, n_elems, elems); +} + +static inline til_tap_t * til_tap_new_u16(uint16_t **ptr, const char *name, size_t n_elems, uint16_t *elems) +{ + return til_tap_new(TIL_TAP_TYPE_U16, ptr, name, n_elems, elems); +} + +static inline til_tap_t * til_tap_new_u32(uint32_t **ptr, const char *name, size_t n_elems, uint32_t *elems) +{ + return til_tap_new(TIL_TAP_TYPE_U32, ptr, name, n_elems, elems); +} + +static inline til_tap_t * til_tap_new_u64(uint64_t **ptr, const char *name, size_t n_elems, uint64_t *elems) +{ + return til_tap_new(TIL_TAP_TYPE_U64, ptr, name, n_elems, elems); +} + +static inline til_tap_t * til_tap_new_float(float **ptr, const char *name, size_t n_elems, float *elems) +{ + return til_tap_new(TIL_TAP_TYPE_FLOAT, ptr, name, n_elems, elems); +} + +static inline til_tap_t * til_tap_new_double(double **ptr, const char *name, size_t n_elems, double *elems) +{ + return til_tap_new(TIL_TAP_TYPE_DOUBLE, ptr, name, n_elems, elems); +} + +static inline til_tap_t * til_tap_new_v2f(v2f_t **ptr, const char *name, size_t n_elems, v2f_t *elems) +{ + return til_tap_new(TIL_TAP_TYPE_V2F, ptr, name, n_elems, elems); +} + +static inline til_tap_t * til_tap_new_v3f(v3f_t **ptr, const char *name, size_t n_elems, v3f_t *elems) +{ + return til_tap_new(TIL_TAP_TYPE_V3F, ptr, name, n_elems, elems); +} + +static inline til_tap_t * til_tap_new_v4f(v4f_t **ptr, const char *name, size_t n_elems, v4f_t *elems) +{ + return til_tap_new(TIL_TAP_TYPE_V4F, ptr, name, n_elems, elems); +} + +static inline til_tap_t * til_tap_new_m4f(m4f_t **ptr, const char *name, size_t n_elems, m4f_t *elems) +{ + return til_tap_new(TIL_TAP_TYPE_M4F, ptr, name, n_elems, elems); +} + +static inline til_tap_t * til_tap_new_voidp(void **ptr, const char *name, size_t n_elems, void *elems) +{ + return til_tap_new(TIL_TAP_TYPE_VOIDP, ptr, name, n_elems, elems); +} + +#endif -- cgit v1.2.3