Files
tactility/Tactility/Source/Tactility.cpp
T
Shadowtrance a4dc633063 Feature additions (#434)
Lots of things "ported" over from the "enhanced" fork. With some adjustments here and there.

KeyboardBacklight driver (for T-Deck only currently)
Trackball driver (for T-Deck only currently)
Keyboard backlight sleep/wake (for T-Deck only currently...also requires keyboard firmware update)
Display sleep/wake
Files - create file/folder
Keyboard settings (for T-Deck only currently)
Time & Date settings tweaks
Locale settings tweaks
Systeminfo additions
Espnow wifi coexist

initI2cDevices - moved to T-deck init.cpp / initBoot
KeyboardInitService - removed,  moved to T-deck init.cpp / initBoot
Adjusted TIMER_UPDATE_INTERVAL to 2 seconds.
Added lock to ActionCreateFolder

Maybe missed some things in the list.

Display wake could do with some kind of block on wake first touch to prevent UI elements being hit when waking device with touch. Same with encoder/trackball/keyboard press i guess.

The original code was written by @cscott0108 at https://github.com/cscott0108/tactility-enhanced-t-deck
2026-01-02 12:14:55 +01:00

350 lines
12 KiB
C++

#ifdef ESP_PLATFORM
#include <sdkconfig.h>
#endif
#include <Tactility/Tactility.h>
#include <Tactility/TactilityConfig.h>
#include <Tactility/app/AppManifestParsing.h>
#include <Tactility/app/AppRegistration.h>
#include <Tactility/DispatcherThread.h>
#include <Tactility/file/File.h>
#include <Tactility/file/FileLock.h>
#include <Tactility/file/PropertiesFile.h>
#include <Tactility/hal/HalPrivate.h>
#include <Tactility/lvgl/LvglPrivate.h>
#include <Tactility/MountPoints.h>
#include <Tactility/network/NtpPrivate.h>
#include <Tactility/service/ServiceManifest.h>
#include <Tactility/service/ServiceRegistration.h>
#include <Tactility/service/loader/Loader.h>
#include <Tactility/settings/TimePrivate.h>
#include <map>
#include <format>
#ifdef ESP_PLATFORM
#include <Tactility/InitEsp.h>
#endif
namespace tt {
constexpr auto* TAG = "Tactility";
static const Configuration* config_instance = nullptr;
static Dispatcher mainDispatcher;
// region Default services
namespace service {
// Primary
namespace gps { extern const ServiceManifest manifest; }
namespace wifi { extern const ServiceManifest manifest; }
namespace sdcard { extern const ServiceManifest manifest; }
#ifdef ESP_PLATFORM
namespace development { extern const ServiceManifest manifest; }
#endif
#ifdef CONFIG_ESP_WIFI_ENABLED
namespace espnow { extern const ServiceManifest manifest; }
#endif
// Secondary (UI)
namespace gui { extern const ServiceManifest manifest; }
namespace loader { extern const ServiceManifest manifest; }
namespace memorychecker { extern const ServiceManifest manifest; }
namespace statusbar { extern const ServiceManifest manifest; }
namespace displayidle { extern const ServiceManifest manifest; }
#if defined(ESP_PLATFORM) && defined(CONFIG_TT_DEVICE_LILYGO_TDECK)
namespace keyboardidle { extern const ServiceManifest manifest; }
#endif
#if TT_FEATURE_SCREENSHOT_ENABLED
namespace screenshot { extern const ServiceManifest manifest; }
#endif
}
// endregion
// region Default apps
namespace app {
namespace addgps { extern const AppManifest manifest; }
namespace apphub { extern const AppManifest manifest; }
namespace apphubdetails { extern const AppManifest manifest; }
namespace alertdialog { extern const AppManifest manifest; }
namespace appdetails { extern const AppManifest manifest; }
namespace applist { extern const AppManifest manifest; }
namespace appsettings { extern const AppManifest manifest; }
namespace boot { extern const AppManifest manifest; }
#ifdef CONFIG_ESP_WIFI_ENABLED
namespace chat { extern const AppManifest manifest; }
#endif
namespace development { extern const AppManifest manifest; }
namespace display { extern const AppManifest manifest; }
namespace files { extern const AppManifest manifest; }
namespace fileselection { extern const AppManifest manifest; }
namespace gpssettings { extern const AppManifest manifest; }
namespace i2cscanner { extern const AppManifest manifest; }
namespace i2csettings { extern const AppManifest manifest; }
namespace imageviewer { extern const AppManifest manifest; }
namespace inputdialog { extern const AppManifest manifest; }
namespace launcher { extern const AppManifest manifest; }
#if defined(ESP_PLATFORM) && defined(CONFIG_TT_DEVICE_LILYGO_TDECK)
namespace keyboardsettings { extern const AppManifest manifest; }
#endif
namespace localesettings { extern const AppManifest manifest; }
namespace notes { extern const AppManifest manifest; }
namespace power { extern const AppManifest manifest; }
namespace selectiondialog { extern const AppManifest manifest; }
namespace settings { extern const AppManifest manifest; }
namespace systeminfo { extern const AppManifest manifest; }
namespace timedatesettings { extern const AppManifest manifest; }
namespace timezone { extern const AppManifest manifest; }
namespace usbsettings { extern const AppManifest manifest; }
namespace wifiapsettings { extern const AppManifest manifest; }
namespace wificonnect { extern const AppManifest manifest; }
namespace wifimanage { extern const AppManifest manifest; }
#if TT_FEATURE_SCREENSHOT_ENABLED
namespace screenshot { extern const AppManifest manifest; }
#endif
#ifdef ESP_PLATFORM
namespace crashdiagnostics { extern const AppManifest manifest; }
#endif
}
#ifndef ESP_PLATFORM
#endif
// endregion
// List of all apps excluding Boot app (as Boot app calls this function indirectly)
static void registerInternalApps() {
TT_LOG_I(TAG, "Registering internal apps");
addAppManifest(app::alertdialog::manifest);
addAppManifest(app::appdetails::manifest);
addAppManifest(app::apphub::manifest);
addAppManifest(app::apphubdetails::manifest);
addAppManifest(app::applist::manifest);
addAppManifest(app::appsettings::manifest);
addAppManifest(app::display::manifest);
addAppManifest(app::files::manifest);
addAppManifest(app::fileselection::manifest);
addAppManifest(app::imageviewer::manifest);
addAppManifest(app::inputdialog::manifest);
addAppManifest(app::launcher::manifest);
#if defined(ESP_PLATFORM) && defined(CONFIG_TT_DEVICE_LILYGO_TDECK)
addAppManifest(app::keyboardsettings::manifest);
#endif
addAppManifest(app::localesettings::manifest);
addAppManifest(app::notes::manifest);
addAppManifest(app::settings::manifest);
addAppManifest(app::selectiondialog::manifest);
addAppManifest(app::systeminfo::manifest);
addAppManifest(app::timedatesettings::manifest);
addAppManifest(app::timezone::manifest);
addAppManifest(app::wifiapsettings::manifest);
addAppManifest(app::wificonnect::manifest);
addAppManifest(app::wifimanage::manifest);
#if defined(CONFIG_TINYUSB_MSC_ENABLED) && CONFIG_TINYUSB_MSC_ENABLED
addAppManifest(app::usbsettings::manifest);
#endif
#if TT_FEATURE_SCREENSHOT_ENABLED
addAppManifest(app::screenshot::manifest);
#endif
#ifdef CONFIG_ESP_WIFI_ENABLED
addAppManifest(app::chat::manifest);
#endif
#ifdef ESP_PLATFORM
addAppManifest(app::crashdiagnostics::manifest);
addAppManifest(app::development::manifest);
#endif
if (!hal::getConfiguration()->i2c.empty()) {
addAppManifest(app::i2cscanner::manifest);
addAppManifest(app::i2csettings::manifest);
}
if (!hal::getConfiguration()->uart.empty()) {
addAppManifest(app::addgps::manifest);
addAppManifest(app::gpssettings::manifest);
}
if (hal::hasDevice(hal::Device::Type::Power)) {
addAppManifest(app::power::manifest);
}
}
static void registerInstalledApp(std::string path) {
TT_LOG_I(TAG, "Registering app at %s", path.c_str());
std::string manifest_path = path + "/manifest.properties";
if (!file::isFile(manifest_path)) {
TT_LOG_E(TAG, "Manifest not found at %s", manifest_path.c_str());
return;
}
std::map<std::string, std::string> properties;
if (!file::loadPropertiesFile(manifest_path, properties)) {
TT_LOG_E(TAG, "Failed to load manifest at %s", manifest_path.c_str());
return;
}
app::AppManifest manifest;
if (!app::parseManifest(properties, manifest)) {
TT_LOG_E(TAG, "Failed to parse manifest at %s", manifest_path.c_str());
return;
}
manifest.appCategory = app::Category::User;
manifest.appLocation = app::Location::external(path);
app::addAppManifest(manifest);
}
static void registerInstalledApps(const std::string& path) {
TT_LOG_I(TAG, "Registering apps from %s", path.c_str());
file::listDirectory(path, [&path](const auto& entry) {
auto absolute_path = std::format("{}/{}", path, entry.d_name);
if (file::isDirectory(absolute_path)) {
registerInstalledApp(absolute_path);
}
});
}
static void registerInstalledAppsFromSdCard(const std::shared_ptr<hal::sdcard::SdCardDevice>& sdcard) {
auto sdcard_root_path = sdcard->getMountPath();
auto app_path = std::format("{}/app", sdcard_root_path);
if (file::isDirectory(app_path)) {
registerInstalledApps(app_path);
}
}
static void registerInstalledAppsFromSdCards() {
auto sdcard_devices = hal::findDevices<hal::sdcard::SdCardDevice>(hal::Device::Type::SdCard);
for (const auto& sdcard : sdcard_devices) {
if (sdcard->isMounted()) {
TT_LOG_I(TAG, "Registering apps from %s", sdcard->getMountPath().c_str());
registerInstalledAppsFromSdCard(sdcard);
}
}
}
static void registerAndStartSecondaryServices() {
TT_LOG_I(TAG, "Registering and starting system services");
addService(service::loader::manifest);
addService(service::gui::manifest);
addService(service::statusbar::manifest);
addService(service::displayidle::manifest);
#if defined(ESP_PLATFORM) && defined(CONFIG_TT_DEVICE_LILYGO_TDECK)
addService(service::keyboardidle::manifest);
#endif
addService(service::memorychecker::manifest);
#if TT_FEATURE_SCREENSHOT_ENABLED
addService(service::screenshot::manifest);
#endif
}
static void registerAndStartPrimaryServices() {
TT_LOG_I(TAG, "Registering and starting system services");
addService(service::gps::manifest);
if (hal::hasDevice(hal::Device::Type::SdCard)) {
addService(service::sdcard::manifest);
}
addService(service::wifi::manifest);
#ifdef ESP_PLATFORM
addService(service::development::manifest);
#endif
#ifdef CONFIG_ESP_WIFI_ENABLED
addService(service::espnow::manifest);
#endif
}
void createTempDirectory(const std::string& rootPath) {
auto temp_path = std::format("{}/tmp", rootPath);
if (!file::isDirectory(temp_path)) {
auto lock = file::getLock(rootPath)->asScopedLock();
if (lock.lock(1000 / portTICK_PERIOD_MS)) {
if (mkdir(temp_path.c_str(), 0777) == 0) {
TT_LOG_I(TAG, "Created %s", temp_path.c_str());
} else {
TT_LOG_E(TAG, "Failed to create %s", temp_path.c_str());
}
} else {
TT_LOG_E(TAG, LOG_MESSAGE_MUTEX_LOCK_FAILED_FMT, rootPath.c_str());
}
} else {
TT_LOG_I(TAG, "Found existing %s", temp_path.c_str());
}
}
void prepareFileSystems() {
// Temporary directories for SD cards
auto sdcard_devices = hal::findDevices<hal::sdcard::SdCardDevice>(hal::Device::Type::SdCard);
for (const auto& sdcard : sdcard_devices) {
if (sdcard->isMounted()) {
createTempDirectory(sdcard->getMountPath());
}
}
// Temporary directory for /data
if (file::isDirectory(file::MOUNT_POINT_DATA)) {
createTempDirectory(file::MOUNT_POINT_DATA);
}
}
void registerApps() {
registerInternalApps();
auto data_apps_path = std::format("{}/app", file::MOUNT_POINT_DATA);
if (file::isDirectory(data_apps_path)) {
registerInstalledApps(data_apps_path);
}
registerInstalledAppsFromSdCards();
}
void run(const Configuration& config) {
TT_LOG_I(TAG, "Tactility v%s on %s (%s)", TT_VERSION, CONFIG_TT_DEVICE_NAME, CONFIG_TT_DEVICE_ID);
assert(config.hardware);
const hal::Configuration& hardware = *config.hardware;
// Assign early so starting services can use it
config_instance = &config;
#ifdef ESP_PLATFORM
initEsp();
#endif
file::setFindLockFunction(file::findLock);
settings::initTimeZone();
hal::init(*config.hardware);
network::ntp::init();
registerAndStartPrimaryServices();
lvgl::init(hardware);
registerAndStartSecondaryServices();
TT_LOG_I(TAG, "Core systems ready");
TT_LOG_I(TAG, "Starting boot app");
// The boot app takes care of registering system apps, user services and user apps
addAppManifest(app::boot::manifest);
app::start(app::boot::manifest.appId);
TT_LOG_I(TAG, "Main dispatcher ready");
while (true) {
mainDispatcher.consume();
}
}
const Configuration* _Nullable getConfiguration() {
return config_instance;
}
Dispatcher& getMainDispatcher() {
return mainDispatcher;
}
} // namespace