diff --git a/README.md b/README.md index aeb88b8..057215a 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Modular RGB effects for ZMK keyboards. Provides per-key per-layer RGB indicators and a framework for adding custom effects without forking ZMK. -Requires: [ZMK fork](https://github.com/afiqzudinhadi/zmk) branch `rgb-modular-v1.0` (modular effect registration API). +Requires: [ZMK fork](https://github.com/afiqzudinhadi/zmk) branch `rgb-modular-v1.1` (modular effect registration API). ## Setup @@ -16,11 +16,11 @@ manifest: projects: - name: zmk remote: afiqzudinhadi - revision: rgb-modular-v1.0 + revision: rgb-modular-v1.1 import: app/west.yml - name: zmk-rgb-effects remote: afiqzudinhadi - revision: v1.0.0 + revision: v1.1.1 ``` Enable in your `.conf`: @@ -55,6 +55,7 @@ Define per-key colors in your keymap/overlay using devicetree: layer1 { layer-id = <1>; + fade-delay = <15>; bindings = < &ugc RGB_BLUE &ugc RGB_BLUE &ugc RGB_OFF ... >; @@ -67,6 +68,7 @@ Define per-key colors in your keymap/overlay using devicetree: - `pixel-lookup` — maps LED chain index to key position - `layer-id` — which keymap layer this color map applies to +- `fade-delay` — seconds before reverting to animated effect (-1 = never) - `bindings` — color per key using behaviors below ### Behaviors @@ -94,7 +96,7 @@ static void my_render(struct zmk_rgb_effect_ctx *ctx) { } ZMK_RGB_EFFECT_DEFINE(my_effect, "My Effect", - my_render, 0, NULL, NULL); + my_render, 0, NULL, NULL, NULL, NULL); ``` Add to `CMakeLists.txt`: @@ -150,7 +152,12 @@ Add to your `west.yml`: revision: main ``` -Requires the ZMK fork with the modular effect API (`rgb-modular-v1.0` or later). No dependency on this module. +Requires the ZMK fork with the modular effect API. No dependency on this module — the linker section collects effect registrations across all modules. + +- `rgb-modular-v1.0` — basic effect API (`ZMK_RGB_EFFECT_DEFINE` with 6 params: render, flags, on_select, on_deselect). Sufficient for simple animated or static effects. +- `rgb-modular-v1.1` — extended API (adds `on_idle`, `is_active` callbacks, `ZMK_RGB_EFFECT_PERSISTENT` flag, `zmk_rgb_request_refresh_wakeup()`, `zmk_rgb_set_tick_delay()`, `zmk_rgb_is_on()`). Needed for effects that persist across RGB toggle/idle or use fade-delay. + +Start with `rgb-modular-v1.0` if you want to explore the API from scratch. ### Effect flags @@ -158,6 +165,7 @@ Requires the ZMK fork with the modular effect API (`rgb-modular-v1.0` or later). |------|-------------| | `0` | Animated — core runs render at 25ms tick | | `ZMK_RGB_EFFECT_STATIC` | No periodic tick — render called on demand via `zmk_rgb_request_refresh()` | +| `ZMK_RGB_EFFECT_PERSISTENT` | Survives RGB toggle and idle sleep | ### Effect callbacks @@ -166,6 +174,8 @@ Requires the ZMK fork with the modular effect API (`rgb-modular-v1.0` or later). | `render` | Each tick (animated) or on refresh (static) | | `on_select` | Effect becomes active | | `on_deselect` | User cycles to different effect | +| `on_idle(bool awake)` | Keyboard sleeps/wakes (persistent effects only) | +| `is_active()` | Queried for ext power gating — return `true` to keep LEDs powered | ### Render context @@ -183,6 +193,9 @@ Requires the ZMK fork with the modular effect API (`rgb-modular-v1.0` or later). - `zmk_rgb_hsb_scale_min_max(hsb)` — scale brightness to configured min/max - `zmk_rgb_hsb_scale_zero_max(hsb)` — scale brightness from 0 to max - `zmk_rgb_request_refresh()` — trigger re-render for static effects +- `zmk_rgb_request_refresh_wakeup(bool)` — refresh with conditional wake from sleep +- `zmk_rgb_set_tick_delay(int seconds)` — delayed tick restart (for fade effects) +- `zmk_rgb_is_on()` — query RGB on/off state ## Versioning diff --git a/src/effects/layer_effect.c b/src/effects/layer_effect.c index e43fde3..58e8eaa 100644 --- a/src/effects/layer_effect.c +++ b/src/effects/layer_effect.c @@ -90,11 +90,16 @@ static void layer_effect_render(struct zmk_rgb_effect_ctx *ctx) { uint8_t layer = rgb_underglow_top_layer(); const struct zmk_behavior_binding *rgbmap = rgb_underglow_get_bindings(layer); if (rgbmap != NULL) { - apply_rgbmap(ctx->pixels, ctx->num_pixels, rgbmap, ZMK_KEYMAP_LEN, - layer, ctx->base_color.b); + if (apply_rgbmap(ctx->pixels, ctx->num_pixels, rgbmap, ZMK_KEYMAP_LEN, + layer, ctx->base_color.b)) { + int fade_delay = zmk_rgbmap_fade_delay(layer); + if (fade_delay >= 0) { + zmk_rgb_set_tick_delay(fade_delay); + } + } } else { - for (int i = 0; i < ctx->num_pixels; i++) { - ctx->pixels[i] = (struct led_rgb){r : 0, g : 0, b : 0}; + if (zmk_rgb_is_on()) { + zmk_rgb_underglow_transient_off(); } } } @@ -107,11 +112,27 @@ static void layer_effect_on_deselect(void) { layer_effect_active = false; } -ZMK_RGB_EFFECT_DEFINE(effect_layer, "Layer Indicators", - layer_effect_render, ZMK_RGB_EFFECT_STATIC, - layer_effect_on_select, layer_effect_on_deselect); +static void layer_effect_on_idle(bool awake) { + if (awake) { + if (layer_effect_active) { + zmk_rgb_request_refresh_wakeup(true); + } + } else { + zmk_rgb_underglow_transient_off(); + } +} -/* Event listeners — refresh pixels on layer/color change */ +static bool layer_effect_is_active(void) { + return layer_effect_active; +} + +ZMK_RGB_EFFECT_DEFINE(effect_layer, "Layer Indicators", + layer_effect_render, + ZMK_RGB_EFFECT_STATIC | ZMK_RGB_EFFECT_PERSISTENT, + layer_effect_on_select, layer_effect_on_deselect, + layer_effect_on_idle, layer_effect_is_active); + +/* Event listeners */ static int layer_effect_event_listener(const zmk_event_t *eh) { if (!layer_effect_active) { @@ -126,13 +147,17 @@ static int layer_effect_event_listener(const zmk_event_t *eh) { #if !IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL) set_peripheral_layers_state(ev->layers); #endif - zmk_rgb_request_refresh(); + zmk_rgb_request_refresh_wakeup(true); return 0; } #endif if (as_zmk_underglow_color_changed(eh)) { - zmk_rgb_request_refresh(); + const struct zmk_underglow_color_changed *ev = as_zmk_underglow_color_changed(eh); + uint8_t layer = rgb_underglow_top_layer(); + if ((ev->layers & BIT(layer)) == BIT(layer)) { + zmk_rgb_request_refresh_wakeup(ev->wakeup); + } return 0; }