summaryrefslogtreecommitdiff
path: root/src/modules/asc
diff options
context:
space:
mode:
authorVito Caputo <vcaputo@pengaru.com>2023-10-03 12:36:00 -0700
committerVito Caputo <vcaputo@pengaru.com>2023-10-03 12:36:00 -0700
commit9a086395644521854ca450821b552fcb67c95aea (patch)
treebd2d70ff8b07461c6aa1b8fbe964b5b3a73ec9a5 /src/modules/asc
parent914c5e5752eb30e1d1108fac2d94897e92b937bd (diff)
modules/asc: implement a simple ascii text module
This just binds the simple libs/txt/txt.c stuff to a rendering module, exposing the minimal options as settings. It's handy for testing libs/txt/txt.c, and introduces a module requiring free-form strings potentially including newlines be handled properly as settings values. This latter aspect is important for improving settings syntax, any improvements must handle these more complicated scenarios and now there's a good test case for exercising those nuances. I suppose there might also be use in the creative process if you want a text element but haven't got around to hacking up a prettier module for it yet. Just use this one temporarily. See commit for some remaining TODO items.
Diffstat (limited to 'src/modules/asc')
-rw-r--r--src/modules/asc/Makefile.am3
-rw-r--r--src/modules/asc/asc.c231
2 files changed, 234 insertions, 0 deletions
diff --git a/src/modules/asc/Makefile.am b/src/modules/asc/Makefile.am
new file mode 100644
index 0000000..c9f7a6e
--- /dev/null
+++ b/src/modules/asc/Makefile.am
@@ -0,0 +1,3 @@
+noinst_LTLIBRARIES = libasc.la
+libasc_la_SOURCES = asc.c
+libasc_la_CPPFLAGS = -I@top_srcdir@/src -I@top_srcdir@/src/libs
diff --git a/src/modules/asc/asc.c b/src/modules/asc/asc.c
new file mode 100644
index 0000000..2d50021
--- /dev/null
+++ b/src/modules/asc/asc.c
@@ -0,0 +1,231 @@
+#include <errno.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "til.h"
+#include "til_fb.h"
+#include "til_module_context.h"
+
+#include "txt/txt.h"
+
+/* Copyright (C) 2023 Vito Caputo <vcaputo@pengaru.com> */
+
+/* This is intended primarily for diagnostic purposes or as a stand-in you'd eventually
+ * replace with something a more visually interesting font/style.
+ *
+ * TODO:
+ * - Wire up taps for the x/y coordinates, making this a handy taps diagnostic
+ *
+ * - Maybe add a dynamic justification mode where the h/v alignment offsets are
+ * just the inverse of the normalized x/y coordinates. This requires extending
+ * libs/txt to support precise offsetting when rendering as an alternative to the
+ * enum'd txt_align_t variant. But it would allow a tapped x/y coordinate user to
+ * sweep the coordinates edge-to-edge with the text smoothly adjusting its offset
+ * throughout the sweep so it doesn't extend off-screen at the nearest edge.
+ */
+
+#define ASC_DEFAULT_STRING "Hello rototiller!"
+#define ASC_DEFAULT_HALIGN TXT_HALIGN_CENTER
+#define ASC_DEFAULT_VALIGN TXT_VALIGN_CENTER
+#define ASC_DEFAULT_X 0
+#define ASC_DEFAULT_Y 0
+
+
+typedef struct asc_setup_t {
+ til_setup_t til_setup;
+
+ const char *string;
+ txt_halign_t halign;
+ txt_valign_t valign;
+ float x, y;
+} asc_setup_t;
+
+typedef struct asc_context_t {
+ til_module_context_t til_module_context;
+
+ txt_t *txt;
+} asc_context_t;
+
+
+static til_module_context_t * asc_create_context(const til_module_t *module, til_stream_t *stream, unsigned seed, unsigned ticks, unsigned n_cpus, til_setup_t *setup)
+{
+ asc_context_t *ctxt;
+
+ ctxt = til_module_context_new(module, sizeof(asc_context_t), stream, seed, ticks, n_cpus, setup);
+ if (!ctxt)
+ return NULL;
+
+ ctxt->txt = txt_new(((asc_setup_t *)setup)->string);
+ if (!ctxt->txt)
+ return til_module_context_free(&ctxt->til_module_context);
+
+ return &ctxt->til_module_context;
+}
+
+
+static void asc_render_fragment(til_module_context_t *context, til_stream_t *stream, unsigned ticks, unsigned cpu, til_fb_fragment_t **fragment_ptr)
+{
+ asc_context_t *ctxt = (asc_context_t *)context;
+ asc_setup_t *s = (asc_setup_t *)context->setup;
+ til_fb_fragment_t *fragment = *fragment_ptr;
+
+ til_fb_fragment_clear(fragment);
+
+ txt_render_fragment(ctxt->txt, fragment, 0xffffffff,
+ s->x * ((float)fragment->frame_width) * .5f + .5f * ((float)fragment->frame_width),
+ s->y * ((float)fragment->frame_height) * .5f + .5f * ((float)fragment->frame_height),
+ (txt_align_t){
+ .horiz = s->halign,
+ .vert = s->valign
+ });
+}
+
+
+static void asc_setup_free(til_setup_t *setup)
+{
+ asc_setup_t *s = (asc_setup_t *)setup;
+
+ if (s) {
+ free((void *)s->string);
+ free(s);
+ }
+}
+
+
+static int asc_setup(const til_settings_t *settings, til_setting_t **res_setting, const til_setting_desc_t **res_desc, til_setup_t **res_setup);
+
+
+til_module_t asc_module = {
+ .create_context = asc_create_context,
+ .render_fragment = asc_render_fragment,
+ .setup = asc_setup,
+ .name = "asc",
+ .description = "ASCII text",
+ .author = "Vito Caputo <vcaputo@pengaru.com>",
+ .flags = TIL_MODULE_OVERLAYABLE,
+};
+
+
+static int asc_setup(const til_settings_t *settings, til_setting_t **res_setting, const til_setting_desc_t **res_desc, til_setup_t **res_setup)
+{
+ til_setting_t *string;
+ til_setting_t *valign;
+ til_setting_t *halign;
+ til_setting_t *x, *y;
+ const char *valign_values[] = {
+ "center",
+ "top",
+ "bottom",
+ NULL
+ };
+ const char *halign_values[] = {
+ "center",
+ "left",
+ "right",
+ NULL
+ };
+ int r;
+
+ r = til_settings_get_and_describe_setting(settings,
+ &(til_setting_spec_t){
+ .name = "Text string",
+ .key = "string",
+ /* .regex = "" TODO */
+ .preferred = ASC_DEFAULT_STRING,
+ },
+ &string,
+ res_setting,
+ res_desc);
+ if (r)
+ return r;
+
+ r = til_settings_get_and_describe_setting(settings,
+ &(til_setting_spec_t){
+ .name = "Vertical alignment",
+ .key = "valign",
+ /* .regex = "" TODO */
+ .preferred = valign_values[ASC_DEFAULT_VALIGN],
+ .values = valign_values,
+ .annotations = NULL
+ },
+ &valign,
+ res_setting,
+ res_desc);
+ if (r)
+ return r;
+
+ r = til_settings_get_and_describe_setting(settings,
+ &(til_setting_spec_t){
+ .name = "Horizontal alignment",
+ .key = "halign",
+ /* .regex = "" TODO */
+ .preferred = halign_values[ASC_DEFAULT_HALIGN],
+ .values = halign_values,
+ .annotations = NULL
+ },
+ &halign,
+ res_setting,
+ res_desc);
+ if (r)
+ return r;
+
+ r = til_settings_get_and_describe_setting(settings,
+ &(til_setting_spec_t){
+ .name = "X coordinate [-1.0...1.0]",
+ .key = "x",
+ /* .regex = "" TODO */
+ .preferred = TIL_SETTINGS_STR(ASC_DEFAULT_X),
+ .annotations = NULL
+ },
+ &x,
+ res_setting,
+ res_desc);
+ if (r)
+ return r;
+
+ r = til_settings_get_and_describe_setting(settings,
+ &(til_setting_spec_t){
+ .name = "Y coordinate [-1.0...1.0]",
+ .key = "y",
+ /* .regex = "" TODO */
+ .preferred = TIL_SETTINGS_STR(ASC_DEFAULT_Y),
+ .annotations = NULL
+ },
+ &y,
+ res_setting,
+ res_desc);
+ if (r)
+ return r;
+
+ if (res_setup) {
+ asc_setup_t *setup;
+
+ setup = til_setup_new(settings, sizeof(*setup), asc_setup_free, &asc_module);
+ if (!setup)
+ return -ENOMEM;
+
+ setup->string = strdup(string->value);
+ if (!setup->string)
+ return til_setup_free_with_failed_setting_ret_err(&setup->til_setup, string, res_setting, -ENOMEM);
+
+ r = til_value_to_pos(halign_values, halign->value, (unsigned *)&setup->halign);
+ if (r < 0)
+ return til_setup_free_with_failed_setting_ret_err(&setup->til_setup, halign, res_setting, -EINVAL);
+
+ r = til_value_to_pos(valign_values, valign->value, (unsigned *)&setup->valign);
+ if (r < 0)
+ return til_setup_free_with_failed_setting_ret_err(&setup->til_setup, valign, res_setting, -EINVAL);
+
+ if (sscanf(x->value, "%f", &setup->x) != 1)
+ return til_setup_free_with_failed_setting_ret_err(&setup->til_setup, x, res_setting, -EINVAL);
+
+ if (sscanf(y->value, "%f", &setup->y) != 1)
+ return til_setup_free_with_failed_setting_ret_err(&setup->til_setup, y, res_setting, -EINVAL);
+
+ *res_setup = &setup->til_setup;
+ }
+
+ return 0;
+}
© All Rights Reserved