diff options
Diffstat (limited to 'src/modules/checkers/checkers.c')
-rw-r--r-- | src/modules/checkers/checkers.c | 260 |
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, |