Various improvements (#547)
- Fixed crash when returning to Setup app (from any of the steps) - Fixed crash when returning to TimeDateSettings app (after locale selection) - Fixed GuiService inconsistent behaviour: now always perform operations async (e.g. to show/hide apps). This fixes crashes in some onHide() calls where onHide() might be called before onShow() was called. - Fix for incorrect WebServService path - Remove CYD-4848S040C SD card functionality, but added a GPIO fix for releasing 3-wire SPI pin sharing. - Fix for saving/loading settings for various apps
This commit is contained in:
committed by
GitHub
parent
a323f8e148
commit
ecad2248d9
@@ -1 +0,0 @@
|
|||||||
enableOnBoot=false
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
enableOnBoot=false
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
enableOnBoot=false
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
language=en-US
|
|
||||||
timeFormat24h=true
|
|
||||||
dateFormat=DD/MM/YYYY
|
|
||||||
region=EU
|
|
||||||
timezone=Europe/Amsterdam
|
|
||||||
@@ -12,9 +12,25 @@
|
|||||||
#include <esp_lcd_panel_ops.h>
|
#include <esp_lcd_panel_ops.h>
|
||||||
#include <esp_lcd_panel_io_additions.h>
|
#include <esp_lcd_panel_io_additions.h>
|
||||||
#include <esp_lcd_st7701.h>
|
#include <esp_lcd_st7701.h>
|
||||||
|
#include <esp_rom_gpio.h>
|
||||||
|
#include <soc/spi_periph.h>
|
||||||
|
#include <driver/spi_master.h>
|
||||||
|
|
||||||
static const auto LOGGER = tt::Logger("St7701Display");
|
static const auto LOGGER = tt::Logger("St7701Display");
|
||||||
|
|
||||||
|
// GPIO47/48 are physically shared between this bit-banged 3-wire command bus
|
||||||
|
// and the SD card's real SPI2 bus (no alternate pins exist on this PCB).
|
||||||
|
// esp_lcd_new_panel_io_3wire_spi() reconfigures them as plain GPIO via
|
||||||
|
// gpio_config(), severing their SPI2 matrix routing. Reconnect them here once
|
||||||
|
// the vendor init sequence is done, so SD card reads/writes keep working.
|
||||||
|
// Safe only because nothing else calls into the ST7701 IO handle after boot.
|
||||||
|
static void reclaimSpiPinsForSdCard() {
|
||||||
|
esp_rom_gpio_connect_out_signal(GPIO_NUM_47, spi_periph_signal[SPI2_HOST].spid_out, false, false);
|
||||||
|
esp_rom_gpio_connect_out_signal(GPIO_NUM_48, spi_periph_signal[SPI2_HOST].spiclk_out, false, false);
|
||||||
|
gpio_set_direction(GPIO_NUM_47, GPIO_MODE_OUTPUT);
|
||||||
|
gpio_set_direction(GPIO_NUM_48, GPIO_MODE_OUTPUT);
|
||||||
|
}
|
||||||
|
|
||||||
static const st7701_lcd_init_cmd_t st7701_lcd_init_cmds[] = {
|
static const st7701_lcd_init_cmd_t st7701_lcd_init_cmds[] = {
|
||||||
// {cmd, { data }, data_size, delay_ms}
|
// {cmd, { data }, data_size, delay_ms}
|
||||||
{0xFF, (uint8_t[]) {0x77, 0x01, 0x00, 0x00, 0x10}, 5, 0},
|
{0xFF, (uint8_t[]) {0x77, 0x01, 0x00, 0x00, 0x10}, 5, 0},
|
||||||
@@ -181,6 +197,8 @@ bool St7701Display::createPanelHandle(esp_lcd_panel_io_handle_t ioHandle, esp_lc
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reclaimSpiPinsForSdCard();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ static error_t stop() {
|
|||||||
return ERROR_NONE;
|
return ERROR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Module cyd_4848s040c_module = {
|
Module cyd_4848s040c_module = {
|
||||||
.name = "cyd-4848s040c",
|
.name = "cyd-4848s040c",
|
||||||
.start = start,
|
.start = start,
|
||||||
.stop = stop,
|
.stop = stop,
|
||||||
|
|||||||
@@ -28,18 +28,4 @@
|
|||||||
pin-sda = <&gpio0 19 GPIO_FLAG_NONE>;
|
pin-sda = <&gpio0 19 GPIO_FLAG_NONE>;
|
||||||
pin-scl = <&gpio0 45 GPIO_FLAG_NONE>;
|
pin-scl = <&gpio0 45 GPIO_FLAG_NONE>;
|
||||||
};
|
};
|
||||||
|
|
||||||
spi0 {
|
|
||||||
compatible = "espressif,esp32-spi";
|
|
||||||
host = <SPI2_HOST>;
|
|
||||||
cs-gpios = <&gpio0 42 GPIO_FLAG_NONE>;
|
|
||||||
pin-mosi = <&gpio0 47 GPIO_FLAG_NONE>;
|
|
||||||
pin-miso = <&gpio0 41 GPIO_FLAG_NONE>;
|
|
||||||
pin-sclk = <&gpio0 48 GPIO_FLAG_NONE>;
|
|
||||||
|
|
||||||
sdcard@0 {
|
|
||||||
compatible = "espressif,esp32-sdspi";
|
|
||||||
frequency-khz = <20000>;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|||||||
+1
-12
@@ -12,8 +12,6 @@
|
|||||||
|
|
||||||
## Higher Priority
|
## Higher Priority
|
||||||
|
|
||||||
- Require either SD card present or at least 8MB of flash. This way we can increase firmware size, ensure performance, and have a single location for storing data.
|
|
||||||
- Consider storing data on SD card in `/tactility` folder (for usage with Launcher)
|
|
||||||
- Add font design tokens such as "regular", "title" and "smaller". Perhaps via the LVGL kernel module.
|
- Add font design tokens such as "regular", "title" and "smaller". Perhaps via the LVGL kernel module.
|
||||||
- Add kernel listening mechanism so that the root device init can be notified when a device becomes available:
|
- Add kernel listening mechanism so that the root device init can be notified when a device becomes available:
|
||||||
Callback for device/start stop with filtering on device type:
|
Callback for device/start stop with filtering on device type:
|
||||||
@@ -21,13 +19,10 @@
|
|||||||
- on_after_start: e.g. to initialize an I2S device via its I2C connection, once I2C becomes available
|
- on_after_start: e.g. to initialize an I2S device via its I2C connection, once I2C becomes available
|
||||||
- on_before_stop: e.g. to stop using a device that was started before
|
- on_before_stop: e.g. to stop using a device that was started before
|
||||||
- on_after_stop: ?
|
- on_after_stop: ?
|
||||||
- DTS: support for #defines
|
|
||||||
- DTS: support for aliases
|
|
||||||
- SPI kernel driver
|
- SPI kernel driver
|
||||||
- Kernel concepts for ELF loading (generic approach for GUI apps, console apps, libraries).
|
- Kernel concepts for ELF loading (generic approach for GUI apps, console apps, libraries).
|
||||||
- Fix glitches when installing app via App Hub with 4.3" Waveshare
|
- Fix glitches when installing app via App Hub with 4.3" Waveshare
|
||||||
- TCA9534 keyboards should use interrupts
|
- TCA9534 keyboards should use interrupts
|
||||||
- GT911 drivers should use interrupts if it's stable
|
|
||||||
- Fix Cardputer (original): use LV_KEY_NEXT and _PREV in keyboard mapping instead of encoder driver hack (and check GPIO app if it then hangs too)
|
- Fix Cardputer (original): use LV_KEY_NEXT and _PREV in keyboard mapping instead of encoder driver hack (and check GPIO app if it then hangs too)
|
||||||
- Expose http::download() and main dispatcher to TactiltyC.
|
- Expose http::download() and main dispatcher to TactiltyC.
|
||||||
- External app loading: Check the version of Tactility and check ESP target hardware to check for compatibility
|
- External app loading: Check the version of Tactility and check ESP target hardware to check for compatibility
|
||||||
@@ -36,15 +31,9 @@
|
|||||||
Create some kind of "intent" handler like on Android.
|
Create some kind of "intent" handler like on Android.
|
||||||
The intent can have an action (e.g. view), a URL and an optional bundle.
|
The intent can have an action (e.g. view), a URL and an optional bundle.
|
||||||
The manifest can provide the intent handler
|
The manifest can provide the intent handler
|
||||||
- When an SD card is detected, check if it has been initialized and assigned as data partition.
|
|
||||||
If the user choses to select it, then copy files from /data over to it.
|
|
||||||
Write the user choice to a file on the card.
|
|
||||||
File contains 3 statuses: ignore, data, .. initdata?
|
|
||||||
The latter is used for auto-selecting it as data partition.
|
|
||||||
- Support direct installation of an `.app` file with `tactility.py install helloworld.app <ip>`
|
- Support direct installation of an `.app` file with `tactility.py install helloworld.app <ip>`
|
||||||
- Support `tactility.py target <ip>` to remember the device IP address.
|
- Support `tactility.py target <ip>` to remember the device IP address.
|
||||||
- minitar/untarFile(): "entry->metadata.path" can escape its confined path (e.g. "../something")
|
- minitar/untarFile(): "entry->metadata.path" can escape its confined path (e.g. "../something")
|
||||||
- Refactor elf loader code to make it multi-platform and to support multiple types of executables
|
|
||||||
|
|
||||||
## Medium Priority
|
## Medium Priority
|
||||||
|
|
||||||
@@ -91,7 +80,7 @@
|
|||||||
- Audio player app
|
- Audio player app
|
||||||
- Audio recording app
|
- Audio recording app
|
||||||
- OTA updates
|
- OTA updates
|
||||||
- T-Deck Plus: Create separate device config?
|
- T-Deck Plus: Create separate device config (pre-requisite: needs more kernel drivers so the driver count in the device project is reduced)
|
||||||
- If present, use LED to show boot/wifi status
|
- If present, use LED to show boot/wifi status
|
||||||
- Capacity based on voltage: estimation for various devices uses a linear voltage curve, but it should use a battery discharge curve.
|
- Capacity based on voltage: estimation for various devices uses a linear voltage curve, but it should use a battery discharge curve.
|
||||||
- Wrapper for lvgl slider widget that shows "+" and "-" buttons, and also the value in a label.
|
- Wrapper for lvgl slider widget that shows "+" and "-" buttons, and also the value in a label.
|
||||||
|
|||||||
@@ -12,9 +12,11 @@ constexpr TickType_t defaultLockTime = 500 / portTICK_PERIOD_MS;
|
|||||||
* LVGL locking function
|
* LVGL locking function
|
||||||
* @param[in] timeout as ticks
|
* @param[in] timeout as ticks
|
||||||
* @warning when passing zero, we wait forever, as this is the default behaviour for esp_lvgl_port, and we want it to remain consistent
|
* @warning when passing zero, we wait forever, as this is the default behaviour for esp_lvgl_port, and we want it to remain consistent
|
||||||
|
* @deprecated Use lvgl_lock() or lvgl_try_lock() from lvgl-module instead.
|
||||||
*/
|
*/
|
||||||
bool lock(TickType_t timeout = portMAX_DELAY);
|
bool lock(TickType_t timeout = portMAX_DELAY);
|
||||||
|
|
||||||
|
/** @deprecated Use lvgl_unlock() from lvgl-module instead. */
|
||||||
void unlock();
|
void unlock();
|
||||||
|
|
||||||
std::shared_ptr<Lock> getSyncLock();
|
std::shared_ptr<Lock> getSyncLock();
|
||||||
|
|||||||
@@ -5,9 +5,9 @@
|
|||||||
namespace tt::settings {
|
namespace tt::settings {
|
||||||
|
|
||||||
struct SystemSettings {
|
struct SystemSettings {
|
||||||
Language language;
|
Language language = Language::en_US;
|
||||||
bool timeFormat24h;
|
bool timeFormat24h = true;
|
||||||
std::string dateFormat; // MM/DD/YYYY, DD/MM/YYYY, YYYY-MM-DD, YYYY/MM/DD
|
std::string dateFormat = std::string("DD/MM/YYYY"); // MM/DD/YYYY, DD/MM/YYYY, YYYY-MM-DD, YYYY/MM/DD
|
||||||
};
|
};
|
||||||
|
|
||||||
bool loadSystemSettings(SystemSettings& properties);
|
bool loadSystemSettings(SystemSettings& properties);
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
namespace tt {
|
namespace tt {
|
||||||
|
|
||||||
esp_err_t initPartitionsEsp();
|
bool initPartitionsEsp();
|
||||||
wl_handle_t getDataPartitionWlHandle();
|
wl_handle_t getDataPartitionWlHandle();
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ struct ChatSettingsData {
|
|||||||
|
|
||||||
ChatSettingsData loadSettings();
|
ChatSettingsData loadSettings();
|
||||||
bool saveSettings(const ChatSettingsData& settings);
|
bool saveSettings(const ChatSettingsData& settings);
|
||||||
ChatSettingsData getDefaultSettings();
|
|
||||||
bool settingsFileExists();
|
bool settingsFileExists();
|
||||||
|
|
||||||
} // namespace tt::app::chat
|
} // namespace tt::app::chat
|
||||||
|
|||||||
@@ -7,16 +7,13 @@
|
|||||||
#include <Tactility/service/Service.h>
|
#include <Tactility/service/Service.h>
|
||||||
#include <Tactility/service/loader/Loader.h>
|
#include <Tactility/service/loader/Loader.h>
|
||||||
|
|
||||||
|
#include <tactility/concurrent/dispatcher.h>
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <lvgl.h>
|
#include <lvgl.h>
|
||||||
|
|
||||||
namespace tt::service::gui {
|
namespace tt::service::gui {
|
||||||
|
|
||||||
constexpr auto GUI_THREAD_FLAG_DRAW = (1 << 0);
|
|
||||||
constexpr auto GUI_THREAD_FLAG_INPUT = (1 << 1);
|
|
||||||
constexpr auto GUI_THREAD_FLAG_EXIT = (1 << 2);
|
|
||||||
constexpr auto GUI_THREAD_FLAG_ALL = (GUI_THREAD_FLAG_DRAW | GUI_THREAD_FLAG_INPUT | GUI_THREAD_FLAG_EXIT);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Output a log warning if the current task is the GUI task.
|
* Output a log warning if the current task is the GUI task.
|
||||||
* This is meant for code that should either create their own task or use a different task to execute on.
|
* This is meant for code that should either create their own task or use a different task to execute on.
|
||||||
@@ -28,7 +25,8 @@ class GuiService final : public Service {
|
|||||||
|
|
||||||
// Thread and lock
|
// Thread and lock
|
||||||
Thread* thread = nullptr;
|
Thread* thread = nullptr;
|
||||||
EventGroup threadFlags;
|
DispatcherHandle_t dispatcher = nullptr;
|
||||||
|
bool exitRequested = false;
|
||||||
RecursiveMutex mutex;
|
RecursiveMutex mutex;
|
||||||
PubSub<loader::LoaderService::Event>::SubscriptionHandle loader_pubsub_subscription = nullptr;
|
PubSub<loader::LoaderService::Event>::SubscriptionHandle loader_pubsub_subscription = nullptr;
|
||||||
|
|
||||||
@@ -46,6 +44,8 @@ class GuiService final : public Service {
|
|||||||
|
|
||||||
static int32_t guiMain();
|
static int32_t guiMain();
|
||||||
|
|
||||||
|
static void onGuiDispatch(void* context);
|
||||||
|
|
||||||
void onLoaderEvent(loader::LoaderService::Event event);
|
void onLoaderEvent(loader::LoaderService::Event event);
|
||||||
|
|
||||||
lv_obj_t* createAppViews(lv_obj_t* parent);
|
lv_obj_t* createAppViews(lv_obj_t* parent);
|
||||||
@@ -70,8 +70,6 @@ public:
|
|||||||
|
|
||||||
void onStop(ServiceContext& service) override;
|
void onStop(ServiceContext& service) override;
|
||||||
|
|
||||||
void requestDraw();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show the on-screen keyboard.
|
* Show the on-screen keyboard.
|
||||||
* @param[in] textarea the textarea to focus the input for
|
* @param[in] textarea the textarea to focus the input for
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ FileSystemApi partition_fs_api = {
|
|||||||
// endregion file_system stub
|
// endregion file_system stub
|
||||||
|
|
||||||
static esp_err_t initNvsFlashSafely() {
|
static esp_err_t initNvsFlashSafely() {
|
||||||
|
LOGGER.info("Init NVS");
|
||||||
esp_err_t result = nvs_flash_init();
|
esp_err_t result = nvs_flash_init();
|
||||||
if (result == ESP_ERR_NVS_NO_FREE_PAGES || result == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
if (result == ESP_ERR_NVS_NO_FREE_PAGES || result == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||||
@@ -75,7 +76,7 @@ size_t getSectorSize() {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t initPartitionsEsp() {
|
bool initPartitionsEsp() {
|
||||||
LOGGER.info("Init partitions");
|
LOGGER.info("Init partitions");
|
||||||
ESP_ERROR_CHECK(initNvsFlashSafely());
|
ESP_ERROR_CHECK(initNvsFlashSafely());
|
||||||
|
|
||||||
@@ -90,22 +91,25 @@ esp_err_t initPartitionsEsp() {
|
|||||||
auto system_result = esp_vfs_fat_spiflash_mount_ro("/system", "system", &mount_config);
|
auto system_result = esp_vfs_fat_spiflash_mount_ro("/system", "system", &mount_config);
|
||||||
if (system_result != ESP_OK) {
|
if (system_result != ESP_OK) {
|
||||||
LOGGER.error("Failed to mount /system ({})", esp_err_to_name(system_result));
|
LOGGER.error("Failed to mount /system ({})", esp_err_to_name(system_result));
|
||||||
} else {
|
return false;
|
||||||
LOGGER.info("Mounted /system");
|
|
||||||
static auto system_fs_data = PartitionFsData("/system");
|
|
||||||
file_system_add(&partition_fs_api, &system_fs_data);
|
|
||||||
}
|
}
|
||||||
|
LOGGER.info("Mounted /system");
|
||||||
|
static auto system_fs_data = PartitionFsData("/system");
|
||||||
|
file_system_add(&partition_fs_api, &system_fs_data);
|
||||||
|
|
||||||
|
#ifdef CONFIG_TT_USER_DATA_LOCATION_INTERNAL
|
||||||
auto data_result = esp_vfs_fat_spiflash_mount_rw_wl("/data", "data", &mount_config, &data_wl_handle);
|
auto data_result = esp_vfs_fat_spiflash_mount_rw_wl("/data", "data", &mount_config, &data_wl_handle);
|
||||||
if (data_result != ESP_OK) {
|
if (data_result != ESP_OK) {
|
||||||
LOGGER.error("Failed to mount /data ({})", esp_err_to_name(data_result));
|
LOGGER.error("Failed to mount /data ({})", esp_err_to_name(data_result));
|
||||||
} else {
|
return false;
|
||||||
LOGGER.info("Mounted /data");
|
|
||||||
static auto data_fs_data = PartitionFsData("/data");
|
|
||||||
file_system_add(&partition_fs_api, &data_fs_data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return system_result == ESP_OK && data_result == ESP_OK;
|
LOGGER.info("Mounted /data");
|
||||||
|
static auto data_fs_data = PartitionFsData("/data");
|
||||||
|
file_system_add(&partition_fs_api, &data_fs_data);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|||||||
@@ -1,38 +1,25 @@
|
|||||||
#ifdef ESP_PLATFORM
|
#ifdef ESP_PLATFORM
|
||||||
|
|
||||||
#include <Tactility/Logger.h>
|
#include <tactility/log.h>
|
||||||
|
#include <tactility/check.h>
|
||||||
|
|
||||||
#include <Tactility/PartitionsEsp.h>
|
#include <Tactility/PartitionsEsp.h>
|
||||||
#include <Tactility/TactilityCore.h>
|
|
||||||
|
|
||||||
#include "esp_event.h"
|
#include "esp_event.h"
|
||||||
#include "esp_netif.h"
|
#include "esp_netif.h"
|
||||||
#include "nvs_flash.h"
|
|
||||||
|
constexpr auto* TAG = "Tactility";
|
||||||
|
|
||||||
namespace tt {
|
namespace tt {
|
||||||
|
|
||||||
static auto LOGGER = Logger("Tactility");
|
|
||||||
|
|
||||||
// Initialize NVS
|
|
||||||
static void initNvs() {
|
|
||||||
LOGGER.info("Init NVS");
|
|
||||||
esp_err_t ret = nvs_flash_init();
|
|
||||||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
|
||||||
LOGGER.info("NVS erasing");
|
|
||||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
|
||||||
ret = nvs_flash_init();
|
|
||||||
}
|
|
||||||
ESP_ERROR_CHECK(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void initNetwork() {
|
static void initNetwork() {
|
||||||
LOGGER.info("Init network");
|
LOG_I(TAG, "Init network");
|
||||||
ESP_ERROR_CHECK(esp_netif_init());
|
ESP_ERROR_CHECK(esp_netif_init());
|
||||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||||
}
|
}
|
||||||
|
|
||||||
void initEsp() {
|
void initEsp() {
|
||||||
initNvs();
|
check(initPartitionsEsp(), "Failed to init partitions");
|
||||||
initPartitionsEsp();
|
|
||||||
initNetwork();
|
initNetwork();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include <Tactility/app/chat/ChatProtocol.h>
|
#include <Tactility/app/chat/ChatProtocol.h>
|
||||||
|
|
||||||
#include <Tactility/crypt/Crypt.h>
|
#include <Tactility/crypt/Crypt.h>
|
||||||
|
#include <Tactility/file/File.h>
|
||||||
#include <Tactility/file/PropertiesFile.h>
|
#include <Tactility/file/PropertiesFile.h>
|
||||||
#include <Tactility/Logger.h>
|
#include <Tactility/Logger.h>
|
||||||
#include <Tactility/Paths.h>
|
#include <Tactility/Paths.h>
|
||||||
@@ -34,6 +35,8 @@ constexpr auto* KEY_NICKNAME = "nickname";
|
|||||||
constexpr auto* KEY_ENCRYPTION_KEY = "encryptionKey";
|
constexpr auto* KEY_ENCRYPTION_KEY = "encryptionKey";
|
||||||
constexpr auto* KEY_CHAT_CHANNEL = "chatChannel";
|
constexpr auto* KEY_CHAT_CHANNEL = "chatChannel";
|
||||||
|
|
||||||
|
uint32_t defaultSenderId = 0;
|
||||||
|
|
||||||
// IV_SEED provides basic obfuscation for stored encryption keys, not strong encryption.
|
// IV_SEED provides basic obfuscation for stored encryption keys, not strong encryption.
|
||||||
// The device master key (from crypt::getIv) provides the actual security.
|
// The device master key (from crypt::getIv) provides the actual security.
|
||||||
static constexpr auto* IV_SEED = "chat_key";
|
static constexpr auto* IV_SEED = "chat_key";
|
||||||
@@ -112,8 +115,9 @@ static uint32_t generateSenderId() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ChatSettingsData getDefaultSettings() {
|
ChatSettingsData getDefaultSettings() {
|
||||||
|
if (defaultSenderId == 0) defaultSenderId = generateSenderId();
|
||||||
return ChatSettingsData{
|
return ChatSettingsData{
|
||||||
.senderId = 0,
|
.senderId = defaultSenderId,
|
||||||
.nickname = "Device",
|
.nickname = "Device",
|
||||||
.encryptionKey = {},
|
.encryptionKey = {},
|
||||||
.hasEncryptionKey = false,
|
.hasEncryptionKey = false,
|
||||||
@@ -124,8 +128,14 @@ ChatSettingsData getDefaultSettings() {
|
|||||||
ChatSettingsData loadSettings() {
|
ChatSettingsData loadSettings() {
|
||||||
ChatSettingsData settings = getDefaultSettings();
|
ChatSettingsData settings = getDefaultSettings();
|
||||||
|
|
||||||
|
auto settings_path = getSettingsFilePath();
|
||||||
|
if (!file::isFile(settings_path)) {
|
||||||
|
settings.senderId = generateSenderId();
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
|
||||||
std::map<std::string, std::string> map;
|
std::map<std::string, std::string> map;
|
||||||
if (!file::loadPropertiesFile(getSettingsFilePath(), map)) {
|
if (!file::loadPropertiesFile(settings_path, map)) {
|
||||||
settings.senderId = generateSenderId();
|
settings.senderId = generateSenderId();
|
||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
@@ -176,7 +186,12 @@ bool saveSettings(const ChatSettingsData& settings) {
|
|||||||
map[KEY_ENCRYPTION_KEY] = "";
|
map[KEY_ENCRYPTION_KEY] = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
return file::savePropertiesFile(getSettingsFilePath(), map);
|
auto settings_path = getSettingsFilePath();
|
||||||
|
if (!file::findOrCreateParentDirectory(settings_path, 0755)) {
|
||||||
|
LOGGER.error("Failed to create parent dir for {}", settings_path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return file::savePropertiesFile(settings_path, map);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool settingsFileExists() {
|
bool settingsFileExists() {
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ class SetupApp final : public App {
|
|||||||
Phase phase = Phase::Welcome;
|
Phase phase = Phase::Welcome;
|
||||||
size_t stepIndex = 0;
|
size_t stepIndex = 0;
|
||||||
std::vector<StepConfiguration> steps;
|
std::vector<StepConfiguration> steps;
|
||||||
|
bool isShown = false;
|
||||||
|
|
||||||
lv_obj_t* titleLabel = nullptr;
|
lv_obj_t* titleLabel = nullptr;
|
||||||
lv_obj_t* descriptionLabel = nullptr;
|
lv_obj_t* descriptionLabel = nullptr;
|
||||||
@@ -105,7 +106,12 @@ class SetupApp final : public App {
|
|||||||
phase = Phase::Done;
|
phase = Phase::Done;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderCurrent();
|
// Widgets may not exist yet: onShow() runs asynchronously on the GUI task and
|
||||||
|
// may not have (re)created them by the time onResult() advances the state.
|
||||||
|
// onShow() calls renderCurrent() itself once the widgets are ready.
|
||||||
|
if (isShown) {
|
||||||
|
renderCurrent();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void onSkipClicked() {
|
void onSkipClicked() {
|
||||||
@@ -180,9 +186,14 @@ public:
|
|||||||
lv_obj_align(continueButton, LV_ALIGN_BOTTOM_RIGHT, -12, -12);
|
lv_obj_align(continueButton, LV_ALIGN_BOTTOM_RIGHT, -12, -12);
|
||||||
lv_obj_add_event_cb(continueButton, onContinueClickedCallback, LV_EVENT_SHORT_CLICKED, this);
|
lv_obj_add_event_cb(continueButton, onContinueClickedCallback, LV_EVENT_SHORT_CLICKED, this);
|
||||||
|
|
||||||
|
isShown = true;
|
||||||
renderCurrent();
|
renderCurrent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void onHide(AppContext& app) override {
|
||||||
|
isShown = false;
|
||||||
|
}
|
||||||
|
|
||||||
void onResult(AppContext& app, LaunchId launchId, Result result, std::unique_ptr<Bundle> bundle) override {
|
void onResult(AppContext& app, LaunchId launchId, Result result, std::unique_ptr<Bundle> bundle) override {
|
||||||
lvgl_lock();
|
lvgl_lock();
|
||||||
advanceTo(stepIndex + 1);
|
advanceTo(stepIndex + 1);
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
|
#include "tactility/lvgl_module.h"
|
||||||
|
|
||||||
|
|
||||||
|
#include <Tactility/Logger.h>
|
||||||
|
#include <Tactility/RecursiveMutex.h>
|
||||||
#include <Tactility/app/AppManifest.h>
|
#include <Tactility/app/AppManifest.h>
|
||||||
#include <Tactility/app/timezone/TimeZone.h>
|
#include <Tactility/app/timezone/TimeZone.h>
|
||||||
#include <Tactility/Logger.h>
|
|
||||||
#include <Tactility/lvgl/Toolbar.h>
|
|
||||||
#include <Tactility/lvgl/LvglSync.h>
|
#include <Tactility/lvgl/LvglSync.h>
|
||||||
#include <Tactility/RecursiveMutex.h>
|
#include <Tactility/lvgl/Toolbar.h>
|
||||||
#include <Tactility/service/loader/Loader.h>
|
#include <Tactility/service/loader/Loader.h>
|
||||||
#include <Tactility/settings/Time.h>
|
|
||||||
#include <Tactility/settings/SystemSettings.h>
|
#include <Tactility/settings/SystemSettings.h>
|
||||||
|
#include <Tactility/settings/Time.h>
|
||||||
|
|
||||||
#include <lvgl.h>
|
#include <lvgl.h>
|
||||||
|
|
||||||
@@ -23,6 +26,7 @@ class TimeDateSettingsApp final : public App {
|
|||||||
RecursiveMutex mutex;
|
RecursiveMutex mutex;
|
||||||
lv_obj_t* timeZoneLabel = nullptr;
|
lv_obj_t* timeZoneLabel = nullptr;
|
||||||
lv_obj_t* dateFormatDropdown = nullptr;
|
lv_obj_t* dateFormatDropdown = nullptr;
|
||||||
|
bool isShown = false;
|
||||||
|
|
||||||
static void onTimeFormatChanged(lv_event_t* event) {
|
static void onTimeFormatChanged(lv_event_t* event) {
|
||||||
auto* widget = lv_event_get_target_obj(event);
|
auto* widget = lv_event_get_target_obj(event);
|
||||||
@@ -133,6 +137,12 @@ public:
|
|||||||
}
|
}
|
||||||
lv_obj_center(timeZoneLabel);
|
lv_obj_center(timeZoneLabel);
|
||||||
lv_label_set_text(timeZoneLabel, timeZoneName.c_str());
|
lv_label_set_text(timeZoneLabel, timeZoneName.c_str());
|
||||||
|
|
||||||
|
isShown = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void onHide(AppContext& app) override {
|
||||||
|
isShown = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void onResult(AppContext& app, LaunchId launchId, Result result, std::unique_ptr<Bundle> bundle) override {
|
void onResult(AppContext& app, LaunchId launchId, Result result, std::unique_ptr<Bundle> bundle) override {
|
||||||
@@ -141,11 +151,13 @@ public:
|
|||||||
const auto code = timezone::getResultCode(*bundle);
|
const auto code = timezone::getResultCode(*bundle);
|
||||||
LOGGER.info("Result name={} code={}", name, code);
|
LOGGER.info("Result name={} code={}", name, code);
|
||||||
|
|
||||||
if (!name.empty()) {
|
// onShow() may not have (re)created the widgets yet: onResult() runs synchronously
|
||||||
if (lvgl::lock(100 / portTICK_PERIOD_MS)) {
|
// on the loader thread and can race ahead of the async gui-task redraw.
|
||||||
|
if (!name.empty() && lvgl_try_lock(100 / portTICK_PERIOD_MS)) {
|
||||||
|
if (isShown) {
|
||||||
lv_label_set_text(timeZoneLabel, name.c_str());
|
lv_label_set_text(timeZoneLabel, name.c_str());
|
||||||
lvgl::unlock();
|
|
||||||
}
|
}
|
||||||
|
lvgl_unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,8 +60,11 @@ bool hasFileForDevice(const std::string& addr_hex) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool load(const std::string& addr_hex, PairedDevice& device) {
|
bool load(const std::string& addr_hex, PairedDevice& device) {
|
||||||
|
auto file_path = getFilePath(addr_hex);
|
||||||
|
if (!file::isFile(file_path)) return false;
|
||||||
|
|
||||||
std::map<std::string, std::string> map;
|
std::map<std::string, std::string> map;
|
||||||
if (!file::loadPropertiesFile(getFilePath(addr_hex), map)) return false;
|
if (!file::loadPropertiesFile(file_path, map)) return false;
|
||||||
if (!map.contains(KEY_ADDR)) return false;
|
if (!map.contains(KEY_ADDR)) return false;
|
||||||
if (!hexToAddr(map[KEY_ADDR], device.addr)) return false;
|
if (!hexToAddr(map[KEY_ADDR], device.addr)) return false;
|
||||||
|
|
||||||
@@ -83,7 +86,12 @@ bool save(const PairedDevice& device) {
|
|||||||
map[KEY_ADDR] = addr_hex;
|
map[KEY_ADDR] = addr_hex;
|
||||||
map[KEY_AUTO_CONNECT] = device.autoConnect ? "true" : "false";
|
map[KEY_AUTO_CONNECT] = device.autoConnect ? "true" : "false";
|
||||||
map[KEY_PROFILE_ID] = std::to_string(device.profileId);
|
map[KEY_PROFILE_ID] = std::to_string(device.profileId);
|
||||||
return file::savePropertiesFile(getFilePath(addr_hex), map);
|
auto file_path = getFilePath(addr_hex);
|
||||||
|
if (!file::findOrCreateParentDirectory(file_path, 0755)) {
|
||||||
|
LOGGER.error("Failed to create parent dir for {}", file_path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return file::savePropertiesFile(file_path, map);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool remove(const std::string& addr_hex) {
|
bool remove(const std::string& addr_hex) {
|
||||||
|
|||||||
@@ -29,8 +29,13 @@ static BluetoothSettings cached;
|
|||||||
static bool cached_valid = false;
|
static bool cached_valid = false;
|
||||||
|
|
||||||
static bool load(BluetoothSettings& out) {
|
static bool load(BluetoothSettings& out) {
|
||||||
|
auto settings_path = getSettingsPath();
|
||||||
|
if (!file::isFile(settings_path)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
std::map<std::string, std::string> map;
|
std::map<std::string, std::string> map;
|
||||||
if (!file::loadPropertiesFile(getSettingsPath(), map)) {
|
if (!file::loadPropertiesFile(settings_path, map)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
auto it = map.find(KEY_ENABLE_ON_BOOT);
|
auto it = map.find(KEY_ENABLE_ON_BOOT);
|
||||||
@@ -47,11 +52,18 @@ static bool load(BluetoothSettings& out) {
|
|||||||
|
|
||||||
static bool save(const BluetoothSettings& s) {
|
static bool save(const BluetoothSettings& s) {
|
||||||
std::map<std::string, std::string> map;
|
std::map<std::string, std::string> map;
|
||||||
file::loadPropertiesFile(getSettingsPath(), map); // ignore failure — may not exist yet
|
if (file::isFile(getSettingsPath())) {
|
||||||
|
file::loadPropertiesFile(getSettingsPath(), map);
|
||||||
|
}
|
||||||
map[KEY_ENABLE_ON_BOOT] = s.enableOnBoot ? "true" : "false";
|
map[KEY_ENABLE_ON_BOOT] = s.enableOnBoot ? "true" : "false";
|
||||||
map[KEY_SPP_AUTO_START] = s.sppAutoStart ? "true" : "false";
|
map[KEY_SPP_AUTO_START] = s.sppAutoStart ? "true" : "false";
|
||||||
map[KEY_MIDI_AUTO_START] = s.midiAutoStart ? "true" : "false";
|
map[KEY_MIDI_AUTO_START] = s.midiAutoStart ? "true" : "false";
|
||||||
return file::savePropertiesFile(getSettingsPath(), map);
|
auto settings_path = getSettingsPath();
|
||||||
|
if (!file::findOrCreateParentDirectory(settings_path, 0755)) {
|
||||||
|
LOGGER.error("Failed to create parent dir for {}", settings_path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return file::savePropertiesFile(settings_path, map);
|
||||||
}
|
}
|
||||||
|
|
||||||
static BluetoothSettings getCachedOrLoad() {
|
static BluetoothSettings getCachedOrLoad() {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#ifdef ESP_PLATFORM
|
#ifdef ESP_PLATFORM
|
||||||
|
#include <Tactility/file/File.h>
|
||||||
#include <Tactility/file/PropertiesFile.h>
|
#include <Tactility/file/PropertiesFile.h>
|
||||||
#include <Tactility/Logger.h>
|
#include <Tactility/Logger.h>
|
||||||
#include <Tactility/Paths.h>
|
#include <Tactility/Paths.h>
|
||||||
@@ -21,8 +22,13 @@ struct DevelopmentSettings {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static bool load(DevelopmentSettings& settings) {
|
static bool load(DevelopmentSettings& settings) {
|
||||||
|
auto settings_path = getSettingsFilePath();
|
||||||
|
if (!file::isFile(settings_path)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
std::map<std::string, std::string> map;
|
std::map<std::string, std::string> map;
|
||||||
if (!file::loadPropertiesFile(getSettingsFilePath(), map)) {
|
if (!file::loadPropertiesFile(settings_path, map)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,7 +44,12 @@ static bool load(DevelopmentSettings& settings) {
|
|||||||
static bool save(const DevelopmentSettings& settings) {
|
static bool save(const DevelopmentSettings& settings) {
|
||||||
std::map<std::string, std::string> map;
|
std::map<std::string, std::string> map;
|
||||||
map[SETTINGS_KEY_ENABLE_ON_BOOT] = settings.enableOnBoot ? "true" : "false";
|
map[SETTINGS_KEY_ENABLE_ON_BOOT] = settings.enableOnBoot ? "true" : "false";
|
||||||
return file::savePropertiesFile(getSettingsFilePath(), map);
|
auto settings_path = getSettingsFilePath();
|
||||||
|
if (!file::findOrCreateParentDirectory(settings_path, 0755)) {
|
||||||
|
LOGGER.error("Failed to create parent dir for {}", settings_path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return file::savePropertiesFile(settings_path, map);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setEnableOnBoot(bool enable) {
|
void setEnableOnBoot(bool enable) {
|
||||||
|
|||||||
@@ -27,14 +27,49 @@ void warnIfRunningOnGuiTask(const char* context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
enum class GuiDispatchType { Show, Hide, Exit };
|
||||||
|
|
||||||
|
struct GuiDispatchItem {
|
||||||
|
GuiService* service;
|
||||||
|
GuiDispatchType type;
|
||||||
|
std::shared_ptr<app::AppInstance> appInstance; // only used for Show
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
// region AppManifest
|
// region AppManifest
|
||||||
|
|
||||||
|
void GuiService::onGuiDispatch(void* context) {
|
||||||
|
std::unique_ptr<GuiDispatchItem> item(static_cast<GuiDispatchItem*>(context));
|
||||||
|
switch (item->type) {
|
||||||
|
case GuiDispatchType::Show:
|
||||||
|
item->service->showApp(item->appInstance);
|
||||||
|
break;
|
||||||
|
case GuiDispatchType::Hide:
|
||||||
|
item->service->hideApp();
|
||||||
|
break;
|
||||||
|
case GuiDispatchType::Exit:
|
||||||
|
item->service->exitRequested = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void GuiService::onLoaderEvent(LoaderService::Event event) {
|
void GuiService::onLoaderEvent(LoaderService::Event event) {
|
||||||
|
GuiDispatchItem* item;
|
||||||
if (event == LoaderService::Event::ApplicationShowing) {
|
if (event == LoaderService::Event::ApplicationShowing) {
|
||||||
auto app_instance = std::static_pointer_cast<app::AppInstance>(app::getCurrentAppContext());
|
auto app_instance = std::static_pointer_cast<app::AppInstance>(app::getCurrentAppContext());
|
||||||
showApp(app_instance);
|
item = new GuiDispatchItem{this, GuiDispatchType::Show, app_instance};
|
||||||
} else if (event == LoaderService::Event::ApplicationHiding) {
|
} else if (event == LoaderService::Event::ApplicationHiding) {
|
||||||
hideApp();
|
item = new GuiDispatchItem{this, GuiDispatchType::Hide, nullptr};
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dispatcher_dispatch(dispatcher, item, onGuiDispatch) != ERROR_NONE) {
|
||||||
|
LOGGER.error("Failed to dispatch gui event");
|
||||||
|
delete item;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,26 +117,8 @@ int32_t GuiService::guiMain() {
|
|||||||
|
|
||||||
lvgl::unlock();
|
lvgl::unlock();
|
||||||
|
|
||||||
while (true) {
|
while (!service->exitRequested) {
|
||||||
uint32_t flags = 0;
|
dispatcher_consume(service->dispatcher);
|
||||||
if (service->threadFlags.wait(GUI_THREAD_FLAG_ALL, false, true, &flags, portMAX_DELAY)) {
|
|
||||||
// When service not started or starting -> exit
|
|
||||||
State service_state = getState(manifest.id);
|
|
||||||
if (service_state != State::Started && service_state != State::Starting) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process and dispatch draw call
|
|
||||||
if (flags & GUI_THREAD_FLAG_DRAW) {
|
|
||||||
service->threadFlags.clear(GUI_THREAD_FLAG_DRAW);
|
|
||||||
service->redraw();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flags & GUI_THREAD_FLAG_EXIT) {
|
|
||||||
service->threadFlags.clear(GUI_THREAD_FLAG_EXIT);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
service->appRootWidget = nullptr;
|
service->appRootWidget = nullptr;
|
||||||
@@ -177,6 +194,9 @@ void GuiService::redraw() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool GuiService::onStart(ServiceContext& service) {
|
bool GuiService::onStart(ServiceContext& service) {
|
||||||
|
exitRequested = false;
|
||||||
|
dispatcher = dispatcher_alloc();
|
||||||
|
|
||||||
thread = new Thread(
|
thread = new Thread(
|
||||||
GUI_TASK_NAME,
|
GUI_TASK_NAME,
|
||||||
4096, // Last known minimum was 2800 for launching desktop
|
4096, // Last known minimum was 2800 for launching desktop
|
||||||
@@ -211,8 +231,14 @@ void GuiService::onStop(ServiceContext& service) {
|
|||||||
appToRender = nullptr;
|
appToRender = nullptr;
|
||||||
isStarted = false;
|
isStarted = false;
|
||||||
|
|
||||||
threadFlags.set(GUI_THREAD_FLAG_EXIT);
|
|
||||||
unlock();
|
unlock();
|
||||||
|
|
||||||
|
auto* exit_item = new GuiDispatchItem{this, GuiDispatchType::Exit, nullptr};
|
||||||
|
if (dispatcher_dispatch(dispatcher, exit_item, onGuiDispatch) != ERROR_NONE) {
|
||||||
|
LOGGER.error("Failed to dispatch gui exit event");
|
||||||
|
check(false, "Failed to dispatch exit signal to thread.");
|
||||||
|
delete exit_item;
|
||||||
|
}
|
||||||
thread->join();
|
thread->join();
|
||||||
|
|
||||||
if (lvgl::lock()) {
|
if (lvgl::lock()) {
|
||||||
@@ -226,10 +252,8 @@ void GuiService::onStop(ServiceContext& service) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
delete thread;
|
delete thread;
|
||||||
}
|
dispatcher_free(dispatcher);
|
||||||
|
dispatcher = nullptr;
|
||||||
void GuiService::requestDraw() {
|
|
||||||
threadFlags.set(GUI_THREAD_FLAG_DRAW);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GuiService::showApp(std::shared_ptr<app::AppInstance> app) {
|
void GuiService::showApp(std::shared_ptr<app::AppInstance> app) {
|
||||||
@@ -253,7 +277,7 @@ void GuiService::showApp(std::shared_ptr<app::AppInstance> app) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
appToRender = std::move(app);
|
appToRender = std::move(app);
|
||||||
requestDraw();
|
redraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GuiService::hideApp() {
|
void GuiService::hideApp() {
|
||||||
|
|||||||
@@ -1454,7 +1454,7 @@ esp_err_t WebServerService::handleApiScreenshot(httpd_req_t* request) {
|
|||||||
|
|
||||||
#if TT_FEATURE_SCREENSHOT_ENABLED
|
#if TT_FEATURE_SCREENSHOT_ENABLED
|
||||||
// Determine save location: prefer SD card root if mounted, otherwise /data
|
// Determine save location: prefer SD card root if mounted, otherwise /data
|
||||||
std::string save_path = getUserDataRootPath();
|
std::string save_path = getUserDataPath();
|
||||||
|
|
||||||
// Find next available filename with incrementing number
|
// Find next available filename with incrementing number
|
||||||
std::string screenshot_path;
|
std::string screenshot_path;
|
||||||
|
|||||||
@@ -127,6 +127,10 @@ bool load(const std::string& ssid, WifiApSettings& apSettings) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const auto file_path = getApPropertiesFilePath(service_context->getPaths(), ssid);
|
const auto file_path = getApPropertiesFilePath(service_context->getPaths(), ssid);
|
||||||
|
if (!file::isFile(file_path)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
std::map<std::string, std::string> map;
|
std::map<std::string, std::string> map;
|
||||||
if (!file::loadPropertiesFile(file_path, map)) {
|
if (!file::loadPropertiesFile(file_path, map)) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -120,21 +120,25 @@ static void importWifiApSettingsFromDir(const std::string& path) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void bootSplashInit() {
|
void bootSplashInit() {
|
||||||
LOGGER.info("bootSplashInit begin");
|
LOGGER.info("bootSplashInit dispatch");
|
||||||
getMainDispatcher().dispatch([] {
|
getMainDispatcher().dispatch([] {
|
||||||
LOGGER.info("bootSplashInit dispatch begin");
|
LOGGER.info("bootSplashInit dispatch begin");
|
||||||
// First import any provisioning files placed on the system data partition.
|
// Import any provisioning files placed on the system data partition.
|
||||||
const std::string data_settings_path = file::getChildPath(getUserDataPath(), "provisioning");
|
const std::string provisioning_path = file::getChildPath(getUserDataPath(), "provisioning");
|
||||||
importWifiApSettingsFromDir(data_settings_path);
|
if (file::isDirectory(provisioning_path)) {
|
||||||
|
importWifiApSettingsFromDir(provisioning_path);
|
||||||
|
} else {
|
||||||
|
LOGGER.info("Skip provisioning: no files at {}", provisioning_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dispatch WiFi on
|
||||||
if (settings::shouldEnableOnBoot()) {
|
if (settings::shouldEnableOnBoot()) {
|
||||||
LOGGER.info("Auto-enabling due to setting");
|
LOGGER.info("Auto-enabling WiFi");
|
||||||
getMainDispatcher().dispatch([] -> void { setEnabled(true); });
|
getMainDispatcher().dispatch([] -> void { setEnabled(true); });
|
||||||
}
|
}
|
||||||
|
|
||||||
LOGGER.info("bootSplashInit dispatch end");
|
LOGGER.info("bootSplashInit dispatch end");
|
||||||
});
|
});
|
||||||
LOGGER.info("bootSplashInit end");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#include <Tactility/MountPoints.h>
|
#include <Tactility/MountPoints.h>
|
||||||
#include <Tactility/file/File.h>
|
#include <Tactility/file/File.h>
|
||||||
#include <Tactility/file/PropertiesFile.h>
|
#include <Tactility/file/PropertiesFile.h>
|
||||||
#include <Tactility/Logger.h>
|
|
||||||
#include <Tactility/settings/BootSettings.h>
|
#include <Tactility/settings/BootSettings.h>
|
||||||
|
|
||||||
#include <Tactility/Paths.h>
|
#include <Tactility/Paths.h>
|
||||||
@@ -10,8 +9,6 @@
|
|||||||
|
|
||||||
namespace tt::settings {
|
namespace tt::settings {
|
||||||
|
|
||||||
constexpr auto* TAG = "BootSettings";
|
|
||||||
|
|
||||||
constexpr auto* PROPERTIES_FILE_FORMAT = "{}/settings/boot.properties";
|
constexpr auto* PROPERTIES_FILE_FORMAT = "{}/settings/boot.properties";
|
||||||
constexpr auto* PROPERTIES_KEY_LAUNCHER_APP_ID = "launcherAppId";
|
constexpr auto* PROPERTIES_KEY_LAUNCHER_APP_ID = "launcherAppId";
|
||||||
constexpr auto* PROPERTIES_KEY_AUTO_START_APP_ID = "autoStartAppId";
|
constexpr auto* PROPERTIES_KEY_AUTO_START_APP_ID = "autoStartAppId";
|
||||||
@@ -22,6 +19,10 @@ static std::string getPropertiesFilePath() {
|
|||||||
|
|
||||||
bool loadBootSettings(BootSettings& properties) {
|
bool loadBootSettings(BootSettings& properties) {
|
||||||
const std::string path = getPropertiesFilePath();
|
const std::string path = getPropertiesFilePath();
|
||||||
|
if (!file::isFile(path)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!file::loadPropertiesFile(path, [&properties](auto& key, auto& value) {
|
if (!file::loadPropertiesFile(path, [&properties](auto& key, auto& value) {
|
||||||
if (key == PROPERTIES_KEY_AUTO_START_APP_ID) {
|
if (key == PROPERTIES_KEY_AUTO_START_APP_ID) {
|
||||||
properties.autoStartAppId = value;
|
properties.autoStartAppId = value;
|
||||||
@@ -29,7 +30,6 @@ bool loadBootSettings(BootSettings& properties) {
|
|||||||
properties.launcherAppId = value;
|
properties.launcherAppId = value;
|
||||||
}
|
}
|
||||||
})) {
|
})) {
|
||||||
LOG_I(TAG, "No settings at %s", path.c_str());
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include <Tactility/settings/DisplaySettings.h>
|
#include <Tactility/settings/DisplaySettings.h>
|
||||||
|
|
||||||
|
#include <Tactility/file/File.h>
|
||||||
#include <Tactility/file/PropertiesFile.h>
|
#include <Tactility/file/PropertiesFile.h>
|
||||||
#include <Tactility/Paths.h>
|
#include <Tactility/Paths.h>
|
||||||
#include <tactility/hal/Device.h>
|
#include <tactility/hal/Device.h>
|
||||||
@@ -109,8 +110,13 @@ static bool fromString(const std::string& str, ScreensaverType& type) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool load(DisplaySettings& settings) {
|
bool load(DisplaySettings& settings) {
|
||||||
|
auto settings_path = getSettingsFilePath();
|
||||||
|
if (!file::isFile(settings_path)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
std::map<std::string, std::string> map;
|
std::map<std::string, std::string> map;
|
||||||
if (!file::loadPropertiesFile(getSettingsFilePath(), map)) {
|
if (!file::loadPropertiesFile(settings_path, map)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,7 +196,11 @@ bool save(const DisplaySettings& settings) {
|
|||||||
map[SETTINGS_KEY_TIMEOUT_ENABLED] = settings.backlightTimeoutEnabled ? "1" : "0";
|
map[SETTINGS_KEY_TIMEOUT_ENABLED] = settings.backlightTimeoutEnabled ? "1" : "0";
|
||||||
map[SETTINGS_KEY_TIMEOUT_MS] = std::to_string(settings.backlightTimeoutMs);
|
map[SETTINGS_KEY_TIMEOUT_MS] = std::to_string(settings.backlightTimeoutMs);
|
||||||
map[SETTINGS_KEY_SCREENSAVER_TYPE] = toString(settings.screensaverType);
|
map[SETTINGS_KEY_SCREENSAVER_TYPE] = toString(settings.screensaverType);
|
||||||
return file::savePropertiesFile(getSettingsFilePath(), map);
|
auto settings_path = getSettingsFilePath();
|
||||||
|
if (!file::findOrCreateParentDirectory(settings_path, 0755)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return file::savePropertiesFile(settings_path, map);
|
||||||
}
|
}
|
||||||
|
|
||||||
lv_display_rotation_t toLvglDisplayRotation(Orientation orientation) {
|
lv_display_rotation_t toLvglDisplayRotation(Orientation orientation) {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include <Tactility/settings/KeyboardSettings.h>
|
#include <Tactility/settings/KeyboardSettings.h>
|
||||||
|
#include <Tactility/file/File.h>
|
||||||
#include <Tactility/file/PropertiesFile.h>
|
#include <Tactility/file/PropertiesFile.h>
|
||||||
#include <Tactility/Paths.h>
|
#include <Tactility/Paths.h>
|
||||||
|
|
||||||
@@ -17,8 +18,13 @@ constexpr auto* KEY_BACKLIGHT_TIMEOUT_ENABLED = "backlightTimeoutEnabled";
|
|||||||
constexpr auto* KEY_BACKLIGHT_TIMEOUT_MS = "backlightTimeoutMs";
|
constexpr auto* KEY_BACKLIGHT_TIMEOUT_MS = "backlightTimeoutMs";
|
||||||
|
|
||||||
bool load(KeyboardSettings& settings) {
|
bool load(KeyboardSettings& settings) {
|
||||||
|
auto settings_path = getSettingsFilePath();
|
||||||
|
if (!file::isFile(settings_path)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
std::map<std::string, std::string> map;
|
std::map<std::string, std::string> map;
|
||||||
if (!file::loadPropertiesFile(getSettingsFilePath(), map)) {
|
if (!file::loadPropertiesFile(settings_path, map)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,7 +64,11 @@ bool save(const KeyboardSettings& settings) {
|
|||||||
map[KEY_BACKLIGHT_BRIGHTNESS] = std::to_string(settings.backlightBrightness);
|
map[KEY_BACKLIGHT_BRIGHTNESS] = std::to_string(settings.backlightBrightness);
|
||||||
map[KEY_BACKLIGHT_TIMEOUT_ENABLED] = settings.backlightTimeoutEnabled ? "1" : "0";
|
map[KEY_BACKLIGHT_TIMEOUT_ENABLED] = settings.backlightTimeoutEnabled ? "1" : "0";
|
||||||
map[KEY_BACKLIGHT_TIMEOUT_MS] = std::to_string(settings.backlightTimeoutMs);
|
map[KEY_BACKLIGHT_TIMEOUT_MS] = std::to_string(settings.backlightTimeoutMs);
|
||||||
return file::savePropertiesFile(getSettingsFilePath(), map);
|
auto settings_path = getSettingsFilePath();
|
||||||
|
if (!file::findOrCreateParentDirectory(settings_path, 0755)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return file::savePropertiesFile(settings_path, map);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,6 +80,11 @@ bool saveSystemSettings(const SystemSettings& properties) {
|
|||||||
map["timeFormat24h"] = properties.timeFormat24h ? "true" : "false";
|
map["timeFormat24h"] = properties.timeFormat24h ? "true" : "false";
|
||||||
map["dateFormat"] = properties.dateFormat;
|
map["dateFormat"] = properties.dateFormat;
|
||||||
|
|
||||||
|
if (!file::findOrCreateParentDirectory(file_path, 0755)) {
|
||||||
|
LOGGER.error("Failed to create parent dir for {}", file_path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!file::savePropertiesFile(file_path, map)) {
|
if (!file::savePropertiesFile(file_path, map)) {
|
||||||
LOGGER.error("Failed to save {}", file_path);
|
LOGGER.error("Failed to save {}", file_path);
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include <Tactility/settings/TouchCalibrationSettings.h>
|
#include <Tactility/settings/TouchCalibrationSettings.h>
|
||||||
|
|
||||||
|
#include <Tactility/file/File.h>
|
||||||
#include <Tactility/file/PropertiesFile.h>
|
#include <Tactility/file/PropertiesFile.h>
|
||||||
#include <Tactility/Mutex.h>
|
#include <Tactility/Mutex.h>
|
||||||
#include <Tactility/Paths.h>
|
#include <Tactility/Paths.h>
|
||||||
@@ -64,8 +65,13 @@ bool isValid(const TouchCalibrationSettings& settings) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool load(TouchCalibrationSettings& settings) {
|
bool load(TouchCalibrationSettings& settings) {
|
||||||
|
auto settings_path = getSettingsFilePath();
|
||||||
|
if (!file::isFile(settings_path)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
std::map<std::string, std::string> map;
|
std::map<std::string, std::string> map;
|
||||||
if (!file::loadPropertiesFile(getSettingsFilePath(), map)) {
|
if (!file::loadPropertiesFile(settings_path, map)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,7 +122,12 @@ bool save(const TouchCalibrationSettings& settings) {
|
|||||||
map[SETTINGS_KEY_Y_MIN] = std::to_string(settings.yMin);
|
map[SETTINGS_KEY_Y_MIN] = std::to_string(settings.yMin);
|
||||||
map[SETTINGS_KEY_Y_MAX] = std::to_string(settings.yMax);
|
map[SETTINGS_KEY_Y_MAX] = std::to_string(settings.yMax);
|
||||||
|
|
||||||
if (!file::savePropertiesFile(getSettingsFilePath(), map)) {
|
auto settings_path = getSettingsFilePath();
|
||||||
|
if (!file::findOrCreateParentDirectory(settings_path, 0755)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file::savePropertiesFile(settings_path, map)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include <Tactility/settings/TrackballSettings.h>
|
#include <Tactility/settings/TrackballSettings.h>
|
||||||
|
#include <Tactility/file/File.h>
|
||||||
#include <Tactility/file/PropertiesFile.h>
|
#include <Tactility/file/PropertiesFile.h>
|
||||||
#include <Tactility/Paths.h>
|
#include <Tactility/Paths.h>
|
||||||
|
|
||||||
@@ -23,8 +24,13 @@ constexpr uint8_t MIN_POINTER_SENSITIVITY = 1;
|
|||||||
constexpr uint8_t MAX_POINTER_SENSITIVITY = 10;
|
constexpr uint8_t MAX_POINTER_SENSITIVITY = 10;
|
||||||
|
|
||||||
bool load(TrackballSettings& settings) {
|
bool load(TrackballSettings& settings) {
|
||||||
|
auto settings_path = getSettingsFilePath();
|
||||||
|
if (!file::isFile(settings_path)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
std::map<std::string, std::string> map;
|
std::map<std::string, std::string> map;
|
||||||
if (!file::loadPropertiesFile(getSettingsFilePath(), map)) {
|
if (!file::loadPropertiesFile(settings_path, map)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,7 +90,11 @@ bool save(const TrackballSettings& settings) {
|
|||||||
map[KEY_TRACKBALL_MODE] = (settings.trackballMode == TrackballMode::Pointer) ? "1" : "0";
|
map[KEY_TRACKBALL_MODE] = (settings.trackballMode == TrackballMode::Pointer) ? "1" : "0";
|
||||||
map[KEY_ENCODER_SENSITIVITY] = std::to_string(std::clamp(settings.encoderSensitivity, MIN_ENCODER_SENSITIVITY, MAX_ENCODER_SENSITIVITY));
|
map[KEY_ENCODER_SENSITIVITY] = std::to_string(std::clamp(settings.encoderSensitivity, MIN_ENCODER_SENSITIVITY, MAX_ENCODER_SENSITIVITY));
|
||||||
map[KEY_POINTER_SENSITIVITY] = std::to_string(std::clamp(settings.pointerSensitivity, MIN_POINTER_SENSITIVITY, MAX_POINTER_SENSITIVITY));
|
map[KEY_POINTER_SENSITIVITY] = std::to_string(std::clamp(settings.pointerSensitivity, MIN_POINTER_SENSITIVITY, MAX_POINTER_SENSITIVITY));
|
||||||
return file::savePropertiesFile(getSettingsFilePath(), map);
|
auto settings_path = getSettingsFilePath();
|
||||||
|
if (!file::findOrCreateParentDirectory(settings_path, 0755)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return file::savePropertiesFile(settings_path, map);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,8 +90,13 @@ static bool isEmptyCredential(const std::string& value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool load(WebServerSettings& settings) {
|
bool load(WebServerSettings& settings) {
|
||||||
|
auto settings_path = getSettingsFilePath();
|
||||||
|
if (!file::isFile(settings_path)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
std::map<std::string, std::string> map;
|
std::map<std::string, std::string> map;
|
||||||
if (!file::loadPropertiesFile(getSettingsFilePath(), map)) {
|
if (!file::loadPropertiesFile(settings_path, map)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,7 +262,12 @@ bool save(const WebServerSettings& settings) {
|
|||||||
map[KEY_WEBSERVER_USERNAME] = settings.webServerUsername;
|
map[KEY_WEBSERVER_USERNAME] = settings.webServerUsername;
|
||||||
map[KEY_WEBSERVER_PASSWORD] = settings.webServerPassword;
|
map[KEY_WEBSERVER_PASSWORD] = settings.webServerPassword;
|
||||||
|
|
||||||
return file::savePropertiesFile(getSettingsFilePath(), map);
|
auto settings_path = getSettingsFilePath();
|
||||||
|
if (!file::findOrCreateParentDirectory(settings_path, 0755)) {
|
||||||
|
LOGGER.error("Failed to create parent dir for {}", settings_path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return file::savePropertiesFile(settings_path, map);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
bool root_is_model(const struct Device* device, const char* buffer) {
|
bool root_is_model(const Device* device, const char* buffer) {
|
||||||
auto* config = static_cast<const RootConfig*>(device->config);
|
auto* config = static_cast<const RootConfig*>(device->config);
|
||||||
return strcmp(config->model, buffer) == 0;
|
return strcmp(config->model, buffer) == 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user