Misc: Post-refactor cleanups

This commit is contained in:
Stenzek
2023-08-23 22:06:48 +10:00
parent 82cdef45b3
commit dc9c99438b
55 changed files with 1243 additions and 1396 deletions

View File

@ -16,8 +16,6 @@ add_library(core
cheats.h
controller.cpp
controller.h
common_host.cpp
common_host.h
cpu_code_cache.cpp
cpu_code_cache.h
cpu_core.cpp
@ -62,8 +60,7 @@ add_library(core
host.h
host_interface_progress_callback.cpp
host_interface_progress_callback.h
host_settings.cpp
host_settings.h
hotkeys.cpp
input_types.h
imgui_overlays.cpp
imgui_overlays.h
@ -157,7 +154,6 @@ endif()
if(ENABLE_CHEEVOS)
target_sources(core PRIVATE
achievements.cpp
achievements_private.h
)
target_compile_definitions(core PUBLIC -DWITH_CHEEVOS=1)
target_link_libraries(core PRIVATE rcheevos rapidjson)

View File

@ -1,13 +1,12 @@
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
#include "achievements_private.h"
#include "achievements.h"
#include "bios.h"
#include "bus.h"
#include "cpu_core.h"
#include "fullscreen_ui.h"
#include "host.h"
#include "host_settings.h"
#include "system.h"
#include "scmversion/scmversion.h"
@ -682,8 +681,7 @@ void Achievements::FrameUpdate()
#ifdef WITH_RAINTEGRATION
if (IsUsingRAIntegration())
{
if (!System::IsPaused())
RA_DoAchievementsFrame();
RA_DoAchievementsFrame();
return;
}
#endif
@ -693,17 +691,8 @@ void Achievements::FrameUpdate()
if (HasActiveGame())
{
std::unique_lock lock(s_achievements_mutex);
if (!System::IsPaused())
rc_runtime_do_frame(&s_rcheevos_runtime, &CheevosEventHandler, &PeekMemory, nullptr, nullptr);
rc_runtime_do_frame(&s_rcheevos_runtime, &CheevosEventHandler, &PeekMemory, nullptr, nullptr);
UpdateRichPresence();
if (!IsTestModeActive())
{
const s32 ping_frequency =
g_settings.achievements_rich_presence ? RICH_PRESENCE_PING_FREQUENCY : NO_RICH_PRESENCE_PING_FREQUENCY;
if (static_cast<s32>(s_last_ping_time.GetTimeSeconds()) >= ping_frequency)
SendPing();
}
}
}
@ -714,7 +703,18 @@ void Achievements::ProcessPendingHTTPRequests()
return;
#endif
if (!s_http_downloader)
return;
s_http_downloader->PollRequests();
if (HasActiveGame() && !IsTestModeActive())
{
const s32 ping_frequency =
g_settings.achievements_rich_presence ? RICH_PRESENCE_PING_FREQUENCY : NO_RICH_PRESENCE_PING_FREQUENCY;
if (static_cast<s32>(s_last_ping_time.GetTimeSeconds()) >= ping_frequency)
SendPing();
}
}
bool Achievements::DoState(StateWrapper& sw)
@ -1029,7 +1029,7 @@ void Achievements::DisplayAchievementSummary()
// Technically not going through the resource API, but since we're passing this to something else, we can't.
if (g_settings.achievements_sound_effects)
FrontendCommon::PlaySoundAsync(Path::Combine(EmuFolders::Resources, INFO_SOUND_NAME).c_str());
PlatformMisc::PlaySoundAsync(Path::Combine(EmuFolders::Resources, INFO_SOUND_NAME).c_str());
});
}
@ -1773,7 +1773,7 @@ void Achievements::SubmitLeaderboardCallback(s32 status_code, std::string conten
// Technically not going through the resource API, but since we're passing this to something else, we can't.
if (g_settings.achievements_sound_effects)
FrontendCommon::PlaySoundAsync(Path::Combine(EmuFolders::Resources, LBSUBMIT_SOUND_NAME).c_str());
PlatformMisc::PlaySoundAsync(Path::Combine(EmuFolders::Resources, LBSUBMIT_SOUND_NAME).c_str());
}
void Achievements::UnlockAchievement(u32 achievement_id, bool add_notification /* = true*/)
@ -1818,7 +1818,7 @@ void Achievements::UnlockAchievement(u32 achievement_id, bool add_notification /
GetAchievementBadgePath(*achievement));
}
if (g_settings.achievements_sound_effects)
FrontendCommon::PlaySoundAsync(Path::Combine(EmuFolders::Resources, UNLOCK_SOUND_NAME).c_str());
PlatformMisc::PlaySoundAsync(Path::Combine(EmuFolders::Resources, UNLOCK_SOUND_NAME).c_str());
if (IsMastered())
DisplayMasteredNotification();

View File

