From 4f3adb8d2cc2583b17208353f4e656dbe7c9b2e3 Mon Sep 17 00:00:00 2001 From: jdgleaver Date: Fri, 12 Jul 2019 17:10:53 +0100 Subject: [PATCH] An environment callback for selectively hiding core options --- libretro-common/include/libretro.h | 30 +++++ managers/core_option_manager.h | 14 +++ menu/menu_displaylist.c | 184 +++++++++++++++++++---------- retroarch.c | 69 +++++++++++ retroarch.h | 1 + 5 files changed, 238 insertions(+), 60 deletions(-) diff --git a/libretro-common/include/libretro.h b/libretro-common/include/libretro.h index 8b223bfbb9..9fae22d0c3 100644 --- a/libretro-common/include/libretro.h +++ b/libretro-common/include/libretro.h @@ -1218,6 +1218,26 @@ enum retro_mod * instead. */ +#define RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY 55 + /* struct retro_core_option_display * -- + * + * Allows an implementation to signal the environment to show + * or hide a variable when displaying core options. This is + * considered a *suggestion*. The frontend is free to ignore + * this callback, and its implementation not considered mandatory. + * + * 'data' points to a retro_core_option_display struct + * + * retro_core_option_display::key is a variable identifier + * which has already been set by SET_VARIABLES/SET_CORE_OPTIONS. + * + * retro_core_option_display::visible is a boolean, specifying + * whether variable should be displayed + * + * Note that all core option variables will be set visible by + * default when calling SET_VARIABLES/SET_CORE_OPTIONS. + */ + /* VFS functionality */ /* File paths: @@ -2463,6 +2483,16 @@ struct retro_variable const char *value; }; +struct retro_core_option_display +{ + /* Variable to configure in RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY */ + const char *key; + + /* Specifies whether variable should be displayed + * when presenting core options to the user */ + bool visible; +}; + /* Maximum number of values permitted for a core option * NOTE: This may be increased on a core-by-core basis * if required (doing so has no effect on the frontend) */ diff --git a/managers/core_option_manager.h b/managers/core_option_manager.h index e9161d4b9a..8e4c6b8d5b 100644 --- a/managers/core_option_manager.h +++ b/managers/core_option_manager.h @@ -35,6 +35,7 @@ struct core_option struct string_list *vals; struct string_list *val_labels; size_t index; + bool visible; }; struct core_option_manager @@ -106,6 +107,19 @@ const char *core_option_manager_get_val(core_option_manager_t *opt, const char *core_option_manager_get_val_label(core_option_manager_t *opt, size_t idx); +/** + * core_option_manager_get_visible: + * @opt : options manager handle + * @idx : idx identifier of the option + * + * Gets whether option should be visible when displaying + * core options in the frontend + * + * Returns: 'true' if option should be displayed by the frontend. + **/ +bool core_option_manager_get_visible(core_option_manager_t *opt, + size_t idx); + void core_option_manager_set_val(core_option_manager_t *opt, size_t idx, size_t val_idx); diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index d00a95d362..047cd743d1 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -6068,63 +6068,86 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, info->need_push = true; break; case DISPLAYLIST_CORE_OPTIONS: - menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); - - if (rarch_ctl(RARCH_CTL_HAS_CORE_OPTIONS, NULL)) { - size_t opts = 0; - settings_t *settings = config_get_ptr(); + /* Number of displayed options is dynamic. If user opens + * 'Quick Menu > Core Options', toggles something + * that changes the number of displayed items, then + * toggles the Quick Menu off and on again (returning + * to the Core Options menu) the menu must be refreshed + * (or undefined behaviour occurs). + * The only way to check whether the number of visible + * options has changed is to cache the last set menu size, + * and compare this with the new size after processing + * the current core_option_manager_t struct */ + size_t prev_count = info->list->size; - rarch_ctl(RARCH_CTL_GET_CORE_OPTION_SIZE, &opts); + menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); - if (settings->bools.game_specific_options) + if (rarch_ctl(RARCH_CTL_HAS_CORE_OPTIONS, NULL)) { - if (!rarch_ctl(RARCH_CTL_IS_GAME_OPTIONS_ACTIVE, NULL)) + size_t opts = 0; + settings_t *settings = config_get_ptr(); + + rarch_ctl(RARCH_CTL_GET_CORE_OPTION_SIZE, &opts); + + if (settings->bools.game_specific_options) { - if (menu_entries_append_enum(info->list, - msg_hash_to_str( - MENU_ENUM_LABEL_VALUE_GAME_SPECIFIC_OPTIONS_CREATE), - msg_hash_to_str( - MENU_ENUM_LABEL_GAME_SPECIFIC_OPTIONS_CREATE), - MENU_ENUM_LABEL_GAME_SPECIFIC_OPTIONS_CREATE, - MENU_SETTINGS_CORE_OPTION_CREATE, 0, 0)) - count++; + if (!rarch_ctl(RARCH_CTL_IS_GAME_OPTIONS_ACTIVE, NULL)) + { + if (menu_entries_append_enum(info->list, + msg_hash_to_str( + MENU_ENUM_LABEL_VALUE_GAME_SPECIFIC_OPTIONS_CREATE), + msg_hash_to_str( + MENU_ENUM_LABEL_GAME_SPECIFIC_OPTIONS_CREATE), + MENU_ENUM_LABEL_GAME_SPECIFIC_OPTIONS_CREATE, + MENU_SETTINGS_CORE_OPTION_CREATE, 0, 0)) + count++; + } + else + if (menu_entries_append_enum(info->list, + msg_hash_to_str( + MENU_ENUM_LABEL_VALUE_GAME_SPECIFIC_OPTIONS_IN_USE), + msg_hash_to_str( + MENU_ENUM_LABEL_GAME_SPECIFIC_OPTIONS_IN_USE), + MENU_ENUM_LABEL_GAME_SPECIFIC_OPTIONS_IN_USE, + MENU_SETTINGS_CORE_OPTION_CREATE, 0, 0)) + count++; } - else - if (menu_entries_append_enum(info->list, - msg_hash_to_str( - MENU_ENUM_LABEL_VALUE_GAME_SPECIFIC_OPTIONS_IN_USE), - msg_hash_to_str( - MENU_ENUM_LABEL_GAME_SPECIFIC_OPTIONS_IN_USE), - MENU_ENUM_LABEL_GAME_SPECIFIC_OPTIONS_IN_USE, - MENU_SETTINGS_CORE_OPTION_CREATE, 0, 0)) - count++; - } - if (opts != 0) - { - core_option_manager_t *coreopts = NULL; - - rarch_ctl(RARCH_CTL_CORE_OPTIONS_LIST_GET, &coreopts); - - for (i = 0; i < opts; i++) + if (opts != 0) { - menu_entries_append_enum(info->list, - core_option_manager_get_desc(coreopts, i), "", - MENU_ENUM_LABEL_CORE_OPTION_ENTRY, - (unsigned)(MENU_SETTINGS_CORE_OPTION_START + i), 0, 0); - count++; + core_option_manager_t *coreopts = NULL; + + rarch_ctl(RARCH_CTL_CORE_OPTIONS_LIST_GET, &coreopts); + + for (i = 0; i < opts; i++) + { + if (core_option_manager_get_visible(coreopts, i)) + { + menu_entries_append_enum(info->list, + core_option_manager_get_desc(coreopts, i), "", + MENU_ENUM_LABEL_CORE_OPTION_ENTRY, + (unsigned)(MENU_SETTINGS_CORE_OPTION_START + i), 0, 0); + count++; + } + } } } + + if (count == 0) + menu_entries_append_enum(info->list, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_CORE_OPTIONS_AVAILABLE), + msg_hash_to_str(MENU_ENUM_LABEL_NO_CORE_OPTIONS_AVAILABLE), + MENU_ENUM_LABEL_NO_CORE_OPTIONS_AVAILABLE, + MENU_SETTINGS_CORE_OPTION_NONE, 0, 0); + + if (count != prev_count) + { + info->need_refresh = true; + info->need_navigation_clear = true; + } + info->need_push = true; } - - if (count == 0) - menu_entries_append_enum(info->list, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_CORE_OPTIONS_AVAILABLE), - msg_hash_to_str(MENU_ENUM_LABEL_NO_CORE_OPTIONS_AVAILABLE), - MENU_ENUM_LABEL_NO_CORE_OPTIONS_AVAILABLE, - MENU_SETTINGS_CORE_OPTION_NONE, 0, 0); - info->need_push = true; break; case DISPLAYLIST_ARCHIVE_ACTION: menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); @@ -8047,24 +8070,42 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, { settings_t *settings = config_get_ptr(); unsigned size = (unsigned)tmp_str_list->size; - unsigned i = atoi(tmp_str_list->elems[size-1].data); + unsigned menu_index = atoi(tmp_str_list->elems[size-1].data); + unsigned visible_index = 0; + unsigned option_index = 0; + bool option_found = false; struct core_option *option = NULL; bool checked_found = false; unsigned checked = 0; + unsigned i; /* Note: Although we display value labels here, * most logic is performed using values. This seems * more appropriate somehow... */ + /* Convert menu index to option index */ if (settings->bools.game_specific_options) - { - val = core_option_manager_get_val(coreopts, i-1); - i--; - } - else - val = core_option_manager_get_val(coreopts, i); + menu_index--; - option = (struct core_option*)&coreopts->opts[i]; + for (i = 0; i < coreopts->size; i++) + { + if (core_option_manager_get_visible(coreopts, i)) + { + if (visible_index == menu_index) + { + option_found = true; + option_index = i; + break; + } + visible_index++; + } + } + + if (option_found) + { + val = core_option_manager_get_val(coreopts, option_index); + option = (struct core_option*)&coreopts->opts[option_index]; + } if (option) { @@ -8077,7 +8118,7 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, if (!string_is_empty(val_label_str)) { char val_d[256]; - snprintf(val_d, sizeof(val_d), "%d", i); + snprintf(val_d, sizeof(val_d), "%d", option_index); if (string_is_equal(val_label_str, msg_hash_to_str(MENU_ENUM_LABEL_ENABLED))) val_label_str = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ON); @@ -8379,25 +8420,48 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, if (tmp_str_list && tmp_str_list->size > 0) { core_option_manager_t *coreopts = NULL; + const char *val = NULL; rarch_ctl(RARCH_CTL_CORE_OPTIONS_LIST_GET, &coreopts); if (coreopts) { unsigned size = (unsigned)tmp_str_list->size; - unsigned i = atoi(tmp_str_list->elems[size-1].data); + unsigned menu_index = atoi(tmp_str_list->elems[size-1].data); + unsigned visible_index = 0; + unsigned option_index = 0; + bool option_found = false; struct core_option *option = NULL; bool checked_found = false; unsigned checked = 0; - const char *val = core_option_manager_get_val(coreopts, i-1); + unsigned i; /* Note: Although we display value labels here, * most logic is performed using values. This seems * more appropriate somehow... */ - i--; + /* Convert menu index to option index */ + menu_index--; - option = (struct core_option*)&coreopts->opts[i]; + for (i = 0; i < coreopts->size; i++) + { + if (core_option_manager_get_visible(coreopts, i)) + { + if (visible_index == menu_index) + { + option_found = true; + option_index = i; + break; + } + visible_index++; + } + } + + if (option_found) + { + val = core_option_manager_get_val(coreopts, option_index); + option = (struct core_option*)&coreopts->opts[option_index]; + } if (option) { @@ -8410,7 +8474,7 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, if (!string_is_empty(val_label_str)) { char val_d[256]; - snprintf(val_d, sizeof(val_d), "%d", i); + snprintf(val_d, sizeof(val_d), "%d", option_index); if (string_is_equal(val_label_str, msg_hash_to_str(MENU_ENUM_LABEL_ENABLED))) val_label_str = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ON); diff --git a/retroarch.c b/retroarch.c index 1919f6f621..9037e21178 100644 --- a/retroarch.c +++ b/retroarch.c @@ -1693,6 +1693,9 @@ static bool core_option_manager_parse_variable( char *config_val = NULL; struct core_option *option = (struct core_option*)&opt->opts[idx]; + /* All options are visible by default */ + option->visible = true; + if (!string_is_empty(var->key)) option->key = strdup(var->key); if (!string_is_empty(var->value)) @@ -1758,6 +1761,9 @@ static bool core_option_manager_parse_option( struct core_option *option = (struct core_option*)&opt->opts[idx]; const struct retro_core_option_value *values = option_def->values; + /* All options are visible by default */ + option->visible = true; + if (!string_is_empty(option_def->key)) option->key = strdup(option_def->key); @@ -2133,6 +2139,26 @@ const char *core_option_manager_get_val_label(core_option_manager_t *opt, size_t return option->val_labels->elems[option->index].data; } +/** + * core_option_manager_get_visible: + * @opt : options manager handle + * @idx : idx identifier of the option + * + * Gets whether option should be visible when displaying + * core options in the frontend + * + * Returns: 'true' if option should be displayed by the frontend. + **/ +bool core_option_manager_get_visible(core_option_manager_t *opt, + size_t idx) +{ + if (!opt) + return NULL; + if (idx >= opt->size) + return NULL; + return opt->opts[idx].visible; +} + void core_option_manager_set_val(core_option_manager_t *opt, size_t idx, size_t val_idx) { @@ -2302,6 +2328,26 @@ static struct retro_core_option_definition *core_option_manager_get_definitions( return option_defs; } +static void core_option_manager_set_display(core_option_manager_t *opt, + const char *key, bool visible) +{ + size_t i; + + if (!opt || string_is_empty(key)) + return; + + for (i = 0; i < opt->size; i++) + { + if (string_is_empty(opt->opts[i].key)) + continue; + + if (string_is_equal(opt->opts[i].key, key)) + { + opt->opts[i].visible = visible; + return; + } + } +} /* DYNAMIC LIBRETRO CORE */ @@ -2924,6 +2970,13 @@ bool rarch_environment_cb(unsigned cmd, void *data) break; + case RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY: + RARCH_LOG("Environ RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY.\n"); + + rarch_ctl(RARCH_CTL_CORE_OPTIONS_DISPLAY, data); + + break; + case RETRO_ENVIRONMENT_SET_MESSAGE: { const struct retro_message *msg = (const struct retro_message*)data; @@ -19015,6 +19068,22 @@ bool rarch_ctl(enum rarch_ctl_state state, void *data) runloop_core_options = NULL; } break; + + case RARCH_CTL_CORE_OPTIONS_DISPLAY: + { + const struct retro_core_option_display *core_options_display = + (const struct retro_core_option_display*)data; + + if (!runloop_core_options || !core_options_display) + return false; + + core_option_manager_set_display( + runloop_core_options, + core_options_display->key, + core_options_display->visible); + } + break; + case RARCH_CTL_KEY_EVENT_GET: { retro_keyboard_event_t **key_event = diff --git a/retroarch.h b/retroarch.h index ca7fa3e84a..65d704e69b 100644 --- a/retroarch.h +++ b/retroarch.h @@ -184,6 +184,7 @@ enum rarch_ctl_state RARCH_CTL_CORE_OPTIONS_INIT, RARCH_CTL_CORE_OPTIONS_INTL_INIT, RARCH_CTL_CORE_OPTIONS_DEINIT, + RARCH_CTL_CORE_OPTIONS_DISPLAY, /* System info */ RARCH_CTL_SYSTEM_INFO_INIT,