Qt: Make "Ignore Inversion" a global mapping setting

Instead of being specific to DInput.
This commit is contained in:
Stenzek 2024-01-26 16:03:57 +10:00 committed by Connor McLaughlin
parent 22037e75ba
commit 850b839fc3
16 changed files with 177 additions and 30 deletions

View File

@ -75,6 +75,7 @@ target_sources(pcsx2-qt PRIVATE
Settings/ControllerGlobalSettingsWidget.ui Settings/ControllerGlobalSettingsWidget.ui
Settings/ControllerMacroEditWidget.ui Settings/ControllerMacroEditWidget.ui
Settings/ControllerMacroWidget.ui Settings/ControllerMacroWidget.ui
Settings/ControllerMappingSettingsDialog.ui
Settings/ControllerMouseSettingsDialog.ui Settings/ControllerMouseSettingsDialog.ui
Settings/ControllerSettingsWindow.cpp Settings/ControllerSettingsWindow.cpp
Settings/ControllerSettingsWindow.h Settings/ControllerSettingsWindow.h

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team // SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
// SPDX-License-Identifier: LGPL-3.0+ // SPDX-License-Identifier: LGPL-3.0+
#include "Settings/ControllerGlobalSettingsWidget.h" #include "Settings/ControllerGlobalSettingsWidget.h"
@ -40,7 +40,6 @@ ControllerGlobalSettingsWidget::ControllerGlobalSettingsWidget(QWidget* parent,
#ifdef _WIN32 #ifdef _WIN32
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableXInputSource, "InputSources", "XInput", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableXInputSource, "InputSources", "XInput", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableDInputSource, "InputSources", "DInput", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableDInputSource, "InputSources", "DInput", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.ignoreDInputInversion, "InputSources", "IgnoreDInputInversion", false);
#else #else
m_ui.mainLayout->removeWidget(m_ui.xinputGroup); m_ui.mainLayout->removeWidget(m_ui.xinputGroup);
m_ui.xinputGroup->deleteLater(); m_ui.xinputGroup->deleteLater();
@ -176,3 +175,19 @@ ControllerMouseSettingsDialog::ControllerMouseSettingsDialog(QWidget* parent, Co
} }
ControllerMouseSettingsDialog::~ControllerMouseSettingsDialog() = default; ControllerMouseSettingsDialog::~ControllerMouseSettingsDialog() = default;
ControllerMappingSettingsDialog::ControllerMappingSettingsDialog(ControllerSettingsWindow* parent)
: QDialog(parent)
{
m_ui.setupUi(this);
SettingsInterface* sif = parent->getProfileSettingsInterface();
m_ui.icon->setPixmap(QIcon::fromTheme(QStringLiteral("settings-3-line")).pixmap(32, 32));
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.ignoreInversion, "InputSources", "IgnoreInversion", false);
connect(m_ui.buttonBox->button(QDialogButtonBox::Close), &QPushButton::clicked, this, &QDialog::accept);
}
ControllerMappingSettingsDialog::~ControllerMappingSettingsDialog() = default;

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team // SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
// SPDX-License-Identifier: LGPL-3.0+ // SPDX-License-Identifier: LGPL-3.0+
#pragma once #pragma once
@ -12,6 +12,7 @@
#include "ui_ControllerGlobalSettingsWidget.h" #include "ui_ControllerGlobalSettingsWidget.h"
#include "ui_ControllerLEDSettingsDialog.h" #include "ui_ControllerLEDSettingsDialog.h"
#include "ui_ControllerMappingSettingsDialog.h"
#include "ui_ControllerMouseSettingsDialog.h" #include "ui_ControllerMouseSettingsDialog.h"
class ControllerSettingsWindow; class ControllerSettingsWindow;
@ -66,3 +67,15 @@ public:
private: private:
Ui::ControllerMouseSettingsDialog m_ui; Ui::ControllerMouseSettingsDialog m_ui;
}; };
class ControllerMappingSettingsDialog : public QDialog
{
Q_OBJECT
public:
ControllerMappingSettingsDialog(ControllerSettingsWindow* parent);
~ControllerMappingSettingsDialog();
private:
Ui::ControllerMappingSettingsDialog m_ui;
};

