mirror of
https://github.com/libretro/RetroArch.git
synced 2024-11-23 16:09:47 +00:00
933 lines
28 KiB
C
933 lines
28 KiB
C
/* RetroArch - A frontend for libretro.
|
|
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
|
|
* Copyright (C) 2011-2021 - Daniel De Matteis
|
|
*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "core_info.h"
|
|
#include "driver.h"
|
|
#include "retroarch.h"
|
|
#include "runloop.h"
|
|
#include "verbosity.h"
|
|
|
|
#ifdef HAVE_BLUETOOTH
|
|
#include "bluetooth/bluetooth_driver.h"
|
|
#endif
|
|
#ifdef HAVE_NETWORKING
|
|
#ifdef HAVE_WIFI
|
|
#include "network/wifi_driver.h"
|
|
#endif
|
|
#endif
|
|
#include "led/led_driver.h"
|
|
#include "midi_driver.h"
|
|
#include "gfx/video_driver.h"
|
|
#include "gfx/video_display_server.h"
|
|
#include "audio/audio_driver.h"
|
|
#include "camera/camera_driver.h"
|
|
#include "record/record_driver.h"
|
|
#include "location_driver.h"
|
|
|
|
#ifdef HAVE_GFX_WIDGETS
|
|
#include "gfx/gfx_widgets.h"
|
|
#endif
|
|
|
|
#ifdef HAVE_MENU
|
|
#include "menu/menu_driver.h"
|
|
#ifdef HAVE_CHEEVOS
|
|
#include "cheevos/cheevos_menu.h"
|
|
#endif
|
|
#endif
|
|
|
|
static void retro_frame_null(const void *data, unsigned width,
|
|
unsigned height, size_t pitch) { }
|
|
void retro_input_poll_null(void) { }
|
|
|
|
/**
|
|
* find_driver_nonempty:
|
|
* @label : string of driver type to be found.
|
|
* @i : index of driver.
|
|
* @str : identifier name of the found driver
|
|
* gets written to this string.
|
|
* @len : size of @str.
|
|
*
|
|
* Find driver based on @label.
|
|
*
|
|
* Returns: NULL if no driver based on @label found, otherwise
|
|
* pointer to driver.
|
|
**/
|
|
static const void *find_driver_nonempty(
|
|
const char *label, int i,
|
|
char *s, size_t len)
|
|
{
|
|
if (string_is_equal(label, "camera_driver"))
|
|
{
|
|
if (camera_drivers[i])
|
|
{
|
|
const char *ident = camera_drivers[i]->ident;
|
|
|
|
strlcpy(s, ident, len);
|
|
return camera_drivers[i];
|
|
}
|
|
}
|
|
else if (string_is_equal(label, "location_driver"))
|
|
{
|
|
if (location_drivers[i])
|
|
{
|
|
const char *ident = location_drivers[i]->ident;
|
|
|
|
strlcpy(s, ident, len);
|
|
return location_drivers[i];
|
|
}
|
|
}
|
|
#ifdef HAVE_MENU
|
|
else if (string_is_equal(label, "menu_driver"))
|
|
{
|
|
if (menu_ctx_drivers[i])
|
|
{
|
|
const char *ident = menu_ctx_drivers[i]->ident;
|
|
|
|
strlcpy(s, ident, len);
|
|
return menu_ctx_drivers[i];
|
|
}
|
|
}
|
|
#endif
|
|
else if (string_is_equal(label, "input_driver"))
|
|
{
|
|
if (input_drivers[i])
|
|
{
|
|
const char *ident = input_drivers[i]->ident;
|
|
|
|
strlcpy(s, ident, len);
|
|
return input_drivers[i];
|
|
}
|
|
}
|
|
else if (string_is_equal(label, "input_joypad_driver"))
|
|
{
|
|
if (joypad_drivers[i])
|
|
{
|
|
const char *ident = joypad_drivers[i]->ident;
|
|
|
|
strlcpy(s, ident, len);
|
|
return joypad_drivers[i];
|
|
}
|
|
}
|
|
else if (string_is_equal(label, "video_driver"))
|
|
{
|
|
if (video_drivers[i])
|
|
{
|
|
const char *ident = video_drivers[i]->ident;
|
|
|
|
strlcpy(s, ident, len);
|
|
return video_drivers[i];
|
|
}
|
|
}
|
|
else if (string_is_equal(label, "audio_driver"))
|
|
{
|
|
if (audio_drivers[i])
|
|
{
|
|
const char *ident = audio_drivers[i]->ident;
|
|
|
|
strlcpy(s, ident, len);
|
|
return audio_drivers[i];
|
|
}
|
|
}
|
|
else if (string_is_equal(label, "record_driver"))
|
|
{
|
|
if (record_drivers[i])
|
|
{
|
|
const char *ident = record_drivers[i]->ident;
|
|
|
|
strlcpy(s, ident, len);
|
|
return record_drivers[i];
|
|
}
|
|
}
|
|
else if (string_is_equal(label, "midi_driver"))
|
|
{
|
|
if (midi_driver_find_handle(i))
|
|
{
|
|
const char *ident = midi_drivers[i]->ident;
|
|
|
|
strlcpy(s, ident, len);
|
|
return midi_drivers[i];
|
|
}
|
|
}
|
|
else if (string_is_equal(label, "audio_resampler_driver"))
|
|
{
|
|
if (audio_resampler_driver_find_handle(i))
|
|
{
|
|
const char *ident = audio_resampler_driver_find_ident(i);
|
|
|
|
strlcpy(s, ident, len);
|
|
return audio_resampler_driver_find_handle(i);
|
|
}
|
|
}
|
|
#ifdef HAVE_BLUETOOTH
|
|
else if (string_is_equal(label, "bluetooth_driver"))
|
|
{
|
|
if (bluetooth_drivers[i])
|
|
{
|
|
const char *ident = bluetooth_drivers[i]->ident;
|
|
|
|
strlcpy(s, ident, len);
|
|
return bluetooth_drivers[i];
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef HAVE_WIFI
|
|
else if (string_is_equal(label, "wifi_driver"))
|
|
{
|
|
if (wifi_drivers[i])
|
|
{
|
|
const char *ident = wifi_drivers[i]->ident;
|
|
|
|
strlcpy(s, ident, len);
|
|
return wifi_drivers[i];
|
|
}
|
|
}
|
|
#endif
|
|
return NULL;
|
|
}
|
|
|
|
int driver_find_index(const char *label, const char *drv)
|
|
{
|
|
unsigned i;
|
|
char str[NAME_MAX_LENGTH];
|
|
|
|
str[0] = '\0';
|
|
|
|
for (i = 0;
|
|
find_driver_nonempty(label, i, str, sizeof(str)) != NULL; i++)
|
|
{
|
|
if (string_is_empty(str))
|
|
break;
|
|
if (string_is_equal_noncase(drv, str))
|
|
return i;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* driver_find_last:
|
|
* @label : string of driver type to be found.
|
|
* @s : identifier of driver to be found.
|
|
* @len : size of @s.
|
|
*
|
|
* Find last driver in driver array.
|
|
**/
|
|
static void driver_find_last(const char *label, char *s, size_t len)
|
|
{
|
|
unsigned i;
|
|
|
|
for (i = 0;
|
|
find_driver_nonempty(label, i, s, len) != NULL; i++) { }
|
|
|
|
if (i)
|
|
i = i - 1;
|
|
else
|
|
i = 0;
|
|
|
|
find_driver_nonempty(label, i, s, len);
|
|
}
|
|
|
|
/**
|
|
* driver_find_prev:
|
|
* @label : string of driver type to be found.
|
|
* @s : identifier of driver to be found.
|
|
* @len : size of @s.
|
|
*
|
|
* Find previous driver in driver array.
|
|
**/
|
|
static bool driver_find_prev(const char *label, char *s, size_t len)
|
|
{
|
|
int i = driver_find_index(label, s);
|
|
|
|
if (i > 0)
|
|
{
|
|
find_driver_nonempty(label, i - 1, s, len);
|
|
return true;
|
|
}
|
|
|
|
RARCH_WARN(
|
|
"Couldn't find any previous driver (current one: \"%s\").\n", s);
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* driver_find_next:
|
|
* @label : string of driver type to be found.
|
|
* @s : identifier of driver to be found.
|
|
* @len : size of @s.
|
|
*
|
|
* Find next driver in driver array.
|
|
**/
|
|
static bool driver_find_next(const char *label, char *s, size_t len)
|
|
{
|
|
int i = driver_find_index(label, s);
|
|
|
|
if (i >= 0 && string_is_not_equal(s, "null"))
|
|
{
|
|
find_driver_nonempty(label, i + 1, s, len);
|
|
return true;
|
|
}
|
|
|
|
RARCH_WARN("%s (current one: \"%s\").\n",
|
|
msg_hash_to_str(MSG_COULD_NOT_FIND_ANY_NEXT_DRIVER),
|
|
s);
|
|
return false;
|
|
}
|
|
|
|
static void driver_adjust_system_rates(
|
|
bool vrr_runloop_enable,
|
|
float video_refresh_rate,
|
|
float audio_max_timing_skew,
|
|
bool video_adaptive_vsync,
|
|
unsigned video_swap_interval)
|
|
{
|
|
runloop_state_t *runloop_st = runloop_state_get_ptr();
|
|
video_driver_state_t *video_st = video_state_get_ptr();
|
|
struct retro_system_av_info *av_info = &video_st->av_info;
|
|
const struct retro_system_timing *info =
|
|
(const struct retro_system_timing*)&av_info->timing;
|
|
double input_sample_rate = info->sample_rate;
|
|
double input_fps = info->fps;
|
|
|
|
/* Update video swap interval if automatic
|
|
* switching is enabled */
|
|
runloop_set_video_swap_interval(
|
|
vrr_runloop_enable,
|
|
video_st->flags & VIDEO_FLAG_CRT_SWITCHING_ACTIVE,
|
|
video_swap_interval,
|
|
audio_max_timing_skew,
|
|
video_refresh_rate,
|
|
input_fps);
|
|
video_swap_interval = runloop_get_video_swap_interval(
|
|
video_swap_interval);
|
|
|
|
if (input_sample_rate > 0.0)
|
|
{
|
|
audio_driver_state_t *audio_st = audio_state_get_ptr();
|
|
if (vrr_runloop_enable)
|
|
audio_st->input = input_sample_rate;
|
|
else
|
|
audio_st->input =
|
|
audio_driver_monitor_adjust_system_rates(
|
|
input_sample_rate,
|
|
input_fps,
|
|
video_refresh_rate,
|
|
video_swap_interval,
|
|
audio_max_timing_skew);
|
|
|
|
RARCH_LOG("[Audio]: Set audio input rate to: %.2f Hz.\n",
|
|
audio_st->input);
|
|
}
|
|
|
|
runloop_st->flags &= ~RUNLOOP_FLAG_FORCE_NONBLOCK;
|
|
|
|
if (input_fps > 0.0)
|
|
{
|
|
float timing_skew_hz = video_refresh_rate;
|
|
|
|
if (video_st->flags & VIDEO_FLAG_CRT_SWITCHING_ACTIVE)
|
|
timing_skew_hz = input_fps;
|
|
video_st->core_hz = input_fps;
|
|
|
|
if (!video_driver_monitor_adjust_system_rates(
|
|
timing_skew_hz,
|
|
video_refresh_rate,
|
|
vrr_runloop_enable,
|
|
audio_max_timing_skew,
|
|
video_swap_interval,
|
|
input_fps))
|
|
{
|
|
/* We won't be able to do VSync reliably
|
|
when game FPS > monitor FPS. */
|
|
runloop_st->flags |= RUNLOOP_FLAG_FORCE_NONBLOCK;
|
|
RARCH_LOG("[Video]: Game FPS > Monitor FPS. Cannot rely on VSync.\n");
|
|
|
|
if (VIDEO_DRIVER_GET_PTR_INTERNAL(video_st))
|
|
{
|
|
if (video_st->current_video->set_nonblock_state)
|
|
video_st->current_video->set_nonblock_state(
|
|
video_st->data, true,
|
|
video_driver_test_all_flags(GFX_CTX_FLAGS_ADAPTIVE_VSYNC)
|
|
&& video_adaptive_vsync,
|
|
video_swap_interval);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (VIDEO_DRIVER_GET_PTR_INTERNAL(video_st))
|
|
driver_set_nonblock_state();
|
|
}
|
|
|
|
/**
|
|
* driver_set_nonblock_state:
|
|
*
|
|
* Sets audio and video drivers to nonblock state (if enabled).
|
|
*
|
|
* If nonblock state is false, sets
|
|
* blocking state for both audio and video drivers instead.
|
|
**/
|
|
void driver_set_nonblock_state(void)
|
|
{
|
|
runloop_state_t *runloop_st = runloop_state_get_ptr();
|
|
input_driver_state_t
|
|
*input_st = input_state_get_ptr();
|
|
audio_driver_state_t
|
|
*audio_st = audio_state_get_ptr();
|
|
video_driver_state_t
|
|
*video_st = video_state_get_ptr();
|
|
bool enable = input_st ?
|
|
(input_st->flags & INP_FLAG_NONBLOCKING) : false;
|
|
settings_t *settings = config_get_ptr();
|
|
bool audio_sync = settings->bools.audio_sync;
|
|
bool video_vsync = settings->bools.video_vsync;
|
|
bool adaptive_vsync = settings->bools.video_adaptive_vsync;
|
|
unsigned swap_interval = runloop_get_video_swap_interval(
|
|
settings->uints.video_swap_interval);
|
|
bool video_driver_active = video_st->flags & VIDEO_FLAG_ACTIVE;
|
|
bool audio_driver_active = audio_st->flags & AUDIO_FLAG_ACTIVE;
|
|
bool runloop_force_nonblock = runloop_st->flags & RUNLOOP_FLAG_FORCE_NONBLOCK;
|
|
|
|
/* Only apply non-block-state for video if we're using vsync. */
|
|
if (video_driver_active && VIDEO_DRIVER_GET_PTR_INTERNAL(video_st))
|
|
{
|
|
if (video_st->current_video->set_nonblock_state)
|
|
{
|
|
bool video_nonblock = enable;
|
|
if (!video_vsync || runloop_force_nonblock)
|
|
video_nonblock = true;
|
|
video_st->current_video->set_nonblock_state(video_st->data,
|
|
video_nonblock,
|
|
video_driver_test_all_flags(GFX_CTX_FLAGS_ADAPTIVE_VSYNC) &&
|
|
adaptive_vsync, swap_interval);
|
|
}
|
|
}
|
|
|
|
if (audio_driver_active && audio_st->context_audio_data)
|
|
audio_st->current_audio->set_nonblock_state(
|
|
audio_st->context_audio_data,
|
|
audio_sync ? enable : true);
|
|
|
|
audio_st->chunk_size = enable
|
|
? audio_st->chunk_nonblock_size
|
|
: audio_st->chunk_block_size;
|
|
}
|
|
|
|
void drivers_init(
|
|
settings_t *settings,
|
|
int flags,
|
|
bool verbosity_enabled)
|
|
{
|
|
runloop_state_t *runloop_st = runloop_state_get_ptr();
|
|
audio_driver_state_t *audio_st = audio_state_get_ptr();
|
|
input_driver_state_t *input_st = input_state_get_ptr();
|
|
video_driver_state_t *video_st = video_state_get_ptr();
|
|
#ifdef HAVE_MENU
|
|
struct menu_state *menu_st = menu_state_get_ptr();
|
|
#endif
|
|
camera_driver_state_t
|
|
*camera_st = camera_state_get_ptr();
|
|
location_driver_state_t
|
|
*location_st = location_state_get_ptr();
|
|
bool video_is_threaded = VIDEO_DRIVER_IS_THREADED_INTERNAL(video_st);
|
|
gfx_display_t *p_disp = disp_get_ptr();
|
|
#if defined(HAVE_GFX_WIDGETS)
|
|
bool video_font_enable = settings->bools.video_font_enable;
|
|
bool menu_enable_widgets = settings->bools.menu_enable_widgets;
|
|
dispgfx_widget_t *p_dispwidget = dispwidget_get_ptr();
|
|
/* By default, we want display widgets to persist through driver reinits. */
|
|
p_dispwidget->flags |= DISPGFX_WIDGET_FLAG_PERSISTING;
|
|
#endif
|
|
#ifdef HAVE_MENU
|
|
/* By default, we want the menu to persist through driver reinits. */
|
|
if (menu_st)
|
|
menu_st->flags |= MENU_ST_FLAG_DATA_OWN;
|
|
#endif
|
|
|
|
if (flags & (DRIVER_VIDEO_MASK | DRIVER_AUDIO_MASK))
|
|
driver_adjust_system_rates(
|
|
settings->bools.vrr_runloop_enable,
|
|
settings->floats.video_refresh_rate,
|
|
settings->floats.audio_max_timing_skew,
|
|
settings->bools.video_adaptive_vsync,
|
|
settings->uints.video_swap_interval
|
|
);
|
|
|
|
/* Initialize video driver */
|
|
if (flags & DRIVER_VIDEO_MASK)
|
|
{
|
|
struct retro_hw_render_callback *hwr =
|
|
VIDEO_DRIVER_GET_HW_CONTEXT_INTERNAL(video_st);
|
|
|
|
video_st->frame_time_count = 0;
|
|
|
|
video_driver_lock_new();
|
|
#ifdef HAVE_VIDEO_FILTER
|
|
video_driver_filter_free();
|
|
#endif
|
|
video_driver_set_cached_frame_ptr(NULL);
|
|
if (!video_driver_init_internal(&video_is_threaded,
|
|
verbosity_enabled))
|
|
retroarch_fail(1, "video_driver_init_internal()");
|
|
|
|
if ( !(video_st->flags & VIDEO_FLAG_CACHE_CONTEXT_ACK)
|
|
&& hwr->context_reset)
|
|
hwr->context_reset();
|
|
video_st->flags &= ~VIDEO_FLAG_CACHE_CONTEXT_ACK;
|
|
runloop_st->frame_time_last = 0;
|
|
}
|
|
|
|
/* Initialize audio driver */
|
|
if (flags & DRIVER_AUDIO_MASK)
|
|
{
|
|
audio_driver_init_internal(
|
|
settings,
|
|
audio_st->callback.callback != NULL);
|
|
if ( audio_st->current_audio
|
|
&& audio_st->current_audio->device_list_new
|
|
&& audio_st->context_audio_data)
|
|
audio_st->devices_list = (struct string_list*)
|
|
audio_st->current_audio->device_list_new(
|
|
audio_st->context_audio_data);
|
|
}
|
|
|
|
/* Regular display refresh rate startup autoswitch based on content av_info */
|
|
if (flags & (DRIVER_VIDEO_MASK | DRIVER_AUDIO_MASK))
|
|
{
|
|
struct retro_system_av_info *av_info = &video_st->av_info;
|
|
float refresh_rate = av_info->timing.fps;
|
|
unsigned autoswitch_refresh_rate = settings->uints.video_autoswitch_refresh_rate;
|
|
bool exclusive_fullscreen = settings->bools.video_fullscreen && !settings->bools.video_windowed_fullscreen;
|
|
bool windowed_fullscreen = settings->bools.video_fullscreen && settings->bools.video_windowed_fullscreen;
|
|
bool all_fullscreen = settings->bools.video_fullscreen || settings->bools.video_windowed_fullscreen;
|
|
|
|
if ( refresh_rate > 0.0
|
|
&& !settings->uints.crt_switch_resolution
|
|
&& !settings->bools.vrr_runloop_enable
|
|
&& video_display_server_has_resolution_list()
|
|
&& (autoswitch_refresh_rate != AUTOSWITCH_REFRESH_RATE_OFF)
|
|
&& (fabs(settings->floats.video_refresh_rate - refresh_rate) > 1))
|
|
{
|
|
if (((autoswitch_refresh_rate == AUTOSWITCH_REFRESH_RATE_EXCLUSIVE_FULLSCREEN) && exclusive_fullscreen) ||
|
|
((autoswitch_refresh_rate == AUTOSWITCH_REFRESH_RATE_WINDOWED_FULLSCREEN) && windowed_fullscreen) ||
|
|
((autoswitch_refresh_rate == AUTOSWITCH_REFRESH_RATE_ALL_FULLSCREEN) && all_fullscreen))
|
|
{
|
|
bool video_switch_refresh_rate = false;
|
|
|
|
video_switch_refresh_rate_maybe(&refresh_rate, &video_switch_refresh_rate);
|
|
|
|
if (video_switch_refresh_rate && video_display_server_set_refresh_rate(refresh_rate))
|
|
{
|
|
int reinit_flags = DRIVER_AUDIO_MASK;
|
|
video_monitor_set_refresh_rate(refresh_rate);
|
|
/* Audio must reinit after successful rate switch */
|
|
command_event(CMD_EVENT_REINIT, &reinit_flags);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (flags & DRIVER_CAMERA_MASK)
|
|
{
|
|
/* Only initialize camera driver if we're ever going to use it. */
|
|
if (camera_st->active)
|
|
{
|
|
/* Resource leaks will follow if camera is initialized twice. */
|
|
if (!camera_st->data)
|
|
{
|
|
if (!camera_driver_find_driver("camera driver",
|
|
verbosity_enabled))
|
|
retroarch_fail(1, "find_camera_driver()");
|
|
|
|
if (camera_st->driver)
|
|
{
|
|
camera_st->data = camera_st->driver->init(
|
|
*settings->arrays.camera_device ?
|
|
settings->arrays.camera_device : NULL,
|
|
camera_st->cb.caps,
|
|
settings->uints.camera_width ?
|
|
settings->uints.camera_width : camera_st->cb.width,
|
|
settings->uints.camera_height ?
|
|
settings->uints.camera_height : camera_st->cb.height);
|
|
|
|
if (!camera_st->data)
|
|
{
|
|
RARCH_ERR("Failed to initialize camera driver. Will continue without camera.\n");
|
|
camera_st->active = false;
|
|
}
|
|
|
|
if (camera_st->cb.initialized)
|
|
camera_st->cb.initialized();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef HAVE_BLUETOOTH
|
|
if (flags & DRIVER_BLUETOOTH_MASK)
|
|
bluetooth_driver_ctl(RARCH_BLUETOOTH_CTL_INIT, NULL);
|
|
#endif
|
|
#ifdef HAVE_WIFI
|
|
if ((flags & DRIVER_WIFI_MASK))
|
|
wifi_driver_ctl(RARCH_WIFI_CTL_INIT, NULL);
|
|
#endif
|
|
|
|
if (flags & DRIVER_LOCATION_MASK)
|
|
{
|
|
/* Only initialize location driver if we're ever going to use it. */
|
|
if (location_st->active)
|
|
if (!init_location(&runloop_state_get_ptr()->system,
|
|
settings, verbosity_is_enabled()))
|
|
location_st->active = false;
|
|
}
|
|
|
|
core_info_init_current_core();
|
|
|
|
#if defined(HAVE_GFX_WIDGETS)
|
|
/* Note that we only enable widgets if 'video_font_enable'
|
|
* is true. 'video_font_enable' corresponds to the generic
|
|
* 'On-Screen Notifications' setting, which should serve as
|
|
* a global notifications on/off toggle switch */
|
|
if ( video_font_enable
|
|
&& menu_enable_widgets
|
|
&& video_driver_has_widgets())
|
|
{
|
|
bool rarch_force_fullscreen = video_st->flags &
|
|
VIDEO_FLAG_FORCE_FULLSCREEN;
|
|
bool video_is_fullscreen = settings->bools.video_fullscreen ||
|
|
rarch_force_fullscreen;
|
|
|
|
p_dispwidget->active= gfx_widgets_init(
|
|
p_disp,
|
|
anim_get_ptr(),
|
|
settings,
|
|
(uintptr_t)&p_dispwidget->active,
|
|
video_is_threaded,
|
|
video_st->width,
|
|
video_st->height,
|
|
video_is_fullscreen,
|
|
settings->paths.directory_assets,
|
|
settings->paths.path_font);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
gfx_display_init_first_driver(p_disp, video_is_threaded);
|
|
}
|
|
|
|
#ifdef HAVE_MENU
|
|
if (flags & DRIVER_VIDEO_MASK)
|
|
{
|
|
/* Initialize menu driver */
|
|
if (flags & DRIVER_MENU_MASK)
|
|
{
|
|
if (!menu_driver_init(video_is_threaded))
|
|
RARCH_ERR("Unable to init menu driver.\n");
|
|
|
|
#ifdef HAVE_LIBRETRODB
|
|
menu_explore_context_init();
|
|
#endif
|
|
menu_contentless_cores_context_init();
|
|
}
|
|
}
|
|
|
|
/* Initialising the menu driver will also initialise
|
|
* core info - if we are not initialising the menu
|
|
* driver, must initialise core info 'by hand' */
|
|
if (!(flags & DRIVER_VIDEO_MASK) ||
|
|
!(flags & DRIVER_MENU_MASK))
|
|
{
|
|
command_event(CMD_EVENT_CORE_INFO_INIT, NULL);
|
|
command_event(CMD_EVENT_LOAD_CORE_PERSIST, NULL);
|
|
}
|
|
|
|
#else
|
|
/* Qt uses core info, even if the menu is disabled */
|
|
command_event(CMD_EVENT_CORE_INFO_INIT, NULL);
|
|
command_event(CMD_EVENT_LOAD_CORE_PERSIST, NULL);
|
|
#endif
|
|
|
|
/* Keep non-throttled state as good as possible. */
|
|
if (flags & (DRIVER_VIDEO_MASK | DRIVER_AUDIO_MASK))
|
|
if (input_st && (input_st->flags & INP_FLAG_NONBLOCKING))
|
|
driver_set_nonblock_state();
|
|
|
|
/* Initialize LED driver */
|
|
if (flags & DRIVER_LED_MASK)
|
|
led_driver_init(settings->arrays.led_driver);
|
|
|
|
/* Initialize MIDI driver */
|
|
if (flags & DRIVER_MIDI_MASK)
|
|
midi_driver_init(settings);
|
|
|
|
#ifndef HAVE_LAKKA_SWITCH
|
|
#ifdef HAVE_LAKKA
|
|
cpu_scaling_driver_init();
|
|
#endif
|
|
#endif /* #ifndef HAVE_LAKKA_SWITCH */
|
|
}
|
|
|
|
void driver_uninit(int flags)
|
|
{
|
|
runloop_state_t *runloop_st = runloop_state_get_ptr();
|
|
video_driver_state_t *video_st = video_state_get_ptr();
|
|
camera_driver_state_t *camera_st = camera_state_get_ptr();
|
|
#if defined(HAVE_GFX_WIDGETS)
|
|
dispgfx_widget_t *p_dispwidget = dispwidget_get_ptr();
|
|
#endif
|
|
|
|
core_info_deinit_list();
|
|
core_info_free_current_core();
|
|
|
|
#if defined(HAVE_GFX_WIDGETS)
|
|
/* This absolutely has to be done before video_driver_free_internal()
|
|
* is called/completes, otherwise certain menu drivers
|
|
* (e.g. Vulkan) will segfault */
|
|
if (p_dispwidget->flags & DISPGFX_WIDGET_FLAG_INITED)
|
|
{
|
|
gfx_widgets_deinit(p_dispwidget->flags & DISPGFX_WIDGET_FLAG_PERSISTING);
|
|
p_dispwidget->active = false;
|
|
}
|
|
#endif
|
|
|
|
#ifdef HAVE_MENU
|
|
if (flags & DRIVER_MENU_MASK)
|
|
{
|
|
#ifdef HAVE_LIBRETRODB
|
|
menu_explore_context_deinit();
|
|
#endif
|
|
menu_contentless_cores_context_deinit();
|
|
|
|
#ifdef HAVE_CHEEVOS
|
|
rcheevos_menu_reset_badges();
|
|
#endif
|
|
|
|
menu_driver_ctl(RARCH_MENU_CTL_DEINIT, NULL);
|
|
}
|
|
#endif
|
|
|
|
if ((flags & DRIVER_LOCATION_MASK))
|
|
uninit_location(&runloop_st->system);
|
|
|
|
if ((flags & DRIVER_CAMERA_MASK))
|
|
{
|
|
if (camera_st->data && camera_st->driver)
|
|
{
|
|
if (camera_st->cb.deinitialized)
|
|
camera_st->cb.deinitialized();
|
|
|
|
if (camera_st->driver->free)
|
|
camera_st->driver->free(camera_st->data);
|
|
}
|
|
|
|
camera_st->data = NULL;
|
|
}
|
|
|
|
#ifdef HAVE_BLUETOOTH
|
|
if ((flags & DRIVER_BLUETOOTH_MASK))
|
|
bluetooth_driver_ctl(RARCH_BLUETOOTH_CTL_DEINIT, NULL);
|
|
#endif
|
|
#ifdef HAVE_WIFI
|
|
if ((flags & DRIVER_WIFI_MASK))
|
|
wifi_driver_ctl(RARCH_WIFI_CTL_DEINIT, NULL);
|
|
#endif
|
|
|
|
if (flags & DRIVER_LED)
|
|
led_driver_free();
|
|
|
|
if (flags & DRIVERS_VIDEO_INPUT)
|
|
{
|
|
video_driver_free_internal();
|
|
VIDEO_DRIVER_LOCK_FREE(video_st);
|
|
video_st->data = NULL;
|
|
video_driver_set_cached_frame_ptr(NULL);
|
|
}
|
|
|
|
if (flags & DRIVER_AUDIO_MASK)
|
|
audio_driver_deinit();
|
|
|
|
if ((flags & DRIVER_VIDEO_MASK))
|
|
video_st->data = NULL;
|
|
|
|
if ((flags & DRIVER_INPUT_MASK))
|
|
input_state_get_ptr()->current_data = NULL;
|
|
|
|
if ((flags & DRIVER_AUDIO_MASK))
|
|
audio_state_get_ptr()->context_audio_data = NULL;
|
|
|
|
if (flags & DRIVER_MIDI_MASK)
|
|
midi_driver_free();
|
|
|
|
#ifndef HAVE_LAKKA_SWITCH
|
|
#ifdef HAVE_LAKKA
|
|
cpu_scaling_driver_free();
|
|
#endif
|
|
#endif /* #ifndef HAVE_LAKKA_SWITCH */
|
|
}
|
|
|
|
void retroarch_deinit_drivers(struct retro_callbacks *cbs)
|
|
{
|
|
input_driver_state_t *input_st = input_state_get_ptr();
|
|
video_driver_state_t *video_st = video_state_get_ptr();
|
|
camera_driver_state_t *camera_st= camera_state_get_ptr();
|
|
location_driver_state_t
|
|
*location_st = location_state_get_ptr();
|
|
runloop_state_t *runloop_st = runloop_state_get_ptr();
|
|
#if defined(HAVE_GFX_WIDGETS)
|
|
/* Tear down display widgets no matter what
|
|
* in case the handle is lost in the threaded
|
|
* video driver in the meantime
|
|
* (breaking video_driver_has_widgets) */
|
|
dispgfx_widget_t *p_dispwidget = dispwidget_get_ptr();
|
|
if (p_dispwidget->flags & DISPGFX_WIDGET_FLAG_INITED)
|
|
{
|
|
gfx_widgets_deinit(
|
|
p_dispwidget->flags & DISPGFX_WIDGET_FLAG_PERSISTING);
|
|
p_dispwidget->active = false;
|
|
}
|
|
#endif
|
|
|
|
#if defined(HAVE_CRTSWITCHRES)
|
|
/* Switchres deinit */
|
|
if (video_st->flags & VIDEO_FLAG_CRT_SWITCHING_ACTIVE)
|
|
crt_destroy_modes(&video_st->crt_switch_st);
|
|
#endif
|
|
|
|
/* Video */
|
|
video_display_server_destroy();
|
|
|
|
video_st->flags &= ~(VIDEO_FLAG_ACTIVE | VIDEO_FLAG_USE_RGBA |
|
|
VIDEO_FLAG_HDR_SUPPORT | VIDEO_FLAG_CACHE_CONTEXT |
|
|
VIDEO_FLAG_CACHE_CONTEXT_ACK
|
|
);
|
|
video_st->record_gpu_buffer = NULL;
|
|
video_st->current_video = NULL;
|
|
video_driver_set_cached_frame_ptr(NULL);
|
|
|
|
/* Audio */
|
|
audio_state_get_ptr()->flags &= ~AUDIO_FLAG_ACTIVE;
|
|
audio_state_get_ptr()->current_audio = NULL;
|
|
|
|
if (input_st)
|
|
{
|
|
/* Input */
|
|
input_st->flags &= ~(INP_FLAG_KB_LINEFEED_ENABLE
|
|
| INP_FLAG_BLOCK_HOTKEY
|
|
| INP_FLAG_BLOCK_LIBRETRO_INPUT
|
|
| INP_FLAG_NONBLOCKING);
|
|
|
|
memset(&input_st->turbo_btns, 0, sizeof(turbo_buttons_t));
|
|
memset(&input_st->analog_requested, 0,
|
|
sizeof(input_st->analog_requested));
|
|
input_st->current_driver = NULL;
|
|
}
|
|
|
|
#ifdef HAVE_MENU
|
|
menu_driver_destroy(
|
|
menu_state_get_ptr());
|
|
#endif
|
|
location_st->active = false;
|
|
destroy_location();
|
|
|
|
/* Camera */
|
|
camera_st->active = false;
|
|
camera_st->driver = NULL;
|
|
camera_st->data = NULL;
|
|
|
|
#ifdef HAVE_BLUETOOTH
|
|
bluetooth_driver_ctl(RARCH_BLUETOOTH_CTL_DESTROY, NULL);
|
|
#endif
|
|
#ifdef HAVE_WIFI
|
|
wifi_driver_ctl(RARCH_WIFI_CTL_DESTROY, NULL);
|
|
#endif
|
|
|
|
cbs->frame_cb = retro_frame_null;
|
|
cbs->poll_cb = retro_input_poll_null;
|
|
cbs->sample_cb = NULL;
|
|
cbs->sample_batch_cb = NULL;
|
|
cbs->state_cb = NULL;
|
|
|
|
runloop_st->current_core.flags &= ~RETRO_CORE_FLAG_INITED;
|
|
}
|
|
|
|
bool driver_ctl(enum driver_ctl_state state, void *data)
|
|
{
|
|
driver_ctx_info_t *drv = (driver_ctx_info_t*)data;
|
|
|
|
switch (state)
|
|
{
|
|
case RARCH_DRIVER_CTL_SET_REFRESH_RATE:
|
|
{
|
|
float *hz = (float*)data;
|
|
audio_driver_state_t
|
|
*audio_st = audio_state_get_ptr();
|
|
settings_t *settings = config_get_ptr();
|
|
unsigned
|
|
audio_output_sample_rate = settings->uints.audio_output_sample_rate;
|
|
bool vrr_runloop_enable = settings->bools.vrr_runloop_enable;
|
|
float video_refresh_rate = settings->floats.video_refresh_rate;
|
|
float audio_max_timing_skew = settings->floats.audio_max_timing_skew;
|
|
bool video_adaptive_vsync = settings->bools.video_adaptive_vsync;
|
|
unsigned video_swap_interval = settings->uints.video_swap_interval;
|
|
|
|
video_monitor_set_refresh_rate(*hz);
|
|
|
|
/* Sets audio monitor rate to new value. */
|
|
audio_st->source_ratio_original =
|
|
audio_st->source_ratio_current =
|
|
(double)audio_output_sample_rate / audio_st->input;
|
|
|
|
driver_adjust_system_rates(
|
|
vrr_runloop_enable,
|
|
video_refresh_rate,
|
|
audio_max_timing_skew,
|
|
video_adaptive_vsync,
|
|
video_swap_interval
|
|
);
|
|
}
|
|
break;
|
|
case RARCH_DRIVER_CTL_FIND_FIRST:
|
|
if (!drv)
|
|
return false;
|
|
find_driver_nonempty(drv->label, 0, drv->s, drv->len);
|
|
break;
|
|
case RARCH_DRIVER_CTL_FIND_LAST:
|
|
if (!drv)
|
|
return false;
|
|
driver_find_last(drv->label, drv->s, drv->len);
|
|
break;
|
|
case RARCH_DRIVER_CTL_FIND_PREV:
|
|
if (!drv)
|
|
return false;
|
|
return driver_find_prev(drv->label, drv->s, drv->len);
|
|
case RARCH_DRIVER_CTL_FIND_NEXT:
|
|
if (!drv)
|
|
return false;
|
|
return driver_find_next(drv->label, drv->s, drv->len);
|
|
case RARCH_DRIVER_CTL_NONE:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|