From 31ebe842e858babe8bbdbff8f33693ecc948ed40 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Mon, 30 Jan 2023 19:25:58 +1000 Subject: [PATCH] Qt: Add per-bind sensitivity/deadzone controls (shift-click) --- pcsx2-qt/Settings/InputBindingDialog.cpp | 33 +++++++++ pcsx2-qt/Settings/InputBindingDialog.h | 3 + pcsx2-qt/Settings/InputBindingDialog.ui | 89 +++++++++++++++++++++++- pcsx2/Frontend/InputManager.cpp | 25 +++++-- pcsx2/PAD/Host/PAD.cpp | 31 ++++++--- 5 files changed, 166 insertions(+), 15 deletions(-) diff --git a/pcsx2-qt/Settings/InputBindingDialog.cpp b/pcsx2-qt/Settings/InputBindingDialog.cpp index c6afd3cbcb..0bd6c3e94c 100644 --- a/pcsx2-qt/Settings/InputBindingDialog.cpp +++ b/pcsx2-qt/Settings/InputBindingDialog.cpp @@ -17,6 +17,7 @@ #include "QtHost.h" #include "QtUtils.h" +#include "Settings/ControllerSettingWidgetBinder.h" #include "Settings/InputBindingDialog.h" #include "Settings/InputBindingWidget.h" #include @@ -24,6 +25,8 @@ #include #include +#include "fmt/format.h" + // _BitScanForward() #include "pcsx2/GS/GSIntrin.h" @@ -45,6 +48,26 @@ InputBindingDialog::InputBindingDialog(SettingsInterface* sif, InputBindingInfo: connect(m_ui.clearBindings, &QPushButton::clicked, this, &InputBindingDialog::onClearBindingsButtonClicked); connect(m_ui.buttonBox, &QDialogButtonBox::rejected, [this]() { done(0); }); updateList(); + + // Only show the sensitivity controls for binds where it's applicable. + if (bind_type == InputBindingInfo::Type::Button || bind_type == InputBindingInfo::Type::Axis || + bind_type == InputBindingInfo::Type::HalfAxis) + { + ControllerSettingWidgetBinder::BindWidgetToInputProfileNormalized( + sif, m_ui.sensitivity, m_section_name, fmt::format("{}Scale", m_key_name), 100.0f, 1.0f); + ControllerSettingWidgetBinder::BindWidgetToInputProfileNormalized( + sif, m_ui.deadzone, m_section_name, fmt::format("{}Deadzone", m_key_name), 100.0f, 0.0f); + + connect(m_ui.sensitivity, &QSlider::valueChanged, this, &InputBindingDialog::onSensitivityChanged); + connect(m_ui.deadzone, &QSlider::valueChanged, this, &InputBindingDialog::onDeadzoneChanged); + + onSensitivityChanged(m_ui.sensitivity->value()); + onDeadzoneChanged(m_ui.deadzone->value()); + } + else + { + m_ui.verticalLayout->removeWidget(m_ui.sensitivityWidget); + } } InputBindingDialog::~InputBindingDialog() @@ -318,6 +341,16 @@ void InputBindingDialog::inputManagerHookCallback(InputBindingKey key, float val } } +void InputBindingDialog::onSensitivityChanged(int value) +{ + m_ui.sensitivityValue->setText(tr("%1%").arg(value)); +} + +void InputBindingDialog::onDeadzoneChanged(int value) +{ + m_ui.deadzoneValue->setText(tr("%1%").arg(value)); +} + void InputBindingDialog::hookInputManager() { InputManager::SetHook([this](InputBindingKey key, float value) { diff --git a/pcsx2-qt/Settings/InputBindingDialog.h b/pcsx2-qt/Settings/InputBindingDialog.h index e0133e98f4..b516551e0d 100644 --- a/pcsx2-qt/Settings/InputBindingDialog.h +++ b/pcsx2-qt/Settings/InputBindingDialog.h @@ -41,6 +41,9 @@ protected Q_SLOTS: void onInputListenTimerTimeout(); void inputManagerHookCallback(InputBindingKey key, float value); + void onSensitivityChanged(int value); + void onDeadzoneChanged(int value); + protected: enum : u32 { diff --git a/pcsx2-qt/Settings/InputBindingDialog.ui b/pcsx2-qt/Settings/InputBindingDialog.ui index c951b5a377..d430cb387c 100644 --- a/pcsx2-qt/Settings/InputBindingDialog.ui +++ b/pcsx2-qt/Settings/InputBindingDialog.ui @@ -10,7 +10,7 @@ 0 0 533 - 283 + 266 @@ -30,6 +30,93 @@ + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Sensitivity: + + + + + + + 1 + + + 200 + + + 100 + + + Qt::Horizontal + + + QSlider::TicksBelow + + + + + + + 100% + + + + + + + Deadzone: + + + + + + + 0 + + + 100 + + + 1 + + + Qt::Horizontal + + + QSlider::TicksBelow + + + 5 + + + + + + + 100% + + + + + + diff --git a/pcsx2/Frontend/InputManager.cpp b/pcsx2/Frontend/InputManager.cpp index 29dc64d161..7ca89dfecc 100644 --- a/pcsx2/Frontend/InputManager.cpp +++ b/pcsx2/Frontend/InputManager.cpp @@ -105,6 +105,7 @@ namespace InputManager static bool ParseBindingAndGetSource(const std::string_view& binding, InputBindingKey* key, InputSource** source); static bool IsAxisHandler(const InputEventHandler& handler); + static float ApplySingleBindingScale(float sensitivity, float deadzone, float value); static void AddHotkeyBindings(SettingsInterface& si); static void AddPadBindings(SettingsInterface& si, u32 pad, const char* default_type); @@ -585,6 +586,12 @@ std::string InputManager::GetPointerDeviceName(u32 pointer_index) // Binding Enumeration // ------------------------------------------------------------------------ +float InputManager::ApplySingleBindingScale(float scale, float deadzone, float value) +{ + const float svalue = std::clamp(value * scale, 0.0f, 1.0f); + return (deadzone > 0.0f && svalue < deadzone) ? 0.0f : svalue; +} + std::vector InputManager::GetHotkeyList() { std::vector ret; @@ -613,7 +620,7 @@ void InputManager::AddHotkeyBindings(SettingsInterface& si) void InputManager::AddPadBindings(SettingsInterface& si, u32 pad_index, const char* default_type) { - const std::string section(StringUtil::StdStringFromFormat("Pad%u", pad_index + 1)); + const std::string section(fmt::format("Pad{}", pad_index + 1)); const std::string type(si.GetStringValue(section.c_str(), "Type", default_type)); if (type.empty() || type == "None") return; @@ -636,9 +643,12 @@ void InputManager::AddPadBindings(SettingsInterface& si, u32 pad_index, const ch if (!bindings.empty()) { // we use axes for all pad bindings to simplify things, and because they are pressure sensitive - AddBindings(bindings, InputAxisEventHandler{[pad_index, bind_index = bi.bind_index](float value) { - PAD::SetControllerState(pad_index, bind_index, value); - }}); + const float sensitivity = si.GetFloatValue(section.c_str(), fmt::format("{}Scale", bi.name).c_str(), 1.0f); + const float deadzone = si.GetFloatValue(section.c_str(), fmt::format("{}Deadzone", bi.name).c_str(), 0.0f); + AddBindings( + bindings, InputAxisEventHandler{[pad_index, bind_index = bi.bind_index, sensitivity, deadzone](float value) { + PAD::SetControllerState(pad_index, bind_index, ApplySingleBindingScale(sensitivity, deadzone, value)); + }}); } } break; @@ -717,8 +727,11 @@ void InputManager::AddUSBBindings(SettingsInterface& si, u32 port) const std::vector bindings(si.GetStringList(section.c_str(), bind_name.c_str())); if (!bindings.empty()) { - AddBindings(bindings, InputAxisEventHandler{[port, bind_index = bi.bind_index]( - float value) { USB::SetDeviceBindValue(port, bind_index, value); }}); + const float sensitivity = si.GetFloatValue(section.c_str(), fmt::format("{}Scale", bi.name).c_str(), 1.0f); + const float deadzone = si.GetFloatValue(section.c_str(), fmt::format("{}Deadzone", bi.name).c_str(), 0.0f); + AddBindings(bindings, InputAxisEventHandler{[port, bind_index = bi.bind_index, sensitivity, deadzone](float value) { + USB::SetDeviceBindValue(port, bind_index, ApplySingleBindingScale(sensitivity, deadzone, value)); + }}); } } break; diff --git a/pcsx2/PAD/Host/PAD.cpp b/pcsx2/PAD/Host/PAD.cpp index 78f7da56a1..79e56813c6 100644 --- a/pcsx2/PAD/Host/PAD.cpp +++ b/pcsx2/PAD/Host/PAD.cpp @@ -504,7 +504,12 @@ void PAD::ClearPortBindings(SettingsInterface& si, u32 port) return; for (u32 i = 0; i < info->num_bindings; i++) - si.DeleteValue(section.c_str(), info->bindings[i].name); + { + const InputBindingInfo& bi = info->bindings[i]; + si.DeleteValue(section.c_str(), bi.name); + si.DeleteValue(section.c_str(), fmt::format("{}Scale", bi.name).c_str()); + si.DeleteValue(section.c_str(), fmt::format("{}Deadzone", bi.name).c_str()); + } } void PAD::CopyConfiguration(SettingsInterface* dest_si, const SettingsInterface& src_si, @@ -545,6 +550,8 @@ void PAD::CopyConfiguration(SettingsInterface* dest_si, const SettingsInterface& { const InputBindingInfo& bi = info->bindings[i]; dest_si->CopyStringListValue(src_si, section.c_str(), bi.name); + dest_si->CopyFloatValue(src_si, section.c_str(), fmt::format("{}Sensitivity", bi.name).c_str()); + dest_si->CopyFloatValue(src_si, section.c_str(), fmt::format("{}Deadzone", bi.name).c_str()); } for (u32 i = 0; i < NUM_MACRO_BUTTONS_PER_CONTROLLER; i++) @@ -601,8 +608,8 @@ void PAD::CopyConfiguration(SettingsInterface* dest_si, const SettingsInterface& } static u32 TryMapGenericMapping(SettingsInterface& si, const std::string& section, - const InputManager::GenericInputBindingMapping& mapping, GenericInputBinding generic_name, - const char* bind_name) + const InputManager::GenericInputBindingMapping& mapping, InputBindingInfo::Type bind_type, + GenericInputBinding generic_name, const char* bind_name) { // find the mapping it corresponds to const std::string* found_mapping = nullptr; @@ -615,6 +622,14 @@ static u32 TryMapGenericMapping(SettingsInterface& si, const std::string& sectio } } + // Remove previously-set binding scales. + if (bind_type == InputBindingInfo::Type::Button || bind_type == InputBindingInfo::Type::Axis || + bind_type == InputBindingInfo::Type::HalfAxis) + { + si.DeleteValue(section.c_str(), fmt::format("{}Scale", bind_name).c_str()); + si.DeleteValue(section.c_str(), fmt::format("{}Deadzone", bind_name).c_str()); + } + if (found_mapping) { Console.WriteLn("(MapController) Map %s/%s to '%s'", section.c_str(), bind_name, found_mapping->c_str()); @@ -645,17 +660,17 @@ bool PAD::MapController(SettingsInterface& si, u32 controller, if (bi.generic_mapping == GenericInputBinding::Unknown) continue; - num_mappings += TryMapGenericMapping(si, section, mapping, bi.generic_mapping, bi.name); + num_mappings += TryMapGenericMapping(si, section, mapping, bi.bind_type, bi.generic_mapping, bi.name); } if (info->vibration_caps == VibrationCapabilities::LargeSmallMotors) { - num_mappings += TryMapGenericMapping(si, section, mapping, GenericInputBinding::SmallMotor, "SmallMotor"); - num_mappings += TryMapGenericMapping(si, section, mapping, GenericInputBinding::LargeMotor, "LargeMotor"); + num_mappings += TryMapGenericMapping(si, section, mapping, InputBindingInfo::Type::Motor, GenericInputBinding::SmallMotor, "SmallMotor"); + num_mappings += TryMapGenericMapping(si, section, mapping, InputBindingInfo::Type::Motor, GenericInputBinding::LargeMotor, "LargeMotor"); } else if (info->vibration_caps == VibrationCapabilities::SingleMotor) { - if (TryMapGenericMapping(si, section, mapping, GenericInputBinding::LargeMotor, "Motor") == 0) - num_mappings += TryMapGenericMapping(si, section, mapping, GenericInputBinding::SmallMotor, "Motor"); + if (TryMapGenericMapping(si, section, mapping, InputBindingInfo::Type::Motor, GenericInputBinding::LargeMotor, "Motor") == 0) + num_mappings += TryMapGenericMapping(si, section, mapping, InputBindingInfo::Type::Motor, GenericInputBinding::SmallMotor, "Motor"); else num_mappings++; }