From 0a2bab4e38e2607235f89df5efc528b9f81c508f Mon Sep 17 00:00:00 2001 From: Ken Van Hoeylandt Date: Sat, 22 Aug 2026 23:34:32 +0200 Subject: [PATCH] Fixes --- Drivers/tca8418-module/bindings/ti,tca8418.yaml | 3 +-- Modules/lvgl-module/README.md | 2 +- Modules/lvgl-module/include/lvgl/devices/keyboard.h | 5 ++++- Modules/lvgl-module/source/devices/keyboard.cpp | 12 ++++++++++++ Tactility/Source/lvgl/Statusbar.cpp | 8 +++++--- TactilityKernel/include/tactility/drivers/keyboard.h | 2 +- 6 files changed, 24 insertions(+), 8 deletions(-) diff --git a/Drivers/tca8418-module/bindings/ti,tca8418.yaml b/Drivers/tca8418-module/bindings/ti,tca8418.yaml index 5a7856043..a6b217cef 100644 --- a/Drivers/tca8418-module/bindings/ti,tca8418.yaml +++ b/Drivers/tca8418-module/bindings/ti,tca8418.yaml @@ -35,8 +35,7 @@ properties: silkscreen/keymap column order - see reverse-columns). 0 = no key at this position (e.g. a blank matrix position, or a position handled as a modifier via shift-row/shift-col/ sym-row/sym-col instead). Non-zero bytes are sent as-is via KeyboardKeyData::key (a Unicode - codepoint - byte range covers Latin-1 - or an LVGL LV_KEY_* code for keys with no character - representation). + codepoint - byte range covers Latin-1 - for character and non-character keys). keymap-uc: type: array element-type: uint8_t diff --git a/Modules/lvgl-module/README.md b/Modules/lvgl-module/README.md index d36133022..2db25aacb 100644 --- a/Modules/lvgl-module/README.md +++ b/Modules/lvgl-module/README.md @@ -43,7 +43,7 @@ LVGL's malloc/realloc/free can be routed through a custom backend instead of its plain `malloc`. Three things are required: **1. Select the backend.** On ESP32, via Kconfig (`sdkconfig`): -``` +```sdkconfig CONFIG_LV_USE_CUSTOM_MALLOC=y ``` On Simulator/POSIX, ESP-IDF's Kconfig doesn't apply - select it in `lv_conf.h` instead: diff --git a/Modules/lvgl-module/include/lvgl/devices/keyboard.h b/Modules/lvgl-module/include/lvgl/devices/keyboard.h index 66e62dc71..b134178f3 100644 --- a/Modules/lvgl-module/include/lvgl/devices/keyboard.h +++ b/Modules/lvgl-module/include/lvgl/devices/keyboard.h @@ -21,9 +21,12 @@ struct LvglSoftwareKeyboard { * @warning Caller must hold the LVGL lock (see lvgl_lock() in lvgl_module.h) — call this from * LvglModuleConfig.on_start, or after calling lvgl_lock() explicitly. * + * @note Idempotent per device: if an indev is already bound to this device (see + * lvgl_keyboard_find_by_device()), that indev is returned instead of creating a second one. + * * @param[in] device a device of type KEYBOARD_TYPE * @param[in] display the display this indev should be associated with, or NULL to leave it unset - * @param[out] out_indev the created indev, valid only when ERROR_NONE is returned + * @param[out] out_indev the created (or already-existing) indev, valid only when ERROR_NONE is returned * @retval ERROR_NONE on success * @retval ERROR_INVALID_ARGUMENT if device or out_indev is NULL, or device is not of type KEYBOARD_TYPE * @retval ERROR_OUT_OF_MEMORY if allocation failed diff --git a/Modules/lvgl-module/source/devices/keyboard.cpp b/Modules/lvgl-module/source/devices/keyboard.cpp index b50e88d84..107a5d1d6 100644 --- a/Modules/lvgl-module/source/devices/keyboard.cpp +++ b/Modules/lvgl-module/source/devices/keyboard.cpp @@ -84,6 +84,18 @@ error_t lvgl_keyboard_add(struct Device* device, lv_display_t* display, lv_indev return ERROR_INVALID_ARGUMENT; } + // A device can reach here twice: lvgl_devices_attach()'s boot scan binds every KEYBOARD_TYPE + // device unconditionally (started or not), and a device that wasn't started yet at that point + // fires DEVICE_EVENT_STARTED later, driving a second call through + // KeyboardDeviceListener::onKeyboardDeviceStarted(). Without this check that would create a + // second indev for the same device, and onKeyboardDeviceStopped() only ever removes one of + // them, leaving the other dangling - polling a destructed device on the next LVGL tick. + lv_indev_t* existing = lvgl_keyboard_find_by_device(device); + if (existing != NULL) { + *out_indev = existing; + return ERROR_NONE; + } + auto* wrapper = new(std::nothrow) LvglDeviceContext(nullptr); if (wrapper == NULL) { return ERROR_OUT_OF_MEMORY; diff --git a/Tactility/Source/lvgl/Statusbar.cpp b/Tactility/Source/lvgl/Statusbar.cpp index ce2d4aaac..716e85e42 100644 --- a/Tactility/Source/lvgl/Statusbar.cpp +++ b/Tactility/Source/lvgl/Statusbar.cpp @@ -191,13 +191,15 @@ lv_obj_t* statusbar_create(lv_obj_t* parent) { update_icon(image, &(statusbar_data.icons[i])); } - statusbar_data.mutex.unlock(); - // Only now is statusbar->icons[] fully populated - see statusbar_constructor()'s comment for - // why the subscription can't be registered any earlier. + // why the subscription can't be registered any earlier. Subscribing before unlocking (rather + // than after) closes a narrow window where a concurrent statusbar_icon_add()/_remove()/ + // _set_image()/_set_visibility() call could publish - and be missed by this instance - after + // the icons are populated but before it's subscribed. statusbar->pubsub_subscription = statusbar_data.pubsub->subscribe([statusbar](auto) { statusbar_pubsub_event(statusbar); }); + statusbar_data.mutex.unlock(); return obj; } diff --git a/TactilityKernel/include/tactility/drivers/keyboard.h b/TactilityKernel/include/tactility/drivers/keyboard.h index 74978441f..067596ea3 100644 --- a/TactilityKernel/include/tactility/drivers/keyboard.h +++ b/TactilityKernel/include/tactility/drivers/keyboard.h @@ -115,7 +115,7 @@ struct KeyboardApi { /** * @brief Optional: reports whether the keyboard is physically present right now. Only - * meaningful for hot-pluggable/detachable keyboarcardputer_keyboard.cppds (e.g. a removable accessory) whose + * meaningful for hot-pluggable/detachable keyboards (e.g. a removable accessory) whose * kernel device is constructed and started once at boot regardless of physical attachment - * leave NULL for a keyboard that's always physically present whenever its device is active * (the common case; callers must treat NULL the same as "always present").