Add enviroment callback to enable cores to notify the frontend that a core option value has changed

This commit is contained in:
jdgleaver 2021-10-06 16:42:55 +01:00
parent 54cbeecbb7
commit 534f8da487
4 changed files with 178 additions and 3 deletions

View File

@ -725,13 +725,20 @@ static bool core_option_manager_parse_variable(
if (!option->val_labels)
goto error;
/* > Loop over values and 'extract' labels */
/* > Loop over values and:
* - Set value hashes
* - 'Extract' labels */
for (i = 0; i < option->vals->size; i++)
{
const char *value = option->vals->elems[i].data;
uint32_t *value_hash = (uint32_t *)malloc(sizeof(uint32_t));
const char *value_label = core_option_manager_parse_value_label(
value, NULL);
/* Set value hash */
*value_hash = core_option_manager_hash_string(value);
option->vals->elems[i].userdata = (void*)value_hash;
/* Redundant safely check... */
value_label = string_is_empty(value_label) ?
value : value_label;
@ -753,9 +760,15 @@ static bool core_option_manager_parse_variable(
/* Set current config value */
if (entry && !string_is_empty(entry->value))
{
uint32_t entry_value_hash = core_option_manager_hash_string(entry->value);
for (i = 0; i < option->vals->size; i++)
{
if (string_is_equal(option->vals->elems[i].data, entry->value))
const char *value = option->vals->elems[i].data;
uint32_t value_hash = *((uint32_t*)option->vals->elems[i].userdata);
if ((value_hash == entry_value_hash) &&
string_is_equal(value, entry->value))
{
option->index = i;
break;
@ -998,12 +1011,17 @@ static bool core_option_manager_parse_option(
for (i = 0; i < num_vals; i++)
{
const char *value = values[i].value;
uint32_t *value_hash = (uint32_t *)malloc(sizeof(uint32_t));
const char *value_label = values[i].label;
/* Append value string
* > We know that 'value' is always valid */
string_list_append(option->vals, value, attr);
/* > Set value hash */
*value_hash = core_option_manager_hash_string(value);
option->vals->elems[option->vals->size - 1].userdata = (void*)value_hash;
/* Value label requires additional processing */
value_label = core_option_manager_parse_value_label(
value, value_label);
@ -1034,9 +1052,15 @@ static bool core_option_manager_parse_option(
/* Set current config value */
if (entry && !string_is_empty(entry->value))
{
uint32_t entry_value_hash = core_option_manager_hash_string(entry->value);
for (i = 0; i < option->vals->size; i++)
{
if (string_is_equal(option->vals->elems[i].data, entry->value))
const char *value = option->vals->elems[i].data;
uint32_t value_hash = *((uint32_t*)option->vals->elems[i].userdata);
if ((value_hash == entry_value_hash) &&
string_is_equal(value, entry->value))
{
option->index = i;
break;
@ -1479,6 +1503,57 @@ bool core_option_manager_get_idx(core_option_manager_t *opt,
return false;
}
/**
* core_option_manager_get_val_idx:
*
* @opt : options manager handle
* @idx : core option index
* @val : string representation of the
* core option value
* @val_idx : index of core option value
* corresponding to @val
*
* Fetches the index of the core option value
* identified by the specified core option @idx
* and @val string.
*
* Returns: true if option value matching the
* specified option index and value string
* was found, otherwise false.
**/
bool core_option_manager_get_val_idx(core_option_manager_t *opt,
size_t idx, const char *val, size_t *val_idx)
{
struct core_option *option = NULL;
uint32_t val_hash;
size_t i;
if (!opt ||
(idx >= opt->size) ||
string_is_empty(val) ||
!val_idx)
return false;
val_hash = core_option_manager_hash_string(val);
option = (struct core_option*)&opt->opts[idx];
for (i = 0; i < option->vals->size; i++)
{
const char *option_val = option->vals->elems[i].data;
uint32_t option_val_hash = *((uint32_t*)option->vals->elems[i].userdata);
if ((val_hash == option_val_hash) &&
!string_is_empty(option_val) &&
string_is_equal(val, option_val))
{
*val_idx = i;
return true;
}
}
return false;
}
/**
* core_option_manager_get_desc:
*

View File

@ -278,6 +278,27 @@ bool core_option_manager_get_category_visible(core_option_manager_t *opt,
bool core_option_manager_get_idx(core_option_manager_t *opt,
const char *key, size_t *idx);
/**
* core_option_manager_get_val_idx:
*
* @opt : options manager handle
* @idx : core option index
* @val : string representation of the
* core option value
* @val_idx : index of core option value
* corresponding to @val
*
* Fetches the index of the core option value
* identified by the specified core option @idx
* and @val string.
*
* Returns: true if option value matching the
* specified option index and value string
* was found, otherwise false.
**/
bool core_option_manager_get_val_idx(core_option_manager_t *opt,
size_t idx, const char *val, size_t *val_idx);
/**
* core_option_manager_get_desc:
*

View File

@ -1722,6 +1722,31 @@ enum retro_mod
* Must be called in retro_set_environment().
*/
#define RETRO_ENVIRONMENT_SET_VARIABLE 70
/* const struct retro_variable * --
* Allows an implementation to notify the frontend
* that a core option value has changed.
*
* retro_variable::key and retro_variable::value
* must match strings that have been set previously
* via one of the following:
*
* - RETRO_ENVIRONMENT_SET_VARIABLES
* - RETRO_ENVIRONMENT_SET_CORE_OPTIONS
* - RETRO_ENVIRONMENT_SET_CORE_OPTIONS_INTL
* - RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2
* - RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2_INTL
*
* After changing a core option value via this
* callback, RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE
* will return true.
*
* If data is NULL, no changes will be registered
* and the callback will return true; an
* implementation may therefore pass NULL in order
* to test whether the callback is supported.
*/
/* VFS functionality */
/* File paths:

View File

@ -10267,6 +10267,60 @@ static bool retroarch_environment_cb(unsigned cmd, void *data)
break;
case RETRO_ENVIRONMENT_SET_VARIABLE:
{
unsigned log_level = settings->uints.libretro_log_level;
const struct retro_variable *var = (const struct retro_variable*)data;
size_t opt_idx;
size_t val_idx;
/* If core passes NULL to the callback, return
* value indicates whether callback is supported */
if (!var)
return true;
if (string_is_empty(var->key) ||
string_is_empty(var->value))
return false;
if (!runloop_state.core_options)
{
RARCH_LOG("[Environ]: SET_VARIABLE %s: not implemented.\n",
var->key);
return false;
}
/* Check whether key is valid */
if (!core_option_manager_get_idx(runloop_state.core_options,
var->key, &opt_idx))
{
RARCH_LOG("[Environ]: SET_VARIABLE %s: invalid key.\n",
var->key);
return false;
}
/* Check whether value is valid */
if (!core_option_manager_get_val_idx(runloop_state.core_options,
opt_idx, var->value, &val_idx))
{
RARCH_LOG("[Environ]: SET_VARIABLE %s: invalid value: %s\n",
var->key, var->value);
return false;
}
/* Update option value if core-requested value
* is not currently set */
if (val_idx != runloop_state.core_options->opts[opt_idx].index)
core_option_manager_set_val(runloop_state.core_options,
opt_idx, val_idx, true);
if (log_level == RETRO_LOG_DEBUG)
RARCH_LOG("[Environ]: SET_VARIABLE %s:\n\t%s\n",
var->key, var->value);
}
break;
/* SET_VARIABLES: Legacy path */
case RETRO_ENVIRONMENT_SET_VARIABLES:
RARCH_LOG("[Environ]: SET_VARIABLES.\n");