Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an environment call for querying the device's battery #15387

Merged
merged 14 commits into from
Aug 12, 2023
Merged
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ being able to do the expected tvOS behavior of "backing out" of the app.
- LIBRETRO: Add API to check JIT availability on iOS
- LIBRETRO: Allow RETRO_ENVIRONMENT_SET_MEMORY_MAPS also after core startup. Change the comment in libretro.h about the removed limit and handle the environment call during core runtime in RetroArch.
- LIBRETRO/MICROPHONE: Add new API for microphone support.
- LIBRETRO: Add new API for querying the device's power state.
- LINUX: Input driver fix 8+ joypads. It was reported that controllers beyond 8 worked only partially (analogs yes, but not buttons), and the found fix was also confirmed.
- MENU: Start directory browsing from current value
- MENU: Fix menu toggle combo hold with same 'enable_hotkey'
Expand Down
98 changes: 97 additions & 1 deletion libretro-common/include/libretro.h
Original file line number Diff line number Diff line change
Expand Up @@ -1833,6 +1833,22 @@ enum retro_mod
* input devices does not need to take any action on its own.
*/

#define RETRO_ENVIRONMENT_GET_DEVICE_POWER (77 | RETRO_ENVIRONMENT_EXPERIMENTAL)
/* struct retro_device_power * --
* Returns the device's current power state as reported by the frontend.
* This is useful for emulating the battery level in handheld consoles,
* or for reducing power consumption when on battery power.
*
* The return value indicates whether the frontend can provide this information,
* even if the parameter is NULL.
*
* If the frontend does not support this functionality,
* then the provided argument will remain unchanged.
*
* Note that this environment call describes the power state for the entire device,
* not for individual peripherals like controllers.
*/

/* VFS functionality */

/* File paths:
Expand Down Expand Up @@ -3133,7 +3149,7 @@ typedef void (RETRO_CALLCONV *retro_netpacket_disconnected_t)(uint16_t client_id

/**
* A callback interface for giving a core the ability to send and receive custom
* network packets during a multiplayer session between two or more instances
* network packets during a multiplayer session between two or more instances
* of a libretro frontend.
*
* @see RETRO_ENVIRONMENT_SET_NETPACKET_INTERFACE
Expand Down Expand Up @@ -4153,6 +4169,86 @@ struct retro_microphone_interface
retro_read_mic_t read_mic;
};

/**
* Describes how a device is being powered.
* @see RETRO_ENVIRONMENT_GET_DEVICE_POWER
*/
enum retro_power_state
{
/**
* Indicates that the frontend cannot report its power state at this time,
* most likely due to a lack of support.
*
* \c RETRO_ENVIRONMENT_GET_DEVICE_POWER will not return this value;
* instead, the environment callback will return \c false.
*/
RETRO_POWERSTATE_UNKNOWN = 0,

/**
* Indicates that the device is running on its battery.
* Usually applies to portable devices such as handhelds, laptops, and smartphones.
*/
RETRO_POWERSTATE_DISCHARGING,

/**
* Indicates that the device's battery is currently charging.
*/
RETRO_POWERSTATE_CHARGING,

/**
* Indicates that the device is connected to a power source
* and that its battery has finished charging.
*/
RETRO_POWERSTATE_CHARGED,

/**
* Indicates that the device is connected to a power source
* and that it does not have a battery.
* This usually suggests a desktop computer or a non-portable game console.
*/
RETRO_POWERSTATE_PLUGGED_IN
};

/**
* Indicates that an estimate is not available for the battery level or time remaining,
* even if the actual power state is known.
*/
#define RETRO_POWERSTATE_NO_ESTIMATE (-1)

