Improvements and fixes (#595)
- Fix BluetoothSettings app not updating when toggling on Bluetooth on - Fix BluetoothSettings app crash when closing while scanning - Fix BluetoothSettings app crash when turning BT off while scanning - WebServer doesn't save config anymore as a side-effect of reading the config - Add missing license headers - Use TactilityKernel's time and delay functions instead of TactilityFreeRtos ones
This commit is contained in:
committed by
GitHub
parent
e6e1dcd0ca
commit
bd108cc3c4
@@ -1,13 +1,13 @@
|
||||
#include "Tactility/app/alertdialog/AlertDialog.h"
|
||||
|
||||
#include <lvgl/widgets/toolbar.h>
|
||||
#include "Tactility/service/loader/Loader.h"
|
||||
|
||||
#include <Tactility/service/loader/Loader.h>
|
||||
#include <Tactility/StringUtils.h>
|
||||
|
||||
#include <lvgl.h>
|
||||
#include <tactility/log.h>
|
||||
|
||||
#include <lvgl.h>
|
||||
#include <lvgl/widgets/toolbar.h>
|
||||
|
||||
namespace tt::app::alertdialog {
|
||||
|
||||
#define PARAMETER_BUNDLE_KEY_TITLE "title"
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include "Tactility/lvgl/Lvgl.h"
|
||||
#include "tactility/drivers/backlight.h"
|
||||
#include "tactility/drivers/display.h"
|
||||
#include <tactility/delay.h>
|
||||
#include <tactility/drivers/backlight.h>
|
||||
#include <tactility/drivers/display.h>
|
||||
#include <tactility/log.h>
|
||||
#include <tactility/time.h>
|
||||
|
||||
#include <Tactility/CpuAffinity.h>
|
||||
#include <Tactility/Paths.h>
|
||||
@@ -10,13 +12,13 @@
|
||||
#include <Tactility/app/AppPaths.h>
|
||||
#include <Tactility/app/alertdialog/AlertDialog.h>
|
||||
#include <Tactility/hal/usb/Usb.h>
|
||||
#include <Tactility/lvgl/Lvgl.h>
|
||||
#include <Tactility/lvgl/Style.h>
|
||||
#include <Tactility/service/loader/Loader.h>
|
||||
#include <Tactility/settings/BootSettings.h>
|
||||
#include <Tactility/settings/DisplaySettings.h>
|
||||
|
||||
#include <lvgl.h>
|
||||
#include <tactility/log.h>
|
||||
|
||||
#include <atomic>
|
||||
|
||||
@@ -108,23 +110,23 @@ class BootApp : public App {
|
||||
}
|
||||
|
||||
static void waitForMinimalSplashDuration(TickType_t startTime) {
|
||||
const auto end_time = kernel::getTicks();
|
||||
const auto end_time = get_ticks();
|
||||
const auto ticks_passed = end_time - startTime;
|
||||
constexpr auto minimum_ticks = (CONFIG_TT_SPLASH_DURATION / portTICK_PERIOD_MS);
|
||||
if (minimum_ticks > ticks_passed) {
|
||||
kernel::delayTicks(minimum_ticks - ticks_passed);
|
||||
delay_ticks(minimum_ticks - ticks_passed);
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t bootThreadCallback() {
|
||||
LOG_I(TAG, "Starting boot thread");
|
||||
const auto start_time = kernel::getTicks();
|
||||
const auto start_time = get_ticks();
|
||||
|
||||
// Give the UI some time to redraw
|
||||
// If we don't do this, various init calls will read files and block SPI IO for the display
|
||||
// This would result in a blank/black screen being shown during this phase of the boot process
|
||||
// This works with 5 ms on a T-Lora Pager, so we give it 10 ms to be safe
|
||||
kernel::delayMillis(10);
|
||||
delay_millis(10);
|
||||
|
||||
// TODO: Support for multiple displays
|
||||
LOG_I(TAG, "Setup display");
|
||||
|
||||
@@ -23,10 +23,20 @@ static void onBtToggled(bool requestOn) {
|
||||
bool radio_on = bluetooth::isRadioOnOrPending(dev);
|
||||
if (requestOn && !radio_on) {
|
||||
LOG_I(TAG, "Turning on");
|
||||
bluetooth::start(dev);
|
||||
if (bluetooth::start(dev)) {
|
||||
// The driver only allocates its callback list once the device is started,
|
||||
// so the registration attempted in onShow() (while radio was off) was a
|
||||
// no-op. Register again now that the device is actually up.
|
||||
auto bt = std::static_pointer_cast<BtManage>(getCurrentApp());
|
||||
bt->registerDeviceCallback(dev);
|
||||
}
|
||||
} else if (!requestOn && radio_on) {
|
||||
LOG_I(TAG, "Turning off");
|
||||
bluetooth::stop(dev);
|
||||
if (bluetooth::stop(dev)) {
|
||||
// A completed stop frees the driver's callback list.
|
||||
auto bt = std::static_pointer_cast<BtManage>(getCurrentApp());
|
||||
bt->forgetCallbackRegistration();
|
||||
}
|
||||
}
|
||||
device_put(dev);
|
||||
} else {
|
||||
@@ -90,13 +100,19 @@ void BtManage::unlock() {
|
||||
}
|
||||
|
||||
void BtManage::requestViewUpdate() {
|
||||
// Lock order must match onShow()/onHide(): both run under GuiService's lvgl_lock()
|
||||
// and then take `mutex` internally. Taking `mutex` before lvgl_lock() here would
|
||||
// invert that order and deadlock against a concurrent onHide()/onShow() (GUI task
|
||||
// holding LVGL lock, waiting on `mutex`; this task holding `mutex`, waiting on LVGL
|
||||
// lock) - exactly what happens when BT events fire rapidly (e.g. during scanning)
|
||||
// while the app is being hidden.
|
||||
lvgl_lock();
|
||||
lock();
|
||||
if (isViewEnabled) {
|
||||
lvgl_lock();
|
||||
view.update();
|
||||
lvgl_unlock();
|
||||
}
|
||||
unlock();
|
||||
lvgl_unlock();
|
||||
}
|
||||
|
||||
void BtManage::onBtEvent(const BtEvent& event) {
|
||||
@@ -148,17 +164,45 @@ static void onKernelBtEvent(Device* /*device*/, void* context, BtEvent event) {
|
||||
// nimble_port_stop), creating a permanent deadlock. Dispatch to the main task so
|
||||
// the NimBLE host task is never blocked by BtManage's state updates or LVGL lock.
|
||||
auto* self = static_cast<BtManage*>(context);
|
||||
getMainDispatcher().dispatch([self, event] {
|
||||
// Captured while `self` is still guaranteed valid (the callback is only invoked
|
||||
// while registered, i.e. before onHide() removes it). Comparing this later - without
|
||||
// dereferencing `self` - lets the dispatched lambda detect a stale event from a
|
||||
// session that has since been hidden (and possibly destroyed) without a UAF.
|
||||
auto generation = self->getGeneration();
|
||||
int expectedGeneration = generation->load();
|
||||
getMainDispatcher().dispatch([self, generation, expectedGeneration, event] {
|
||||
if (generation->load() != expectedGeneration) {
|
||||
return;
|
||||
}
|
||||
self->onBtEvent(event);
|
||||
});
|
||||
}
|
||||
|
||||
void BtManage::registerDeviceCallback(Device* dev) {
|
||||
lock();
|
||||
if (btDevice == dev && !callbackRegistered) {
|
||||
// Only latch the flag on success: while the radio is off the driver has no
|
||||
// callback list yet, so this add is a silent no-op and must be retried once
|
||||
// bluetooth::start() actually brings the device up.
|
||||
if (bluetooth_add_event_callback(dev, this, onKernelBtEvent) == ERROR_NONE) {
|
||||
callbackRegistered = true;
|
||||
}
|
||||
}
|
||||
unlock();
|
||||
}
|
||||
|
||||
void BtManage::forgetCallbackRegistration() {
|
||||
lock();
|
||||
callbackRegistered = false;
|
||||
unlock();
|
||||
}
|
||||
|
||||
void BtManage::onShow(AppContext& app, lv_obj_t* parent) {
|
||||
// Initialise state and view before subscribing to avoid incoming events
|
||||
// racing with state initialisation.
|
||||
state.setRadioState(bluetooth::getRadioState());
|
||||
Device* dev = nullptr;
|
||||
device_get_first_active_by_type(&BLUETOOTH_TYPE, &dev);
|
||||
device_get_first_by_type(&BLUETOOTH_TYPE, &dev);
|
||||
|
||||
state.setScanning(dev ? bluetooth_is_scanning(dev) : false);
|
||||
state.updateScanResults();
|
||||
@@ -177,7 +221,7 @@ void BtManage::onShow(AppContext& app, lv_obj_t* parent) {
|
||||
|
||||
btDevice = dev;
|
||||
if (btDevice) {
|
||||
bluetooth_add_event_callback(btDevice, this, onKernelBtEvent);
|
||||
registerDeviceCallback(btDevice);
|
||||
}
|
||||
|
||||
auto radio_state = bluetooth::getRadioState();
|
||||
@@ -192,9 +236,17 @@ void BtManage::onShow(AppContext& app, lv_obj_t* parent) {
|
||||
}
|
||||
|
||||
void BtManage::onHide(AppContext& app) {
|
||||
// Invalidate any BT event dispatched-but-not-yet-run for this session before doing
|
||||
// anything else, so it can't race a subsequent destruction of this instance (see
|
||||
// onKernelBtEvent()/getGeneration()).
|
||||
generation->fetch_add(1);
|
||||
|
||||
lock();
|
||||
if (btDevice) {
|
||||
bluetooth_remove_event_callback(btDevice, onKernelBtEvent);
|
||||
if (callbackRegistered) {
|
||||
bluetooth_remove_event_callback(btDevice, onKernelBtEvent);
|
||||
callbackRegistered = false;
|
||||
}
|
||||
device_put(btDevice);
|
||||
btDevice = nullptr;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <Tactility/lvgl/Toolbar.h>
|
||||
|
||||
#include <tactility/device.h>
|
||||
#include <tactility/time.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <cstring>
|
||||
@@ -211,7 +212,7 @@ public:
|
||||
GpsSettingsApp() {
|
||||
// Runs while the screen is shown - there's no push notification for GPS device state
|
||||
// changes, so this is the only way this screen finds out about them.
|
||||
timer = std::make_unique<Timer>(Timer::Type::Periodic, kernel::secondsToTicks(1), [this] {
|
||||
timer = std::make_unique<Timer>(Timer::Type::Periodic, seconds_to_ticks(1), [this] {
|
||||
updateDeviceStates();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <tactility/device.h>
|
||||
#include <tactility/drivers/power_supply.h>
|
||||
#include <tactility/time.h>
|
||||
|
||||
#include <lvgl/lvgl.h>
|
||||
#include <lvgl/icons/shared.h>
|
||||
@@ -53,7 +54,7 @@ struct DeviceEntry {
|
||||
|
||||
class PowerApp : public App {
|
||||
|
||||
Timer update_timer = Timer(Timer::Type::Periodic, kernel::millisToTicks(1000),[]() { onTimer(); });
|
||||
Timer update_timer = Timer(Timer::Type::Periodic, millis_to_ticks(1000),[]() { onTimer(); });
|
||||
|
||||
std::vector<DeviceEntry> entries;
|
||||
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
#include <Tactility/TactilityConfig.h>
|
||||
#include <Tactility/lvgl/Toolbar.h>
|
||||
#include <Tactility/Tactility.h>
|
||||
#include <Tactility/Timer.h>
|
||||
#include "tactility/time.h"
|
||||
|
||||
|
||||
#include <Tactility/Paths.h>
|
||||
#include <Tactility/Tactility.h>
|
||||
#include <Tactility/TactilityConfig.h>
|
||||
#include <Tactility/Timer.h>
|
||||
#include <Tactility/lvgl/Toolbar.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
@@ -242,7 +245,7 @@ static std::shared_ptr<SystemInfoApp> optApp() {
|
||||
}
|
||||
|
||||
class SystemInfoApp final : public App {
|
||||
Timer memoryTimer = Timer(Timer::Type::Periodic, kernel::millisToTicks(10000), [] {
|
||||
Timer memoryTimer = Timer(Timer::Type::Periodic, millis_to_ticks(10000), [] {
|
||||
auto app = optApp();
|
||||
if (app) {
|
||||
lvgl_lock();
|
||||
@@ -251,7 +254,7 @@ class SystemInfoApp final : public App {
|
||||
}
|
||||
});
|
||||
|
||||
Timer tasksTimer = Timer(Timer::Type::Periodic, kernel::millisToTicks(15000), [] {
|
||||
Timer tasksTimer = Timer(Timer::Type::Periodic, millis_to_ticks(15000), [] {
|
||||
auto app = optApp();
|
||||
if (app) {
|
||||
lvgl_lock();
|
||||
|
||||
Reference in New Issue
Block a user