underglow-layer: use behaviors to control RGBs

This commit is contained in:
darknao 2025-01-04 20:01:15 +01:00 committed by afiqzudinhadi
parent aa151f7473
commit f178f86a54
20 changed files with 352 additions and 26 deletions

View file

@ -0,0 +1,39 @@
/*
* 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) */

View file

@ -0,0 +1,80 @@
/*
* 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});
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) */

View file

@ -0,0 +1,10 @@
/*
* 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);

View file

@ -17,6 +17,7 @@
#include <zmk/endpoints.h>
#include <zmk/keymap.h>
#include <zmk/matrix.h>
#include <zmk/behavior.h>
#include <zmk/hid_indicators.h>
#include <zmk/usb.h>
@ -25,6 +26,7 @@
#include <zephyr/drivers/led_strip.h>
#include <drivers/ext_power.h>
#include <drivers/behavior.h>
#include <zmk/rgb_underglow.h>
#include <zmk/rgb_underglow_layer.h>
@ -33,6 +35,8 @@
#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>
@ -52,13 +56,13 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#endif
#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
#endif
#define STRIP_CHOSEN DT_CHOSEN(zmk_underglow)
#define STRIP_NUM_PIXELS DT_PROP(STRIP_CHOSEN, chain_length)
#define HUE_MAX 360
#define SAT_MAX 100
#define BRT_MAX 100
@ -455,27 +459,38 @@ static struct led_rgb hex_to_rgb(uint8_t r, uint8_t g, uint8_t b) {
};
}
static int zmk_rgb_underglow_apply_rgbmap(uint32_t rgbmap[], size_t rgbmap_len) {
// TODO: Glove80 specifics, move that part to board's devicetree
#ifdef LEFT_HALF
const uint8_t LED_MATRIX[] = {52, 53, 54, 69, 70, 71, 15, 27, 39, 51, 4, 14, 26, 38,
50, 68, 3, 13, 25, 37, 49, 67, 2, 12, 24, 36, 48, 66,
1, 11, 23, 35, 47, 65, 0, 10, 22, 34, 46, 64};
#else
const uint8_t LED_MATRIX[] = {57, 56, 55, 74, 73, 72, 16, 28, 40, 58, 5, 17, 29, 41,
59, 75, 6, 18, 30, 42, 60, 76, 7, 19, 31, 43, 61, 77,
8, 20, 32, 44, 62, 78, 9, 21, 33, 45, 63, 79};
#endif
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 = LED_MATRIX[i];
uint8_t midx = rgb_pixel_lookup(i);
if (midx >= ZMK_KEYMAP_LEN) {
LOG_DBG("out of range");
} else {
pixels[i] = hex_to_rgb((rgbmap[midx] & 0xFF0000) >> 16, (rgbmap[midx] & 0xFF00) >> 8,
rgbmap[midx] & 0xFF);
if (rgbmap[midx] > 0)
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;
@ -485,7 +500,7 @@ static void zmk_rgb_underglow_set_layer(uint8_t layer) {
if (!state.layer_enabled)
return;
uint32_t *rgbmap = rgb_underglow_get_bindings(layer);
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();
@ -914,6 +929,15 @@ static int rgb_underglow_event_listener(const zmk_event_t *eh) {
zmk_rgb_underglow_set_layer(layer);
return 0;
}
if (as_zmk_underglow_color_changed(eh)) {
const struct zmk_underglow_color_changed *ev = as_zmk_underglow_color_changed(eh);
LOG_DBG("refresh layer %d", ev->layers);
uint8_t layer = rgb_underglow_top_layer();
if ((ev->layers & (BIT(layer))) == BIT(layer)) {
zmk_rgb_underglow_set_layer(rgb_underglow_top_layer());
}
return 0;
}
#endif /* UNDERGLOW_LAYER_ENABLED */
#if IS_ENABLED(CONFIG_ZMK_RGB_UNDERGLOW_AUTO_OFF_USB)
@ -940,6 +964,7 @@ ZMK_SUBSCRIPTION(rgb_underglow, zmk_usb_conn_state_changed);
#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);

View file

@ -24,13 +24,24 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#define UNDERGLOW_LAYER_ENABLED
#define LAYER_ID(node) DT_PROP(node, layer_id)
#define RGB_BINDINGS(node) DT_PROP(node, bindings)
static uint32_t zmk_rgbmap[ZMK_RGBMAP_LAYERS_LEN][ZMK_KEYMAP_LEN] = {
DT_INST_FOREACH_CHILD_SEP(0, RGB_BINDINGS, (, ))};
#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, (, ))};
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) {
@ -40,7 +51,7 @@ const int zmk_rgbmap_id(uint8_t layer) {
return -1;
}
uint32_t *rgb_underglow_get_bindings(uint8_t layer) {
const struct zmk_behavior_binding *rgb_underglow_get_bindings(uint8_t layer) {
int rgblayer = zmk_rgbmap_id(layer);
if (rgblayer == -1) {
return NULL;