@ -1,10 +1,19 @@
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
#pragma once
#include "settings.h"
#include "types.h"
#include "common/string.h"
#include "common/types.h"
#include <functional>
#include <optional>
#include <string>
#include <utility>
#include <vector>
class StateWrapper;
class CDImage;
@ -13,23 +22,157 @@ namespace Achievements {
#ifdef WITH_CHEEVOS
// Implemented in Host.
extern bool ConfirmSystemReset();
extern void ResetRuntime();
extern bool DoState(StateWrapper& sw);
extern void GameChanged(const std::string& path, CDImage* image);
enum class AchievementCategory : u8
{
Local = 0,
Core = 3,
Unofficial = 5
};
struct Achievement
{
u32 id;
std::string title;
std::string description;
std::string memaddr;
std::string badge_name;
// badge paths are mutable because they're resolved when they're needed.
mutable std::string locked_badge_path;
mutable std::string unlocked_badge_path;
u32 points;
AchievementCategory category;
bool locked;
bool active;
bool primed;
};
struct Leaderboard
{
u32 id;
std::string title;
std::string description;
int format;
};
struct LeaderboardEntry
{
std::string user;
std::string formatted_score;
time_t submitted;
u32 rank;
bool is_self;
};
// RAIntegration only exists for Windows, so no point checking it on other platforms.
#ifdef WITH_RAINTEGRATION
bool IsUsingRAIntegration();
#else
static ALWAYS_INLINE bool IsUsingRAIntegration()
{
return false;
}
#endif
bool IsActive();
bool IsLoggedIn();
bool ChallengeModeActive();
bool LeaderboardsActive();
bool IsTestModeActive();
bool IsUnofficialTestModeActive();
bool IsRichPresenceEnabled();
bool HasActiveGame();
u32 GetGameID();
/// Acquires the achievements lock. Must be held when accessing any achievement state from another thread.
std::unique_lock<std::recursive_mutex> GetLock();
void Initialize();
void UpdateSettings(const Settings& old_config);
void ResetRuntime();
/// Called when the system is being reset. If it returns false, the reset should be aborted.
bool ConfirmSystemReset();
/// Called when the system is being shut down. If Shutdown() returns false, the shutdown should be aborted.
bool Shutdown();
/// Called when the system is being paused and resumed.
void OnSystemPaused(bool paused);
/// Called once a frame at vsync time on the CPU thread.
void FrameUpdate();
/// Called when the system is paused, because FrameUpdate() won't be getting called.
void ProcessPendingHTTPRequests();
/// Saves/loads state.
bool DoState(StateWrapper& sw);
/// Returns true if the current game has any achievements or leaderboards.
/// Does not need to have the lock held.
bool SafeHasAchievementsOrLeaderboards();
const std::string& GetUsername();
const std::string& GetRichPresenceString();
bool LoginAsync(const char* username, const char* password);
bool Login(const char* username, const char* password);
void Logout();
void GameChanged(const std::string& path, CDImage* image);
/// Re-enables hardcode mode if it is enabled in the settings.
extern bool ResetChallengeMode();
bool ResetChallengeMode();
/// Forces hardcore mode off until next reset.
extern void DisableChallengeMode();
void DisableChallengeMode();
/// Prompts the user to disable hardcore mode, if they agree, returns true.
extern bool ConfirmChallengeModeDisable(const char* trigger);
bool ConfirmChallengeModeDisable(const char* trigger);
/// Returns true if features such as save states should be disabled.
extern bool ChallengeModeActive();
bool ChallengeModeActive();
const std::string& GetGameTitle();
const std::string& GetGameIcon();
bool EnumerateAchievements(std::function<bool(const Achievement&)> callback);
u32 GetUnlockedAchiementCount();
u32 GetAchievementCount();
u32 GetMaximumPointsForGame();
u32 GetCurrentPointsForGame();
bool EnumerateLeaderboards(std::function<bool(const Leaderboard&)> callback);
std::optional<bool> TryEnumerateLeaderboardEntries(u32 id, std::function<bool(const LeaderboardEntry&)> callback);
const Leaderboard* GetLeaderboardByID(u32 id);
u32 GetLeaderboardCount();
bool IsLeaderboardTimeType(const Leaderboard& leaderboard);
u32 GetPrimedAchievementCount();
const Achievement* GetAchievementByID(u32 id);
std::pair<u32, u32> GetAchievementProgress(const Achievement& achievement);
TinyString GetAchievementProgressText(const Achievement& achievement);
const std::string& GetAchievementBadgePath(const Achievement& achievement, bool download_if_missing = true,
bool force_unlocked_icon = false);
std::string GetAchievementBadgeURL(const Achievement& achievement);
#ifdef WITH_RAINTEGRATION
void SwitchToRAIntegration();
namespace RAIntegration {
void MainWindowChanged(void* new_handle);
void GameChanged();
std::vector<std::tuple<int, std::string, bool>> GetMenuItems();
void ActivateMenuItem(int item);
} // namespace RAIntegration
#endif
#else
@ -38,7 +181,9 @@ static inline bool ConfirmSystemReset()
{
return true;
}
static inline void ResetRuntime() {}
static inline void ResetRuntime()
{
}
static inline bool DoState(StateWrapper& sw)
{
return true;
@ -53,7 +198,9 @@ static inline bool ResetChallengeMode()
return false;
}
static inline void DisableChallengeMode() {}
static inline void DisableChallengeMode()
{
}
static inline bool ConfirmChallengeModeDisable(const char* trigger)
{
@ -63,3 +210,9 @@ static inline bool ConfirmChallengeModeDisable(const char* trigger)
#endif
} // namespace Achievements
/// Functions implemented in the frontend.
namespace Host {
void OnAchievementsRefreshed();
void OnAchievementsChallengeModeChanged();
} // namespace Host

View File

@ -1,178 +0,0 @@
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
#pragma once
#include "achievements.h"
#include "settings.h"
#include "types.h"
#include "common/string.h"
#include <functional>
#include <optional>
#include <string>
#include <utility>
#include <vector>
class CDImage;
class StateWrapper;
namespace Achievements {
enum class AchievementCategory : u8
{
Local = 0,
Core = 3,
Unofficial = 5
};
struct Achievement
{
u32 id;
std::string title;
std::string description;
std::string memaddr;
std::string badge_name;
// badge paths are mutable because they're resolved when they're needed.
mutable std::string locked_badge_path;
mutable std::string unlocked_badge_path;
u32 points;
AchievementCategory category;
bool locked;
bool active;
bool primed;
};
struct Leaderboard
{
u32 id;
std::string title;
std::string description;
int format;
};
struct LeaderboardEntry
{
std::string user;
std::string formatted_score;
time_t submitted;
u32 rank;
bool is_self;
};
// RAIntegration only exists for Windows, so no point checking it on other platforms.
#ifdef WITH_RAINTEGRATION
bool IsUsingRAIntegration();
#else
static ALWAYS_INLINE bool IsUsingRAIntegration()
{
return false;
}
#endif
bool IsActive();
bool IsLoggedIn();
bool ChallengeModeActive();
bool LeaderboardsActive();
bool IsTestModeActive();
bool IsUnofficialTestModeActive();
bool IsRichPresenceEnabled();
bool HasActiveGame();
u32 GetGameID();
/// Acquires the achievements lock. Must be held when accessing any achievement state from another thread.
std::unique_lock<std::recursive_mutex> GetLock();
void Initialize();
void UpdateSettings(const Settings& old_config);
/// Called when the system is being reset. If it returns false, the reset should be aborted.
bool ConfirmSystemReset();
/// Called when the system is being shut down. If Shutdown() returns false, the shutdown should be aborted.
bool Shutdown();
/// Called when the system is being paused and resumed.
void OnSystemPaused(bool paused);
/// Called once a frame at vsync time on the CPU thread.
void FrameUpdate();
/// Called when the system is paused, because FrameUpdate() won't be getting called.
void ProcessPendingHTTPRequests();
/// Saves/loads state.
bool DoState(StateWrapper& sw);
/// Returns true if the current game has any achievements or leaderboards.
/// Does not need to have the lock held.
bool SafeHasAchievementsOrLeaderboards();
const std::string& GetUsername();
const std::string& GetRichPresenceString();
bool LoginAsync(const char* username, const char* password);
bool Login(const char* username, const char* password);
void Logout();
void GameChanged(const std::string& path, CDImage* image);
/// Re-enables hardcode mode if it is enabled in the settings.
bool ResetChallengeMode();
/// Forces hardcore mode off until next reset.
void DisableChallengeMode();
/// Prompts the user to disable hardcore mode, if they agree, returns true.
bool ConfirmChallengeModeDisable(const char* trigger);
/// Returns true if features such as save states should be disabled.
bool ChallengeModeActive();
const std::string& GetGameTitle();
const std::string& GetGameIcon();
bool EnumerateAchievements(std::function<bool(const Achievement&)> callback);
u32 GetUnlockedAchiementCount();
u32 GetAchievementCount();
u32 GetMaximumPointsForGame();
u32 GetCurrentPointsForGame();
bool EnumerateLeaderboards(std::function<bool(const Leaderboard&)> callback);
std::optional<bool> TryEnumerateLeaderboardEntries(u32 id, std::function<bool(const LeaderboardEntry&)> callback);
const Leaderboard* GetLeaderboardByID(u32 id);
u32 GetLeaderboardCount();
bool IsLeaderboardTimeType(const Leaderboard& leaderboard);
u32 GetPrimedAchievementCount();
const Achievement* GetAchievementByID(u32 id);
std::pair<u32, u32> GetAchievementProgress(const Achievement& achievement);
TinyString GetAchievementProgressText(const Achievement& achievement);
const std::string& GetAchievementBadgePath(const Achievement& achievement, bool download_if_missing = true,
bool force_unlocked_icon = false);
std::string GetAchievementBadgeURL(const Achievement& achievement);
#ifdef WITH_RAINTEGRATION
void SwitchToRAIntegration();
namespace RAIntegration {
void MainWindowChanged(void* new_handle);
void GameChanged();
std::vector<std::tuple<int, std::string, bool>> GetMenuItems();
void ActivateMenuItem(int item);
} // namespace RAIntegration
#endif
} // namespace Achievements
/// Functions implemented in the frontend.
namespace Host {
void OnAchievementsRefreshed();
void OnAchievementsChallengeModeChanged();
} // namespace Host

View File

@ -8,6 +8,7 @@
#include "host.h"
#include "settings.h"
#include "system.h"
#include "util/input_manager.h"
#include "util/state_wrapper.h"
#include <cmath>
Log_SetChannel(AnalogController);
@ -348,7 +349,7 @@ void AnalogController::UpdateHostVibration()
hvalues[motor] = (state != 0) ? static_cast<float>(strength / 65535.0) : 0.0f;
}
Host::SetPadVibrationIntensity(m_index, hvalues[0], hvalues[1]);
InputManager::SetPadVibrationIntensity(m_index, hvalues[0], hvalues[1]);
}
u8 AnalogController::GetExtraButtonMaskLSB() const
@ -820,7 +821,7 @@ static const Controller::ControllerBindingInfo s_binding_info[] = {
AXIS("RRight", TRANSLATE_NOOP("AnalogController", "Right Stick Right"), AnalogController::HalfAxis::RRight, GenericInputBinding::RightStickRight),
AXIS("RDown", TRANSLATE_NOOP("AnalogController", "Right Stick Down"), AnalogController::HalfAxis::RDown, GenericInputBinding::RightStickDown),
AXIS("RUp", TRANSLATE_NOOP("AnalogController", "Right Stick Up"), AnalogController::HalfAxis::RUp, GenericInputBinding::RightStickUp),
// clang-format on
// clang-format on
#undef AXIS
#undef BUTTON

View File

@ -9,7 +9,6 @@
#include "common/path.h"
#include "cpu_disasm.h"
#include "host.h"
#include "host_settings.h"
#include "settings.h"
#include <cerrno>
Log_SetChannel(BIOS);

View File

@ -1,46 +0,0 @@
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
#pragma once
#include "system.h"
#include <memory>
#include <mutex>
#include <string>
#include <utility>
#include <vector>
class SettingsInterface;
class AudioStream;
enum class AudioStretchMode : u8;
namespace CommonHost {
/// Initializes configuration.
void UpdateLogSettings();
void Initialize();
void Shutdown();
void SetDefaultSettings(SettingsInterface& si);
void SetDefaultControllerSettings(SettingsInterface& si);
void SetDefaultHotkeyBindings(SettingsInterface& si);
void LoadSettings(SettingsInterface& si, std::unique_lock<std::mutex>& lock);
void CheckForSettingsChanges(const Settings& old_settings);
void OnSystemStarting();
void OnSystemStarted();
void OnSystemDestroyed();
void OnSystemPaused();
void OnSystemResumed();
void OnGameChanged(const std::string& disc_path, const std::string& game_serial, const std::string& game_name);
void PumpMessagesOnCPUThread();
/// Returns the time elapsed in the current play session.
u64 GetSessionPlayedTime();
} // namespace CommonHost
namespace ImGuiManager {
void RenderDebugWindows();
}

View File

@ -10,7 +10,6 @@
<ClCompile Include="cdrom.cpp" />
<ClCompile Include="cdrom_async_reader.cpp" />
<ClCompile Include="cheats.cpp" />
<ClCompile Include="common_host.cpp" />
<ClCompile Include="cpu_core.cpp" />
<ClCompile Include="cpu_disasm.cpp" />
<ClCompile Include="cpu_code_cache.cpp" />
@ -49,7 +48,7 @@
<ClCompile Include="gpu_hw.cpp" />
<ClCompile Include="host.cpp" />
<ClCompile Include="host_interface_progress_callback.cpp" />
<ClCompile Include="host_settings.cpp" />
<ClCompile Include="hotkeys.cpp" />
<ClCompile Include="imgui_overlays.cpp" />
<ClCompile Include="interrupt_controller.cpp" />
<ClCompile Include="mdec.cpp" />
@ -74,7 +73,6 @@
<ClCompile Include="timing_event.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="achievements_private.h" />
<ClInclude Include="analog_controller.h" />
<ClInclude Include="analog_joystick.h" />
<ClInclude Include="bios.h" />
@ -83,7 +81,6 @@
<ClInclude Include="cdrom_async_reader.h" />
<ClInclude Include="cheats.h" />
<ClInclude Include="achievements.h" />
<ClInclude Include="common_host.h" />
<ClInclude Include="cpu_core.h" />
<ClInclude Include="cpu_core_private.h" />
<ClInclude Include="cpu_disasm.h" />
@ -118,7 +115,6 @@
<ClInclude Include="gte_types.h" />
<ClInclude Include="host.h" />
<ClInclude Include="host_interface_progress_callback.h" />
<ClInclude Include="host_settings.h" />
<ClInclude Include="imgui_overlays.h" />
<ClInclude Include="input_types.h" />
<ClInclude Include="interrupt_controller.h" />

View File

@ -54,11 +54,10 @@
<ClCompile Include="game_database.cpp" />
<ClCompile Include="pcdrv.cpp" />
<ClCompile Include="game_list.cpp" />
<ClCompile Include="host_settings.cpp" />
<ClCompile Include="imgui_overlays.cpp" />
<ClCompile Include="fullscreen_ui.cpp" />
<ClCompile Include="common_host.cpp" />
<ClCompile Include="achievements.cpp" />
<ClCompile Include="hotkeys.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="types.h" />
@ -113,7 +112,6 @@
<ClInclude Include="multitap.h" />
<ClInclude Include="gdb_protocol.h" />
<ClInclude Include="host.h" />
<ClInclude Include="host_settings.h" />
<ClInclude Include="achievements.h" />
<ClInclude Include="game_database.h" />
<ClInclude Include="input_types.h" />
@ -121,8 +119,6 @@
<ClInclude Include="game_list.h" />
<ClInclude Include="imgui_overlays.h" />
<ClInclude Include="fullscreen_ui.h" />
<ClInclude Include="common_host.h" />
<ClInclude Include="achievements_private.h" />
<ClInclude Include="shader_cache_version.h" />
</ItemGroup>
</Project>

View File

@ -7,14 +7,12 @@
#include "achievements.h"
#include "bios.h"
#include "cheats.h"
#include "common_host.h"
#include "controller.h"
#include "core/memory_card_image.h"
#include "cpu_core.h"
#include "game_list.h"
#include "gpu.h"
#include "host.h"
#include "host_settings.h"
#include "resources.h"
#include "settings.h"
#include "system.h"
@ -53,10 +51,6 @@
Log_SetChannel(FullscreenUI);
#ifdef WITH_CHEEVOS
#include "achievements_private.h"
#endif
#define TR_CONTEXT "FullscreenUI"
namespace {
@ -3170,7 +3164,7 @@ void FullscreenUI::ResetControllerSettings()
{
SettingsInterface* dsi = GetEditingSettingsInterface();
CommonHost::SetDefaultControllerSettings(*dsi);
Settings::SetDefaultControllerConfig(*dsi);
ShowToast(std::string(), FSUI_STR("Controller settings reset to default."));
}
@ -4831,7 +4825,7 @@ void FullscreenUI::DrawPauseMenu(MainWindowType type)
if (!serial.empty())
{
const std::time_t cached_played_time = GameList::GetCachedPlayedTimeForSerial(serial);
const std::time_t session_time = static_cast<std::time_t>(CommonHost::GetSessionPlayedTime());
const std::time_t session_time = static_cast<std::time_t>(System::GetSessionPlayedTime());
buffer.Fmt(FSUI_FSTR("Session: {}"), GameList::FormatTimespan(session_time, true).GetStringView());
const ImVec2 session_size(g_medium_font->CalcTextSizeA(g_medium_font->FontSize, std::numeric_limits<float>::max(),

View File

@ -4,7 +4,6 @@
#include "game_list.h"
#include "bios.h"
#include "host.h"
#include "host_settings.h"
#include "psf_loader.h"
#include "settings.h"
#include "system.h"

View File

@ -4,17 +4,19 @@
#include "gdb_protocol.h"
#include "bus.h"
#include "cpu_core_private.h"
#include "cpu_core.h"
#include "system.h"
#include "common/log.h"
#include "common/string_util.h"
#include "cpu_core.h"
#include "common_host.h"
#include "system.h"
#include <functional>
#include <iomanip>
#include <map>
#include <optional>
#include <sstream>
#include <string>
Log_SetChannel(GDBProtocol);
namespace GDBProtocol

View File

@ -2,15 +2,17 @@
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
#include "host.h"
#include "common_host.h"
#include "fullscreen_ui.h"
#include "imgui_overlays.h"
#include "shader_cache_version.h"
#include "system.h"
#include "util/gpu_device.h"
#include "util/imgui_manager.h"
#include "common/assert.h"
#include "common/heterogeneous_containers.h"
#include "common/layered_settings_interface.h"
#include "common/log.h"
#include "common/string_util.h"
@ -23,6 +25,9 @@ namespace Host {
static std::pair<const char*, u32> LookupTranslationString(const std::string_view& context,
const std::string_view& msg);
static std::mutex s_settings_mutex;
static LayeredSettingsInterface s_layered_settings_interface;
static constexpr u32 TRANSLATION_STRING_CACHE_SIZE = 4 * 1024 * 1024;
using TranslationStringMap = UnorderedStringMap<std::pair<u32, u32>>;
using TranslationStringContextMap = UnorderedStringMap<TranslationStringMap>;
@ -32,6 +37,196 @@ static std::vector<char> s_translation_string_cache;
static u32 s_translation_string_cache_pos;
} // namespace Host
std::unique_lock<std::mutex> Host::GetSettingsLock()
{
return std::unique_lock<std::mutex>(s_settings_mutex);
}
SettingsInterface* Host::GetSettingsInterface()
{
return &s_layered_settings_interface;
}
SettingsInterface* Host::GetSettingsInterfaceForBindings()
{
SettingsInterface* input_layer = s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_INPUT);
return input_layer ? input_layer : &s_layered_settings_interface;
}
std::string Host::GetBaseStringSettingValue(const char* section, const char* key, const char* default_value /*= ""*/)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)
->GetStringValue(section, key, default_value);
}
bool Host::GetBaseBoolSettingValue(const char* section, const char* key, bool default_value /*= false*/)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)
->GetBoolValue(section, key, default_value);
}
s32 Host::GetBaseIntSettingValue(const char* section, const char* key, s32 default_value /*= 0*/)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)
->GetIntValue(section, key, default_value);
}
u32 Host::GetBaseUIntSettingValue(const char* section, const char* key, u32 default_value /*= 0*/)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)
->GetUIntValue(section, key, default_value);
}
float Host::GetBaseFloatSettingValue(const char* section, const char* key, float default_value /*= 0.0f*/)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)
->GetFloatValue(section, key, default_value);
}
double Host::GetBaseDoubleSettingValue(const char* section, const char* key, double default_value /* = 0.0f */)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)
->GetDoubleValue(section, key, default_value);
}
std::vector<std::string> Host::GetBaseStringListSetting(const char* section, const char* key)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->GetStringList(section, key);
}
std::string Host::GetStringSettingValue(const char* section, const char* key, const char* default_value /*= ""*/)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetStringValue(section, key, default_value);
}
bool Host::GetBoolSettingValue(const char* section, const char* key, bool default_value /*= false*/)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetBoolValue(section, key, default_value);
}
s32 Host::GetIntSettingValue(const char* section, const char* key, s32 default_value /*= 0*/)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetIntValue(section, key, default_value);
}
u32 Host::GetUIntSettingValue(const char* section, const char* key, u32 default_value /*= 0*/)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetUIntValue(section, key, default_value);
}
float Host::GetFloatSettingValue(const char* section, const char* key, float default_value /*= 0.0f*/)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetFloatValue(section, key, default_value);
}
double Host::GetDoubleSettingValue(const char* section, const char* key, double default_value /*= 0.0f*/)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetDoubleValue(section, key, default_value);
}
std::vector<std::string> Host::GetStringListSetting(const char* section, const char* key)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetStringList(section, key);
}
void Host::SetBaseBoolSettingValue(const char* section, const char* key, bool value)
{
std::unique_lock lock(s_settings_mutex);
s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->SetBoolValue(section, key, value);
}
void Host::SetBaseIntSettingValue(const char* section, const char* key, int value)
{
std::unique_lock lock(s_settings_mutex);
s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->SetIntValue(section, key, value);
}
void Host::SetBaseFloatSettingValue(const char* section, const char* key, float value)
{
std::unique_lock lock(s_settings_mutex);
s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->SetFloatValue(section, key, value);
}
void Host::SetBaseStringSettingValue(const char* section, const char* key, const char* value)
{
std::unique_lock lock(s_settings_mutex);
s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->SetStringValue(section, key, value);
}
void Host::SetBaseStringListSettingValue(const char* section, const char* key, const std::vector<std::string>& values)
{
std::unique_lock lock(s_settings_mutex);
s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->SetStringList(section, key, values);
}
bool Host::AddValueToBaseStringListSetting(const char* section, const char* key, const char* value)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)
->AddToStringList(section, key, value);
}
bool Host::RemoveValueFromBaseStringListSetting(const char* section, const char* key, const char* value)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)
->RemoveFromStringList(section, key, value);
}
void Host::DeleteBaseSettingValue(const char* section, const char* key)
{
std::unique_lock lock(s_settings_mutex);
s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->DeleteValue(section, key);
}
SettingsInterface* Host::Internal::GetBaseSettingsLayer()
{
return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE);
}
SettingsInterface* Host::Internal::GetGameSettingsLayer()
{
return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_GAME);
}
SettingsInterface* Host::Internal::GetInputSettingsLayer()
{
return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_INPUT);
}
void Host::Internal::SetBaseSettingsLayer(SettingsInterface* sif)
{
AssertMsg(s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE) == nullptr,
"Base layer has not been set");
s_layered_settings_interface.SetLayer(LayeredSettingsInterface::LAYER_BASE, sif);
}
void Host::Internal::SetGameSettingsLayer(SettingsInterface* sif)
{
std::unique_lock lock(s_settings_mutex);
s_layered_settings_interface.SetLayer(LayeredSettingsInterface::LAYER_GAME, sif);
}
void Host::Internal::SetInputSettingsLayer(SettingsInterface* sif)
{
std::unique_lock lock(s_settings_mutex);
s_layered_settings_interface.SetLayer(LayeredSettingsInterface::LAYER_INPUT, sif);
}
std::pair<const char*, u32> Host::LookupTranslationString(const std::string_view& context, const std::string_view& msg)
{
// TODO: TranslatableString, compile-time hashing.
@ -162,6 +357,118 @@ void Host::ReportFormattedDebuggerMessage(const char* format, ...)
ReportDebuggerMessage(message);
}
bool Host::CreateGPUDevice(RenderAPI api)
{
DebugAssert(!g_gpu_device);
Log_InfoPrintf("Trying to create a %s GPU device...", GPUDevice::RenderAPIToString(api));
g_gpu_device = GPUDevice::CreateDeviceForAPI(api);
// TODO: FSUI should always use vsync..
const bool vsync = System::IsValid() ? System::ShouldUseVSync() : g_settings.video_sync_enabled;
if (!g_gpu_device || !g_gpu_device->Create(g_settings.gpu_adapter,
g_settings.gpu_disable_shader_cache ? std::string_view() :
std::string_view(EmuFolders::Cache),
SHADER_CACHE_VERSION, g_settings.gpu_use_debug_device, vsync,
g_settings.gpu_threaded_presentation))
{
Log_ErrorPrintf("Failed to initialize GPU device.");
if (g_gpu_device)
g_gpu_device->Destroy();
g_gpu_device.reset();
return false;
}
if (!ImGuiManager::Initialize())
{
Log_ErrorPrintf("Failed to initialize ImGuiManager.");
g_gpu_device->Destroy();
g_gpu_device.reset();
return false;
}
return true;
}
void Host::UpdateDisplayWindow()
{
if (!g_gpu_device)
return;
if (!g_gpu_device->UpdateWindow())
{
Host::ReportErrorAsync("Error", "Failed to change window after update. The log may contain more information.");
return;
}
ImGuiManager::WindowResized();
// If we're paused, re-present the current frame at the new window size.
if (System::IsValid() && System::IsPaused())
RenderDisplay(false);
}
void Host::ResizeDisplayWindow(s32 width, s32 height, float scale)
{
if (!g_gpu_device)
return;
Log_DevPrintf("Display window resized to %dx%d", width, height);
g_gpu_device->ResizeWindow(width, height, scale);
ImGuiManager::WindowResized();
// If we're paused, re-present the current frame at the new window size.
if (System::IsValid())
{
if (System::IsPaused())
RenderDisplay(false);
System::HostDisplayResized();
}
}
void Host::ReleaseGPUDevice()
{
if (!g_gpu_device)
return;
SaveStateSelectorUI::DestroyTextures();
FullscreenUI::Shutdown();
ImGuiManager::Shutdown();
Log_InfoPrintf("Destroying %s GPU device...", GPUDevice::RenderAPIToString(g_gpu_device->GetRenderAPI()));
g_gpu_device->Destroy();
g_gpu_device.reset();
}
#ifndef __ANDROID__
std::unique_ptr<AudioStream> Host::CreateAudioStream(AudioBackend backend, u32 sample_rate, u32 channels, u32 buffer_ms,
u32 latency_ms, AudioStretchMode stretch)
{
switch (backend)
{
#ifdef WITH_CUBEB
case AudioBackend::Cubeb:
return AudioStream::CreateCubebAudioStream(sample_rate, channels, buffer_ms, latency_ms, stretch);
#endif
#ifdef _WIN32
case AudioBackend::XAudio2:
return AudioStream::CreateXAudio2Stream(sample_rate, channels, buffer_ms, latency_ms, stretch);
#endif
case AudioBackend::Null:
return AudioStream::CreateNullStream(sample_rate, channels, buffer_ms);
default:
return nullptr;
}
}
#endif
void Host::RenderDisplay(bool skip_present)
{
Host::BeginPresentFrame();

View File

@ -9,14 +9,17 @@
#include <ctime>
#include <functional>
#include <memory>
#include <mutex>
#include <optional>
#include <string>
#include <string_view>
#include <vector>
class SettingsInterface;
struct WindowInfo;
enum class AudioBackend : u8;
enum class AudioStretchMode : u8;
enum class RenderAPI : u32;
class AudioStream;
class CDImage;
@ -38,6 +41,46 @@ std::optional<std::string> ReadResourceFileToString(const char* filename);
/// Returns the modified time of a resource.
std::optional<std::time_t> GetResourceFileTimestamp(const char* filename);
// Base setting retrieval, bypasses layers.
std::string GetBaseStringSettingValue(const char* section, const char* key, const char* default_value = "");
bool GetBaseBoolSettingValue(const char* section, const char* key, bool default_value = false);
s32 GetBaseIntSettingValue(const char* section, const char* key, s32 default_value = 0);
u32 GetBaseUIntSettingValue(const char* section, const char* key, u32 default_value = 0);
float GetBaseFloatSettingValue(const char* section, const char* key, float default_value = 0.0f);
double GetBaseDoubleSettingValue(const char* section, const char* key, double default_value = 0.0);
std::vector<std::string> GetBaseStringListSetting(const char* section, const char* key);
// Allows the emucore to write settings back to the frontend. Use with care.
// You should call CommitBaseSettingChanges() if you directly write to the layer (i.e. not these functions), or it may
// not be written to disk.
void SetBaseBoolSettingValue(const char* section, const char* key, bool value);
void SetBaseIntSettingValue(const char* section, const char* key, s32 value);
void SetBaseUIntSettingValue(const char* section, const char* key, u32 value);
void SetBaseFloatSettingValue(const char* section, const char* key, float value);
void SetBaseStringSettingValue(const char* section, const char* key, const char* value);
void SetBaseStringListSettingValue(const char* section, const char* key, const std::vector<std::string>& values);
bool AddValueToBaseStringListSetting(const char* section, const char* key, const char* value);
bool RemoveValueFromBaseStringListSetting(const char* section, const char* key, const char* value);
void DeleteBaseSettingValue(const char* section, const char* key);
void CommitBaseSettingChanges();
// Settings access, thread-safe.
std::string GetStringSettingValue(const char* section, const char* key, const char* default_value = "");
bool GetBoolSettingValue(const char* section, const char* key, bool default_value = false);
int GetIntSettingValue(const char* section, const char* key, s32 default_value = 0);
u32 GetUIntSettingValue(const char* section, const char* key, u32 default_value = 0);
float GetFloatSettingValue(const char* section, const char* key, float default_value = 0.0f);
double GetDoubleSettingValue(const char* section, const char* key, double default_value = 0.0);
std::vector<std::string> GetStringListSetting(const char* section, const char* key);
/// Direct access to settings interface. Must hold the lock when calling GetSettingsInterface() and while using it.
std::unique_lock<std::mutex> GetSettingsLock();
SettingsInterface* GetSettingsInterface();
/// Returns the settings interface that controller bindings should be loaded from.
/// If an input profile is being used, this will be the input layer, otherwise the layered interface.
SettingsInterface* GetSettingsInterfaceForBindings();
/// Returns a localized version of the specified string within the specified context.
/// The pointer is guaranteed to be valid until the next language change.
const char* TranslateToCString(const std::string_view& context, const std::string_view& msg);
@ -85,10 +128,6 @@ void ReportFormattedDebuggerMessage(const char* format, ...);
/// such as compiling shaders when starting up.
void DisplayLoadingScreen(const char* message, int progress_min = -1, int progress_max = -1, int progress_value = -1);
/// Internal method used by pads to dispatch vibration updates to input sources.
/// Intensity is normalized from 0 to 1.
void SetPadVibrationIntensity(u32 pad_index, float large_or_single_motor_intensity, float small_motor_intensity);
/// Enables "relative" mouse mode, locking the cursor position and returning relative coordinates.
void SetMouseMode(bool relative, bool hide_cursor);
@ -105,6 +144,18 @@ bool CopyTextToClipboard(const std::string_view& text);
/// if the user cancels the shutdown confirmation.
void RequestExit(bool allow_confirm);
/// Attempts to create the rendering device backend.
bool CreateGPUDevice(RenderAPI api);
/// Handles fullscreen transitions and such.
void UpdateDisplayWindow();
/// Called when the window is resized.
void ResizeDisplayWindow(s32 width, s32 height, float scale);
/// Destroys any active rendering device.
void ReleaseGPUDevice();
/// Called before drawing the OSD and other display elements.
void BeginPresentFrame();
@ -113,6 +164,24 @@ void RenderDisplay(bool skip_present);
void InvalidateDisplay();
namespace Internal {
/// Retrieves the base settings layer. Must call with lock held.
SettingsInterface* GetBaseSettingsLayer();
/// Retrieves the game settings layer, if present. Must call with lock held.
SettingsInterface* GetGameSettingsLayer();
/// Retrieves the input settings layer, if present. Must call with lock held.
SettingsInterface* GetInputSettingsLayer();
/// Sets the base settings layer. Should be called by the host at initialization time.
void SetBaseSettingsLayer(SettingsInterface* sif);
/// Sets the game settings layer. Called by VMManager when the game changes.
void SetGameSettingsLayer(SettingsInterface* sif);
/// Sets the input profile settings layer. Called by VMManager when the game changes.
void SetInputSettingsLayer(SettingsInterface* sif);
/// Implementation to retrieve a translated string.
s32 GetTranslatedStringImpl(const std::string_view& context, const std::string_view& msg, char* tbuf,
size_t tbuf_space);

View File

@ -1,192 +0,0 @@
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
#include "core/host.h"
#include "core/host_settings.h"
#include "common/assert.h"
#include "common/layered_settings_interface.h"
static std::mutex s_settings_mutex;
static LayeredSettingsInterface s_layered_settings_interface;
std::unique_lock<std::mutex> Host::GetSettingsLock()
{
return std::unique_lock<std::mutex>(s_settings_mutex);
}
SettingsInterface* Host::GetSettingsInterface()
{
return &s_layered_settings_interface;
}
SettingsInterface* Host::GetSettingsInterfaceForBindings()
{
SettingsInterface* input_layer = s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_INPUT);
return input_layer ? input_layer : &s_layered_settings_interface;
}
std::string Host::GetBaseStringSettingValue(const char* section, const char* key, const char* default_value /*= ""*/)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->GetStringValue(section, key, default_value);
}
bool Host::GetBaseBoolSettingValue(const char* section, const char* key, bool default_value /*= false*/)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->GetBoolValue(section, key, default_value);
}
s32 Host::GetBaseIntSettingValue(const char* section, const char* key, s32 default_value /*= 0*/)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->GetIntValue(section, key, default_value);
}
u32 Host::GetBaseUIntSettingValue(const char* section, const char* key, u32 default_value /*= 0*/)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->GetUIntValue(section, key, default_value);
}
float Host::GetBaseFloatSettingValue(const char* section, const char* key, float default_value /*= 0.0f*/)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->GetFloatValue(section, key, default_value);
}
double Host::GetBaseDoubleSettingValue(const char* section, const char* key, double default_value /* = 0.0f */)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->GetDoubleValue(section, key, default_value);
}
std::vector<std::string> Host::GetBaseStringListSetting(const char* section, const char* key)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->GetStringList(section, key);
}
std::string Host::GetStringSettingValue(const char* section, const char* key, const char* default_value /*= ""*/)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetStringValue(section, key, default_value);
}
bool Host::GetBoolSettingValue(const char* section, const char* key, bool default_value /*= false*/)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetBoolValue(section, key, default_value);
}
s32 Host::GetIntSettingValue(const char* section, const char* key, s32 default_value /*= 0*/)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetIntValue(section, key, default_value);
}
u32 Host::GetUIntSettingValue(const char* section, const char* key, u32 default_value /*= 0*/)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetUIntValue(section, key, default_value);
}
float Host::GetFloatSettingValue(const char* section, const char* key, float default_value /*= 0.0f*/)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetFloatValue(section, key, default_value);
}
double Host::GetDoubleSettingValue(const char* section, const char* key, double default_value /*= 0.0f*/)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetDoubleValue(section, key, default_value);
}
std::vector<std::string> Host::GetStringListSetting(const char* section, const char* key)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetStringList(section, key);
}
void Host::SetBaseBoolSettingValue(const char* section, const char* key, bool value)
{
std::unique_lock lock(s_settings_mutex);
s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->SetBoolValue(section, key, value);
}
void Host::SetBaseIntSettingValue(const char* section, const char* key, int value)
{
std::unique_lock lock(s_settings_mutex);
s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->SetIntValue(section, key, value);
}
void Host::SetBaseFloatSettingValue(const char* section, const char* key, float value)
{
std::unique_lock lock(s_settings_mutex);
s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->SetFloatValue(section, key, value);
}
void Host::SetBaseStringSettingValue(const char* section, const char* key, const char* value)
{
std::unique_lock lock(s_settings_mutex);
s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->SetStringValue(section, key, value);
}
void Host::SetBaseStringListSettingValue(const char* section, const char* key, const std::vector<std::string>& values)
{
std::unique_lock lock(s_settings_mutex);
s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->SetStringList(section, key, values);
}
bool Host::AddValueToBaseStringListSetting(const char* section, const char* key, const char* value)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->AddToStringList(section, key, value);
}
bool Host::RemoveValueFromBaseStringListSetting(const char* section, const char* key, const char* value)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)
->RemoveFromStringList(section, key, value);
}
void Host::DeleteBaseSettingValue(const char* section, const char* key)
{
std::unique_lock lock(s_settings_mutex);
s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->DeleteValue(section, key);
}
SettingsInterface* Host::Internal::GetBaseSettingsLayer()
{
return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE);
}
SettingsInterface* Host::Internal::GetGameSettingsLayer()
{
return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_GAME);
}
SettingsInterface* Host::Internal::GetInputSettingsLayer()
{
return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_INPUT);
}
void Host::Internal::SetBaseSettingsLayer(SettingsInterface* sif)
{
AssertMsg(s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE) == nullptr, "Base layer has not been set");
s_layered_settings_interface.SetLayer(LayeredSettingsInterface::LAYER_BASE, sif);
}
void Host::Internal::SetGameSettingsLayer(SettingsInterface* sif)
{
std::unique_lock lock(s_settings_mutex);
s_layered_settings_interface.SetLayer(LayeredSettingsInterface::LAYER_GAME, sif);
}
void Host::Internal::SetInputSettingsLayer(SettingsInterface* sif)
{
std::unique_lock lock(s_settings_mutex);
s_layered_settings_interface.SetLayer(LayeredSettingsInterface::LAYER_INPUT, sif);
}

