underglow-layer: add fade-delay property

This commit is contained in:
darknao 2025-03-15 14:40:27 +01:00 committed by afiqzudinhadi
parent 62dc6d8ec5
commit 01f8f363f8
4 changed files with 105 additions and 67 deletions

View file

@ -18,3 +18,7 @@ child-binding:
layer-id:
type: int
required: true
fade-delay:
type: int
required: false
default: -1

View file

@ -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);

View file

@ -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);

View file

@ -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) {