From 3df0fca15db51a323d2192e746eba0471f92f3e9 Mon Sep 17 00:00:00 2001 From: Sofus Addington Date: Fri, 4 Oct 2024 13:44:50 +0200 Subject: [PATCH] Add widget --- config/boards/shields/sweeple/CMakeList.txt | 18 ++ .../shields/sweeple/custom_status_screen.c | 23 +++ .../shields/sweeple/custom_status_screen.h | 5 + .../shields/sweeple/widgets/battery_status.c | 155 ++++++++++++++++++ .../shields/sweeple/widgets/battery_status.h | 12 ++ module.yml | 3 + 6 files changed, 216 insertions(+) create mode 100644 config/boards/shields/sweeple/CMakeList.txt create mode 100644 config/boards/shields/sweeple/custom_status_screen.c create mode 100644 config/boards/shields/sweeple/custom_status_screen.h create mode 100644 config/boards/shields/sweeple/widgets/battery_status.c create mode 100644 config/boards/shields/sweeple/widgets/battery_status.h create mode 100644 module.yml diff --git a/config/boards/shields/sweeple/CMakeList.txt b/config/boards/shields/sweeple/CMakeList.txt new file mode 100644 index 0000000..dd4e60f --- /dev/null +++ b/config/boards/shields/sweeple/CMakeList.txt @@ -0,0 +1,18 @@ +if(CONFIG_ZMK_DISPLAY AND CONFIG_ZMK_DISPLAY_STATUS_SCREEN_CUSTOM AND ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL)) + zephyr_library() + zephyr_library_sources(${ZEPHYR_BASE}/misc/empty_file.c) + zephyr_library_include_directories(${ZEPHYR_LVGL_MODULE_DIR}) + zephyr_library_include_directories(${ZEPHYR_BASE}/lib/gui/lvgl/) + zephyr_library_include_directories(${ZEPHYR_BASE}/drivers) + zephyr_library_include_directories(${CMAKE_SOURCE_DIR}/include) + zephyr_library_sources(custom_status_screen.c) + zephyr_library_sources(widgets/battery_status.c) + zephyr_library_sources(widgets/bongo_cat.c) + zephyr_library_sources(widgets/bongo_cat_images.c) + target_sources_ifdef(CONFIG_ZMK_HID_INDICATORS app PRIVATE widgets/hid_indicators.c) + zephyr_library_sources(widgets/layer_status.c) + zephyr_library_sources(widgets/modifiers.c) + zephyr_library_sources(widgets/modifiers_sym.c) + zephyr_library_sources(widgets/output_status.c) + zephyr_library_sources(widgets/output_status_sym.c) +endif() diff --git a/config/boards/shields/sweeple/custom_status_screen.c b/config/boards/shields/sweeple/custom_status_screen.c new file mode 100644 index 0000000..fcc4e6c --- /dev/null +++ b/config/boards/shields/sweeple/custom_status_screen.c @@ -0,0 +1,23 @@ +#include "custom_status_screen.h" +#include "widgets/battery_status.h" + +static struct zmk_widget_dongle_battery_status dongle_battery_status_widget; + +lv_style_t global_style; + +lv_obj_t *zmk_display_status_screen() { + lv_obj_t *screen; + + screen = lv_obj_create(NULL); + + lv_style_init(&global_style); + lv_style_set_text_font(&global_style, &lv_font_unscii_8); + lv_style_set_text_letter_space(&global_style, 1); + lv_style_set_text_line_space(&global_style, 1); + lv_obj_add_style(screen, &global_style, LV_PART_MAIN); + + zmk_widget_dongle_battery_status_init(&dongle_battery_status_widget, screen); + lv_obj_align(zmk_widget_dongle_battery_status_obj(&dongle_battery_status_widget), LV_ALIGN_TOP_RIGHT, 0, 0); + + return screen; +} diff --git a/config/boards/shields/sweeple/custom_status_screen.h b/config/boards/shields/sweeple/custom_status_screen.h new file mode 100644 index 0000000..28ad184 --- /dev/null +++ b/config/boards/shields/sweeple/custom_status_screen.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +lv_obj_t *zmk_display_status_screen(); diff --git a/config/boards/shields/sweeple/widgets/battery_status.c b/config/boards/shields/sweeple/widgets/battery_status.c new file mode 100644 index 0000000..4e7ceff --- /dev/null +++ b/config/boards/shields/sweeple/widgets/battery_status.c @@ -0,0 +1,155 @@ +#include +#include + +#include +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#include +#include +#include +#include +#include +#include +#include + +#include "battery_status.h" + +#if IS_ENABLED(CONFIG_ZMK_DONGLE_DISPLAY_DONGLE_BATTERY) + #define SOURCE_OFFSET 1 +#else + #define SOURCE_OFFSET 0 +#endif + +static sys_slist_t widgets = SYS_SLIST_STATIC_INIT(&widgets); + +struct battery_state { + uint8_t source; + uint8_t level; + bool usb_present; +}; + +static lv_color_t battery_image_buffer[ZMK_SPLIT_BLE_PERIPHERAL_COUNT + SOURCE_OFFSET][5 * 8]; + +static void draw_battery(lv_obj_t *canvas, uint8_t level, bool usb_present) { + lv_canvas_fill_bg(canvas, lv_color_black(), LV_OPA_COVER); + + lv_draw_rect_dsc_t rect_fill_dsc; + lv_draw_rect_dsc_init(&rect_fill_dsc); + + if (usb_present) { + rect_fill_dsc.bg_opa = LV_OPA_TRANSP; + rect_fill_dsc.border_color = lv_color_white(); + rect_fill_dsc.border_width = 1; + } + + lv_canvas_set_px(canvas, 0, 0, lv_color_white()); + lv_canvas_set_px(canvas, 4, 0, lv_color_white()); + + if (level <= 10 || usb_present) { + lv_canvas_draw_rect(canvas, 1, 2, 3, 5, &rect_fill_dsc); + } else if (level <= 30) { + lv_canvas_draw_rect(canvas, 1, 2, 3, 4, &rect_fill_dsc); + } else if (level <= 50) { + lv_canvas_draw_rect(canvas, 1, 2, 3, 3, &rect_fill_dsc); + } else if (level <= 70) { + lv_canvas_draw_rect(canvas, 1, 2, 3, 2, &rect_fill_dsc); + } else if (level <= 90) { + lv_canvas_draw_rect(canvas, 1, 2, 3, 1, &rect_fill_dsc); + } +} + +static void set_battery_symbol(lv_obj_t *widget, struct battery_state state) { + if (state.source >= ZMK_SPLIT_BLE_PERIPHERAL_COUNT + SOURCE_OFFSET) { + return; + } + LOG_DBG("source: %d, level: %d, usb: %d", state.source, state.level, state.usb_present); + lv_obj_t *symbol = lv_obj_get_child(widget, state.source * 2); + lv_obj_t *label = lv_obj_get_child(widget, state.source * 2 + 1); + + draw_battery(symbol, state.level, state.usb_present); + lv_label_set_text_fmt(label, "%4u%%", state.level); + + if (state.level > 0 || state.usb_present) { + lv_obj_clear_flag(symbol, LV_OBJ_FLAG_HIDDEN); + lv_obj_clear_flag(label, LV_OBJ_FLAG_HIDDEN); + } else { + lv_obj_add_flag(symbol, LV_OBJ_FLAG_HIDDEN); + lv_obj_add_flag(label, LV_OBJ_FLAG_HIDDEN); + } +} + +void battery_status_update_cb(struct battery_state state) { + struct zmk_widget_dongle_battery_status *widget; + SYS_SLIST_FOR_EACH_CONTAINER(&widgets, widget, node) { set_battery_symbol(widget->obj, state); } +} + +static struct battery_state peripheral_battery_status_get_state(const zmk_event_t *eh) { + const struct zmk_peripheral_battery_state_changed *ev = as_zmk_peripheral_battery_state_changed(eh); + return (struct battery_state){ + .source = ev->source + SOURCE_OFFSET, + .level = ev->state_of_charge, + }; +} + +static struct battery_state central_battery_status_get_state(const zmk_event_t *eh) { + const struct zmk_battery_state_changed *ev = as_zmk_battery_state_changed(eh); + return (struct battery_state) { + .source = 0, + .level = (ev != NULL) ? ev->state_of_charge : zmk_battery_state_of_charge(), +#if IS_ENABLED(CONFIG_USB_DEVICE_STACK) + .usb_present = zmk_usb_is_powered(), +#endif /* IS_ENABLED(CONFIG_USB_DEVICE_STACK) */ + }; +} + +static struct battery_state battery_status_get_state(const zmk_event_t *eh) { + if (as_zmk_peripheral_battery_state_changed(eh) != NULL) { + return peripheral_battery_status_get_state(eh); + } else { + return central_battery_status_get_state(eh); + } +} + +ZMK_DISPLAY_WIDGET_LISTENER(widget_dongle_battery_status, struct battery_state, + battery_status_update_cb, battery_status_get_state) + +ZMK_SUBSCRIPTION(widget_dongle_battery_status, zmk_peripheral_battery_state_changed); + +#if IS_ENABLED(CONFIG_ZMK_DONGLE_DISPLAY_DONGLE_BATTERY) +#if !IS_ENABLED(CONFIG_ZMK_SPLIT) || IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL) + +ZMK_SUBSCRIPTION(widget_dongle_battery_status, zmk_battery_state_changed); +#if IS_ENABLED(CONFIG_USB_DEVICE_STACK) +ZMK_SUBSCRIPTION(widget_dongle_battery_status, zmk_usb_conn_state_changed); +#endif /* IS_ENABLED(CONFIG_USB_DEVICE_STACK) */ +#endif /* !IS_ENABLED(CONFIG_ZMK_SPLIT) || IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL) */ +#endif /* IS_ENABLED(CONFIG_ZMK_DONGLE_DISPLAY_DONGLE_BATTERY) */ + +int zmk_widget_dongle_battery_status_init(struct zmk_widget_dongle_battery_status *widget, lv_obj_t *parent) { + widget->obj = lv_obj_create(parent); + + lv_obj_set_size(widget->obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT); + + for (int i = 0; i < ZMK_SPLIT_BLE_PERIPHERAL_COUNT + SOURCE_OFFSET; i++) { + lv_obj_t *image_canvas = lv_canvas_create(widget->obj); + lv_obj_t *battery_label = lv_label_create(widget->obj); + + lv_canvas_set_buffer(image_canvas, battery_image_buffer[i], 5, 8, LV_IMG_CF_TRUE_COLOR); + + lv_obj_align(image_canvas, LV_ALIGN_TOP_RIGHT, 0, i * 10); + lv_obj_align(battery_label, LV_ALIGN_TOP_RIGHT, -7, i * 10); + + lv_obj_add_flag(image_canvas, LV_OBJ_FLAG_HIDDEN); + lv_obj_add_flag(battery_label, LV_OBJ_FLAG_HIDDEN); + } + + sys_slist_append(&widgets, &widget->node); + + widget_dongle_battery_status_init(); + + return 0; +} + +lv_obj_t *zmk_widget_dongle_battery_status_obj(struct zmk_widget_dongle_battery_status *widget) { + return widget->obj; +} diff --git a/config/boards/shields/sweeple/widgets/battery_status.h b/config/boards/shields/sweeple/widgets/battery_status.h new file mode 100644 index 0000000..b3b9ec6 --- /dev/null +++ b/config/boards/shields/sweeple/widgets/battery_status.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +struct zmk_widget_dongle_battery_status { + sys_snode_t node; + lv_obj_t *obj; +}; + +int zmk_widget_dongle_battery_status_init(struct zmk_widget_dongle_battery_status *widget, lv_obj_t *parent); +lv_obj_t *zmk_widget_dongle_battery_status_obj(struct zmk_widget_dongle_battery_status *widget); diff --git a/module.yml b/module.yml new file mode 100644 index 0000000..1cc2b35 --- /dev/null +++ b/module.yml @@ -0,0 +1,3 @@ +build: + settings: + board_root: .