View File

@ -1,73 +0,0 @@
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
#pragma once
#include "common/types.h"
#include <mutex>
#include <string>
#include <vector>
class SettingsInterface;
namespace Host {
// Base setting retrieval, bypasses layers.
std::string GetBaseStringSettingValue(const char* section, const char* key, const char* default_value = "");
bool GetBaseBoolSettingValue(const char* section, const char* key, bool default_value = false);
s32 GetBaseIntSettingValue(const char* section, const char* key, s32 default_value = 0);
u32 GetBaseUIntSettingValue(const char* section, const char* key, u32 default_value = 0);
float GetBaseFloatSettingValue(const char* section, const char* key, float default_value = 0.0f);
double GetBaseDoubleSettingValue(const char* section, const char* key, double default_value = 0.0);
std::vector<std::string> GetBaseStringListSetting(const char* section, const char* key);
// Allows the emucore to write settings back to the frontend. Use with care.
// You should call CommitBaseSettingChanges() if you directly write to the layer (i.e. not these functions), or it may
// not be written to disk.
void SetBaseBoolSettingValue(const char* section, const char* key, bool value);
void SetBaseIntSettingValue(const char* section, const char* key, s32 value);
void SetBaseUIntSettingValue(const char* section, const char* key, u32 value);
void SetBaseFloatSettingValue(const char* section, const char* key, float value);
void SetBaseStringSettingValue(const char* section, const char* key, const char* value);
void SetBaseStringListSettingValue(const char* section, const char* key, const std::vector<std::string>& values);
bool AddValueToBaseStringListSetting(const char* section, const char* key, const char* value);
bool RemoveValueFromBaseStringListSetting(const char* section, const char* key, const char* value);
void DeleteBaseSettingValue(const char* section, const char* key);
void CommitBaseSettingChanges();
// Settings access, thread-safe.
std::string GetStringSettingValue(const char* section, const char* key, const char* default_value = "");
bool GetBoolSettingValue(const char* section, const char* key, bool default_value = false);
int GetIntSettingValue(const char* section, const char* key, s32 default_value = 0);
u32 GetUIntSettingValue(const char* section, const char* key, u32 default_value = 0);
float GetFloatSettingValue(const char* section, const char* key, float default_value = 0.0f);
double GetDoubleSettingValue(const char* section, const char* key, double default_value = 0.0);
std::vector<std::string> GetStringListSetting(const char* section, const char* key);
/// Direct access to settings interface. Must hold the lock when calling GetSettingsInterface() and while using it.
std::unique_lock<std::mutex> GetSettingsLock();
SettingsInterface* GetSettingsInterface();
/// Returns the settings interface that controller bindings should be loaded from.
/// If an input profile is being used, this will be the input layer, otherwise the layered interface.
SettingsInterface* GetSettingsInterfaceForBindings();
namespace Internal {
/// Retrieves the base settings layer. Must call with lock held.
SettingsInterface* GetBaseSettingsLayer();
/// Retrieves the game settings layer, if present. Must call with lock held.
SettingsInterface* GetGameSettingsLayer();
/// Retrieves the input settings layer, if present. Must call with lock held.
SettingsInterface* GetInputSettingsLayer();
/// Sets the base settings layer. Should be called by the host at initialization time.
void SetBaseSettingsLayer(SettingsInterface* sif);
/// Sets the game settings layer. Called by VMManager when the game changes.
void SetGameSettingsLayer(SettingsInterface* sif);
/// Sets the input profile settings layer. Called by VMManager when the game changes.
void SetInputSettingsLayer(SettingsInterface* sif);
} // namespace Internal
} // namespace Host

