From b59ccad3db1629feb7b6b574db73b689b3b83996 Mon Sep 17 00:00:00 2001 From: gullradriel Date: Tue, 10 Feb 2026 01:20:34 +0100 Subject: [PATCH 1/9] remove UNUSED as we are using 'model' just a line after, add a furi_check --- views/protopirate_receiver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/protopirate_receiver.c b/views/protopirate_receiver.c index 05aa123..095e60c 100644 --- a/views/protopirate_receiver.c +++ b/views/protopirate_receiver.c @@ -44,8 +44,8 @@ typedef struct { } ProtoPirateReceiverModel; static void protopirate_view_rssi_draw(Canvas* canvas, ProtoPirateReceiverModel* model) { + furi_check(model); uint8_t u_rssi = 0; - UNUSED(model); if(model->rssi >= SUBGHZ_RAW_THRESHOLD_MIN) { u_rssi = (uint8_t)(model->rssi - SUBGHZ_RAW_THRESHOLD_MIN); From 28f7324957fbe585b815dbec4c9c005bf7b88df9 Mon Sep 17 00:00:00 2001 From: gullradriel Date: Tue, 10 Feb 2026 01:24:29 +0100 Subject: [PATCH 2/9] clamp u_rssi to avoid offscreen drawing --- views/protopirate_receiver.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/views/protopirate_receiver.c b/views/protopirate_receiver.c index 095e60c..71a880c 100644 --- a/views/protopirate_receiver.c +++ b/views/protopirate_receiver.c @@ -48,7 +48,12 @@ static void protopirate_view_rssi_draw(Canvas* canvas, ProtoPirateReceiverModel* uint8_t u_rssi = 0; if(model->rssi >= SUBGHZ_RAW_THRESHOLD_MIN) { - u_rssi = (uint8_t)(model->rssi - SUBGHZ_RAW_THRESHOLD_MIN); + /* Clamp to a sane range to prevent wrap and off-screen drawing */ + /* we are using 90.0 to keep (46 + i + (i/5)) within screen bounds (128px wide) */ + float v = model->rssi - SUBGHZ_RAW_THRESHOLD_MIN; + if(v < 0.0f) v = 0.0f; + if(v > 90.0f) v = 90.0f; /* 90 is arbitrary but safe for the screen width */ + u_rssi = (uint8_t)v; } //Add a 1px space between the segments From 5dcbf5891fdff693a2bbb319756ea44b28cbe0af Mon Sep 17 00:00:00 2001 From: gullradriel Date: Tue, 10 Feb 2026 01:28:12 +0100 Subject: [PATCH 3/9] =?UTF-8?q?keep=20an=20internal=20animation=5Fframe=20?= =?UTF-8?q?value=20so=20we=20are=20not=20messing=20with=20the=20model.=20I?= =?UTF-8?q?n=20many=20Flipper=20view=20patterns,=20the=20model=20passed=20?= =?UTF-8?q?to=20draw=20is=20intended=20to=20be=20read-only=20(it=E2=80=99s?= =?UTF-8?q?=20typically=20provided=20under=20a=20lock=20owned=20by=20the?= =?UTF-8?q?=20view=20system).=20Mutating=20it=20in=20draw=20can=20cause=20?= =?UTF-8?q?hard-to-debug=20behavior?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- views/protopirate_receiver.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/views/protopirate_receiver.c b/views/protopirate_receiver.c index 71a880c..a0c9433 100644 --- a/views/protopirate_receiver.c +++ b/views/protopirate_receiver.c @@ -186,7 +186,8 @@ void protopirate_view_receiver_draw(Canvas* canvas, ProtoPirateReceiverModel* mo canvas_set_font(canvas, FontSecondary); // Increment animation frame - model->animation_frame = (model->animation_frame + 1) % 96; + static uint8_t animation_frame = 0; + animation_frame = (animation_frame + 1) % 96; size_t item_count = ProtoPirateReceiverMenuItemArray_size(model->history_item_arr); bool scrollbar = item_count > MENU_ITEMS; @@ -269,7 +270,7 @@ void protopirate_view_receiver_draw(Canvas* canvas, ProtoPirateReceiverModel* mo // Three waves of expanding circles with different speeds for(int wave = 0; wave < 3; wave++) { // Calculate radius for this wave with offset - int base_radius = ((model->animation_frame + wave * 32) % 96) / 3; + int base_radius = ((animation_frame + wave * 32) % 96) / 3; if(base_radius < 28) { // Calculate fade based on distance from center @@ -313,7 +314,7 @@ void protopirate_view_receiver_draw(Canvas* canvas, ProtoPirateReceiverModel* mo } // Rotating sweep line with glow effect - float sweep_angle = (model->animation_frame * 3.75f) * 3.14159f / 180.0f; + float sweep_angle = (animation_frame * 3.75f) * 3.14159f / 180.0f; // Main sweep line int sweep_x = center_x + 22 * cosf(sweep_angle); @@ -351,7 +352,7 @@ void protopirate_view_receiver_draw(Canvas* canvas, ProtoPirateReceiverModel* mo } // Pulsing center - int pulse = (model->animation_frame % 32); + int pulse = (animation_frame % 32); if(pulse < 16) { canvas_draw_disc(canvas, center_x, center_y, 2); } else { From 71f9605a67f1b7c1f3ba99d8f32dd55b3fb6348d Mon Sep 17 00:00:00 2001 From: gullradriel Date: Tue, 10 Feb 2026 01:40:00 +0100 Subject: [PATCH 4/9] receiver: fix InputKeyOk model locking and redraw behavior --- views/protopirate_receiver.c | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/views/protopirate_receiver.c b/views/protopirate_receiver.c index a0c9433..ea146c9 100644 --- a/views/protopirate_receiver.c +++ b/views/protopirate_receiver.c @@ -469,34 +469,36 @@ bool protopirate_view_receiver_input(InputEvent* event, void* context) { consumed = true; break; case InputKeyOk: + bool do_ok_cb = false; + bool do_toggle = false; + /* Read-only: do not redraw */ with_view_model( receiver->view, ProtoPirateReceiverModel * model, { size_t item_count = ProtoPirateReceiverMenuItemArray_size(model->history_item_arr); + if(item_count > 0) { - if(receiver->callback) { - receiver->callback( - ProtoPirateCustomEventViewReceiverOK, receiver->context); - } - } else { - if(event->type == InputTypeLong) { - with_view_model( - receiver->view, - ProtoPirateReceiverModel * model, - { model->dolphin_view = !model->dolphin_view; }, - true); - } + do_ok_cb = true; + } else if(event->type == InputTypeLong) { + do_toggle = true; } }, false); - consumed = true; - break; - case InputKeyBack: - if(receiver->callback) { - receiver->callback(ProtoPirateCustomEventViewReceiverBack, receiver->context); + /* Only redraw if we actually changed dolphin_view */ + if(do_toggle) { + with_view_model( + receiver->view, + ProtoPirateReceiverModel * model, + { model->dolphin_view = !model->dolphin_view; }, + true); } + + if(do_ok_cb && receiver->callback) { + receiver->callback(ProtoPirateCustomEventViewReceiverOK, receiver->context); + } + consumed = true; break; default: From f593ff6fdb7c2aaafc395ffb955cfe264fe510fd Mon Sep 17 00:00:00 2001 From: gullradriel Date: Tue, 10 Feb 2026 01:44:38 +0100 Subject: [PATCH 5/9] call a callback where needed ? --- views/protopirate_receiver_info.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/views/protopirate_receiver_info.c b/views/protopirate_receiver_info.c index 32f2192..b8ba33a 100644 --- a/views/protopirate_receiver_info.c +++ b/views/protopirate_receiver_info.c @@ -42,16 +42,16 @@ void protopirate_view_receiver_info_draw(Canvas* canvas, ProtoPirateReceiverInfo bool protopirate_view_receiver_info_input(InputEvent* event, void* context) { furi_check(context); - UNUSED(context); - bool consumed = false; + ProtoPirateReceiverInfo* receiver_info = context; - if(event->type == InputTypeShort) { - if(event->key == InputKeyBack) { - consumed = true; + if(event->type == InputTypeShort && event->key == InputKeyBack) { + if(receiver_info->callback) { + receiver_info->callback( + ProtoPirateCustomEventViewReceiverBack, receiver_info->context); } + return true; } - - return consumed; + return false; } void protopirate_view_receiver_info_enter(void* context) { From 94c4e9a7487e84faadc5b1e1b1ce96ec72118373 Mon Sep 17 00:00:00 2001 From: gullradriel Date: Tue, 10 Feb 2026 01:47:57 +0100 Subject: [PATCH 6/9] protopirate_view_receiver_add_item_to_menu: safe copy of name even if NULL --- views/protopirate_receiver.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/views/protopirate_receiver.c b/views/protopirate_receiver.c index ea146c9..b7b069e 100644 --- a/views/protopirate_receiver.c +++ b/views/protopirate_receiver.c @@ -140,7 +140,8 @@ void protopirate_view_receiver_add_item_to_menu( { ProtoPirateReceiverMenuItem* item_menu = ProtoPirateReceiverMenuItemArray_push_raw(model->history_item_arr); - item_menu->item_str = furi_string_alloc_set(name); + const char* safe_name = name ? name : "EMPTY_NAME"; + item_menu->item_str = furi_string_alloc_set(safe_name); item_menu->type = type; }, true); From dc8f1318b588161cf5dca09d628e95a2e45b3040 Mon Sep 17 00:00:00 2001 From: gullradriel Date: Tue, 10 Feb 2026 01:48:21 +0100 Subject: [PATCH 7/9] check malloc of receiver_info --- views/protopirate_receiver_info.c | 1 + 1 file changed, 1 insertion(+) diff --git a/views/protopirate_receiver_info.c b/views/protopirate_receiver_info.c index b8ba33a..dc405cf 100644 --- a/views/protopirate_receiver_info.c +++ b/views/protopirate_receiver_info.c @@ -74,6 +74,7 @@ void protopirate_view_receiver_info_exit(void* context) { ProtoPirateReceiverInfo* protopirate_view_receiver_info_alloc() { ProtoPirateReceiverInfo* receiver_info = malloc(sizeof(ProtoPirateReceiverInfo)); + furi_check(receiver_info); receiver_info->view = view_alloc(); view_allocate_model( From 2b1a010ef4b14e3cc56ce552535732107370d565 Mon Sep 17 00:00:00 2001 From: gullradriel Date: Tue, 10 Feb 2026 01:55:40 +0100 Subject: [PATCH 8/9] added missing initializers --- views/protopirate_receiver.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/views/protopirate_receiver.c b/views/protopirate_receiver.c index b7b069e..ca40cbc 100644 --- a/views/protopirate_receiver.c +++ b/views/protopirate_receiver.c @@ -547,6 +547,8 @@ ProtoPirateReceiver* protopirate_view_receiver_alloc(bool auto_save) { model->lock_count = 0; model->auto_save = auto_save; model->animation_frame = 0; + model->dolphin_view = false; + model->sub_decode_mode = false; }, true); From 9e70c9fbd413abb666ea773dd7b349d5c7d6983c Mon Sep 17 00:00:00 2001 From: gullradriel Date: Tue, 10 Feb 2026 02:04:18 +0100 Subject: [PATCH 9/9] reuse previously processed lock value, call callback outside model lock --- views/protopirate_receiver.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/views/protopirate_receiver.c b/views/protopirate_receiver.c index ca40cbc..3095eff 100644 --- a/views/protopirate_receiver.c +++ b/views/protopirate_receiver.c @@ -404,6 +404,8 @@ bool protopirate_view_receiver_input(InputEvent* event, void* context) { receiver->view, ProtoPirateReceiverModel * model, { lock = model->lock; }, false); if(lock == ProtoPirateLockOn) { + bool do_unlock_cb = false; + with_view_model( receiver->view, ProtoPirateReceiverModel * model, @@ -414,10 +416,7 @@ bool protopirate_view_receiver_input(InputEvent* event, void* context) { if(model->lock_count >= UNLOCK_CNT) { model->lock = ProtoPirateLockOff; model->lock_count = 0; - if(receiver->callback) { - receiver->callback( - ProtoPirateCustomEventViewReceiverUnlock, receiver->context); - } + do_unlock_cb = true; } } } else if( @@ -427,6 +426,11 @@ bool protopirate_view_receiver_input(InputEvent* event, void* context) { } }, true); + + if(do_unlock_cb && receiver->callback) { + receiver->callback(ProtoPirateCustomEventViewReceiverUnlock, receiver->context); + } + consumed = true; } else if( event->type == InputTypeShort || event->type == InputTypeLong ||