diff --git a/pcsx2-qt/CMakeLists.txt b/pcsx2-qt/CMakeLists.txt index 0ff06e19cd..99973a897c 100644 --- a/pcsx2-qt/CMakeLists.txt +++ b/pcsx2-qt/CMakeLists.txt @@ -60,6 +60,7 @@ target_sources(pcsx2-qt PRIVATE Settings/ControllerSettingsDialog.cpp Settings/ControllerSettingsDialog.h Settings/ControllerSettingsDialog.ui + Settings/ControllerSettingWidgetBinder.h Settings/CreateMemoryCardDialog.cpp Settings/CreateMemoryCardDialog.h Settings/CreateMemoryCardDialog.ui diff --git a/pcsx2-qt/EmuThread.cpp b/pcsx2-qt/EmuThread.cpp index 1e126df475..b5fbaf99d1 100644 --- a/pcsx2-qt/EmuThread.cpp +++ b/pcsx2-qt/EmuThread.cpp @@ -504,11 +504,12 @@ void EmuThread::reloadInputSources() std::unique_lock lock = Host::GetSettingsLock(); SettingsInterface* si = Host::GetSettingsInterface(); + SettingsInterface* bindings_si = Host::GetSettingsInterfaceForBindings(); InputManager::ReloadSources(*si, lock); // skip loading bindings if we're not running, since it'll get done on startup anyway if (VMManager::HasValidVM()) - InputManager::ReloadBindings(*si); + InputManager::ReloadBindings(*si, *bindings_si); } void EmuThread::reloadInputBindings() @@ -525,7 +526,8 @@ void EmuThread::reloadInputBindings() auto lock = Host::GetSettingsLock(); SettingsInterface* si = Host::GetSettingsInterface(); - InputManager::ReloadBindings(*si); + SettingsInterface* bindings_si = Host::GetSettingsInterfaceForBindings(); + InputManager::ReloadBindings(*si, *bindings_si); } void EmuThread::requestDisplaySize(float scale) diff --git a/pcsx2-qt/QtUtils.cpp b/pcsx2-qt/QtUtils.cpp index cc8aa47dc2..4ee8e5caf6 100644 --- a/pcsx2-qt/QtUtils.cpp +++ b/pcsx2-qt/QtUtils.cpp @@ -690,4 +690,15 @@ namespace QtUtils { return str.empty() ? QString() : QString::fromUtf8(str.data(), str.size()); } + + void SetWidgetFontForInheritedSetting(QWidget* widget, bool inherited) + { + if (widget->font().italic() != inherited) + { + QFont new_font(widget->font()); + new_font.setItalic(inherited); + widget->setFont(new_font); + } + } + } // namespace QtUtils diff --git a/pcsx2-qt/QtUtils.h b/pcsx2-qt/QtUtils.h index dda073d7fe..3301ff84e1 100644 --- a/pcsx2-qt/QtUtils.h +++ b/pcsx2-qt/QtUtils.h @@ -76,4 +76,7 @@ namespace QtUtils /// Converts a std::string_view to a QString safely. QString StringViewToQString(const std::string_view& str); + + /// Sets a widget to italics if the setting value is inherited. + void SetWidgetFontForInheritedSetting(QWidget* widget, bool inherited); } // namespace QtUtils \ No newline at end of file diff --git a/pcsx2-qt/Settings/ControllerBindingWidget.ui b/pcsx2-qt/Settings/ControllerBindingWidget.ui index eb542dd762..327d289d3c 100644 --- a/pcsx2-qt/Settings/ControllerBindingWidget.ui +++ b/pcsx2-qt/Settings/ControllerBindingWidget.ui @@ -6,7 +6,7 @@ 0 0 - 834 + 833 560 @@ -61,35 +61,14 @@ - - - - Load Profile - - - - .. - - - - - - - Save Profile - - - - .. - - - Clear Bindings - + + .. diff --git a/pcsx2-qt/Settings/ControllerBindingWidgets.cpp b/pcsx2-qt/Settings/ControllerBindingWidgets.cpp index 5748279d06..29cbbebfd0 100644 --- a/pcsx2-qt/Settings/ControllerBindingWidgets.cpp +++ b/pcsx2-qt/Settings/ControllerBindingWidgets.cpp @@ -19,19 +19,18 @@ #include #include -#include "ControllerBindingWidgets.h" -#include "ControllerSettingsDialog.h" +#include "Settings/ControllerBindingWidgets.h" +#include "Settings/ControllerSettingsDialog.h" +#include "Settings/ControllerSettingWidgetBinder.h" +#include "Settings/SettingsDialog.h" #include "EmuThread.h" #include "QtUtils.h" #include "SettingWidgetBinder.h" -#include "SettingsDialog.h" #include "common/StringUtil.h" #include "pcsx2/HostSettings.h" #include "pcsx2/PAD/Host/PAD.h" -#include "SettingWidgetBinder.h" - ControllerBindingWidget::ControllerBindingWidget(QWidget* parent, ControllerSettingsDialog* dialog, u32 port) : QWidget(parent) , m_dialog(dialog) @@ -42,7 +41,9 @@ ControllerBindingWidget::ControllerBindingWidget(QWidget* parent, ControllerSett populateControllerTypes(); onTypeChanged(); - SettingWidgetBinder::BindWidgetToStringSetting(nullptr, m_ui.controllerType, m_config_section, "Type", "None"); + ControllerSettingWidgetBinder::BindWidgetToInputProfileString(m_dialog->getProfileSettingsInterface(), + m_ui.controllerType, m_config_section, "Type", PAD::GetDefaultPadType(port)); + connect(m_ui.controllerType, QOverload::of(&QComboBox::currentIndexChanged), this, &ControllerBindingWidget::onTypeChanged); connect(m_ui.automaticBinding, &QPushButton::clicked, this, &ControllerBindingWidget::doAutomaticBinding); connect(m_ui.clearBindings, &QPushButton::clicked, this, &ControllerBindingWidget::doClearBindings); @@ -50,6 +51,11 @@ ControllerBindingWidget::ControllerBindingWidget(QWidget* parent, ControllerSett ControllerBindingWidget::~ControllerBindingWidget() = default; +QIcon ControllerBindingWidget::getIcon() const +{ + return m_current_widget->getIcon(); +} + void ControllerBindingWidget::populateControllerTypes() { for (const auto& [name, display_name] : PAD::GetControllerTypeNames()) @@ -58,15 +64,16 @@ void ControllerBindingWidget::populateControllerTypes() void ControllerBindingWidget::onTypeChanged() { - if (m_current_widget) + const bool is_initializing = (m_current_widget == nullptr); + m_controller_type = m_dialog->getStringValue(m_config_section.c_str(), "Type", PAD::GetDefaultPadType(m_port_number)); + + if (!is_initializing) { m_ui.verticalLayout->removeWidget(m_current_widget); delete m_current_widget; m_current_widget = nullptr; } - m_controller_type = Host::GetBaseStringSettingValue(m_config_section.c_str(), "Type"); - const int index = m_ui.controllerType->findData(QString::fromStdString(m_controller_type)); if (index >= 0 && index != m_ui.controllerType->currentIndex()) { @@ -80,6 +87,10 @@ void ControllerBindingWidget::onTypeChanged() m_current_widget = new ControllerBindingWidget_Base(this); m_ui.verticalLayout->addWidget(m_current_widget, 1); + + // no need to do this on first init, only changes + if (!is_initializing) + m_dialog->updateListDescription(m_port_number, this); } void ControllerBindingWidget::doAutomaticBinding() @@ -110,15 +121,20 @@ void ControllerBindingWidget::doAutomaticBinding() void ControllerBindingWidget::doClearBindings() { if (QMessageBox::question(QtUtils::GetRootWidget(this), tr("Clear Bindings"), - tr("Are you sure you want to clear all bindings for this controller? This action cannot be undone.")) != QMessageBox::Yes) + tr("Are you sure you want to clear all bindings for this controller? This action cannot be undone.")) != QMessageBox::Yes) { return; } + if (m_dialog->isEditingGlobalSettings()) { auto lock = Host::GetSettingsLock(); PAD::ClearPortBindings(*Host::Internal::GetBaseSettingsLayer(), m_port_number); } + else + { + PAD::ClearPortBindings(*m_dialog->getProfileSettingsInterface(), m_port_number); + } saveAndRefresh(); } @@ -134,10 +150,17 @@ void ControllerBindingWidget::doDeviceAutomaticBinding(const QString& device) } bool result; + if (m_dialog->isEditingGlobalSettings()) { auto lock = Host::GetSettingsLock(); result = PAD::MapController(*Host::Internal::GetBaseSettingsLayer(), m_port_number, mapping); } + else + { + result = PAD::MapController(*m_dialog->getProfileSettingsInterface(), m_port_number, mapping); + m_dialog->getProfileSettingsInterface()->Save(); + g_emu_thread->reloadInputBindings(); + } // force a refresh after mapping if (result) @@ -162,11 +185,17 @@ ControllerBindingWidget_Base::~ControllerBindingWidget_Base() { } +QIcon ControllerBindingWidget_Base::getIcon() const +{ + return QIcon::fromTheme("artboard-2-line"); +} + void ControllerBindingWidget_Base::initBindingWidgets() { const std::string& type = getControllerType(); const std::string& config_section = getConfigSection(); std::vector bindings(PAD::GetControllerBinds(type)); + SettingsInterface* sif = getDialog()->getProfileSettingsInterface(); for (std::string& binding : bindings) { @@ -178,7 +207,7 @@ void ControllerBindingWidget_Base::initBindingWidgets() continue; } - widget->setKey(config_section, std::move(binding)); + widget->initialize(sif, config_section, std::move(binding)); } const PAD::VibrationCapabilities vibe_caps = PAD::GetControllerVibrationCapabilities(type); @@ -221,13 +250,13 @@ void ControllerBindingWidget_Base::initBindingWidgets() }); } - SettingWidgetBinder::BindWidgetToNormalizedSetting(nullptr, widget, config_section, "AxisScale", range, 1.0f); + ControllerSettingWidgetBinder::BindWidgetToInputProfileNormalized(sif, widget, config_section, "AxisScale", range, 1.0f); } if (QDoubleSpinBox* widget = findChild(QStringLiteral("SmallMotorScale")); widget) - SettingWidgetBinder::BindWidgetToFloatSetting(nullptr, widget, config_section, "SmallMotorScale", 1.0f); + ControllerSettingWidgetBinder::BindWidgetToInputProfileFloat(sif, widget, config_section, "SmallMotorScale", 1.0f); if (QDoubleSpinBox* widget = findChild(QStringLiteral("LargeMotorScale")); widget) - SettingWidgetBinder::BindWidgetToFloatSetting(nullptr, widget, config_section, "LargeMotorScale", 1.0f); + ControllerSettingWidgetBinder::BindWidgetToInputProfileFloat(sif, widget, config_section, "LargeMotorScale", 1.0f); } ControllerBindingWidget_DualShock2::ControllerBindingWidget_DualShock2(ControllerBindingWidget* parent) @@ -241,6 +270,11 @@ ControllerBindingWidget_DualShock2::~ControllerBindingWidget_DualShock2() { } +QIcon ControllerBindingWidget_DualShock2::getIcon() const +{ + return QIcon::fromTheme("gamepad-line"); +} + ControllerBindingWidget_Base* ControllerBindingWidget_DualShock2::createInstance(ControllerBindingWidget* parent) { return new ControllerBindingWidget_DualShock2(parent); diff --git a/pcsx2-qt/Settings/ControllerBindingWidgets.h b/pcsx2-qt/Settings/ControllerBindingWidgets.h index e798338914..c91794039a 100644 --- a/pcsx2-qt/Settings/ControllerBindingWidgets.h +++ b/pcsx2-qt/Settings/ControllerBindingWidgets.h @@ -32,6 +32,8 @@ public: ControllerBindingWidget(QWidget* parent, ControllerSettingsDialog* dialog, u32 port); ~ControllerBindingWidget(); + QIcon getIcon() const; + __fi ControllerSettingsDialog* getDialog() const { return m_dialog; } __fi const std::string& getConfigSection() const { return m_config_section; } __fi const std::string& getControllerType() const { return m_controller_type; } @@ -71,6 +73,8 @@ public: __fi const std::string& getControllerType() const { return static_cast(parent())->getControllerType(); } __fi u32 getPortNumber() const { return static_cast(parent())->getPortNumber(); } + virtual QIcon getIcon() const; + protected: void initBindingWidgets(); }; @@ -83,6 +87,8 @@ public: ControllerBindingWidget_DualShock2(ControllerBindingWidget* parent); ~ControllerBindingWidget_DualShock2(); + QIcon getIcon() const override; + static ControllerBindingWidget_Base* createInstance(ControllerBindingWidget* parent); private: diff --git a/pcsx2-qt/Settings/ControllerGlobalSettingsWidget.cpp b/pcsx2-qt/Settings/ControllerGlobalSettingsWidget.cpp index 7accc907af..de04a28ffe 100644 --- a/pcsx2-qt/Settings/ControllerGlobalSettingsWidget.cpp +++ b/pcsx2-qt/Settings/ControllerGlobalSettingsWidget.cpp @@ -18,23 +18,43 @@ #include "Frontend/InputManager.h" #include "Settings/ControllerGlobalSettingsWidget.h" #include "Settings/ControllerSettingsDialog.h" +#include "Settings/ControllerSettingWidgetBinder.h" #include "QtUtils.h" #include "SettingWidgetBinder.h" ControllerGlobalSettingsWidget::ControllerGlobalSettingsWidget(QWidget* parent, ControllerSettingsDialog* dialog) : QWidget(parent) + , m_dialog(dialog) { m_ui.setupUi(this); - SettingWidgetBinder::BindWidgetToBoolSetting(nullptr, m_ui.enableSDLSource, "InputSources", "SDL", true); - SettingWidgetBinder::BindWidgetToBoolSetting(nullptr, m_ui.enableSDLEnhancedMode, "InputSources", "SDLControllerEnhancedMode", false); - SettingWidgetBinder::BindWidgetToBoolSetting(nullptr, m_ui.enableXInputSource, "InputSources", "XInput", false); - SettingWidgetBinder::BindWidgetToBoolSetting(nullptr, m_ui.multitapPort1, "EmuCore", "MultitapPort0_Enabled", false); - SettingWidgetBinder::BindWidgetToBoolSetting(nullptr, m_ui.multitapPort2, "EmuCore", "MultitapPort1_Enabled", false); + SettingsInterface* sif = dialog->getProfileSettingsInterface(); + + SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableSDLSource, "InputSources", "SDL", true); + SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableSDLEnhancedMode, "InputSources", "SDLControllerEnhancedMode", false); + SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableXInputSource, "InputSources", "XInput", false); + ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.multitapPort1, "Pad", "MultitapPort1", false); + ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.multitapPort2, "Pad", "MultitapPort2", false); + + if (dialog->isEditingProfile()) + { + m_ui.useProfileHotkeyBindings->setChecked(m_dialog->getBoolValue("Pad", "UseProfileHotkeyBindings", false)); + connect(m_ui.useProfileHotkeyBindings, &QCheckBox::stateChanged, this, [this](int new_state) { + m_dialog->setBoolValue("Pad", "UseProfileHotkeyBindings", (new_state == Qt::Checked)); + emit bindingSetupChanged(); + }); + } + else + { + // remove profile options from the UI. + m_ui.mainLayout->removeWidget(m_ui.profileSettings); + m_ui.profileSettings->deleteLater(); + m_ui.profileSettings = nullptr; + } connect(m_ui.enableSDLSource, &QCheckBox::stateChanged, this, &ControllerGlobalSettingsWidget::updateSDLOptionsEnabled); for (QCheckBox* cb : {m_ui.multitapPort1, m_ui.multitapPort2}) - connect(cb, &QCheckBox::stateChanged, this, [this]() { emit multitapModeChanged(); }); + connect(cb, &QCheckBox::stateChanged, this, [this]() { emit bindingSetupChanged(); }); updateSDLOptionsEnabled(); } diff --git a/pcsx2-qt/Settings/ControllerGlobalSettingsWidget.h b/pcsx2-qt/Settings/ControllerGlobalSettingsWidget.h index 9e032ff9e8..476971585a 100644 --- a/pcsx2-qt/Settings/ControllerGlobalSettingsWidget.h +++ b/pcsx2-qt/Settings/ControllerGlobalSettingsWidget.h @@ -36,10 +36,11 @@ public: void removeDeviceFromList(const QString& identifier); Q_SIGNALS: - void multitapModeChanged(); + void bindingSetupChanged(); private: void updateSDLOptionsEnabled(); Ui::ControllerGlobalSettingsWidget m_ui; + ControllerSettingsDialog* m_dialog; }; diff --git a/pcsx2-qt/Settings/ControllerGlobalSettingsWidget.ui b/pcsx2-qt/Settings/ControllerGlobalSettingsWidget.ui index e57ce21f50..ddb1249d54 100644 --- a/pcsx2-qt/Settings/ControllerGlobalSettingsWidget.ui +++ b/pcsx2-qt/Settings/ControllerGlobalSettingsWidget.ui @@ -13,7 +13,7 @@ Form - + 0 @@ -26,116 +26,138 @@ 0 - - - - - - SDL Input Source - - - - - - The SDL input source supports most controllers, and provides advanced functionality for DualShock 4 / DualSense pads in Bluetooth mode (Vibration / LED Control). - - - true - - - - - - - Enable SDL Input Source - - - - - - - DualShock 4 / DualSense Enhanced Mode - - - - - - - - - - XInput Source - - - - - - The XInput source provides support for XBox 360 / XBox One / XBox Series controllers, and third party controllers which implement the XInput protocol. - - - true - - - - - - - Enable XInput Input Source - - - - - - - - - - Controller Multitap - - - - - - The multitap enables up to 8 controllers to be connected to the console. Each multitap provides 4 ports. Multitap is not supported by all games. - - - true - - - - - - - Multitap on Console Port 1 - - - - - - - Multitap on Console Port 2 - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - + + + + SDL Input Source + + + + + + The SDL input source supports most controllers, and provides advanced functionality for DualShock 4 / DualSense pads in Bluetooth mode (Vibration / LED Control). + + + true + + + + + + + DualShock 4 / DualSense Enhanced Mode + + + + + + + Enable SDL Input Source + + + + + - + + + + XInput Source + + + + + + The XInput source provides support for XBox 360 / XBox One / XBox Series controllers, and third party controllers which implement the XInput protocol. + + + true + + + + + + + Enable XInput Input Source + + + + + + + + + + Controller Multitap + + + + + + The multitap enables up to 8 controllers to be connected to the console. Each multitap provides 4 ports. Multitap is not supported by all games. + + + true + + + + + + + Multitap on Console Port 1 + + + + + + + Multitap on Console Port 2 + + + + + + + + + + Profile Settings + + + + + + When this option is enabled, hotkeys can be set in this input profile, and will be used instead of the global hotkeys. By default, hotkeys are always shared between all profiles. + + + true + + + + + + + Use Per-Profile Hotkeys + + + + + + + + + + Qt::Vertical + + + + 20 + 45 + + + + + Detected Devices diff --git a/pcsx2-qt/Settings/ControllerSettingWidgetBinder.h b/pcsx2-qt/Settings/ControllerSettingWidgetBinder.h new file mode 100644 index 0000000000..a5dd9b0176 --- /dev/null +++ b/pcsx2-qt/Settings/ControllerSettingWidgetBinder.h @@ -0,0 +1,177 @@ +/* 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 . + */ + +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pcsx2/HostSettings.h" + +#include "EmuThread.h" +#include "QtHost.h" + +#include "SettingWidgetBinder.h" + +/// This nastyness is required because input profiles aren't overlaid settings like the rest of them, it's +/// input profile *or* global, not both. +namespace ControllerSettingWidgetBinder +{ + /// Interface specific method of BindWidgetToBoolSetting(). + template + static void BindWidgetToInputProfileBool(SettingsInterface* sif, WidgetType* widget, std::string section, std::string key, bool default_value) + { + using Accessor = SettingWidgetBinder::SettingAccessor; + + if (sif) + { + const bool value = sif->GetBoolValue(section.c_str(), key.c_str(), default_value); + Accessor::setBoolValue(widget, value); + + Accessor::connectValueChanged(widget, [sif, widget, section = std::move(section), key = std::move(key)]() { + const bool new_value = Accessor::getBoolValue(widget); + sif->SetBoolValue(section.c_str(), key.c_str(), new_value); + sif->Save(); + g_emu_thread->reloadGameSettings(); + }); + } + else + { + const bool value = Host::GetBaseBoolSettingValue(section.c_str(), key.c_str(), default_value); + Accessor::setBoolValue(widget, value); + + Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key)]() { + const bool new_value = Accessor::getBoolValue(widget); + QtHost::SetBaseBoolSettingValue(section.c_str(), key.c_str(), new_value); + g_emu_thread->applySettings(); + }); + } + } + + /// Interface specific method of BindWidgetToFloatSetting(). + template + static void BindWidgetToInputProfileFloat(SettingsInterface* sif, WidgetType* widget, std::string section, std::string key, float default_value) + { + using Accessor = SettingWidgetBinder::SettingAccessor; + + if (sif) + { + const float value = sif->GetFloatValue(section.c_str(), key.c_str(), default_value); + Accessor::setBoolValue(widget, value); + + Accessor::connectValueChanged(widget, [sif, widget, section = std::move(section), key = std::move(key)]() { + const float new_value = Accessor::getFloatValue(widget); + sif->SetFloatValue(section.c_str(), key.c_str(), new_value); + sif->Save(); + g_emu_thread->reloadGameSettings(); + }); + } + else + { + const float value = Host::GetBaseFloatSettingValue(section.c_str(), key.c_str(), default_value); + Accessor::setBoolValue(widget, value); + + Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key)]() { + const float new_value = Accessor::getFloatValue(widget); + QtHost::SetBaseFloatSettingValue(section.c_str(), key.c_str(), new_value); + g_emu_thread->applySettings(); + }); + } + } + + /// Interface specific method of BindWidgetToNormalizedSetting(). + template + static void BindWidgetToInputProfileNormalized( + SettingsInterface* sif, WidgetType* widget, std::string section, std::string key, float range, float default_value) + { + using Accessor = SettingWidgetBinder::SettingAccessor; + + + if (sif) + { + const float value = sif->GetFloatValue(section.c_str(), key.c_str(), default_value); + Accessor::setIntValue(widget, static_cast(value * range)); + + Accessor::connectValueChanged(widget, [sif, widget, section = std::move(section), key = std::move(key), range]() { + const int new_value = Accessor::getIntValue(widget); + sif->SetFloatValue(section.c_str(), key.c_str(), static_cast(new_value) / range); + sif->Save(); + g_emu_thread->reloadGameSettings(); + }); + } + else + { + const float value = Host::GetBaseFloatSettingValue(section.c_str(), key.c_str(), default_value); + Accessor::setIntValue(widget, static_cast(value * range)); + + Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key), range]() { + const float new_value = (static_cast(Accessor::getIntValue(widget)) / range); + QtHost::SetBaseFloatSettingValue(section.c_str(), key.c_str(), new_value); + g_emu_thread->applySettings(); + }); + } + } + + /// Interface specific method of BindWidgetToStringSetting(). + template + static void BindWidgetToInputProfileString( + SettingsInterface* sif, WidgetType* widget, std::string section, std::string key, std::string default_value = std::string()) + { + using Accessor = SettingWidgetBinder::SettingAccessor; + + if (sif) + { + const QString value(QString::fromStdString(sif->GetStringValue(section.c_str(), key.c_str(), default_value.c_str()))); + + Accessor::setStringValue(widget, value); + + Accessor::connectValueChanged(widget, [widget, sif, section = std::move(section), key = std::move(key)]() { + const QString new_value = Accessor::getStringValue(widget); + if (!new_value.isEmpty()) + sif->SetStringValue(section.c_str(), key.c_str(), new_value.toUtf8().constData()); + else + sif->DeleteValue(section.c_str(), key.c_str()); + + sif->Save(); + g_emu_thread->reloadGameSettings(); + }); + } + else + { + const QString value(QString::fromStdString(Host::GetBaseStringSettingValue(section.c_str(), key.c_str(), default_value.c_str()))); + + Accessor::setStringValue(widget, value); + + Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key)]() { + const QString new_value = Accessor::getStringValue(widget); + if (!new_value.isEmpty()) + QtHost::SetBaseStringSettingValue(section.c_str(), key.c_str(), new_value.toUtf8().constData()); + else + QtHost::RemoveBaseSettingValue(section.c_str(), key.c_str()); + + g_emu_thread->applySettings(); + }); + } + } +} // namespace ControllerSettingWidgetBinder diff --git a/pcsx2-qt/Settings/ControllerSettingsDialog.cpp b/pcsx2-qt/Settings/ControllerSettingsDialog.cpp index 7bfc738929..c3914acbad 100644 --- a/pcsx2-qt/Settings/ControllerSettingsDialog.cpp +++ b/pcsx2-qt/Settings/ControllerSettingsDialog.cpp @@ -22,14 +22,21 @@ #include "Settings/ControllerBindingWidgets.h" #include "Settings/HotkeySettingsWidget.h" +#include "pcsx2/Frontend/INISettingsInterface.h" +#include "pcsx2/PAD/Host/PAD.h" #include "pcsx2/Sio.h" +#include "pcsx2/VMManager.h" #include "common/Assertions.h" +#include "common/FileSystem.h" #include +#include #include #include +static constexpr const std::array s_mtap_slot_names = {{'A', 'B', 'C', 'D'}}; + ControllerSettingsDialog::ControllerSettingsDialog(QWidget* parent /* = nullptr */) : QDialog(parent) { @@ -37,26 +44,23 @@ ControllerSettingsDialog::ControllerSettingsDialog(QWidget* parent /* = nullptr setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); - // These are preset in the ui file. - m_global_settings = new ControllerGlobalSettingsWidget(m_ui.settingsContainer, this); - m_ui.settingsContainer->addWidget(m_global_settings); - m_hotkey_settings = new HotkeySettingsWidget(m_ui.settingsContainer, this); - m_ui.settingsContainer->addWidget(m_hotkey_settings); - - // add remainder of ports - createPortWidgets(); + refreshProfileList(); + createWidgets(); m_ui.settingsCategory->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); connect(m_ui.settingsCategory, &QListWidget::currentRowChanged, this, &ControllerSettingsDialog::onCategoryCurrentRowChanged); + connect(m_ui.currentProfile, &QComboBox::currentIndexChanged, this, &ControllerSettingsDialog::onCurrentProfileChanged); connect(m_ui.buttonBox, &QDialogButtonBox::rejected, this, &ControllerSettingsDialog::close); + connect(m_ui.newProfile, &QPushButton::clicked, this, &ControllerSettingsDialog::onNewProfileClicked); + connect(m_ui.loadProfile, &QPushButton::clicked, this, &ControllerSettingsDialog::onLoadProfileClicked); + connect(m_ui.deleteProfile, &QPushButton::clicked, this, &ControllerSettingsDialog::onDeleteProfileClicked); + connect(m_ui.restoreDefaults, &QPushButton::clicked, this, &ControllerSettingsDialog::onRestoreDefaultsClicked); connect(g_emu_thread, &EmuThread::onInputDevicesEnumerated, this, &ControllerSettingsDialog::onInputDevicesEnumerated); connect(g_emu_thread, &EmuThread::onInputDeviceConnected, this, &ControllerSettingsDialog::onInputDeviceConnected); connect(g_emu_thread, &EmuThread::onInputDeviceDisconnected, this, &ControllerSettingsDialog::onInputDeviceDisconnected); connect(g_emu_thread, &EmuThread::onVibrationMotorsEnumerated, this, &ControllerSettingsDialog::onVibrationMotorsEnumerated); - connect(m_global_settings, &ControllerGlobalSettingsWidget::multitapModeChanged, this, &ControllerSettingsDialog::createPortWidgets); - // trigger a device enumeration to populate the device list g_emu_thread->enumerateInputDevices(); g_emu_thread->enumerateVibrationMotors(); @@ -91,6 +95,123 @@ void ControllerSettingsDialog::onCategoryCurrentRowChanged(int row) m_ui.settingsContainer->setCurrentIndex(row); } +void ControllerSettingsDialog::onCurrentProfileChanged(int index) +{ + switchProfile((index == 0) ? 0 : m_ui.currentProfile->itemText(index)); +} + +void ControllerSettingsDialog::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()) + return; + + std::string profile_path(VMManager::GetInputProfilePath(profile_name.toStdString())); + if (FileSystem::FileExists(profile_path.c_str())) + { + QMessageBox::critical(this, tr("Error"), tr("A profile with the name '%1' already exists.").arg(profile_name)); + return; + } + + const int res = QMessageBox::question(this, tr("Create Input Profile"), + tr("Do you want to copy all bindings from the currently-selected profile to the new profile? Selecting No will create a completely empty profile."), + QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); + if (res == QMessageBox::Cancel) + return; + + INISettingsInterface temp_si(std::move(profile_path)); + if (res == QMessageBox::Yes) + { + // copy from global or the current profile + if (!m_profile_interface) + { + // from global + auto lock = Host::GetSettingsLock(); + PAD::CopyConfiguration(&temp_si, *Host::Internal::GetBaseSettingsLayer(), true, true, false); + } + else + { + // from profile + const bool copy_hotkey_bindings = m_profile_interface->GetBoolValue("Pad", "UseProfileHotkeyBindings", false); + temp_si.SetBoolValue("Pad", "UseProfileHotkeyBindings", copy_hotkey_bindings); + PAD::CopyConfiguration(&temp_si, *m_profile_interface, true, true, copy_hotkey_bindings); + } + } + + if (!temp_si.Save()) + { + QMessageBox::critical(this, tr("Error"), tr("Failed to save the new profile to '%1'.").arg(QString::fromStdString(temp_si.GetFileName()))); + return; + } + + refreshProfileList(); + switchProfile(profile_name); +} + +void ControllerSettingsDialog::onLoadProfileClicked() +{ + if (QMessageBox::question(this, tr("Load Input Profile"), + tr("Are you sure you want to load the input profile named '%1'?\n\n" + "All current global bindings will be removed, and the profile bindings loaded.\n\n" + "You cannot undo this action.") + .arg(m_profile_name)) != QMessageBox::Yes) + { + return; + } + + { + auto lock = Host::GetSettingsLock(); + PAD::CopyConfiguration(Host::Internal::GetBaseSettingsLayer(), *m_profile_interface, true, true, false); + QtHost::QueueSettingsSave(); + } + + // make it visible + switchProfile({}); +} + +void ControllerSettingsDialog::onDeleteProfileClicked() +{ + if (QMessageBox::question(this, tr("Delete Input Profile"), + tr("Are you sure you want to delete the input profile named '%1'?\n\n" + "You cannot undo this action.") + .arg(m_profile_name)) != QMessageBox::Yes) + { + return; + } + + std::string profile_path(VMManager::GetInputProfilePath(m_profile_name.toStdString())); + if (!FileSystem::DeleteFilePath(profile_path.c_str())) + { + QMessageBox::critical(this, tr("Error"), tr("Failed to delete '%1'.").arg(QString::fromStdString(profile_path))); + return; + } + + // switch back to global + refreshProfileList(); + switchProfile({}); +} + +void ControllerSettingsDialog::onRestoreDefaultsClicked() +{ + if (QMessageBox::question(this, tr("Restore Defaults"), + tr("Are you sure you want to restore the default controller configuration?\n\n" + "All shared bindings and configuration will be lost, but your input profiles will remain.\n\n" + "You cannot undo this action.")) != QMessageBox::Yes) + { + return; + } + + // actually restore it + { + auto lock = Host::GetSettingsLock(); + PAD::SetDefaultConfig(*Host::Internal::GetBaseSettingsLayer()); + QtHost::QueueSettingsSave(); + } + + // reload all settings + switchProfile({}); +} + void ControllerSettingsDialog::onInputDevicesEnumerated(const QList>& devices) { m_device_list = devices; @@ -133,38 +254,105 @@ void ControllerSettingsDialog::onVibrationMotorsEnumerated(const QListGetBoolValue(section, key, default_value); + else + return Host::GetBaseBoolSettingValue(section, key, default_value); +} + +std::string ControllerSettingsDialog::getStringValue(const char* section, const char* key, const char* default_value) const +{ + std::string value; + if (m_profile_interface) + value = m_profile_interface->GetStringValue(section, key, default_value); + else + value = Host::GetBaseStringSettingValue(section, key, default_value); + return value; +} + +void ControllerSettingsDialog::setBoolValue(const char* section, const char* key, bool value) +{ + if (m_profile_interface) { - QSignalBlocker sb(m_ui.settingsContainer); - QSignalBlocker sb2(m_ui.settingsCategory); - m_ui.settingsContainer->setCurrentIndex(0); - m_ui.settingsCategory->setCurrentRow(0); + m_profile_interface->SetBoolValue(section, key, value); + m_profile_interface->Save(); + g_emu_thread->reloadGameSettings(); } - - // remove all except global and hotkeys (i.e. first and last) - pxAssert(m_ui.settingsCategory->count() == m_ui.settingsContainer->count()); - while (m_ui.settingsContainer->count() > 2) + else { - delete m_ui.settingsCategory->takeItem(1); + QtHost::SetBaseBoolSettingValue(section, key, value); + g_emu_thread->applySettings(); + } +} - QWidget* widget = m_ui.settingsContainer->widget(1); +void ControllerSettingsDialog::setStringValue(const char* section, const char* key, const char* value) +{ + if (m_profile_interface) + { + m_profile_interface->SetStringValue(section, key, value); + m_profile_interface->Save(); + g_emu_thread->reloadGameSettings(); + } + else + { + QtHost::SetBaseStringSettingValue(key, section, value); + g_emu_thread->applySettings(); + } +} + +void ControllerSettingsDialog::clearSettingValue(const char* section, const char* key) +{ + if (m_profile_interface) + { + m_profile_interface->DeleteValue(section, key); + m_profile_interface->Save(); + g_emu_thread->reloadGameSettings(); + } + else + { + QtHost::RemoveBaseSettingValue(section, key); + g_emu_thread->applySettings(); + } +} + +void ControllerSettingsDialog::createWidgets() +{ + QSignalBlocker sb(m_ui.settingsContainer); + QSignalBlocker sb2(m_ui.settingsCategory); + + while (m_ui.settingsContainer->count() > 0) + { + QWidget* widget = m_ui.settingsContainer->widget(m_ui.settingsContainer->count() - 1); m_ui.settingsContainer->removeWidget(widget); - delete widget; + widget->deleteLater(); } - // because we can't insert and shuffle everything forward, we need to temporarily remove hotkeys - QListWidgetItem* const hotkeys_row = m_ui.settingsCategory->takeItem(1); - QWidget* const hotkeys_widget = m_ui.settingsContainer->widget(1); - m_ui.settingsContainer->removeWidget(hotkeys_widget); + m_ui.settingsCategory->clear(); + + m_global_settings = nullptr; + m_hotkey_settings = nullptr; + + { + // global settings + QListWidgetItem* item = new QListWidgetItem(); + item->setText(tr("Global Settings")); + item->setIcon(QIcon::fromTheme("settings-3-line")); + m_ui.settingsCategory->addItem(item); + m_ui.settingsCategory->setCurrentRow(0); + m_global_settings = new ControllerGlobalSettingsWidget(m_ui.settingsContainer, this); + m_ui.settingsContainer->addWidget(m_global_settings); + connect(m_global_settings, &ControllerGlobalSettingsWidget::bindingSetupChanged, this, &ControllerSettingsDialog::createWidgets); + for (const QPair& dev : m_device_list) + m_global_settings->addDeviceToList(dev.first, dev.second); + } // load mtap settings - const std::array mtap_enabled = {{Host::GetBaseBoolSettingValue("EmuCore", "MultitapPort0_Enabled", false), - Host::GetBaseBoolSettingValue("EmuCore", "MultitapPort1_Enabled", false)}}; + const std::array mtap_enabled = {{getBoolValue("Pad", "MultitapPort1", false), + getBoolValue("Pad", "MultitapPort2", false)}}; // we reorder things a little to make it look less silly for mtap - static constexpr const std::array mtap_slot_names = {{'A', 'B', 'C', 'D'}}; static constexpr const std::array mtap_port_order = {{0, 2, 3, 4, 1, 5, 6, 7}}; // create the ports @@ -175,18 +363,103 @@ void ControllerSettingsDialog::createPortWidgets() if (is_mtap_port && !mtap_enabled[port]) continue; - QListWidgetItem* item = new QListWidgetItem(); - item->setText(mtap_enabled[port] ? - (tr("Controller Port %1%2").arg(port + 1).arg(mtap_slot_names[slot])) : - tr("Controller Port %1").arg(port + 1)); - item->setIcon(QIcon::fromTheme("gamepad-line")); - m_ui.settingsCategory->addItem(item); - m_port_bindings[global_slot] = new ControllerBindingWidget(m_ui.settingsContainer, this, global_slot); m_ui.settingsContainer->addWidget(m_port_bindings[global_slot]); + + const PAD::ControllerInfo* ci = PAD::GetControllerInfo(m_port_bindings[global_slot]->getControllerType()); + const QString display_name(ci ? QString::fromUtf8(ci->display_name) : QStringLiteral("Unknown")); + + QListWidgetItem* item = new QListWidgetItem(); + item->setText(mtap_enabled[port] ? + (tr("Controller Port %1%2\n%3").arg(port + 1).arg(s_mtap_slot_names[slot]).arg(display_name)) : + tr("Controller Port %1\n%2").arg(port + 1).arg(display_name)); + item->setIcon(m_port_bindings[global_slot]->getIcon()); + item->setData(Qt::UserRole, QVariant(global_slot)); + m_ui.settingsCategory->addItem(item); } - // and re-insert hotkeys - m_ui.settingsCategory->addItem(hotkeys_row); - m_ui.settingsContainer->addWidget(hotkeys_widget); -} \ No newline at end of file + // only add hotkeys if we're editing global settings + if (!m_profile_interface || m_profile_interface->GetBoolValue("Pad", "UseProfileHotkeyBindings", false)) + { + QListWidgetItem* item = new QListWidgetItem(); + item->setText(tr("Hotkeys")); + item->setIcon(QIcon::fromTheme("keyboard-line")); + m_ui.settingsCategory->addItem(item); + m_hotkey_settings = new HotkeySettingsWidget(m_ui.settingsContainer, this); + m_ui.settingsContainer->addWidget(m_hotkey_settings); + } + + m_ui.loadProfile->setEnabled(isEditingProfile()); + m_ui.deleteProfile->setEnabled(isEditingProfile()); + m_ui.restoreDefaults->setEnabled(isEditingGlobalSettings()); +} + +void ControllerSettingsDialog::updateListDescription(u32 global_slot, ControllerBindingWidget* widget) +{ + for (int i = 0; i < m_ui.settingsCategory->count(); i++) + { + QListWidgetItem* item = m_ui.settingsCategory->item(i); + const QVariant data(item->data(Qt::UserRole)); + if (data.type() == QVariant::UInt && data.toUInt() == global_slot) + { + const bool is_mtap_port = sioPadIsMultitapSlot(global_slot); + const auto [port, slot] = sioConvertPadToPortAndSlot(global_slot); + const bool mtap_enabled = getBoolValue("Pad", (port == 0) ? "MultitapPort1" : "MultitapPort2", false); + + const PAD::ControllerInfo* ci = PAD::GetControllerInfo(widget->getControllerType()); + const QString display_name(ci ? QString::fromUtf8(ci->display_name) : QStringLiteral("Unknown")); + + item->setText(mtap_enabled ? + (tr("Controller Port %1%2\n%3").arg(port + 1).arg(s_mtap_slot_names[slot]).arg(display_name)) : + tr("Controller Port %1\n%2").arg(port + 1).arg(display_name)); + item->setIcon(widget->getIcon()); + break; + } + } +} +void ControllerSettingsDialog::refreshProfileList() +{ + const std::vector names(PAD::GetInputProfileNames()); + + QSignalBlocker sb(m_ui.currentProfile); + m_ui.currentProfile->clear(); + m_ui.currentProfile->addItem(tr("Shared")); + if (isEditingGlobalSettings()) + m_ui.currentProfile->setCurrentIndex(0); + + for (const std::string& name : names) + { + const QString qname(QString::fromStdString(name)); + m_ui.currentProfile->addItem(qname); + if (qname == m_profile_name) + m_ui.currentProfile->setCurrentIndex(m_ui.currentProfile->count() - 1); + } +} + +void ControllerSettingsDialog::switchProfile(const QString& name) +{ + QSignalBlocker sb(m_ui.currentProfile); + + if (!name.isEmpty()) + { + std::string path(VMManager::GetInputProfilePath(name.toStdString())); + if (!FileSystem::FileExists(path.c_str())) + { + QMessageBox::critical(this, tr("Error"), tr("The input profile named '%1' cannot be found.").arg(name)); + return; + } + + 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)); + } + else + { + m_profile_interface.reset(); + m_ui.currentProfile->setCurrentIndex(0); + } + + m_profile_name = name; + createWidgets(); +} diff --git a/pcsx2-qt/Settings/ControllerSettingsDialog.h b/pcsx2-qt/Settings/ControllerSettingsDialog.h index f0d380adf3..149623cb2d 100644 --- a/pcsx2-qt/Settings/ControllerSettingsDialog.h +++ b/pcsx2-qt/Settings/ControllerSettingsDialog.h @@ -22,11 +22,14 @@ #include #include #include +#include class ControllerGlobalSettingsWidget; class ControllerBindingWidget; class HotkeySettingsWidget; +class SettingsInterface; + class ControllerSettingsDialog final : public QDialog { Q_OBJECT @@ -48,25 +51,51 @@ public: ControllerSettingsDialog(QWidget* parent = nullptr); ~ControllerSettingsDialog(); - HotkeySettingsWidget* getHotkeySettingsWidget() const { return m_hotkey_settings; } + __fi HotkeySettingsWidget* getHotkeySettingsWidget() const { return m_hotkey_settings; } __fi const QList>& getDeviceList() const { return m_device_list; } __fi const QStringList& getVibrationMotors() const { return m_vibration_motors; } + __fi bool isEditingGlobalSettings() const { return m_profile_name.isEmpty(); } + __fi bool isEditingProfile() const { return !m_profile_name.isEmpty(); } + __fi SettingsInterface* getProfileSettingsInterface() { return m_profile_interface.get(); } + + void updateListDescription(u32 global_slot, ControllerBindingWidget* widget); + + // Helper functions for updating setting values globally or in the profile. + bool getBoolValue(const char* section, const char* key, bool default_value) const; + std::string getStringValue(const char* section, const char* key, const char* default_value) const; + void setBoolValue(const char* section, const char* key, bool value); + void setStringValue(const char* section, const char* key, const char* value); + void clearSettingValue(const char* section, const char* key); + +Q_SIGNALS: + void inputProfileSwitched(); + public Q_SLOTS: void setCategory(Category category); private Q_SLOTS: void onCategoryCurrentRowChanged(int row); + void onCurrentProfileChanged(int index); + void onNewProfileClicked(); + void onLoadProfileClicked(); + void onDeleteProfileClicked(); + void onRestoreDefaultsClicked(); void onInputDevicesEnumerated(const QList>& devices); void onInputDeviceConnected(const QString& identifier, const QString& device_name); void onInputDeviceDisconnected(const QString& identifier); void onVibrationMotorsEnumerated(const QList& motors); - void createPortWidgets(); + void createWidgets(); private: + static QIcon getIconForType(const std::string& type); + + void refreshProfileList(); + void switchProfile(const QString& name); + Ui::ControllerSettingsDialog m_ui; ControllerGlobalSettingsWidget* m_global_settings = nullptr; @@ -75,4 +104,7 @@ private: QList> m_device_list; QStringList m_vibration_motors; + + QString m_profile_name; + std::unique_ptr m_profile_interface; }; diff --git a/pcsx2-qt/Settings/ControllerSettingsDialog.ui b/pcsx2-qt/Settings/ControllerSettingsDialog.ui index 11fc21d118..f1e4807824 100644 --- a/pcsx2-qt/Settings/ControllerSettingsDialog.ui +++ b/pcsx2-qt/Settings/ControllerSettingsDialog.ui @@ -9,8 +9,8 @@ 0 0 - 1274 - 668 + 1276 + 672 @@ -22,7 +22,7 @@ PCSX2 Controller Settings - + @@ -49,24 +49,6 @@ 32 - - - Global Settings - - - - .. - - - - - Hotkeys - - - - .. - - @@ -80,11 +62,71 @@ - - - QDialogButtonBox::Close - - + + + + + + + Profile: + + + + + + + + + + New Profile + + + + .. + + + + + + + Load Profile + + + + + + + + + + Delete Profile + + + + .. + + + + + + + Restore Defaults + + + + + + + + + + + + QDialogButtonBox::Close + + + + diff --git a/pcsx2-qt/Settings/GameSummaryWidget.cpp b/pcsx2-qt/Settings/GameSummaryWidget.cpp index d6eb271b66..cb81f7d7cf 100644 --- a/pcsx2-qt/Settings/GameSummaryWidget.cpp +++ b/pcsx2-qt/Settings/GameSummaryWidget.cpp @@ -18,11 +18,14 @@ #include "common/StringUtil.h" #include "Frontend/GameList.h" +#include "PAD/Host/PAD.h" #include "GameSummaryWidget.h" +#include "SettingsDialog.h" #include "QtHost.h" GameSummaryWidget::GameSummaryWidget(const GameList::Entry* entry, SettingsDialog* dialog, QWidget* parent) + : m_dialog(dialog) { m_ui.setupUi(this); @@ -39,6 +42,8 @@ GameSummaryWidget::GameSummaryWidget(const GameList::Entry* entry, SettingsDialo } populateUi(entry); + + connect(m_ui.inputProfile, &QComboBox::currentIndexChanged, this, &GameSummaryWidget::onInputProfileChanged); } GameSummaryWidget::~GameSummaryWidget() = default; @@ -52,4 +57,21 @@ void GameSummaryWidget::populateUi(const GameList::Entry* entry) m_ui.type->setCurrentIndex(static_cast(entry->type)); m_ui.region->setCurrentIndex(static_cast(entry->region)); m_ui.compatibility->setCurrentIndex(static_cast(entry->compatibility_rating)); + + for (const std::string& name : PAD::GetInputProfileNames()) + m_ui.inputProfile->addItem(QString::fromStdString(name)); + + std::optional profile(m_dialog->getStringValue("EmuCore", "InputProfileName", std::nullopt)); + if (profile.has_value()) + m_ui.inputProfile->setCurrentIndex(m_ui.inputProfile->findText(QString::fromStdString(profile.value()))); + else + m_ui.inputProfile->setCurrentIndex(0); +} + +void GameSummaryWidget::onInputProfileChanged(int index) +{ + if (index == 0) + m_dialog->setStringSettingValue("EmuCore", "InputProfileName", std::nullopt); + else + m_dialog->setStringSettingValue("EmuCore", "InputProfileName", m_ui.inputProfile->itemText(index).toUtf8()); } diff --git a/pcsx2-qt/Settings/GameSummaryWidget.h b/pcsx2-qt/Settings/GameSummaryWidget.h index e1809c850c..f99bef235f 100644 --- a/pcsx2-qt/Settings/GameSummaryWidget.h +++ b/pcsx2-qt/Settings/GameSummaryWidget.h @@ -37,5 +37,8 @@ public: private: void populateUi(const GameList::Entry* entry); + void onInputProfileChanged(int index); + Ui::GameSummaryWidget m_ui; + SettingsDialog* m_dialog; }; diff --git a/pcsx2-qt/Settings/GameSummaryWidget.ui b/pcsx2-qt/Settings/GameSummaryWidget.ui index e1b34e949c..a25938d0db 100644 --- a/pcsx2-qt/Settings/GameSummaryWidget.ui +++ b/pcsx2-qt/Settings/GameSummaryWidget.ui @@ -82,6 +82,65 @@ + + + + Type: + + + + + + + false + + + + 0 + 0 + + + + true + + + + PS2 Disc + + + + :/icons/media-optical-24.png:/icons/media-optical-24.png + + + + + PS1 Disc + + + + :/icons/media-optical-24.png:/icons/media-optical-24.png + + + + + ELF (PS2 Executable) + + + + :/icons/applications-system-24.png:/icons/applications-system-24.png + + + + + Playlist + + + + :/icons/address-book-new-22.png:/icons/address-book-new-22.png + + + + @@ -94,6 +153,12 @@ false + + + 0 + 0 + + true @@ -251,24 +316,17 @@ - - - - Qt::Vertical - - - - 20 - 40 - - - - false + + + 0 + 0 + + true @@ -301,62 +359,44 @@ Perfect - z + - - + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + 0 + 0 + + + + + Shared + + + + + + - Type: + Input Profile: - - - - false - - - true - - - - PS2 Disc - - - - :/icons/media-optical-24.png:/icons/media-optical-24.png - - - - - PS1 Disc - - - - :/icons/media-optical-24.png:/icons/media-optical-24.png - - - - - ELF (PS2 Executable) - - - - :/icons/applications-system-24.png:/icons/applications-system-24.png - - - - - Playlist - - - - :/icons/address-book-new-22.png:/icons/address-book-new-22.png - - - - diff --git a/pcsx2-qt/Settings/HotkeySettingsWidget.cpp b/pcsx2-qt/Settings/HotkeySettingsWidget.cpp index c9f88fa14f..90d800bef3 100644 --- a/pcsx2-qt/Settings/HotkeySettingsWidget.cpp +++ b/pcsx2-qt/Settings/HotkeySettingsWidget.cpp @@ -29,6 +29,7 @@ HotkeySettingsWidget::HotkeySettingsWidget(QWidget* parent, ControllerSettingsDialog* dialog) : QWidget(parent) + , m_dialog(dialog) { createUi(); } @@ -79,6 +80,6 @@ void HotkeySettingsWidget::createButtons() const int target_row = layout->count() / 2; layout->addWidget(new QLabel(qApp->translate("Hotkeys", hotkey->display_name), container), target_row, 0); - layout->addWidget(new InputBindingWidget(container, "Hotkeys", hotkey->name), target_row, 1); + layout->addWidget(new InputBindingWidget(container, m_dialog->getProfileSettingsInterface(), "Hotkeys", hotkey->name), target_row, 1); } } diff --git a/pcsx2-qt/Settings/HotkeySettingsWidget.h b/pcsx2-qt/Settings/HotkeySettingsWidget.h index 00dfd5d994..d151e9f4df 100644 --- a/pcsx2-qt/Settings/HotkeySettingsWidget.h +++ b/pcsx2-qt/Settings/HotkeySettingsWidget.h @@ -37,6 +37,7 @@ private: void createUi(); void createButtons(); + ControllerSettingsDialog* m_dialog; QTabWidget* m_tab_widget; struct Category diff --git a/pcsx2-qt/Settings/InputBindingDialog.cpp b/pcsx2-qt/Settings/InputBindingDialog.cpp index 6d6a9998a6..8da4dce42b 100644 --- a/pcsx2-qt/Settings/InputBindingDialog.cpp +++ b/pcsx2-qt/Settings/InputBindingDialog.cpp @@ -25,9 +25,10 @@ // _BitScanForward() #include "pcsx2/GS/GSIntrin.h" -InputBindingDialog::InputBindingDialog(std::string section_name, std::string key_name, +InputBindingDialog::InputBindingDialog(SettingsInterface* sif, std::string section_name, std::string key_name, std::vector bindings, QWidget* parent) : QDialog(parent) + , m_sif(sif) , m_section_name(std::move(section_name)) , m_key_name(std::move(key_name)) , m_bindings(std::move(bindings)) @@ -186,12 +187,23 @@ void InputBindingDialog::updateList() void InputBindingDialog::saveListToSettings() { - if (!m_bindings.empty()) - QtHost::SetBaseStringListSettingValue(m_section_name.c_str(), m_key_name.c_str(), m_bindings); + if (m_sif) + { + if (!m_bindings.empty()) + m_sif->SetStringList(m_section_name.c_str(), m_key_name.c_str(), m_bindings); + else + m_sif->DeleteValue(m_section_name.c_str(), m_key_name.c_str()); + m_sif->Save(); + g_emu_thread->reloadGameSettings(); + } else - QtHost::RemoveBaseSettingValue(m_section_name.c_str(), m_key_name.c_str()); - - g_emu_thread->reloadInputBindings(); + { + if (!m_bindings.empty()) + QtHost::SetBaseStringListSettingValue(m_section_name.c_str(), m_key_name.c_str(), m_bindings); + else + QtHost::RemoveBaseSettingValue(m_section_name.c_str(), m_key_name.c_str()); + g_emu_thread->reloadInputBindings(); + } } void InputBindingDialog::inputManagerHookCallback(InputBindingKey key, float value) diff --git a/pcsx2-qt/Settings/InputBindingDialog.h b/pcsx2-qt/Settings/InputBindingDialog.h index 515febea62..2f3b0ceeb8 100644 --- a/pcsx2-qt/Settings/InputBindingDialog.h +++ b/pcsx2-qt/Settings/InputBindingDialog.h @@ -21,12 +21,14 @@ #include #include +class SettingsInterface; + class InputBindingDialog : public QDialog { Q_OBJECT public: - InputBindingDialog(std::string section_name, std::string key_name, std::vector bindings, QWidget* parent); + InputBindingDialog(SettingsInterface* sif, std::string section_name, std::string key_name, std::vector bindings, QWidget* parent); ~InputBindingDialog(); protected Q_SLOTS: @@ -58,6 +60,7 @@ protected: Ui::InputBindingDialog m_ui; + SettingsInterface* m_sif; std::string m_section_name; std::string m_key_name; std::vector m_bindings; diff --git a/pcsx2-qt/Settings/InputBindingWidget.cpp b/pcsx2-qt/Settings/InputBindingWidget.cpp index 08f6fc30c7..bb69cd7e83 100644 --- a/pcsx2-qt/Settings/InputBindingWidget.cpp +++ b/pcsx2-qt/Settings/InputBindingWidget.cpp @@ -39,7 +39,7 @@ InputBindingWidget::InputBindingWidget(QWidget* parent) connect(this, &QPushButton::clicked, this, &InputBindingWidget::onClicked); } -InputBindingWidget::InputBindingWidget(QWidget* parent, std::string section_name, std::string key_name) +InputBindingWidget::InputBindingWidget(QWidget* parent, SettingsInterface* sif, std::string section_name, std::string key_name) : QPushButton(parent) { setMinimumWidth(225); @@ -47,7 +47,7 @@ InputBindingWidget::InputBindingWidget(QWidget* parent, std::string section_name connect(this, &QPushButton::clicked, this, &InputBindingWidget::onClicked); - setKey(std::move(section_name), std::move(key_name)); + initialize(sif, std::move(section_name), std::move(key_name)); } InputBindingWidget::~InputBindingWidget() @@ -55,12 +55,12 @@ InputBindingWidget::~InputBindingWidget() Q_ASSERT(!isListeningForInput()); } -void InputBindingWidget::setKey(std::string section_name, std::string key_name) +void InputBindingWidget::initialize(SettingsInterface* sif, std::string section_name, std::string key_name) { + m_sif = sif; m_section_name = std::move(section_name); m_key_name = std::move(key_name); - m_bindings = Host::GetBaseStringListSetting(m_section_name.c_str(), m_key_name.c_str()); - updateText(); + reloadBinding(); } void InputBindingWidget::updateText() @@ -168,8 +168,17 @@ void InputBindingWidget::setNewBinding() InputManager::ConvertInputBindingKeysToString(m_new_bindings.data(), m_new_bindings.size())); if (!new_binding.empty()) { - QtHost::SetBaseStringSettingValue(m_section_name.c_str(), m_key_name.c_str(), new_binding.c_str()); - g_emu_thread->reloadInputBindings(); + if (m_sif) + { + m_sif->SetStringValue(m_section_name.c_str(), m_key_name.c_str(), new_binding.c_str()); + m_sif->Save(); + g_emu_thread->reloadGameSettings(); + } + else + { + QtHost::SetBaseStringSettingValue(m_section_name.c_str(), m_key_name.c_str(), new_binding.c_str()); + g_emu_thread->reloadInputBindings(); + } } m_bindings.clear(); @@ -179,14 +188,25 @@ void InputBindingWidget::setNewBinding() void InputBindingWidget::clearBinding() { m_bindings.clear(); - QtHost::RemoveBaseSettingValue(m_section_name.c_str(), m_key_name.c_str()); - g_emu_thread->reloadInputBindings(); - updateText(); + if (m_sif) + { + m_sif->DeleteValue(m_section_name.c_str(), m_key_name.c_str()); + m_sif->Save(); + g_emu_thread->reloadGameSettings(); + } + else + { + QtHost::RemoveBaseSettingValue(m_section_name.c_str(), m_key_name.c_str()); + g_emu_thread->reloadInputBindings(); + } + reloadBinding(); } void InputBindingWidget::reloadBinding() { - m_bindings = Host::GetBaseStringListSetting(m_section_name.c_str(), m_key_name.c_str()); + m_bindings = m_sif ? + m_sif->GetStringList(m_section_name.c_str(), m_key_name.c_str()) : + Host::GetBaseStringListSetting(m_section_name.c_str(), m_key_name.c_str()); updateText(); } @@ -236,7 +256,7 @@ void InputBindingWidget::startListeningForInput(u32 timeout_in_seconds) void InputBindingWidget::stopListeningForInput() { - updateText(); + reloadBinding(); delete m_input_listen_timer; m_input_listen_timer = nullptr; std::vector().swap(m_new_bindings); @@ -293,7 +313,7 @@ void InputBindingWidget::unhookInputManager() void InputBindingWidget::openDialog() { - InputBindingDialog binding_dialog(m_section_name, m_key_name, m_bindings, QtUtils::GetRootWidget(this)); + InputBindingDialog binding_dialog(m_sif, m_section_name, m_key_name, m_bindings, QtUtils::GetRootWidget(this)); binding_dialog.exec(); reloadBinding(); } diff --git a/pcsx2-qt/Settings/InputBindingWidget.h b/pcsx2-qt/Settings/InputBindingWidget.h index 59b4b31c61..3f53b6be6e 100644 --- a/pcsx2-qt/Settings/InputBindingWidget.h +++ b/pcsx2-qt/Settings/InputBindingWidget.h @@ -22,6 +22,7 @@ class QTimer; class ControllerSettingsDialog; +class SettingsInterface; class InputBindingWidget : public QPushButton { @@ -29,10 +30,10 @@ class InputBindingWidget : public QPushButton public: InputBindingWidget(QWidget* parent); - InputBindingWidget(QWidget* parent, std::string section_name, std::string key_name); + InputBindingWidget(QWidget* parent, SettingsInterface* sif, std::string section_name, std::string key_name); ~InputBindingWidget(); - void setKey(std::string section_name, std::string key_name); + void initialize(SettingsInterface* sif, std::string section_name, std::string key_name); public Q_SLOTS: void clearBinding(); @@ -65,6 +66,7 @@ protected: void hookInputManager(); void unhookInputManager(); + SettingsInterface* m_sif = nullptr; std::string m_section_name; std::string m_key_name; std::vector m_bindings; diff --git a/pcsx2-qt/Settings/SettingsDialog.cpp b/pcsx2-qt/Settings/SettingsDialog.cpp index 25a5d6e6ef..29de96500f 100644 --- a/pcsx2-qt/Settings/SettingsDialog.cpp +++ b/pcsx2-qt/Settings/SettingsDialog.cpp @@ -363,13 +363,13 @@ void SettingsDialog::setBoolSettingValue(const char* section, const char* key, s { value.has_value() ? m_sif->SetBoolValue(section, key, value.value()) : m_sif->DeleteValue(section, key); m_sif->Save(); + g_emu_thread->reloadGameSettings(); } else { value.has_value() ? QtHost::SetBaseBoolSettingValue(section, key, value.value()) : QtHost::RemoveBaseSettingValue(section, key); + g_emu_thread->applySettings(); } - - g_emu_thread->applySettings(); } void SettingsDialog::setIntSettingValue(const char* section, const char* key, std::optional value) @@ -378,13 +378,13 @@ void SettingsDialog::setIntSettingValue(const char* section, const char* key, st { value.has_value() ? m_sif->SetIntValue(section, key, value.value()) : m_sif->DeleteValue(section, key); m_sif->Save(); + g_emu_thread->reloadGameSettings(); } else { value.has_value() ? QtHost::SetBaseIntSettingValue(section, key, value.value()) : QtHost::RemoveBaseSettingValue(section, key); + g_emu_thread->applySettings(); } - - g_emu_thread->applySettings(); } void SettingsDialog::setFloatSettingValue(const char* section, const char* key, std::optional value) @@ -393,13 +393,13 @@ void SettingsDialog::setFloatSettingValue(const char* section, const char* key, { value.has_value() ? m_sif->SetFloatValue(section, key, value.value()) : m_sif->DeleteValue(section, key); m_sif->Save(); + g_emu_thread->reloadGameSettings(); } else { value.has_value() ? QtHost::SetBaseFloatSettingValue(section, key, value.value()) : QtHost::RemoveBaseSettingValue(section, key); + g_emu_thread->applySettings(); } - - g_emu_thread->applySettings(); } void SettingsDialog::setStringSettingValue(const char* section, const char* key, std::optional value) @@ -408,13 +408,13 @@ void SettingsDialog::setStringSettingValue(const char* section, const char* key, { value.has_value() ? m_sif->SetStringValue(section, key, value.value()) : m_sif->DeleteValue(section, key); m_sif->Save(); + g_emu_thread->reloadGameSettings(); } else { value.has_value() ? QtHost::SetBaseStringSettingValue(section, key, value.value()) : QtHost::RemoveBaseSettingValue(section, key); + g_emu_thread->applySettings(); } - - g_emu_thread->applySettings(); } void SettingsDialog::openGamePropertiesDialog(const GameList::Entry* game, const std::string_view& serial, u32 crc) diff --git a/pcsx2-qt/pcsx2-qt.vcxproj b/pcsx2-qt/pcsx2-qt.vcxproj index e2c09299cc..f9e8374808 100644 --- a/pcsx2-qt/pcsx2-qt.vcxproj +++ b/pcsx2-qt/pcsx2-qt.vcxproj @@ -194,6 +194,7 @@ + @@ -331,4 +332,4 @@ - + \ No newline at end of file diff --git a/pcsx2-qt/pcsx2-qt.vcxproj.filters b/pcsx2-qt/pcsx2-qt.vcxproj.filters index a21ac8c3a7..99a8f64460 100644 --- a/pcsx2-qt/pcsx2-qt.vcxproj.filters +++ b/pcsx2-qt/pcsx2-qt.vcxproj.filters @@ -231,6 +231,9 @@ Settings + + Settings + @@ -392,4 +395,4 @@ Tools\Input Recording - + \ No newline at end of file diff --git a/pcsx2/Frontend/INISettingsInterface.cpp b/pcsx2/Frontend/INISettingsInterface.cpp index 7a23c817ad..36b9cc5da7 100644 --- a/pcsx2/Frontend/INISettingsInterface.cpp +++ b/pcsx2/Frontend/INISettingsInterface.cpp @@ -203,7 +203,7 @@ void INISettingsInterface::ClearSection(const char* section) m_ini.SetValue(section, nullptr, nullptr); } -std::vector INISettingsInterface::GetStringList(const char* section, const char* key) +std::vector INISettingsInterface::GetStringList(const char* section, const char* key) const { std::list entries; if (!m_ini.GetAllValues(section, key, entries)) diff --git a/pcsx2/Frontend/INISettingsInterface.h b/pcsx2/Frontend/INISettingsInterface.h index e258ed23e8..76d0a81788 100644 --- a/pcsx2/Frontend/INISettingsInterface.h +++ b/pcsx2/Frontend/INISettingsInterface.h @@ -52,7 +52,7 @@ public: void DeleteValue(const char* section, const char* key) override; void ClearSection(const char* section) override; - std::vector GetStringList(const char* section, const char* key) override; + std::vector GetStringList(const char* section, const char* key) const override; void SetStringList(const char* section, const char* key, const std::vector& items) override; bool RemoveFromStringList(const char* section, const char* key, const char* item) override; bool AddToStringList(const char* section, const char* key, const char* item) override; diff --git a/pcsx2/Frontend/InputManager.cpp b/pcsx2/Frontend/InputManager.cpp index b6b3a30a7a..92b8e09e04 100644 --- a/pcsx2/Frontend/InputManager.cpp +++ b/pcsx2/Frontend/InputManager.cpp @@ -808,7 +808,7 @@ bool InputManager::DoEventHook(InputBindingKey key, float value) // Binding Updater // ------------------------------------------------------------------------ -void InputManager::ReloadBindings(SettingsInterface& si) +void InputManager::ReloadBindings(SettingsInterface& si, SettingsInterface& binding_si) { PauseVibration(); @@ -817,10 +817,14 @@ void InputManager::ReloadBindings(SettingsInterface& si) s_binding_map.clear(); s_pad_vibration_array.clear(); - AddHotkeyBindings(si); + // Hotkeys use the base configuration, except if the custom hotkeys option is enabled. + const bool use_profile_hotkeys = si.GetBoolValue("Pad", "UseProfileHotkeyBindings", false); + AddHotkeyBindings(use_profile_hotkeys ? binding_si : si); + // If there's an input profile, we load pad bindings from it alone, rather than + // falling back to the base configuration. for (u32 pad = 0; pad < PAD::NUM_CONTROLLER_PORTS; pad++) - AddPadBindings(si, pad, PAD::GetDefaultPadType(pad)); + AddPadBindings(binding_si, pad, PAD::GetDefaultPadType(pad)); } // ------------------------------------------------------------------------ diff --git a/pcsx2/Frontend/InputManager.h b/pcsx2/Frontend/InputManager.h index cf2e6b3ba4..ce4e000297 100644 --- a/pcsx2/Frontend/InputManager.h +++ b/pcsx2/Frontend/InputManager.h @@ -223,7 +223,7 @@ namespace InputManager GenericInputBindingMapping GetGenericBindingMapping(const std::string_view& device); /// Re-parses the config and registers all hotkey and pad bindings. - void ReloadBindings(SettingsInterface& si); + void ReloadBindings(SettingsInterface& si, SettingsInterface& binding_si); /// Re-parses the sources part of the config and initializes any backends. void ReloadSources(SettingsInterface& si, std::unique_lock& settings_lock); diff --git a/pcsx2/PAD/Host/PAD.cpp b/pcsx2/PAD/Host/PAD.cpp index 4c19b5e990..b7a7f2daa4 100644 --- a/pcsx2/PAD/Host/PAD.cpp +++ b/pcsx2/PAD/Host/PAD.cpp @@ -15,6 +15,8 @@ #include "PrecompiledHeader.h" +#include "common/FileSystem.h" +#include "common/Path.h" #include "common/StringUtil.h" #include "common/SettingsInterface.h" @@ -184,6 +186,9 @@ void PAD::LoadConfig(const SettingsInterface& si) { PAD::s_macro_buttons = {}; + EmuConfig.MultitapPort0_Enabled = si.GetBoolValue("Pad", "MultitapPort1", false); + EmuConfig.MultitapPort1_Enabled = si.GetBoolValue("Pad", "MultitapPort2", false); + // This is where we would load controller types, if onepad supported them. for (u32 i = 0; i < NUM_CONTROLLER_PORTS; i++) { @@ -232,6 +237,8 @@ void PAD::SetDefaultConfig(SettingsInterface& si) si.SetBoolValue("InputSources", "SDL", true); si.SetBoolValue("InputSources", "SDLControllerEnhancedMode", false); si.SetBoolValue("InputSources", "XInput", false); + si.SetBoolValue("Pad", "MultitapPort1", false); + si.SetBoolValue("Pad", "MultitapPort2", false); // PCSX2 Controller Settings - Controller 1 / Controller 2 / ... // Use the automapper to set this up. @@ -386,6 +393,62 @@ void PAD::ClearPortBindings(SettingsInterface& si, u32 port) si.DeleteValue(section.c_str(), info->bindings[i].name); } +void PAD::CopyConfiguration(SettingsInterface* dest_si, const SettingsInterface& src_si, + bool copy_pad_config, bool copy_pad_bindings, bool copy_hotkey_bindings) +{ + if (copy_pad_config) + { + dest_si->CopyBoolValue(src_si, "Pad", "MultitapPort1"); + dest_si->CopyBoolValue(src_si, "Pad", "MultitapPort2"); + } + + for (u32 port = 0; port < NUM_CONTROLLER_PORTS; port++) + { + const std::string section(fmt::format("Pad{}", port + 1)); + const std::string type(src_si.GetStringValue(section.c_str(), "Type", GetDefaultPadType(port))); + if (copy_pad_config) + dest_si->SetStringValue(section.c_str(), "Type", type.c_str()); + + const ControllerInfo* info = GetControllerInfo(type); + if (!info) + return; + + if (copy_pad_bindings) + { + for (u32 i = 0; i < info->num_bindings; i++) + { + const ControllerBindingInfo& bi = info->bindings[i]; + dest_si->CopyStringListValue(src_si, section.c_str(), bi.name); + } + + for (u32 i = 0; i < NUM_MACRO_BUTTONS_PER_CONTROLLER; i++) + { + dest_si->CopyStringListValue(src_si, section.c_str(), fmt::format("Macro{}", i + 1).c_str()); + dest_si->CopyStringValue(src_si, section.c_str(), fmt::format("Macro{}Binds", i + 1).c_str()); + dest_si->CopyUIntValue(src_si, section.c_str(), fmt::format("Macro{}Frequency", i + 1).c_str()); + } + } + + if (copy_pad_config) + { + dest_si->CopyFloatValue(src_si, section.c_str(), "AxisScale"); + + if (info->vibration_caps != VibrationCapabilities::NoVibration) + { + dest_si->CopyFloatValue(src_si, section.c_str(), "LargeMotorScale"); + dest_si->CopyFloatValue(src_si, section.c_str(), "SmallMotorScale"); + } + } + } + + if (copy_hotkey_bindings) + { + std::vector hotkeys(InputManager::GetHotkeyList()); + for (const HotkeyInfo* hki : hotkeys) + dest_si->CopyStringListValue(src_si, "Hotkeys", hki->name); + } +} + PAD::VibrationCapabilities PAD::GetControllerVibrationCapabilities(const std::string_view& type) { const ControllerInfo* info = GetControllerInfo(type); @@ -520,6 +583,20 @@ void PAD::SetMacroButtonState(u32 pad, u32 index, bool state) } } +std::vector PAD::GetInputProfileNames() +{ + FileSystem::FindResultsArray results; + FileSystem::FindFiles(EmuFolders::InputProfiles.c_str(), "*.ini", + FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_HIDDEN_FILES | FILESYSTEM_FIND_RELATIVE_PATHS, + &results); + + std::vector ret; + ret.reserve(results.size()); + for (FILESYSTEM_FIND_DATA& fd : results) + ret.emplace_back(Path::GetFileTitle(fd.FileName)); + return ret; +} + void PAD::ApplyMacroButton(u32 pad, const MacroButton& mb) { const float value = mb.toggle_state ? 1.0f : 0.0f; diff --git a/pcsx2/PAD/Host/PAD.h b/pcsx2/PAD/Host/PAD.h index 005994b452..53d5ff3a98 100644 --- a/pcsx2/PAD/Host/PAD.h +++ b/pcsx2/PAD/Host/PAD.h @@ -99,6 +99,10 @@ namespace PAD /// Clears all bindings for a given port. void ClearPortBindings(SettingsInterface& si, u32 port); + /// Copies pad configuration from one interface (ini) to another. + void CopyConfiguration(SettingsInterface* dest_si, const SettingsInterface& src_si, + bool copy_pad_config = true, bool copy_pad_bindings = true, bool copy_hotkey_bindings = true); + /// Updates vibration and other internal state. Called at the *end* of a frame. void Update(); @@ -124,4 +128,7 @@ namespace PAD /// Sets the state of the specified macro button. void SetMacroButtonState(u32 pad, u32 index, bool state); + + /// Returns a list of input profiles available. + std::vector GetInputProfileNames(); } // namespace PAD diff --git a/pcsx2/Pcsx2Config.cpp b/pcsx2/Pcsx2Config.cpp index b74796f37b..4f7fd52a36 100644 --- a/pcsx2/Pcsx2Config.cpp +++ b/pcsx2/Pcsx2Config.cpp @@ -1067,8 +1067,11 @@ void Pcsx2Config::LoadSave(SettingsWrapper& wrap) SettingsWrapBitBool(SavestateZstdCompression); SettingsWrapBitBool(McdEnableEjection); SettingsWrapBitBool(McdFolderAutoManage); +#ifndef PCSX2_CORE + // We put mtap in the Pad section for Qt to make it easier to manually edit input profiles. SettingsWrapBitBool(MultitapPort0_Enabled); SettingsWrapBitBool(MultitapPort1_Enabled); +#endif // Process various sub-components: diff --git a/pcsx2/VMManager.cpp b/pcsx2/VMManager.cpp index 7c348304d8..d22b1cb624 100644 --- a/pcsx2/VMManager.cpp +++ b/pcsx2/VMManager.cpp @@ -300,11 +300,12 @@ 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(*si); + PAD::LoadConfig(*binding_si); InputManager::ReloadSources(*si, lock); - InputManager::ReloadBindings(*si); + InputManager::ReloadBindings(*si, *binding_si); // Remove any user-specified hacks in the config (we don't want stale/conflicting values when it's globally disabled). EmuConfig.GS.MaskUserHacks(); @@ -425,7 +426,7 @@ bool VMManager::UpdateGameSettingsLayer() std::string input_profile_name; if (new_interface) - new_interface->GetStringValue("Pad", "InputProfileName", &input_profile_name); + new_interface->GetStringValue("EmuCore", "InputProfileName", &input_profile_name); if (!s_game_settings_interface && !new_interface && s_input_profile_name == input_profile_name) return false;