Settings: Remove second source of truth for OSD notifications

And split it into warnings/messages. Warnings are always displayed
regardless of the "Show OSD Messages" setting, because they're critical
and users forget they disable messages.
This commit is contained in:
Stenzek 2024-09-10 22:10:42 +10:00
parent 2ff6154f74
commit 2ff936e4fb
No known key found for this signature in database
9 changed files with 170 additions and 92 deletions

View File

@ -120,6 +120,7 @@ To download:
You will need a device with armv7 (32-bit ARM), AArch64 (64-bit ARM), or x86_64 (64-bit x86). 64-bit is preferred, the requirements are higher for 32-bit, you'll probably want at least a 1.5GHz CPU.
Download from Google Play: https://play.google.com/store/apps/details?id=com.github.stenzek.duckstation
APK and Beta Downloads: https://www.duckstation.org/android/
**No support is provided for the Android app**, it is free and your expectations should be in line with that. Please **do not** email me about issues about it, or ask for help, you will be ignored.
@ -206,9 +207,9 @@ If you wish to use a "portable" build, where the user directory is the same as w
in the same directory as the DuckStation executable.
## Bindings for Qt frontend
Your keyboard or game controller can be used to simulate a variety of PlayStation controllers. Controller input is supported through DInput, XInput, and SDL backends and can be changed through `Settings -> General Settings`.
Your keyboard or game controller can be used to simulate a variety of PlayStation controllers. Controller input is supported through DInput, XInput, and SDL backends and can be changed through `Settings -> Controllers`.
To bind your input device, go to `Settings -> Controllers`. Each of the buttons/axes for the simulated controller will be listed, alongside the corresponding key/button on your device that it is currently bound to. To rebind, click the box next to the button/axis name, and press the key or button on your input device that you wish to bind to. When binding rumble, simply press any button on the controller you wish to send rumble to.
To bind your input device, go to `Settings -> Controllers`, and select the virtual controller you want to map. Automatic mapping handles the majority of ocntrollers. However, if you need to manually bind a controller, click the box below the button/axis name, and press the key or button on your input device that you wish to bind to.
## SDL Game Controller Database
DuckStation releases ship with a database of game controller mappings for the SDL controller backend, courtesy of https://github.com/mdqinc/SDL_GameControllerDB. The included `gamecontrollerdb.txt` file can be found in the `resources` subdirectory of the DuckStation program directory.
@ -216,6 +217,9 @@ DuckStation releases ship with a database of game controller mappings for the SD
If you are experiencing issues binding your controller with the SDL controller backend, you may need to add a custom mapping to the database file. Make a copy of `gamecontrollerdb.txt` and place it in your [user directory](#user-directories) (or directly in the program directory, if running in portable mode) and then follow the instructions in the [SDL_GameControllerDB repository](https://github.com/mdqinc/SDL_GameControllerDB) for creating a new mapping. Add this mapping to the new copy of `gamecontrollerdb.txt` and your controller should then be recognized properly.
## Default bindings
Bindings for controllers and hotkeys can be changed in `Settings -> Controllers`.
Controller 1:
- **Left Stick:** W/A/S/D
- **Right Stick:** T/F/G/H
@ -229,6 +233,11 @@ Controller 1:
Hotkeys:
- **Escape:** Open Pause Menu
- **F1:** Load State
- **F2:** Save State
- **F3:** Select Previous Save State
- **F4:** Select Next Save State
- **F10:** Save Screenshot
- **F11:** Toggle Fullscreen
- **Tab:** Temporarily Disable Speed Limiter
- **Space:** Pause/Resume Emulation

View File

@ -543,7 +543,7 @@ void GameDatabase::Entry::ApplySettings(Settings& settings, bool display_osd_mes
}
else if (settings.gpu_pgxp_enable && settings.gpu_pgxp_vertex_cache)
{
Host::AddIconOSDMessage(
Host::AddIconOSDWarning(
"gamedb_force_pgxp_vertex_cache", ICON_EMOJI_WARNING,
TRANSLATE_STR(
"GameDatabase",
@ -558,7 +558,7 @@ void GameDatabase::Entry::ApplySettings(Settings& settings, bool display_osd_mes
#ifndef __ANDROID__
APPEND_MESSAGE(TRANSLATE_SV("GameDatabase", "PGXP CPU mode enabled."));
#else
Host::AddIconOSDMessage("gamedb_force_pgxp_cpu", ICON_EMOJI_WARNING,
Host::AddIconOSDWarning("gamedb_force_pgxp_cpu", ICON_EMOJI_WARNING,
"This game requires PGXP CPU mode, which increases system requirements.\n"
" If the game runs too slow, disable PGXP for this game.",
Host::OSD_WARNING_DURATION);
@ -569,7 +569,7 @@ void GameDatabase::Entry::ApplySettings(Settings& settings, bool display_osd_mes
}
else if (settings.UsingPGXPCPUMode())
{
Host::AddIconOSDMessage(
Host::AddIconOSDWarning(
"gamedb_force_pgxp_cpu", ICON_EMOJI_WARNING,
TRANSLATE_STR("GameDatabase",
"PGXP CPU mode is enabled, but it is not required for this game. This may cause rendering errors."),

View File

@ -315,8 +315,7 @@ bool Host::CreateGPUDevice(RenderAPI api, Error* error)
return false;
}
if (!ImGuiManager::Initialize(g_settings.display_osd_scale / 100.0f, g_settings.display_show_osd_messages,
&create_error))
if (!ImGuiManager::Initialize(g_settings.display_osd_scale / 100.0f, &create_error))
{
ERROR_LOG("Failed to initialize ImGuiManager: {}", create_error.GetDescription());
Error::SetStringFmt(error, "Failed to initialize ImGuiManager: {}", create_error.GetDescription());

View File

@ -305,7 +305,7 @@ void Settings::Load(SettingsInterface& si, SettingsInterface& controller_si)
display_active_end_offset = static_cast<s16>(si.GetIntValue("Display", "ActiveEndOffset", 0));
display_line_start_offset = static_cast<s8>(si.GetIntValue("Display", "LineStartOffset", 0));
display_line_end_offset = static_cast<s8>(si.GetIntValue("Display", "LineEndOffset", 0));
display_show_osd_messages = si.GetBoolValue("Display", "ShowOSDMessages", true);
ImGuiManager::SetShowOSDMessages(si.GetBoolValue("Display", "ShowOSDMessages", true));
display_show_fps = si.GetBoolValue("Display", "ShowFPS", false);
display_show_speed = si.GetBoolValue("Display", "ShowSpeed", false);
display_show_gpu_stats = si.GetBoolValue("Display", "ShowGPUStatistics", false);
@ -575,7 +575,7 @@ void Settings::Save(SettingsInterface& si, bool ignore_base) const
si.GetIntValue("Display", "CustomAspectRatioDenominator", display_aspect_ratio_custom_denominator);
if (!ignore_base)
{
si.SetBoolValue("Display", "ShowOSDMessages", display_show_osd_messages);
si.SetBoolValue("Display", "ShowOSDMessages", ImGuiManager::IsShowingOSDMessages());
si.SetBoolValue("Display", "ShowFPS", display_show_fps);
si.SetBoolValue("Display", "ShowSpeed", display_show_speed);
si.SetBoolValue("Display", "ShowResolution", display_show_resolution);

View File

@ -160,7 +160,6 @@ struct Settings
bool display_disable_mailbox_presentation : 1 = true;
bool display_force_4_3_for_24bit : 1 = false;
bool display_24bit_chroma_smoothing : 1 = false;
bool display_show_osd_messages : 1 = true;
bool display_show_fps : 1 = false;
bool display_show_speed : 1 = false;
bool display_show_gpu_stats : 1 = false;

View File

@ -1236,7 +1236,7 @@ void System::HandleHostGPUDeviceLost()
std::memcpy(g_vram, vram_backup.data(), VRAM_SIZE);
// First frame after reopening is definitely going to be trash, so skip it.
Host::AddIconOSDMessage(
Host::AddIconOSDWarning(
"HostGPUDeviceLost", ICON_EMOJI_WARNING,
TRANSLATE_STR("System", "Host GPU device encountered an error and has recovered. This may cause broken rendering."),
Host::OSD_CRITICAL_ERROR_DURATION);
@ -1301,7 +1301,7 @@ LayeredSettingsInterface System::GetControllerSettingsLayers(std::unique_lock<st
// Select input profile _or_ game settings, not both.
if (SettingsInterface* isi = Host::Internal::GetInputSettingsLayer())
{
ret.SetLayer(LayeredSettingsInterface::Layer::LAYER_INPUT, Host::Internal::GetInputSettingsLayer());
ret.SetLayer(LayeredSettingsInterface::Layer::LAYER_INPUT, isi);
}
else if (SettingsInterface* gsi = Host::Internal::GetGameSettingsLayer();
gsi && gsi->GetBoolValue("ControllerPorts", "UseGameSettingsForController", false))
@ -1332,7 +1332,6 @@ void System::SetDefaultSettings(SettingsInterface& si)
Settings temp;
// we don't want to reset some things (e.g. OSD)
temp.display_show_osd_messages = g_settings.display_show_osd_messages;
temp.display_show_fps = g_settings.display_show_fps;
temp.display_show_speed = g_settings.display_show_speed;
temp.display_show_gpu_stats = g_settings.display_show_gpu_stats;
@ -1940,7 +1939,8 @@ void System::DestroySystem()
GDBServer::Shutdown();
#endif
Host::ClearOSDMessages();
// TODO-GPU-THREAD: Needs to be called on GPU thread.
Host::ClearOSDMessages(true);
PostProcessing::Shutdown();
@ -2398,7 +2398,7 @@ bool System::DoState(StateWrapper& sw, GPUTexture** host_texture, bool update_di
{
WARNING_LOG("BIOS hash mismatch: System: {} | State: {}", BIOS::ImageInfo::GetHashString(s_bios_hash),
BIOS::ImageInfo::GetHashString(bios_hash));
Host::AddIconOSDMessage(
Host::AddIconOSDWarning(
"StateBIOSMismatch", ICON_FA_EXCLAMATION_TRIANGLE,
TRANSLATE_STR("System", "This save state was created with a different BIOS. This may cause stability issues."),
Host::OSD_WARNING_DURATION);
@ -3934,7 +3934,7 @@ bool System::InsertMedia(const char* path)
std::unique_ptr<CDImage> image = CDImage::Open(path, g_settings.cdrom_load_image_patches, &error);
if (!image)
{
Host::AddIconOSDMessage(
Host::AddIconOSDWarning(
"DiscInserted", ICON_FA_COMPACT_DISC,
fmt::format(TRANSLATE_FS("OSDMessage", "Failed to open disc image '{}': {}."), path, error.GetDescription()),
Host::OSD_ERROR_DURATION);
@ -4474,8 +4474,6 @@ void System::CheckForSettingsChanges(const Settings& old_settings)
{
if (g_settings.display_osd_scale != old_settings.display_osd_scale)
ImGuiManager::SetGlobalScale(g_settings.display_osd_scale / 100.0f);
if (g_settings.display_show_osd_messages != old_settings.display_show_osd_messages)
ImGuiManager::SetShowOSDMessages(g_settings.display_show_osd_messages);
}
bool controllers_updated = false;
@ -4601,6 +4599,8 @@ void System::WarnAboutUnsafeSettings()
{
append(ICON_EMOJI_WARNING, TRANSLATE_SV("System", "All enhancements are currently disabled."));
if (ImGuiManager::IsShowingOSDMessages())
{
#define APPEND_SUBMESSAGE(msg) \
do \
{ \
@ -4653,6 +4653,7 @@ void System::WarnAboutUnsafeSettings()
#undef APPEND_SUBMESSAGE
}
}
if (!g_settings.apply_compatibility_settings)
{
@ -4666,11 +4667,11 @@ void System::WarnAboutUnsafeSettings()
messages.pop_back();
LogUnsafeSettingsToConsole(messages);
Host::AddKeyedOSDMessage("performance_settings_warning", std::string(messages.view()), Host::OSD_WARNING_DURATION);
Host::AddKeyedOSDWarning("performance_settings_warning", std::string(messages.view()), Host::OSD_WARNING_DURATION);
}
else
{
Host::RemoveKeyedOSDMessage("performance_settings_warning");
Host::RemoveKeyedOSDWarning("performance_settings_warning");
}
}
@ -5217,7 +5218,7 @@ bool System::StartMediaCapture(std::string path, bool capture_video, bool captur
std::string(),
&error))
{
Host::AddIconOSDMessage(
Host::AddIconOSDWarning(
"MediaCapture", ICON_FA_EXCLAMATION_TRIANGLE,
fmt::format(TRANSLATE_FS("System", "Failed to create media capture: {0}"), error.GetDescription()),
Host::OSD_ERROR_DURATION);
@ -5256,7 +5257,7 @@ void System::StopMediaCapture()
}
else
{
Host::AddIconOSDMessage(
Host::AddIconOSDWarning(
"MediaCapture", ICON_FA_EXCLAMATION_TRIANGLE,
fmt::format(TRANSLATE_FS("System", "Stopped {0}: {1}."),
GetCaptureTypeForMessage(s_media_capture->IsCapturingVideo(), s_media_capture->IsCapturingAudio()),

View File

@ -383,7 +383,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget*
dialog->registerWidgetHelp(
m_ui.stretchDisplayVertically, tr("Stretch Vertically"), tr("Unchecked"),
tr("Prefers stretching the display vertically instead of horizontally, when applying the display aspect ratio."));
dialog->registerWidgetHelp(m_ui.stretchDisplayVertically, tr("Automatically Resize Window"), tr("Unchecked"),
dialog->registerWidgetHelp(m_ui.automaticallyResizeWindow, tr("Automatically Resize Window"), tr("Unchecked"),
tr("Automatically resizes the window to match the internal resolution. <strong>For high "
"internal resolutions, this will create very large windows.</strong>"));
#ifdef _WIN32

View File

@ -56,6 +56,7 @@ struct OSDMessage
float duration;
float target_y;
float last_y;
bool is_warning;
};
} // namespace
@ -71,6 +72,9 @@ static bool AddImGuiFonts(bool fullscreen_fonts);
static ImFont* AddTextFont(float size, bool full_glyph_range);
static ImFont* AddFixedFont(float size);
static bool AddIconFonts(float size);
static void AddOSDMessage(std::string key, std::string message, float duration, bool is_warning);
static void RemoveKeyedOSDMessage(std::string key, bool is_warning);
static void ClearOSDMessages(bool clear_warnings);
static void AcquirePendingOSDMessages(Common::Timer::Value current_time);
static void DrawOSDMessages(Common::Timer::Value current_time);
static void CreateSoftwareCursorTextures();
@ -203,6 +207,11 @@ void ImGuiManager::SetGlobalScale(float global_scale)
s_scale_changed = true;
}
bool ImGuiManager::IsShowingOSDMessages()
{
return s_show_osd_messages;
}
void ImGuiManager::SetShowOSDMessages(bool enable)
{
if (s_show_osd_messages == enable)
@ -210,10 +219,10 @@ void ImGuiManager::SetShowOSDMessages(bool enable)
s_show_osd_messages = enable;
if (!enable)
Host::ClearOSDMessages();
Host::ClearOSDMessages(false);
}
bool ImGuiManager::Initialize(float global_scale, bool show_osd_messages, Error* error)
bool ImGuiManager::Initialize(float global_scale, Error* error)
{
if (!LoadFontData())
{
@ -224,7 +233,6 @@ bool ImGuiManager::Initialize(float global_scale, bool show_osd_messages, Error*
s_global_prescale = global_scale;
s_global_scale = std::max(g_gpu_device->GetWindowScale() * global_scale, 1.0f);
s_scale_changed = false;
s_show_osd_messages = show_osd_messages;
ImGui::CreateContext();
@ -743,24 +751,19 @@ bool ImGuiManager::HasFullscreenFonts()
return (s_medium_font && s_large_font);
}
void Host::AddOSDMessage(std::string message, float duration /*= 2.0f*/)
{
AddKeyedOSDMessage(std::string(), std::move(message), duration);
}
void Host::AddKeyedOSDMessage(std::string key, std::string message, float duration /* = 2.0f */)
void ImGuiManager::AddOSDMessage(std::string key, std::string message, float duration, bool is_warning)
{
if (!key.empty())
INFO_LOG("OSD [{}]: {}", key, message);
else
INFO_LOG("OSD: {}", message);
if (!ImGuiManager::s_show_osd_messages)
if (!s_show_osd_messages && !is_warning)
return;
const Common::Timer::Value current_time = Common::Timer::GetCurrentValue();
ImGuiManager::OSDMessage msg;
OSDMessage msg;
msg.key = std::move(key);
msg.text = std::move(message);
msg.duration = duration;
@ -768,37 +771,60 @@ void Host::AddKeyedOSDMessage(std::string key, std::string message, float durati
msg.move_time = current_time;
msg.target_y = -1.0f;
msg.last_y = -1.0f;
msg.is_warning = is_warning;
std::unique_lock<std::mutex> lock(ImGuiManager::s_osd_messages_lock);
ImGuiManager::s_osd_posted_messages.push_back(std::move(msg));
std::unique_lock<std::mutex> lock(s_osd_messages_lock);
s_osd_posted_messages.push_back(std::move(msg));
}
void Host::AddIconOSDMessage(std::string key, const char* icon, std::string message, float duration /* = 2.0f */)
void ImGuiManager::RemoveKeyedOSDMessage(std::string key, bool is_warning)
{
return AddKeyedOSDMessage(std::move(key), fmt::format("{} {}", icon, message), duration);
}
void Host::RemoveKeyedOSDMessage(std::string key)
{
if (!ImGuiManager::s_show_osd_messages)
if (!s_show_osd_messages && !is_warning)
return;
ImGuiManager::OSDMessage msg = {};
msg.key = std::move(key);
msg.duration = 0.0f;
msg.is_warning = is_warning;
std::unique_lock<std::mutex> lock(ImGuiManager::s_osd_messages_lock);
ImGuiManager::s_osd_posted_messages.push_back(std::move(msg));
std::unique_lock<std::mutex> lock(s_osd_messages_lock);
s_osd_posted_messages.push_back(std::move(msg));
}
void Host::ClearOSDMessages()
void ImGuiManager::ClearOSDMessages(bool clear_warnings)
{
{
std::unique_lock<std::mutex> lock(ImGuiManager::s_osd_messages_lock);
ImGuiManager::s_osd_posted_messages.clear();
std::unique_lock<std::mutex> lock(s_osd_messages_lock);
if (clear_warnings)
{
s_osd_posted_messages.clear();
}
else
{
for (auto iter = s_osd_posted_messages.begin(); iter != s_osd_posted_messages.end();)
{
if (!iter->is_warning)
iter = s_osd_posted_messages.erase(iter);
else
++iter;
}
}
}
ImGuiManager::s_osd_active_messages.clear();
if (clear_warnings)
{
s_osd_active_messages.clear();
}
else
{
for (auto iter = s_osd_active_messages.begin(); iter != s_osd_active_messages.end();)
{
if (!iter->is_warning)
s_osd_active_messages.erase(iter);
else
++iter;
}
}
}
void ImGuiManager::AcquirePendingOSDMessages(Common::Timer::Value current_time)
@ -940,6 +966,46 @@ void ImGuiManager::RenderOSDMessages()
DrawOSDMessages(current_time);
}
void Host::AddOSDMessage(std::string message, float duration /*= 2.0f*/)
{
ImGuiManager::AddOSDMessage(std::string(), std::move(message), duration, false);
}
void Host::AddKeyedOSDMessage(std::string key, std::string message, float duration /* = 2.0f */)
{
ImGuiManager::AddOSDMessage(std::move(key), std::move(message), duration, false);
}
void Host::AddIconOSDMessage(std::string key, const char* icon, std::string message, float duration /* = 2.0f */)
{
ImGuiManager::AddOSDMessage(std::move(key), fmt::format("{} {}", icon, message), duration, false);
}
void Host::AddKeyedOSDWarning(std::string key, std::string message, float duration /* = 2.0f */)
{
ImGuiManager::AddOSDMessage(std::move(key), std::move(message), duration, true);
}
void Host::AddIconOSDWarning(std::string key, const char* icon, std::string message, float duration /* = 2.0f */)
{
ImGuiManager::AddOSDMessage(std::move(key), fmt::format("{} {}", icon, message), duration, true);
}
void Host::RemoveKeyedOSDMessage(std::string key)
{
ImGuiManager::RemoveKeyedOSDMessage(std::move(key), false);
}
void Host::RemoveKeyedOSDWarning(std::string key)
{
ImGuiManager::RemoveKeyedOSDMessage(std::move(key), true);
}
void Host::ClearOSDMessages(bool clear_warnings)
{
ImGuiManager::ClearOSDMessages(clear_warnings);
}
float ImGuiManager::GetGlobalScale()
{
return s_global_scale;

View File

@ -33,10 +33,11 @@ std::vector<WCharType> CompactFontRange(std::span<const WCharType> range);
void SetGlobalScale(float global_scale);
/// Changes whether OSD messages are silently dropped.
bool IsShowingOSDMessages();
void SetShowOSDMessages(bool enable);
/// Initializes ImGui, creates fonts, etc.
bool Initialize(float global_scale, bool show_osd_messages, Error* error);
bool Initialize(float global_scale, Error* error);
/// Frees all ImGui resources.
void Shutdown();
@ -137,6 +138,9 @@ static constexpr float OSD_QUICK_DURATION = 2.5f;
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 AddKeyedOSDWarning(std::string key, std::string message, float duration = 2.0f);
void AddIconOSDWarning(std::string key, const char* icon, std::string message, float duration = 2.0f);
void RemoveKeyedOSDMessage(std::string key);
void ClearOSDMessages();
void RemoveKeyedOSDWarning(std::string key);
void ClearOSDMessages(bool clear_warnings);
} // namespace Host