refactor: modular RGB effect registration API
Replace hardcoded effect enum/switch-case with STRUCT_SECTION_ITERABLE registration. Effects register via ZMK_RGB_EFFECT_DEFINE macro. Core API (rgb_effect.h): - zmk_rgb_effect_ctx passed to render callbacks - zmk_rgb_request_refresh() for static effects - ZMK_RGB_EFFECT_STATIC flag skips animation timer - on_select/on_deselect lifecycle hooks - zmk_rgb_hsb_to_rgb/scale helpers now public Built-in effects (solid/breathe/spectrum/swirl) use same API. Per-key layer RGB, behaviors, DTS bindings extracted to external zmk-rgb-effects module.
This commit is contained in:
parent
d94d1597c1
commit
8f3c4d3929
16 changed files with 209 additions and 656 deletions
|
|
@ -11,6 +11,9 @@ project(zmk)
|
|||
|
||||
zephyr_linker_sources(SECTIONS include/linker/zmk-behaviors.ld)
|
||||
zephyr_linker_sources(RODATA include/linker/zmk-events.ld)
|
||||
if(CONFIG_ZMK_RGB_UNDERGLOW)
|
||||
zephyr_linker_sources(SECTIONS include/linker/zmk-rgb-effects.ld)
|
||||
endif()
|
||||
|
||||
if(CONFIG_ZMK_BEHAVIOR_LOCAL_IDS)
|
||||
zephyr_linker_sources(DATA_SECTIONS include/linker/zmk-behavior-local-id-map.ld)
|
||||
|
|
@ -84,10 +87,6 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
|
|||
endif()
|
||||
|
||||
target_sources_ifdef(CONFIG_ZMK_RGB_UNDERGLOW app PRIVATE src/behaviors/behavior_rgb_underglow.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_RGB_UNDERGLOW app PRIVATE src/behaviors/behavior_underglow_color.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_RGB_UNDERGLOW app PRIVATE src/behaviors/behavior_underglow_indicators.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_RGB_UNDERGLOW app PRIVATE src/behaviors/behavior_underglow_battery.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_RGB_UNDERGLOW app PRIVATE src/events/underglow_color_changed.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_BACKLIGHT app PRIVATE src/behaviors/behavior_backlight.c)
|
||||
|
||||
target_sources_ifdef(CONFIG_ZMK_BATTERY_REPORTING app PRIVATE src/events/battery_state_changed.c)
|
||||
|
|
@ -109,7 +108,6 @@ add_subdirectory_ifdef(CONFIG_ZMK_SPLIT src/split)
|
|||
target_sources_ifdef(CONFIG_USB_DEVICE_STACK app PRIVATE src/usb.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_USB app PRIVATE src/usb_hid.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_RGB_UNDERGLOW app PRIVATE src/rgb_underglow.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_RGB_UNDERGLOW app PRIVATE src/rgb_underglow_layer.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_BACKLIGHT app PRIVATE src/backlight.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_LOW_PRIORITY_WORK_QUEUE app PRIVATE src/workqueue.c)
|
||||
target_sources(app PRIVATE src/main.c)
|
||||
|
|
|
|||
|
|
@ -322,8 +322,8 @@ config ZMK_RGB_UNDERGLOW_SPD_START
|
|||
range 1 5
|
||||
|
||||
config ZMK_RGB_UNDERGLOW_EFF_START
|
||||
int "RGB underglow start effect int value related to the effect enum list"
|
||||
range 0 4
|
||||
int "RGB underglow start effect index (clamped to available effects at runtime)"
|
||||
range 0 15
|
||||
|
||||
config ZMK_RGB_UNDERGLOW_ON_START
|
||||
bool "RGB underglow starts on by default"
|
||||
|
|
@ -335,10 +335,6 @@ config ZMK_RGB_UNDERGLOW_AUTO_OFF_USB
|
|||
bool "Turn off RGB underglow when USB is disconnected"
|
||||
depends on USB_DEVICE_STACK
|
||||
|
||||
config EXPERIMENTAL_RGB_LAYER
|
||||
bool "Experimental per-key per-layer RGB underglow"
|
||||
default n
|
||||
|
||||
endif # ZMK_RGB_UNDERGLOW
|
||||
|
||||
menuconfig ZMK_BACKLIGHT
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
# Copyright (c) 2024, The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
description: Set underglow color based on battery level
|
||||
|
||||
compatible: "zmk,behavior-underglow-battery"
|
||||
|
||||
include: two_param.yaml
|
||||
|
||||
properties:
|
||||
threshold:
|
||||
type: int
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
# Copyright (c) 2024, The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
description: Set underglow to specified color
|
||||
|
||||
compatible: "zmk,behavior-underglow-color"
|
||||
|
||||
include: one_param.yaml
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
# Copyright (c) 2024, The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
description: Set underglow for num lock indicator
|
||||
|
||||
compatible: "zmk,behavior-underglow-indicators"
|
||||
|
||||
include: two_param.yaml
|
||||
|
||||
properties:
|
||||
indicator:
|
||||
type: int
|
||||
default: 0
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
description: |
|
||||
Allows defining a rgbmap composed of multiple layers
|
||||
|
||||
compatible: "zmk,underglow-layer"
|
||||
|
||||
properties:
|
||||
pixel-lookup:
|
||||
type: array
|
||||
required: true
|
||||
|
||||
child-binding:
|
||||
description: "A layer to be used in a rgbmap"
|
||||
|
||||
properties:
|
||||
bindings:
|
||||
type: phandle-array
|
||||
required: true
|
||||
layer-id:
|
||||
type: int
|
||||
required: true
|
||||
fade-delay:
|
||||
type: int
|
||||
required: false
|
||||
default: -1
|
||||
9
app/include/linker/zmk-rgb-effects.ld
Normal file
9
app/include/linker/zmk-rgb-effects.ld
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
/*
|
||||
* Copyright (c) 2024 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <zephyr/linker/linker-defs.h>
|
||||
|
||||
ITERABLE_SECTION_ROM(zmk_rgb_effect, 4)
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2024 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <zmk/event_manager.h>
|
||||
|
||||
struct zmk_underglow_color_changed {
|
||||
uint32_t layers;
|
||||
bool wakeup;
|
||||
};
|
||||
|
||||
ZMK_EVENT_DECLARE(zmk_underglow_color_changed);
|
||||
47
app/include/zmk/rgb_effect.h
Normal file
47
app/include/zmk/rgb_effect.h
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright (c) 2024 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <zephyr/drivers/led_strip.h>
|
||||
#include <zmk/rgb_underglow.h>
|
||||
|
||||
struct zmk_rgb_effect_ctx {
|
||||
struct led_rgb *pixels;
|
||||
uint16_t num_pixels;
|
||||
struct zmk_led_hsb base_color;
|
||||
uint16_t *animation_step;
|
||||
uint8_t animation_speed;
|
||||
};
|
||||
|
||||
typedef void (*zmk_rgb_effect_render_fn)(struct zmk_rgb_effect_ctx *ctx);
|
||||
typedef void (*zmk_rgb_effect_event_fn)(void);
|
||||
|
||||
#define ZMK_RGB_EFFECT_STATIC BIT(0)
|
||||
|
||||
struct zmk_rgb_effect {
|
||||
const char *name;
|
||||
zmk_rgb_effect_render_fn render;
|
||||
zmk_rgb_effect_event_fn on_select;
|
||||
zmk_rgb_effect_event_fn on_deselect;
|
||||
uint8_t flags;
|
||||
};
|
||||
|
||||
#define ZMK_RGB_EFFECT_DEFINE(_sym, _name_str, _render, _flags, _on_select, _on_deselect) \
|
||||
STRUCT_SECTION_ITERABLE(zmk_rgb_effect, _sym) = { \
|
||||
.name = _name_str, \
|
||||
.render = _render, \
|
||||
.flags = _flags, \
|
||||
.on_select = _on_select, \
|
||||
.on_deselect = _on_deselect, \
|
||||
}
|
||||
|
||||
void zmk_rgb_request_refresh(void);
|
||||
int zmk_rgb_effect_get_count(void);
|
||||
|
||||
struct led_rgb zmk_rgb_hsb_to_rgb(struct zmk_led_hsb hsb);
|
||||
struct zmk_led_hsb zmk_rgb_hsb_scale_min_max(struct zmk_led_hsb hsb);
|
||||
struct zmk_led_hsb zmk_rgb_hsb_scale_zero_max(struct zmk_led_hsb hsb);
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <zmk/keymap.h>
|
||||
|
||||
#define ZMK_RGB_CHILD_LEN_PLUS_ONE(node) 1 +
|
||||
|
||||
#define ZMK_RGBMAP_LAYERS_LEN \
|
||||
(DT_FOREACH_CHILD(DT_INST(0, zmk_underglow_layer), ZMK_RGB_CHILD_LEN_PLUS_ONE) 0)
|
||||
|
||||
#define ZMK_RGBMAP_EXTRACT_BINDING(idx, drv_inst) \
|
||||
{ \
|
||||
.behavior_dev = DEVICE_DT_NAME(DT_PHANDLE_BY_IDX(drv_inst, bindings, idx)), \
|
||||
.param1 = COND_CODE_0(DT_PHA_HAS_CELL_AT_IDX(drv_inst, bindings, idx, param1), (0), \
|
||||
(DT_PHA_BY_IDX(drv_inst, bindings, idx, param1))), \
|
||||
.param2 = COND_CODE_0(DT_PHA_HAS_CELL_AT_IDX(drv_inst, bindings, idx, param2), (0), \
|
||||
(DT_PHA_BY_IDX(drv_inst, bindings, idx, param2))), \
|
||||
}
|
||||
|
||||
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);
|
||||
uint8_t rgb_underglow_top_layer(void);
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2024 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT zmk_behavior_underglow_battery
|
||||
|
||||
// Dependencies
|
||||
#include <zephyr/device.h>
|
||||
#include <drivers/behavior.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zmk/battery.h>
|
||||
#include <zmk/event_manager.h>
|
||||
#include <zmk/events/battery_state_changed.h>
|
||||
#include <zmk/events/underglow_color_changed.h>
|
||||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
||||
|
||||
struct underglow_battery_data {
|
||||
uint32_t layers;
|
||||
};
|
||||
|
||||
struct underglow_battery_config {
|
||||
int threshold;
|
||||
};
|
||||
|
||||
static struct underglow_battery_data underglow_battery_data = {.layers = 0};
|
||||
|
||||
static int underglow_battery_init(const struct device *dev) { return 0; };
|
||||
|
||||
static int underglow_battery_process(struct zmk_behavior_binding *binding,
|
||||
struct zmk_behavior_binding_event event) {
|
||||
const struct device *dev = zmk_behavior_get_binding(binding->behavior_dev);
|
||||
const struct underglow_battery_config *config = dev->config;
|
||||
struct underglow_battery_data *data = dev->data;
|
||||
data->layers |= BIT(event.layer);
|
||||
int bat = zmk_battery_state_of_charge();
|
||||
|
||||
if (bat >= config->threshold)
|
||||
return binding->param2;
|
||||
else
|
||||
return binding->param1;
|
||||
}
|
||||
|
||||
static const struct behavior_driver_api underglow_battery_driver_api = {
|
||||
.binding_pressed = underglow_battery_process,
|
||||
.locality = BEHAVIOR_LOCALITY_GLOBAL,
|
||||
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
|
||||
.get_parameter_metadata = zmk_behavior_get_empty_param_metadata,
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
|
||||
};
|
||||
|
||||
static int underglow_battery_listener(const zmk_event_t *eh);
|
||||
|
||||
ZMK_LISTENER(behavior_underglow_battery, underglow_battery_listener);
|
||||
ZMK_SUBSCRIPTION(behavior_underglow_battery, zmk_battery_state_changed);
|
||||
|
||||
static int underglow_battery_listener(const zmk_event_t *eh) {
|
||||
raise_zmk_underglow_color_changed((struct zmk_underglow_color_changed){
|
||||
.layers = underglow_battery_data.layers, .wakeup = false});
|
||||
|
||||
return ZMK_EV_EVENT_BUBBLE;
|
||||
}
|
||||
|
||||
#define KP_INST(n) \
|
||||
static struct underglow_battery_config underglow_battery_config_##n = { \
|
||||
.threshold = DT_INST_PROP(n, threshold)}; \
|
||||
BEHAVIOR_DT_INST_DEFINE(n, underglow_battery_init, NULL, &underglow_battery_data, \
|
||||
&underglow_battery_config_##n, POST_KERNEL, \
|
||||
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &underglow_battery_driver_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(KP_INST)
|
||||
|
||||
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2024 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT zmk_behavior_underglow_color
|
||||
|
||||
// Dependencies
|
||||
#include <zephyr/device.h>
|
||||
#include <drivers/behavior.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
||||
|
||||
// Initialization Function
|
||||
static int underglow_color_init(const struct device *dev) { return 0; };
|
||||
|
||||
static int underglow_color_process(struct zmk_behavior_binding *binding,
|
||||
struct zmk_behavior_binding_event event) {
|
||||
return binding->param1;
|
||||
}
|
||||
|
||||
// API Structure
|
||||
static const struct behavior_driver_api underglow_color_driver_api = {
|
||||
.binding_pressed = underglow_color_process,
|
||||
.locality = BEHAVIOR_LOCALITY_GLOBAL,
|
||||
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
|
||||
.get_parameter_metadata = zmk_behavior_get_empty_param_metadata,
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
|
||||
|
||||
};
|
||||
|
||||
BEHAVIOR_DT_INST_DEFINE(0, underglow_color_init, NULL, NULL, NULL, POST_KERNEL,
|
||||
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &underglow_color_driver_api);
|
||||
|
||||
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2024 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT zmk_behavior_underglow_indicators
|
||||
|
||||
// Dependencies
|
||||
#include <zephyr/device.h>
|
||||
#include <drivers/behavior.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zmk/hid_indicators.h>
|
||||
#include <zmk/event_manager.h>
|
||||
#include <zmk/events/hid_indicators_changed.h>
|
||||
#include <zmk/events/underglow_color_changed.h>
|
||||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
||||
|
||||
struct underglow_indicators_data {
|
||||
zmk_hid_indicators_t indicators;
|
||||
uint32_t layers;
|
||||
};
|
||||
|
||||
struct underglow_indicators_config {
|
||||
int indicator;
|
||||
};
|
||||
|
||||
static int underglow_indicators_init(const struct device *dev) { return 0; };
|
||||
|
||||
static int underglow_indicators_process(struct zmk_behavior_binding *binding,
|
||||
struct zmk_behavior_binding_event event) {
|
||||
const struct device *dev = zmk_behavior_get_binding(binding->behavior_dev);
|
||||
struct underglow_indicators_data *data = dev->data;
|
||||
const struct underglow_indicators_config *config = dev->config;
|
||||
data->layers |= BIT(event.layer);
|
||||
|
||||
if (data->indicators & BIT(config->indicator))
|
||||
return binding->param2;
|
||||
else
|
||||
return binding->param1;
|
||||
}
|
||||
|
||||
static const struct behavior_driver_api underglow_indicators_driver_api = {
|
||||
.binding_pressed = underglow_indicators_process,
|
||||
.locality = BEHAVIOR_LOCALITY_GLOBAL,
|
||||
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
|
||||
.get_parameter_metadata = zmk_behavior_get_empty_param_metadata,
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
|
||||
};
|
||||
|
||||
static int underglow_indicators_listener(const zmk_event_t *eh);
|
||||
|
||||
ZMK_LISTENER(behavior_underglow_indicators, underglow_indicators_listener);
|
||||
ZMK_SUBSCRIPTION(behavior_underglow_indicators, zmk_hid_indicators_changed);
|
||||
|
||||
static struct underglow_indicators_data underglow_indicators_data = {.indicators = 0, .layers = 0};
|
||||
|
||||
static int underglow_indicators_listener(const zmk_event_t *eh) {
|
||||
const struct zmk_hid_indicators_changed *ev = as_zmk_hid_indicators_changed(eh);
|
||||
underglow_indicators_data.indicators = ev->indicators;
|
||||
raise_zmk_underglow_color_changed((struct zmk_underglow_color_changed){
|
||||
.layers = underglow_indicators_data.layers, .wakeup = true});
|
||||
|
||||
return ZMK_EV_EVENT_BUBBLE;
|
||||
}
|
||||
|
||||
#define KP_INST(n) \
|
||||
static struct underglow_indicators_config underglow_indicators_config_##n = { \
|
||||
.indicator = DT_INST_PROP(n, indicator)}; \
|
||||
BEHAVIOR_DT_INST_DEFINE(n, underglow_indicators_init, NULL, &underglow_indicators_data, \
|
||||
&underglow_indicators_config_##n, POST_KERNEL, \
|
||||
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
|
||||
&underglow_indicators_driver_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(KP_INST)
|
||||
|
||||
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zmk/events/underglow_color_changed.h>
|
||||
|
||||
ZMK_EVENT_IMPL(zmk_underglow_color_changed);
|
||||
|
|
@ -29,25 +29,19 @@
|
|||
#include <drivers/behavior.h>
|
||||
|
||||
#include <zmk/rgb_underglow.h>
|
||||
#include <zmk/rgb_underglow_layer.h>
|
||||
#include <zmk/rgb_effect.h>
|
||||
|
||||
#include <zmk/activity.h>
|
||||
#include <zmk/event_manager.h>
|
||||
#include <zmk/events/activity_state_changed.h>
|
||||
#include <zmk/events/usb_conn_state_changed.h>
|
||||
#include <zmk/events/underglow_color_changed.h>
|
||||
|
||||
#include <zmk/workqueue.h>
|
||||
#include <zmk/events/split_peripheral_layer_changed.h>
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING)
|
||||
#include <zmk/split/central.h>
|
||||
#endif
|
||||
|
||||
#if !IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
|
||||
#include <zmk/split/bluetooth/peripheral_layers.h>
|
||||
#endif
|
||||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
#if !DT_HAS_CHOSEN(zmk_underglow)
|
||||
|
|
@ -59,11 +53,6 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
|||
#define STRIP_CHOSEN DT_CHOSEN(zmk_underglow)
|
||||
#define STRIP_NUM_PIXELS DT_PROP(STRIP_CHOSEN, chain_length)
|
||||
|
||||
#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, bool wakeup);
|
||||
#endif
|
||||
|
||||
#define HUE_MAX 360
|
||||
#define SAT_MAX 100
|
||||
#define BRT_MAX 100
|
||||
|
|
@ -71,17 +60,6 @@ static void zmk_rgb_underglow_set_layer(uint8_t layer, bool wakeup);
|
|||
BUILD_ASSERT(CONFIG_ZMK_RGB_UNDERGLOW_BRT_MIN <= CONFIG_ZMK_RGB_UNDERGLOW_BRT_MAX,
|
||||
"ERROR: RGB underglow maximum brightness is less than minimum brightness");
|
||||
|
||||
enum rgb_underglow_effect {
|
||||
UNDERGLOW_EFFECT_SOLID,
|
||||
UNDERGLOW_EFFECT_BREATHE,
|
||||
UNDERGLOW_EFFECT_SPECTRUM,
|
||||
UNDERGLOW_EFFECT_SWIRL,
|
||||
#if IS_ENABLED(UNDERGLOW_LAYER_ENABLED)
|
||||
UNDERGLOW_EFFECT_LAYER_INDICATORS,
|
||||
#endif
|
||||
UNDERGLOW_EFFECT_NUMBER // Used to track number of underglow effects
|
||||
};
|
||||
|
||||
struct rgb_underglow_state {
|
||||
struct zmk_led_hsb color;
|
||||
uint8_t animation_speed;
|
||||
|
|
@ -89,7 +67,6 @@ struct rgb_underglow_state {
|
|||
uint16_t animation_step;
|
||||
bool on;
|
||||
bool status_active;
|
||||
bool layer_enabled;
|
||||
uint16_t status_animation_step;
|
||||
};
|
||||
|
||||
|
|
@ -106,18 +83,20 @@ static const struct device *const ext_power = DEVICE_DT_GET(DT_INST(0, zmk_ext_p
|
|||
|
||||
void zmk_rgb_set_ext_power(void);
|
||||
|
||||
static struct zmk_led_hsb hsb_scale_min_max(struct zmk_led_hsb hsb) {
|
||||
/* --- Public utility functions for effects --- */
|
||||
|
||||
struct zmk_led_hsb zmk_rgb_hsb_scale_min_max(struct zmk_led_hsb hsb) {
|
||||
hsb.b = CONFIG_ZMK_RGB_UNDERGLOW_BRT_MIN +
|
||||
(CONFIG_ZMK_RGB_UNDERGLOW_BRT_MAX - CONFIG_ZMK_RGB_UNDERGLOW_BRT_MIN) * hsb.b / BRT_MAX;
|
||||
return hsb;
|
||||
}
|
||||
|
||||
static struct zmk_led_hsb hsb_scale_zero_max(struct zmk_led_hsb hsb) {
|
||||
struct zmk_led_hsb zmk_rgb_hsb_scale_zero_max(struct zmk_led_hsb hsb) {
|
||||
hsb.b = hsb.b * CONFIG_ZMK_RGB_UNDERGLOW_BRT_MAX / BRT_MAX;
|
||||
return hsb;
|
||||
}
|
||||
|
||||
static struct led_rgb hsb_to_rgb(struct zmk_led_hsb hsb) {
|
||||
struct led_rgb zmk_rgb_hsb_to_rgb(struct zmk_led_hsb hsb) {
|
||||
float r = 0, g = 0, b = 0;
|
||||
|
||||
uint8_t i = hsb.h / 60;
|
||||
|
|
@ -166,56 +145,78 @@ static struct led_rgb hsb_to_rgb(struct zmk_led_hsb hsb) {
|
|||
return rgb;
|
||||
}
|
||||
|
||||
static void zmk_rgb_underglow_effect_solid(void) {
|
||||
for (int i = 0; i < STRIP_NUM_PIXELS; i++) {
|
||||
pixels[i] = hsb_to_rgb(hsb_scale_min_max(state.color));
|
||||
/* --- Built-in effects --- */
|
||||
|
||||
static void effect_solid_render(struct zmk_rgb_effect_ctx *ctx) {
|
||||
for (int i = 0; i < ctx->num_pixels; i++) {
|
||||
ctx->pixels[i] = zmk_rgb_hsb_to_rgb(zmk_rgb_hsb_scale_min_max(ctx->base_color));
|
||||
}
|
||||
}
|
||||
|
||||
static void zmk_rgb_underglow_effect_breathe(void) {
|
||||
for (int i = 0; i < STRIP_NUM_PIXELS; i++) {
|
||||
struct zmk_led_hsb hsb = state.color;
|
||||
hsb.b = abs(state.animation_step - 1200) / 12;
|
||||
static void effect_breathe_render(struct zmk_rgb_effect_ctx *ctx) {
|
||||
for (int i = 0; i < ctx->num_pixels; i++) {
|
||||
struct zmk_led_hsb hsb = ctx->base_color;
|
||||
hsb.b = abs(*ctx->animation_step - 1200) / 12;
|
||||
|
||||
pixels[i] = hsb_to_rgb(hsb_scale_zero_max(hsb));
|
||||
ctx->pixels[i] = zmk_rgb_hsb_to_rgb(zmk_rgb_hsb_scale_zero_max(hsb));
|
||||
}
|
||||
|
||||
state.animation_step += state.animation_speed * 10;
|
||||
*ctx->animation_step += ctx->animation_speed * 10;
|
||||
|
||||
if (state.animation_step > 2400) {
|
||||
state.animation_step = 0;
|
||||
if (*ctx->animation_step > 2400) {
|
||||
*ctx->animation_step = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void zmk_rgb_underglow_effect_spectrum(void) {
|
||||
for (int i = 0; i < STRIP_NUM_PIXELS; i++) {
|
||||
struct zmk_led_hsb hsb = state.color;
|
||||
hsb.h = state.animation_step;
|
||||
static void effect_spectrum_render(struct zmk_rgb_effect_ctx *ctx) {
|
||||
for (int i = 0; i < ctx->num_pixels; i++) {
|
||||
struct zmk_led_hsb hsb = ctx->base_color;
|
||||
hsb.h = *ctx->animation_step;
|
||||
|
||||
pixels[i] = hsb_to_rgb(hsb_scale_min_max(hsb));
|
||||
ctx->pixels[i] = zmk_rgb_hsb_to_rgb(zmk_rgb_hsb_scale_min_max(hsb));
|
||||
}
|
||||
|
||||
state.animation_step += state.animation_speed;
|
||||
state.animation_step = state.animation_step % HUE_MAX;
|
||||
*ctx->animation_step += ctx->animation_speed;
|
||||
*ctx->animation_step = *ctx->animation_step % HUE_MAX;
|
||||
}
|
||||
|
||||
static void zmk_rgb_underglow_effect_swirl(void) {
|
||||
for (int i = 0; i < STRIP_NUM_PIXELS; i++) {
|
||||
struct zmk_led_hsb hsb = state.color;
|
||||
hsb.h = (HUE_MAX / STRIP_NUM_PIXELS * i + state.animation_step) % HUE_MAX;
|
||||
static void effect_swirl_render(struct zmk_rgb_effect_ctx *ctx) {
|
||||
for (int i = 0; i < ctx->num_pixels; i++) {
|
||||
struct zmk_led_hsb hsb = ctx->base_color;
|
||||
hsb.h = (HUE_MAX / ctx->num_pixels * i + *ctx->animation_step) % HUE_MAX;
|
||||
|
||||
pixels[i] = hsb_to_rgb(hsb_scale_min_max(hsb));
|
||||
ctx->pixels[i] = zmk_rgb_hsb_to_rgb(zmk_rgb_hsb_scale_min_max(hsb));
|
||||
}
|
||||
|
||||
state.animation_step += state.animation_speed * 2;
|
||||
state.animation_step = state.animation_step % HUE_MAX;
|
||||
*ctx->animation_step += ctx->animation_speed * 2;
|
||||
*ctx->animation_step = *ctx->animation_step % HUE_MAX;
|
||||
}
|
||||
|
||||
static void zmk_rgb_underglow_effect_layer(void) {
|
||||
// Layer indicator colors are static — set by set_layer() on layer change.
|
||||
// No per-frame animation needed.
|
||||
ZMK_RGB_EFFECT_DEFINE(effect_solid, "Solid", effect_solid_render, 0, NULL, NULL);
|
||||
ZMK_RGB_EFFECT_DEFINE(effect_breathe, "Breathe", effect_breathe_render, 0, NULL, NULL);
|
||||
ZMK_RGB_EFFECT_DEFINE(effect_spectrum, "Spectrum", effect_spectrum_render, 0, NULL, NULL);
|
||||
ZMK_RGB_EFFECT_DEFINE(effect_swirl, "Swirl", effect_swirl_render, 0, NULL, NULL);
|
||||
|
||||
/* --- Effect registry helpers --- */
|
||||
|
||||
int zmk_rgb_effect_get_count(void) {
|
||||
int count;
|
||||
STRUCT_SECTION_COUNT(zmk_rgb_effect, &count);
|
||||
return count;
|
||||
}
|
||||
|
||||
static struct zmk_rgb_effect *zmk_rgb_effect_get(int index) {
|
||||
int count = zmk_rgb_effect_get_count();
|
||||
if (index < 0 || index >= count) {
|
||||
return NULL;
|
||||
}
|
||||
struct zmk_rgb_effect *effect;
|
||||
STRUCT_SECTION_GET(zmk_rgb_effect, index, &effect);
|
||||
return effect;
|
||||
}
|
||||
|
||||
/* --- Status indicator overlay (separate from effects) --- */
|
||||
|
||||
static int zmk_led_generate_status(void);
|
||||
|
||||
static void zmk_led_write_pixels(void) {
|
||||
|
|
@ -234,19 +235,16 @@ static void zmk_led_write_pixels(void) {
|
|||
blend = zmk_led_generate_status();
|
||||
}
|
||||
|
||||
// fast path: no status indicators, battery level OK
|
||||
if (blend == 0 && bat0 >= 20) {
|
||||
led_strip_update_rgb(led_strip, pixels, STRIP_NUM_PIXELS);
|
||||
return;
|
||||
}
|
||||
// battery below minimum charge
|
||||
if (bat0 < 10) {
|
||||
memset(pixels, 0, sizeof(struct led_rgb) * STRIP_NUM_PIXELS);
|
||||
#if IS_ENABLED(CONFIG_ZMK_RGB_UNDERGLOW_EXT_POWER)
|
||||
if (state.on) {
|
||||
int c_power = ext_power_get(ext_power);
|
||||
if (c_power && !state.status_active) {
|
||||
// power is on, RGB underglow is on, but battery is too low
|
||||
state.on = false;
|
||||
reset_ext_power = true;
|
||||
}
|
||||
|
|
@ -275,7 +273,6 @@ static void zmk_led_write_pixels(void) {
|
|||
}
|
||||
}
|
||||
|
||||
// battery below 20%, reduce LED brightness
|
||||
if (bat0 < 20) {
|
||||
for (int i = 0; i < STRIP_NUM_PIXELS; i++) {
|
||||
led_buffer[i].r = led_buffer[i].r >> 1;
|
||||
|
|
@ -338,8 +335,6 @@ static void zmk_led_battery_level(int bat_level, const uint8_t *addresses, size_
|
|||
bat_colour = red;
|
||||
}
|
||||
|
||||
// originally, six levels, 0 .. 100
|
||||
|
||||
for (int i = 0; i < addresses_len; i++) {
|
||||
int min_level = (i * 100) / (addresses_len - 1);
|
||||
if (bat_level >= min_level) {
|
||||
|
|
@ -363,7 +358,6 @@ static int zmk_led_generate_status(void) {
|
|||
status_pixels[i] = (struct led_rgb){r : 0, g : 0, b : 0};
|
||||
}
|
||||
|
||||
// BATTERY STATUS
|
||||
#if IS_ENABLED(CONFIG_ZMK_BATTERY_REPORTING)
|
||||
zmk_led_battery_level(zmk_battery_state_of_charge(), underglow_bat_lhs,
|
||||
DT_PROP_LEN(UNDERGLOW_INDICATORS, bat_lhs));
|
||||
|
|
@ -379,10 +373,9 @@ static int zmk_led_generate_status(void) {
|
|||
} else if (rc == -EINVAL) {
|
||||
LOG_ERR("Invalid peripheral index requested for battery level read: 0");
|
||||
}
|
||||
#endif // CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING
|
||||
#endif // CONFIG_ZMK_BATTERY_REPORTING
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// CAPSLOCK/NUMLOCK/SCROLLOCK STATUS
|
||||
zmk_hid_indicators_t led_flags = zmk_hid_indicators_get_current_profile();
|
||||
|
||||
if (led_flags & ZMK_LED_CAPSLOCK_BIT)
|
||||
|
|
@ -392,7 +385,6 @@ static int zmk_led_generate_status(void) {
|
|||
if (led_flags & ZMK_LED_SCROLLLOCK_BIT)
|
||||
status_pixels[DT_PROP(UNDERGLOW_INDICATORS, scrolllock)] = red;
|
||||
|
||||
// LAYER STATUS
|
||||
for (uint8_t i = 0; i < DT_PROP_LEN(UNDERGLOW_INDICATORS, layer_state); i++) {
|
||||
if (zmk_keymap_layer_active(i))
|
||||
status_pixels[underglow_layer_state[i]] = magenta;
|
||||
|
|
@ -410,13 +402,13 @@ static int zmk_led_generate_status(void) {
|
|||
int8_t status = zmk_ble_profile_status(i);
|
||||
int ble_pixel = underglow_ble_state[i];
|
||||
if (status == 2 && active_endpoint.transport == ZMK_TRANSPORT_BLE &&
|
||||
active_ble_profile_index == i) { // connected AND active
|
||||
active_ble_profile_index == i) {
|
||||
status_pixels[ble_pixel] = white;
|
||||
} else if (status == 2) { // connected
|
||||
} else if (status == 2) {
|
||||
status_pixels[ble_pixel] = dull_green;
|
||||
} else if (status == 1) { // paired
|
||||
} else if (status == 1) {
|
||||
status_pixels[ble_pixel] = red;
|
||||
} else if (status == 0) { // unused
|
||||
} else if (status == 0) {
|
||||
status_pixels[ble_pixel] = lilac;
|
||||
}
|
||||
}
|
||||
|
|
@ -424,13 +416,13 @@ static int zmk_led_generate_status(void) {
|
|||
|
||||
enum zmk_usb_conn_state usb_state = zmk_usb_get_conn_state();
|
||||
if (usb_state == ZMK_USB_CONN_HID &&
|
||||
active_endpoint.transport == ZMK_TRANSPORT_USB) { // connected AND active
|
||||
active_endpoint.transport == ZMK_TRANSPORT_USB) {
|
||||
status_pixels[DT_PROP(UNDERGLOW_INDICATORS, usb_state)] = white;
|
||||
} else if (usb_state == ZMK_USB_CONN_HID) { // connected
|
||||
} else if (usb_state == ZMK_USB_CONN_HID) {
|
||||
status_pixels[DT_PROP(UNDERGLOW_INDICATORS, usb_state)] = dull_green;
|
||||
} else if (usb_state == ZMK_USB_CONN_POWERED) { // powered
|
||||
} else if (usb_state == ZMK_USB_CONN_POWERED) {
|
||||
status_pixels[DT_PROP(UNDERGLOW_INDICATORS, usb_state)] = red;
|
||||
} else if (usb_state == ZMK_USB_CONN_NONE) { // disconnected
|
||||
} else if (usb_state == ZMK_USB_CONN_NONE) {
|
||||
status_pixels[DT_PROP(UNDERGLOW_INDICATORS, usb_state)] = lilac;
|
||||
}
|
||||
|
||||
|
|
@ -447,34 +439,21 @@ static int zmk_led_generate_status(void) {
|
|||
|
||||
return blend;
|
||||
}
|
||||
#endif // underglow_indicators exists
|
||||
#endif
|
||||
|
||||
static inline struct led_rgb hue_sat(int hue, int sat) {
|
||||
struct zmk_led_hsb hsb = state.color;
|
||||
hsb.h = hue;
|
||||
hsb.s = sat;
|
||||
return hsb_to_rgb(hsb_scale_min_max(hsb));
|
||||
}
|
||||
/* --- Tick / animation loop --- */
|
||||
|
||||
static void zmk_rgb_underglow_tick(struct k_work *work) {
|
||||
switch (state.current_effect) {
|
||||
case UNDERGLOW_EFFECT_SOLID:
|
||||
zmk_rgb_underglow_effect_solid();
|
||||
break;
|
||||
case UNDERGLOW_EFFECT_BREATHE:
|
||||
zmk_rgb_underglow_effect_breathe();
|
||||
break;
|
||||
case UNDERGLOW_EFFECT_SPECTRUM:
|
||||
zmk_rgb_underglow_effect_spectrum();
|
||||
break;
|
||||
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
|
||||
struct zmk_rgb_effect *effect = zmk_rgb_effect_get(state.current_effect);
|
||||
if (effect && effect->render) {
|
||||
struct zmk_rgb_effect_ctx ctx = {
|
||||
.pixels = pixels,
|
||||
.num_pixels = STRIP_NUM_PIXELS,
|
||||
.base_color = state.color,
|
||||
.animation_step = &state.animation_step,
|
||||
.animation_speed = state.animation_speed,
|
||||
};
|
||||
effect->render(&ctx);
|
||||
}
|
||||
|
||||
zmk_led_write_pixels();
|
||||
|
|
@ -492,6 +471,12 @@ static void zmk_rgb_underglow_tick_handler(struct k_timer *timer) {
|
|||
|
||||
K_TIMER_DEFINE(underglow_tick, zmk_rgb_underglow_tick_handler, NULL);
|
||||
|
||||
void zmk_rgb_request_refresh(void) {
|
||||
k_work_submit_to_queue(zmk_workqueue_lowprio_work_q(), &underglow_tick_work);
|
||||
}
|
||||
|
||||
/* --- Settings persistence --- */
|
||||
|
||||
#if IS_ENABLED(CONFIG_SETTINGS)
|
||||
static int rgb_settings_set(const char *name, size_t len, settings_read_cb read_cb, void *cb_arg) {
|
||||
const char *next;
|
||||
|
|
@ -503,14 +488,13 @@ static int rgb_settings_set(const char *name, size_t len, settings_read_cb read_
|
|||
|
||||
rc = read_cb(cb_arg, &state, sizeof(state));
|
||||
if (rc >= 0) {
|
||||
int effect_count = zmk_rgb_effect_get_count();
|
||||
if (state.current_effect >= effect_count) {
|
||||
state.current_effect = 0;
|
||||
}
|
||||
if (state.on) {
|
||||
k_timer_start(&underglow_tick, K_NO_WAIT, K_MSEC(50));
|
||||
}
|
||||
#if IS_ENABLED(UNDERGLOW_LAYER_ENABLED)
|
||||
if (state.layer_enabled) {
|
||||
zmk_rgb_underglow_set_layer(rgb_underglow_top_layer(), true);
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -558,14 +542,20 @@ static int zmk_rgb_underglow_init(void) {
|
|||
state.on = zmk_usb_is_powered();
|
||||
#endif
|
||||
|
||||
int effect_count = zmk_rgb_effect_get_count();
|
||||
if (state.current_effect >= effect_count && effect_count > 0) {
|
||||
state.current_effect = 0;
|
||||
}
|
||||
|
||||
if (state.on) {
|
||||
k_timer_start(&underglow_tick, K_NO_WAIT, K_MSEC(25));
|
||||
struct zmk_rgb_effect *effect = zmk_rgb_effect_get(state.current_effect);
|
||||
if (effect && (effect->flags & ZMK_RGB_EFFECT_STATIC)) {
|
||||
zmk_rgb_request_refresh();
|
||||
} else {
|
||||
k_timer_start(&underglow_tick, K_NO_WAIT, K_MSEC(25));
|
||||
}
|
||||
}
|
||||
#if IS_ENABLED(UNDERGLOW_LAYER_ENABLED)
|
||||
if (state.layer_enabled) {
|
||||
zmk_rgb_underglow_set_layer(rgb_underglow_top_layer(), true);
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -582,7 +572,7 @@ int zmk_rgb_underglow_get_state(bool *on_off) {
|
|||
if (!led_strip)
|
||||
return -ENODEV;
|
||||
|
||||
*on_off = state.on || state.layer_enabled;
|
||||
*on_off = state.on;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -598,13 +588,12 @@ void zmk_rgb_set_ext_power(void) {
|
|||
int desired_state = state.on || state.status_active;
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_BATTERY_REPORTING)
|
||||
// force power off, when battery low (<10%)
|
||||
if (state.on && !state.status_active) {
|
||||
if (zmk_battery_state_of_charge() < 10) {
|
||||
desired_state = false;
|
||||
}
|
||||
}
|
||||
#endif // CONFIG_ZMK_BATTERY_REPORTING
|
||||
#endif
|
||||
|
||||
if (desired_state && !c_power) {
|
||||
int rc = ext_power_enable(ext_power);
|
||||
|
|
@ -617,18 +606,11 @@ void zmk_rgb_set_ext_power(void) {
|
|||
LOG_ERR("Unable to disable EXT_POWER: %d", rc);
|
||||
}
|
||||
}
|
||||
#endif // CONFIG_ZMK_RGB_UNDERGLOW_EXT_POWER
|
||||
#endif
|
||||
}
|
||||
|
||||
int zmk_rgb_underglow_on(void) {
|
||||
zmk_rgb_underglow_transient_on();
|
||||
#if IS_ENABLED(UNDERGLOW_LAYER_ENABLED)
|
||||
if (state.current_effect == UNDERGLOW_EFFECT_LAYER_INDICATORS) {
|
||||
state.layer_enabled = true;
|
||||
memset(pixels, 0, sizeof(struct led_rgb) * STRIP_NUM_PIXELS);
|
||||
zmk_rgb_underglow_set_layer(rgb_underglow_top_layer(), false);
|
||||
}
|
||||
#endif
|
||||
return zmk_rgb_underglow_save_state();
|
||||
}
|
||||
|
||||
|
|
@ -640,7 +622,16 @@ int zmk_rgb_underglow_transient_on(void) {
|
|||
zmk_rgb_set_ext_power();
|
||||
|
||||
state.animation_step = 0;
|
||||
k_timer_start(&underglow_tick, K_NO_WAIT, K_MSEC(25));
|
||||
|
||||
struct zmk_rgb_effect *effect = zmk_rgb_effect_get(state.current_effect);
|
||||
if (effect && effect->on_select) {
|
||||
effect->on_select();
|
||||
}
|
||||
if (effect && (effect->flags & ZMK_RGB_EFFECT_STATIC)) {
|
||||
zmk_rgb_request_refresh();
|
||||
} else {
|
||||
k_timer_start(&underglow_tick, K_NO_WAIT, K_MSEC(25));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -656,7 +647,6 @@ K_WORK_DEFINE(underglow_off_work, zmk_rgb_underglow_off_handler);
|
|||
|
||||
int zmk_rgb_underglow_off(void) {
|
||||
zmk_rgb_underglow_transient_off();
|
||||
state.layer_enabled = false;
|
||||
return zmk_rgb_underglow_save_state();
|
||||
}
|
||||
|
||||
|
|
@ -664,6 +654,11 @@ int zmk_rgb_underglow_transient_off(void) {
|
|||
if (!led_strip)
|
||||
return -ENODEV;
|
||||
|
||||
struct zmk_rgb_effect *effect = zmk_rgb_effect_get(state.current_effect);
|
||||
if (effect && effect->on_deselect) {
|
||||
effect->on_deselect();
|
||||
}
|
||||
|
||||
k_work_submit_to_queue(zmk_workqueue_lowprio_work_q(), &underglow_off_work);
|
||||
|
||||
k_timer_stop(&underglow_tick);
|
||||
|
|
@ -674,33 +669,43 @@ int zmk_rgb_underglow_transient_off(void) {
|
|||
}
|
||||
|
||||
int zmk_rgb_underglow_calc_effect(int direction) {
|
||||
return (state.current_effect + UNDERGLOW_EFFECT_NUMBER + direction) % UNDERGLOW_EFFECT_NUMBER;
|
||||
int count = zmk_rgb_effect_get_count();
|
||||
if (count == 0)
|
||||
return 0;
|
||||
return (state.current_effect + count + direction) % count;
|
||||
}
|
||||
|
||||
int zmk_rgb_underglow_select_effect(int effect) {
|
||||
if (!led_strip)
|
||||
return -ENODEV;
|
||||
|
||||
if (effect < 0 || effect >= UNDERGLOW_EFFECT_NUMBER) {
|
||||
int count = zmk_rgb_effect_get_count();
|
||||
if (effect < 0 || effect >= count) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(UNDERGLOW_LAYER_ENABLED)
|
||||
bool was_layer = state.layer_enabled;
|
||||
#endif
|
||||
struct zmk_rgb_effect *old_eff = zmk_rgb_effect_get(state.current_effect);
|
||||
if (old_eff && old_eff->on_deselect) {
|
||||
old_eff->on_deselect();
|
||||
}
|
||||
|
||||
state.current_effect = effect;
|
||||
state.animation_step = 0;
|
||||
|
||||
#if IS_ENABLED(UNDERGLOW_LAYER_ENABLED)
|
||||
state.layer_enabled = (effect == UNDERGLOW_EFFECT_LAYER_INDICATORS);
|
||||
if (state.layer_enabled && state.on) {
|
||||
memset(pixels, 0, sizeof(struct led_rgb) * STRIP_NUM_PIXELS);
|
||||
zmk_rgb_underglow_set_layer(rgb_underglow_top_layer(), false);
|
||||
} else if (was_layer && !state.layer_enabled && state.on) {
|
||||
k_timer_start(&underglow_tick, K_NO_WAIT, K_MSEC(25));
|
||||
struct zmk_rgb_effect *new_eff = zmk_rgb_effect_get(state.current_effect);
|
||||
if (new_eff && new_eff->on_select) {
|
||||
new_eff->on_select();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (state.on) {
|
||||
if (new_eff && (new_eff->flags & ZMK_RGB_EFFECT_STATIC)) {
|
||||
k_timer_stop(&underglow_tick);
|
||||
zmk_rgb_request_refresh();
|
||||
} else {
|
||||
k_timer_start(&underglow_tick, K_NO_WAIT, K_MSEC(25));
|
||||
}
|
||||
}
|
||||
|
||||
return zmk_rgb_underglow_save_state();
|
||||
}
|
||||
|
||||
|
|
@ -712,81 +717,7 @@ 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, uint8_t layer) {
|
||||
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, .layer = layer, .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, bool wakeup) {
|
||||
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, layer)) {
|
||||
if (!state.on) {
|
||||
if (!wakeup) {
|
||||
LOG_DBG("rgb off and no wakeup, abort refresh");
|
||||
return;
|
||||
}
|
||||
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) */
|
||||
/* --- Status overlay timer --- */
|
||||
|
||||
static void zmk_led_write_pixels_work(struct k_work *work);
|
||||
static void zmk_rgb_underglow_status_update(struct k_timer *timer);
|
||||
|
|
@ -830,6 +761,8 @@ int zmk_rgb_underglow_status(void) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* --- HSB control functions --- */
|
||||
|
||||
int zmk_rgb_underglow_set_hsb(struct zmk_led_hsb color) {
|
||||
if (color.h > HUE_MAX || color.s > SAT_MAX || color.b > BRT_MAX) {
|
||||
return -ENOTSUP;
|
||||
|
|
@ -916,8 +849,10 @@ int zmk_rgb_underglow_change_spd(int direction) {
|
|||
return zmk_rgb_underglow_save_state();
|
||||
}
|
||||
|
||||
/* --- Auto off / idle listeners --- */
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_RGB_UNDERGLOW_AUTO_OFF_IDLE) || \
|
||||
IS_ENABLED(CONFIG_ZMK_RGB_UNDERGLOW_AUTO_OFF_USB) || IS_ENABLED(UNDERGLOW_LAYER_ENABLED)
|
||||
IS_ENABLED(CONFIG_ZMK_RGB_UNDERGLOW_AUTO_OFF_USB)
|
||||
struct rgb_underglow_sleep_state {
|
||||
bool is_awake;
|
||||
bool rgb_state_before_sleeping;
|
||||
|
|
@ -929,19 +864,12 @@ static int rgb_underglow_auto_state(bool target_wake_state) {
|
|||
rgb_state_before_sleeping : false
|
||||
};
|
||||
|
||||
// wake up event while awake, or sleep event while sleeping -> no-op
|
||||
if (target_wake_state == sleep_state.is_awake) {
|
||||
return 0;
|
||||
}
|
||||
sleep_state.is_awake = target_wake_state;
|
||||
|
||||
if (sleep_state.is_awake) {
|
||||
#if IS_ENABLED(UNDERGLOW_LAYER_ENABLED)
|
||||
if (state.layer_enabled) {
|
||||
zmk_rgb_underglow_set_layer(rgb_underglow_top_layer(), true);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
if (sleep_state.rgb_state_before_sleeping) {
|
||||
return zmk_rgb_underglow_transient_on();
|
||||
} else {
|
||||
|
|
@ -961,30 +889,6 @@ static int rgb_underglow_event_listener(const zmk_event_t *eh) {
|
|||
}
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(UNDERGLOW_LAYER_ENABLED)
|
||||
if (as_zmk_split_peripheral_layer_changed(eh)) {
|
||||
const struct zmk_split_peripheral_layer_changed *ev =
|
||||
as_zmk_split_peripheral_layer_changed(eh);
|
||||
LOG_DBG("zmk_split_peripheral_layer_changed: %08x", ev->layers);
|
||||
#if !IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
|
||||
set_peripheral_layers_state(ev->layers);
|
||||
#endif
|
||||
uint8_t layer = rgb_underglow_top_layer();
|
||||
LOG_DBG("top layer: %d", layer);
|
||||
zmk_rgb_underglow_set_layer(layer, true);
|
||||
return 0;
|
||||
}
|
||||
if (as_zmk_underglow_color_changed(eh)) {
|
||||
const struct zmk_underglow_color_changed *ev = as_zmk_underglow_color_changed(eh);
|
||||
uint8_t layer = rgb_underglow_top_layer();
|
||||
LOG_DBG("refresh layers %d, current: %d, wakeup: %d", ev->layers, layer, ev->wakeup);
|
||||
if ((ev->layers & (BIT(layer))) == BIT(layer)) {
|
||||
zmk_rgb_underglow_set_layer(rgb_underglow_top_layer(), ev->wakeup);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif /* UNDERGLOW_LAYER_ENABLED */
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_RGB_UNDERGLOW_AUTO_OFF_USB)
|
||||
if (as_zmk_usb_conn_state_changed(eh)) {
|
||||
return rgb_underglow_auto_state(zmk_usb_is_powered());
|
||||
|
|
@ -995,9 +899,7 @@ static int rgb_underglow_event_listener(const zmk_event_t *eh) {
|
|||
}
|
||||
|
||||
ZMK_LISTENER(rgb_underglow, rgb_underglow_event_listener);
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_RGB_UNDERGLOW_AUTO_OFF_IDLE) ||
|
||||
// IS_ENABLED(CONFIG_ZMK_RGB_UNDERGLOW_AUTO_OFF_USB) ||
|
||||
// IS_ENABLED(UNDERGLOW_LAYER_ENABLED)
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_RGB_UNDERGLOW_AUTO_OFF_IDLE)
|
||||
ZMK_SUBSCRIPTION(rgb_underglow, zmk_activity_state_changed);
|
||||
|
|
@ -1007,9 +909,4 @@ ZMK_SUBSCRIPTION(rgb_underglow, zmk_activity_state_changed);
|
|||
ZMK_SUBSCRIPTION(rgb_underglow, zmk_usb_conn_state_changed);
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(UNDERGLOW_LAYER_ENABLED)
|
||||
ZMK_SUBSCRIPTION(rgb_underglow, zmk_split_peripheral_layer_changed);
|
||||
ZMK_SUBSCRIPTION(rgb_underglow, zmk_underglow_color_changed);
|
||||
#endif
|
||||
|
||||
SYS_INIT(zmk_rgb_underglow_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);
|
||||
|
|
|
|||
|
|
@ -1,84 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <zephyr/device.h>
|
||||
#include <drivers/behavior.h>
|
||||
#include <zephyr/sys/util.h>
|
||||
#include <zephyr/bluetooth/bluetooth.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
#include <zmk/matrix.h>
|
||||
#include <zmk/keymap.h>
|
||||
#include <zmk/rgb_underglow_layer.h>
|
||||
|
||||
#if !IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
|
||||
#include <zmk/split/bluetooth/peripheral_layers.h>
|
||||
#endif
|
||||
|
||||
#define DT_DRV_COMPAT zmk_underglow_layer
|
||||
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
||||
|
||||
#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), \
|
||||
(LISTIFY(DT_PROP_LEN(node, bindings), ZMK_RGBMAP_EXTRACT_BINDING, (, ), node)), \
|
||||
())}
|
||||
|
||||
#define RGBMAP_VAR(_name, _opts) \
|
||||
static _opts struct zmk_behavior_binding _name[ZMK_RGBMAP_LAYERS_LEN][ZMK_KEYMAP_LEN] = { \
|
||||
DT_INST_FOREACH_CHILD_STATUS_OKAY_SEP(0, TRANSFORMED_RGB_LAYER, (, ))};
|
||||
|
||||
RGBMAP_VAR(zmk_rgbmap, COND_CODE_1(IS_ENABLED(CONFIG_ZMK_KEYMAP_SETTINGS_STORAGE), (), (const)))
|
||||
|
||||
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]; };
|
||||
|
||||
const int zmk_rgbmap_id(uint8_t layer) {
|
||||
for (uint8_t i = 0; i < ZMK_RGBMAP_LAYERS_LEN; i++) {
|
||||
if (zmk_rgbmap_ids[i] == layer) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
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) {
|
||||
return NULL;
|
||||
} else {
|
||||
return zmk_rgbmap[rgblayer];
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t rgb_underglow_top_layer_with_state(uint32_t state_to_test) {
|
||||
for (uint8_t layer = ZMK_KEYMAP_LAYERS_LEN - 1; layer > 0; layer--) {
|
||||
if ((state_to_test & (BIT(layer))) == (BIT(layer)) || layer == 0) {
|
||||
return layer;
|
||||
}
|
||||
}
|
||||
// return default layer (0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t rgb_underglow_top_layer(void) {
|
||||
#if IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
|
||||
return zmk_keymap_highest_layer_active();
|
||||
#else
|
||||
return peripheral_highest_layer_active();
|
||||
#endif
|
||||
}
|
||||
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
|
||||
Loading…
Add table
Add a link
Reference in a new issue