From e23c9875d564154fc5a4654a4f92d415d171e2e8 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sun, 27 Aug 2023 16:00:06 +1000 Subject: [PATCH] Misc: Split core and util Host --- src/core/achievements.cpp | 1 + src/core/analog_controller.cpp | 12 ++- src/core/analog_joystick.cpp | 27 +++--- src/core/cdrom.cpp | 38 +++++---- src/core/dma.cpp | 11 ++- src/core/game_database.cpp | 12 ++- src/core/gpu.cpp | 16 ++-- src/core/gpu_hw.cpp | 22 +++-- src/core/host.cpp | 135 +---------------------------- src/core/host.h | 72 +--------------- src/core/mdec.cpp | 13 ++- src/core/memory_card.cpp | 14 ++- src/core/pad.cpp | 11 ++- src/core/settings.cpp | 1 + src/core/spu.cpp | 13 ++- src/core/system.cpp | 3 + src/core/timers.cpp | 12 ++- src/util/CMakeLists.txt | 2 + src/util/cubeb_audio_stream.cpp | 11 ++- src/util/host.cpp | 147 ++++++++++++++++++++++++++++++++ src/util/host.h | 69 +++++++++++++++ src/util/imgui_manager.cpp | 61 +++++++------ src/util/imgui_manager.h | 26 +++++- src/util/util.vcxproj | 2 + src/util/util.vcxproj.filters | 4 +- 25 files changed, 429 insertions(+), 306 deletions(-) create mode 100644 src/util/host.cpp create mode 100644 src/util/host.h diff --git a/src/core/achievements.cpp b/src/core/achievements.cpp index b478561bc..b3ed52903 100644 --- a/src/core/achievements.cpp +++ b/src/core/achievements.cpp @@ -22,6 +22,7 @@ #include "util/cd_image.h" #include "util/imgui_fullscreen.h" +#include "util/imgui_manager.h" #include "util/platform_misc.h" #include "util/state_wrapper.h" diff --git a/src/core/analog_controller.cpp b/src/core/analog_controller.cpp index eb4591928..26718693a 100644 --- a/src/core/analog_controller.cpp +++ b/src/core/analog_controller.cpp @@ -2,15 +2,21 @@ // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) #include "analog_controller.h" -#include "IconsFontAwesome5.h" -#include "common/log.h" -#include "common/string_util.h" #include "host.h" #include "settings.h" #include "system.h" + +#include "util/imgui_manager.h" #include "util/input_manager.h" #include "util/state_wrapper.h" + +#include "common/log.h" +#include "common/string_util.h" + +#include "IconsFontAwesome5.h" + #include + Log_SetChannel(AnalogController); AnalogController::AnalogController(u32 index) : Controller(index) diff --git a/src/core/analog_joystick.cpp b/src/core/analog_joystick.cpp index 59575638f..85f838a4c 100644 --- a/src/core/analog_joystick.cpp +++ b/src/core/analog_joystick.cpp @@ -2,12 +2,17 @@ // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) #include "analog_joystick.h" -#include "common/log.h" -#include "common/string_util.h" #include "host.h" #include "system.h" + +#include "util/imgui_manager.h" #include "util/state_wrapper.h" + +#include "common/log.h" +#include "common/string_util.h" + #include + Log_SetChannel(AnalogJoystick); AnalogJoystick::AnalogJoystick(u32 index) : Controller(index) @@ -58,9 +63,8 @@ bool AnalogJoystick::DoState(StateWrapper& sw, bool apply_input_state) if (sw.IsReading() && (old_analog_mode != m_analog_mode)) { Host::AddFormattedOSDMessage(5.0f, - m_analog_mode ? - TRANSLATE("AnalogJoystick", "Controller %u switched to analog mode.") : - TRANSLATE("AnalogJoystick", "Controller %u switched to digital mode."), + m_analog_mode ? TRANSLATE("AnalogJoystick", "Controller %u switched to analog mode.") : + TRANSLATE("AnalogJoystick", "Controller %u switched to digital mode."), m_index + 1u); } return true; @@ -234,9 +238,8 @@ void AnalogJoystick::ToggleAnalogMode() Log_InfoPrintf("Joystick %u switched to %s mode.", m_index + 1u, m_analog_mode ? "analog" : "digital"); Host::AddFormattedOSDMessage(5.0f, - m_analog_mode ? - TRANSLATE("AnalogJoystick", "Controller %u switched to analog mode.") : - TRANSLATE("AnalogJoystick", "Controller %u switched to digital mode."), + m_analog_mode ? TRANSLATE("AnalogJoystick", "Controller %u switched to analog mode.") : + TRANSLATE("AnalogJoystick", "Controller %u switched to digital mode."), m_index + 1u); } @@ -370,7 +373,7 @@ static const Controller::ControllerBindingInfo s_binding_info[] = { AXIS("RRight", TRANSLATE_NOOP("AnalogJoystick", "Right Stick Right"), AnalogJoystick::HalfAxis::RRight, GenericInputBinding::RightStickRight), AXIS("RDown", TRANSLATE_NOOP("AnalogJoystick", "Right Stick Down"), AnalogJoystick::HalfAxis::RDown, GenericInputBinding::RightStickDown), AXIS("RUp", TRANSLATE_NOOP("AnalogJoystick", "Right Stick Up"), AnalogJoystick::HalfAxis::RUp, GenericInputBinding::RightStickUp), - // clang-format on +// clang-format on #undef AXIS #undef BUTTON @@ -384,7 +387,7 @@ static const char* s_invert_settings[] = {TRANSLATE_NOOP("AnalogJoystick", "Not static const SettingInfo s_settings[] = { {SettingInfo::Type::Float, "AnalogDeadzone", TRANSLATE_NOOP("AnalogJoystick", "Analog Deadzone"), TRANSLATE_NOOP("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", "%.0f%%", nullptr, 100.0f}, {SettingInfo::Type::Float, "AnalogSensitivity", TRANSLATE_NOOP("AnalogJoystick", "Analog Sensitivity"), TRANSLATE_NOOP( @@ -396,8 +399,8 @@ static const SettingInfo s_settings[] = { TRANSLATE_NOOP("AnalogJoystick", "Inverts the direction of the left analog stick."), "0", "0", "3", nullptr, nullptr, s_invert_settings, 0.0f}, {SettingInfo::Type::IntegerList, "InvertRightStick", TRANSLATE_NOOP("AnalogJoystick", "Invert Right Stick"), - TRANSLATE_NOOP("AnalogJoystick", "Inverts the direction of the right analog stick."), "0", "0", "3", nullptr, nullptr, - s_invert_settings, 0.0f}, + TRANSLATE_NOOP("AnalogJoystick", "Inverts the direction of the right analog stick."), "0", "0", "3", nullptr, + nullptr, s_invert_settings, 0.0f}, }; const Controller::ControllerInfo AnalogJoystick::INFO = {ControllerType::AnalogJoystick, diff --git a/src/core/cdrom.cpp b/src/core/cdrom.cpp index 4573002ee..404f04c00 100644 --- a/src/core/cdrom.cpp +++ b/src/core/cdrom.cpp @@ -3,6 +3,19 @@ #include "cdrom.h" #include "cdrom_async_reader.h" +#include "dma.h" +#include "host.h" +#include "host_interface_progress_callback.h" +#include "interrupt_controller.h" +#include "settings.h" +#include "spu.h" +#include "system.h" + +#include "util/cd_image.h" +#include "util/cd_xa.h" +#include "util/imgui_manager.h" +#include "util/state_wrapper.h" + #include "common/align.h" #include "common/bitfield.h" #include "common/fifo_queue.h" @@ -10,17 +23,9 @@ #include "common/heap_array.h" #include "common/log.h" #include "common/platform.h" -#include "dma.h" -#include "host.h" -#include "host_interface_progress_callback.h" + #include "imgui.h" -#include "interrupt_controller.h" -#include "settings.h" -#include "spu.h" -#include "system.h" -#include "util/cd_image.h" -#include "util/cd_xa.h" -#include "util/state_wrapper.h" + #include #include Log_SetChannel(CDROM); @@ -719,8 +724,8 @@ void CDROM::InsertMedia(std::unique_ptr media, DiscRegion region) if (CanReadMedia()) RemoveMedia(true); - Log_InfoPrintf("Inserting new media, disc region: %s, console region: %s", - Settings::GetDiscRegionName(region), Settings::GetConsoleRegionName(System::GetRegion())); + Log_InfoPrintf("Inserting new media, disc region: %s, console region: %s", Settings::GetDiscRegionName(region), + Settings::GetConsoleRegionName(System::GetRegion())); s_disc_region = region; m_reader.SetMedia(std::move(media)); @@ -779,17 +784,16 @@ bool CDROM::PrecacheMedia() if (m_reader.GetMedia()->HasSubImages() && m_reader.GetMedia()->GetSubImageCount() > 1) { - Host::AddFormattedOSDMessage( - 15.0f, TRANSLATE("OSDMessage", "CD image preloading not available for multi-disc image '%s'"), - FileSystem::GetDisplayNameFromPath(m_reader.GetMedia()->GetFileName()).c_str()); + Host::AddFormattedOSDMessage(15.0f, + TRANSLATE("OSDMessage", "CD image preloading not available for multi-disc image '%s'"), + FileSystem::GetDisplayNameFromPath(m_reader.GetMedia()->GetFileName()).c_str()); return false; } HostInterfaceProgressCallback callback; if (!m_reader.Precache(&callback)) { - Host::AddOSDMessage(TRANSLATE_STR("OSDMessage", "Precaching CD image failed, it may be unreliable."), - 15.0f); + Host::AddOSDMessage(TRANSLATE_STR("OSDMessage", "Precaching CD image failed, it may be unreliable."), 15.0f); return false; } diff --git a/src/core/dma.cpp b/src/core/dma.cpp index 75975b827..26f96fe67 100644 --- a/src/core/dma.cpp +++ b/src/core/dma.cpp @@ -4,9 +4,6 @@ #include "dma.h" #include "bus.h" #include "cdrom.h" -#include "common/bitfield.h" -#include "common/log.h" -#include "common/string_util.h" #include "cpu_code_cache.h" #include "cpu_core.h" #include "gpu.h" @@ -17,10 +14,18 @@ #include "pad.h" #include "spu.h" #include "system.h" + +#include "util/imgui_manager.h" #include "util/state_wrapper.h" + +#include "common/bitfield.h" +#include "common/log.h" +#include "common/string_util.h" + #include #include #include + Log_SetChannel(DMA); namespace DMA { diff --git a/src/core/game_database.cpp b/src/core/game_database.cpp index f8bec8692..4e3d34c54 100644 --- a/src/core/game_database.cpp +++ b/src/core/game_database.cpp @@ -2,6 +2,12 @@ // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) #include "game_database.h" +#include "host.h" +#include "system.h" + +#include "util/cd_image.h" +#include "util/imgui_manager.h" + #include "common/assert.h" #include "common/byte_stream.h" #include "common/heterogeneous_containers.h" @@ -9,15 +15,15 @@ #include "common/path.h" #include "common/string_util.h" #include "common/timer.h" -#include "host.h" + #include "rapidjson/document.h" #include "rapidjson/error/en.h" -#include "system.h" -#include "util/cd_image.h" + #include #include #include #include + Log_SetChannel(GameDatabase); #ifdef _WIN32 diff --git a/src/core/gpu.cpp b/src/core/gpu.cpp index 6887d1234..cca17fdf0 100644 --- a/src/core/gpu.cpp +++ b/src/core/gpu.cpp @@ -2,21 +2,27 @@ // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) #include "gpu.h" -#include "common/file_system.h" -#include "common/heap_array.h" -#include "common/log.h" -#include "common/string_util.h" #include "dma.h" #include "host.h" #include "imgui.h" #include "interrupt_controller.h" #include "settings.h" -#include "stb_image_write.h" #include "system.h" #include "timers.h" + #include "util/gpu_device.h" +#include "util/imgui_manager.h" #include "util/state_wrapper.h" + +#include "common/file_system.h" +#include "common/heap_array.h" +#include "common/log.h" +#include "common/string_util.h" + +#include "stb_image_write.h" + #include + Log_SetChannel(GPU); std::unique_ptr g_gpu; diff --git a/src/core/gpu_hw.cpp b/src/core/gpu_hw.cpp index 13722a16f..ec479c217 100644 --- a/src/core/gpu_hw.cpp +++ b/src/core/gpu_hw.cpp @@ -2,23 +2,29 @@ // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) #include "gpu_hw.h" +#include "cpu_core.h" +#include "gpu_hw_shadergen.h" +#include "gpu_sw_backend.h" +#include "host.h" +#include "pgxp.h" +#include "settings.h" +#include "system.h" + +#include "util/imgui_manager.h" +#include "util/state_wrapper.h" + #include "common/align.h" #include "common/assert.h" #include "common/log.h" #include "common/scoped_guard.h" #include "common/string_util.h" -#include "cpu_core.h" -#include "gpu_hw_shadergen.h" -#include "gpu_sw_backend.h" -#include "host.h" + #include "imgui.h" -#include "pgxp.h" -#include "settings.h" -#include "system.h" -#include "util/state_wrapper.h" + #include #include #include + Log_SetChannel(GPU_HW); // TODO: instead of full state restore, only restore what changed diff --git a/src/core/host.cpp b/src/core/host.cpp index c9f21b291..e41a14226 100644 --- a/src/core/host.cpp +++ b/src/core/host.cpp @@ -12,30 +12,17 @@ #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" #include -#include Log_SetChannel(Host); namespace Host { -static std::pair 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>; -using TranslationStringContextMap = UnorderedStringMap; -static std::shared_mutex s_translation_string_mutex; -static TranslationStringContextMap s_translation_string_map; -static std::vector s_translation_string_cache; -static u32 s_translation_string_cache_pos; } // namespace Host std::unique_lock Host::GetSettingsLock() @@ -228,126 +215,6 @@ void Host::Internal::SetInputSettingsLayer(SettingsInterface* sif) s_layered_settings_interface.SetLayer(LayeredSettingsInterface::LAYER_INPUT, sif); } -std::pair Host::LookupTranslationString(const std::string_view& context, const std::string_view& msg) -{ - // TODO: TranslatableString, compile-time hashing. - - TranslationStringContextMap::iterator ctx_it; - TranslationStringMap::iterator msg_it; - std::pair ret; - s32 len; - - // Shouldn't happen, but just in case someone tries to translate an empty string. - if (UNLIKELY(msg.empty())) - { - ret.first = &s_translation_string_cache[0]; - ret.second = 0; - return ret; - } - - s_translation_string_mutex.lock_shared(); - ctx_it = UnorderedStringMapFind(s_translation_string_map, context); - - if (UNLIKELY(ctx_it == s_translation_string_map.end())) - goto add_string; - - msg_it = UnorderedStringMapFind(ctx_it->second, msg); - if (UNLIKELY(msg_it == ctx_it->second.end())) - goto add_string; - - ret.first = &s_translation_string_cache[msg_it->second.first]; - ret.second = msg_it->second.second; - s_translation_string_mutex.unlock_shared(); - return ret; - -add_string: - s_translation_string_mutex.unlock_shared(); - s_translation_string_mutex.lock(); - - if (UNLIKELY(s_translation_string_cache.empty())) - { - // First element is always an empty string. - s_translation_string_cache.resize(TRANSLATION_STRING_CACHE_SIZE); - s_translation_string_cache[0] = '\0'; - s_translation_string_cache_pos = 0; - } - - if ((len = - Internal::GetTranslatedStringImpl(context, msg, &s_translation_string_cache[s_translation_string_cache_pos], - TRANSLATION_STRING_CACHE_SIZE - 1 - s_translation_string_cache_pos)) < 0) - { - Log_ErrorPrint("WARNING: Clearing translation string cache, it might need to be larger."); - s_translation_string_cache_pos = 0; - if ((len = - Internal::GetTranslatedStringImpl(context, msg, &s_translation_string_cache[s_translation_string_cache_pos], - TRANSLATION_STRING_CACHE_SIZE - 1 - s_translation_string_cache_pos)) < 0) - { - Panic("Failed to get translated string after clearing cache."); - len = 0; - } - } - - // New context? - if (ctx_it == s_translation_string_map.end()) - ctx_it = s_translation_string_map.emplace(context, TranslationStringMap()).first; - - // Impl doesn't null terminate, we need that for C strings. - // TODO: do we want to consider aligning the buffer? - const u32 insert_pos = s_translation_string_cache_pos; - s_translation_string_cache[insert_pos + static_cast(len)] = 0; - - ctx_it->second.emplace(msg, std::pair(insert_pos, static_cast(len))); - s_translation_string_cache_pos = insert_pos + static_cast(len) + 1; - - ret.first = &s_translation_string_cache[insert_pos]; - ret.second = static_cast(len); - s_translation_string_mutex.unlock(); - return ret; -} - -const char* Host::TranslateToCString(const std::string_view& context, const std::string_view& msg) -{ - return LookupTranslationString(context, msg).first; -} - -std::string_view Host::TranslateToStringView(const std::string_view& context, const std::string_view& msg) -{ - const auto mp = LookupTranslationString(context, msg); - return std::string_view(mp.first, mp.second); -} - -std::string Host::TranslateToString(const std::string_view& context, const std::string_view& msg) -{ - return std::string(TranslateToStringView(context, msg)); -} - -void Host::ClearTranslationCache() -{ - s_translation_string_mutex.lock(); - s_translation_string_map.clear(); - s_translation_string_cache_pos = 0; - s_translation_string_mutex.unlock(); -} - -void Host::ReportFormattedErrorAsync(const std::string_view& title, const char* format, ...) -{ - std::va_list ap; - va_start(ap, format); - std::string message(StringUtil::StdStringFromFormatV(format, ap)); - va_end(ap); - ReportErrorAsync(title, message); -} - -bool Host::ConfirmFormattedMessage(const std::string_view& title, const char* format, ...) -{ - std::va_list ap; - va_start(ap, format); - std::string message = StringUtil::StdStringFromFormatV(format, ap); - va_end(ap); - - return ConfirmMessage(title, message); -} - void Host::ReportFormattedDebuggerMessage(const char* format, ...) { std::va_list ap; @@ -380,7 +247,7 @@ bool Host::CreateGPUDevice(RenderAPI api) return false; } - if (!ImGuiManager::Initialize()) + if (!ImGuiManager::Initialize(g_settings.display_osd_scale / 100.0f)) { Log_ErrorPrintf("Failed to initialize ImGuiManager."); g_gpu_device->Destroy(); diff --git a/src/core/host.h b/src/core/host.h index 8408e3298..e432d6072 100644 --- a/src/core/host.h +++ b/src/core/host.h @@ -3,6 +3,8 @@ #pragma once +#include "util/host.h" + #include "common/string.h" #include "common/types.h" @@ -24,23 +26,6 @@ class AudioStream; class CDImage; namespace Host { -/// Typical durations for OSD messages. -static constexpr float OSD_CRITICAL_ERROR_DURATION = 20.0f; -static constexpr float OSD_ERROR_DURATION = 15.0f; -static constexpr float OSD_WARNING_DURATION = 10.0f; -static constexpr float OSD_INFO_DURATION = 5.0f; -static constexpr float OSD_QUICK_DURATION = 2.5f; - -/// Reads a file from the resources directory of the application. -/// This may be outside of the "normal" filesystem on platforms such as Mac. -std::optional> ReadResourceFile(const char* filename); - -/// Reads a resource file file from the resources directory as a string. -std::optional ReadResourceFileToString(const char* filename); - -/// Returns the modified time of a resource. -std::optional 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); @@ -81,45 +66,11 @@ SettingsInterface* GetSettingsInterface(); /// 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); -/// Returns a localized version of the specified string within the specified context. -/// The view is guaranteed to be valid until the next language change. -/// NOTE: When passing this to fmt, positional arguments should be used in the base string, as -/// not all locales follow the same word ordering. -std::string_view TranslateToStringView(const std::string_view& context, const std::string_view& msg); - -/// Returns a localized version of the specified string within the specified context. -std::string TranslateToString(const std::string_view& context, const std::string_view& msg); - -/// Clears the translation cache. All previously used strings should be considered invalid. -void ClearTranslationCache(); std::unique_ptr CreateAudioStream(AudioBackend backend, u32 sample_rate, u32 channels, u32 buffer_ms, u32 latency_ms, AudioStretchMode stretch); -/// Returns the scale of OSD elements. -float GetOSDScale(); - -/// Adds OSD messages, duration is in seconds. -void AddOSDMessage(std::string message, float duration = 2.0f); -void AddKeyedOSDMessage(std::string key, std::string message, float duration = 2.0f); -void AddIconOSDMessage(std::string key, const char* icon, std::string message, float duration = 2.0f); -void AddFormattedOSDMessage(float duration, const char* format, ...); -void AddKeyedFormattedOSDMessage(std::string key, float duration, const char* format, ...); -void RemoveKeyedOSDMessage(std::string key); -void ClearOSDMessages(); - -/// Displays an asynchronous error on the UI thread, i.e. doesn't block the caller. -void ReportErrorAsync(const std::string_view& title, const std::string_view& message); -void ReportFormattedErrorAsync(const std::string_view& title, const char* format, ...); - -/// Displays a synchronous confirmation on the UI thread, i.e. blocks the caller. -bool ConfirmMessage(const std::string_view& title, const std::string_view& message); -bool ConfirmFormattedMessage(const std::string_view& title, const char* format, ...); - /// Debugger feedback. void ReportDebuggerMessage(const std::string_view& message); void ReportFormattedDebuggerMessage(const char* format, ...); @@ -134,12 +85,6 @@ void SetMouseMode(bool relative, bool hide_cursor); /// Safely executes a function on the VM thread. void RunOnCPUThread(std::function function, bool block = false); -/// Opens a URL, using the default application. -void OpenURL(const std::string_view& url); - -/// Copies the provided text to the host's clipboard, if present. -bool CopyTextToClipboard(const std::string_view& text); - /// Requests shut down and exit of the hosting application. This may not actually exit, /// if the user cancels the shutdown confirmation. void RequestExit(bool allow_confirm); @@ -181,18 +126,5 @@ 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); } // namespace Internal } // namespace Host - -// Helper macros for retrieving translated strings. -#define TRANSLATE(context, msg) Host::TranslateToCString(context, msg) -#define TRANSLATE_SV(context, msg) Host::TranslateToStringView(context, msg) -#define TRANSLATE_STR(context, msg) Host::TranslateToString(context, msg) -#define TRANSLATE_FS(context, msg) fmt::runtime(Host::TranslateToStringView(context, msg)) - -// Does not translate the string at runtime, but allows the UI to in its own way. -#define TRANSLATE_NOOP(context, msg) msg diff --git a/src/core/mdec.cpp b/src/core/mdec.cpp index 8fc133442..b3ff4f541 100644 --- a/src/core/mdec.cpp +++ b/src/core/mdec.cpp @@ -2,16 +2,21 @@ // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) #include "mdec.h" -#include "common/bitfield.h" -#include "common/fifo_queue.h" -#include "common/log.h" #include "cpu_core.h" #include "dma.h" #include "host.h" -#include "imgui.h" #include "interrupt_controller.h" #include "system.h" + +#include "util/imgui_manager.h" #include "util/state_wrapper.h" + +#include "common/bitfield.h" +#include "common/fifo_queue.h" +#include "common/log.h" + +#include "imgui.h" + #include #include diff --git a/src/core/memory_card.cpp b/src/core/memory_card.cpp index 28aeda4b2..4caf8f87f 100644 --- a/src/core/memory_card.cpp +++ b/src/core/memory_card.cpp @@ -2,16 +2,22 @@ // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) #include "memory_card.h" -#include "IconsFontAwesome5.h" +#include "host.h" +#include "system.h" + +#include "util/imgui_manager.h" +#include "util/state_wrapper.h" + #include "common/byte_stream.h" #include "common/file_system.h" #include "common/log.h" #include "common/path.h" #include "common/string_util.h" -#include "host.h" -#include "system.h" -#include "util/state_wrapper.h" + +#include "IconsFontAwesome5.h" + #include + Log_SetChannel(MemoryCard); MemoryCard::MemoryCard() diff --git a/src/core/pad.cpp b/src/core/pad.cpp index ad324b0e9..c302e84d3 100644 --- a/src/core/pad.cpp +++ b/src/core/pad.cpp @@ -2,9 +2,6 @@ // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) #include "pad.h" -#include "common/bitfield.h" -#include "common/fifo_queue.h" -#include "common/log.h" #include "controller.h" #include "host.h" #include "interrupt_controller.h" @@ -13,9 +10,17 @@ #include "save_state_version.h" #include "system.h" #include "types.h" + +#include "util/imgui_manager.h" #include "util/state_wrapper.h" + +#include "common/bitfield.h" +#include "common/fifo_queue.h" +#include "common/log.h" + #include #include + Log_SetChannel(Pad); namespace Pad { diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 93ce49c49..66431b07b 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -8,6 +8,7 @@ #include "system.h" #include "util/gpu_device.h" +#include "util/imgui_manager.h" #include "util/input_manager.h" #include "common/assert.h" diff --git a/src/core/spu.cpp b/src/core/spu.cpp index 31dd26b28..766b79aca 100644 --- a/src/core/spu.cpp +++ b/src/core/spu.cpp @@ -3,19 +3,24 @@ #include "spu.h" #include "cdrom.h" -#include "common/bitfield.h" -#include "common/fifo_queue.h" -#include "common/log.h" -#include "common/path.h" #include "dma.h" #include "host.h" #include "imgui.h" #include "interrupt_controller.h" #include "system.h" + #include "util/audio_stream.h" +#include "util/imgui_manager.h" #include "util/state_wrapper.h" #include "util/wav_writer.h" + +#include "common/bitfield.h" +#include "common/fifo_queue.h" +#include "common/log.h" +#include "common/path.h" + #include + Log_SetChannel(SPU); // Enable to dump all voices of the SPU audio individually. diff --git a/src/core/system.cpp b/src/core/system.cpp index 2946197c1..c9dd51a42 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -3684,6 +3684,9 @@ void System::CheckForSettingsChanges(const Settings& old_settings) } } + if (g_gpu_device && g_settings.display_osd_scale != old_settings.display_osd_scale) + ImGuiManager::SetGlobalScale(g_settings.display_osd_scale / 100.0f); + bool controllers_updated = false; for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++) { diff --git a/src/core/timers.cpp b/src/core/timers.cpp index f55ef47f2..6289f2283 100644 --- a/src/core/timers.cpp +++ b/src/core/timers.cpp @@ -2,16 +2,22 @@ // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) #include "timers.h" -#include "common/bitfield.h" -#include "common/log.h" #include "gpu.h" #include "host.h" -#include "imgui.h" #include "interrupt_controller.h" #include "system.h" + +#include "util/imgui_manager.h" #include "util/state_wrapper.h" + +#include "common/bitfield.h" +#include "common/log.h" + +#include "imgui.h" + #include #include + Log_SetChannel(Timers); namespace Timers { diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index f81ebd1ef..73a640747 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -27,6 +27,8 @@ add_library(util gpu_shader_cache.h gpu_texture.cpp gpu_texture.h + host.cpp + host.h imgui_fullscreen.cpp imgui_fullscreen.h imgui_manager.cpp diff --git a/src/util/cubeb_audio_stream.cpp b/src/util/cubeb_audio_stream.cpp index bb8082165..6a3de873f 100644 --- a/src/util/cubeb_audio_stream.cpp +++ b/src/util/cubeb_audio_stream.cpp @@ -2,21 +2,26 @@ // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) #include "cubeb_audio_stream.h" +#include "host.h" +#include "imgui_manager.h" + +#include "core/settings.h" + #include "common/assert.h" #include "common/log.h" #include "common/scoped_guard.h" #include "common/string_util.h" -#include "core/host.h" -#include "core/settings.h" + #include "cubeb/cubeb.h" #include "fmt/format.h" -Log_SetChannel(CubebAudioStream); #ifdef _WIN32 #include "common/windows_headers.h" #include #endif +Log_SetChannel(CubebAudioStream); + static void StateCallback(cubeb_stream* stream, void* user_ptr, cubeb_state state); CubebAudioStream::CubebAudioStream(u32 sample_rate, u32 channels, u32 buffer_ms, AudioStretchMode stretch) diff --git a/src/util/host.cpp b/src/util/host.cpp new file mode 100644 index 000000000..2f49439cd --- /dev/null +++ b/src/util/host.cpp @@ -0,0 +1,147 @@ +// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin +// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) + +#include "host.h" + +#include "common/assert.h" +#include "common/heterogeneous_containers.h" +#include "common/log.h" +#include "common/string_util.h" + +#include +#include + +Log_SetChannel(Host); + +namespace Host { +static std::pair LookupTranslationString(const std::string_view& context, + const std::string_view& msg); + +static constexpr u32 TRANSLATION_STRING_CACHE_SIZE = 4 * 1024 * 1024; +using TranslationStringMap = UnorderedStringMap>; +using TranslationStringContextMap = UnorderedStringMap; +static std::shared_mutex s_translation_string_mutex; +static TranslationStringContextMap s_translation_string_map; +static std::vector s_translation_string_cache; +static u32 s_translation_string_cache_pos; +} // namespace Host + +std::pair Host::LookupTranslationString(const std::string_view& context, const std::string_view& msg) +{ + // TODO: TranslatableString, compile-time hashing. + + TranslationStringContextMap::iterator ctx_it; + TranslationStringMap::iterator msg_it; + std::pair ret; + s32 len; + + // Shouldn't happen, but just in case someone tries to translate an empty string. + if (UNLIKELY(msg.empty())) + { + ret.first = &s_translation_string_cache[0]; + ret.second = 0; + return ret; + } + + s_translation_string_mutex.lock_shared(); + ctx_it = UnorderedStringMapFind(s_translation_string_map, context); + + if (UNLIKELY(ctx_it == s_translation_string_map.end())) + goto add_string; + + msg_it = UnorderedStringMapFind(ctx_it->second, msg); + if (UNLIKELY(msg_it == ctx_it->second.end())) + goto add_string; + + ret.first = &s_translation_string_cache[msg_it->second.first]; + ret.second = msg_it->second.second; + s_translation_string_mutex.unlock_shared(); + return ret; + +add_string: + s_translation_string_mutex.unlock_shared(); + s_translation_string_mutex.lock(); + + if (UNLIKELY(s_translation_string_cache.empty())) + { + // First element is always an empty string. + s_translation_string_cache.resize(TRANSLATION_STRING_CACHE_SIZE); + s_translation_string_cache[0] = '\0'; + s_translation_string_cache_pos = 0; + } + + if ((len = + Internal::GetTranslatedStringImpl(context, msg, &s_translation_string_cache[s_translation_string_cache_pos], + TRANSLATION_STRING_CACHE_SIZE - 1 - s_translation_string_cache_pos)) < 0) + { + Log_ErrorPrint("WARNING: Clearing translation string cache, it might need to be larger."); + s_translation_string_cache_pos = 0; + if ((len = + Internal::GetTranslatedStringImpl(context, msg, &s_translation_string_cache[s_translation_string_cache_pos], + TRANSLATION_STRING_CACHE_SIZE - 1 - s_translation_string_cache_pos)) < 0) + { + Panic("Failed to get translated string after clearing cache."); + len = 0; + } + } + + // New context? + if (ctx_it == s_translation_string_map.end()) + ctx_it = s_translation_string_map.emplace(context, TranslationStringMap()).first; + + // Impl doesn't null terminate, we need that for C strings. + // TODO: do we want to consider aligning the buffer? + const u32 insert_pos = s_translation_string_cache_pos; + s_translation_string_cache[insert_pos + static_cast(len)] = 0; + + ctx_it->second.emplace(msg, std::pair(insert_pos, static_cast(len))); + s_translation_string_cache_pos = insert_pos + static_cast(len) + 1; + + ret.first = &s_translation_string_cache[insert_pos]; + ret.second = static_cast(len); + s_translation_string_mutex.unlock(); + return ret; +} + +const char* Host::TranslateToCString(const std::string_view& context, const std::string_view& msg) +{ + return LookupTranslationString(context, msg).first; +} + +std::string_view Host::TranslateToStringView(const std::string_view& context, const std::string_view& msg) +{ + const auto mp = LookupTranslationString(context, msg); + return std::string_view(mp.first, mp.second); +} + +std::string Host::TranslateToString(const std::string_view& context, const std::string_view& msg) +{ + return std::string(TranslateToStringView(context, msg)); +} + +void Host::ClearTranslationCache() +{ + s_translation_string_mutex.lock(); + s_translation_string_map.clear(); + s_translation_string_cache_pos = 0; + s_translation_string_mutex.unlock(); +} + +void Host::ReportFormattedErrorAsync(const std::string_view& title, const char* format, ...) +{ + std::va_list ap; + va_start(ap, format); + std::string message(StringUtil::StdStringFromFormatV(format, ap)); + va_end(ap); + ReportErrorAsync(title, message); +} + +bool Host::ConfirmFormattedMessage(const std::string_view& title, const char* format, ...) +{ + std::va_list ap; + va_start(ap, format); + std::string message = StringUtil::StdStringFromFormatV(format, ap); + va_end(ap); + + return ConfirmMessage(title, message); +} diff --git a/src/util/host.h b/src/util/host.h new file mode 100644 index 000000000..4d79ecf57 --- /dev/null +++ b/src/util/host.h @@ -0,0 +1,69 @@ +// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin +// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) + +#pragma once + +#include "common/types.h" + +#include +#include +#include +#include +#include + +namespace Host { +/// Reads a file from the resources directory of the application. +/// This may be outside of the "normal" filesystem on platforms such as Mac. +std::optional> ReadResourceFile(const char* filename); + +/// Reads a resource file file from the resources directory as a string. +std::optional ReadResourceFileToString(const char* filename); + +/// Returns the modified time of a resource. +std::optional GetResourceFileTimestamp(const char* filename); + +/// Displays an asynchronous error on the UI thread, i.e. doesn't block the caller. +void ReportErrorAsync(const std::string_view& title, const std::string_view& message); +void ReportFormattedErrorAsync(const std::string_view& title, const char* format, ...); + +/// Displays a synchronous confirmation on the UI thread, i.e. blocks the caller. +bool ConfirmMessage(const std::string_view& title, const std::string_view& message); +bool ConfirmFormattedMessage(const std::string_view& title, const char* format, ...); + +/// Opens a URL, using the default application. +void OpenURL(const std::string_view& url); + +/// Copies the provided text to the host's clipboard, if present. +bool CopyTextToClipboard(const std::string_view& text); + +/// 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); + +/// Returns a localized version of the specified string within the specified context. +/// The view is guaranteed to be valid until the next language change. +/// NOTE: When passing this to fmt, positional arguments should be used in the base string, as +/// not all locales follow the same word ordering. +std::string_view TranslateToStringView(const std::string_view& context, const std::string_view& msg); + +/// Returns a localized version of the specified string within the specified context. +std::string TranslateToString(const std::string_view& context, const std::string_view& msg); + +/// Clears the translation cache. All previously used strings should be considered invalid. +void ClearTranslationCache(); + +namespace Internal { +/// Implementation to retrieve a translated string. +s32 GetTranslatedStringImpl(const std::string_view& context, const std::string_view& msg, char* tbuf, + size_t tbuf_space); +} // namespace Internal +} // namespace Host + +// Helper macros for retrieving translated strings. +#define TRANSLATE(context, msg) Host::TranslateToCString(context, msg) +#define TRANSLATE_SV(context, msg) Host::TranslateToStringView(context, msg) +#define TRANSLATE_STR(context, msg) Host::TranslateToString(context, msg) +#define TRANSLATE_FS(context, msg) fmt::runtime(Host::TranslateToStringView(context, msg)) + +// Does not translate the string at runtime, but allows the UI to in its own way. +#define TRANSLATE_NOOP(context, msg) msg diff --git a/src/util/imgui_manager.cpp b/src/util/imgui_manager.cpp index f539ec864..67f43375f 100644 --- a/src/util/imgui_manager.cpp +++ b/src/util/imgui_manager.cpp @@ -1,21 +1,23 @@ -// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin +// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) #include "imgui_manager.h" -#include "IconsFontAwesome5.h" +#include "gpu_device.h" +#include "host.h" +#include "imgui_fullscreen.h" +#include "input_manager.h" + #include "common/assert.h" #include "common/file_system.h" #include "common/log.h" #include "common/string_util.h" #include "common/timer.h" -#include "gpu_device.h" -#include "core/host.h" -#include "core/system.h" + +#include "IconsFontAwesome5.h" #include "fmt/format.h" #include "imgui.h" -#include "imgui_fullscreen.h" #include "imgui_internal.h" -#include "input_manager.h" + #include #include #include @@ -37,6 +39,7 @@ static void AcquirePendingOSDMessages(); static void DrawOSDMessages(); } // namespace ImGuiManager +static float s_global_prescale = 1.0f; // before window scale static float s_global_scale = 1.0f; static std::string s_font_path; @@ -72,7 +75,13 @@ void ImGuiManager::SetFontRange(const u16* range) s_standard_font_data = {}; } -bool ImGuiManager::Initialize() +void ImGuiManager::SetGlobalScale(float global_scale) +{ + s_global_prescale = global_scale; + UpdateScale(); +} + +bool ImGuiManager::Initialize(float global_scale) { if (!LoadFontData()) { @@ -80,8 +89,8 @@ bool ImGuiManager::Initialize() return false; } - s_global_scale = - std::max(g_gpu_device->GetWindowScale() * static_cast(g_settings.display_osd_scale / 100.0f), 1.0f); + s_global_prescale = global_scale; + s_global_scale = std::max(g_gpu_device->GetWindowScale() * global_scale, 1.0f); ImGui::CreateContext(); @@ -105,7 +114,6 @@ bool ImGuiManager::Initialize() SetKeyMap(); SetStyle(); - if (!AddImGuiFonts(false) || !g_gpu_device->UpdateImGuiFontTexture()) { Panic("Failed to create ImGui font text"); @@ -149,7 +157,7 @@ void ImGuiManager::WindowResized() void ImGuiManager::UpdateScale() { const float window_scale = g_gpu_device ? g_gpu_device->GetWindowScale() : 1.0f; - const float scale = std::max(window_scale * static_cast(g_settings.display_osd_scale / 100.0f), 1.0f); + const float scale = std::max(window_scale * s_global_prescale, 1.0f); if (scale == s_global_scale && (!HasFullscreenFonts() || !ImGuiFullscreen::UpdateLayoutScale())) return; @@ -623,23 +631,20 @@ void ImGuiManager::AcquirePendingOSDMessages() if (s_osd_posted_messages.empty()) break; - if (g_settings.display_show_osd_messages) + OSDMessage& new_msg = s_osd_posted_messages.front(); + std::deque::iterator iter; + if (!new_msg.key.empty() && (iter = std::find_if(s_osd_active_messages.begin(), s_osd_active_messages.end(), + [&new_msg](const OSDMessage& other) { + return new_msg.key == other.key; + })) != s_osd_active_messages.end()) { - OSDMessage& new_msg = s_osd_posted_messages.front(); - std::deque::iterator iter; - if (!new_msg.key.empty() && (iter = std::find_if(s_osd_active_messages.begin(), s_osd_active_messages.end(), - [&new_msg](const OSDMessage& other) { - return new_msg.key == other.key; - })) != s_osd_active_messages.end()) - { - iter->text = std::move(new_msg.text); - iter->duration = new_msg.duration; - iter->time = new_msg.time; - } - else - { - s_osd_active_messages.push_back(std::move(new_msg)); - } + iter->text = std::move(new_msg.text); + iter->duration = new_msg.duration; + iter->time = new_msg.time; + } + else + { + s_osd_active_messages.push_back(std::move(new_msg)); } s_osd_posted_messages.pop_front(); diff --git a/src/util/imgui_manager.h b/src/util/imgui_manager.h index e5f01fc21..b5ec12dcb 100644 --- a/src/util/imgui_manager.h +++ b/src/util/imgui_manager.h @@ -18,8 +18,11 @@ void SetFontPath(std::string path); /// Sets the glyph range to use when loading fonts. void SetFontRange(const u16* range); +/// Changes the global scale. +void SetGlobalScale(float global_scale); + /// Initializes ImGui, creates fonts, etc. -bool Initialize(); +bool Initialize(float global_scale); /// Frees all ImGui resources. void Shutdown(); @@ -83,3 +86,24 @@ bool ProcessHostKeyEvent(InputBindingKey key, float value); /// Called on the CPU thread when any input event fires. Allows imgui to take over controller navigation. bool ProcessGenericInputEvent(GenericInputBinding key, float value); } // namespace ImGuiManager + +namespace Host { +/// Typical durations for OSD messages. +static constexpr float OSD_CRITICAL_ERROR_DURATION = 20.0f; +static constexpr float OSD_ERROR_DURATION = 15.0f; +static constexpr float OSD_WARNING_DURATION = 10.0f; +static constexpr float OSD_INFO_DURATION = 5.0f; +static constexpr float OSD_QUICK_DURATION = 2.5f; + +/// Returns the scale of OSD elements. +float GetOSDScale(); + +/// Adds OSD messages, duration is in seconds. +void AddOSDMessage(std::string message, float duration = 2.0f); +void AddKeyedOSDMessage(std::string key, std::string message, float duration = 2.0f); +void AddIconOSDMessage(std::string key, const char* icon, std::string message, float duration = 2.0f); +void AddFormattedOSDMessage(float duration, const char* format, ...); +void AddKeyedFormattedOSDMessage(std::string key, float duration, const char* format, ...); +void RemoveKeyedOSDMessage(std::string key); +void ClearOSDMessages(); +} // namespace Host diff --git a/src/util/util.vcxproj b/src/util/util.vcxproj index 81e8be4a8..b7248eb1b 100644 --- a/src/util/util.vcxproj +++ b/src/util/util.vcxproj @@ -28,6 +28,7 @@ + @@ -139,6 +140,7 @@ + diff --git a/src/util/util.vcxproj.filters b/src/util/util.vcxproj.filters index 7a76f2d01..1a4021dc3 100644 --- a/src/util/util.vcxproj.filters +++ b/src/util/util.vcxproj.filters @@ -68,6 +68,7 @@ + @@ -142,10 +143,11 @@ + {e637fc5b-2483-4a31-abc3-89a16d45c223} - + \ No newline at end of file