From 6810a9d593185758a51bc02ee376494fe4629a98 Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Wed, 14 Sep 2022 22:41:50 +1000 Subject: [PATCH] Qt: Deduplicate interface code and reduce core coupling This enables more code sharing between interfaces in the future (e.g. nogui, gsrunner, etc). Eventually I'll move everything in Frontend/ to its own project. - VMManager now no longer depends on anything in Frontend. - Moved INISettingsInterface out of Frontend. - Log settings can now be overridden per-game (if you really want to).. - Hotkeys get their own file. --- pcsx2-qt/MainWindow.cpp | 13 - pcsx2-qt/MainWindow.h | 1 - pcsx2-qt/QtHost.cpp | 108 ++++--- pcsx2-qt/QtHost.h | 5 +- .../Settings/ControllerSettingsDialog.cpp | 2 +- pcsx2-qt/Settings/DEV9SettingsWidget.cpp | 2 +- pcsx2-qt/Settings/SettingsDialog.cpp | 4 +- pcsx2/CMakeLists.txt | 5 +- pcsx2/Frontend/CommonHost.cpp | 111 +++++++- pcsx2/Frontend/CommonHost.h | 41 +++ pcsx2/Frontend/CommonHotkeys.cpp | 240 ++++++++++++++++ pcsx2/Frontend/FullscreenUI.cpp | 4 +- pcsx2/Frontend/GameList.cpp | 12 +- pcsx2/Frontend/GameList.h | 2 - pcsx2/Frontend/InputManager.cpp | 2 +- pcsx2/Frontend/InputManager.h | 2 +- pcsx2/Frontend/LogSink.cpp | 28 +- pcsx2/Frontend/LogSink.h | 7 +- pcsx2/{Frontend => }/INISettingsInterface.cpp | 0 pcsx2/{Frontend => }/INISettingsInterface.h | 0 pcsx2/VMManager.cpp | 268 ++---------------- pcsx2/VMManager.h | 15 +- pcsx2/pcsx2core.vcxproj | 7 +- pcsx2/pcsx2core.vcxproj.filters | 7 +- 24 files changed, 525 insertions(+), 361 deletions(-) create mode 100644 pcsx2/Frontend/CommonHotkeys.cpp rename pcsx2/{Frontend => }/INISettingsInterface.cpp (100%) rename pcsx2/{Frontend => }/INISettingsInterface.h (100%) diff --git a/pcsx2-qt/MainWindow.cpp b/pcsx2-qt/MainWindow.cpp index 73b0f2e617..01bcdf655e 100644 --- a/pcsx2-qt/MainWindow.cpp +++ b/pcsx2-qt/MainWindow.cpp @@ -309,23 +309,17 @@ void MainWindow::connectSignals() SettingWidgetBinder::BindWidgetToBoolSetting(nullptr, m_ui.actionViewStatusBarVerbose, "UI", "VerboseStatusBar", false); SettingWidgetBinder::BindWidgetToBoolSetting(nullptr, m_ui.actionEnableSystemConsole, "Logging", "EnableSystemConsole", false); - connect(m_ui.actionEnableSystemConsole, &QAction::triggered, this, &MainWindow::onLoggingOptionChanged); #ifndef PCSX2_DEVBUILD SettingWidgetBinder::BindWidgetToBoolSetting(nullptr, m_ui.actionEnableVerboseLogging, "Logging", "EnableVerbose", false); - connect(m_ui.actionEnableVerboseLogging, &QAction::triggered, this, &MainWindow::onLoggingOptionChanged); #else // Dev builds always have verbose logging. m_ui.actionEnableVerboseLogging->setChecked(true); m_ui.actionEnableVerboseLogging->setEnabled(false); #endif SettingWidgetBinder::BindWidgetToBoolSetting(nullptr, m_ui.actionEnableEEConsoleLogging, "Logging", "EnableEEConsole", true); - connect(m_ui.actionEnableEEConsoleLogging, &QAction::triggered, this, &MainWindow::onLoggingOptionChanged); SettingWidgetBinder::BindWidgetToBoolSetting(nullptr, m_ui.actionEnableIOPConsoleLogging, "Logging", "EnableIOPConsole", true); - connect(m_ui.actionEnableIOPConsoleLogging, &QAction::triggered, this, &MainWindow::onLoggingOptionChanged); SettingWidgetBinder::BindWidgetToBoolSetting(nullptr, m_ui.actionEnableFileLogging, "Logging", "EnableFileLogging", false); - connect(m_ui.actionEnableFileLogging, &QAction::triggered, this, &MainWindow::onLoggingOptionChanged); SettingWidgetBinder::BindWidgetToBoolSetting(nullptr, m_ui.actionEnableLogTimestamps, "Logging", "EnableTimestamps", true); - connect(m_ui.actionEnableLogTimestamps, &QAction::triggered, this, &MainWindow::onLoggingOptionChanged); SettingWidgetBinder::BindWidgetToBoolSetting(nullptr, m_ui.actionEnableCDVDVerboseReads, "EmuCore", "CdvdVerboseReads", false); SettingWidgetBinder::BindWidgetToBoolSetting(nullptr, m_ui.actionSaveBlockDump, "EmuCore", "CdvdDumpBlocks", false); connect(m_ui.actionSaveBlockDump, &QAction::toggled, this, &MainWindow::onBlockDumpActionToggled); @@ -337,9 +331,7 @@ void MainWindow::connectSignals() connect(m_ui.actionInputRecPlay, &QAction::triggered, this, &MainWindow::onInputRecPlayActionTriggered); connect(m_ui.actionInputRecStop, &QAction::triggered, this, &MainWindow::onInputRecStopActionTriggered); SettingWidgetBinder::BindWidgetToBoolSetting(nullptr, m_ui.actionInputRecConsoleLogs, "Logging", "EnableInputRecordingLogs", false); - connect(m_ui.actionInputRecConsoleLogs, &QAction::triggered, this, &MainWindow::onLoggingOptionChanged); SettingWidgetBinder::BindWidgetToBoolSetting(nullptr, m_ui.actionInputRecControllerLogs, "Logging", "EnableControllerLogs", false); - connect(m_ui.actionInputRecControllerLogs, &QAction::triggered, this, &MainWindow::onLoggingOptionChanged); // These need to be queued connections to stop crashing due to menus opening/closing and switching focus. connect(m_game_list_widget, &GameListWidget::refreshProgress, this, &MainWindow::onGameListRefreshProgress); @@ -1509,11 +1501,6 @@ void MainWindow::updateTheme() m_game_list_widget->refreshImages(); } -void MainWindow::onLoggingOptionChanged() -{ - Host::UpdateLogging(QtHost::InNoGUIMode()); -} - void MainWindow::onInputRecNewActionTriggered() { const bool wasPaused = s_vm_paused; diff --git a/pcsx2-qt/MainWindow.h b/pcsx2-qt/MainWindow.h index 60cd29dfe3..86abe09052 100644 --- a/pcsx2-qt/MainWindow.h +++ b/pcsx2-qt/MainWindow.h @@ -149,7 +149,6 @@ private Q_SLOTS: void onCheckForUpdatesActionTriggered(); void onToolsOpenDataDirectoryTriggered(); void updateTheme(); - void onLoggingOptionChanged(); void onScreenshotActionTriggered(); void onSaveGSDumpActionTriggered(); void onBlockDumpActionToggled(bool checked); diff --git a/pcsx2-qt/QtHost.cpp b/pcsx2-qt/QtHost.cpp index 1621bc98fa..1aab833656 100644 --- a/pcsx2-qt/QtHost.cpp +++ b/pcsx2-qt/QtHost.cpp @@ -37,13 +37,13 @@ #include "pcsx2/Frontend/GameList.h" #include "pcsx2/Frontend/InputManager.h" #include "pcsx2/Frontend/ImGuiManager.h" -#include "pcsx2/Frontend/INISettingsInterface.h" #include "pcsx2/Frontend/LogSink.h" #include "pcsx2/GS.h" #include "pcsx2/GS/GS.h" #include "pcsx2/GSDumpReplayer.h" #include "pcsx2/HostDisplay.h" #include "pcsx2/HostSettings.h" +#include "pcsx2/INISettingsInterface.h" #include "pcsx2/PAD/Host/PAD.h" #include "pcsx2/PerformanceMetrics.h" #include "pcsx2/VMManager.h" @@ -230,14 +230,11 @@ void EmuThread::startVM(std::shared_ptr boot_params) pxAssertRel(!VMManager::HasValidVM(), "VM is shut down"); - // Only initialize fullscreen/render-to-main when we're not running big picture. - if (!m_run_fullscreen_ui) - loadOurInitialSettings(); - + // Determine whether to start fullscreen or not. if (boot_params->fullscreen.has_value()) m_is_fullscreen = boot_params->fullscreen.value(); - - emit onVMStarting(); + else + m_is_fullscreen = Host::GetBaseBoolSettingValue("UI", "StartFullscreen", false); if (!VMManager::Initialize(*boot_params)) return; @@ -359,26 +356,19 @@ void EmuThread::saveStateToSlot(qint32 slot) void EmuThread::run() { - Threading::SetNameOfCurrentThread("EmuThread"); - PerformanceMetrics::SetCPUThread(Threading::ThreadHandle::GetForCallingThread()); + // Qt-specific initialization. m_event_loop = new QEventLoop(); m_started_semaphore.release(); + connectSignals(); - // neither of these should ever fail. - if (!VMManager::Internal::InitializeGlobals() || !VMManager::Internal::InitializeMemory()) - pxFailRel("Failed to allocate memory map"); - - // We want settings loaded so we choose the correct renderer for big picture mode. - // This also sorts out input sources. - loadOurSettings(); - loadOurInitialSettings(); - VMManager::LoadSettings(); + // Common host initialization (VM setup, etc). + CommonHost::CPUThreadInitialize(); // Start background polling because the VM won't do it for us. createBackgroundControllerPollTimer(); startBackgroundControllerPollTimer(); - connectSignals(); + // Main CPU thread loop. while (!m_shutdown_flag.load()) { if (!VMManager::HasValidVM()) @@ -390,13 +380,12 @@ void EmuThread::run() executeVM(); } + // Teardown in reverse order. stopBackgroundControllerPollTimer(); destroyBackgroundControllerPollTimer(); - InputManager::CloseSources(); - VMManager::WaitForSaveStateFlush(); - VMManager::Internal::ReleaseMemory(); - VMManager::Internal::ReleaseGlobals(); - PerformanceMetrics::SetCPUThread(Threading::ThreadHandle()); + CommonHost::CPUThreadShutdown(); + + // Move back to the UI thread, since we're no longer running. moveToThread(m_ui_thread); deleteLater(); } @@ -410,6 +399,7 @@ void EmuThread::destroyVM() m_last_internal_height = 0; m_was_paused_by_focus_loss = false; VMManager::Shutdown(m_save_state_on_shutdown); + m_save_state_on_shutdown = false; } void EmuThread::executeVM() @@ -540,7 +530,6 @@ void EmuThread::applySettings() return; } - checkForSettingChanges(); VMManager::ApplySettings(); } @@ -553,11 +542,7 @@ void EmuThread::reloadGameSettings() } // this will skip applying settings when they're not active - if (VMManager::ReloadGameSettings()) - { - // none of these settings below are per-game.. for now. but in case they are in the future. - checkForSettingChanges(); - } + VMManager::ReloadGameSettings(); } void EmuThread::updateEmuFolders() @@ -571,26 +556,24 @@ void EmuThread::updateEmuFolders() Host::Internal::UpdateEmuFolders(); } -void EmuThread::loadOurSettings() -{ - m_verbose_status = Host::GetBaseBoolSettingValue("UI", "VerboseStatusBar", false); - m_pause_on_focus_loss = Host::GetBaseBoolSettingValue("UI", "PauseOnFocusLoss", false); -} - void EmuThread::connectSignals() { connect(qApp, &QGuiApplication::applicationStateChanged, this, &EmuThread::onApplicationStateChanged); } -void EmuThread::loadOurInitialSettings() +void EmuThread::loadSettings(SettingsInterface& si, std::unique_lock& lock) { - m_is_fullscreen = Host::GetBaseBoolSettingValue("UI", "StartFullscreen", false); - m_is_rendering_to_main = shouldRenderToMain(); - m_is_surfaceless = false; - m_save_state_on_shutdown = false; + m_verbose_status = si.GetBoolValue("UI", "VerboseStatusBar", false); + m_pause_on_focus_loss = si.GetBoolValue("UI", "PauseOnFocusLoss", false); } -void EmuThread::checkForSettingChanges() +void Host::LoadSettings(SettingsInterface& si, std::unique_lock& lock) +{ + CommonHost::LoadSettings(si, lock); + g_emu_thread->loadSettings(si, lock); +} + +void EmuThread::checkForSettingChanges(const Pcsx2Config& old_config) { QMetaObject::invokeMethod(g_main_window, &MainWindow::checkForSettingChanges, Qt::QueuedConnection); @@ -605,12 +588,13 @@ void EmuThread::checkForSettingChanges() } } - const bool last_verbose_status = m_verbose_status; + updatePerformanceMetrics(true); +} - loadOurSettings(); - - if (m_verbose_status != last_verbose_status) - updatePerformanceMetrics(true); +void Host::CheckForSettingsChanges(const Pcsx2Config& old_config) +{ + CommonHost::CheckForSettingsChanges(old_config); + g_emu_thread->checkForSettingChanges(old_config); } bool EmuThread::shouldRenderToMain() const @@ -855,6 +839,8 @@ void EmuThread::updateDisplay() bool EmuThread::acquireHostDisplay(HostDisplay::RenderAPI api) { pxAssertRel(!g_host_display, "Host display does not exist on create"); + m_is_rendering_to_main = shouldRenderToMain(); + m_is_surfaceless = false; g_host_display = HostDisplay::CreateDisplayForAPI(api); if (!g_host_display) @@ -967,29 +953,35 @@ void Host::UpdateHostDisplay() void Host::OnVMStarting() { + CommonHost::OnVMStarting(); g_emu_thread->stopBackgroundControllerPollTimer(); emit g_emu_thread->onVMStarting(); } void Host::OnVMStarted() { + CommonHost::OnVMStarted(); emit g_emu_thread->onVMStarted(); } void Host::OnVMDestroyed() { + CommonHost::OnVMDestroyed(); emit g_emu_thread->onVMStopped(); g_emu_thread->startBackgroundControllerPollTimer(); } void Host::OnVMPaused() { + CommonHost::OnVMPaused(); g_emu_thread->startBackgroundControllerPollTimer(); emit g_emu_thread->onVMPaused(); } void Host::OnVMResumed() { + CommonHost::OnVMResumed(); + // exit the event loop when we eventually return g_emu_thread->getEventLoop()->quit(); g_emu_thread->stopBackgroundControllerPollTimer(); @@ -1001,11 +993,10 @@ void Host::OnVMResumed() emit g_emu_thread->onVMResumed(); } -void Host::OnGameChanged(const std::string& disc_path, const std::string& game_serial, const std::string& game_name, - u32 game_crc) +void Host::OnGameChanged(const std::string& disc_path, const std::string& game_serial, const std::string& game_name, u32 game_crc) { - emit g_emu_thread->onGameChanged(QString::fromStdString(disc_path), QString::fromStdString(game_serial), - QString::fromStdString(game_name), game_crc); + CommonHost::OnGameChanged(disc_path, game_serial, game_name, game_crc); + emit g_emu_thread->onGameChanged(QString::fromStdString(disc_path), QString::fromStdString(game_serial), QString::fromStdString(game_name), game_crc); } void EmuThread::updatePerformanceMetrics(bool force) @@ -1117,9 +1108,10 @@ void Host::OnSaveStateSaved(const std::string_view& filename) emit g_emu_thread->onSaveStateSaved(QtUtils::StringViewToQString(filename)); } -void Host::PumpMessagesOnCPUThread() +void Host::CPUThreadVSync() { g_emu_thread->getEventLoop()->processEvents(QEventLoop::AllEvents); + CommonHost::CPUThreadVSync(); } void Host::RunOnCPUThread(std::function function, bool block /* = false */) @@ -1211,15 +1203,13 @@ bool QtHost::InitializeConfig() SaveSettings(); } + CommonHost::SetBlockSystemConsole(QtHost::InNoGUIMode()); CommonHost::LoadStartupSettings(); - Host::UpdateLogging(QtHost::InNoGUIMode()); return true; } void Host::SetDefaultUISettings(SettingsInterface& si) { - Host::SetDefaultLoggingSettings(si); - si.SetBoolValue("UI", "InhibitScreensaver", true); si.SetBoolValue("UI", "ConfirmShutdown", true); si.SetBoolValue("UI", "StartPaused", false); @@ -1472,7 +1462,7 @@ void QtHost::HookSignals() void QtHost::PrintCommandLineVersion() { - Host::InitializeEarlyConsole(); + CommonHost::InitializeEarlyConsole(); std::fprintf(stderr, "%s\n", (GetAppNameAndVersion() + GetAppConfigSuffix()).toUtf8().constData()); std::fprintf(stderr, "https://pcsx2.net/\n"); std::fprintf(stderr, "\n"); @@ -1592,7 +1582,7 @@ bool QtHost::ParseCommandLineOptions(int argc, char* argv[], std::shared_ptrsource_type.has_value() && autoboot->filename.empty() && autoboot->elf_override.empty()) { - Host::InitializeEarlyConsole(); + CommonHost::InitializeEarlyConsole(); Console.Warning("Skipping autoboot due to no boot parameters."); autoboot.reset(); } diff --git a/pcsx2-qt/QtHost.h b/pcsx2-qt/QtHost.h index 3e6c428a0e..511f4c3ee2 100644 --- a/pcsx2-qt/QtHost.h +++ b/pcsx2-qt/QtHost.h @@ -80,6 +80,8 @@ public: public Q_SLOTS: bool confirmMessage(const QString& title, const QString& message); + void loadSettings(SettingsInterface& si, std::unique_lock& lock); + void checkForSettingChanges(const Pcsx2Config& old_config); void startFullscreenUI(bool fullscreen); void stopFullscreenUI(); void startVM(std::shared_ptr boot_params); @@ -161,14 +163,11 @@ private: void destroyVM(); void executeVM(); - void checkForSettingChanges(); bool shouldRenderToMain() const; void createBackgroundControllerPollTimer(); void destroyBackgroundControllerPollTimer(); void connectSignals(); - void loadOurSettings(); - void loadOurInitialSettings(); private Q_SLOTS: void stopInThread(); diff --git a/pcsx2-qt/Settings/ControllerSettingsDialog.cpp b/pcsx2-qt/Settings/ControllerSettingsDialog.cpp index bef8d19e56..edb33401ec 100644 --- a/pcsx2-qt/Settings/ControllerSettingsDialog.cpp +++ b/pcsx2-qt/Settings/ControllerSettingsDialog.cpp @@ -22,7 +22,7 @@ #include "Settings/HotkeySettingsWidget.h" #include "pcsx2/Frontend/CommonHost.h" -#include "pcsx2/Frontend/INISettingsInterface.h" +#include "pcsx2/INISettingsInterface.h" #include "pcsx2/PAD/Host/PAD.h" #include "pcsx2/Sio.h" #include "pcsx2/VMManager.h" diff --git a/pcsx2-qt/Settings/DEV9SettingsWidget.cpp b/pcsx2-qt/Settings/DEV9SettingsWidget.cpp index f0334f01f8..7626a3970a 100644 --- a/pcsx2-qt/Settings/DEV9SettingsWidget.cpp +++ b/pcsx2-qt/Settings/DEV9SettingsWidget.cpp @@ -24,13 +24,13 @@ #include "common/StringUtil.h" #include "pcsx2/HostSettings.h" +#include "pcsx2/INISettingsInterface.h" #include "DEV9SettingsWidget.h" #include "QtHost.h" #include "QtUtils.h" #include "SettingWidgetBinder.h" #include "SettingsDialog.h" -#include "Frontend/INISettingsInterface.h" #include "HddCreateQt.h" diff --git a/pcsx2-qt/Settings/SettingsDialog.cpp b/pcsx2-qt/Settings/SettingsDialog.cpp index 4d823dafb1..3880db9fe9 100644 --- a/pcsx2-qt/Settings/SettingsDialog.cpp +++ b/pcsx2-qt/Settings/SettingsDialog.cpp @@ -19,9 +19,9 @@ #include "common/Path.h" #include "common/StringUtil.h" -#include "pcsx2/HostSettings.h" #include "pcsx2/Frontend/GameList.h" -#include "pcsx2/Frontend/INISettingsInterface.h" +#include "pcsx2/HostSettings.h" +#include "pcsx2/INISettingsInterface.h" #include "MainWindow.h" #include "QtHost.h" diff --git a/pcsx2/CMakeLists.txt b/pcsx2/CMakeLists.txt index 5fb705ff21..3050a20b43 100644 --- a/pcsx2/CMakeLists.txt +++ b/pcsx2/CMakeLists.txt @@ -1064,16 +1064,17 @@ endif() if(PCSX2_CORE) list(APPEND pcsx2FrontendSources Frontend/CommonHost.cpp + Frontend/CommonHotkeys.cpp Frontend/FullscreenUI.cpp Frontend/GameList.cpp Frontend/HostSettings.cpp Frontend/ImGuiFullscreen.cpp - Frontend/INISettingsInterface.cpp Frontend/InputManager.cpp Frontend/InputSource.cpp Frontend/LayeredSettingsInterface.cpp Frontend/LogSink.cpp GSDumpReplayer.cpp + INISettingsInterface.cpp VMManager.cpp ) list(APPEND pcsx2FrontendHeaders @@ -1081,13 +1082,13 @@ if(PCSX2_CORE) Frontend/FullscreenUI.h Frontend/GameList.h Frontend/ImGuiFullscreen.h - Frontend/INISettingsInterface.h Frontend/InputManager.h Frontend/InputSource.h Frontend/LayeredSettingsInterface.h Frontend/LogSink.h GSDumpReplayer.h HostSettings.h + INISettingsInterface.h VMManager.h) endif() diff --git a/pcsx2/Frontend/CommonHost.cpp b/pcsx2/Frontend/CommonHost.cpp index 3048dd7022..647901963f 100644 --- a/pcsx2/Frontend/CommonHost.cpp +++ b/pcsx2/Frontend/CommonHost.cpp @@ -18,14 +18,21 @@ #include "common/CrashHandler.h" #include "common/FileSystem.h" #include "common/Path.h" +#include "common/Threading.h" #include "Frontend/CommonHost.h" +#include "Frontend/FullscreenUI.h" +#include "Frontend/GameList.h" #include "Frontend/LayeredSettingsInterface.h" +#include "Frontend/InputManager.h" +#include "Frontend/LogSink.h" #include "GS.h" #include "GS/Renderers/HW/GSTextureReplacements.h" #include "Host.h" #include "HostSettings.h" +#include "IconsFontAwesome5.h" #include "MemoryCardFile.h" #include "PAD/Host/PAD.h" +#include "PerformanceMetrics.h" #include "Sio.h" #include "VMManager.h" @@ -175,6 +182,7 @@ void CommonHost::LoadStartupSettings() SettingsInterface* bsi = Host::Internal::GetBaseSettingsLayer(); EmuFolders::LoadConfig(*bsi); EmuFolders::EnsureFoldersExist(); + UpdateLogging(*bsi); } void CommonHost::SetDefaultSettings(SettingsInterface& si, bool folders, bool core, bool controllers, bool hotkeys, bool ui) @@ -199,5 +207,106 @@ void CommonHost::SetDefaultSettings(SettingsInterface& si, bool folders, bool co void CommonHost::SetCommonDefaultSettings(SettingsInterface& si) { - // Nothing here yet. + SetDefaultLoggingSettings(si); +} + +void CommonHost::CPUThreadInitialize() +{ + Threading::SetNameOfCurrentThread("CPU Thread"); + PerformanceMetrics::SetCPUThread(Threading::ThreadHandle::GetForCallingThread()); + + // neither of these should ever fail. + if (!VMManager::Internal::InitializeGlobals() || !VMManager::Internal::InitializeMemory()) + pxFailRel("Failed to allocate memory map"); + + // We want settings loaded so we choose the correct renderer for big picture mode. + // This also sorts out input sources. + VMManager::LoadSettings(); +} + +void CommonHost::CPUThreadShutdown() +{ + InputManager::CloseSources(); + VMManager::WaitForSaveStateFlush(); + VMManager::Internal::ReleaseMemory(); + VMManager::Internal::ReleaseGlobals(); + PerformanceMetrics::SetCPUThread(Threading::ThreadHandle()); +} + +void CommonHost::LoadSettings(SettingsInterface& si, std::unique_lock& lock) +{ + SettingsInterface* binding_si = Host::GetSettingsInterfaceForBindings(); + InputManager::ReloadSources(si, lock); + InputManager::ReloadBindings(si, *binding_si); + UpdateLogging(si); +} + +void CommonHost::CheckForSettingsChanges(const Pcsx2Config& old_config) +{ + // Nothing yet. +} + +void CommonHost::OnVMStarting() +{ + CommonHost::Internal::ResetVMHotkeyState(); +} + +void CommonHost::OnVMStarted() +{ + FullscreenUI::OnVMStarted(); +} + +void CommonHost::OnVMDestroyed() +{ + FullscreenUI::OnVMDestroyed(); +} + +void CommonHost::OnVMPaused() +{ + InputManager::PauseVibration(); + FullscreenUI::OnVMPaused(); +} + +void CommonHost::OnVMResumed() +{ + FullscreenUI::OnVMResumed(); +} + +void CommonHost::OnGameChanged(const std::string& disc_path, const std::string& game_serial, const std::string& game_name, u32 game_crc) +{ + if (FullscreenUI::IsInitialized()) + { + GetMTGS().RunOnGSThread([disc_path, game_serial, game_name, game_crc]() { + FullscreenUI::OnRunningGameChanged(std::move(disc_path), std::move(game_serial), std::move(game_name), game_crc); + }); + } +} + +void CommonHost::CPUThreadVSync() +{ + InputManager::PollSources(); +} + +bool Host::GetSerialAndCRCForFilename(const char* filename, std::string* serial, u32* crc) +{ + { + auto lock = GameList::GetLock(); + if (const GameList::Entry* entry = GameList::GetEntryForPath(filename); entry) + { + *serial = entry->serial; + *crc = entry->crc; + return true; + } + } + + // Just scan it.. hopefully it'll come back okay. + GameList::Entry temp_entry; + if (GameList::PopulateEntryFromPath(filename, &temp_entry)) + { + *serial = std::move(temp_entry.serial); + *crc = temp_entry.crc; + return true; + } + + return false; } diff --git a/pcsx2/Frontend/CommonHost.h b/pcsx2/Frontend/CommonHost.h index 5e6442f9ff..c5c3dea3c6 100644 --- a/pcsx2/Frontend/CommonHost.h +++ b/pcsx2/Frontend/CommonHost.h @@ -19,6 +19,8 @@ #include #include +#include "Config.h" + class SettingsInterface; namespace Host @@ -40,4 +42,43 @@ namespace CommonHost /// Sets default settings for the specified categories. void SetDefaultSettings(SettingsInterface& si, bool folders, bool core, bool controllers, bool hotkeys, bool ui); + + /// Initializes common host state, called on the CPU thread. + void CPUThreadInitialize(); + + /// Cleans up common host state, called on the CPU thread. + void CPUThreadShutdown(); + + /// Loads common host settings (including input bindings). + void LoadSettings(SettingsInterface& si, std::unique_lock& lock); + + /// Called after settings are updated. + void CheckForSettingsChanges(const Pcsx2Config& old_config); + + /// Called when the VM is starting initialization, but has not been completed yet. + void OnVMStarting(); + + /// Called when the VM is created. + void OnVMStarted(); + + /// Called when the VM is shut down or destroyed. + void OnVMDestroyed(); + + /// Called when the VM is paused. + void OnVMPaused(); + + /// Called when the VM is resumed after being paused. + void OnVMResumed(); + + /// Called when the running executable changes. + void OnGameChanged(const std::string& disc_path, const std::string& game_serial, const std::string& game_name, u32 game_crc); + + /// Provided by the host; called once per frame at guest vsync. + void CPUThreadVSync(); + + namespace Internal + { + /// Resets any state for hotkey-related VMs, called on VM startup. + void ResetVMHotkeyState(); + } // namespace Internal } // namespace CommonHost diff --git a/pcsx2/Frontend/CommonHotkeys.cpp b/pcsx2/Frontend/CommonHotkeys.cpp new file mode 100644 index 0000000000..5afb162da6 --- /dev/null +++ b/pcsx2/Frontend/CommonHotkeys.cpp @@ -0,0 +1,240 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2022 PCSX2 Dev Team + * + * PCSX2 is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with PCSX2. + * If not, see . + */ + +#include "PrecompiledHeader.h" +#include "common/Assertions.h" +#include "common/FileSystem.h" +#include "common/Path.h" +#include "Frontend/CommonHost.h" +#include "Frontend/FullscreenUI.h" +#include "Frontend/InputManager.h" +#include "GS.h" +#include "Host.h" +#include "IconsFontAwesome5.h" +#include "Recording/InputRecordingControls.h" +#include "VMManager.h" + +static s32 s_current_save_slot = 1; +static std::optional s_limiter_mode_prior_to_hold_interaction; + +void CommonHost::Internal::ResetVMHotkeyState() +{ + s_current_save_slot = 1; + s_limiter_mode_prior_to_hold_interaction.reset(); +} + +static void HotkeyAdjustTargetSpeed(double delta) +{ + EmuConfig.Framerate.NominalScalar = EmuConfig.GS.LimitScalar + delta; + VMManager::SetLimiterMode(LimiterModeType::Nominal); + gsUpdateFrequency(EmuConfig); + GetMTGS().SetVSync(EmuConfig.GetEffectiveVsyncMode()); + Host::AddIconOSDMessage("SpeedChanged", ICON_FA_CLOCK, + fmt::format("Target speed set to {:.0f}%.", std::round(EmuConfig.Framerate.NominalScalar * 100.0)), 5.0f); +} + +static constexpr s32 CYCLE_SAVE_STATE_SLOTS = 10; + +static void HotkeyCycleSaveSlot(s32 delta) +{ + // 1..10 + s_current_save_slot = ((s_current_save_slot - 1) + delta); + if (s_current_save_slot < 0) + s_current_save_slot = CYCLE_SAVE_STATE_SLOTS; + else + s_current_save_slot = (s_current_save_slot % CYCLE_SAVE_STATE_SLOTS) + 1; + + const u32 crc = VMManager::GetGameCRC(); + const std::string serial(VMManager::GetGameSerial()); + const std::string filename(VMManager::GetSaveStateFileName(serial.c_str(), crc, s_current_save_slot)); + FILESYSTEM_STAT_DATA sd; + if (!filename.empty() && FileSystem::StatFile(filename.c_str(), &sd)) + { + char date_buf[128] = {}; +#ifdef _WIN32 + ctime_s(date_buf, std::size(date_buf), &sd.ModificationTime); +#else + ctime_r(&sd.ModificationTime, date_buf); +#endif + + // remove terminating \n + size_t len = std::strlen(date_buf); + if (len > 0 && date_buf[len - 1] == '\n') + date_buf[len - 1] = 0; + + Host::AddIconOSDMessage( + "CycleSaveSlot", ICON_FA_SEARCH, fmt::format("Save slot {} selected (last save: {}).", s_current_save_slot, date_buf), 5.0f); + } + else + { + Host::AddIconOSDMessage( + "CycleSaveSlot", ICON_FA_SEARCH, fmt::format("Save slot {} selected (no save yet).", s_current_save_slot), 5.0f); + } +} + +static void HotkeyLoadStateSlot(s32 slot) +{ + const u32 crc = VMManager::GetGameCRC(); + if (crc == 0) + { + Host::AddIconOSDMessage( + "LoadStateFromSlot", ICON_FA_EXCLAMATION_TRIANGLE, "Cannot load state from a slot without a game running.", 10.0f); + return; + } + + const std::string serial(VMManager::GetGameSerial()); + if (!VMManager::HasSaveStateInSlot(serial.c_str(), crc, slot)) + { + Host::AddIconOSDMessage("LoadStateFromSlot", ICON_FA_EXCLAMATION_TRIANGLE, fmt::format("No save state found in slot {}.", slot)); + return; + } + + VMManager::LoadStateFromSlot(slot); +} + +static void HotkeySaveStateSlot(s32 slot) +{ + if (VMManager::GetGameCRC() == 0) + { + Host::AddIconOSDMessage( + "SaveStateToSlot", ICON_FA_EXCLAMATION_TRIANGLE, "Cannot save state to a slot without a game running.", 10.0f); + return; + } + + VMManager::SaveStateToSlot(slot); +} + +BEGIN_HOTKEY_LIST(g_common_hotkeys) +DEFINE_HOTKEY("OpenPauseMenu", "System", "Open Pause Menu", [](s32 pressed) { + if (!pressed && VMManager::HasValidVM()) + FullscreenUI::OpenPauseMenu(); +}) +DEFINE_HOTKEY("TogglePause", "System", "Toggle Pause", [](s32 pressed) { + if (!pressed && VMManager::HasValidVM()) + VMManager::SetPaused(VMManager::GetState() != VMState::Paused); +}) +DEFINE_HOTKEY("ToggleFullscreen", "System", "Toggle Fullscreen", [](s32 pressed) { + if (!pressed) + Host::SetFullscreen(!Host::IsFullscreen()); +}) +DEFINE_HOTKEY("ToggleFrameLimit", "System", "Toggle Frame Limit", [](s32 pressed) { + if (!pressed && VMManager::HasValidVM()) + { + VMManager::SetLimiterMode( + (EmuConfig.LimiterMode != LimiterModeType::Unlimited) ? LimiterModeType::Unlimited : LimiterModeType::Nominal); + } +}) +DEFINE_HOTKEY("ToggleTurbo", "System", "Toggle Turbo", [](s32 pressed) { + if (!pressed && VMManager::HasValidVM()) + { + VMManager::SetLimiterMode((EmuConfig.LimiterMode != LimiterModeType::Turbo) ? LimiterModeType::Turbo : LimiterModeType::Nominal); + } +}) +DEFINE_HOTKEY("ToggleSlowMotion", "System", "Toggle Slow Motion", [](s32 pressed) { + if (!pressed && VMManager::HasValidVM()) + { + VMManager::SetLimiterMode((EmuConfig.LimiterMode != LimiterModeType::Slomo) ? LimiterModeType::Slomo : LimiterModeType::Nominal); + } +}) +DEFINE_HOTKEY("HoldTurbo", "System", "Turbo (Hold)", [](s32 pressed) { + if (!VMManager::HasValidVM()) + return; + if (pressed > 0 && !s_limiter_mode_prior_to_hold_interaction.has_value()) + { + s_limiter_mode_prior_to_hold_interaction = VMManager::GetLimiterMode(); + VMManager::SetLimiterMode((s_limiter_mode_prior_to_hold_interaction.value() != LimiterModeType::Turbo) ? LimiterModeType::Turbo : + LimiterModeType::Nominal); + } + else if (pressed >= 0 && s_limiter_mode_prior_to_hold_interaction.has_value()) + { + VMManager::SetLimiterMode(s_limiter_mode_prior_to_hold_interaction.value()); + s_limiter_mode_prior_to_hold_interaction.reset(); + } +}) +DEFINE_HOTKEY("IncreaseSpeed", "System", "Increase Target Speed", [](s32 pressed) { + if (!pressed) + HotkeyAdjustTargetSpeed(0.1); +}) +DEFINE_HOTKEY("DecreaseSpeed", "System", "Decrease Target Speed", [](s32 pressed) { + if (!pressed) + HotkeyAdjustTargetSpeed(-0.1); +}) +DEFINE_HOTKEY("FrameAdvance", "System", "Frame Advance", [](s32 pressed) { + if (!pressed && VMManager::HasValidVM()) + VMManager::FrameAdvance(1); +}) +DEFINE_HOTKEY("ShutdownVM", "System", "Shut Down Virtual Machine", [](s32 pressed) { + if (!pressed && VMManager::HasValidVM()) + Host::RequestVMShutdown(true, true, EmuConfig.SaveStateOnShutdown); +}) +DEFINE_HOTKEY("ResetVM", "System", "Reset Virtual Machine", [](s32 pressed) { + if (!pressed && VMManager::HasValidVM()) + VMManager::Reset(); +}) +DEFINE_HOTKEY("InputRecToggleMode", "System", "Toggle Input Recording Mode", [](s32 pressed) { + if (!pressed && VMManager::HasValidVM()) + g_InputRecordingControls.RecordModeToggle(); +}) + +DEFINE_HOTKEY("PreviousSaveStateSlot", "Save States", "Select Previous Save Slot", [](s32 pressed) { + if (!pressed && VMManager::HasValidVM()) + HotkeyCycleSaveSlot(-1); +}) +DEFINE_HOTKEY("NextSaveStateSlot", "Save States", "Select Next Save Slot", [](s32 pressed) { + if (!pressed && VMManager::HasValidVM()) + HotkeyCycleSaveSlot(1); +}) +DEFINE_HOTKEY("SaveStateToSlot", "Save States", "Save State To Selected Slot", [](s32 pressed) { + if (!pressed && VMManager::HasValidVM()) + VMManager::SaveStateToSlot(s_current_save_slot); +}) +DEFINE_HOTKEY("LoadStateFromSlot", "Save States", "Load State From Selected Slot", [](s32 pressed) { + if (!pressed && VMManager::HasValidVM()) + HotkeyLoadStateSlot(s_current_save_slot); +}) + +#define DEFINE_HOTKEY_SAVESTATE_X(slotnum) \ + DEFINE_HOTKEY("SaveStateToSlot" #slotnum, "Save States", "Save State To Slot " #slotnum, [](s32 pressed) { \ + if (!pressed) \ + HotkeySaveStateSlot(slotnum); \ + }) +#define DEFINE_HOTKEY_LOADSTATE_X(slotnum) \ + DEFINE_HOTKEY("LoadStateFromSlot" #slotnum, "Save States", "Load State From Slot " #slotnum, [](s32 pressed) { \ + if (!pressed) \ + HotkeyLoadStateSlot(slotnum); \ + }) +DEFINE_HOTKEY_SAVESTATE_X(1) +DEFINE_HOTKEY_LOADSTATE_X(1) +DEFINE_HOTKEY_SAVESTATE_X(2) +DEFINE_HOTKEY_LOADSTATE_X(2) +DEFINE_HOTKEY_SAVESTATE_X(3) +DEFINE_HOTKEY_LOADSTATE_X(3) +DEFINE_HOTKEY_SAVESTATE_X(4) +DEFINE_HOTKEY_LOADSTATE_X(4) +DEFINE_HOTKEY_SAVESTATE_X(5) +DEFINE_HOTKEY_LOADSTATE_X(5) +DEFINE_HOTKEY_SAVESTATE_X(6) +DEFINE_HOTKEY_LOADSTATE_X(6) +DEFINE_HOTKEY_SAVESTATE_X(7) +DEFINE_HOTKEY_LOADSTATE_X(7) +DEFINE_HOTKEY_SAVESTATE_X(8) +DEFINE_HOTKEY_LOADSTATE_X(8) +DEFINE_HOTKEY_SAVESTATE_X(9) +DEFINE_HOTKEY_LOADSTATE_X(9) +DEFINE_HOTKEY_SAVESTATE_X(10) +DEFINE_HOTKEY_LOADSTATE_X(10) +#undef DEFINE_HOTKEY_SAVESTATE_X +#undef DEFINE_HOTKEY_LOADSTATE_X +END_HOTKEY_LIST() \ No newline at end of file diff --git a/pcsx2/Frontend/FullscreenUI.cpp b/pcsx2/Frontend/FullscreenUI.cpp index 85f5f72d3d..5f9add4119 100644 --- a/pcsx2/Frontend/FullscreenUI.cpp +++ b/pcsx2/Frontend/FullscreenUI.cpp @@ -20,7 +20,6 @@ #include "Frontend/FullscreenUI.h" #include "Frontend/ImGuiManager.h" #include "Frontend/ImGuiFullscreen.h" -#include "Frontend/INISettingsInterface.h" #include "Frontend/InputManager.h" #include "Frontend/GameList.h" #include "IconsFontAwesome5.h" @@ -40,6 +39,7 @@ #include "Host.h" #include "HostDisplay.h" #include "HostSettings.h" +#include "INISettingsInterface.h" #include "MemoryCardFile.h" #include "PAD/Host/PAD.h" #include "ps2/BiosTools.h" @@ -855,7 +855,7 @@ void FullscreenUI::DoChangeDiscFromFile() auto callback = [](const std::string& path) { if (!path.empty()) { - if (!GameList::IsScannableFilename(path)) + if (!VMManager::IsDiscFileName(path)) { ShowToast({}, fmt::format("{} is not a valid disc image.", FileSystem::GetDisplayNameFromPath(path))); } diff --git a/pcsx2/Frontend/GameList.cpp b/pcsx2/Frontend/GameList.cpp index 63f8a5a60b..8c757aae26 100644 --- a/pcsx2/Frontend/GameList.cpp +++ b/pcsx2/Frontend/GameList.cpp @@ -46,6 +46,8 @@ namespace GameList { using CacheMap = std::unordered_map; + static bool IsScannableFilename(const std::string_view& path); + static Entry* GetMutableEntryForPath(const char* path); static bool GetElfListEntry(const std::string& path, GameList::Entry* entry); @@ -119,15 +121,7 @@ const char* GameList::EntryCompatibilityRatingToString(CompatibilityRating ratin bool GameList::IsScannableFilename(const std::string_view& path) { - static const char* extensions[] = {".iso", ".mdf", ".nrg", ".bin", ".img", ".gz", ".cso", ".chd", ".elf", ".irx"}; - - for (const char* test_extension : extensions) - { - if (StringUtil::EndsWithNoCase(path, test_extension)) - return true; - } - - return false; + return VMManager::IsDiscFileName(path) || VMManager::IsElfFileName(path); } void GameList::FillBootParametersForEntry(VMBootParameters* params, const Entry* entry) diff --git a/pcsx2/Frontend/GameList.h b/pcsx2/Frontend/GameList.h index 48f80dd0e9..f7d59ead65 100644 --- a/pcsx2/Frontend/GameList.h +++ b/pcsx2/Frontend/GameList.h @@ -100,8 +100,6 @@ namespace GameList const char* RegionToString(Region region); const char* EntryCompatibilityRatingToString(CompatibilityRating rating); - bool IsScannableFilename(const std::string_view& path); - /// Fills in boot parameters (iso or elf) based on the game list entry. void FillBootParametersForEntry(VMBootParameters* params, const Entry* entry); diff --git a/pcsx2/Frontend/InputManager.cpp b/pcsx2/Frontend/InputManager.cpp index 1ebb984066..abef4108ce 100644 --- a/pcsx2/Frontend/InputManager.cpp +++ b/pcsx2/Frontend/InputManager.cpp @@ -134,7 +134,7 @@ static std::array, static_cast(InputSourceType // ------------------------------------------------------------------------ // Hotkeys // ------------------------------------------------------------------------ -static const HotkeyInfo* const s_hotkey_list[] = {g_host_hotkeys, g_vm_manager_hotkeys, g_gs_hotkeys}; +static const HotkeyInfo* const s_hotkey_list[] = {g_common_hotkeys, g_gs_hotkeys, g_host_hotkeys}; // ------------------------------------------------------------------------ // Tracking host mouse movement and turning into relative events diff --git a/pcsx2/Frontend/InputManager.h b/pcsx2/Frontend/InputManager.h index 51083ebdf7..d81b48fd90 100644 --- a/pcsx2/Frontend/InputManager.h +++ b/pcsx2/Frontend/InputManager.h @@ -131,7 +131,7 @@ struct HotkeyInfo } \ ; -DECLARE_HOTKEY_LIST(g_vm_manager_hotkeys); +DECLARE_HOTKEY_LIST(g_common_hotkeys); DECLARE_HOTKEY_LIST(g_gs_hotkeys); DECLARE_HOTKEY_LIST(g_host_hotkeys); diff --git a/pcsx2/Frontend/LogSink.cpp b/pcsx2/Frontend/LogSink.cpp index 9450f7fbe0..8bd2b441e0 100644 --- a/pcsx2/Frontend/LogSink.cpp +++ b/pcsx2/Frontend/LogSink.cpp @@ -66,6 +66,7 @@ static const char s_console_colors[][ConsoleColors_Count] = { }; #undef CC +static bool s_block_system_console = false; static Common::Timer::Value s_log_start_timestamp = Common::Timer::GetCurrentValue(); static bool s_log_timestamps = false; static std::mutex s_log_mutex; @@ -364,31 +365,36 @@ static void UpdateLoggingSinks(bool system_console, bool file_log) Console_SetActiveHandler(ConsoleWriter_Null); } -void Host::InitializeEarlyConsole() +void CommonHost::SetBlockSystemConsole(bool block) +{ + s_block_system_console = block; +} + +void CommonHost::InitializeEarlyConsole() { UpdateLoggingSinks(true, false); } -void Host::UpdateLogging(bool disable_system_console) +void CommonHost::UpdateLogging(SettingsInterface& si) { - const bool system_console_enabled = !disable_system_console && Host::GetBaseBoolSettingValue("Logging", "EnableSystemConsole", false); - const bool file_logging_enabled = Host::GetBaseBoolSettingValue("Logging", "EnableFileLogging", false); + const bool system_console_enabled = !s_block_system_console && si.GetBoolValue("Logging", "EnableSystemConsole", false); + const bool file_logging_enabled = si.GetBoolValue("Logging", "EnableFileLogging", false); - s_log_timestamps = Host::GetBaseBoolSettingValue("Logging", "EnableTimestamps", true); + s_log_timestamps = si.GetBoolValue("Logging", "EnableTimestamps", true); const bool any_logging_sinks = system_console_enabled || file_logging_enabled; - DevConWriterEnabled = any_logging_sinks && (IsDevBuild || Host::GetBaseBoolSettingValue("Logging", "EnableVerbose", false)); - SysConsole.eeConsole.Enabled = any_logging_sinks && Host::GetBaseBoolSettingValue("Logging", "EnableEEConsole", false); - SysConsole.iopConsole.Enabled = any_logging_sinks && Host::GetBaseBoolSettingValue("Logging", "EnableIOPConsole", false); + DevConWriterEnabled = any_logging_sinks && (IsDevBuild || si.GetBoolValue("Logging", "EnableVerbose", false)); + SysConsole.eeConsole.Enabled = any_logging_sinks && si.GetBoolValue("Logging", "EnableEEConsole", false); + SysConsole.iopConsole.Enabled = any_logging_sinks && si.GetBoolValue("Logging", "EnableIOPConsole", false); // Input Recording Logs - SysConsole.recordingConsole.Enabled = any_logging_sinks && Host::GetBaseBoolSettingValue("Logging", "EnableInputRecordingLogs", true); - SysConsole.controlInfo.Enabled = any_logging_sinks && Host::GetBaseBoolSettingValue("Logging", "EnableControllerLogs", false); + SysConsole.recordingConsole.Enabled = any_logging_sinks && si.GetBoolValue("Logging", "EnableInputRecordingLogs", true); + SysConsole.controlInfo.Enabled = any_logging_sinks && si.GetBoolValue("Logging", "EnableControllerLogs", false); UpdateLoggingSinks(system_console_enabled, file_logging_enabled); } -void Host::SetDefaultLoggingSettings(SettingsInterface& si) +void CommonHost::SetDefaultLoggingSettings(SettingsInterface& si) { si.SetBoolValue("Logging", "EnableSystemConsole", false); si.SetBoolValue("Logging", "EnableFileLogging", false); diff --git a/pcsx2/Frontend/LogSink.h b/pcsx2/Frontend/LogSink.h index 8e032a43eb..2e526d04f0 100644 --- a/pcsx2/Frontend/LogSink.h +++ b/pcsx2/Frontend/LogSink.h @@ -17,10 +17,13 @@ class SettingsInterface; -namespace Host +namespace CommonHost { + /// Prevents the system console from being displayed. + void SetBlockSystemConsole(bool block); + /// Updates the Console handler based on the current configuration. - void UpdateLogging(bool disable_system_console); + void UpdateLogging(SettingsInterface& si); /// Initializes early console logging (for printing command line arguments). void InitializeEarlyConsole(); diff --git a/pcsx2/Frontend/INISettingsInterface.cpp b/pcsx2/INISettingsInterface.cpp similarity index 100% rename from pcsx2/Frontend/INISettingsInterface.cpp rename to pcsx2/INISettingsInterface.cpp diff --git a/pcsx2/Frontend/INISettingsInterface.h b/pcsx2/INISettingsInterface.h similarity index 100% rename from pcsx2/Frontend/INISettingsInterface.h rename to pcsx2/INISettingsInterface.h diff --git a/pcsx2/VMManager.cpp b/pcsx2/VMManager.cpp index 6b33ce8e7c..770cb8f3a2 100644 --- a/pcsx2/VMManager.cpp +++ b/pcsx2/VMManager.cpp @@ -39,6 +39,7 @@ #include "GSDumpReplayer.h" #include "HostDisplay.h" #include "HostSettings.h" +#include "INISettingsInterface.h" #include "IopBios.h" #include "MTVU.h" #include "MemoryCardFile.h" @@ -56,10 +57,6 @@ #include "DebugTools/MIPSAnalyst.h" #include "DebugTools/SymbolMap.h" -#include "Frontend/FullscreenUI.h" -#include "Frontend/INISettingsInterface.h" -#include "Frontend/InputManager.h" -#include "Frontend/GameList.h" #include "IconsFontAwesome5.h" #include "common/emitter/tools.h" @@ -135,10 +132,8 @@ static std::vector s_no_interlacing_cheats_data; static bool s_no_interlacing_cheats_loaded = false; static s32 s_active_widescreen_patches = 0; static u32 s_active_no_interlacing_patches = 0; -static s32 s_current_save_slot = 1; static u32 s_frame_advance_count = 0; static u32 s_mxcsr_saved; -static std::optional s_limiter_mode_prior_to_hold_interaction; static bool s_gs_open_on_initialize = false; bool VMManager::PerformEarlyHardwareChecks(const char** error) @@ -193,7 +188,6 @@ void VMManager::SetState(VMState state) if (THREAD_VU1) vu1Thread.WaitVU(); GetMTGS().WaitGS(false); - InputManager::PauseVibration(); } else { @@ -203,15 +197,9 @@ void VMManager::SetState(VMState state) SPU2SetOutputPaused(state == VMState::Paused); if (state == VMState::Paused) - { Host::OnVMPaused(); - FullscreenUI::OnVMPaused(); - } else - { Host::OnVMResumed(); - FullscreenUI::OnVMResumed(); - } } } @@ -319,12 +307,10 @@ void VMManager::LoadSettings() { std::unique_lock lock = Host::GetSettingsLock(); SettingsInterface* si = Host::GetSettingsInterface(); - SettingsInterface* binding_si = Host::GetSettingsInterfaceForBindings(); SettingsLoadWrapper slw(*si); EmuConfig.LoadSave(slw); - PAD::LoadConfig(*binding_si); - InputManager::ReloadSources(*si, lock); - InputManager::ReloadBindings(*si, *binding_si); + PAD::LoadConfig(*si); + Host::LoadSettings(*si, lock); // Remove any user-specified hacks in the config (we don't want stale/conflicting values when it's globally disabled). EmuConfig.GS.MaskUserHacks(); @@ -703,12 +689,6 @@ void VMManager::UpdateRunningGame(bool resetting, bool game_starting) GetMTGS().SendGameCRC(new_crc); Host::OnGameChanged(s_disc_path, s_game_serial, s_game_name, s_game_crc); - if (FullscreenUI::IsInitialized()) - { - GetMTGS().RunOnGSThread([disc_path = s_disc_path, game_serial = s_game_serial, game_name = s_game_name, game_crc = s_game_crc]() { - FullscreenUI::OnRunningGameChanged(std::move(disc_path), std::move(game_serial), std::move(game_name), game_crc); - }); - } #if 0 // TODO: Enable this when the debugger is added to Qt, and it's active. Otherwise, this is just a waste of time. @@ -992,7 +972,6 @@ bool VMManager::Initialize(VMBootParameters boot_params) Console.WriteLn("VM subsystems initialized in %.2f ms", init_timer.GetTimeMilliseconds()); s_state.store(VMState::Paused, std::memory_order_release); Host::OnVMStarted(); - FullscreenUI::OnVMStarted(); UpdateRunningGame(true, false); @@ -1055,7 +1034,6 @@ void VMManager::Shutdown(bool save_resume_state) s_active_game_fixes = 0; s_active_widescreen_patches = 0; s_active_no_interlacing_patches = 0; - s_limiter_mode_prior_to_hold_interaction.reset(); UpdateGameSettingsLayer(); @@ -1094,7 +1072,6 @@ void VMManager::Shutdown(bool save_resume_state) s_state.store(VMState::Shutdown, std::memory_order_release); Host::OnVMDestroyed(); - FullscreenUI::OnVMDestroyed(); } void VMManager::Reset() @@ -1104,7 +1081,6 @@ void VMManager::Reset() s_active_game_fixes = 0; s_active_widescreen_patches = 0; s_active_no_interlacing_patches = 0; - s_limiter_mode_prior_to_hold_interaction.reset(); SysClearExecutionCache(); memBindConditionalHandlers(); @@ -1138,22 +1114,10 @@ std::string VMManager::GetSaveStateFileName(const char* filename, s32 slot) pxAssertRel(!HasValidVM(), "Should not have a VM when calling the non-gamelist GetSaveStateFileName()"); std::string ret; - - // try the game list first, but this won't work if we're in batch mode - auto lock = GameList::GetLock(); - if (const GameList::Entry* entry = GameList::GetEntryForPath(filename); entry) - { - ret = GetSaveStateFileName(entry->serial.c_str(), entry->crc, slot); - } - else - { - // just scan it.. hopefully it'll come back okay - GameList::Entry temp_entry; - if (GameList::PopulateEntryFromPath(filename, &temp_entry)) - { - ret = GetSaveStateFileName(temp_entry.serial.c_str(), temp_entry.crc, slot); - } - } + std::string serial; + u32 crc; + if (Host::GetSerialAndCRCForFilename(filename, &serial, &crc)) + ret = GetSaveStateFileName(serial.c_str(), crc, slot); return ret; } @@ -1409,9 +1373,22 @@ bool VMManager::IsSaveStateFileName(const std::string_view& path) return StringUtil::EndsWithNoCase(path, ".p2s"); } +bool VMManager::IsDiscFileName(const std::string_view& path) +{ + static const char* extensions[] = {".iso", ".bin", ".img", ".gz", ".cso", ".chd"}; + + for (const char* test_extension : extensions) + { + if (StringUtil::EndsWithNoCase(path, test_extension)) + return true; + } + + return false; +} + bool VMManager::IsLoadableFileName(const std::string_view& path) { - return IsElfFileName(path) || IsGSDumpFileName(path) || IsBlockDumpFileName(path) || GameList::IsScannableFilename(path); + return IsDiscFileName(path) || IsElfFileName(path) || IsGSDumpFileName(path) || IsBlockDumpFileName(path); } void VMManager::Execute() @@ -1482,8 +1459,7 @@ void VMManager::Internal::VSyncOnCPUThread() } } - Host::PumpMessagesOnCPUThread(); - InputManager::PollSources(); + Host::CPUThreadVSync(); } void VMManager::CheckForCPUConfigChanges(const Pcsx2Config& old_config) @@ -1681,6 +1657,8 @@ void VMManager::CheckForConfigChanges(const Pcsx2Config& old_config) // and we don't update its config when we start the VM. if (HasValidVM() || GetMTGS().IsOpen()) CheckForGSConfigChanges(old_config); + + Host::CheckForSettingsChanges(old_config); } void VMManager::ApplySettings() @@ -1808,204 +1786,6 @@ void VMManager::WarnAboutUnsafeSettings() } } -static void HotkeyAdjustTargetSpeed(double delta) -{ - EmuConfig.Framerate.NominalScalar = EmuConfig.GS.LimitScalar + delta; - VMManager::SetLimiterMode(LimiterModeType::Nominal); - gsUpdateFrequency(EmuConfig); - GetMTGS().SetVSync(EmuConfig.GetEffectiveVsyncMode()); - Host::AddIconOSDMessage("SpeedChanged", ICON_FA_CLOCK, fmt::format("Target speed set to {:.0f}%.", std::round(EmuConfig.Framerate.NominalScalar * 100.0)), 5.0f); -} - -static constexpr s32 CYCLE_SAVE_STATE_SLOTS = 10; - -static void HotkeyCycleSaveSlot(s32 delta) -{ - // 1..10 - s_current_save_slot = ((s_current_save_slot - 1) + delta); - if (s_current_save_slot < 0) - s_current_save_slot = CYCLE_SAVE_STATE_SLOTS; - else - s_current_save_slot = (s_current_save_slot % CYCLE_SAVE_STATE_SLOTS) + 1; - - const std::string filename(VMManager::GetSaveStateFileName(s_game_serial.c_str(), s_game_crc, s_current_save_slot)); - FILESYSTEM_STAT_DATA sd; - if (!filename.empty() && FileSystem::StatFile(filename.c_str(), &sd)) - { - char date_buf[128] = {}; -#ifdef _WIN32 - ctime_s(date_buf, std::size(date_buf), &sd.ModificationTime); -#else - ctime_r(&sd.ModificationTime, date_buf); -#endif - - // remove terminating \n - size_t len = std::strlen(date_buf); - if (len > 0 && date_buf[len - 1] == '\n') - date_buf[len - 1] = 0; - - Host::AddIconOSDMessage("CycleSaveSlot", ICON_FA_SEARCH, fmt::format("Save slot {} selected (last save: {}).", s_current_save_slot, date_buf), 5.0f); - } - else - { - Host::AddIconOSDMessage("CycleSaveSlot", ICON_FA_SEARCH, fmt::format("Save slot {} selected (no save yet).", s_current_save_slot), 5.0f); - } -} - -static void HotkeyLoadStateSlot(s32 slot) -{ - if (s_game_crc == 0) - { - Host::AddIconOSDMessage("LoadStateFromSlot", ICON_FA_EXCLAMATION_TRIANGLE, "Cannot load state from a slot without a game running.", 10.0f); - return; - } - - if (!VMManager::HasSaveStateInSlot(s_game_serial.c_str(), s_game_crc, slot)) - { - Host::AddIconOSDMessage("LoadStateFromSlot", ICON_FA_EXCLAMATION_TRIANGLE, fmt::format("No save state found in slot {}.", slot)); - return; - } - - VMManager::LoadStateFromSlot(slot); -} - -static void HotkeySaveStateSlot(s32 slot) -{ - if (s_game_crc == 0) - { - Host::AddIconOSDMessage("SaveStateToSlot", ICON_FA_EXCLAMATION_TRIANGLE, "Cannot save state to a slot without a game running.", 10.0f); - return; - } - - VMManager::SaveStateToSlot(slot); -} - -BEGIN_HOTKEY_LIST(g_vm_manager_hotkeys) -DEFINE_HOTKEY("OpenPauseMenu", "System", "Open Pause Menu", [](s32 pressed) { - if (!pressed && VMManager::HasValidVM()) - FullscreenUI::OpenPauseMenu(); -}) -DEFINE_HOTKEY("TogglePause", "System", "Toggle Pause", [](s32 pressed) { - if (!pressed && VMManager::HasValidVM()) - VMManager::SetPaused(VMManager::GetState() != VMState::Paused); -}) -DEFINE_HOTKEY("ToggleFullscreen", "System", "Toggle Fullscreen", [](s32 pressed) { - if (!pressed) - Host::SetFullscreen(!Host::IsFullscreen()); -}) -DEFINE_HOTKEY("ToggleFrameLimit", "System", "Toggle Frame Limit", [](s32 pressed) { - if (!pressed && VMManager::HasValidVM()) - { - VMManager::SetLimiterMode((EmuConfig.LimiterMode != LimiterModeType::Unlimited) ? - LimiterModeType::Unlimited : - LimiterModeType::Nominal); - } -}) -DEFINE_HOTKEY("ToggleTurbo", "System", "Toggle Turbo", [](s32 pressed) { - if (!pressed && VMManager::HasValidVM()) - { - VMManager::SetLimiterMode((EmuConfig.LimiterMode != LimiterModeType::Turbo) ? - LimiterModeType::Turbo : - LimiterModeType::Nominal); - } -}) -DEFINE_HOTKEY("ToggleSlowMotion", "System", "Toggle Slow Motion", [](s32 pressed) { - if (!pressed && VMManager::HasValidVM()) - { - VMManager::SetLimiterMode((EmuConfig.LimiterMode != LimiterModeType::Slomo) ? - LimiterModeType::Slomo : - LimiterModeType::Nominal); - } -}) -DEFINE_HOTKEY("HoldTurbo", "System", "Turbo (Hold)", [](s32 pressed) { - if (!VMManager::HasValidVM()) - return; - if (pressed > 0 && !s_limiter_mode_prior_to_hold_interaction.has_value()) - { - s_limiter_mode_prior_to_hold_interaction = VMManager::GetLimiterMode(); - VMManager::SetLimiterMode((s_limiter_mode_prior_to_hold_interaction.value() != LimiterModeType::Turbo) ? - LimiterModeType::Turbo : - LimiterModeType::Nominal); - } - else if (pressed >= 0 && s_limiter_mode_prior_to_hold_interaction.has_value()) - { - VMManager::SetLimiterMode(s_limiter_mode_prior_to_hold_interaction.value()); - s_limiter_mode_prior_to_hold_interaction.reset(); - } -}) -DEFINE_HOTKEY("IncreaseSpeed", "System", "Increase Target Speed", [](s32 pressed) { - if (!pressed) - HotkeyAdjustTargetSpeed(0.1); -}) -DEFINE_HOTKEY("DecreaseSpeed", "System", "Decrease Target Speed", [](s32 pressed) { - if (!pressed) - HotkeyAdjustTargetSpeed(-0.1); -}) -DEFINE_HOTKEY("FrameAdvance", "System", "Frame Advance", [](s32 pressed) { - if (!pressed && VMManager::HasValidVM()) - VMManager::FrameAdvance(1); -}) -DEFINE_HOTKEY("ShutdownVM", "System", "Shut Down Virtual Machine", [](s32 pressed) { - if (!pressed && VMManager::HasValidVM()) - Host::RequestVMShutdown(true, true, EmuConfig.SaveStateOnShutdown); -}) -DEFINE_HOTKEY("ResetVM", "System", "Reset Virtual Machine", [](s32 pressed) { - if (!pressed && VMManager::HasValidVM()) - VMManager::Reset(); -}) -DEFINE_HOTKEY("InputRecToggleMode", "System", "Toggle Input Recording Mode", [](s32 pressed) { - if (!pressed && VMManager::HasValidVM()) - g_InputRecordingControls.RecordModeToggle(); -}) - -DEFINE_HOTKEY("PreviousSaveStateSlot", "Save States", "Select Previous Save Slot", [](s32 pressed) { - if (!pressed && VMManager::HasValidVM()) - HotkeyCycleSaveSlot(-1); -}) -DEFINE_HOTKEY("NextSaveStateSlot", "Save States", "Select Next Save Slot", [](s32 pressed) { - if (!pressed && VMManager::HasValidVM()) - HotkeyCycleSaveSlot(1); -}) -DEFINE_HOTKEY("SaveStateToSlot", "Save States", "Save State To Selected Slot", [](s32 pressed) { - if (!pressed && VMManager::HasValidVM()) - VMManager::SaveStateToSlot(s_current_save_slot); -}) -DEFINE_HOTKEY("LoadStateFromSlot", "Save States", "Load State From Selected Slot", [](s32 pressed) { - if (!pressed && VMManager::HasValidVM()) - HotkeyLoadStateSlot(s_current_save_slot); -}) - -#define DEFINE_HOTKEY_SAVESTATE_X(slotnum) DEFINE_HOTKEY("SaveStateToSlot" #slotnum, \ - "Save States", "Save State To Slot " #slotnum, [](s32 pressed) { if (!pressed) HotkeySaveStateSlot(slotnum); }) -#define DEFINE_HOTKEY_LOADSTATE_X(slotnum) DEFINE_HOTKEY("LoadStateFromSlot" #slotnum, \ - "Save States", "Load State From Slot " #slotnum, [](s32 pressed) { \ - if (!pressed) \ - HotkeyLoadStateSlot(slotnum); \ - }) -DEFINE_HOTKEY_SAVESTATE_X(1) -DEFINE_HOTKEY_LOADSTATE_X(1) -DEFINE_HOTKEY_SAVESTATE_X(2) -DEFINE_HOTKEY_LOADSTATE_X(2) -DEFINE_HOTKEY_SAVESTATE_X(3) -DEFINE_HOTKEY_LOADSTATE_X(3) -DEFINE_HOTKEY_SAVESTATE_X(4) -DEFINE_HOTKEY_LOADSTATE_X(4) -DEFINE_HOTKEY_SAVESTATE_X(5) -DEFINE_HOTKEY_LOADSTATE_X(5) -DEFINE_HOTKEY_SAVESTATE_X(6) -DEFINE_HOTKEY_LOADSTATE_X(6) -DEFINE_HOTKEY_SAVESTATE_X(7) -DEFINE_HOTKEY_LOADSTATE_X(7) -DEFINE_HOTKEY_SAVESTATE_X(8) -DEFINE_HOTKEY_LOADSTATE_X(8) -DEFINE_HOTKEY_SAVESTATE_X(9) -DEFINE_HOTKEY_LOADSTATE_X(9) -DEFINE_HOTKEY_SAVESTATE_X(10) -DEFINE_HOTKEY_LOADSTATE_X(10) -#undef DEFINE_HOTKEY_SAVESTATE_X -#undef DEFINE_HOTKEY_LOADSTATE_X -END_HOTKEY_LIST() - #ifdef _WIN32 #include "common/RedtapeWindows.h" diff --git a/pcsx2/VMManager.h b/pcsx2/VMManager.h index 8c76be72f1..52ce7e2db7 100644 --- a/pcsx2/VMManager.h +++ b/pcsx2/VMManager.h @@ -152,6 +152,9 @@ namespace VMManager /// Returns true if the specified path is a save state. bool IsSaveStateFileName(const std::string_view& path); + /// Returns true if the specified path is a disc image. + bool IsDiscFileName(const std::string_view& path); + /// Returns true if the specified path is a disc/elf/etc. bool IsLoadableFileName(const std::string_view& path); @@ -198,6 +201,12 @@ namespace VMManager namespace Host { + /// Called with the settings lock held, when system settings are being loaded (should load input sources, etc). + void LoadSettings(SettingsInterface& si, std::unique_lock& lock); + + /// Called after settings are updated. + void CheckForSettingsChanges(const Pcsx2Config& old_config); + /// Called when the VM is starting initialization, but has not been completed yet. void OnVMStarting(); @@ -216,6 +225,10 @@ namespace Host /// Called when performance metrics are updated, approximately once a second. void OnPerformanceMetricsUpdated(); + /// Looks up the serial and CRC for a game in the most efficient manner possible. + /// Implemented in the host because it may have a game list cache. + bool GetSerialAndCRCForFilename(const char* filename, std::string* serial, u32* crc); + /// Called when a save state is loading, before the file is processed. void OnSaveStateLoading(const std::string_view& filename); @@ -230,7 +243,7 @@ namespace Host void OnGameChanged(const std::string& disc_path, const std::string& game_serial, const std::string& game_name, u32 game_crc); /// Provided by the host; called once per frame at guest vsync. - void PumpMessagesOnCPUThread(); + void CPUThreadVSync(); /// Provided by the host; called when a state is saved, and the frontend should invalidate its save state cache. void InvalidateSaveStateCache(); diff --git a/pcsx2/pcsx2core.vcxproj b/pcsx2/pcsx2core.vcxproj index feb16f3393..310a2df6c4 100644 --- a/pcsx2/pcsx2core.vcxproj +++ b/pcsx2/pcsx2core.vcxproj @@ -187,6 +187,7 @@ + @@ -197,7 +198,7 @@ - + @@ -520,7 +521,7 @@ - + @@ -817,4 +818,4 @@ - \ No newline at end of file + diff --git a/pcsx2/pcsx2core.vcxproj.filters b/pcsx2/pcsx2core.vcxproj.filters index f8bcffceb4..059170d014 100644 --- a/pcsx2/pcsx2core.vcxproj.filters +++ b/pcsx2/pcsx2core.vcxproj.filters @@ -1172,7 +1172,7 @@ Host - + Host @@ -1284,6 +1284,9 @@ Host + + Host + @@ -2037,7 +2040,7 @@ Host - + Host