From 02f799cf9ce31ac234ac38b70e7fb3a510d131ed Mon Sep 17 00:00:00 2001 From: andy Date: Thu, 10 Sep 2026 23:48:11 +0100 Subject: [PATCH] Fix Back being trapped on the connecting popup (all three connect flows) BT Inspector, BLE HID Host and Sensor Dashboard showed a "Connecting..." / "Discovering..." popup whose Back handler ignored the key, so if a connect or service discovery ran long the user was stuck until it timed out. Back on that popup now sets a cancel flag and returns to the scan list immediately; the worker thread notices the flag when its current operation finishes, drops the half-made connection and resumes scanning, and the completion handler ignores the cancelled result instead of jumping into the device/readings view. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01LEaD4ZeAfvWLBUxXp8G11r --- ble_hid_host/ble_hid_host.c | 25 +++++++++++++++++++++---- ble_sensor/ble_sensor.c | 29 +++++++++++++++++++++++------ bt_inspector/bt_inspector.c | 23 ++++++++++++++++++++++- 3 files changed, 66 insertions(+), 11 deletions(-) diff --git a/ble_hid_host/ble_hid_host.c b/ble_hid_host/ble_hid_host.c index c29fbb6..0a2a532 100644 --- a/ble_hid_host/ble_hid_host.c +++ b/ble_hid_host/ble_hid_host.c @@ -84,6 +84,7 @@ typedef struct { Sub subs[SUBS_MAX]; int sub_count; bool connected_ok; + volatile bool cancel; char setup_msg[48]; } App; @@ -95,6 +96,8 @@ typedef struct { // ---- logging ---------------------------------------------------------------- +static App* s_app; // popup Back callback (popup view ctx isn't the App) + static void app_log(App* app, const char* fmt, ...) { if(!app->log) return; DateTime dt; @@ -240,7 +243,13 @@ static int32_t worker_thread(void* ctx) { while(furi_message_queue_get(app->jobs, &job, FuriWaitForever) == FuriStatusOk) { if(job == JobQuit) break; if(job == JobConnect) { - app->connected_ok = bc_connect(app->bc, &app->dev, 8000) && hid_setup(app); + bool ok = bc_connect(app->bc, &app->dev, 8000) && hid_setup(app); + if(app->cancel) { + if(bc_is_connected(app->bc)) bc_disconnect(app->bc); + bc_scan_start(app->bc); + ok = false; + } + app->connected_ok = ok; } else if(job == JobDisconnect) { bc_disconnect(app->bc); bc_scan_start(app->bc); @@ -442,9 +451,11 @@ static uint32_t nav_mon_back(void* ctx) { return ViewScan; } -static uint32_t nav_stay_popup(void* ctx) { +static uint32_t nav_connecting_back(void* ctx) { UNUSED(ctx); - return ViewPopup; + s_app->cancel = true; + s_app->cur_view = ViewScan; + return ViewScan; } // ---- events ----------------------------------------------------------------- @@ -508,12 +519,17 @@ static bool custom_event_cb(void* ctx, uint32_t event) { false); if(have) { app_log(app, "CONNECTING %s", dev_label(&app->dev)); + app->cancel = false; show_popup(app, "Connecting...", dev_label(&app->dev), false); furi_message_queue_put(app->jobs, &(JobType){JobConnect}, 0); } } break; case EvtNotify: on_notify(app); break; case EvtJobDone: + if(app->cancel) { + app->cancel = false; + break; + } if(app->connected_ok) { char hdr[40]; snprintf(hdr, sizeof(hdr), "%s", dev_label(&app->dev)); @@ -554,6 +570,7 @@ int32_t ble_hid_host_app(void* p) { UNUSED(p); App* app = malloc(sizeof(App)); memset(app, 0, sizeof(App)); + s_app = app; app->gui = furi_record_open(RECORD_GUI); app->storage = furi_record_open(RECORD_STORAGE); app->jobs = furi_message_queue_alloc(8, sizeof(JobType)); @@ -582,7 +599,7 @@ int32_t ble_hid_host_app(void* p) { view_dispatcher_add_view(app->vd, ViewMonitor, app->mon_view); app->popup = popup_alloc(); - view_set_previous_callback(popup_get_view(app->popup), nav_stay_popup); + view_set_previous_callback(popup_get_view(app->popup), nav_connecting_back); view_dispatcher_add_view(app->vd, ViewPopup, popup_get_view(app->popup)); app->timer = furi_timer_alloc(timer_cb, FuriTimerTypePeriodic, app); diff --git a/ble_sensor/ble_sensor.c b/ble_sensor/ble_sensor.c index c433f14..61ad0fe 100644 --- a/ble_sensor/ble_sensor.c +++ b/ble_sensor/ble_sensor.c @@ -70,9 +70,12 @@ typedef struct { BcService services[BC_MAX_SERVICES]; BcChar chars[BC_MAX_CHARS]; bool connected_ok; + volatile bool cancel; char setup_msg[40]; } App; +static App* s_app; // for the popup Back callback (popup view ctx isn't the App) + static const char* dev_label(const BcDevice* d) { return d->name[0] ? d->name : "(no name)"; } @@ -139,9 +142,15 @@ static int32_t worker_thread(void* ctx) { JobType job; while(furi_message_queue_get(app->jobs, &job, FuriWaitForever) == FuriStatusOk) { if(job == JobQuit) break; - if(job == JobConnect) - app->connected_ok = bc_connect(app->bc, &app->dev, 8000) && sensor_setup(app); - else if(job == JobDisconnect) { + if(job == JobConnect) { + bool ok = bc_connect(app->bc, &app->dev, 8000) && sensor_setup(app); + if(app->cancel) { // user pressed Back while we were busy + if(bc_is_connected(app->bc)) bc_disconnect(app->bc); + bc_scan_start(app->bc); + ok = false; + } + app->connected_ok = ok; + } else if(job == JobDisconnect) { bc_disconnect(app->bc); bc_scan_start(app->bc); } @@ -288,9 +297,11 @@ static uint32_t scan_exit(void* ctx) { UNUSED(ctx); return VIEW_NONE; } -static uint32_t stay_popup(void* ctx) { +static uint32_t connecting_back(void* ctx) { UNUSED(ctx); - return ViewPopup; + s_app->cancel = true; // worker will drop the half-made connection + s_app->cur = ViewScan; + return ViewScan; } static void popup_cb(void* ctx) { @@ -343,6 +354,7 @@ static bool custom_cb(void* ctx, uint32_t event) { popup_set_context(app->popup, app); popup_set_header(app->popup, "Connecting...", 64, 26, AlignCenter, AlignCenter); popup_set_text(app->popup, dev_label(&app->dev), 64, 40, AlignCenter, AlignCenter); + app->cancel = false; app->cur = ViewPopup; view_dispatcher_switch_to_view(app->vd, ViewPopup); furi_message_queue_put(app->jobs, &(JobType){JobConnect}, 0); @@ -350,6 +362,10 @@ static bool custom_cb(void* ctx, uint32_t event) { } break; case EvtNotify: on_notify(app); break; case EvtJobDone: + if(app->cancel) { // user already backed out to the scan list + app->cancel = false; + break; + } if(app->connected_ok) { with_view_model( app->readings_view, ReadingsModel * m, @@ -383,6 +399,7 @@ int32_t ble_sensor_app(void* p) { UNUSED(p); App* app = malloc(sizeof(App)); memset(app, 0, sizeof(App)); + s_app = app; app->gui = furi_record_open(RECORD_GUI); app->jobs = furi_message_queue_alloc(8, sizeof(JobType)); app->notify_q = furi_message_queue_alloc(16, sizeof(NotifyMsg)); @@ -410,7 +427,7 @@ int32_t ble_sensor_app(void* p) { view_dispatcher_add_view(app->vd, ViewReadings, app->readings_view); app->popup = popup_alloc(); - view_set_previous_callback(popup_get_view(app->popup), stay_popup); + view_set_previous_callback(popup_get_view(app->popup), connecting_back); view_dispatcher_add_view(app->vd, ViewPopup, popup_get_view(app->popup)); app->timer = furi_timer_alloc(timer_cb, FuriTimerTypePeriodic, app); diff --git a/bt_inspector/bt_inspector.c b/bt_inspector/bt_inspector.c index b39c5ef..b2db3c4 100644 --- a/bt_inspector/bt_inspector.c +++ b/bt_inspector/bt_inspector.c @@ -129,6 +129,7 @@ typedef struct { JobType job; bool job_ok; + volatile bool cancel; FuriString* status; FuriString* info_text; } App; @@ -853,7 +854,10 @@ static uint32_t nav_exit(void* ctx) { static uint32_t nav_stay_popup(void* ctx) { UNUSED(ctx); - return ViewPopup; + // Back on a connect/discover popup: abort and return to the scan list. + nav_app->cancel = true; + nav_app->cur_view = ViewScan; + return ViewScan; } static uint32_t nav_page_back(void* ctx) { @@ -916,6 +920,16 @@ static void on_tick(App* app) { } static void on_job_done(App* app) { + if(app->cancel) { // user backed out of the connect/discover popup + app->cancel = false; + if(app->job != JobDisconnect) { + if(bc_is_connected(app->bc)) + post_job(app, JobDisconnect); + else + bc_scan_start(app->bc); + } + return; + } switch(app->job) { case JobConnect: if(app->job_ok) { @@ -1038,6 +1052,7 @@ static bool custom_event_cb(void* ctx, uint32_t event) { case EvtPageCenter: if(app->page_kind == PageDetails) { app_log(app, "CONNECTING %s", dev_label(&app->dev)); + app->cancel = false; show_popup(app, "Connecting...", dev_label(&app->dev), false); post_job(app, JobConnect); } else if(app->page_kind == PageChar) { @@ -1059,6 +1074,12 @@ static bool custom_event_cb(void* ctx, uint32_t event) { case EvtJobDone: on_job_done(app); break; case EvtNotify: on_notify(app); break; case EvtPopupDone: + if(app->cancel) { + app->cancel = false; + bc_scan_start(app->bc); + show_view(app, ViewScan); + break; + } if(app->after_popup == AfterPopupServices) { show_view(app, ViewServices); } else {