From a2e5d9563f9dec84907f4f2060af6dfad895c4ba Mon Sep 17 00:00:00 2001 From: William Wilgus Date: Mon, 20 Mar 2023 22:15:33 -0400 Subject: [PATCH] [Feature] resume TSR plugins after interruption WIP save tsr plugin path for later resume tsr plugin when user stops the interrupting plugin expand return of tsr_exit function to allow continue, suspend, terminate tsr plugins check parameter at start to determine if the plugin is being resumed Change-Id: I6fc70de664c7771e7dbc9a1af7a831e7b50b1d15 --- apps/plugin.c | 51 ++++++++++----- apps/plugin.h | 30 +++++---- apps/plugins/announce_status.c | 107 ++++++++++++++++++-------------- apps/plugins/battery_bench.c | 66 +++++++++++--------- apps/plugins/lastfm_scrobbler.c | 32 ++++++---- apps/plugins/test_usb.c | 13 ++-- 6 files changed, 176 insertions(+), 123 deletions(-) diff --git a/apps/plugin.c b/apps/plugin.c index 3db4bb2d80..cd58209fb7 100644 --- a/apps/plugin.c +++ b/apps/plugin.c @@ -74,7 +74,7 @@ extern unsigned char pluginbuf[]; /* for actual plugins only, not for codecs */ static int plugin_size = 0; -static bool (*pfn_tsr_exit)(bool reenter) = NULL; /* TSR exit callback */ +static int (*pfn_tsr_exit)(bool reenter) = NULL; /* TSR exit callback */ static char current_plugin[MAX_PATH]; /* NULL if no plugin is loaded, otherwise the handle that lc_open() returned */ static void *current_plugin_handle; @@ -83,7 +83,7 @@ char *plugin_get_current_filename(void); static void* plugin_get_audio_buffer(size_t *buffer_size); static void plugin_release_audio_buffer(void); -static void plugin_tsr(bool (*exit_callback)(bool)); +static void plugin_tsr(int (*exit_callback)(bool)); #ifdef HAVE_PLUGIN_CHECK_OPEN_CLOSE /* File handle leak prophylaxis */ @@ -183,6 +183,8 @@ static const struct plugin_api rockbox_api = { /* lcd */ splash, splashf, + splash_progress, + splash_progress_set_delay, #ifdef HAVE_LCD_CONTRAST lcd_set_contrast, #endif @@ -436,6 +438,7 @@ static const struct plugin_api rockbox_api = { set_current_file, set_dirfilter, onplay_show_playlist_menu, + onplay_show_playlist_cat_menu, browse_id3, /* talking */ @@ -587,6 +590,7 @@ static const struct plugin_api rockbox_api = { buflib_get_data, /* sound */ + adjust_volume, sound_set, sound_current, /*stub*/ sound_default, @@ -674,6 +678,7 @@ static const struct plugin_api rockbox_api = { tagcache_fill_tags, #endif #endif + tagtree_subentries_do_action, #endif /* HAVE_TAGCACHE */ #ifdef HAVE_ALBUMART @@ -685,6 +690,7 @@ static const struct plugin_api rockbox_api = { playlist_amount, playlist_resume, playlist_resume_track, + playlist_set_modified, playlist_start, playlist_add, playlist_sync, @@ -814,6 +820,7 @@ static const struct plugin_api rockbox_api = { sys_reboot, /* pathfuncs */ + fix_path_part, #ifdef HAVE_MULTIVOLUME path_strip_volume, #endif @@ -821,15 +828,6 @@ static const struct plugin_api rockbox_api = { /* new stuff at the end, sort into place next time the API gets incompatible */ - splash_progress, - splash_progress_set_delay, - fix_path_part, - onplay_show_playlist_cat_menu, -#if defined(HAVE_TAGCACHE) - tagtree_subentries_do_action, -#endif - adjust_volume, - playlist_set_modified, }; static int plugin_buffer_handle; @@ -839,17 +837,31 @@ int plugin_load(const char* plugin, const void* parameter) { struct plugin_header *p_hdr; struct lc_header *hdr; + const char * resume_plugin = NULL; + + if (!plugin) + return PLUGIN_ERROR; if (current_plugin_handle && pfn_tsr_exit) { /* if we have a resident old plugin and a callback */ - if (pfn_tsr_exit(!strcmp(current_plugin, plugin)) == false ) + bool reenter = (strcmp(current_plugin, plugin) == 0); + int exit_status = pfn_tsr_exit(reenter); + if (exit_status == PLUGIN_TSR_CONTINUE) { /* not allowing another plugin to load */ return PLUGIN_OK; } - lc_close(current_plugin_handle); - current_plugin_handle = pfn_tsr_exit = NULL; - plugin_buffer_handle = core_free(plugin_buffer_handle); + else + { + lc_close(current_plugin_handle); + current_plugin_handle = pfn_tsr_exit = NULL; + plugin_buffer_handle = core_free(plugin_buffer_handle); + + if (!reenter) + resume_plugin = strdupa(current_plugin); + else if (exit_status == PLUGIN_TSR_TERMINATE) + return PLUGIN_OK; /* don't even load the new plugin either */ + } } #ifdef HAVE_DISK_STORAGE @@ -857,7 +869,6 @@ int plugin_load(const char* plugin, const void* parameter) splash(0, ID2P(LANG_WAIT)); #endif strcpy(current_plugin, plugin); - current_plugin_handle = lc_open(plugin, pluginbuf, PLUGIN_BUFFER_SIZE); if (current_plugin_handle == NULL) { splashf(HZ*2, str(LANG_PLUGIN_CANT_OPEN), plugin); @@ -990,6 +1001,12 @@ int plugin_load(const char* plugin, const void* parameter) if (rc == PLUGIN_ERROR) splash(HZ*2, str(LANG_PLUGIN_ERROR)); + if (resume_plugin && rc != PLUGIN_GOTO_PLUGIN && !pfn_tsr_exit) + { + /*plugin = resume_plugin;*/ + /*parameter = rockbox_api.plugin_tsr;*/ + return plugin_load(resume_plugin, rockbox_api.plugin_tsr); + } return rc; } @@ -1067,7 +1084,7 @@ static void plugin_release_audio_buffer(void) /* The plugin wants to stay resident after leaving its main function, e.g. runs from timer or own thread. The callback is registered to later instruct it to free its resources before a new plugin gets loaded. */ -static void plugin_tsr(bool (*exit_callback)(bool)) +static void plugin_tsr(int (*exit_callback)(bool)) { pfn_tsr_exit = exit_callback; /* remember the callback for later */ } diff --git a/apps/plugin.h b/apps/plugin.h index c54ef180ec..3eb4101cbe 100644 --- a/apps/plugin.h +++ b/apps/plugin.h @@ -162,7 +162,7 @@ int plugin_open(const char *plugin, const char *parameter); * when this happens please take the opportunity to sort in * any new functions "waiting" at the end of the list. */ -#define PLUGIN_API_VERSION 266 +#define PLUGIN_API_VERSION 267 /* 239 Marks the removal of ARCHOS HWCODEC and CHARCELL */ @@ -179,6 +179,12 @@ enum plugin_status { PLUGIN_ERROR = -1, }; +enum plugin_tsr_status { + PLUGIN_TSR_CONTINUE = 0, /* TSR continues running */ + PLUGIN_TSR_SUSPEND, /* TSR exits but will restart later */ + PLUGIN_TSR_TERMINATE, /* TSR exits and will not be restarted */ +}; + /* NOTE: To support backwards compatibility, only add new functions at the end of the structure. Every time you add a new function, remember to increase PLUGIN_API_VERSION. If you make changes to the @@ -195,6 +201,8 @@ struct plugin_api { /* lcd */ void (*splash)(int ticks, const char *str); void (*splashf)(int ticks, const char *fmt, ...) ATTRIBUTE_PRINTF(2, 3); + void (*splash_progress)(int current, int total, const char *fmt, ...) ATTRIBUTE_PRINTF(3, 4); + void (*splash_progress_set_delay)(long delay_ticks); #ifdef HAVE_LCD_CONTRAST void (*lcd_set_contrast)(int x); #endif @@ -488,6 +496,8 @@ struct plugin_api { void (*set_dirfilter)(int l_dirfilter); void (*onplay_show_playlist_menu)(const char* path, void (*playlist_insert_cb)); + void (*onplay_show_playlist_cat_menu)(const char* track_name, int attr, + void (*add_to_pl_cb)); bool (*browse_id3)(struct mp3entry *id3, int playlist_display_index, int playlist_amount, struct tm *modified); @@ -660,6 +670,7 @@ struct plugin_api { void* (*buflib_get_data)(struct buflib_context* ctx, int handle); /* sound */ + void (*adjust_volume)(int steps); void (*sound_set)(int setting, int value); int (*sound_current)(int setting); /*stub*/ int (*sound_default)(int setting); @@ -769,6 +780,7 @@ struct plugin_api { bool (*tagcache_fill_tags)(struct mp3entry *id3, const char *filename); #endif #endif + bool (*tagtree_subentries_do_action)(bool (*action_cb)(const char *file_name)); #endif /* HAVE_TAGCACHE */ #ifdef HAVE_ALBUMART @@ -782,6 +794,7 @@ struct plugin_api { int (*playlist_resume)(void); void (*playlist_resume_track)(int start_index, unsigned int crc, unsigned long elapsed, unsigned long offset); + void (*playlist_set_modified)(struct playlist_info *playlist, bool modified); void (*playlist_start)(int start_index, unsigned long elapsed, unsigned long offset); int (*playlist_add)(const char *filename); @@ -932,29 +945,20 @@ struct plugin_api { void* (*plugin_get_buffer)(size_t *buffer_size); void* (*plugin_get_audio_buffer)(size_t *buffer_size); void (*plugin_release_audio_buffer)(void); - void (*plugin_tsr)(bool (*exit_callback)(bool reenter)); + void (*plugin_tsr)(int (*exit_callback)(bool reenter)); char* (*plugin_get_current_filename)(void); size_t (*plugin_reserve_buffer)(size_t buffer_size); /* reboot and poweroff */ void (*sys_poweroff)(void); void (*sys_reboot)(void); /* pathfuncs */ + void (*fix_path_part)(char* path, int offset, int count); #ifdef HAVE_MULTIVOLUME int (*path_strip_volume)(const char *name, const char **nameptr, bool greedy); #endif /* new stuff at the end, sort into place next time the API gets incompatible */ - - void (*splash_progress)(int current, int total, const char *fmt, ...) ATTRIBUTE_PRINTF(3, 4); - void (*splash_progress_set_delay)(long delay_ticks); - void (*fix_path_part)(char* path, int offset, int count); - void (*onplay_show_playlist_cat_menu)(const char* track_name, int attr, - void (*add_to_pl_cb)); -#ifdef HAVE_TAGCACHE - bool (*tagtree_subentries_do_action)(bool (*action_cb)(const char *file_name)); -#endif - void (*adjust_volume)(int steps); - void (*playlist_set_modified)(struct playlist_info *playlist, bool modified); + }; /* plugin header */ diff --git a/apps/plugins/announce_status.c b/apps/plugins/announce_status.c index 77e9015000..1ccfc1e70a 100644 --- a/apps/plugins/announce_status.c +++ b/apps/plugins/announce_status.c @@ -97,6 +97,7 @@ enum plugin_status plugin_start(const void* parameter); /* entry */ static struct { bool exiting; /* signal to the thread that we want to exit */ + bool resume; unsigned int id; /* worker thread id */ struct event_queue queue; /* thread event queue */ long stack[THREAD_STACK_SIZE / sizeof(long)]; @@ -393,7 +394,7 @@ static int settings_menu(void) break; case 4: /*sep*/ continue; - case 5: + case 5: /* quit the plugin */ return -1; break; case 6: @@ -433,7 +434,8 @@ void thread(void) in_usb = false; /*fall through*/ case EV_STARTUP: - rb->beep_play(1500, 100, 1000); + if (!gThread.resume) + rb->beep_play(1500, 100, 1000); break; case EV_EXIT: return; @@ -479,17 +481,45 @@ void thread_quit(void) } } +static bool check_user_input(void) +{ + int i = 0; + rb->button_clear_queue(); + if (rb->button_get_w_tmo(HZ) > BUTTON_NONE) + { + while ((rb->button_get(false) & BUTTON_REL) != BUTTON_REL) + { + if (i & 1) + rb->beep_play(800, 100, 1000 - i * (1000 / 15)); + + if (++i > 15) + { + return true; + } + rb->sleep(HZ / 5); + } + } + return false; +} + /* callback to end the TSR plugin, called before a new one gets loaded */ -static bool exit_tsr(bool reenter) +static int exit_tsr(bool reenter) { if (reenter) { rb->queue_post(&gThread.queue, EV_OTHINSTANCE, 0); - return false; /* dont let it start again */ + + /* quit the plugin if user holds a button */ + if (check_user_input() == true) + { + if (settings_menu() < 0) + return PLUGIN_TSR_TERMINATE; /*kill TSR dont let it start again */ + } + return PLUGIN_TSR_CONTINUE; /* dont let new plugin start*/ } thread_quit(); - return true; + return PLUGIN_TSR_SUSPEND; } @@ -497,58 +527,35 @@ static bool exit_tsr(bool reenter) int plugin_main(const void* parameter) { - (void)parameter; - bool settings = false; - int i = 0; - rb->memset(&gThread, 0, sizeof(gThread)); gAnnounce.index = 0; gAnnounce.timeout = 0; - - rb->splash(HZ / 2, "Announce Status"); - - if (configfile_load(CFG_FILE, config, gCfg_sz, CFG_VER) < 0) + /* Resume plugin ? */ + if (parameter == rb->plugin_tsr) { - /* If the loading failed, save a new config file */ - config_set_defaults(); - configfile_save(CFG_FILE, config, gCfg_sz, CFG_VER); - - rb->splash(HZ, ID2P(LANG_HOLD_FOR_SETTINGS)); + gThread.resume = true; } - - if (gAnnounce.show_prompt) + else { - if (rb->mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) != CHANNEL_PLAYING) + rb->splash(HZ / 2, "Announce Status"); + if (gAnnounce.show_prompt) { - rb->talk_id(LANG_HOLD_FOR_SETTINGS, false); - } - rb->splash(HZ, ID2P(LANG_HOLD_FOR_SETTINGS)); - } - - rb->button_clear_queue(); - if (rb->button_get_w_tmo(HZ) > BUTTON_NONE) - { - while ((rb->button_get(false) & BUTTON_REL) != BUTTON_REL) - { - if (i & 1) - rb->beep_play(800, 100, 1000); - - if (++i > 15) + if (rb->mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) != CHANNEL_PLAYING) { - settings = true; - break; + rb->talk_id(LANG_HOLD_FOR_SETTINGS, false); } - rb->sleep(HZ / 5); + rb->splash(HZ, ID2P(LANG_HOLD_FOR_SETTINGS)); } - } - if (settings) - { - rb->splash(100, ID2P(LANG_SETTINGS)); - int ret = settings_menu(); - if (ret < 0) - return 0; + + if (check_user_input() == true) + { + rb->splash(100, ID2P(LANG_SETTINGS)); + int ret = settings_menu(); + if (ret < 0) + return 0; + } } gAnnounce.timeout = *rb->current_tick; @@ -571,6 +578,16 @@ enum plugin_status plugin_start(const void* parameter) /* now go ahead and have fun! */ if (rb->usb_inserted() == true) return PLUGIN_USB_CONNECTED; + + config_set_defaults(); + if (configfile_load(CFG_FILE, config, gCfg_sz, CFG_VER) < 0) + { + /* If the loading failed, save a new config file */ + config_set_defaults(); + configfile_save(CFG_FILE, config, gCfg_sz, CFG_VER); + rb->splash(HZ, ID2P(LANG_HOLD_FOR_SETTINGS)); + } + int ret = plugin_main(parameter); return (ret==0) ? PLUGIN_OK : PLUGIN_ERROR; } diff --git a/apps/plugins/battery_bench.c b/apps/plugins/battery_bench.c index 17d3b918cf..f258492363 100644 --- a/apps/plugins/battery_bench.c +++ b/apps/plugins/battery_bench.c @@ -289,11 +289,11 @@ static struct event_queue thread_q SHAREDBSS_ATTR; static bool in_usb_mode; static unsigned int buf_idx; -static bool exit_tsr(bool reenter) +static int exit_tsr(bool reenter) { - bool is_exit; + int exit_status; long button; - (void)reenter; + rb->lcd_clear_display(); rb->lcd_puts_scroll(0, 0, "Batt.Bench is currently running."); rb->lcd_puts_scroll(0, 1, "Press " BATTERY_OFF_TXT " to cancel the test"); @@ -313,16 +313,17 @@ static bool exit_tsr(bool reenter) rb->thread_wait(gThread.id); /* remove the thread's queue from the broadcast list */ rb->queue_delete(&thread_q); - is_exit = true; + exit_status = (reenter ? PLUGIN_TSR_TERMINATE : PLUGIN_TSR_SUSPEND); + } - else is_exit = false; + else exit_status = PLUGIN_TSR_CONTINUE; break; } FOR_NB_SCREENS(idx) rb->screens[idx]->scroll_stop(); - return is_exit; + return exit_status; } #define BIT_CHARGER 0x1 @@ -502,14 +503,19 @@ static void put_centered_str(const char* str, plcdfunc putsxy, int lcd_width, in enum plugin_status plugin_start(const void* parameter) { - (void)parameter; int button, fd; + bool resume = false; bool on = false; start_tick = *rb->current_tick; int i; const char *msgs[] = { "Battery Benchmark","Check file", BATTERY_LOG, "for more info", BATTERY_ON_TXT, BATTERY_OFF_TXT " - quit" }; - rb->lcd_clear_display(); + + if (parameter == rb->plugin_tsr) + { + resume = true; + on = true; + } rb->lcd_clear_display(); rb->lcd_setfont(FONT_SYSFIXED); @@ -529,36 +535,38 @@ enum plugin_status plugin_start(const void* parameter) rb->lcd_remote_putsxy,LCD_REMOTE_WIDTH,2); rb->lcd_remote_update(); #endif - - do + if (!resume) { - button = rb->button_get(true); - switch (button) + do { - case BATTERY_ON: -#ifdef BATTERY_RC_ON - case BATTERY_RC_ON: -#endif - on = true; - break; - case BATTERY_OFF: -#ifdef BATTERY_RC_OFF - case BATTERY_RC_OFF: -#endif - return PLUGIN_OK; - - default: - if (rb->default_event_handler(button) == SYS_USB_CONNECTED) - return PLUGIN_USB_CONNECTED; - } - }while(!on); + button = rb->button_get(true); + switch (button) + { + case BATTERY_ON: + #ifdef BATTERY_RC_ON + case BATTERY_RC_ON: + #endif + on = true; + break; + case BATTERY_OFF: + #ifdef BATTERY_RC_OFF + case BATTERY_RC_OFF: + #endif + return PLUGIN_OK; + default: + if (rb->default_event_handler(button) == SYS_USB_CONNECTED) + return PLUGIN_USB_CONNECTED; + } + }while(!on); + } fd = rb->open(BATTERY_LOG, O_RDONLY); if (fd < 0) { fd = rb->open(BATTERY_LOG, O_RDWR | O_CREAT, 0666); if (fd >= 0) { + rb->fdprintf(fd, "# This plugin will log your battery performance in a\n" "# file (%s) every minute.\n" diff --git a/apps/plugins/lastfm_scrobbler.c b/apps/plugins/lastfm_scrobbler.c index 5565eed4c5..dce6be0d1e 100644 --- a/apps/plugins/lastfm_scrobbler.c +++ b/apps/plugins/lastfm_scrobbler.c @@ -98,7 +98,7 @@ static struct bool force_flush; } gCache; -static struct +static struct lastfm_config { int savepct; int beeplvl; @@ -528,7 +528,7 @@ void thread_quit(void) } /* callback to end the TSR plugin, called before a new one gets loaded */ -static bool exit_tsr(bool reenter) +static int exit_tsr(bool reenter) { MENUITEM_STRINGLIST(menu, ID2P(LANG_AUDIOSCROBBLER), NULL, ID2P(LANG_SETTINGS), "Flush Cache", "Exit Plugin", ID2P(LANG_BACK)); @@ -556,19 +556,13 @@ static bool exit_tsr(bool reenter) case 2: /* exit plugin - quit */ if(rb->gui_syncyesno_run(&quit_prompt, NULL, NULL) == YESNO_YES) { + scrobbler_flush_cache(); thread_quit(); - if (reenter) - rb->plugin_tsr(NULL); /* remove TSR cb */ - return !reenter; + return (reenter ? PLUGIN_TSR_TERMINATE : PLUGIN_TSR_SUSPEND); } - - if(!reenter) - return false; - - break; - + /* Fall Through */ case 3: /* back to menu */ - return false; + return PLUGIN_TSR_CONTINUE; } } } @@ -576,7 +570,17 @@ static bool exit_tsr(bool reenter) /****************** main ******************/ static int plugin_main(const void* parameter) { - (void)parameter; + struct lastfm_config cfg; + rb->memcpy(&cfg, & gConfig, sizeof(struct lastfm_config)); + + /* Resume plugin ? */ + if (parameter == rb->plugin_tsr) + { + + gConfig.beeplvl = 0; + gConfig.playback = false; + gConfig.verbose = false; + } rb->memset(&gThread, 0, sizeof(gThread)); if (gConfig.verbose) @@ -586,9 +590,11 @@ static int plugin_main(const void* parameter) rb->plugin_tsr(exit_tsr); /* stay resident */ thread_create(); + rb->memcpy(&gConfig, &cfg, sizeof(struct lastfm_config)); if (gConfig.playback) return PLUGIN_GOTO_WPS; + return PLUGIN_OK; } diff --git a/apps/plugins/test_usb.c b/apps/plugins/test_usb.c index 6bb77c40be..28ef8f7e5f 100644 --- a/apps/plugins/test_usb.c +++ b/apps/plugins/test_usb.c @@ -85,7 +85,7 @@ static void kill_tsr(void) rb->queue_delete(&queue); } -static bool exit_tsr(bool reenter) +static int exit_tsr(bool reenter) { MENUITEM_STRINGLIST(menu, "USB test menu", NULL, "Status", "Stop plugin", "Back"); @@ -100,9 +100,9 @@ static bool exit_tsr(bool reenter) case 1: rb->splashf(HZ, "Stopping USB test thread"); kill_tsr(); - return true; + return (reenter ? PLUGIN_TSR_TERMINATE : PLUGIN_TSR_SUSPEND); case 2: - return false; + return PLUGIN_TSR_CONTINUE; } } } @@ -119,14 +119,15 @@ static void run_tsr(void) enum plugin_status plugin_start(const void* parameter) { - (void)parameter; + bool resume = (parameter == rb->plugin_tsr); + MENUITEM_STRINGLIST(menu, "USB test menu", NULL, "Start", "Quit"); - switch(rb->do_menu(&menu, NULL, NULL, false)) { + switch(!resume ? rb->do_menu(&menu, NULL, NULL, false) : 0) { case 0: run_tsr(); - rb->splashf(HZ, "Thread started"); + rb->splashf(HZ, "USB test thread started"); return PLUGIN_OK; case 1: return PLUGIN_OK;