This commit is contained in:
Steven 2024-09-02 21:48:49 +05:30 committed by GitHub
commit bafe28dc81
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 274 additions and 0 deletions

View file

@ -102,6 +102,7 @@ target_sources_ifdef(CONFIG_ZMK_RGB_UNDERGLOW app PRIVATE src/rgb_underglow.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)
target_sources_ifdef(CONFIG_ZMK_LED_WIDGETS app PRIVATE src/led_widgets.c)
add_subdirectory(src/display/)
add_subdirectory_ifdef(CONFIG_SETTINGS src/settings/)

View file

@ -269,6 +269,21 @@ menuconfig ZMK_RGB_UNDERGLOW
select LED_STRIP
select ZMK_LOW_PRIORITY_WORK_QUEUE
config ZMK_LED_WIDGETS
bool "Display widgets on top of LEDs"
if ZMK_LED_WIDGETS
config ZMK_LED_WIDGETS_MAX_WIDGET_NUM
int "Maximum number of widgets for a event type"
default 5
config ZMK_LED_WIDGETS_INIT_PRIORITY
int "LED widgets init priority"
default 99
endif
if ZMK_RGB_UNDERGLOW
# This default value cuts down on tons of excess .conf files, if you're using GPIO, manually disable this

View file

@ -0,0 +1,55 @@
#pragma once
#include <zephyr/devicetree.h>
#include <stdint.h>
typedef enum {
LED_EVENT_BOOT = 0,
LED_EVENT_BATTERY,
LED_EVENT_LAYER,
LED_EVENT_OUTPUT,
LED_EVENT_PROFILE,
LED_EVENT_CONN,
LED_EVENT_SIZE,
} led_event_type_t;
typedef enum {
LED_ENDPOINT_CONN = 0,
LED_ENDPOINT_DISCONN,
} led_endpoint_connected_t;
typedef enum {
LED_STATE_IDLE = 0,
LED_STATE_PAUSE,
LED_STATE_ACTIVE,
} led_state_t;
#define _NARG(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, N, ...) N
#define NARG(...) _NARG(__VA_ARGS__, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
/* #define _F(x) 1 */
/* #define NARG(x) FOR_EACH(_F, (+), x) */
#define LEDS LIST_DROP_EMPTY(DT_SUPPORTS_DEP_ORDS(DT_CHOSEN(zmk_led_widgets_dev)))
#define NUM_LEDS UTIL_EVAL(NARG(LEDS))
typedef struct {
uint8_t brightness[NUM_LEDS];
uint16_t timeout;
} led_cmd_t;
typedef struct {
uint8_t arg;
uint8_t priority;
uint32_t period;
uint8_t cmd_len;
led_cmd_t commands[5];
} led_widget_t;
#define _ZERO(a) 0
#define WAIT(t) \
{ {FOR_EACH(_ZERO, (, ), LEDS)}, t }
#define CMD(t, ...) \
{ {__VA_ARGS__}, t }
#define WIDGET(a, b, c, d, ...) \
{ \
.arg = a, .b, .c, .d, { __VA_ARGS__ } \
}

203
app/src/led_widgets.c Normal file
View file

