mirror of
https://github.com/ByteWelder/Tactility.git
synced 2026-10-17 02:45:05 +00:00
app-module: - Implement `AppStartContext` to reduce amount of `app_start_` and `app_exec` functions: replace them with `app_start_with_context()` - Add support for environment variables when starting apps - Implement support for environment variables for apps graphics-module: - Add this new module - Move font-module code into this module - Implement pixel_buffer apps: - Terminal app: reduce memory usage by only buffering a single row of characters, and try to buffer in IRAM when possible. Implement new pixel_buffer. - App List: Mark apps as favourite to pin them on top. Use touch or press "x" key. - App List: Add toolbar button that shows alert dialog with info about using favourites
135 lines
4.2 KiB
C++
135 lines
4.2 KiB
C++
// SPDX-License-Identifier: Apache-2.0
|
|
#include <app/env.h>
|
|
#include <app/private/env_internal.h>
|
|
#include <app/private/ledger.h>
|
|
#include <app/scheduler.h>
|
|
|
|
#include <tactility/concurrent/mutex.h>
|
|
|
|
#include <algorithm>
|
|
#include <cstring>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace {
|
|
|
|
bool is_valid_name(const char* name) {
|
|
return name != nullptr && name[0] != '\0' && strchr(name, '=') == nullptr;
|
|
}
|
|
|
|
// "NAME=..." prefix match, not just a prefix: requires the '=' right after it, so "FOO" doesn't match "FOOBAR=x".
|
|
bool entry_matches_name(const std::string& entry, const char* name, size_t name_len) {
|
|
return entry.size() > name_len && entry[name_len] == '=' && entry.compare(0, name_len, name) == 0;
|
|
}
|
|
|
|
// Finds the calling app instance's environment under the ledger lock.
|
|
// On a non-NULL return, the lock is left held for the caller to keep mutating/reading it,
|
|
// and must be released via mutex_unlock(&app_ledger().mutex) before returning to app_env_*()'s own caller.
|
|
std::vector<std::string>* lock_current_env() {
|
|
auto& ledger = app_ledger();
|
|
mutex_lock(&ledger.mutex);
|
|
auto iterator = ledger.instances.find(app_scheduler_current_app_id());
|
|
if (iterator == ledger.instances.end()) {
|
|
mutex_unlock(&ledger.mutex);
|
|
return nullptr;
|
|
}
|
|
return &iterator->second.env;
|
|
}
|
|
|
|
std::vector<std::string>::iterator find_entry(std::vector<std::string>& env, const char* name, size_t name_len) {
|
|
return std::find_if(env.begin(), env.end(), [&](const std::string& entry) {
|
|
return entry_matches_name(entry, name, name_len);
|
|
});
|
|
}
|
|
|
|
} // namespace
|
|
|
|
void app_env_apply(std::vector<std::string>& env, const char* const* overlay) {
|
|
for (int i = 0; overlay != nullptr && overlay[i] != nullptr; i++) {
|
|
const char* separator = strchr(overlay[i], '=');
|
|
if (separator == nullptr || separator == overlay[i]) {
|
|
continue; // matches app_env_put()'s own rejection of a nameless/equals-less entry
|
|
}
|
|
const size_t name_len = static_cast<size_t>(separator - overlay[i]);
|
|
const std::string name(overlay[i], name_len);
|
|
auto iterator = find_entry(env, name.c_str(), name_len);
|
|
if (iterator == env.end()) {
|
|
env.emplace_back(overlay[i]);
|
|
} else {
|
|
*iterator = overlay[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
extern "C" {
|
|
|
|
error_t app_env_set(const char* name, const char* value, bool overwrite) {
|
|
if (!is_valid_name(name) || value == nullptr) {
|
|
return ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
std::vector<std::string>* env = lock_current_env();
|
|
if (env == nullptr) {
|
|
return ERROR_NOT_FOUND;
|
|
}
|
|
|
|
const size_t name_len = strlen(name);
|
|
auto iterator = find_entry(*env, name, name_len);
|
|
if (iterator == env->end()) {
|
|
env->emplace_back(std::string(name) + "=" + value);
|
|
} else if (overwrite) {
|
|
*iterator = std::string(name) + "=" + value;
|
|
}
|
|
mutex_unlock(&app_ledger().mutex);
|
|
return ERROR_NONE;
|
|
}
|
|
|
|
error_t app_env_unset(const char* name) {
|
|
if (!is_valid_name(name)) {
|
|
return ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
std::vector<std::string>* env = lock_current_env();
|
|
if (env == nullptr) {
|
|
return ERROR_NOT_FOUND;
|
|
}
|
|
|
|
auto iterator = find_entry(*env, name, strlen(name));
|
|
if (iterator != env->end()) {
|
|
env->erase(iterator);
|
|
}
|
|
mutex_unlock(&app_ledger().mutex);
|
|
return ERROR_NONE;
|
|
}
|
|
|
|
const char* app_env_get(const char* name) {
|
|
if (!is_valid_name(name)) {
|
|
return nullptr;
|
|
}
|
|
|
|
std::vector<std::string>* env = lock_current_env();
|
|
if (env == nullptr) {
|
|
return nullptr;
|
|
}
|
|
|
|
const size_t name_len = strlen(name);
|
|
auto iterator = find_entry(*env, name, name_len);
|
|
const char* result = iterator != env->end() ? iterator->c_str() + name_len + 1 : nullptr;
|
|
mutex_unlock(&app_ledger().mutex);
|
|
return result;
|
|
}
|
|
|
|
error_t app_env_put(const char* string) {
|
|
if (string == nullptr) {
|
|
return ERROR_INVALID_ARGUMENT;
|
|
}
|
|
const char* separator = strchr(string, '=');
|
|
if (separator == nullptr || separator == string) {
|
|
return ERROR_INVALID_ARGUMENT;
|
|
}
|
|
const std::string name(string, separator - string);
|
|
return app_env_set(name.c_str(), separator + 1, true);
|
|
}
|
|
|
|
} // extern "C"
|