View File

@ -1,375 +1,28 @@
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
#include "common_host.h"
#include "cdrom.h"
#include "cheats.h"
#include "controller.h"
#include "cpu_code_cache.h"
#include "dma.h"
#include "cpu_core.h"
#include "fullscreen_ui.h"
#include "game_list.h"
#include "gpu.h"
#include "gte.h"
#include "host.h"
#include "host_settings.h"
#include "imgui_overlays.h"
#include "mdec.h"
#include "pgxp.h"
#include "resources.h"
#include "save_state_version.h"
#include "settings.h"
#include "shader_cache_version.h"
#include "spu.h"
#include "system.h"
#include "texture_replacements.h"
#include "timers.h"
#include "scmversion/scmversion.h"
#include "util/audio_stream.h"
#include "util/gpu_device.h"
#include "util/imgui_fullscreen.h"
#include "util/imgui_manager.h"
#include "util/ini_settings_interface.h"
#include "util/input_manager.h"
#include "util/platform_misc.h"
#include "common/assert.h"
#include "common/byte_stream.h"
#include "common/crash_handler.h"
#include "common/file_system.h"
#include "common/log.h"
#include "common/path.h"
#include "common/string_util.h"
#include "IconsFontAwesome5.h"
#include "imgui.h"
#include <cmath>
#include <cstdio>
#include <cstring>
#include <ctime>
#ifdef WITH_DISCORD_PRESENCE
#include "discord_rpc.h"
#endif
#ifdef WITH_CHEEVOS
#include "achievements_private.h"
#endif
#ifdef _WIN32
#include "common/windows_headers.h"
#include <KnownFolders.h>
#include <ShlObj.h>
#include <mmsystem.h>
#endif
Log_SetChannel(CommonHostInterface);
namespace CommonHost {
static void UpdateSessionTime(const std::string& new_serial);
#ifdef WITH_DISCORD_PRESENCE
static void SetDiscordPresenceEnabled(bool enabled);
static void InitializeDiscordPresence();
static void ShutdownDiscordPresence();
static void UpdateDiscordPresence(bool rich_presence_only);
static void PollDiscordPresence();
#endif
} // namespace CommonHost
// Used to track play time. We use a monotonic timer here, in case of clock changes.
static u64 s_session_start_time = 0;
static std::string s_session_serial;
#ifdef WITH_DISCORD_PRESENCE
// discord rich presence
bool m_discord_presence_enabled = false;
bool m_discord_presence_active = false;
#ifdef WITH_CHEEVOS
std::string m_discord_presence_cheevos_string;
#endif
#endif
void CommonHost::Initialize()
{
// This will call back to Host::LoadSettings() -> ReloadSources().
System::LoadSettings(false);
#ifdef WITH_CHEEVOS
#ifdef WITH_RAINTEGRATION
if (Host::GetBaseBoolSettingValue("Cheevos", "UseRAIntegration", false))
Achievements::SwitchToRAIntegration();
#endif
if (g_settings.achievements_enabled)
Achievements::Initialize();
#endif
}
void CommonHost::Shutdown()
{
#ifdef WITH_DISCORD_PRESENCE
CommonHost::ShutdownDiscordPresence();
#endif
#ifdef WITH_CHEEVOS
Achievements::Shutdown();
#endif
InputManager::CloseSources();
}
void CommonHost::PumpMessagesOnCPUThread()
{
InputManager::PollSources();
#ifdef WITH_DISCORD_PRESENCE
PollDiscordPresence();
#endif
#ifdef WITH_CHEEVOS
if (Achievements::IsActive())
Achievements::FrameUpdate();
#endif
}
bool Host::CreateGPUDevice(RenderAPI api)
{
DebugAssert(!g_gpu_device);
Log_InfoPrintf("Trying to create a %s GPU device...", GPUDevice::RenderAPIToString(api));
g_gpu_device = GPUDevice::CreateDeviceForAPI(api);
// TODO: FSUI should always use vsync..
const bool vsync = System::IsValid() ? System::ShouldUseVSync() : g_settings.video_sync_enabled;
if (!g_gpu_device || !g_gpu_device->Create(g_settings.gpu_adapter,
g_settings.gpu_disable_shader_cache ? std::string_view() :
std::string_view(EmuFolders::Cache),
SHADER_CACHE_VERSION, g_settings.gpu_use_debug_device, vsync,
g_settings.gpu_threaded_presentation))
{
Log_ErrorPrintf("Failed to initialize GPU device.");
if (g_gpu_device)
g_gpu_device->Destroy();
g_gpu_device.reset();
return false;
}
if (!ImGuiManager::Initialize())
{
Log_ErrorPrintf("Failed to initialize ImGuiManager.");
g_gpu_device->Destroy();
g_gpu_device.reset();
return false;
}
return true;
}
void Host::UpdateDisplayWindow()
{
if (!g_gpu_device)
return;
if (!g_gpu_device->UpdateWindow())
{
Host::ReportErrorAsync("Error", "Failed to change window after update. The log may contain more information.");
return;
}
ImGuiManager::WindowResized();
// If we're paused, re-present the current frame at the new window size.
if (System::IsValid() && System::IsPaused())
RenderDisplay(false);
}
void Host::ResizeDisplayWindow(s32 width, s32 height, float scale)
{
if (!g_gpu_device)
return;
Log_DevPrintf("Display window resized to %dx%d", width, height);
g_gpu_device->ResizeWindow(width, height, scale);
ImGuiManager::WindowResized();
// If we're paused, re-present the current frame at the new window size.
if (System::IsValid())
{
if (System::IsPaused())
RenderDisplay(false);
System::HostDisplayResized();
}
}
void Host::ReleaseGPUDevice()
{
if (!g_gpu_device)
return;
SaveStateSelectorUI::DestroyTextures();
FullscreenUI::Shutdown();
ImGuiManager::Shutdown();
Log_InfoPrintf("Destroying %s GPU device...", GPUDevice::RenderAPIToString(g_gpu_device->GetRenderAPI()));
g_gpu_device->Destroy();
g_gpu_device.reset();
}
#ifndef __ANDROID__
std::unique_ptr<AudioStream> Host::CreateAudioStream(AudioBackend backend, u32 sample_rate, u32 channels, u32 buffer_ms,
u32 latency_ms, AudioStretchMode stretch)
{
switch (backend)
{
#ifdef WITH_CUBEB
case AudioBackend::Cubeb:
return AudioStream::CreateCubebAudioStream(sample_rate, channels, buffer_ms, latency_ms, stretch);
#endif
#ifdef _WIN32
case AudioBackend::XAudio2:
return AudioStream::CreateXAudio2Stream(sample_rate, channels, buffer_ms, latency_ms, stretch);
#endif
case AudioBackend::Null:
return AudioStream::CreateNullStream(sample_rate, channels, buffer_ms);
default:
return nullptr;
}
}
#endif
void CommonHost::UpdateLogSettings()
{
Log::SetFilterLevel(g_settings.log_level);
Log::SetConsoleOutputParams(g_settings.log_to_console,
g_settings.log_filter.empty() ? nullptr : g_settings.log_filter.c_str(),
g_settings.log_level);
Log::SetDebugOutputParams(g_settings.log_to_debug,
g_settings.log_filter.empty() ? nullptr : g_settings.log_filter.c_str(),
g_settings.log_level);
if (g_settings.log_to_file)
{
Log::SetFileOutputParams(g_settings.log_to_file, Path::Combine(EmuFolders::DataRoot, "duckstation.log").c_str(),
true, g_settings.log_filter.empty() ? nullptr : g_settings.log_filter.c_str(),
g_settings.log_level);
}
else
{
Log::SetFileOutputParams(false, nullptr);
}
}
void CommonHost::OnSystemStarting()
{
//
}
void CommonHost::OnSystemStarted()
{
FullscreenUI::OnSystemStarted();
if (g_settings.inhibit_screensaver)
FrontendCommon::SuspendScreensaver();
}
void CommonHost::OnSystemPaused()
{
FullscreenUI::OnSystemPaused();
InputManager::PauseVibration();
#ifdef WITH_CHEEVOS
Achievements::OnSystemPaused(true);
#endif
if (g_settings.inhibit_screensaver)
FrontendCommon::ResumeScreensaver();
}
void CommonHost::OnSystemResumed()
{
FullscreenUI::OnSystemResumed();
#ifdef WITH_CHEEVOS
Achievements::OnSystemPaused(false);
#endif
if (g_settings.inhibit_screensaver)
FrontendCommon::SuspendScreensaver();
}
void CommonHost::OnSystemDestroyed()
{
Host::ClearOSDMessages();
SaveStateSelectorUI::Close(true);
FullscreenUI::OnSystemDestroyed();
InputManager::PauseVibration();
if (g_settings.inhibit_screensaver)
FrontendCommon::ResumeScreensaver();
}
void CommonHost::OnGameChanged(const std::string& disc_path, const std::string& game_serial,
const std::string& game_name)
{
#ifdef WITH_DISCORD_PRESENCE
UpdateDiscordPresence(false);
#endif
UpdateSessionTime(game_serial);
SaveStateSelectorUI::RefreshList();
}
void CommonHost::SetDefaultSettings(SettingsInterface& si)
{
#ifdef WITH_DISCORD_PRESENCE
si.SetBoolValue("Main", "EnableDiscordPresence", false);
#endif
#if defined(WITH_CHEEVOS) && defined(WITH_RAINTEGRATION)
si.SetBoolValue("Cheevos", "UseRAIntegration", false);
#endif
}
void CommonHost::SetDefaultControllerSettings(SettingsInterface& si)
{
InputManager::SetDefaultConfig(si);
// Global Settings
si.SetStringValue("ControllerPorts", "MultitapMode", Settings::GetMultitapModeName(Settings::DEFAULT_MULTITAP_MODE));
si.SetFloatValue("ControllerPorts", "PointerXScale", 8.0f);
si.SetFloatValue("ControllerPorts", "PointerYScale", 8.0f);
si.SetBoolValue("ControllerPorts", "PointerXInvert", false);
si.SetBoolValue("ControllerPorts", "PointerYInvert", false);
// Default pad types and parameters.
for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
{
const std::string section(Controller::GetSettingsSection(i));
si.ClearSection(section.c_str());
si.SetStringValue(section.c_str(), "Type", Controller::GetDefaultPadType(i));
}
#ifndef __ANDROID__
// Use the automapper to set this up.
InputManager::MapController(si, 0, InputManager::GetGenericBindingMapping("Keyboard"));
#endif
}
void CommonHost::SetDefaultHotkeyBindings(SettingsInterface& si)
void Settings::SetDefaultHotkeyConfig(SettingsInterface& si)
{
si.ClearSection("Hotkeys");
@ -387,276 +40,6 @@ void CommonHost::SetDefaultHotkeyBindings(SettingsInterface& si)
#endif
}
void CommonHost::LoadSettings(SettingsInterface& si, std::unique_lock<std::mutex>& lock)
{
UpdateLogSettings();
InputManager::ReloadSources(si, lock);
InputManager::ReloadBindings(si, *Host::GetSettingsInterfaceForBindings());
#ifdef WITH_DISCORD_PRESENCE
SetDiscordPresenceEnabled(si.GetBoolValue("Main", "EnableDiscordPresence", false));
#endif
}
void CommonHost::CheckForSettingsChanges(const Settings& old_settings)
{
if (System::IsValid())
{
if (g_settings.inhibit_screensaver != old_settings.inhibit_screensaver)
{
if (g_settings.inhibit_screensaver)
FrontendCommon::SuspendScreensaver();
else
FrontendCommon::ResumeScreensaver();
}
}
#ifdef WITH_CHEEVOS
Achievements::UpdateSettings(old_settings);
#endif
FullscreenUI::CheckForConfigChanges(old_settings);
if (g_settings.log_level != old_settings.log_level || g_settings.log_filter != old_settings.log_filter ||
g_settings.log_to_console != old_settings.log_to_console ||
g_settings.log_to_debug != old_settings.log_to_debug || g_settings.log_to_window != old_settings.log_to_window ||
g_settings.log_to_file != old_settings.log_to_file)
{
UpdateLogSettings();
}
}
void CommonHost::UpdateSessionTime(const std::string& new_serial)
{
if (s_session_serial == new_serial)
return;
const u64 ctime = Common::Timer::GetCurrentValue();
if (!s_session_serial.empty())
{
// round up to seconds
const std::time_t etime =
static_cast<std::time_t>(std::round(Common::Timer::ConvertValueToSeconds(ctime - s_session_start_time)));
const std::time_t wtime = std::time(nullptr);
GameList::AddPlayedTimeForSerial(s_session_serial, wtime, etime);
}
s_session_serial = new_serial;
s_session_start_time = ctime;
}
u64 CommonHost::GetSessionPlayedTime()
{
const u64 ctime = Common::Timer::GetCurrentValue();
return static_cast<u64>(std::round(Common::Timer::ConvertValueToSeconds(ctime - s_session_start_time)));
}
void Host::SetPadVibrationIntensity(u32 pad_index, float large_or_single_motor_intensity, float small_motor_intensity)
{
InputManager::SetPadVibrationIntensity(pad_index, large_or_single_motor_intensity, small_motor_intensity);
}
void Host::DisplayLoadingScreen(const char* message, int progress_min /*= -1*/, int progress_max /*= -1*/,
int progress_value /*= -1*/)
{
const auto& io = ImGui::GetIO();
const float scale = ImGuiManager::GetGlobalScale();
const float width = (400.0f * scale);
const bool has_progress = (progress_min < progress_max);
// eat the last imgui frame, it might've been partially rendered by the caller.
ImGui::EndFrame();
ImGui::NewFrame();
const float logo_width = 260.0f * scale;
const float logo_height = 260.0f * scale;
ImGui::SetNextWindowSize(ImVec2(logo_width, logo_height), ImGuiCond_Always);
ImGui::SetNextWindowPos(ImVec2(io.DisplaySize.x * 0.5f, (io.DisplaySize.y * 0.5f) - (50.0f * scale)),
ImGuiCond_Always, ImVec2(0.5f, 0.5f));
if (ImGui::Begin("LoadingScreenLogo", nullptr,
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoNav |
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing |
ImGuiWindowFlags_NoBackground))
{
GPUTexture* tex = ImGuiFullscreen::GetCachedTexture("images/duck.png");
if (tex)
ImGui::Image(tex, ImVec2(logo_width, logo_height));
}
ImGui::End();
const float padding_and_rounding = 15.0f * scale;
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, padding_and_rounding);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(padding_and_rounding, padding_and_rounding));
ImGui::SetNextWindowSize(ImVec2(width, (has_progress ? 80.0f : 50.0f) * scale), ImGuiCond_Always);
ImGui::SetNextWindowPos(ImVec2(io.DisplaySize.x * 0.5f, (io.DisplaySize.y * 0.5f) + (100.0f * scale)),
ImGuiCond_Always, ImVec2(0.5f, 0.0f));
if (ImGui::Begin("LoadingScreen", nullptr,
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoNav |
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing))
{
if (has_progress)
{
ImGui::TextUnformatted(message);
TinyString buf;
buf.Fmt("{}/{}", progress_value, progress_max);
const ImVec2 prog_size = ImGui::CalcTextSize(buf.GetCharArray(), buf.GetCharArray() + buf.GetLength());
ImGui::SameLine();
ImGui::SetCursorPosX(width - padding_and_rounding - prog_size.x);
ImGui::TextUnformatted(buf.GetCharArray(), buf.GetCharArray() + buf.GetLength());
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 5.0f);
ImGui::ProgressBar(static_cast<float>(progress_value) / static_cast<float>(progress_max - progress_min),
ImVec2(-1.0f, 0.0f), "");
Log_InfoPrintf("%s: %d/%d", message, progress_value, progress_max);
}
else
{
const ImVec2 text_size(ImGui::CalcTextSize(message));
ImGui::SetCursorPosX((width - text_size.x) / 2.0f);
ImGui::TextUnformatted(message);
Log_InfoPrintf("%s", message);
}
}
ImGui::End();
ImGui::PopStyleVar(2);
ImGui::EndFrame();
g_gpu_device->Render(false);
ImGui::NewFrame();
}
void ImGuiManager::RenderDebugWindows()
{
if (System::IsValid())
{
if (g_settings.debugging.show_gpu_state)
g_gpu->DrawDebugStateWindow();
if (g_settings.debugging.show_cdrom_state)
CDROM::DrawDebugWindow();
if (g_settings.debugging.show_timers_state)
Timers::DrawDebugStateWindow();
if (g_settings.debugging.show_spu_state)
SPU::DrawDebugStateWindow();
if (g_settings.debugging.show_mdec_state)
MDEC::DrawDebugStateWindow();
if (g_settings.debugging.show_dma_state)
DMA::DrawDebugStateWindow();
}
}
#ifdef WITH_DISCORD_PRESENCE
void CommonHost::SetDiscordPresenceEnabled(bool enabled)
{
if (m_discord_presence_enabled == enabled)
return;
m_discord_presence_enabled = enabled;
if (enabled)
InitializeDiscordPresence();
else
ShutdownDiscordPresence();
}
void CommonHost::InitializeDiscordPresence()
{
if (m_discord_presence_active)
return;
DiscordEventHandlers handlers = {};
Discord_Initialize("705325712680288296", &handlers, 0, nullptr);
m_discord_presence_active = true;
UpdateDiscordPresence(false);
}
void CommonHost::ShutdownDiscordPresence()
{
if (!m_discord_presence_active)
return;
Discord_ClearPresence();
Discord_Shutdown();
m_discord_presence_active = false;
#ifdef WITH_CHEEVOS
m_discord_presence_cheevos_string.clear();
#endif
}
void CommonHost::UpdateDiscordPresence(bool rich_presence_only)
{
if (!m_discord_presence_active)
return;
#ifdef WITH_CHEEVOS
// Update only if RetroAchievements rich presence has changed
const std::string& new_rich_presence = Achievements::GetRichPresenceString();
if (new_rich_presence == m_discord_presence_cheevos_string && rich_presence_only)
{
return;
}
m_discord_presence_cheevos_string = new_rich_presence;
#else
if (rich_presence_only)
{
return;
}
#endif
// https://discord.com/developers/docs/rich-presence/how-to#updating-presence-update-presence-payload-fields
DiscordRichPresence rp = {};
rp.largeImageKey = "duckstation_logo";
rp.largeImageText = "DuckStation PS1/PSX Emulator";
rp.startTimestamp = std::time(nullptr);
SmallString details_string;
if (!System::IsShutdown())
{
details_string.AppendFormattedString("%s (%s)", System::GetGameTitle().c_str(), System::GetGameSerial().c_str());
}
else
{
details_string.AppendString("No Game Running");
}
#ifdef WITH_CHEEVOS
SmallString state_string;
// Trim to 128 bytes as per Discord-RPC requirements
if (m_discord_presence_cheevos_string.length() >= 128)
{
// 124 characters + 3 dots + null terminator
state_string = m_discord_presence_cheevos_string.substr(0, 124);
state_string.AppendString("...");
}
else
{
state_string = m_discord_presence_cheevos_string;
}
rp.state = state_string;
#endif
rp.details = details_string;
Discord_UpdatePresence(&rp);
}
void CommonHost::PollDiscordPresence()
{
if (!m_discord_presence_active)
return;
UpdateDiscordPresence(true);
Discord_RunCallbacks();
}
#endif
static void HotkeyModifyResolutionScale(s32 increment)
{
const u32 new_resolution_scale = std::clamp<u32>(

View File

@ -1,12 +1,14 @@
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
#include "imgui_overlays.h"
#include "cdrom.h"
#include "controller.h"
#include "dma.h"
#include "fullscreen_ui.h"
#include "gpu.h"
#include "host.h"
#include "host_settings.h"
#include "mdec.h"
#include "resources.h"
#include "settings.h"
#include "spu.h"
@ -131,6 +133,99 @@ static std::tuple<float, float> GetMinMax(gsl::span<const float> values)
static bool s_save_state_selector_ui_open = false;
void Host::DisplayLoadingScreen(const char* message, int progress_min /*= -1*/, int progress_max /*= -1*/,
int progress_value /*= -1*/)
{
const auto& io = ImGui::GetIO();
const float scale = ImGuiManager::GetGlobalScale();
const float width = (400.0f * scale);
const bool has_progress = (progress_min < progress_max);
// eat the last imgui frame, it might've been partially rendered by the caller.
ImGui::EndFrame();
ImGui::NewFrame();
const float logo_width = 260.0f * scale;
const float logo_height = 260.0f * scale;
ImGui::SetNextWindowSize(ImVec2(logo_width, logo_height), ImGuiCond_Always);
ImGui::SetNextWindowPos(ImVec2(io.DisplaySize.x * 0.5f, (io.DisplaySize.y * 0.5f) - (50.0f * scale)),
ImGuiCond_Always, ImVec2(0.5f, 0.5f));
if (ImGui::Begin("LoadingScreenLogo", nullptr,
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoNav |
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing |
ImGuiWindowFlags_NoBackground))
{
GPUTexture* tex = ImGuiFullscreen::GetCachedTexture("images/duck.png");
if (tex)
ImGui::Image(tex, ImVec2(logo_width, logo_height));
}
ImGui::End();
const float padding_and_rounding = 15.0f * scale;
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, padding_and_rounding);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(padding_and_rounding, padding_and_rounding));
ImGui::SetNextWindowSize(ImVec2(width, (has_progress ? 80.0f : 50.0f) * scale), ImGuiCond_Always);
ImGui::SetNextWindowPos(ImVec2(io.DisplaySize.x * 0.5f, (io.DisplaySize.y * 0.5f) + (100.0f * scale)),
ImGuiCond_Always, ImVec2(0.5f, 0.0f));
if (ImGui::Begin("LoadingScreen", nullptr,
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoNav |
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing))
{
if (has_progress)
{
ImGui::TextUnformatted(message);
TinyString buf;
buf.Fmt("{}/{}", progress_value, progress_max);
const ImVec2 prog_size = ImGui::CalcTextSize(buf.GetCharArray(), buf.GetCharArray() + buf.GetLength());
ImGui::SameLine();
ImGui::SetCursorPosX(width - padding_and_rounding - prog_size.x);
ImGui::TextUnformatted(buf.GetCharArray(), buf.GetCharArray() + buf.GetLength());
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 5.0f);
ImGui::ProgressBar(static_cast<float>(progress_value) / static_cast<float>(progress_max - progress_min),
ImVec2(-1.0f, 0.0f), "");
Log_InfoPrintf("%s: %d/%d", message, progress_value, progress_max);
}
else
{
const ImVec2 text_size(ImGui::CalcTextSize(message));
ImGui::SetCursorPosX((width - text_size.x) / 2.0f);
ImGui::TextUnformatted(message);
Log_InfoPrintf("%s", message);
}
}
ImGui::End();
ImGui::PopStyleVar(2);
ImGui::EndFrame();
g_gpu_device->Render(false);
ImGui::NewFrame();
}
void ImGuiManager::RenderDebugWindows()
{
if (System::IsValid())
{
if (g_settings.debugging.show_gpu_state)
g_gpu->DrawDebugStateWindow();
if (g_settings.debugging.show_cdrom_state)
CDROM::DrawDebugWindow();
if (g_settings.debugging.show_timers_state)
Timers::DrawDebugStateWindow();
if (g_settings.debugging.show_spu_state)
SPU::DrawDebugStateWindow();
if (g_settings.debugging.show_mdec_state)
MDEC::DrawDebugStateWindow();
if (g_settings.debugging.show_dma_state)
DMA::DrawDebugStateWindow();
}
}
void ImGuiManager::RenderTextOverlays()
{
const System::State state = System::GetState();

View File

@ -7,8 +7,9 @@
namespace ImGuiManager {
void RenderTextOverlays();
void RenderDebugWindows();
void RenderOverlayWindows();
}
} // namespace ImGuiManager
namespace SaveStateSelectorUI {

View File

@ -5,10 +5,10 @@
#include "achievements.h"
#include "controller.h"
#include "host.h"
#include "host_settings.h"
#include "system.h"
#include "util/gpu_device.h"
#include "util/input_manager.h"
#include "common/assert.h"
#include "common/file_system.h"
@ -182,6 +182,7 @@ void Settings::Load(SettingsInterface& si)
apply_game_settings = si.GetBoolValue("Main", "ApplyGameSettings", true);
auto_load_cheats = si.GetBoolValue("Main", "AutoLoadCheats", true);
disable_all_enhancements = si.GetBoolValue("Main", "DisableAllEnhancements", false);
enable_discord_presence = si.GetBoolValue("Main", "EnableDiscordPresence", false);
rewind_enable = si.GetBoolValue("Main", "RewindEnable", false);
rewind_save_frequency = si.GetFloatValue("Main", "RewindFrequency", 10.0f);
rewind_save_slots = static_cast<u32>(si.GetIntValue("Main", "RewindSaveSlots", 10));
@ -369,6 +370,7 @@ void Settings::Load(SettingsInterface& si)
achievements_notifications = si.GetBoolValue("Cheevos", "Notifications", true);
achievements_sound_effects = si.GetBoolValue("Cheevos", "SoundEffects", true);
achievements_primed_indicators = si.GetBoolValue("Cheevos", "PrimedIndicators", true);
achievements_use_raintegration = si.GetBoolValue("Cheevos", "UseRAIntegration", false);
log_level = ParseLogLevelName(si.GetStringValue("Logging", "LogLevel", GetLogLevelName(DEFAULT_LOG_LEVEL)).c_str())
.value_or(DEFAULT_LOG_LEVEL);
@ -426,6 +428,7 @@ void Settings::Save(SettingsInterface& si) const
si.SetBoolValue("Main", "ApplyGameSettings", apply_game_settings);
si.SetBoolValue("Main", "AutoLoadCheats", auto_load_cheats);
si.SetBoolValue("Main", "DisableAllEnhancements", disable_all_enhancements);
si.SetBoolValue("Main", "EnableDiscordPresence", enable_discord_presence);
si.SetBoolValue("Main", "RewindEnable", rewind_enable);
si.SetFloatValue("Main", "RewindFrequency", rewind_save_frequency);
si.SetIntValue("Main", "RewindSaveSlots", rewind_save_slots);
@ -565,6 +568,7 @@ void Settings::Save(SettingsInterface& si) const
si.SetBoolValue("Cheevos", "Notifications", achievements_notifications);
si.SetBoolValue("Cheevos", "SoundEffects", achievements_sound_effects);
si.SetBoolValue("Cheevos", "PrimedIndicators", achievements_primed_indicators);
si.SetBoolValue("Cheevos", "UseRAIntegration", achievements_use_raintegration);
si.SetStringValue("Logging", "LogLevel", GetLogLevelName(log_level));
si.SetStringValue("Logging", "LogFilter", log_filter.c_str());
@ -726,6 +730,46 @@ void Settings::FixIncompatibleSettings(bool display_osd_messages)
}
}
void Settings::UpdateLogSettings()
{
Log::SetFilterLevel(log_level);
Log::SetConsoleOutputParams(log_to_console, log_filter.empty() ? nullptr : log_filter.c_str(), log_level);
Log::SetDebugOutputParams(log_to_debug, log_filter.empty() ? nullptr : log_filter.c_str(), log_level);
if (log_to_file)
{
Log::SetFileOutputParams(log_to_file, Path::Combine(EmuFolders::DataRoot, "duckstation.log").c_str(), true,
log_filter.empty() ? nullptr : log_filter.c_str(), log_level);
}
else
{
Log::SetFileOutputParams(false, nullptr);
}
}
void Settings::SetDefaultControllerConfig(SettingsInterface& si)
{
// Global Settings
si.SetStringValue("ControllerPorts", "MultitapMode", GetMultitapModeName(Settings::DEFAULT_MULTITAP_MODE));
si.SetFloatValue("ControllerPorts", "PointerXScale", 8.0f);
si.SetFloatValue("ControllerPorts", "PointerYScale", 8.0f);
si.SetBoolValue("ControllerPorts", "PointerXInvert", false);
si.SetBoolValue("ControllerPorts", "PointerYInvert", false);
// Default pad types and parameters.
for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
{
const std::string section(Controller::GetSettingsSection(i));
si.ClearSection(section.c_str());
si.SetStringValue(section.c_str(), "Type", Controller::GetDefaultPadType(i));
}
#ifndef __ANDROID__
// Use the automapper to set this up.
InputManager::MapController(si, 0, InputManager::GetGenericBindingMapping("Keyboard"));
#endif
}
static std::array<const char*, LOGLEVEL_COUNT> s_log_level_names = {
{"None", "Error", "Warning", "Perf", "Info", "Verbose", "Dev", "Profile", "Debug", "Trace"}};
static std::array<const char*, LOGLEVEL_COUNT> s_log_level_display_names = {

View File

@ -85,6 +85,7 @@ struct Settings
bool apply_game_settings = true;
bool auto_load_cheats = true;
bool disable_all_enhancements = false;
bool enable_discord_presence = false;
bool rewind_enable = false;
float rewind_save_frequency = 10.0f;
@ -190,6 +191,7 @@ struct Settings
bool achievements_notifications = true;
bool achievements_sound_effects = true;
bool achievements_primed_indicators = true;
bool achievements_use_raintegration = false;
struct DebugSettings
{
@ -336,6 +338,12 @@ struct Settings
void FixIncompatibleSettings(bool display_osd_messages);
/// Initializes configuration.
void UpdateLogSettings();
static void SetDefaultControllerConfig(SettingsInterface& si);
static void SetDefaultHotkeyConfig(SettingsInterface& si);
static std::optional<LOGLEVEL> ParseLogLevelName(const char* str);
static const char* GetLogLevelName(LOGLEVEL level);
static const char* GetLogLevelDisplayName(LOGLEVEL level);

View File

@ -12,14 +12,14 @@
#include "cpu_code_cache.h"
#include "cpu_core.h"
#include "dma.h"
#include "fmt/chrono.h"
#include "fmt/format.h"
#include "fullscreen_ui.h"
#include "game_database.h"
#include "game_list.h"
#include "gpu.h"
#include "gte.h"
#include "host.h"
#include "host_interface_progress_callback.h"
#include "host_settings.h"
#include "imgui_overlays.h"
#include "interrupt_controller.h"
#include "mdec.h"
#include "memory_card.h"
@ -38,7 +38,9 @@
#include "util/cd_image.h"
#include "util/gpu_device.h"
#include "util/ini_settings_interface.h"
#include "util/input_manager.h"
#include "util/iso_reader.h"
#include "util/platform_misc.h"
#include "util/state_wrapper.h"
#include "common/error.h"
@ -49,6 +51,8 @@
#include "common/string_util.h"
#include "common/threading.h"
#include "fmt/chrono.h"
#include "fmt/format.h"
#include "xxhash.h"
#include <cctype>
@ -67,6 +71,10 @@ Log_SetChannel(System);
#include <mmsystem.h>
#endif
#ifdef WITH_DISCORD_PRESENCE
#include "discord_rpc.h"
#endif
// #define PROFILE_MEMORY_SAVE_STATES 1
SystemBootParameters::SystemBootParameters() = default;
@ -130,7 +138,16 @@ static void UpdateRunningGame(const char* path, CDImage* image, bool booting);
static bool CheckForSBIFile(CDImage* image);
static std::unique_ptr<MemoryCard> GetMemoryCardForSlot(u32 slot, MemoryCardType type);
static void UpdateSessionTime(const std::string& prev_serial);
static void SetTimerResolutionIncreased(bool enabled);
#ifdef WITH_DISCORD_PRESENCE
static void InitializeDiscordPresence();
static void ShutdownDiscordPresence();
static void UpdateDiscordPresence(bool rich_presence_only);
static void PollDiscordPresence();
#endif
} // namespace System
static constexpr const float PERFORMANCE_COUNTER_UPDATE_INTERVAL = 1.0f;
@ -222,11 +239,63 @@ static bool s_runahead_replay_pending = false;
static u32 s_runahead_frames = 0;
static u32 s_runahead_replay_frames = 0;
// Used to track play time. We use a monotonic timer here, in case of clock changes.
static u64 s_session_start_time = 0;
#ifdef WITH_DISCORD_PRESENCE
// discord rich presence
static bool s_discord_presence_active = false;
#ifdef WITH_CHEEVOS
static std::string s_discord_presence_cheevos_string;
#endif
#endif
static TinyString GetTimestampStringForFileName()
{
return TinyString::FromFmt("{:%Y-%m-%d_%H-%M-%S}", fmt::localtime(std::time(nullptr)));
}
void System::Internal::ProcessStartup()
{
// This will call back to Host::LoadSettings() -> ReloadSources().
LoadSettings(false);
#ifdef WITH_CHEEVOS
#ifdef WITH_RAINTEGRATION
if (Host::GetBaseBoolSettingValue("Cheevos", "UseRAIntegration", false))
Achievements::SwitchToRAIntegration();
#endif
if (g_settings.achievements_enabled)
Achievements::Initialize();
#endif
}
void System::Internal::ProcessShutdown()
{
#ifdef WITH_DISCORD_PRESENCE
ShutdownDiscordPresence();
#endif
#ifdef WITH_CHEEVOS
Achievements::Shutdown();
#endif
InputManager::CloseSources();
}
void System::Internal::IdlePollUpdate()
{
InputManager::PollSources();
#ifdef WITH_DISCORD_PRESENCE
PollDiscordPresence();
#endif
#ifdef WITH_CHEEVOS
Achievements::ProcessPendingHTTPRequests();
#endif
}
System::State System::GetState()
{
return s_state;
@ -845,7 +914,11 @@ void System::LoadSettings(bool display_osd_messages)
std::unique_lock<std::mutex> lock = Host::GetSettingsLock();
SettingsInterface& si = *Host::GetSettingsInterface();
g_settings.Load(si);
g_settings.UpdateLogSettings();
Host::LoadSettings(si, lock);
InputManager::ReloadSources(si, lock);
InputManager::ReloadBindings(si, *Host::GetSettingsInterfaceForBindings());
// apply compatibility settings
if (g_settings.apply_compatibility_settings && !s_running_game_serial.empty())
@ -1017,12 +1090,34 @@ void System::PauseSystem(bool paused)
if (paused)
{
FullscreenUI::OnSystemPaused();
InputManager::PauseVibration();
#ifdef WITH_CHEEVOS
Achievements::OnSystemPaused(true);
#endif
if (g_settings.inhibit_screensaver)
PlatformMisc::ResumeScreensaver();
Host::OnSystemPaused();
}
else
{
Host::OnSystemResumed();
FullscreenUI::OnSystemResumed();
#ifdef WITH_CHEEVOS
Achievements::OnSystemPaused(false);
#endif
if (g_settings.inhibit_screensaver)
PlatformMisc::SuspendScreensaver();
UpdateDisplaySync();
Host::OnSystemResumed();
ResetPerformanceCounters();
ResetThrottler();
}
@ -1330,16 +1425,16 @@ bool System::BootSystem(SystemBootParameters parameters)
}
// Good to go.
s_state =
(g_settings.start_paused || parameters.override_start_paused.value_or(false)) ? State::Paused : State::Running;
s_state = State::Running;
UpdateSoftwareCursor();
SPU::GetOutputStream()->SetPaused(false);
Host::OnSystemStarted();
if (s_state == State::Paused)
Host::OnSystemPaused();
else
Host::OnSystemResumed();
FullscreenUI::OnSystemStarted();
if (g_settings.inhibit_screensaver)
PlatformMisc::SuspendScreensaver();
Host::OnSystemStarted();
// try to load the state, if it fails, bail out
if (!parameters.save_state.empty())
@ -1371,6 +1466,9 @@ bool System::BootSystem(SystemBootParameters parameters)
if (g_settings.audio_dump_on_boot)
StartDumpingAudio();
if (g_settings.start_paused || parameters.override_start_paused.value_or(false))
PauseSystem(true);
ResetPerformanceCounters();
if (IsRunning())
UpdateSpeedLimiterState();
@ -1520,6 +1618,16 @@ void System::DestroySystem()
if (s_state == State::Shutdown)
return;
Host::ClearOSDMessages();
SaveStateSelectorUI::Close(true);
FullscreenUI::OnSystemDestroyed();
InputManager::PauseVibration();
if (g_settings.inhibit_screensaver)
PlatformMisc::ResumeScreensaver();
SetTimerResolutionIncreased(false);
s_cpu_thread_usage = {};
@ -1569,6 +1677,8 @@ void System::DestroySystem()
void System::ClearRunningGame()
{
UpdateSessionTime(s_running_game_serial);
s_running_game_serial.clear();
s_running_game_path.clear();
s_running_game_title.clear();
@ -1580,6 +1690,10 @@ void System::ClearRunningGame()
#ifdef WITH_CHEEVOS
Achievements::GameChanged(s_running_game_path, nullptr);
#endif
#ifdef WITH_DISCORD_PRESENCE
UpdateDiscordPresence(false);
#endif
}
bool System::FastForwardToFirstFrame()
@ -1644,6 +1758,7 @@ void System::FrameDone()
g_gpu->FlushRender();
// Generate any pending samples from the SPU before sleeping, this way we reduce the chances of underruns.
// TODO: when running ahead, we can skip this (and the flush above)
SPU::GeneratePendingSamples();
if (s_cheat_list)
@ -1677,6 +1792,8 @@ void System::FrameDone()
// *technically* this means higher input latency (by less than a frame), but runahead itself
// counter-acts that.
Host::PumpMessagesOnCPUThread();
InputManager::PollSources();
if (IsExecutionInterrupted())
{
s_system_interrupted = false;
@ -1694,6 +1811,15 @@ void System::FrameDone()
SaveRunaheadState();
}
#ifdef WITH_CHEEVOS
if (Achievements::IsActive())
Achievements::FrameUpdate();
#endif
#ifdef WITH_DISCORD_PRESENCE
PollDiscordPresence();
#endif
const Common::Timer::Value current_time = Common::Timer::GetCurrentValue();
if (current_time < s_next_frame_time || s_display_all_frames || s_last_frame_skipped)
{
@ -1722,6 +1848,7 @@ void System::FrameDone()
if (s_runahead_frames == 0)
{
Host::PumpMessagesOnCPUThread();
InputManager::PollSources();
if (IsExecutionInterrupted())
{
@ -3150,8 +3277,10 @@ void System::UpdateRunningGame(const char* path, CDImage* image, bool booting)
if (!booting && s_running_game_path == path)
return;
const std::string prev_serial = std::move(s_running_game_serial);
s_running_game_path.clear();
s_running_game_serial.clear();
s_running_game_serial = {};
s_running_game_title.clear();
s_running_game_entry = nullptr;
s_running_game_hash = 0;
@ -3207,6 +3336,15 @@ void System::UpdateRunningGame(const char* path, CDImage* image, bool booting)
if (g_settings.auto_load_cheats && !Achievements::ChallengeModeActive())
LoadCheatListFromGameTitle();
if (s_running_game_serial != prev_serial)
UpdateSessionTime(prev_serial);
SaveStateSelectorUI::RefreshList();
#ifdef WITH_DISCORD_PRESENCE
UpdateDiscordPresence(false);
#endif
Host::OnGameChanged(s_running_game_path, s_running_game_serial, s_running_game_title);
}
@ -3541,6 +3679,14 @@ void System::CheckForSettingsChanges(const Settings& old_settings)
g_gpu_device->SetPostProcessingChain({});
}
}
if (g_settings.inhibit_screensaver != old_settings.inhibit_screensaver)
{
if (g_settings.inhibit_screensaver)
PlatformMisc::SuspendScreensaver();
else
PlatformMisc::ResumeScreensaver();
}
}
bool controllers_updated = false;
@ -3566,6 +3712,28 @@ void System::CheckForSettingsChanges(const Settings& old_settings)
if (g_settings.multitap_mode != old_settings.multitap_mode)
UpdateMultitaps();
#ifdef WITH_CHEEVOS
Achievements::UpdateSettings(old_settings);
#endif
FullscreenUI::CheckForConfigChanges(old_settings);
if (g_settings.enable_discord_presence != old_settings.enable_discord_presence)
{
if (g_settings.enable_discord_presence)
InitializeDiscordPresence();
else
ShutdownDiscordPresence();
}
if (g_settings.log_level != old_settings.log_level || g_settings.log_filter != old_settings.log_filter ||
g_settings.log_to_console != old_settings.log_to_console ||
g_settings.log_to_debug != old_settings.log_to_debug || g_settings.log_to_window != old_settings.log_to_window ||
g_settings.log_to_file != old_settings.log_to_file)
{
g_settings.UpdateLogSettings();
}
}
void System::CalculateRewindMemoryUsage(u32 num_saves, u64* ram_usage, u64* vram_usage)
@ -4538,3 +4706,120 @@ void System::SetTimerResolutionIncreased(bool enabled)
timeEndPeriod(1);
#endif
}
void System::UpdateSessionTime(const std::string& prev_serial)
{
const u64 ctime = Common::Timer::GetCurrentValue();
if (!prev_serial.empty())
{
// round up to seconds
const std::time_t etime =
static_cast<std::time_t>(std::round(Common::Timer::ConvertValueToSeconds(ctime - s_session_start_time)));
const std::time_t wtime = std::time(nullptr);
GameList::AddPlayedTimeForSerial(prev_serial, wtime, etime);
}
s_session_start_time = ctime;
}
u64 System::GetSessionPlayedTime()
{
const u64 ctime = Common::Timer::GetCurrentValue();
return static_cast<u64>(std::round(Common::Timer::ConvertValueToSeconds(ctime - s_session_start_time)));
}
#ifdef WITH_DISCORD_PRESENCE
void System::InitializeDiscordPresence()
{
if (s_discord_presence_active)
return;
DiscordEventHandlers handlers = {};
Discord_Initialize("705325712680288296", &handlers, 0, nullptr);
s_discord_presence_active = true;
UpdateDiscordPresence(false);
}
void System::ShutdownDiscordPresence()
{
if (!s_discord_presence_active)
return;
Discord_ClearPresence();
Discord_Shutdown();
s_discord_presence_active = false;
#ifdef WITH_CHEEVOS
s_discord_presence_cheevos_string.clear();
#endif
}
void System::UpdateDiscordPresence(bool rich_presence_only)
{
if (!s_discord_presence_active)
return;
#ifdef WITH_CHEEVOS
// Update only if RetroAchievements rich presence has changed
const std::string& new_rich_presence = Achievements::GetRichPresenceString();
if (new_rich_presence == s_discord_presence_cheevos_string && rich_presence_only)
{
return;
}
s_discord_presence_cheevos_string = new_rich_presence;
#else
if (rich_presence_only)
{
return;
}
#endif
// https://discord.com/developers/docs/rich-presence/how-to#updating-presence-update-presence-payload-fields
DiscordRichPresence rp = {};
rp.largeImageKey = "duckstation_logo";
rp.largeImageText = "DuckStation PS1/PSX Emulator";
rp.startTimestamp = std::time(nullptr);
SmallString details_string;
if (!System::IsShutdown())
{
details_string.AppendFormattedString("%s (%s)", System::GetGameTitle().c_str(), System::GetGameSerial().c_str());
}
else
{
details_string.AppendString("No Game Running");
}
#ifdef WITH_CHEEVOS
SmallString state_string;
// Trim to 128 bytes as per Discord-RPC requirements
if (s_discord_presence_cheevos_string.length() >= 128)
{
// 124 characters + 3 dots + null terminator
state_string = s_discord_presence_cheevos_string.substr(0, 124);
state_string.AppendString("...");
}
else
{
state_string = s_discord_presence_cheevos_string;
}
rp.state = state_string;
#endif
rp.details = details_string;
Discord_UpdatePresence(&rp);
}
void System::PollDiscordPresence()
{
if (!s_discord_presence_active)
return;
UpdateDiscordPresence(true);
Discord_RunCallbacks();
}
#endif

