Filesystem improvements and more (#148)
- Rename `assets` and `config` partitions to `system` and `data` - Change partition type from `spiffs` to `fat`, so we can have sub-directories - Fix crash when doing WiFi scan: Increased system event task size to 3kB. - Free up IRAM on ESP32 (it was required for the Core2, but I also freed up the same amount for Yellow Board) - Introduced `Paths` objects that can be retrieved by `AppContext` and `ServiceContext`. Apps and services now have their own relative paths. Assets were re-arranged into the correct paths. - Rename simulator window title to "Tactility" - Refactored statusbar widget so it persists icon paths properly (it kept a const char* reference, but didn't copy it, so it crashed when the related std::string was destroyed) - Created `Partitions.h` to expose some useful variables - Moved USB config in various `sdkconfig` (it was part of the "default" section, but it shouldn't be) - Updated domain name
@ -82,6 +82,7 @@ static void lvgl_task(TT_UNUSED void* arg) {
|
||||
* but somehow that doesn't work. Waiting here from a ThreadFlag when that happens
|
||||
* also doesn't work. It seems that it must be called from this task. */
|
||||
displayHandle = lv_sdl_window_create(320, 240);
|
||||
lv_sdl_window_set_title(displayHandle, "Tactility");
|
||||
|
||||
uint32_t task_delay_ms = task_max_sleep_ms;
|
||||
|
||||
|
||||
2
Data/data/test.txt
Normal file
@ -0,0 +1,2 @@
|
||||
This file exists to test the partition.
|
||||
It can be deleted when the partition contains other files.
|
||||
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 688 B After Width: | Height: | Size: 688 B |
|
Before Width: | Height: | Size: 564 B After Width: | Height: | Size: 564 B |
|
Before Width: | Height: | Size: 724 B After Width: | Height: | Size: 724 B |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 350 B After Width: | Height: | Size: 350 B |
|
Before Width: | Height: | Size: 457 B After Width: | Height: | Size: 457 B |
|
Before Width: | Height: | Size: 360 B After Width: | Height: | Size: 360 B |
|
Before Width: | Height: | Size: 505 B After Width: | Height: | Size: 505 B |
|
Before Width: | Height: | Size: 333 B After Width: | Height: | Size: 333 B |
|
Before Width: | Height: | Size: 240 B After Width: | Height: | Size: 240 B |
|
Before Width: | Height: | Size: 286 B After Width: | Height: | Size: 286 B |
|
Before Width: | Height: | Size: 429 B After Width: | Height: | Size: 429 B |
|
Before Width: | Height: | Size: 299 B After Width: | Height: | Size: 299 B |
|
Before Width: | Height: | Size: 535 B After Width: | Height: | Size: 535 B |
|
Before Width: | Height: | Size: 436 B After Width: | Height: | Size: 436 B |
|
Before Width: | Height: | Size: 142 B After Width: | Height: | Size: 142 B |
|
Before Width: | Height: | Size: 144 B After Width: | Height: | Size: 144 B |
|
Before Width: | Height: | Size: 149 B After Width: | Height: | Size: 149 B |
|
Before Width: | Height: | Size: 146 B After Width: | Height: | Size: 146 B |
|
Before Width: | Height: | Size: 146 B After Width: | Height: | Size: 146 B |
|
Before Width: | Height: | Size: 149 B After Width: | Height: | Size: 149 B |
|
Before Width: | Height: | Size: 149 B After Width: | Height: | Size: 149 B |
|
Before Width: | Height: | Size: 149 B After Width: | Height: | Size: 149 B |
|
Before Width: | Height: | Size: 149 B After Width: | Height: | Size: 149 B |
|
Before Width: | Height: | Size: 149 B After Width: | Height: | Size: 149 B |
|
Before Width: | Height: | Size: 149 B After Width: | Height: | Size: 149 B |
|
Before Width: | Height: | Size: 193 B After Width: | Height: | Size: 193 B |
|
Before Width: | Height: | Size: 196 B After Width: | Height: | Size: 196 B |
|
Before Width: | Height: | Size: 394 B After Width: | Height: | Size: 394 B |
|
Before Width: | Height: | Size: 407 B After Width: | Height: | Size: 407 B |
|
Before Width: | Height: | Size: 524 B After Width: | Height: | Size: 524 B |
|
Before Width: | Height: | Size: 517 B After Width: | Height: | Size: 517 B |
|
Before Width: | Height: | Size: 534 B After Width: | Height: | Size: 534 B |
|
Before Width: | Height: | Size: 384 B After Width: | Height: | Size: 384 B |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 8.3 KiB |
|
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
1
Data/system_sources/app/Desktop/apps.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="M240-160q-33 0-56.5-23.5T160-240q0-33 23.5-56.5T240-320q33 0 56.5 23.5T320-240q0 33-23.5 56.5T240-160Zm240 0q-33 0-56.5-23.5T400-240q0-33 23.5-56.5T480-320q33 0 56.5 23.5T560-240q0 33-23.5 56.5T480-160Zm240 0q-33 0-56.5-23.5T640-240q0-33 23.5-56.5T720-320q33 0 56.5 23.5T800-240q0 33-23.5 56.5T720-160ZM240-400q-33 0-56.5-23.5T160-480q0-33 23.5-56.5T240-560q33 0 56.5 23.5T320-480q0 33-23.5 56.5T240-400Zm240 0q-33 0-56.5-23.5T400-480q0-33 23.5-56.5T480-560q33 0 56.5 23.5T560-480q0 33-23.5 56.5T480-400Zm240 0q-33 0-56.5-23.5T640-480q0-33 23.5-56.5T720-560q33 0 56.5 23.5T800-480q0 33-23.5 56.5T720-400ZM240-640q-33 0-56.5-23.5T160-720q0-33 23.5-56.5T240-800q33 0 56.5 23.5T320-720q0 33-23.5 56.5T240-640Zm240 0q-33 0-56.5-23.5T400-720q0-33 23.5-56.5T480-800q33 0 56.5 23.5T560-720q0 33-23.5 56.5T480-640Zm240 0q-33 0-56.5-23.5T640-720q0-33 23.5-56.5T720-800q33 0 56.5 23.5T800-720q0 33-23.5 56.5T720-640Z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
1
Data/system_sources/app/Desktop/folder.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="M160-160q-33 0-56.5-23.5T80-240v-480q0-33 23.5-56.5T160-800h240l80 80h320q33 0 56.5 23.5T880-640v400q0 33-23.5 56.5T800-160H160Zm0-80h640v-400H447l-80-80H160v480Zm0 0v-480 480Z"/></svg>
|
||||
|
After Width: | Height: | Size: 301 B |
1
Data/system_sources/app/Desktop/settings.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="m370-80-16-128q-13-5-24.5-12T307-235l-119 50L78-375l103-78q-1-7-1-13.5v-27q0-6.5 1-13.5L78-585l110-190 119 50q11-8 23-15t24-12l16-128h220l16 128q13 5 24.5 12t22.5 15l119-50 110 190-103 78q1 7 1 13.5v27q0 6.5-2 13.5l103 78-110 190-118-50q-11 8-23 15t-24 12L590-80H370Zm70-80h79l14-106q31-8 57.5-23.5T639-327l99 41 39-68-86-65q5-14 7-29.5t2-31.5q0-16-2-31.5t-7-29.5l86-65-39-68-99 42q-22-23-48.5-38.5T533-694l-13-106h-79l-14 106q-31 8-57.5 23.5T321-633l-99-41-39 68 86 64q-5 15-7 30t-2 32q0 16 2 31t7 30l-86 65 39 68 99-42q22 23 48.5 38.5T427-266l13 106Zm42-180q58 0 99-41t41-99q0-58-41-99t-99-41q-59 0-99.5 41T342-480q0 58 40.5 99t99.5 41Zm-2-140Z"/></svg>
|
||||
|
After Width: | Height: | Size: 771 B |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 369 B After Width: | Height: | Size: 369 B |
|
Before Width: | Height: | Size: 514 B After Width: | Height: | Size: 514 B |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
@ -10,6 +10,7 @@
|
||||
- Show error in WiFi screen (e.g. AlertDialog when SPI is not enabled and available memory is below a certain amount)
|
||||
- Clean up static_cast when casting to base class.
|
||||
- M5Stack CoreS3 SD card mounts, but cannot be read. There is currently a notice about it [here](https://github.com/espressif/esp-bsp/blob/master/bsp/m5stack_core_s3/README.md).
|
||||
- SD card statusbar icon shows error when there's a read timeout on the SD card status. Don't show the error icon in this scenario.
|
||||
|
||||
# TODOs
|
||||
- Call tt::lvgl::isSyncSet after HAL init and show error (and crash?) when it is not set.
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
Tactility is an operating system that focuses on the ESP32 microcontroller family.
|
||||
|
||||
See [https://tactility.bytewelder.com](https://tactility.bytewelder.com) for more information.
|
||||
See [https://tactility.one](https://tactility.one) for more information.
|
||||
|
||||
Status: pre-release
|
||||
|
||||
|
||||
@ -82,6 +82,9 @@ public:
|
||||
void setResult(Result result) override;
|
||||
void setResult(Result result, std::shared_ptr<const Bundle> bundle) override;
|
||||
bool hasResult() const override;
|
||||
|
||||
std::unique_ptr<Paths> getPaths() const override;
|
||||
|
||||
std::unique_ptr<ResultHolder>& getResult() { return resultHolder; }
|
||||
};
|
||||
|
||||
|
||||
28
Tactility/Private/app/AppInstancePaths.h
Normal file
@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include "app/AppInstance.h"
|
||||
|
||||
namespace tt::app {
|
||||
|
||||
class AppInstancePaths final : public Paths {
|
||||
|
||||
private:
|
||||
|
||||
const AppManifest& manifest;
|
||||
|
||||
public:
|
||||
|
||||
explicit AppInstancePaths(const AppManifest& manifest) : manifest(manifest) {}
|
||||
~AppInstancePaths() final = default;
|
||||
|
||||
std::string getDataDirectory() const final;
|
||||
std::string getDataDirectoryLvgl() const final;
|
||||
std::string getDataPath(const std::string& childPath) const final;
|
||||
std::string getDataPathLvgl(const std::string& childPath) const final;
|
||||
std::string getSystemDirectory() const final;
|
||||
std::string getSystemDirectoryLvgl() const final;
|
||||
std::string getSystemPath(const std::string& childPath) const final;
|
||||
std::string getSystemPathLvgl(const std::string& childPath) const final;
|
||||
};
|
||||
|
||||
}
|
||||
@ -6,6 +6,8 @@
|
||||
|
||||
namespace tt::app {
|
||||
|
||||
class Paths;
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
bool showStatusbar : 1;
|
||||
@ -33,6 +35,68 @@ public:
|
||||
virtual void setResult(Result result) = 0;
|
||||
virtual void setResult(Result result, std::shared_ptr<const Bundle> bundle)= 0;
|
||||
virtual bool hasResult() const = 0;
|
||||
virtual std::unique_ptr<Paths> getPaths() const = 0;
|
||||
};
|
||||
|
||||
class Paths {
|
||||
|
||||
public:
|
||||
|
||||
Paths() = default;
|
||||
virtual ~Paths() = default;
|
||||
|
||||
/**
|
||||
* Returns the directory path for the data location for an app.
|
||||
* The data directory is intended to survive OS upgrades.
|
||||
* The path will not end with a "/".
|
||||
*/
|
||||
virtual std::string getDataDirectory() const = 0;
|
||||
|
||||
/**
|
||||
* @see getDataDirectory(), but with LVGL prefix.
|
||||
*/
|
||||
virtual std::string getDataDirectoryLvgl() const = 0;
|
||||
|
||||
/**
|
||||
* Returns the full path for an entry inside the data location for an app.
|
||||
* The data directory is intended to survive OS upgrades.
|
||||
* Configuration data should be stored here.
|
||||
* @param[in] childPath the path without a "/" prefix
|
||||
*/
|
||||
virtual std::string getDataPath(const std::string& childPath) const = 0;
|
||||
|
||||
/**
|
||||
* @see getDataPath(), but with LVGL prefix.
|
||||
*/
|
||||
virtual std::string getDataPathLvgl(const std::string& childPath) const = 0;
|
||||
|
||||
/**
|
||||
* Returns the directory path for the system location for an app.
|
||||
* The system directory is not intended to survive OS upgrades.
|
||||
* You should not store configuration data here.
|
||||
* The path will not end with a "/".
|
||||
* This is mainly used for core apps (system/boot/settings type).
|
||||
*/
|
||||
virtual std::string getSystemDirectory() const = 0;
|
||||
|
||||
/**
|
||||
* @see getSystemDirectory(), but with LVGL prefix.
|
||||
*/
|
||||
virtual std::string getSystemDirectoryLvgl() const = 0;
|
||||
|
||||
/**
|
||||
* Returns the full path for an entry inside the system location for an app.
|
||||
* The data directory is not intended to survive OS upgrades.
|
||||
* You should not store configuration data here.
|
||||
* This is mainly used for core apps (system/boot/settings type).
|
||||
* @param[in] childPath the path without a "/" prefix
|
||||
*/
|
||||
virtual std::string getSystemPath(const std::string& childPath) const = 0;
|
||||
|
||||
/**
|
||||
* @see getSystemPath(), but with LVGL prefix.
|
||||
*/
|
||||
virtual std::string getSystemPathLvgl(const std::string& childPath) const = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
#include "app/AppInstance.h"
|
||||
#include "app/AppInstancePaths.h"
|
||||
|
||||
namespace tt::app {
|
||||
|
||||
@ -81,4 +82,8 @@ bool AppInstance::hasResult() const {
|
||||
return has_result;
|
||||
}
|
||||
|
||||
std::unique_ptr<Paths> AppInstance::getPaths() const {
|
||||
return std::make_unique<AppInstancePaths>(manifest);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
48
Tactility/Source/app/AppInstancePaths.cpp
Normal file
@ -0,0 +1,48 @@
|
||||
#include "app/AppInstancePaths.h"
|
||||
#include "Partitions.h"
|
||||
|
||||
#define LVGL_PATH_PREFIX std::string("A:/")
|
||||
#ifdef ESP_PLATFORM
|
||||
#define PARTITION_PREFIX std::string("/")
|
||||
#else
|
||||
#define PARTITION_PREFIX std::string("")
|
||||
#endif
|
||||
|
||||
namespace tt::app {
|
||||
|
||||
std::string AppInstancePaths::getDataDirectory() const {
|
||||
return PARTITION_PREFIX + DATA_PARTITION_NAME + "/app/" + manifest.id;
|
||||
}
|
||||
|
||||
std::string AppInstancePaths::getDataDirectoryLvgl() const {
|
||||
return LVGL_PATH_PREFIX + DATA_PARTITION_NAME + "/app/" + manifest.id;
|
||||
}
|
||||
|
||||
std::string AppInstancePaths::getDataPath(const std::string& childPath) const {
|
||||
assert(!childPath.starts_with('/'));
|
||||
return PARTITION_PREFIX + DATA_PARTITION_NAME + "/app/" + manifest.id + '/' + childPath;
|
||||
}
|
||||
|
||||
std::string AppInstancePaths::getDataPathLvgl(const std::string& childPath) const {
|
||||
assert(!childPath.starts_with('/'));
|
||||
return LVGL_PATH_PREFIX + DATA_PARTITION_NAME + "/app/" + manifest.id + '/' + childPath;
|
||||
}
|
||||
|
||||
std::string AppInstancePaths::getSystemDirectory() const {
|
||||
return PARTITION_PREFIX + SYSTEM_PARTITION_NAME + "/app/" + manifest.id;
|
||||
}
|
||||
|
||||
std::string AppInstancePaths::getSystemDirectoryLvgl() const {
|
||||
return LVGL_PATH_PREFIX + SYSTEM_PARTITION_NAME + "/app/" + manifest.id;
|
||||
}
|
||||
|
||||
std::string AppInstancePaths::getSystemPath(const std::string& childPath) const {
|
||||
assert(!childPath.starts_with('/'));
|
||||
return PARTITION_PREFIX + SYSTEM_PARTITION_NAME + "/app/" + manifest.id + '/' + childPath;
|
||||
}
|
||||
|
||||
std::string AppInstancePaths::getSystemPathLvgl(const std::string& childPath) const {
|
||||
return LVGL_PATH_PREFIX + SYSTEM_PARTITION_NAME + "/app/" + manifest.id + '/' + childPath;
|
||||
}
|
||||
|
||||
}
|
||||
@ -18,7 +18,7 @@
|
||||
#define CONFIG_TT_SPLASH_DURATION 0
|
||||
#endif
|
||||
|
||||
#define TAG "Boot"
|
||||
#define TAG "boot"
|
||||
|
||||
namespace tt::app::boot {
|
||||
|
||||
@ -90,11 +90,11 @@ static void onShow(TT_UNUSED AppContext& app, lv_obj_t* parent) {
|
||||
lv_obj_t* image = lv_image_create(parent);
|
||||
lv_obj_set_size(image, LV_PCT(100), LV_PCT(100));
|
||||
|
||||
if (hal::usb::isUsbBootMode()) {
|
||||
lv_image_set_src(image, TT_ASSETS_BOOT_LOGO_USB);
|
||||
} else {
|
||||
lv_image_set_src(image, TT_ASSETS_BOOT_LOGO);
|
||||
}
|
||||
auto paths = app.getPaths();
|
||||
const char* logo = hal::usb::isUsbBootMode() ? "logo_usb.png" : "logo.png";
|
||||
auto logo_path = paths->getSystemPathLvgl(logo);
|
||||
TT_LOG_I(TAG, "%s", logo_path.c_str());
|
||||
lv_image_set_src(image, logo_path.c_str());
|
||||
|
||||
lvgl::obj_set_style_bg_blacken(parent);
|
||||
|
||||
|
||||
@ -25,9 +25,8 @@ std::string getUrlFromCrashData() {
|
||||
|
||||
std::stringstream stream;
|
||||
|
||||
stream << "https://oops.bytewelder.com?";
|
||||
stream << "i=1"; // Application id
|
||||
stream << "&v=" << TT_VERSION; // Version
|
||||
stream << "https://oops.tactility.one";
|
||||
stream << "?v=" << TT_VERSION; // Version
|
||||
stream << "&a=" << CONFIG_IDF_TARGET; // Architecture
|
||||
stream << "&s="; // Stacktrace
|
||||
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
#include "app/AppContext.h"
|
||||
#include "app/ManifestRegistry.h"
|
||||
#include "Check.h"
|
||||
#include "lvgl.h"
|
||||
#include "service/loader/Loader.h"
|
||||
#include "Assets.h"
|
||||
|
||||
namespace tt::app::desktop {
|
||||
|
||||
@ -61,9 +63,13 @@ static void onShow(TT_UNUSED AppContext& app, lv_obj_t* parent) {
|
||||
int32_t available_width = lv_display_get_horizontal_resolution(display) - (3 * 80);
|
||||
int32_t padding = TT_MIN(available_width / 4, 64);
|
||||
|
||||
createAppButton(wrapper, "Apps", "A:/assets/desktop_icon_apps.png", "AppList", 0);
|
||||
createAppButton(wrapper, "Files", "A:/assets/desktop_icon_files.png", "Files", padding);
|
||||
createAppButton(wrapper, "Settings", "A:/assets/desktop_icon_settings.png", "Settings", padding);
|
||||
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");
|
||||
createAppButton(wrapper, "Apps", apps_icon_path.c_str(), "AppList", 0);
|
||||
createAppButton(wrapper, "Files", files_icon_path.c_str(), "Files", padding);
|
||||
createAppButton(wrapper, "Settings", settings_icon_path.c_str(), "Settings", padding);
|
||||
}
|
||||
|
||||
extern const AppManifest manifest = {
|
||||
|
||||
@ -36,6 +36,7 @@ int scandir(
|
||||
ScandirFilter _Nullable filterMethod,
|
||||
ScandirSort _Nullable sortMethod
|
||||
) {
|
||||
TT_LOG_I(TAG, "scandir start");
|
||||
DIR* dir = opendir(path.c_str());
|
||||
if (dir == nullptr) {
|
||||
TT_LOG_E(TAG, "Failed to open dir %s", path.c_str());
|
||||
@ -55,6 +56,7 @@ int scandir(
|
||||
sort(outList.begin(), outList.end(), sortMethod);
|
||||
}
|
||||
|
||||
TT_LOG_I(TAG, "scandir finish");
|
||||
return (int)outList.size();
|
||||
};
|
||||
|
||||
|
||||
@ -2,6 +2,8 @@
|
||||
#include "kernel/Kernel.h"
|
||||
#include "Log.h"
|
||||
#include "FileUtils.h"
|
||||
#include "Partitions.h"
|
||||
#include "hal/SdCard.h"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
@ -51,17 +53,17 @@ bool State::setEntriesForPath(const std::string& path) {
|
||||
dir_entries.push_back({
|
||||
.d_ino = 0,
|
||||
.d_type = TT_DT_DIR,
|
||||
.d_name = "assets"
|
||||
.d_name = SYSTEM_PARTITION_NAME
|
||||
});
|
||||
dir_entries.push_back({
|
||||
.d_ino = 1,
|
||||
.d_type = TT_DT_DIR,
|
||||
.d_name = "config"
|
||||
.d_name = DATA_PARTITION_NAME
|
||||
});
|
||||
dir_entries.push_back({
|
||||
.d_ino = 2,
|
||||
.d_type = TT_DT_DIR,
|
||||
.d_name = "sdcard"
|
||||
.d_name = TT_SDCARD_MOUNT_NAME
|
||||
});
|
||||
|
||||
current_path = path;
|
||||
|
||||
@ -24,11 +24,16 @@ static void onShow(AppContext& app, lv_obj_t* parent) {
|
||||
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
|
||||
auto parameters = app.getParameters();
|
||||
tt_check(parameters != nullptr, "Parameters missing");
|
||||
bool success = false;
|
||||
std::string file_argument;
|
||||
if (parameters->optString(TEXT_VIEWER_FILE_ARGUMENT, file_argument)) {
|
||||
TT_LOG_I(TAG, "Opening %s", file_argument.c_str());
|
||||
lvgl::label_set_text_file(label, file_argument.c_str());
|
||||
} else {
|
||||
if (lvgl::label_set_text_file(label, file_argument.c_str())) {
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
lv_label_set_text_fmt(label, "Failed to load %s", file_argument.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
#include "WifiManage.h"
|
||||
|
||||
#include "Log.h"
|
||||
#include "Assets.h"
|
||||
#include "service/wifi/Wifi.h"
|
||||
#include "lvgl/Style.h"
|
||||
#include "lvgl/Toolbar.h"
|
||||
@ -19,11 +18,11 @@ std::shared_ptr<WifiManage> _Nullable optWifiManage();
|
||||
|
||||
const char* getWifiStatusIconForRssi(int rssi) {
|
||||
if (rssi >= -60) {
|
||||
return TT_ASSETS_ICON_WIFI_SIGNAL_STRONG_BLACK;
|
||||
return "signal_strong.png";
|
||||
} else if (rssi >= -70) {
|
||||
return TT_ASSETS_ICON_WIFI_SIGNAL_MEDIUM_BLACK;
|
||||
return "signal_medium.png";
|
||||
} else {
|
||||
return TT_ASSETS_ICON_WIFI_SIGNAL_WEAK_BLACK;
|
||||
return "signal_weak.png";
|
||||
}
|
||||
}
|
||||
|
||||
@ -111,13 +110,15 @@ void View::createSsidListItem(const service::wifi::WifiApRecord& record, bool is
|
||||
lv_obj_align_to(connecting_spinner, info_wrapper, LV_ALIGN_OUT_LEFT_MID, -8, 0);
|
||||
} else {
|
||||
const char* icon = getWifiStatusIconForRssi(record.rssi);
|
||||
auto icon_path = paths->getSystemPathLvgl(icon);
|
||||
lv_obj_t* rssi_image = lv_image_create(wrapper);
|
||||
lv_image_set_src(rssi_image, icon);
|
||||
lv_image_set_src(rssi_image, icon_path.c_str());
|
||||
lv_obj_align(rssi_image, LV_ALIGN_RIGHT_MID, -42, 0);
|
||||
|
||||
if (record.auth_mode != WIFI_AUTH_OPEN) {
|
||||
lv_obj_t* lock_image = lv_image_create(wrapper);
|
||||
lv_image_set_src(lock_image, TT_ASSETS_ICON_WIFI_LOCK_BLACK);
|
||||
auto lock = paths->getSystemPathLvgl("lock.png");
|
||||
lv_image_set_src(lock_image, lock.c_str());
|
||||
lv_obj_align(lock_image, LV_ALIGN_RIGHT_MID, -62, 0);
|
||||
}
|
||||
}
|
||||
@ -232,6 +233,8 @@ void View::updateEnableOnBootToggle() {
|
||||
void View::init(const AppContext& app, lv_obj_t* parent) {
|
||||
root = parent;
|
||||
|
||||
paths = std::move(app.getPaths());
|
||||
|
||||
// Toolbar
|
||||
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
|
||||
lv_obj_t* toolbar = lvgl::toolbar_create(parent, app);
|
||||
|
||||
@ -13,6 +13,7 @@ private:
|
||||
|
||||
Bindings* bindings;
|
||||
State* state;
|
||||
std::unique_ptr<app::Paths> paths;
|
||||
lv_obj_t* root = nullptr;
|
||||
lv_obj_t* enable_switch = nullptr;
|
||||
lv_obj_t* enable_on_boot_switch = nullptr;
|
||||
|
||||
@ -5,9 +5,14 @@ namespace tt::lvgl {
|
||||
|
||||
#define TAG "tt_lv_label"
|
||||
|
||||
void label_set_text_file(lv_obj_t* label, const char* filepath) {
|
||||
bool label_set_text_file(lv_obj_t* label, const char* filepath) {
|
||||
auto text = file::readString(filepath);
|
||||
lv_label_set_text(label, (const char*)text.get());
|
||||
if (text != nullptr) {
|
||||
lv_label_set_text(label, (const char*)text.get());
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@ -4,6 +4,6 @@
|
||||
|
||||
namespace tt::lvgl {
|
||||
|
||||
void label_set_text_file(lv_obj_t* label, const char* filepath);
|
||||
bool label_set_text_file(lv_obj_t* label, const char* filepath);
|
||||
|
||||
} // namespace
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
#include "Spacer.h"
|
||||
#include "Style.h"
|
||||
|
||||
namespace tt::lvgl {
|
||||
|
||||
lv_obj_t* spacer_create(lv_obj_t* parent, int32_t width, int32_t height) {
|
||||
lv_obj_t* spacer = lv_obj_create(parent);
|
||||
lv_obj_set_size(spacer, width, height);
|
||||
obj_set_style_bg_invisible(spacer);
|
||||
return spacer;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@ -1,10 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "lvgl.h"
|
||||
|
||||
namespace tt::lvgl {
|
||||
|
||||
[[deprecated("Use margin")]]
|
||||
lv_obj_t* spacer_create(lv_obj_t* parent, int32_t width, int32_t height);
|
||||
|
||||
} // namespace
|
||||
@ -4,7 +4,6 @@
|
||||
#include "Mutex.h"
|
||||
#include "Pubsub.h"
|
||||
#include "TactilityCore.h"
|
||||
#include "lvgl/Spacer.h"
|
||||
#include "lvgl/Style.h"
|
||||
|
||||
#include "LvglSync.h"
|
||||
@ -15,7 +14,7 @@ namespace tt::lvgl {
|
||||
#define TAG "statusbar"
|
||||
|
||||
struct StatusbarIcon {
|
||||
const char* image = nullptr;
|
||||
std::string image;
|
||||
bool visible = false;
|
||||
bool claimed = false;
|
||||
};
|
||||
@ -89,8 +88,8 @@ static void statusbar_destructor(TT_UNUSED const lv_obj_class_t* class_p, lv_obj
|
||||
}
|
||||
|
||||
static void update_icon(lv_obj_t* image, const StatusbarIcon* icon) {
|
||||
if (icon->image != nullptr && icon->visible && icon->claimed) {
|
||||
lv_image_set_src(image, icon->image);
|
||||
if (!icon->image.empty() && icon->visible && icon->claimed) {
|
||||
lv_image_set_src(image, icon->image.c_str());
|
||||
lv_obj_remove_flag(image, LV_OBJ_FLAG_HIDDEN);
|
||||
} else {
|
||||
lv_obj_add_flag(image, LV_OBJ_FLAG_HIDDEN);
|
||||
@ -110,7 +109,9 @@ lv_obj_t* statusbar_create(lv_obj_t* parent) {
|
||||
lv_obj_center(obj);
|
||||
lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_ROW);
|
||||
|
||||
lv_obj_t* left_spacer = spacer_create(obj, 1, 1);
|
||||
lv_obj_t* left_spacer = lv_obj_create(obj);
|
||||
lv_obj_set_size(left_spacer, 1, 1);
|
||||
obj_set_style_bg_invisible(left_spacer);
|
||||
lv_obj_set_flex_grow(left_spacer, 1);
|
||||
|
||||
statusbar_lock(TtWaitForever);
|
||||
@ -154,13 +155,13 @@ static void statusbar_event(TT_UNUSED const lv_obj_class_t* class_p, lv_event_t*
|
||||
}
|
||||
}
|
||||
|
||||
int8_t statusbar_icon_add(const char* _Nullable image) {
|
||||
int8_t statusbar_icon_add(const std::string& image) {
|
||||
statusbar_lock(TtWaitForever);
|
||||
int8_t result = -1;
|
||||
for (int8_t i = 0; i < STATUSBAR_ICON_LIMIT; ++i) {
|
||||
if (!statusbar_data.icons[i].claimed) {
|
||||
statusbar_data.icons[i].claimed = true;
|
||||
statusbar_data.icons[i].visible = (image != nullptr);
|
||||
statusbar_data.icons[i].visible = !image.empty();
|
||||
statusbar_data.icons[i].image = image;
|
||||
result = i;
|
||||
TT_LOG_I(TAG, "id %d: added", i);
|
||||
@ -172,6 +173,10 @@ int8_t statusbar_icon_add(const char* _Nullable image) {
|
||||
return result;
|
||||
}
|
||||
|
||||
int8_t statusbar_icon_add() {
|
||||
return statusbar_icon_add("");
|
||||
}
|
||||
|
||||
void statusbar_icon_remove(int8_t id) {
|
||||
TT_LOG_I(TAG, "id %d: remove", id);
|
||||
tt_check(id >= 0 && id < STATUSBAR_ICON_LIMIT);
|
||||
@ -179,13 +184,13 @@ void statusbar_icon_remove(int8_t id) {
|
||||
StatusbarIcon* icon = &statusbar_data.icons[id];
|
||||
icon->claimed = false;
|
||||
icon->visible = false;
|
||||
icon->image = nullptr;
|
||||
icon->image = "";
|
||||
tt_pubsub_publish(statusbar_data.pubsub, nullptr);
|
||||
statusbar_unlock();
|
||||
}
|
||||
|
||||
void statusbar_icon_set_image(int8_t id, const char* image) {
|
||||
TT_LOG_I(TAG, "id %d: set image %s", id, image ? image : "(none)");
|
||||
void statusbar_icon_set_image(int8_t id, const std::string& image) {
|
||||
TT_LOG_I(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)) {
|
||||
StatusbarIcon* icon = &statusbar_data.icons[id];
|
||||
|
||||
@ -10,9 +10,10 @@ namespace tt::lvgl {
|
||||
#define STATUSBAR_HEIGHT (STATUSBAR_ICON_SIZE + 4) // 4 extra pixels for border and outline
|
||||
|
||||
lv_obj_t* statusbar_create(lv_obj_t* parent);
|
||||
int8_t statusbar_icon_add(const char* _Nullable image);
|
||||
int8_t statusbar_icon_add(const std::string& image);
|
||||
int8_t statusbar_icon_add();
|
||||
void statusbar_icon_remove(int8_t id);
|
||||
void statusbar_icon_set_image(int8_t id, const char* image);
|
||||
void statusbar_icon_set_image(int8_t id, const std::string& image);
|
||||
void statusbar_icon_set_visibility(int8_t id, bool visible);
|
||||
|
||||
} // namespace
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
#include "Toolbar.h"
|
||||
|
||||
#include "service/loader/Loader.h"
|
||||
#include "lvgl/Spacer.h"
|
||||
#include "lvgl/Style.h"
|
||||
#include "Spinner.h"
|
||||
|
||||
|
||||
@ -16,4 +16,5 @@ void toolbar_set_nav_action(lv_obj_t* obj, const char* icon, lv_event_cb_t callb
|
||||
lv_obj_t* toolbar_add_button_action(lv_obj_t* obj, const char* icon, lv_event_cb_t callback, void* user_data);
|
||||
lv_obj_t* toolbar_add_switch_action(lv_obj_t* obj);
|
||||
lv_obj_t* toolbar_add_spinner_action(lv_obj_t* obj);
|
||||
|
||||
} // namespace
|
||||
|
||||
@ -243,7 +243,7 @@ static void stopAppInternal() {
|
||||
return;
|
||||
}
|
||||
|
||||
std::unique_ptr<app::ResultHolder> result_holder = std::move(app_to_stop->getResult());
|
||||
auto result_holder = std::move(app_to_stop->getResult());
|
||||
|
||||
const app::AppManifest& manifest = app_to_stop->getManifest();
|
||||
transitionAppToState(*app_to_stop, app::StateHiding);
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
#include "Assets.h"
|
||||
#include "Mutex.h"
|
||||
#include "Timer.h"
|
||||
#include "Tactility.h"
|
||||
@ -16,18 +15,44 @@ namespace tt::service::statusbar {
|
||||
|
||||
#define TAG "statusbar_service"
|
||||
|
||||
// SD card status
|
||||
#define STATUSBAR_ICON_SDCARD "sdcard.png"
|
||||
#define STATUSBAR_ICON_SDCARD_ALERT "sdcard_alert.png"
|
||||
|
||||
// Wifi status
|
||||
#define STATUSBAR_ICON_WIFI_OFF_WHITE "wifi_off_white.png"
|
||||
#define STATUSBAR_ICON_WIFI_SCAN_WHITE "wifi_scan_white.png"
|
||||
#define STATUSBAR_ICON_WIFI_SIGNAL_WEAK_WHITE "wifi_signal_weak_white.png"
|
||||
#define STATUSBAR_ICON_WIFI_SIGNAL_MEDIUM_WHITE "wifi_signal_medium_white.png"
|
||||
#define STATUSBAR_ICON_WIFI_SIGNAL_STRONG_WHITE "wifi_signal_strong_white.png"
|
||||
|
||||
// Power status
|
||||
#define STATUSBAR_ICON_POWER_0 "power_0.png"
|
||||
#define STATUSBAR_ICON_POWER_10 "power_10.png"
|
||||
#define STATUSBAR_ICON_POWER_20 "power_20.png"
|
||||
#define STATUSBAR_ICON_POWER_30 "power_30.png"
|
||||
#define STATUSBAR_ICON_POWER_40 "power_40.png"
|
||||
#define STATUSBAR_ICON_POWER_50 "power_50.png"
|
||||
#define STATUSBAR_ICON_POWER_60 "power_60.png"
|
||||
#define STATUSBAR_ICON_POWER_70 "power_70.png"
|
||||
#define STATUSBAR_ICON_POWER_80 "power_80.png"
|
||||
#define STATUSBAR_ICON_POWER_90 "power_90.png"
|
||||
#define STATUSBAR_ICON_POWER_100 "power_100.png"
|
||||
|
||||
extern const ServiceManifest manifest;
|
||||
|
||||
struct ServiceData {
|
||||
Mutex mutex;
|
||||
std::unique_ptr<Timer> updateTimer;
|
||||
int8_t wifi_icon_id = lvgl::statusbar_icon_add(nullptr);
|
||||
int8_t wifi_icon_id = lvgl::statusbar_icon_add();
|
||||
const char* wifi_last_icon = nullptr;
|
||||
int8_t sdcard_icon_id = lvgl::statusbar_icon_add(nullptr);
|
||||
int8_t sdcard_icon_id = lvgl::statusbar_icon_add();
|
||||
const char* sdcard_last_icon = nullptr;
|
||||
int8_t power_icon_id = lvgl::statusbar_icon_add(nullptr);
|
||||
int8_t power_icon_id = lvgl::statusbar_icon_add();
|
||||
const char* power_last_icon = nullptr;
|
||||
|
||||
std::unique_ptr<service::Paths> paths;
|
||||
|
||||
~ServiceData() {
|
||||
lvgl::statusbar_icon_remove(wifi_icon_id);
|
||||
lvgl::statusbar_icon_remove(sdcard_icon_id);
|
||||
@ -47,24 +72,24 @@ struct ServiceData {
|
||||
|
||||
const char* getWifiStatusIconForRssi(int rssi) {
|
||||
if (rssi >= -60) {
|
||||
return TT_ASSETS_ICON_WIFI_SIGNAL_STRONG_WHITE;
|
||||
return STATUSBAR_ICON_WIFI_SIGNAL_STRONG_WHITE;
|
||||
} else if (rssi >= -70) {
|
||||
return TT_ASSETS_ICON_WIFI_SIGNAL_MEDIUM_WHITE;
|
||||
return STATUSBAR_ICON_WIFI_SIGNAL_MEDIUM_WHITE;
|
||||
} else {
|
||||
return TT_ASSETS_ICON_WIFI_SIGNAL_WEAK_WHITE;
|
||||
return STATUSBAR_ICON_WIFI_SIGNAL_WEAK_WHITE;
|
||||
}
|
||||
}
|
||||
|
||||
static const char* wifi_get_status_icon(wifi::WifiRadioState state, bool secure) {
|
||||
static const char* getWifiStatusIcon(wifi::WifiRadioState state, bool secure) {
|
||||
int rssi;
|
||||
switch (state) {
|
||||
case wifi::WIFI_RADIO_ON:
|
||||
case wifi::WIFI_RADIO_ON_PENDING:
|
||||
case wifi::WIFI_RADIO_CONNECTION_PENDING:
|
||||
return TT_ASSETS_ICON_WIFI_SCAN_WHITE;
|
||||
return STATUSBAR_ICON_WIFI_SCAN_WHITE;
|
||||
case wifi::WIFI_RADIO_OFF_PENDING:
|
||||
case wifi::WIFI_RADIO_OFF:
|
||||
return TT_ASSETS_ICON_WIFI_OFF_WHITE;
|
||||
return STATUSBAR_ICON_WIFI_OFF_WHITE;
|
||||
case wifi::WIFI_RADIO_CONNECTION_ACTIVE:
|
||||
rssi = wifi::getRssi();
|
||||
return getWifiStatusIconForRssi(rssi);
|
||||
@ -73,12 +98,18 @@ static const char* wifi_get_status_icon(wifi::WifiRadioState state, bool secure)
|
||||
}
|
||||
}
|
||||
|
||||
static void update_wifi_icon(std::shared_ptr<ServiceData> data) {
|
||||
static void updateWifiIcon(const service::Paths* paths, const std::shared_ptr<ServiceData>& data) {
|
||||
wifi::WifiRadioState radio_state = wifi::getRadioState();
|
||||
bool is_secure = wifi::isConnectionSecure();
|
||||
const char* desired_icon = wifi_get_status_icon(radio_state, is_secure);
|
||||
const char* desired_icon = getWifiStatusIcon(radio_state, is_secure);
|
||||
if (data->wifi_last_icon != desired_icon) {
|
||||
lvgl::statusbar_icon_set_image(data->wifi_icon_id, desired_icon);
|
||||
if (desired_icon != nullptr) {
|
||||
auto icon_path = paths->getSystemPathLvgl(desired_icon);
|
||||
lvgl::statusbar_icon_set_image(data->wifi_icon_id, icon_path);
|
||||
lvgl::statusbar_icon_set_visibility(data->wifi_icon_id, true);
|
||||
} else {
|
||||
lvgl::statusbar_icon_set_visibility(data->wifi_icon_id, false);
|
||||
}
|
||||
data->wifi_last_icon = desired_icon;
|
||||
}
|
||||
}
|
||||
@ -87,27 +118,32 @@ static void update_wifi_icon(std::shared_ptr<ServiceData> data) {
|
||||
|
||||
// region sdcard
|
||||
|
||||
static const char* sdcard_get_status_icon(hal::SdCard::State state) {
|
||||
static const char* getSdCardStatusIcon(hal::SdCard::State state) {
|
||||
switch (state) {
|
||||
case hal::SdCard::StateMounted:
|
||||
return TT_ASSETS_ICON_SDCARD;
|
||||
return STATUSBAR_ICON_SDCARD;
|
||||
case hal::SdCard::StateError:
|
||||
case hal::SdCard::StateUnmounted:
|
||||
case hal::SdCard::StateUnknown:
|
||||
return TT_ASSETS_ICON_SDCARD_ALERT;
|
||||
return STATUSBAR_ICON_SDCARD_ALERT;
|
||||
default:
|
||||
tt_crash("Unhandled SdCard state");
|
||||
}
|
||||
}
|
||||
|
||||
static void update_sdcard_icon(std::shared_ptr<ServiceData> data) {
|
||||
static void updateSdCardIcon(const service::Paths* paths, const std::shared_ptr<ServiceData>& data) {
|
||||
auto sdcard = tt::hal::getConfiguration().sdcard;
|
||||
if (sdcard != nullptr) {
|
||||
auto state = sdcard->getState();
|
||||
const char* desired_icon = sdcard_get_status_icon(state);
|
||||
const char* desired_icon = getSdCardStatusIcon(state);
|
||||
if (data->sdcard_last_icon != desired_icon) {
|
||||
lvgl::statusbar_icon_set_image(data->sdcard_icon_id, desired_icon);
|
||||
lvgl::statusbar_icon_set_visibility(data->sdcard_icon_id, desired_icon != nullptr);
|
||||
if (desired_icon != nullptr) {
|
||||
auto icon_path = paths->getSystemPathLvgl(desired_icon);
|
||||
lvgl::statusbar_icon_set_image(data->sdcard_icon_id, icon_path);
|
||||
lvgl::statusbar_icon_set_visibility(data->sdcard_icon_id, true);
|
||||
} else {
|
||||
lvgl::statusbar_icon_set_visibility(data->sdcard_icon_id, false);
|
||||
}
|
||||
data->sdcard_last_icon = desired_icon;
|
||||
}
|
||||
}
|
||||
@ -117,7 +153,7 @@ static void update_sdcard_icon(std::shared_ptr<ServiceData> data) {
|
||||
|
||||
// region power
|
||||
|
||||
static _Nullable const char* power_get_status_icon() {
|
||||
static _Nullable const char* getPowerStatusIcon() {
|
||||
auto get_power = getConfiguration()->hardware->power;
|
||||
if (get_power == nullptr) {
|
||||
return nullptr;
|
||||
@ -133,35 +169,40 @@ static _Nullable const char* power_get_status_icon() {
|
||||
uint8_t charge = charge_level.valueAsUint8;
|
||||
|
||||
if (charge >= 95) {
|
||||
return TT_ASSETS_ICON_POWER_100;
|
||||
return STATUSBAR_ICON_POWER_100;
|
||||
} else if (charge >= 85) {
|
||||
return TT_ASSETS_ICON_POWER_90;
|
||||
return STATUSBAR_ICON_POWER_90;
|
||||
} else if (charge >= 75) {
|
||||
return TT_ASSETS_ICON_POWER_80;
|
||||
return STATUSBAR_ICON_POWER_80;
|
||||
} else if (charge >= 65) {
|
||||
return TT_ASSETS_ICON_POWER_70;
|
||||
return STATUSBAR_ICON_POWER_70;
|
||||
} else if (charge >= 55) {
|
||||
return TT_ASSETS_ICON_POWER_60;
|
||||
return STATUSBAR_ICON_POWER_60;
|
||||
} else if (charge >= 45) {
|
||||
return TT_ASSETS_ICON_POWER_50;
|
||||
return STATUSBAR_ICON_POWER_50;
|
||||
} else if (charge >= 35) {
|
||||
return TT_ASSETS_ICON_POWER_40;
|
||||
return STATUSBAR_ICON_POWER_40;
|
||||
} else if (charge >= 25) {
|
||||
return TT_ASSETS_ICON_POWER_30;
|
||||
return STATUSBAR_ICON_POWER_30;
|
||||
} else if (charge >= 15) {
|
||||
return TT_ASSETS_ICON_POWER_20;
|
||||
return STATUSBAR_ICON_POWER_20;
|
||||
} else if (charge >= 5) {
|
||||
return TT_ASSETS_ICON_POWER_10;
|
||||
return STATUSBAR_ICON_POWER_10;
|
||||
} else {
|
||||
return TT_ASSETS_ICON_POWER_0;
|
||||
return STATUSBAR_ICON_POWER_0;
|
||||
}
|
||||
}
|
||||
|
||||
static void update_power_icon(std::shared_ptr<ServiceData> data) {
|
||||
const char* desired_icon = power_get_status_icon();
|
||||
static void updatePowerStatusIcon(const service::Paths* paths, const std::shared_ptr<ServiceData>& data) {
|
||||
const char* desired_icon = getPowerStatusIcon();
|
||||
if (data->power_last_icon != desired_icon) {
|
||||
lvgl::statusbar_icon_set_image(data->power_icon_id, desired_icon);
|
||||
lvgl::statusbar_icon_set_visibility(data->power_icon_id, desired_icon != nullptr);
|
||||
if (desired_icon != nullptr) {
|
||||
auto icon_path = paths->getSystemPathLvgl(desired_icon);
|
||||
lvgl::statusbar_icon_set_image(data->power_icon_id, icon_path);
|
||||
lvgl::statusbar_icon_set_visibility(data->power_icon_id, true);
|
||||
} else {
|
||||
lvgl::statusbar_icon_set_visibility(data->power_icon_id, false);
|
||||
}
|
||||
data->power_last_icon = desired_icon;
|
||||
}
|
||||
}
|
||||
@ -177,9 +218,10 @@ static void service_data_free(ServiceData* data) {
|
||||
static void onUpdate(std::shared_ptr<void> parameter) {
|
||||
auto data = std::static_pointer_cast<ServiceData>(parameter);
|
||||
// TODO: Make thread-safe for LVGL
|
||||
update_wifi_icon(data);
|
||||
update_sdcard_icon(data);
|
||||
update_power_icon(data);
|
||||
auto* paths = data->paths.get();
|
||||
updateWifiIcon(paths, data);
|
||||
updateSdCardIcon(paths, data);
|
||||
updatePowerStatusIcon(paths, data);
|
||||
}
|
||||
|
||||
static void onStart(ServiceContext& service) {
|
||||
@ -187,13 +229,15 @@ static void onStart(ServiceContext& service) {
|
||||
auto data = std::make_shared<ServiceData>();
|
||||
lvgl::unlock();
|
||||
|
||||
data->paths = std::move(service.getPaths());
|
||||
|
||||
service.setData(data);
|
||||
|
||||
// TODO: Make thread-safe for LVGL
|
||||
lvgl::statusbar_icon_set_visibility(data->wifi_icon_id, true);
|
||||
update_wifi_icon(data);
|
||||
update_sdcard_icon(data); // also updates visibility
|
||||
update_power_icon(data);
|
||||
updateWifiIcon(data->paths.get(), data);
|
||||
updateSdCardIcon(data->paths.get(), data); // also updates visibility
|
||||
updatePowerStatusIcon(data->paths.get(), data);
|
||||
|
||||
data->updateTimer = std::make_unique<Timer>(Timer::TypePeriodic, onUpdate, data);
|
||||
// We want to try and scan more often in case of startup or scan lock failure
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
|
||||
namespace tt {
|
||||
|
||||
#define TAG "Dispatcher"
|
||||
#define TAG "dispatcher"
|
||||
#define BACKPRESSURE_WARNING_COUNT 100
|
||||
#define WAIT_FLAG 1
|
||||
|
||||
|
||||
@ -118,7 +118,7 @@ static uint64_t getTimestamp() {
|
||||
|
||||
void log(LogLevel level, const char* tag, const char* format, ...) {
|
||||
std::stringstream buffer;
|
||||
buffer << toColour(level) << toPrefix(level) << " (" << getTimestamp() << ") " << tag << " " << format << "\033[0m\n";
|
||||
buffer << toColour(level) << toPrefix(level) << " (" << getTimestamp() << ") " << tag << ": " << format << "\033[0m\n";
|
||||
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
|
||||
@ -49,7 +49,7 @@ static std::unique_ptr<uint8_t[]> readBinaryInternal(const std::string& filepath
|
||||
size_t buffer_offset = 0;
|
||||
while (buffer_offset < content_length) {
|
||||
size_t bytes_read = fread(&data.get()[buffer_offset], 1, content_length - buffer_offset, file);
|
||||
TT_LOG_I(TAG, "Read %d bytes", bytes_read);
|
||||
TT_LOG_D(TAG, "Read %d bytes", bytes_read);
|
||||
if (bytes_read > 0) {
|
||||
buffer_offset += bytes_read;
|
||||
} else { // Something went wrong?
|
||||
@ -71,11 +71,15 @@ std::unique_ptr<uint8_t[]> readBinary(const std::string& filepath, size_t& outSi
|
||||
std::unique_ptr<uint8_t[]> readString(const std::string& filepath) {
|
||||
size_t size = 0;
|
||||
auto data = readBinaryInternal(filepath, size, 1);
|
||||
if (size > 0) {
|
||||
if (data == nullptr) {
|
||||
return nullptr;
|
||||
} else if (size > 0) {
|
||||
data.get()[size] = 0; // Append null terminator
|
||||
return data;
|
||||
} else {
|
||||
return nullptr;
|
||||
} else { // Empty file: return empty string
|
||||
auto value = std::make_unique<uint8_t[]>(1);
|
||||
value[0] = 0;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
if (DEFINED ENV{ESP_IDF_VERSION})
|
||||
file(GLOB_RECURSE SOURCE_FILES Source/*.c*)
|
||||
|
||||
list(APPEND REQUIRES_LIST TactilityCore esp_wifi nvs_flash driver spiffs vfs fatfs )
|
||||
list(APPEND REQUIRES_LIST TactilityCore esp_wifi nvs_flash driver spiffs vfs fatfs)
|
||||
if("${IDF_TARGET}" STREQUAL "esp32s3")
|
||||
list(APPEND REQUIRES_LIST esp_tinyusb)
|
||||
endif()
|
||||
@ -19,10 +19,10 @@ if (DEFINED ENV{ESP_IDF_VERSION})
|
||||
)
|
||||
|
||||
if (NOT DEFINED TACTILITY_SKIP_SPIFFS)
|
||||
set(ASSETS_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../Data/assets")
|
||||
spiffs_create_partition_image(assets ${ASSETS_SRC_DIR} FLASH_IN_PROJECT)
|
||||
set(CONFIG_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../Data/config")
|
||||
spiffs_create_partition_image(config ${CONFIG_SRC_DIR} FLASH_IN_PROJECT)
|
||||
# Read-only
|
||||
fatfs_create_rawflash_image(system "${CMAKE_CURRENT_SOURCE_DIR}/../Data/system" FLASH_IN_PROJECT PRESERVE_TIME)
|
||||
# Read-write
|
||||
fatfs_create_spiflash_image(data "${CMAKE_CURRENT_SOURCE_DIR}/../Data/data" FLASH_IN_PROJECT PRESERVE_TIME)
|
||||
endif()
|
||||
|
||||
add_definitions(-DESP_PLATFORM)
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
|
||||
namespace tt {
|
||||
|
||||
esp_err_t initEspPartitions();
|
||||
esp_err_t initPartitionsEsp();
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
@ -20,6 +20,8 @@ public:
|
||||
const service::ServiceManifest& getManifest() const override;
|
||||
std::shared_ptr<void> getData() const override;
|
||||
void setData(std::shared_ptr<void> newData) override;
|
||||
|
||||
std::unique_ptr<Paths> getPaths() const override;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
28
TactilityHeadless/Private/service/ServiceInstancePaths.h
Normal file
@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include "service/ServiceInstance.h"
|
||||
|
||||
namespace tt::service {
|
||||
|
||||
class ServiceInstancePaths final : public Paths {
|
||||
|
||||
private:
|
||||
|
||||
const ServiceManifest& manifest;
|
||||
|
||||
public:
|
||||
|
||||
explicit ServiceInstancePaths(const ServiceManifest& manifest) : manifest(manifest) {}
|
||||
~ServiceInstancePaths() final = default;
|
||||
|
||||
std::string getDataDirectory() const final;
|
||||
std::string getDataDirectoryLvgl() const final;
|
||||
std::string getDataPath(const std::string& childPath) const final;
|
||||
std::string getDataPathLvgl(const std::string& childPath) const final;
|
||||
std::string getSystemDirectory() const final;
|
||||
std::string getSystemDirectoryLvgl() const final;
|
||||
std::string getSystemPath(const std::string& childPath) const final;
|
||||
std::string getSystemPathLvgl(const std::string& childPath) const final;
|
||||
};
|
||||
|
||||
}
|
||||
@ -1,12 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#define TT_ASSET_FOLDER "A:/assets/"
|
||||
#define TT_ASSET_FOLDER "A:/system/"
|
||||
#define TT_ASSET(file) TT_ASSET_FOLDER file
|
||||
|
||||
// Splash
|
||||
#define TT_ASSETS_BOOT_LOGO TT_ASSET("boot_logo.png")
|
||||
#define TT_ASSETS_BOOT_LOGO_USB TT_ASSET("boot_logo_usb.png")
|
||||
|
||||
// UI
|
||||
#define TT_ASSETS_UI_SPINNER TT_ASSET("spinner.png")
|
||||
|
||||
@ -18,32 +14,3 @@
|
||||
#define TT_ASSETS_APP_ICON_I2C_SETTINGS TT_ASSET("app_icon_i2c.png")
|
||||
#define TT_ASSETS_APP_ICON_SETTINGS TT_ASSET("app_icon_settings.png")
|
||||
#define TT_ASSETS_APP_ICON_SYSTEM_INFO TT_ASSET("app_icon_system_info.png")
|
||||
|
||||
// SD card status
|
||||
#define TT_ASSETS_ICON_SDCARD TT_ASSET("sdcard.png")
|
||||
#define TT_ASSETS_ICON_SDCARD_ALERT TT_ASSET("sdcard_alert.png")
|
||||
|
||||
// Wifi status
|
||||
#define TT_ASSETS_ICON_WIFI_OFF_WHITE TT_ASSET("wifi_off_white.png")
|
||||
#define TT_ASSETS_ICON_WIFI_SCAN_WHITE TT_ASSET("wifi_scan_white.png")
|
||||
#define TT_ASSETS_ICON_WIFI_SIGNAL_WEAK_WHITE TT_ASSET("wifi_signal_weak_white.png")
|
||||
#define TT_ASSETS_ICON_WIFI_SIGNAL_MEDIUM_WHITE TT_ASSET("wifi_signal_medium_white.png")
|
||||
#define TT_ASSETS_ICON_WIFI_SIGNAL_STRONG_WHITE TT_ASSET("wifi_signal_strong_white.png")
|
||||
// Black (Wifi Manage)
|
||||
#define TT_ASSETS_ICON_WIFI_LOCK_BLACK TT_ASSET("wifi_lock_black.png")
|
||||
#define TT_ASSETS_ICON_WIFI_SIGNAL_WEAK_BLACK TT_ASSET("wifi_signal_weak_black.png")
|
||||
#define TT_ASSETS_ICON_WIFI_SIGNAL_MEDIUM_BLACK TT_ASSET("wifi_signal_medium_black.png")
|
||||
#define TT_ASSETS_ICON_WIFI_SIGNAL_STRONG_BLACK TT_ASSET("wifi_signal_strong_black.png")
|
||||
|
||||
// Power status
|
||||
#define TT_ASSETS_ICON_POWER_0 TT_ASSET("power_0.png")
|
||||
#define TT_ASSETS_ICON_POWER_10 TT_ASSET("power_10.png")
|
||||
#define TT_ASSETS_ICON_POWER_20 TT_ASSET("power_20.png")
|
||||
#define TT_ASSETS_ICON_POWER_30 TT_ASSET("power_30.png")
|
||||
#define TT_ASSETS_ICON_POWER_40 TT_ASSET("power_40.png")
|
||||
#define TT_ASSETS_ICON_POWER_50 TT_ASSET("power_50.png")
|
||||
#define TT_ASSETS_ICON_POWER_60 TT_ASSET("power_60.png")
|
||||
#define TT_ASSETS_ICON_POWER_70 TT_ASSET("power_70.png")
|
||||
#define TT_ASSETS_ICON_POWER_80 TT_ASSET("power_80.png")
|
||||
#define TT_ASSETS_ICON_POWER_90 TT_ASSET("power_90.png")
|
||||
#define TT_ASSETS_ICON_POWER_100 TT_ASSET("power_100.png")
|
||||
|
||||
@ -1,76 +0,0 @@
|
||||
#ifdef ESP_TARGET
|
||||
|
||||
#include "EspPartitions.h"
|
||||
#include "EspPartitions_i.h"
|
||||
#include "Log.h"
|
||||
|
||||
#include "esp_spiffs.h"
|
||||
#include "nvs_flash.h"
|
||||
|
||||
namespace tt {
|
||||
|
||||
static const char* TAG = "filesystem";
|
||||
|
||||
static esp_err_t initNvsFlashSafely() {
|
||||
esp_err_t result = nvs_flash_init();
|
||||
if (result == ESP_ERR_NVS_NO_FREE_PAGES || result == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
result = nvs_flash_init();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static esp_err_t initSpiffs(esp_vfs_spiffs_conf_t* conf) {
|
||||
esp_err_t ret = esp_vfs_spiffs_register(conf);
|
||||
if (ret != ESP_OK) {
|
||||
if (ret == ESP_FAIL) {
|
||||
TT_LOG_E(TAG, "Failed to mount or format filesystem %s", conf->base_path);
|
||||
} else if (ret == ESP_ERR_NOT_FOUND) {
|
||||
TT_LOG_E(TAG, "Failed to find SPIFFS partition %s", conf->base_path);
|
||||
} else {
|
||||
TT_LOG_E(TAG, "Failed to initialize SPIFFS %s (%s)", conf->base_path, esp_err_to_name(ret));
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
size_t total = -1, used = 0;
|
||||
ret = esp_spiffs_info(NULL, &total, &used);
|
||||
if (ret != ESP_OK) {
|
||||
TT_LOG_E(TAG, "Failed to get SPIFFS partition information for %s (%s)", conf->base_path, esp_err_to_name(ret));
|
||||
} else {
|
||||
TT_LOG_I(TAG, "Partition size for %s: total: %d, used: %d", conf->base_path, total, used);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t initEspPartitions() {
|
||||
ESP_ERROR_CHECK(initNvsFlashSafely());
|
||||
|
||||
esp_vfs_spiffs_conf_t assets_spiffs = {
|
||||
.base_path = MOUNT_POINT_ASSETS,
|
||||
.partition_label = NULL,
|
||||
.max_files = 100,
|
||||
.format_if_mount_failed = false
|
||||
};
|
||||
|
||||
if (initSpiffs(&assets_spiffs) != ESP_OK) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_vfs_spiffs_conf_t config_spiffs = {
|
||||
.base_path = MOUNT_POINT_CONFIG,
|
||||
.partition_label = "config",
|
||||
.max_files = 100,
|
||||
.format_if_mount_failed = false
|
||||
};
|
||||
|
||||
if (initSpiffs(&config_spiffs) != ESP_OK) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif // ESP_TARGET
|
||||
@ -1,12 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef ESP_TARGET
|
||||
|
||||
namespace tt {
|
||||
|
||||
#define MOUNT_POINT_ASSETS "/assets"
|
||||
#define MOUNT_POINT_CONFIG "/config"
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif // ESP_TARGET
|
||||
21
TactilityHeadless/Source/Partitions.h
Normal file
@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
namespace tt {
|
||||
|
||||
#define SYSTEM_PARTITION_NAME "system"
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
#define MOUNT_POINT_SYSTEM "/system"
|
||||
#else
|
||||
#define MOUNT_POINT_SYSTEM "system"
|
||||
#endif
|
||||
|
||||
#define DATA_PARTITION_NAME "data"
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
#define MOUNT_POINT_DATA "/data"
|
||||
#else
|
||||
#define MOUNT_POINT_DATA "data"
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
54
TactilityHeadless/Source/PartitionsEsp.cpp
Normal file
@ -0,0 +1,54 @@
|
||||
#ifdef ESP_TARGET
|
||||
|
||||
#include "EspPartitions_i.h"
|
||||
#include "Log.h"
|
||||
|
||||
#include <esp_vfs_fat.h>
|
||||
#include <nvs_flash.h>
|
||||
|
||||
namespace tt {
|
||||
|
||||
static const char* TAG = "partitions";
|
||||
|
||||
static esp_err_t initNvsFlashSafely() {
|
||||
esp_err_t result = nvs_flash_init();
|
||||
if (result == ESP_ERR_NVS_NO_FREE_PAGES || result == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
result = nvs_flash_init();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static wl_handle_t data_wl_handle = WL_INVALID_HANDLE;
|
||||
|
||||
esp_err_t initPartitionsEsp() {
|
||||
ESP_ERROR_CHECK(initNvsFlashSafely());
|
||||
|
||||
const esp_vfs_fat_mount_config_t mount_config = {
|
||||
.format_if_mount_failed = false,
|
||||
.max_files = 4,
|
||||
.allocation_unit_size = CONFIG_WL_SECTOR_SIZE,
|
||||
.disk_status_check_enable = false,
|
||||
.use_one_fat = true,
|
||||
};
|
||||
|
||||
auto system_result = esp_vfs_fat_spiflash_mount_ro("/system", "system", &mount_config);
|
||||
if (system_result != ESP_OK) {
|
||||
TT_LOG_E(TAG, "Failed to mount /system (%s)", esp_err_to_name(system_result));
|
||||
} else {
|
||||
TT_LOG_I(TAG, "Mounted /system");
|
||||
}
|
||||
|
||||
auto data_result = esp_vfs_fat_spiflash_mount_rw_wl("/data", "data", &mount_config, &data_wl_handle);
|
||||
if (data_result != ESP_OK) {
|
||||
TT_LOG_E(TAG, "Failed to mount /data (%s)", esp_err_to_name(data_result));
|
||||
} else {
|
||||
TT_LOG_I(TAG, "Mounted /data");
|
||||
}
|
||||
|
||||
return system_result == ESP_OK && data_result == ESP_OK;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif // ESP_TARGET
|
||||
@ -1,7 +1,5 @@
|
||||
#include "TactilityCore.h"
|
||||
|
||||
#ifdef ESP_TARGET
|
||||
|
||||
#include "TactilityCore.h"
|
||||
#include "EspPartitions_i.h"
|
||||
|
||||
#include "esp_event.h"
|
||||
@ -13,7 +11,7 @@ namespace tt {
|
||||
#define TAG "tactility"
|
||||
|
||||
// Initialize NVS
|
||||
static void initEspNvs() {
|
||||
static void initNvs() {
|
||||
esp_err_t ret = nvs_flash_init();
|
||||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
TT_LOG_I(TAG, "nvs erasing");
|
||||
@ -24,15 +22,15 @@ static void initEspNvs() {
|
||||
TT_LOG_I(TAG, "nvs initialized");
|
||||
}
|
||||
|
||||
static void initEspNetwork() {
|
||||
static void initNetwork() {
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
}
|
||||
|
||||
void initEsp() {
|
||||
initEspNvs();
|
||||
initEspPartitions();
|
||||
initEspNetwork();
|
||||
initNvs();
|
||||
initPartitionsEsp();
|
||||
initNetwork();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@ -19,7 +19,7 @@ void init(const Configuration& configuration) {
|
||||
|
||||
if (configuration.sdcard != nullptr) {
|
||||
TT_LOG_I(TAG, "Mounting sdcard");
|
||||
if (!configuration.sdcard->mount(TT_SDCARD_MOUNT_POINT )) {
|
||||
if (!configuration.sdcard->mount(TT_SDCARD_MOUNT_POINT)) {
|
||||
TT_LOG_W(TAG, "SD card mount failed (init can continue)");
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
|
||||
namespace tt::hal {
|
||||
|
||||
#define TT_SDCARD_MOUNT_NAME "sdcard"
|
||||
#define TT_SDCARD_MOUNT_POINT "/sdcard"
|
||||
|
||||
class SdCard {
|
||||
|
||||
@ -6,6 +6,8 @@
|
||||
|
||||
namespace tt::service {
|
||||
|
||||
class Paths;
|
||||
|
||||
class ServiceContext {
|
||||
|
||||
protected:
|
||||
@ -17,6 +19,68 @@ public:
|
||||
virtual const service::ServiceManifest& getManifest() const = 0;
|
||||
virtual std::shared_ptr<void> getData() const = 0;
|
||||
virtual void setData(std::shared_ptr<void> newData) = 0;
|
||||
virtual std::unique_ptr<Paths> getPaths() const = 0;
|
||||
};
|
||||
|
||||
class Paths {
|
||||
|
||||
public:
|
||||
|
||||
Paths() = default;
|
||||
virtual ~Paths() = default;
|
||||
|
||||
/**
|
||||
* Returns the directory path for the data location for a service.
|
||||
* The data directory is intended to survive OS upgrades.
|
||||
* The path will not end with a "/".
|
||||
*/
|
||||
virtual std::string getDataDirectory() const = 0;
|
||||
|
||||
/**
|
||||
* @see getDataDirectory(), but with LVGL prefix.
|
||||
*/
|
||||
virtual std::string getDataDirectoryLvgl() const = 0;
|
||||
|
||||
/**
|
||||
* Returns the full path for an entry inside the data location for a service.
|
||||
* The data directory is intended to survive OS upgrades.
|
||||
* Configuration data should be stored here.
|
||||
* @param[in] childPath the path without a "/" prefix
|
||||
*/
|
||||
virtual std::string getDataPath(const std::string& childPath) const = 0;
|
||||
|
||||
/**
|
||||
* @see getDataPath(), but with LVGL prefix.
|
||||
*/
|
||||
virtual std::string getDataPathLvgl(const std::string& childPath) const = 0;
|
||||
|
||||
/**
|
||||
* Returns the directory path for the system location for a service.
|
||||
* The system directory is not intended to survive OS upgrades.
|
||||
* You should not store configuration data here.
|
||||
* The path will not end with a "/".
|
||||
* This is mainly used for core services.
|
||||
*/
|
||||
virtual std::string getSystemDirectory() const = 0;
|
||||
|
||||
/**
|
||||
* @see getSystemDirectory(), but with LVGL prefix.
|
||||
*/
|
||||
virtual std::string getSystemDirectoryLvgl() const = 0;
|
||||
|
||||
/**
|
||||
* Returns the full path for an entry inside the system location for an app.
|
||||
* The data directory is not intended to survive OS upgrades.
|
||||
* You should not store configuration data here.
|
||||
* This is mainly used for core apps (system/boot/settings type).
|
||||
* @param[in] childPath the path without a "/" prefix
|
||||
*/
|
||||
virtual std::string getSystemPath(const std::string& childPath) const = 0;
|
||||
|
||||
/**
|
||||
* @see getSystemPath(), but with LVGL prefix.
|
||||
*/
|
||||
virtual std::string getSystemPathLvgl(const std::string& childPath) const = 0;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
#include <utility>
|
||||
|
||||
#include "service/ServiceInstance.h"
|
||||
#include "service/ServiceInstancePaths.h"
|
||||
|
||||
namespace tt::service {
|
||||
|
||||
@ -21,4 +22,8 @@ void ServiceInstance::setData(std::shared_ptr<void> newData) {
|
||||
mutex.release();
|
||||
}
|
||||
|
||||
std::unique_ptr<Paths> ServiceInstance::getPaths() const {
|
||||
return std::make_unique<ServiceInstancePaths>(manifest);
|
||||
}
|
||||
|
||||
}
|
||||