This commit is contained in:
Connor McLaughlin 2022-08-09 01:14:22 +10:00
parent 73a80d3a1d
commit 33590d58a1
21 changed files with 426 additions and 132 deletions

View File

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

View File

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -0,0 +1,97 @@
/*
*
* Created: 29.03.2018
*
* Authors:
*
* Assembled from the code released on Stackoverflow by:
* Dennis (instructable.com/member/nqtronix) | https://stackoverflow.com/questions/23032002/c-c-how-to-get-integer-unix-timestamp-of-build-time-not-string
* and
* Alexis Wilke | https://stackoverflow.com/questions/10538444/do-you-know-of-a-c-macro-to-compute-unix-time-and-date
*
* Assembled by Jean Rabault
*
* BUILD_UNIX_TIMESTAMP gives the UNIX timestamp (unsigned long integer of seconds since 1st Jan 1970) of compilation from macros using the compiler defined __TIME__ macro.
* This should include Gregorian calendar leap days, in particular the 29ths of February, 100 and 400 years modulo leaps.
*
* Careful: __TIME__ is the local time of the computer, NOT the UTC time in general!
*
*/
#ifndef COMPILE_TIME_H_
#define COMPILE_TIME_H_
// Some definitions for calculation
#define SEC_PER_MIN 60UL
#define SEC_PER_HOUR 3600UL
#define SEC_PER_DAY 86400UL
#define SEC_PER_YEAR (SEC_PER_DAY*365)
// extracts 1..4 characters from a string and interprets it as a decimal value
#define CONV_STR2DEC_1(str, i) (str[i]>'0'?str[i]-'0':0)
#define CONV_STR2DEC_2(str, i) (CONV_STR2DEC_1(str, i)*10 + str[i+1]-'0')
#define CONV_STR2DEC_3(str, i) (CONV_STR2DEC_2(str, i)*10 + str[i+2]-'0')
#define CONV_STR2DEC_4(str, i) (CONV_STR2DEC_3(str, i)*10 + str[i+3]-'0')
// Custom "glue logic" to convert the month name to a usable number
#define GET_MONTH(str, i) (str[i]=='J' && str[i+1]=='a' && str[i+2]=='n' ? 1 : \
str[i]=='F' && str[i+1]=='e' && str[i+2]=='b' ? 2 : \
str[i]=='M' && str[i+1]=='a' && str[i+2]=='r' ? 3 : \
str[i]=='A' && str[i+1]=='p' && str[i+2]=='r' ? 4 : \
str[i]=='M' && str[i+1]=='a' && str[i+2]=='y' ? 5 : \
str[i]=='J' && str[i+1]=='u' && str[i+2]=='n' ? 6 : \
str[i]=='J' && str[i+1]=='u' && str[i+2]=='l' ? 7 : \
str[i]=='A' && str[i+1]=='u' && str[i+2]=='g' ? 8 : \
str[i]=='S' && str[i+1]=='e' && str[i+2]=='p' ? 9 : \
str[i]=='O' && str[i+1]=='c' && str[i+2]=='t' ? 10 : \
str[i]=='N' && str[i+1]=='o' && str[i+2]=='v' ? 11 : \
str[i]=='D' && str[i+1]=='e' && str[i+2]=='c' ? 12 : 0)
// extract the information from the time string given by __TIME__ and __DATE__
#define __TIME_SECONDS__ CONV_STR2DEC_2(__TIME__, 6)
#define __TIME_MINUTES__ CONV_STR2DEC_2(__TIME__, 3)
#define __TIME_HOURS__ CONV_STR2DEC_2(__TIME__, 0)
#define __TIME_DAYS__ CONV_STR2DEC_2(__DATE__, 4)
#define __TIME_MONTH__ GET_MONTH(__DATE__, 0)
#define __TIME_YEARS__ CONV_STR2DEC_4(__DATE__, 7)
// Days in February
#define _UNIX_TIMESTAMP_FDAY(year) \
(((year) % 400) == 0UL ? 29UL : \
(((year) % 100) == 0UL ? 28UL : \
(((year) % 4) == 0UL ? 29UL : \
28UL)))
// Days in the year
#define _UNIX_TIMESTAMP_YDAY(year, month, day) \
( \
/* January */ day \
/* February */ + (month >= 2 ? 31UL : 0UL) \
/* March */ + (month >= 3 ? _UNIX_TIMESTAMP_FDAY(year) : 0UL) \
/* April */ + (month >= 4 ? 31UL : 0UL) \
/* May */ + (month >= 5 ? 30UL : 0UL) \
/* June */ + (month >= 6 ? 31UL : 0UL) \
/* July */ + (month >= 7 ? 30UL : 0UL) \
/* August */ + (month >= 8 ? 31UL : 0UL) \
/* September */+ (month >= 9 ? 31UL : 0UL) \
/* October */ + (month >= 10 ? 30UL : 0UL) \
/* November */ + (month >= 11 ? 31UL : 0UL) \
/* December */ + (month >= 12 ? 30UL : 0UL) \
)
// get the UNIX timestamp from a digits representation
#define _UNIX_TIMESTAMP(year, month, day, hour, minute, second) \
( /* time */ second \
+ minute * SEC_PER_MIN \
+ hour * SEC_PER_HOUR \
+ /* year day (month + day) */ (_UNIX_TIMESTAMP_YDAY(year, month, day) - 1) * SEC_PER_DAY \
+ /* year */ (year - 1970UL) * SEC_PER_YEAR \
+ ((year - 1969UL) / 4UL) * SEC_PER_DAY \
- ((year - 1901UL) / 100UL) * SEC_PER_DAY \
+ ((year - 1601UL) / 400UL) * SEC_PER_DAY \
)
// the UNIX timestamp
#define BUILD_UNIX_TIMESTAMP (_UNIX_TIMESTAMP(__TIME_YEARS__, __TIME_MONTH__, __TIME_DAYS__, __TIME_HOURS__, __TIME_MINUTES__, __TIME_SECONDS__))
#endif

View File

@ -6,6 +6,7 @@
<ClInclude Include="assert.h" /> <ClInclude Include="assert.h" />
<ClInclude Include="bitfield.h" /> <ClInclude Include="bitfield.h" />
<ClInclude Include="bitutils.h" /> <ClInclude Include="bitutils.h" />
<ClInclude Include="build_timestamp.h" />
<ClInclude Include="byte_stream.h" /> <ClInclude Include="byte_stream.h" />
<ClInclude Include="crash_handler.h" /> <ClInclude Include="crash_handler.h" />
<ClInclude Include="d3d11\shader_cache.h" /> <ClInclude Include="d3d11\shader_cache.h" />

View File

@ -137,6 +137,7 @@
<ClInclude Include="memory_settings_interface.h" /> <ClInclude Include="memory_settings_interface.h" />
<ClInclude Include="threading.h" /> <ClInclude Include="threading.h" />
<ClInclude Include="scoped_guard.h" /> <ClInclude Include="scoped_guard.h" />
<ClInclude Include="build_timestamp.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="gl\program.cpp"> <ClCompile Include="gl\program.cpp">

View File

