Qt: Implement per-game controller configuration

This commit is contained in:
Stenzek 2024-08-24 14:10:25 +10:00
parent 9e3507e0f4
commit bda6869084
No known key found for this signature in database
24 changed files with 427 additions and 179 deletions

View File

@ -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<SettingsInterface*, NUM_LAYERS> m_layers{};

View File

@ -10,7 +10,7 @@ class MemorySettingsInterface final : public SettingsInterface
{
public:
MemorySettingsInterface();
~MemorySettingsInterface();
~MemorySettingsInterface() override;
bool Save(Error* error = nullptr) override;

View File

@ -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();

View File

@ -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<bool, 2> 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;
}

View File

@ -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);

View File

@ -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<std::mutex>& lock);
static LayeredSettingsInterface GetControllerSettingsLayers(std::unique_lock<std::mutex>& lock);
static LayeredSettingsInterface GetHotkeySettingsLayer(std::unique_lock<std::mutex>& 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex>& lock)
LayeredSettingsInterface System::GetControllerSettingsLayers(std::unique_lock<std::mutex>& 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<std::mutex>& 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)

View File

@ -247,7 +247,7 @@ void ControllerBindingWidget::onTypeChanged()
m_controller_info = Controller::GetControllerInfo(static_cast<ControllerType>(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)

View File

@ -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);
}

View File

@ -50,7 +50,7 @@
</widget>
</item>
<item row="0" column="1" rowspan="7">
<widget class="QGroupBox" name="groupBox_3">
<widget class="QGroupBox" name="deviceListGroup">
<property name="title">
<string>Detected Devices</string>
</property>
@ -162,8 +162,7 @@
<string>Controller LED Settings</string>
</property>
<property name="icon">
<iconset theme="lightbulb-line">
<normaloff>.</normaloff>.</iconset>
<iconset theme="lightbulb-line"/>
</property>
</widget>
</item>
@ -284,7 +283,7 @@
<number>30</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
</widget>
</item>
@ -339,7 +338,7 @@
<number>30</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
</widget>
</item>
@ -388,7 +387,7 @@
<item row="6" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>

View File

@ -23,41 +23,87 @@
static constexpr const std::array<char, 4> 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);
if (!game_sif)
{
refreshProfileList();
// trigger a device enumeration to populate the device list
g_emu_thread->enumerateInputDevices();
g_emu_thread->enumerateVibrationMotors();
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<bool, 2> 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<std::pair<std::string, std::string>>& devices)
{
m_device_list = devices;
@ -280,16 +362,16 @@ void ControllerSettingsWindow::onVibrationMotorsEnumerated(const QList<InputBind
bool ControllerSettingsWindow::getBoolValue(const char* section, const char* key, bool default_value) const
{
if (m_profile_interface)
return m_profile_interface->GetBoolValue(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<INISettingsInterface> sif(std::make_unique<INISettingsInterface>(std::move(path)));
std::unique_ptr<INISettingsInterface> sif = std::make_unique<INISettingsInterface>(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();
}

View File

@ -20,6 +20,8 @@
#include <utility>
#include <vector>
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<std::pair<std::string, std::string>>& 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<std::pair<std::string, std::string>>& 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<bool, 2> getEnabledMultitaps() const;
Ui::ControllerSettingsWindow m_ui;
SettingsInterface* m_editing_settings_interface = nullptr;
ControllerGlobalSettingsWidget* m_global_settings = nullptr;
std::array<ControllerBindingWidget*, MAX_PORTS> m_port_bindings{};
HotkeySettingsWidget* m_hotkey_settings = nullptr;
@ -108,5 +128,5 @@ private:
QStringList m_vibration_motors;
QString m_profile_name;
std::unique_ptr<SettingsInterface> m_profile_interface;
std::unique_ptr<SettingsInterface> m_profile_settings_interface;
};

View File

@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>1279</width>
<width>1284</width>
<height>672</height>
</rect>
</property>
@ -20,7 +20,7 @@
<string>DuckStation Controller Settings</string>
</property>
<property name="windowIcon">
<iconset resource="resources/resources.qrc">
<iconset resource="resources/duckstation-qt.qrc">
<normaloff>:/icons/duck.png</normaloff>:/icons/duck.png</iconset>
</property>
<layout class="QGridLayout" name="gridLayout">
@ -65,9 +65,9 @@
<item row="1" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<layout class="QHBoxLayout" name="editProfileLayout">
<item>
<widget class="QLabel" name="label">
<widget class="QLabel" name="editProfileLabel">
<property name="text">
<string>Editing Profile:</string>
</property>
@ -106,6 +106,16 @@
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="copyGlobalSettings">
<property name="text">
<string>Copy Global Settings</string>
</property>
<property name="icon">
<iconset theme="folder-open-line"/>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="restoreDefaults">
<property name="text">
@ -130,7 +140,7 @@
</layout>
</widget>
<resources>
<include location="resources/resources.qrc"/>
<include location="resources/duckstation-qt.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -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<std::string> 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<std::string> 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<std::string> 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++)

View File

@ -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();

View File

@ -60,17 +60,17 @@
</item>
<item row="6" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QComboBox" name="region">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<item>
<widget class="QComboBox" name="region">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="restoreRegion">
<property name="enabled">
<bool>false</bool>
@ -79,7 +79,7 @@
<string>Restore</string>
</property>
</widget>
</item>
</item>
</layout>
</item>
<item row="0" column="0">
@ -106,7 +106,7 @@
<item row="18" column="0" colspan="2">
<widget class="QTableWidget" name="tracks">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
<set>QAbstractItemView::EditTrigger::NoEditTriggers</set>
</property>
<property name="cornerButtonEnabled">
<bool>false</bool>
@ -216,15 +216,12 @@
</property>
</widget>
</item>
<item row="13" column="1">
<widget class="QComboBox" name="inputProfile"/>
</item>
<item row="16" column="1">
<layout class="QHBoxLayout" name="verifyLayout" stretch="0,1,0">
<item>
<spacer name="verifySpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
@ -306,8 +303,21 @@
<string>Comments</string>
</property>
<property name="icon">
<iconset theme="information-line">
<normaloff>.</normaloff>.</iconset>
<iconset theme="information-line"/>
</property>
</widget>
</item>
</layout>
</item>
<item row="13" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="1,0">
<item>
<widget class="QComboBox" name="inputProfile"/>
</item>
<item>
<widget class="QPushButton" name="editInputProfile">
<property name="text">
<string>Edit...</string>
</property>
</widget>
</item>

View File

@ -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);

View File

@ -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<CPUExecutionMode> current_mode =

View File

@ -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();

View File

@ -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();

View File

@ -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);

View File

@ -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<int>(err));
}
return (err == SI_OK);
}

View File

@ -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;

View File

@ -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<u32>(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);
}

View File

@ -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<std::mutex>& settings_lock);