#ifdef ESP_PLATFORM #include "Tactility/app/ElfApp.h" #include "Tactility/file/File.h" #include "Tactility/service/loader/Loader.h" #include "Tactility/hal/sdcard/SdCardDevice.h" #include #include #include "esp_elf.h" #include #include namespace tt::app { #define TAG "elf_app" struct ElfManifest { /** The user-readable name of the app. Used in UI. */ std::string name; /** Optional icon. */ std::string icon; CreateData _Nullable createData = nullptr; DestroyData _Nullable destroyData = nullptr; OnCreate _Nullable onCreate = nullptr; OnDestroy _Nullable onDestroy = nullptr; OnShow _Nullable onShow = nullptr; OnHide _Nullable onHide = nullptr; OnResult _Nullable onResult = nullptr; }; static size_t elfManifestSetCount = 0; static ElfManifest elfManifest; class ElfApp : public App { const std::string filePath; std::unique_ptr elfFileData; esp_elf_t elf; bool shouldCleanupElf = false; // Whether we have to clean up the above "elf" object std::unique_ptr manifest; void* data = nullptr; bool startElf() { TT_LOG_I(TAG, "Starting ELF %s", filePath.c_str()); assert(elfFileData == nullptr); size_t size = 0; hal::sdcard::withSdCardLock(filePath, [this, &size](){ elfFileData = file::readBinary(filePath, size); }); if (elfFileData == nullptr) { return false; } if (esp_elf_init(&elf) < 0) { TT_LOG_E(TAG, "Failed to initialize"); shouldCleanupElf = true; return false; } if (esp_elf_relocate(&elf, elfFileData.get()) < 0) { TT_LOG_E(TAG, "Failed to load executable"); return false; } int argc = 0; char* argv[] = {}; if (esp_elf_request(&elf, 0, argc, argv) < 0) { TT_LOG_W(TAG, "Executable returned error code"); return false; } return true; } void stopElf() { TT_LOG_I(TAG, "Cleaning up ELF"); if (shouldCleanupElf) { esp_elf_deinit(&elf); } if (elfFileData != nullptr) { elfFileData = nullptr; } } public: explicit ElfApp(std::string filePath) : filePath(std::move(filePath)) {} void onCreate(AppContext& appContext) override { auto initial_count = elfManifestSetCount; if (startElf()) { if (elfManifestSetCount > initial_count) { manifest = std::make_unique(elfManifest); if (manifest->createData != nullptr) { data = manifest->createData(); } if (manifest->onCreate != nullptr) { manifest->onCreate(&appContext, data); } } } else { service::loader::stopApp(); } } void onDestroy(AppContext& appContext) override { TT_LOG_I(TAG, "Cleaning up app"); if (manifest != nullptr) { if (manifest->onDestroy != nullptr) { manifest->onDestroy(&appContext, data); } if (manifest->destroyData != nullptr && data != nullptr) { manifest->destroyData(data); } this->manifest = nullptr; } stopElf(); } void onShow(AppContext& appContext, lv_obj_t* parent) override { if (manifest != nullptr && manifest->onShow != nullptr) { manifest->onShow(&appContext, data, parent); } } void onHide(AppContext& appContext) override { if (manifest != nullptr && manifest->onHide != nullptr) { manifest->onHide(&appContext, data); } } void onResult(AppContext& appContext, LaunchId launchId, Result result, std::unique_ptr resultBundle) override { if (manifest != nullptr && manifest->onResult != nullptr) { manifest->onResult(&appContext, data, launchId, result, resultBundle.get()); } } }; void setElfAppManifest( const char* name, const char* _Nullable icon, CreateData _Nullable createData, DestroyData _Nullable destroyData, OnCreate _Nullable onCreate, OnDestroy _Nullable onDestroy, OnShow _Nullable onShow, OnHide _Nullable onHide, OnResult _Nullable onResult ) { elfManifest = ElfManifest { .name = name ? name : "", .icon = icon ? icon : "", .createData = createData, .destroyData = destroyData, .onCreate = onCreate, .onDestroy = onDestroy, .onShow = onShow, .onHide = onHide, .onResult = onResult }; elfManifestSetCount++; } std::string getElfAppId(const std::string& filePath) { return filePath; } bool registerElfApp(const std::string& filePath) { if (findAppById(filePath) == nullptr) { auto manifest = AppManifest { .id = getElfAppId(filePath), .name = tt::string::removeFileExtension(tt::string::getLastPathSegment(filePath)), .type = Type::User, .location = Location::external(filePath) }; addApp(manifest); } return false; } std::shared_ptr createElfApp(const std::shared_ptr& manifest) { TT_LOG_I(TAG, "createElfApp"); assert(manifest != nullptr); assert(manifest->location.isExternal()); return std::make_shared(manifest->location.getPath()); } } // namespace #endif // ESP_PLATFORM