@ -186,8 +186,21 @@ void Timer::SleepUntil(Value value, bool exact)
{ {
if (exact) if (exact)
{ {
while (GetCurrentValue() < value) for (;;)
SleepUntil(value, false); {
Value current = GetCurrentValue();
if (current >= value)
break;
static constexpr Value min_sleep_time = 1 * 1000000;
// spin for the last 1ms
if ((value - current) > min_sleep_time)
{
SleepUntil(value, false);
continue;
}
}
} }
else else
{ {

View File

@ -1,4 +1,5 @@
#include "analog_controller.h" #include "analog_controller.h"
#include "IconsFontAwesome5.h"
#include "common/log.h" #include "common/log.h"
#include "common/string_util.h" #include "common/string_util.h"
#include "host.h" #include "host.h"
@ -10,8 +11,9 @@ Log_SetChannel(AnalogController);
AnalogController::AnalogController(u32 index) : Controller(index) AnalogController::AnalogController(u32 index) : Controller(index)
{ {
m_status_byte = 0x5A;
m_axis_state.fill(0x80); m_axis_state.fill(0x80);
Reset(); m_rumble_config.fill(0xFF);
} }
AnalogController::~AnalogController() = default; AnalogController::~AnalogController() = default;
@ -45,14 +47,16 @@ void AnalogController::Reset()
{ {
if (g_settings.controller_disable_analog_mode_forcing) if (g_settings.controller_disable_analog_mode_forcing)
{ {
Host::AddKeyedOSDMessage( Host::AddIconOSDMessage(
"analog_controller_mode_ignored", fmt::format("Controller{}AnalogMode", m_index), ICON_FA_GAMEPAD,
Host::TranslateStdString( Host::TranslateStdString(
"OSDMessage", "Analog mode forcing is disabled by game settings. Controller will start in digital mode."), "OSDMessage", "Analog mode forcing is disabled by game settings. Controller will start in digital mode."),
10.0f); 10.0f);
} }
else else
SetAnalogMode(true); {
SetAnalogMode(true, false);
}
} }
} }
@ -94,11 +98,14 @@ bool AnalogController::DoState(StateWrapper& sw, bool apply_input_state)
if (old_analog_mode != m_analog_mode) if (old_analog_mode != m_analog_mode)
{ {
Host::AddFormattedOSDMessage( Host::AddIconOSDMessage(
5.0f, fmt::format("Controller{}AnalogMode", m_index), ICON_FA_GAMEPAD,
m_analog_mode ? Host::TranslateString("AnalogController", "Controller %u switched to analog mode.") : fmt::format((m_analog_mode ?
Host::TranslateString("AnalogController", "Controller %u switched to digital mode."), Host::TranslateString("AnalogController", "Controller {} switched to analog mode.") :
m_index + 1u); Host::TranslateString("AnalogController", "Controller {} switched to digital mode."))
.GetCharArray(),
m_index + 1u),
5.0f);
} }
} }
return true; return true;
@ -230,17 +237,22 @@ void AnalogController::ResetTransferState()
m_command_step = 0; m_command_step = 0;
} }
void AnalogController::SetAnalogMode(bool enabled) void AnalogController::SetAnalogMode(bool enabled, bool show_message)
{ {
if (m_analog_mode == enabled) if (m_analog_mode == enabled)
return; return;
Log_InfoPrintf("Controller %u switched to %s mode.", m_index + 1u, enabled ? "analog" : "digital"); Log_InfoPrintf("Controller %u switched to %s mode.", m_index + 1u, enabled ? "analog" : "digital");
Host::AddFormattedOSDMessage(5.0f, if (show_message)
enabled ? {
Host::TranslateString("AnalogController", "Controller %u switched to analog mode.") : Host::AddIconOSDMessage(
Host::TranslateString("AnalogController", "Controller %u switched to digital mode."), fmt::format("Controller{}AnalogMode", m_index), ICON_FA_GAMEPAD,
m_index + 1u); fmt::format((enabled ? Host::TranslateString("AnalogController", "Controller {} switched to analog mode.") :
Host::TranslateString("AnalogController", "Controller {} switched to digital mode."))
.GetCharArray(),
m_index + 1u),
5.0f);
}
m_analog_mode = enabled; m_analog_mode = enabled;
} }
@ -248,15 +260,18 @@ void AnalogController::ProcessAnalogModeToggle()
{ {
if (m_analog_locked) if (m_analog_locked)
{ {
Host::AddFormattedOSDMessage( Host::AddIconOSDMessage(
5.0f, fmt::format("Controller{}AnalogMode", m_index), ICON_FA_GAMEPAD,
m_analog_mode ? Host::TranslateString("AnalogController", "Controller %u is locked to analog mode by the game.") : fmt::format((m_analog_mode ?
Host::TranslateString("AnalogController", "Controller %u is locked to digital mode by the game."), Host::TranslateString("AnalogController", "Controller %u is locked to analog mode by the game.") :
m_index + 1u); Host::TranslateString("AnalogController", "Controller %u is locked to digital mode by the game."))
.GetCharArray(),
m_index + 1u),
5.0f);
} }
else else
{ {
SetAnalogMode(!m_analog_mode); SetAnalogMode(!m_analog_mode, true);
ResetRumbleConfig(); ResetRumbleConfig();
if (m_dualshock_enabled) if (m_dualshock_enabled)
@ -604,7 +619,7 @@ bool AnalogController::Transfer(const u8 data_in, u8* data_out)
Log_DevPrintf("analog mode val 0x%02x", data_in); Log_DevPrintf("analog mode val 0x%02x", data_in);
if (data_in == 0x00 || data_in == 0x01) if (data_in == 0x00 || data_in == 0x01)
SetAnalogMode((data_in == 0x01)); SetAnalogMode((data_in == 0x01), true);
} }
else if (m_command_step == 3) else if (m_command_step == 3)
{ {
@ -777,17 +792,17 @@ static const SettingInfo s_settings[] = {
{SettingInfo::Type::Float, "AnalogDeadzone", TRANSLATABLE("AnalogController", "Analog Deadzone"), {SettingInfo::Type::Float, "AnalogDeadzone", TRANSLATABLE("AnalogController", "Analog Deadzone"),
TRANSLATABLE("AnalogController", TRANSLATABLE("AnalogController",
"Sets the analog stick deadzone, i.e. the fraction of the stick movement which will be ignored."), "Sets the analog stick deadzone, i.e. the fraction of the stick movement which will be ignored."),
"0.00f", "0.00f", "1.00f", "0.01f"}, "0.00f", "0.00f", "1.00f", "0.01f", "%.0f%%", 100.0f},
{SettingInfo::Type::Float, "AnalogSensitivity", TRANSLATABLE("AnalogController", "Analog Sensitivity"), {SettingInfo::Type::Float, "AnalogSensitivity", TRANSLATABLE("AnalogController", "Analog Sensitivity"),
TRANSLATABLE( TRANSLATABLE(
"AnalogController", "AnalogController",
"Sets the analog stick axis scaling factor. A value between 1.30 and 1.40 is recommended when using recent " "Sets the analog stick axis scaling factor. A value between 130% and 140% is recommended when using recent "
"controllers, e.g. DualShock 4, Xbox One Controller."), "controllers, e.g. DualShock 4, Xbox One Controller."),
"1.33f", "0.01f", "2.00f", "0.01f"}, "1.33f", "0.01f", "2.00f", "0.01f", "%.0f%%", 100.0f},
{SettingInfo::Type::Integer, "VibrationBias", TRANSLATABLE("AnalogController", "Vibration Bias"), {SettingInfo::Type::Integer, "VibrationBias", TRANSLATABLE("AnalogController", "Vibration Bias"),
TRANSLATABLE("AnalogController", "Sets the rumble bias value. If rumble in some games is too weak or not " TRANSLATABLE("AnalogController", "Sets the rumble bias value. If rumble in some games is too weak or not "
"functioning, try increasing this value."), "functioning, try increasing this value."),
"8", "0", "255", "1"}}; "8", "0", "255", "1", "%d", 1.0f}};
const Controller::ControllerInfo AnalogController::INFO = {ControllerType::AnalogController, const Controller::ControllerInfo AnalogController::INFO = {ControllerType::AnalogController,
"AnalogController", "AnalogController",

View File

@ -108,7 +108,7 @@ private:
u8 GetModeID() const; u8 GetModeID() const;
u8 GetIDByte() const; u8 GetIDByte() const;
void SetAnalogMode(bool enabled); void SetAnalogMode(bool enabled, bool show_message);
void ProcessAnalogModeToggle(); void ProcessAnalogModeToggle();
void SetMotorState(u32 motor, u8 value); void SetMotorState(u32 motor, u8 value);
void UpdateHostVibration(); void UpdateHostVibration();
@ -140,7 +140,7 @@ private:
int m_rumble_config_small_motor_index = -1; int m_rumble_config_small_motor_index = -1;
bool m_analog_toggle_queued = false; bool m_analog_toggle_queued = false;
u8 m_status_byte = 0x5A; u8 m_status_byte = 0;
// TODO: Set this with command 0x4D and increase response length in digital mode accordingly // TODO: Set this with command 0x4D and increase response length in digital mode accordingly
u8 m_digital_mode_extra_halfwords = 0; u8 m_digital_mode_extra_halfwords = 0;

View File

@ -328,13 +328,13 @@ static const SettingInfo s_settings[] = {
{SettingInfo::Type::Float, "AnalogDeadzone", TRANSLATABLE("AnalogJoystick", "Analog Deadzone"), {SettingInfo::Type::Float, "AnalogDeadzone", TRANSLATABLE("AnalogJoystick", "Analog Deadzone"),
TRANSLATABLE("AnalogJoystick", TRANSLATABLE("AnalogJoystick",
"Sets the analog stick deadzone, i.e. the fraction of the stick movement which will be ignored."), "Sets the analog stick deadzone, i.e. the fraction of the stick movement which will be ignored."),
"1.00f", "0.00f", "1.00f", "0.01f"}, "1.00f", "0.00f", "1.00f", "0.01f", "%.0f%%", 100.0f},
{SettingInfo::Type::Float, "AnalogSensitivity", TRANSLATABLE("AnalogJoystick", "Analog Sensitivity"), {SettingInfo::Type::Float, "AnalogSensitivity", TRANSLATABLE("AnalogJoystick", "Analog Sensitivity"),
TRANSLATABLE( TRANSLATABLE(
"AnalogJoystick", "AnalogJoystick",
"Sets the analog stick axis scaling factor. A value between 1.30 and 1.40 is recommended when using recent " "Sets the analog stick axis scaling factor. A value between 130% and 140% is recommended when using recent "
"controllers, e.g. DualShock 4, Xbox One Controller."), "controllers, e.g. DualShock 4, Xbox One Controller."),
"1.33f", "0.01f", "2.00f", "0.01f"}}; "1.33f", "0.01f", "2.00f", "0.01f", "%.0f%%", 100.0f}};
const Controller::ControllerInfo AnalogJoystick::INFO = {ControllerType::AnalogJoystick, const Controller::ControllerInfo AnalogJoystick::INFO = {ControllerType::AnalogJoystick,
"AnalogJoystick", "AnalogJoystick",

View File

@ -542,6 +542,12 @@ bool GameDatabase::LoadFromCache()
return false; return false;
} }
if (gamedb_ts != file_gamedb_ts || gamesettings_ts != file_gamesettings_ts || compat_ts != file_compat_ts)
{
Log_DevPrintf("Cache is out of date, recreating.");
return false;
}
s_entries.reserve(num_entries); s_entries.reserve(num_entries);
for (u32 i = 0; i < num_entries; i++) for (u32 i = 0; i < num_entries; i++)

View File

@ -220,10 +220,10 @@ static const SettingInfo s_settings[] = {
{SettingInfo::Type::Path, "CrosshairImagePath", TRANSLATABLE("GunCon", "Crosshair Image Path"), {SettingInfo::Type::Path, "CrosshairImagePath", TRANSLATABLE("GunCon", "Crosshair Image Path"),
TRANSLATABLE("GunCon", "Path to an image to use as a crosshair/cursor.")}, TRANSLATABLE("GunCon", "Path to an image to use as a crosshair/cursor.")},
{SettingInfo::Type::Float, "CrosshairScale", TRANSLATABLE("GunCon", "Crosshair Image Scale"), {SettingInfo::Type::Float, "CrosshairScale", TRANSLATABLE("GunCon", "Crosshair Image Scale"),
TRANSLATABLE("GunCon", "Scale of crosshair image on screen."), "1.0", "0.0001", "100.0", "0.10"}, TRANSLATABLE("GunCon", "Scale of crosshair image on screen."), "1.0", "0.0001", "100.0", "0.10", "%.0f%%", 100.0f},
{SettingInfo::Type::Float, "XScale", TRANSLATABLE("GunCon", "X Scale"), {SettingInfo::Type::Float, "XScale", TRANSLATABLE("GunCon", "X Scale"),
TRANSLATABLE("GunCon", "Scales X coordinates relative to the center of the screen."), "1.0", "0.01", "2.0", TRANSLATABLE("GunCon", "Scales X coordinates relative to the center of the screen."), "1.0", "0.01", "2.0",
"0.01"}}; "0.01", "%.0f%%", 100.0f}};
const Controller::ControllerInfo GunCon::INFO = {ControllerType::GunCon, const Controller::ControllerInfo GunCon::INFO = {ControllerType::GunCon,
"GunCon", "GunCon",

View File

@ -251,7 +251,7 @@ static const Controller::ControllerBindingInfo s_binding_info[] = {
static const SettingInfo s_settings[] = { static const SettingInfo s_settings[] = {
{SettingInfo::Type::Float, "SteeringDeadzone", TRANSLATABLE("NeGcon", "Steering Axis Deadzone"), {SettingInfo::Type::Float, "SteeringDeadzone", TRANSLATABLE("NeGcon", "Steering Axis Deadzone"),
TRANSLATABLE("NeGcon", "Sets deadzone size for steering axis."), "0.00f", "0.00f", "0.99f", "0.01f"}}; TRANSLATABLE("NeGcon", "Sets deadzone size for steering axis."), "0.00f", "0.00f", "0.99f", "0.01f", "%.0f%%", 100.0f}};
const Controller::ControllerInfo NeGcon::INFO = {ControllerType::NeGcon, const Controller::ControllerInfo NeGcon::INFO = {ControllerType::NeGcon,
"NeGcon", "NeGcon",

View File

@ -21,13 +21,15 @@ struct SettingInfo
}; };
Type type; Type type;
const char* key; const char* name;
const char* visible_name; const char* display_name;
const char* description; const char* description;
const char* default_value; const char* default_value;
const char* min_value; const char* min_value;
const char* max_value; const char* max_value;
const char* step_value; const char* step_value;
const char* format;
float multiplier;
const char* StringDefaultValue() const; const char* StringDefaultValue() const;
bool BooleanDefaultValue() const; bool BooleanDefaultValue() const;

View File

@ -862,8 +862,13 @@ bool System::UpdateGameSettingsLayer()
} }
std::string input_profile_name; std::string input_profile_name;
bool use_game_settings_for_controller = false;
if (new_interface) if (new_interface)
new_interface->GetStringValue("ControllerPorts", "InputProfileName", &input_profile_name); {
new_interface->GetBoolValue("ControllerPorts", "UseGameSettingsForController", &use_game_settings_for_controller);
if (!use_game_settings_for_controller)
new_interface->GetStringValue("ControllerPorts", "InputProfileName", &input_profile_name);
}
if (!s_game_settings_interface && !new_interface && s_input_profile_name == input_profile_name) if (!s_game_settings_interface && !new_interface && s_input_profile_name == input_profile_name)
return false; return false;
@ -872,31 +877,39 @@ bool System::UpdateGameSettingsLayer()
s_game_settings_interface = std::move(new_interface); s_game_settings_interface = std::move(new_interface);
std::unique_ptr<INISettingsInterface> input_interface; std::unique_ptr<INISettingsInterface> input_interface;
if (!input_profile_name.empty()) if (!use_game_settings_for_controller)
{ {
const std::string filename(GetInputProfilePath(input_profile_name)); if (!input_profile_name.empty())
if (FileSystem::FileExists(filename.c_str()))
{ {
Log_InfoPrintf("Loading input profile from '%s'...", filename.c_str()); const std::string filename(GetInputProfilePath(input_profile_name));
input_interface = std::make_unique<INISettingsInterface>(std::move(filename)); if (FileSystem::FileExists(filename.c_str()))
if (!input_interface->Load())
{ {
Log_ErrorPrintf("Failed to parse input profile ini '%s'", input_interface->GetFileName().c_str()); Log_InfoPrintf("Loading input profile from '%s'...", filename.c_str());
input_interface.reset(); input_interface = std::make_unique<INISettingsInterface>(std::move(filename));
if (!input_interface->Load())
{
Log_ErrorPrintf("Failed to parse input profile ini '%s'", input_interface->GetFileName().c_str());
input_interface.reset();
input_profile_name = {};
}
}
else
{
Log_InfoPrintf("No input profile found (tried '%s')", filename.c_str());
input_profile_name = {}; input_profile_name = {};
} }
} }
else
{ Host::Internal::SetInputSettingsLayer(input_interface.get());
Log_InfoPrintf("No input profile found (tried '%s')", filename.c_str()); }
input_profile_name = {}; else
} {
// using game settings for bindings too
Host::Internal::SetInputSettingsLayer(s_game_settings_interface.get());
} }
Host::Internal::SetInputSettingsLayer(input_interface.get());
s_input_settings_interface = std::move(input_interface); s_input_settings_interface = std::move(input_interface);
s_input_profile_name = std::move(input_profile_name); s_input_profile_name = std::move(input_profile_name);
return true; return true;
} }
@ -930,7 +943,7 @@ void System::PauseSystem(bool paused)
else else
{ {
Host::OnSystemResumed(); Host::OnSystemResumed();
g_host_display->SetVSync(ShouldUseVSync()); UpdateDisplaySync();
ResetPerformanceCounters(); ResetPerformanceCounters();
ResetThrottler(); ResetThrottler();
} }
@ -1249,6 +1262,7 @@ bool System::Initialize(bool force_software_renderer)
s_frame_number = 1; s_frame_number = 1;
s_internal_frame_number = 1; s_internal_frame_number = 1;
s_target_speed = g_settings.emulation_speed;
s_throttle_frequency = 60.0f; s_throttle_frequency = 60.0f;
s_frame_period = 0; s_frame_period = 0;
s_next_frame_time = 0; s_next_frame_time = 0;
@ -2064,6 +2078,7 @@ void System::ResetThrottler()
void System::Throttle() void System::Throttle()
{ {
#if 0
// Allow variance of up to 40ms either way. // Allow variance of up to 40ms either way.
#ifndef __ANDROID__ #ifndef __ANDROID__
static constexpr double MAX_VARIANCE_TIME_NS = 40 * 1000000; static constexpr double MAX_VARIANCE_TIME_NS = 40 * 1000000;
@ -2085,6 +2100,7 @@ void System::Throttle()
ResetThrottler(); ResetThrottler();
} }
else else
#endif
{ {
Common::Timer::SleepUntil(s_next_frame_time, true); Common::Timer::SleepUntil(s_next_frame_time, true);
} }
@ -2210,12 +2226,7 @@ void System::UpdateSpeedLimiterState()
} }
} }
const bool video_sync_enabled = ShouldUseVSync(); Log_VerbosePrintf("Target speed: %f%%", s_target_speed * 100.0f);
const float max_display_fps = (!IsRunning() || m_throttler_enabled) ? 0.0f : g_settings.display_max_fps;
Log_InfoPrintf("Target speed: %f%%", target_speed * 100.0f);
Log_InfoPrintf("Using vsync: %s", video_sync_enabled ? "YES" : "NO");
Log_InfoPrintf("Max display fps: %f (%s)", max_display_fps,
m_display_all_frames ? "displaying all frames" : "skipping displaying frames when needed");
if (IsValid()) if (IsValid())
{ {
@ -2235,23 +2246,36 @@ void System::UpdateSpeedLimiterState()
ResetThrottler(); ResetThrottler();
} }
g_host_display->SetDisplayMaxFPS(max_display_fps); // Defer vsync update until we unpause, in case of fullscreen UI.
g_host_display->SetVSync(video_sync_enabled); if (IsRunning())
UpdateDisplaySync();
if (g_settings.increase_timer_resolution) if (g_settings.increase_timer_resolution)
SetTimerResolutionIncreased(m_throttler_enabled); SetTimerResolutionIncreased(m_throttler_enabled);
// When syncing to host and using vsync, we don't need to sleep. // When syncing to host and using vsync, we don't need to sleep.
if (syncing_to_host && video_sync_enabled && m_display_all_frames) if (syncing_to_host && ShouldUseVSync() && m_display_all_frames)
{ {
Log_InfoPrintf("Using host vsync for throttling."); Log_InfoPrintf("Using host vsync for throttling.");
m_throttler_enabled = false; m_throttler_enabled = false;
} }
} }
void System::UpdateDisplaySync()
{
const bool video_sync_enabled = ShouldUseVSync();
const float max_display_fps = m_throttler_enabled ? 0.0f : g_settings.display_max_fps;
Log_VerbosePrintf("Using vsync: %s", video_sync_enabled ? "YES" : "NO");
Log_VerbosePrintf("Max display fps: %f (%s)", max_display_fps,
m_display_all_frames ? "displaying all frames" : "skipping displaying frames when needed");
g_host_display->SetDisplayMaxFPS(max_display_fps);
g_host_display->SetVSync(video_sync_enabled);
}
bool System::ShouldUseVSync() bool System::ShouldUseVSync()
{ {
return (!IsRunning() || (m_throttler_enabled && g_settings.video_sync_enabled && !IsRunningAtNonStandardSpeed())); return g_settings.video_sync_enabled && !IsRunningAtNonStandardSpeed();
} }
bool System::IsFastForwardEnabled() bool System::IsFastForwardEnabled()

