summaryrefslogtreecommitdiff
path: root/src/modules/checkers/checkers.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/modules/checkers/checkers.c')
-rw-r--r--src/modules/checkers/checkers.c260
1 files changed, 256 insertions, 4 deletions
diff --git a/src/modules/checkers/checkers.c b/src/modules/checkers/checkers.c
index c936269..8b30445 100644
--- a/src/modules/checkers/checkers.c
+++ b/src/modules/checkers/checkers.c
@@ -27,6 +27,9 @@
#define CHECKERS_DEFAULT_PATTERN CHECKERS_PATTERN_CHECKERED
#define CHECKERS_DEFAULT_DYNAMICS CHECKERS_DYNAMICS_ODD
#define CHECKERS_DEFAULT_DYNAMICS_RATE 1.0
+#define CHECKERS_DEFAULT_FILL CHECKERS_FILL_COLOR
+#define CHECKERS_DEFAULT_COLOR 0xffffff
+#define CHECKERS_DEFAULT_FILL_MODULE "none"
typedef enum checkers_pattern_t {
@@ -41,17 +44,29 @@ typedef enum checkers_dynamics_t {
CHECKERS_DYNAMICS_RANDOM,
} checkers_dynamics_t;
+typedef enum checkers_fill_t {
+ CHECKERS_FILL_COLOR,
+ CHECKERS_FILL_SAMPLED,
+ CHECKERS_FILL_TEXTURED,
+ CHECKERS_FILL_RANDOM,
+ CHECKERS_FILL_MIXED,
+} checkers_fill_t;
+
typedef struct checkers_setup_t {
til_setup_t til_setup;
unsigned size;
checkers_pattern_t pattern;
checkers_dynamics_t dynamics;
float rate;
+ checkers_fill_t fill;
+ uint32_t color;
+ const til_module_t *fill_module;
} checkers_setup_t;
typedef struct checkers_context_t {
til_module_context_t til_module_context;
checkers_setup_t setup;
+ til_module_context_t *fill_module_contexts[];
} checkers_context_t;
@@ -60,26 +75,61 @@ static checkers_setup_t checkers_default_setup = {
.pattern = CHECKERS_DEFAULT_PATTERN,
.dynamics = CHECKERS_DEFAULT_DYNAMICS,
.rate = CHECKERS_DEFAULT_DYNAMICS_RATE,
+ .color = CHECKERS_DEFAULT_COLOR,
};
static til_module_context_t * checkers_create_context(unsigned seed, unsigned ticks, unsigned n_cpus, til_setup_t *setup)
{
+ size_t size = sizeof(checkers_context_t);
checkers_context_t *ctxt;
if (!setup)
setup = &checkers_default_setup.til_setup;
- ctxt = til_module_context_new(sizeof(checkers_context_t), ticks, seed, n_cpus);
+ if (((checkers_setup_t *)setup)->fill_module)
+ size += sizeof(til_module_context_t *) * n_cpus;
+
+ ctxt = til_module_context_new(size, ticks, seed, n_cpus);
if (!ctxt)
return NULL;
ctxt->setup = *(checkers_setup_t *)setup;
+ if (ctxt->setup.fill_module) {
+ const til_module_t *module = ctxt->setup.fill_module;
+ til_setup_t *module_setup = NULL;
+
+ (void) til_module_randomize_setup(module, &module_setup, NULL);
+
+ /* since checkers is already threaded, create an n_cpus=1 context per-cpu */
+ for (unsigned i = 0; i < n_cpus; i++) /* TODO: errors */
+ (void) til_module_create_context(module, seed, ticks, 1, module_setup, &ctxt->fill_module_contexts[i]);
+
+ /* XXX: it would be interesting to support various patterns/layouts by varying the seed, but this will require
+ * more complex context allocation strategies while also maintaining the per-cpu allocation.
+ */
+
+ til_setup_free(module_setup);
+ }
+
return &ctxt->til_module_context;
}
+static void checkers_destroy_context(til_module_context_t *context)
+{
+ checkers_context_t *ctxt = (checkers_context_t *)context;
+
+ if (ctxt->setup.fill_module) {
+ for (unsigned i = 0; i < context->n_cpus; i++)
+ til_module_context_free(ctxt->fill_module_contexts[i]);
+ }
+
+ free(ctxt);
+}
+
+
static int checkers_fragmenter(til_module_context_t *context, const til_fb_fragment_t *fragment, unsigned number, til_fb_fragment_t *res_fragment)
{
checkers_context_t *ctxt = (checkers_context_t *)context;
@@ -92,7 +142,15 @@ static void checkers_prepare_frame(til_module_context_t *context, unsigned ticks
{
checkers_context_t *ctxt = (checkers_context_t *)context;
- *res_frame_plan = (til_frame_plan_t){ .fragmenter = checkers_fragmenter };
+ /* XXX: note cpu_affinity is required when fill_module is used, to ensure module_contexts
+ * have a stable relationship to fragnum. Otherwise the output would be unstable because the
+ * module contexts would be randomly distributed across the filled checkers frame-to-frame.
+ * This is unfortunate since cpu_affinity is likely to be slower than just letting the render
+ * threads render fragments in whatever order (the preferred default). fill_module here
+ * is actually *the* reason til_frame_plan_t.cpu_affinity got implemented, before this there
+ * wasn't even a til_frame_plan_t container; a bare til_fragmenter_t was returned.
+ */
+ *res_frame_plan = (til_frame_plan_t){ .fragmenter = checkers_fragmenter, .cpu_affinity = !!ctxt->setup.fill_module };
}
@@ -110,6 +168,8 @@ static inline unsigned hash(unsigned x)
static void checkers_render_fragment(til_module_context_t *context, unsigned ticks, unsigned cpu, til_fb_fragment_t *fragment)
{
checkers_context_t *ctxt = (checkers_context_t *)context;
+ uint32_t color = ctxt->setup.color, flags = 0;
+ checkers_fill_t fill = ctxt->setup.fill;
int state;
switch (ctxt->setup.pattern) {
@@ -142,10 +202,104 @@ static void checkers_render_fragment(til_module_context_t *context, unsigned tic
break;
}
+ if (fill == CHECKERS_FILL_RANDOM || fill == CHECKERS_FILL_MIXED)
+ fill = rand() % CHECKERS_FILL_RANDOM; /* TODO: mixed should have a setting for controlling the ratios */
+
+ switch (ctxt->setup.fill) {
+ case CHECKERS_FILL_SAMPLED:
+ if (fragment->cleared)
+ color = til_fb_fragment_get_pixel_unchecked(fragment, fragment->x + (fragment->width >> 1), fragment->y + (fragment->height >> 1));
+ break;
+ case CHECKERS_FILL_TEXTURED:
+ flags = TIL_FB_DRAW_FLAG_TEXTURABLE;
+ break;
+ case CHECKERS_FILL_COLOR:
+ default:
+ break;
+ }
+
if (!state)
til_fb_fragment_clear(fragment);
- else
- til_fb_fragment_fill(fragment, TIL_FB_DRAW_FLAG_TEXTURABLE, 0xffffffff);
+ else {
+ if (!ctxt->setup.fill_module)
+ til_fb_fragment_fill(fragment, flags, color);
+ else {
+ fragment->frame_width = ctxt->setup.size;
+ fragment->frame_height = ctxt->setup.size;
+ fragment->x = fragment->y = 0;
+
+ /* TODO: we need a way to send down color and flags, and use the module render as a brush of sorts */
+ til_module_render(ctxt->fill_module_contexts[cpu], ticks, fragment);
+ }
+ }
+}
+
+
+/* TODO: migrate to libtil */
+static char * checkers_random_color(void)
+{
+ /* til should probably have a common randomize color helper for this with a large collection of
+ * reasonable colors, and maybe even have themed palettes one can choose from... */
+ static const char * colors[] = {
+ "#ffffff",
+ "#ff0000",
+ "#00ff00",
+ "#0000ff",
+ "#ffff00",
+ "#00ffff",
+ "#ff00ff",
+ };
+
+ return strdup(colors[rand() % nelems(colors)]);
+}
+
+
+/* TODO: migrate to libtil */
+static int checkers_rgb_to_uint32(const char *in, uint32_t *out)
+{
+ uint32_t color = 0;
+
+ /* this isn't html, but accept #rrggbb syntax */
+ if (*in == '#')
+ in++;
+ else if (in[0] == '0' && in[1] == 'x') /* and 0xrrggbb */
+ in += 2;
+
+ if (strlen(in) != 6)
+ return -EINVAL;
+
+ for (int i = 0; i < 6;) {
+ uint8_t c = 0;
+
+ color <<= 8;
+
+ for (int j = 0; j < 2; in++, j++, i++) {
+ c <<= 4;
+
+ switch (*in) {
+ case '0'...'9':
+ c |= (*in) - '0';
+ break;
+
+ case 'a'...'f':
+ c |= (*in) - 'a' + 10;
+ break;
+
+ case 'A'...'F':
+ c |= (*in) - 'A' + 10;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ }
+
+ color |= c;
+ }
+
+ *out = color;
+
+ return 0;
}
@@ -153,8 +307,11 @@ static int checkers_setup(const til_settings_t *settings, til_setting_t **res_se
{
const char *size;
const char *pattern;
+ const char *fill_module;
const char *dynamics;
const char *dynamics_rate;
+ const char *fill;
+ const char *color;
const char *size_values[] = {
"4",
"8",
@@ -169,6 +326,19 @@ static int checkers_setup(const til_settings_t *settings, til_setting_t **res_se
"random",
NULL
};
+ const char *fill_module_values[] = {
+ "none",
+ "blinds",
+ "moire",
+ "pixbounce",
+ "plato",
+ "roto",
+ "shapes",
+ "snow",
+ "spiro",
+ "stars",
+ NULL
+ };
const char *dynamics_values[] = {
"odd",
"even",
@@ -187,6 +357,14 @@ static int checkers_setup(const til_settings_t *settings, til_setting_t **res_se
".0001",
NULL
};
+ const char *fill_values[] = {
+ "color",
+ "sampled",
+ "textured",
+ "random",
+ "mixed",
+ NULL
+ };
int r;
r = til_settings_get_and_describe_value(settings,
@@ -220,6 +398,20 @@ static int checkers_setup(const til_settings_t *settings, til_setting_t **res_se
r = til_settings_get_and_describe_value(settings,
&(til_setting_desc_t){
+ .name = "Filled cell module (\"none\" for plain checkers)",
+ .key = "fill_module",
+ .preferred = fill_module_values[0],
+ .values = fill_module_values,
+ .annotations = NULL
+ },
+ &fill_module,
+ res_setting,
+ res_desc);
+ if (r)
+ return r;
+
+ r = til_settings_get_and_describe_value(settings,
+ &(til_setting_desc_t){
.name = "Checkers dynamics",
.key = "dynamics",
.preferred = dynamics_values[0],
@@ -248,6 +440,38 @@ static int checkers_setup(const til_settings_t *settings, til_setting_t **res_se
return r;
}
+ r = til_settings_get_and_describe_value(settings,
+ &(til_setting_desc_t){
+ .name = "Fill mode",
+ .key = "fill",
+ .preferred = fill_values[CHECKERS_DEFAULT_FILL],
+ .values = fill_values,
+ .annotations = NULL
+ },
+ &fill,
+ res_setting,
+ res_desc);
+ if (r)
+ return r;
+
+ /* Even though sampled and textured fills don't neceesarily use the color,
+ * if there's no texture or no underlay to sample, we should have a color to fallback on.
+ */
+ r = til_settings_get_and_describe_value(settings,
+ &(til_setting_desc_t){
+ .name = "Fill color",
+ .key = "color",
+ .preferred = TIL_SETTINGS_STR(CHECKERS_DEFAULT_COLOR),
+ .random = checkers_random_color,
+ .values = NULL,
+ .annotations = NULL
+ },
+ &color,
+ res_setting,
+ res_desc);
+ if (r)
+ return r;
+
if (res_setup) {
checkers_setup_t *setup;
@@ -266,6 +490,14 @@ static int checkers_setup(const til_settings_t *settings, til_setting_t **res_se
return -EINVAL;
}
+ if (strcasecmp(fill_module, "none")) {
+ setup->fill_module = til_lookup_module(fill_module);
+ if (!setup->fill_module) {
+ free(setup);
+ return -ENOMEM;
+ }
+ }
+
if (!strcasecmp(dynamics, "odd"))
setup->dynamics = CHECKERS_DYNAMICS_ODD;
else if (!strcasecmp(dynamics, "even"))
@@ -282,6 +514,25 @@ static int checkers_setup(const til_settings_t *settings, til_setting_t **res_se
if (setup->dynamics != CHECKERS_DYNAMICS_ODD && setup->dynamics != CHECKERS_DYNAMICS_EVEN)
sscanf(dynamics_rate, "%f", &setup->rate);
+ if (!strcasecmp(fill, "color"))
+ setup->fill = CHECKERS_FILL_COLOR;
+ else if (!strcasecmp(fill, "sampled"))
+ setup->fill = CHECKERS_FILL_SAMPLED;
+ else if (!strcasecmp(fill, "textured"))
+ setup->fill = CHECKERS_FILL_TEXTURED;
+ else if (!strcasecmp(fill, "random"))
+ setup->fill = CHECKERS_FILL_RANDOM;
+ else if (!strcasecmp(fill, "mixed"))
+ setup->fill = CHECKERS_FILL_MIXED;
+ else {
+ free(setup);
+ return -EINVAL;
+ }
+
+ r = checkers_rgb_to_uint32(color, &setup->color);
+ if (r < 0)
+ return r;
+
*res_setup = &setup->til_setup;
}
@@ -291,6 +542,7 @@ static int checkers_setup(const til_settings_t *settings, til_setting_t **res_se
til_module_t checkers_module = {
.create_context = checkers_create_context,
+ .destroy_context = checkers_destroy_context,
.prepare_frame = checkers_prepare_frame,
.render_fragment = checkers_render_fragment,
.setup = checkers_setup,
© All Rights Reserved