From db45b6967264568947ec9bc9bf1081f4be4dd846 Mon Sep 17 00:00:00 2001 From: urob <978080+urob@users.noreply.github.com> Date: Thu, 27 Apr 2023 11:55:06 -0400 Subject: [PATCH] Zen display & battery tweaks --- app/boards/arm/corneish_zen/Kconfig.defconfig | 3 + .../arm/corneish_zen/custom_status_screen.c | 23 ++++++++ .../arm/corneish_zen/widgets/battery_status.c | 55 +++++++++++++++---- .../arm/corneish_zen/widgets/layer_status.c | 10 ++++ app/include/zmk/keymap.h | 4 +- app/module/drivers/display/Kconfig.il0323 | 10 +++- app/module/drivers/display/il0323.c | 12 +++- app/src/behaviors/behavior_momentary_layer.c | 2 +- app/src/conditional_layer.c | 11 +++- app/src/display/Kconfig | 11 ++++ app/src/display/main.c | 18 ++++++ app/src/keymap.c | 23 ++++++-- 12 files changed, 157 insertions(+), 25 deletions(-) diff --git a/app/boards/arm/corneish_zen/Kconfig.defconfig b/app/boards/arm/corneish_zen/Kconfig.defconfig index feab3eca..8b0cab87 100644 --- a/app/boards/arm/corneish_zen/Kconfig.defconfig +++ b/app/boards/arm/corneish_zen/Kconfig.defconfig @@ -77,6 +77,9 @@ menuconfig CUSTOM_WIDGET_LAYER_STATUS menuconfig CUSTOM_WIDGET_PERIPHERAL_STATUS bool "custom peripheral status widget" +config CUSTOM_WIDGET_LAYER_STATUS_HIDE_HEADING + bool "Hide heading strip for layer widget and center widgets on 1/3rds" + endif # BOARD_CORNEISH_ZEN_LEFT || BOARD_CORNEISH_ZEN_RIGHT if BOARD_CORNEISH_ZEN_V1_LEFT || BOARD_CORNEISH_ZEN_V1_RIGHT diff --git a/app/boards/arm/corneish_zen/custom_status_screen.c b/app/boards/arm/corneish_zen/custom_status_screen.c index 492239c8..159803b6 100644 --- a/app/boards/arm/corneish_zen/custom_status_screen.c +++ b/app/boards/arm/corneish_zen/custom_status_screen.c @@ -40,37 +40,60 @@ lv_obj_t *zmk_display_status_screen() { #if IS_ENABLED(CONFIG_CUSTOM_WIDGET_BATTERY_STATUS) zmk_widget_battery_status_init(&battery_status_widget, screen); +#if IS_ENABLED(CONFIG_CUSTOM_WIDGET_LAYER_STATUS_HIDE_HEADING) + lv_obj_align(zmk_widget_battery_status_obj(&battery_status_widget), LV_ALIGN_CENTER, 0, -43); +#else lv_obj_align(zmk_widget_battery_status_obj(&battery_status_widget), LV_ALIGN_TOP_MID, 0, 2); #endif +#endif #if IS_ENABLED(CONFIG_CUSTOM_WIDGET_OUTPUT_STATUS) zmk_widget_output_status_init(&output_status_widget, screen); +#if IS_ENABLED(CONFIG_CUSTOM_WIDGET_LAYER_STATUS_HIDE_HEADING) + lv_obj_align(zmk_widget_output_status_obj(&output_status_widget), LV_ALIGN_CENTER, 0, 0); +#else lv_obj_align(zmk_widget_output_status_obj(&output_status_widget), LV_ALIGN_TOP_MID, 0, 41); #endif +#endif #if IS_ENABLED(CONFIG_CUSTOM_WIDGET_PERIPHERAL_STATUS) zmk_widget_peripheral_status_init(&peripheral_status_widget, screen); +#if IS_ENABLED(CONFIG_CUSTOM_WIDGET_LAYER_STATUS_HIDE_HEADING) + lv_obj_align(zmk_widget_peripheral_status_obj(&peripheral_status_widget), LV_ALIGN_CENTER, 0, + 0); +#else lv_obj_align(zmk_widget_peripheral_status_obj(&peripheral_status_widget), LV_ALIGN_TOP_MID, 0, 41); #endif +#endif #if IS_ENABLED(CONFIG_CUSTOM_WIDGET_LAYER_STATUS) +#if !IS_ENABLED(CONFIG_CUSTOM_WIDGET_LAYER_STATUS_HIDE_HEADING) lv_obj_t *LayersHeading; LayersHeading = lv_img_create(screen); lv_obj_align(LayersHeading, LV_ALIGN_BOTTOM_MID, 0, -30); lv_img_set_src(LayersHeading, &layers2); +#endif zmk_widget_layer_status_init(&layer_status_widget, screen); lv_obj_set_style_text_font(zmk_widget_layer_status_obj(&layer_status_widget), &lv_font_montserrat_16, LV_PART_MAIN); +#if IS_ENABLED(CONFIG_CUSTOM_WIDGET_LAYER_STATUS_HIDE_HEADING) + lv_obj_align(zmk_widget_layer_status_obj(&layer_status_widget), LV_ALIGN_CENTER, 0, 43); +#else lv_obj_align(zmk_widget_layer_status_obj(&layer_status_widget), LV_ALIGN_BOTTOM_MID, 0, -5); #endif +#endif #if !IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL) lv_obj_t *zenlogo_icon; zenlogo_icon = lv_img_create(screen); lv_img_set_src(zenlogo_icon, &zenlogo); +#if IS_ENABLED(CONFIG_CUSTOM_WIDGET_LAYER_STATUS_HIDE_HEADING) + lv_obj_align(zenlogo_icon, LV_ALIGN_CENTER, 0, 43); +#else lv_obj_align(zenlogo_icon, LV_ALIGN_BOTTOM_MID, 0, -5); +#endif #endif return screen; diff --git a/app/boards/arm/corneish_zen/widgets/battery_status.c b/app/boards/arm/corneish_zen/widgets/battery_status.c index 9a2189d1..bf2bd15c 100644 --- a/app/boards/arm/corneish_zen/widgets/battery_status.c +++ b/app/boards/arm/corneish_zen/widgets/battery_status.c @@ -41,21 +41,52 @@ LV_IMG_DECLARE(batt_0); LV_IMG_DECLARE(batt_0_chg); static void set_battery_symbol(lv_obj_t *icon, struct battery_status_state state) { - uint8_t level = state.level; - #if IS_ENABLED(CONFIG_USB_DEVICE_STACK) - if (level > 95) { - lv_img_set_src(icon, state.usb_present ? &batt_100_chg : &batt_100); - } else if (level > 74) { - lv_img_set_src(icon, state.usb_present ? &batt_75_chg : &batt_75); - } else if (level > 49) { - lv_img_set_src(icon, state.usb_present ? &batt_50_chg : &batt_50); - } else if (level > 24) { - lv_img_set_src(icon, state.usb_present ? &batt_25_chg : &batt_25); + static uint8_t stage_prev = 255; + static bool usb_prev = false; + + uint8_t level = state.level; + bool usb_present = state.usb_present; + uint8_t stage; + + if (level > 87) { + stage = 5; + } else if (level > 62) { + stage = 4; + } else if (level > 37) { + stage = 3; + } else if (level > 12) { + stage = 2; } else if (level > 5) { - lv_img_set_src(icon, state.usb_present ? &batt_5_chg : &batt_5); + stage = 1; } else { - lv_img_set_src(icon, state.usb_present ? &batt_0_chg : &batt_0); + stage = 0; + } + + // check if there is a change requiring an update + if (usb_present != usb_prev || stage != stage_prev) { + switch (stage) { + case 5: + lv_img_set_src(icon, usb_present ? &batt_100_chg : &batt_100); + break; + case 4: + lv_img_set_src(icon, usb_present ? &batt_75_chg : &batt_75); + break; + case 3: + lv_img_set_src(icon, usb_present ? &batt_50_chg : &batt_50); + break; + case 2: + lv_img_set_src(icon, usb_present ? &batt_25_chg : &batt_25); + break; + case 1: + lv_img_set_src(icon, usb_present ? &batt_5_chg : &batt_5); + break; + default: + lv_img_set_src(icon, usb_present ? &batt_0_chg : &batt_0); + break; + } + usb_prev = usb_present; + stage_prev = stage; } #endif /* IS_ENABLED(CONFIG_USB_DEVICE_STACK) */ } diff --git a/app/boards/arm/corneish_zen/widgets/layer_status.c b/app/boards/arm/corneish_zen/widgets/layer_status.c index 3dc33613..b7c78121 100644 --- a/app/boards/arm/corneish_zen/widgets/layer_status.c +++ b/app/boards/arm/corneish_zen/widgets/layer_status.c @@ -27,6 +27,16 @@ static void set_layer_symbol(lv_obj_t *label, struct layer_status_state state) { const char *layer_label = state.label; uint8_t active_layer_index = state.index; +#if IS_ENABLED(CONFIG_ZMK_DISPLAY_HIDE_MOMENTARY_LAYERS) + static uint8_t last_perm_index = 255; + if (!zmk_keymap_layer_momentary(active_layer_index) && last_perm_index != active_layer_index) { + last_perm_index = active_layer_index; + LOG_DBG("Last perm layer index updated to %i", active_layer_index); + } else { + return; + } +#endif + if (layer_label == NULL) { char text[6] = {}; diff --git a/app/include/zmk/keymap.h b/app/include/zmk/keymap.h index 9ce140bf..6f3df7f5 100644 --- a/app/include/zmk/keymap.h +++ b/app/include/zmk/keymap.h @@ -17,8 +17,10 @@ typedef uint32_t zmk_keymap_layers_state_t; uint8_t zmk_keymap_layer_default(); zmk_keymap_layers_state_t zmk_keymap_layer_state(); bool zmk_keymap_layer_active(uint8_t layer); +bool zmk_keymap_layer_momentary(uint8_t layer); +bool zmk_keymap_layers_any_momentary(zmk_keymap_layers_state_t layers_mask); uint8_t zmk_keymap_highest_layer_active(); -int zmk_keymap_layer_activate(uint8_t layer); +int zmk_keymap_layer_activate(uint8_t layer, bool momentary); int zmk_keymap_layer_deactivate(uint8_t layer); int zmk_keymap_layer_toggle(uint8_t layer); int zmk_keymap_layer_to(uint8_t layer); diff --git a/app/module/drivers/display/Kconfig.il0323 b/app/module/drivers/display/Kconfig.il0323 index f3308c16..2704023c 100644 --- a/app/module/drivers/display/Kconfig.il0323 +++ b/app/module/drivers/display/Kconfig.il0323 @@ -8,4 +8,12 @@ config IL0323 depends on SPI depends on HEAP_MEM_POOL_SIZE != 0 help - Enable driver for IL0323 compatible controller. \ No newline at end of file + Enable driver for IL0323 compatible controller. + +config IL0323_INVERT + bool "Invert display" + default n + +config IL0323_ALTERNATIVE_REFRESH + bool "Use an alternative approach for partial refreshes" + default n diff --git a/app/module/drivers/display/il0323.c b/app/module/drivers/display/il0323.c index 6555e5c1..9f16d229 100644 --- a/app/module/drivers/display/il0323.c +++ b/app/module/drivers/display/il0323.c @@ -124,7 +124,11 @@ static int il0323_write(const struct device *dev, const uint16_t x, const uint16 ptl[IL0323_PTL_HRED_IDX] = x_end_idx; ptl[IL0323_PTL_VRST_IDX] = y; ptl[IL0323_PTL_VRED_IDX] = y_end_idx; +#if IS_ENABLED(CONFIG_IL0323_ALTERNATIVE_REFRESH) + ptl[sizeof(ptl) - 1] = 0; // limits fading outside of refresh window +#else ptl[sizeof(ptl) - 1] = IL0323_PTL_PT_SCAN; +#endif LOG_HEXDUMP_DBG(ptl, sizeof(ptl), "ptl"); il0323_busy_wait(cfg); @@ -242,8 +246,12 @@ static void il0323_get_capabilities(const struct device *dev, struct display_cap memset(caps, 0, sizeof(struct display_capabilities)); caps->x_resolution = EPD_PANEL_WIDTH; caps->y_resolution = EPD_PANEL_HEIGHT; - caps->supported_pixel_formats = PIXEL_FORMAT_MONO10; + caps->supported_pixel_formats = PIXEL_FORMAT_MONO10 | PIXEL_FORMAT_MONO01; +#if IS_ENABLED(CONFIG_IL0323_INVERT) + caps->current_pixel_format = PIXEL_FORMAT_MONO01; +#else caps->current_pixel_format = PIXEL_FORMAT_MONO10; +#endif caps->screen_info = SCREEN_INFO_MONO_MSB_FIRST | SCREEN_INFO_EPD; } @@ -254,7 +262,7 @@ static int il0323_set_orientation(const struct device *dev, } static int il0323_set_pixel_format(const struct device *dev, const enum display_pixel_format pf) { - if (pf == PIXEL_FORMAT_MONO10) { + if ((pf == PIXEL_FORMAT_MONO10) || (pf == PIXEL_FORMAT_MONO10)) { return 0; } diff --git a/app/src/behaviors/behavior_momentary_layer.c b/app/src/behaviors/behavior_momentary_layer.c index c2bd0ffc..21a9d8d8 100644 --- a/app/src/behaviors/behavior_momentary_layer.c +++ b/app/src/behaviors/behavior_momentary_layer.c @@ -23,7 +23,7 @@ static int behavior_mo_init(const struct device *dev) { return 0; }; static int mo_keymap_binding_pressed(struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event) { LOG_DBG("position %d layer %d", event.position, binding->param1); - return zmk_keymap_layer_activate(binding->param1); + return zmk_keymap_layer_activate(binding->param1, true); } static int mo_keymap_binding_released(struct zmk_behavior_binding *binding, diff --git a/app/src/conditional_layer.c b/app/src/conditional_layer.c index 9ba02a5c..f575205a 100644 --- a/app/src/conditional_layer.c +++ b/app/src/conditional_layer.c @@ -49,12 +49,12 @@ static const struct conditional_layer_cfg CONDITIONAL_LAYER_CFGS[] = { static const int32_t NUM_CONDITIONAL_LAYER_CFGS = sizeof(CONDITIONAL_LAYER_CFGS) / sizeof(*CONDITIONAL_LAYER_CFGS); -static void conditional_layer_activate(int8_t layer) { +static void conditional_layer_activate(int8_t layer, bool momentary) { // This may trigger another event that could, in turn, activate additional then-layers. However, // the process will eventually terminate (at worst, when every layer is active). if (!zmk_keymap_layer_active(layer)) { LOG_DBG("layer %d", layer); - zmk_keymap_layer_activate(layer); + zmk_keymap_layer_activate(layer, momentary); } } @@ -84,6 +84,7 @@ static int layer_state_changed_listener(const zmk_event_t *ev) { int8_t max_then_layer = -1; uint32_t then_layers = 0; uint32_t then_layer_state = 0; + uint32_t momentariness_state = 0; conditional_layer_updates_needed = false; @@ -100,13 +101,17 @@ static int layer_state_changed_listener(const zmk_event_t *ev) { // also trigger activation of another. if ((zmk_keymap_layer_state() & mask) == mask) { then_layer_state |= BIT(cfg->then_layer); + if (zmk_keymap_layers_any_momentary(mask)) { + momentariness_state |= BIT(cfg->then_layer); + } } } for (uint8_t layer = 0; layer <= max_then_layer; layer++) { if ((BIT(layer) & then_layers) != 0U) { if ((BIT(layer) & then_layer_state) != 0U) { - conditional_layer_activate(layer); + bool momentary = BIT(layer) & momentariness_state; + conditional_layer_activate(layer, momentary); } else { conditional_layer_deactivate(layer); } diff --git a/app/src/display/Kconfig b/app/src/display/Kconfig index a2029481..1613cfe4 100644 --- a/app/src/display/Kconfig +++ b/app/src/display/Kconfig @@ -176,6 +176,17 @@ choice ZMK_LV_FONT_DEFAULT_SMALL select LV_FONT_UNSCII_16 endchoice +config ZMK_DISPLAY_FULL_REFRESH_PERIOD + int "(Optional) Period to issue a full refresh to the display (in seconds)" + default 0 + help + Period in seconds for how often to completely refresh/redraw the whole screen. + Most useful for e-ink/EPD displays that require occasional full redraws. + +config ZMK_DISPLAY_HIDE_MOMENTARY_LAYERS + bool "Do not update layer widget for momentary layer changes" + default n + rsource "widgets/Kconfig" endif diff --git a/app/src/display/main.c b/app/src/display/main.c index e15e2de0..7666784b 100644 --- a/app/src/display/main.c +++ b/app/src/display/main.c @@ -50,6 +50,17 @@ struct k_work_q *zmk_display_work_q() { #endif } +#if CONFIG_ZMK_DISPLAY_FULL_REFRESH_PERIOD > 0 +void full_refresh_work_cb(struct k_work *work) { lv_obj_invalidate(lv_scr_act()); } + +K_WORK_DEFINE(full_refresh_work, full_refresh_work_cb); + +void full_refresh_timer_cb() { k_work_submit_to_queue(zmk_display_work_q(), &full_refresh_work); } + +K_TIMER_DEFINE(full_refresh_timer, full_refresh_timer_cb, NULL); + +#endif + void display_timer_cb() { k_work_submit_to_queue(zmk_display_work_q(), &display_tick_work); } K_TIMER_DEFINE(display_timer, display_timer_cb, NULL); @@ -57,6 +68,10 @@ K_TIMER_DEFINE(display_timer, display_timer_cb, NULL); void unblank_display_cb(struct k_work *work) { display_blanking_off(display); k_timer_start(&display_timer, K_MSEC(TICK_MS), K_MSEC(TICK_MS)); +#if CONFIG_ZMK_DISPLAY_FULL_REFRESH_PERIOD > 0 + k_timer_start(&full_refresh_timer, K_SECONDS(CONFIG_ZMK_DISPLAY_FULL_REFRESH_PERIOD), + K_SECONDS(CONFIG_ZMK_DISPLAY_FULL_REFRESH_PERIOD)); +#endif } #if IS_ENABLED(CONFIG_ZMK_DISPLAY_BLANK_ON_IDLE) @@ -64,6 +79,9 @@ void unblank_display_cb(struct k_work *work) { void blank_display_cb(struct k_work *work) { k_timer_stop(&display_timer); display_blanking_on(display); +#if CONFIG_ZMK_DISPLAY_FULL_REFRESH_PERIOD > 0 + k_timer_stop(&full_refresh_timer); +#endif } K_WORK_DEFINE(blank_display_work, blank_display_cb); K_WORK_DEFINE(unblank_display_work, unblank_display_cb); diff --git a/app/src/keymap.c b/app/src/keymap.c index bda69427..18dce60e 100644 --- a/app/src/keymap.c +++ b/app/src/keymap.c @@ -27,6 +27,7 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #include static zmk_keymap_layers_state_t _zmk_keymap_layer_state = 0; +static zmk_keymap_layers_state_t _zmk_keymap_layer_momentary = 0; static uint8_t _zmk_keymap_layer_default = 0; #define DT_DRV_COMPAT zmk_keymap @@ -78,7 +79,7 @@ static struct zmk_behavior_binding zmk_sensor_keymap[ZMK_KEYMAP_LAYERS_LEN] #endif /* ZMK_KEYMAP_HAS_SENSORS */ -static inline int set_layer_state(uint8_t layer, bool state) { +static inline int set_layer_state(uint8_t layer, bool state, bool momentary) { if (layer >= ZMK_KEYMAP_LAYERS_LEN) { return -EINVAL; } @@ -93,6 +94,7 @@ static inline int set_layer_state(uint8_t layer, bool state) { // Don't send state changes unless there was an actual change if (old_state != _zmk_keymap_layer_state) { LOG_DBG("layer_changed: layer %d state %d", layer, state); + WRITE_BIT(_zmk_keymap_layer_momentary, layer, momentary); ZMK_EVENT_RAISE(create_layer_state_changed(layer, state)); } @@ -113,6 +115,15 @@ bool zmk_keymap_layer_active(uint8_t layer) { return zmk_keymap_layer_active_with_state(layer, _zmk_keymap_layer_state); }; +bool zmk_keymap_layer_momentary(uint8_t layer) { + return layer != _zmk_keymap_layer_default && + (_zmk_keymap_layer_momentary & (BIT(layer))) == (BIT(layer)); +}; + +bool zmk_keymap_layers_any_momentary(zmk_keymap_layers_state_t layers_mask) { + return (_zmk_keymap_layer_momentary & layers_mask) > 0; +}; + uint8_t zmk_keymap_highest_layer_active() { for (uint8_t layer = ZMK_KEYMAP_LAYERS_LEN - 1; layer > 0; layer--) { if (zmk_keymap_layer_active(layer)) { @@ -122,16 +133,18 @@ uint8_t zmk_keymap_highest_layer_active() { return zmk_keymap_layer_default(); } -int zmk_keymap_layer_activate(uint8_t layer) { return set_layer_state(layer, true); }; +int zmk_keymap_layer_activate(uint8_t layer, bool momentary) { + return set_layer_state(layer, true, momentary); +}; -int zmk_keymap_layer_deactivate(uint8_t layer) { return set_layer_state(layer, false); }; +int zmk_keymap_layer_deactivate(uint8_t layer) { return set_layer_state(layer, false, false); }; int zmk_keymap_layer_toggle(uint8_t layer) { if (zmk_keymap_layer_active(layer)) { return zmk_keymap_layer_deactivate(layer); } - return zmk_keymap_layer_activate(layer); + return zmk_keymap_layer_activate(layer, false); }; int zmk_keymap_layer_to(uint8_t layer) { @@ -139,7 +152,7 @@ int zmk_keymap_layer_to(uint8_t layer) { zmk_keymap_layer_deactivate(i); } - zmk_keymap_layer_activate(layer); + zmk_keymap_layer_activate(layer, false); return 0; }