diff --git a/Documentation/ideas.md b/Documentation/ideas.md index 1c002d81..3e13cfd7 100644 --- a/Documentation/ideas.md +++ b/Documentation/ideas.md @@ -34,6 +34,7 @@ ## Lower Priority +- Rename `Lock::lock()` and `Lock::unlock()` to `Lock::acquire()` and `Lock::release()`? - elf_loader: make main() entry-point optional (so we can build libraries, or have the `manifest` as a global symbol) - Implement system suspend that turns off the screen - The boot button on some devices can be used as GPIO_NUM_0 at runtime diff --git a/Tactility/Include/Tactility/app/AppPaths.h b/Tactility/Include/Tactility/app/AppPaths.h index 621dd58a..90150088 100644 --- a/Tactility/Include/Tactility/app/AppPaths.h +++ b/Tactility/Include/Tactility/app/AppPaths.h @@ -34,7 +34,7 @@ public: * The path will not end with a "/". * This is mainly used for core apps (system/boot/settings type). */ - std::string getAssetsDirectory() const; + std::string getAssetsPath() const; /** * You should not store configuration data here. diff --git a/Tactility/Source/app/AppPaths.cpp b/Tactility/Source/app/AppPaths.cpp index 79b761f8..13ff32a2 100644 --- a/Tactility/Source/app/AppPaths.cpp +++ b/Tactility/Source/app/AppPaths.cpp @@ -28,7 +28,7 @@ std::string AppPaths::getUserDataPath(const std::string& childPath) const { } -std::string AppPaths::getAssetsDirectory() const { +std::string AppPaths::getAssetsPath() const { if (manifest.appLocation.isInternal()) { return std::format("{}{}/app/{}/assets", PARTITION_PREFIX, file::SYSTEM_PARTITION_NAME, manifest.appId); } else { @@ -38,7 +38,7 @@ std::string AppPaths::getAssetsDirectory() const { std::string AppPaths::getAssetsPath(const std::string& childPath) const { assert(!childPath.starts_with('/')); - return std::format("{}/{}", getAssetsDirectory(), childPath); + return std::format("{}/{}", getAssetsPath(), childPath); } } diff --git a/TactilityC/Include/tt_app.h b/TactilityC/Include/tt_app.h index 6c0c840d..e7b8ded4 100644 --- a/TactilityC/Include/tt_app.h +++ b/TactilityC/Include/tt_app.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "tt_app_manifest.h" #ifdef __cplusplus @@ -7,7 +9,6 @@ extern "C" { #endif typedef void* AppHandle; -typedef void* AppPathsHandle; /** @return the bundle that belongs to this application, or null if it wasn't started with parameters. */ BundleHandle _Nullable tt_app_get_parameters(AppHandle handle); @@ -24,12 +25,39 @@ void tt_app_set_result(AppHandle handle, AppResult result, BundleHandle _Nullabl /** @return true if a result was set for this app context */ bool tt_app_has_result(AppHandle handle); -/** Get the path to the data directory of this app. +/** Get the path to the user data directory for this app. + * The app can store user-specific (mutable) data in there such as app settings. * @param[in] handle the app handle * @param[out] buffer the output buffer (recommended size is 256 bytes) * @param[inout] size used as input for maximum buffer size (including null terminator) and is set with the path string length by this function */ -void tt_app_get_data_directory(AppPathsHandle handle, char* buffer, size_t* size); +void tt_app_get_user_data_path(AppHandle handle, char* buffer, size_t* size); + +/** Resolve a child path in the user directory of this app. + * The app can store user-specific (mutable) data in there such as app settings. + * @param[in] handle the app handle + * @param[in] childPath the child path to resolve + * @param[out] buffer the output buffer (recommended size is 256 bytes) + * @param[inout] size used as input for maximum buffer size (including null terminator) and is set with the path string length by this function + */ +void tt_app_get_user_data_child_path(AppHandle handle, const char* childPath, char* buffer, size_t* size); + +/** Get the path to the assets directory of this app. + * The content in this path should be treated as read-only. + * @param[in] handle the app handle + * @param[out] buffer the output buffer (recommended size is 256 bytes) + * @param[inout] size used as input for maximum buffer size (including null terminator) and is set with the path string length by this function + */ +void tt_app_get_assets_path(AppHandle handle, char* buffer, size_t* size); + +/** Resolve a child path in the assets directory of this app. + * The content in this path should be treated as read-only. + * @param[in] handle the app handle + * @param[in] childPath the child path to resolve + * @param[out] buffer the output buffer (recommended size is 256 bytes) + * @param[inout] size used as input for maximum buffer size (including null terminator) and is set with the path string length by this function + */ +void tt_app_get_assets_child_path(AppHandle handle, const char* childPath, char* buffer, size_t* size); /** * Start an app by id. diff --git a/TactilityC/Include/tt_file.h b/TactilityC/Include/tt_file.h new file mode 100644 index 00000000..77589bf0 --- /dev/null +++ b/TactilityC/Include/tt_file.h @@ -0,0 +1,13 @@ +#pragma once + +#include "tt_lock.h" + +#ifdef __cplusplus +extern "C" { +#endif + +LockHandle tt_lock_alloc_for_file(const char* path); + +#ifdef __cplusplus +} +#endif diff --git a/TactilityC/Include/tt_lock.h b/TactilityC/Include/tt_lock.h new file mode 100644 index 00000000..db88bb4e --- /dev/null +++ b/TactilityC/Include/tt_lock.h @@ -0,0 +1,34 @@ +#pragma once + +#include "tt_kernel.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** A handle that represents a lock instance. A lock could be a Mutex or similar construct */ +typedef void* LockHandle; + +/** + * Attempt to lock the lock. + * @param[in] handle the handle that represents the mutex instance + * @param[in] timeout the maximum amount of ticks to wait when trying to lock + * @return true when the lock was acquired + */ +bool tt_lock_acquire(LockHandle handle, TickType timeout); + +/** + * Attempt to unlock the lock. + * @param[in] handle the handle that represents the mutex instance + * @return true when the lock was unlocked + */ +bool tt_lock_release(LockHandle handle); + +/** Free the memory for this lock + * @param[in] handle the handle that represents the mutex instance + */ +void tt_lock_free(LockHandle handle); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/TactilityC/Include/tt_mutex.h b/TactilityC/Include/tt_mutex.h index f3ba3ddf..d316c875 100644 --- a/TactilityC/Include/tt_mutex.h +++ b/TactilityC/Include/tt_mutex.h @@ -1,6 +1,6 @@ #pragma once -#include +#include "tt_kernel.h" #ifdef __cplusplus extern "C" { @@ -33,7 +33,7 @@ void tt_mutex_free(MutexHandle handle); * @param[in] timeout the maximum amount of ticks to wait when trying to lock * @return true when the lock was acquired */ -bool tt_mutex_lock(MutexHandle handle, TickType_t timeout); +bool tt_mutex_lock(MutexHandle handle, TickType timeout); /** * Attempt to unlock a mutex. diff --git a/TactilityC/Private/tt_lock_private.h b/TactilityC/Private/tt_lock_private.h new file mode 100644 index 00000000..4d0489c2 --- /dev/null +++ b/TactilityC/Private/tt_lock_private.h @@ -0,0 +1,8 @@ +#pragma once + +#include +#include + +struct LockHolder { + std::shared_ptr lock; +}; diff --git a/TactilityC/Source/tt_app.cpp b/TactilityC/Source/tt_app.cpp index 09471f9f..104d5fd5 100644 --- a/TactilityC/Source/tt_app.cpp +++ b/TactilityC/Source/tt_app.cpp @@ -2,6 +2,7 @@ #include #include #include +#include extern "C" { @@ -34,13 +35,13 @@ void tt_app_stop() { tt::app::stop(); } -void tt_app_get_data_directory(AppPathsHandle handle, char* buffer, size_t* size) { +void tt_app_get_user_data_path(AppHandle handle, char* buffer, size_t* size) { assert(buffer != nullptr); assert(size != nullptr); assert(*size > 0); - auto paths = HANDLE_AS_APP_CONTEXT(handle)->getPaths(); - auto data_path = paths->getUserDataPath(); - auto expected_length = data_path.length() + 1; + const auto paths = HANDLE_AS_APP_CONTEXT(handle)->getPaths(); + const auto data_path = paths->getUserDataPath(); + const auto expected_length = data_path.length() + 1; if (*size < expected_length) { TT_LOG_E(TAG, "Path buffer not large enough (%d < %d)", *size, expected_length); *size = 0; @@ -52,4 +53,59 @@ void tt_app_get_data_directory(AppPathsHandle handle, char* buffer, size_t* size *size = data_path.length(); } +void tt_app_get_user_data_child_path(AppHandle handle, const char* childPath, char* buffer, size_t* size) { + assert(buffer != nullptr); + assert(size != nullptr); + assert(*size > 0); + const auto paths = HANDLE_AS_APP_CONTEXT(handle)->getPaths(); + const auto resolved_path = paths->getUserDataPath(childPath); + const auto resolved_path_length = resolved_path.length(); + if (*size < (resolved_path_length + 1)) { + TT_LOG_E(TAG, "Path buffer not large enough (%d < %d)", *size, (resolved_path_length + 1)); + *size = 0; + buffer[0] = 0; + return; + } + + strcpy(buffer, resolved_path.c_str()); + *size = resolved_path_length; +} + +void tt_app_get_assets_path(AppHandle handle, char* buffer, size_t* size) { + assert(buffer != nullptr); + assert(size != nullptr); + assert(*size > 0); + const auto paths = HANDLE_AS_APP_CONTEXT(handle)->getPaths(); + const auto assets_path = paths->getAssetsPath(); + const auto expected_length = assets_path.length() + 1; + if (*size < expected_length) { + TT_LOG_E(TAG, "Path buffer not large enough (%d < %d)", *size, expected_length); + *size = 0; + buffer[0] = 0; + return; + } + + strcpy(buffer, assets_path.c_str()); + *size = assets_path.length(); +} + +void tt_app_get_assets_child_path(AppHandle handle, const char* childPath, char* buffer, size_t* size) { + assert(buffer != nullptr); + assert(size != nullptr); + assert(*size > 0); + const auto paths = HANDLE_AS_APP_CONTEXT(handle)->getPaths(); + const auto resolved_path = paths->getAssetsPath(childPath); + const auto resolved_path_length = resolved_path.length(); + if (*size < (resolved_path_length + 1)) { + TT_LOG_E(TAG, "Path buffer not large enough (%d < %d)", *size, (resolved_path_length + 1)); + *size = 0; + buffer[0] = 0; + return; + } + + strcpy(buffer, resolved_path.c_str()); + *size = resolved_path_length; + +} + } \ No newline at end of file diff --git a/TactilityC/Source/tt_file.cpp b/TactilityC/Source/tt_file.cpp new file mode 100644 index 00000000..76db9e37 --- /dev/null +++ b/TactilityC/Source/tt_file.cpp @@ -0,0 +1,13 @@ +#include +#include +#include + +extern "C" { + +LockHandle tt_lock_alloc_for_file(const char* path) { + auto lock = tt::file::getLock(path); + auto holder = new LockHolder(lock); + return holder; +} + +} diff --git a/TactilityC/Source/tt_init.cpp b/TactilityC/Source/tt_init.cpp index 9317f3d1..c02d7227 100644 --- a/TactilityC/Source/tt_init.cpp +++ b/TactilityC/Source/tt_init.cpp @@ -38,6 +38,7 @@ #include #include #include +#include extern "C" { @@ -54,6 +55,9 @@ const esp_elfsym elf_symbols[] { ESP_ELFSYM_EXPORT(calloc), ESP_ELFSYM_EXPORT(realloc), ESP_ELFSYM_EXPORT(free), + ESP_ELFSYM_EXPORT(rand), + ESP_ELFSYM_EXPORT(srand), + ESP_ELFSYM_EXPORT(rand_r), // unistd.h ESP_ELFSYM_EXPORT(usleep), ESP_ELFSYM_EXPORT(sleep), @@ -116,6 +120,7 @@ const esp_elfsym elf_symbols[] { ESP_ELFSYM_EXPORT(snprintf), ESP_ELFSYM_EXPORT(sprintf), ESP_ELFSYM_EXPORT(vsprintf), + ESP_ELFSYM_EXPORT(vsnprintf), // cstring ESP_ELFSYM_EXPORT(strlen), ESP_ELFSYM_EXPORT(strcmp), @@ -225,7 +230,14 @@ const esp_elfsym elf_symbols[] { ESP_ELFSYM_EXPORT(tt_app_selectiondialog_get_result_index), ESP_ELFSYM_EXPORT(tt_app_alertdialog_start), ESP_ELFSYM_EXPORT(tt_app_alertdialog_get_result_index), - ESP_ELFSYM_EXPORT(tt_app_get_data_directory), + ESP_ELFSYM_EXPORT(tt_app_get_user_data_path), + ESP_ELFSYM_EXPORT(tt_app_get_user_data_child_path), + ESP_ELFSYM_EXPORT(tt_app_get_assets_path), + ESP_ELFSYM_EXPORT(tt_app_get_assets_child_path), + ESP_ELFSYM_EXPORT(tt_lock_alloc_for_file), + ESP_ELFSYM_EXPORT(tt_lock_acquire), + ESP_ELFSYM_EXPORT(tt_lock_release), + ESP_ELFSYM_EXPORT(tt_lock_free), ESP_ELFSYM_EXPORT(tt_bundle_alloc), ESP_ELFSYM_EXPORT(tt_bundle_free), ESP_ELFSYM_EXPORT(tt_bundle_opt_bool), diff --git a/TactilityC/Source/tt_lock.cpp b/TactilityC/Source/tt_lock.cpp new file mode 100644 index 00000000..99f3597e --- /dev/null +++ b/TactilityC/Source/tt_lock.cpp @@ -0,0 +1,21 @@ +#include +#include + +extern "C" { + +bool tt_lock_acquire(LockHandle handle, TickType timeout) { + auto holder = static_cast(handle); + return holder->lock->lock(timeout); +} + +bool tt_lock_release(LockHandle handle) { + auto holder = static_cast(handle); + return holder->lock->unlock(); +} + +void tt_lock_free(LockHandle handle) { + auto holder = static_cast(handle); + delete holder; +} + +} \ No newline at end of file diff --git a/TactilityC/Source/tt_mutex.cpp b/TactilityC/Source/tt_mutex.cpp index 55dcc1f2..6cd2e8c5 100644 --- a/TactilityC/Source/tt_mutex.cpp +++ b/TactilityC/Source/tt_mutex.cpp @@ -5,11 +5,11 @@ extern "C" { #define HANDLE_AS_MUTEX(handle) ((tt::Mutex*)(handle)) -MutexHandle tt_mutex_alloc(enum TtMutexType type) { +MutexHandle tt_mutex_alloc(TtMutexType type) { switch (type) { - case TtMutexType::MUTEX_TYPE_NORMAL: + case MUTEX_TYPE_NORMAL: return new tt::Mutex(tt::Mutex::Type::Normal); - case TtMutexType::MUTEX_TYPE_RECURSIVE: + case MUTEX_TYPE_RECURSIVE: return new tt::Mutex(tt::Mutex::Type::Recursive); default: tt_crash("Type not supported"); @@ -20,8 +20,8 @@ void tt_mutex_free(MutexHandle handle) { delete HANDLE_AS_MUTEX(handle); } -bool tt_mutex_lock(MutexHandle handle, TickType_t timeout) { - return HANDLE_AS_MUTEX(handle)->lock((TickType_t)timeout); +bool tt_mutex_lock(MutexHandle handle, TickType timeout) { + return HANDLE_AS_MUTEX(handle)->lock(timeout); } bool tt_mutex_unlock(MutexHandle handle) {