View File

@ -194,6 +194,9 @@ GameHash GetGameHash();
bool IsRunningUnknownGame();
bool WasFastBooted();
/// Returns the time elapsed in the current play session.
u64 GetSessionPlayedTime();
const BIOS::ImageInfo* GetBIOSImageInfo();
const BIOS::Hash& GetBIOSHash();
@ -459,6 +462,18 @@ void UpdateMemorySaveStateSettings();
bool LoadRewindState(u32 skip_saves = 0, bool consume_state = true);
void SetRunaheadReplayFlag();
namespace Internal
{
/// Called on process startup.
void ProcessStartup();
/// Called on process shutdown.
void ProcessShutdown();
/// Polls input, updates subsystems which are present while paused/inactive.
void IdlePollUpdate();
}
} // namespace System
namespace Host {
@ -497,16 +512,4 @@ void RequestResizeHostDisplay(s32 width, s32 height);
/// Requests shut down of the current virtual machine.
void RequestSystemShutdown(bool allow_confirm, bool save_state);
/// Attempts to create the rendering device backend.
bool CreateGPUDevice(RenderAPI api);
/// Handles fullscreen transitions and such.
void UpdateDisplayWindow();
/// Called when the window is resized.
void ResizeDisplayWindow(s32 width, s32 height, float scale);
/// Destroys any active rendering device.
void ReleaseGPUDevice();
} // namespace Host