diff --git a/Makefile.common b/Makefile.common index 2a33ab7f5b..8a85c909b2 100644 --- a/Makefile.common +++ b/Makefile.common @@ -563,7 +563,8 @@ ifeq ($(HAVE_LIBRETRODB), 1) tasks/task_database_cue.o ifeq ($(HAVE_MENU), 1) - OBJ += menu/menu_explore.o + OBJ += menu/menu_explore.o \ + tasks/task_menu_explore.o endif endif diff --git a/griffin/griffin.c b/griffin/griffin.c index f9b0f76c05..a418341aeb 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -1398,6 +1398,7 @@ MENU #include "../menu/menu_displaylist.c" #ifdef HAVE_LIBRETRODB #include "../menu/menu_explore.c" +#include "../tasks/task_menu_explore.c" #endif #endif diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h index 05bf95a6ca..5779d6a72a 100644 --- a/intl/msg_hash_lbl.h +++ b/intl/msg_hash_lbl.h @@ -62,6 +62,10 @@ MSG_HASH( MENU_ENUM_LABEL_EXPLORE_ITEM, "explore_item" ) +MSG_HASH( + MENU_ENUM_LABEL_EXPLORE_INITIALISING_LIST, + "explore_initialising_list" + ) MSG_HASH( MENU_ENUM_LABEL_ADD_TAB, "add_tab" diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 48bb0c2948..e0904a468a 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -6201,6 +6201,10 @@ MSG_HASH( ) /* Explore tab */ +MSG_HASH( + MENU_ENUM_LABEL_VALUE_EXPLORE_INITIALISING_LIST, + "Initializing list..." + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_EXPLORE_CATEGORY_RELEASE_YEAR, "Release Year" diff --git a/menu/cbs/menu_cbs_left.c b/menu/cbs/menu_cbs_left.c index 35e7faedb4..b3c6e545d5 100644 --- a/menu/cbs/menu_cbs_left.c +++ b/menu/cbs/menu_cbs_left.c @@ -1039,6 +1039,7 @@ static int menu_cbs_init_bind_left_compare_label(menu_file_list_cbs_t *cbs, break; case MENU_ENUM_LABEL_NO_ITEMS: case MENU_ENUM_LABEL_NO_PLAYLIST_ENTRIES_AVAILABLE: + case MENU_ENUM_LABEL_EXPLORE_INITIALISING_LIST: if ( string_ends_with_size(menu_label, "_tab", strlen(menu_label), diff --git a/menu/cbs/menu_cbs_right.c b/menu/cbs/menu_cbs_right.c index 951fdcf322..b6ad0fa2ae 100644 --- a/menu/cbs/menu_cbs_right.c +++ b/menu/cbs/menu_cbs_right.c @@ -1160,6 +1160,7 @@ static int menu_cbs_init_bind_right_compare_label(menu_file_list_cbs_t *cbs, break; case MENU_ENUM_LABEL_NO_ITEMS: case MENU_ENUM_LABEL_NO_PLAYLIST_ENTRIES_AVAILABLE: + case MENU_ENUM_LABEL_EXPLORE_INITIALISING_LIST: if ( string_ends_with_size(menu_label, "_tab", strlen(menu_label), diff --git a/menu/menu_driver.h b/menu/menu_driver.h index aaf01279f1..8e3c423caa 100644 --- a/menu/menu_driver.h +++ b/menu/menu_driver.h @@ -614,10 +614,15 @@ void menu_display_handle_wallpaper_upload(retro_task_t *task, void *user_data, const char *err); #if defined(HAVE_LIBRETRODB) +typedef struct explore_state explore_state_t; +explore_state_t *menu_explore_build_list(const char *directory_playlist, + const char *directory_database); uintptr_t menu_explore_get_entry_icon(unsigned type); void menu_explore_context_init(void); void menu_explore_context_deinit(void); +void menu_explore_free_state(explore_state_t *state); void menu_explore_free(void); +void menu_explore_set_state(explore_state_t *state); #endif /* Returns true if search filter is enabled diff --git a/menu/menu_explore.c b/menu/menu_explore.c index ea5877ed11..02b2631d7c 100644 --- a/menu/menu_explore.c +++ b/menu/menu_explore.c @@ -21,6 +21,7 @@ #include "../configuration.h" #include "../playlist.h" #include "../libretro-db/libretrodb.h" +#include "../tasks/tasks_internal.h" #include #include #include @@ -81,7 +82,7 @@ typedef struct #endif } explore_entry_t; -typedef struct +struct explore_state { ex_arena arena; /* ptr alignment */ explore_string_t **by[EXPLORE_CAT_COUNT]; @@ -95,7 +96,8 @@ typedef struct char title[1024]; char find_string[1024]; bool has_unknown[EXPLORE_CAT_COUNT]; -} explore_state_t; + bool menu_initialised; +}; static const struct { @@ -329,26 +331,6 @@ static void explore_unload_icons(explore_state_t *state) video_driver_texture_unload(&state->icons[i]); } -static void explore_free(explore_state_t *state) -{ - unsigned i; - if (!state) - return; - for (i = 0; i != EXPLORE_CAT_COUNT; i++) - RBUF_FREE(state->by[i]); - - RBUF_FREE(state->entries); - - for (i = 0; i != RBUF_LEN(state->playlists); i++) - playlist_free(state->playlists[i]); - RBUF_FREE(state->playlists); - - explore_unload_icons(state); - RBUF_FREE(state->icons); - - ex_arena_free(&state->arena); -} - static void explore_load_icons(explore_state_t *state) { char path[PATH_MAX_LENGTH]; @@ -399,7 +381,8 @@ static void explore_load_icons(explore_state_t *state) } } -static explore_state_t *explore_build_list(settings_t *settings) +explore_state_t *menu_explore_build_list(const char *directory_playlist, + const char *directory_database) { unsigned i; char tmp[PATH_MAX_LENGTH]; @@ -415,8 +398,6 @@ static explore_state_t *explore_build_list(settings_t *settings) int *rdb_indices = NULL; explore_string_t **cat_maps[EXPLORE_CAT_COUNT] = {NULL}; explore_string_t **split_buf = NULL; - const char *directory_playlist = settings->paths.directory_playlist; - const char *directory_database = settings->paths.path_content_database; libretro_vfs_implementation_dir *dir = NULL; explore_state_t *explore = (explore_state_t*)calloc( @@ -740,6 +721,14 @@ static explore_state_t *explore_build_list(settings_t *settings) return explore; } +static int explore_action_get_title_default( + const char *path, const char *label, + unsigned menu_type, char *s, size_t len) +{ + strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_EXPLORE_TAB), len); + return 0; +} + static int explore_action_get_title( const char *path, const char *label, unsigned menu_type, char *s, size_t len) @@ -844,9 +833,32 @@ unsigned menu_displaylist_explore(file_list_t *list, if (!explore_state) { - explore_state = explore_build_list(settings); + if (!menu_explore_init_in_progress(NULL)) + task_push_menu_explore_init( + settings->paths.directory_playlist, + settings->paths.path_content_database); + + menu_entries_append_enum(list, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_EXPLORE_INITIALISING_LIST), + msg_hash_to_str(MENU_ENUM_LABEL_EXPLORE_INITIALISING_LIST), + MENU_ENUM_LABEL_EXPLORE_INITIALISING_LIST, + FILE_TYPE_NONE, 0, 0); + + if (menu_stack->size > 1) + { + struct item_file *stack = &menu_stack->list[menu_stack->size - 1]; + menu_file_list_cbs_t* cbs = ((menu_file_list_cbs_t*)stack->actiondata); + cbs->action_get_title = explore_action_get_title_default; + } + + return list->size; + } + + if (!explore_state->menu_initialised) + { explore_state->top_depth = (unsigned)menu_stack->size - 1; explore_load_icons(explore_state); + explore_state->menu_initialised = true; } if (menu_stack->size > 1) @@ -1223,12 +1235,43 @@ void menu_explore_context_deinit(void) explore_unload_icons(explore_state); } +void menu_explore_free_state(explore_state_t *state) +{ + unsigned i; + if (!state) + return; + for (i = 0; i != EXPLORE_CAT_COUNT; i++) + RBUF_FREE(state->by[i]); + + RBUF_FREE(state->entries); + + for (i = 0; i != RBUF_LEN(state->playlists); i++) + playlist_free(state->playlists[i]); + RBUF_FREE(state->playlists); + + explore_unload_icons(state); + RBUF_FREE(state->icons); + + ex_arena_free(&state->arena); +} + void menu_explore_free(void) { if (!explore_state) return; - explore_free(explore_state); + menu_explore_free_state(explore_state); free(explore_state); explore_state = NULL; } + +void menu_explore_set_state(explore_state_t *state) +{ + if (!state) + return; + + if (explore_state) + menu_explore_free(); + + explore_state = state; +} diff --git a/msg_hash.h b/msg_hash.h index 89e1acea3a..34e4e45aa6 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -2052,6 +2052,7 @@ enum msg_hash_enums MENU_LABEL(RDB_ENTRY_DETAIL), /* Explore tab */ + MENU_LABEL(EXPLORE_INITIALISING_LIST), MENU_ENUM_LABEL_VALUE_EXPLORE_CATEGORY_RELEASE_YEAR, MENU_ENUM_LABEL_VALUE_EXPLORE_CATEGORY_PLAYER_COUNT, MENU_ENUM_LABEL_VALUE_EXPLORE_CATEGORY_REGION, diff --git a/retroarch.c b/retroarch.c index 3f3b0eafa5..65eedd3f13 100644 --- a/retroarch.c +++ b/retroarch.c @@ -1183,6 +1183,10 @@ bool menu_driver_ctl(enum rarch_menu_ctl_state state, void *data) core_updater_list_free_cached(); #endif #if defined(HAVE_MENU) && defined(HAVE_LIBRETRODB) + /* Before freeing the explore menu, we + * must wait for any explore menu initialisation + * tasks to complete */ + menu_explore_wait_for_init_task(); menu_explore_free(); #endif diff --git a/tasks/task_menu_explore.c b/tasks/task_menu_explore.c new file mode 100644 index 0000000000..c8eedffa36 --- /dev/null +++ b/tasks/task_menu_explore.c @@ -0,0 +1,245 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2021 - Hans-Kristian Arntzen + * Copyright (C) 2011-2021 - Daniel De Matteis + * Copyright (C) 2019-2021 - James Leaver + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include +#include +#include +#include + +#include + +#include "tasks_internal.h" + +#include "../menu/menu_entries.h" +#include "../menu/menu_driver.h" + +typedef struct menu_explore_init_handle +{ + explore_state_t *state; + char *directory_playlist; + char *directory_database; +} menu_explore_init_handle_t; + +/*********************/ +/* Utility Functions */ +/*********************/ + +static void free_menu_explore_init_handle( + menu_explore_init_handle_t *menu_explore) +{ + if (!menu_explore) + return; + + if (menu_explore->directory_playlist) + { + free(menu_explore->directory_playlist); + menu_explore->directory_playlist = NULL; + } + + if (menu_explore->directory_database) + { + free(menu_explore->directory_database); + menu_explore->directory_database = NULL; + } + + if (menu_explore->state) + { + menu_explore_free_state(menu_explore->state); + free(menu_explore->state); + menu_explore->state = NULL; + } + + free(menu_explore); + menu_explore = NULL; +} + +static void cb_task_menu_explore_init( + retro_task_t *task, void *task_data, + void *user_data, const char *err) +{ + menu_explore_init_handle_t *menu_explore = NULL; + const char *menu_label = NULL; + + if (!task) + return; + + menu_explore = (menu_explore_init_handle_t*)task->state; + + if (!menu_explore) + return; + + /* Assign global menu explore state object */ + menu_explore_set_state(menu_explore->state); + menu_explore->state = NULL; + + /* If the explore menu is currently displayed, + * it must be refreshed */ + menu_entries_get_last_stack(NULL, &menu_label, NULL, NULL, NULL); + + if (string_is_empty(menu_label)) + return; + + if (string_is_equal(menu_label, + msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_EXPLORE_LIST)) || + string_is_equal(menu_label, + msg_hash_to_str(MENU_ENUM_LABEL_EXPLORE_TAB))) + { + bool refresh = false; + menu_entries_ctl(MENU_ENTRIES_CTL_SET_REFRESH, &refresh); + menu_driver_ctl(RARCH_MENU_CTL_SET_PREVENT_POPULATE, NULL); + } +} + +static void task_menu_explore_init_free(retro_task_t *task) +{ + menu_explore_init_handle_t *menu_explore = NULL; + + if (!task) + return; + + menu_explore = (menu_explore_init_handle_t*)task->state; + + free_menu_explore_init_handle(menu_explore); +} + +/*******************************/ +/* Explore Menu Initialisation */ +/*******************************/ + +static void task_menu_explore_init_handler(retro_task_t *task) +{ + menu_explore_init_handle_t *menu_explore = NULL; + + if (!task) + goto task_finished; + + menu_explore = (menu_explore_init_handle_t*)task->state; + + if (!menu_explore) + goto task_finished; + + if (task_get_cancelled(task)) + goto task_finished; + + /* TODO/FIXME: It could be beneficial to + * initialise the explore menu iteratively, + * but this would require a non-trivial rewrite + * of the menu_explore code. For now, we will + * do it in a single shot (the most important + * consideration here is to place this + * initialisation on a background thread) */ + menu_explore->state = menu_explore_build_list( + menu_explore->directory_playlist, + menu_explore->directory_database); + + task_set_progress(task, 100); + +task_finished: + + if (task) + task_set_finished(task, true); +} + +static bool task_menu_explore_init_finder( + retro_task_t *task, void *user_data) +{ + if (!task) + return false; + + if (task->handler == task_menu_explore_init_handler) + return true; + + return false; +} + +bool task_push_menu_explore_init(const char *directory_playlist, + const char *directory_database) +{ + task_finder_data_t find_data; + retro_task_t *task = NULL; + menu_explore_init_handle_t *menu_explore = NULL; + + if (string_is_empty(directory_playlist) || + string_is_empty(directory_database)) + goto error; + + task = task_init(); + menu_explore = (menu_explore_init_handle_t*)calloc(1, + sizeof(menu_explore_init_handle_t)); + + if (!task || !menu_explore) + goto error; + + /* Explore menu is singular - cannot perform + * multiple initialisations simultaneously */ + find_data.func = task_menu_explore_init_finder; + find_data.userdata = NULL; + + if (task_queue_find(&find_data)) + goto error; + + /* Configure handle */ + menu_explore->state = NULL; + menu_explore->directory_playlist = strdup(directory_playlist); + menu_explore->directory_database = strdup(directory_database); + + /* Configure task + * > Note: This is silent task, with no title + * and no user notification messages */ + task->handler = task_menu_explore_init_handler; + task->state = menu_explore; + task->mute = true; + task->title = NULL; + task->progress = 0; + task->callback = cb_task_menu_explore_init; + task->cleanup = task_menu_explore_init_free; + + task_queue_push(task); + + return true; + +error: + + if (task) + { + free(task); + task = NULL; + } + + free_menu_explore_init_handle(menu_explore); + menu_explore = NULL; + + return false; +} + +bool menu_explore_init_in_progress(void *data) +{ + task_finder_data_t find_data; + + find_data.func = task_menu_explore_init_finder; + find_data.userdata = NULL; + + if (task_queue_find(&find_data)) + return true; + + return false; +} + +void menu_explore_wait_for_init_task(void) +{ + task_queue_wait(menu_explore_init_in_progress, NULL); +} diff --git a/tasks/tasks_internal.h b/tasks/tasks_internal.h index b4b1960940..983f03ad28 100644 --- a/tasks/tasks_internal.h +++ b/tasks/tasks_internal.h @@ -267,6 +267,14 @@ void set_save_state_in_background(bool state); void task_push_cdrom_dump(const char *drive); #endif +/* Menu explore tasks */ +#if defined(HAVE_MENU) && defined(HAVE_LIBRETRODB) +bool task_push_menu_explore_init(const char *directory_playlist, + const char *directory_database); +bool menu_explore_init_in_progress(void *data); +void menu_explore_wait_for_init_task(void); +#endif + RETRO_END_DECLS #endif