diff --git a/.gitignore b/.gitignore index 89e129ac..bf17a94e 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ *.swp *.swo *.gdb_history +*.old # LSP diff --git a/applications/services/cli/cli_commands.c b/applications/services/cli/cli_commands.c index 0f042f6c..3f94deeb 100644 --- a/applications/services/cli/cli_commands.c +++ b/applications/services/cli/cli_commands.c @@ -220,11 +220,9 @@ void cli_command_sysctl_debug(Cli* cli, FuriString* args, void* context) { UNUSED(context); if(!furi_string_cmp(args, "0")) { furi_hal_rtc_reset_flag(FuriHalRtcFlagDebug); - loader_update_menu(); printf("Debug disabled."); } else if(!furi_string_cmp(args, "1")) { furi_hal_rtc_set_flag(FuriHalRtcFlagDebug); - loader_update_menu(); printf("Debug enabled."); } else { cli_print_usage("sysctl debug", "<1|0>", furi_string_get_cstr(args)); diff --git a/applications/services/desktop/scenes/desktop_scene_main.c b/applications/services/desktop/scenes/desktop_scene_main.c index 4d1fa495..053ac56f 100644 --- a/applications/services/desktop/scenes/desktop_scene_main.c +++ b/applications/services/desktop/scenes/desktop_scene_main.c @@ -106,10 +106,12 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { switch(event.event) { - case DesktopMainEventOpenMenu: - loader_show_menu(); + case DesktopMainEventOpenMenu: { + Loader* loader = furi_record_open(RECORD_LOADER); + loader_show_menu(loader); + furi_record_close(RECORD_LOADER); consumed = true; - break; + } break; case DesktopMainEventOpenLockMenu: scene_manager_next_scene(desktop->scene_manager, DesktopSceneLockMenu); diff --git a/applications/services/gui/modules/menu.c b/applications/services/gui/modules/menu.c index 3e3b6c2e..afae8b8f 100644 --- a/applications/services/gui/modules/menu.c +++ b/applications/services/gui/modules/menu.c @@ -154,6 +154,8 @@ Menu* menu_alloc() { void menu_free(Menu* menu) { furi_assert(menu); menu_reset(menu); + with_view_model( + menu->view, MenuModel * model, { MenuItemArray_clear(model->items); }, false); view_free(menu->view); free(menu); } diff --git a/applications/services/loader/application.fam b/applications/services/loader/application.fam index 49f3c414..f4d006e0 100644 --- a/applications/services/loader/application.fam +++ b/applications/services/loader/application.fam @@ -5,6 +5,7 @@ App( entry_point="loader_srv", cdefines=["SRV_LOADER"], requires=["gui"], + provides=["loader_start"], stack_size=2 * 1024, order=90, sdk_headers=[ @@ -12,3 +13,11 @@ App( "firmware_api/firmware_api.h", ], ) + +App( + appid="loader_start", + apptype=FlipperAppType.STARTUP, + entry_point="loader_on_system_start", + requires=["loader"], + order=90, +) diff --git a/applications/services/loader/loader.c b/applications/services/loader/loader.c index f83d47d6..be16e509 100644 --- a/applications/services/loader/loader.c +++ b/applications/services/loader/loader.c @@ -1,76 +1,114 @@ -#include "applications.h" -#include -#include "loader/loader.h" +#include "loader.h" #include "loader_i.h" +#include "loader_menu.h" +#include +#include -#define TAG "LoaderSrv" +#define TAG "Loader" +#define LOADER_MAGIC_THREAD_VALUE 0xDEADBEEF +// api -#define LOADER_THREAD_FLAG_SHOW_MENU (1 << 0) -#define LOADER_THREAD_FLAG_ALL (LOADER_THREAD_FLAG_SHOW_MENU) +LoaderStatus loader_start(Loader* loader, const char* name, const char* args) { + LoaderMessage message; + LoaderMessageLoaderStatusResult result; -static Loader* loader_instance = NULL; - -static bool - loader_start_application(const FlipperApplication* application, const char* arguments) { - loader_instance->application = application; - - furi_assert(loader_instance->application_arguments == NULL); - if(arguments && strlen(arguments) > 0) { - loader_instance->application_arguments = strdup(arguments); - } - - FURI_LOG_I(TAG, "Starting: %s", loader_instance->application->name); - - FuriHalRtcHeapTrackMode mode = furi_hal_rtc_get_heap_track_mode(); - if(mode > FuriHalRtcHeapTrackModeNone) { - furi_thread_enable_heap_trace(loader_instance->application_thread); - } else { - furi_thread_disable_heap_trace(loader_instance->application_thread); - } - - furi_thread_set_name(loader_instance->application_thread, loader_instance->application->name); - furi_thread_set_appid( - loader_instance->application_thread, loader_instance->application->appid); - furi_thread_set_stack_size( - loader_instance->application_thread, loader_instance->application->stack_size); - furi_thread_set_context( - loader_instance->application_thread, loader_instance->application_arguments); - furi_thread_set_callback( - loader_instance->application_thread, loader_instance->application->app); - - furi_thread_start(loader_instance->application_thread); - - return true; + message.type = LoaderMessageTypeStartByName; + message.start.name = name; + message.start.args = args; + message.api_lock = api_lock_alloc_locked(); + message.status_value = &result; + furi_message_queue_put(loader->queue, &message, FuriWaitForever); + api_lock_wait_unlock_and_free(message.api_lock); + return result.value; } -static void loader_menu_callback(void* _ctx, uint32_t index) { - UNUSED(index); - const FlipperApplication* application = _ctx; +bool loader_lock(Loader* loader) { + LoaderMessage message; + LoaderMessageBoolResult result; + message.type = LoaderMessageTypeLock; + message.api_lock = api_lock_alloc_locked(); + message.bool_value = &result; + furi_message_queue_put(loader->queue, &message, FuriWaitForever); + api_lock_wait_unlock_and_free(message.api_lock); + return result.value; +} - furi_assert(application->app); - furi_assert(application->name); +void loader_unlock(Loader* loader) { + LoaderMessage message; + message.type = LoaderMessageTypeUnlock; + furi_message_queue_put(loader->queue, &message, FuriWaitForever); +} - if(!loader_lock(loader_instance)) { - FURI_LOG_E(TAG, "Loader is locked"); - return; +bool loader_is_locked(Loader* loader) { + LoaderMessage message; + LoaderMessageBoolResult result; + message.type = LoaderMessageTypeIsLocked; + message.api_lock = api_lock_alloc_locked(); + message.bool_value = &result; + furi_message_queue_put(loader->queue, &message, FuriWaitForever); + api_lock_wait_unlock_and_free(message.api_lock); + return result.value; +} + +void loader_show_menu(Loader* loader) { + LoaderMessage message; + message.type = LoaderMessageTypeShowMenu; + furi_message_queue_put(loader->queue, &message, FuriWaitForever); +} + +FuriPubSub* loader_get_pubsub(Loader* loader) { + furi_assert(loader); + // it's safe to return pubsub without locking + // because it's never freed and loader is never exited + // also the loader instance cannot be obtained until the pubsub is created + return loader->pubsub; +} + +// callbacks + +static void loader_menu_closed_callback(void* context) { + Loader* loader = context; + LoaderMessage message; + message.type = LoaderMessageTypeMenuClosed; + furi_message_queue_put(loader->queue, &message, FuriWaitForever); +} + +static void loader_menu_click_callback(const char* name, void* context) { + Loader* loader = context; + loader_start(loader, name, NULL); +} + +static void loader_thread_state_callback(FuriThreadState thread_state, void* context) { + furi_assert(context); + + Loader* loader = context; + LoaderEvent event; + + if(thread_state == FuriThreadStateRunning) { + event.type = LoaderEventTypeApplicationStarted; + furi_pubsub_publish(loader->pubsub, &event); + } else if(thread_state == FuriThreadStateStopped) { + LoaderMessage message; + message.type = LoaderMessageTypeAppClosed; + furi_message_queue_put(loader->queue, &message, FuriWaitForever); + + event.type = LoaderEventTypeApplicationStopped; + furi_pubsub_publish(loader->pubsub, &event); } - - loader_start_application(application, NULL); } -static void loader_submenu_callback(void* context, uint32_t index) { - UNUSED(index); - uint32_t view_id = (uint32_t)context; - view_dispatcher_switch_to_view(loader_instance->view_dispatcher, view_id); -} +// implementation -static void loader_cli_print_usage() { - printf("Usage:\r\n"); - printf("loader \r\n"); - printf("Cmd list:\r\n"); - printf("\tlist\t - List available applications\r\n"); - printf("\topen \t - Open application by name\r\n"); - printf("\tinfo\t - Show loader state\r\n"); +static Loader* loader_alloc() { + Loader* loader = malloc(sizeof(Loader)); + loader->pubsub = furi_pubsub_alloc(); + loader->queue = furi_message_queue_alloc(1, sizeof(LoaderMessage)); + loader->loader_menu = NULL; + loader->app.args = NULL; + loader->app.name = NULL; + loader->app.thread = NULL; + loader->app.insomniac = false; + return loader; } static FlipperApplication const* loader_find_application_by_name_in_list( @@ -85,7 +123,7 @@ static FlipperApplication const* loader_find_application_by_name_in_list( return NULL; } -const FlipperApplication* loader_find_application_by_name(const char* name) { +static const FlipperApplication* loader_find_application_by_name(const char* name) { const FlipperApplication* application = NULL; application = loader_find_application_by_name_in_list(name, FLIPPER_APPS, FLIPPER_APPS_COUNT); if(!application) { @@ -100,346 +138,167 @@ const FlipperApplication* loader_find_application_by_name(const char* name) { return application; } -static void loader_cli_open(Cli* cli, FuriString* args, Loader* instance) { - UNUSED(cli); - if(loader_is_locked(instance)) { - if(instance->application) { - furi_assert(instance->application->name); - printf("Can't start, %s application is running", instance->application->name); - } else { - printf("Can't start, furi application is running"); - } - return; +static void + loader_start_internal_app(Loader* loader, const FlipperApplication* app, const char* args) { + FURI_LOG_I(TAG, "Starting %s", app->name); + + // store args + furi_assert(loader->app.args == NULL); + if(args && strlen(args) > 0) { + loader->app.args = strdup(args); } - FuriString* application_name; - application_name = furi_string_alloc(); + // store name + furi_assert(loader->app.name == NULL); + loader->app.name = strdup(app->name); - do { - if(!args_read_probably_quoted_string_and_trim(args, application_name)) { - printf("No application provided\r\n"); - break; - } + // setup app thread + loader->app.thread = + furi_thread_alloc_ex(app->name, app->stack_size, app->app, loader->app.args); + furi_thread_set_appid(loader->app.thread, app->appid); - const FlipperApplication* application = - loader_find_application_by_name(furi_string_get_cstr(application_name)); - if(!application) { - printf("%s doesn't exists\r\n", furi_string_get_cstr(application_name)); - break; - } - - furi_string_trim(args); - if(!loader_start_application(application, furi_string_get_cstr(args))) { - printf("Can't start, furi application is running"); - return; - } else { - // We must to increment lock counter to keep balance - // TODO: rewrite whole thing, it's complex as hell - FURI_CRITICAL_ENTER(); - instance->lock_count++; - FURI_CRITICAL_EXIT(); - } - } while(false); - - furi_string_free(application_name); -} - -static void loader_cli_list(Cli* cli, FuriString* args, Loader* instance) { - UNUSED(cli); - UNUSED(args); - UNUSED(instance); - printf("Applications:\r\n"); - for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) { - printf("\t%s\r\n", FLIPPER_APPS[i].name); - } -} - -static void loader_cli_info(Cli* cli, FuriString* args, Loader* instance) { - UNUSED(cli); - UNUSED(args); - if(!loader_is_locked(instance)) { - printf("No application is running\r\n"); + // setup heap trace + FuriHalRtcHeapTrackMode mode = furi_hal_rtc_get_heap_track_mode(); + if(mode > FuriHalRtcHeapTrackModeNone) { + furi_thread_enable_heap_trace(loader->app.thread); } else { - printf("Running application: "); - if(instance->application) { - furi_assert(instance->application->name); - printf("%s\r\n", instance->application->name); - } else { - printf("unknown\r\n"); - } + furi_thread_disable_heap_trace(loader->app.thread); + } + + // setup insomnia + if(!(app->flags & FlipperApplicationFlagInsomniaSafe)) { + furi_hal_power_insomnia_enter(); + loader->app.insomniac = true; + } else { + loader->app.insomniac = false; + } + + // setup app thread callbacks + furi_thread_set_state_context(loader->app.thread, loader); + furi_thread_set_state_callback(loader->app.thread, loader_thread_state_callback); + + // start app thread + furi_thread_start(loader->app.thread); +} + +// process messages + +static void loader_do_menu_show(Loader* loader) { + if(!loader->loader_menu) { + loader->loader_menu = loader_menu_alloc(); + loader_menu_set_closed_callback(loader->loader_menu, loader_menu_closed_callback, loader); + loader_menu_set_click_callback(loader->loader_menu, loader_menu_click_callback, loader); + loader_menu_start(loader->loader_menu); } } -static void loader_cli(Cli* cli, FuriString* args, void* _ctx) { - furi_assert(_ctx); - Loader* instance = _ctx; - - FuriString* cmd; - cmd = furi_string_alloc(); - - do { - if(!args_read_string_and_trim(args, cmd)) { - loader_cli_print_usage(); - break; - } - - if(furi_string_cmp_str(cmd, "list") == 0) { - loader_cli_list(cli, args, instance); - break; - } - - if(furi_string_cmp_str(cmd, "open") == 0) { - loader_cli_open(cli, args, instance); - break; - } - - if(furi_string_cmp_str(cmd, "info") == 0) { - loader_cli_info(cli, args, instance); - break; - } - - loader_cli_print_usage(); - } while(false); - - furi_string_free(cmd); +static void loader_do_menu_closed(Loader* loader) { + if(loader->loader_menu) { + loader_menu_stop(loader->loader_menu); + loader_menu_free(loader->loader_menu); + loader->loader_menu = NULL; + } } -LoaderStatus loader_start(Loader* instance, const char* name, const char* args) { - UNUSED(instance); - furi_assert(name); +static bool loader_do_is_locked(Loader* loader) { + return loader->app.thread != NULL; +} - const FlipperApplication* application = loader_find_application_by_name(name); - - if(!application) { - FURI_LOG_E(TAG, "Can't find application with name %s", name); - return LoaderStatusErrorUnknownApp; - } - - if(!loader_lock(loader_instance)) { - FURI_LOG_E(TAG, "Loader is locked"); +static LoaderStatus loader_do_start_by_name(Loader* loader, const char* name, const char* args) { + if(loader_do_is_locked(loader)) { return LoaderStatusErrorAppStarted; } - if(!loader_start_application(application, args)) { - return LoaderStatusErrorInternal; + const FlipperApplication* app = loader_find_application_by_name(name); + + if(!app) { + return LoaderStatusErrorUnknownApp; } + loader_start_internal_app(loader, app, args); return LoaderStatusOk; } -bool loader_lock(Loader* instance) { - FURI_CRITICAL_ENTER(); - bool result = false; - if(instance->lock_count == 0) { - instance->lock_count++; - result = true; - } - FURI_CRITICAL_EXIT(); - return result; -} - -void loader_unlock(Loader* instance) { - FURI_CRITICAL_ENTER(); - if(instance->lock_count > 0) instance->lock_count--; - FURI_CRITICAL_EXIT(); -} - -bool loader_is_locked(const Loader* instance) { - return instance->lock_count > 0; -} - -static void loader_thread_state_callback(FuriThreadState thread_state, void* context) { - furi_assert(context); - - Loader* instance = context; - LoaderEvent event; - - if(thread_state == FuriThreadStateRunning) { - event.type = LoaderEventTypeApplicationStarted; - furi_pubsub_publish(loader_instance->pubsub, &event); - - if(!(loader_instance->application->flags & FlipperApplicationFlagInsomniaSafe)) { - furi_hal_power_insomnia_enter(); - } - } else if(thread_state == FuriThreadStateStopped) { - FURI_LOG_I(TAG, "Application stopped. Free heap: %zu", memmgr_get_free_heap()); - - if(loader_instance->application_arguments) { - free(loader_instance->application_arguments); - loader_instance->application_arguments = NULL; - } - - if(!(loader_instance->application->flags & FlipperApplicationFlagInsomniaSafe)) { - furi_hal_power_insomnia_exit(); - } - loader_unlock(instance); - - event.type = LoaderEventTypeApplicationStopped; - furi_pubsub_publish(loader_instance->pubsub, &event); - } -} - -static uint32_t loader_hide_menu(void* context) { - UNUSED(context); - return VIEW_NONE; -} - -static uint32_t loader_back_to_primary_menu(void* context) { - furi_assert(context); - Submenu* submenu = context; - submenu_set_selected_item(submenu, 0); - return LoaderMenuViewPrimary; -} - -static Loader* loader_alloc() { - Loader* instance = malloc(sizeof(Loader)); - - instance->application_thread = furi_thread_alloc(); - - furi_thread_set_state_context(instance->application_thread, instance); - furi_thread_set_state_callback(instance->application_thread, loader_thread_state_callback); - - instance->pubsub = furi_pubsub_alloc(); - -#ifdef SRV_CLI - instance->cli = furi_record_open(RECORD_CLI); - cli_add_command( - instance->cli, RECORD_LOADER, CliCommandFlagParallelSafe, loader_cli, instance); -#else - UNUSED(loader_cli); -#endif - - instance->loader_thread = furi_thread_get_current_id(); - - // Gui - instance->gui = furi_record_open(RECORD_GUI); - instance->view_dispatcher = view_dispatcher_alloc(); - view_dispatcher_attach_to_gui( - instance->view_dispatcher, instance->gui, ViewDispatcherTypeFullscreen); - // Primary menu - instance->primary_menu = menu_alloc(); - view_set_previous_callback(menu_get_view(instance->primary_menu), loader_hide_menu); - view_dispatcher_add_view( - instance->view_dispatcher, LoaderMenuViewPrimary, menu_get_view(instance->primary_menu)); - // Settings menu - instance->settings_menu = submenu_alloc(); - view_set_context(submenu_get_view(instance->settings_menu), instance->settings_menu); - view_set_previous_callback( - submenu_get_view(instance->settings_menu), loader_back_to_primary_menu); - view_dispatcher_add_view( - instance->view_dispatcher, - LoaderMenuViewSettings, - submenu_get_view(instance->settings_menu)); - - view_dispatcher_enable_queue(instance->view_dispatcher); - - return instance; -} - -static void loader_free(Loader* instance) { - furi_assert(instance); - - if(instance->cli) { - furi_record_close(RECORD_CLI); +static bool loader_do_lock(Loader* loader) { + if(loader->app.thread) { + return false; } - furi_pubsub_free(instance->pubsub); - - furi_thread_free(instance->application_thread); - - menu_free(loader_instance->primary_menu); - view_dispatcher_remove_view(loader_instance->view_dispatcher, LoaderMenuViewPrimary); - submenu_free(loader_instance->settings_menu); - view_dispatcher_remove_view(loader_instance->view_dispatcher, LoaderMenuViewSettings); - view_dispatcher_free(loader_instance->view_dispatcher); - - furi_record_close(RECORD_GUI); - - free(instance); - instance = NULL; + loader->app.thread = (FuriThread*)LOADER_MAGIC_THREAD_VALUE; + return true; } -static void loader_build_menu() { - FURI_LOG_I(TAG, "Building main menu"); - size_t i; - for(i = 0; i < FLIPPER_APPS_COUNT; i++) { - menu_add_item( - loader_instance->primary_menu, - FLIPPER_APPS[i].name, - FLIPPER_APPS[i].icon, - i, - loader_menu_callback, - (void*)&FLIPPER_APPS[i]); +static void loader_do_unlock(Loader* loader) { + furi_assert(loader->app.thread == (FuriThread*)LOADER_MAGIC_THREAD_VALUE); + loader->app.thread = NULL; +} + +static void loader_do_app_closed(Loader* loader) { + furi_assert(loader->app.thread); + FURI_LOG_I(TAG, "Application stopped. Free heap: %zu", memmgr_get_free_heap()); + if(loader->app.args) { + free(loader->app.args); + loader->app.args = NULL; } - menu_add_item( - loader_instance->primary_menu, - "Settings", - &A_Settings_14, - i++, - loader_submenu_callback, - (void*)LoaderMenuViewSettings); -} -static void loader_build_submenu() { - FURI_LOG_I(TAG, "Building settings menu"); - for(size_t i = 0; i < FLIPPER_SETTINGS_APPS_COUNT; i++) { - submenu_add_item( - loader_instance->settings_menu, - FLIPPER_SETTINGS_APPS[i].name, - i, - loader_menu_callback, - (void*)&FLIPPER_SETTINGS_APPS[i]); + if(loader->app.insomniac) { + furi_hal_power_insomnia_exit(); } + + free(loader->app.name); + loader->app.name = NULL; + + furi_thread_free(loader->app.thread); + loader->app.thread = NULL; } -void loader_show_menu() { - furi_assert(loader_instance); - furi_thread_flags_set(loader_instance->loader_thread, LOADER_THREAD_FLAG_SHOW_MENU); -} - -void loader_update_menu() { - menu_reset(loader_instance->primary_menu); - loader_build_menu(); -} +// app int32_t loader_srv(void* p) { UNUSED(p); + Loader* loader = loader_alloc(); + furi_record_create(RECORD_LOADER, loader); + FURI_LOG_I(TAG, "Executing system start hooks"); for(size_t i = 0; i < FLIPPER_ON_SYSTEM_START_COUNT; i++) { FLIPPER_ON_SYSTEM_START[i](); } - FURI_LOG_I(TAG, "Starting"); - loader_instance = loader_alloc(); - - loader_build_menu(); - loader_build_submenu(); - - FURI_LOG_I(TAG, "Started"); - - furi_record_create(RECORD_LOADER, loader_instance); - if(FLIPPER_AUTORUN_APP_NAME && strlen(FLIPPER_AUTORUN_APP_NAME)) { - loader_start(loader_instance, FLIPPER_AUTORUN_APP_NAME, NULL); + loader_do_start_by_name(loader, FLIPPER_AUTORUN_APP_NAME, NULL); } - while(1) { - uint32_t flags = - furi_thread_flags_wait(LOADER_THREAD_FLAG_ALL, FuriFlagWaitAny, FuriWaitForever); - if(flags & LOADER_THREAD_FLAG_SHOW_MENU) { - menu_set_selected_item(loader_instance->primary_menu, 0); - view_dispatcher_switch_to_view( - loader_instance->view_dispatcher, LoaderMenuViewPrimary); - view_dispatcher_run(loader_instance->view_dispatcher); + LoaderMessage message; + while(true) { + if(furi_message_queue_get(loader->queue, &message, FuriWaitForever) == FuriStatusOk) { + switch(message.type) { + case LoaderMessageTypeStartByName: + message.status_value->value = + loader_do_start_by_name(loader, message.start.name, message.start.args); + api_lock_unlock(message.api_lock); + break; + case LoaderMessageTypeShowMenu: + loader_do_menu_show(loader); + break; + case LoaderMessageTypeMenuClosed: + loader_do_menu_closed(loader); + break; + case LoaderMessageTypeIsLocked: + message.bool_value->value = loader_do_is_locked(loader); + api_lock_unlock(message.api_lock); + break; + case LoaderMessageTypeAppClosed: + loader_do_app_closed(loader); + break; + case LoaderMessageTypeLock: + message.bool_value->value = loader_do_lock(loader); + api_lock_unlock(message.api_lock); + break; + case LoaderMessageTypeUnlock: + loader_do_unlock(loader); + } } } - furi_record_destroy(RECORD_LOADER); - loader_free(loader_instance); - return 0; -} - -FuriPubSub* loader_get_pubsub(Loader* instance) { - return instance->pubsub; -} +} \ No newline at end of file diff --git a/applications/services/loader/loader.h b/applications/services/loader/loader.h index 8dbc4fc3..e3a691b7 100644 --- a/applications/services/loader/loader.h +++ b/applications/services/loader/loader.h @@ -1,7 +1,5 @@ #pragma once - -#include -#include +#include #ifdef __cplusplus extern "C" { @@ -43,17 +41,14 @@ bool loader_lock(Loader* instance); void loader_unlock(Loader* instance); /** Get loader lock status */ -bool loader_is_locked(const Loader* instance); +bool loader_is_locked(Loader* instance); /** Show primary loader */ -void loader_show_menu(); - -/** Show primary loader */ -void loader_update_menu(); +void loader_show_menu(Loader* instance); /** Show primary loader */ FuriPubSub* loader_get_pubsub(Loader* instance); #ifdef __cplusplus } -#endif +#endif \ No newline at end of file diff --git a/applications/services/loader/loader_cli.c b/applications/services/loader/loader_cli.c new file mode 100644 index 00000000..2d460221 --- /dev/null +++ b/applications/services/loader/loader_cli.c @@ -0,0 +1,117 @@ +#include +#include +#include +#include +#include "loader.h" + +static void loader_cli_print_usage() { + printf("Usage:\r\n"); + printf("loader \r\n"); + printf("Cmd list:\r\n"); + printf("\tlist\t - List available applications\r\n"); + printf("\topen \t - Open application by name\r\n"); + printf("\tinfo\t - Show loader state\r\n"); +} + +static void loader_cli_list() { + printf("Applications:\r\n"); + for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) { + printf("\t%s\r\n", FLIPPER_APPS[i].name); + } + printf("Settings:\r\n"); + for(size_t i = 0; i < FLIPPER_SETTINGS_APPS_COUNT; i++) { + printf("\t%s\r\n", FLIPPER_SETTINGS_APPS[i].name); + } +} + +static void loader_cli_info(Loader* loader) { + if(!loader_is_locked(loader)) { + printf("No application is running\r\n"); + } else { + // TODO: print application name ??? + printf("Application is running\r\n"); + } +} + +static void loader_cli_open(FuriString* args, Loader* loader) { + FuriString* app_name = furi_string_alloc(); + + do { + if(!args_read_probably_quoted_string_and_trim(args, app_name)) { + printf("No application provided\r\n"); + break; + } + furi_string_trim(args); + + const char* args_str = furi_string_get_cstr(args); + if(strlen(args_str) == 0) { + args_str = NULL; + } + + const char* app_name_str = furi_string_get_cstr(app_name); + + LoaderStatus status = loader_start(loader, app_name_str, args_str); + + switch(status) { + case LoaderStatusOk: + break; + case LoaderStatusErrorAppStarted: + printf("Can't start, application is running"); + break; + case LoaderStatusErrorUnknownApp: + printf("%s doesn't exists\r\n", app_name_str); + break; + case LoaderStatusErrorInternal: + printf("Internal error\r\n"); + break; + } + } while(false); + + furi_string_free(app_name); +} + +static void loader_cli(Cli* cli, FuriString* args, void* context) { + UNUSED(cli); + UNUSED(context); + Loader* loader = furi_record_open(RECORD_LOADER); + + FuriString* cmd; + cmd = furi_string_alloc(); + + do { + if(!args_read_string_and_trim(args, cmd)) { + loader_cli_print_usage(); + break; + } + + if(furi_string_cmp_str(cmd, "list") == 0) { + loader_cli_list(); + break; + } + + if(furi_string_cmp_str(cmd, "open") == 0) { + loader_cli_open(args, loader); + break; + } + + if(furi_string_cmp_str(cmd, "info") == 0) { + loader_cli_info(loader); + break; + } + + loader_cli_print_usage(); + } while(false); + + furi_string_free(cmd); + furi_record_close(RECORD_LOADER); +} + +void loader_on_system_start() { +#ifdef SRV_CLI + Cli* cli = furi_record_open(RECORD_CLI); + cli_add_command(cli, RECORD_LOADER, CliCommandFlagParallelSafe, loader_cli, NULL); + furi_record_close(RECORD_CLI); +#else + UNUSED(loader_cli); +#endif +} \ No newline at end of file diff --git a/applications/services/loader/loader_i.h b/applications/services/loader/loader_i.h index 00028cd6..2e3f10da 100644 --- a/applications/services/loader/loader_i.h +++ b/applications/services/loader/loader_i.h @@ -1,39 +1,56 @@ -#include "loader.h" - +#pragma once #include -#include -#include -#include -#include +#include +#include "loader.h" +#include "loader_menu.h" -#include - -#include -#include - -#include -#include +typedef struct { + char* args; + char* name; + FuriThread* thread; + bool insomniac; +} LoaderAppData; struct Loader { - FuriThreadId loader_thread; - - const FlipperApplication* application; - FuriThread* application_thread; - char* application_arguments; - - Cli* cli; - Gui* gui; - - ViewDispatcher* view_dispatcher; - Menu* primary_menu; - Submenu* settings_menu; - - volatile uint8_t lock_count; - FuriPubSub* pubsub; + FuriMessageQueue* queue; + LoaderMenu* loader_menu; + LoaderAppData app; }; typedef enum { - LoaderMenuViewPrimary, - LoaderMenuViewSettings, -} LoaderMenuView; + LoaderMessageTypeStartByName, + LoaderMessageTypeAppClosed, + LoaderMessageTypeShowMenu, + LoaderMessageTypeMenuClosed, + LoaderMessageTypeLock, + LoaderMessageTypeUnlock, + LoaderMessageTypeIsLocked, +} LoaderMessageType; + +typedef struct { + const char* name; + const char* args; +} LoaderMessageStartByName; + +typedef struct { + LoaderStatus value; +} LoaderMessageLoaderStatusResult; + +typedef struct { + bool value; +} LoaderMessageBoolResult; + +typedef struct { + FuriApiLock api_lock; + LoaderMessageType type; + + union { + LoaderMessageStartByName start; + }; + + union { + LoaderMessageLoaderStatusResult* status_value; + LoaderMessageBoolResult* bool_value; + }; +} LoaderMessage; diff --git a/applications/services/loader/loader_menu.c b/applications/services/loader/loader_menu.c new file mode 100644 index 00000000..ec853661 --- /dev/null +++ b/applications/services/loader/loader_menu.c @@ -0,0 +1,187 @@ +#include +#include +#include +#include +#include +#include + +#include "loader_menu.h" + +#define TAG "LoaderMenu" + +struct LoaderMenu { + Gui* gui; + ViewDispatcher* view_dispatcher; + Menu* primary_menu; + Submenu* settings_menu; + + void (*closed_callback)(void*); + void* closed_callback_context; + + void (*click_callback)(const char*, void*); + void* click_callback_context; + + FuriThread* thread; +}; + +typedef enum { + LoaderMenuViewPrimary, + LoaderMenuViewSettings, +} LoaderMenuView; + +static int32_t loader_menu_thread(void* p); + +LoaderMenu* loader_menu_alloc() { + LoaderMenu* loader_menu = malloc(sizeof(LoaderMenu)); + loader_menu->gui = furi_record_open(RECORD_GUI); + loader_menu->view_dispatcher = view_dispatcher_alloc(); + loader_menu->primary_menu = menu_alloc(); + loader_menu->settings_menu = submenu_alloc(); + loader_menu->thread = NULL; + return loader_menu; +} + +void loader_menu_free(LoaderMenu* loader_menu) { + furi_assert(loader_menu); + // check if thread is running + furi_assert(!loader_menu->thread); + + submenu_free(loader_menu->settings_menu); + menu_free(loader_menu->primary_menu); + view_dispatcher_free(loader_menu->view_dispatcher); + furi_record_close(RECORD_GUI); + free(loader_menu); +} + +void loader_menu_start(LoaderMenu* loader_menu) { + furi_assert(loader_menu); + furi_assert(!loader_menu->thread); + loader_menu->thread = furi_thread_alloc_ex(TAG, 1024, loader_menu_thread, loader_menu); + furi_thread_start(loader_menu->thread); +} + +void loader_menu_stop(LoaderMenu* loader_menu) { + furi_assert(loader_menu); + furi_assert(loader_menu->thread); + view_dispatcher_stop(loader_menu->view_dispatcher); + furi_thread_join(loader_menu->thread); + furi_thread_free(loader_menu->thread); + loader_menu->thread = NULL; +} + +void loader_menu_set_closed_callback( + LoaderMenu* loader_menu, + void (*callback)(void*), + void* context) { + loader_menu->closed_callback = callback; + loader_menu->closed_callback_context = context; +} + +void loader_menu_set_click_callback( + LoaderMenu* loader_menu, + void (*callback)(const char*, void*), + void* context) { + loader_menu->click_callback = callback; + loader_menu->click_callback_context = context; +} + +static void loader_menu_callback(void* context, uint32_t index) { + LoaderMenu* loader_menu = context; + const char* name = FLIPPER_APPS[index].name; + if(loader_menu->click_callback) { + loader_menu->click_callback(name, loader_menu->click_callback_context); + } +} + +static void loader_menu_settings_menu_callback(void* context, uint32_t index) { + LoaderMenu* loader_menu = context; + const char* name = FLIPPER_SETTINGS_APPS[index].name; + if(loader_menu->click_callback) { + loader_menu->click_callback(name, loader_menu->click_callback_context); + } +} + +static void loader_menu_switch_to_settings(void* context, uint32_t index) { + UNUSED(index); + LoaderMenu* loader_menu = context; + view_dispatcher_switch_to_view(loader_menu->view_dispatcher, LoaderMenuViewSettings); +} + +static uint32_t loader_menu_switch_to_primary(void* context) { + UNUSED(context); + return LoaderMenuViewPrimary; +} + +static uint32_t loader_menu_exit(void* context) { + UNUSED(context); + return VIEW_NONE; +} + +static void loader_menu_build_menu(LoaderMenu* loader_menu) { + size_t i; + for(i = 0; i < FLIPPER_APPS_COUNT; i++) { + menu_add_item( + loader_menu->primary_menu, + FLIPPER_APPS[i].name, + FLIPPER_APPS[i].icon, + i, + loader_menu_callback, + (void*)loader_menu); + } + menu_add_item( + loader_menu->primary_menu, + "Settings", + &A_Settings_14, + i++, + loader_menu_switch_to_settings, + loader_menu); +}; + +static void loader_menu_build_submenu(LoaderMenu* loader_menu) { + for(size_t i = 0; i < FLIPPER_SETTINGS_APPS_COUNT; i++) { + submenu_add_item( + loader_menu->settings_menu, + FLIPPER_SETTINGS_APPS[i].name, + i, + loader_menu_settings_menu_callback, + loader_menu); + } +} + +static int32_t loader_menu_thread(void* p) { + LoaderMenu* loader_menu = p; + furi_assert(loader_menu); + + loader_menu_build_menu(loader_menu); + loader_menu_build_submenu(loader_menu); + + view_dispatcher_attach_to_gui( + loader_menu->view_dispatcher, loader_menu->gui, ViewDispatcherTypeFullscreen); + + // Primary menu + View* primary_view = menu_get_view(loader_menu->primary_menu); + view_set_context(primary_view, loader_menu->primary_menu); + view_set_previous_callback(primary_view, loader_menu_exit); + view_dispatcher_add_view(loader_menu->view_dispatcher, LoaderMenuViewPrimary, primary_view); + + // Settings menu + View* settings_view = submenu_get_view(loader_menu->settings_menu); + view_set_context(settings_view, loader_menu->settings_menu); + view_set_previous_callback(settings_view, loader_menu_switch_to_primary); + view_dispatcher_add_view(loader_menu->view_dispatcher, LoaderMenuViewSettings, settings_view); + + view_dispatcher_enable_queue(loader_menu->view_dispatcher); + view_dispatcher_switch_to_view(loader_menu->view_dispatcher, LoaderMenuViewPrimary); + + // run view dispatcher + view_dispatcher_run(loader_menu->view_dispatcher); + + view_dispatcher_remove_view(loader_menu->view_dispatcher, LoaderMenuViewPrimary); + view_dispatcher_remove_view(loader_menu->view_dispatcher, LoaderMenuViewSettings); + + if(loader_menu->closed_callback) { + loader_menu->closed_callback(loader_menu->closed_callback_context); + } + + return 0; +} \ No newline at end of file diff --git a/applications/services/loader/loader_menu.h b/applications/services/loader/loader_menu.h new file mode 100644 index 00000000..7405b87b --- /dev/null +++ b/applications/services/loader/loader_menu.h @@ -0,0 +1,30 @@ +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct LoaderMenu LoaderMenu; + +LoaderMenu* loader_menu_alloc(); + +void loader_menu_free(LoaderMenu* loader_menu); + +void loader_menu_start(LoaderMenu* loader_menu); + +void loader_menu_stop(LoaderMenu* loader_menu); + +void loader_menu_set_closed_callback( + LoaderMenu* loader_menu, + void (*callback)(void*), + void* context); + +void loader_menu_set_click_callback( + LoaderMenu* loader_menu, + void (*callback)(const char*, void*), + void* context); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/applications/settings/system/system_settings.c b/applications/settings/system/system_settings.c index 597710a5..dd3c0dc6 100644 --- a/applications/settings/system/system_settings.c +++ b/applications/settings/system/system_settings.c @@ -43,7 +43,6 @@ static void debug_changed(VariableItem* item) { } else { furi_hal_rtc_reset_flag(FuriHalRtcFlagDebug); } - loader_update_menu(); } const char* const heap_trace_mode_text[] = { @@ -137,8 +136,6 @@ static void hand_orient_changed(VariableItem* item) { } else { furi_hal_rtc_reset_flag(FuriHalRtcFlagHandOrient); } - - loader_update_menu(); } const char* const sleep_method[] = { diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index f84bf074..bc6844d3 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,23.3,, +Version,+,24.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -1375,12 +1375,11 @@ Function,-,ldiv,ldiv_t,"long, long" Function,-,llabs,long long,long long Function,-,lldiv,lldiv_t,"long long, long long" Function,+,loader_get_pubsub,FuriPubSub*,Loader* -Function,+,loader_is_locked,_Bool,const Loader* +Function,+,loader_is_locked,_Bool,Loader* Function,+,loader_lock,_Bool,Loader* -Function,+,loader_show_menu,void, +Function,+,loader_show_menu,void,Loader* Function,+,loader_start,LoaderStatus,"Loader*, const char*, const char*" Function,+,loader_unlock,void,Loader* -Function,+,loader_update_menu,void, Function,+,loading_alloc,Loading*, Function,+,loading_free,void,Loading* Function,+,loading_get_view,View*,Loading* diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index c176b2d7..1c8424fe 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,23.3,, +Version,+,24.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -1795,12 +1795,11 @@ Function,-,llround,long long int,double Function,-,llroundf,long long int,float Function,-,llroundl,long long int,long double Function,+,loader_get_pubsub,FuriPubSub*,Loader* -Function,+,loader_is_locked,_Bool,const Loader* +Function,+,loader_is_locked,_Bool,Loader* Function,+,loader_lock,_Bool,Loader* -Function,+,loader_show_menu,void, +Function,+,loader_show_menu,void,Loader* Function,+,loader_start,LoaderStatus,"Loader*, const char*, const char*" Function,+,loader_unlock,void,Loader* -Function,+,loader_update_menu,void, Function,+,loading_alloc,Loading*, Function,+,loading_free,void,Loading* Function,+,loading_get_view,View*,Loading*