From 4e828dd0a6df490116eb0b69a3114086d1faf0ca Mon Sep 17 00:00:00 2001 From: Ken Van Hoeylandt Date: Tue, 13 Oct 2026 18:30:51 +0200 Subject: [PATCH] Fixes --- Documentation/ideas.md | 11 ++++++++--- Modules/location-module/source/gps_provider.cpp | 7 ++----- Modules/location-module/source/ip_provider.cpp | 12 +++++------- .../tests/source/ip_provider_test.cpp | 4 ++++ TactilityKernel/source/concurrent/thread.cpp | 6 ++++-- 5 files changed, 23 insertions(+), 17 deletions(-) diff --git a/Documentation/ideas.md b/Documentation/ideas.md index 689681620..2554b6437 100644 --- a/Documentation/ideas.md +++ b/Documentation/ideas.md @@ -7,11 +7,14 @@ ## Higher Priority -- Create location service, which works with location providers. GPS is a location provider. IP is also a location provider (must have wifi connected) - - Use GPS time to set/update the current time: when enabled, at boot, when wifi is not enabled, start the gps device and wait until a lock is acquired, then get the time and sync it. Then stop the GPS device. +- https://github.com/TactilityProject/Tactility/blob/main/LICENSE.md#overview -> put overview table on top +- Fix Wi-Fi so it actually connects to something +- Support http/https on posix +- Vi apps are both named "Vi" in the App Hub +- Use GPS time to set/update the current time: when enabled, at boot, when wifi is not enabled, start the gps device and wait until a lock is acquired, then get the time and sync it. Then stop the GPS device. - lvgl-module's spinner relies on hard-coded spinner asset from Tactility main project, should be sized based on DPI - Cursor is hard-coded, needs size based on DPI -- Improve Setup: Show progress of some kind +- Improve Setup: Show progress of some kind. Put skip/next buttons closer? And add outline button style (update docs!) - Files app should have a page indicator like Launcher. - Move Bluetooth code from kernel to Modules/bluetooth-module, and have includes in path bluetooth/ then make a bluetooth_command.cpp like wifi_command.cpp - Inspect code for memory improvements: replace `std::*` with `tt::std::*` @@ -30,6 +33,8 @@ ## Medium Priority +- CL-32 display driver performance +- Move more window management code from Tactility/ to window-manager - Implement signal/raise for terminal apps (for CTRL+C and window resize signals) - Build the simulator with `-fno-exceptions` to match ESP32. First make app exit() work without C++ unwinding there: scheduler.cpp's DeferredUnload relies on pthread_exit()'s forced unwind running its destructor. diff --git a/Modules/location-module/source/gps_provider.cpp b/Modules/location-module/source/gps_provider.cpp index 1d7b9981f..62fbfc250 100644 --- a/Modules/location-module/source/gps_provider.cpp +++ b/Modules/location-module/source/gps_provider.cpp @@ -21,7 +21,6 @@ namespace { constexpr uint32_t AWAIT_TIMEOUT_MS = 250; constexpr uint32_t DEVICE_POLL_INTERVAL_MS = 1000; -constexpr uint32_t THREAD_STOP_TIMEOUT_MS = 3000; constexpr float METERS_PER_SECOND_PER_KNOT = 0.514444f; // Typical user equivalent range error of consumer receivers, which turns dilution of precision into meters constexpr float RANGE_ERROR_METERS = 5.0f; @@ -203,10 +202,8 @@ void* create(LocationHost* host) { void destroy(void*) { provider.running = false; - if (thread_join(provider.thread, millis_to_ticks(THREAD_STOP_TIMEOUT_MS), millis_to_ticks(10)) != ERROR_NONE) { - LOG_E(TAG, "Thread did not stop in time"); - return; - } + // The provider state can only be reused after the worker exits. It does so within AWAIT_TIMEOUT_MS. + thread_join(provider.thread, portMAX_DELAY, millis_to_ticks(10)); device_listener_remove(onDeviceEvent, nullptr); thread_free(provider.thread); provider.thread = nullptr; diff --git a/Modules/location-module/source/ip_provider.cpp b/Modules/location-module/source/ip_provider.cpp index 53630d3bc..ec0994d51 100644 --- a/Modules/location-module/source/ip_provider.cpp +++ b/Modules/location-module/source/ip_provider.cpp @@ -40,8 +40,6 @@ constexpr uint64_t MIN_INTERVAL_MS = 10 * 1000; constexpr uint32_t CHECK_INTERVAL_MS = 10 * 1000; constexpr int SOCKET_TIMEOUT_SECONDS = 5; constexpr size_t RESPONSE_MAX_LENGTH = 1024; -// DNS resolution has no timeout of its own -constexpr uint32_t THREAD_STOP_TIMEOUT_MS = 30 * 1000; // IP geolocation is accurate to roughly city level constexpr float ACCURACY_METERS = 25000.0f; @@ -264,10 +262,8 @@ void* create(LocationHost* host) { void destroy(void*) { provider.running = false; task_event_group_signal(&provider.group, provider.stopBit); - if (thread_join(provider.thread, millis_to_ticks(THREAD_STOP_TIMEOUT_MS), millis_to_ticks(10)) != ERROR_NONE) { - LOG_E(TAG, "Thread did not stop in time"); - return; - } + // The provider state can only be reused after the worker exits. All its blocking calls time out. + thread_join(provider.thread, portMAX_DELAY, millis_to_ticks(10)); thread_free(provider.thread); provider.thread = nullptr; system_event_unsubscribe(&provider.networkSubscription); @@ -347,7 +343,9 @@ bool ip_api_parse_response(const char* response, IpApiResponse* out) { char* longitudeEnd; out->latitude = strtod(latitude.c_str(), &latitudeEnd); out->longitude = strtod(longitude.c_str(), &longitudeEnd); - if (latitude.empty() || *latitudeEnd != '\0' || longitude.empty() || *longitudeEnd != '\0') { + if (latitude.empty() || *latitudeEnd != '\0' || longitude.empty() || *longitudeEnd != '\0' || + !std::isfinite(out->latitude) || !std::isfinite(out->longitude) || + std::fabs(out->latitude) > 90.0 || std::fabs(out->longitude) > 180.0) { return false; } out->success = true; diff --git a/Modules/location-module/tests/source/ip_provider_test.cpp b/Modules/location-module/tests/source/ip_provider_test.cpp index 93ce612a1..34c1c5862 100644 --- a/Modules/location-module/tests/source/ip_provider_test.cpp +++ b/Modules/location-module/tests/source/ip_provider_test.cpp @@ -50,4 +50,8 @@ TEST_CASE("ip_api_parse_response rejects malformed responses") { CHECK_FALSE(ip_api_parse_response("HTTP/1.1 200 OK\r\nX-Rl: 1\r\n", &parsed)); CHECK_FALSE(ip_api_parse_response("HTTP/1.1 200 OK\r\n\r\nsuccess\nabc\n1.0\n", &parsed)); CHECK_FALSE(ip_api_parse_response("HTTP/1.1 200 OK\r\n\r\nunknown\n", &parsed)); + CHECK_FALSE(ip_api_parse_response("HTTP/1.1 200 OK\r\n\r\nsuccess\nnan\n1.0\n", &parsed)); + CHECK_FALSE(ip_api_parse_response("HTTP/1.1 200 OK\r\n\r\nsuccess\n1.0\ninf\n", &parsed)); + CHECK_FALSE(ip_api_parse_response("HTTP/1.1 200 OK\r\n\r\nsuccess\n90.5\n1.0\n", &parsed)); + CHECK_FALSE(ip_api_parse_response("HTTP/1.1 200 OK\r\n\r\nsuccess\n1.0\n-180.5\n", &parsed)); } diff --git a/TactilityKernel/source/concurrent/thread.cpp b/TactilityKernel/source/concurrent/thread.cpp index 522659f30..d863c7d20 100644 --- a/TactilityKernel/source/concurrent/thread.cpp +++ b/TactilityKernel/source/concurrent/thread.cpp @@ -81,8 +81,8 @@ static void thread_main_body(void* context) { vTaskSetThreadLocalStoragePointer(nullptr, LOCAL_STORAGE_SELF_POINTER_INDEX, nullptr); // The thread can be freed once taskHandle is cleared - const bool createdWithCaps = thread->createdWithCaps; thread->lock(); + const bool createdWithCaps = thread->createdWithCaps; thread->taskHandle = nullptr; thread->unlock(); @@ -223,7 +223,9 @@ error_t thread_start(Thread* thread) { affinity != -1 ? affinity : tskNO_AFFINITY, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT ); - thread->createdWithCaps = (result == pdPASS); + if (result != pdPASS) { + thread->createdWithCaps = false; + } } #endif if (result == pdPASS) {