Merge develop into main (#307)
## Launcher - Launcher now has optional power button to show - Launcher layout improvements - Removed text from Launcher (translations with larger amounts of text did not fit small device formats) ## T-Lora Pager - Implement power off (created `BQ25896` driver) - Implemented haptics (created `DRV2605` driver project) and buzz on startup - Reversed scroll wheel - Created `TloraEncoder` device and relocated its logic from `TloraKeyboard` - Disabled SPIRAM test to save 0.5 seconds of boot time (current boot time is very slow) - Update `ST7796` esp_lcd driver to v1.3.4 - Fixed keyboard bug: delete queue in destructor - Fixed driver dependencies: Avoiding usage of global static shared_ptr. Properly constructor-inject everywhere, or use `tt::hal::findDevices()` - I2C configuration is now immutable (you cannot disable it anymore from the I2C Settings app, as it would break crucial drivers) - Renamed I2C and UART subsystems to "Internal" ## Drivers - On/off interface added to `PowerDevice` - Created `tt::hal::Configuration.createDevices`, which is intended to replace all custom create calls for display, keyboard, etc. - Created `EncoderDevice` as a `Device` subtype ## Other Improvements - Changed `findDevices(type, function)` into a templatized function. - Improved SD card mounting ## Fixes - Show Screenshot app again - Fixed Statusbar: some updates were allowed to time out and fail silently: When the Statusbar service would do a state update, the LVGL statusbar would never get updated due to this timeout. - Fixed memory leaks in all `createSdCard()` functions (in most board implementations)
This commit is contained in:
committed by
GitHub
parent
e9f72490fc
commit
50007ea9ed
@@ -1,16 +1,16 @@
|
||||
#include "Tactility/Tactility.h"
|
||||
|
||||
#include "Tactility/app/AppRegistration.h"
|
||||
#include "Tactility/lvgl/LvglPrivate.h"
|
||||
#include "Tactility/service/ServiceManifest.h"
|
||||
#include <Tactility/Tactility.h>
|
||||
|
||||
#include <Tactility/app/AppRegistration.h>
|
||||
#include <Tactility/TactilityConfig.h>
|
||||
#include <Tactility/TactilityHeadless.h>
|
||||
#include <Tactility/lvgl/LvglPrivate.h>
|
||||
#include <Tactility/service/ServiceManifest.h>
|
||||
#include <Tactility/service/ServiceRegistration.h>
|
||||
#include <Tactility/service/loader/Loader.h>
|
||||
|
||||
namespace tt {
|
||||
|
||||
#define TAG "tactility"
|
||||
#define TAG "Tactility"
|
||||
|
||||
static const Configuration* config_instance = nullptr;
|
||||
|
||||
@@ -114,7 +114,7 @@ static void registerSystemApps() {
|
||||
addApp(app::development::manifest);
|
||||
#endif
|
||||
|
||||
if (getConfiguration()->hardware->power != nullptr) {
|
||||
if (hal::findDevices(hal::Device::Type::Power).size() > 0) {
|
||||
addApp(app::power::manifest);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "Tactility/service/ServiceRegistration.h"
|
||||
|
||||
#include <Tactility/Dispatcher.h>
|
||||
#include <Tactility/hal/sdcard/SdCardMounting.h>
|
||||
#include <Tactility/settings/TimePrivate.h>
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
@@ -14,7 +15,7 @@
|
||||
|
||||
namespace tt {
|
||||
|
||||
#define TAG "tactility"
|
||||
constexpr auto* TAG = "Tactility";
|
||||
|
||||
namespace service::gps { extern const ServiceManifest manifest; }
|
||||
namespace service::wifi { extern const ServiceManifest manifest; }
|
||||
@@ -47,11 +48,11 @@ void initHeadless(const hal::Configuration& config) {
|
||||
hardwareConfig = &config;
|
||||
settings::initTimeZone();
|
||||
hal::init(config);
|
||||
hal::sdcard::mountAll();
|
||||
network::ntp::init();
|
||||
registerAndStartSystemServices();
|
||||
}
|
||||
|
||||
|
||||
Dispatcher& getMainDispatcher() {
|
||||
return mainDispatcher;
|
||||
}
|
||||
|
||||
@@ -22,10 +22,10 @@
|
||||
#define CONFIG_TT_SPLASH_DURATION 0
|
||||
#endif
|
||||
|
||||
#define TAG "boot"
|
||||
|
||||
namespace tt::app::boot {
|
||||
|
||||
constexpr auto* TAG = "Boot";
|
||||
|
||||
static std::shared_ptr<hal::display::DisplayDevice> getHalDisplay() {
|
||||
return hal::findFirstDevice<hal::display::DisplayDevice>(hal::Device::Type::Display);
|
||||
}
|
||||
@@ -60,6 +60,7 @@ class BootApp : public App {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static bool setupUsbBootMode() {
|
||||
if (!hal::usb::isUsbBootMode()) {
|
||||
return false;
|
||||
@@ -86,7 +87,7 @@ class BootApp : public App {
|
||||
|
||||
kernel::publishSystemEvent(kernel::SystemEvent::BootSplash);
|
||||
|
||||
setupDisplay();
|
||||
setupDisplay(); // Set backlight
|
||||
|
||||
if (!setupUsbBootMode()) {
|
||||
initFromBootApp();
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#include "Tactility/app/AppContext.h"
|
||||
#include "Tactility/app/launcher/TextResources.h"
|
||||
#include "Tactility/app/AppRegistration.h"
|
||||
#include "Tactility/service/loader/Loader.h"
|
||||
|
||||
@@ -7,53 +6,61 @@
|
||||
|
||||
#include <lvgl.h>
|
||||
#include <Tactility/BootProperties.h>
|
||||
#include <Tactility/hal/power/PowerDevice.h>
|
||||
|
||||
namespace tt::app::launcher {
|
||||
|
||||
constexpr auto* TAG = "Launcher";
|
||||
constexpr auto BUTTON_SIZE = 64;
|
||||
|
||||
static void onAppPressed(TT_UNUSED lv_event_t* e) {
|
||||
auto* appId = (const char*)lv_event_get_user_data(e);
|
||||
service::loader::startApp(appId);
|
||||
}
|
||||
class LauncherApp final : public App {
|
||||
|
||||
static lv_obj_t* createAppButton(lv_obj_t* parent, const char* title, const char* imageFile, const char* appId, int32_t buttonPaddingLeft) {
|
||||
auto* wrapper = lv_obj_create(parent);
|
||||
lv_obj_set_size(wrapper, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
|
||||
lv_obj_set_style_pad_ver(wrapper, 0, 0);
|
||||
lv_obj_set_style_pad_left(wrapper, buttonPaddingLeft, 0);
|
||||
lv_obj_set_style_pad_right(wrapper, 0, 0);
|
||||
lv_obj_set_style_border_width(wrapper, 0, 0);
|
||||
static lv_obj_t* createAppButton(lv_obj_t* parent, const char* imageFile, const char* appId, int32_t horizontalMargin) {
|
||||
auto* apps_button = lv_button_create(parent);
|
||||
lv_obj_set_style_pad_all(apps_button, 0, LV_STATE_DEFAULT);
|
||||
lv_obj_set_style_margin_hor(apps_button, horizontalMargin, LV_STATE_DEFAULT);
|
||||
lv_obj_set_style_shadow_width(apps_button, 0, LV_STATE_DEFAULT);
|
||||
lv_obj_set_style_bg_opa(apps_button, 0, LV_STATE_DEFAULT);
|
||||
|
||||
auto* apps_button = lv_button_create(wrapper);
|
||||
lv_obj_set_style_pad_hor(apps_button, 0, 0);
|
||||
lv_obj_set_style_pad_top(apps_button, 0, 0);
|
||||
lv_obj_set_style_pad_bottom(apps_button, 8, 0);
|
||||
lv_obj_set_style_shadow_width(apps_button, 0, 0);
|
||||
lv_obj_set_style_border_width(apps_button, 0, 0);
|
||||
lv_obj_set_style_bg_opa(apps_button, 0, LV_PART_MAIN);
|
||||
auto* button_image = lv_image_create(apps_button);
|
||||
lv_image_set_src(button_image, imageFile);
|
||||
lv_obj_set_style_image_recolor(button_image, lv_theme_get_color_primary(parent), LV_STATE_DEFAULT);
|
||||
lv_obj_set_style_image_recolor_opa(button_image, LV_OPA_COVER, LV_STATE_DEFAULT);
|
||||
// Ensure buttons are still tappable when the asset fails to load
|
||||
// Icon images are 40x40, so we get some extra padding too
|
||||
lv_obj_set_size(button_image, BUTTON_SIZE, BUTTON_SIZE);
|
||||
|
||||
auto* button_image = lv_image_create(apps_button);
|
||||
lv_image_set_src(button_image, imageFile);
|
||||
lv_obj_set_style_image_recolor(button_image, lv_theme_get_color_primary(parent), LV_STATE_DEFAULT);
|
||||
lv_obj_set_style_image_recolor_opa(button_image, LV_OPA_COVER, LV_STATE_DEFAULT);
|
||||
// Ensure buttons are still tappable when asset fails to load
|
||||
// Icon images are 40x40, so we get some extra padding too
|
||||
lv_obj_set_size(button_image, 64, 64);
|
||||
lv_obj_add_event_cb(apps_button, onAppPressed, LV_EVENT_SHORT_CLICKED, (void*)appId);
|
||||
|
||||
auto* label = lv_label_create(wrapper);
|
||||
lv_label_set_text(label, title);
|
||||
lv_obj_align(label, LV_ALIGN_BOTTOM_MID, 0, 0);
|
||||
return apps_button;
|
||||
}
|
||||
|
||||
lv_obj_add_event_cb(wrapper, onAppPressed, LV_EVENT_SHORT_CLICKED, (void*)appId);
|
||||
lv_obj_add_event_cb(apps_button, onAppPressed, LV_EVENT_SHORT_CLICKED, (void*)appId);
|
||||
lv_obj_add_event_cb(label, onAppPressed, LV_EVENT_SHORT_CLICKED, (void*)appId);
|
||||
static bool shouldShowPowerButton() {
|
||||
bool show_power_button = false;
|
||||
hal::findDevices<hal::power::PowerDevice>(hal::Device::Type::Power, [&show_power_button](const auto& device) {
|
||||
if (device->supportsPowerOff()) {
|
||||
show_power_button = true;
|
||||
return false; // stop iterating
|
||||
} else {
|
||||
return true; // continue iterating
|
||||
}
|
||||
});
|
||||
return show_power_button;
|
||||
}
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
static void onAppPressed(TT_UNUSED lv_event_t* e) {
|
||||
auto* appId = static_cast<const char*>(lv_event_get_user_data(e));
|
||||
service::loader::startApp(appId);
|
||||
}
|
||||
|
||||
class LauncherApp : public App {
|
||||
tt::i18n::TextResources textResources = tt::i18n::TextResources("/system/app/Launcher/i18n");
|
||||
static void onPowerOffPressed(lv_event_t* e) {
|
||||
auto power = hal::findFirstDevice<hal::power::PowerDevice>(hal::Device::Type::Power);
|
||||
if (power != nullptr && power->supportsPowerOff()) {
|
||||
power->powerOff();
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
void onCreate(TT_UNUSED AppContext& app) override {
|
||||
BootProperties boot_properties;
|
||||
@@ -64,41 +71,48 @@ class LauncherApp : public App {
|
||||
}
|
||||
|
||||
void onShow(TT_UNUSED AppContext& app, lv_obj_t* parent) override {
|
||||
textResources.load();
|
||||
auto* buttons_wrapper = lv_obj_create(parent);
|
||||
|
||||
auto* wrapper = lv_obj_create(parent);
|
||||
lv_obj_align(buttons_wrapper, LV_ALIGN_CENTER, 0, 0);
|
||||
// lv_obj_set_style_pad_all(buttons_wrapper, 0, LV_STATE_DEFAULT);
|
||||
lv_obj_set_size(buttons_wrapper, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
|
||||
lv_obj_set_style_border_width(buttons_wrapper, 0, LV_STATE_DEFAULT);
|
||||
lv_obj_set_flex_grow(buttons_wrapper, 1);
|
||||
|
||||
lv_obj_align(wrapper, LV_ALIGN_CENTER, 0, 0);
|
||||
lv_obj_set_style_pad_all(wrapper, 0, 0);
|
||||
lv_obj_set_size(wrapper, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
|
||||
lv_obj_set_style_border_width(wrapper, 0, 0);
|
||||
lv_obj_set_flex_grow(wrapper, 1);
|
||||
|
||||
auto* display = lv_obj_get_display(parent);
|
||||
auto horizontal_px = lv_display_get_horizontal_resolution(display);
|
||||
auto vertical_px = lv_display_get_vertical_resolution(display);
|
||||
bool is_landscape_display = horizontal_px >= vertical_px;
|
||||
const auto* display = lv_obj_get_display(parent);
|
||||
const auto horizontal_px = lv_display_get_horizontal_resolution(display);
|
||||
const auto vertical_px = lv_display_get_vertical_resolution(display);
|
||||
const bool is_landscape_display = horizontal_px >= vertical_px;
|
||||
if (is_landscape_display) {
|
||||
lv_obj_set_flex_flow(wrapper, LV_FLEX_FLOW_ROW);
|
||||
lv_obj_set_flex_flow(buttons_wrapper, LV_FLEX_FLOW_ROW);
|
||||
} else {
|
||||
lv_obj_set_flex_flow(wrapper, LV_FLEX_FLOW_COLUMN);
|
||||
lv_obj_set_flex_flow(buttons_wrapper, LV_FLEX_FLOW_COLUMN);
|
||||
}
|
||||
|
||||
int32_t available_width = lv_display_get_horizontal_resolution(display) - (3 * 80);
|
||||
int32_t padding = is_landscape_display ? std::min(available_width / 4, (int32_t)64) : 0;
|
||||
const int32_t available_width = lv_display_get_horizontal_resolution(display) - (3 * BUTTON_SIZE);
|
||||
const int32_t margin = is_landscape_display ? std::min<int32_t>(available_width / 16, BUTTON_SIZE) : 0;
|
||||
|
||||
auto paths = app.getPaths();
|
||||
auto apps_icon_path = paths->getSystemPathLvgl("icon_apps.png");
|
||||
auto files_icon_path = paths->getSystemPathLvgl("icon_files.png");
|
||||
auto settings_icon_path = paths->getSystemPathLvgl("icon_settings.png");
|
||||
const auto paths = app.getPaths();
|
||||
const auto apps_icon_path = paths->getSystemPathLvgl("icon_apps.png");
|
||||
const auto files_icon_path = paths->getSystemPathLvgl("icon_files.png");
|
||||
const auto settings_icon_path = paths->getSystemPathLvgl("icon_settings.png");
|
||||
|
||||
const auto& apps_title = textResources[i18n::Text::APPS];
|
||||
const auto& files_title = textResources[i18n::Text::FILES];
|
||||
const auto& settings_title = textResources[i18n::Text::SETTINGS];
|
||||
createAppButton(buttons_wrapper, apps_icon_path.c_str(), "AppList", margin);
|
||||
createAppButton(buttons_wrapper, files_icon_path.c_str(), "Files", margin);
|
||||
createAppButton(buttons_wrapper, settings_icon_path.c_str(), "Settings", margin);
|
||||
|
||||
createAppButton(wrapper, apps_title.c_str(), apps_icon_path.c_str(), "AppList", 0);
|
||||
createAppButton(wrapper, files_title.c_str(), files_icon_path.c_str(), "Files", padding);
|
||||
createAppButton(wrapper, settings_title.c_str(), settings_icon_path.c_str(), "Settings", padding);
|
||||
if (shouldShowPowerButton()) {
|
||||
auto* power_button = lv_btn_create(parent);
|
||||
lv_obj_set_style_pad_all(power_button, 8, 0);
|
||||
lv_obj_align(power_button, LV_ALIGN_BOTTOM_MID, 0, -10);
|
||||
lv_obj_add_event_cb(power_button, onPowerOffPressed, LV_EVENT_SHORT_CLICKED, nullptr);
|
||||
lv_obj_set_style_shadow_width(power_button, 0, LV_STATE_DEFAULT);
|
||||
lv_obj_set_style_bg_opa(power_button, 0, LV_PART_MAIN);
|
||||
|
||||
auto* power_label = lv_label_create(power_button);
|
||||
lv_label_set_text(power_label, LV_SYMBOL_POWER);
|
||||
lv_obj_set_style_text_color(power_label, lv_theme_get_color_primary(parent), LV_STATE_DEFAULT);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ class ScreenshotApp final : public App {
|
||||
public:
|
||||
|
||||
ScreenshotApp();
|
||||
~ScreenshotApp();
|
||||
~ScreenshotApp() override;
|
||||
|
||||
void onShow(AppContext& app, lv_obj_t* parent) override;
|
||||
void onStartPressed();
|
||||
|
||||
@@ -88,15 +88,6 @@ std::vector<std::shared_ptr<Device>> findDevices(Device::Type type) {
|
||||
});
|
||||
}
|
||||
|
||||
void findDevices(Device::Type type, std::function<bool(const std::shared_ptr<Device>&)> onDeviceFound) {
|
||||
auto devices_view = findDevices(type);
|
||||
for (auto& device : devices_view) {
|
||||
if (!onDeviceFound(device)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<Device>> getDevices() {
|
||||
return devices;
|
||||
}
|
||||
|
||||
@@ -8,12 +8,35 @@
|
||||
|
||||
#include <Tactility/kernel/SystemEvents.h>
|
||||
|
||||
#define TAG "hal"
|
||||
|
||||
#define TT_SDCARD_MOUNT_POINT "/sdcard"
|
||||
|
||||
namespace tt::hal {
|
||||
|
||||
constexpr auto* TAG = "hal";
|
||||
|
||||
void registerDevices(const Configuration& configuration) {
|
||||
TT_LOG_I(TAG, "Registering devices");
|
||||
|
||||
if (configuration.sdcard != nullptr) {
|
||||
registerDevice(configuration.sdcard);
|
||||
}
|
||||
|
||||
if (configuration.power != nullptr) {
|
||||
std::shared_ptr<power::PowerDevice> power = configuration.power();
|
||||
registerDevice(power);
|
||||
}
|
||||
|
||||
if (configuration.createKeyboard) {
|
||||
auto keyboard = configuration.createKeyboard();
|
||||
if (keyboard != nullptr) {
|
||||
registerDevice(std::reinterpret_pointer_cast<Device>(keyboard));
|
||||
}
|
||||
}
|
||||
|
||||
auto devices = configuration.createDevices();
|
||||
for (auto& device : devices) {
|
||||
registerDevice(device);
|
||||
}
|
||||
}
|
||||
|
||||
void init(const Configuration& configuration) {
|
||||
kernel::publishSystemEvent(kernel::SystemEvent::BootInitHalBegin);
|
||||
|
||||
@@ -34,18 +57,7 @@ void init(const Configuration& configuration) {
|
||||
tt_check(configuration.initBoot(), "Init power failed");
|
||||
}
|
||||
|
||||
if (configuration.sdcard != nullptr) {
|
||||
TT_LOG_I(TAG, "Mounting sdcard");
|
||||
if (!configuration.sdcard->mount(TT_SDCARD_MOUNT_POINT)) {
|
||||
TT_LOG_W(TAG, "SD card mount failed (init can continue)");
|
||||
}
|
||||
registerDevice(configuration.sdcard);
|
||||
}
|
||||
|
||||
if (configuration.power != nullptr) {
|
||||
std::shared_ptr<power::PowerDevice> power = configuration.power();
|
||||
registerDevice(power);
|
||||
}
|
||||
registerDevices(configuration);
|
||||
|
||||
kernel::publishSystemEvent(kernel::SystemEvent::BootInitHalEnd);
|
||||
}
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
namespace tt::hal::i2c {
|
||||
|
||||
bool I2cDevice::read(uint8_t* data, size_t dataSize, TickType_t timeout) {
|
||||
return tt::hal::i2c::masterRead(port, address, data, dataSize, timeout);
|
||||
return masterRead(port, address, data, dataSize, timeout);
|
||||
}
|
||||
|
||||
bool I2cDevice::write(const uint8_t* data, uint16_t dataSize, TickType_t timeout) {
|
||||
return tt::hal::i2c::masterWrite(port, address, data, dataSize, timeout);
|
||||
return masterWrite(port, address, data, dataSize, timeout);
|
||||
}
|
||||
|
||||
bool I2cDevice::writeRead(const uint8_t* writeData, size_t writeDataSize, uint8_t* readData, size_t readDataSize, TickType_t timeout) {
|
||||
@@ -22,7 +22,7 @@ bool I2cDevice::writeRegister(uint8_t reg, const uint8_t* data, uint16_t dataSiz
|
||||
|
||||
bool I2cDevice::readRegister12(uint8_t reg, float& out) const {
|
||||
std::uint8_t data[2] = {0};
|
||||
if (tt::hal::i2c::masterReadRegister(port, address, reg, data, 2, DEFAULT_TIMEOUT)) {
|
||||
if (masterReadRegister(port, address, reg, data, 2, DEFAULT_TIMEOUT)) {
|
||||
out = (data[0] & 0x0F) << 8 | data[1];
|
||||
return true;
|
||||
} else {
|
||||
@@ -32,7 +32,7 @@ bool I2cDevice::readRegister12(uint8_t reg, float& out) const {
|
||||
|
||||
bool I2cDevice::readRegister14(uint8_t reg, float& out) const {
|
||||
std::uint8_t data[2] = {0};
|
||||
if (tt::hal::i2c::masterReadRegister(port, address, reg, data, 2, DEFAULT_TIMEOUT)) {
|
||||
if (masterReadRegister(port, address, reg, data, 2, DEFAULT_TIMEOUT)) {
|
||||
out = (data[0] & 0x3F) << 8 | data[1];
|
||||
return true;
|
||||
} else {
|
||||
@@ -42,7 +42,7 @@ bool I2cDevice::readRegister14(uint8_t reg, float& out) const {
|
||||
|
||||
bool I2cDevice::readRegister16(uint8_t reg, uint16_t& out) const {
|
||||
std::uint8_t data[2] = {0};
|
||||
if (tt::hal::i2c::masterReadRegister(port, address, reg, data, 2, DEFAULT_TIMEOUT)) {
|
||||
if (masterReadRegister(port, address, reg, data, 2, DEFAULT_TIMEOUT)) {
|
||||
out = data[0] << 8 | data[1];
|
||||
return true;
|
||||
} else {
|
||||
@@ -51,11 +51,11 @@ bool I2cDevice::readRegister16(uint8_t reg, uint16_t& out) const {
|
||||
}
|
||||
|
||||
bool I2cDevice::readRegister8(uint8_t reg, uint8_t& result) const {
|
||||
return tt::hal::i2c::masterWriteRead(port, address, ®, 1, &result, 1, DEFAULT_TIMEOUT);
|
||||
return masterWriteRead(port, address, ®, 1, &result, 1, DEFAULT_TIMEOUT);
|
||||
}
|
||||
|
||||
bool I2cDevice::writeRegister8(uint8_t reg, uint8_t value) const {
|
||||
return tt::hal::i2c::masterWriteRegister(port, address, reg, &value, 1, DEFAULT_TIMEOUT);
|
||||
return masterWriteRegister(port, address, reg, &value, 1, DEFAULT_TIMEOUT);
|
||||
}
|
||||
|
||||
bool I2cDevice::bitOn(uint8_t reg, uint8_t bitmask) const {
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
#include <Tactility/hal/sdcard/SdCardMounting.h>
|
||||
#include <Tactility/hal/sdcard/SdCardDevice.h>
|
||||
|
||||
namespace tt::hal::sdcard {
|
||||
|
||||
constexpr auto* TAG = "SdCardMounting";
|
||||
constexpr auto* TT_SDCARD_MOUNT_POINT = "/sdcard";
|
||||
|
||||
static void mount(const std::shared_ptr<SdCardDevice>& sdcard, const std::string& path) {
|
||||
sdcard->getLock()->withLock([&sdcard, &path] {
|
||||
TT_LOG_I(TAG, "Mounting sdcard at %s", path.c_str());
|
||||
if (!sdcard->mount(path)) {
|
||||
TT_LOG_W(TAG, "SD card mount failed for %s (init can continue)", path.c_str());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void mountAll() {
|
||||
auto sdcards = hal::findDevices<SdCardDevice>(Device::Type::SdCard);
|
||||
if (!sdcards.empty()) {
|
||||
if (sdcards.size() == 1) {
|
||||
// Fixed mount path name
|
||||
auto sdcard = sdcards[0];
|
||||
if (!sdcard->isMounted()) {
|
||||
mount(sdcard, TT_SDCARD_MOUNT_POINT);
|
||||
}
|
||||
} else {
|
||||
// Numbered mount path name
|
||||
for (int i = 0; i < sdcards.size(); i++) {
|
||||
auto sdcard = sdcards[i];
|
||||
if (!sdcard->isMounted()) {
|
||||
std::string mount_path = TT_SDCARD_MOUNT_POINT + std::to_string(i);
|
||||
mount(sdcard, mount_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,30 +21,29 @@ static Mode currentMode = Mode::Default;
|
||||
static RTC_NOINIT_ATTR BootMode bootMode;
|
||||
|
||||
sdmmc_card_t* _Nullable getCard() {
|
||||
auto sdcard = getConfiguration()->sdcard;
|
||||
if (sdcard == nullptr) {
|
||||
TT_LOG_W(TAG, "No SD card configuration found");
|
||||
auto sdcards = findDevices<sdcard::SpiSdCardDevice>(Device::Type::SdCard);
|
||||
|
||||
std::shared_ptr<sdcard::SpiSdCardDevice> usable_sdcard;
|
||||
for (auto& sdcard : sdcards) {
|
||||
auto sdcard_candidate = std::static_pointer_cast<sdcard::SpiSdCardDevice>(sdcard);
|
||||
if (sdcard_candidate != nullptr && sdcard_candidate->isMounted() && sdcard_candidate->getCard() != nullptr) {
|
||||
usable_sdcard = sdcard_candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (usable_sdcard == nullptr) {
|
||||
TT_LOG_W(TAG, "Couldn't find a mounted SpiSdCard");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!sdcard->isMounted()) {
|
||||
TT_LOG_W(TAG, "SD card not mounted");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto spi_sdcard = std::static_pointer_cast<sdcard::SpiSdCardDevice>(sdcard);
|
||||
if (spi_sdcard == nullptr) {
|
||||
TT_LOG_W(TAG, "SD card interface is not supported (must be SpiSdCard)");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto* card = spi_sdcard->getCard();
|
||||
if (card == nullptr) {
|
||||
auto* sdmmc_card = usable_sdcard->getCard();
|
||||
if (sdmmc_card == nullptr) {
|
||||
TT_LOG_W(TAG, "SD card has no card object available");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return card;
|
||||
return sdmmc_card;
|
||||
}
|
||||
|
||||
static bool canStartNewMode() {
|
||||
|
||||
@@ -1,43 +1,55 @@
|
||||
#include "Tactility/app/display/DisplaySettings.h"
|
||||
#include "Tactility/lvgl/Keyboard.h"
|
||||
#include "Tactility/lvgl/Lvgl.h"
|
||||
|
||||
#include "Tactility/hal/display/DisplayDevice.h"
|
||||
#include "Tactility/hal/touch/TouchDevice.h"
|
||||
#include <Tactility/app/display/DisplaySettings.h>
|
||||
#include <Tactility/hal/Configuration.h>
|
||||
#include <Tactility/hal/encoder/EncoderDevice.h>
|
||||
#include <Tactility/hal/display/DisplayDevice.h>
|
||||
#include <Tactility/hal/keyboard/KeyboardDevice.h>
|
||||
#include <Tactility/hal/touch/TouchDevice.h>
|
||||
#include <Tactility/lvgl/Keyboard.h>
|
||||
#include <Tactility/lvgl/Lvgl.h>
|
||||
#include <Tactility/lvgl/LvglSync.h>
|
||||
#include <Tactility/kernel/SystemEvents.h>
|
||||
#include <Tactility/service/ServiceRegistration.h>
|
||||
#include <Tactility/TactilityHeadless.h>
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
#include "Tactility/lvgl/EspLvglPort.h"
|
||||
#include <Tactility/lvgl/EspLvglPort.h>
|
||||
#endif
|
||||
|
||||
#include <lvgl.h>
|
||||
#include <Tactility/TactilityHeadless.h>
|
||||
#include <Tactility/lvgl/LvglSync.h>
|
||||
#include <Tactility/service/ServiceRegistration.h>
|
||||
|
||||
namespace tt::lvgl {
|
||||
|
||||
#define TAG "Lvgl"
|
||||
constexpr auto* TAG = "Lvgl";
|
||||
|
||||
static bool started = false;
|
||||
|
||||
static std::shared_ptr<hal::display::DisplayDevice> createDisplay(const hal::Configuration& config) {
|
||||
assert(config.createDisplay);
|
||||
auto display = config.createDisplay();
|
||||
assert(display != nullptr);
|
||||
|
||||
if (!display->start()) {
|
||||
TT_LOG_E(TAG, "Display start failed");
|
||||
return nullptr;
|
||||
// TODO: Move to hal init
|
||||
static void initDisplays(const hal::Configuration& config) {
|
||||
TT_LOG_I(TAG, "Init displays");
|
||||
if (config.createDisplay != nullptr) {
|
||||
auto display = config.createDisplay();
|
||||
if (display != nullptr) {
|
||||
hal::registerDevice(display);
|
||||
}
|
||||
}
|
||||
|
||||
if (display->supportsBacklightDuty()) {
|
||||
display->setBacklightDuty(0);
|
||||
}
|
||||
TT_LOG_I(TAG, "Start displays");
|
||||
auto displays = hal::findDevices<hal::display::DisplayDevice>(hal::Device::Type::Display);
|
||||
for (auto& display : displays) {
|
||||
if (!display->start()) {
|
||||
TT_LOG_E(TAG, "Display start failed");
|
||||
}
|
||||
|
||||
return display;
|
||||
if (display->supportsBacklightDuty()) {
|
||||
display->setBacklightDuty(0);
|
||||
}
|
||||
|
||||
auto touch = display->getTouchDevice();
|
||||
if (touch != nullptr) {
|
||||
hal::registerDevice(touch);
|
||||
touch->start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void init(const hal::Configuration& config) {
|
||||
@@ -49,25 +61,7 @@ void init(const hal::Configuration& config) {
|
||||
}
|
||||
#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);
|
||||
}
|
||||
}
|
||||
initDisplays(config);
|
||||
|
||||
start();
|
||||
|
||||
@@ -91,20 +85,27 @@ void start() {
|
||||
|
||||
// Start displays (their related touch devices start automatically within)
|
||||
|
||||
TT_LOG_I(TAG, "Start displays");
|
||||
auto displays = hal::findDevices<hal::display::DisplayDevice>(hal::Device::Type::Display);
|
||||
for (auto display : displays) {
|
||||
if (display->supportsLvgl() && display->startLvgl()) {
|
||||
auto lvgl_display = display->getLvglDisplay();
|
||||
assert(lvgl_display != nullptr);
|
||||
lv_display_rotation_t rotation = app::display::getRotation();
|
||||
if (rotation != lv_display_get_rotation(lvgl_display)) {
|
||||
lv_display_set_rotation(lvgl_display, rotation);
|
||||
if (display->supportsLvgl()) {
|
||||
if (display->startLvgl()) {
|
||||
TT_LOG_I(TAG, "Started %s", display->getName().c_str());
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
TT_LOG_E(TAG, "Start failed for %s", display->getName().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Start touch
|
||||
|
||||
TT_LOG_I(TAG, "Start touch devices");
|
||||
auto touch_devices = hal::findDevices<hal::touch::TouchDevice>(hal::Device::Type::Touch);
|
||||
for (auto touch_device : touch_devices) {
|
||||
if (displays.size() > 0) {
|
||||
@@ -112,13 +113,17 @@ void start() {
|
||||
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());
|
||||
if (touch_device->startLvgl(display->getLvglDisplay())) {
|
||||
TT_LOG_I(TAG, "Started %s", touch_device->getName().c_str());
|
||||
} else {
|
||||
TT_LOG_E(TAG, "Start failed for %s", touch_device->getName().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Start keyboards
|
||||
|
||||
TT_LOG_I(TAG, "Start keyboards");
|
||||
auto keyboards = hal::findDevices<hal::keyboard::KeyboardDevice>(hal::Device::Type::Keyboard);
|
||||
for (auto keyboard : keyboards) {
|
||||
if (displays.size() > 0) {
|
||||
@@ -128,14 +133,29 @@ void start() {
|
||||
if (keyboard->startLvgl(display->getLvglDisplay())) {
|
||||
lv_indev_t* keyboard_indev = keyboard->getLvglIndev();
|
||||
hardware_keyboard_set_indev(keyboard_indev);
|
||||
TT_LOG_I(TAG, "Keyboard started");
|
||||
TT_LOG_I(TAG, "Started %s", keyboard->getName().c_str());
|
||||
} else {
|
||||
TT_LOG_E(TAG, "Keyboard start failed");
|
||||
TT_LOG_E(TAG, "Start failed for %s", keyboard->getName().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Start encoders
|
||||
TT_LOG_I(TAG, "Start encoders");
|
||||
auto encoders = hal::findDevices<hal::encoder::EncoderDevice>(hal::Device::Type::Encoder);
|
||||
for (auto encoder : encoders) {
|
||||
if (displays.size() > 0) {
|
||||
// TODO: Consider implementing support for multiple displays
|
||||
auto display = displays[0];
|
||||
if (encoder->startLvgl(display->getLvglDisplay())) {
|
||||
TT_LOG_I(TAG, "Started %s", encoder->getName().c_str());
|
||||
} else {
|
||||
TT_LOG_E(TAG, "Start failed for %s", encoder->getName().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Restart services
|
||||
|
||||
if (service::getState("Gui") == service::State::Stopped) {
|
||||
@@ -158,7 +178,7 @@ void start() {
|
||||
}
|
||||
|
||||
void stop() {
|
||||
TT_LOG_I(TAG, "Stop LVGL");
|
||||
TT_LOG_I(TAG, "Stopping LVGL");
|
||||
|
||||
if (!started) {
|
||||
TT_LOG_W(TAG, "Can't stop LVGL: not started");
|
||||
@@ -175,6 +195,7 @@ void stop() {
|
||||
|
||||
// Stop keyboards
|
||||
|
||||
TT_LOG_I(TAG, "Stopping keyboards");
|
||||
auto keyboards = hal::findDevices<hal::keyboard::KeyboardDevice>(hal::Device::Type::Keyboard);
|
||||
for (auto keyboard : keyboards) {
|
||||
if (keyboard->getLvglIndev() != nullptr) {
|
||||
@@ -184,6 +205,7 @@ void stop() {
|
||||
|
||||
// Stop touch
|
||||
|
||||
TT_LOG_I(TAG, "Stopping touch");
|
||||
// The display generally stops their own touch devices, but we'll clean up anything that didn't
|
||||
auto touch_devices = hal::findDevices<hal::touch::TouchDevice>(hal::Device::Type::Touch);
|
||||
for (auto touch_device : touch_devices) {
|
||||
@@ -192,8 +214,19 @@ void stop() {
|
||||
}
|
||||
}
|
||||
|
||||
// Stop encoders
|
||||
|
||||
TT_LOG_I(TAG, "Stopping encoders");
|
||||
// The display generally stops their own touch devices, but we'll clean up anything that didn't
|
||||
auto encoder_devices = hal::findDevices<hal::encoder::EncoderDevice>(hal::Device::Type::Encoder);
|
||||
for (auto encoder_device : encoder_devices) {
|
||||
if (encoder_device->getLvglIndev() != nullptr) {
|
||||
encoder_device->stopLvgl();
|
||||
}
|
||||
}
|
||||
// Stop displays (and their touch devices)
|
||||
|
||||
TT_LOG_I(TAG, "Stopping displays");
|
||||
auto displays = hal::findDevices<hal::display::DisplayDevice>(hal::Device::Type::Display);
|
||||
for (auto display : displays) {
|
||||
if (display->supportsLvgl() && display->getLvglDisplay() != nullptr && !display->stopLvgl()) {
|
||||
@@ -204,6 +237,8 @@ void stop() {
|
||||
started = false;
|
||||
|
||||
kernel::publishSystemEvent(kernel::SystemEvent::LvglStopped);
|
||||
|
||||
TT_LOG_I(TAG, "Stopped LVGL");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -111,7 +111,7 @@ static const lv_obj_class_t statusbar_class = {
|
||||
static void statusbar_pubsub_event(TT_UNUSED const void* message, void* obj) {
|
||||
TT_LOG_D(TAG, "event");
|
||||
auto* statusbar = static_cast<Statusbar*>(obj);
|
||||
if (lock(kernel::millisToTicks(100))) {
|
||||
if (lock(portMAX_DELAY)) {
|
||||
update_main(statusbar);
|
||||
lv_obj_invalidate(&statusbar->obj);
|
||||
unlock();
|
||||
@@ -119,7 +119,7 @@ static void statusbar_pubsub_event(TT_UNUSED const void* message, void* obj) {
|
||||
}
|
||||
|
||||
static void onTimeChanged(TT_UNUSED kernel::SystemEvent event) {
|
||||
if (statusbar_data.mutex.lock(100 / portTICK_PERIOD_MS)) {
|
||||
if (statusbar_data.mutex.lock()) {
|
||||
statusbar_data.time_update_timer->stop();
|
||||
statusbar_data.time_update_timer->start(5);
|
||||
|
||||
@@ -136,7 +136,7 @@ static void statusbar_constructor(const lv_obj_class_t* class_p, lv_obj_t* obj)
|
||||
statusbar->pubsub_subscription = statusbar_data.pubsub->subscribe(&statusbar_pubsub_event, statusbar);
|
||||
|
||||
if (!statusbar_data.time_update_timer->isRunning()) {
|
||||
statusbar_data.time_update_timer->start(50 / portTICK_PERIOD_MS);
|
||||
statusbar_data.time_update_timer->start(200 / portTICK_PERIOD_MS);
|
||||
statusbar_data.systemEventSubscription = kernel::subscribeSystemEvent(
|
||||
kernel::SystemEvent::Time,
|
||||
onTimeChanged
|
||||
@@ -210,7 +210,7 @@ static void update_time(Statusbar* statusbar) {
|
||||
static void update_main(Statusbar* statusbar) {
|
||||
update_time(statusbar);
|
||||
|
||||
if (statusbar_lock(50 / portTICK_PERIOD_MS)) {
|
||||
if (statusbar_lock(200 / portTICK_PERIOD_MS)) {
|
||||
for (int i = 0; i < STATUSBAR_ICON_LIMIT; ++i) {
|
||||
update_icon(statusbar->icons[i], &(statusbar_data.icons[i]));
|
||||
}
|
||||
@@ -270,7 +270,7 @@ void statusbar_icon_remove(int8_t id) {
|
||||
void statusbar_icon_set_image(int8_t id, const std::string& image) {
|
||||
TT_LOG_D(TAG, "id %d: set image %s", id, image.empty() ? "(none)" : image.c_str());
|
||||
tt_check(id >= 0 && id < STATUSBAR_ICON_LIMIT);
|
||||
if (statusbar_lock(50 / portTICK_PERIOD_MS)) {
|
||||
if (statusbar_lock()) {
|
||||
StatusbarIcon* icon = &statusbar_data.icons[id];
|
||||
tt_check(icon->claimed);
|
||||
icon->image = image;
|
||||
@@ -282,7 +282,7 @@ void statusbar_icon_set_image(int8_t id, const std::string& image) {
|
||||
void statusbar_icon_set_visibility(int8_t id, bool visible) {
|
||||
TT_LOG_D(TAG, "id %d: set visibility %d", id, visible);
|
||||
tt_check(id >= 0 && id < STATUSBAR_ICON_LIMIT);
|
||||
if (statusbar_lock(50 / portTICK_PERIOD_MS)) {
|
||||
if (statusbar_lock()) {
|
||||
StatusbarIcon* icon = &statusbar_data.icons[id];
|
||||
tt_check(icon->claimed);
|
||||
icon->visible = visible;
|
||||
|
||||
@@ -26,8 +26,11 @@ class SdCardService final : public Service {
|
||||
}
|
||||
|
||||
void update() {
|
||||
auto sdcard = hal::getConfiguration()->sdcard;
|
||||
assert(sdcard);
|
||||
// TODO: Support multiple SD cards
|
||||
auto sdcard = hal::findFirstDevice<hal::sdcard::SdCardDevice>(hal::Device::Type::SdCard);
|
||||
if (sdcard == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (lock(50)) {
|
||||
auto new_state = sdcard->getState();
|
||||
@@ -50,16 +53,12 @@ class SdCardService final : public Service {
|
||||
public:
|
||||
|
||||
void onStart(ServiceContext& serviceContext) override {
|
||||
if (hal::getConfiguration()->sdcard != nullptr) {
|
||||
auto service = findServiceById<SdCardService>(manifest.id);
|
||||
updateTimer = std::make_unique<Timer>(Timer::Type::Periodic, [service]() {
|
||||
service->update();
|
||||
});
|
||||
// We want to try and scan more often in case of startup or scan lock failure
|
||||
updateTimer->start(1000);
|
||||
} else {
|
||||
TT_LOG_I(TAG, "Timer not started: no SD card config");
|
||||
}
|
||||
auto service = findServiceById<SdCardService>(manifest.id);
|
||||
updateTimer = std::make_unique<Timer>(Timer::Type::Periodic, [service]() {
|
||||
service->update();
|
||||
});
|
||||
// We want to try and scan more often in case of startup or scan lock failure
|
||||
updateTimer->start(1000);
|
||||
}
|
||||
|
||||
void onStop(ServiceContext& serviceContext) override {
|
||||
|
||||
@@ -89,13 +89,20 @@ static const char* getSdCardStatusIcon(hal::sdcard::SdCardDevice::State state) {
|
||||
}
|
||||
|
||||
static _Nullable const char* getPowerStatusIcon() {
|
||||
auto get_power = getConfiguration()->hardware->power;
|
||||
if (get_power == nullptr) {
|
||||
// TODO: Support multiple power devices?
|
||||
std::shared_ptr<hal::power::PowerDevice> power;
|
||||
hal::findDevices<hal::power::PowerDevice>(hal::Device::Type::Power, [&power](const auto& device) {
|
||||
if (device->supportsMetric(hal::power::PowerDevice::MetricType::ChargeLevel)) {
|
||||
power = device;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if (power == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto power = get_power();
|
||||
|
||||
hal::power::PowerDevice::MetricData charge_level;
|
||||
if (!power->getMetric(hal::power::PowerDevice::MetricType::ChargeLevel, charge_level)) {
|
||||
return nullptr;
|
||||
@@ -197,7 +204,9 @@ class StatusbarService final : public Service {
|
||||
}
|
||||
|
||||
void updateSdCardIcon() {
|
||||
auto sdcard = hal::getConfiguration()->sdcard;
|
||||
auto sdcards = hal::findDevices<hal::sdcard::SdCardDevice>(hal::Device::Type::SdCard);
|
||||
// TODO: Support multiple SD cards
|
||||
auto sdcard = sdcards.empty() ? nullptr : sdcards[0];
|
||||
if (sdcard != nullptr) {
|
||||
auto state = sdcard->getState(50 / portTICK_PERIOD_MS);
|
||||
if (state != hal::sdcard::SdCardDevice::State::Timeout) {
|
||||
|
||||
Reference in New Issue
Block a user