Fixes and improvements (#616)

This commit is contained in:
Ken Van Hoeylandt
2026-08-18 20:43:07 +02:00
committed by GitHub
parent f943c4dd69
commit b03759a111
141 changed files with 899 additions and 221 deletions
+3
View File
@@ -25,6 +25,7 @@
#include <Tactility/bluetooth/Bluetooth.h>
#include <Tactility/file/File.h>
#include <Tactility/hal/SdCard.h>
#include <Tactility/lvgl/KeyboardDeviceListener.h>
#include <Tactility/lvgl/Statusbar.h>
#include <Tactility/lvgl/TrackballInit.h>
#include <Tactility/lvgl/UsbHidInput.h>
@@ -441,6 +442,7 @@ static void onLvglStarted() {
#endif
lvgl::startUsbHidInput();
lvgl::startKeyboardDeviceListener();
lvgl::initTrackball();
#ifdef CONFIG_TT_TOUCH_CALIBRATION_SUPPORTED
@@ -457,6 +459,7 @@ static void onLvglStopped() {
module_stop(&lvgl_window_manager_module);
lvgl::stopKeyboardDeviceListener();
lvgl::stopUsbHidInput();
#if TT_FEATURE_SCREENSHOT_ENABLED
+1 -1
View File
@@ -237,7 +237,7 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
} // namespace
extern const ::AppManifest manifest = {
.id = "AddGps",
.id = "tactility.addgps",
.name = "Add GPS",
.category = APP_CATEGORY_SYSTEM,
.location = { APP_LOCATION_MEMORY, reinterpret_cast<void*>(appMain) },
@@ -152,7 +152,7 @@ uint32_t start(uint32_t callerAppInstanceId, const std::string& title, const std
}
extern const ::AppManifest manifest = {
.id = "AlertDialog",
.id = "tactility.alertdialog",
.name = "Alert Dialog",
.category = APP_CATEGORY_SYSTEM,
.location = { APP_LOCATION_MEMORY, reinterpret_cast<void*>(appMain) },
@@ -162,7 +162,7 @@ void start(const std::string& appId) {
}
extern const ::AppManifest manifest = {
.id = "AppDetails",
.id = "tactility.appdetails",
.name = "App Details",
.category = APP_CATEGORY_SYSTEM,
.location = { APP_LOCATION_MEMORY, reinterpret_cast<void*>(appMain) },
+33 -7
View File
@@ -36,8 +36,11 @@ struct Context {
lv_obj_t* contentWrapper = nullptr;
lv_obj_t* refreshButton = nullptr;
std::string cachedAppsJsonFile = std::format("{}/app_hub.json", getTempPath());
std::vector<AppHubEntry> entries;
AppHubEntryList entries;
Mutex mutex;
// Survives across a bury/resurface cycle (e.g. opening AppHubDetailsApp and returning),
int32_t scrollY = 0;
};
@@ -46,10 +49,6 @@ void refresh(Context* ctx);
void onBackPressed(lv_event_t* event) {
auto* ctx = static_cast<Context*>(lv_event_get_user_data(event));
// Async, non-blocking - must NOT call app_manager_stop() directly here: that bound-waits
// (thread_join) for this app's own thread to finish, which needs the LVGL lock
// (window_manager_remove()) - but this callback runs ON the LVGL task, which would
// deadlock against itself.
AppEvent closeEvent { .type = APP_EVENT_CLOSE, .timestamp = 0, .result = {} };
app_event_emit(ctx->appInstanceId, &closeEvent);
}
@@ -86,9 +85,25 @@ void showNoInternet(Context* ctx) {
}
void showApps(Context* ctx) {
// Refresh rebuilds the list from scratch (cached copy, then again once the network fetch
// lands), which would otherwise reset the user's scroll position each time.
auto scrollY = lv_obj_get_scroll_y(ctx->contentWrapper);
lv_obj_clean(ctx->contentWrapper);
ctx->mutex.lock();
if (parseJson(ctx->cachedAppsJsonFile, ctx->entries)) {
// An empty targetPlatforms list means the entry runs everywhere; otherwise it must name
// this build's own target to be installable here. The simulator isn't a real MCU target,
// so it has nothing to match against and skips this filter entirely.
std::erase_if(ctx->entries, [](const AppHubEntry& entry) {
#ifdef ESP_PLATFORM
return !entry.targetPlatforms.empty() &&
std::ranges::find(entry.targetPlatforms, CONFIG_IDF_TARGET) == entry.targetPlatforms.end();
#else
(void)entry;
return false;
#endif
});
std::ranges::sort(ctx->entries, [](auto left, auto right) {
return left.appName < right.appName;
});
@@ -106,6 +121,8 @@ void showApps(Context* ctx) {
lv_obj_set_user_data(entry_button, int_as_voidptr);
lv_obj_add_event_cb(entry_button, onAppPressed, LV_EVENT_SHORT_CLICKED, ctx);
}
lv_obj_scroll_to_y(ctx->contentWrapper, scrollY, LV_ANIM_OFF);
} else {
showRefreshFailedError(ctx, "Failed to load content");
}
@@ -169,6 +186,15 @@ void createWidgets(lv_obj_t* parent, void* userData) {
lv_obj_set_style_pad_ver(ctx->contentWrapper, 0, LV_STATE_DEFAULT);
refresh(ctx);
lv_obj_scroll_to_y(ctx->contentWrapper, ctx->scrollY, LV_ANIM_OFF);
}
void destroyWidgets(void* userData) {
auto* ctx = static_cast<Context*>(userData);
ctx->scrollY = lv_obj_get_scroll_y(ctx->contentWrapper);
ctx->contentWrapper = nullptr;
ctx->refreshButton = nullptr;
}
int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
@@ -179,7 +205,7 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
sub.app_instance_id = appInstanceId;
app_event_subscribe(&sub);
WindowId window = window_manager_create(appInstanceId, createWidgets, &ctx);
WindowId window = window_manager_create_ext(appInstanceId, createWidgets, destroyWidgets, &ctx);
bool shouldClose = false;
while (!shouldClose) {
@@ -206,7 +232,7 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
} // namespace
extern const ::AppManifest manifest = {
.id = "AppHub",
.id = "tactility.apphub",
.name = "App Hub",
.category = APP_CATEGORY_SYSTEM,
.location = { APP_LOCATION_MEMORY, reinterpret_cast<void*>(appMain) }
+1 -1
View File
@@ -20,7 +20,7 @@ static bool parseEntry(const cJSON* object, AppHubEntry& entry) {
reader.readStringArray("targetPlatforms", entry.targetPlatforms);
}
bool parseJson(const std::string& filePath, std::vector<AppHubEntry>& entries) {
bool parseJson(const std::string& filePath, AppHubEntryList& entries) {
file::FileMutexGuard guard(filePath);
auto data = file::readString(filePath);
@@ -1,7 +1,3 @@
#include "../../../../Modules/app-module/private/app/private/app_ledger.h"
#include "app/metadata.h"
#include <Tactility/DeprecatedPaths.h>
#include <Tactility/StringUtils.h>
#include <Tactility/app/alertdialog/AlertDialog.h>
@@ -12,6 +8,7 @@
#include <app/event.h>
#include <app/install.h>
#include <app/metadata.h>
#include <app/manager.h>
#include <app/manifest.h>
@@ -64,10 +61,6 @@ uint32_t showConfirmDialog(Context* ctx, const char* action) {
void onBackPressed(lv_event_t* e) {
auto* ctx = static_cast<Context*>(lv_event_get_user_data(e));
// Async, non-blocking - must NOT call app_manager_stop() directly here: that bound-waits
// (thread_join) for this app's own thread to finish, which needs the LVGL lock
// (window_manager_remove()) - but this callback runs ON the LVGL task, which would
// deadlock against itself.
AppEvent closeEvent { .type = APP_EVENT_CLOSE, .timestamp = 0, .result = {} };
app_event_emit(ctx->appInstanceId, &closeEvent);
}
@@ -308,7 +301,7 @@ void start(const apphub::AppHubEntry& entry) {
}
extern const ::AppManifest manifest = {
.id = "AppHubDetails",
.id = "tactility.apphubdetails",
.name = "App Details",
.category = APP_CATEGORY_SYSTEM,
.location = { APP_LOCATION_MEMORY, reinterpret_cast<void*>(appMain) },
+1 -1
View File
@@ -106,7 +106,7 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
} // namespace
extern const ::AppManifest manifest = {
.id = "AppList",
.id = "tactility.applist",
.name = "Apps",
.category = APP_CATEGORY_SYSTEM,
.location = { APP_LOCATION_MEMORY, reinterpret_cast<void*>(appMain) },
@@ -121,7 +121,7 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
} // namespace
extern const ::AppManifest manifest = {
.id = "AppSettings",
.id = "tactility.appsettings",
.name = "Apps",
.category = APP_CATEGORY_SETTINGS,
.location = { APP_LOCATION_MEMORY, reinterpret_cast<void*>(appMain) }
@@ -176,7 +176,7 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
} // namespace
extern const ::AppManifest manifest = {
.id = "ApWebServer",
.id = "tactility.apwebserver",
.name = "AP Web Server",
.category = APP_CATEGORY_SYSTEM,
.location = { APP_LOCATION_MEMORY, reinterpret_cast<void*>(appMain) },
@@ -251,7 +251,7 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
} // namespace
extern const ::AppManifest manifest = {
.id = "AudioSettings",
.id = "tactility.audiosettings",
.name = "Audio",
.category = APP_CATEGORY_SETTINGS,
.location = { APP_LOCATION_MEMORY, reinterpret_cast<void*>(appMain) }
+1 -1
View File
@@ -326,7 +326,7 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
} // namespace
extern const ::AppManifest manifest = {
.id = "Boot",
.id = "tactility.boot",
.name = "Boot",
.category = APP_CATEGORY_SYSTEM,
.location = { APP_LOCATION_MEMORY, reinterpret_cast<void*>(appMain) },
+1 -1
View File
@@ -283,7 +283,7 @@ uint32_t start() {
}
extern const ::AppManifest manifest = {
.id = "BtManage",
.id = "tactility.btmanage",
.name = "Bluetooth",
.category = APP_CATEGORY_SETTINGS,
.location = { APP_LOCATION_MEMORY, reinterpret_cast<void*>(appMain) }
@@ -256,7 +256,7 @@ void start(const std::string& addrHex) {
}
extern const ::AppManifest manifest = {
.id = "BtPeerSettings",
.id = "tactility.btpeersettings",
.name = "BT Device Settings",
.category = APP_CATEGORY_SYSTEM,
.location = { APP_LOCATION_MEMORY, reinterpret_cast<void*>(appMain) },
+1 -1
View File
@@ -219,7 +219,7 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
} // namespace
extern const ::AppManifest manifest = {
.id = "Chat",
.id = "tactility.chat",
.name = "Chat",
.category = APP_CATEGORY_USER,
.location = { APP_LOCATION_MEMORY, reinterpret_cast<void*>(appMain) }
+6 -2
View File
@@ -7,10 +7,10 @@
#include <Tactility/app/chat/ChatSettings.h>
#include <Tactility/app/chat/ChatProtocol.h>
#include <Tactility/DeprecatedPaths.h>
#include <Tactility/file/File.h>
#include <Tactility/file/PropertiesFile.h>
#include <app/paths.h>
#include <crypt/crypt.h>
#include <esp_random.h>
@@ -28,7 +28,11 @@ namespace tt::app::chat {
constexpr auto* TAG = "ChatSettings";
static std::string getSettingsFilePath() {
return getUserDataPath() + "/settings/chat.properties";
char path[256];
if (app_paths_get_user_data_path("tactility.chat", "chat.properties", path, sizeof(path)) != ERROR_NONE) {
return "";
}
return path;
}
constexpr auto* KEY_SENDER_ID = "senderId";
@@ -199,7 +199,7 @@ void start() {
}
extern const ::AppManifest manifest = {
.id = "CrashDiagnostics",
.id = "tactility.crashdiagnostics",
.name = "Crash Diagnostics",
.category = APP_CATEGORY_SYSTEM,
.location = { APP_LOCATION_MEMORY, reinterpret_cast<void*>(appMain) },
@@ -226,7 +226,7 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
} // namespace
extern const ::AppManifest manifest = {
.id = "Development",
.id = "tactility.development",
.name = "Development",
.category = APP_CATEGORY_SETTINGS,
.location = { APP_LOCATION_MEMORY, reinterpret_cast<void*>(appMain) }
+1 -1
View File
@@ -66,7 +66,7 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
} // namespace
extern const ::AppManifest manifest = {
.id = "Files",
.id = "tactility.files",
.name = "Files",
.category = APP_CATEGORY_SYSTEM,
.location = { APP_LOCATION_MEMORY, reinterpret_cast<void*>(appMain) },
@@ -106,7 +106,7 @@ uint32_t startForExistingOrNewFile(uint32_t callerAppInstanceId) {
}
extern const ::AppManifest manifest = {
.id = "FileSelection",
.id = "tactility.fileselection",
.name = "File Selection",
.category = APP_CATEGORY_SYSTEM,
.location = { APP_LOCATION_MEMORY, reinterpret_cast<void*>(appMain) },
@@ -320,7 +320,7 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
} // namespace
extern const ::AppManifest manifest = {
.id = "GpsSettings",
.id = "tactility.gpssettings",
.name = "GPS",
.category = APP_CATEGORY_SETTINGS,
.location = { APP_LOCATION_MEMORY, reinterpret_cast<void*>(appMain) }
@@ -124,7 +124,7 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
} // namespace
extern const ::AppManifest manifest = {
.id = "GroveSettings",
.id = "tactility.grovesettings",
.name = "Grove",
.category = APP_CATEGORY_SETTINGS,
.location = { APP_LOCATION_MEMORY, reinterpret_cast<void*>(appMain) }
@@ -419,7 +419,7 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
} // namespace
extern const ::AppManifest manifest = {
.id = "I2cScanner",
.id = "tactility.i2cscanner",
.name = "I2C Scanner",
.category = APP_CATEGORY_SYSTEM,
.location = { APP_LOCATION_MEMORY, reinterpret_cast<void*>(appMain) }
@@ -126,7 +126,7 @@ void start(const std::string& file) {
}
extern const ::AppManifest manifest = {
.id = "ImageViewer",
.id = "tactility.imageviewer",
.name = "Image Viewer",
.category = APP_CATEGORY_SYSTEM,
.location = { APP_LOCATION_MEMORY, reinterpret_cast<void*>(appMain) },
@@ -148,7 +148,7 @@ std::string getLastText() {
}
extern const ::AppManifest manifest = {
.id = "InputDialog",
.id = "tactility.inputdialog",
.name = "Input Dialog",
.category = APP_CATEGORY_SYSTEM,
.location = { APP_LOCATION_MEMORY, reinterpret_cast<void*>(appMain) },
@@ -340,7 +340,7 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
} // namespace
extern const ::AppManifest manifest = {
.id = "Display",
.id = "tactility.display",
.name = "Display",
.category = APP_CATEGORY_SETTINGS,
.location = { APP_LOCATION_MEMORY, reinterpret_cast<void*>(appMain) }
@@ -241,7 +241,7 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
} // namespace
extern const ::AppManifest manifest = {
.id = "KeyboardSettings",
.id = "tactility.keyboardsettings",
.name = "Keyboard",
.category = APP_CATEGORY_SETTINGS,
.location = { APP_LOCATION_MEMORY, reinterpret_cast<void*>(appMain) }
+5 -5
View File
@@ -168,9 +168,9 @@ void createWidgets(lv_obj_t* parent, void*) {
? computeButtonMargin(lv_display_get_horizontal_resolution(display), total_button_size)
: computeButtonMargin(lv_display_get_vertical_resolution(display), total_button_size);
auto* app_list_button = createAppButton(buttons_wrapper, ui_density, LVGL_ICON_LAUNCHER_APPS, "AppList", margin, is_landscape_display);
createAppButton(buttons_wrapper, ui_density, LVGL_ICON_LAUNCHER_FOLDER, "Files", margin, is_landscape_display);
createAppButton(buttons_wrapper, ui_density, LVGL_ICON_LAUNCHER_SETTINGS, "Settings", margin, is_landscape_display);
auto* app_list_button = createAppButton(buttons_wrapper, ui_density, LVGL_ICON_LAUNCHER_APPS, "tactility.applist", margin, is_landscape_display);
createAppButton(buttons_wrapper, ui_density, LVGL_ICON_LAUNCHER_FOLDER, "tactility.files", margin, is_landscape_display);
createAppButton(buttons_wrapper, ui_density, LVGL_ICON_LAUNCHER_SETTINGS, "tactility.settings", margin, is_landscape_display);
// The launcher's container is several levels below the screen, and LVGL only sends
// LV_EVENT_SIZE_CHANGED to the screen object itself on a resolution change - so the
@@ -184,7 +184,7 @@ void createWidgets(lv_obj_t* parent, void*) {
auto* power_button = lv_button_create(parent);
lv_obj_set_style_pad_all(power_button, 8, 0);
lv_obj_align(power_button, LV_ALIGN_BOTTOM_MID, 0, -10);
lv_obj_add_event_cb(power_button, onAppPressed, LV_EVENT_SHORT_CLICKED, (void*)"PowerOff");
lv_obj_add_event_cb(power_button, onAppPressed, LV_EVENT_SHORT_CLICKED, (void*)"tactility.poweroff");
lv_obj_set_style_shadow_width(power_button, 0, LV_STATE_DEFAULT);
lv_obj_set_style_bg_opa(power_button, 0, LV_PART_MAIN);
@@ -261,7 +261,7 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
} // namespace
extern const ::AppManifest manifest = {
.id = "Launcher",
.id = "tactility.launcher",
.name = "Launcher",
.category = APP_CATEGORY_SYSTEM,
.location = { APP_LOCATION_MEMORY, reinterpret_cast<void*>(appMain) },
@@ -176,7 +176,7 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
} // namespace
extern const ::AppManifest manifest = {
.id = "LocaleSettings",
.id = "tactility.localesettings",
.name = "Region & Language",
.category = APP_CATEGORY_SETTINGS,
.location = { APP_LOCATION_MEMORY, reinterpret_cast<void*>(appMain) }
+1 -1
View File
@@ -249,7 +249,7 @@ void start(const std::string& filePath) {
}
extern const ::AppManifest manifest = {
.id = "Notes",
.id = "tactility.notes",
.name = "Notes",
.category = APP_CATEGORY_USER,
.location = { APP_LOCATION_MEMORY, reinterpret_cast<void*>(appMain) }
+1 -1
View File
@@ -273,7 +273,7 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
} // namespace
extern const ::AppManifest manifest = {
.id = "Power",
.id = "tactility.power",
.name = "Power",
.category = APP_CATEGORY_SETTINGS,
.location = { APP_LOCATION_MEMORY, reinterpret_cast<void*>(appMain) }
+1 -1
View File
@@ -167,7 +167,7 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
} // namespace
extern const ::AppManifest manifest = {
.id = "PowerOff",
.id = "tactility.poweroff",
.name = "Power Off",
.category = APP_CATEGORY_SYSTEM,
.location = { APP_LOCATION_MEMORY, reinterpret_cast<void*>(appMain) },
@@ -285,7 +285,7 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
} // namespace
extern const ::AppManifest manifest = {
.id = "Screenshot",
.id = "tactility.screenshot",
.name = "Screenshot",
.category = APP_CATEGORY_SYSTEM,
.location = { APP_LOCATION_MEMORY, reinterpret_cast<void*>(appMain) }
@@ -150,7 +150,7 @@ AppInstanceId start(AppInstanceId callerAppInstanceId, const std::string& title,
}
extern const AppManifest manifest = {
.id = "SelectionDialog",
.id = "tactility.selectiondialog",
.name = "Selection Dialog",
.category = APP_CATEGORY_SYSTEM,
.location = { APP_LOCATION_MEMORY, reinterpret_cast<void*>(appMain) },
+1 -1
View File
@@ -104,7 +104,7 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
} // namespace
extern const ::AppManifest manifest = {
.id = "Settings",
.id = "tactility.settings",
.name = "Settings",
.category = APP_CATEGORY_SYSTEM,
.location = { APP_LOCATION_MEMORY, reinterpret_cast<void*>(appMain) },
+1 -1
View File
@@ -271,7 +271,7 @@ void start() {
}
extern const ::AppManifest manifest = {
.id = "Setup",
.id = "tactility.setup",
.name = "Setup",
.category = APP_CATEGORY_SYSTEM,
.location = { APP_LOCATION_MEMORY, reinterpret_cast<void*>(appMain) },
@@ -452,7 +452,7 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
} // namespace
extern const ::AppManifest manifest = {
.id = "SystemInfo",
.id = "tactility.systeminfo",
.name = "System Info",
.category = APP_CATEGORY_SYSTEM,
.location = { APP_LOCATION_MEMORY, reinterpret_cast<void*>(appMain) }
@@ -209,7 +209,7 @@ uint32_t start() {
}
extern const ::AppManifest manifest = {
.id = "TimeDateSettings",
.id = "tactility.timedatesettings",
.name = "Time & Date",
.category = APP_CATEGORY_SETTINGS,
.location = { APP_LOCATION_MEMORY, reinterpret_cast<void*>(appMain) }
+1 -1
View File
@@ -283,7 +283,7 @@ std::string getLastCode() {
}
extern const ::AppManifest manifest = {
.id = "TimeZone",
.id = "tactility.timezone",
.name = "Select Time zone",
.category = APP_CATEGORY_SYSTEM,
.location = { APP_LOCATION_MEMORY, reinterpret_cast<void*>(appMain) },
@@ -288,7 +288,7 @@ uint32_t start(uint32_t callerAppInstanceId) {
}
extern const ::AppManifest manifest = {
.id = "TouchCalibration",
.id = "tactility.touchcalibration",
.name = "Touch Calibration",
.category = APP_CATEGORY_SETTINGS,
.location = { APP_LOCATION_MEMORY, reinterpret_cast<void*>(appMain) }
@@ -304,7 +304,7 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
} // namespace
extern const ::AppManifest manifest = {
.id = "TrackballSettings",
.id = "tactility.trackballsettings",
.name = "Trackball",
.category = APP_CATEGORY_SETTINGS,
.location = { APP_LOCATION_MEMORY, reinterpret_cast<void*>(appMain) }
@@ -116,7 +116,7 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
} // namespace
extern const ::AppManifest manifest = {
.id = "UsbSettings",
.id = "tactility.usbsettings",
.name = "USB",
.category = APP_CATEGORY_SETTINGS,
.location = { APP_LOCATION_MEMORY, reinterpret_cast<void*>(appMain) }
@@ -431,7 +431,7 @@ int32_t appMain(uint32_t appInstanceId, int argc, char* argv[]) {
} // namespace
extern const ::AppManifest manifest = {
.id = "WebServerSettings",
.id = "tactility.webserversettings",
.name = "Web Server",
.category = APP_CATEGORY_SYSTEM,
.location = { APP_LOCATION_MEMORY, reinterpret_cast<void*>(appMain) }
@@ -272,7 +272,7 @@ void start(const std::string& ssid) {
}
extern const ::AppManifest manifest = {
.id = "WifiApSettings",
.id = "tactility.wifiapsettings",
.name = "Wi-Fi AP Settings",
.category = APP_CATEGORY_SYSTEM,
.location = { APP_LOCATION_MEMORY, reinterpret_cast<void*>(appMain) },
@@ -360,7 +360,7 @@ void start(const std::string& ssid, const std::string& password) {
}
extern const ::AppManifest manifest = {
.id = "WifiConnect",
.id = "tactility.wificonnect",
.name = "Wi-Fi Connect",
.category = APP_CATEGORY_SYSTEM,
.location = { APP_LOCATION_MEMORY, reinterpret_cast<void*>(appMain) },
@@ -188,7 +188,7 @@ uint32_t start(uint32_t callerAppInstanceId) {
}
extern const ::AppManifest manifest = {
.id = "WifiManage",
.id = "tactility.wifimanage",
.name = "Wi-Fi",
.category = APP_CATEGORY_SETTINGS,
.location = { APP_LOCATION_MEMORY, reinterpret_cast<void*>(appMain) }
@@ -0,0 +1,133 @@
#include <Tactility/lvgl/KeyboardDeviceListener.h>
#include <Tactility/Mutex.h>
#include <tactility/device.h>
#include <tactility/device_listener.h>
#include <tactility/drivers/keyboard.h>
#include <tactility/log.h>
#include <lvgl/devices/keyboard.h>
#include <lvgl/lvgl.h>
#include <vector>
namespace tt::lvgl {
constexpr auto* TAG = "KeyboardDeviceListener";
namespace {
struct KeyboardBinding {
Device* device;
lv_indev_t* indev;
};
Mutex& bindingsMutex() {
static Mutex mutex;
return mutex;
}
std::vector<KeyboardBinding>& bindings() {
static std::vector<KeyboardBinding> list;
return list;
}
// Guards, under bindingsMutex, against onKeyboardDeviceStarted() registering a binding after
// stopKeyboardDeviceListener() has already drained bindings() and returned.
bool& listenerActive() {
static bool active = false;
return active;
}
void onKeyboardDeviceStarted(Device* device) {
// Held for the whole add+register step so a same-device STOPPING can't observe the binding
// as neither-registered-nor-added: onKeyboardDeviceStopped() and shutdown's drain both take
// this same lock, so they see either the fully-registered binding or nothing at all.
auto lock = bindingsMutex().asScopedLock();
lock.lock();
if (!listenerActive()) {
return;
}
lv_indev_t* indev = nullptr;
lvgl_lock();
error_t error = lvgl_keyboard_add(device, lv_display_get_default(), &indev);
lvgl_unlock();
if (error != ERROR_NONE) {
LOG_E(TAG, "failed to bind keyboard device %s to LVGL", device->name);
return;
}
bindings().push_back({ device, indev });
}
void onKeyboardDeviceStopped(Device* device) {
auto lock = bindingsMutex().asScopedLock();
lock.lock();
lv_indev_t* indev = nullptr;
auto& list = bindings();
for (auto it = list.begin(); it != list.end(); ++it) {
if (it->device == device) {
indev = it->indev;
list.erase(it);
break;
}
}
if (indev == nullptr) {
return;
}
lvgl_lock();
lvgl_keyboard_remove(indev);
lvgl_unlock();
}
void onDeviceEvent(Device* device, DeviceEvent event, void* context) {
(void)context;
if (device_get_type(device) != &KEYBOARD_TYPE) {
return;
}
// Detach on STOPPING (before stop_device() frees the device's resources), not STOPPED (which
// only fires after stop_device() already ran) - see DEVICE_EVENT_STOPPING's doc comment.
if (event == DEVICE_EVENT_STARTED) {
onKeyboardDeviceStarted(device);
} else if (event == DEVICE_EVENT_STOPPING) {
onKeyboardDeviceStopped(device);
}
}
} // namespace
void startKeyboardDeviceListener() {
auto lock = bindingsMutex().asScopedLock();
lock.lock();
listenerActive() = true;
device_listener_add(onDeviceEvent, nullptr);
}
void stopKeyboardDeviceListener() {
std::vector<KeyboardBinding> remaining;
{
// device_listener_remove() doesn't wait for an in-flight onDeviceEvent() to finish, so a
// STARTED callback already past this point can still land after we drain below; it takes
// this same lock and checks listenerActive() before registering, so it backs off instead
// of adding a binding nothing will ever remove.
auto lock = bindingsMutex().asScopedLock();
lock.lock();
listenerActive() = false;
device_listener_remove(onDeviceEvent);
remaining = std::move(bindings());
bindings().clear();
}
if (remaining.empty()) {
return;
}
lvgl_lock();
for (auto& binding : remaining) {
lvgl_keyboard_remove(binding.indev);
}
lvgl_unlock();
}
} // namespace tt::lvgl
+18 -6
View File
@@ -18,6 +18,7 @@
#include <lvgl/devices/keyboard.h>
#include <atomic>
#include <new>
namespace tt::lvgl {
@@ -286,12 +287,19 @@ static void usbHidInputTask(void* arg) {
void startUsbHidInput() {
if (s_ctx != nullptr) return;
auto* ctx = new UsbHidInputCtx();
static constexpr MemoryPolicy CTX_POLICY = { 0, MEMORY_CAPABILITY_EXTERNAL, 0 };
auto* ctx_mem = memory_alloc_with_policy(sizeof(UsbHidInputCtx), &CTX_POLICY);
if (!ctx_mem) {
LOG_E(TAG, "failed to allocate context");
return;
}
auto* ctx = new (ctx_mem) UsbHidInputCtx();
ctx->hid_queue = xQueueCreate(HID_EVENT_QUEUE_SIZE, sizeof(UsbHidEvent));
if (!ctx->hid_queue) {
LOG_E(TAG, "failed to create HID event queue");
delete ctx;
ctx->~UsbHidInputCtx();
memory_free(ctx);
return;
}
@@ -299,7 +307,8 @@ void startUsbHidInput() {
if (!ctx->key_queue) {
LOG_E(TAG, "failed to create key event queue");
vQueueDelete(ctx->hid_queue);
delete ctx;
ctx->~UsbHidInputCtx();
memory_free(ctx);
return;
}
@@ -308,7 +317,8 @@ void startUsbHidInput() {
LOG_E(TAG, "failed to create task done semaphore");
vQueueDelete(ctx->hid_queue);
vQueueDelete(ctx->key_queue);
delete ctx;
ctx->~UsbHidInputCtx();
memory_free(ctx);
return;
}
@@ -371,7 +381,8 @@ void startUsbHidInput() {
vQueueDelete(ctx->hid_queue);
vQueueDelete(ctx->key_queue);
vSemaphoreDelete(ctx->task_done);
delete ctx;
ctx->~UsbHidInputCtx();
memory_free(ctx);
return;
}
@@ -429,7 +440,8 @@ void stopUsbHidInput() {
vQueueDelete(ctx->hid_queue);
vQueueDelete(ctx->key_queue);
vSemaphoreDelete(ctx->task_done);
delete ctx;
ctx->~UsbHidInputCtx();
memory_free(ctx);
LOG_I(TAG, "stopped");
}
@@ -329,7 +329,7 @@ std::shared_ptr<AudioService> findAudioService() {
}
extern const ServiceManifest manifest = {
.id = "Audio",
.id = "tactility.audio",
.createService = create<AudioService>
};
@@ -252,7 +252,7 @@ std::shared_ptr<DevelopmentService> findService() {
}
extern const ServiceManifest manifest = {
.id = "Development",
.id = "tactility.development",
.createService = create<DevelopmentService>
};
@@ -1,11 +1,11 @@
#ifdef ESP_PLATFORM
#include <Tactility/DeprecatedPaths.h>
#include <Tactility/file/File.h>
#include <Tactility/file/PropertiesFile.h>
#include <Tactility/service/development/DevelopmentSettings.h>
#include <map>
#include <string>
#include <app/paths.h>
#include <tactility/log.h>
namespace tt::service::development {
@@ -13,7 +13,11 @@ namespace tt::service::development {
constexpr auto* TAG = "DevSettings";
static std::string getSettingsFilePath() {
return getUserDataPath() + "/settings/development.properties";
char path[256];
if (app_paths_get_user_data_path("tactility.development", "development.properties", path, sizeof(path)) != ERROR_NONE) {
return "";
}
return path;
}
constexpr auto* SETTINGS_KEY_ENABLE_ON_BOOT = "enableOnBoot";
@@ -280,12 +280,12 @@ void DisplayIdleService::reloadSettings() {
std::shared_ptr<DisplayIdleService> findService() {
return std::static_pointer_cast<DisplayIdleService>(
findServiceById("DisplayIdle")
findServiceById("tactility.displayidle")
);
}
extern const ServiceManifest manifest = {
.id = "DisplayIdle",
.id = "tactility.displayidle",
.createService = create<DisplayIdleService>
};
@@ -193,7 +193,7 @@ std::shared_ptr<EspNowService> findService() {
}
extern const ServiceManifest manifest = {
.id = "EspNow",
.id = "tactility.espnow",
.createService = create<EspNowService>
};
@@ -113,7 +113,7 @@ public:
};
extern const ServiceManifest manifest = {
.id = "KeyboardIdle",
.id = "tactility.keyboardidle",
.createService = create<KeyboardIdleService>
};
@@ -85,7 +85,7 @@ void MemoryCheckerService::onTimerUpdate() {
}
extern const ServiceManifest manifest = {
.id = "MemoryChecker",
.id = "tactility.memorychecker",
.createService = create<MemoryCheckerService>
};
@@ -140,7 +140,7 @@ void RtcTimeService::onStop(ServiceContext& serviceContext) {
}
extern const ServiceManifest manifest = {
.id = "RtcTime",
.id = "tactility.rtctime",
.createService = create<RtcTimeService>
};
@@ -97,7 +97,7 @@ bool ScreenshotService::isTaskStarted() {
}
extern const ServiceManifest manifest = {
.id = "Screenshot",
.id = "tactility.screenshot",
.createService = create<ScreenshotService>
};
@@ -359,7 +359,7 @@ public:
};
extern const ServiceManifest manifest = {
.id = "Statusbar",
.id = "tactility.statusbar",
.createService = create<StatusbarService>
};
@@ -1797,7 +1797,7 @@ esp_err_t WebServerService::handleAssets(httpd_req_t* request) {
}
extern const ServiceManifest manifest = {
.id = "WebServer",
.id = "tactility.webserver",
.createService = create<WebServerService>
};
+1 -1
View File
@@ -551,7 +551,7 @@ public:
} // namespace
extern const ServiceManifest manifest = {
.id = "wifi",
.id = "tactility.wifi",
.createService = create<WifiService>
};
@@ -2,7 +2,8 @@
#include <Tactility/file/File.h>
#include <Tactility/file/PropertiesFile.h>
#include <Tactility/DeprecatedPaths.h>
#include <app/paths.h>
#include <map>
#include <string>
@@ -11,7 +12,11 @@
namespace tt::settings::display {
static std::string getSettingsFilePath() {
return getUserDataPath() + "/settings/display.properties";
char path[256];
if (app_paths_get_user_data_path("tactility.display", "display.properties", path, sizeof(path)) != ERROR_NONE) {
return "";
}
return path;
}
constexpr auto* SETTINGS_KEY_ORIENTATION = "orientation";
@@ -1,7 +1,8 @@
#include <Tactility/settings/TrackballSettings.h>
#include <Tactility/file/File.h>
#include <Tactility/file/PropertiesFile.h>
#include <Tactility/DeprecatedPaths.h>
#include <app/paths.h>
#include <map>
#include <string>
@@ -10,7 +11,11 @@
namespace tt::settings::trackball {
static std::string getSettingsFilePath() {
return getUserDataPath() + "/settings/trackball.properties";
char path[256];
if (app_paths_get_user_data_path("tactility.trackballsettings", "trackball.properties", path, sizeof(path)) != ERROR_NONE) {
return "";
}
return path;
}
constexpr auto* KEY_TRACKBALL_ENABLED = "trackballEnabled";
@@ -1,8 +1,8 @@
#include <Tactility/DeprecatedPaths.h>
#include <Tactility/file/File.h>
#include <Tactility/file/PropertiesFile.h>
#include <Tactility/settings/WebServerSettings.h>
#include <app/paths.h>
#include <tactility/log.h>
#include <charconv>
@@ -22,7 +22,11 @@ namespace tt::settings::webserver {
constexpr auto* TAG = "WebServerSettings";
static std::string getSettingsFilePath() {
return getUserDataPath() + "/settings/webserver.properties";
char path[256];
if (app_paths_get_user_data_path("tactility.webserversettings", "webserver.properties", path, sizeof(path)) != ERROR_NONE) {
return "";
}
return path;
}
// Property keys