diff --git a/App/Source/Main.cpp b/App/Source/Main.cpp index 5586930d..574d3f75 100644 --- a/App/Source/Main.cpp +++ b/App/Source/Main.cpp @@ -5,7 +5,7 @@ #include "tt_init.h" #endif -// extern const tt::app::AppManifest hello_world_app; +extern const tt::app::AppManifest hello_world_app; extern "C" { diff --git a/App/idf_component.yml b/App/idf_component.yml index 4dbbcb52..7b143f95 100644 --- a/App/idf_component.yml +++ b/App/idf_component.yml @@ -9,7 +9,7 @@ dependencies: espressif/esp_io_expander: "1.0.1" espressif/esp_io_expander_tca95xx_16bit: "1.0.1" espressif/esp_lcd_st7701: - version: "1.1.1" + version: "1.1.3" rules: - if: "target in [esp32s3, esp32p4]" espressif/esp_lcd_st7796: diff --git a/Boards/CYD-2432S024C/Source/hal/YellowDisplay.cpp b/Boards/CYD-2432S024C/Source/hal/YellowDisplay.cpp index 27ace208..b30d7d75 100644 --- a/Boards/CYD-2432S024C/Source/hal/YellowDisplay.cpp +++ b/Boards/CYD-2432S024C/Source/hal/YellowDisplay.cpp @@ -32,5 +32,6 @@ std::shared_ptr createDisplay() { configuration->mirrorX = true; configuration->backlightDutyFunction = driver::pwmbacklight::setBacklightDuty; - return std::make_shared(std::move(configuration)); + auto display = std::make_shared(std::move(configuration)); + return std::reinterpret_pointer_cast(display); } diff --git a/Boards/CYD-2432S032C/Source/CYD2432S032C.cpp b/Boards/CYD-2432S032C/Source/CYD2432S032C.cpp index a90fb644..f313b2e8 100644 --- a/Boards/CYD-2432S032C/Source/CYD2432S032C.cpp +++ b/Boards/CYD-2432S032C/Source/CYD2432S032C.cpp @@ -14,7 +14,7 @@ bool initBoot() { // This display has a weird glitch with gamma during boot, which results in uneven dark gray colours. // Setting gamma curve index to 0 doesn't work at boot for an unknown reason, so we set the curve index to 1: - tt::kernel::subscribeSystemEvent(tt::kernel::SystemEvent::BootInitLvglEnd, [](auto event) { + tt::kernel::subscribeSystemEvent(tt::kernel::SystemEvent::BootSplash, [](auto) { auto display = tt::hal::findFirstDevice(tt::hal::Device::Type::Display); assert(display != nullptr); tt::lvgl::lock(portMAX_DELAY); diff --git a/Boards/CYD-2432S032C/Source/hal/CydDisplay.cpp b/Boards/CYD-2432S032C/Source/hal/CydDisplay.cpp index c0466f6f..ed6b00a0 100644 --- a/Boards/CYD-2432S032C/Source/hal/CydDisplay.cpp +++ b/Boards/CYD-2432S032C/Source/hal/CydDisplay.cpp @@ -35,5 +35,6 @@ std::shared_ptr createDisplay() { configuration->backlightDutyFunction = driver::pwmbacklight::setBacklightDuty; - return std::make_shared(std::move(configuration)); + auto display = std::make_shared(std::move(configuration)); + return std::reinterpret_pointer_cast(display); } diff --git a/Boards/CYD-4848S040C/CMakeLists.txt b/Boards/CYD-4848S040C/CMakeLists.txt index c5f6eb49..ae2c58a5 100644 --- a/Boards/CYD-4848S040C/CMakeLists.txt +++ b/Boards/CYD-4848S040C/CMakeLists.txt @@ -3,5 +3,5 @@ file(GLOB_RECURSE SOURCE_FILES Source/*.c*) idf_component_register( SRCS ${SOURCE_FILES} INCLUDE_DIRS "Source" - REQUIRES Tactility esp_lvgl_port esp_lcd esp_lcd_st7701 esp_lcd_panel_io_additions GT911 PwmBacklight driver vfs fatfs + REQUIRES Tactility EspLcdCompat esp_lcd_st7701 esp_lcd_panel_io_additions GT911 PwmBacklight driver vfs fatfs ) diff --git a/Boards/CYD-4848S040C/Source/hal/CydDisplay.cpp b/Boards/CYD-4848S040C/Source/hal/CydDisplay.cpp index ac9f3e8e..2ad2ceb9 100644 --- a/Boards/CYD-4848S040C/Source/hal/CydDisplay.cpp +++ b/Boards/CYD-4848S040C/Source/hal/CydDisplay.cpp @@ -1,19 +1,17 @@ #include "CydDisplay.h" -#include "PwmBacklight.h" #include +#include #include #include #include #include #include -#include #include #include -#include -#define TAG "cyd_display" +constexpr auto TAG = "ST7701"; static const st7701_lcd_init_cmd_t st7701_lcd_init_cmds[] = { // {cmd, { data }, data_size, delay_ms} @@ -58,9 +56,7 @@ static const st7701_lcd_init_cmd_t st7701_lcd_init_cmds[] = { {0x29, (uint8_t[]) {0x00}, 0, 0}, //Display On }; -bool CydDisplay::start() { - TT_LOG_I(TAG, "Starting"); - +bool CydDisplay::createIoHandle(esp_lcd_panel_io_handle_t& outHandle) { spi_line_config_t line_config = { .cs_io_type = IO_TYPE_GPIO, .cs_gpio_num = GPIO_NUM_39, @@ -68,11 +64,13 @@ bool CydDisplay::start() { .scl_gpio_num = GPIO_NUM_48, .sda_io_type = IO_TYPE_GPIO, .sda_gpio_num = GPIO_NUM_47, - .io_expander = NULL, + .io_expander = nullptr, }; esp_lcd_panel_io_3wire_spi_config_t panel_io_config = ST7701_PANEL_IO_3WIRE_SPI_CONFIG(line_config, 0); - ESP_ERROR_CHECK(esp_lcd_new_panel_io_3wire_spi(&panel_io_config, &ioHandle)); + return esp_lcd_new_panel_io_3wire_spi(&panel_io_config, &outHandle) == ESP_OK; +} +bool CydDisplay::createPanelHandle(esp_lcd_panel_io_handle_t ioHandle, esp_lcd_panel_handle_t& panelHandle) { const esp_lcd_rgb_panel_config_t rgb_config = { .clk_src = LCD_CLK_SRC_DEFAULT, .timings = { @@ -179,7 +177,11 @@ bool CydDisplay::start() { TT_LOG_E(TAG, "Failed to turn display on"); } - const lvgl_port_display_cfg_t disp_cfg = { + return true; +} + +lvgl_port_display_cfg_t CydDisplay::getLvglPortDisplayConfig(esp_lcd_panel_io_handle_t ioHandle, esp_lcd_panel_handle_t panelHandle) { + return { .io_handle = ioHandle, .panel_handle = panelHandle, .control_handle = nullptr, @@ -204,44 +206,29 @@ bool CydDisplay::start() { .direct_mode = false } }; +} - const lvgl_port_display_rgb_cfg_t rgb_cfg = { +lvgl_port_display_rgb_cfg_t CydDisplay::getLvglPortDisplayRgbConfig(esp_lcd_panel_io_handle_t ioHandle, esp_lcd_panel_handle_t panelHandle) { + return { .flags = { .bb_mode = true, .avoid_tearing = false } }; - - displayHandle = lvgl_port_add_disp_rgb(&disp_cfg, &rgb_cfg); - TT_LOG_I(TAG, "Finished"); - return displayHandle != nullptr; } -bool CydDisplay::stop() { - assert(displayHandle != nullptr); +std::shared_ptr _Nullable CydDisplay::getTouchDevice() { + if (touchDevice == nullptr) { + auto configuration = std::make_unique( + I2C_NUM_0, + 480, + 480 + ); - lvgl_port_remove_disp(displayHandle); - - if (esp_lcd_panel_del(panelHandle) != ESP_OK) { - return false; + touchDevice = std::make_shared(std::move(configuration)); } - if (esp_lcd_panel_io_del(ioHandle) != ESP_OK) { - return false; - } - - displayHandle = nullptr; - return true; -} - -std::shared_ptr _Nullable CydDisplay::createTouch() { - auto configuration = std::make_unique( - I2C_NUM_0, - 480, - 480 - ); - - return std::make_shared(std::move(configuration)); + return touchDevice; } void CydDisplay::setBacklightDuty(uint8_t backlightDuty) { @@ -249,5 +236,6 @@ void CydDisplay::setBacklightDuty(uint8_t backlightDuty) { } std::shared_ptr createDisplay() { - return std::make_shared(); + auto display = std::make_shared(); + return std::reinterpret_pointer_cast(display); } diff --git a/Boards/CYD-4848S040C/Source/hal/CydDisplay.h b/Boards/CYD-4848S040C/Source/hal/CydDisplay.h index 8418afed..61a3c1ef 100644 --- a/Boards/CYD-4848S040C/Source/hal/CydDisplay.h +++ b/Boards/CYD-4848S040C/Source/hal/CydDisplay.h @@ -1,32 +1,33 @@ #pragma once -#include "Tactility/hal/display/DisplayDevice.h" -#include -#include +#include +#include -class CydDisplay : public tt::hal::display::DisplayDevice { +class CydDisplay final : public EspLcdDisplay { -private: + std::shared_ptr _Nullable touchDevice; - esp_lcd_panel_io_handle_t ioHandle = nullptr; - esp_lcd_panel_handle_t panelHandle = nullptr; - lv_display_t* displayHandle = nullptr; + bool createIoHandle(esp_lcd_panel_io_handle_t& outHandle) override; + + bool createPanelHandle(esp_lcd_panel_io_handle_t ioHandle, esp_lcd_panel_handle_t& panelHandle) override; + + lvgl_port_display_cfg_t getLvglPortDisplayConfig(esp_lcd_panel_io_handle_t ioHandle, esp_lcd_panel_handle_t panelHandle) override; + + bool isRgbPanel() const override { return true; } + + lvgl_port_display_rgb_cfg_t getLvglPortDisplayRgbConfig(esp_lcd_panel_io_handle_t ioHandle, esp_lcd_panel_handle_t panelHandle) override; public: - std::string getName() const final { return "ST7701S"; } - std::string getDescription() const final { return "RGB Display"; } + std::string getName() const override { return "ST7701S"; } - bool start() override; + std::string getDescription() const override { return "ST7701S RGB display"; } - bool stop() override; - - std::shared_ptr _Nullable createTouch() override; + std::shared_ptr _Nullable getTouchDevice() override; void setBacklightDuty(uint8_t backlightDuty) override; - bool supportsBacklightDuty() const override { return true; } - lv_display_t* _Nullable getLvglDisplay() const override { return displayHandle; } + bool supportsBacklightDuty() const override { return true; } }; std::shared_ptr createDisplay(); diff --git a/Boards/CYD-JC2432W328C/Source/hal/YellowDisplay.cpp b/Boards/CYD-JC2432W328C/Source/hal/YellowDisplay.cpp index 7e8fbb63..ed680d05 100644 --- a/Boards/CYD-JC2432W328C/Source/hal/YellowDisplay.cpp +++ b/Boards/CYD-JC2432W328C/Source/hal/YellowDisplay.cpp @@ -34,5 +34,6 @@ std::shared_ptr createDisplay() { configuration->backlightDutyFunction = driver::pwmbacklight::setBacklightDuty; - return std::make_shared(std::move(configuration)); + auto display = std::make_shared(std::move(configuration)); + return std::reinterpret_pointer_cast(display); } diff --git a/Boards/ElecrowCrowpanelAdvance28/CMakeLists.txt b/Boards/ElecrowCrowpanelAdvance28/CMakeLists.txt index cc93507f..a115d11b 100644 --- a/Boards/ElecrowCrowpanelAdvance28/CMakeLists.txt +++ b/Boards/ElecrowCrowpanelAdvance28/CMakeLists.txt @@ -3,5 +3,5 @@ file(GLOB_RECURSE SOURCE_FILES Source/*.c*) idf_component_register( SRCS ${SOURCE_FILES} INCLUDE_DIRS "Source" - REQUIRES Tactility esp_lvgl_port FT5x06 ST7789 PwmBacklight driver + REQUIRES Tactility FT5x06 ST7789 PwmBacklight driver ) diff --git a/Boards/ElecrowCrowpanelAdvance28/Source/hal/CrowPanelDisplay.cpp b/Boards/ElecrowCrowpanelAdvance28/Source/hal/CrowPanelDisplay.cpp index 11bfe866..857436cc 100644 --- a/Boards/ElecrowCrowpanelAdvance28/Source/hal/CrowPanelDisplay.cpp +++ b/Boards/ElecrowCrowpanelAdvance28/Source/hal/CrowPanelDisplay.cpp @@ -5,8 +5,6 @@ #include #include -#define TAG "crowpanel_display" - static std::shared_ptr createTouch() { // Note for future changes: Reset pin is 48 and interrupt pin is 47 auto configuration = std::make_unique( @@ -39,5 +37,6 @@ std::shared_ptr createDisplay() { configuration->backlightDutyFunction = driver::pwmbacklight::setBacklightDuty; - return std::make_shared(std::move(configuration)); + auto display = std::make_shared(std::move(configuration)); + return std::reinterpret_pointer_cast(display); } diff --git a/Boards/ElecrowCrowpanelAdvance28/Source/hal/CrowPanelDisplay.h b/Boards/ElecrowCrowpanelAdvance28/Source/hal/CrowPanelDisplay.h index 6c0fde39..7a9b967d 100644 --- a/Boards/ElecrowCrowpanelAdvance28/Source/hal/CrowPanelDisplay.h +++ b/Boards/ElecrowCrowpanelAdvance28/Source/hal/CrowPanelDisplay.h @@ -1,40 +1,5 @@ #pragma once -#include "Tactility/hal/display/DisplayDevice.h" -#include -#include - -class CrowPanelDisplay : public tt::hal::display::DisplayDevice { - -private: - - esp_lcd_panel_io_handle_t ioHandle = nullptr; - esp_lcd_panel_handle_t panelHandle = nullptr; - lv_display_t* displayHandle = nullptr; - bool poweredOn = false; - -public: - - std::string getName() const final { return "ST7789"; } - std::string getDescription() const final { return "SPI display"; } - - bool start() override; - - bool stop() override; - - void setPowerOn(bool turnOn) override; - bool isPoweredOn() const override { return poweredOn; }; - bool supportsPowerControl() const override { return true; } - - std::shared_ptr _Nullable createTouch() override; - - void setBacklightDuty(uint8_t backlightDuty) override; - bool supportsBacklightDuty() const override { return true; } - - void setGammaCurve(uint8_t index) override; - uint8_t getGammaCurveCount() const override { return 4; }; - - lv_display_t* _Nullable getLvglDisplay() const override { return displayHandle; } -}; +#include std::shared_ptr createDisplay(); diff --git a/Boards/ElecrowCrowpanelAdvance35/Source/hal/CrowPanelDisplay.cpp b/Boards/ElecrowCrowpanelAdvance35/Source/hal/CrowPanelDisplay.cpp index 2cc985f1..0c7ef6cb 100644 --- a/Boards/ElecrowCrowpanelAdvance35/Source/hal/CrowPanelDisplay.cpp +++ b/Boards/ElecrowCrowpanelAdvance35/Source/hal/CrowPanelDisplay.cpp @@ -36,5 +36,6 @@ std::shared_ptr createDisplay() { configuration->backlightDutyFunction = driver::pwmbacklight::setBacklightDuty; - return std::make_shared(std::move(configuration)); + auto display = std::make_shared(std::move(configuration)); + return std::reinterpret_pointer_cast(display); } diff --git a/Boards/ElecrowCrowpanelAdvance35/Source/hal/CrowPanelDisplay.h b/Boards/ElecrowCrowpanelAdvance35/Source/hal/CrowPanelDisplay.h index 6c0fde39..7a9b967d 100644 --- a/Boards/ElecrowCrowpanelAdvance35/Source/hal/CrowPanelDisplay.h +++ b/Boards/ElecrowCrowpanelAdvance35/Source/hal/CrowPanelDisplay.h @@ -1,40 +1,5 @@ #pragma once -#include "Tactility/hal/display/DisplayDevice.h" -#include -#include - -class CrowPanelDisplay : public tt::hal::display::DisplayDevice { - -private: - - esp_lcd_panel_io_handle_t ioHandle = nullptr; - esp_lcd_panel_handle_t panelHandle = nullptr; - lv_display_t* displayHandle = nullptr; - bool poweredOn = false; - -public: - - std::string getName() const final { return "ST7789"; } - std::string getDescription() const final { return "SPI display"; } - - bool start() override; - - bool stop() override; - - void setPowerOn(bool turnOn) override; - bool isPoweredOn() const override { return poweredOn; }; - bool supportsPowerControl() const override { return true; } - - std::shared_ptr _Nullable createTouch() override; - - void setBacklightDuty(uint8_t backlightDuty) override; - bool supportsBacklightDuty() const override { return true; } - - void setGammaCurve(uint8_t index) override; - uint8_t getGammaCurveCount() const override { return 4; }; - - lv_display_t* _Nullable getLvglDisplay() const override { return displayHandle; } -}; +#include std::shared_ptr createDisplay(); diff --git a/Boards/ElecrowCrowpanelAdvance50/Source/hal/CrowPanelDisplay.h b/Boards/ElecrowCrowpanelAdvance50/Source/hal/CrowPanelDisplay.h index 5a0d81b3..7a9b967d 100644 --- a/Boards/ElecrowCrowpanelAdvance50/Source/hal/CrowPanelDisplay.h +++ b/Boards/ElecrowCrowpanelAdvance50/Source/hal/CrowPanelDisplay.h @@ -1,5 +1,5 @@ #pragma once -#include "Tactility/hal/display/DisplayDevice.h" +#include std::shared_ptr createDisplay(); diff --git a/Boards/ElecrowCrowpanelBasic28/Source/hal/CrowPanelDisplay.cpp b/Boards/ElecrowCrowpanelBasic28/Source/hal/CrowPanelDisplay.cpp index d9dc1ca6..427d4f7f 100644 --- a/Boards/ElecrowCrowpanelBasic28/Source/hal/CrowPanelDisplay.cpp +++ b/Boards/ElecrowCrowpanelBasic28/Source/hal/CrowPanelDisplay.cpp @@ -35,5 +35,6 @@ std::shared_ptr createDisplay() { configuration->mirrorX = true; configuration->backlightDutyFunction = driver::pwmbacklight::setBacklightDuty; - return std::make_shared(std::move(configuration)); + auto display = std::make_shared(std::move(configuration)); + return std::reinterpret_pointer_cast(display); } diff --git a/Boards/ElecrowCrowpanelBasic28/Source/hal/CrowPanelDisplay.h b/Boards/ElecrowCrowpanelBasic28/Source/hal/CrowPanelDisplay.h index 6c0fde39..5a0d81b3 100644 --- a/Boards/ElecrowCrowpanelBasic28/Source/hal/CrowPanelDisplay.h +++ b/Boards/ElecrowCrowpanelBasic28/Source/hal/CrowPanelDisplay.h @@ -1,40 +1,5 @@ #pragma once #include "Tactility/hal/display/DisplayDevice.h" -#include -#include - -class CrowPanelDisplay : public tt::hal::display::DisplayDevice { - -private: - - esp_lcd_panel_io_handle_t ioHandle = nullptr; - esp_lcd_panel_handle_t panelHandle = nullptr; - lv_display_t* displayHandle = nullptr; - bool poweredOn = false; - -public: - - std::string getName() const final { return "ST7789"; } - std::string getDescription() const final { return "SPI display"; } - - bool start() override; - - bool stop() override; - - void setPowerOn(bool turnOn) override; - bool isPoweredOn() const override { return poweredOn; }; - bool supportsPowerControl() const override { return true; } - - std::shared_ptr _Nullable createTouch() override; - - void setBacklightDuty(uint8_t backlightDuty) override; - bool supportsBacklightDuty() const override { return true; } - - void setGammaCurve(uint8_t index) override; - uint8_t getGammaCurveCount() const override { return 4; }; - - lv_display_t* _Nullable getLvglDisplay() const override { return displayHandle; } -}; std::shared_ptr createDisplay(); diff --git a/Boards/ElecrowCrowpanelBasic35/Source/hal/CrowPanelDisplay.cpp b/Boards/ElecrowCrowpanelBasic35/Source/hal/CrowPanelDisplay.cpp index cc6cfc1b..4f50d62c 100644 --- a/Boards/ElecrowCrowpanelBasic35/Source/hal/CrowPanelDisplay.cpp +++ b/Boards/ElecrowCrowpanelBasic35/Source/hal/CrowPanelDisplay.cpp @@ -42,5 +42,6 @@ std::shared_ptr createDisplay() { configuration->mirrorX = true; configuration->backlightDutyFunction = driver::pwmbacklight::setBacklightDuty; - return std::make_shared(std::move(configuration)); + auto display = std::make_shared(std::move(configuration)); + return std::reinterpret_pointer_cast(display); } diff --git a/Boards/ElecrowCrowpanelBasic35/Source/hal/CrowPanelDisplay.h b/Boards/ElecrowCrowpanelBasic35/Source/hal/CrowPanelDisplay.h index 6c0fde39..7a9b967d 100644 --- a/Boards/ElecrowCrowpanelBasic35/Source/hal/CrowPanelDisplay.h +++ b/Boards/ElecrowCrowpanelBasic35/Source/hal/CrowPanelDisplay.h @@ -1,40 +1,5 @@ #pragma once -#include "Tactility/hal/display/DisplayDevice.h" -#include -#include - -class CrowPanelDisplay : public tt::hal::display::DisplayDevice { - -private: - - esp_lcd_panel_io_handle_t ioHandle = nullptr; - esp_lcd_panel_handle_t panelHandle = nullptr; - lv_display_t* displayHandle = nullptr; - bool poweredOn = false; - -public: - - std::string getName() const final { return "ST7789"; } - std::string getDescription() const final { return "SPI display"; } - - bool start() override; - - bool stop() override; - - void setPowerOn(bool turnOn) override; - bool isPoweredOn() const override { return poweredOn; }; - bool supportsPowerControl() const override { return true; } - - std::shared_ptr _Nullable createTouch() override; - - void setBacklightDuty(uint8_t backlightDuty) override; - bool supportsBacklightDuty() const override { return true; } - - void setGammaCurve(uint8_t index) override; - uint8_t getGammaCurveCount() const override { return 4; }; - - lv_display_t* _Nullable getLvglDisplay() const override { return displayHandle; } -}; +#include std::shared_ptr createDisplay(); diff --git a/Boards/ElecrowCrowpanelBasic35/Source/hal/CrowPanelSdCard.cpp b/Boards/ElecrowCrowpanelBasic35/Source/hal/CrowPanelSdCard.cpp index 0959c55c..0274fd3e 100644 --- a/Boards/ElecrowCrowpanelBasic35/Source/hal/CrowPanelSdCard.cpp +++ b/Boards/ElecrowCrowpanelBasic35/Source/hal/CrowPanelSdCard.cpp @@ -3,8 +3,6 @@ #include #include -#include - using tt::hal::sdcard::SpiSdCardDevice; std::shared_ptr createSdCard() { diff --git a/Boards/ElecrowCrowpanelBasic50/Source/hal/CrowPanelDisplay.cpp b/Boards/ElecrowCrowpanelBasic50/Source/hal/CrowPanelDisplay.cpp index d81bc5c5..6533da9a 100644 --- a/Boards/ElecrowCrowpanelBasic50/Source/hal/CrowPanelDisplay.cpp +++ b/Boards/ElecrowCrowpanelBasic50/Source/hal/CrowPanelDisplay.cpp @@ -101,5 +101,6 @@ std::shared_ptr createDisplay() { driver::pwmbacklight::setBacklightDuty ); - return std::make_shared(std::move(configuration)); + auto display = std::make_shared(std::move(configuration)); + return std::reinterpret_pointer_cast(display); } diff --git a/Boards/ElecrowCrowpanelBasic50/Source/hal/CrowPanelDisplay.h b/Boards/ElecrowCrowpanelBasic50/Source/hal/CrowPanelDisplay.h index 5a0d81b3..7a9b967d 100644 --- a/Boards/ElecrowCrowpanelBasic50/Source/hal/CrowPanelDisplay.h +++ b/Boards/ElecrowCrowpanelBasic50/Source/hal/CrowPanelDisplay.h @@ -1,5 +1,5 @@ #pragma once -#include "Tactility/hal/display/DisplayDevice.h" +#include std::shared_ptr createDisplay(); diff --git a/Boards/LilygoTLoraPager/CMakeLists.txt b/Boards/LilygoTLoraPager/CMakeLists.txt index 3e3a7c3c..5746b57d 100644 --- a/Boards/LilygoTLoraPager/CMakeLists.txt +++ b/Boards/LilygoTLoraPager/CMakeLists.txt @@ -3,5 +3,5 @@ file(GLOB_RECURSE SOURCE_FILES Source/*.c*) idf_component_register( SRCS ${SOURCE_FILES} INCLUDE_DIRS "Source" - REQUIRES Tactility esp_lvgl_port esp_lcd ST7796 BQ27220 TCA8418 PwmBacklight driver esp_adc + REQUIRES Tactility esp_lcd ST7796 BQ27220 TCA8418 PwmBacklight driver esp_adc ) diff --git a/Boards/LilygoTLoraPager/Source/hal/TpagerDisplay.cpp b/Boards/LilygoTLoraPager/Source/hal/TpagerDisplay.cpp index 30dceb9a..5f4c6755 100644 --- a/Boards/LilygoTLoraPager/Source/hal/TpagerDisplay.cpp +++ b/Boards/LilygoTLoraPager/Source/hal/TpagerDisplay.cpp @@ -6,8 +6,6 @@ #include -#define TAG "TPAGER_display" - std::shared_ptr createDisplay() { auto configuration = std::make_unique( TPAGER_LCD_SPI_HOST, @@ -26,5 +24,6 @@ std::shared_ptr createDisplay() { configuration->backlightDutyFunction = driver::pwmbacklight::setBacklightDuty; - return std::make_shared(std::move(configuration)); + auto display = std::make_shared(std::move(configuration)); + return std::reinterpret_pointer_cast(display); } diff --git a/Boards/LilygoTLoraPager/Source/hal/TpagerDisplay.h b/Boards/LilygoTLoraPager/Source/hal/TpagerDisplay.h index df09ebc9..7a9b967d 100644 --- a/Boards/LilygoTLoraPager/Source/hal/TpagerDisplay.h +++ b/Boards/LilygoTLoraPager/Source/hal/TpagerDisplay.h @@ -1,40 +1,5 @@ #pragma once -#include "Tactility/hal/display/DisplayDevice.h" -#include -#include - -class TpagerDisplay : public tt::hal::display::DisplayDevice { - -private: - - esp_lcd_panel_io_handle_t ioHandle = nullptr; - esp_lcd_panel_handle_t panelHandle = nullptr; - lv_display_t* displayHandle = nullptr; - bool poweredOn = false; - -public: - - std::string getName() const final { return "ST7796"; } - std::string getDescription() const final { return "SPI display"; } - - bool start() override; - - bool stop() override; - - void setPowerOn(bool turnOn) override; - bool isPoweredOn() const override { return poweredOn; }; - bool supportsPowerControl() const override { return true; } - - std::shared_ptr _Nullable createTouch() override; - - void setBacklightDuty(uint8_t backlightDuty) override; - bool supportsBacklightDuty() const override { return true; } - - void setGammaCurve(uint8_t index) override; - uint8_t getGammaCurveCount() const override { return 4; }; - - lv_display_t* _Nullable getLvglDisplay() const override { return displayHandle; } -}; +#include std::shared_ptr createDisplay(); diff --git a/Boards/LilygoTLoraPager/Source/hal/TpagerKeyboard.cpp b/Boards/LilygoTLoraPager/Source/hal/TpagerKeyboard.cpp index 93b31d4f..092073de 100644 --- a/Boards/LilygoTLoraPager/Source/hal/TpagerKeyboard.cpp +++ b/Boards/LilygoTLoraPager/Source/hal/TpagerKeyboard.cpp @@ -161,7 +161,7 @@ void TpagerKeyboard::processKeyboard() { } } -bool TpagerKeyboard::start(lv_display_t* display) { +bool TpagerKeyboard::startLvgl(lv_display_t* display) { backlightOkay = initBacklight(BACKLIGHT, 30000, LEDC_TIMER_0, LEDC_CHANNEL_1); initEncoder(); keypad->init(KB_ROWS, KB_COLS); @@ -195,7 +195,7 @@ bool TpagerKeyboard::start(lv_display_t* display) { return true; } -bool TpagerKeyboard::stop() { +bool TpagerKeyboard::stopLvgl() { assert(inputTimer); inputTimer->stop(); inputTimer = nullptr; diff --git a/Boards/LilygoTLoraPager/Source/hal/TpagerKeyboard.h b/Boards/LilygoTLoraPager/Source/hal/TpagerKeyboard.h index 1aba8c08..5f09dcd7 100644 --- a/Boards/LilygoTLoraPager/Source/hal/TpagerKeyboard.h +++ b/Boards/LilygoTLoraPager/Source/hal/TpagerKeyboard.h @@ -1,6 +1,5 @@ #pragma once -#include #include #include @@ -10,10 +9,7 @@ #include - -class TpagerKeyboard : public tt::hal::keyboard::KeyboardDevice { - -private: +class TpagerKeyboard final : public tt::hal::keyboard::KeyboardDevice { lv_indev_t* _Nullable kbHandle = nullptr; lv_indev_t* _Nullable encHandle = nullptr; @@ -36,13 +32,13 @@ private: public: TpagerKeyboard(std::shared_ptr tca) : keypad(std::move(tca)) {} - ~TpagerKeyboard() {} - std::string getName() const final { return "T-Lora Pager Keyboard"; } - std::string getDescription() const final { return "I2C keyboard with encoder"; } + std::string getName() const override { return "T-Lora Pager Keyboard"; } + std::string getDescription() const override { return "T-Lora Pager I2C keyboard with encoder"; } + + bool startLvgl(lv_display_t* display) override; + bool stopLvgl() override; - bool start(lv_display_t* display) override; - bool stop() override; bool isAttached() const override; lv_indev_t* _Nullable getLvglIndev() override { return kbHandle; } diff --git a/Boards/LilygoTdeck/CMakeLists.txt b/Boards/LilygoTdeck/CMakeLists.txt index d1096554..3c380b69 100644 --- a/Boards/LilygoTdeck/CMakeLists.txt +++ b/Boards/LilygoTdeck/CMakeLists.txt @@ -3,5 +3,5 @@ file(GLOB_RECURSE SOURCE_FILES Source/*.c*) idf_component_register( SRCS ${SOURCE_FILES} INCLUDE_DIRS "Source" - REQUIRES Tactility esp_lvgl_port esp_lcd ST7789 GT911 PwmBacklight driver esp_adc + REQUIRES Tactility EspLcdCompat ST7789 GT911 PwmBacklight driver esp_adc ) diff --git a/Boards/LilygoTdeck/Source/hal/TdeckDisplay.cpp b/Boards/LilygoTdeck/Source/hal/TdeckDisplay.cpp index 01175a21..7b9e5fbd 100644 --- a/Boards/LilygoTdeck/Source/hal/TdeckDisplay.cpp +++ b/Boards/LilygoTdeck/Source/hal/TdeckDisplay.cpp @@ -41,5 +41,6 @@ std::shared_ptr createDisplay() { configuration->backlightDutyFunction = driver::pwmbacklight::setBacklightDuty; - return std::make_shared(std::move(configuration)); + auto display = std::make_shared(std::move(configuration)); + return std::reinterpret_pointer_cast(display); } diff --git a/Boards/LilygoTdeck/Source/hal/TdeckDisplay.h b/Boards/LilygoTdeck/Source/hal/TdeckDisplay.h index e9a26ec4..5a0d81b3 100644 --- a/Boards/LilygoTdeck/Source/hal/TdeckDisplay.h +++ b/Boards/LilygoTdeck/Source/hal/TdeckDisplay.h @@ -1,40 +1,5 @@ #pragma once #include "Tactility/hal/display/DisplayDevice.h" -#include -#include - -class TdeckDisplay : public tt::hal::display::DisplayDevice { - -private: - - esp_lcd_panel_io_handle_t ioHandle = nullptr; - esp_lcd_panel_handle_t panelHandle = nullptr; - lv_display_t* displayHandle = nullptr; - bool poweredOn = false; - -public: - - std::string getName() const final { return "ST7789"; } - std::string getDescription() const final { return "SPI display"; } - - bool start() override; - - bool stop() override; - - void setPowerOn(bool turnOn) override; - bool isPoweredOn() const override { return poweredOn; }; - bool supportsPowerControl() const override { return true; } - - std::shared_ptr _Nullable createTouch() override; - - void setBacklightDuty(uint8_t backlightDuty) override; - bool supportsBacklightDuty() const override { return true; } - - void setGammaCurve(uint8_t index) override; - uint8_t getGammaCurveCount() const override { return 4; }; - - lv_display_t* _Nullable getLvglDisplay() const override { return displayHandle; } -}; std::shared_ptr createDisplay(); diff --git a/Boards/LilygoTdeck/Source/hal/TdeckKeyboard.cpp b/Boards/LilygoTdeck/Source/hal/TdeckKeyboard.cpp index 47c8590d..0c357d82 100644 --- a/Boards/LilygoTdeck/Source/hal/TdeckKeyboard.cpp +++ b/Boards/LilygoTdeck/Source/hal/TdeckKeyboard.cpp @@ -7,7 +7,7 @@ #define TDECK_KEYBOARD_I2C_BUS_HANDLE I2C_NUM_0 #define TDECK_KEYBOARD_SLAVE_ADDRESS 0x55 -static inline bool keyboard_i2c_read(uint8_t* output) { +static bool keyboard_i2c_read(uint8_t* output) { return tt::hal::i2c::masterRead(TDECK_KEYBOARD_I2C_BUS_HANDLE, TDECK_KEYBOARD_SLAVE_ADDRESS, output, 1, 100 / portTICK_PERIOD_MS); } @@ -43,7 +43,7 @@ static void keyboard_read_callback(TT_UNUSED lv_indev_t* indev, lv_indev_data_t* last_buffer = read_buffer; } -bool TdeckKeyboard::start(lv_display_t* display) { +bool TdeckKeyboard::startLvgl(lv_display_t* display) { deviceHandle = lv_indev_create(); lv_indev_set_type(deviceHandle, LV_INDEV_TYPE_KEYPAD); lv_indev_set_read_cb(deviceHandle, &keyboard_read_callback); @@ -52,7 +52,7 @@ bool TdeckKeyboard::start(lv_display_t* display) { return true; } -bool TdeckKeyboard::stop() { +bool TdeckKeyboard::stopLvgl() { lv_indev_delete(deviceHandle); deviceHandle = nullptr; return true; diff --git a/Boards/LilygoTdeck/Source/hal/TdeckKeyboard.h b/Boards/LilygoTdeck/Source/hal/TdeckKeyboard.h index 26eea592..e526d2dc 100644 --- a/Boards/LilygoTdeck/Source/hal/TdeckKeyboard.h +++ b/Boards/LilygoTdeck/Source/hal/TdeckKeyboard.h @@ -5,19 +5,17 @@ #include #include -class TdeckKeyboard : public tt::hal::keyboard::KeyboardDevice { - -private: +class TdeckKeyboard final : public tt::hal::keyboard::KeyboardDevice { lv_indev_t* _Nullable deviceHandle = nullptr; public: - std::string getName() const final { return "T-Deck Keyboard"; } - std::string getDescription() const final { return "I2C keyboard"; } + std::string getName() const override { return "T-Deck Keyboard"; } + std::string getDescription() const override { return "I2C keyboard"; } - bool start(lv_display_t* display) override; - bool stop() override; + bool startLvgl(lv_display_t* display) override; + bool stopLvgl() override; bool isAttached() const override; lv_indev_t* _Nullable getLvglIndev() override { return deviceHandle; } }; diff --git a/Boards/M5stackCore2/Source/hal/Core2Display.cpp b/Boards/M5stackCore2/Source/hal/Core2Display.cpp index 585d2d28..25f7f7e5 100644 --- a/Boards/M5stackCore2/Source/hal/Core2Display.cpp +++ b/Boards/M5stackCore2/Source/hal/Core2Display.cpp @@ -6,10 +6,13 @@ std::shared_ptr createTouch() { auto configuration = std::make_unique( I2C_NUM_0, - GPIO_NUM_39 + GPIO_NUM_39, + CORE2_LCD_HORIZONTAL_RESOLUTION, + CORE2_LCD_VERTICAL_RESOLUTION ); - return std::make_shared(std::move(configuration)); + auto touch = std::make_shared(std::move(configuration)); + return std::reinterpret_pointer_cast(touch); } std::shared_ptr createDisplay() { @@ -28,5 +31,6 @@ std::shared_ptr createDisplay() { true ); - return std::make_shared(std::move(configuration)); + auto display = std::make_shared(std::move(configuration)); + return std::reinterpret_pointer_cast(display); } diff --git a/Boards/M5stackCore2/Source/hal/Core2Power.cpp b/Boards/M5stackCore2/Source/hal/Core2Power.cpp index 504ff594..6256186d 100644 --- a/Boards/M5stackCore2/Source/hal/Core2Power.cpp +++ b/Boards/M5stackCore2/Source/hal/Core2Power.cpp @@ -2,7 +2,7 @@ #include #include "axp192/axp192.h" -#define TAG "core2_power" +constexpr auto TAG = "Core2Power"; extern axp192_t axpDevice; diff --git a/Boards/M5stackCoreS3/Source/hal/CoreS3Display.cpp b/Boards/M5stackCoreS3/Source/hal/CoreS3Display.cpp index f9b97241..250e1cc9 100644 --- a/Boards/M5stackCoreS3/Source/hal/CoreS3Display.cpp +++ b/Boards/M5stackCoreS3/Source/hal/CoreS3Display.cpp @@ -1,5 +1,4 @@ #include "CoreS3Display.h" - #include "CoreS3Constants.h" #include @@ -7,7 +6,7 @@ #include #include -#define TAG "cores3" +constexpr auto TAG = "CoreS3Display"; static void setBacklightDuty(uint8_t backlightDuty) { const uint8_t voltage = 20 + ((8 * backlightDuty) / 255); // [0b00000, 0b11100] - under 20 is too dark @@ -25,7 +24,8 @@ static std::shared_ptr createTouch() { 240 ); - return std::make_shared(std::move(configuration)); + auto touch = std::make_shared(std::move(configuration)); + return std::reinterpret_pointer_cast(touch); } std::shared_ptr createDisplay() { @@ -46,5 +46,6 @@ std::shared_ptr createDisplay() { configuration->backlightDutyFunction = ::setBacklightDuty; - return std::make_shared(std::move(configuration)); + auto display = std::make_shared(std::move(configuration)); + return std::reinterpret_pointer_cast(display); } diff --git a/Boards/M5stackCoreS3/Source/hal/CoreS3Display.h b/Boards/M5stackCoreS3/Source/hal/CoreS3Display.h index 5a0d81b3..7a9b967d 100644 --- a/Boards/M5stackCoreS3/Source/hal/CoreS3Display.h +++ b/Boards/M5stackCoreS3/Source/hal/CoreS3Display.h @@ -1,5 +1,5 @@ #pragma once -#include "Tactility/hal/display/DisplayDevice.h" +#include std::shared_ptr createDisplay(); diff --git a/Boards/Simulator/Source/hal/SdlDisplay.h b/Boards/Simulator/Source/hal/SdlDisplay.h index ab2aa805..f8b7c338 100644 --- a/Boards/Simulator/Source/hal/SdlDisplay.h +++ b/Boards/Simulator/Source/hal/SdlDisplay.h @@ -1,8 +1,7 @@ #pragma once #include "SdlTouch.h" -#include "Tactility/hal/display/DisplayDevice.h" -#include +#include /** Hack: variable comes from LvglTask.cpp */ extern lv_disp_t* displayHandle; @@ -11,8 +10,8 @@ class SdlDisplay final : public tt::hal::display::DisplayDevice { public: - std::string getName() const final { return "SDL Display"; } - std::string getDescription() const final { return ""; } + std::string getName() const override { return "SDL Display"; } + std::string getDescription() const override { return ""; } bool start() override { return displayHandle != nullptr; @@ -20,7 +19,11 @@ public: bool stop() override { tt_crash("Not supported"); } - std::shared_ptr _Nullable createTouch() override { return std::make_shared(); } + bool supportsLvgl() const override { return true; } + bool startLvgl() override { return true; } + bool stopLvgl() override { tt_crash("Not supported"); } + + std::shared_ptr _Nullable getTouchDevice() override { return std::make_shared(); } lv_display_t* _Nullable getLvglDisplay() const override { return displayHandle; } }; diff --git a/Boards/Simulator/Source/hal/SdlKeyboard.h b/Boards/Simulator/Source/hal/SdlKeyboard.h index f9730286..9bf3dc50 100644 --- a/Boards/Simulator/Source/hal/SdlKeyboard.h +++ b/Boards/Simulator/Source/hal/SdlKeyboard.h @@ -4,20 +4,20 @@ #include class SdlKeyboard final : public tt::hal::keyboard::KeyboardDevice { -private: + lv_indev_t* _Nullable handle = nullptr; public: - std::string getName() const final { return "SDL Keyboard"; } - std::string getDescription() const final { return "SDL keyboard device"; } + std::string getName() const override { return "SDL Keyboard"; } + std::string getDescription() const override { return "SDL keyboard device"; } - bool start(lv_display_t* display) override { + bool startLvgl(lv_display_t* display) override { handle = lv_sdl_keyboard_create(); return handle != nullptr; } - bool stop() override { tt_crash("Not supported"); } + bool stopLvgl() override { tt_crash("Not supported"); } bool isAttached() const override { return true; } diff --git a/Boards/Simulator/Source/hal/SdlTouch.h b/Boards/Simulator/Source/hal/SdlTouch.h index 613742d6..9ae1790e 100644 --- a/Boards/Simulator/Source/hal/SdlTouch.h +++ b/Boards/Simulator/Source/hal/SdlTouch.h @@ -4,21 +4,32 @@ #include class SdlTouch final : public tt::hal::touch::TouchDevice { -private: + lv_indev_t* _Nullable handle = nullptr; public: - std::string getName() const final { return "SDL Pointer"; } - std::string getDescription() const final { return "SDL mouse/touch pointer device"; } + std::string getName() const override { return "SDL Mouse"; } - bool start(lv_display_t* display) override { + std::string getDescription() const override { return "SDL mouse/touch pointer device"; } + + bool start() override { return true; } + + bool stop() override { tt_crash("Not supported"); } + + bool supportsLvgl() const override { return true; } + + bool startLvgl(lv_display_t* display) override { handle = lv_sdl_mouse_create(); return handle != nullptr; } - bool stop() override { tt_crash("Not supported"); } + bool stopLvgl() override { tt_crash("Not supported"); } lv_indev_t* _Nullable getLvglIndev() override { return handle; } + + bool supportsTouchDriver() override { return false; } + + std::shared_ptr _Nullable getTouchDriver() override { return nullptr; }; }; diff --git a/Boards/UnPhone/Source/hal/UnPhoneDisplay.cpp b/Boards/UnPhone/Source/hal/UnPhoneDisplay.cpp index 22f0fb8b..a00e12e7 100644 --- a/Boards/UnPhone/Source/hal/UnPhoneDisplay.cpp +++ b/Boards/UnPhone/Source/hal/UnPhoneDisplay.cpp @@ -9,13 +9,13 @@ #include #include -#define TAG "unphone_display" -#define BUFFER_SIZE (UNPHONE_LCD_HORIZONTAL_RESOLUTION * UNPHONE_LCD_DRAW_BUFFER_HEIGHT * LV_COLOR_DEPTH / 8) +constexpr auto TAG = "UnPhoneDisplay"; +constexpr auto BUFFER_SIZE = (UNPHONE_LCD_HORIZONTAL_RESOLUTION * UNPHONE_LCD_DRAW_BUFFER_HEIGHT * LV_COLOR_DEPTH / 8); extern std::shared_ptr unPhoneFeatures; bool UnPhoneDisplay::start() { - TT_LOG_I(TAG, "Starting"); + TT_LOG_I(TAG, "start"); disp_spi_add_device(SPI2_HOST); @@ -24,39 +24,75 @@ bool UnPhoneDisplay::start() { uint8_t madctl = (1U << MADCTL_BIT_INDEX_COLUMN_ADDRESS_ORDER); hx8357_set_madctl(madctl); - displayHandle = lv_display_create(UNPHONE_LCD_HORIZONTAL_RESOLUTION, UNPHONE_LCD_VERTICAL_RESOLUTION); - lv_display_set_physical_resolution(displayHandle, UNPHONE_LCD_HORIZONTAL_RESOLUTION, UNPHONE_LCD_VERTICAL_RESOLUTION); - lv_display_set_color_format(displayHandle, LV_COLOR_FORMAT_NATIVE); + return true; +} + +bool UnPhoneDisplay::stop() { + TT_LOG_I(TAG, "stop"); + disp_spi_remove_device(); + return true; +} + +bool UnPhoneDisplay::startLvgl() { + TT_LOG_I(TAG, "startLvgl"); + + if (lvglDisplay != nullptr) { + TT_LOG_W(TAG, "LVGL was already started"); + return false; + } + + lvglDisplay = lv_display_create(UNPHONE_LCD_HORIZONTAL_RESOLUTION, UNPHONE_LCD_VERTICAL_RESOLUTION); + lv_display_set_physical_resolution(lvglDisplay, UNPHONE_LCD_HORIZONTAL_RESOLUTION, UNPHONE_LCD_VERTICAL_RESOLUTION); + lv_display_set_color_format(lvglDisplay, LV_COLOR_FORMAT_NATIVE); // TODO malloc to use SPIRAM - buffer = (uint8_t*)heap_caps_malloc(BUFFER_SIZE, MALLOC_CAP_DMA); + buffer = static_cast(heap_caps_malloc(BUFFER_SIZE, MALLOC_CAP_DMA)); assert(buffer != nullptr); lv_display_set_buffers( - displayHandle, + lvglDisplay, buffer, nullptr, BUFFER_SIZE, LV_DISPLAY_RENDER_MODE_PARTIAL ); - lv_display_set_flush_cb(displayHandle, hx8357_flush); + lv_display_set_flush_cb(lvglDisplay, hx8357_flush); - if (displayHandle != nullptr) { - TT_LOG_I(TAG, "Finished"); - unPhoneFeatures->setBacklightPower(true); - return true; - } else { + if (lvglDisplay == nullptr) { TT_LOG_I(TAG, "Failed"); return false; } + + unPhoneFeatures->setBacklightPower(true); + + auto touch_device = getTouchDevice(); + if (touch_device != nullptr) { + touch_device->startLvgl(lvglDisplay); + } + + return true; } -bool UnPhoneDisplay::stop() { - assert(displayHandle != nullptr); +bool UnPhoneDisplay::stopLvgl() { + TT_LOG_I(TAG, "stopLvgl"); - lv_display_delete(displayHandle); - displayHandle = nullptr; + if (lvglDisplay == nullptr) { + TT_LOG_W(TAG, "LVGL was already stopped"); + return false; + } + + // Just in case + disp_wait_for_pending_transactions(); + + auto touch_device = getTouchDevice(); + if (touch_device != nullptr && touch_device->getLvglIndev() != nullptr) { + TT_LOG_I(TAG, "Stopping touch device"); + touch_device->stopLvgl(); + } + + lv_display_delete(lvglDisplay); + lvglDisplay = nullptr; heap_caps_free(buffer); buffer = nullptr; @@ -64,10 +100,21 @@ bool UnPhoneDisplay::stop() { return true; } -std::shared_ptr _Nullable UnPhoneDisplay::createTouch() { - return ::createTouch(); +std::shared_ptr _Nullable UnPhoneDisplay::getTouchDevice() { + if (touchDevice == nullptr) { + touchDevice = std::reinterpret_pointer_cast(createTouch()); + TT_LOG_I(TAG, "Created touch device"); + } + + return touchDevice; } std::shared_ptr createDisplay() { return std::make_shared(); } + +bool UnPhoneDisplay::UnPhoneDisplayDriver::drawBitmap(int xStart, int yStart, int xEnd, int yEnd, const void* pixelData) { + lv_area_t area = { xStart, yStart, xEnd, yEnd }; + hx8357_flush(nullptr, &area, (uint8_t*)pixelData); + return true; +} diff --git a/Boards/UnPhone/Source/hal/UnPhoneDisplay.h b/Boards/UnPhone/Source/hal/UnPhoneDisplay.h index 134969e4..2c25a42b 100644 --- a/Boards/UnPhone/Source/hal/UnPhoneDisplay.h +++ b/Boards/UnPhone/Source/hal/UnPhoneDisplay.h @@ -3,13 +3,24 @@ #include "Tactility/hal/display/DisplayDevice.h" #include #include +#include + +#include "UnPhoneDisplayConstants.h" class UnPhoneDisplay : public tt::hal::display::DisplayDevice { -private: + uint8_t* _Nullable buffer = nullptr; + lv_display_t* _Nullable lvglDisplay = nullptr; + std::shared_ptr _Nullable touchDevice; + std::shared_ptr _Nullable nativeDisplay; - lv_display_t* displayHandle = nullptr; - uint8_t* buffer = nullptr; + + class UnPhoneDisplayDriver : public tt::hal::display::DisplayDriver { + tt::hal::display::ColorFormat getColorFormat() const override { return tt::hal::display::ColorFormat::RGB888; } + uint16_t getPixelWidth() const override { return UNPHONE_LCD_HORIZONTAL_RESOLUTION; } + uint16_t getPixelHeight() const override { return UNPHONE_LCD_VERTICAL_RESOLUTION; } + bool drawBitmap(int xStart, int yStart, int xEnd, int yEnd, const void* pixelData) override; + }; public: @@ -20,9 +31,26 @@ public: bool stop() override; - std::shared_ptr _Nullable createTouch() override; + bool supportsLvgl() const override { return true; } - lv_display_t* _Nullable getLvglDisplay() const override { return displayHandle; } + bool startLvgl() override; + + bool stopLvgl() override; + + std::shared_ptr _Nullable getTouchDevice() override; + + lv_display_t* _Nullable getLvglDisplay() const override { return lvglDisplay; } + + // TODO: Set to true after fixing UnPhoneDisplayDriver + bool supportsDisplayDriver() const override { return false; } + + std::shared_ptr _Nullable getDisplayDriver() override { + if (nativeDisplay == nullptr) { + nativeDisplay = std::make_shared(); + } + assert(nativeDisplay != nullptr); + return nativeDisplay; + } }; std::shared_ptr createDisplay(); diff --git a/CMakeLists.txt b/CMakeLists.txt index 6ff5f4c5..50938ae7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,6 +55,7 @@ if (DEFINED ENV{ESP_IDF_VERSION}) add_compile_definitions(LV_CONF_PATH="${LVGL_CONFIG_FULL_PATH}/lv_conf_kconfig.h") idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=esp_panic_handler" APPEND) idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=esp_log_write" APPEND) + idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=lv_textarea_create" APPEND) else () message("Building for sim target") add_compile_definitions(CONFIG_TT_BOARD_ID="simulator") diff --git a/Documentation/ideas.md b/Documentation/ideas.md index 34db7bb5..e0b2f32d 100644 --- a/Documentation/ideas.md +++ b/Documentation/ideas.md @@ -1,22 +1,22 @@ # TODOs +- Revisit TinyUSB mouse idea: the bugs related to cleanup seem to be fixed in the library. - Bug: When a Wi-Fi SSID is too long, then it fails to save the credentials -- Add a Keyboard setting app to override the behaviour of soft keyboard hiding (e.g. keyboard hardware is present, but user wants soft keyboard) +- Add a Keyboard setting app to override the behaviour of soft keyboard hiding (e.g. keyboard hardware is present, but the user wants to use a soft keyboard) - HAL for display touch calibration - Start using non_null (either via MS GSL, or custom) - `hal/Configuration.h` defines C function types: Use C++ std::function instead -- Fix system time to not be 1980 (use build year as minimum) +- Fix system time to not be 1980 (use build year as a minimum). Consider keeping track of the last known time. - Use std::span or string_view in StringUtils https://youtu.be/FRkJCvHWdwQ?t=2754 - Fix bug in T-Deck/etc: esp_lvgl_port settings has a large stack size (~9kB) to fix an issue where the T-Deck would get a stackoverflow. This sometimes happens when WiFi is auto-enabled and you open the app while it is still connecting. - Mutex: Implement give/take from ISR support (works only for non-recursive ones) - Extend unPhone power driver: add charging status, usb connection status, etc. - Expose app::Paths to TactilityC -- Experiment with what happens when using C++ code in an external app (without using standard library!) - Make a ledger for setting CPU affinity of various services and tasks - CrashHandler: use "corrupted" flag - CrashHandler: process other types of crashes (WDT?) -- Call tt::lvgl::isSyncSet after HAL init and show error (and crash?) when it is not set. -- Create different partitions files for different ESP flash size targets (N4, N8, N16, N32) +- Call tt::lvgl::isSyncSet after HAL init and show an error (and crash?) when it is not set. +- Create different partition files for different ESP flash size targets (N4, N8, N16, N32) - T-Deck: Clear screen before turning on blacklight - T-Deck: Use knob for UI selection? - Crash monitoring: Keep track of which system phase the app crashed in (e.g. which app in which state) @@ -26,28 +26,27 @@ - Show a warning screen when a user plugs in the SD card on a device that only supports mounting at boot. - Localisation of texts (load in boot app from sd?) - Explore LVGL9's FreeRTOS functionality -- External app loading: Check version of Tactility and check ESP target hardware, to check for compatibility. +- External app loading: Check version of Tactility and check ESP target hardware to check for compatibility. - Scanning SD card for external apps and auto-register them (in a temporary register?) - Support hot-plugging SD card - All drivers (e.g. display, touch, etc.) should call stop() in their destructor, or at least assert that they should not be running. - Use GPS time to set/update the current time -- Investigate EEZ Studio - Remove flex_flow from app_container in Gui.cpp # Nice-to-haves -- Give external app a different icon. Allow an external app update their id, icon, type and name once they are running(and persist that info?). Loader will need to be able to find app by (external) location. +- Give external app a different icon. Allow an external app to update their id, icon, type and name once they are running (and persist that info?). Loader will need to be able to find app by (external) location. - Audio player app - Audio recording app - OTA updates - T-Deck Plus: Create separate board config? - Support for displays with different DPI. Consider the layer-based system like on Android. - If present, use LED to show boot/wifi status -- Capacity based on voltage: estimation for various devices uses linear voltage curve, but it should use some sort of battery discharge curve. +- Capacity based on voltage: estimation for various devices uses a linear voltage curve, but it should use some sort of battery discharge curve. - Statusbar widget to show how much memory is in use? -- Wrapper for Slider that shows "+" and "-" buttons, and also the value in a label. +- Wrapper for lvgl slider widget that shows "+" and "-" buttons, and also the value in a label. - Files app: copy/paste actions -- On crash, try to save current log to flash or SD card? (this is risky, though, so ask in Discord first) +- On crash, try to save the current log to flash or SD card? (this is risky, though, so ask in Discord first) - Support more than 1 hardware keyboard (see lvgl::hardware_keyboard_set_indev()). LVGL init currently calls keyboard init, but that part should probably be done from the KeyboardDevice base class. # App Ideas diff --git a/Drivers/CST816S/CMakeLists.txt b/Drivers/CST816S/CMakeLists.txt index b1ab94a8..108babc4 100644 --- a/Drivers/CST816S/CMakeLists.txt +++ b/Drivers/CST816S/CMakeLists.txt @@ -1,5 +1,5 @@ idf_component_register( SRC_DIRS "Source" INCLUDE_DIRS "Source" - REQUIRES Tactility esp_lvgl_port esp_lcd_touch esp_lcd_touch_cst816s driver + REQUIRES Tactility EspLcdCompat esp_lcd_touch_cst816s driver ) diff --git a/Drivers/CST816S/Source/Cst816Touch.h b/Drivers/CST816S/Source/Cst816Touch.h index 1bec3472..a926c347 100644 --- a/Drivers/CST816S/Source/Cst816Touch.h +++ b/Drivers/CST816S/Source/Cst816Touch.h @@ -1,9 +1,8 @@ #pragma once -#include -#include +#include -class Cst816sTouch final : public tt::hal::touch::TouchDevice { +class Cst816sTouch final : public EspLcdTouch { public: @@ -54,16 +53,18 @@ private: void cleanup(); + bool createIoHandle(esp_lcd_panel_io_handle_t& ioHandle) override; + + bool createTouchHandle(esp_lcd_panel_io_handle_t ioHandle, const esp_lcd_touch_config_t& configuration, esp_lcd_touch_handle_t& touchHandle) override; + + esp_lcd_touch_config_t createEspLcdTouchConfig() override; + public: explicit Cst816sTouch(std::unique_ptr inConfiguration) : configuration(std::move(inConfiguration)) { assert(configuration != nullptr); } - std::string getName() const final { return "CST816S"; } - std::string getDescription() const final { return "I2C touch driver"; } - - bool start(lv_display_t* display) override; - bool stop() override; - lv_indev_t* _Nullable getLvglIndev() override { return deviceHandle; } + std::string getName() const override { return "CST816S"; } + std::string getDescription() const override { return "CST816S I2C touch driver"; } }; diff --git a/Drivers/CST816S/Source/Cst816sTouch.cpp b/Drivers/CST816S/Source/Cst816sTouch.cpp index 14a02221..a7088f4b 100644 --- a/Drivers/CST816S/Source/Cst816sTouch.cpp +++ b/Drivers/CST816S/Source/Cst816sTouch.cpp @@ -1,25 +1,18 @@ #include "Cst816Touch.h" -#include - -#include -#include -#include #include -#include -#define TAG "cst816s" +bool Cst816sTouch::createIoHandle(esp_lcd_panel_io_handle_t& ioHandle) { + constexpr esp_lcd_panel_io_i2c_config_t touch_io_config = ESP_LCD_TOUCH_IO_I2C_CST816S_CONFIG(); + return esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t)configuration->port, &touch_io_config, &ioHandle) == ESP_OK; +} -bool Cst816sTouch::start(lv_display_t* display) { - TT_LOG_I(TAG, "Starting"); - const esp_lcd_panel_io_i2c_config_t touch_io_config = ESP_LCD_TOUCH_IO_I2C_CST816S_CONFIG(); +bool Cst816sTouch::createTouchHandle(esp_lcd_panel_io_handle_t ioHandle, const esp_lcd_touch_config_t& touchConfiguration, esp_lcd_touch_handle_t& touchHandle) { + return esp_lcd_touch_new_i2c_cst816s(ioHandle, &touchConfiguration, &touchHandle) == ESP_OK; +} - if (esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t)configuration->port, &touch_io_config, &ioHandle) != ESP_OK) { - TT_LOG_E(TAG, "Touch I2C IO init failed"); - return false; - } - - esp_lcd_touch_config_t config = { +esp_lcd_touch_config_t Cst816sTouch::createEspLcdTouchConfig() { + return { .x_max = configuration->xMax, .y_max = configuration->yMax, .rst_gpio_num = configuration->pinReset, @@ -38,47 +31,4 @@ bool Cst816sTouch::start(lv_display_t* display) { .user_data = nullptr, .driver_data = nullptr }; - - if (esp_lcd_touch_new_i2c_cst816s(ioHandle, &config, &touchHandle) != ESP_OK) { - TT_LOG_E(TAG, "Driver init failed"); - cleanup(); - return false; - } - - const lvgl_port_touch_cfg_t touch_cfg = { - .disp = display, - .handle = touchHandle, - }; - - deviceHandle = lvgl_port_add_touch(&touch_cfg); - if (deviceHandle == nullptr) { - TT_LOG_E(TAG, "Adding touch failed"); - cleanup(); - return false; - } - - TT_LOG_I(TAG, "Finished"); - return true; -} - -bool Cst816sTouch::stop() { - cleanup(); - return true; -} - -void Cst816sTouch::cleanup() { - if (deviceHandle != nullptr) { - lv_indev_delete(deviceHandle); - deviceHandle = nullptr; - } - - if (touchHandle != nullptr) { - esp_lcd_touch_del(touchHandle); - touchHandle = nullptr; - } - - if (ioHandle != nullptr) { - esp_lcd_panel_io_del(ioHandle); - ioHandle = nullptr; - } } diff --git a/Drivers/EspLcdCompat/CMakeLists.txt b/Drivers/EspLcdCompat/CMakeLists.txt new file mode 100644 index 00000000..efefad7b --- /dev/null +++ b/Drivers/EspLcdCompat/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + SRC_DIRS "Source" + INCLUDE_DIRS "Source" + REQUIRES Tactility esp_lcd esp_lcd_touch esp_lvgl_port +) diff --git a/Drivers/EspLcdCompat/README.md b/Drivers/EspLcdCompat/README.md new file mode 100644 index 00000000..4d2bfbd5 --- /dev/null +++ b/Drivers/EspLcdCompat/README.md @@ -0,0 +1,3 @@ +# EspLcdCompat + +A set of helper classes to implement `esp_lcd` drivers in Tactility. diff --git a/Drivers/EspLcdCompat/Source/EspLcdDisplay.cpp b/Drivers/EspLcdCompat/Source/EspLcdDisplay.cpp new file mode 100644 index 00000000..adef57f0 --- /dev/null +++ b/Drivers/EspLcdCompat/Source/EspLcdDisplay.cpp @@ -0,0 +1,102 @@ +#include "EspLcdDisplay.h" +#include "EspLcdDisplayDriver.h" + +#include +#include +#include +#include +#include + +constexpr const char* TAG = "EspLcdDispDrv"; + +EspLcdDisplay::~EspLcdDisplay() { + if (displayDriver != nullptr && displayDriver.use_count() > 1) { + tt_crash("DisplayDriver is still in use. This will cause memory access violations."); + } +} + +bool EspLcdDisplay::start() { + if (!createIoHandle(ioHandle)) { + TT_LOG_E(TAG, "Failed to create IO handle"); + return false; + } + + if (!createPanelHandle(ioHandle, panelHandle)) { + TT_LOG_E(TAG, "Failed to create panel handle"); + esp_lcd_panel_io_del(ioHandle); + return false; + } + + return true; +} + +bool EspLcdDisplay::stop() { + if (lvglDisplay != nullptr) { + stopLvgl(); + lvglDisplay = nullptr; + } + + if (panelHandle != nullptr && esp_lcd_panel_del(panelHandle) != ESP_OK) { + return false; + } + + if (ioHandle != nullptr && esp_lcd_panel_io_del(ioHandle) != ESP_OK) { + return false; + } + + if (displayDriver != nullptr && displayDriver.use_count() > 1) { + TT_LOG_W(TAG, "DisplayDriver is still in use."); + } + + return true; +} + +bool EspLcdDisplay::startLvgl() { + assert(lvglDisplay == nullptr); + + if (displayDriver != nullptr && displayDriver.use_count() > 1) { + TT_LOG_W(TAG, "DisplayDriver is still in use."); + } + + lvglPortDisplayConfig = getLvglPortDisplayConfig(ioHandle, panelHandle); + + if (isRgbPanel()) { + auto rgb_config = getLvglPortDisplayRgbConfig(ioHandle, panelHandle); + lvglDisplay = lvgl_port_add_disp_rgb(&lvglPortDisplayConfig, &rgb_config); + } else { + lvglDisplay = lvgl_port_add_disp(&lvglPortDisplayConfig); + } + + auto touch_device = getTouchDevice(); + if (touch_device != nullptr) { + touch_device->startLvgl(lvglDisplay); + } + + return lvglDisplay != nullptr; +} + +bool EspLcdDisplay::stopLvgl() { + if (lvglDisplay == nullptr) { + return false; + } + + auto touch_device = getTouchDevice(); + if (touch_device != nullptr) { + touch_device->stopLvgl(); + } + + lvgl_port_remove_disp(lvglDisplay); + lvglDisplay = nullptr; + return true; +} + +std::shared_ptr EspLcdDisplay::getDisplayDriver() { + assert(lvglDisplay == nullptr); // Still attached to LVGL context. Call stopLvgl() first. + if (displayDriver == nullptr) { + displayDriver = std::make_shared( + panelHandle, + lvglPortDisplayConfig + ); + } + return displayDriver; +} diff --git a/Drivers/EspLcdCompat/Source/EspLcdDisplay.h b/Drivers/EspLcdCompat/Source/EspLcdDisplay.h new file mode 100644 index 00000000..3fe40c70 --- /dev/null +++ b/Drivers/EspLcdCompat/Source/EspLcdDisplay.h @@ -0,0 +1,60 @@ +#pragma once + +#include + +#include +#include +#include + +class EspLcdDisplay : tt::hal::display::DisplayDevice { + + esp_lcd_panel_io_handle_t _Nullable ioHandle = nullptr; + esp_lcd_panel_handle_t _Nullable panelHandle = nullptr; + lv_display_t* _Nullable lvglDisplay = nullptr; + lvgl_port_display_cfg_t _Nullable lvglPortDisplayConfig; + std::shared_ptr _Nullable displayDriver; + +protected: + + // Used for sending commands such as setting curves + esp_lcd_panel_io_handle_t getIoHandle() const { return ioHandle; } + + virtual bool createIoHandle(esp_lcd_panel_io_handle_t& outHandle) = 0; + + virtual bool createPanelHandle(esp_lcd_panel_io_handle_t ioHandle, esp_lcd_panel_handle_t& panelHandle) = 0; + + virtual lvgl_port_display_cfg_t getLvglPortDisplayConfig(esp_lcd_panel_io_handle_t ioHandle, esp_lcd_panel_handle_t panelHandle) = 0; + + virtual bool isRgbPanel() const { return false; } + + virtual lvgl_port_display_rgb_cfg_t getLvglPortDisplayRgbConfig(esp_lcd_panel_io_handle_t ioHandle, esp_lcd_panel_handle_t panelHandle) { tt_crash("Not supported"); } + +public: + + ~EspLcdDisplay() override; + + bool start() final; + + bool stop() final; + + // region LVGL + + bool supportsLvgl() const final { return true; } + + bool startLvgl() final; + + bool stopLvgl() final; + + lv_display_t* _Nullable getLvglDisplay() const final { return lvglDisplay; } + + // endregion + + // region NativeDisplay + + bool supportsDisplayDriver() const override { return true; } + + /** @return a NativeDisplay instance if this device supports it */ + std::shared_ptr _Nullable getDisplayDriver() final; + + // endregion +}; diff --git a/Drivers/EspLcdCompat/Source/EspLcdDisplayDriver.h b/Drivers/EspLcdCompat/Source/EspLcdDisplayDriver.h new file mode 100644 index 00000000..ceae9f5f --- /dev/null +++ b/Drivers/EspLcdCompat/Source/EspLcdDisplayDriver.h @@ -0,0 +1,44 @@ +#pragma once + +#include +#include +#include + +using namespace tt::hal; + +class EspLcdDisplayDriver : public display::DisplayDriver { + + esp_lcd_panel_handle_t panelHandle; + const lvgl_port_display_cfg_t& lvglPortDisplayConfig; + +public: + + EspLcdDisplayDriver( + esp_lcd_panel_handle_t panelHandle, + const lvgl_port_display_cfg_t& lvglPortDisplayConfig + ) : panelHandle(panelHandle), lvglPortDisplayConfig(lvglPortDisplayConfig) {} + + display::ColorFormat getColorFormat() const override { + using display::ColorFormat; + switch (lvglPortDisplayConfig.color_format) { + case LV_COLOR_FORMAT_I1: + return ColorFormat::Monochrome; + case LV_COLOR_FORMAT_RGB565: + // swap_bytes is only used for the 565 color format + // see lvgl_port_flush_callback() in esp_lvgl_port_disp.c + return lvglPortDisplayConfig.flags.swap_bytes ? ColorFormat::BGR565 : ColorFormat::RGB565; + case LV_COLOR_FORMAT_RGB888: + return ColorFormat::RGB888; + default: + return ColorFormat::RGB565; + } + } + + bool drawBitmap(int xStart, int yStart, int xEnd, int yEnd, const void* pixelData) override { + return esp_lcd_panel_draw_bitmap(panelHandle, xStart, yStart, xEnd, yEnd, pixelData) == ESP_OK; + } + + uint16_t getPixelWidth() const override { return lvglPortDisplayConfig.hres; } + + uint16_t getPixelHeight() const override { return lvglPortDisplayConfig.vres; } +}; diff --git a/Drivers/EspLcdCompat/Source/EspLcdTouch.cpp b/Drivers/EspLcdCompat/Source/EspLcdTouch.cpp new file mode 100644 index 00000000..03ca52e3 --- /dev/null +++ b/Drivers/EspLcdCompat/Source/EspLcdTouch.cpp @@ -0,0 +1,92 @@ +#include "EspLcdTouch.h" + +#include +#include +#include + +constexpr const char* TAG = "EspLcdTouch"; + +bool EspLcdTouch::start() { + if (!createIoHandle(ioHandle) != ESP_OK) { + TT_LOG_E(TAG, "Touch IO failed"); + return false; + } + + config = createEspLcdTouchConfig(); + + if (!createTouchHandle(ioHandle, config, touchHandle)) { + TT_LOG_E(TAG, "Driver init failed"); + esp_lcd_panel_io_del(ioHandle); + ioHandle = nullptr; + return false; + } + + return true; +} + +bool EspLcdTouch::stop() { + if (lvglDevice != nullptr) { + stopLvgl(); + } + + if (ioHandle != nullptr) { + esp_lcd_panel_io_del(ioHandle); + ioHandle = nullptr; + } + + if (touchHandle != nullptr) { + esp_lcd_touch_del(touchHandle); + touchHandle = nullptr; + } + + return true; +} + +bool EspLcdTouch::startLvgl(lv_disp_t* display) { + if (lvglDevice != nullptr) { + return false; + } + + if (touchDriver != nullptr && touchDriver.use_count() > 1) { + TT_LOG_W(TAG, "TouchDriver is still in use."); + } + + const lvgl_port_touch_cfg_t touch_cfg = { + .disp = display, + .handle = touchHandle, + }; + + TT_LOG_I(TAG, "Adding touch to LVGL"); + lvglDevice = lvgl_port_add_touch(&touch_cfg); + if (lvglDevice == nullptr) { + TT_LOG_E(TAG, "Adding touch failed"); + return false; + } + + return true; +} + +bool EspLcdTouch::stopLvgl() { + if (lvglDevice == nullptr) { + return false; + } + + lvgl_port_remove_touch(lvglDevice); + lvglDevice = nullptr; + + return true; +} + +std::shared_ptr _Nullable EspLcdTouch::getTouchDriver() { + assert(lvglDevice == nullptr); // Still attached to LVGL context. Call stopLvgl() first. + + if (touchHandle == nullptr) { + return nullptr; + } + + if (touchDriver == nullptr) { + touchDriver = std::make_shared(touchHandle); + } + + return touchDriver; +} diff --git a/Drivers/EspLcdCompat/Source/EspLcdTouch.h b/Drivers/EspLcdCompat/Source/EspLcdTouch.h new file mode 100644 index 00000000..035c6483 --- /dev/null +++ b/Drivers/EspLcdCompat/Source/EspLcdTouch.h @@ -0,0 +1,44 @@ +#pragma once + +#include +#include +#include +#include +#include + +class EspLcdTouch : public tt::hal::touch::TouchDevice { + + esp_lcd_touch_config_t config; + esp_lcd_panel_io_handle_t _Nullable ioHandle = nullptr; + esp_lcd_touch_handle_t _Nullable touchHandle = nullptr; + lv_indev_t* _Nullable lvglDevice = nullptr; + std::shared_ptr touchDriver; + +protected: + + esp_lcd_touch_handle_t _Nullable getTouchHandle() const { return touchHandle; } + + virtual bool createIoHandle(esp_lcd_panel_io_handle_t& ioHandle) = 0; + + virtual bool createTouchHandle(esp_lcd_panel_io_handle_t ioHandle, const esp_lcd_touch_config_t& configuration, esp_lcd_touch_handle_t& touchHandle) = 0; + + virtual esp_lcd_touch_config_t createEspLcdTouchConfig() = 0; + +public: + + bool start() final; + + bool stop() final; + + bool supportsLvgl() const final { return true; } + + bool startLvgl(lv_display_t* display) final; + + bool stopLvgl() final; + + lv_indev_t* _Nullable getLvglIndev() final { return lvglDevice; } + + bool supportsTouchDriver() override { return true; } + + std::shared_ptr _Nullable getTouchDriver() final; +}; diff --git a/Drivers/EspLcdCompat/Source/EspLcdTouchDriver.cpp b/Drivers/EspLcdCompat/Source/EspLcdTouchDriver.cpp new file mode 100644 index 00000000..7d186ac0 --- /dev/null +++ b/Drivers/EspLcdCompat/Source/EspLcdTouchDriver.cpp @@ -0,0 +1,13 @@ +#include "EspLcdTouchDriver.h" + +#include + +constexpr const char* TAG = "EspLcdTouchDriver"; + +bool EspLcdTouchDriver::getTouchedPoints(uint16_t* x, uint16_t* y, uint16_t* _Nullable strength, uint8_t* pointCount, uint8_t maxPointCount) { + if (esp_lcd_touch_read_data(handle) != ESP_OK) { + TT_LOG_E(TAG, "Read data failed"); + return false; + } + return esp_lcd_touch_get_coordinates(handle, x, y, strength, pointCount, maxPointCount) == ESP_OK; +} diff --git a/Drivers/EspLcdCompat/Source/EspLcdTouchDriver.h b/Drivers/EspLcdCompat/Source/EspLcdTouchDriver.h new file mode 100644 index 00000000..3b7d9605 --- /dev/null +++ b/Drivers/EspLcdCompat/Source/EspLcdTouchDriver.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +class EspLcdTouchDriver final : public tt::hal::touch::TouchDriver { + + esp_lcd_touch_handle_t handle; + +public: + + EspLcdTouchDriver(esp_lcd_touch_handle_t handle) : handle(handle) {} + + bool getTouchedPoints(uint16_t* x, uint16_t* y, uint16_t* strength, uint8_t* pointCount, uint8_t maxPointCount) override; +}; diff --git a/Drivers/FT5x06/CMakeLists.txt b/Drivers/FT5x06/CMakeLists.txt index 8c3e1680..3e4eabdc 100644 --- a/Drivers/FT5x06/CMakeLists.txt +++ b/Drivers/FT5x06/CMakeLists.txt @@ -1,5 +1,5 @@ idf_component_register( SRC_DIRS "Source" INCLUDE_DIRS "Source" - REQUIRES Tactility esp_lvgl_port esp_lcd_touch esp_lcd_touch_ft5x06 driver + REQUIRES Tactility EspLcdCompat esp_lcd_touch_ft5x06 driver ) diff --git a/Drivers/FT5x06/Source/Ft5x06Touch.cpp b/Drivers/FT5x06/Source/Ft5x06Touch.cpp index 2c64ce1c..12f6339a 100644 --- a/Drivers/FT5x06/Source/Ft5x06Touch.cpp +++ b/Drivers/FT5x06/Source/Ft5x06Touch.cpp @@ -8,15 +8,17 @@ #define TAG "ft5x06" -bool Ft5x06Touch::start(lv_display_t* display) { +bool Ft5x06Touch::createIoHandle(esp_lcd_panel_io_handle_t& outHandle) { esp_lcd_panel_io_i2c_config_t io_config = ESP_LCD_TOUCH_IO_I2C_FT5x06_CONFIG(); + return esp_lcd_new_panel_io_i2c(configuration->port, &io_config, &outHandle) == ESP_OK; +} - if (esp_lcd_new_panel_io_i2c(configuration->port, &io_config, &ioHandle) != ESP_OK) { - TT_LOG_E(TAG, "Touch IO I2C creation failed"); - return false; - } +bool Ft5x06Touch::createTouchHandle(esp_lcd_panel_io_handle_t ioHandle, const esp_lcd_touch_config_t& configuration, esp_lcd_touch_handle_t& panelHandle) { + return esp_lcd_touch_new_i2c_ft5x06(ioHandle, &configuration, &panelHandle) == ESP_OK; +} - esp_lcd_touch_config_t config = { +esp_lcd_touch_config_t Ft5x06Touch::createEspLcdTouchConfig() { + return { .x_max = configuration->xMax, .y_max = configuration->yMax, .rst_gpio_num = configuration->pinReset, @@ -35,47 +37,4 @@ bool Ft5x06Touch::start(lv_display_t* display) { .user_data = nullptr, .driver_data = nullptr }; - - if (esp_lcd_touch_new_i2c_ft5x06(ioHandle, &config, &touchHandle) != ESP_OK) { - TT_LOG_E(TAG, "Driver init failed"); - cleanup(); - return false; - } - - const lvgl_port_touch_cfg_t touch_cfg = { - .disp = display, - .handle = touchHandle, - }; - - TT_LOG_I(TAG, "Adding touch to LVGL"); - deviceHandle = lvgl_port_add_touch(&touch_cfg); - if (deviceHandle == nullptr) { - TT_LOG_E(TAG, "Adding touch failed"); - cleanup(); - return false; - } - - return true; -} - -bool Ft5x06Touch::stop() { - cleanup(); - return true; -} - -void Ft5x06Touch::cleanup() { - if (deviceHandle != nullptr) { - lv_indev_delete(deviceHandle); - deviceHandle = nullptr; - } - - if (touchHandle != nullptr) { - esp_lcd_touch_del(touchHandle); - touchHandle = nullptr; - } - - if (ioHandle != nullptr) { - esp_lcd_panel_io_del(ioHandle); - ioHandle = nullptr; - } } diff --git a/Drivers/FT5x06/Source/Ft5x06Touch.h b/Drivers/FT5x06/Source/Ft5x06Touch.h index 90ddc594..76a00466 100644 --- a/Drivers/FT5x06/Source/Ft5x06Touch.h +++ b/Drivers/FT5x06/Source/Ft5x06Touch.h @@ -4,10 +4,9 @@ #include #include -#include -#include +#include -class Ft5x06Touch final : public tt::hal::touch::TouchDevice { +class Ft5x06Touch final : public EspLcdTouch { public: @@ -52,11 +51,12 @@ public: private: std::unique_ptr configuration; - esp_lcd_panel_io_handle_t _Nullable ioHandle = nullptr; - esp_lcd_touch_handle_t _Nullable touchHandle = nullptr; - lv_indev_t* _Nullable deviceHandle = nullptr; - void cleanup(); + bool createIoHandle(esp_lcd_panel_io_handle_t& outHandle) override; + + bool createTouchHandle(esp_lcd_panel_io_handle_t ioHandle, const esp_lcd_touch_config_t& configuration, esp_lcd_touch_handle_t& panelHandle) override; + + esp_lcd_touch_config_t createEspLcdTouchConfig() override; public: @@ -64,10 +64,7 @@ public: assert(configuration != nullptr); } - bool start(lv_display_t* display) override; - bool stop() override; - lv_indev_t* _Nullable getLvglIndev() override { return deviceHandle; } + std::string getName() const override { return "FT5x06"; } - std::string getName() const final { return "FT5x06"; } - std::string getDescription() const final { return "I2C Touch Driver"; } + std::string getDescription() const override { return "FT5x06 I2C touch driver"; } }; diff --git a/Drivers/FT6x36/Source/Ft6x36Touch.cpp b/Drivers/FT6x36/Source/Ft6x36Touch.cpp index e163c768..ed4ef07a 100644 --- a/Drivers/FT6x36/Source/Ft6x36Touch.cpp +++ b/Drivers/FT6x36/Source/Ft6x36Touch.cpp @@ -1,5 +1,6 @@ #include "Ft6x36Touch.h" +#include #include #include @@ -7,22 +8,23 @@ #define TAG "ft6x36" -static void touchReadCallback(lv_indev_t* indev, lv_indev_data_t* data) { +void Ft6x36Touch::touchReadCallback(lv_indev_t* indev, lv_indev_data_t* data) { auto* touch = (Ft6x36Touch*)lv_indev_get_driver_data(indev); - touch->readLast(data); + touch->mutex.lock(); + data->point = touch->lastPoint; + data->state = touch->lastState; + touch->mutex.unlock(); } Ft6x36Touch::Ft6x36Touch(std::unique_ptr inConfiguration) : - configuration(std::move(inConfiguration)), - driverThread(tt::Thread("ft6x36", 4096, [this]() { - driverThreadMain(); - return 0; - })) -{} + configuration(std::move(inConfiguration)) { + nativeTouch = std::make_shared(*this); +} Ft6x36Touch::~Ft6x36Touch() { - if (driverThread.getState() != tt::Thread::State::Stopped) { - stop(); + if (driverThread != nullptr && driverThread->getState() != tt::Thread::State::Stopped) { + interruptDriverThread = true; + driverThread->join(); } } @@ -59,7 +61,7 @@ void Ft6x36Touch::driverThreadMain() { } } -bool Ft6x36Touch::shouldInterruptDriverThread() { +bool Ft6x36Touch::shouldInterruptDriverThread() const { bool interrupt = false; if (mutex.lock(50 / portTICK_PERIOD_MS)) { interrupt = interruptDriverThread; @@ -68,35 +70,65 @@ bool Ft6x36Touch::shouldInterruptDriverThread() { return interrupt; } -bool Ft6x36Touch::start(lv_display_t* display) { - TT_LOG_I(TAG, "start"); +bool Ft6x36Touch::start() { + TT_LOG_I(TAG, "Start"); - driverThread.start(); - - uint16_t width = lv_display_get_horizontal_resolution(display); - uint16_t height = lv_display_get_vertical_resolution(display); - if (!driver.begin(FT6X36_DEFAULT_THRESHOLD, width, height)) { + if (!driver.begin(FT6X36_DEFAULT_THRESHOLD, configuration->width, configuration->height)) { TT_LOG_E(TAG, "driver.begin() failed"); return false; } + mutex.lock(); + + interruptDriverThread = false; + + driverThread = std::make_shared("ft6x36", 4096, [this] { + driverThreadMain(); + return 0; + }); + + driverThread->start(); + + mutex.unlock(); + + return true; +} + +bool Ft6x36Touch::stop() { + TT_LOG_I(TAG, "Stop"); + + mutex.lock(); + interruptDriverThread = true; + mutex.unlock(); + + driverThread->join(); + + mutex.lock(); + driverThread = nullptr; + mutex.unlock(); + + return false; +} + +bool Ft6x36Touch::startLvgl(lv_display_t* display) { + if (deviceHandle != nullptr) { + return false; + } + deviceHandle = lv_indev_create(); lv_indev_set_type(deviceHandle, LV_INDEV_TYPE_POINTER); lv_indev_set_driver_data(deviceHandle, this); lv_indev_set_read_cb(deviceHandle, touchReadCallback); - TT_LOG_I(TAG, "start success"); return true; } -bool Ft6x36Touch::stop() { +bool Ft6x36Touch::stopLvgl() { + if (deviceHandle == nullptr) { + return false; + } + lv_indev_delete(deviceHandle); - interruptDriverThread = true; - driverThread.join(); + deviceHandle = nullptr; return true; } - -void Ft6x36Touch::readLast(lv_indev_data_t* data) { - data->point = lastPoint; - data->state = lastState; -} diff --git a/Drivers/FT6x36/Source/Ft6x36Touch.h b/Drivers/FT6x36/Source/Ft6x36Touch.h index 75ef8af1..2c4cd864 100644 --- a/Drivers/FT6x36/Source/Ft6x36Touch.h +++ b/Drivers/FT6x36/Source/Ft6x36Touch.h @@ -15,41 +15,78 @@ public: Configuration( i2c_port_t port, - gpio_num_t pinInterrupt + gpio_num_t pinInterrupt, + uint16_t width, + uint16_t height ) : port(port), - pinInterrupt(pinInterrupt) + pinInterrupt(pinInterrupt), + width(width), + height(height) {} i2c_port_t port; gpio_num_t pinInterrupt; - }; + uint16_t width; + uint16_t height; +}; private: std::unique_ptr configuration; lv_indev_t* _Nullable deviceHandle = nullptr; FT6X36 driver = FT6X36(configuration->port, configuration->pinInterrupt); - tt::Thread driverThread; + std::shared_ptr driverThread; bool interruptDriverThread = false; tt::Mutex mutex; + std::shared_ptr nativeTouch; lv_point_t lastPoint = { .x = 0, .y = 0 }; lv_indev_state_t lastState = LV_INDEV_STATE_RELEASED; - bool shouldInterruptDriverThread(); + bool shouldInterruptDriverThread() const; + + void driverThreadMain(); + + static void touchReadCallback(lv_indev_t* indev, lv_indev_data_t* data); public: explicit Ft6x36Touch(std::unique_ptr inConfiguration); - ~Ft6x36Touch() final; + ~Ft6x36Touch() override; - std::string getName() const final { return "FT6x36"; } - std::string getDescription() const final { return "I2C touch driver"; } + std::string getName() const override { return "FT6x36"; } + std::string getDescription() const override { return "FT6x36 I2C touch driver"; } - bool start(lv_display_t* display) override; + bool start() override; bool stop() override; - void readLast(lv_indev_data_t* data); + bool supportsLvgl() const override { return true; } + bool startLvgl(lv_display_t* display) override; + bool stopLvgl() override; + lv_indev_t* _Nullable getLvglIndev() override { return deviceHandle; } - void driverThreadMain(); + + class Ft6TouchDriver : public tt::hal::touch::TouchDriver { + public: + const Ft6x36Touch& parent; + Ft6TouchDriver(const Ft6x36Touch& parent) : parent(parent) {} + + bool getTouchedPoints(uint16_t* x, uint16_t* y, uint16_t* _Nullable strength, uint8_t* pointCount, uint8_t maxPointCount) { + auto lock = parent.mutex.asScopedLock(); + lock.lock(); + if (parent.lastState == LV_INDEV_STATE_PRESSED) { + *x = parent.lastPoint.x; + *y = parent.lastPoint.y; + *pointCount = 1; + return true; + } else { + *pointCount = 0; + return false; + } + } + }; + + bool supportsTouchDriver() override { return true; } + + std::shared_ptr _Nullable getTouchDriver() override { return nativeTouch; } }; diff --git a/Drivers/GT911/CMakeLists.txt b/Drivers/GT911/CMakeLists.txt index 04e974c0..cb0bb61c 100644 --- a/Drivers/GT911/CMakeLists.txt +++ b/Drivers/GT911/CMakeLists.txt @@ -1,5 +1,5 @@ idf_component_register( SRC_DIRS "Source" INCLUDE_DIRS "Source" - REQUIRES Tactility esp_lvgl_port esp_lcd_touch esp_lcd_touch_gt911 driver + REQUIRES Tactility EspLcdCompat esp_lcd_touch_gt911 driver ) diff --git a/Drivers/GT911/Source/Gt911Touch.cpp b/Drivers/GT911/Source/Gt911Touch.cpp index d911a35e..b2959a9e 100644 --- a/Drivers/GT911/Source/Gt911Touch.cpp +++ b/Drivers/GT911/Source/Gt911Touch.cpp @@ -5,11 +5,10 @@ #include #include -#include #define TAG "GT911" -bool Gt911Touch::start(lv_display_t* display) { +bool Gt911Touch::createIoHandle(esp_lcd_panel_io_handle_t& outHandle) { esp_lcd_panel_io_i2c_config_t io_config = ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG(); /** @@ -26,12 +25,15 @@ bool Gt911Touch::start(lv_display_t* display) { return false; } - if (esp_lcd_new_panel_io_i2c(configuration->port, &io_config, &ioHandle) != ESP_OK) { - TT_LOG_E(TAG, "Touch IO I2C creation failed"); - return false; - } + return esp_lcd_new_panel_io_i2c(configuration->port, &io_config, &outHandle) == ESP_OK; +} - esp_lcd_touch_config_t config = { +bool Gt911Touch::createTouchHandle(esp_lcd_panel_io_handle_t ioHandle, const esp_lcd_touch_config_t& configuration, esp_lcd_touch_handle_t& panelHandle) { + return esp_lcd_touch_new_i2c_gt911(ioHandle, &configuration, &panelHandle) == ESP_OK; +} + +esp_lcd_touch_config_t Gt911Touch::createEspLcdTouchConfig() { + return { .x_max = configuration->xMax, .y_max = configuration->yMax, .rst_gpio_num = configuration->pinReset, @@ -50,43 +52,4 @@ bool Gt911Touch::start(lv_display_t* display) { .user_data = nullptr, .driver_data = nullptr }; - - if (esp_lcd_touch_new_i2c_gt911(ioHandle, &config, &touchHandle) != ESP_OK) { - TT_LOG_E(TAG, "Driver init failed"); - cleanup(); - return false; - } - - const lvgl_port_touch_cfg_t touch_cfg = { - .disp = display, - .handle = touchHandle, - }; - - TT_LOG_I(TAG, "Adding touch to LVGL"); - deviceHandle = lvgl_port_add_touch(&touch_cfg); - if (deviceHandle == nullptr) { - TT_LOG_E(TAG, "Adding touch failed"); - cleanup(); - return false; - } - - return true; -} - -bool Gt911Touch::stop() { - cleanup(); - return true; -} - -void Gt911Touch::cleanup() { - if (deviceHandle != nullptr) { - lv_indev_delete(deviceHandle); - deviceHandle = nullptr; - touchHandle = nullptr; - } - - if (ioHandle != nullptr) { - esp_lcd_panel_io_del(ioHandle); - ioHandle = nullptr; - } } diff --git a/Drivers/GT911/Source/Gt911Touch.h b/Drivers/GT911/Source/Gt911Touch.h index 41e6393a..1ded5fda 100644 --- a/Drivers/GT911/Source/Gt911Touch.h +++ b/Drivers/GT911/Source/Gt911Touch.h @@ -4,10 +4,9 @@ #include #include -#include -#include +#include -class Gt911Touch final : public tt::hal::touch::TouchDevice { +class Gt911Touch final : public EspLcdTouch { public: @@ -52,11 +51,12 @@ public: private: std::unique_ptr configuration; - esp_lcd_panel_io_handle_t _Nullable ioHandle = nullptr; - esp_lcd_touch_handle_t _Nullable touchHandle = nullptr; - lv_indev_t* _Nullable deviceHandle = nullptr; - void cleanup(); + bool createIoHandle(esp_lcd_panel_io_handle_t& outHandle) override; + + bool createTouchHandle(esp_lcd_panel_io_handle_t ioHandle, const esp_lcd_touch_config_t& configuration, esp_lcd_touch_handle_t& panelHandle) override; + + esp_lcd_touch_config_t createEspLcdTouchConfig() override; public: @@ -64,10 +64,7 @@ public: assert(configuration != nullptr); } - bool start(lv_display_t* display) override; - bool stop() override; - lv_indev_t* _Nullable getLvglIndev() override { return deviceHandle; } + std::string getName() const override { return "GT911"; } - std::string getName() const final { return "GT911"; } - std::string getDescription() const final { return "I2C Touch Driver"; } + std::string getDescription() const override { return "GT911 I2C touch driver"; } }; diff --git a/Drivers/ILI934x/CMakeLists.txt b/Drivers/ILI934x/CMakeLists.txt index ac5bb4bf..ec97215b 100644 --- a/Drivers/ILI934x/CMakeLists.txt +++ b/Drivers/ILI934x/CMakeLists.txt @@ -1,5 +1,5 @@ idf_component_register( SRC_DIRS "Source" INCLUDE_DIRS "Source" - REQUIRES Tactility esp_lvgl_port esp_lcd esp_lcd_ili9341 driver + REQUIRES Tactility EspLcdCompat esp_lcd_ili9341 driver ) diff --git a/Drivers/ILI934x/Source/Ili934xDisplay.cpp b/Drivers/ILI934x/Source/Ili934xDisplay.cpp index 90d23224..71f7aa0a 100644 --- a/Drivers/ILI934x/Source/Ili934xDisplay.cpp +++ b/Drivers/ILI934x/Source/Ili934xDisplay.cpp @@ -6,11 +6,9 @@ #include #include -#define TAG "ili934x" - -bool Ili934xDisplay::start() { - TT_LOG_I(TAG, "Starting"); +constexpr const char* TAG = "ILI934x"; +bool Ili934xDisplay::createIoHandle(esp_lcd_panel_io_handle_t& outHandle) { const esp_lcd_panel_io_spi_config_t panel_io_config = { .cs_gpio_num = configuration->csPin, .dc_gpio_num = configuration->dcPin, @@ -35,11 +33,10 @@ bool Ili934xDisplay::start() { } }; - if (esp_lcd_new_panel_io_spi(configuration->spiBusHandle, &panel_io_config, &ioHandle) != ESP_OK) { - TT_LOG_E(TAG, "Failed to create panel"); - return false; - } + return esp_lcd_new_panel_io_spi(configuration->spiBusHandle, &panel_io_config, &outHandle) == ESP_OK; +} +bool Ili934xDisplay::createPanelHandle(esp_lcd_panel_io_handle_t ioHandle, esp_lcd_panel_handle_t& panelHandle) { const esp_lcd_panel_dev_config_t panel_config = { .reset_gpio_num = configuration->resetPin, .rgb_ele_order = configuration->rgbElementOrder, @@ -86,18 +83,15 @@ bool Ili934xDisplay::start() { return false; } - uint32_t buffer_size; - if (configuration->bufferSize == 0) { - buffer_size = configuration->horizontalResolution * configuration->verticalResolution / 10; - } else { - buffer_size = configuration->bufferSize; - } + return true; +} - const lvgl_port_display_cfg_t disp_cfg = { +lvgl_port_display_cfg_t Ili934xDisplay::getLvglPortDisplayConfig(esp_lcd_panel_io_handle_t ioHandle, esp_lcd_panel_handle_t panelHandle) { + return { .io_handle = ioHandle, .panel_handle = panelHandle, .control_handle = nullptr, - .buffer_size = buffer_size, + .buffer_size = configuration->bufferSize, .double_buffer = false, .trans_size = 0, .hres = configuration->horizontalResolution, @@ -118,28 +112,6 @@ bool Ili934xDisplay::start() { .direct_mode = false } }; - - displayHandle = lvgl_port_add_disp(&disp_cfg); - - TT_LOG_I(TAG, "Finished"); - return displayHandle != nullptr; -} - -bool Ili934xDisplay::stop() { - assert(displayHandle != nullptr); - - lvgl_port_remove_disp(displayHandle); - - if (esp_lcd_panel_del(panelHandle) != ESP_OK) { - return false; - } - - if (esp_lcd_panel_io_del(ioHandle) != ESP_OK) { - return false; - } - - displayHandle = nullptr; - return true; } /** @@ -174,7 +146,7 @@ void Ili934xDisplay::setGammaCurve(uint8_t index) { gamma_curve }; - if (esp_lcd_panel_io_tx_param(ioHandle , LCD_CMD_GAMSET, param, 1) != ESP_OK) { + if (esp_lcd_panel_io_tx_param(getIoHandle() , LCD_CMD_GAMSET, param, 1) != ESP_OK) { TT_LOG_E(TAG, "Failed to set gamma"); } } diff --git a/Drivers/ILI934x/Source/Ili934xDisplay.h b/Drivers/ILI934x/Source/Ili934xDisplay.h index 3034dac4..ff37dedf 100644 --- a/Drivers/ILI934x/Source/Ili934xDisplay.h +++ b/Drivers/ILI934x/Source/Ili934xDisplay.h @@ -9,7 +9,9 @@ #include #include -class Ili934xDisplay final : public tt::hal::display::DisplayDevice { +#include + +class Ili934xDisplay final : public EspLcdDisplay { public: @@ -41,8 +43,12 @@ public: invertColor(invertColor), bufferSize(bufferSize), rgbElementOrder(rgbElementOrder), - touch(std::move(touch)) - {} + touch(std::move(touch) + ) { + if (this->bufferSize == 0) { + this->bufferSize = horizontalResolution * verticalResolution / 10; + } + } esp_lcd_spi_bus_handle_t spiBusHandle; gpio_num_t csPin; @@ -65,9 +71,12 @@ public: private: std::unique_ptr configuration; - esp_lcd_panel_io_handle_t ioHandle = nullptr; - esp_lcd_panel_handle_t panelHandle = nullptr; - lv_display_t* displayHandle = nullptr; + + bool createIoHandle(esp_lcd_panel_io_handle_t& outHandle) override; + + bool createPanelHandle(esp_lcd_panel_io_handle_t ioHandle, esp_lcd_panel_handle_t& panelHandle) override; + + lvgl_port_display_cfg_t getLvglPortDisplayConfig(esp_lcd_panel_io_handle_t ioHandle, esp_lcd_panel_handle_t panelHandle) override; public: @@ -75,25 +84,21 @@ public: assert(configuration != nullptr); } - std::string getName() const final { return "ILI934x"; } - std::string getDescription() const final { return "ILI934x display"; } + std::string getName() const override { return "ILI934x"; } - bool start() final; + std::string getDescription() const override { return "ILI934x display"; } - bool stop() final; + std::shared_ptr _Nullable getTouchDevice() override { return configuration->touch; } - std::shared_ptr _Nullable createTouch() final { return configuration->touch; } - - void setBacklightDuty(uint8_t backlightDuty) final { + void setBacklightDuty(uint8_t backlightDuty) override { if (configuration->backlightDutyFunction != nullptr) { configuration->backlightDutyFunction(backlightDuty); } } - bool supportsBacklightDuty() const final { return configuration->backlightDutyFunction != nullptr; } + bool supportsBacklightDuty() const override { return configuration->backlightDutyFunction != nullptr; } - void setGammaCurve(uint8_t index) final; - uint8_t getGammaCurveCount() const final { return 4; }; + void setGammaCurve(uint8_t index) override; - lv_display_t* _Nullable getLvglDisplay() const final { return displayHandle; } + uint8_t getGammaCurveCount() const override { return 4; }; }; diff --git a/Drivers/ILI9488/CMakeLists.txt b/Drivers/ILI9488/CMakeLists.txt index c04b4c91..6fc0164f 100644 --- a/Drivers/ILI9488/CMakeLists.txt +++ b/Drivers/ILI9488/CMakeLists.txt @@ -1,5 +1,5 @@ idf_component_register( SRC_DIRS "Source" INCLUDE_DIRS "Source" - REQUIRES Tactility esp_lvgl_port esp_lcd esp_lcd_ili9488 driver + REQUIRES Tactility EspLcdCompat esp_lcd_ili9488 driver ) diff --git a/Drivers/ILI9488/Source/Ili9488Display.cpp b/Drivers/ILI9488/Source/Ili9488Display.cpp index fe370fab..96e11f18 100644 --- a/Drivers/ILI9488/Source/Ili9488Display.cpp +++ b/Drivers/ILI9488/Source/Ili9488Display.cpp @@ -6,11 +6,9 @@ #include #include -#define TAG "ili9488" - -bool Ili9488Display::start() { - TT_LOG_I(TAG, "Starting"); +#define TAG "ILI9488" +bool Ili9488Display::createIoHandle(esp_lcd_panel_io_handle_t& outHandle) { const esp_lcd_panel_io_spi_config_t panel_io_config = { .cs_gpio_num = configuration->csPin, .dc_gpio_num = configuration->dcPin, @@ -35,10 +33,10 @@ bool Ili9488Display::start() { } }; - if (esp_lcd_new_panel_io_spi(configuration->spiBusHandle, &panel_io_config, &ioHandle) != ESP_OK) { - TT_LOG_E(TAG, "Failed to create panel"); - return false; - } + return esp_lcd_new_panel_io_spi(configuration->spiBusHandle, &panel_io_config, &outHandle) == ESP_OK; +} + +bool Ili9488Display::createPanelHandle(esp_lcd_panel_io_handle_t ioHandle, esp_lcd_panel_handle_t& panelHandle) { const esp_lcd_panel_dev_config_t panel_config = { .reset_gpio_num = configuration->resetPin, @@ -51,14 +49,7 @@ bool Ili9488Display::start() { .vendor_config = nullptr }; - uint32_t buffer_size; - if (configuration->bufferSize == 0) { - buffer_size = configuration->horizontalResolution * configuration->verticalResolution / 20; - } else { - buffer_size = configuration->bufferSize; - } - - if (esp_lcd_new_panel_ili9488(ioHandle, &panel_config, buffer_size, &panelHandle) != ESP_OK) { + if (esp_lcd_new_panel_ili9488(ioHandle, &panel_config, configuration->bufferSize, &panelHandle) != ESP_OK) { TT_LOG_E(TAG, "Failed to create panel"); return false; } @@ -93,11 +84,16 @@ bool Ili9488Display::start() { return false; } - const lvgl_port_display_cfg_t disp_cfg = { + return true; +} + + +lvgl_port_display_cfg_t Ili9488Display::getLvglPortDisplayConfig(esp_lcd_panel_io_handle_t ioHandle, esp_lcd_panel_handle_t panelHandle) { + return { .io_handle = ioHandle, .panel_handle = panelHandle, .control_handle = nullptr, - .buffer_size = buffer_size, + .buffer_size = configuration->bufferSize, .double_buffer = false, .trans_size = 0, .hres = configuration->horizontalResolution, @@ -118,26 +114,4 @@ bool Ili9488Display::start() { .direct_mode = false } }; - - displayHandle = lvgl_port_add_disp(&disp_cfg); - - TT_LOG_I(TAG, "Finished"); - return displayHandle != nullptr; -} - -bool Ili9488Display::stop() { - assert(displayHandle != nullptr); - - lvgl_port_remove_disp(displayHandle); - - if (esp_lcd_panel_del(panelHandle) != ESP_OK) { - return false; - } - - if (esp_lcd_panel_io_del(ioHandle) != ESP_OK) { - return false; - } - - displayHandle = nullptr; - return true; } diff --git a/Drivers/ILI9488/Source/Ili9488Display.h b/Drivers/ILI9488/Source/Ili9488Display.h index 7478b15b..5455e300 100644 --- a/Drivers/ILI9488/Source/Ili9488Display.h +++ b/Drivers/ILI9488/Source/Ili9488Display.h @@ -2,14 +2,13 @@ #include "Tactility/hal/display/DisplayDevice.h" -#include +#include + #include -#include -#include #include #include -class Ili9488Display final : public tt::hal::display::DisplayDevice { +class Ili9488Display final : public EspLcdDisplay { public: @@ -39,8 +38,11 @@ public: mirrorY(mirrorY), invertColor(invertColor), bufferSize(bufferSize), - touch(std::move(touch)) - {} + touch(std::move(touch)) { + if (this->bufferSize == 0) { + this->bufferSize = horizontalResolution * verticalResolution / 10; + } + } esp_lcd_spi_bus_handle_t spiBusHandle; gpio_num_t csPin; @@ -62,9 +64,12 @@ public: private: std::unique_ptr configuration; - esp_lcd_panel_io_handle_t ioHandle = nullptr; - esp_lcd_panel_handle_t panelHandle = nullptr; - lv_display_t* displayHandle = nullptr; + + bool createIoHandle(esp_lcd_panel_io_handle_t& outHandle) override; + + bool createPanelHandle(esp_lcd_panel_io_handle_t ioHandle, esp_lcd_panel_handle_t& panelHandle) override; + + lvgl_port_display_cfg_t getLvglPortDisplayConfig(esp_lcd_panel_io_handle_t ioHandle, esp_lcd_panel_handle_t panelHandle) override; public: @@ -72,24 +77,19 @@ public: assert(configuration != nullptr); } - std::string getName() const final { return "ILI9488"; } - std::string getDescription() const final { return "ILI9488 display"; } + std::string getName() const override { return "ILI9488"; } - bool start() final; + std::string getDescription() const override { return "ILI9488 display"; } - bool stop() final; + std::shared_ptr _Nullable getTouchDevice() override { return configuration->touch; } - std::shared_ptr _Nullable createTouch() final { return configuration->touch; } - - void setBacklightDuty(uint8_t backlightDuty) final { + void setBacklightDuty(uint8_t backlightDuty) override { if (configuration->backlightDutyFunction != nullptr) { configuration->backlightDutyFunction(backlightDuty); } } - bool supportsBacklightDuty() const final { return configuration->backlightDutyFunction != nullptr; } - - lv_display_t* _Nullable getLvglDisplay() const final { return displayHandle; } + bool supportsBacklightDuty() const override { return configuration->backlightDutyFunction != nullptr; } }; std::shared_ptr createDisplay(); diff --git a/Drivers/RgbDisplay/CMakeLists.txt b/Drivers/RgbDisplay/CMakeLists.txt index 8fd62cfe..f0e2b6c2 100644 --- a/Drivers/RgbDisplay/CMakeLists.txt +++ b/Drivers/RgbDisplay/CMakeLists.txt @@ -1,5 +1,5 @@ idf_component_register( SRC_DIRS "Source" INCLUDE_DIRS "Source" - REQUIRES Tactility esp_lvgl_port esp_lcd + REQUIRES Tactility EspLcdCompat ) diff --git a/Drivers/RgbDisplay/Source/RgbDisplay.cpp b/Drivers/RgbDisplay/Source/RgbDisplay.cpp index d75da726..88007e3d 100644 --- a/Drivers/RgbDisplay/Source/RgbDisplay.cpp +++ b/Drivers/RgbDisplay/Source/RgbDisplay.cpp @@ -6,8 +6,16 @@ #include #include #include +#include +#include -#define TAG "RgbDisplay" +constexpr auto TAG = "RgbDisplay"; + +RgbDisplay::~RgbDisplay() { + if (displayDriver != nullptr && displayDriver.use_count() > 1) { + tt_crash("DisplayDriver is still in use. This will cause memory access violations."); + } +} bool RgbDisplay::start() { TT_LOG_I(TAG, "Starting"); @@ -42,25 +50,85 @@ bool RgbDisplay::start() { return false; } - auto horizontal_resolution = configuration->panelConfig.timings.h_res; - auto vertical_resolution = configuration->panelConfig.timings.v_res; + return true; +} - uint32_t buffer_size; - if (configuration->bufferConfiguration.size == 0) { - buffer_size = horizontal_resolution * vertical_resolution / 15; - } else { - buffer_size = configuration->bufferConfiguration.size; +bool RgbDisplay::stop() { + if (lvglDisplay != nullptr) { + stopLvgl(); + lvglDisplay = nullptr; } - const lvgl_port_display_cfg_t display_config = { - .io_handle = ioHandle, + if (panelHandle != nullptr && esp_lcd_panel_del(panelHandle) != ESP_OK) { + return false; + } + + if (displayDriver != nullptr && displayDriver.use_count() > 1) { + TT_LOG_W(TAG, "DisplayDriver is still in use."); + } + + auto touch_device = getTouchDevice(); + if (touch_device != nullptr) { + touch_device->startLvgl(lvglDisplay); + } + + return true; +} + + +bool RgbDisplay::startLvgl() { + assert(lvglDisplay == nullptr); + + if (displayDriver != nullptr && displayDriver.use_count() > 1) { + TT_LOG_W(TAG, "DisplayDriver is still in use."); + } + + auto display_config = getLvglPortDisplayConfig(); + + const lvgl_port_display_rgb_cfg_t rgb_config = { + .flags = { + .bb_mode = configuration->bufferConfiguration.bounceBufferMode, + .avoid_tearing = configuration->bufferConfiguration.avoidTearing + } + }; + + lvglDisplay = lvgl_port_add_disp_rgb(&display_config, &rgb_config); + TT_LOG_I(TAG, "Finished"); + + auto touch_device = getTouchDevice(); + if (touch_device != nullptr) { + touch_device->startLvgl(lvglDisplay); + } + + return lvglDisplay != nullptr; +} + +bool RgbDisplay::stopLvgl() { + if (lvglDisplay == nullptr) { + return false; + } + + auto touch_device = getTouchDevice(); + if (touch_device != nullptr) { + touch_device->stopLvgl(); + } + + lvgl_port_remove_disp(lvglDisplay); + lvglDisplay = nullptr; + + return true; +} + +lvgl_port_display_cfg_t RgbDisplay::getLvglPortDisplayConfig() const { + return { + .io_handle = nullptr, .panel_handle = panelHandle, .control_handle = nullptr, - .buffer_size = buffer_size, + .buffer_size = configuration->bufferConfiguration.size, .double_buffer = configuration->bufferConfiguration.doubleBuffer, .trans_size = 0, - .hres = horizontal_resolution, - .vres = vertical_resolution, + .hres = configuration->panelConfig.timings.h_res, + .vres = configuration->panelConfig.timings.v_res, .monochrome = false, .rotation = { .swap_xy = configuration->swapXY, @@ -77,33 +145,5 @@ bool RgbDisplay::start() { .direct_mode = false } }; - - const lvgl_port_display_rgb_cfg_t rgb_config = { - .flags = { - .bb_mode = configuration->bufferConfiguration.bounceBufferMode, - .avoid_tearing = configuration->bufferConfiguration.avoidTearing - } - }; - - displayHandle = lvgl_port_add_disp_rgb(&display_config, &rgb_config); - TT_LOG_I(TAG, "Finished"); - - return displayHandle != nullptr; } -bool RgbDisplay::stop() { - assert(displayHandle != nullptr); - - lvgl_port_remove_disp(displayHandle); - - if (esp_lcd_panel_del(panelHandle) != ESP_OK) { - return false; - } - - if (esp_lcd_panel_io_del(ioHandle) != ESP_OK) { - return false; - } - - displayHandle = nullptr; - return true; -} diff --git a/Drivers/RgbDisplay/Source/RgbDisplay.h b/Drivers/RgbDisplay/Source/RgbDisplay.h index 3da18c35..84cc6469 100644 --- a/Drivers/RgbDisplay/Source/RgbDisplay.h +++ b/Drivers/RgbDisplay/Source/RgbDisplay.h @@ -1,14 +1,10 @@ #pragma once -#include "Tactility/hal/display/DisplayDevice.h" - +#include +#include #include -#include -#include -#include - -class RgbDisplay : public tt::hal::display::DisplayDevice { +class RgbDisplay final : public display::DisplayDevice { public: @@ -25,7 +21,7 @@ public: esp_lcd_rgb_panel_config_t panelConfig; BufferConfiguration bufferConfiguration; - std::shared_ptr touch; + std::shared_ptr touch; lv_color_format_t colorFormat; bool swapXY; bool mirrorX; @@ -36,7 +32,7 @@ public: Configuration( esp_lcd_rgb_panel_config_t panelConfig, BufferConfiguration bufferConfiguration, - std::shared_ptr touch, + std::shared_ptr touch, lv_color_format_t colorFormat, bool swapXY = false, bool mirrorX = false, @@ -51,16 +47,23 @@ public: mirrorX(mirrorX), mirrorY(mirrorY), invertColor(invertColor), - backlightDutyFunction(std::move(backlightDutyFunction)) - {} + backlightDutyFunction(std::move(backlightDutyFunction)) { + if (this->bufferConfiguration.size == 0) { + auto horizontal_resolution = panelConfig.timings.h_res; + auto vertical_resolution = panelConfig.timings.v_res; + this->bufferConfiguration.size = horizontal_resolution * vertical_resolution / 15; + } + } }; private: - std::unique_ptr configuration = nullptr; - esp_lcd_panel_io_handle_t ioHandle = nullptr; - esp_lcd_panel_handle_t panelHandle = nullptr; - lv_display_t* displayHandle = nullptr; + std::unique_ptr _Nullable configuration = nullptr; + esp_lcd_panel_handle_t _Nullable panelHandle = nullptr; + lv_display_t* _Nullable lvglDisplay = nullptr; + std::shared_ptr _Nullable displayDriver; + + lvgl_port_display_cfg_t getLvglPortDisplayConfig() const; public: @@ -68,24 +71,41 @@ public: assert(configuration != nullptr); } - std::string getName() const final { return "RGB Display"; } - std::string getDescription() const final { return "RGB Display"; } + ~RgbDisplay(); + + std::string getName() const override { return "RGB Display"; } + std::string getDescription() const override { return "RGB Display"; } bool start() override; bool stop() override; - std::shared_ptr _Nullable createTouch() final { return configuration->touch; } + bool supportsLvgl() const override { return true; } - void setBacklightDuty(uint8_t backlightDuty) final { + bool startLvgl() override; + + bool stopLvgl() override; + + std::shared_ptr _Nullable getTouchDevice() override { return configuration->touch; } + + void setBacklightDuty(uint8_t backlightDuty) override { if (configuration->backlightDutyFunction != nullptr) { configuration->backlightDutyFunction(backlightDuty); } } - bool supportsBacklightDuty() const final { return configuration->backlightDutyFunction != nullptr; } + bool supportsBacklightDuty() const override { return configuration->backlightDutyFunction != nullptr; } - lv_display_t* _Nullable getLvglDisplay() const override { return displayHandle; } + lv_display_t* _Nullable getLvglDisplay() const override { return lvglDisplay; } + + bool supportsDisplayDriver() const override { return true; } + + std::shared_ptr _Nullable getDisplayDriver() override { + if (displayDriver == nullptr) { + displayDriver = std::make_shared(panelHandle, getLvglPortDisplayConfig()); + } + return displayDriver; + } }; std::shared_ptr createDisplay(); diff --git a/Drivers/ST7789/CMakeLists.txt b/Drivers/ST7789/CMakeLists.txt index 2dd2736d..7cd677c7 100644 --- a/Drivers/ST7789/CMakeLists.txt +++ b/Drivers/ST7789/CMakeLists.txt @@ -1,5 +1,5 @@ idf_component_register( SRC_DIRS "Source" INCLUDE_DIRS "Source" - REQUIRES Tactility esp_lvgl_port esp_lcd driver + REQUIRES Tactility driver EspLcdCompat ) diff --git a/Drivers/ST7789/Source/St7789Display.cpp b/Drivers/ST7789/Source/St7789Display.cpp index c7e49a21..99a00ac2 100644 --- a/Drivers/ST7789/Source/St7789Display.cpp +++ b/Drivers/ST7789/Source/St7789Display.cpp @@ -7,9 +7,9 @@ #include #include -#define TAG "st7789" +constexpr auto TAG = "ST7789"; -bool St7789Display::start() { +bool St7789Display::createIoHandle(esp_lcd_panel_io_handle_t& outHandle) { TT_LOG_I(TAG, "Starting"); const esp_lcd_panel_io_spi_config_t panel_io_config = { @@ -36,11 +36,16 @@ bool St7789Display::start() { } }; - if (esp_lcd_new_panel_io_spi(configuration->spiBusHandle, &panel_io_config, &ioHandle) != ESP_OK) { + if (esp_lcd_new_panel_io_spi(configuration->spiBusHandle, &panel_io_config, &outHandle) != ESP_OK) { TT_LOG_E(TAG, "Failed to create panel"); return false; } + return true; +} + +bool St7789Display::createPanelHandle(esp_lcd_panel_io_handle_t ioHandle, esp_lcd_panel_handle_t& panelHandle) { + const esp_lcd_panel_dev_config_t panel_config = { .reset_gpio_num = configuration->resetPin, .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, @@ -86,18 +91,16 @@ bool St7789Display::start() { TT_LOG_E(TAG, "Failed to turn display on"); return false; } - uint32_t buffer_size; - if (configuration->bufferSize == 0) { - buffer_size = configuration->horizontalResolution * configuration->verticalResolution / 10; - } else { - buffer_size = configuration->bufferSize; - } - const lvgl_port_display_cfg_t disp_cfg = { + return true; +} + +lvgl_port_display_cfg_t St7789Display::getLvglPortDisplayConfig(esp_lcd_panel_io_handle_t ioHandle, esp_lcd_panel_handle_t panelHandle) { + return lvgl_port_display_cfg_t { .io_handle = ioHandle, .panel_handle = panelHandle, .control_handle = nullptr, - .buffer_size = buffer_size, + .buffer_size = configuration->bufferSize, .double_buffer = false, .trans_size = 0, .hres = configuration->horizontalResolution, @@ -118,30 +121,7 @@ bool St7789Display::start() { .direct_mode = false } }; - - displayHandle = lvgl_port_add_disp(&disp_cfg); - - TT_LOG_I(TAG, "Finished"); - return displayHandle != nullptr; } - -bool St7789Display::stop() { - assert(displayHandle != nullptr); - - lvgl_port_remove_disp(displayHandle); - - if (esp_lcd_panel_del(panelHandle) != ESP_OK) { - return false; - } - - if (esp_lcd_panel_io_del(ioHandle) != ESP_OK) { - return false; - } - - displayHandle = nullptr; - return true; -} - /** * Note: * The datasheet implies this should work, but it doesn't: @@ -174,7 +154,9 @@ void St7789Display::setGammaCurve(uint8_t index) { gamma_curve }; - if (esp_lcd_panel_io_tx_param(ioHandle , LCD_CMD_GAMSET, param, 1) != ESP_OK) { + auto io_handle = getIoHandle(); + assert(io_handle != nullptr); + if (esp_lcd_panel_io_tx_param(io_handle, LCD_CMD_GAMSET, param, 1) != ESP_OK) { TT_LOG_E(TAG, "Failed to set gamma"); } } diff --git a/Drivers/ST7789/Source/St7789Display.h b/Drivers/ST7789/Source/St7789Display.h index 8296d255..b4bdd941 100644 --- a/Drivers/ST7789/Source/St7789Display.h +++ b/Drivers/ST7789/Source/St7789Display.h @@ -1,15 +1,15 @@ #pragma once -#include "Tactility/hal/display/DisplayDevice.h" +#include +#include -#include #include #include #include #include #include -class St7789Display final : public tt::hal::display::DisplayDevice { +class St7789Display final : public EspLcdDisplay { public: @@ -40,7 +40,11 @@ public: invertColor(invertColor), bufferSize(bufferSize), touch(std::move(touch)) - {} + { + if (this->bufferSize == 0) { + this->bufferSize = horizontalResolution * verticalResolution / 10; + } + } esp_lcd_spi_bus_handle_t spiBusHandle; gpio_num_t csPin; @@ -62,9 +66,12 @@ public: private: std::unique_ptr configuration; - esp_lcd_panel_io_handle_t ioHandle = nullptr; - esp_lcd_panel_handle_t panelHandle = nullptr; - lv_display_t* displayHandle = nullptr; + + bool createIoHandle(esp_lcd_panel_io_handle_t& ioHandle) override; + + bool createPanelHandle(esp_lcd_panel_io_handle_t ioHandle, esp_lcd_panel_handle_t& panelHandle) override; + + lvgl_port_display_cfg_t getLvglPortDisplayConfig(esp_lcd_panel_io_handle_t ioHandle, esp_lcd_panel_handle_t panelHandle) override; public: @@ -72,27 +79,22 @@ public: assert(configuration != nullptr); } - std::string getName() const final { return "ST7789"; } - std::string getDescription() const final { return "ST7789 display"; } + std::string getName() const override { return "ST7789"; } - bool start() final; + std::string getDescription() const override { return "ST7789 display"; } - bool stop() final; + std::shared_ptr _Nullable getTouchDevice() override { return configuration->touch; } - std::shared_ptr _Nullable createTouch() final { return configuration->touch; } - - void setBacklightDuty(uint8_t backlightDuty) final { + void setBacklightDuty(uint8_t backlightDuty) override { if (configuration->backlightDutyFunction != nullptr) { configuration->backlightDutyFunction(backlightDuty); } } - bool supportsBacklightDuty() const final { return configuration->backlightDutyFunction != nullptr; } + bool supportsBacklightDuty() const override { return configuration->backlightDutyFunction != nullptr; } - void setGammaCurve(uint8_t index) final; - uint8_t getGammaCurveCount() const final { return 4; }; - - lv_display_t* _Nullable getLvglDisplay() const final { return displayHandle; } + void setGammaCurve(uint8_t index) override; + uint8_t getGammaCurveCount() const override { return 4; }; }; std::shared_ptr createDisplay(); diff --git a/Drivers/ST7796/CMakeLists.txt b/Drivers/ST7796/CMakeLists.txt index 983cc248..f86c3121 100644 --- a/Drivers/ST7796/CMakeLists.txt +++ b/Drivers/ST7796/CMakeLists.txt @@ -1,5 +1,5 @@ idf_component_register( SRC_DIRS "Source" INCLUDE_DIRS "Source" - REQUIRES Tactility esp_lvgl_port esp_lcd_st7796 driver + REQUIRES Tactility EspLcdCompat esp_lcd_st7796 driver ) diff --git a/Drivers/ST7796/Source/St7796Display.cpp b/Drivers/ST7796/Source/St7796Display.cpp index 22ae268f..0cf1c4ec 100644 --- a/Drivers/ST7796/Source/St7796Display.cpp +++ b/Drivers/ST7796/Source/St7796Display.cpp @@ -2,16 +2,13 @@ #include -#include #include #include #include -#define TAG "st7796" - -bool St7796Display::start() { - TT_LOG_I(TAG, "Starting"); +constexpr auto TAG = "ST7796"; +bool St7796Display::createIoHandle(esp_lcd_panel_io_handle_t& ioHandle) { const esp_lcd_panel_io_spi_config_t panel_io_config = { .cs_gpio_num = configuration->csPin, .dc_gpio_num = configuration->dcPin, @@ -36,11 +33,10 @@ bool St7796Display::start() { } }; - if (esp_lcd_new_panel_io_spi(configuration->spiBusHandle, &panel_io_config, &ioHandle) != ESP_OK) { - TT_LOG_E(TAG, "Failed to create panel"); - return false; - } + return esp_lcd_new_panel_io_spi(configuration->spiBusHandle, &panel_io_config, &ioHandle) == ESP_OK; +} +bool St7796Display::createPanelHandle(esp_lcd_panel_io_handle_t ioHandle, esp_lcd_panel_handle_t& panelHandle) { static const st7796_lcd_init_cmd_t lcd_init_cmds[] = { {0x01, (uint8_t[]) {0x00}, 0, 120}, {0x11, (uint8_t[]) {0x00}, 0, 120}, @@ -69,7 +65,6 @@ bool St7796Display::start() { .init_cmds_size = sizeof(lcd_init_cmds) / sizeof(st7796_lcd_init_cmd_t), }; - const esp_lcd_panel_dev_config_t panel_config = { .reset_gpio_num = configuration->resetPin, // Set to -1 if not use #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) @@ -92,7 +87,7 @@ bool St7796Display::start() { }, .vendor_config = nullptr }; -*/ + */ if (esp_lcd_new_panel_st7796(ioHandle, &panel_config, &panelHandle) != ESP_OK) { TT_LOG_E(TAG, "Failed to create panel"); return false; @@ -133,18 +128,15 @@ bool St7796Display::start() { return false; } - uint32_t buffer_size; - if (configuration->bufferSize == 0) { - buffer_size = configuration->horizontalResolution * configuration->verticalResolution / 10; - } else { - buffer_size = configuration->bufferSize; - } + return true; +} - const lvgl_port_display_cfg_t disp_cfg = { +lvgl_port_display_cfg_t St7796Display::getLvglPortDisplayConfig(esp_lcd_panel_io_handle_t ioHandle, esp_lcd_panel_handle_t panelHandle) { + return { .io_handle = ioHandle, .panel_handle = panelHandle, .control_handle = nullptr, - .buffer_size = buffer_size, + .buffer_size = configuration->bufferSize, .double_buffer = false, .trans_size = 0, .hres = configuration->horizontalResolution, @@ -158,28 +150,6 @@ bool St7796Display::start() { .color_format = LV_COLOR_FORMAT_NATIVE, .flags = {.buff_dma = true, .buff_spiram = false, .sw_rotate = false, .swap_bytes = true, .full_refresh = false, .direct_mode = false} }; - - displayHandle = lvgl_port_add_disp(&disp_cfg); - - TT_LOG_I(TAG, "Finished"); - return displayHandle != nullptr; -} - -bool St7796Display::stop() { - assert(displayHandle != nullptr); - - lvgl_port_remove_disp(displayHandle); - - if (esp_lcd_panel_del(panelHandle) != ESP_OK) { - return false; - } - - if (esp_lcd_panel_io_del(ioHandle) != ESP_OK) { - return false; - } - - displayHandle = nullptr; - return true; } void St7796Display::setGammaCurve(uint8_t index) { @@ -200,6 +170,7 @@ void St7796Display::setGammaCurve(uint8_t index) { default: return; } + const uint8_t param[] = { gamma_curve }; diff --git a/Drivers/ST7796/Source/St7796Display.h b/Drivers/ST7796/Source/St7796Display.h index 3b7bf0a8..8ded6d59 100644 --- a/Drivers/ST7796/Source/St7796Display.h +++ b/Drivers/ST7796/Source/St7796Display.h @@ -1,15 +1,10 @@ #pragma once -#include "Tactility/hal/display/DisplayDevice.h" - +#include #include -#include -#include -#include #include -#include -class St7796Display final : public tt::hal::display::DisplayDevice { +class St7796Display final : public EspLcdDisplay { public: @@ -43,7 +38,11 @@ public: gapX(gapX), gapY(gapY), bufferSize(bufferSize), - touch(std::move(touch)) {} + touch(std::move(touch)) { + if (this->bufferSize == 0) { + this->bufferSize = horizontalResolution * verticalResolution / 10; + } + } esp_lcd_spi_bus_handle_t spiBusHandle; gpio_num_t csPin; @@ -67,9 +66,12 @@ public: private: std::unique_ptr configuration; - esp_lcd_panel_io_handle_t ioHandle = nullptr; - esp_lcd_panel_handle_t panelHandle = nullptr; - lv_display_t* displayHandle = nullptr; + + bool createIoHandle(esp_lcd_panel_io_handle_t& ioHandle) override; + + bool createPanelHandle(esp_lcd_panel_io_handle_t ioHandle, esp_lcd_panel_handle_t& panelHandle) override; + + lvgl_port_display_cfg_t getLvglPortDisplayConfig(esp_lcd_panel_io_handle_t ioHandle, esp_lcd_panel_handle_t panelHandle) override; public: @@ -77,27 +79,23 @@ public: assert(configuration != nullptr); } - std::string getName() const final { return "ST7796"; } - std::string getDescription() const final { return "ST7796 display"; } + std::string getName() const override { return "ST7796"; } - bool start() final; + std::string getDescription() const override { return "ST7796 display"; } - bool stop() final; + std::shared_ptr _Nullable getTouchDevice() override { return configuration->touch; } - std::shared_ptr _Nullable createTouch() final { return configuration->touch; } - - void setBacklightDuty(uint8_t backlightDuty) final { + void setBacklightDuty(uint8_t backlightDuty) override { if (configuration->backlightDutyFunction != nullptr) { configuration->backlightDutyFunction(backlightDuty); } } - void setGammaCurve(uint8_t index) final; - uint8_t getGammaCurveCount() const final { return 4; }; + void setGammaCurve(uint8_t index) override; - bool supportsBacklightDuty() const final { return configuration->backlightDutyFunction != nullptr; } + uint8_t getGammaCurveCount() const override { return 4; }; - lv_display_t* _Nullable getLvglDisplay() const final { return displayHandle; } + bool supportsBacklightDuty() const override { return configuration->backlightDutyFunction != nullptr; } }; std::shared_ptr createDisplay(); diff --git a/Drivers/XPT2046/CMakeLists.txt b/Drivers/XPT2046/CMakeLists.txt index 6697b708..3d442aa9 100644 --- a/Drivers/XPT2046/CMakeLists.txt +++ b/Drivers/XPT2046/CMakeLists.txt @@ -1,5 +1,5 @@ idf_component_register( SRC_DIRS "Source" INCLUDE_DIRS "Source" - REQUIRES Tactility esp_lcd_touch esp_lcd_touch_xpt2046 + REQUIRES Tactility EspLcdCompat esp_lcd_touch_xpt2046 ) diff --git a/Drivers/XPT2046/Source/Xpt2046Power.cpp b/Drivers/XPT2046/Source/Xpt2046Power.cpp index 8715d74c..45780d15 100644 --- a/Drivers/XPT2046/Source/Xpt2046Power.cpp +++ b/Drivers/XPT2046/Source/Xpt2046Power.cpp @@ -2,11 +2,28 @@ #include "Xpt2046Touch.h" #include +#include -#define TAG "xpt2046_power" +constexpr auto TAG = "Xpt2046Power"; +constexpr auto BATTERY_VOLTAGE_MIN = 3.2f; +constexpr auto BATTERY_VOLTAGE_MAX = 4.2f; +constexpr auto MAX_VOLTAGE_SAMPLES = 15; -#define BATTERY_VOLTAGE_MIN 3.2f -#define BATTERY_VOLTAGE_MAX 4.2f +static std::shared_ptr findXp2046TouchDevice() { + // Make a safe copy + auto touch = tt::hal::findFirstDevice(tt::hal::Device::Type::Touch); + if (touch == nullptr) { + TT_LOG_E(TAG, "Touch device not found"); + return nullptr; + } + + if (touch->getName() != "XPT2046") { + TT_LOG_E(TAG, "Touch device name mismatch"); + return nullptr; + } + + return std::reinterpret_pointer_cast(touch); +} static uint8_t estimateChargeLevelFromVoltage(uint32_t milliVolt) { float volts = std::min((float)milliVolt / 1000.f, BATTERY_VOLTAGE_MAX); @@ -47,25 +64,26 @@ bool Xpt2046Power::getMetric(MetricType type, MetricData& data) { } } -bool Xpt2046Power::readBatteryVoltageOnce(uint32_t& output) const { - // Make a safe copy - auto touch = Xpt2046Touch::getInstance(); - if (touch != nullptr) { - float vbat; - if (touch->getVBat(vbat)) { - // Convert to mV - output = (uint32_t)(vbat * 1000.f); - return true; +bool Xpt2046Power::readBatteryVoltageOnce(uint32_t& output) { + if (xptTouch == nullptr) { + xptTouch = findXp2046TouchDevice(); + if (xptTouch == nullptr) { + TT_LOG_E(TAG, "XPT2046 touch device not found"); + return false; } } - return false; + float vbat; + if (!xptTouch->getVBat(vbat)) { + return false; + } + + // Convert to mV + output = (uint32_t)(vbat * 1000.f); + return true; } - -#define MAX_VOLTAGE_SAMPLES 15 - -bool Xpt2046Power::readBatteryVoltageSampled(uint32_t& output) const { +bool Xpt2046Power::readBatteryVoltageSampled(uint32_t& output) { size_t samples_read = 0; uint32_t sample_accumulator = 0; uint32_t sample_read_buffer; diff --git a/Drivers/XPT2046/Source/Xpt2046Power.h b/Drivers/XPT2046/Source/Xpt2046Power.h index 3615c885..68b1e93e 100644 --- a/Drivers/XPT2046/Source/Xpt2046Power.h +++ b/Drivers/XPT2046/Source/Xpt2046Power.h @@ -1,8 +1,8 @@ #pragma once #include -#include +class Xpt2046Touch; using tt::hal::power::PowerDevice; /** @@ -11,9 +11,13 @@ using tt::hal::power::PowerDevice; */ class Xpt2046Power : public PowerDevice { + std::shared_ptr xptTouch; + + bool readBatteryVoltageOnce(uint32_t& output); + bool readBatteryVoltageSampled(uint32_t& output); + public: - Xpt2046Power() = default; ~Xpt2046Power() = default; std::string getName() const final { return "XPT2046 Power Measurement"; } @@ -22,10 +26,6 @@ public: bool supportsMetric(MetricType type) const override; bool getMetric(MetricType type, MetricData& data) override; -private: - - bool readBatteryVoltageOnce(uint32_t& output) const; - bool readBatteryVoltageSampled(uint32_t& output) const; }; std::shared_ptr getOrCreatePower(); diff --git a/Drivers/XPT2046/Source/Xpt2046Touch.cpp b/Drivers/XPT2046/Source/Xpt2046Touch.cpp index 8bc3e767..fa3b0b4c 100644 --- a/Drivers/XPT2046/Source/Xpt2046Touch.cpp +++ b/Drivers/XPT2046/Source/Xpt2046Touch.cpp @@ -7,19 +7,19 @@ #include #include -#define TAG "xpt2046_touch" - Xpt2046Touch* Xpt2046Touch::instance = nullptr; -bool Xpt2046Touch::start(lv_display_t* display) { +bool Xpt2046Touch::createIoHandle(esp_lcd_panel_io_handle_t& outHandle) { const esp_lcd_panel_io_spi_config_t io_config = ESP_LCD_TOUCH_IO_SPI_XPT2046_CONFIG(configuration->spiPinCs); + return esp_lcd_new_panel_io_spi(SPI2_HOST, &io_config, &outHandle) == ESP_OK; +} - if (esp_lcd_new_panel_io_spi(SPI2_HOST, &io_config, &ioHandle) != ESP_OK) { - TT_LOG_E(TAG, "Touch IO SPI creation failed"); - return false; - } +bool Xpt2046Touch::createTouchHandle(esp_lcd_panel_io_handle_t ioHandle, const esp_lcd_touch_config_t& config, esp_lcd_touch_handle_t& panelHandle) { + return esp_lcd_touch_new_spi_xpt2046(ioHandle, &config, &panelHandle) == ESP_OK; +} - esp_lcd_touch_config_t config = { +esp_lcd_touch_config_t Xpt2046Touch::createEspLcdTouchConfig() { + return { .x_max = configuration->xMax, .y_max = configuration->yMax, .rst_gpio_num = GPIO_NUM_NC, @@ -38,61 +38,20 @@ bool Xpt2046Touch::start(lv_display_t* display) { .user_data = nullptr, .driver_data = nullptr }; - - if (esp_lcd_touch_new_spi_xpt2046(ioHandle, &config, &touchHandle) != ESP_OK) { - TT_LOG_E(TAG, "XPT2046 driver init failed"); - cleanup(); - return false; - } - - const lvgl_port_touch_cfg_t touch_cfg = { - .disp = display, - .handle = touchHandle, - }; - - TT_LOG_I(TAG, "Adding touch to LVGL"); - deviceHandle = lvgl_port_add_touch(&touch_cfg); - if (deviceHandle == nullptr) { - TT_LOG_E(TAG, "Adding touch failed"); - cleanup(); - return false; - } - - instance = this; - return true; -} - -bool Xpt2046Touch::stop() { - instance = nullptr; - cleanup(); - return true; -} - -void Xpt2046Touch::cleanup() { - if (deviceHandle != nullptr) { - lv_indev_delete(deviceHandle); - deviceHandle = nullptr; - } - - if (touchHandle != nullptr) { - esp_lcd_touch_del(touchHandle); - touchHandle = nullptr; - } - - if (ioHandle != nullptr) { - esp_lcd_panel_io_del(ioHandle); - ioHandle = nullptr; - } } bool Xpt2046Touch::getVBat(float& outputVbat) { - if (touchHandle != nullptr) { - // Shares the SPI bus with the display, so we have to sync/lock as this method might be called from anywhere - if (tt::lvgl::lock(50 / portTICK_PERIOD_MS)) { - esp_lcd_touch_xpt2046_read_battery_level(touchHandle, &outputVbat); - tt::lvgl::unlock(); - return true; - } + auto touch_handle = getTouchHandle(); + if (touch_handle == nullptr) { + return false; } - return false; + + // Shares the SPI bus with the display, so we have to sync/lock as this method might be called from anywhere + if (!tt::lvgl::lock(50 / portTICK_PERIOD_MS)) { + return false; + } + + esp_lcd_touch_xpt2046_read_battery_level(touch_handle, &outputVbat); + tt::lvgl::unlock(); + return true; } diff --git a/Drivers/XPT2046/Source/Xpt2046Touch.h b/Drivers/XPT2046/Source/Xpt2046Touch.h index 21fe3660..e70ba94f 100644 --- a/Drivers/XPT2046/Source/Xpt2046Touch.h +++ b/Drivers/XPT2046/Source/Xpt2046Touch.h @@ -1,13 +1,10 @@ #pragma once -#include "Tactility/hal/touch/TouchDevice.h" +#include -#include +#include -#include -#include - -class Xpt2046Touch : public tt::hal::touch::TouchDevice { +class Xpt2046Touch : public EspLcdTouch { public: @@ -45,11 +42,12 @@ private: static Xpt2046Touch* instance; std::unique_ptr configuration; - esp_lcd_panel_io_handle_t _Nullable ioHandle = nullptr; - esp_lcd_touch_handle_t _Nullable touchHandle = nullptr; - lv_indev_t* _Nullable deviceHandle = nullptr; - void cleanup(); + bool createIoHandle(esp_lcd_panel_io_handle_t& outHandle) override; + + bool createTouchHandle(esp_lcd_panel_io_handle_t ioHandle, const esp_lcd_touch_config_t& configuration, esp_lcd_touch_handle_t& panelHandle) override; + + esp_lcd_touch_config_t createEspLcdTouchConfig() override; public: @@ -58,14 +56,8 @@ public: } std::string getName() const final { return "XPT2046"; } - std::string getDescription() const final { return "I2C touch driver"; } - bool start(lv_display_t* display) override; - bool stop() override; - lv_indev_t* _Nullable getLvglIndev() override { return deviceHandle; } + std::string getDescription() const final { return "XPT2046 I2C touch driver"; } bool getVBat(float& outputVbat); - - /** Used for accessing getVBat() in Power driver */ - static Xpt2046Touch* getInstance() { return instance; } }; diff --git a/ExternalApps/Calculator/tactility.py b/ExternalApps/Calculator/tactility.py index e5ec6c3a..a1ede4ea 100644 --- a/ExternalApps/Calculator/tactility.py +++ b/ExternalApps/Calculator/tactility.py @@ -17,7 +17,7 @@ platform_targets = ["esp32", "esp32s3"] platform_arguments = platform_targets.copy() platform_arguments.append("all") ttbuild_path = ".tactility" -ttbuild_version = "1.2.0" +ttbuild_version = "1.2.1" ttbuild_properties_file = "tactility.properties" ttbuild_cdn = "https://cdn.tactility.one" ttbuild_sdk_json_validity = 3600 # seconds @@ -339,8 +339,6 @@ def read_sdk_json(): def build_action(platform_arg): # Environment validation validate_environment() - # Environment setup - setup_environment() platforms_to_build = platform_targets if platform_arg == "all" else [platform_arg] if not is_valid_platform_name(platform_arg): print_help() @@ -448,6 +446,8 @@ if __name__ == "__main__": verbose = "--verbose" in sys.argv skip_build = "--skip-build" in sys.argv use_local_sdk = "--local-sdk" in sys.argv + # Environment setup + setup_environment() # Update SDK cache (sdk.json) if should_update_sdk_json() and not update_sdk_json(): exit_with_error("Failed to retrieve SDK info") diff --git a/ExternalApps/HelloWorld/tactility.py b/ExternalApps/HelloWorld/tactility.py index e5ec6c3a..a1ede4ea 100644 --- a/ExternalApps/HelloWorld/tactility.py +++ b/ExternalApps/HelloWorld/tactility.py @@ -17,7 +17,7 @@ platform_targets = ["esp32", "esp32s3"] platform_arguments = platform_targets.copy() platform_arguments.append("all") ttbuild_path = ".tactility" -ttbuild_version = "1.2.0" +ttbuild_version = "1.2.1" ttbuild_properties_file = "tactility.properties" ttbuild_cdn = "https://cdn.tactility.one" ttbuild_sdk_json_validity = 3600 # seconds @@ -339,8 +339,6 @@ def read_sdk_json(): def build_action(platform_arg): # Environment validation validate_environment() - # Environment setup - setup_environment() platforms_to_build = platform_targets if platform_arg == "all" else [platform_arg] if not is_valid_platform_name(platform_arg): print_help() @@ -448,6 +446,8 @@ if __name__ == "__main__": verbose = "--verbose" in sys.argv skip_build = "--skip-build" in sys.argv use_local_sdk = "--local-sdk" in sys.argv + # Environment setup + setup_environment() # Update SDK cache (sdk.json) if should_update_sdk_json() and not update_sdk_json(): exit_with_error("Failed to retrieve SDK info") diff --git a/Tactility/Include/Tactility/app/serialconsole/ConnectView.h b/Tactility/Include/Tactility/app/serialconsole/ConnectView.h index 4ac49152..b34ec561 100644 --- a/Tactility/Include/Tactility/app/serialconsole/ConnectView.h +++ b/Tactility/Include/Tactility/app/serialconsole/ConnectView.h @@ -8,10 +8,8 @@ #include "Tactility/hal/uart/Uart.h" #include "Tactility/lvgl/LvglSync.h" #include "Tactility/lvgl/Style.h" -#include "Tactility/service/gui/Gui.h" #include -#include #include namespace tt::app::serialconsole { @@ -117,7 +115,6 @@ public: lv_textarea_set_one_line(speedTextarea, true); lv_obj_set_width(speedTextarea, LV_PCT(50)); lv_obj_align(speedTextarea, LV_ALIGN_TOP_RIGHT, 0, 40); - service::gui::keyboardAddTextArea(speedTextarea); auto* baud_rate_label = lv_label_create(wrapper); lv_obj_align(baud_rate_label, LV_ALIGN_TOP_LEFT, 0, 50); @@ -130,7 +127,7 @@ public: lv_obj_add_event_cb(connect_button, onConnectCallback, LV_EVENT_SHORT_CLICKED, this); } - void onStop() final { + void onStop() { int speed = getSpeedInput(); if (speed > 0) { preferences.putInt32("speed", speed); diff --git a/Tactility/Include/Tactility/app/serialconsole/ConsoleView.h b/Tactility/Include/Tactility/app/serialconsole/ConsoleView.h index df6dee2a..d824dc6b 100644 --- a/Tactility/Include/Tactility/app/serialconsole/ConsoleView.h +++ b/Tactility/Include/Tactility/app/serialconsole/ConsoleView.h @@ -186,7 +186,6 @@ public: lv_obj_set_width(logTextarea, LV_PCT(100)); lv_obj_add_state(logTextarea, LV_STATE_DISABLED); lv_obj_set_style_margin_ver(logTextarea, 0, 0); - service::gui::keyboardAddTextArea(logTextarea); auto* input_wrapper = lv_obj_create(parent); lv_obj_set_size(input_wrapper, LV_PCT(100), LV_SIZE_CONTENT); @@ -200,7 +199,6 @@ public: lv_textarea_set_placeholder_text(inputTextarea, "Text to send"); lv_obj_set_width(inputTextarea, LV_PCT(100)); lv_obj_set_flex_grow(inputTextarea, 1); - service::gui::keyboardAddTextArea(inputTextarea); auto* terminator_dropdown = lv_dropdown_create(input_wrapper); lv_dropdown_set_options(terminator_dropdown, "\\n\n\\r\\n"); diff --git a/Tactility/Include/Tactility/hal/Device.h b/Tactility/Include/Tactility/hal/Device.h index 0f01fe18..864550ca 100644 --- a/Tactility/Include/Tactility/hal/Device.h +++ b/Tactility/Include/Tactility/hal/Device.h @@ -36,7 +36,7 @@ public: virtual ~Device() = default; /** Unique identifier */ - inline Id getId() const { return id; } + Id getId() const { return id; } /** The type of device */ virtual Type getType() const = 0; diff --git a/Tactility/Include/Tactility/hal/display/DisplayDevice.h b/Tactility/Include/Tactility/hal/display/DisplayDevice.h index ea3080fa..6a9dc05f 100644 --- a/Tactility/Include/Tactility/hal/display/DisplayDevice.h +++ b/Tactility/Include/Tactility/hal/display/DisplayDevice.h @@ -10,12 +10,15 @@ class TouchDevice; namespace tt::hal::display { +class DisplayDriver; + class DisplayDevice : public Device { public: Type getType() const override { return Type::Display; } + /** Starts the internal driver */ virtual bool start() = 0; virtual bool stop() = 0; @@ -23,7 +26,7 @@ public: virtual bool isPoweredOn() const { return true; } virtual bool supportsPowerControl() const { return false; } - virtual std::shared_ptr _Nullable createTouch() = 0; + virtual std::shared_ptr _Nullable getTouchDevice() = 0; /** Set a value in the range [0, 255] */ virtual void setBacklightDuty(uint8_t backlightDuty) { /* NO-OP */ } @@ -33,8 +36,14 @@ public: virtual void setGammaCurve(uint8_t index) { /* NO-OP */ } virtual uint8_t getGammaCurveCount() const { return 0; }; - /** After start() returns true, this should return a valid pointer until stop() is called and returns true */ + virtual bool supportsLvgl() const { return false; } + virtual bool startLvgl() { return false; } + virtual bool stopLvgl() { return false; } + virtual lv_display_t* _Nullable getLvglDisplay() const = 0; + + virtual bool supportsDisplayDriver() const { return false; } + virtual std::shared_ptr _Nullable getDisplayDriver() { return nullptr; } }; } // namespace tt::hal::display diff --git a/Tactility/Include/Tactility/hal/display/DisplayDriver.h b/Tactility/Include/Tactility/hal/display/DisplayDriver.h new file mode 100644 index 00000000..a8cd4bed --- /dev/null +++ b/Tactility/Include/Tactility/hal/display/DisplayDriver.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +namespace tt::hal::display { + +enum class ColorFormat { + Monochrome, // 1 bpp + BGR565, + RGB565, + RGB888 +}; + +class DisplayDriver { + +public: + + virtual ~DisplayDriver() = default; + + virtual ColorFormat getColorFormat() const = 0; + virtual uint16_t getPixelWidth() const = 0; + virtual uint16_t getPixelHeight() const = 0; + virtual bool drawBitmap(int xStart, int yStart, int xEnd, int yEnd, const void* pixelData) = 0; +}; + +} \ No newline at end of file diff --git a/Tactility/Include/Tactility/hal/keyboard/KeyboardDevice.h b/Tactility/Include/Tactility/hal/keyboard/KeyboardDevice.h index 1aa618c8..599288f3 100644 --- a/Tactility/Include/Tactility/hal/keyboard/KeyboardDevice.h +++ b/Tactility/Include/Tactility/hal/keyboard/KeyboardDevice.h @@ -14,8 +14,8 @@ public: Type getType() const override { return Type::Keyboard; } - virtual bool start(lv_display_t* display) = 0; - virtual bool stop() = 0; + virtual bool startLvgl(lv_display_t* display) = 0; + virtual bool stopLvgl() = 0; virtual bool isAttached() const = 0; virtual lv_indev_t* _Nullable getLvglIndev() = 0; diff --git a/Tactility/Include/Tactility/hal/touch/TouchDevice.h b/Tactility/Include/Tactility/hal/touch/TouchDevice.h index a6da2fa8..35e9827e 100644 --- a/Tactility/Include/Tactility/hal/touch/TouchDevice.h +++ b/Tactility/Include/Tactility/hal/touch/TouchDevice.h @@ -1,6 +1,7 @@ #pragma once #include "../Device.h" +#include "TouchDriver.h" #include @@ -14,10 +15,18 @@ public: Type getType() const override { return Type::Touch; } - virtual bool start(lv_display_t* display) = 0; + virtual bool start() = 0; virtual bool stop() = 0; + virtual bool supportsLvgl() const = 0; + virtual bool startLvgl(lv_display_t* display) = 0; + virtual bool stopLvgl() = 0; + virtual lv_indev_t* _Nullable getLvglIndev() = 0; + + virtual bool supportsTouchDriver() = 0; + + virtual std::shared_ptr _Nullable getTouchDriver() = 0; }; } diff --git a/Tactility/Include/Tactility/hal/touch/TouchDriver.h b/Tactility/Include/Tactility/hal/touch/TouchDriver.h new file mode 100644 index 00000000..a14f3abb --- /dev/null +++ b/Tactility/Include/Tactility/hal/touch/TouchDriver.h @@ -0,0 +1,23 @@ +#pragma once + +namespace tt::hal::touch { + +class TouchDriver { + +public: + + /** + * Get the coordinates for the currently touched points on the screen. + * + * @param[in] x array of X coordinates + * @param[in] y array of Y coordinates + * @param[in] strength optional array of strengths + * @param[in] pointCount the number of points currently touched on the screen + * @param[in] maxPointCount the maximum number of points that can be touched at once + * + * @return true when touched and coordinates are available + */ + virtual bool getTouchedPoints(uint16_t* x, uint16_t* y, uint16_t* _Nullable strength, uint8_t* pointCount, uint8_t maxPointCount) = 0; +}; + +} diff --git a/Tactility/Include/Tactility/kernel/SystemEvents.h b/Tactility/Include/Tactility/kernel/SystemEvents.h index d186e4c2..36302a77 100644 --- a/Tactility/Include/Tactility/kernel/SystemEvents.h +++ b/Tactility/Include/Tactility/kernel/SystemEvents.h @@ -14,12 +14,14 @@ enum class SystemEvent { BootInitSpiEnd, BootInitUartBegin, BootInitUartEnd, - BootInitLvglBegin, - BootInitLvglEnd, BootSplash, /** Gained IP address */ NetworkConnected, NetworkDisconnected, + /** LVGL devices are initialized and usable */ + LvglStarted, + /** LVGL devices were removed and not usable anymore */ + LvglStopped, /** An important system time-related event, such as NTP update or time-zone change */ Time, }; diff --git a/Tactility/Include/Tactility/lvgl/Keyboard.h b/Tactility/Include/Tactility/lvgl/Keyboard.h index 92534363..83482e7f 100644 --- a/Tactility/Include/Tactility/lvgl/Keyboard.h +++ b/Tactility/Include/Tactility/lvgl/Keyboard.h @@ -48,12 +48,4 @@ bool hardware_keyboard_is_available(); */ void hardware_keyboard_set_indev(lv_indev_t* device); -/** - * Glue code for the on-screen keyboard and the hardware keyboard: - * - Attach automatic hide/show parameters for the on-screen keyboard. - * - Registers the textarea to the default lv_group_t for hardware keyboards. - * @param[in] textarea - */ -void keyboard_add_textarea(lv_obj_t* textarea); - } \ No newline at end of file diff --git a/Tactility/Include/Tactility/lvgl/Lvgl.h b/Tactility/Include/Tactility/lvgl/Lvgl.h index 53117f26..e9872f6a 100644 --- a/Tactility/Include/Tactility/lvgl/Lvgl.h +++ b/Tactility/Include/Tactility/lvgl/Lvgl.h @@ -1,5 +1,11 @@ #pragma once -#include +namespace tt::lvgl { -#include "./Colors.h" +bool isStarted(); + +void start(); + +void stop(); + +} diff --git a/Tactility/Include/Tactility/service/Service.h b/Tactility/Include/Tactility/service/Service.h index c5983c4a..3c772d55 100644 --- a/Tactility/Include/Tactility/service/Service.h +++ b/Tactility/Include/Tactility/service/Service.h @@ -4,6 +4,13 @@ namespace tt::service { +enum class State { + Starting, + Started, + Stopping, + Stopped +}; + // Forward declaration class ServiceContext; diff --git a/Tactility/Include/Tactility/service/ServiceContext.h b/Tactility/Include/Tactility/service/ServiceContext.h index 58127c77..fc1cd86a 100644 --- a/Tactility/Include/Tactility/service/ServiceContext.h +++ b/Tactility/Include/Tactility/service/ServiceContext.h @@ -22,8 +22,8 @@ protected: public: - /** @return a reference ot the service's manifest */ - virtual const service::ServiceManifest& getManifest() const = 0; + /** @return a reference to the service's manifest */ + virtual const ServiceManifest& getManifest() const = 0; /** Retrieve the paths that are relevant to this service */ virtual std::unique_ptr getPaths() const = 0; diff --git a/Tactility/Include/Tactility/service/ServiceRegistry.h b/Tactility/Include/Tactility/service/ServiceRegistry.h index fbae2292..6a6f03c9 100644 --- a/Tactility/Include/Tactility/service/ServiceRegistry.h +++ b/Tactility/Include/Tactility/service/ServiceRegistry.h @@ -29,6 +29,13 @@ bool startService(const std::string& id); */ bool stopService(const std::string& id); +/** Get the state of a service. + * @warning If the service is not found, the returned result will be "Stopped" - even if the service id does not exist + * @param[in] the service id as defined in its manifest + * @return the service state + */ +State getState(const std::string& id); + /** Find a service manifest by its id. * @param[in] id the id as defined in the manifest * @return the matching manifest or nullptr when it wasn't found diff --git a/Tactility/Private/Tactility/lvgl/Init_i.h b/Tactility/Private/Tactility/lvgl/LvglPrivate.h similarity index 100% rename from Tactility/Private/Tactility/lvgl/Init_i.h rename to Tactility/Private/Tactility/lvgl/LvglPrivate.h diff --git a/Tactility/Private/Tactility/service/ServiceInstance.h b/Tactility/Private/Tactility/service/ServiceInstance.h index dc7e0cab..5d4da957 100644 --- a/Tactility/Private/Tactility/service/ServiceInstance.h +++ b/Tactility/Private/Tactility/service/ServiceInstance.h @@ -5,26 +5,29 @@ namespace tt::service { -class ServiceInstance : public ServiceContext { - -private: +class ServiceInstance final : public ServiceContext { Mutex mutex = Mutex(Mutex::Type::Normal); std::shared_ptr manifest; std::shared_ptr service; + State state = State::Stopped; public: - explicit ServiceInstance(std::shared_ptr manifest); + explicit ServiceInstance(std::shared_ptr manifest); ~ServiceInstance() override = default; - /** @return a reference ot the service's manifest */ - const service::ServiceManifest& getManifest() const override; + /** @return a reference to the service's manifest */ + const ServiceManifest& getManifest() const override; /** Retrieve the paths that are relevant to this service */ std::unique_ptr getPaths() const override; std::shared_ptr getService() const { return service; } + + State getState() const { return state; } + + void setState(State newState) { state = newState; } }; } diff --git a/Tactility/Private/Tactility/service/gui/Gui.h b/Tactility/Private/Tactility/service/gui/Gui.h deleted file mode 100644 index 4aa6c98d..00000000 --- a/Tactility/Private/Tactility/service/gui/Gui.h +++ /dev/null @@ -1,88 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "Tactility/app/AppContext.h" - -#include - -#include - -namespace tt::service::gui { - -#define GUI_THREAD_FLAG_DRAW (1 << 0) -#define GUI_THREAD_FLAG_INPUT (1 << 1) -#define GUI_THREAD_FLAG_EXIT (1 << 2) -#define GUI_THREAD_FLAG_ALL (GUI_THREAD_FLAG_DRAW | GUI_THREAD_FLAG_INPUT | GUI_THREAD_FLAG_EXIT) - -/** Gui structure */ -struct Gui { - // Thread and lock - Thread* thread = nullptr; - Mutex mutex = Mutex(Mutex::Type::Recursive); - PubSub::SubscriptionHandle loader_pubsub_subscription = nullptr; - - // Layers and Canvas - lv_obj_t* appRootWidget = nullptr; - lv_obj_t* statusbarWidget = nullptr; - - // App-specific - std::shared_ptr appToRender = nullptr; - - lv_obj_t* _Nullable keyboard = nullptr; - lv_group_t* keyboardGroup = nullptr; -}; - -/** Update GUI, request redraw */ -void requestDraw(); - -/** Lock GUI */ -void lock(); - -/** Unlock GUI */ -void unlock(); - -/** - * Set the app viewport in the gui state and request the gui to draw it. - * @param[in] app - */ -void showApp(std::shared_ptr app); - -/** - * Hide the current app's viewport. - * Does not request a re-draw because after hiding the current app, - * we always show the previous app, and there is always at least 1 app running. - */ -void hideApp(); - -/** - * Show the on-screen keyboard. - * @param[in] textarea the textarea to focus the input for - */ -void softwareKeyboardShow(lv_obj_t* textarea); - -/** - * Hide the on-screen keyboard. - * Has no effect when the keyboard is not visible. - */ -void softwareKeyboardHide(); - -/** - * The on-screen keyboard is only shown when both of these conditions are true: - * - there is no hardware keyboard - * - TT_CONFIG_FORCE_ONSCREEN_KEYBOARD is set to true in tactility_config.h - * @return if we should show a on-screen keyboard for text input inside our apps - */ -bool softwareKeyboardIsEnabled(); - -/** - * Glue code for the on-screen keyboard and the hardware keyboard: - * - Attach automatic hide/show parameters for the on-screen keyboard. - * - Registers the textarea to the default lv_group_t for hardware keyboards. - * @param[in] textarea - */ -void keyboardAddTextArea(lv_obj_t* textarea); - -} // namespace diff --git a/Tactility/Private/Tactility/service/gui/GuiService.h b/Tactility/Private/Tactility/service/gui/GuiService.h new file mode 100644 index 00000000..addec094 --- /dev/null +++ b/Tactility/Private/Tactility/service/gui/GuiService.h @@ -0,0 +1,101 @@ +#pragma once + +#include +#include +#include +#include + +#include "Tactility/app/AppContext.h" + +#include + +#include + +namespace tt::service::gui { + +#define GUI_THREAD_FLAG_DRAW (1 << 0) +#define GUI_THREAD_FLAG_INPUT (1 << 1) +#define GUI_THREAD_FLAG_EXIT (1 << 2) +#define GUI_THREAD_FLAG_ALL (GUI_THREAD_FLAG_DRAW | GUI_THREAD_FLAG_INPUT | GUI_THREAD_FLAG_EXIT) + +class GuiService : public Service { + + // Thread and lock + Thread* thread = nullptr; + Mutex mutex = Mutex(Mutex::Type::Recursive); + PubSub::SubscriptionHandle loader_pubsub_subscription = nullptr; + + // Layers and Canvas + lv_obj_t* appRootWidget = nullptr; + lv_obj_t* statusbarWidget = nullptr; + + // App-specific + std::shared_ptr appToRender = nullptr; + + lv_obj_t* _Nullable keyboard = nullptr; + lv_group_t* keyboardGroup = nullptr; + + bool isStarted = false; + + static void onLoaderMessage(const void* message, TT_UNUSED void* context); + + static int32_t guiMain(); + + lv_obj_t* createAppViews(lv_obj_t* parent); + + void redraw(); + + void lock() const { + tt_check(mutex.lock(pdMS_TO_TICKS(1000))); + } + + void unlock() const { + tt_check(mutex.unlock()); + } + +public: + + void onStart(TT_UNUSED ServiceContext& service) override; + + void onStop(TT_UNUSED ServiceContext& service) override; + + void requestDraw(); + + void showApp(std::shared_ptr app); + + void hideApp(); + + + /** + * Show the on-screen keyboard. + * @param[in] textarea the textarea to focus the input for + */ + void softwareKeyboardShow(lv_obj_t* textarea); + + /** + * Hide the on-screen keyboard. + * Has no effect when the keyboard is not visible. + */ + void softwareKeyboardHide(); + + /** + * The on-screen keyboard is only shown when both of these conditions are true: + * - there is no hardware keyboard + * - TT_CONFIG_FORCE_ONSCREEN_KEYBOARD is set to true in tactility_config.h + * @return if we should show a on-screen keyboard for text input inside our apps + */ + bool softwareKeyboardIsEnabled(); + + /** + * Glue code for the on-screen keyboard and the hardware keyboard: + * - Attach automatic hide/show parameters for the on-screen keyboard. + * - Registers the textarea to the default lv_group_t for hardware keyboards. + * @param[in] textarea + */ + void keyboardAddTextArea(lv_obj_t* textarea); + +}; + +std::shared_ptr findService(); + +} // namespace diff --git a/Tactility/Source/Tactility.cpp b/Tactility/Source/Tactility.cpp index 5cf7e9cf..5ee03dff 100644 --- a/Tactility/Source/Tactility.cpp +++ b/Tactility/Source/Tactility.cpp @@ -1,7 +1,7 @@ #include "Tactility/Tactility.h" #include "Tactility/app/ManifestRegistry.h" -#include "Tactility/lvgl/Init_i.h" +#include "Tactility/lvgl/LvglPrivate.h" #include "Tactility/service/ServiceManifest.h" #include diff --git a/Tactility/Source/app/chat/ChatApp.cpp b/Tactility/Source/app/chat/ChatApp.cpp index f0bb1749..726e83bb 100644 --- a/Tactility/Source/app/chat/ChatApp.cpp +++ b/Tactility/Source/app/chat/ChatApp.cpp @@ -5,7 +5,6 @@ #include #include -#include "Tactility/service/gui/Gui.h" #include "Tactility/lvgl/LvglSync.h" #include @@ -16,7 +15,7 @@ namespace tt::app::chat { constexpr const char* TAG = "ChatApp"; -constexpr const uint8_t BROADCAST_ADDRESS[ESP_NOW_ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +constexpr uint8_t BROADCAST_ADDRESS[ESP_NOW_ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; class ChatApp : public App { @@ -154,7 +153,6 @@ public: lv_obj_set_height(input_field, LV_PCT(100)); lv_textarea_set_placeholder_text(input_field, "Type a message..."); lv_textarea_set_one_line(input_field, true); - service::gui::keyboardAddTextArea(input_field); // Send button auto* send_btn = lv_btn_create(input_panel); diff --git a/Tactility/Source/app/display/Display.cpp b/Tactility/Source/app/display/Display.cpp index bd0b72cb..51169d5b 100644 --- a/Tactility/Source/app/display/Display.cpp +++ b/Tactility/Source/app/display/Display.cpp @@ -72,9 +72,7 @@ class DisplayApp : public App { static void onGammaSliderEvent(lv_event_t* event) { auto* slider = static_cast(lv_event_get_target(event)); - auto* lvgl_display = lv_display_get_default(); - assert(lvgl_display != nullptr); - auto* hal_display = static_cast(lv_display_get_user_data(lvgl_display)); + auto hal_display = hal::findFirstDevice(hal::Device::Type::Display); assert(hal_display != nullptr); if (hal_display->getGammaCurveCount() > 0) { diff --git a/Tactility/Source/app/fileselection/View.cpp b/Tactility/Source/app/fileselection/View.cpp index 10513923..0d5043f2 100644 --- a/Tactility/Source/app/fileselection/View.cpp +++ b/Tactility/Source/app/fileselection/View.cpp @@ -10,7 +10,6 @@ #include #include -#include #ifdef ESP_PLATFORM #include "Tactility/service/loader/Loader.h" @@ -200,7 +199,6 @@ void View::init(lv_obj_t* parent, Mode mode) { path_textarea = lv_textarea_create(bottom_wrapper); lv_textarea_set_one_line(path_textarea, true); lv_obj_set_flex_grow(path_textarea, 1); - service::gui::keyboardAddTextArea(path_textarea); lv_obj_add_event_cb(path_textarea, onPathTextChanged, LV_EVENT_VALUE_CHANGED, this); select_button = lv_button_create(bottom_wrapper); diff --git a/Tactility/Source/app/inputdialog/InputDialog.cpp b/Tactility/Source/app/inputdialog/InputDialog.cpp index 749b11a6..44d6e63e 100644 --- a/Tactility/Source/app/inputdialog/InputDialog.cpp +++ b/Tactility/Source/app/inputdialog/InputDialog.cpp @@ -2,7 +2,6 @@ #include "Tactility/lvgl/Toolbar.h" #include "Tactility/service/loader/Loader.h" -#include "Tactility/service/gui/Gui.h" #include @@ -47,8 +46,6 @@ static std::string getTitleParameter(const std::shared_ptr& bundle class InputDialogApp : public App { -private: - static void createButton(lv_obj_t* parent, const std::string& text, void* callbackContext) { lv_obj_t* button = lv_button_create(parent); lv_obj_t* button_label = lv_label_create(button); @@ -71,9 +68,9 @@ private: auto bundle = std::make_unique(); const char* text = lv_textarea_get_text((lv_obj_t*)user_data); bundle->putString(RESULT_BUNDLE_KEY_RESULT, text); - setResult(app::Result::Ok, std::move(bundle)); + setResult(Result::Ok, std::move(bundle)); } else { - setResult(app::Result::Cancelled); + setResult(Result::Cancelled); } service::loader::stopApp(); @@ -106,7 +103,6 @@ public: if (parameters->optString(PARAMETER_BUNDLE_KEY_PREFILLED, prefilled)) { lv_textarea_set_text(textarea, prefilled.c_str()); } - service::gui::keyboardAddTextArea(textarea); auto* button_wrapper = lv_obj_create(parent); lv_obj_set_flex_flow(button_wrapper, LV_FLEX_FLOW_ROW); diff --git a/Tactility/Source/app/notes/Notes.cpp b/Tactility/Source/app/notes/Notes.cpp index b35e3f12..985bcd94 100644 --- a/Tactility/Source/app/notes/Notes.cpp +++ b/Tactility/Source/app/notes/Notes.cpp @@ -172,8 +172,6 @@ class NotesApp : public App { if (!file::findOrCreateDirectory(context.getPaths()->getDataDirectory(), 0777)) { TT_LOG_E(TAG, "Failed to find or create path %s", context.getPaths()->getDataDirectory().c_str()); } - - lvgl::keyboard_add_textarea(uiNoteText); } void onResult(AppContext& appContext, LaunchId launchId, Result result, std::unique_ptr resultData) override { diff --git a/Tactility/Source/app/screenshot/Screenshot.cpp b/Tactility/Source/app/screenshot/Screenshot.cpp index c414c775..a87b22c2 100644 --- a/Tactility/Source/app/screenshot/Screenshot.cpp +++ b/Tactility/Source/app/screenshot/Screenshot.cpp @@ -9,8 +9,6 @@ #include "Tactility/app/AppManifest.h" #include "Tactility/lvgl/LvglSync.h" #include "Tactility/lvgl/Toolbar.h" -#include "Tactility/service/gui/Gui.h" -#include "Tactility/service/loader/Loader.h" #include "Tactility/service/screenshot/Screenshot.h" #include @@ -39,7 +37,7 @@ class ScreenshotApp final : public App { public: ScreenshotApp(); - ~ScreenshotApp() final; + ~ScreenshotApp(); void onShow(AppContext& app, lv_obj_t* parent) override; void onStartPressed(); @@ -271,9 +269,6 @@ void ScreenshotApp::onShow(AppContext& appContext, lv_obj_t* parent) { createFilePathWidgets(wrapper); createTimerSettingsWidgets(wrapper); - service::gui::keyboardAddTextArea(delayTextArea); - service::gui::keyboardAddTextArea(pathTextArea); - updateScreenshotMode(); if (!updateTimer->isRunning()) { diff --git a/Tactility/Source/app/timezone/TimeZone.cpp b/Tactility/Source/app/timezone/TimeZone.cpp index f95326f7..c8372060 100644 --- a/Tactility/Source/app/timezone/TimeZone.cpp +++ b/Tactility/Source/app/timezone/TimeZone.cpp @@ -3,7 +3,6 @@ #include "Tactility/app/timezone/TimeZone.h" #include "Tactility/lvgl/Toolbar.h" #include "Tactility/lvgl/LvglSync.h" -#include "Tactility/service/gui/Gui.h" #include "Tactility/service/loader/Loader.h" #include @@ -66,8 +65,6 @@ void setResultCode(Bundle& bundle, const std::string& code) { class TimeZoneApp : public App { -private: - Mutex mutex; std::vector entries; std::unique_ptr updateTimer; @@ -107,7 +104,7 @@ private: setResultName(*bundle, entry.name); setResultCode(*bundle, entry.code); - setResult(app::Result::Ok, std::move(bundle)); + setResult(Result::Ok, std::move(bundle)); service::loader::stopApp(); } @@ -221,7 +218,6 @@ public: lv_obj_add_event_cb(textarea, onTextareaValueChangedCallback, LV_EVENT_VALUE_CHANGED, this); filterTextareaWidget = textarea; lv_obj_set_flex_grow(textarea, 1); - service::gui::keyboardAddTextArea(textarea); auto* list = lv_list_create(parent); lv_obj_set_width(list, LV_PCT(100)); diff --git a/Tactility/Source/app/wificonnect/View.cpp b/Tactility/Source/app/wificonnect/View.cpp index e01717f3..21f08976 100644 --- a/Tactility/Source/app/wificonnect/View.cpp +++ b/Tactility/Source/app/wificonnect/View.cpp @@ -1,10 +1,8 @@ #include "Tactility/app/wificonnect/View.h" #include "Tactility/app/wificonnect/WifiConnect.h" -#include "Tactility/lvgl/Style.h" #include "Tactility/lvgl/Toolbar.h" #include "Tactility/lvgl/Spinner.h" -#include "Tactility/service/gui/Gui.h" #include #include @@ -187,10 +185,6 @@ void View::init(AppContext& app, lv_obj_t* parent) { // Bottom buttons createBottomButtons(wrapper); - // Keyboard bindings - service::gui::keyboardAddTextArea(ssid_textarea); - service::gui::keyboardAddTextArea(password_textarea); - // Init from app parameters auto bundle = app.getParameters(); if (bundle != nullptr) { diff --git a/Tactility/Source/hal/Hal.cpp b/Tactility/Source/hal/Hal.cpp index 3a7dee72..59f79671 100644 --- a/Tactility/Source/hal/Hal.cpp +++ b/Tactility/Source/hal/Hal.cpp @@ -39,12 +39,12 @@ void init(const Configuration& configuration) { if (!configuration.sdcard->mount(TT_SDCARD_MOUNT_POINT)) { TT_LOG_W(TAG, "SD card mount failed (init can continue)"); } - hal::registerDevice(configuration.sdcard); + registerDevice(configuration.sdcard); } if (configuration.power != nullptr) { - std::shared_ptr power = configuration.power(); - hal::registerDevice(power); + std::shared_ptr power = configuration.power(); + registerDevice(power); } kernel::publishSystemEvent(kernel::SystemEvent::BootInitHalEnd); diff --git a/Tactility/Source/kernel/SystemEvents.cpp b/Tactility/Source/kernel/SystemEvents.cpp index 95d222d2..55152a8e 100644 --- a/Tactility/Source/kernel/SystemEvents.cpp +++ b/Tactility/Source/kernel/SystemEvents.cpp @@ -38,16 +38,16 @@ static const char* getEventName(SystemEvent event) { return TT_STRINGIFY(BootInitUartBegin); case BootInitUartEnd: return TT_STRINGIFY(BootInitUartEnd); - case BootInitLvglBegin: - return TT_STRINGIFY(BootInitLvglBegin); - case BootInitLvglEnd: - return TT_STRINGIFY(BootInitLvglEnd); case BootSplash: return TT_STRINGIFY(BootSplash); case NetworkConnected: return TT_STRINGIFY(NetworkConnected); case NetworkDisconnected: return TT_STRINGIFY(NetworkDisconnected); + case LvglStarted: + return TT_STRINGIFY(LvglStarted); + case LvglStopped: + return TT_STRINGIFY(LvglStopped); case Time: return TT_STRINGIFY(Time); } diff --git a/Tactility/Source/lvgl/Init.cpp b/Tactility/Source/lvgl/Init.cpp deleted file mode 100644 index 2b181455..00000000 --- a/Tactility/Source/lvgl/Init.cpp +++ /dev/null @@ -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 -#include -#include - -#ifdef ESP_PLATFORM -#include "Tactility/lvgl/EspLvglPort.h" -#endif - -#include - -namespace tt::lvgl { - -#define TAG "lvgl_init" - -static std::shared_ptr 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(rotation)); - } - - return display; -} - -static bool initTouch(const std::shared_ptr& display, const std::shared_ptr& 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& display, const std::shared_ptr& 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 diff --git a/Tactility/Source/lvgl/Keyboard.cpp b/Tactility/Source/lvgl/Keyboard.cpp index 34783226..ecf7a0cb 100644 --- a/Tactility/Source/lvgl/Keyboard.cpp +++ b/Tactility/Source/lvgl/Keyboard.cpp @@ -1,20 +1,33 @@ #include "Tactility/lvgl/Keyboard.h" -#include "Tactility/service/gui/Gui.h" +#include "Tactility/service/gui/GuiService.h" + +#include 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); -} - } diff --git a/Tactility/Source/lvgl/Lvgl.cpp b/Tactility/Source/lvgl/Lvgl.cpp new file mode 100644 index 00000000..4bdf8f22 --- /dev/null +++ b/Tactility/Source/lvgl/Lvgl.cpp @@ -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 +#include +#include + +#ifdef ESP_PLATFORM +#include "Tactility/lvgl/EspLvglPort.h" +#endif + +#include +#include +#include +#include +#include + +namespace tt::lvgl { + +#define TAG "Lvgl" + +static bool started = false; + +static std::shared_ptr 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::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::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::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::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::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::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 diff --git a/Tactility/Source/lvgl/Wrappers.cpp b/Tactility/Source/lvgl/Wrappers.cpp new file mode 100644 index 00000000..1e3f162c --- /dev/null +++ b/Tactility/Source/lvgl/Wrappers.cpp @@ -0,0 +1,26 @@ +#ifdef ESP_PLATFORM + +#include "Tactility/app/App.h" +#include "Tactility/service/gui/GuiService.h" + +#include +#include + +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 \ No newline at end of file diff --git a/Tactility/Source/service/ServiceRegistry.cpp b/Tactility/Source/service/ServiceRegistry.cpp index 57ef8e2e..80a893ac 100644 --- a/Tactility/Source/service/ServiceRegistry.cpp +++ b/Tactility/Source/service/ServiceRegistry.cpp @@ -7,6 +7,7 @@ #include #include +#include namespace tt::service { @@ -76,7 +77,9 @@ bool startService(const std::string& id) { service_instance_map[manifest->id] = service_instance; instance_mutex.unlock(); + service_instance->setState(State::Starting); service_instance->getService()->onStart(*service_instance); + service_instance->setState(State::Started); TT_LOG_I(TAG, "Started %s", id.c_str()); @@ -96,11 +99,13 @@ bool stopService(const std::string& id) { TT_LOG_I(TAG, "Stopping %s", id.c_str()); auto service_instance = findServiceInstanceById(id); if (service_instance == nullptr) { - TT_LOG_W(TAG, "service not running: %s", id.c_str()); + TT_LOG_W(TAG, "Service not running: %s", id.c_str()); return false; } + service_instance->setState(State::Stopping); service_instance->getService()->onStop(*service_instance); + service_instance->setState(State::Stopped); instance_mutex.lock(); service_instance_map.erase(id); @@ -115,4 +120,12 @@ bool stopService(const std::string& id) { return true; } +State getState(const std::string& id) { + auto service_instance = findServiceInstanceById(id); + if (service_instance == nullptr) { + return State::Stopped; + } + return service_instance->getState(); +} + } // namespace diff --git a/Tactility/Source/service/gui/Gui.cpp b/Tactility/Source/service/gui/Gui.cpp deleted file mode 100644 index 43ca3ddb..00000000 --- a/Tactility/Source/service/gui/Gui.cpp +++ /dev/null @@ -1,178 +0,0 @@ -#include "Tactility/service/gui/Gui.h" -#include "Tactility/lvgl/LvglSync.h" -#include "Tactility/lvgl/Statusbar.h" -#include "Tactility/lvgl/Style.h" -#include "Tactility/service/loader/Loader.h" - -#include -#include - -namespace tt::service::gui { - -#define TAG "gui" - -// Forward declarations -void redraw(Gui*); -static int32_t guiMain(); - -Gui* gui = nullptr; - -void onLoaderMessage(const void* message, TT_UNUSED void* context) { - auto* event = static_cast(message); - if (event->type == loader::LoaderEventTypeApplicationShowing) { - auto app_instance = app::getCurrentAppContext(); - showApp(app_instance); - } else if (event->type == loader::LoaderEventTypeApplicationHiding) { - hideApp(); - } -} - -Gui* gui_alloc() { - auto* instance = new Gui(); - tt_check(instance != nullptr); - instance->thread = new Thread( - "gui", - 4096, // Last known minimum was 2800 for launching desktop - []() { return guiMain(); } - ); - instance->loader_pubsub_subscription = loader::getPubsub()->subscribe(&onLoaderMessage, instance); - tt_check(lvgl::lock(1000 / portTICK_PERIOD_MS)); - instance->keyboardGroup = lv_group_create(); - auto* screen_root = lv_scr_act(); - assert(screen_root != nullptr); - - lvgl::obj_set_style_bg_blacken(screen_root); - - lv_obj_t* vertical_container = lv_obj_create(screen_root); - lv_obj_set_size(vertical_container, LV_PCT(100), LV_PCT(100)); - lv_obj_set_flex_flow(vertical_container, LV_FLEX_FLOW_COLUMN); - lv_obj_set_style_pad_all(vertical_container, 0, 0); - lv_obj_set_style_pad_gap(vertical_container, 0, 0); - lvgl::obj_set_style_bg_blacken(vertical_container); - - instance->statusbarWidget = lvgl::statusbar_create(vertical_container); - - auto* app_container = lv_obj_create(vertical_container); - lv_obj_set_style_pad_all(app_container, 0, 0); - lv_obj_set_style_border_width(app_container, 0, 0); - lvgl::obj_set_style_bg_blacken(app_container); - lv_obj_set_width(app_container, LV_PCT(100)); - lv_obj_set_flex_grow(app_container, 1); - lv_obj_set_flex_flow(app_container, LV_FLEX_FLOW_COLUMN); - - instance->appRootWidget = app_container; - - lvgl::unlock(); - - return instance; -} - -void gui_free(Gui* instance) { - assert(instance != nullptr); - delete instance->thread; - - lv_group_delete(instance->keyboardGroup); - tt_check(lvgl::lock(1000 / portTICK_PERIOD_MS)); - lv_group_del(instance->keyboardGroup); - lvgl::unlock(); - - delete instance; -} - -void lock() { - assert(gui); - tt_check(gui->mutex.lock(configTICK_RATE_HZ)); -} - -void unlock() { - assert(gui); - tt_check(gui->mutex.unlock()); -} - -void requestDraw() { - assert(gui); - ThreadId thread_id = gui->thread->getId(); - Thread::setFlags(thread_id, GUI_THREAD_FLAG_DRAW); -} - -void showApp(std::shared_ptr app) { - lock(); - tt_check(gui->appToRender == nullptr); - gui->appToRender = std::move(app); - unlock(); - requestDraw(); -} - -void hideApp() { - lock(); - tt_check(gui->appToRender != nullptr); - - // We must lock the LVGL port, because the viewport hide callbacks - // might call LVGL APIs (e.g. to remove the keyboard from the screen root) - tt_check(lvgl::lock(configTICK_RATE_HZ)); - gui->appToRender->getApp()->onHide(*gui->appToRender); - lvgl::unlock(); - - gui->appToRender = nullptr; - unlock(); -} - -static int32_t guiMain() { - tt_check(gui); - Gui* local_gui = gui; - - while (true) { - uint32_t flags = Thread::awaitFlags(GUI_THREAD_FLAG_ALL, EventFlag::WaitAny, (uint32_t)portMAX_DELAY); - - // Process and dispatch draw call - if (flags & GUI_THREAD_FLAG_DRAW) { - Thread::clearFlags(GUI_THREAD_FLAG_DRAW); - redraw(local_gui); - } - - if (flags & GUI_THREAD_FLAG_EXIT) { - Thread::clearFlags(GUI_THREAD_FLAG_EXIT); - break; - } - } - - return 0; -} - -// region AppManifest - -class GuiService : public Service { - -public: - - void onStart(TT_UNUSED ServiceContext& service) override { - assert(gui == nullptr); - gui = gui_alloc(); - - gui->thread->setPriority(THREAD_PRIORITY_SERVICE); - gui->thread->start(); - } - - void onStop(TT_UNUSED ServiceContext& service) override { - assert(gui != nullptr); - lock(); - - ThreadId thread_id = gui->thread->getId(); - Thread::setFlags(thread_id, GUI_THREAD_FLAG_EXIT); - gui->thread->join(); - delete gui->thread; - - unlock(); - - gui_free(gui); - } -}; - -extern const ServiceManifest manifest = { - .id = "Gui", - .createService = create -}; - -// endregion - -} // namespace diff --git a/Tactility/Source/service/gui/GuiDraw.cpp b/Tactility/Source/service/gui/GuiDraw.cpp deleted file mode 100644 index a3ba2333..00000000 --- a/Tactility/Source/service/gui/GuiDraw.cpp +++ /dev/null @@ -1,75 +0,0 @@ -#include "Tactility/service/gui/Gui.h" - -#include "Tactility/app/AppInstance.h" -#include "Tactility/lvgl/LvglSync.h" -#include "Tactility/lvgl/Style.h" - -#include -#include - -namespace tt::service::gui { - -#define TAG "gui" - -static lv_obj_t* createAppViews(Gui* gui, lv_obj_t* parent) { - lv_obj_send_event(gui->statusbarWidget, LV_EVENT_DRAW_MAIN, nullptr); - lv_obj_t* child_container = lv_obj_create(parent); - lv_obj_set_style_pad_all(child_container, 0, 0); - lv_obj_set_width(child_container, LV_PCT(100)); - lv_obj_set_flex_grow(child_container, 1); - - if (softwareKeyboardIsEnabled()) { - gui->keyboard = lv_keyboard_create(parent); - lv_obj_add_flag(gui->keyboard, LV_OBJ_FLAG_HIDDEN); - } else { - gui->keyboard = nullptr; - } - - return child_container; -} - -void redraw(Gui* gui) { - assert(gui); - - // Lock GUI and LVGL - lock(); - - if (lvgl::lock(1000)) { - lv_obj_clean(gui->appRootWidget); - - if (gui->appToRender != nullptr) { - - // Create a default group which adds all objects automatically, - // and assign all indevs to it. - // This enables navigation with limited input, such as encoder wheels. - lv_group_t* group = lv_group_create(); - auto* indev = lv_indev_get_next(nullptr); - while (indev) { - lv_indev_set_group(indev, group); - indev = lv_indev_get_next(indev); - } - lv_group_set_default(group); - - app::Flags flags = std::static_pointer_cast(gui->appToRender)->getFlags(); - if (flags.showStatusbar) { - lv_obj_remove_flag(gui->statusbarWidget, LV_OBJ_FLAG_HIDDEN); - } else { - lv_obj_add_flag(gui->statusbarWidget, LV_OBJ_FLAG_HIDDEN); - } - - lv_obj_t* container = createAppViews(gui, gui->appRootWidget); - gui->appToRender->getApp()->onShow(*gui->appToRender, container); - } else { - TT_LOG_W(TAG, "nothing to draw"); - } - - // Unlock GUI and LVGL - lvgl::unlock(); - } else { - TT_LOG_E(TAG, LOG_MESSAGE_MUTEX_LOCK_FAILED_FMT, "LVGL"); - } - - unlock(); -} - -} // namespace tt::service::gui diff --git a/Tactility/Source/service/gui/GuiService.cpp b/Tactility/Source/service/gui/GuiService.cpp new file mode 100644 index 00000000..1e886702 --- /dev/null +++ b/Tactility/Source/service/gui/GuiService.cpp @@ -0,0 +1,232 @@ +#include "Tactility/service/gui/GuiService.h" +#include "Tactility/lvgl/LvglSync.h" +#include "Tactility/lvgl/Statusbar.h" +#include "Tactility/lvgl/Style.h" +#include "Tactility/service/loader/Loader.h" + +#include +#include +#include + +namespace tt::service::gui { + +extern const ServiceManifest manifest; + +constexpr const char* TAG = "gui"; + +// region AppManifest + +void GuiService::onLoaderMessage(const void* message, TT_UNUSED void* context) { + auto service = findService(); + if (service == nullptr) { + return; + } + + auto* event = static_cast(message); + if (event->type == loader::LoaderEventTypeApplicationShowing) { + auto app_instance = app::getCurrentAppContext(); + service->showApp(app_instance); + } else if (event->type == loader::LoaderEventTypeApplicationHiding) { + service->hideApp(); + } +} + +int32_t GuiService::guiMain() { + while (true) { + uint32_t flags = Thread::awaitFlags(GUI_THREAD_FLAG_ALL, EventFlag::WaitAny, (uint32_t)portMAX_DELAY); + + // When service not started or starting -> exit + State service_state = getState(manifest.id); + if (service_state != State::Started && service_state != State::Starting) { + break; + } + + // Process and dispatch draw call + if (flags & GUI_THREAD_FLAG_DRAW) { + Thread::clearFlags(GUI_THREAD_FLAG_DRAW); + auto service = findService(); + if (service != nullptr) { + service->redraw(); + } + } + + if (flags & GUI_THREAD_FLAG_EXIT) { + Thread::clearFlags(GUI_THREAD_FLAG_EXIT); + break; + } + } + + return 0; +} + +lv_obj_t* GuiService::createAppViews(lv_obj_t* parent) { + lv_obj_send_event(statusbarWidget, LV_EVENT_DRAW_MAIN, nullptr); + lv_obj_t* child_container = lv_obj_create(parent); + lv_obj_set_style_pad_all(child_container, 0, 0); + lv_obj_set_width(child_container, LV_PCT(100)); + lv_obj_set_flex_grow(child_container, 1); + + if (softwareKeyboardIsEnabled()) { + keyboard = lv_keyboard_create(parent); + lv_obj_add_flag(keyboard, LV_OBJ_FLAG_HIDDEN); + } else { + keyboard = nullptr; + } + + return child_container; +} + +void GuiService::redraw() { + // Lock GUI and LVGL + lock(); + + if (lvgl::lock(1000)) { + lv_obj_clean(appRootWidget); + + if (appToRender != nullptr) { + + // Create a default group which adds all objects automatically, + // and assign all indevs to it. + // This enables navigation with limited input, such as encoder wheels. + lv_group_t* group = lv_group_create(); + auto* indev = lv_indev_get_next(nullptr); + while (indev) { + lv_indev_set_group(indev, group); + indev = lv_indev_get_next(indev); + } + lv_group_set_default(group); + + app::Flags flags = std::static_pointer_cast(appToRender)->getFlags(); + if (flags.showStatusbar) { + lv_obj_remove_flag(statusbarWidget, LV_OBJ_FLAG_HIDDEN); + } else { + lv_obj_add_flag(statusbarWidget, LV_OBJ_FLAG_HIDDEN); + } + + lv_obj_t* container = createAppViews(appRootWidget); + appToRender->getApp()->onShow(*appToRender, container); + } else { + TT_LOG_W(TAG, "nothing to draw"); + } + + // Unlock GUI and LVGL + lvgl::unlock(); + } else { + TT_LOG_E(TAG, LOG_MESSAGE_MUTEX_LOCK_FAILED_FMT, "LVGL"); + } + + unlock(); +} + +void GuiService::onStart(TT_UNUSED ServiceContext& service) { + thread = new Thread( + "gui", + 4096, // Last known minimum was 2800 for launching desktop + []() { return guiMain(); } + ); + loader_pubsub_subscription = loader::getPubsub()->subscribe(&onLoaderMessage, nullptr); + tt_check(lvgl::lock(1000 / portTICK_PERIOD_MS)); + keyboardGroup = lv_group_create(); + auto* screen_root = lv_screen_active(); + assert(screen_root != nullptr); + + lvgl::obj_set_style_bg_blacken(screen_root); + + lv_obj_t* vertical_container = lv_obj_create(screen_root); + lv_obj_set_size(vertical_container, LV_PCT(100), LV_PCT(100)); + lv_obj_set_flex_flow(vertical_container, LV_FLEX_FLOW_COLUMN); + lv_obj_set_style_pad_all(vertical_container, 0, 0); + lv_obj_set_style_pad_gap(vertical_container, 0, 0); + lvgl::obj_set_style_bg_blacken(vertical_container); + + statusbarWidget = lvgl::statusbar_create(vertical_container); + + auto* app_container = lv_obj_create(vertical_container); + lv_obj_set_style_pad_all(app_container, 0, 0); + lv_obj_set_style_border_width(app_container, 0, 0); + lvgl::obj_set_style_bg_blacken(app_container); + lv_obj_set_width(app_container, LV_PCT(100)); + lv_obj_set_flex_grow(app_container, 1); + lv_obj_set_flex_flow(app_container, LV_FLEX_FLOW_COLUMN); + + appRootWidget = app_container; + + lvgl::unlock(); + + isStarted = true; + + thread->setPriority(THREAD_PRIORITY_SERVICE); + thread->start(); +} + +void GuiService::onStop(TT_UNUSED ServiceContext& service) { + lock(); + + loader::getPubsub()->unsubscribe(loader_pubsub_subscription); + + appToRender = nullptr; + isStarted = false; + + ThreadId thread_id = thread->getId(); + Thread::setFlags(thread_id, GUI_THREAD_FLAG_EXIT); + thread->join(); + delete thread; + + unlock(); + + tt_check(lvgl::lock(1000 / portTICK_PERIOD_MS)); + lv_group_delete(keyboardGroup); + lvgl::unlock(); +} + +void GuiService::requestDraw() { + ThreadId thread_id = thread->getId(); + Thread::setFlags(thread_id, GUI_THREAD_FLAG_DRAW); +} + +void GuiService::showApp(std::shared_ptr app) { + lock(); + if (!isStarted) { + TT_LOG_W(TAG, "Failed to show app %s: GUI not started", app->getManifest().id.c_str()); + } else { + // Ensure previous app triggers onHide() logic + if (appToRender != nullptr) { + hideApp(); + } + appToRender = std::move(app); + } + unlock(); + requestDraw(); +} + +void GuiService::hideApp() { + lock(); + if (!isStarted) { + TT_LOG_W(TAG, "Failed to hide app: GUI not started"); + } else if (appToRender == nullptr) { + TT_LOG_W(TAG, "hideApp() called but no app is currently shown"); + } else { + // We must lock the LVGL port, because the viewport hide callbacks + // might call LVGL APIs (e.g. to remove the keyboard from the screen root) + tt_check(lvgl::lock(configTICK_RATE_HZ)); + appToRender->getApp()->onHide(*appToRender); + lvgl::unlock(); + appToRender = nullptr; + } + unlock(); +} + +std::shared_ptr findService() { + return std::static_pointer_cast( + findServiceById(manifest.id) + ); +} + +extern const ServiceManifest manifest = { + .id = "Gui", + .createService = create +}; + +// endregion + +} // namespace diff --git a/Tactility/Source/service/gui/Keyboard.cpp b/Tactility/Source/service/gui/Keyboard.cpp index 0aede061..ae754848 100644 --- a/Tactility/Source/service/gui/Keyboard.cpp +++ b/Tactility/Source/service/gui/Keyboard.cpp @@ -1,65 +1,74 @@ #include "Tactility/lvgl/Keyboard.h" #include "Tactility/Check.h" #include "Tactility/lvgl/LvglSync.h" -#include "Tactility/service/gui/Gui.h" +#include "Tactility/service/gui/GuiService.h" #include +#include namespace tt::service::gui { -extern Gui* gui; - static void show_keyboard(lv_event_t* event) { - lv_obj_t* target = lv_event_get_current_target_obj(event); - softwareKeyboardShow(target); - lv_obj_scroll_to_view(target, LV_ANIM_ON); + auto service = findService(); + if (service != nullptr) { + lv_obj_t* target = lv_event_get_current_target_obj(event); + service->softwareKeyboardShow(target); + lv_obj_scroll_to_view(target, LV_ANIM_ON); + } } static void hide_keyboard(TT_UNUSED lv_event_t* event) { - softwareKeyboardHide(); + auto service = findService(); + if (service != nullptr) { + service->softwareKeyboardHide(); + } } -bool softwareKeyboardIsEnabled() { +bool GuiService::softwareKeyboardIsEnabled() { return !lvgl::hardware_keyboard_is_available() || TT_CONFIG_FORCE_ONSCREEN_KEYBOARD; } -void softwareKeyboardShow(lv_obj_t* textarea) { +void GuiService::softwareKeyboardShow(lv_obj_t* textarea) { lock(); - if (gui->keyboard) { - lv_obj_clear_flag(gui->keyboard, LV_OBJ_FLAG_HIDDEN); - lv_keyboard_set_textarea(gui->keyboard, textarea); + if (isStarted && keyboard != nullptr) { + lv_obj_clear_flag(keyboard, LV_OBJ_FLAG_HIDDEN); + lv_keyboard_set_textarea(keyboard, textarea); } unlock(); } -void softwareKeyboardHide() { +void GuiService::softwareKeyboardHide() { lock(); - if (gui->keyboard) { - lv_obj_add_flag(gui->keyboard, LV_OBJ_FLAG_HIDDEN); + if (isStarted && keyboard != nullptr) { + lv_obj_add_flag(keyboard, LV_OBJ_FLAG_HIDDEN); } unlock(); } -void keyboardAddTextArea(lv_obj_t* textarea) { +void GuiService::keyboardAddTextArea(lv_obj_t* textarea) { lock(); - tt_check(lvgl::lock(0), "lvgl should already be locked before calling this method"); - if (softwareKeyboardIsEnabled()) { - lv_obj_add_event_cb(textarea, show_keyboard, LV_EVENT_FOCUSED, nullptr); - lv_obj_add_event_cb(textarea, hide_keyboard, LV_EVENT_DEFOCUSED, nullptr); - lv_obj_add_event_cb(textarea, hide_keyboard, LV_EVENT_READY, nullptr); + if (isStarted) { + tt_check(lvgl::lock(0), "lvgl should already be locked before calling this method"); + + if (softwareKeyboardIsEnabled()) { + lv_obj_add_event_cb(textarea, show_keyboard, LV_EVENT_FOCUSED, nullptr); + lv_obj_add_event_cb(textarea, hide_keyboard, LV_EVENT_DEFOCUSED, nullptr); + lv_obj_add_event_cb(textarea, hide_keyboard, LV_EVENT_READY, nullptr); + } + + // lv_obj_t auto-remove themselves from the group when they are destroyed (last checked in LVGL 8.3) + lv_group_add_obj(keyboardGroup, textarea); + + lvgl::software_keyboard_activate(keyboardGroup); + + lvgl::unlock(); } - // lv_obj_t auto-remove themselves from the group when they are destroyed (last checked in LVGL 8.3) - lv_group_add_obj(gui->keyboardGroup, textarea); - - lvgl::software_keyboard_activate(gui->keyboardGroup); - - lvgl::unlock(); unlock(); } diff --git a/Tactility/Source/service/statusbar/Statusbar.cpp b/Tactility/Source/service/statusbar/Statusbar.cpp index d7b1ae8f..06059cd6 100644 --- a/Tactility/Source/service/statusbar/Statusbar.cpp +++ b/Tactility/Source/service/statusbar/Statusbar.cpp @@ -130,20 +130,18 @@ static _Nullable const char* getPowerStatusIcon() { class StatusbarService final : public Service { -private: - Mutex mutex; std::unique_ptr updateTimer; - int8_t gps_icon_id = lvgl::statusbar_icon_add(); + int8_t gps_icon_id; bool gps_last_state = false; - int8_t wifi_icon_id = lvgl::statusbar_icon_add(); + int8_t wifi_icon_id; const char* wifi_last_icon = nullptr; - int8_t sdcard_icon_id = lvgl::statusbar_icon_add(); + int8_t sdcard_icon_id; const char* sdcard_last_icon = nullptr; - int8_t power_icon_id = lvgl::statusbar_icon_add(); + int8_t power_icon_id; const char* power_last_icon = nullptr; - std::unique_ptr paths; + std::unique_ptr paths; void lock() const { mutex.lock(); @@ -154,7 +152,7 @@ private: } void updateGpsIcon() { - auto gps_state = service::gps::findGpsService()->getState(); + auto gps_state = gps::findGpsService()->getState(); bool show_icon = (gps_state == gps::State::OnPending) || (gps_state == gps::State::On); if (gps_last_state != show_icon) { if (show_icon) { @@ -199,7 +197,7 @@ private: } void updateSdCardIcon() { - auto sdcard = tt::hal::getConfiguration()->sdcard; + auto sdcard = hal::getConfiguration()->sdcard; if (sdcard != nullptr) { auto state = sdcard->getState(); if (state != hal::sdcard::SdCardDevice::State::Unknown) { @@ -229,10 +227,18 @@ private: public: - ~StatusbarService() final { + StatusbarService() { + gps_icon_id = lvgl::statusbar_icon_add(); + sdcard_icon_id = lvgl::statusbar_icon_add(); + wifi_icon_id = lvgl::statusbar_icon_add(); + power_icon_id = lvgl::statusbar_icon_add(); + } + + ~StatusbarService() override { lvgl::statusbar_icon_remove(wifi_icon_id); lvgl::statusbar_icon_remove(sdcard_icon_id); lvgl::statusbar_icon_remove(power_icon_id); + lvgl::statusbar_icon_remove(gps_icon_id); } void onStart(ServiceContext& serviceContext) override { @@ -245,7 +251,7 @@ public: assert(service); onUpdate(service); - updateTimer = std::make_unique(Timer::Type::Periodic, [service]() { + updateTimer = std::make_unique(Timer::Type::Periodic, [service] { onUpdate(service); }); diff --git a/TactilityC/Include/tt_lvgl_keyboard.h b/TactilityC/Include/tt_lvgl_keyboard.h index b7469c9c..78c162c0 100644 --- a/TactilityC/Include/tt_lvgl_keyboard.h +++ b/TactilityC/Include/tt_lvgl_keyboard.h @@ -50,14 +50,6 @@ bool tt_lvgl_hardware_keyboard_is_available(); */ void tt_lvgl_hardware_keyboard_set_indev(lv_indev_t* device); -/** - * Glue code for the on-screen keyboard and the hardware keyboard: - * - Attach automatic hide/show parameters for the on-screen keyboard. - * - Registers the textarea to the default lv_group_t for hardware keyboards. - * @param[in] textarea - */ -void tt_lvgl_keyboard_add_textarea(lv_obj_t* textarea); - #ifdef __cplusplus } #endif \ No newline at end of file diff --git a/TactilityC/Source/tt_init.cpp b/TactilityC/Source/tt_init.cpp index afb75cd0..55139462 100644 --- a/TactilityC/Source/tt_init.cpp +++ b/TactilityC/Source/tt_init.cpp @@ -38,7 +38,7 @@ extern double __muldf3 (double a, double b); extern double __divdf3 (double a, double b); extern int __nedf2 (double a, double b); -const struct esp_elfsym elf_symbols[] { +const esp_elfsym elf_symbols[] { // Hidden functions work-around ESP_ELFSYM_EXPORT(_ZdlPvj), // new? ESP_ELFSYM_EXPORT(_Znwj), // delete? @@ -139,7 +139,6 @@ const struct esp_elfsym elf_symbols[] { ESP_ELFSYM_EXPORT(tt_lvgl_software_keyboard_deactivate), ESP_ELFSYM_EXPORT(tt_lvgl_hardware_keyboard_is_available), ESP_ELFSYM_EXPORT(tt_lvgl_hardware_keyboard_set_indev), - ESP_ELFSYM_EXPORT(tt_lvgl_keyboard_add_textarea), ESP_ELFSYM_EXPORT(tt_lvgl_toolbar_create), ESP_ELFSYM_EXPORT(tt_lvgl_toolbar_create_for_app), ESP_ELFSYM_EXPORT(tt_message_queue_alloc), diff --git a/TactilityC/Source/tt_lvgl_keyboard.cpp b/TactilityC/Source/tt_lvgl_keyboard.cpp index 1a46ce40..db9e3cb2 100644 --- a/TactilityC/Source/tt_lvgl_keyboard.cpp +++ b/TactilityC/Source/tt_lvgl_keyboard.cpp @@ -30,8 +30,4 @@ void tt_lvgl_hardware_keyboard_set_indev(lv_indev_t* device) { tt::lvgl::hardware_keyboard_set_indev(device); } -void tt_lvgl_keyboard_add_textarea(lv_obj_t* textarea) { - tt::lvgl::keyboard_add_textarea(textarea); -} - }