Touch and display driver subsystems reworked (and more) (#302)

- Refactored `TouchDevice`: it can now start/stop LVGL separately, and it has an optional `TouchDriver` interface
- Refactored `DisplayDevice`: it can now start/stop LVGL separately, and it has an optional `DisplayDriver` interface
- Updated all boards and drivers for above changes
- LVGL can now be stopped and (re)started on the fly
- Fixed issues with restarting Gui and Statusbar services
- Refactored `Gui` service to be class and renamed `Gui` to `GuiService`
- Fixed `Statusbar` service: forgot to deregister one of the icons
- Updated `esp_lcd_st7701` to v1.1.3
- `lv_textarea_create()` now automatically registers the hardware keyboard hooks (by wrapping the function)
- Fixed and updated `tactility.py`
- Cleanup of a lot of code
- `BootInitLvglBegin` and `BootInitLvglEnd` are replaced by `LvglStarted` and `LvglStopped`.
- Introduced `tt::service::State` which is accessible via `tt::service::getState()` (and internally via `ServiceInstance`)
- Started replacing `#define TAG` with `constexpr auto TAG = "..";`
This commit is contained in:
Ken Van Hoeylandt
2025-08-16 20:22:49 +02:00
committed by GitHub
parent 15f4fbfdc6
commit d875ade8cb
127 changed files with 1907 additions and 1605 deletions
-120
View File
@@ -1,120 +0,0 @@
#include "Tactility/app/display/DisplaySettings.h"
#include "Tactility/lvgl/Keyboard.h"
#include "Tactility/hal/display/DisplayDevice.h"
#include "Tactility/hal/touch/TouchDevice.h"
#include <Tactility/hal/Configuration.h>
#include <Tactility/hal/keyboard/KeyboardDevice.h>
#include <Tactility/kernel/SystemEvents.h>
#ifdef ESP_PLATFORM
#include "Tactility/lvgl/EspLvglPort.h"
#endif
#include <lvgl.h>
namespace tt::lvgl {
#define TAG "lvgl_init"
static std::shared_ptr<tt::hal::display::DisplayDevice> initDisplay(const hal::Configuration& config) {
assert(config.createDisplay);
auto display = config.createDisplay();
assert(display != nullptr);
if (!display->start()) {
TT_LOG_E(TAG, "Display start failed");
return nullptr;
}
lv_display_t* lvgl_display = display->getLvglDisplay();
assert(lvgl_display);
if (display->supportsBacklightDuty()) {
display->setBacklightDuty(0);
}
void* existing_display_user_data = lv_display_get_user_data(lvgl_display);
// esp_lvgl_port users user_data by default, so we have to modify the source
// this is a check for when we upgrade esp_lvgl_port and forget to modify it again
assert(existing_display_user_data == nullptr);
lv_display_set_user_data(lvgl_display, display.get());
lv_display_rotation_t rotation = app::display::getRotation();
if (rotation != lv_disp_get_rotation(lv_disp_get_default())) {
lv_disp_set_rotation(lv_disp_get_default(), static_cast<lv_display_rotation_t>(rotation));
}
return display;
}
static bool initTouch(const std::shared_ptr<hal::display::DisplayDevice>& display, const std::shared_ptr<hal::touch::TouchDevice>& touch) {
TT_LOG_I(TAG, "Touch init");
assert(display);
assert(touch);
if (touch->start(display->getLvglDisplay())) {
return true;
} else {
TT_LOG_E(TAG, "Touch init failed");
return false;
}
}
static bool initKeyboard(const std::shared_ptr<hal::display::DisplayDevice>& display, const std::shared_ptr<hal::keyboard::KeyboardDevice>& keyboard) {
TT_LOG_I(TAG, "Keyboard init");
assert(display);
assert(keyboard);
if (keyboard->isAttached()) {
if (keyboard->start(display->getLvglDisplay())) {
lv_indev_t* keyboard_indev = keyboard->getLvglIndev();
lv_indev_set_user_data(keyboard_indev, keyboard.get());
tt::lvgl::hardware_keyboard_set_indev(keyboard_indev);
TT_LOG_I(TAG, "Keyboard started");
return true;
} else {
TT_LOG_E(TAG, "Keyboard start failed");
return false;
}
} else {
TT_LOG_E(TAG, "Keyboard attach failed");
return false;
}
}
void init(const hal::Configuration& config) {
TT_LOG_I(TAG, "Starting");
kernel::publishSystemEvent(kernel::SystemEvent::BootInitLvglBegin);
#ifdef ESP_PLATFORM
if (config.lvglInit == hal::LvglInit::Default && !initEspLvglPort()) {
return;
}
#endif
auto display = initDisplay(config);
if (display == nullptr) {
return;
}
hal::registerDevice(display);
auto touch = display->createTouch();
if (touch != nullptr) {
hal::registerDevice(touch);
initTouch(display, touch);
}
if (config.createKeyboard) {
auto keyboard = config.createKeyboard();
if (keyboard != nullptr) {
hal::registerDevice(keyboard);
initKeyboard(display, keyboard);
}
}
TT_LOG_I(TAG, "Finished");
kernel::publishSystemEvent(kernel::SystemEvent::BootInitLvglEnd);
}
} // namespace
+17 -8
View File
@@ -1,20 +1,33 @@
#include "Tactility/lvgl/Keyboard.h"
#include "Tactility/service/gui/Gui.h"
#include "Tactility/service/gui/GuiService.h"
#include <Tactility/service/espnow/EspNowService.h>
namespace tt::lvgl {
static lv_indev_t* keyboard_device = nullptr;
void software_keyboard_show(lv_obj_t* textarea) {
service::gui::softwareKeyboardShow(textarea);
auto gui_service = service::gui::findService();
if (gui_service != nullptr) {
gui_service->softwareKeyboardShow(textarea);
}
}
void software_keyboard_hide() {
service::gui::softwareKeyboardHide();
auto gui_service = service::gui::findService();
if (gui_service != nullptr) {
gui_service->softwareKeyboardHide();
}
}
bool software_keyboard_is_enabled() {
return service::gui::softwareKeyboardIsEnabled();
auto gui_service = service::gui::findService();
if (gui_service != nullptr) {
return gui_service->softwareKeyboardIsEnabled();
} else {
return false;
}
}
void software_keyboard_activate(lv_group_t* group) {
@@ -37,8 +50,4 @@ void hardware_keyboard_set_indev(lv_indev_t* device) {
keyboard_device = device;
}
void keyboard_add_textarea(lv_obj_t* textarea) {
service::gui::keyboardAddTextArea(textarea);
}
}
+210
View File
@@ -0,0 +1,210 @@
#include "Tactility/app/display/DisplaySettings.h"
#include "Tactility/lvgl/Keyboard.h"
#include "Tactility/lvgl/Lvgl.h"
#include "Tactility/hal/display/DisplayDevice.h"
#include "Tactility/hal/touch/TouchDevice.h"
#include <Tactility/hal/Configuration.h>
#include <Tactility/hal/keyboard/KeyboardDevice.h>
#include <Tactility/kernel/SystemEvents.h>
#ifdef ESP_PLATFORM
#include "Tactility/lvgl/EspLvglPort.h"
#endif
#include <lvgl.h>
#include <Tactility/Tactility.h>
#include <Tactility/TactilityHeadless.h>
#include <Tactility/lvgl/LvglSync.h>
#include <Tactility/service/ServiceRegistry.h>
namespace tt::lvgl {
#define TAG "Lvgl"
static bool started = false;
static std::shared_ptr<hal::display::DisplayDevice> createDisplay(const hal::Configuration& config) {
assert(config.createDisplay);
auto display = config.createDisplay();
assert(display != nullptr);
if (!display->start()) {
TT_LOG_E(TAG, "Display start failed");
return nullptr;
}
if (display->supportsBacklightDuty()) {
display->setBacklightDuty(0);
}
return display;
}
void init(const hal::Configuration& config) {
TT_LOG_I(TAG, "Init started");
#ifdef ESP_PLATFORM
if (config.lvglInit == hal::LvglInit::Default && !initEspLvglPort()) {
return;
}
#endif
auto display = createDisplay(config);
if (display == nullptr) {
return;
}
hal::registerDevice(display);
auto touch = display->getTouchDevice();
if (touch != nullptr) {
touch->start();
hal::registerDevice(touch);
}
auto configuration = hal::getConfiguration();
if (configuration->createKeyboard) {
auto keyboard = configuration->createKeyboard();
if (keyboard != nullptr) {
hal::registerDevice(keyboard);
}
}
start();
TT_LOG_I(TAG, "Init finished");
}
bool isStarted() {
return started;
}
void start() {
TT_LOG_I(TAG, "Start LVGL");
if (started) {
TT_LOG_W(TAG, "Can't start LVGL twice");
return;
}
auto lock = getSyncLock()->asScopedLock();
lock.lock();
// Start displays (their related touch devices start automatically within)
auto displays = hal::findDevices<hal::display::DisplayDevice>(hal::Device::Type::Display);
for (auto display : displays) {
if (display->supportsLvgl() && display->startLvgl()) {
auto lvgl_display = display->getLvglDisplay();
assert(lvgl_display != nullptr);
lv_display_rotation_t rotation = app::display::getRotation();
if (rotation != lv_display_get_rotation(lvgl_display)) {
lv_display_set_rotation(lvgl_display, rotation);
}
}
}
// Start touch
auto touch_devices = hal::findDevices<hal::touch::TouchDevice>(hal::Device::Type::Touch);
for (auto touch_device : touch_devices) {
if (displays.size() > 0) {
// TODO: Consider implementing support for multiple displays
auto display = displays[0];
// Start any touch devices that haven't been started yet
if (touch_device->supportsLvgl() && touch_device->getLvglIndev() == nullptr) {
touch_device->startLvgl(display->getLvglDisplay());
}
}
}
// Start keyboards
auto keyboards = hal::findDevices<hal::keyboard::KeyboardDevice>(hal::Device::Type::Keyboard);
for (auto keyboard : keyboards) {
if (displays.size() > 0) {
// TODO: Consider implementing support for multiple displays
auto display = displays[0];
if (keyboard->isAttached()) {
if (keyboard->startLvgl(display->getLvglDisplay())) {
lv_indev_t* keyboard_indev = keyboard->getLvglIndev();
hardware_keyboard_set_indev(keyboard_indev);
TT_LOG_I(TAG, "Keyboard started");
} else {
TT_LOG_E(TAG, "Keyboard start failed");
}
}
}
}
// Restart services
if (service::getState("Gui") == service::State::Stopped) {
service::startService("Gui");
} else {
TT_LOG_E(TAG, "Gui service is not in Stopped state");
}
if (service::getState("Statusbar") == service::State::Stopped) {
service::startService("Statusbar");
} else {
TT_LOG_E(TAG, "Statusbar service is not in Stopped state");
}
// Finalize
kernel::publishSystemEvent(kernel::SystemEvent::LvglStarted);
started = true;
}
void stop() {
TT_LOG_I(TAG, "Stop LVGL");
if (!started) {
TT_LOG_W(TAG, "Can't stop LVGL: not started");
return;
}
auto lock = getSyncLock()->asScopedLock();
lock.lock();
// Stop services that highly depend on LVGL
service::stopService("Statusbar");
service::stopService("Gui");
// Stop keyboards
auto keyboards = hal::findDevices<hal::keyboard::KeyboardDevice>(hal::Device::Type::Keyboard);
for (auto keyboard : keyboards) {
if (keyboard->getLvglIndev() != nullptr) {
keyboard->stopLvgl();
}
}
// Stop touch
// The display generally stops their own touch devices, but we'll clean up anything that didn't
auto touch_devices = hal::findDevices<hal::touch::TouchDevice>(hal::Device::Type::Touch);
for (auto touch_device : touch_devices) {
if (touch_device->getLvglIndev() != nullptr) {
touch_device->stopLvgl();
}
}
// Stop displays (and their touch devices)
auto displays = hal::findDevices<hal::display::DisplayDevice>(hal::Device::Type::Display);
for (auto display : displays) {
if (display->supportsLvgl() && display->getLvglDisplay() != nullptr && !display->stopLvgl()) {
TT_LOG_E("HelloWorld", "Failed to detach display from LVGL");
}
}
started = false;
kernel::publishSystemEvent(kernel::SystemEvent::LvglStopped);
}
} // namespace
+26
View File
@@ -0,0 +1,26 @@
#ifdef ESP_PLATFORM
#include "Tactility/app/App.h"
#include "Tactility/service/gui/GuiService.h"
#include <lvgl.h>
#include <Tactility/service/gui/GuiService.h>
extern "C" {
extern lv_obj_t * __real_lv_textarea_create(lv_obj_t * parent);
lv_obj_t * __wrap_lv_textarea_create(lv_obj_t * parent) {
auto textarea = __real_lv_textarea_create(parent);
auto gui_service = tt::service::gui::findService();
if (gui_service != nullptr) {
gui_service->keyboardAddTextArea(textarea);
}
return textarea;
}
}
#endif // ESP_PLATFORM