/**
* Describes the power state of the device running the frontend.
* @see RETRO_ENVIRONMENT_GET_DEVICE_POWER
*/
struct retro_device_power
{
/**
* The current state of the frontend's power usage.
*/
enum retro_power_state state;

/**
* A rough estimate of the amount of time remaining (in seconds)
* before the device powers off.
* This value depends on a variety of factors,
* so it is not guaranteed to be accurate.
*
* Will be set to \c RETRO_POWERSTATE_NO_ESTIMATE if \c state does not equal \c RETRO_POWERSTATE_DISCHARGING.
* May still be set to \c RETRO_POWERSTATE_NO_ESTIMATE if the frontend is unable to provide an estimate.
*/
int seconds;

/**
* The approximate percentage of battery charge,
* ranging from 0 to 100 (inclusive).
* The device may power off before this reaches 0.
*
* The user might have configured their device
* to stop charging before the battery is full,
* so do not assume that this will be 100 in the \c RETRO_POWERSTATE_CHARGED state.
*/
int8_t percent;
};

/* Callbacks */

/* Environment callback. Gives implementations a way of performing
Expand Down
75 changes: 62 additions & 13 deletions runloop.c
Original file line number Diff line number Diff line change
Expand Up @@ -1277,7 +1277,7 @@ static void runloop_init_core_options_path(

/* If not using per-core options, or if a per-core options
* file does not yet exist, must fetch 'global' options path */
if ( !per_core_options
if ( !per_core_options
|| !per_core_options_exist)
{
const char *options_path = path_core_options;
Expand Down Expand Up @@ -1975,7 +1975,7 @@ bool runloop_environment_cb(unsigned cmd, void *data)
const char *dir_system = settings->paths.directory_system;
bool systemfiles_in_content_dir = settings->bools.systemfiles_in_content_dir;

if ( string_is_empty(dir_system)
if ( string_is_empty(dir_system)
|| systemfiles_in_content_dir)
{
const char *fullpath = path_get(RARCH_PATH_CONTENT);
Expand Down Expand Up @@ -2657,7 +2657,7 @@ bool runloop_environment_cb(unsigned cmd, void *data)

/* First set new refresh rate and display rate, then after REINIT do
* another display rate change to make sure the change stays */
if ( video_switch_refresh_rate
if ( video_switch_refresh_rate
&& video_display_server_set_refresh_rate(refresh_rate))
video_monitor_set_refresh_rate(refresh_rate);

Expand Down Expand Up @@ -3258,7 +3258,7 @@ bool runloop_environment_cb(unsigned cmd, void *data)

/* VSync overrides the mode if the rate is limited by the display. */
if ( menu_opened /* Menu currently always runs with vsync on. */
|| (settings->bools.video_vsync
|| (settings->bools.video_vsync
&& (!(runloop_st->flags & RUNLOOP_FLAG_FORCE_NONBLOCK))
&& !(input_state_get_ptr()->flags & INP_FLAG_NONBLOCKING)))
{
Expand Down Expand Up @@ -3500,6 +3500,55 @@ bool runloop_environment_cb(unsigned cmd, void *data)
return false;
#endif

case RETRO_ENVIRONMENT_GET_DEVICE_POWER:
{
struct retro_device_power *status = (struct retro_device_power *)data;
frontend_ctx_driver_t *frontend = frontend_get_ptr();
int seconds = 0;
int percent = 0;

/* If the frontend driver is unavailable... */
if (!frontend)
return false;

/* If the core just wants to query support for this environment call... */
if (!status)
return frontend->get_powerstate != NULL;

/* If the frontend driver doesn't support reporting the powerstate... */
if (frontend->get_powerstate == NULL)
return false;

switch (frontend->get_powerstate(&seconds, &percent))
{
case FRONTEND_POWERSTATE_ON_POWER_SOURCE: /* on battery power */
status->state = RETRO_POWERSTATE_DISCHARGING;
status->percent = (int8_t)percent;
status->seconds = seconds == 0 ? RETRO_POWERSTATE_NO_ESTIMATE : seconds;
break;
case FRONTEND_POWERSTATE_CHARGING /* battery available, charging */:
status->state = RETRO_POWERSTATE_CHARGING;
status->percent = (int8_t)percent;
status->seconds = seconds == 0 ? RETRO_POWERSTATE_NO_ESTIMATE : seconds;
break;
case FRONTEND_POWERSTATE_CHARGED: /* on AC, battery is full */
status->state = RETRO_POWERSTATE_CHARGED;
status->percent = (int8_t)percent;
status->seconds = RETRO_POWERSTATE_NO_ESTIMATE;
break;
case FRONTEND_POWERSTATE_NO_SOURCE: /* on AC, no battery available */
status->state = RETRO_POWERSTATE_PLUGGED_IN;
status->percent = RETRO_POWERSTATE_NO_ESTIMATE;
status->seconds = RETRO_POWERSTATE_NO_ESTIMATE;
break;
default:
/* The frontend driver supports power status queries,
* but it still gave us bad information for whatever reason. */
return false;
break;
}
}
break;
default:
RARCH_LOG("[Environ]: UNSUPPORTED (#%u).\n", cmd);
return false;
Expand Down Expand Up @@ -4073,7 +4122,7 @@ static bool runloop_path_init_subsystem(runloop_state_t *runloop_st)
{
unsigned num_content = MIN(info->num_roms,
subsystem_path_empty
? 0
? 0
: (unsigned)runloop_st->subsystem_fullpaths->size);

for (i = 0; i < num_content; i++)
Expand Down Expand Up @@ -4229,7 +4278,7 @@ static bool event_init_content(
are true.
*/
#ifdef HAVE_CHEEVOS
if ( !cheevos_enable
if ( !cheevos_enable
|| !cheevos_hardcore_mode_enable)
#endif
{
Expand All @@ -4239,7 +4288,7 @@ static bool event_init_content(
if (!(input_st->bsv_movie_state.flags & BSV_FLAG_MOVIE_START_PLAYBACK))
#endif
{
if ( runloop_st->entry_state_slot
if ( runloop_st->entry_state_slot
&& !command_event_load_entry_state(settings))
{
/* loading the state failed, reset entry slot */
Expand Down Expand Up @@ -5441,8 +5490,8 @@ static enum runloop_state_enum runloop_check_state(
if (input_active || (menu_st->input_driver_flushing_input > 0))
{
BIT256_CLEAR_ALL(current_bits);
if ( runloop_paused
&& !runloop_paused_hotkey
if ( runloop_paused
&& !runloop_paused_hotkey
&& settings->bools.menu_pause_libretro)
BIT256_SET(current_bits, RARCH_PAUSE_TOGGLE);
else if (runloop_paused_hotkey)
Expand Down Expand Up @@ -5601,7 +5650,7 @@ static enum runloop_state_enum runloop_check_state(
quit_press_twice = settings->bools.quit_press_twice;

/* Check double press if enabled */
if ( trig_quit_key
if ( trig_quit_key
&& quit_press_twice)
{
static retro_time_t quit_key_time = 0;
Expand Down Expand Up @@ -7996,13 +8045,13 @@ void runloop_path_set_redirect(settings_t *settings,

if (string_is_empty(new_savefile_dir))
RARCH_LOG("Cannot resolve save file path.\n");
else if (sort_savefiles_enable
else if (sort_savefiles_enable
|| sort_savefiles_by_content_enable)
RARCH_LOG("Saving files in content directory is set. This overrides other save file directory settings.\n");
}

/* Set savestate directory if empty based on content directory */
if ( string_is_empty(new_savestate_dir)
if ( string_is_empty(new_savestate_dir)
|| savestates_in_content_dir)
{
strlcpy(new_savestate_dir,
Expand All @@ -8012,7 +8061,7 @@ void runloop_path_set_redirect(settings_t *settings,

if (string_is_empty(new_savestate_dir))
RARCH_LOG("Cannot resolve save state file path.\n");
else if (sort_savestates_enable
else if (sort_savestates_enable
|| sort_savestates_by_content_enable)
RARCH_LOG("Saving save states in content directory is set. This overrides other save state file directory settings.\n");
}
Expand Down
Loading