diff --git a/app/include/drivers/animation.h b/app/include/drivers/animation.h index 45e80524..0d47f13a 100644 --- a/app/include/drivers/animation.h +++ b/app/include/drivers/animation.h @@ -37,7 +37,8 @@ typedef void (*animation_api_prep_next_frame)(const struct device *dev); * * @see animation_prep_next_frame() for argument descriptions. */ -typedef void (*animation_api_get_pixel)(const struct device *dev, const struct animation_pixel_position *pixel_position, +typedef void (*animation_api_get_pixel)(const struct device *dev, + const struct animation_pixel_position *pixel_position, struct zmk_color_rgb *value); struct animation_api { @@ -50,7 +51,7 @@ struct animation_api { * @param dev [description] */ static inline void animation_prep_next_frame(const struct device *dev) { - const struct animation_api *api = (const struct animation_api *) dev->api; + const struct animation_api *api = (const struct animation_api *)dev->api; return api->prep_next_frame(dev); } @@ -60,9 +61,10 @@ static inline void animation_prep_next_frame(const struct device *dev) { * @param dev [description] * @param pixel [description] */ -static inline void animation_get_pixel(const struct device *dev, const struct animation_pixel_position *pixel_position, - struct zmk_color_rgb *value) { - const struct animation_api *api = (const struct animation_api *) dev->api; +static inline void animation_get_pixel(const struct device *dev, + const struct animation_pixel_position *pixel_position, + struct zmk_color_rgb *value) { + const struct animation_api *api = (const struct animation_api *)dev->api; return api->get_pixel(dev, pixel_position, value); } diff --git a/app/include/zmk/animation.h b/app/include/zmk/animation.h index df488851..64bffd8e 100644 --- a/app/include/zmk/animation.h +++ b/app/include/zmk/animation.h @@ -34,8 +34,8 @@ struct zmk_color_hsl { /** * Converts color from HSL to RGB. * - * @param hsl [description] - * @param rgb [description] + * @param hsl Color to convert + * @param rgb Converted color */ void zmk_hsl_to_rgb(const struct zmk_color_hsl *hsl, struct zmk_color_rgb *rgb); @@ -43,17 +43,17 @@ void zmk_hsl_to_rgb(const struct zmk_color_hsl *hsl, struct zmk_color_rgb *rgb); * Converts the internal RGB representation into a led_rgb struct * for use with led_strip drivers. * - * @param rgb [description] - * @param led [description] + * @param rgb Color to convert + * @param led Converted color */ void zmk_rgb_to_led_rgb(const struct zmk_color_rgb *rgb, struct led_rgb *led); /** * Returns true if two HSL colors are the same. * - * @param a [description] - * @param b [description] - * @return [description] + * @param a HSL color to compare + * @param b HSL color to compare + * @return True when colors share the same values */ bool zmk_cmp_hsl(const struct zmk_color_hsl *a, const struct zmk_color_hsl *b); @@ -61,10 +61,10 @@ bool zmk_cmp_hsl(const struct zmk_color_hsl *a, const struct zmk_color_hsl *b); * Perform linear interpolation between HSL values of two colors * at a given distance (step) and store the resulting value in the given pointer. * - * @param from [description] - * @param to [description] - * @param result [description] - * @param step [description] + * @param from HSL color to interpolate + * @param to HSL color to interpolate + * @param result Resulting HSL color + * @param step Interpolation step */ void zmk_interpolate_hsl(const struct zmk_color_hsl *from, const struct zmk_color_hsl *to, - struct zmk_color_hsl *result, float step); + struct zmk_color_hsl *result, float step); diff --git a/app/src/animation/animation.c b/app/src/animation/animation.c index e9c572f8..a94f7a20 100644 --- a/app/src/animation/animation.c +++ b/app/src/animation/animation.c @@ -22,30 +22,29 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); // Zephyr 2.7.0 comes with DT_INST_FOREACH_PROP_ELEM // that we can't use quite yet as we're still on 2.5.* -#define ZMK_DT_INST_FOREACH_PROP_ELEM(inst, prop, fn) \ - UTIL_LISTIFY(DT_INST_PROP_LEN(inst, prop), fn, DT_DRV_INST(inst), prop) +#define ZMK_DT_INST_FOREACH_PROP_ELEM(inst, prop, fn) \ + UTIL_LISTIFY(DT_INST_PROP_LEN(inst, prop), fn, DT_DRV_INST(inst), prop) -#define PHANDLE_TO_DEVICE(idx, node_id, prop) \ - DEVICE_DT_GET(DT_PHANDLE_BY_IDX(node_id, prop, idx)), +#define PHANDLE_TO_DEVICE(idx, node_id, prop) DEVICE_DT_GET(DT_PHANDLE_BY_IDX(node_id, prop, idx)), -#define PHANDLE_TO_CHAIN_LENGTH(idx, node_id, prop) \ - DT_PROP_BY_PHANDLE_IDX(node_id, prop, idx, chain_length), +#define PHANDLE_TO_CHAIN_LENGTH(idx, node_id, prop) \ + DT_PROP_BY_PHANDLE_IDX(node_id, prop, idx, chain_length), -#define PHANDLE_TO_PIXEL(idx, node_id, prop) \ - { \ - .animation = PHANDLE_TO_DEVICE(idx, node_id, prop) \ - .position = { \ - .x = DT_PHA_BY_IDX(node_id, prop, idx, position_x),\ - .y = DT_PHA_BY_IDX(node_id, prop, idx, position_y),\ - }, \ - }, +#define PHANDLE_TO_PIXEL(idx, node_id, prop) \ + { \ + .animation = DEVICE_DT_GET(DT_PHANDLE_BY_IDX(node_id, prop, idx)), \ + .position = \ + { \ + .x = DT_PHA_BY_IDX(node_id, prop, idx, position_x), \ + .y = DT_PHA_BY_IDX(node_id, prop, idx, position_y), \ + }, \ + }, /** * LED Driver device pointers. */ static const struct device *drivers[] = { - ZMK_DT_INST_FOREACH_PROP_ELEM(0, drivers, PHANDLE_TO_DEVICE) -}; + ZMK_DT_INST_FOREACH_PROP_ELEM(0, drivers, PHANDLE_TO_DEVICE)}; /** * Size of the LED driver device pointers array. @@ -56,15 +55,13 @@ static const size_t drivers_size = DT_INST_PROP_LEN(0, drivers); * Array containing the number of LEDs handled by each device. */ static const uint8_t pixels_per_driver[] = { - ZMK_DT_INST_FOREACH_PROP_ELEM(0, drivers, PHANDLE_TO_CHAIN_LENGTH) -}; + ZMK_DT_INST_FOREACH_PROP_ELEM(0, drivers, PHANDLE_TO_CHAIN_LENGTH)}; /** * Pointers to all active animation devices. */ static const struct device *animations[] = { - ZMK_DT_INST_FOREACH_PROP_ELEM(0, animations, PHANDLE_TO_DEVICE) -}; + ZMK_DT_INST_FOREACH_PROP_ELEM(0, animations, PHANDLE_TO_DEVICE)}; /** * Size of the animation device pointers array. @@ -75,8 +72,7 @@ static const size_t animations_size = DT_INST_PROP_LEN(0, animations); * Pixel configuration. */ static const struct animation_pixel pixels[] = { - ZMK_DT_INST_FOREACH_PROP_ELEM(0, pixels, PHANDLE_TO_PIXEL) -}; + ZMK_DT_INST_FOREACH_PROP_ELEM(0, pixels, PHANDLE_TO_PIXEL)}; /** * Size of the pixels array. @@ -89,49 +85,43 @@ static const size_t pixels_size = DT_INST_PROP_LEN(0, pixels); static struct led_rgb px_buffer[DT_INST_PROP_LEN(0, pixels)]; static void zmk_animation_tick(struct k_work *work) { - for (size_t i = 0; i < animations_size; ++i) { - animation_prep_next_frame(animations[i]); - } + for (size_t i = 0; i < animations_size; ++i) { + animation_prep_next_frame(animations[i]); + } - for (size_t i = 0; i < pixels_size; ++i) { - struct zmk_color_rgb rgb = { - .r = 0, - .g = 0, - .b = 0, - }; + for (size_t i = 0; i < pixels_size; ++i) { + struct zmk_color_rgb rgb = { + .r = 0, + .g = 0, + .b = 0, + }; - animation_get_pixel(pixels[i].animation, &pixels[i].position, &rgb); + animation_get_pixel(pixels[i].animation, &pixels[i].position, &rgb); - zmk_rgb_to_led_rgb(&rgb, &px_buffer[i]); - } + zmk_rgb_to_led_rgb(&rgb, &px_buffer[i]); + } - size_t pixels_updated = 0; + size_t pixels_updated = 0; - for (size_t i = 0; i < drivers_size; ++i) { - led_strip_update_rgb( - drivers[i], - &px_buffer[pixels_updated], - pixels_per_driver[i] - ); + for (size_t i = 0; i < drivers_size; ++i) { + led_strip_update_rgb(drivers[i], &px_buffer[pixels_updated], pixels_per_driver[i]); - pixels_updated += (size_t) pixels_per_driver; - } + pixels_updated += (size_t)pixels_per_driver; + } } K_WORK_DEFINE(animation_work, zmk_animation_tick); -static void zmk_animation_tick_handler(struct k_timer *timer) { - k_work_submit(&animation_work); -} +static void zmk_animation_tick_handler(struct k_timer *timer) { k_work_submit(&animation_work); } K_TIMER_DEFINE(animation_tick, zmk_animation_tick_handler, NULL); static int zmk_animation_init(const struct device *dev) { - LOG_INF("ZMK Animation Ready"); + LOG_INF("ZMK Animation Ready"); - k_timer_start(&animation_tick, K_NO_WAIT, K_MSEC(1000 / CONFIG_ZMK_ANIMATION_FPS)); + k_timer_start(&animation_tick, K_NO_WAIT, K_MSEC(1000 / CONFIG_ZMK_ANIMATION_FPS)); - return 0; + return 0; } SYS_INIT(zmk_animation_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY); diff --git a/app/src/animation/animation_solid.c b/app/src/animation/animation_solid.c index 6d485dce..6512b427 100644 --- a/app/src/animation/animation_solid.c +++ b/app/src/animation/animation_solid.c @@ -45,12 +45,9 @@ static void animation_solid_prep_next_frame(const struct device *dev) { struct zmk_color_hsl next_hsl; - zmk_interpolate_hsl( - &config->colors[from], - &config->colors[to], - &next_hsl, - (data->counter % config->transition_duration) / (float) config->transition_duration - ); + zmk_interpolate_hsl(&config->colors[from], &config->colors[to], &next_hsl, + (data->counter % config->transition_duration) / + (float)config->transition_duration); data->has_changed = !zmk_cmp_hsl(&data->current_hsl, &next_hsl); @@ -60,8 +57,9 @@ static void animation_solid_prep_next_frame(const struct device *dev) { data->counter = (data->counter + 1) % config->duration; } -static void animation_solid_get_pixel(const struct device *dev, const struct animation_pixel_position *position, - struct zmk_color_rgb *value) { +static void animation_solid_get_pixel(const struct device *dev, + const struct animation_pixel_position *position, + struct zmk_color_rgb *value) { const struct animation_solid_data *data = dev->data; value->r = data->current_rgb.r; @@ -86,59 +84,23 @@ static const struct animation_api animation_solid_api = { .get_pixel = animation_solid_get_pixel, }; -#define ANIMATION_SOLID_DEVICE(idx) \ - \ - static struct animation_solid_data animation_solid_##idx##_data; \ - \ - static uint32_t animation_solid_##idx##_colors[DT_INST_PROP_LEN(idx, colors)] = DT_INST_PROP(idx, colors); \ - \ - static struct animation_solid_config animation_solid_##idx##_config = { \ - .colors = (struct zmk_color_hsl *) animation_solid_##idx##_colors, \ - .num_colors = DT_INST_PROP_LEN(idx, colors), \ - .duration = DT_INST_PROP(idx, duration) * CONFIG_ZMK_ANIMATION_FPS, \ - .transition_duration = (DT_INST_PROP(idx, duration) * CONFIG_ZMK_ANIMATION_FPS) / DT_INST_PROP_LEN(idx, colors), \ - }; \ - \ - DEVICE_DT_INST_DEFINE(idx, &animation_solid_init, NULL, &animation_solid_##idx##_data, \ - &animation_solid_##idx##_config, POST_KERNEL, CONFIG_LED_STRIP_INIT_PRIORITY, \ - &animation_solid_api); +#define ANIMATION_SOLID_DEVICE(idx) \ + \ + static struct animation_solid_data animation_solid_##idx##_data; \ + \ + static uint32_t animation_solid_##idx##_colors[DT_INST_PROP_LEN(idx, colors)] = \ + DT_INST_PROP(idx, colors); \ + \ + static struct animation_solid_config animation_solid_##idx##_config = { \ + .colors = (struct zmk_color_hsl *)animation_solid_##idx##_colors, \ + .num_colors = DT_INST_PROP_LEN(idx, colors), \ + .duration = DT_INST_PROP(idx, duration) * CONFIG_ZMK_ANIMATION_FPS, \ + .transition_duration = (DT_INST_PROP(idx, duration) * CONFIG_ZMK_ANIMATION_FPS) / \ + DT_INST_PROP_LEN(idx, colors), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(idx, &animation_solid_init, NULL, &animation_solid_##idx##_data, \ + &animation_solid_##idx##_config, POST_KERNEL, \ + CONFIG_APPLICATION_INIT_PRIORITY, &animation_solid_api); DT_INST_FOREACH_STATUS_OKAY(ANIMATION_SOLID_DEVICE); - - -// To do: -// -// STEP 1: single animation -// - Start with a single animation, just color -// - Add layer for taking the output from here and putting it to the led strip -// - Make it work -// -// STEP 2: areas, in fact, instead of defining them explicitly we can just use appropriate x,y coordinates and animation. -// - Split keyboard in two independent areas -// - Make it work -// -// STEP 3: add additional animation effects -// - Basically, carry over rgb_underglow. -// - Make it work -// -// STEP 4: add animation triggers -// - Allow an animation to be triggered by behaviors or key-presses -// - Make it work -// -// STEP 5: add animation layers and a MULTIPLY mode (again, opacity would be set on individual pixels so... that affects some optimizations I guess) -// - Normal mode: overrides layers below -// - Multiply mode: auguments whatever is below (opacity, whatever) -// -// Voila! Animation composition! -// -// STEP 6, BONUS!: -// - Figure out a way to switch animations during runtime? -// -// Notes: -// - Any animation settings go into 'driver' config & data, so they can be updated at runtime. -// - Main limitation is space, so the amount of different animations one can have loaded -// -// More notes: -// - Solid color would be one animation (just transitions between colors) -// - Gradient (SPECTRUM) would be another, you choose how they're distributed accross the keys and if they move? -// - Effects like 'breathe' can be implemented by specifying #000 as one of the colors or using a multiply layer? diff --git a/app/src/animation/color.c b/app/src/animation/color.c index 53bec6e4..25892dc4 100644 --- a/app/src/animation/color.c +++ b/app/src/animation/color.c @@ -9,98 +9,97 @@ #include static float fmod(float a, float b) { - float mod = a < 0 ? -a : a; - float x = b < 0 ? -b : b; + float mod = a < 0 ? -a : a; + float x = b < 0 ? -b : b; - while (mod >= x) { - mod = mod - x; - } + while (mod >= x) { + mod = mod - x; + } - return a < 0 ? -mod : mod; + return a < 0 ? -mod : mod; } -static float fabs(float a) { - return a < 0 ? -a : a; -} +static float fabs(float a) { return a < 0 ? -a : a; } /** * HSL chosen over HSV/HSB as it shares the same parameters with LCh or HSLuv. * The latter color spaces could be interesting to experiment with because of their * perceptual uniformity, but it would come at the cost of some performance. - * Using the same parameters would make it easy to toggle any such behavior using a single config flag. + * Using the same parameters would make it easy to toggle any such behavior + * using a single config flag. * * Algorithm source: https://www.tlbx.app/color-converter */ void zmk_hsl_to_rgb(const struct zmk_color_hsl *hsl, struct zmk_color_rgb *rgb) { - float s = (float) hsl->s / 100; - float l = (float) hsl->l / 100; + float s = (float)hsl->s / 100; + float l = (float)hsl->l / 100; - float a = (float) hsl->h / 60; - float chroma = s * (1 - fabs(2 * l - 1)); - float x = chroma * (1 - fabs(fmod(a, 2) - 1)); - float m = l - chroma / 2; + float a = (float)hsl->h / 60; + float chroma = s * (1 - fabs(2 * l - 1)); + float x = chroma * (1 - fabs(fmod(a, 2) - 1)); + float m = l - chroma / 2; - switch ((uint8_t) a % 6) { - case 0: - rgb->r = m + chroma; - rgb->g = m + x; - rgb->b = m; - break; - case 1: - rgb->r = m + x; - rgb->g = m + chroma; - rgb->b = m; - break; - case 2: - rgb->r = m; - rgb->g = m + chroma; - rgb->b = m + x; - break; - case 3: - rgb->r = m; - rgb->g = m + x; - rgb->b = m + chroma; - break; - case 4: - rgb->r = m + x; - rgb->g = m; - rgb->b = m + chroma; - break; - case 5: - rgb->r = m + chroma; - rgb->g = m; - rgb->b = m + x; - break; - } + switch ((uint8_t)a % 6) { + case 0: + rgb->r = m + chroma; + rgb->g = m + x; + rgb->b = m; + break; + case 1: + rgb->r = m + x; + rgb->g = m + chroma; + rgb->b = m; + break; + case 2: + rgb->r = m; + rgb->g = m + chroma; + rgb->b = m + x; + break; + case 3: + rgb->r = m; + rgb->g = m + x; + rgb->b = m + chroma; + break; + case 4: + rgb->r = m + x; + rgb->g = m; + rgb->b = m + chroma; + break; + case 5: + rgb->r = m + chroma; + rgb->g = m; + rgb->b = m + x; + break; + } } /** * Converts ZMKs RGB (float) to Zephyr's led_rgb (uint8_t) format. */ void zmk_rgb_to_led_rgb(const struct zmk_color_rgb *rgb, struct led_rgb *led) { - led->r = (uint8_t) (rgb->r * 255); - led->g = (uint8_t) (rgb->g * 255); - led->b = (uint8_t) (rgb->b * 255); + led->r = rgb->r * 255; + led->g = rgb->g * 255; + led->b = rgb->b * 255; } /** * Compares two HSL colors. */ bool zmk_cmp_hsl(const struct zmk_color_hsl *a, const struct zmk_color_hsl *b) { - return a->h == b->h && a->s == b->s && a->l == b->l; + return a->h == b->h && a->s == b->s && a->l == b->l; } /** * Interpolate between two colors using the cylindrical model (HSL). */ void zmk_interpolate_hsl(const struct zmk_color_hsl *from, const struct zmk_color_hsl *to, - struct zmk_color_hsl *result, float step) { + struct zmk_color_hsl *result, float step) { int16_t hue_delta; hue_delta = from->h - to->h; hue_delta = hue_delta + (180 < abs(hue_delta) ? (hue_delta < 0 ? 360 : -360) : 0); - result->h = (uint16_t) (360 + from->h - (hue_delta * step)) % 360; + result->h = (uint16_t)(360 + from->h - (hue_delta * step)) % 360; result->s = from->s - (from->s - to->s) * step; result->l = from->l - (from->l - to->l) * step; }