From eb102275c2181919a6178ea2a8b78b04ff077f88 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sat, 24 Aug 2024 14:10:25 +1000 Subject: [PATCH] Qt: Implement per-game controller configuration --- src/common/layered_settings_interface.h | 3 +- src/common/memory_settings_interface.h | 2 +- src/core/fullscreen_ui.cpp | 12 +- src/core/settings.cpp | 7 +- src/core/settings.h | 2 +- src/core/system.cpp | 65 +++-- .../controllerbindingwidgets.cpp | 18 +- .../controllerglobalsettingswidget.cpp | 20 +- .../controllerglobalsettingswidget.ui | 11 +- .../controllersettingswindow.cpp | 224 +++++++++++++----- src/duckstation-qt/controllersettingswindow.h | 30 ++- .../controllersettingswindow.ui | 20 +- src/duckstation-qt/gamesummarywidget.cpp | 109 +++++++-- src/duckstation-qt/gamesummarywidget.h | 2 + src/duckstation-qt/gamesummarywidget.ui | 48 ++-- src/duckstation-qt/hotkeysettingswidget.cpp | 2 +- src/duckstation-qt/mainwindow.cpp | 7 + src/duckstation-qt/mainwindow.h | 3 + src/duckstation-qt/settingswindow.cpp | 2 +- src/duckstation-qt/settingswindow.h | 1 + src/util/ini_settings_interface.cpp | 11 +- src/util/ini_settings_interface.h | 2 +- src/util/input_manager.cpp | 7 +- src/util/input_manager.h | 2 +- 24 files changed, 429 insertions(+), 181 deletions(-) diff --git a/src/common/layered_settings_interface.h b/src/common/layered_settings_interface.h index 3a89b5960..3d25f104a 100644 --- a/src/common/layered_settings_interface.h +++ b/src/common/layered_settings_interface.h @@ -10,7 +10,6 @@ class LayeredSettingsInterface final : public SettingsInterface public: enum Layer : u32 { - LAYER_CMDLINE, LAYER_GAME, LAYER_INPUT, LAYER_BASE, @@ -66,7 +65,7 @@ public: using SettingsInterface::GetUIntValue; private: - static constexpr Layer FIRST_LAYER = LAYER_CMDLINE; + static constexpr Layer FIRST_LAYER = LAYER_GAME; static constexpr Layer LAST_LAYER = LAYER_BASE; std::array m_layers{}; diff --git a/src/common/memory_settings_interface.h b/src/common/memory_settings_interface.h index 612abf202..b1ca083b4 100644 --- a/src/common/memory_settings_interface.h +++ b/src/common/memory_settings_interface.h @@ -10,7 +10,7 @@ class MemorySettingsInterface final : public SettingsInterface { public: MemorySettingsInterface(); - ~MemorySettingsInterface(); + ~MemorySettingsInterface() override; bool Save(Error* error = nullptr) override; diff --git a/src/core/fullscreen_ui.cpp b/src/core/fullscreen_ui.cpp index 0b9358470..b6af1755a 100644 --- a/src/core/fullscreen_ui.cpp +++ b/src/core/fullscreen_ui.cpp @@ -2798,7 +2798,7 @@ void FullscreenUI::DoCopyGameSettings() return; Settings temp_settings; - temp_settings.Load(*GetEditingSettingsInterface(false)); + temp_settings.Load(*GetEditingSettingsInterface(false), *GetEditingSettingsInterface(false)); temp_settings.Save(*s_game_settings_interface, true); SetSettingsChanged(s_game_settings_interface.get()); @@ -3652,20 +3652,20 @@ void FullscreenUI::DrawControllerSettingsPage() if (IsEditingGameSettings(bsi)) { if (DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_COG, "Per-Game Configuration"), - FSUI_CSTR("Uses game-specific settings for controllers for this game."), "Pad", + FSUI_CSTR("Uses game-specific settings for controllers for this game."), "ControllerPorts", "UseGameSettingsForController", false, IsEditingGameSettings(bsi), false)) { // did we just enable per-game for the first time? - if (bsi->GetBoolValue("Pad", "UseGameSettingsForController", false) && - !bsi->GetBoolValue("Pad", "GameSettingsInitialized", false)) + if (bsi->GetBoolValue("ControllerPorts", "UseGameSettingsForController", false) && + !bsi->GetBoolValue("ControllerPorts", "GameSettingsInitialized", false)) { - bsi->SetBoolValue("Pad", "GameSettingsInitialized", true); + bsi->SetBoolValue("ControllerPorts", "GameSettingsInitialized", true); CopyGlobalControllerSettingsToGame(); } } } - if (IsEditingGameSettings(bsi) && !bsi->GetBoolValue("Pad", "UseGameSettingsForController", false)) + if (IsEditingGameSettings(bsi) && !bsi->GetBoolValue("ControllerPorts", "UseGameSettingsForController", false)) { // nothing to edit.. EndMenuButtons(); diff --git a/src/core/settings.cpp b/src/core/settings.cpp index dc34e6c06..db23ed84e 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -134,7 +134,7 @@ void Settings::UpdateOverclockActive() cpu_overclock_active = (cpu_overclock_enable && (cpu_overclock_numerator != 1 || cpu_overclock_denominator != 1)); } -void Settings::Load(SettingsInterface& si) +void Settings::Load(SettingsInterface& si, SettingsInterface& controller_si) { region = ParseConsoleRegionName( @@ -364,7 +364,8 @@ void Settings::Load(SettingsInterface& si) multitap_mode = ParseMultitapModeName( - si.GetStringValue("ControllerPorts", "MultitapMode", GetMultitapModeName(DEFAULT_MULTITAP_MODE)).c_str()) + controller_si.GetStringValue("ControllerPorts", "MultitapMode", GetMultitapModeName(DEFAULT_MULTITAP_MODE)) + .c_str()) .value_or(DEFAULT_MULTITAP_MODE); const std::array mtap_enabled = {{IsPort1MultitapEnabled(), IsPort2MultitapEnabled()}}; @@ -379,7 +380,7 @@ void Settings::Load(SettingsInterface& si) } const ControllerType default_type = (i == 0) ? DEFAULT_CONTROLLER_1_TYPE : DEFAULT_CONTROLLER_2_TYPE; - const Controller::ControllerInfo* cinfo = Controller::GetControllerInfo(si.GetTinyStringValue( + const Controller::ControllerInfo* cinfo = Controller::GetControllerInfo(controller_si.GetTinyStringValue( Controller::GetSettingsSection(i).c_str(), "Type", Controller::GetControllerInfo(default_type)->name)); controller_types[i] = cinfo ? cinfo->type : default_type; } diff --git a/src/core/settings.h b/src/core/settings.h index 492eef216..3a0390642 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -354,7 +354,7 @@ struct Settings DEFAULT_VRAM_WRITE_DUMP_HEIGHT_THRESHOLD = 128, }; - void Load(SettingsInterface& si); + void Load(SettingsInterface& si, SettingsInterface& controller_si); void Save(SettingsInterface& si, bool ignore_base) const; static void Clear(SettingsInterface& si); diff --git a/src/core/system.cpp b/src/core/system.cpp index e5a445036..628121553 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -54,6 +54,7 @@ #include "common/dynamic_library.h" #include "common/error.h" #include "common/file_system.h" +#include "common/layered_settings_interface.h" #include "common/log.h" #include "common/path.h" #include "common/string_util.h" @@ -136,7 +137,8 @@ struct MemorySaveState static void CheckCacheLineSize(); static void LogStartupInformation(); -static void LoadInputBindings(SettingsInterface& si, std::unique_lock& lock); +static LayeredSettingsInterface GetControllerSettingsLayers(std::unique_lock& lock); +static LayeredSettingsInterface GetHotkeySettingsLayer(std::unique_lock& lock); static std::string GetExecutableNameForImage(IsoReader& iso, bool strip_subdirectories); static bool ReadExecutableFromImage(IsoReader& iso, std::string* out_executable_name, @@ -1202,12 +1204,14 @@ void System::LoadSettings(bool display_osd_messages) { std::unique_lock lock = Host::GetSettingsLock(); SettingsInterface& si = *Host::GetSettingsInterface(); - g_settings.Load(si); + LayeredSettingsInterface controller_si = GetControllerSettingsLayers(lock); + LayeredSettingsInterface hotkey_si = GetHotkeySettingsLayer(lock); + g_settings.Load(si, controller_si); g_settings.UpdateLogSettings(); Host::LoadSettings(si, lock); - InputManager::ReloadSources(si, lock); - LoadInputBindings(si, lock); + InputManager::ReloadSources(controller_si, lock); + InputManager::ReloadBindings(controller_si, hotkey_si); WarnAboutUnsafeSettings(); // apply compatibility settings @@ -1224,12 +1228,15 @@ void System::LoadSettings(bool display_osd_messages) void System::ReloadInputSources() { std::unique_lock lock = Host::GetSettingsLock(); - SettingsInterface* si = Host::GetSettingsInterface(); - InputManager::ReloadSources(*si, lock); + LayeredSettingsInterface controller_si = GetControllerSettingsLayers(lock); + InputManager::ReloadSources(controller_si, lock); // skip loading bindings if we're not running, since it'll get done on startup anyway if (IsValid()) - LoadInputBindings(*si, lock); + { + LayeredSettingsInterface hotkey_si = GetHotkeySettingsLayer(lock); + InputManager::ReloadBindings(controller_si, hotkey_si); + } } void System::ReloadInputBindings() @@ -1239,37 +1246,43 @@ void System::ReloadInputBindings() return; std::unique_lock lock = Host::GetSettingsLock(); - SettingsInterface* si = Host::GetSettingsInterface(); - LoadInputBindings(*si, lock); + LayeredSettingsInterface controller_si = GetControllerSettingsLayers(lock); + LayeredSettingsInterface hotkey_si = GetHotkeySettingsLayer(lock); + InputManager::ReloadBindings(controller_si, hotkey_si); } -void System::LoadInputBindings(SettingsInterface& si, std::unique_lock& lock) +LayeredSettingsInterface System::GetControllerSettingsLayers(std::unique_lock& lock) { - // Hotkeys use the base configuration, except if the custom hotkeys option is enabled. + LayeredSettingsInterface ret; + ret.SetLayer(LayeredSettingsInterface::Layer::LAYER_BASE, Host::Internal::GetBaseSettingsLayer()); + + // Select input profile _or_ game settings, not both. if (SettingsInterface* isi = Host::Internal::GetInputSettingsLayer()) { - const bool use_profile_hotkeys = isi->GetBoolValue("ControllerPorts", "UseProfileHotkeyBindings", false); - if (use_profile_hotkeys) - { - InputManager::ReloadBindings(si, *isi, *isi); - } - else - { - // Temporarily disable the input profile layer, so it doesn't take precedence. - Host::Internal::SetInputSettingsLayer(nullptr, lock); - InputManager::ReloadBindings(si, *isi, si); - Host::Internal::SetInputSettingsLayer(s_input_settings_interface.get(), lock); - } + ret.SetLayer(LayeredSettingsInterface::Layer::LAYER_INPUT, Host::Internal::GetInputSettingsLayer()); } else if (SettingsInterface* gsi = Host::Internal::GetGameSettingsLayer(); gsi && gsi->GetBoolValue("ControllerPorts", "UseGameSettingsForController", false)) { - InputManager::ReloadBindings(si, *gsi, si); + ret.SetLayer(LayeredSettingsInterface::Layer::LAYER_GAME, gsi); } - else + + return ret; +} + +LayeredSettingsInterface System::GetHotkeySettingsLayer(std::unique_lock& lock) +{ + LayeredSettingsInterface ret; + ret.SetLayer(LayeredSettingsInterface::Layer::LAYER_BASE, Host::Internal::GetBaseSettingsLayer()); + + // Only add input profile layer if the option is enabled. + if (SettingsInterface* isi = Host::Internal::GetInputSettingsLayer(); + isi && isi->GetBoolValue("ControllerPorts", "UseProfileHotkeyBindings", false)) { - InputManager::ReloadBindings(si, si, si); + ret.SetLayer(LayeredSettingsInterface::Layer::LAYER_INPUT, Host::Internal::GetInputSettingsLayer()); } + + return ret; } void System::SetDefaultSettings(SettingsInterface& si) diff --git a/src/duckstation-qt/controllerbindingwidgets.cpp b/src/duckstation-qt/controllerbindingwidgets.cpp index 10cb21e59..9f05db321 100644 --- a/src/duckstation-qt/controllerbindingwidgets.cpp +++ b/src/duckstation-qt/controllerbindingwidgets.cpp @@ -247,7 +247,7 @@ void ControllerBindingWidget::onTypeChanged() m_controller_info = Controller::GetControllerInfo(static_cast(index)); DebugAssert(m_controller_info); - SettingsInterface* sif = m_dialog->getProfileSettingsInterface(); + SettingsInterface* sif = m_dialog->getEditingSettingsInterface(); if (sif) { sif->SetStringValue(m_config_section.c_str(), "Type", m_controller_info->name); @@ -307,7 +307,7 @@ void ControllerBindingWidget::onClearBindingsClicked() } else { - InputManager::ClearPortBindings(*m_dialog->getProfileSettingsInterface(), m_port_number); + InputManager::ClearPortBindings(*m_dialog->getEditingSettingsInterface(), m_port_number); } saveAndRefresh(); @@ -358,8 +358,8 @@ void ControllerBindingWidget::doDeviceAutomaticBinding(const QString& device) } else { - result = InputManager::MapController(*m_dialog->getProfileSettingsInterface(), m_port_number, mapping); - QtHost::SaveGameSettings(m_dialog->getProfileSettingsInterface(), false); + result = InputManager::MapController(*m_dialog->getEditingSettingsInterface(), m_port_number, mapping); + QtHost::SaveGameSettings(m_dialog->getEditingSettingsInterface(), false); g_emu_thread->reloadInputBindings(); } @@ -377,7 +377,7 @@ void ControllerBindingWidget::saveAndRefresh() void ControllerBindingWidget::createBindingWidgets(QWidget* parent) { - SettingsInterface* sif = getDialog()->getProfileSettingsInterface(); + SettingsInterface* sif = getDialog()->getEditingSettingsInterface(); DebugAssert(m_controller_info); QGroupBox* axis_gbox = nullptr; @@ -471,7 +471,7 @@ void ControllerBindingWidget::createBindingWidgets(QWidget* parent) void ControllerBindingWidget::bindBindingWidgets(QWidget* parent) { - SettingsInterface* sif = getDialog()->getProfileSettingsInterface(); + SettingsInterface* sif = getDialog()->getEditingSettingsInterface(); DebugAssert(m_controller_info); const std::string& config_section = getConfigSection(); @@ -601,12 +601,12 @@ ControllerMacroEditWidget::ControllerMacroEditWidget(ControllerMacroWidget* pare } m_frequency = dialog->getIntValue(section.c_str(), TinyString::from_format("Macro{}Frequency", index + 1u), 0); - ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(dialog->getProfileSettingsInterface(), m_ui.triggerToggle, + ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(dialog->getEditingSettingsInterface(), m_ui.triggerToggle, section.c_str(), fmt::format("Macro{}Toggle", index + 1u), false); updateFrequencyText(); - m_ui.trigger->initialize(dialog->getProfileSettingsInterface(), InputBindingInfo::Type::Macro, section, + m_ui.trigger->initialize(dialog->getEditingSettingsInterface(), InputBindingInfo::Type::Macro, section, fmt::format("Macro{}", index + 1u)); connect(m_ui.increaseFrequency, &QAbstractButton::clicked, this, [this]() { modFrequency(1); }); @@ -747,7 +747,7 @@ void ControllerCustomSettingsWidget::createSettingWidgets(ControllerBindingWidge QGridLayout* layout, const Controller::ControllerInfo* cinfo) { const std::string& section = parent->getConfigSection(); - SettingsInterface* sif = parent->getDialog()->getProfileSettingsInterface(); + SettingsInterface* sif = parent->getDialog()->getEditingSettingsInterface(); int current_row = 0; for (const SettingInfo& si : cinfo->settings) diff --git a/src/duckstation-qt/controllerglobalsettingswidget.cpp b/src/duckstation-qt/controllerglobalsettingswidget.cpp index 53a1077c9..0d64005de 100644 --- a/src/duckstation-qt/controllerglobalsettingswidget.cpp +++ b/src/duckstation-qt/controllerglobalsettingswidget.cpp @@ -14,7 +14,7 @@ ControllerGlobalSettingsWidget::ControllerGlobalSettingsWidget(QWidget* parent, { m_ui.setupUi(this); - SettingsInterface* sif = dialog->getProfileSettingsInterface(); + SettingsInterface* sif = dialog->getEditingSettingsInterface(); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableSDLSource, "InputSources", "SDL", true); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableSDLEnhancedMode, "InputSources", @@ -28,10 +28,10 @@ ControllerGlobalSettingsWidget::ControllerGlobalSettingsWidget(QWidget* parent, SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableSDLMFIDriver, "InputSources", "SDLMFIDriver", true); #else m_ui.sdlGridLayout->removeWidget(m_ui.enableSDLIOKitDriver); - m_ui.enableSDLIOKitDriver->deleteLater(); + delete m_ui.enableSDLIOKitDriver; m_ui.enableSDLIOKitDriver = nullptr; m_ui.sdlGridLayout->removeWidget(m_ui.enableSDLMFIDriver); - m_ui.enableSDLMFIDriver->deleteLater(); + delete m_ui.enableSDLMFIDriver; m_ui.enableSDLMFIDriver = nullptr; #endif @@ -41,10 +41,10 @@ ControllerGlobalSettingsWidget::ControllerGlobalSettingsWidget(QWidget* parent, SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableRawInput, "InputSources", "RawInput", false); #else m_ui.mainLayout->removeWidget(m_ui.xinputGroup); - m_ui.xinputGroup->deleteLater(); + delete m_ui.xinputGroup; m_ui.xinputGroup = nullptr; m_ui.mainLayout->removeWidget(m_ui.dinputGroup); - m_ui.dinputGroup->deleteLater(); + delete m_ui.dinputGroup; m_ui.dinputGroup = nullptr; #endif @@ -71,10 +71,13 @@ ControllerGlobalSettingsWidget::ControllerGlobalSettingsWidget(QWidget* parent, { // remove profile options from the UI. m_ui.mainLayout->removeWidget(m_ui.profileSettings); - m_ui.profileSettings->deleteLater(); + delete m_ui.profileSettings; m_ui.profileSettings = nullptr; } + if (dialog->isEditingGameSettings()) + m_ui.deviceListGroup->setEnabled(false); + connect(m_ui.multitapMode, &QComboBox::currentIndexChanged, this, [this]() { emit bindingSetupChanged(); }); connect(m_ui.pointerXScale, &QSlider::valueChanged, this, @@ -134,9 +137,10 @@ ControllerLEDSettingsDialog::ControllerLEDSettingsDialog(QWidget* parent, Contro linkButton(m_ui.SDL2LED, 2); linkButton(m_ui.SDL3LED, 3); - SettingsInterface* sif = dialog->getProfileSettingsInterface(); + SettingsInterface* sif = dialog->getEditingSettingsInterface(); - ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.enableSDLPS5PlayerLED, "InputSources", "SDLPS5PlayerLED", false); + ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.enableSDLPS5PlayerLED, "InputSources", + "SDLPS5PlayerLED", false); connect(m_ui.buttonBox->button(QDialogButtonBox::Close), &QPushButton::clicked, this, &QDialog::accept); } diff --git a/src/duckstation-qt/controllerglobalsettingswidget.ui b/src/duckstation-qt/controllerglobalsettingswidget.ui index 749d0b844..abf4160b2 100644 --- a/src/duckstation-qt/controllerglobalsettingswidget.ui +++ b/src/duckstation-qt/controllerglobalsettingswidget.ui @@ -50,7 +50,7 @@ - + Detected Devices @@ -162,8 +162,7 @@ Controller LED Settings - - .. + @@ -284,7 +283,7 @@ 30 - Qt::Horizontal + Qt::Orientation::Horizontal @@ -339,7 +338,7 @@ 30 - Qt::Horizontal + Qt::Orientation::Horizontal @@ -388,7 +387,7 @@ - Qt::Vertical + Qt::Orientation::Vertical diff --git a/src/duckstation-qt/controllersettingswindow.cpp b/src/duckstation-qt/controllersettingswindow.cpp index 59a31efbb..3da41c4d2 100644 --- a/src/duckstation-qt/controllersettingswindow.cpp +++ b/src/duckstation-qt/controllersettingswindow.cpp @@ -23,41 +23,87 @@ static constexpr const std::array s_mtap_slot_names = {{'A', 'B', 'C', 'D'}}; -ControllerSettingsWindow::ControllerSettingsWindow() : QWidget() +ControllerSettingsWindow::ControllerSettingsWindow(SettingsInterface* game_sif /* = nullptr */, + QWidget* parent /* = nullptr */) + : QWidget(parent), m_editing_settings_interface(game_sif) { m_ui.setupUi(this); setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); - refreshProfileList(); - createWidgets(); - m_ui.settingsCategory->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + connect(m_ui.settingsCategory, &QListWidget::currentRowChanged, this, &ControllerSettingsWindow::onCategoryCurrentRowChanged); - connect(m_ui.currentProfile, &QComboBox::currentIndexChanged, this, - &ControllerSettingsWindow::onCurrentProfileChanged); connect(m_ui.buttonBox, &QDialogButtonBox::rejected, this, &ControllerSettingsWindow::close); - connect(m_ui.newProfile, &QPushButton::clicked, this, &ControllerSettingsWindow::onNewProfileClicked); - connect(m_ui.applyProfile, &QPushButton::clicked, this, &ControllerSettingsWindow::onApplyProfileClicked); - connect(m_ui.deleteProfile, &QPushButton::clicked, this, &ControllerSettingsWindow::onDeleteProfileClicked); - connect(m_ui.restoreDefaults, &QPushButton::clicked, this, &ControllerSettingsWindow::onRestoreDefaultsClicked); - - connect(g_emu_thread, &EmuThread::onInputDevicesEnumerated, this, - &ControllerSettingsWindow::onInputDevicesEnumerated); - connect(g_emu_thread, &EmuThread::onInputDeviceConnected, this, &ControllerSettingsWindow::onInputDeviceConnected); - connect(g_emu_thread, &EmuThread::onInputDeviceDisconnected, this, - &ControllerSettingsWindow::onInputDeviceDisconnected); - connect(g_emu_thread, &EmuThread::onVibrationMotorsEnumerated, this, - &ControllerSettingsWindow::onVibrationMotorsEnumerated); - - // trigger a device enumeration to populate the device list - g_emu_thread->enumerateInputDevices(); - g_emu_thread->enumerateVibrationMotors(); + + if (!game_sif) + { + refreshProfileList(); + + m_ui.editProfileLayout->removeWidget(m_ui.copyGlobalSettings); + delete m_ui.copyGlobalSettings; + m_ui.copyGlobalSettings = nullptr; + + connect(m_ui.currentProfile, &QComboBox::currentIndexChanged, this, + &ControllerSettingsWindow::onCurrentProfileChanged); + connect(m_ui.newProfile, &QPushButton::clicked, this, &ControllerSettingsWindow::onNewProfileClicked); + connect(m_ui.applyProfile, &QPushButton::clicked, this, &ControllerSettingsWindow::onApplyProfileClicked); + connect(m_ui.deleteProfile, &QPushButton::clicked, this, &ControllerSettingsWindow::onDeleteProfileClicked); + connect(m_ui.restoreDefaults, &QPushButton::clicked, this, &ControllerSettingsWindow::onRestoreDefaultsClicked); + + connect(g_emu_thread, &EmuThread::onInputDevicesEnumerated, this, + &ControllerSettingsWindow::onInputDevicesEnumerated); + connect(g_emu_thread, &EmuThread::onInputDeviceConnected, this, &ControllerSettingsWindow::onInputDeviceConnected); + connect(g_emu_thread, &EmuThread::onInputDeviceDisconnected, this, + &ControllerSettingsWindow::onInputDeviceDisconnected); + connect(g_emu_thread, &EmuThread::onVibrationMotorsEnumerated, this, + &ControllerSettingsWindow::onVibrationMotorsEnumerated); + + // trigger a device enumeration to populate the device list + g_emu_thread->enumerateInputDevices(); + g_emu_thread->enumerateVibrationMotors(); + } + else + { + m_ui.editProfileLayout->removeWidget(m_ui.editProfileLabel); + delete m_ui.editProfileLabel; + m_ui.editProfileLabel = nullptr; + m_ui.editProfileLayout->removeWidget(m_ui.currentProfile); + delete m_ui.currentProfile; + m_ui.currentProfile = nullptr; + m_ui.editProfileLayout->removeWidget(m_ui.newProfile); + delete m_ui.newProfile; + m_ui.newProfile = nullptr; + m_ui.editProfileLayout->removeWidget(m_ui.applyProfile); + delete m_ui.applyProfile; + m_ui.applyProfile = nullptr; + m_ui.editProfileLayout->removeWidget(m_ui.deleteProfile); + delete m_ui.deleteProfile; + m_ui.deleteProfile = nullptr; + + connect(m_ui.copyGlobalSettings, &QPushButton::clicked, this, + &ControllerSettingsWindow::onCopyGlobalSettingsClicked); + connect(m_ui.restoreDefaults, &QPushButton::clicked, this, + &ControllerSettingsWindow::onRestoreDefaultsForGameClicked); + } + + createWidgets(); } ControllerSettingsWindow::~ControllerSettingsWindow() = default; +void ControllerSettingsWindow::editControllerSettingsForGame(QWidget* parent, SettingsInterface* sif) +{ + ControllerSettingsWindow* dlg = new ControllerSettingsWindow(sif, parent); + dlg->setWindowFlag(Qt::Window); + dlg->setAttribute(Qt::WA_DeleteOnClose); + dlg->setWindowModality(Qt::WindowModality::WindowModal); + dlg->setWindowTitle(parent->windowTitle()); + dlg->setWindowIcon(parent->windowIcon()); + dlg->show(); +} + int ControllerSettingsWindow::getHotkeyCategoryIndex() const { const std::array mtap_enabled = getEnabledMultitaps(); @@ -103,20 +149,26 @@ void ControllerSettingsWindow::onCategoryCurrentRowChanged(int row) void ControllerSettingsWindow::onCurrentProfileChanged(int index) { - switchProfile((index == 0) ? 0 : m_ui.currentProfile->itemText(index)); + std::string profile_name; + if (index > 0) + profile_name = m_ui.currentProfile->itemText(index).toStdString(); + + switchProfile(profile_name); } void ControllerSettingsWindow::onNewProfileClicked() { - const QString profile_name( - QInputDialog::getText(this, tr("Create Input Profile"), tr("Enter the name for the new input profile:"))); - if (profile_name.isEmpty()) + const std::string profile_name = + QInputDialog::getText(this, tr("Create Input Profile"), tr("Enter the name for the new input profile:")) + .toStdString(); + if (profile_name.empty()) return; - std::string profile_path(System::GetInputProfilePath(profile_name.toStdString())); + std::string profile_path = System::GetInputProfilePath(profile_name); if (FileSystem::FileExists(profile_path.c_str())) { - QMessageBox::critical(this, tr("Error"), tr("A profile with the name '%1' already exists.").arg(profile_name)); + QMessageBox::critical(this, tr("Error"), + tr("A profile with the name '%1' already exists.").arg(QString::fromStdString(profile_name))); return; } @@ -131,7 +183,7 @@ void ControllerSettingsWindow::onNewProfileClicked() if (res == QMessageBox::Yes) { // copy from global or the current profile - if (!m_profile_interface) + if (!m_editing_settings_interface) { const int hkres = QMessageBox::question( this, tr("Create Input Profile"), @@ -153,9 +205,9 @@ void ControllerSettingsWindow::onNewProfileClicked() { // from profile const bool copy_hotkey_bindings = - m_profile_interface->GetBoolValue("ControllerPorts", "UseProfileHotkeyBindings", false); + m_editing_settings_interface->GetBoolValue("ControllerPorts", "UseProfileHotkeyBindings", false); temp_si.SetBoolValue("ControllerPorts", "UseProfileHotkeyBindings", copy_hotkey_bindings); - InputManager::CopyConfiguration(&temp_si, *m_profile_interface, true, true, copy_hotkey_bindings); + InputManager::CopyConfiguration(&temp_si, *m_editing_settings_interface, true, true, copy_hotkey_bindings); } } @@ -184,9 +236,9 @@ void ControllerSettingsWindow::onApplyProfileClicked() { const bool copy_hotkey_bindings = - m_profile_interface->GetBoolValue("ControllerPorts", "UseProfileHotkeyBindings", false); + m_editing_settings_interface->GetBoolValue("ControllerPorts", "UseProfileHotkeyBindings", false); auto lock = Host::GetSettingsLock(); - InputManager::CopyConfiguration(Host::Internal::GetBaseSettingsLayer(), *m_profile_interface, true, true, + InputManager::CopyConfiguration(Host::Internal::GetBaseSettingsLayer(), *m_editing_settings_interface, true, true, copy_hotkey_bindings); QtHost::QueueSettingsSave(); } @@ -236,6 +288,36 @@ void ControllerSettingsWindow::onRestoreDefaultsClicked() switchProfile({}); } +void ControllerSettingsWindow::onCopyGlobalSettingsClicked() +{ + DebugAssert(isEditingGameSettings()); + + { + const auto lock = Host::GetSettingsLock(); + InputManager::CopyConfiguration(m_editing_settings_interface, *Host::Internal::GetBaseSettingsLayer(), true, true, + false); + } + + m_editing_settings_interface->Save(); + g_emu_thread->reloadGameSettings(); + createWidgets(); + + QMessageBox::information(QtUtils::GetRootWidget(this), tr("DuckStation Controller Settings"), + tr("Per-game controller configuration reset to global settings.")); +} + +void ControllerSettingsWindow::onRestoreDefaultsForGameClicked() +{ + DebugAssert(isEditingGameSettings()); + Settings::SetDefaultControllerConfig(*m_editing_settings_interface); + m_editing_settings_interface->Save(); + g_emu_thread->reloadGameSettings(); + createWidgets(); + + QMessageBox::information(QtUtils::GetRootWidget(this), tr("DuckStation Controller Settings"), + tr("Per-game controller configuration reset to default settings.")); +} + void ControllerSettingsWindow::onInputDevicesEnumerated(const std::vector>& devices) { m_device_list = devices; @@ -280,16 +362,16 @@ void ControllerSettingsWindow::onVibrationMotorsEnumerated(const QListGetBoolValue(section, key, default_value); + if (m_editing_settings_interface) + return m_editing_settings_interface->GetBoolValue(section, key, default_value); else return Host::GetBaseBoolSettingValue(section, key, default_value); } s32 ControllerSettingsWindow::getIntValue(const char* section, const char* key, s32 default_value) const { - if (m_profile_interface) - return m_profile_interface->GetIntValue(section, key, default_value); + if (m_editing_settings_interface) + return m_editing_settings_interface->GetIntValue(section, key, default_value); else return Host::GetBaseIntSettingValue(section, key, default_value); } @@ -298,8 +380,8 @@ std::string ControllerSettingsWindow::getStringValue(const char* section, const const char* default_value) const { std::string value; - if (m_profile_interface) - value = m_profile_interface->GetStringValue(section, key, default_value); + if (m_editing_settings_interface) + value = m_editing_settings_interface->GetStringValue(section, key, default_value); else value = Host::GetBaseStringSettingValue(section, key, default_value); return value; @@ -307,9 +389,9 @@ std::string ControllerSettingsWindow::getStringValue(const char* section, const void ControllerSettingsWindow::setBoolValue(const char* section, const char* key, bool value) { - if (m_profile_interface) + if (m_editing_settings_interface) { - m_profile_interface->SetBoolValue(section, key, value); + m_editing_settings_interface->SetBoolValue(section, key, value); saveAndReloadGameSettings(); } else @@ -322,9 +404,9 @@ void ControllerSettingsWindow::setBoolValue(const char* section, const char* key void ControllerSettingsWindow::setIntValue(const char* section, const char* key, s32 value) { - if (m_profile_interface) + if (m_editing_settings_interface) { - m_profile_interface->SetIntValue(section, key, value); + m_editing_settings_interface->SetIntValue(section, key, value); saveAndReloadGameSettings(); } else @@ -337,9 +419,9 @@ void ControllerSettingsWindow::setIntValue(const char* section, const char* key, void ControllerSettingsWindow::setStringValue(const char* section, const char* key, const char* value) { - if (m_profile_interface) + if (m_editing_settings_interface) { - m_profile_interface->SetStringValue(section, key, value); + m_editing_settings_interface->SetStringValue(section, key, value); saveAndReloadGameSettings(); } else @@ -352,17 +434,17 @@ void ControllerSettingsWindow::setStringValue(const char* section, const char* k void ControllerSettingsWindow::saveAndReloadGameSettings() { - DebugAssert(m_profile_interface); - QtHost::SaveGameSettings(m_profile_interface.get(), false); + DebugAssert(m_editing_settings_interface); + QtHost::SaveGameSettings(m_editing_settings_interface, false); g_emu_thread->reloadGameSettings(false); } void ControllerSettingsWindow::clearSettingValue(const char* section, const char* key) { - if (m_profile_interface) + if (m_editing_settings_interface) { - m_profile_interface->DeleteValue(section, key); - m_profile_interface->Save(); + m_editing_settings_interface->DeleteValue(section, key); + m_editing_settings_interface->Save(); g_emu_thread->reloadGameSettings(); } else @@ -434,7 +516,8 @@ void ControllerSettingsWindow::createWidgets() } // only add hotkeys if we're editing global settings - if (!m_profile_interface || m_profile_interface->GetBoolValue("ControllerPorts", "UseProfileHotkeyBindings", false)) + if (!m_editing_settings_interface || + m_editing_settings_interface->GetBoolValue("ControllerPorts", "UseProfileHotkeyBindings", false)) { QListWidgetItem* item = new QListWidgetItem(); item->setText(tr("Hotkeys")); @@ -444,9 +527,18 @@ void ControllerSettingsWindow::createWidgets() m_ui.settingsContainer->addWidget(m_hotkey_settings); } - m_ui.applyProfile->setEnabled(isEditingProfile()); - m_ui.deleteProfile->setEnabled(isEditingProfile()); - m_ui.restoreDefaults->setEnabled(isEditingGlobalSettings()); + if (!isEditingGameSettings()) + { + m_ui.applyProfile->setEnabled(isEditingProfile()); + m_ui.deleteProfile->setEnabled(isEditingProfile()); + m_ui.restoreDefaults->setEnabled(isEditingGlobalSettings()); + } +} + +void ControllerSettingsWindow::closeEvent(QCloseEvent* event) +{ + QWidget::closeEvent(event); + emit windowClosed(); } void ControllerSettingsWindow::updateListDescription(u32 global_slot, ControllerBindingWidget* widget) @@ -501,30 +593,36 @@ void ControllerSettingsWindow::refreshProfileList() } } -void ControllerSettingsWindow::switchProfile(const QString& name) +void ControllerSettingsWindow::switchProfile(const std::string_view name) { QSignalBlocker sb(m_ui.currentProfile); - if (!name.isEmpty()) + if (!name.empty()) { - std::string path(System::GetInputProfilePath(name.toStdString())); + const QString name_qstr = QtUtils::StringViewToQString(name); + + std::string path = System::GetInputProfilePath(name); if (!FileSystem::FileExists(path.c_str())) { - QMessageBox::critical(this, tr("Error"), tr("The input profile named '%1' cannot be found.").arg(name)); + QMessageBox::critical(this, tr("Error"), tr("The input profile named '%1' cannot be found.").arg(name_qstr)); return; } - std::unique_ptr sif(std::make_unique(std::move(path))); + std::unique_ptr sif = std::make_unique(std::move(path)); sif->Load(); - m_profile_interface = std::move(sif); - m_ui.currentProfile->setCurrentIndex(m_ui.currentProfile->findText(name)); + + m_profile_settings_interface = std::move(sif); + m_editing_settings_interface = m_profile_settings_interface.get(); + m_ui.currentProfile->setCurrentIndex(m_ui.currentProfile->findText(name_qstr)); + m_profile_name = name_qstr; } else { - m_profile_interface.reset(); + m_profile_settings_interface.reset(); + m_editing_settings_interface = nullptr; m_ui.currentProfile->setCurrentIndex(0); + m_profile_name = QString(); } - m_profile_name = name; createWidgets(); } diff --git a/src/duckstation-qt/controllersettingswindow.h b/src/duckstation-qt/controllersettingswindow.h index 78ec50ee6..7f080d627 100644 --- a/src/duckstation-qt/controllersettingswindow.h +++ b/src/duckstation-qt/controllersettingswindow.h @@ -20,6 +20,8 @@ #include #include +class Error; + class ControllerGlobalSettingsWidget; class ControllerBindingWidget; class HotkeySettingsWidget; @@ -44,22 +46,33 @@ public: MAX_PORTS = 8 }; - ControllerSettingsWindow(); + ControllerSettingsWindow(SettingsInterface* game_sif = nullptr, QWidget* parent = nullptr); ~ControllerSettingsWindow(); + static void editControllerSettingsForGame(QWidget* parent, SettingsInterface* sif); + ALWAYS_INLINE HotkeySettingsWidget* getHotkeySettingsWidget() const { return m_hotkey_settings; } ALWAYS_INLINE const std::vector>& getDeviceList() const { return m_device_list; } ALWAYS_INLINE const QStringList& getVibrationMotors() const { return m_vibration_motors; } - ALWAYS_INLINE bool isEditingGlobalSettings() const { return m_profile_name.isEmpty(); } + ALWAYS_INLINE bool isEditingGlobalSettings() const + { + return (m_profile_name.isEmpty() && !m_editing_settings_interface); + } + ALWAYS_INLINE bool isEditingGameSettings() const + { + return (m_profile_name.isEmpty() && m_editing_settings_interface); + } ALWAYS_INLINE bool isEditingProfile() const { return !m_profile_name.isEmpty(); } - ALWAYS_INLINE SettingsInterface* getProfileSettingsInterface() { return m_profile_interface.get(); } + ALWAYS_INLINE SettingsInterface* getEditingSettingsInterface() { return m_editing_settings_interface; } Category getCurrentCategory() const; void updateListDescription(u32 global_slot, ControllerBindingWidget* widget); + void switchProfile(const std::string_view name); + // Helper functions for updating setting values globally or in the profile. bool getBoolValue(const char* section, const char* key, bool default_value) const; s32 getIntValue(const char* section, const char* key, s32 default_value) const; @@ -71,6 +84,7 @@ public: void saveAndReloadGameSettings(); Q_SIGNALS: + void windowClosed(); void inputProfileSwitched(); public Q_SLOTS: @@ -83,6 +97,8 @@ private Q_SLOTS: void onApplyProfileClicked(); void onDeleteProfileClicked(); void onRestoreDefaultsClicked(); + void onCopyGlobalSettingsClicked(); + void onRestoreDefaultsForGameClicked(); void onInputDevicesEnumerated(const std::vector>& devices); void onInputDeviceConnected(const std::string& identifier, const std::string& device_name); @@ -91,15 +107,19 @@ private Q_SLOTS: void createWidgets(); +protected: + void closeEvent(QCloseEvent* event) override; + private: int getHotkeyCategoryIndex() const; void refreshProfileList(); - void switchProfile(const QString& name); std::array getEnabledMultitaps() const; Ui::ControllerSettingsWindow m_ui; + SettingsInterface* m_editing_settings_interface = nullptr; + ControllerGlobalSettingsWidget* m_global_settings = nullptr; std::array m_port_bindings{}; HotkeySettingsWidget* m_hotkey_settings = nullptr; @@ -108,5 +128,5 @@ private: QStringList m_vibration_motors; QString m_profile_name; - std::unique_ptr m_profile_interface; + std::unique_ptr m_profile_settings_interface; }; diff --git a/src/duckstation-qt/controllersettingswindow.ui b/src/duckstation-qt/controllersettingswindow.ui index fa77a8495..0b63346e6 100644 --- a/src/duckstation-qt/controllersettingswindow.ui +++ b/src/duckstation-qt/controllersettingswindow.ui @@ -6,7 +6,7 @@ 0 0 - 1279 + 1284 672 @@ -20,7 +20,7 @@ DuckStation Controller Settings - + :/icons/duck.png:/icons/duck.png @@ -65,9 +65,9 @@ - + - + Editing Profile: @@ -106,6 +106,16 @@ + + + + Copy Global Settings + + + + + + @@ -130,7 +140,7 @@ - + diff --git a/src/duckstation-qt/gamesummarywidget.cpp b/src/duckstation-qt/gamesummarywidget.cpp index 299e39362..aafb92ebb 100644 --- a/src/duckstation-qt/gamesummarywidget.cpp +++ b/src/duckstation-qt/gamesummarywidget.cpp @@ -11,6 +11,7 @@ #include "core/game_database.h" #include "core/game_list.h" +#include "common/error.h" #include "common/string_util.h" #include "fmt/format.h" @@ -54,6 +55,7 @@ GameSummaryWidget::GameSummaryWidget(const std::string& path, const std::string& connect(m_ui.compatibilityComments, &QToolButton::clicked, this, &GameSummaryWidget::onCompatibilityCommentsClicked); connect(m_ui.inputProfile, &QComboBox::currentIndexChanged, this, &GameSummaryWidget::onInputProfileChanged); + connect(m_ui.editInputProfile, &QAbstractButton::clicked, this, &GameSummaryWidget::onEditInputProfileClicked); connect(m_ui.computeHashes, &QAbstractButton::clicked, this, &GameSummaryWidget::onComputeHashClicked); connect(m_ui.title, &QLineEdit::editingFinished, this, [this]() { @@ -159,15 +161,27 @@ void GameSummaryWidget::populateUi(const std::string& path, const std::string& s m_ui.compatibilityComments->setVisible(!m_compatibility_comments.isEmpty()); - m_ui.inputProfile->addItem(QIcon::fromTheme(QStringLiteral("controller-digital-line")), tr("Use Global Settings")); + m_ui.inputProfile->addItem(QIcon::fromTheme(QStringLiteral("global-line")), tr("Use Global Settings")); + m_ui.inputProfile->addItem(QIcon::fromTheme(QStringLiteral("controller-digital-line")), + tr("Game Specific Configuration")); for (const std::string& name : InputManager::GetInputProfileNames()) m_ui.inputProfile->addItem(QString::fromStdString(name)); - std::optional profile(m_dialog->getStringValue("ControllerPorts", "InputProfileName", std::nullopt)); - if (profile.has_value()) - m_ui.inputProfile->setCurrentIndex(m_ui.inputProfile->findText(QString::fromStdString(profile.value()))); + if (m_dialog->getBoolValue("ControllerPorts", "UseGameSettingsForController", std::nullopt).value_or(false)) + { + m_ui.inputProfile->setCurrentIndex(1); + } + else if (const std::optional profile_name = + m_dialog->getStringValue("ControllerPorts", "InputProfileName", std::nullopt); + profile_name.has_value() && !profile_name->empty()) + { + m_ui.inputProfile->setCurrentIndex(m_ui.inputProfile->findText(QString::fromStdString(profile_name.value()))); + } else + { m_ui.inputProfile->setCurrentIndex(0); + } + m_ui.editInputProfile->setEnabled(m_ui.inputProfile->currentIndex() >= 1); populateCustomAttributes(); populateTracksInfo(); @@ -221,6 +235,21 @@ void GameSummaryWidget::setCustomRegion(int region) g_main_window->refreshGameListModel(); } +void GameSummaryWidget::setRevisionText(const QString& text) +{ + if (text.isEmpty()) + return; + + if (m_ui.verifySpacer) + { + m_ui.verifyLayout->removeItem(m_ui.verifySpacer); + delete m_ui.verifySpacer; + m_ui.verifySpacer = nullptr; + } + m_ui.revision->setText(text); + m_ui.revision->setVisible(true); +} + static QString MSFTotString(const CDImage::Position& position) { return QStringLiteral("%1:%2:%3 (LBA %4)") @@ -242,6 +271,11 @@ void GameSummaryWidget::populateTracksInfo() if (!image) return; + setRevisionText(tr("%1 tracks covering %2 MB (%3 MB on disk)") + .arg(image->GetTrackCount()) + .arg(((image->GetLBACount() * CDImage::RAW_SECTOR_SIZE) + 1048575) / 1048576) + .arg((image->GetSizeOnDisk() + 1048575) / 1048576)); + const u32 num_tracks = image->GetTrackCount(); for (u32 track = 1; track <= num_tracks; track++) { @@ -288,10 +322,61 @@ void GameSummaryWidget::onCompatibilityCommentsClicked() void GameSummaryWidget::onInputProfileChanged(int index) { + + SettingsInterface* sif = m_dialog->getSettingsInterface(); if (index == 0) - m_dialog->setStringSettingValue("ControllerPorts", "InputProfileName", std::nullopt); + { + // Use global settings. + sif->DeleteValue("ControllerPorts", "InputProfileName"); + sif->DeleteValue("ControllerPorts", "UseGameSettingsForController"); + } + else if (index == 1) + { + // Per-game configuration. + sif->DeleteValue("ControllerPorts", "InputProfileName"); + sif->SetBoolValue("ControllerPorts", "UseGameSettingsForController", true); + + if (!sif->GetBoolValue("ControllerPorts", "GameSettingsInitialized", false)) + { + sif->SetBoolValue("ControllerPorts", "GameSettingsInitialized", true); + + { + const auto lock = Host::GetSettingsLock(); + SettingsInterface* base_sif = Host::Internal::GetBaseSettingsLayer(); + InputManager::CopyConfiguration(sif, *base_sif, true, true, false); + + QWidget* dlg_parent = QtUtils::GetRootWidget(this); + QMessageBox::information(dlg_parent, dlg_parent->windowTitle(), + tr("Per-game controller configuration initialized with global settings.")); + } + } + } else - m_dialog->setStringSettingValue("ControllerPorts", "InputProfileName", m_ui.inputProfile->itemText(index).toUtf8()); + { + // Input profile. + sif->SetStringValue("ControllerPorts", "InputProfileName", m_ui.inputProfile->itemText(index).toUtf8()); + sif->DeleteValue("ControllerPorts", "UseGameSettingsForController"); + } + + m_dialog->saveAndReloadGameSettings(); + m_ui.editInputProfile->setEnabled(index > 0); +} + +void GameSummaryWidget::onEditInputProfileClicked() +{ + if (m_dialog->getBoolValue("ControllerPorts", "UseGameSettingsForController", std::nullopt).value_or(false)) + { + // Edit game configuration. + ControllerSettingsWindow::editControllerSettingsForGame(QtUtils::GetRootWidget(this), + m_dialog->getSettingsInterface()); + } + else if (const std::optional profile_name = + m_dialog->getStringValue("ControllerPorts", "InputProfileName", std::nullopt); + profile_name.has_value() && !profile_name->empty()) + { + // Edit input profile. + g_main_window->openInputProfileEditor(profile_name.value()); + } } void GameSummaryWidget::onComputeHashClicked() @@ -417,17 +502,7 @@ void GameSummaryWidget::onComputeHashClicked() text = mismatch_str; } - if (!text.isEmpty()) - { - if (m_ui.verifySpacer) - { - m_ui.verifyLayout->removeItem(m_ui.verifySpacer); - delete m_ui.verifySpacer; - m_ui.verifySpacer = nullptr; - } - m_ui.revision->setText(text); - m_ui.revision->setVisible(true); - } + setRevisionText(text); } for (u8 track = 0; track < image->GetTrackCount(); track++) diff --git a/src/duckstation-qt/gamesummarywidget.h b/src/duckstation-qt/gamesummarywidget.h index cbc11fc25..b27526f05 100644 --- a/src/duckstation-qt/gamesummarywidget.h +++ b/src/duckstation-qt/gamesummarywidget.h @@ -27,6 +27,7 @@ public: private Q_SLOTS: void onCompatibilityCommentsClicked(); void onInputProfileChanged(int index); + void onEditInputProfileClicked(); void onComputeHashClicked(); private: @@ -36,6 +37,7 @@ private: void updateWindowTitle(); void setCustomTitle(const std::string& text); void setCustomRegion(int region); + void setRevisionText(const QString& text); void populateTracksInfo(); diff --git a/src/duckstation-qt/gamesummarywidget.ui b/src/duckstation-qt/gamesummarywidget.ui index 66734be96..c4f21e620 100644 --- a/src/duckstation-qt/gamesummarywidget.ui +++ b/src/duckstation-qt/gamesummarywidget.ui @@ -60,17 +60,17 @@ - - - - - 0 - 0 - - - - - + + + + + 0 + 0 + + + + + false @@ -79,7 +79,7 @@ Restore - + @@ -106,7 +106,7 @@ - QAbstractItemView::NoEditTriggers + QAbstractItemView::EditTrigger::NoEditTriggers false @@ -216,15 +216,12 @@ - - - - Qt::Horizontal + Qt::Orientation::Horizontal @@ -306,8 +303,21 @@ Comments - - .. + + + + + + + + + + + + + + + Edit... diff --git a/src/duckstation-qt/hotkeysettingswidget.cpp b/src/duckstation-qt/hotkeysettingswidget.cpp index 66e94606b..f71b8509a 100644 --- a/src/duckstation-qt/hotkeysettingswidget.cpp +++ b/src/duckstation-qt/hotkeysettingswidget.cpp @@ -73,7 +73,7 @@ void HotkeySettingsWidget::createButtons() QLabel* label = new QLabel(qApp->translate("Hotkeys", hotkey->display_name), m_container); layout->addWidget(label, target_row, 0); - InputBindingWidget* bind = new InputBindingWidget(m_container, m_dialog->getProfileSettingsInterface(), + InputBindingWidget* bind = new InputBindingWidget(m_container, m_dialog->getEditingSettingsInterface(), InputBindingInfo::Type::Button, "Hotkeys", hotkey->name); bind->setMinimumWidth(300); layout->addWidget(bind, target_row, 1); diff --git a/src/duckstation-qt/mainwindow.cpp b/src/duckstation-qt/mainwindow.cpp index cbf255bc0..bfece5b39 100644 --- a/src/duckstation-qt/mainwindow.cpp +++ b/src/duckstation-qt/mainwindow.cpp @@ -2394,6 +2394,13 @@ void MainWindow::doControllerSettings( dlg->setCategory(category); } +void MainWindow::openInputProfileEditor(const std::string_view name) +{ + ControllerSettingsWindow* dlg = getControllerSettingsWindow(); + QtUtils::ShowOrRaiseWindow(dlg); + dlg->switchProfile(name); +} + void MainWindow::updateDebugMenuCPUExecutionMode() { std::optional current_mode = diff --git a/src/duckstation-qt/mainwindow.h b/src/duckstation-qt/mainwindow.h index 36425e3da..fba4d7e36 100644 --- a/src/duckstation-qt/mainwindow.h +++ b/src/duckstation-qt/mainwindow.h @@ -99,6 +99,9 @@ public: /// Accessors for child windows. CheatManagerWindow* getCheatManagerWindow() const { return m_cheat_manager_window; } + /// Opens the editor for a specific input profile. + void openInputProfileEditor(const std::string_view name); + public Q_SLOTS: /// Updates debug menu visibility (hides if disabled). void updateDebugMenuVisibility(); diff --git a/src/duckstation-qt/settingswindow.cpp b/src/duckstation-qt/settingswindow.cpp index 443226d40..67a7eb90d 100644 --- a/src/duckstation-qt/settingswindow.cpp +++ b/src/duckstation-qt/settingswindow.cpp @@ -296,7 +296,7 @@ void SettingsWindow::onCopyGlobalSettingsClicked() { auto lock = Host::GetSettingsLock(); Settings temp; - temp.Load(*Host::Internal::GetBaseSettingsLayer()); + temp.Load(*Host::Internal::GetBaseSettingsLayer(), *Host::Internal::GetBaseSettingsLayer()); temp.Save(*m_sif.get(), true); } saveAndReloadGameSettings(); diff --git a/src/duckstation-qt/settingswindow.h b/src/duckstation-qt/settingswindow.h index 3486dacd5..d6ef46066 100644 --- a/src/duckstation-qt/settingswindow.h +++ b/src/duckstation-qt/settingswindow.h @@ -93,6 +93,7 @@ public: bool containsSettingValue(const char* section, const char* key) const; void removeSettingValue(const char* section, const char* key); void saveAndReloadGameSettings(); + void reloadGameSettingsFromIni(); bool hasGameTrait(GameDatabase::Trait trait); diff --git a/src/util/ini_settings_interface.cpp b/src/util/ini_settings_interface.cpp index ab9ddabbd..6e3b1c8ac 100644 --- a/src/util/ini_settings_interface.cpp +++ b/src/util/ini_settings_interface.cpp @@ -74,16 +74,23 @@ INISettingsInterface::~INISettingsInterface() Save(); } -bool INISettingsInterface::Load() +bool INISettingsInterface::Load(Error* error /* = nullptr */) { if (m_filename.empty()) + { + Error::SetStringView(error, "Filename is not set."); return false; + } std::unique_lock lock(s_ini_load_save_mutex); SI_Error err = SI_FAIL; - auto fp = FileSystem::OpenManagedCFile(m_filename.c_str(), "rb"); + auto fp = FileSystem::OpenManagedCFile(m_filename.c_str(), "rb", error); if (fp) + { err = m_ini.LoadFile(fp.get()); + if (err != SI_OK) + Error::SetStringFmt(error, "INI LoadFile() failed: {}", static_cast(err)); + } return (err == SI_OK); } diff --git a/src/util/ini_settings_interface.h b/src/util/ini_settings_interface.h index eba6f13dc..8e1c19423 100644 --- a/src/util/ini_settings_interface.h +++ b/src/util/ini_settings_interface.h @@ -18,7 +18,7 @@ public: const std::string& GetFileName() const { return m_filename; } - bool Load(); + bool Load(Error* error = nullptr); bool Save(Error* error = nullptr) override; void Clear() override; diff --git a/src/util/input_manager.cpp b/src/util/input_manager.cpp index 542ca4561..8d7cf1fc4 100644 --- a/src/util/input_manager.cpp +++ b/src/util/input_manager.cpp @@ -1809,8 +1809,7 @@ bool InputManager::DoEventHook(InputBindingKey key, float value) // Binding Updater // ------------------------------------------------------------------------ -void InputManager::ReloadBindings(SettingsInterface& si, SettingsInterface& binding_si, - SettingsInterface& hotkey_binding_si) +void InputManager::ReloadBindings(SettingsInterface& binding_si, SettingsInterface& hotkey_binding_si) { PauseVibration(); @@ -1843,8 +1842,8 @@ void InputManager::ReloadBindings(SettingsInterface& si, SettingsInterface& bind // From lilypad: 1 mouse pixel = 1/8th way down. const float default_scale = (axis <= static_cast(InputPointerAxis::Y)) ? 8.0f : 1.0f; s_pointer_axis_scale[axis] = - 1.0f / std::max(si.GetFloatValue("Pad", fmt::format("Pointer{}Scale", s_pointer_axis_names[axis]).c_str(), - default_scale), + 1.0f / std::max(binding_si.GetFloatValue("Pad", fmt::format("Pointer{}Scale", s_pointer_axis_names[axis]).c_str(), + default_scale), 1.0f); } diff --git a/src/util/input_manager.h b/src/util/input_manager.h index dfba363c1..b878645ae 100644 --- a/src/util/input_manager.h +++ b/src/util/input_manager.h @@ -253,7 +253,7 @@ GenericInputBindingMapping GetGenericBindingMapping(std::string_view device); bool IsInputSourceEnabled(SettingsInterface& si, InputSourceType type); /// Re-parses the config and registers all hotkey and pad bindings. -void ReloadBindings(SettingsInterface& si, SettingsInterface& binding_si, SettingsInterface& hotkey_binding_si); +void ReloadBindings(SettingsInterface& si, SettingsInterface& hotkey_binding_si); /// Re-parses the sources part of the config and initializes any backends. void ReloadSources(SettingsInterface& si, std::unique_lock& settings_lock);