View File

@ -230,6 +230,9 @@ void Throttle();
void UpdatePerformanceCounters(); void UpdatePerformanceCounters();
void ResetPerformanceCounters(); void ResetPerformanceCounters();
/// Resets vsync/max present fps state.
void UpdateDisplaySync();
// Access controllers for simulating input. // Access controllers for simulating input.
Controller* GetController(u32 slot); Controller* GetController(u32 slot);
void UpdateControllers(); void UpdateControllers();

View File

@ -521,14 +521,14 @@ void ControllerCustomSettingsWidget::createSettingWidgets(ControllerBindingWidge
for (u32 i = 0; i < cinfo->num_settings; i++) for (u32 i = 0; i < cinfo->num_settings; i++)
{ {
const SettingInfo& si = cinfo->settings[i]; const SettingInfo& si = cinfo->settings[i];
std::string key_name = si.key; std::string key_name = si.name;
switch (si.type) switch (si.type)
{ {
case SettingInfo::Type::Boolean: case SettingInfo::Type::Boolean:
{ {
QCheckBox* cb = new QCheckBox(qApp->translate(cinfo->name, si.visible_name), this); QCheckBox* cb = new QCheckBox(qApp->translate(cinfo->name, si.display_name), this);
cb->setObjectName(QString::fromUtf8(si.key)); cb->setObjectName(QString::fromUtf8(si.name));
ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, cb, section, std::move(key_name), ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, cb, section, std::move(key_name),
si.BooleanDefaultValue()); si.BooleanDefaultValue());
layout->addWidget(cb, current_row, 0, 1, 4); layout->addWidget(cb, current_row, 0, 1, 4);
@ -539,12 +539,12 @@ void ControllerCustomSettingsWidget::createSettingWidgets(ControllerBindingWidge
case SettingInfo::Type::Integer: case SettingInfo::Type::Integer:
{ {
QSpinBox* sb = new QSpinBox(this); QSpinBox* sb = new QSpinBox(this);
sb->setObjectName(QString::fromUtf8(si.key)); sb->setObjectName(QString::fromUtf8(si.name));
sb->setMinimum(si.IntegerMinValue()); sb->setMinimum(si.IntegerMinValue());
sb->setMaximum(si.IntegerMaxValue()); sb->setMaximum(si.IntegerMaxValue());
sb->setSingleStep(si.IntegerStepValue()); sb->setSingleStep(si.IntegerStepValue());
SettingWidgetBinder::BindWidgetToIntSetting(sif, sb, section, std::move(key_name), si.IntegerDefaultValue()); SettingWidgetBinder::BindWidgetToIntSetting(sif, sb, section, std::move(key_name), si.IntegerDefaultValue());
layout->addWidget(new QLabel(qApp->translate(cinfo->name, si.visible_name), this), current_row, 0); layout->addWidget(new QLabel(qApp->translate(cinfo->name, si.display_name), this), current_row, 0);
layout->addWidget(sb, current_row, 1, 1, 3); layout->addWidget(sb, current_row, 1, 1, 3);
current_row++; current_row++;
} }
@ -553,12 +553,12 @@ void ControllerCustomSettingsWidget::createSettingWidgets(ControllerBindingWidge
case SettingInfo::Type::Float: case SettingInfo::Type::Float:
{ {
QDoubleSpinBox* sb = new QDoubleSpinBox(this); QDoubleSpinBox* sb = new QDoubleSpinBox(this);
sb->setObjectName(QString::fromUtf8(si.key)); sb->setObjectName(QString::fromUtf8(si.name));
sb->setMinimum(si.FloatMinValue()); sb->setMinimum(si.FloatMinValue());
sb->setMaximum(si.FloatMaxValue()); sb->setMaximum(si.FloatMaxValue());
sb->setSingleStep(si.FloatStepValue()); sb->setSingleStep(si.FloatStepValue());
SettingWidgetBinder::BindWidgetToFloatSetting(sif, sb, section, std::move(key_name), si.FloatDefaultValue()); SettingWidgetBinder::BindWidgetToFloatSetting(sif, sb, section, std::move(key_name), si.FloatDefaultValue());
layout->addWidget(new QLabel(qApp->translate(cinfo->name, si.visible_name), this), current_row, 0); layout->addWidget(new QLabel(qApp->translate(cinfo->name, si.display_name), this), current_row, 0);
layout->addWidget(sb, current_row, 1, 1, 3); layout->addWidget(sb, current_row, 1, 1, 3);
current_row++; current_row++;
} }
@ -567,9 +567,9 @@ void ControllerCustomSettingsWidget::createSettingWidgets(ControllerBindingWidge
case SettingInfo::Type::String: case SettingInfo::Type::String:
{ {
QLineEdit* le = new QLineEdit(this); QLineEdit* le = new QLineEdit(this);
le->setObjectName(QString::fromUtf8(si.key)); le->setObjectName(QString::fromUtf8(si.name));
SettingWidgetBinder::BindWidgetToStringSetting(sif, le, section, std::move(key_name), si.StringDefaultValue()); SettingWidgetBinder::BindWidgetToStringSetting(sif, le, section, std::move(key_name), si.StringDefaultValue());
layout->addWidget(new QLabel(qApp->translate(cinfo->name, si.visible_name), this), current_row, 0); layout->addWidget(new QLabel(qApp->translate(cinfo->name, si.display_name), this), current_row, 0);
layout->addWidget(le, current_row, 1, 1, 3); layout->addWidget(le, current_row, 1, 1, 3);
current_row++; current_row++;
} }
@ -578,7 +578,7 @@ void ControllerCustomSettingsWidget::createSettingWidgets(ControllerBindingWidge
case SettingInfo::Type::Path: case SettingInfo::Type::Path:
{ {
QLineEdit* le = new QLineEdit(this); QLineEdit* le = new QLineEdit(this);
le->setObjectName(QString::fromUtf8(si.key)); le->setObjectName(QString::fromUtf8(si.name));
QPushButton* browse_button = new QPushButton(tr("Browse..."), this); QPushButton* browse_button = new QPushButton(tr("Browse..."), this);
SettingWidgetBinder::BindWidgetToStringSetting(sif, le, section, std::move(key_name), si.StringDefaultValue()); SettingWidgetBinder::BindWidgetToStringSetting(sif, le, section, std::move(key_name), si.StringDefaultValue());
connect(browse_button, &QPushButton::clicked, [this, le]() { connect(browse_button, &QPushButton::clicked, [this, le]() {
@ -591,7 +591,7 @@ void ControllerCustomSettingsWidget::createSettingWidgets(ControllerBindingWidge
hbox->addWidget(le, 1); hbox->addWidget(le, 1);
hbox->addWidget(browse_button); hbox->addWidget(browse_button);
layout->addWidget(new QLabel(qApp->translate(cinfo->name, si.visible_name), this), current_row, 0); layout->addWidget(new QLabel(qApp->translate(cinfo->name, si.display_name), this), current_row, 0);
layout->addLayout(hbox, current_row, 1, 1, 3); layout->addLayout(hbox, current_row, 1, 1, 3);
current_row++; current_row++;
} }
@ -615,13 +615,13 @@ void ControllerCustomSettingsWidget::restoreDefaults()
for (u32 i = 0; i < cinfo->num_settings; i++) for (u32 i = 0; i < cinfo->num_settings; i++)
{ {
const SettingInfo& si = cinfo->settings[i]; const SettingInfo& si = cinfo->settings[i];
const QString key(QString::fromStdString(si.key)); const QString key(QString::fromStdString(si.name));
switch (si.type) switch (si.type)
{ {
case SettingInfo::Type::Boolean: case SettingInfo::Type::Boolean:
{ {
QCheckBox* widget = findChild<QCheckBox*>(QString::fromStdString(si.key)); QCheckBox* widget = findChild<QCheckBox*>(QString::fromStdString(si.name));
if (widget) if (widget)
widget->setChecked(si.BooleanDefaultValue()); widget->setChecked(si.BooleanDefaultValue());
} }
@ -629,7 +629,7 @@ void ControllerCustomSettingsWidget::restoreDefaults()
case SettingInfo::Type::Integer: case SettingInfo::Type::Integer:
{ {
QSpinBox* widget = findChild<QSpinBox*>(QString::fromStdString(si.key)); QSpinBox* widget = findChild<QSpinBox*>(QString::fromStdString(si.name));
if (widget) if (widget)
widget->setValue(si.IntegerDefaultValue()); widget->setValue(si.IntegerDefaultValue());
} }
@ -637,7 +637,7 @@ void ControllerCustomSettingsWidget::restoreDefaults()
case SettingInfo::Type::Float: case SettingInfo::Type::Float:
{ {
QDoubleSpinBox* widget = findChild<QDoubleSpinBox*>(QString::fromStdString(si.key)); QDoubleSpinBox* widget = findChild<QDoubleSpinBox*>(QString::fromStdString(si.name));
if (widget) if (widget)
widget->setValue(si.FloatDefaultValue()); widget->setValue(si.FloatDefaultValue());
} }
@ -645,7 +645,7 @@ void ControllerCustomSettingsWidget::restoreDefaults()
case SettingInfo::Type::String: case SettingInfo::Type::String:
{ {
QLineEdit* widget = findChild<QLineEdit*>(QString::fromStdString(si.key)); QLineEdit* widget = findChild<QLineEdit*>(QString::fromStdString(si.name));
if (widget) if (widget)
widget->setText(QString::fromUtf8(si.StringDefaultValue())); widget->setText(QString::fromUtf8(si.StringDefaultValue()));
} }
@ -653,7 +653,7 @@ void ControllerCustomSettingsWidget::restoreDefaults()
case SettingInfo::Type::Path: case SettingInfo::Type::Path:
{ {
QLineEdit* widget = findChild<QLineEdit*>(QString::fromStdString(si.key)); QLineEdit* widget = findChild<QLineEdit*>(QString::fromStdString(si.name));
if (widget) if (widget)
widget->setText(QString::fromUtf8(si.StringDefaultValue())); widget->setText(QString::fromUtf8(si.StringDefaultValue()));
} }

View File

@ -63,7 +63,7 @@ static void DeactivateAchievement(Achievement* achievement);
static void SendPing(); static void SendPing();
static void SendPlaying(); static void SendPlaying();
static void UpdateRichPresence(); static void UpdateRichPresence();
static Achievement* GetAchievementByID(u32 id); static Achievement* GetMutableAchievementByID(u32 id);
static void ClearGameInfo(bool clear_achievements = true, bool clear_leaderboards = true); static void ClearGameInfo(bool clear_achievements = true, bool clear_leaderboards = true);
static void ClearGameHash(); static void ClearGameHash();
static std::string GetUserAgent(); static std::string GetUserAgent();
@ -80,6 +80,7 @@ static void GetLbInfoCallback(s32 status_code, Common::HTTPDownloader::Request::
static void GetPatches(u32 game_id); static void GetPatches(u32 game_id);
static std::string GetGameHash(CDImage* image); static std::string GetGameHash(CDImage* image);
static void SetChallengeMode(bool enabled); static void SetChallengeMode(bool enabled);
static void SendGetGameId();
static void GetGameIdCallback(s32 status_code, Common::HTTPDownloader::Request::Data data); static void GetGameIdCallback(s32 status_code, Common::HTTPDownloader::Request::Data data);
static void SendPlayingCallback(s32 status_code, Common::HTTPDownloader::Request::Data data); static void SendPlayingCallback(s32 status_code, Common::HTTPDownloader::Request::Data data);
static void UpdateRichPresence(); static void UpdateRichPresence();
@ -205,6 +206,18 @@ public:
Achievements::DownloadImage(api_request.url, std::move(cache_filename)); Achievements::DownloadImage(api_request.url, std::move(cache_filename));
return true; return true;
} }
std::string GetURL()
{
const int error = InitFunc(&api_request, this);
if (error != RC_OK)
{
FormattedError("%s failed: error %d (%s)", RAPIStructName<T>(), error, rc_error_str(error));
return std::string();
}
return api_request.url;
}
}; };
template<typename T, int (*ParseFunc)(T*, const char*), void (*DestroyFunc)(T*)> template<typename T, int (*ParseFunc)(T*, const char*), void (*DestroyFunc)(T*)>
@ -278,7 +291,18 @@ void Achievements::LogFailedResponseJSON(const Common::HTTPDownloader::Request::
Log_ErrorPrintf("API call failed. Response JSON was:\n%s", str_data.c_str()); Log_ErrorPrintf("API call failed. Response JSON was:\n%s", str_data.c_str());
} }
static Achievements::Achievement* Achievements::GetAchievementByID(u32 id) const Achievements::Achievement* Achievements::GetAchievementByID(u32 id)
{
for (const Achievement& ach : s_achievements)
{
if (ach.id == id)
return &ach;
}
return nullptr;
}
Achievements::Achievement* Achievements::GetMutableAchievementByID(u32 id)
{ {
for (Achievement& ach : s_achievements) for (Achievement& ach : s_achievements)
{ {
@ -420,7 +444,7 @@ void Achievements::Initialize()
s_api_token = Host::GetBaseStringSettingValue("Cheevos", "Token"); s_api_token = Host::GetBaseStringSettingValue("Cheevos", "Token");
s_logged_in = (!s_username.empty() && !s_api_token.empty()); s_logged_in = (!s_username.empty() && !s_api_token.empty());
if (IsLoggedIn() && System::IsValid()) if (System::IsValid())
GameChanged(System::GetRunningPath(), nullptr); GameChanged(System::GetRunningPath(), nullptr);
} }
@ -742,6 +766,7 @@ bool Achievements::DoState(StateWrapper& sw)
} }
} }
else else
#endif
{ {
// internally this happens twice.. not great. // internally this happens twice.. not great.
const int size = rc_runtime_progress_size(&s_rcheevos_runtime, nullptr); const int size = rc_runtime_progress_size(&s_rcheevos_runtime, nullptr);
@ -757,7 +782,6 @@ bool Achievements::DoState(StateWrapper& sw)
data_size = 0; data_size = 0;
} }
} }
#endif
sw.Do(&data_size); sw.Do(&data_size);
if (data_size > 0) if (data_size > 0)
@ -829,8 +853,8 @@ void Achievements::LoginCallback(s32 status_code, Common::HTTPDownloader::Reques
s_logged_in = true; s_logged_in = true;
// If we have a game running, set it up. // If we have a game running, set it up.
if (System::IsValid()) if (!s_game_hash.empty())
GameChanged(System::GetRunningPath(), nullptr); SendGetGameId();
} }
} }
@ -968,7 +992,10 @@ void Achievements::DisplayAchievementSummary()
} }
} }
ImGuiFullscreen::AddNotification(10.0f, std::move(title), std::move(summary), s_game_icon); Host::RunOnCPUThread([title = std::move(title), summary = std::move(summary), icon = s_game_icon]() {
if (FullscreenUI::IsInitialized())
ImGuiFullscreen::AddNotification(10.0f, std::move(title), std::move(summary), std::move(icon));
});
} }
void Achievements::GetUserUnlocksCallback(s32 status_code, Common::HTTPDownloader::Request::Data data) void Achievements::GetUserUnlocksCallback(s32 status_code, Common::HTTPDownloader::Request::Data data)
@ -990,7 +1017,7 @@ void Achievements::GetUserUnlocksCallback(s32 status_code, Common::HTTPDownloade
// flag achievements as unlocked // flag achievements as unlocked
for (u32 i = 0; i < response.num_achievement_ids; i++) for (u32 i = 0; i < response.num_achievement_ids; i++)
{ {
Achievement* cheevo = GetAchievementByID(response.achievement_ids[i]); Achievement* cheevo = GetMutableAchievementByID(response.achievement_ids[i]);
if (!cheevo) if (!cheevo)
{ {
Log_ErrorPrintf("Server returned unknown achievement %u", response.achievement_ids[i]); Log_ErrorPrintf("Server returned unknown achievement %u", response.achievement_ids[i]);
@ -1075,7 +1102,7 @@ void Achievements::GetPatchesCallback(s32 status_code, Common::HTTPDownloader::R
continue; continue;
} }
if (GetAchievementByID(defn.id)) if (GetMutableAchievementByID(defn.id))
{ {
Log_ErrorPrintf("Achievement %u already exists", defn.id); Log_ErrorPrintf("Achievement %u already exists", defn.id);
continue; continue;
@ -1351,6 +1378,12 @@ void Achievements::GameChanged(const std::string& path, CDImage* image)
return; return;
} }
if (IsLoggedIn())
SendGetGameId();
}
void Achievements::SendGetGameId()
{
RAPIRequest<rc_api_resolve_hash_request_t, rc_api_init_resolve_hash_request> request; RAPIRequest<rc_api_resolve_hash_request_t, rc_api_init_resolve_hash_request> request;
request.username = s_username.c_str(); request.username = s_username.c_str();
request.api_token = s_api_token.c_str(); request.api_token = s_api_token.c_str();
@ -1643,7 +1676,7 @@ void Achievements::SubmitLeaderboardCallback(s32 status_code, Common::HTTPDownlo
void Achievements::UnlockAchievement(u32 achievement_id, bool add_notification /* = true*/) void Achievements::UnlockAchievement(u32 achievement_id, bool add_notification /* = true*/)
{ {
Achievement* achievement = GetAchievementByID(achievement_id); Achievement* achievement = GetMutableAchievementByID(achievement_id);
if (!achievement) if (!achievement)
{ {
Log_ErrorPrintf("Attempting to unlock unknown achievement %u", achievement_id); Log_ErrorPrintf("Attempting to unlock unknown achievement %u", achievement_id);
@ -1740,7 +1773,7 @@ std::string Achievements::GetAchievementProgressText(const Achievement& achievem
return buf; return buf;
} }
const std::string& Achievements::GetAchievementBadgePath(const Achievement& achievement) const std::string& Achievements::GetAchievementBadgePath(const Achievement& achievement, bool download_if_missing)
{ {
std::string& badge_path = achievement.locked ? achievement.locked_badge_path : achievement.unlocked_badge_path; std::string& badge_path = achievement.locked ? achievement.locked_badge_path : achievement.unlocked_badge_path;
if (!badge_path.empty() || achievement.badge_name.empty()) if (!badge_path.empty() || achievement.badge_name.empty())
@ -1754,11 +1787,23 @@ const std::string& Achievements::GetAchievementBadgePath(const Achievement& achi
return badge_path; return badge_path;
// need to download it // need to download it
if (download_if_missing)
{
RAPIRequest<rc_api_fetch_image_request_t, rc_api_init_fetch_image_request> request;
request.image_name = achievement.badge_name.c_str();
request.image_type = achievement.locked ? RC_IMAGE_TYPE_ACHIEVEMENT_LOCKED : RC_IMAGE_TYPE_ACHIEVEMENT;
request.DownloadImage(badge_path);
}
return badge_path;
}
std::string Achievements::GetAchievementBadgeURL(const Achievement& achievement)
{
RAPIRequest<rc_api_fetch_image_request_t, rc_api_init_fetch_image_request> request; RAPIRequest<rc_api_fetch_image_request_t, rc_api_init_fetch_image_request> request;
request.image_name = achievement.badge_name.c_str(); request.image_name = achievement.badge_name.c_str();
request.image_type = achievement.locked ? RC_IMAGE_TYPE_ACHIEVEMENT_LOCKED : RC_IMAGE_TYPE_ACHIEVEMENT; request.image_type = achievement.locked ? RC_IMAGE_TYPE_ACHIEVEMENT_LOCKED : RC_IMAGE_TYPE_ACHIEVEMENT;
request.DownloadImage(badge_path); return request.GetURL();
return badge_path;
} }
void Achievements::CheevosEventHandler(const rc_runtime_event_t* runtime_event) void Achievements::CheevosEventHandler(const rc_runtime_event_t* runtime_event)

View File

@ -143,9 +143,11 @@ const Leaderboard* GetLeaderboardByID(u32 id);
u32 GetLeaderboardCount(); u32 GetLeaderboardCount();
bool IsLeaderboardTimeType(const Leaderboard& leaderboard); bool IsLeaderboardTimeType(const Leaderboard& leaderboard);
const Achievement* GetAchievementByID(u32 id);
std::pair<u32, u32> GetAchievementProgress(const Achievement& achievement); std::pair<u32, u32> GetAchievementProgress(const Achievement& achievement);
std::string GetAchievementProgressText(const Achievement& achievement); std::string GetAchievementProgressText(const Achievement& achievement);
const std::string& GetAchievementBadgePath(const Achievement& achievement); const std::string& GetAchievementBadgePath(const Achievement& achievement, bool download_if_missing = true);
std::string GetAchievementBadgeURL(const Achievement& achievement);
void UnlockAchievement(u32 achievement_id, bool add_notification = true); void UnlockAchievement(u32 achievement_id, bool add_notification = true);
void SubmitLeaderboard(u32 leaderboard_id, int value); void SubmitLeaderboard(u32 leaderboard_id, int value);

View File

@ -358,7 +358,7 @@ void Host::DisplayLoadingScreen(const char* message, int progress_min /*= -1*/,
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing |
ImGuiWindowFlags_NoBackground)) ImGuiWindowFlags_NoBackground))
{ {
HostDisplayTexture* tex = ImGuiFullscreen::GetCachedTexture("fullscreenui/duck.png"); HostDisplayTexture* tex = ImGuiFullscreen::GetCachedTexture("images/duck.png");
if (tex) if (tex)
ImGui::Image(tex->GetHandle(), ImVec2(logo_width, logo_height)); ImGui::Image(tex->GetHandle(), ImVec2(logo_width, logo_height));
} }

View File

@ -116,6 +116,7 @@ using ImGuiFullscreen::ThreeWayToggleButton;
using ImGuiFullscreen::ToggleButton; using ImGuiFullscreen::ToggleButton;
using ImGuiFullscreen::WantsToCloseMenu; using ImGuiFullscreen::WantsToCloseMenu;
#ifndef __ANDROID__
namespace FullscreenUI { namespace FullscreenUI {
enum class MainWindowType enum class MainWindowType
{ {
@ -159,11 +160,6 @@ enum class SettingsPage
Count Count
}; };
//////////////////////////////////////////////////////////////////////////
// Utility
//////////////////////////////////////////////////////////////////////////
static std::string TimeToPrintableString(time_t t);
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// Main // Main
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
@ -388,24 +384,6 @@ static std::optional<u32> s_open_leaderboard_id;
#endif #endif
} // namespace FullscreenUI } // namespace FullscreenUI
//////////////////////////////////////////////////////////////////////////
// Utility
//////////////////////////////////////////////////////////////////////////
std::string FullscreenUI::TimeToPrintableString(time_t t)
{
struct tm lt = {};
#ifdef _MSC_VER
localtime_s(&lt, &t);
#else
localtime_r(&t, &lt);
#endif
char buf[256];
std::strftime(buf, sizeof(buf), "%c", &lt);
return std::string(buf);
}
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// Main // Main
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
@ -421,7 +399,7 @@ bool FullscreenUI::Initialize()
ImGuiFullscreen::SetTheme(); ImGuiFullscreen::SetTheme();
ImGuiFullscreen::UpdateLayoutScale(); ImGuiFullscreen::UpdateLayoutScale();
if (!ImGuiManager::AddFullscreenFontsIfMissing() || !ImGuiFullscreen::Initialize("fullscreenui/placeholder.png") || if (!ImGuiManager::AddFullscreenFontsIfMissing() || !ImGuiFullscreen::Initialize("images/placeholder.png") ||
!LoadResources()) !LoadResources())
{ {
DestroyResources(); DestroyResources();
@ -437,7 +415,6 @@ bool FullscreenUI::Initialize()
s_was_paused_on_quick_menu_open = false; s_was_paused_on_quick_menu_open = false;
s_about_window_open = false; s_about_window_open = false;
s_hotkey_list_cache = InputManager::GetHotkeyList(); s_hotkey_list_cache = InputManager::GetHotkeyList();
// GetMTGS().SetRunIdle(true);
if (!System::IsValid()) if (!System::IsValid())
SwitchToLanding(); SwitchToLanding();
@ -467,15 +444,14 @@ void FullscreenUI::OnSystemStarted()
void FullscreenUI::OnSystemPaused() void FullscreenUI::OnSystemPaused()
{ {
if (!IsInitialized()) // noop
return;
g_host_display->SetVSync(true);
} }
void FullscreenUI::OnSystemResumed() void FullscreenUI::OnSystemResumed()
{ {
// noop // get rid of pause menu if we unpaused another way
if (s_current_main_window == MainWindowType::PauseMenu)
ClosePauseMenu();
} }
void FullscreenUI::OnSystemDestroyed() void FullscreenUI::OnSystemDestroyed()
@ -505,7 +481,15 @@ void FullscreenUI::PauseForMenuOpen()
{ {
s_was_paused_on_quick_menu_open = (System::GetState() == System::State::Paused); s_was_paused_on_quick_menu_open = (System::GetState() == System::State::Paused);
if (g_settings.pause_on_menu && !s_was_paused_on_quick_menu_open) if (g_settings.pause_on_menu && !s_was_paused_on_quick_menu_open)
Host::RunOnCPUThread([]() { System::PauseSystem(true); }); {
Host::RunOnCPUThread([]() {
System::PauseSystem(true);
// force vsync on when pausing
if (g_host_display)
g_host_display->SetVSync(true);
});
}
s_pause_menu_was_open = true; s_pause_menu_was_open = true;
} }
@ -635,7 +619,7 @@ void FullscreenUI::ReturnToMainWindow()
bool FullscreenUI::LoadResources() bool FullscreenUI::LoadResources()
{ {
s_app_icon_texture = LoadTexture("fullscreenui/duck.png"); s_app_icon_texture = LoadTexture("images/duck.png");
s_fallback_disc_texture = LoadTexture("fullscreenui/media-cdrom.png"); s_fallback_disc_texture = LoadTexture("fullscreenui/media-cdrom.png");
s_fallback_exe_texture = LoadTexture("fullscreenui/applications-system.png"); s_fallback_exe_texture = LoadTexture("fullscreenui/applications-system.png");
@ -5796,3 +5780,104 @@ void DrawDebugDebugMenu()
} }
} }
#endif #endif
#else
// "Lightweight" version with only notifications for Android.
namespace FullscreenUI {
static bool s_initialized = false;
static bool s_tried_to_initialize = false;
}
bool FullscreenUI::Initialize()
{
if (s_initialized)
return true;
if (s_tried_to_initialize)
return false;
ImGuiFullscreen::SetTheme();
ImGuiFullscreen::UpdateLayoutScale();
if (!ImGuiManager::AddFullscreenFontsIfMissing() || !ImGuiFullscreen::Initialize("images/placeholder.png"))
{
ImGuiFullscreen::Shutdown();
s_tried_to_initialize = true;
return false;
}
s_initialized = true;
return true;
}
bool FullscreenUI::IsInitialized()
{
return s_initialized;
}
bool FullscreenUI::HasActiveWindow()
{
return false;
}
void FullscreenUI::OnSystemStarted()
{
// noop
}
void FullscreenUI::OnSystemPaused()
{
// noop
}
void FullscreenUI::OnSystemResumed()
{
// noop
}
void FullscreenUI::OnSystemDestroyed()
{
// noop
}
void FullscreenUI::OnRunningGameChanged()
{
// noop
}
void FullscreenUI::OpenPauseMenu()
{
// noop
}
bool FullscreenUI::OpenAchievementsWindow()
{
return false;
}
bool FullscreenUI::OpenLeaderboardsWindow()
{
return false;
}
void FullscreenUI::Shutdown()
{
ImGuiFullscreen::Shutdown();
s_initialized = false;
s_tried_to_initialize = false;
}
void FullscreenUI::Render()
{
if (!s_initialized)
return;
ImGuiFullscreen::UploadAsyncTextures();
ImGuiFullscreen::BeginLayout();
ImGuiFullscreen::EndLayout();
ImGuiFullscreen::ResetCloseMenuIfNeeded();
}
#endif // __ANDROID__

View File

@ -114,7 +114,7 @@ bool GameList::GetExeListEntry(const std::string& path, GameList::Entry* entry)
const std::string display_name(FileSystem::GetDisplayNameFromPath(path)); const std::string display_name(FileSystem::GetDisplayNameFromPath(path));
entry->serial.clear(); entry->serial.clear();
entry->title = Path::StripExtension(display_name); entry->title = Path::GetFileTitle(display_name);
entry->region = BIOS::GetPSExeDiscRegion(header); entry->region = BIOS::GetPSExeDiscRegion(header);
entry->total_size = ZeroExtend64(file_size); entry->total_size = ZeroExtend64(file_size);
entry->type = EntryType::PSExe; entry->type = EntryType::PSExe;
@ -156,7 +156,7 @@ bool GameList::GetPsfListEntry(const std::string& path, Entry* entry)
else else
{ {
const std::string display_name(FileSystem::GetDisplayNameFromPath(path)); const std::string display_name(FileSystem::GetDisplayNameFromPath(path));
entry->title += Path::StripExtension(display_name); entry->title += Path::GetFileTitle(display_name);
} }
return true; return true;
@ -193,7 +193,7 @@ bool GameList::GetDiscListEntry(const std::string& path, Entry* entry)
} }
else else
{ {
const std::string display_name(Path::GetFileTitle(FileSystem::GetDisplayNameFromPath(path))); const std::string display_name(FileSystem::GetDisplayNameFromPath(path));
// no game code, so use the filename title // no game code, so use the filename title
entry->serial = System::GetGameCodeForImage(cdi.get(), true); entry->serial = System::GetGameCodeForImage(cdi.get(), true);