diff --git a/Devices/m5stack-tab5/Source/devices/tab5_keyboard.cpp b/Devices/m5stack-tab5/Source/devices/tab5_keyboard.cpp index 3de918af2..25ef0d004 100644 --- a/Devices/m5stack-tab5/Source/devices/tab5_keyboard.cpp +++ b/Devices/m5stack-tab5/Source/devices/tab5_keyboard.cpp @@ -575,6 +575,7 @@ static error_t tab5_keyboard_read_key(Device* device, KeyboardKeyData* data) { static const KeyboardApi tab5_keyboard_api = { .read_key = tab5_keyboard_read_key, + .is_present = tab5_keyboard_is_attached, }; // Defined in module.cpp - this driver is registered directly by m5stack-tab5's own module, diff --git a/Modules/lvgl-module/source/devices/keyboard.cpp b/Modules/lvgl-module/source/devices/keyboard.cpp index bdc08780a..0774fcece 100644 --- a/Modules/lvgl-module/source/devices/keyboard.cpp +++ b/Modules/lvgl-module/source/devices/keyboard.cpp @@ -106,8 +106,11 @@ bool lvgl_hardware_keyboard_is_available() { return false; } + // TODO: Refactor the driver subsystem to so it does proper probing/releasing of such devices + // This work-around exists for the Tab5 keyboard driver. + bool present = keyboard_is_present(keyboard_device); device_put(keyboard_device); - return true; + return present; } void lvgl_hardware_keyboard_add_custom(lv_indev_t* indev) { @@ -137,9 +140,15 @@ static void textarea_show_keyboard(lv_event_t* event) { } static void textarea_hide_keyboard(lv_event_t* event) { - if (last_software_keyboard.object != nullptr) { - lvgl_software_keyboard_hide(&last_software_keyboard); + if (last_software_keyboard.object == nullptr) { + return; } + // Only hide if the keyboard is actually bound to the textarea that triggered this + lv_obj_t* target = lv_event_get_current_target_obj(event); + if (lv_keyboard_get_textarea(last_software_keyboard.object) != target) { + return; + } + lvgl_software_keyboard_hide(&last_software_keyboard); } void lvgl_software_keyboard_construct(LvglSoftwareKeyboard* keyboard, lv_obj_t* parent) { @@ -187,6 +196,7 @@ void lvgl_keyboard_add_textarea(LvglSoftwareKeyboard* keyboard, lv_obj_t* textar lv_obj_add_event_cb(textarea, textarea_show_keyboard, LV_EVENT_FOCUSED, nullptr); lv_obj_add_event_cb(textarea, textarea_hide_keyboard, LV_EVENT_DEFOCUSED, nullptr); lv_obj_add_event_cb(textarea, textarea_hide_keyboard, LV_EVENT_READY, nullptr); + lv_obj_add_event_cb(textarea, textarea_hide_keyboard, LV_EVENT_DELETE, nullptr); } // lv_obj_t auto-remove themselves from the group when they are destroyed (last checked in LVGL 8.3) diff --git a/Tactility/Source/Tactility.cpp b/Tactility/Source/Tactility.cpp index 024487da6..769cd711c 100644 --- a/Tactility/Source/Tactility.cpp +++ b/Tactility/Source/Tactility.cpp @@ -42,6 +42,7 @@ #include #include +#include #include #include #include @@ -361,6 +362,9 @@ static void stopAppFromToolbar(lv_event_t*) { app_event_emit(topmost, &event); } +// The on-screen keyboard widget itself, constructed during windowManagerScreenInit +static LvglSoftwareKeyboard softwareKeyboard { .object = nullptr }; + static lv_obj_t* windowManagerScreenInit(lv_obj_t* root) { lv_obj_t* vertical_container = lv_obj_create(root); lv_obj_set_size(vertical_container, LV_PCT(100), LV_PCT(100)); @@ -380,6 +384,11 @@ static lv_obj_t* windowManagerScreenInit(lv_obj_t* root) { lv_obj_set_flex_grow(app_container, 1); lv_obj_set_flex_flow(app_container, LV_FLEX_FLOW_COLUMN); + // Parented to root (not app_container/vertical_container) so it overlays on top of + // everything, including the statusbar, regardless of which app is showing. Hidden until a + // focused textarea shows it (see lvgl_keyboard_add_textarea()/textarea_show_keyboard()). + lvgl_software_keyboard_construct(&softwareKeyboard, root); + return app_container; } @@ -440,6 +449,10 @@ static void onLvglStarted() { } static void onLvglStopped() { + if (softwareKeyboard.object != nullptr) { + lvgl_software_keyboard_destruct(&softwareKeyboard); + } + module_stop(&lvgl_window_manager_module); lvgl::stopUsbHidInput(); diff --git a/TactilityKernel/include/tactility/drivers/keyboard.h b/TactilityKernel/include/tactility/drivers/keyboard.h index f1390f22d..56386ba01 100644 --- a/TactilityKernel/include/tactility/drivers/keyboard.h +++ b/TactilityKernel/include/tactility/drivers/keyboard.h @@ -66,6 +66,17 @@ struct KeyboardApi { * @retval ERROR_NOT_SUPPORTED when this device has no backlight */ error_t (*get_backlight)(struct Device* device, struct Device** backlight_device); + + /** + * @brief Optional: reports whether the keyboard is physically present right now. Only + * 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"). + * @param[in] device the keyboard device + * @return true if physically attached/present + */ + bool (*is_present)(struct Device* device); }; /** @@ -83,6 +94,14 @@ error_t keyboard_read_key(struct Device* device, struct KeyboardKeyData* data); */ error_t keyboard_get_backlight(struct Device* device, struct Device** backlight_device); +/** + * @brief Whether the keyboard device is physically present right now. True when the driver + * doesn't implement KeyboardApi::is_present (i.e. it's always physically present whenever its + * device is active) - see that field's doc comment. + * @param[in] device the keyboard device + */ +bool keyboard_is_present(struct Device* device); + extern const struct DeviceType KEYBOARD_TYPE; #ifdef __cplusplus diff --git a/TactilityKernel/source/drivers/keyboard.cpp b/TactilityKernel/source/drivers/keyboard.cpp index 8c14bcd28..3bc870259 100644 --- a/TactilityKernel/source/drivers/keyboard.cpp +++ b/TactilityKernel/source/drivers/keyboard.cpp @@ -28,6 +28,16 @@ error_t keyboard_get_backlight(Device* device, Device** backlight_device) { return KEYBOARD_DRIVER_API(driver)->get_backlight(device, backlight_device); } +bool keyboard_is_present(Device* device) { + const auto* driver = device_get_driver(device); + + if (KEYBOARD_DRIVER_API(driver)->is_present == nullptr) { + return true; + } + + return KEYBOARD_DRIVER_API(driver)->is_present(device); +} + const DeviceType KEYBOARD_TYPE { .name = "keyboard" };