From 7f65ae46cf856602663e68c005bfa2305a55a525 Mon Sep 17 00:00:00 2001 From: Georgii Surkov Date: Thu, 7 Mar 2024 18:06:36 +0300 Subject: [PATCH 01/11] Introduce ConcurrentRunner, load universal and regular remotes concurrently --- applications/main/infrared/infrared_app_i.h | 1 + .../main/infrared/infrared_custom_event.h | 1 + .../common/infrared_scene_universal_common.c | 48 +++++++++++-- .../scenes/infrared_scene_remote_list.c | 70 +++++++++++++------ .../scenes/infrared_scene_universal_ac.c | 13 +--- .../scenes/infrared_scene_universal_audio.c | 13 +--- .../infrared_scene_universal_projector.c | 13 +--- .../scenes/infrared_scene_universal_tv.c | 13 +--- lib/toolbox/SConscript | 1 + lib/toolbox/concurrent_runner.c | 60 ++++++++++++++++ lib/toolbox/concurrent_runner.h | 52 ++++++++++++++ targets/f18/api_symbols.csv | 4 +- targets/f7/api_symbols.csv | 4 +- 13 files changed, 215 insertions(+), 78 deletions(-) create mode 100644 lib/toolbox/concurrent_runner.c create mode 100644 lib/toolbox/concurrent_runner.h diff --git a/applications/main/infrared/infrared_app_i.h b/applications/main/infrared/infrared_app_i.h index 7a9202b28ed..df2f4b92543 100644 --- a/applications/main/infrared/infrared_app_i.h +++ b/applications/main/infrared/infrared_app_i.h @@ -83,6 +83,7 @@ typedef struct { bool is_learning_new_remote; /**< Learning new remote or adding to an existing one. */ bool is_debug_enabled; /**< Whether to enable or disable debugging features. */ bool is_transmitting; /**< Whether a signal is currently being transmitted. */ + bool is_load_success; /**< Whether the last load operation was successful. */ InfraredEditTarget edit_target : 8; /**< Selected editing target (a remote or a button). */ InfraredEditMode edit_mode : 8; /**< Selected editing operation (rename or delete). */ int32_t current_button_index; /**< Selected button index (move destination). */ diff --git a/applications/main/infrared/infrared_custom_event.h b/applications/main/infrared/infrared_custom_event.h index 30bb0f729cd..f363d3ad83f 100644 --- a/applications/main/infrared/infrared_custom_event.h +++ b/applications/main/infrared/infrared_custom_event.h @@ -14,6 +14,7 @@ enum InfraredCustomEventType { InfraredCustomEventTypePopupClosed, InfraredCustomEventTypeButtonSelected, InfraredCustomEventTypeBackPressed, + InfraredCustomEventTypeLoadFinished, InfraredCustomEventTypeRpcLoadFile, InfraredCustomEventTypeRpcExit, diff --git a/applications/main/infrared/scenes/common/infrared_scene_universal_common.c b/applications/main/infrared/scenes/common/infrared_scene_universal_common.c index 4967d195664..e97ca05ef81 100644 --- a/applications/main/infrared/scenes/common/infrared_scene_universal_common.c +++ b/applications/main/infrared/scenes/common/infrared_scene_universal_common.c @@ -1,6 +1,9 @@ #include "../../infrared_app_i.h" #include +#include + +#define BRUTE_FORCE_LOADER_STACK_SIZE (2048UL) void infrared_scene_universal_common_item_callback(void* context, uint32_t index) { InfraredApp* infrared = context; @@ -32,9 +35,34 @@ static void infrared_scene_universal_common_hide_popup(InfraredApp* infrared) { infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStop); } +static void infrared_scene_universal_common_load_callback(void* context) { + InfraredApp* infrared = context; + infrared->app_state.is_load_success = + infrared_brute_force_calculate_messages(infrared->brute_force); +} + +static void infrared_scene_universal_common_load_finished_callback(void* context) { + InfraredApp* infrared = context; + view_dispatcher_send_custom_event( + infrared->view_dispatcher, + infrared_custom_event_pack(InfraredCustomEventTypeLoadFinished, 0)); +} + void infrared_scene_universal_common_on_enter(void* context) { InfraredApp* infrared = context; + view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical); + view_stack_add_view(infrared->view_stack, button_panel_get_view(infrared->button_panel)); + view_stack_add_view(infrared->view_stack, loading_get_view(infrared->loading)); + + view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack); + + // Load universal remote data in background + concurrent_runner_start( + BRUTE_FORCE_LOADER_STACK_SIZE, + infrared_scene_universal_common_load_callback, + infrared_scene_universal_common_load_finished_callback, + context); } bool infrared_scene_universal_common_on_event(void* context, SceneManagerEvent event) { @@ -58,26 +86,34 @@ bool infrared_scene_universal_common_on_event(void* context, SceneManagerEvent e if(infrared_custom_event_get_type(event.event) == InfraredCustomEventTypeBackPressed) { infrared_brute_force_stop(brute_force); infrared_scene_universal_common_hide_popup(infrared); - consumed = true; } + consumed = true; } } else { if(event.type == SceneManagerEventTypeBack) { scene_manager_previous_scene(scene_manager); consumed = true; } else if(event.type == SceneManagerEventTypeCustom) { - if(infrared_custom_event_get_type(event.event) == - InfraredCustomEventTypeButtonSelected) { + uint16_t event_type; + int16_t event_value; + infrared_custom_event_unpack(event.event, &event_type, &event_value); + + if(event_type == InfraredCustomEventTypeButtonSelected) { uint32_t record_count; - if(infrared_brute_force_start( - brute_force, infrared_custom_event_get_value(event.event), &record_count)) { + if(infrared_brute_force_start(brute_force, event_value, &record_count)) { dolphin_deed(DolphinDeedIrSend); infrared_scene_universal_common_show_popup(infrared, record_count); } else { scene_manager_next_scene(scene_manager, InfraredSceneErrorDatabases); } - consumed = true; + } else if(event_type == InfraredCustomEventTypeLoadFinished) { + view_stack_remove_view(infrared->view_stack, loading_get_view(infrared->loading)); + + if(!infrared->app_state.is_load_success) { + scene_manager_next_scene(infrared->scene_manager, InfraredSceneErrorDatabases); + } } + consumed = true; } } diff --git a/applications/main/infrared/scenes/infrared_scene_remote_list.c b/applications/main/infrared/scenes/infrared_scene_remote_list.c index 2276e270a0c..4328df371bd 100644 --- a/applications/main/infrared/scenes/infrared_scene_remote_list.c +++ b/applications/main/infrared/scenes/infrared_scene_remote_list.c @@ -1,41 +1,67 @@ #include "../infrared_app_i.h" -void infrared_scene_remote_list_on_enter(void* context) { +#include + +#define REMOTE_LOADER_STACK_SIZE (2048UL) + +static void infrared_scene_remote_list_load_callback(void* context) { InfraredApp* infrared = context; - SceneManager* scene_manager = infrared->scene_manager; - ViewDispatcher* view_dispatcher = infrared->view_dispatcher; + infrared->app_state.is_load_success = + infrared_remote_load(infrared->remote, furi_string_get_cstr(infrared->file_path)); +} - view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical); - view_dispatcher_switch_to_view(view_dispatcher, InfraredViewStack); +static void infrared_scene_remote_list_load_finished_callback(void* context) { + InfraredApp* infrared = context; + view_dispatcher_send_custom_event( + infrared->view_dispatcher, InfraredCustomEventTypeLoadFinished); +} +static void infrared_scene_remote_list_select_and_load(InfraredApp* infrared) { DialogsFileBrowserOptions browser_options; dialog_file_browser_set_basic_options(&browser_options, INFRARED_APP_EXTENSION, &I_ir_10px); browser_options.base_path = INFRARED_APP_FOLDER; - while(dialog_file_browser_show( - infrared->dialogs, infrared->file_path, infrared->file_path, &browser_options)) { - const char* file_path = furi_string_get_cstr(infrared->file_path); - - infrared_show_loading_popup(infrared, true); - const bool remote_loaded = infrared_remote_load(infrared->remote, file_path); - infrared_show_loading_popup(infrared, false); - - if(remote_loaded) { - scene_manager_next_scene(scene_manager, InfraredSceneRemote); - return; - } else { - infrared_show_error_message(infrared, "Failed to load\n\"%s\"", file_path); - } + if(dialog_file_browser_show( + infrared->dialogs, infrared->file_path, infrared->file_path, &browser_options)) { + view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical); + view_stack_add_view(infrared->view_stack, loading_get_view(infrared->loading)); + view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack); + // Load remote in background + concurrent_runner_start( + REMOTE_LOADER_STACK_SIZE, + infrared_scene_remote_list_load_callback, + infrared_scene_remote_list_load_finished_callback, + infrared); + } else { + scene_manager_previous_scene(infrared->scene_manager); } +} - scene_manager_previous_scene(scene_manager); +void infrared_scene_remote_list_on_enter(void* context) { + InfraredApp* infrared = context; + infrared_scene_remote_list_select_and_load(infrared); } bool infrared_scene_remote_list_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); + InfraredApp* infrared = context; + bool consumed = false; + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == InfraredCustomEventTypeLoadFinished) { + view_stack_remove_view(infrared->view_stack, loading_get_view(infrared->loading)); + + if(infrared->app_state.is_load_success) { + scene_manager_next_scene(infrared->scene_manager, InfraredSceneRemote); + } else { + infrared_show_error_message( + infrared, "Failed to load\n\"%s\"", furi_string_get_cstr(infrared->file_path)); + infrared_scene_remote_list_select_and_load(infrared); + } + } + consumed = true; + } + return consumed; } diff --git a/applications/main/infrared/scenes/infrared_scene_universal_ac.c b/applications/main/infrared/scenes/infrared_scene_universal_ac.c index 764a9518909..b82bcc1f9af 100644 --- a/applications/main/infrared/scenes/infrared_scene_universal_ac.c +++ b/applications/main/infrared/scenes/infrared_scene_universal_ac.c @@ -3,8 +3,6 @@ #include "common/infrared_scene_universal_common.h" void infrared_scene_universal_ac_on_enter(void* context) { - infrared_scene_universal_common_on_enter(context); - InfraredApp* infrared = context; ButtonPanel* button_panel = infrared->button_panel; InfraredBruteForce* brute_force = infrared->brute_force; @@ -122,16 +120,7 @@ void infrared_scene_universal_ac_on_enter(void* context) { button_panel_add_label(button_panel, 4, 10, FontPrimary, "AC remote"); - view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical); - view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack); - - infrared_show_loading_popup(infrared, true); - bool success = infrared_brute_force_calculate_messages(brute_force); - infrared_show_loading_popup(infrared, false); - - if(!success) { - scene_manager_next_scene(infrared->scene_manager, InfraredSceneErrorDatabases); - } + infrared_scene_universal_common_on_enter(context); } bool infrared_scene_universal_ac_on_event(void* context, SceneManagerEvent event) { diff --git a/applications/main/infrared/scenes/infrared_scene_universal_audio.c b/applications/main/infrared/scenes/infrared_scene_universal_audio.c index 241a22bcbb7..a15b2ce9949 100644 --- a/applications/main/infrared/scenes/infrared_scene_universal_audio.c +++ b/applications/main/infrared/scenes/infrared_scene_universal_audio.c @@ -3,8 +3,6 @@ #include "common/infrared_scene_universal_common.h" void infrared_scene_universal_audio_on_enter(void* context) { - infrared_scene_universal_common_on_enter(context); - InfraredApp* infrared = context; ButtonPanel* button_panel = infrared->button_panel; InfraredBruteForce* brute_force = infrared->brute_force; @@ -119,16 +117,7 @@ void infrared_scene_universal_audio_on_enter(void* context) { button_panel_add_label(button_panel, 1, 10, FontPrimary, "Mus. remote"); button_panel_add_icon(button_panel, 34, 56, &I_vol_ac_text_30x30); - view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical); - view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack); - - infrared_show_loading_popup(infrared, true); - bool success = infrared_brute_force_calculate_messages(brute_force); - infrared_show_loading_popup(infrared, false); - - if(!success) { - scene_manager_next_scene(infrared->scene_manager, InfraredSceneErrorDatabases); - } + infrared_scene_universal_common_on_enter(context); } bool infrared_scene_universal_audio_on_event(void* context, SceneManagerEvent event) { diff --git a/applications/main/infrared/scenes/infrared_scene_universal_projector.c b/applications/main/infrared/scenes/infrared_scene_universal_projector.c index d8520deb39e..c665444fb11 100644 --- a/applications/main/infrared/scenes/infrared_scene_universal_projector.c +++ b/applications/main/infrared/scenes/infrared_scene_universal_projector.c @@ -3,8 +3,6 @@ #include "common/infrared_scene_universal_common.h" void infrared_scene_universal_projector_on_enter(void* context) { - infrared_scene_universal_common_on_enter(context); - InfraredApp* infrared = context; ButtonPanel* button_panel = infrared->button_panel; InfraredBruteForce* brute_force = infrared->brute_force; @@ -68,16 +66,7 @@ void infrared_scene_universal_projector_on_enter(void* context) { button_panel_add_label(button_panel, 3, 11, FontPrimary, "Proj. remote"); button_panel_add_icon(button_panel, 17, 72, &I_vol_ac_text_30x30); - view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical); - view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack); - - infrared_show_loading_popup(infrared, true); - bool success = infrared_brute_force_calculate_messages(brute_force); - infrared_show_loading_popup(infrared, false); - - if(!success) { - scene_manager_next_scene(infrared->scene_manager, InfraredSceneErrorDatabases); - } + infrared_scene_universal_common_on_enter(context); } bool infrared_scene_universal_projector_on_event(void* context, SceneManagerEvent event) { diff --git a/applications/main/infrared/scenes/infrared_scene_universal_tv.c b/applications/main/infrared/scenes/infrared_scene_universal_tv.c index 6031205f551..16633e29cbe 100644 --- a/applications/main/infrared/scenes/infrared_scene_universal_tv.c +++ b/applications/main/infrared/scenes/infrared_scene_universal_tv.c @@ -3,8 +3,6 @@ #include "common/infrared_scene_universal_common.h" void infrared_scene_universal_tv_on_enter(void* context) { - infrared_scene_universal_common_on_enter(context); - InfraredApp* infrared = context; ButtonPanel* button_panel = infrared->button_panel; InfraredBruteForce* brute_force = infrared->brute_force; @@ -95,16 +93,7 @@ void infrared_scene_universal_tv_on_enter(void* context) { button_panel_add_label(button_panel, 5, 10, FontPrimary, "TV remote"); - view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical); - view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack); - - infrared_show_loading_popup(infrared, true); - bool success = infrared_brute_force_calculate_messages(brute_force); - infrared_show_loading_popup(infrared, false); - - if(!success) { - scene_manager_next_scene(infrared->scene_manager, InfraredSceneErrorDatabases); - } + infrared_scene_universal_common_on_enter(context); } bool infrared_scene_universal_tv_on_event(void* context, SceneManagerEvent event) { diff --git a/lib/toolbox/SConscript b/lib/toolbox/SConscript index 121362424eb..891483f7620 100644 --- a/lib/toolbox/SConscript +++ b/lib/toolbox/SConscript @@ -35,6 +35,7 @@ env.Append( File("simple_array.h"), File("bit_buffer.h"), File("keys_dict.h"), + File("concurrent_runner.h") ], ) diff --git a/lib/toolbox/concurrent_runner.c b/lib/toolbox/concurrent_runner.c new file mode 100644 index 00000000000..67b43a607bf --- /dev/null +++ b/lib/toolbox/concurrent_runner.c @@ -0,0 +1,60 @@ +#include "concurrent_runner.h" + +#include + +#include +#include + +typedef struct { + FuriThread* thread; + ConcurrentRunnerCallback run_callback; + ConcurrentRunnerCallback finished_callback; + void* context; +} ConcurrentRunner; + +static void concurrent_runner_timer_callback(void* context, uint32_t arg) { + UNUSED(arg); + + ConcurrentRunner* instance = context; + furi_thread_join(instance->thread); + + if(instance->finished_callback) { + instance->finished_callback(instance->context); + } + + furi_thread_free(instance->thread); + free(instance); +} + +static int32_t concurrent_runner_thread_callback(void* context) { + ConcurrentRunner* instance = context; + instance->run_callback(instance->context); + return 0; +} + +static void concurrent_runner_thread_state_callback(FuriThreadState state, void* context) { + if(state == FuriThreadStateStopped) { + furi_timer_pending_callback(concurrent_runner_timer_callback, context, 0); + } +} + +void concurrent_runner_start( + uint32_t stack_size, + ConcurrentRunnerCallback run_callback, + ConcurrentRunnerCallback finished_callback, + void* context) { + furi_check(run_callback); + + ConcurrentRunner* instance = malloc(sizeof(ConcurrentRunner)); + + instance->thread = furi_thread_alloc_ex( + "ConcurrentRunner", stack_size, concurrent_runner_thread_callback, instance); + furi_thread_set_state_callback(instance->thread, concurrent_runner_thread_state_callback); + furi_thread_set_state_context(instance->thread, instance); + + instance->run_callback = run_callback; + instance->finished_callback = finished_callback; + instance->context = context; + + furi_thread_start(instance->thread); +} diff --git a/lib/toolbox/concurrent_runner.h b/lib/toolbox/concurrent_runner.h new file mode 100644 index 00000000000..01d50280c1d --- /dev/null +++ b/lib/toolbox/concurrent_runner.h @@ -0,0 +1,52 @@ +/** + * @file concurrent_runner.h + * @brief Helper for running code in a separate thread. + * + * Sometimes it is necessary to quickly spawn a thread, run a blocking + * operation (such as loading a file or processing a large chunk of data) + * and delete it upon completion. + * + * The ConcurrentRunner helper automates this task by taking a function + * and running it in a separate thread. Another optional function + * can be provided to notify the caller about completion of the task. + * + * Input and output parameters are handled through the context parameter. + * ConcurrentRunner does not provide any error handling of the user-supplied + * code, so it must be taken care of on the application side. + */ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Callback type declaration for use with ConcurrentRunner. + * + * @param[in,out] context Pointer to a user-specific object. + */ +typedef void (*ConcurrentRunnerCallback)(void* context); + +/** + * @brief Execute a function in a separate thread. + * + * If completion notification is not necessary, pass NULL to finished_callback. + * + * @warning Setting stack_size too low will lead to a system crash. + * + * @param[in] stack_size size of the created thread, in bytes. + * @param[in] run_callback pointer to a function to be run in a separate thread. + * @param[in] finished_callback optional pointer to a function to be called upon completion of the task. + * @param[in,out] context pointer to a user-specific object. Will be passed to both callbacks. + */ +void concurrent_runner_start( + uint32_t stack_size, + ConcurrentRunnerCallback run_callback, + ConcurrentRunnerCallback finished_callback, + void* context); + +#ifdef __cplusplus +} +#endif diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index bdfa8c7a4ee..678a65c3cde 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,58.0,, +Version,+,58.1,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -148,6 +148,7 @@ Header,+,lib/toolbox/api_lock.h,, Header,+,lib/toolbox/args.h,, Header,+,lib/toolbox/bit_buffer.h,, Header,+,lib/toolbox/compress.h,, +Header,+,lib/toolbox/concurrent_runner.h,, Header,+,lib/toolbox/crc32_calc.h,, Header,+,lib/toolbox/dir_walk.h,, Header,+,lib/toolbox/float_tools.h,, @@ -774,6 +775,7 @@ Function,+,compress_free,void,Compress* Function,+,compress_icon_alloc,CompressIcon*, Function,+,compress_icon_decode,void,"CompressIcon*, const uint8_t*, uint8_t**" Function,+,compress_icon_free,void,CompressIcon* +Function,+,concurrent_runner_start,void,"uint32_t, ConcurrentRunnerCallback, ConcurrentRunnerCallback, void*" Function,-,copysign,double,"double, double" Function,-,copysignf,float,"float, float" Function,-,copysignl,long double,"long double, long double" diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index d856dc6948a..c3f32585870 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,58.0,, +Version,+,58.1,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, @@ -211,6 +211,7 @@ Header,+,lib/toolbox/api_lock.h,, Header,+,lib/toolbox/args.h,, Header,+,lib/toolbox/bit_buffer.h,, Header,+,lib/toolbox/compress.h,, +Header,+,lib/toolbox/concurrent_runner.h,, Header,+,lib/toolbox/crc32_calc.h,, Header,+,lib/toolbox/dir_walk.h,, Header,+,lib/toolbox/float_tools.h,, @@ -842,6 +843,7 @@ Function,+,compress_free,void,Compress* Function,+,compress_icon_alloc,CompressIcon*, Function,+,compress_icon_decode,void,"CompressIcon*, const uint8_t*, uint8_t**" Function,+,compress_icon_free,void,CompressIcon* +Function,+,concurrent_runner_start,void,"uint32_t, ConcurrentRunnerCallback, ConcurrentRunnerCallback, void*" Function,-,copysign,double,"double, double" Function,-,copysignf,float,"float, float" Function,-,copysignl,long double,"long double, long double" From 3724ce81c28d74654f4593289eb1e549d20ebc2e Mon Sep 17 00:00:00 2001 From: Georgii Surkov Date: Thu, 7 Mar 2024 21:08:48 +0300 Subject: [PATCH 02/11] Perform all lengthy operations in a ConcurrentRunner --- applications/main/infrared/infrared_app.c | 15 ----- applications/main/infrared/infrared_app_i.h | 18 +----- .../main/infrared/infrared_custom_event.h | 2 +- .../common/infrared_scene_universal_common.c | 8 +-- .../scenes/infrared_scene_edit_delete.c | 57 +++++++++++++---- .../scenes/infrared_scene_edit_move.c | 49 ++++++++++----- .../scenes/infrared_scene_edit_rename.c | 61 ++++++++++++++----- .../scenes/infrared_scene_remote_list.c | 8 +-- 8 files changed, 137 insertions(+), 81 deletions(-) diff --git a/applications/main/infrared/infrared_app.c b/applications/main/infrared/infrared_app.c index 645659bbc5d..f3975ff87aa 100644 --- a/applications/main/infrared/infrared_app.c +++ b/applications/main/infrared/infrared_app.c @@ -397,21 +397,6 @@ void infrared_play_notification_message( notification_message(infrared->notifications, infrared_notification_sequences[message]); } -void infrared_show_loading_popup(const InfraredApp* infrared, bool show) { - ViewStack* view_stack = infrared->view_stack; - Loading* loading = infrared->loading; - - if(show) { - // Raise timer priority so that animations can play - furi_timer_set_thread_priority(FuriTimerThreadPriorityElevated); - view_stack_add_view(view_stack, loading_get_view(loading)); - } else { - view_stack_remove_view(view_stack, loading_get_view(loading)); - // Restore default timer priority - furi_timer_set_thread_priority(FuriTimerThreadPriorityNormal); - } -} - void infrared_show_error_message(const InfraredApp* infrared, const char* fmt, ...) { va_list args; va_start(args, fmt); diff --git a/applications/main/infrared/infrared_app_i.h b/applications/main/infrared/infrared_app_i.h index df2f4b92543..b23481b211c 100644 --- a/applications/main/infrared/infrared_app_i.h +++ b/applications/main/infrared/infrared_app_i.h @@ -19,12 +19,13 @@ #include #include +#include #include #include #include -#include +#include #include "infrared_app.h" #include "infrared_remote.h" @@ -36,8 +37,6 @@ #include "views/infrared_debug_view.h" #include "views/infrared_move_view.h" -#include "rpc/rpc_app.h" - #define INFRARED_FILE_NAME_SIZE 100 #define INFRARED_TEXT_STORE_NUM 2 #define INFRARED_TEXT_STORE_SIZE 128 @@ -83,7 +82,7 @@ typedef struct { bool is_learning_new_remote; /**< Learning new remote or adding to an existing one. */ bool is_debug_enabled; /**< Whether to enable or disable debugging features. */ bool is_transmitting; /**< Whether a signal is currently being transmitted. */ - bool is_load_success; /**< Whether the last load operation was successful. */ + bool is_task_success; /**< Whether the last concurrent operation was successful. */ InfraredEditTarget edit_target : 8; /**< Selected editing target (a remote or a button). */ InfraredEditMode edit_mode : 8; /**< Selected editing operation (rename or delete). */ int32_t current_button_index; /**< Selected button index (move destination). */ @@ -240,17 +239,6 @@ void infrared_play_notification_message( const InfraredApp* infrared, InfraredNotificationMessage message); -/** - * @brief Show a loading pop-up screen. - * - * In order for this to work, a Stack view must be currently active and - * the main view must be added to it. - * - * @param[in] infrared pointer to the application instance. - * @param[in] show whether to show or hide the pop-up. - */ -void infrared_show_loading_popup(const InfraredApp* infrared, bool show); - /** * @brief Show a formatted error messsage. * diff --git a/applications/main/infrared/infrared_custom_event.h b/applications/main/infrared/infrared_custom_event.h index f363d3ad83f..b53e52a2f3e 100644 --- a/applications/main/infrared/infrared_custom_event.h +++ b/applications/main/infrared/infrared_custom_event.h @@ -14,7 +14,7 @@ enum InfraredCustomEventType { InfraredCustomEventTypePopupClosed, InfraredCustomEventTypeButtonSelected, InfraredCustomEventTypeBackPressed, - InfraredCustomEventTypeLoadFinished, + InfraredCustomEventTypeTaskFinished, InfraredCustomEventTypeRpcLoadFile, InfraredCustomEventTypeRpcExit, diff --git a/applications/main/infrared/scenes/common/infrared_scene_universal_common.c b/applications/main/infrared/scenes/common/infrared_scene_universal_common.c index e97ca05ef81..2b7c55fda32 100644 --- a/applications/main/infrared/scenes/common/infrared_scene_universal_common.c +++ b/applications/main/infrared/scenes/common/infrared_scene_universal_common.c @@ -37,7 +37,7 @@ static void infrared_scene_universal_common_hide_popup(InfraredApp* infrared) { static void infrared_scene_universal_common_load_callback(void* context) { InfraredApp* infrared = context; - infrared->app_state.is_load_success = + infrared->app_state.is_task_success = infrared_brute_force_calculate_messages(infrared->brute_force); } @@ -45,7 +45,7 @@ static void infrared_scene_universal_common_load_finished_callback(void* context InfraredApp* infrared = context; view_dispatcher_send_custom_event( infrared->view_dispatcher, - infrared_custom_event_pack(InfraredCustomEventTypeLoadFinished, 0)); + infrared_custom_event_pack(InfraredCustomEventTypeTaskFinished, 0)); } void infrared_scene_universal_common_on_enter(void* context) { @@ -106,10 +106,10 @@ bool infrared_scene_universal_common_on_event(void* context, SceneManagerEvent e } else { scene_manager_next_scene(scene_manager, InfraredSceneErrorDatabases); } - } else if(event_type == InfraredCustomEventTypeLoadFinished) { + } else if(event_type == InfraredCustomEventTypeTaskFinished) { view_stack_remove_view(infrared->view_stack, loading_get_view(infrared->loading)); - if(!infrared->app_state.is_load_success) { + if(!infrared->app_state.is_task_success) { scene_manager_next_scene(infrared->scene_manager, InfraredSceneErrorDatabases); } } diff --git a/applications/main/infrared/scenes/infrared_scene_edit_delete.c b/applications/main/infrared/scenes/infrared_scene_edit_delete.c index 0cb88efdb66..61f507e2c22 100644 --- a/applications/main/infrared/scenes/infrared_scene_edit_delete.c +++ b/applications/main/infrared/scenes/infrared_scene_edit_delete.c @@ -1,11 +1,32 @@ #include "../infrared_app_i.h" +#include + +#define REMOTE_DELETE_STACK_SIZE (2048UL) + static void infrared_scene_edit_delete_dialog_result_callback(DialogExResult result, void* context) { InfraredApp* infrared = context; view_dispatcher_send_custom_event(infrared->view_dispatcher, result); } +static void infrared_scene_edit_delete_button_callback(void* context) { + InfraredApp* infrared = context; + infrared->app_state.is_task_success = + infrared_remote_delete_signal(infrared->remote, infrared->app_state.current_button_index); +} + +static void infrared_scene_edit_delete_remote_callback(void* context) { + InfraredApp* infrared = context; + infrared->app_state.is_task_success = infrared_remote_remove(infrared->remote); +} + +static void infrared_scene_edit_delete_finished_callback(void* context) { + InfraredApp* infrared = context; + view_dispatcher_send_custom_event( + infrared->view_dispatcher, InfraredCustomEventTypeTaskFinished); +} + void infrared_scene_edit_delete_on_enter(void* context) { InfraredApp* infrared = context; DialogEx* dialog_ex = infrared->dialog_ex; @@ -82,31 +103,39 @@ bool infrared_scene_edit_delete_on_event(void* context, SceneManagerEvent event) bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { + InfraredAppState* app_state = &infrared->app_state; + const InfraredEditTarget edit_target = app_state->edit_target; + if(event.event == DialogExResultLeft) { scene_manager_previous_scene(scene_manager); - consumed = true; } else if(event.event == DialogExResultRight) { - bool success = false; - InfraredRemote* remote = infrared->remote; - InfraredAppState* app_state = &infrared->app_state; - const InfraredEditTarget edit_target = app_state->edit_target; + view_stack_add_view(infrared->view_stack, loading_get_view(infrared->loading)); if(edit_target == InfraredEditTargetButton) { furi_assert(app_state->current_button_index != InfraredButtonIndexNone); - infrared_show_loading_popup(infrared, true); - success = infrared_remote_delete_signal(remote, app_state->current_button_index); - infrared_show_loading_popup(infrared, false); - app_state->current_button_index = InfraredButtonIndexNone; + // Delete button in a separate thread + concurrent_runner_start( + REMOTE_DELETE_STACK_SIZE, + infrared_scene_edit_delete_button_callback, + infrared_scene_edit_delete_finished_callback, + infrared); } else if(edit_target == InfraredEditTargetRemote) { - success = infrared_remote_remove(remote); - app_state->current_button_index = InfraredButtonIndexNone; + // Delete remote in a separate thread (for consistency) + concurrent_runner_start( + REMOTE_DELETE_STACK_SIZE, + infrared_scene_edit_delete_remote_callback, + infrared_scene_edit_delete_finished_callback, + infrared); } else { furi_crash(); } + } else if(event.event == InfraredCustomEventTypeTaskFinished) { + view_stack_remove_view(infrared->view_stack, loading_get_view(infrared->loading)); - if(success) { + if(app_state->is_task_success) { scene_manager_next_scene(scene_manager, InfraredSceneEditDeleteDone); } else { + const InfraredEditTarget edit_target = app_state->edit_target; infrared_show_error_message( infrared, "Failed to\ndelete %s", @@ -115,8 +144,10 @@ bool infrared_scene_edit_delete_on_event(void* context, SceneManagerEvent event) scene_manager_search_and_switch_to_previous_scene_one_of( scene_manager, possible_scenes, COUNT_OF(possible_scenes)); } - consumed = true; + + app_state->current_button_index = InfraredButtonIndexNone; } + consumed = true; } return consumed; diff --git a/applications/main/infrared/scenes/infrared_scene_edit_move.c b/applications/main/infrared/scenes/infrared_scene_edit_move.c index 4959a831095..18bb91cb6c5 100644 --- a/applications/main/infrared/scenes/infrared_scene_edit_move.c +++ b/applications/main/infrared/scenes/infrared_scene_edit_move.c @@ -1,5 +1,23 @@ #include "../infrared_app_i.h" +#include + +#define BUTTON_MOVE_STACK_SIZE (2048UL) + +static void infrared_scene_edit_move_task_callback(void* context) { + InfraredApp* infrared = context; + infrared->app_state.is_task_success = infrared_remote_move_signal( + infrared->remote, + infrared->app_state.prev_button_index, + infrared->app_state.current_button_index); +} + +static void infrared_scene_edit_move_finished_callback(void* context) { + InfraredApp* infrared = context; + view_dispatcher_send_custom_event( + infrared->view_dispatcher, InfraredCustomEventTypeTaskFinished); +} + static void infrared_scene_edit_move_button_callback( uint32_t index_old, uint32_t index_new, @@ -38,25 +56,26 @@ bool infrared_scene_edit_move_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == InfraredCustomEventTypeButtonSelected) { - infrared_show_loading_popup(infrared, true); - const bool button_moved = infrared_remote_move_signal( - infrared->remote, - infrared->app_state.prev_button_index, - infrared->app_state.current_button_index); - infrared_show_loading_popup(infrared, false); - - if(!button_moved) { - infrared_show_error_message( - infrared, - "Failed to move\n\"%s\"", - infrared_remote_get_signal_name( - infrared->remote, infrared->app_state.current_button_index)); + view_stack_add_view(infrared->view_stack, loading_get_view(infrared->loading)); + // Move button in a separate thread + concurrent_runner_start( + BUTTON_MOVE_STACK_SIZE, + infrared_scene_edit_move_task_callback, + infrared_scene_edit_move_finished_callback, + infrared); + + } else if(event.event == InfraredCustomEventTypeTaskFinished) { + view_stack_remove_view(infrared->view_stack, loading_get_view(infrared->loading)); + + if(!infrared->app_state.is_task_success) { + const char* signal_name = infrared_remote_get_signal_name( + infrared->remote, infrared->app_state.current_button_index); + infrared_show_error_message(infrared, "Failed to move\n\"%s\"", signal_name); scene_manager_search_and_switch_to_previous_scene( infrared->scene_manager, InfraredSceneRemoteList); } - - consumed = true; } + consumed = true; } return consumed; diff --git a/applications/main/infrared/scenes/infrared_scene_edit_rename.c b/applications/main/infrared/scenes/infrared_scene_edit_rename.c index 178690926d4..90af06025ce 100644 --- a/applications/main/infrared/scenes/infrared_scene_edit_rename.c +++ b/applications/main/infrared/scenes/infrared_scene_edit_rename.c @@ -1,7 +1,29 @@ #include "../infrared_app_i.h" #include + #include +#include + +#define REMOTE_RENAME_STACK_SIZE (2048UL) + +static void infrared_scene_edit_rename_button_callback(void* context) { + InfraredApp* infrared = context; + infrared->app_state.is_task_success = infrared_remote_rename_signal( + infrared->remote, infrared->app_state.current_button_index, infrared->text_store[0]); +} + +static void infrared_scene_edit_rename_remote_callback(void* context) { + InfraredApp* infrared = context; + infrared->app_state.is_task_success = + infrared_rename_current_remote(infrared, infrared->text_store[0]); +} + +static void infrared_scene_edit_rename_finished_callback(void* context) { + InfraredApp* infrared = context; + view_dispatcher_send_custom_event( + infrared->view_dispatcher, InfraredCustomEventTypeTaskFinished); +} void infrared_scene_edit_rename_on_enter(void* context) { InfraredApp* infrared = context; @@ -61,30 +83,39 @@ void infrared_scene_edit_rename_on_enter(void* context) { bool infrared_scene_edit_rename_on_event(void* context, SceneManagerEvent event) { InfraredApp* infrared = context; - InfraredRemote* remote = infrared->remote; SceneManager* scene_manager = infrared->scene_manager; - InfraredAppState* app_state = &infrared->app_state; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { + InfraredAppState* app_state = &infrared->app_state; + const InfraredEditTarget edit_target = app_state->edit_target; + if(event.event == InfraredCustomEventTypeTextEditDone) { - bool success = false; - const InfraredEditTarget edit_target = app_state->edit_target; + view_stack_add_view(infrared->view_stack, loading_get_view(infrared->loading)); + if(edit_target == InfraredEditTargetButton) { - const int32_t current_button_index = app_state->current_button_index; - furi_assert(current_button_index != InfraredButtonIndexNone); - infrared_show_loading_popup(infrared, true); - success = infrared_remote_rename_signal( - remote, current_button_index, infrared->text_store[0]); - infrared_show_loading_popup(infrared, false); - app_state->current_button_index = InfraredButtonIndexNone; + furi_assert(app_state->current_button_index != InfraredButtonIndexNone); + // Rename button in a separate thread + concurrent_runner_start( + REMOTE_RENAME_STACK_SIZE, + infrared_scene_edit_rename_button_callback, + infrared_scene_edit_rename_finished_callback, + infrared); } else if(edit_target == InfraredEditTargetRemote) { - success = infrared_rename_current_remote(infrared, infrared->text_store[0]); + // Rename remote in a separate thread (for consistency) + concurrent_runner_start( + REMOTE_RENAME_STACK_SIZE, + infrared_scene_edit_rename_remote_callback, + infrared_scene_edit_rename_finished_callback, + infrared); } else { furi_crash(); } - if(success) { + } else if(event.event == InfraredCustomEventTypeTaskFinished) { + view_stack_remove_view(infrared->view_stack, loading_get_view(infrared->loading)); + + if(app_state->is_task_success) { scene_manager_next_scene(scene_manager, InfraredSceneEditRenameDone); } else { infrared_show_error_message( @@ -94,8 +125,10 @@ bool infrared_scene_edit_rename_on_event(void* context, SceneManagerEvent event) scene_manager_search_and_switch_to_previous_scene( scene_manager, InfraredSceneRemoteList); } - consumed = true; + + app_state->current_button_index = InfraredButtonIndexNone; } + consumed = true; } return consumed; diff --git a/applications/main/infrared/scenes/infrared_scene_remote_list.c b/applications/main/infrared/scenes/infrared_scene_remote_list.c index 4328df371bd..1152b5a174d 100644 --- a/applications/main/infrared/scenes/infrared_scene_remote_list.c +++ b/applications/main/infrared/scenes/infrared_scene_remote_list.c @@ -6,14 +6,14 @@ static void infrared_scene_remote_list_load_callback(void* context) { InfraredApp* infrared = context; - infrared->app_state.is_load_success = + infrared->app_state.is_task_success = infrared_remote_load(infrared->remote, furi_string_get_cstr(infrared->file_path)); } static void infrared_scene_remote_list_load_finished_callback(void* context) { InfraredApp* infrared = context; view_dispatcher_send_custom_event( - infrared->view_dispatcher, InfraredCustomEventTypeLoadFinished); + infrared->view_dispatcher, InfraredCustomEventTypeTaskFinished); } static void infrared_scene_remote_list_select_and_load(InfraredApp* infrared) { @@ -48,10 +48,10 @@ bool infrared_scene_remote_list_on_event(void* context, SceneManagerEvent event) bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - if(event.event == InfraredCustomEventTypeLoadFinished) { + if(event.event == InfraredCustomEventTypeTaskFinished) { view_stack_remove_view(infrared->view_stack, loading_get_view(infrared->loading)); - if(infrared->app_state.is_load_success) { + if(infrared->app_state.is_task_success) { scene_manager_next_scene(infrared->scene_manager, InfraredSceneRemote); } else { infrared_show_error_message( From a419dd07af23b37cc0431910409e89b336787bdc Mon Sep 17 00:00:00 2001 From: Georgii Surkov Date: Thu, 7 Mar 2024 21:37:59 +0300 Subject: [PATCH 03/11] Fix python formatting --- lib/toolbox/SConscript | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/toolbox/SConscript b/lib/toolbox/SConscript index 891483f7620..3d8f68a1f5d 100644 --- a/lib/toolbox/SConscript +++ b/lib/toolbox/SConscript @@ -35,7 +35,7 @@ env.Append( File("simple_array.h"), File("bit_buffer.h"), File("keys_dict.h"), - File("concurrent_runner.h") + File("concurrent_runner.h"), ], ) From 66a644fa01080691d8cac2c5ea78153b697a075e Mon Sep 17 00:00:00 2001 From: Georgii Surkov Date: Fri, 8 Mar 2024 13:19:22 +0300 Subject: [PATCH 04/11] Clean up code --- .../common/infrared_scene_universal_common.c | 12 ++-- .../scenes/infrared_scene_edit_delete.c | 63 ++++++++----------- .../scenes/infrared_scene_edit_move.c | 8 +-- .../scenes/infrared_scene_edit_rename.c | 62 ++++++++---------- .../scenes/infrared_scene_remote_list.c | 12 ++-- 5 files changed, 69 insertions(+), 88 deletions(-) diff --git a/applications/main/infrared/scenes/common/infrared_scene_universal_common.c b/applications/main/infrared/scenes/common/infrared_scene_universal_common.c index 2b7c55fda32..6bee870fc10 100644 --- a/applications/main/infrared/scenes/common/infrared_scene_universal_common.c +++ b/applications/main/infrared/scenes/common/infrared_scene_universal_common.c @@ -3,7 +3,7 @@ #include #include -#define BRUTE_FORCE_LOADER_STACK_SIZE (2048UL) +#define TASK_STACK_SIZE (2048UL) void infrared_scene_universal_common_item_callback(void* context, uint32_t index) { InfraredApp* infrared = context; @@ -35,13 +35,13 @@ static void infrared_scene_universal_common_hide_popup(InfraredApp* infrared) { infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStop); } -static void infrared_scene_universal_common_load_callback(void* context) { +static void infrared_scene_universal_common_task_callback(void* context) { InfraredApp* infrared = context; infrared->app_state.is_task_success = infrared_brute_force_calculate_messages(infrared->brute_force); } -static void infrared_scene_universal_common_load_finished_callback(void* context) { +static void infrared_scene_universal_common_task_finished_callback(void* context) { InfraredApp* infrared = context; view_dispatcher_send_custom_event( infrared->view_dispatcher, @@ -59,9 +59,9 @@ void infrared_scene_universal_common_on_enter(void* context) { // Load universal remote data in background concurrent_runner_start( - BRUTE_FORCE_LOADER_STACK_SIZE, - infrared_scene_universal_common_load_callback, - infrared_scene_universal_common_load_finished_callback, + TASK_STACK_SIZE, + infrared_scene_universal_common_task_callback, + infrared_scene_universal_common_task_finished_callback, context); } diff --git a/applications/main/infrared/scenes/infrared_scene_edit_delete.c b/applications/main/infrared/scenes/infrared_scene_edit_delete.c index 61f507e2c22..10836ae137e 100644 --- a/applications/main/infrared/scenes/infrared_scene_edit_delete.c +++ b/applications/main/infrared/scenes/infrared_scene_edit_delete.c @@ -2,7 +2,7 @@ #include -#define REMOTE_DELETE_STACK_SIZE (2048UL) +#define TASK_STACK_SIZE (2048UL) static void infrared_scene_edit_delete_dialog_result_callback(DialogExResult result, void* context) { @@ -10,18 +10,23 @@ static void view_dispatcher_send_custom_event(infrared->view_dispatcher, result); } -static void infrared_scene_edit_delete_button_callback(void* context) { +static void infrared_scene_edit_delete_task_callback(void* context) { InfraredApp* infrared = context; - infrared->app_state.is_task_success = - infrared_remote_delete_signal(infrared->remote, infrared->app_state.current_button_index); -} + InfraredAppState* app_state = &infrared->app_state; + const InfraredEditTarget edit_target = app_state->edit_target; -static void infrared_scene_edit_delete_remote_callback(void* context) { - InfraredApp* infrared = context; - infrared->app_state.is_task_success = infrared_remote_remove(infrared->remote); + if(edit_target == InfraredEditTargetButton) { + furi_assert(app_state->current_button_index != InfraredButtonIndexNone); + app_state->is_task_success = + infrared_remote_delete_signal(infrared->remote, app_state->current_button_index); + } else if(edit_target == InfraredEditTargetRemote) { + app_state->is_task_success = infrared_remote_remove(infrared->remote); + } else { + furi_crash(); + } } -static void infrared_scene_edit_delete_finished_callback(void* context) { +static void infrared_scene_edit_delete_task_finished_callback(void* context) { InfraredApp* infrared = context; view_dispatcher_send_custom_event( infrared->view_dispatcher, InfraredCustomEventTypeTaskFinished); @@ -31,8 +36,8 @@ void infrared_scene_edit_delete_on_enter(void* context) { InfraredApp* infrared = context; DialogEx* dialog_ex = infrared->dialog_ex; InfraredRemote* remote = infrared->remote; - const InfraredEditTarget edit_target = infrared->app_state.edit_target; + if(edit_target == InfraredEditTargetButton) { dialog_ex_set_header(dialog_ex, "Delete Button?", 64, 0, AlignCenter, AlignTop); @@ -103,43 +108,29 @@ bool infrared_scene_edit_delete_on_event(void* context, SceneManagerEvent event) bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - InfraredAppState* app_state = &infrared->app_state; - const InfraredEditTarget edit_target = app_state->edit_target; - if(event.event == DialogExResultLeft) { scene_manager_previous_scene(scene_manager); } else if(event.event == DialogExResultRight) { view_stack_add_view(infrared->view_stack, loading_get_view(infrared->loading)); + // Delete a button or a remote in a separate thread + concurrent_runner_start( + TASK_STACK_SIZE, + infrared_scene_edit_delete_task_callback, + infrared_scene_edit_delete_task_finished_callback, + infrared); - if(edit_target == InfraredEditTargetButton) { - furi_assert(app_state->current_button_index != InfraredButtonIndexNone); - // Delete button in a separate thread - concurrent_runner_start( - REMOTE_DELETE_STACK_SIZE, - infrared_scene_edit_delete_button_callback, - infrared_scene_edit_delete_finished_callback, - infrared); - } else if(edit_target == InfraredEditTargetRemote) { - // Delete remote in a separate thread (for consistency) - concurrent_runner_start( - REMOTE_DELETE_STACK_SIZE, - infrared_scene_edit_delete_remote_callback, - infrared_scene_edit_delete_finished_callback, - infrared); - } else { - furi_crash(); - } } else if(event.event == InfraredCustomEventTypeTaskFinished) { view_stack_remove_view(infrared->view_stack, loading_get_view(infrared->loading)); + InfraredAppState* app_state = &infrared->app_state; + if(app_state->is_task_success) { scene_manager_next_scene(scene_manager, InfraredSceneEditDeleteDone); } else { - const InfraredEditTarget edit_target = app_state->edit_target; - infrared_show_error_message( - infrared, - "Failed to\ndelete %s", - edit_target == InfraredEditTargetButton ? "button" : "file"); + const char* edit_target_text = + app_state->edit_target == InfraredEditTargetButton ? "button" : "file"; + infrared_show_error_message(infrared, "Failed to\ndelete %s", edit_target_text); + const uint32_t possible_scenes[] = {InfraredSceneRemoteList, InfraredSceneStart}; scene_manager_search_and_switch_to_previous_scene_one_of( scene_manager, possible_scenes, COUNT_OF(possible_scenes)); diff --git a/applications/main/infrared/scenes/infrared_scene_edit_move.c b/applications/main/infrared/scenes/infrared_scene_edit_move.c index 18bb91cb6c5..7b51f6bfc9d 100644 --- a/applications/main/infrared/scenes/infrared_scene_edit_move.c +++ b/applications/main/infrared/scenes/infrared_scene_edit_move.c @@ -2,7 +2,7 @@ #include -#define BUTTON_MOVE_STACK_SIZE (2048UL) +#define TASK_STACK_SIZE (2048UL) static void infrared_scene_edit_move_task_callback(void* context) { InfraredApp* infrared = context; @@ -12,7 +12,7 @@ static void infrared_scene_edit_move_task_callback(void* context) { infrared->app_state.current_button_index); } -static void infrared_scene_edit_move_finished_callback(void* context) { +static void infrared_scene_edit_move_task_finished_callback(void* context) { InfraredApp* infrared = context; view_dispatcher_send_custom_event( infrared->view_dispatcher, InfraredCustomEventTypeTaskFinished); @@ -59,9 +59,9 @@ bool infrared_scene_edit_move_on_event(void* context, SceneManagerEvent event) { view_stack_add_view(infrared->view_stack, loading_get_view(infrared->loading)); // Move button in a separate thread concurrent_runner_start( - BUTTON_MOVE_STACK_SIZE, + TASK_STACK_SIZE, infrared_scene_edit_move_task_callback, - infrared_scene_edit_move_finished_callback, + infrared_scene_edit_move_task_finished_callback, infrared); } else if(event.event == InfraredCustomEventTypeTaskFinished) { diff --git a/applications/main/infrared/scenes/infrared_scene_edit_rename.c b/applications/main/infrared/scenes/infrared_scene_edit_rename.c index 90af06025ce..f9d01399de7 100644 --- a/applications/main/infrared/scenes/infrared_scene_edit_rename.c +++ b/applications/main/infrared/scenes/infrared_scene_edit_rename.c @@ -5,21 +5,26 @@ #include #include -#define REMOTE_RENAME_STACK_SIZE (2048UL) +#define TASK_STACK_SIZE (2048UL) -static void infrared_scene_edit_rename_button_callback(void* context) { +static void infrared_scene_edit_rename_task_callback(void* context) { InfraredApp* infrared = context; - infrared->app_state.is_task_success = infrared_remote_rename_signal( - infrared->remote, infrared->app_state.current_button_index, infrared->text_store[0]); -} + InfraredAppState* app_state = &infrared->app_state; + const InfraredEditTarget edit_target = app_state->edit_target; -static void infrared_scene_edit_rename_remote_callback(void* context) { - InfraredApp* infrared = context; - infrared->app_state.is_task_success = - infrared_rename_current_remote(infrared, infrared->text_store[0]); + if(edit_target == InfraredEditTargetButton) { + furi_assert(app_state->current_button_index != InfraredButtonIndexNone); + app_state->is_task_success = infrared_remote_rename_signal( + infrared->remote, app_state->current_button_index, infrared->text_store[0]); + } else if(edit_target == InfraredEditTargetRemote) { + app_state->is_task_success = + infrared_rename_current_remote(infrared, infrared->text_store[0]); + } else { + furi_crash(); + } } -static void infrared_scene_edit_rename_finished_callback(void* context) { +static void infrared_scene_edit_rename_task_finished_callback(void* context) { InfraredApp* infrared = context; view_dispatcher_send_custom_event( infrared->view_dispatcher, InfraredCustomEventTypeTaskFinished); @@ -87,41 +92,26 @@ bool infrared_scene_edit_rename_on_event(void* context, SceneManagerEvent event) bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - InfraredAppState* app_state = &infrared->app_state; - const InfraredEditTarget edit_target = app_state->edit_target; - if(event.event == InfraredCustomEventTypeTextEditDone) { view_stack_add_view(infrared->view_stack, loading_get_view(infrared->loading)); - - if(edit_target == InfraredEditTargetButton) { - furi_assert(app_state->current_button_index != InfraredButtonIndexNone); - // Rename button in a separate thread - concurrent_runner_start( - REMOTE_RENAME_STACK_SIZE, - infrared_scene_edit_rename_button_callback, - infrared_scene_edit_rename_finished_callback, - infrared); - } else if(edit_target == InfraredEditTargetRemote) { - // Rename remote in a separate thread (for consistency) - concurrent_runner_start( - REMOTE_RENAME_STACK_SIZE, - infrared_scene_edit_rename_remote_callback, - infrared_scene_edit_rename_finished_callback, - infrared); - } else { - furi_crash(); - } + // Rename a button or a remote in a separate thread + concurrent_runner_start( + TASK_STACK_SIZE, + infrared_scene_edit_rename_task_callback, + infrared_scene_edit_rename_task_finished_callback, + infrared); } else if(event.event == InfraredCustomEventTypeTaskFinished) { view_stack_remove_view(infrared->view_stack, loading_get_view(infrared->loading)); + InfraredAppState* app_state = &infrared->app_state; + if(app_state->is_task_success) { scene_manager_next_scene(scene_manager, InfraredSceneEditRenameDone); } else { - infrared_show_error_message( - infrared, - "Failed to\nrename %s", - edit_target == InfraredEditTargetButton ? "button" : "file"); + const char* edit_target_text = + app_state->edit_target == InfraredEditTargetButton ? "button" : "file"; + infrared_show_error_message(infrared, "Failed to\nrename %s", edit_target_text); scene_manager_search_and_switch_to_previous_scene( scene_manager, InfraredSceneRemoteList); } diff --git a/applications/main/infrared/scenes/infrared_scene_remote_list.c b/applications/main/infrared/scenes/infrared_scene_remote_list.c index 1152b5a174d..2deda6924ab 100644 --- a/applications/main/infrared/scenes/infrared_scene_remote_list.c +++ b/applications/main/infrared/scenes/infrared_scene_remote_list.c @@ -2,15 +2,15 @@ #include -#define REMOTE_LOADER_STACK_SIZE (2048UL) +#define TASK_STACK_SIZE (2048UL) -static void infrared_scene_remote_list_load_callback(void* context) { +static void infrared_scene_remote_list_task_callback(void* context) { InfraredApp* infrared = context; infrared->app_state.is_task_success = infrared_remote_load(infrared->remote, furi_string_get_cstr(infrared->file_path)); } -static void infrared_scene_remote_list_load_finished_callback(void* context) { +static void infrared_scene_remote_list_task_finished_callback(void* context) { InfraredApp* infrared = context; view_dispatcher_send_custom_event( infrared->view_dispatcher, InfraredCustomEventTypeTaskFinished); @@ -28,9 +28,9 @@ static void infrared_scene_remote_list_select_and_load(InfraredApp* infrared) { view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack); // Load remote in background concurrent_runner_start( - REMOTE_LOADER_STACK_SIZE, - infrared_scene_remote_list_load_callback, - infrared_scene_remote_list_load_finished_callback, + TASK_STACK_SIZE, + infrared_scene_remote_list_task_callback, + infrared_scene_remote_list_task_finished_callback, infrared); } else { scene_manager_previous_scene(infrared->scene_manager); From 2efa2a44cf2cbd0cadbbc11c1208f12f5935f2e0 Mon Sep 17 00:00:00 2001 From: Georgii Surkov Date: Fri, 8 Mar 2024 13:52:23 +0300 Subject: [PATCH 05/11] Add usage warning --- lib/toolbox/concurrent_runner.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/toolbox/concurrent_runner.h b/lib/toolbox/concurrent_runner.h index 01d50280c1d..51bc58d37d7 100644 --- a/lib/toolbox/concurrent_runner.h +++ b/lib/toolbox/concurrent_runner.h @@ -13,6 +13,10 @@ * Input and output parameters are handled through the context parameter. * ConcurrentRunner does not provide any error handling of the user-supplied * code, so it must be taken care of on the application side. + * + * @warning The application code MUST make sure that all relevant pointers + * (i.e. callbacks and the context) remain valid until the full completion + * of the task being run. */ #pragma once From af10316a4caf35b4cc188d60939abceb975bcba0 Mon Sep 17 00:00:00 2001 From: Georgii Surkov Date: Mon, 11 Mar 2024 16:41:59 +0300 Subject: [PATCH 06/11] Remove ConcurrentRunner, use a plain FuriThread instead --- applications/main/infrared/infrared_app.c | 9 ++- applications/main/infrared/infrared_app_i.h | 1 + .../common/infrared_scene_universal_common.c | 18 ++---- .../scenes/infrared_scene_edit_delete.c | 19 ++---- .../scenes/infrared_scene_edit_move.c | 20 ++----- .../scenes/infrared_scene_edit_rename.c | 19 ++---- .../scenes/infrared_scene_remote_list.c | 18 ++---- lib/toolbox/SConscript | 1 - lib/toolbox/concurrent_runner.c | 60 ------------------- lib/toolbox/concurrent_runner.h | 56 ----------------- targets/f18/api_symbols.csv | 4 +- targets/f7/api_symbols.csv | 4 +- 12 files changed, 38 insertions(+), 191 deletions(-) delete mode 100644 lib/toolbox/concurrent_runner.c delete mode 100644 lib/toolbox/concurrent_runner.h diff --git a/applications/main/infrared/infrared_app.c b/applications/main/infrared/infrared_app.c index f3975ff87aa..0a376def563 100644 --- a/applications/main/infrared/infrared_app.c +++ b/applications/main/infrared/infrared_app.c @@ -6,7 +6,8 @@ #define TAG "InfraredApp" -#define INFRARED_TX_MIN_INTERVAL_MS 50U +#define INFRARED_TX_MIN_INTERVAL_MS (50U) +#define INFRARED_TASK_STACK_SIZE (2048UL) static const NotificationSequence* infrared_notification_sequences[InfraredNotificationMessageCount] = { @@ -128,6 +129,8 @@ static void infrared_find_vacant_remote_name(FuriString* name, const char* path) static InfraredApp* infrared_alloc() { InfraredApp* infrared = malloc(sizeof(InfraredApp)); + infrared->task_thread = + furi_thread_alloc_ex("InfraredTask", INFRARED_TASK_STACK_SIZE, NULL, infrared); infrared->file_path = furi_string_alloc(); infrared->button_name = furi_string_alloc(); @@ -203,6 +206,10 @@ static InfraredApp* infrared_alloc() { static void infrared_free(InfraredApp* infrared) { furi_assert(infrared); + + furi_thread_join(infrared->task_thread); + furi_thread_free(infrared->task_thread); + ViewDispatcher* view_dispatcher = infrared->view_dispatcher; InfraredAppState* app_state = &infrared->app_state; diff --git a/applications/main/infrared/infrared_app_i.h b/applications/main/infrared/infrared_app_i.h index b23481b211c..02840564580 100644 --- a/applications/main/infrared/infrared_app_i.h +++ b/applications/main/infrared/infrared_app_i.h @@ -120,6 +120,7 @@ struct InfraredApp { Loading* loading; /**< Standard view for informing about long operations. */ InfraredProgressView* progress; /**< Custom view for showing brute force progress. */ + FuriThread* task_thread; /**< Pointer to a FuriThread instance for concurrent tasks. */ FuriString* file_path; /**< Full path to the currently loaded file. */ FuriString* button_name; /**< Name of the button requested in RPC mode. */ /** Arbitrary text storage for various inputs. */ diff --git a/applications/main/infrared/scenes/common/infrared_scene_universal_common.c b/applications/main/infrared/scenes/common/infrared_scene_universal_common.c index 6bee870fc10..be59b34320f 100644 --- a/applications/main/infrared/scenes/common/infrared_scene_universal_common.c +++ b/applications/main/infrared/scenes/common/infrared_scene_universal_common.c @@ -1,9 +1,6 @@ #include "../../infrared_app_i.h" #include -#include - -#define TASK_STACK_SIZE (2048UL) void infrared_scene_universal_common_item_callback(void* context, uint32_t index) { InfraredApp* infrared = context; @@ -35,17 +32,15 @@ static void infrared_scene_universal_common_hide_popup(InfraredApp* infrared) { infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStop); } -static void infrared_scene_universal_common_task_callback(void* context) { +static int32_t infrared_scene_universal_common_task_callback(void* context) { InfraredApp* infrared = context; infrared->app_state.is_task_success = infrared_brute_force_calculate_messages(infrared->brute_force); -} - -static void infrared_scene_universal_common_task_finished_callback(void* context) { - InfraredApp* infrared = context; view_dispatcher_send_custom_event( infrared->view_dispatcher, infrared_custom_event_pack(InfraredCustomEventTypeTaskFinished, 0)); + + return 0; } void infrared_scene_universal_common_on_enter(void* context) { @@ -58,11 +53,8 @@ void infrared_scene_universal_common_on_enter(void* context) { view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack); // Load universal remote data in background - concurrent_runner_start( - TASK_STACK_SIZE, - infrared_scene_universal_common_task_callback, - infrared_scene_universal_common_task_finished_callback, - context); + furi_thread_set_callback(infrared->task_thread, infrared_scene_universal_common_task_callback); + furi_thread_start(infrared->task_thread); } bool infrared_scene_universal_common_on_event(void* context, SceneManagerEvent event) { diff --git a/applications/main/infrared/scenes/infrared_scene_edit_delete.c b/applications/main/infrared/scenes/infrared_scene_edit_delete.c index 10836ae137e..1a229f9c3e6 100644 --- a/applications/main/infrared/scenes/infrared_scene_edit_delete.c +++ b/applications/main/infrared/scenes/infrared_scene_edit_delete.c @@ -1,16 +1,12 @@ #include "../infrared_app_i.h" -#include - -#define TASK_STACK_SIZE (2048UL) - static void infrared_scene_edit_delete_dialog_result_callback(DialogExResult result, void* context) { InfraredApp* infrared = context; view_dispatcher_send_custom_event(infrared->view_dispatcher, result); } -static void infrared_scene_edit_delete_task_callback(void* context) { +static int32_t infrared_scene_edit_delete_task_callback(void* context) { InfraredApp* infrared = context; InfraredAppState* app_state = &infrared->app_state; const InfraredEditTarget edit_target = app_state->edit_target; @@ -24,12 +20,11 @@ static void infrared_scene_edit_delete_task_callback(void* context) { } else { furi_crash(); } -} -static void infrared_scene_edit_delete_task_finished_callback(void* context) { - InfraredApp* infrared = context; view_dispatcher_send_custom_event( infrared->view_dispatcher, InfraredCustomEventTypeTaskFinished); + + return 0; } void infrared_scene_edit_delete_on_enter(void* context) { @@ -113,11 +108,9 @@ bool infrared_scene_edit_delete_on_event(void* context, SceneManagerEvent event) } else if(event.event == DialogExResultRight) { view_stack_add_view(infrared->view_stack, loading_get_view(infrared->loading)); // Delete a button or a remote in a separate thread - concurrent_runner_start( - TASK_STACK_SIZE, - infrared_scene_edit_delete_task_callback, - infrared_scene_edit_delete_task_finished_callback, - infrared); + furi_thread_set_callback( + infrared->task_thread, infrared_scene_edit_delete_task_callback); + furi_thread_start(infrared->task_thread); } else if(event.event == InfraredCustomEventTypeTaskFinished) { view_stack_remove_view(infrared->view_stack, loading_get_view(infrared->loading)); diff --git a/applications/main/infrared/scenes/infrared_scene_edit_move.c b/applications/main/infrared/scenes/infrared_scene_edit_move.c index 7b51f6bfc9d..8d6ffead123 100644 --- a/applications/main/infrared/scenes/infrared_scene_edit_move.c +++ b/applications/main/infrared/scenes/infrared_scene_edit_move.c @@ -1,21 +1,15 @@ #include "../infrared_app_i.h" -#include - -#define TASK_STACK_SIZE (2048UL) - -static void infrared_scene_edit_move_task_callback(void* context) { +static int32_t infrared_scene_edit_move_task_callback(void* context) { InfraredApp* infrared = context; infrared->app_state.is_task_success = infrared_remote_move_signal( infrared->remote, infrared->app_state.prev_button_index, infrared->app_state.current_button_index); -} - -static void infrared_scene_edit_move_task_finished_callback(void* context) { - InfraredApp* infrared = context; view_dispatcher_send_custom_event( infrared->view_dispatcher, InfraredCustomEventTypeTaskFinished); + + return 0; } static void infrared_scene_edit_move_button_callback( @@ -58,11 +52,9 @@ bool infrared_scene_edit_move_on_event(void* context, SceneManagerEvent event) { if(event.event == InfraredCustomEventTypeButtonSelected) { view_stack_add_view(infrared->view_stack, loading_get_view(infrared->loading)); // Move button in a separate thread - concurrent_runner_start( - TASK_STACK_SIZE, - infrared_scene_edit_move_task_callback, - infrared_scene_edit_move_task_finished_callback, - infrared); + furi_thread_set_callback( + infrared->task_thread, infrared_scene_edit_move_task_callback); + furi_thread_start(infrared->task_thread); } else if(event.event == InfraredCustomEventTypeTaskFinished) { view_stack_remove_view(infrared->view_stack, loading_get_view(infrared->loading)); diff --git a/applications/main/infrared/scenes/infrared_scene_edit_rename.c b/applications/main/infrared/scenes/infrared_scene_edit_rename.c index f9d01399de7..813bcbfd19c 100644 --- a/applications/main/infrared/scenes/infrared_scene_edit_rename.c +++ b/applications/main/infrared/scenes/infrared_scene_edit_rename.c @@ -1,13 +1,9 @@ #include "../infrared_app_i.h" #include - #include -#include - -#define TASK_STACK_SIZE (2048UL) -static void infrared_scene_edit_rename_task_callback(void* context) { +static int32_t infrared_scene_edit_rename_task_callback(void* context) { InfraredApp* infrared = context; InfraredAppState* app_state = &infrared->app_state; const InfraredEditTarget edit_target = app_state->edit_target; @@ -22,12 +18,11 @@ static void infrared_scene_edit_rename_task_callback(void* context) { } else { furi_crash(); } -} -static void infrared_scene_edit_rename_task_finished_callback(void* context) { - InfraredApp* infrared = context; view_dispatcher_send_custom_event( infrared->view_dispatcher, InfraredCustomEventTypeTaskFinished); + + return 0; } void infrared_scene_edit_rename_on_enter(void* context) { @@ -95,11 +90,9 @@ bool infrared_scene_edit_rename_on_event(void* context, SceneManagerEvent event) if(event.event == InfraredCustomEventTypeTextEditDone) { view_stack_add_view(infrared->view_stack, loading_get_view(infrared->loading)); // Rename a button or a remote in a separate thread - concurrent_runner_start( - TASK_STACK_SIZE, - infrared_scene_edit_rename_task_callback, - infrared_scene_edit_rename_task_finished_callback, - infrared); + furi_thread_set_callback( + infrared->task_thread, infrared_scene_edit_rename_task_callback); + furi_thread_start(infrared->task_thread); } else if(event.event == InfraredCustomEventTypeTaskFinished) { view_stack_remove_view(infrared->view_stack, loading_get_view(infrared->loading)); diff --git a/applications/main/infrared/scenes/infrared_scene_remote_list.c b/applications/main/infrared/scenes/infrared_scene_remote_list.c index 2deda6924ab..39da4909096 100644 --- a/applications/main/infrared/scenes/infrared_scene_remote_list.c +++ b/applications/main/infrared/scenes/infrared_scene_remote_list.c @@ -1,19 +1,12 @@ #include "../infrared_app_i.h" -#include - -#define TASK_STACK_SIZE (2048UL) - -static void infrared_scene_remote_list_task_callback(void* context) { +static int32_t infrared_scene_remote_list_task_callback(void* context) { InfraredApp* infrared = context; infrared->app_state.is_task_success = infrared_remote_load(infrared->remote, furi_string_get_cstr(infrared->file_path)); -} - -static void infrared_scene_remote_list_task_finished_callback(void* context) { - InfraredApp* infrared = context; view_dispatcher_send_custom_event( infrared->view_dispatcher, InfraredCustomEventTypeTaskFinished); + return 0; } static void infrared_scene_remote_list_select_and_load(InfraredApp* infrared) { @@ -27,11 +20,8 @@ static void infrared_scene_remote_list_select_and_load(InfraredApp* infrared) { view_stack_add_view(infrared->view_stack, loading_get_view(infrared->loading)); view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack); // Load remote in background - concurrent_runner_start( - TASK_STACK_SIZE, - infrared_scene_remote_list_task_callback, - infrared_scene_remote_list_task_finished_callback, - infrared); + furi_thread_set_callback(infrared->task_thread, infrared_scene_remote_list_task_callback); + furi_thread_start(infrared->task_thread); } else { scene_manager_previous_scene(infrared->scene_manager); } diff --git a/lib/toolbox/SConscript b/lib/toolbox/SConscript index 3d8f68a1f5d..121362424eb 100644 --- a/lib/toolbox/SConscript +++ b/lib/toolbox/SConscript @@ -35,7 +35,6 @@ env.Append( File("simple_array.h"), File("bit_buffer.h"), File("keys_dict.h"), - File("concurrent_runner.h"), ], ) diff --git a/lib/toolbox/concurrent_runner.c b/lib/toolbox/concurrent_runner.c deleted file mode 100644 index 67b43a607bf..00000000000 --- a/lib/toolbox/concurrent_runner.c +++ /dev/null @@ -1,60 +0,0 @@ -#include "concurrent_runner.h" - -#include - -#include -#include - -typedef struct { - FuriThread* thread; - ConcurrentRunnerCallback run_callback; - ConcurrentRunnerCallback finished_callback; - void* context; -} ConcurrentRunner; - -static void concurrent_runner_timer_callback(void* context, uint32_t arg) { - UNUSED(arg); - - ConcurrentRunner* instance = context; - furi_thread_join(instance->thread); - - if(instance->finished_callback) { - instance->finished_callback(instance->context); - } - - furi_thread_free(instance->thread); - free(instance); -} - -static int32_t concurrent_runner_thread_callback(void* context) { - ConcurrentRunner* instance = context; - instance->run_callback(instance->context); - return 0; -} - -static void concurrent_runner_thread_state_callback(FuriThreadState state, void* context) { - if(state == FuriThreadStateStopped) { - furi_timer_pending_callback(concurrent_runner_timer_callback, context, 0); - } -} - -void concurrent_runner_start( - uint32_t stack_size, - ConcurrentRunnerCallback run_callback, - ConcurrentRunnerCallback finished_callback, - void* context) { - furi_check(run_callback); - - ConcurrentRunner* instance = malloc(sizeof(ConcurrentRunner)); - - instance->thread = furi_thread_alloc_ex( - "ConcurrentRunner", stack_size, concurrent_runner_thread_callback, instance); - furi_thread_set_state_callback(instance->thread, concurrent_runner_thread_state_callback); - furi_thread_set_state_context(instance->thread, instance); - - instance->run_callback = run_callback; - instance->finished_callback = finished_callback; - instance->context = context; - - furi_thread_start(instance->thread); -} diff --git a/lib/toolbox/concurrent_runner.h b/lib/toolbox/concurrent_runner.h deleted file mode 100644 index 51bc58d37d7..00000000000 --- a/lib/toolbox/concurrent_runner.h +++ /dev/null @@ -1,56 +0,0 @@ -/** - * @file concurrent_runner.h - * @brief Helper for running code in a separate thread. - * - * Sometimes it is necessary to quickly spawn a thread, run a blocking - * operation (such as loading a file or processing a large chunk of data) - * and delete it upon completion. - * - * The ConcurrentRunner helper automates this task by taking a function - * and running it in a separate thread. Another optional function - * can be provided to notify the caller about completion of the task. - * - * Input and output parameters are handled through the context parameter. - * ConcurrentRunner does not provide any error handling of the user-supplied - * code, so it must be taken care of on the application side. - * - * @warning The application code MUST make sure that all relevant pointers - * (i.e. callbacks and the context) remain valid until the full completion - * of the task being run. - */ -#pragma once - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Callback type declaration for use with ConcurrentRunner. - * - * @param[in,out] context Pointer to a user-specific object. - */ -typedef void (*ConcurrentRunnerCallback)(void* context); - -/** - * @brief Execute a function in a separate thread. - * - * If completion notification is not necessary, pass NULL to finished_callback. - * - * @warning Setting stack_size too low will lead to a system crash. - * - * @param[in] stack_size size of the created thread, in bytes. - * @param[in] run_callback pointer to a function to be run in a separate thread. - * @param[in] finished_callback optional pointer to a function to be called upon completion of the task. - * @param[in,out] context pointer to a user-specific object. Will be passed to both callbacks. - */ -void concurrent_runner_start( - uint32_t stack_size, - ConcurrentRunnerCallback run_callback, - ConcurrentRunnerCallback finished_callback, - void* context); - -#ifdef __cplusplus -} -#endif diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index 678a65c3cde..bdfa8c7a4ee 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,58.1,, +Version,+,58.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -148,7 +148,6 @@ Header,+,lib/toolbox/api_lock.h,, Header,+,lib/toolbox/args.h,, Header,+,lib/toolbox/bit_buffer.h,, Header,+,lib/toolbox/compress.h,, -Header,+,lib/toolbox/concurrent_runner.h,, Header,+,lib/toolbox/crc32_calc.h,, Header,+,lib/toolbox/dir_walk.h,, Header,+,lib/toolbox/float_tools.h,, @@ -775,7 +774,6 @@ Function,+,compress_free,void,Compress* Function,+,compress_icon_alloc,CompressIcon*, Function,+,compress_icon_decode,void,"CompressIcon*, const uint8_t*, uint8_t**" Function,+,compress_icon_free,void,CompressIcon* -Function,+,concurrent_runner_start,void,"uint32_t, ConcurrentRunnerCallback, ConcurrentRunnerCallback, void*" Function,-,copysign,double,"double, double" Function,-,copysignf,float,"float, float" Function,-,copysignl,long double,"long double, long double" diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index c3f32585870..d856dc6948a 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,58.1,, +Version,+,58.0,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, @@ -211,7 +211,6 @@ Header,+,lib/toolbox/api_lock.h,, Header,+,lib/toolbox/args.h,, Header,+,lib/toolbox/bit_buffer.h,, Header,+,lib/toolbox/compress.h,, -Header,+,lib/toolbox/concurrent_runner.h,, Header,+,lib/toolbox/crc32_calc.h,, Header,+,lib/toolbox/dir_walk.h,, Header,+,lib/toolbox/float_tools.h,, @@ -843,7 +842,6 @@ Function,+,compress_free,void,Compress* Function,+,compress_icon_alloc,CompressIcon*, Function,+,compress_icon_decode,void,"CompressIcon*, const uint8_t*, uint8_t**" Function,+,compress_icon_free,void,CompressIcon* -Function,+,concurrent_runner_start,void,"uint32_t, ConcurrentRunnerCallback, ConcurrentRunnerCallback, void*" Function,-,copysign,double,"double, double" Function,-,copysignf,float,"float, float" Function,-,copysignl,long double,"long double, long double" From 53dbee793e820050a3cf9064a3ce731ef9dea87d Mon Sep 17 00:00:00 2001 From: Georgii Surkov Date: Mon, 11 Mar 2024 17:43:02 +0300 Subject: [PATCH 07/11] Load remotes asynchronously in RPC mode as well --- .../scenes/infrared_scene_edit_move.c | 2 +- .../scenes/infrared_scene_remote_list.c | 9 +- .../main/infrared/scenes/infrared_scene_rpc.c | 93 ++++++++++++------- 3 files changed, 67 insertions(+), 37 deletions(-) diff --git a/applications/main/infrared/scenes/infrared_scene_edit_move.c b/applications/main/infrared/scenes/infrared_scene_edit_move.c index 8d6ffead123..ff9d52388f5 100644 --- a/applications/main/infrared/scenes/infrared_scene_edit_move.c +++ b/applications/main/infrared/scenes/infrared_scene_edit_move.c @@ -51,7 +51,7 @@ bool infrared_scene_edit_move_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == InfraredCustomEventTypeButtonSelected) { view_stack_add_view(infrared->view_stack, loading_get_view(infrared->loading)); - // Move button in a separate thread + // Move the button in a separate thread furi_thread_set_callback( infrared->task_thread, infrared_scene_edit_move_task_callback); furi_thread_start(infrared->task_thread); diff --git a/applications/main/infrared/scenes/infrared_scene_remote_list.c b/applications/main/infrared/scenes/infrared_scene_remote_list.c index 39da4909096..39c49090c2c 100644 --- a/applications/main/infrared/scenes/infrared_scene_remote_list.c +++ b/applications/main/infrared/scenes/infrared_scene_remote_list.c @@ -14,14 +14,17 @@ static void infrared_scene_remote_list_select_and_load(InfraredApp* infrared) { dialog_file_browser_set_basic_options(&browser_options, INFRARED_APP_EXTENSION, &I_ir_10px); browser_options.base_path = INFRARED_APP_FOLDER; - if(dialog_file_browser_show( - infrared->dialogs, infrared->file_path, infrared->file_path, &browser_options)) { + const bool file_selected = dialog_file_browser_show( + infrared->dialogs, infrared->file_path, infrared->file_path, &browser_options); + + if(file_selected) { view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical); view_stack_add_view(infrared->view_stack, loading_get_view(infrared->loading)); view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack); - // Load remote in background + // Load the remote in a separate thread furi_thread_set_callback(infrared->task_thread, infrared_scene_remote_list_task_callback); furi_thread_start(infrared->task_thread); + } else { scene_manager_previous_scene(infrared->scene_manager); } diff --git a/applications/main/infrared/scenes/infrared_scene_rpc.c b/applications/main/infrared/scenes/infrared_scene_rpc.c index f3408fba4dd..0c7fd65a934 100644 --- a/applications/main/infrared/scenes/infrared_scene_rpc.c +++ b/applications/main/infrared/scenes/infrared_scene_rpc.c @@ -9,6 +9,15 @@ typedef enum { InfraredRpcStateSending, } InfraredRpcState; +static int32_t infrared_scene_rpc_task_callback(void* context) { + InfraredApp* infrared = context; + infrared->app_state.is_task_success = + infrared_remote_load(infrared->remote, furi_string_get_cstr(infrared->file_path)); + view_dispatcher_send_custom_event( + infrared->view_dispatcher, InfraredCustomEventTypeTaskFinished); + return 0; +} + void infrared_scene_rpc_on_enter(void* context) { InfraredApp* infrared = context; Popup* popup = infrared->popup; @@ -21,7 +30,8 @@ void infrared_scene_rpc_on_enter(void* context) { popup_set_context(popup, context); popup_set_callback(popup, infrared_popup_closed_callback); - view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewPopup); + view_stack_add_view(infrared->view_stack, popup_get_view(infrared->popup)); + view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack); scene_manager_set_scene_state(infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateIdle); @@ -33,76 +43,91 @@ bool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) { bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - consumed = true; - InfraredRpcState state = + InfraredAppState* app_state = &infrared->app_state; + InfraredRpcState rpc_state = scene_manager_get_scene_state(infrared->scene_manager, InfraredSceneRpc); - if(event.event == InfraredCustomEventTypeBackPressed) { - view_dispatcher_stop(infrared->view_dispatcher); - } else if(event.event == InfraredCustomEventTypePopupClosed) { - view_dispatcher_stop(infrared->view_dispatcher); - } else if(event.event == InfraredCustomEventTypeRpcLoadFile) { - bool result = false; - if(state == InfraredRpcStateIdle) { - result = infrared_remote_load( - infrared->remote, furi_string_get_cstr(infrared->file_path)); - if(result) { - scene_manager_set_scene_state( - infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateLoaded); - } + + if(event.event == InfraredCustomEventTypeTaskFinished) { + view_stack_remove_view(infrared->view_stack, loading_get_view(infrared->loading)); + + if(app_state->is_task_success) { + const char* remote_name = infrared_remote_get_name(infrared->remote); + infrared_text_store_set(infrared, 0, "loaded\n%s", remote_name); + scene_manager_set_scene_state( + infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateLoaded); + + } else { + infrared_text_store_set( + infrared, 0, "failed to load\n%s", furi_string_get_cstr(infrared->file_path)); } - const char* remote_name = infrared_remote_get_name(infrared->remote); - infrared_text_store_set(infrared, 0, "loaded\n%s", remote_name); popup_set_text( infrared->popup, infrared->text_store[0], 89, 44, AlignCenter, AlignTop); - rpc_system_app_confirm(infrared->rpc_ctx, result); + rpc_system_app_confirm(infrared->rpc_ctx, app_state->is_task_success); + + } else if(event.event == InfraredCustomEventTypeRpcLoadFile) { + if(rpc_state == InfraredRpcStateIdle) { + view_stack_add_view(infrared->view_stack, loading_get_view(infrared->loading)); + + // Load the remote in a separate thread + furi_thread_set_callback(infrared->task_thread, infrared_scene_rpc_task_callback); + furi_thread_start(infrared->task_thread); + } + } else if( event.event == InfraredCustomEventTypeRpcButtonPressName || event.event == InfraredCustomEventTypeRpcButtonPressIndex) { bool result = false; - if(state == InfraredRpcStateLoaded) { + if(rpc_state == InfraredRpcStateLoaded) { if(event.event == InfraredCustomEventTypeRpcButtonPressName) { const char* button_name = furi_string_get_cstr(infrared->button_name); size_t index; const bool index_found = infrared_remote_get_signal_index(infrared->remote, button_name, &index); - infrared->app_state.current_button_index = - index_found ? (signed)index : InfraredButtonIndexNone; + app_state->current_button_index = index_found ? (signed)index : + InfraredButtonIndexNone; FURI_LOG_D(TAG, "Sending signal with name \"%s\"", button_name); } else { FURI_LOG_D( - TAG, - "Sending signal with index \"%ld\"", - infrared->app_state.current_button_index); + TAG, "Sending signal with index \"%ld\"", app_state->current_button_index); } if(infrared->app_state.current_button_index != InfraredButtonIndexNone) { - infrared_tx_start_button_index( - infrared, infrared->app_state.current_button_index); + infrared_tx_start_button_index(infrared, app_state->current_button_index); scene_manager_set_scene_state( infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateSending); result = true; } } rpc_system_app_confirm(infrared->rpc_ctx, result); + } else if(event.event == InfraredCustomEventTypeRpcButtonRelease) { bool result = false; - if(state == InfraredRpcStateSending) { + + if(rpc_state == InfraredRpcStateSending) { infrared_tx_stop(infrared); result = true; scene_manager_set_scene_state( infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateLoaded); } + rpc_system_app_confirm(infrared->rpc_ctx, result); - } else if(event.event == InfraredCustomEventTypeRpcExit) { - scene_manager_stop(infrared->scene_manager); - view_dispatcher_stop(infrared->view_dispatcher); - rpc_system_app_confirm(infrared->rpc_ctx, true); - } else if(event.event == InfraredCustomEventTypeRpcSessionClose) { + + } else if( + event.event == InfraredCustomEventTypeRpcExit || + event.event == InfraredCustomEventTypeRpcSessionClose || + event.event == InfraredCustomEventTypePopupClosed) { scene_manager_stop(infrared->scene_manager); view_dispatcher_stop(infrared->view_dispatcher); + + if(event.event == InfraredCustomEventTypeRpcExit) { + rpc_system_app_confirm(infrared->rpc_ctx, true); + } } + + consumed = true; } + return consumed; } @@ -112,5 +137,7 @@ void infrared_scene_rpc_on_exit(void* context) { InfraredRpcStateSending) { infrared_tx_stop(infrared); } + + view_stack_remove_view(infrared->view_stack, popup_get_view(infrared->popup)); popup_reset(infrared->popup); } From 09b76d80c26e6d271eb2c8a66b391158a27170d2 Mon Sep 17 00:00:00 2001 From: Georgii Surkov Date: Mon, 11 Mar 2024 17:46:09 +0300 Subject: [PATCH 08/11] Reorder code for clarity --- .../main/infrared/scenes/infrared_scene_rpc.c | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/applications/main/infrared/scenes/infrared_scene_rpc.c b/applications/main/infrared/scenes/infrared_scene_rpc.c index 0c7fd65a934..7314163efc0 100644 --- a/applications/main/infrared/scenes/infrared_scene_rpc.c +++ b/applications/main/infrared/scenes/infrared_scene_rpc.c @@ -47,7 +47,16 @@ bool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) { InfraredRpcState rpc_state = scene_manager_get_scene_state(infrared->scene_manager, InfraredSceneRpc); - if(event.event == InfraredCustomEventTypeTaskFinished) { + if(event.event == InfraredCustomEventTypeRpcLoadFile) { + if(rpc_state == InfraredRpcStateIdle) { + view_stack_add_view(infrared->view_stack, loading_get_view(infrared->loading)); + + // Load the remote in a separate thread + furi_thread_set_callback(infrared->task_thread, infrared_scene_rpc_task_callback); + furi_thread_start(infrared->task_thread); + } + + } else if(event.event == InfraredCustomEventTypeTaskFinished) { view_stack_remove_view(infrared->view_stack, loading_get_view(infrared->loading)); if(app_state->is_task_success) { @@ -66,15 +75,6 @@ bool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) { rpc_system_app_confirm(infrared->rpc_ctx, app_state->is_task_success); - } else if(event.event == InfraredCustomEventTypeRpcLoadFile) { - if(rpc_state == InfraredRpcStateIdle) { - view_stack_add_view(infrared->view_stack, loading_get_view(infrared->loading)); - - // Load the remote in a separate thread - furi_thread_set_callback(infrared->task_thread, infrared_scene_rpc_task_callback); - furi_thread_start(infrared->task_thread); - } - } else if( event.event == InfraredCustomEventTypeRpcButtonPressName || event.event == InfraredCustomEventTypeRpcButtonPressIndex) { From 1e4eeee8e44a40cea12e882743770c9043aad8aa Mon Sep 17 00:00:00 2001 From: Georgii Surkov Date: Mon, 11 Mar 2024 19:06:55 +0300 Subject: [PATCH 09/11] Clean up, use thread return code to report errors --- applications/main/infrared/infrared_app.c | 12 ++++++++++ applications/main/infrared/infrared_app_i.h | 22 ++++++++++++++++++- .../common/infrared_scene_universal_common.c | 15 +++++-------- .../scenes/infrared_scene_edit_delete.c | 17 ++++++-------- .../scenes/infrared_scene_edit_move.c | 13 +++++------ .../scenes/infrared_scene_edit_rename.c | 18 ++++++--------- .../scenes/infrared_scene_remote_list.c | 13 +++++------ .../main/infrared/scenes/infrared_scene_rpc.c | 15 +++++-------- 8 files changed, 69 insertions(+), 56 deletions(-) diff --git a/applications/main/infrared/infrared_app.c b/applications/main/infrared/infrared_app.c index 0a376def563..50534c660d2 100644 --- a/applications/main/infrared/infrared_app.c +++ b/applications/main/infrared/infrared_app.c @@ -384,6 +384,18 @@ void infrared_tx_stop(InfraredApp* infrared) { infrared->app_state.last_transmit_time = furi_get_tick(); } +void infrared_blocking_task_start(InfraredApp* infrared, FuriThreadCallback callback) { + view_stack_add_view(infrared->view_stack, loading_get_view(infrared->loading)); + furi_thread_set_callback(infrared->task_thread, callback); + furi_thread_start(infrared->task_thread); +} + +bool infrared_blocking_task_finalize(InfraredApp* infrared) { + furi_thread_join(infrared->task_thread); + view_stack_remove_view(infrared->view_stack, loading_get_view(infrared->loading)); + return furi_thread_get_return_code(infrared->task_thread); +} + void infrared_text_store_set(InfraredApp* infrared, uint32_t bank, const char* fmt, ...) { va_list args; va_start(args, fmt); diff --git a/applications/main/infrared/infrared_app_i.h b/applications/main/infrared/infrared_app_i.h index 02840564580..83746b5d76b 100644 --- a/applications/main/infrared/infrared_app_i.h +++ b/applications/main/infrared/infrared_app_i.h @@ -82,7 +82,6 @@ typedef struct { bool is_learning_new_remote; /**< Learning new remote or adding to an existing one. */ bool is_debug_enabled; /**< Whether to enable or disable debugging features. */ bool is_transmitting; /**< Whether a signal is currently being transmitted. */ - bool is_task_success; /**< Whether the last concurrent operation was successful. */ InfraredEditTarget edit_target : 8; /**< Selected editing target (a remote or a button). */ InfraredEditMode edit_mode : 8; /**< Selected editing operation (rename or delete). */ int32_t current_button_index; /**< Selected button index (move destination). */ @@ -211,6 +210,27 @@ void infrared_tx_start_button_index(InfraredApp* infrared, size_t button_index); */ void infrared_tx_stop(InfraredApp* infrared); +/** + * @brief Start a blocking task in a separate thread. + * + * If a ViewStack is currently on screen, a busy "Hourglass" animation + * will be shown and no input will be accepted until completion. + * + * @param[in,out] infrared pointer to the application instance. + * @param[in] callback pointer to the function to be run in the thread. + */ +void infrared_blocking_task_start(InfraredApp* infrared, FuriThreadCallback callback); + +/** + * @brief Wait for a blocking task to finish. + * + * The busy animation will be hidden and input will be accepted again. + * + * @param[in,out] infrared pointer to the application instance. + * @return true if the blocking task finished successfully, false otherwise. + */ +bool infrared_blocking_task_finalize(InfraredApp* infrared); + /** * @brief Set the internal text store with formatted text. * diff --git a/applications/main/infrared/scenes/common/infrared_scene_universal_common.c b/applications/main/infrared/scenes/common/infrared_scene_universal_common.c index be59b34320f..9fc48bd46bd 100644 --- a/applications/main/infrared/scenes/common/infrared_scene_universal_common.c +++ b/applications/main/infrared/scenes/common/infrared_scene_universal_common.c @@ -34,27 +34,22 @@ static void infrared_scene_universal_common_hide_popup(InfraredApp* infrared) { static int32_t infrared_scene_universal_common_task_callback(void* context) { InfraredApp* infrared = context; - infrared->app_state.is_task_success = - infrared_brute_force_calculate_messages(infrared->brute_force); + const bool success = infrared_brute_force_calculate_messages(infrared->brute_force); view_dispatcher_send_custom_event( infrared->view_dispatcher, infrared_custom_event_pack(InfraredCustomEventTypeTaskFinished, 0)); - return 0; + return success; } void infrared_scene_universal_common_on_enter(void* context) { InfraredApp* infrared = context; view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical); - view_stack_add_view(infrared->view_stack, button_panel_get_view(infrared->button_panel)); - view_stack_add_view(infrared->view_stack, loading_get_view(infrared->loading)); - view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack); // Load universal remote data in background - furi_thread_set_callback(infrared->task_thread, infrared_scene_universal_common_task_callback); - furi_thread_start(infrared->task_thread); + infrared_blocking_task_start(infrared, infrared_scene_universal_common_task_callback); } bool infrared_scene_universal_common_on_event(void* context, SceneManagerEvent event) { @@ -99,9 +94,9 @@ bool infrared_scene_universal_common_on_event(void* context, SceneManagerEvent e scene_manager_next_scene(scene_manager, InfraredSceneErrorDatabases); } } else if(event_type == InfraredCustomEventTypeTaskFinished) { - view_stack_remove_view(infrared->view_stack, loading_get_view(infrared->loading)); + const bool task_success = infrared_blocking_task_finalize(infrared); - if(!infrared->app_state.is_task_success) { + if(!task_success) { scene_manager_next_scene(infrared->scene_manager, InfraredSceneErrorDatabases); } } diff --git a/applications/main/infrared/scenes/infrared_scene_edit_delete.c b/applications/main/infrared/scenes/infrared_scene_edit_delete.c index 1a229f9c3e6..8dc4ab6f9f8 100644 --- a/applications/main/infrared/scenes/infrared_scene_edit_delete.c +++ b/applications/main/infrared/scenes/infrared_scene_edit_delete.c @@ -11,12 +11,12 @@ static int32_t infrared_scene_edit_delete_task_callback(void* context) { InfraredAppState* app_state = &infrared->app_state; const InfraredEditTarget edit_target = app_state->edit_target; + bool success; if(edit_target == InfraredEditTargetButton) { furi_assert(app_state->current_button_index != InfraredButtonIndexNone); - app_state->is_task_success = - infrared_remote_delete_signal(infrared->remote, app_state->current_button_index); + success = infrared_remote_delete_signal(infrared->remote, app_state->current_button_index); } else if(edit_target == InfraredEditTargetRemote) { - app_state->is_task_success = infrared_remote_remove(infrared->remote); + success = infrared_remote_remove(infrared->remote); } else { furi_crash(); } @@ -24,7 +24,7 @@ static int32_t infrared_scene_edit_delete_task_callback(void* context) { view_dispatcher_send_custom_event( infrared->view_dispatcher, InfraredCustomEventTypeTaskFinished); - return 0; + return success; } void infrared_scene_edit_delete_on_enter(void* context) { @@ -106,18 +106,15 @@ bool infrared_scene_edit_delete_on_event(void* context, SceneManagerEvent event) if(event.event == DialogExResultLeft) { scene_manager_previous_scene(scene_manager); } else if(event.event == DialogExResultRight) { - view_stack_add_view(infrared->view_stack, loading_get_view(infrared->loading)); // Delete a button or a remote in a separate thread - furi_thread_set_callback( - infrared->task_thread, infrared_scene_edit_delete_task_callback); - furi_thread_start(infrared->task_thread); + infrared_blocking_task_start(infrared, infrared_scene_edit_delete_task_callback); } else if(event.event == InfraredCustomEventTypeTaskFinished) { - view_stack_remove_view(infrared->view_stack, loading_get_view(infrared->loading)); + const bool task_success = infrared_blocking_task_finalize(infrared); InfraredAppState* app_state = &infrared->app_state; - if(app_state->is_task_success) { + if(task_success) { scene_manager_next_scene(scene_manager, InfraredSceneEditDeleteDone); } else { const char* edit_target_text = diff --git a/applications/main/infrared/scenes/infrared_scene_edit_move.c b/applications/main/infrared/scenes/infrared_scene_edit_move.c index ff9d52388f5..6de136978b6 100644 --- a/applications/main/infrared/scenes/infrared_scene_edit_move.c +++ b/applications/main/infrared/scenes/infrared_scene_edit_move.c @@ -2,14 +2,14 @@ static int32_t infrared_scene_edit_move_task_callback(void* context) { InfraredApp* infrared = context; - infrared->app_state.is_task_success = infrared_remote_move_signal( + const bool success = infrared_remote_move_signal( infrared->remote, infrared->app_state.prev_button_index, infrared->app_state.current_button_index); view_dispatcher_send_custom_event( infrared->view_dispatcher, InfraredCustomEventTypeTaskFinished); - return 0; + return success; } static void infrared_scene_edit_move_button_callback( @@ -50,16 +50,13 @@ bool infrared_scene_edit_move_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == InfraredCustomEventTypeButtonSelected) { - view_stack_add_view(infrared->view_stack, loading_get_view(infrared->loading)); // Move the button in a separate thread - furi_thread_set_callback( - infrared->task_thread, infrared_scene_edit_move_task_callback); - furi_thread_start(infrared->task_thread); + infrared_blocking_task_start(infrared, infrared_scene_edit_move_task_callback); } else if(event.event == InfraredCustomEventTypeTaskFinished) { - view_stack_remove_view(infrared->view_stack, loading_get_view(infrared->loading)); + const bool task_success = infrared_blocking_task_finalize(infrared); - if(!infrared->app_state.is_task_success) { + if(task_success) { const char* signal_name = infrared_remote_get_signal_name( infrared->remote, infrared->app_state.current_button_index); infrared_show_error_message(infrared, "Failed to move\n\"%s\"", signal_name); diff --git a/applications/main/infrared/scenes/infrared_scene_edit_rename.c b/applications/main/infrared/scenes/infrared_scene_edit_rename.c index 813bcbfd19c..2763c277738 100644 --- a/applications/main/infrared/scenes/infrared_scene_edit_rename.c +++ b/applications/main/infrared/scenes/infrared_scene_edit_rename.c @@ -8,13 +8,13 @@ static int32_t infrared_scene_edit_rename_task_callback(void* context) { InfraredAppState* app_state = &infrared->app_state; const InfraredEditTarget edit_target = app_state->edit_target; + bool success; if(edit_target == InfraredEditTargetButton) { furi_assert(app_state->current_button_index != InfraredButtonIndexNone); - app_state->is_task_success = infrared_remote_rename_signal( + success = infrared_remote_rename_signal( infrared->remote, app_state->current_button_index, infrared->text_store[0]); } else if(edit_target == InfraredEditTargetRemote) { - app_state->is_task_success = - infrared_rename_current_remote(infrared, infrared->text_store[0]); + success = infrared_rename_current_remote(infrared, infrared->text_store[0]); } else { furi_crash(); } @@ -22,7 +22,7 @@ static int32_t infrared_scene_edit_rename_task_callback(void* context) { view_dispatcher_send_custom_event( infrared->view_dispatcher, InfraredCustomEventTypeTaskFinished); - return 0; + return success; } void infrared_scene_edit_rename_on_enter(void* context) { @@ -88,18 +88,14 @@ bool infrared_scene_edit_rename_on_event(void* context, SceneManagerEvent event) if(event.type == SceneManagerEventTypeCustom) { if(event.event == InfraredCustomEventTypeTextEditDone) { - view_stack_add_view(infrared->view_stack, loading_get_view(infrared->loading)); // Rename a button or a remote in a separate thread - furi_thread_set_callback( - infrared->task_thread, infrared_scene_edit_rename_task_callback); - furi_thread_start(infrared->task_thread); + infrared_blocking_task_start(infrared, infrared_scene_edit_rename_task_callback); } else if(event.event == InfraredCustomEventTypeTaskFinished) { - view_stack_remove_view(infrared->view_stack, loading_get_view(infrared->loading)); - + const bool task_success = infrared_blocking_task_finalize(infrared); InfraredAppState* app_state = &infrared->app_state; - if(app_state->is_task_success) { + if(task_success) { scene_manager_next_scene(scene_manager, InfraredSceneEditRenameDone); } else { const char* edit_target_text = diff --git a/applications/main/infrared/scenes/infrared_scene_remote_list.c b/applications/main/infrared/scenes/infrared_scene_remote_list.c index 39c49090c2c..744409a7ab2 100644 --- a/applications/main/infrared/scenes/infrared_scene_remote_list.c +++ b/applications/main/infrared/scenes/infrared_scene_remote_list.c @@ -2,11 +2,11 @@ static int32_t infrared_scene_remote_list_task_callback(void* context) { InfraredApp* infrared = context; - infrared->app_state.is_task_success = + const bool success = infrared_remote_load(infrared->remote, furi_string_get_cstr(infrared->file_path)); view_dispatcher_send_custom_event( infrared->view_dispatcher, InfraredCustomEventTypeTaskFinished); - return 0; + return success; } static void infrared_scene_remote_list_select_and_load(InfraredApp* infrared) { @@ -19,11 +19,10 @@ static void infrared_scene_remote_list_select_and_load(InfraredApp* infrared) { if(file_selected) { view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical); - view_stack_add_view(infrared->view_stack, loading_get_view(infrared->loading)); view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack); + // Load the remote in a separate thread - furi_thread_set_callback(infrared->task_thread, infrared_scene_remote_list_task_callback); - furi_thread_start(infrared->task_thread); + infrared_blocking_task_start(infrared, infrared_scene_remote_list_task_callback); } else { scene_manager_previous_scene(infrared->scene_manager); @@ -42,9 +41,9 @@ bool infrared_scene_remote_list_on_event(void* context, SceneManagerEvent event) if(event.type == SceneManagerEventTypeCustom) { if(event.event == InfraredCustomEventTypeTaskFinished) { - view_stack_remove_view(infrared->view_stack, loading_get_view(infrared->loading)); + const bool task_success = infrared_blocking_task_finalize(infrared); - if(infrared->app_state.is_task_success) { + if(task_success) { scene_manager_next_scene(infrared->scene_manager, InfraredSceneRemote); } else { infrared_show_error_message( diff --git a/applications/main/infrared/scenes/infrared_scene_rpc.c b/applications/main/infrared/scenes/infrared_scene_rpc.c index 7314163efc0..03a2bff0107 100644 --- a/applications/main/infrared/scenes/infrared_scene_rpc.c +++ b/applications/main/infrared/scenes/infrared_scene_rpc.c @@ -11,11 +11,11 @@ typedef enum { static int32_t infrared_scene_rpc_task_callback(void* context) { InfraredApp* infrared = context; - infrared->app_state.is_task_success = + const bool success = infrared_remote_load(infrared->remote, furi_string_get_cstr(infrared->file_path)); view_dispatcher_send_custom_event( infrared->view_dispatcher, InfraredCustomEventTypeTaskFinished); - return 0; + return success; } void infrared_scene_rpc_on_enter(void* context) { @@ -49,17 +49,14 @@ bool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) { if(event.event == InfraredCustomEventTypeRpcLoadFile) { if(rpc_state == InfraredRpcStateIdle) { - view_stack_add_view(infrared->view_stack, loading_get_view(infrared->loading)); - // Load the remote in a separate thread - furi_thread_set_callback(infrared->task_thread, infrared_scene_rpc_task_callback); - furi_thread_start(infrared->task_thread); + infrared_blocking_task_start(infrared, infrared_scene_rpc_task_callback); } } else if(event.event == InfraredCustomEventTypeTaskFinished) { - view_stack_remove_view(infrared->view_stack, loading_get_view(infrared->loading)); + const bool task_success = infrared_blocking_task_finalize(infrared); - if(app_state->is_task_success) { + if(task_success) { const char* remote_name = infrared_remote_get_name(infrared->remote); infrared_text_store_set(infrared, 0, "loaded\n%s", remote_name); scene_manager_set_scene_state( @@ -73,7 +70,7 @@ bool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) { popup_set_text( infrared->popup, infrared->text_store[0], 89, 44, AlignCenter, AlignTop); - rpc_system_app_confirm(infrared->rpc_ctx, app_state->is_task_success); + rpc_system_app_confirm(infrared->rpc_ctx, task_success); } else if( event.event == InfraredCustomEventTypeRpcButtonPressName || From 7d514d992a65b5f6c5d59f45f1064ea2538ebec1 Mon Sep 17 00:00:00 2001 From: Georgii Surkov Date: Mon, 11 Mar 2024 19:12:23 +0300 Subject: [PATCH 10/11] Improve wording --- applications/main/infrared/infrared_app_i.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/applications/main/infrared/infrared_app_i.h b/applications/main/infrared/infrared_app_i.h index 83746b5d76b..bccd58608a5 100644 --- a/applications/main/infrared/infrared_app_i.h +++ b/applications/main/infrared/infrared_app_i.h @@ -222,9 +222,10 @@ void infrared_tx_stop(InfraredApp* infrared); void infrared_blocking_task_start(InfraredApp* infrared, FuriThreadCallback callback); /** - * @brief Wait for a blocking task to finish. + * @brief Wait for a blocking task to finish and receive the result. * - * The busy animation will be hidden and input will be accepted again. + * Upon the completion of a blocking task, the busy animation will be hidden + * and input will be accepted again. * * @param[in,out] infrared pointer to the application instance. * @return true if the blocking task finished successfully, false otherwise. From bbcc3917de51a4dcea9c7c0ef519515e2d4203df Mon Sep 17 00:00:00 2001 From: Georgii Surkov Date: Mon, 11 Mar 2024 19:58:56 +0300 Subject: [PATCH 11/11] Fix logical error --- applications/main/infrared/scenes/infrared_scene_edit_move.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/main/infrared/scenes/infrared_scene_edit_move.c b/applications/main/infrared/scenes/infrared_scene_edit_move.c index 6de136978b6..500f3d791ed 100644 --- a/applications/main/infrared/scenes/infrared_scene_edit_move.c +++ b/applications/main/infrared/scenes/infrared_scene_edit_move.c @@ -56,7 +56,7 @@ bool infrared_scene_edit_move_on_event(void* context, SceneManagerEvent event) { } else if(event.event == InfraredCustomEventTypeTaskFinished) { const bool task_success = infrared_blocking_task_finalize(infrared); - if(task_success) { + if(!task_success) { const char* signal_name = infrared_remote_get_signal_name( infrared->remote, infrared->app_state.current_button_index); infrared_show_error_message(infrared, "Failed to move\n\"%s\"", signal_name);