diff --git a/app/dts/bindings/zmk,underglow-layer.yaml b/app/dts/bindings/zmk,underglow-layer.yaml index 62fec31b..28221039 100644 --- a/app/dts/bindings/zmk,underglow-layer.yaml +++ b/app/dts/bindings/zmk,underglow-layer.yaml @@ -18,3 +18,7 @@ child-binding: layer-id: type: int required: true + fade-delay: + type: int + required: false + default: -1 diff --git a/app/include/zmk/rgb_underglow_layer.h b/app/include/zmk/rgb_underglow_layer.h index 879aff1a..1b14359e 100644 --- a/app/include/zmk/rgb_underglow_layer.h +++ b/app/include/zmk/rgb_underglow_layer.h @@ -23,6 +23,8 @@ const int rgb_pixel_lookup(int idx); const int zmk_rgbmap_id(uint8_t layer); +const int zmk_rgbmap_fade_delay(uint8_t layer); + const struct zmk_behavior_binding *rgb_underglow_get_bindings(uint8_t layer); uint8_t rgb_underglow_top_layer_with_state(uint32_t state_to_test); diff --git a/app/src/rgb_underglow.c b/app/src/rgb_underglow.c index 4b21c71c..440ac399 100644 --- a/app/src/rgb_underglow.c +++ b/app/src/rgb_underglow.c @@ -61,6 +61,7 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #if DT_HAS_COMPAT_STATUS_OKAY(zmk_underglow_layer) && IS_ENABLED(CONFIG_EXPERIMENTAL_RGB_LAYER) #define UNDERGLOW_LAYER_ENABLED 1 +static void zmk_rgb_underglow_set_layer(uint8_t layer); #endif #define HUE_MAX 360 @@ -210,6 +211,23 @@ static void zmk_rgb_underglow_effect_swirl(void) { state.animation_step = state.animation_step % HUE_MAX; } +static void zmk_rgb_underglow_effect_layer(void) { + bool active = false; + for (int i = 0; i < STRIP_NUM_PIXELS; i++) { + pixels[i].r -= state.animation_speed < pixels[i].r ? state.animation_speed : pixels[i].r; + pixels[i].g -= state.animation_speed < pixels[i].g ? state.animation_speed : pixels[i].g; + pixels[i].b -= state.animation_speed < pixels[i].b ? state.animation_speed : pixels[i].b; + if (pixels[i].r || pixels[i].g || pixels[i].b) { + active = true; + } + } + state.animation_step += state.animation_speed; + + if (state.animation_step > 255 || !active) { + zmk_rgb_underglow_transient_off(); + } +} + static int zmk_led_generate_status(void); static void zmk_led_write_pixels(void) { @@ -448,70 +466,6 @@ static inline struct led_rgb hue_sat(int hue, int sat) { return hsb_to_rgb(hsb_scale_min_max(hsb)); } -#if IS_ENABLED(UNDERGLOW_LAYER_ENABLED) - -static struct led_rgb hex_to_rgb(uint8_t r, uint8_t g, uint8_t b) { - struct zmk_led_hsb hsb = state.color; - return (struct led_rgb){ - r : (hsb.b * (r)) / 0xff, - g : (hsb.b * (g)) / 0xff, - b : (hsb.b * (b)) / 0xff - }; -} - -static int zmk_rgb_underglow_apply_rgbmap(struct zmk_behavior_binding *bindings, - size_t rgbmap_len) { - int rc = 0; - for (int i = 0; i < STRIP_NUM_PIXELS; i++) { - uint8_t midx = rgb_pixel_lookup(i); - if (midx >= ZMK_KEYMAP_LEN) { - LOG_DBG("out of range"); - } else { - const struct device *dev = zmk_behavior_get_binding(bindings[midx].behavior_dev); - - if (dev == NULL) { - continue; - } - - const struct behavior_driver_api *api = (const struct behavior_driver_api *)dev->api; - - if (api->binding_pressed == NULL) { - continue; - } - struct zmk_behavior_binding_event event = {.position = midx, - .timestamp = k_uptime_get()}; - - int color = - api->binding_pressed((const struct zmk_behavior_binding *)&bindings[midx], event); - - if (color > 0) { - pixels[i] = - hex_to_rgb((color & 0xFF0000) >> 16, (color & 0xFF00) >> 8, color & 0xFF); - rc = 1; - } else { - pixels[i] = (struct led_rgb){r : 0, g : 0, b : 0}; - } - } - } - return rc; -} - -static void zmk_rgb_underglow_set_layer(uint8_t layer) { - if (!state.layer_enabled) - return; - - const struct zmk_behavior_binding *rgbmap = rgb_underglow_get_bindings(layer); - if (rgbmap != NULL && zmk_rgb_underglow_apply_rgbmap(rgbmap, ZMK_KEYMAP_LEN)) { - if (!state.on) - zmk_rgb_underglow_transient_on(); - zmk_led_write_pixels(); - } else { - if (state.on) - zmk_rgb_underglow_transient_off(); - } -} -#endif /* IS_ENABLED(UNDERGLOW_LAYER_ENABLED) */ - static void zmk_rgb_underglow_tick(struct k_work *work) { switch (state.current_effect) { case UNDERGLOW_EFFECT_SOLID: @@ -526,6 +480,11 @@ static void zmk_rgb_underglow_tick(struct k_work *work) { case UNDERGLOW_EFFECT_SWIRL: zmk_rgb_underglow_effect_swirl(); break; +#if IS_ENABLED(UNDERGLOW_LAYER_ENABLED) + case UNDERGLOW_EFFECT_LAYER_INDICATORS: + zmk_rgb_underglow_effect_layer(); + break; +#endif } zmk_led_write_pixels(); @@ -534,7 +493,7 @@ static void zmk_rgb_underglow_tick(struct k_work *work) { K_WORK_DEFINE(underglow_tick_work, zmk_rgb_underglow_tick); static void zmk_rgb_underglow_tick_handler(struct k_timer *timer) { - if (!state.on || state.layer_enabled) { + if (!state.on) { return; } @@ -547,7 +506,6 @@ K_TIMER_DEFINE(underglow_tick, zmk_rgb_underglow_tick_handler, NULL); static int rgb_settings_set(const char *name, size_t len, settings_read_cb read_cb, void *cb_arg) { const char *next; int rc; - if (settings_name_steq(name, "state", &next) && !next) { if (len != sizeof(state)) { return -EINVAL; @@ -583,7 +541,6 @@ static struct k_work_delayable underglow_save_work; static int zmk_rgb_underglow_init(void) { led_strip = DEVICE_DT_GET(STRIP_CHOSEN); - #if IS_ENABLED(CONFIG_ZMK_RGB_UNDERGLOW_EXT_POWER) if (!device_is_ready(ext_power)) { LOG_ERR("External power device \"%s\" is not ready", ext_power->name); @@ -752,6 +709,77 @@ int zmk_rgb_underglow_toggle(void) { return state.on ? zmk_rgb_underglow_off() : zmk_rgb_underglow_on(); } +#if IS_ENABLED(UNDERGLOW_LAYER_ENABLED) + +static struct led_rgb hex_to_rgb(uint8_t r, uint8_t g, uint8_t b) { + struct zmk_led_hsb hsb = state.color; + return (struct led_rgb){ + r : (hsb.b * (r)) / 0xff, + g : (hsb.b * (g)) / 0xff, + b : (hsb.b * (b)) / 0xff + }; +} + +static int zmk_rgb_underglow_apply_rgbmap(const struct zmk_behavior_binding *bindings, + size_t rgbmap_len) { + int rc = 0; + for (int i = 0; i < STRIP_NUM_PIXELS; i++) { + uint8_t midx = rgb_pixel_lookup(i); + if (midx >= ZMK_KEYMAP_LEN) { + LOG_DBG("out of range"); + } else { + const struct device *dev = zmk_behavior_get_binding(bindings[midx].behavior_dev); + + if (dev == NULL) { + continue; + } + + const struct behavior_driver_api *api = (const struct behavior_driver_api *)dev->api; + + if (api->binding_pressed == NULL) { + continue; + } + struct zmk_behavior_binding_event event = {.position = midx, + .timestamp = k_uptime_get()}; + + int color = api->binding_pressed((struct zmk_behavior_binding *)&bindings[midx], event); + + if (color > 0) { + pixels[i] = + hex_to_rgb((color & 0xFF0000) >> 16, (color & 0xFF00) >> 8, color & 0xFF); + rc = 1; + } else { + pixels[i] = (struct led_rgb){r : 0, g : 0, b : 0}; + } + } + } + return rc; +} + +static void zmk_rgb_underglow_set_layer(uint8_t layer) { + LOG_DBG("state.layer: %d state.on: %d", state.layer_enabled, state.on); + if (!state.layer_enabled) + return; + + const struct zmk_behavior_binding *rgbmap = rgb_underglow_get_bindings(layer); + if (rgbmap != NULL && zmk_rgb_underglow_apply_rgbmap(rgbmap, ZMK_KEYMAP_LEN)) { + if (!state.on) + zmk_rgb_underglow_transient_on(); + k_timer_stop(&underglow_tick); + state.animation_step = 0; + int fade_delay = zmk_rgbmap_fade_delay(layer); + if (fade_delay >= 0) { + k_timer_start(&underglow_tick, K_SECONDS(fade_delay), K_MSEC(50)); + } + LOG_DBG("write pixels"); + zmk_led_write_pixels(); + } else { + if (state.on) + zmk_rgb_underglow_transient_off(); + } +} +#endif /* IS_ENABLED(UNDERGLOW_LAYER_ENABLED) */ + static void zmk_led_write_pixels_work(struct k_work *work); static void zmk_rgb_underglow_status_update(struct k_timer *timer); diff --git a/app/src/rgb_underglow_layer.c b/app/src/rgb_underglow_layer.c index 6cd0ceb9..d3fbbc6e 100644 --- a/app/src/rgb_underglow_layer.c +++ b/app/src/rgb_underglow_layer.c @@ -24,6 +24,7 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #define UNDERGLOW_LAYER_ENABLED #define LAYER_ID(node) DT_PROP(node, layer_id) +#define FADE_DELAY(node) DT_PROP(node, fade_delay) #define TRANSFORMED_RGB_LAYER(node) \ {COND_CODE_1(DT_NODE_HAS_PROP(node, bindings), \ @@ -39,6 +40,7 @@ RGBMAP_VAR(zmk_rgbmap, COND_CODE_1(IS_ENABLED(CONFIG_ZMK_KEYMAP_SETTINGS_STORAGE const int pixel_lookup_table[] = DT_INST_PROP(0, pixel_lookup); static int zmk_rgbmap_ids[ZMK_RGBMAP_LAYERS_LEN] = {DT_INST_FOREACH_CHILD_SEP(0, LAYER_ID, (, ))}; +static int zmk_rgbmap_fds[ZMK_RGBMAP_LAYERS_LEN] = {DT_INST_FOREACH_CHILD_SEP(0, FADE_DELAY, (, ))}; const int rgb_pixel_lookup(int idx) { return pixel_lookup_table[idx]; }; @@ -51,6 +53,8 @@ const int zmk_rgbmap_id(uint8_t layer) { return -1; } +const int zmk_rgbmap_fade_delay(uint8_t layer) { return zmk_rgbmap_fds[zmk_rgbmap_id(layer)]; } + const struct zmk_behavior_binding *rgb_underglow_get_bindings(uint8_t layer) { int rgblayer = zmk_rgbmap_id(layer); if (rgblayer == -1) {