diff --git a/applications/services/desktop/desktop_i.h b/applications/services/desktop/desktop_i.h index 3306cd948..ff149ca6c 100644 --- a/applications/services/desktop/desktop_i.h +++ b/applications/services/desktop/desktop_i.h @@ -88,6 +88,7 @@ struct Desktop { bool in_transition; bool app_running; bool locked; + bool pending_launch_done; }; void desktop_lock(Desktop* desktop); diff --git a/applications/services/desktop/scenes/desktop_scene_main.c b/applications/services/desktop/scenes/desktop_scene_main.c index b329cc16d..72fb2eb21 100644 --- a/applications/services/desktop/scenes/desktop_scene_main.c +++ b/applications/services/desktop/scenes/desktop_scene_main.c @@ -3,6 +3,7 @@ #include #include #include +#include #include "../desktop_i.h" #include "../views/desktop_events.h" @@ -110,6 +111,24 @@ void desktop_scene_main_on_enter(void* context) { desktop_main_set_callback(main_view, desktop_scene_main_callback, desktop); view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdMain); + + if(!desktop->pending_launch_done) { + desktop->pending_launch_done = true; + + FuriString* pending_name = furi_string_alloc(); + FuriString* pending_args = furi_string_alloc(); + + if(loader_pending_launch_take(pending_name, pending_args)) { + FURI_LOG_I(TAG, "Starting pending app: %s", furi_string_get_cstr(pending_name)); + loader_start_detached_with_gui_error( + desktop->loader, + furi_string_get_cstr(pending_name), + furi_string_empty(pending_args) ? NULL : furi_string_get_cstr(pending_args)); + } + + furi_string_free(pending_args); + furi_string_free(pending_name); + } } bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) { diff --git a/applications/services/loader/loader.c b/applications/services/loader/loader.c index 7d60fef9a..bec4d4251 100644 --- a/applications/services/loader/loader.c +++ b/applications/services/loader/loader.c @@ -1,5 +1,6 @@ #include "loader.h" #include "loader_i.h" +#include "loader_pending.h" #include #include #include @@ -98,6 +99,7 @@ static void loader_dialog_prepare_and_show(DialogsApp* dialogs, const LoaderErro static void loader_show_gui_error( LoaderMessageLoaderStatusResult status, const char* name, + const char* args, FuriString* error_message) { furi_check(name); DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); @@ -150,13 +152,14 @@ static void loader_show_gui_error( message, "Error: Out of Memory", 64, 0, AlignCenter, AlignTop); dialog_message_set_text( message, - "Not enough RAM to run the\napp. Please reboot the device", + "Not enough free RAM.\nReboot and run the app?", 64, 13, AlignCenter, AlignTop); dialog_message_set_buttons(message, NULL, NULL, "Reboot"); if(dialog_message_show(dialogs, message) == DialogMessageButtonRight) { + loader_pending_launch_save(name, args); furi_hal_power_reset(); } break; @@ -204,7 +207,7 @@ LoaderStatus loader_start_with_gui_error(Loader* loader, const char* name, const FuriString* error_message = furi_string_alloc(); LoaderMessageLoaderStatusResult result = loader_start_internal(loader, name, args, error_message); - loader_show_gui_error(result, name, error_message); + loader_show_gui_error(result, name, args, error_message); furi_string_free(error_message); return result.value; } @@ -633,6 +636,18 @@ static LoaderMessageLoaderStatusResult loader_start_external_app( break; } + const FlipperApplicationManifest* run_manifest = + flipper_application_get_manifest(loader->app.fap); + if(memmgr_heap_get_max_free_block() < ((size_t)run_manifest->stack_size + 1024)) { + result.value = loader_make_status_error( + LoaderStatusErrorInternal, + error_message, + "Load failed, %s: no memory for stack", + path); + result.error = LoaderStatusErrorOutOfMemory; + break; + } + loader->app.thread = flipper_application_alloc_thread(loader->app.fap, args); FuriString* app_name = furi_string_alloc(); path_extract_filename_no_ext(path, app_name); @@ -991,7 +1006,7 @@ static bool loader_do_deferred_launch(Loader* loader, LoaderDeferredLaunchRecord } if(record->flags & LoaderDeferredLaunchFlagGui) - loader_show_gui_error(result, app_name_str, error_message); + loader_show_gui_error(result, app_name_str, app_args, error_message); loader_do_next_deferred_launch_if_available(loader); } while(false); @@ -1102,7 +1117,8 @@ int32_t loader_srv(void* p) { FuriString* error_message = furi_string_alloc(); LoaderMessageLoaderStatusResult status = loader_do_start_by_name( loader, message.start.name, message.start.args, error_message); //-V595 - loader_show_gui_error(status, message.start.name, error_message); + loader_show_gui_error( + status, message.start.name, message.start.args, error_message); if(status.value != LoaderStatusOk) loader_do_emit_queue_empty_event(loader); if(message.start.name) free((void*)message.start.name); if(message.start.args) free((void*)message.start.args); diff --git a/applications/services/loader/loader_pending.c b/applications/services/loader/loader_pending.c new file mode 100644 index 000000000..703f0c0ec --- /dev/null +++ b/applications/services/loader/loader_pending.c @@ -0,0 +1,75 @@ +#include "loader_pending.h" + +#include + +#include +#include + +#define TAG "LoaderPending" + +#define LOADER_PENDING_PATH INT_PATH(".loader.pending") +#define LOADER_PENDING_MAGIC (0x4C) +#define LOADER_PENDING_VERSION (1) + +typedef struct { + char name_or_path[128]; + char args[128]; +} LoaderPendingLaunch; + +bool loader_pending_launch_save(const char* name_or_path, const char* args) { + furi_check(name_or_path); + + LoaderPendingLaunch pending = {0}; + + if(strlcpy(pending.name_or_path, name_or_path, sizeof(pending.name_or_path)) >= + sizeof(pending.name_or_path)) { + FURI_LOG_E(TAG, "Name does not fit: %s", name_or_path); + return false; + } + + if(args && strlcpy(pending.args, args, sizeof(pending.args)) >= sizeof(pending.args)) { + FURI_LOG_E(TAG, "Args do not fit: %s", args); + return false; + } + + return saved_struct_save( + LOADER_PENDING_PATH, + &pending, + sizeof(pending), + LOADER_PENDING_MAGIC, + LOADER_PENDING_VERSION); +} + +bool loader_pending_launch_take(FuriString* name_or_path, FuriString* args) { + furi_check(name_or_path); + furi_check(args); + + Storage* storage = furi_record_open(RECORD_STORAGE); + bool taken = false; + + if(storage_file_exists(storage, LOADER_PENDING_PATH)) { + LoaderPendingLaunch pending; + bool loaded = saved_struct_load( + LOADER_PENDING_PATH, + &pending, + sizeof(pending), + LOADER_PENDING_MAGIC, + LOADER_PENDING_VERSION); + + storage_simply_remove(storage, LOADER_PENDING_PATH); + + if(loaded) { + pending.name_or_path[sizeof(pending.name_or_path) - 1] = '\0'; + pending.args[sizeof(pending.args) - 1] = '\0'; + + if(pending.name_or_path[0] != '\0') { + furi_string_set_str(name_or_path, pending.name_or_path); + furi_string_set_str(args, pending.args); + taken = true; + } + } + } + + furi_record_close(RECORD_STORAGE); + return taken; +} diff --git a/applications/services/loader/loader_pending.h b/applications/services/loader/loader_pending.h new file mode 100644 index 000000000..4934ab45a --- /dev/null +++ b/applications/services/loader/loader_pending.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +bool loader_pending_launch_save(const char* name_or_path, const char* args); + +bool loader_pending_launch_take(FuriString* name_or_path, FuriString* args);