@ -0,0 +1,203 @@
#include <zephyr/device.h>
#include <zephyr/drivers/led.h>
#include <zmk/event_manager.h>
#include <zmk/keymap.h>
#include <zmk/events/layer_state_changed.h>
#include <zmk/events/usb_conn_state_changed.h>
#include <zmk/events/battery_state_changed.h>
#include <zmk/events/ble_active_profile_changed.h>
#include <zmk/events/endpoint_changed.h>
#include <zmk/led_widgets.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(led_widgets, 4);
static const struct device *leds = DEVICE_DT_GET(DT_CHOSEN(zmk_led_widgets_dev));
extern const led_widget_t led_widgets[LED_EVENT_SIZE][CONFIG_ZMK_LED_WIDGETS_MAX_WIDGET_NUM];
#define PAUSE_TIMEOUT_MS 500
#define PROFILE_COUNT (CONFIG_BT_MAX_PAIRED - 1)
#define LED_ACTIVE_WIDGET_GET(i) (led_widgets[i][active_widgets_ind[i]])
static void led_widget_work_cb(struct k_work *work);
static K_WORK_DELAYABLE_DEFINE(led_widget_work, led_widget_work_cb);
static led_state_t state = LED_STATE_IDLE;
static int8_t active_widget_type = -1;
static int8_t active_widgets_ind[LED_EVENT_SIZE];
static int8_t last_widgets_ind[LED_EVENT_SIZE];
static uint8_t led_cmd_ind = 0;
static struct k_timer loop_timers[LED_EVENT_SIZE];
static bool loop_timer_started[LED_EVENT_SIZE];
static bool widget_is_status(const led_widget_t *widget) {
return widget->cmd_len == 1 && widget->commands[0].timeout == 0;
}
static void led_off_all() {
LOG_DBG("off");
for (uint8_t i = 0; i < NUM_LEDS; i++) {
led_off(leds, i);
}
}
static void run_widget_cmd(const led_event_type_t ev, const uint8_t cmd_ind) {
const led_widget_t *active_widget = &LED_ACTIVE_WIDGET_GET(ev);
const uint8_t cmd_len = active_widget->cmd_len;
const led_cmd_t *cmd = &active_widget->commands[cmd_ind];
if (cmd_ind == 0) {
LOG_DBG("run %u", ev);
const uint16_t period = active_widget->period;
if (period > 0) {
LOG_DBG("resched %u", period);
if (!loop_timer_started[ev]) {
k_timer_start(&loop_timers[ev], K_MSEC(period), K_MSEC(period));
loop_timer_started[ev] = true;
}
} else {
k_timer_stop(&loop_timers[ev]);
loop_timer_started[ev] = false;
}
}
#define _FMT(_i, _j) " %u"
#define _ARG(i, _j) , cmd->brightness[i]
LOG_DBG("led" LISTIFY(NUM_LEDS, _FMT) LISTIFY(NUM_LEDS, _ARG));
#undef _FMT
#undef _ARG
for (uint8_t i = 0; i < NUM_LEDS; i++) {
led_set_brightness(leds, i, cmd->brightness[i]);
}
if (cmd->timeout > 0) {
LOG_DBG("wait %u", cmd->timeout);
k_work_schedule(&led_widget_work, K_MSEC(cmd->timeout));
}
active_widget_type = ev;
if (cmd_len == cmd_ind + 1) {
LOG_DBG("-> idle");
state = LED_STATE_IDLE;
return;
}
LOG_DBG("-> active");
state = LED_STATE_ACTIVE;
led_cmd_ind = cmd_ind;
}
static void led_widget_pause() {
LOG_DBG("-> pause");
led_off_all();
state = LED_STATE_PAUSE;
k_work_schedule(&led_widget_work, K_MSEC(PAUSE_TIMEOUT_MS));
}
static void led_widget_work_cb(struct k_work *_work) {
switch (state) {
case LED_STATE_IDLE:
LOG_WRN("IDLE");
led_off_all();
LOG_DBG("last[%d] <- %d", active_widget_type, active_widgets_ind[active_widget_type]);
last_widgets_ind[active_widget_type] = active_widgets_ind[active_widget_type];
active_widgets_ind[active_widget_type] = -1;
active_widget_type = -1;
uint8_t max_priority = 0;
for (uint8_t i = 0; i < LED_EVENT_SIZE; i++) {
if (active_widgets_ind[i] != -1 && LED_ACTIVE_WIDGET_GET(i).priority > max_priority) {
max_priority = LED_ACTIVE_WIDGET_GET(i).priority;
active_widget_type = i;
}
}
if (active_widget_type != -1) {
LOG_DBG("next %d", active_widget_type);
led_widget_pause();
}
break;
case LED_STATE_PAUSE:
LOG_WRN("PAUSE");
run_widget_cmd(active_widget_type, 0);
break;
case LED_STATE_ACTIVE:;
LOG_WRN("ACTIVE");
led_off_all();
run_widget_cmd(active_widget_type, led_cmd_ind + 1);
break;
}
}
static void led_widget_schedule(const led_event_type_t ev, const uint8_t widget) {
LOG_DBG("sched %u %u", ev, widget);
if (active_widgets_ind[ev] == widget && widget_is_status(&LED_ACTIVE_WIDGET_GET(ev))) {
return;
}
active_widgets_ind[ev] = widget;
if (active_widget_type > 0) {
LOG_WRN("active %u", active_widget_type);
if (state == LED_STATE_PAUSE || LED_ACTIVE_WIDGET_GET(ev).priority <
LED_ACTIVE_WIDGET_GET(active_widget_type).priority) {
return;
}
if (widget_is_status(&LED_ACTIVE_WIDGET_GET(ev))) {
led_off_all();
run_widget_cmd(ev, 0);
return;
}
active_widget_type = ev;
led_widget_pause();
} else {
run_widget_cmd(ev, 0);
}
}
static void loop_timer_handler(struct k_timer *timer) {
const led_event_type_t ev = (timer - loop_timers) / sizeof(struct k_timer) + 1;
LOG_DBG("loop %u %d", ev, last_widgets_ind[ev]);
led_widget_schedule(ev, last_widgets_ind[ev]);
}
#define widget_handler(TYPE, EV, MEMBER, EXPR, CMP, MSG) \
const struct TYPE *EV##_ev = as_##TYPE(ev); \
if (EV##_ev) { \
const uint8_t match = COND_CODE_0(IS_EMPTY(MEMBER), (EV##_ev->MEMBER), (EXPR)); \
LOG_WRN(MSG, match); \
for (uint8_t i = 0; i < ARRAY_SIZE(led_widgets[LED_EVENT_##EV]); i++) { \
if (match CMP led_widgets[LED_EVENT_##EV][i].arg) { \
led_widget_schedule(LED_EVENT_##EV, i); \
LOG_DBG("found %u", i); \
return ZMK_EV_EVENT_BUBBLE; \
} \
} \
LOG_DBG("not found"); \
active_widgets_ind[LED_EVENT_##EV] = -1; \
k_work_schedule(&led_widget_work, K_NO_WAIT); \
return ZMK_EV_EVENT_BUBBLE; \
}
static int led_widgets_event_listener(const zmk_event_t *ev) {
widget_handler(zmk_battery_state_changed, BATTERY, state_of_charge, , <, "bat level %u");
#ifdef CONFIG_ZMK_BLE
widget_handler(zmk_ble_active_profile_changed, PROFILE, index, , ==, "ble profile %u");
#endif
widget_handler(zmk_layer_state_changed, LAYER, , zmk_keymap_highest_layer_active(), ==,
"layer %u");
widget_handler(zmk_endpoint_changed, OUTPUT, endpoint, , ==, "endpoint %u");
return ZMK_EV_EVENT_BUBBLE;
}
static int led_widgets_init() {
for (uint8_t i = 0; i < LED_EVENT_SIZE; i++) {
active_widgets_ind[i] = -1;
last_widgets_ind[i] = -1;
k_timer_init(&loop_timers[i], loop_timer_handler, NULL);
}
return 0;
}
ZMK_LISTENER(led_widgets_event, led_widgets_event_listener);
ZMK_SUBSCRIPTION(led_widgets_event, zmk_battery_state_changed);
#if defined(CONFIG_USB)
ZMK_SUBSCRIPTION(led_widgets_event, zmk_usb_conn_state_changed);
#endif
#if defined(CONFIG_ZMK_BLE)
ZMK_SUBSCRIPTION(led_widgets_event, zmk_ble_active_profile_changed);
#endif
ZMK_SUBSCRIPTION(led_widgets_event, zmk_layer_state_changed);
ZMK_SUBSCRIPTION(led_widgets_event, zmk_endpoint_changed);
SYS_INIT(led_widgets_init, APPLICATION, CONFIG_ZMK_KSCAN_INIT_PRIORITY);