View File

@ -82,16 +82,6 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="1">
<widget class="QCheckBox" name="ignoreDInputInversion">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Some third party controllers incorrectly flag their analog sticks as inverted on the positive component, but not negative.&lt;/p&gt;&lt;p&gt;As a result, the analog stick will be &amp;quot;stuck on&amp;quot; even while resting at neutral position. &lt;/p&gt;&lt;p&gt;Enabling this setting will tell PCSX2 to ignore inversion flags when creating mappings, allowing such controllers to function normally.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Ignore Inversion</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2"> <item row="1" column="0" colspan="2">
<widget class="QLabel" name="label_9"> <widget class="QLabel" name="label_9">
<property name="text"> <property name="text">

View File

@ -0,0 +1,105 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ControllerMappingSettingsDialog</class>
<widget class="QDialog" name="ControllerMappingSettingsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>654</width>
<height>275</height>
</rect>
</property>
<property name="windowTitle">
<string>Controller Mapping Settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="icon">
<property name="minimumSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:700;&quot;&gt;Controller Mapping Settings&lt;/span&gt;&lt;br/&gt;These settings fine-tune the behavior when mapping physical controllers to the emulated controllers.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string/>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QCheckBox" name="ignoreInversion">
<property name="text">
<string>Ignore Inversion</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Some third party controllers incorrectly flag their analog sticks as inverted on the positive component, but not negative.&lt;/p&gt;&lt;p&gt;As a result, the analog stick will be &amp;quot;stuck on&amp;quot; even while resting at neutral position. &lt;/p&gt;&lt;p&gt;Enabling this setting will tell PCSX2 to ignore inversion flags when creating mappings, allowing such controllers to function normally.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>28</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Close</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="../resources/resources.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -39,6 +39,7 @@ ControllerSettingsWindow::ControllerSettingsWindow()
connect(m_ui.newProfile, &QPushButton::clicked, this, &ControllerSettingsWindow::onNewProfileClicked); connect(m_ui.newProfile, &QPushButton::clicked, this, &ControllerSettingsWindow::onNewProfileClicked);
connect(m_ui.loadProfile, &QPushButton::clicked, this, &ControllerSettingsWindow::onLoadProfileClicked); connect(m_ui.loadProfile, &QPushButton::clicked, this, &ControllerSettingsWindow::onLoadProfileClicked);
connect(m_ui.deleteProfile, &QPushButton::clicked, this, &ControllerSettingsWindow::onDeleteProfileClicked); connect(m_ui.deleteProfile, &QPushButton::clicked, this, &ControllerSettingsWindow::onDeleteProfileClicked);
connect(m_ui.mappingSettings, &QPushButton::clicked, this, &ControllerSettingsWindow::onMappingSettingsClicked);
connect(m_ui.restoreDefaults, &QPushButton::clicked, this, &ControllerSettingsWindow::onRestoreDefaultsClicked); connect(m_ui.restoreDefaults, &QPushButton::clicked, this, &ControllerSettingsWindow::onRestoreDefaultsClicked);
connect(g_emu_thread, &EmuThread::onInputDevicesEnumerated, this, &ControllerSettingsWindow::onInputDevicesEnumerated); connect(g_emu_thread, &EmuThread::onInputDevicesEnumerated, this, &ControllerSettingsWindow::onInputDevicesEnumerated);
@ -186,6 +187,12 @@ void ControllerSettingsWindow::onDeleteProfileClicked()
switchProfile({}); switchProfile({});
} }
void ControllerSettingsWindow::onMappingSettingsClicked()
{
ControllerMappingSettingsDialog dialog(this);
dialog.exec();
}
void ControllerSettingsWindow::onRestoreDefaultsClicked() void ControllerSettingsWindow::onRestoreDefaultsClicked()
{ {
if (QMessageBox::question(this, tr("Restore Defaults"), if (QMessageBox::question(this, tr("Restore Defaults"),

View File

@ -76,6 +76,7 @@ private Q_SLOTS:
void onNewProfileClicked(); void onNewProfileClicked();
void onLoadProfileClicked(); void onLoadProfileClicked();
void onDeleteProfileClicked(); void onDeleteProfileClicked();
void onMappingSettingsClicked();
void onRestoreDefaultsClicked(); void onRestoreDefaultsClicked();
void onInputDevicesEnumerated(const QList<QPair<QString, QString>>& devices); void onInputDevicesEnumerated(const QList<QPair<QString, QString>>& devices);

View File

@ -123,6 +123,16 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QPushButton" name="mappingSettings">
<property name="text">
<string>Mapping Settings</string>
</property>
<property name="icon">
<iconset theme="settings-3-line"/>
</property>
</widget>
</item>
<item> <item>
<widget class="QPushButton" name="restoreDefaults"> <widget class="QPushButton" name="restoreDefaults">
<property name="text"> <property name="text">

View File

@ -428,6 +428,7 @@
<FileType>Document</FileType> <FileType>Document</FileType>
</QtUi> </QtUi>
<None Include="Settings\ControllerBindingWidget_Popn.ui" /> <None Include="Settings\ControllerBindingWidget_Popn.ui" />
<None Include="Settings\ControllerMappingSettingsDialog.ui" />
<None Include="Settings\FolderSettingsWidget.ui" /> <None Include="Settings\FolderSettingsWidget.ui" />
</ItemGroup> </ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />

View File

@ -661,6 +661,9 @@
<None Include="Settings\ControllerBindingWidget_Popn.ui"> <None Include="Settings\ControllerBindingWidget_Popn.ui">
<Filter>Settings</Filter> <Filter>Settings</Filter>
</None> </None>
<None Include="Settings\ControllerMappingSettingsDialog.ui">
<Filter>Settings</Filter>
</None>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<QtTs Include="Translations\pcsx2-qt_en.ts"> <QtTs Include="Translations\pcsx2-qt_en.ts">

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team // SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
// SPDX-License-Identifier: LGPL-3.0+ // SPDX-License-Identifier: LGPL-3.0+
#define INITGUID #define INITGUID
@ -49,8 +49,6 @@ static constexpr std::array<const char*, DInputSource::NUM_HAT_DIRECTIONS> s_hat
bool DInputSource::Initialize(SettingsInterface& si, std::unique_lock<std::mutex>& settings_lock) bool DInputSource::Initialize(SettingsInterface& si, std::unique_lock<std::mutex>& settings_lock)
{ {
LoadSettings(si);
m_dinput_module.reset(LoadLibraryW(L"dinput8")); m_dinput_module.reset(LoadLibraryW(L"dinput8"));
if (!m_dinput_module) if (!m_dinput_module)
{ {
@ -91,12 +89,6 @@ bool DInputSource::Initialize(SettingsInterface& si, std::unique_lock<std::mutex
void DInputSource::UpdateSettings(SettingsInterface& si, std::unique_lock<std::mutex>& settings_lock) void DInputSource::UpdateSettings(SettingsInterface& si, std::unique_lock<std::mutex>& settings_lock)
{ {
LoadSettings(si);
}
void DInputSource::LoadSettings(SettingsInterface& si)
{
m_ignore_inversion = si.GetBoolValue("InputSources", "IgnoreDInputInversion", false);
} }
static BOOL CALLBACK EnumCallback(LPCDIDEVICEINSTANCEW lpddi, LPVOID pvRef) static BOOL CALLBACK EnumCallback(LPCDIDEVICEINSTANCEW lpddi, LPVOID pvRef)
@ -392,7 +384,7 @@ TinyString DInputSource::ConvertKeyToString(InputBindingKey key)
if (key.source_subtype == InputSubclass::ControllerAxis) if (key.source_subtype == InputSubclass::ControllerAxis)
{ {
const char* modifier = (key.modifier == InputModifier::FullAxis ? "Full" : (key.modifier == InputModifier::Negate ? "-" : "+")); const char* modifier = (key.modifier == InputModifier::FullAxis ? "Full" : (key.modifier == InputModifier::Negate ? "-" : "+"));
ret.fmt("DInput-{}/{}Axis{}{}", u32(key.source_index), modifier, u32(key.data), (key.invert && !m_ignore_inversion) ? "~" : ""); ret.fmt("DInput-{}/{}Axis{}{}", u32(key.source_index), modifier, u32(key.data), (key.invert && !ShouldIgnoreInversion()) ? "~" : "");
} }
else if (key.source_subtype == InputSubclass::ControllerButton && key.data >= MAX_NUM_BUTTONS) else if (key.source_subtype == InputSubclass::ControllerButton && key.data >= MAX_NUM_BUTTONS)
{ {

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team // SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
// SPDX-License-Identifier: LGPL-3.0+ // SPDX-License-Identifier: LGPL-3.0+
#pragma once #pragma once
@ -36,7 +36,6 @@ public:
bool Initialize(SettingsInterface& si, std::unique_lock<std::mutex>& settings_lock) override; bool Initialize(SettingsInterface& si, std::unique_lock<std::mutex>& settings_lock) override;
void UpdateSettings(SettingsInterface& si, std::unique_lock<std::mutex>& settings_lock) override; void UpdateSettings(SettingsInterface& si, std::unique_lock<std::mutex>& settings_lock) override;
void LoadSettings(SettingsInterface& si);
bool ReloadDevices() override; bool ReloadDevices() override;
void Shutdown() override; void Shutdown() override;
@ -81,5 +80,4 @@ private:
HWND m_toplevel_window = nullptr; HWND m_toplevel_window = nullptr;
ControllerDataArray m_controllers; ControllerDataArray m_controllers;
bool m_ignore_inversion = false;
}; };

View File

@ -1,7 +1,9 @@
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team // SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
// SPDX-License-Identifier: LGPL-3.0+ // SPDX-License-Identifier: LGPL-3.0+
#include "Input/InputSource.h" #include "Input/InputSource.h"
#include "Host.h"
#include "common/StringUtil.h" #include "common/StringUtil.h"
InputSource::InputSource() = default; InputSource::InputSource() = default;
@ -144,3 +146,9 @@ std::string InputSource::ConvertGenericControllerKeyToString(InputBindingKey key
return {}; return {};
} }
} }
bool InputSource::ShouldIgnoreInversion()
{
// This is only called when binding controllers, so the lookup is fine.
return Host::GetBaseBoolSettingValue("InputSources", "IgnoreInversion", false);
}

View File

@ -66,4 +66,7 @@ public:
/// Converts a generic controller key to a string. /// Converts a generic controller key to a string.
static std::string ConvertGenericControllerKeyToString(InputBindingKey key); static std::string ConvertGenericControllerKeyToString(InputBindingKey key);
/// Returns true if inversion/negation should be ignored when binding axes.
static bool ShouldIgnoreInversion();
}; };

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team // SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
// SPDX-License-Identifier: LGPL-3.0+ // SPDX-License-Identifier: LGPL-3.0+
#include "Config.h" #include "Config.h"
@ -452,7 +452,7 @@ TinyString SDLInputSource::ConvertKeyToString(InputBindingKey key)
if (key.data < std::size(s_sdl_axis_names)) if (key.data < std::size(s_sdl_axis_names))
ret.fmt("SDL-{}/{}{}", static_cast<u32>(key.source_index), modifier, s_sdl_axis_names[key.data]); ret.fmt("SDL-{}/{}{}", static_cast<u32>(key.source_index), modifier, s_sdl_axis_names[key.data]);
else else
ret.fmt("SDL-{}/{}Axis{}{}", static_cast<u32>(key.source_index), modifier, key.data, key.invert ? "~" : ""); ret.fmt("SDL-{}/{}Axis{}{}", static_cast<u32>(key.source_index), modifier, key.data, (key.invert && !ShouldIgnoreInversion()) ? "~" : "");
} }
else if (key.source_subtype == InputSubclass::ControllerButton) else if (key.source_subtype == InputSubclass::ControllerButton)
{ {

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team // SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
// SPDX-License-Identifier: LGPL-3.0+ // SPDX-License-Identifier: LGPL-3.0+
#pragma once #pragma once