diff --git a/pcsx2-qt/Settings/ControllerBindingWidgets.cpp b/pcsx2-qt/Settings/ControllerBindingWidgets.cpp index f2cadfbb86..1fc408ac68 100644 --- a/pcsx2-qt/Settings/ControllerBindingWidgets.cpp +++ b/pcsx2-qt/Settings/ControllerBindingWidgets.cpp @@ -520,6 +520,19 @@ void ControllerCustomSettingsWidget::createSettingWidgets(ControllerBindingWidge } break; + case PAD::ControllerSettingInfo::Type::IntegerList: + { + QComboBox* cb = new QComboBox(widget_parent); + cb->setObjectName(QString::fromUtf8(si.name)); + for (u32 i = 0; si.options[i] != nullptr; i++) + cb->addItem(qApp->translate(cinfo->name, si.options[i])); + SettingWidgetBinder::BindWidgetToIntSetting(sif, cb, section, std::move(key_name), si.IntegerDefaultValue(), si.IntegerMinValue()); + layout->addWidget(new QLabel(qApp->translate(cinfo->name, si.display_name), widget_parent), current_row, 0); + layout->addWidget(cb, current_row, 1, 1, 3); + current_row++; + } + break; + case PAD::ControllerSettingInfo::Type::Float: { QDoubleSpinBox* sb = new QDoubleSpinBox(widget_parent); @@ -605,6 +618,14 @@ void ControllerCustomSettingsWidget::restoreDefaults() } break; + case PAD::ControllerSettingInfo::Type::IntegerList: + { + QComboBox* widget = findChild(QString::fromStdString(si.name)); + if (widget) + widget->setCurrentIndex(si.IntegerDefaultValue() - si.IntegerMinValue()); + } + break; + case PAD::ControllerSettingInfo::Type::Float: { QDoubleSpinBox* widget = findChild(QString::fromStdString(si.name)); diff --git a/pcsx2-qt/Settings/ControllerGlobalSettingsWidget.cpp b/pcsx2-qt/Settings/ControllerGlobalSettingsWidget.cpp index 5fabe9e039..55c223895a 100644 --- a/pcsx2-qt/Settings/ControllerGlobalSettingsWidget.cpp +++ b/pcsx2-qt/Settings/ControllerGlobalSettingsWidget.cpp @@ -36,8 +36,6 @@ ControllerGlobalSettingsWidget::ControllerGlobalSettingsWidget(QWidget* parent, SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableMouseMapping, "UI", "EnableMouseMapping", false); ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.multitapPort1, "Pad", "MultitapPort1", false); ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.multitapPort2, "Pad", "MultitapPort2", false); - ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.pointerXInvert, "Pad", "PointerXInvert", false); - ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.pointerYInvert, "Pad", "PointerYInvert", false); ControllerSettingWidgetBinder::BindWidgetToInputProfileFloat(sif, m_ui.pointerXScale, "Pad", "PointerXScale", 8.0f); ControllerSettingWidgetBinder::BindWidgetToInputProfileFloat(sif, m_ui.pointerYScale, "Pad", "PointerYScale", 8.0f); diff --git a/pcsx2-qt/Settings/ControllerGlobalSettingsWidget.ui b/pcsx2-qt/Settings/ControllerGlobalSettingsWidget.ui index 0ac675b828..0be624b00f 100644 --- a/pcsx2-qt/Settings/ControllerGlobalSettingsWidget.ui +++ b/pcsx2-qt/Settings/ControllerGlobalSettingsWidget.ui @@ -26,6 +26,31 @@ 0 + + + + Detected Devices + + + + + + + 200 + 0 + + + + + 200 + 16777215 + + + + + + + @@ -85,142 +110,39 @@ - - - - Mouse/Pointer Source + + + + Qt::Vertical - - - - - - 0 - 0 - - - - - 20 - 0 - - + + + 20 + 45 + + + + + + + + Profile Settings + + + + - 10 - - - - - - - - 0 - 0 - - - - - 150 - 0 - - - - 1 - - - 30 - - - Qt::Horizontal - - - - - - - Invert - - - - - - - - 0 - 0 - - - - - 150 - 0 - - - - 1 - - - 30 - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - - 20 - 0 - - - - 10 - - - - - - - Invert - - - - - - - Using raw input improves precision when you bind controller sticks to the mouse pointer. Also enables multiple mice to be used. + 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 - - - - Vertical Sensitivity: - - - - + - Horizontal Sensitivity: - - - - - - - Enable Mouse Mapping + Use Per-Profile Hotkeys @@ -260,16 +182,16 @@ - - + + - Profile Settings + Mouse/Pointer Source - - - + + + - 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. + Using raw input improves precision when you bind controller sticks to the mouse pointer. Also enables multiple mice to be used. true @@ -277,50 +199,126 @@ - + - Use Per-Profile Hotkeys + Horizontal Sensitivity: - - - - - - - Qt::Vertical - - - - 20 - 45 - - - - - - - - Detected Devices - - - - - - - 200 - 0 - - - - - 200 - 16777215 - + + + + + + + 0 + 0 + + + + + 150 + 0 + + + + 1 + + + 30 + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + + 20 + 0 + + + + 10 + + + + + + + + + Vertical Sensitivity: + + + + + + + 0 + 0 + + + + + 150 + 0 + + + + 1 + + + 30 + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + + 20 + 0 + + + + 10 + + + + + + + + + + + Enable Mouse Mapping + + + + + diff --git a/pcsx2/Frontend/FullscreenUI.cpp b/pcsx2/Frontend/FullscreenUI.cpp index f51a022661..cdbaf9b60a 100644 --- a/pcsx2/Frontend/FullscreenUI.cpp +++ b/pcsx2/Frontend/FullscreenUI.cpp @@ -1217,6 +1217,12 @@ void FullscreenUI::DrawIntListSetting(SettingsInterface* bsi, const char* title, int default_value, const char* const* options, size_t option_count, int option_offset, bool enabled, float height, ImFont* font, ImFont* summary_font) { + if (options && option_count == 0) + { + while (options[option_count] != nullptr) + option_count++; + } + const bool game_settings = IsEditingGameSettings(bsi); const std::optional value = bsi->GetOptionalIntValue(section, key, game_settings ? std::nullopt : std::optional(default_value)); @@ -3288,6 +3294,10 @@ void FullscreenUI::DrawControllerSettingsPage() DrawIntRangeSetting(bsi, title.c_str(), si.description, section, si.name, si.IntegerDefaultValue(), si.IntegerMinValue(), si.IntegerMaxValue(), si.format, true); break; + case PAD::ControllerSettingInfo::Type::IntegerList: + DrawIntListSetting(bsi, title.c_str(), si.description, section, si.name, si.IntegerDefaultValue(), si.options, 0, + si.IntegerMinValue(), true); + break; case PAD::ControllerSettingInfo::Type::Float: DrawFloatRangeSetting(bsi, title.c_str(), si.description, section, si.name, si.FloatDefaultValue(), si.FloatMinValue(), si.FloatMaxValue(), si.format, si.multiplier, true); diff --git a/pcsx2/Frontend/InputManager.cpp b/pcsx2/Frontend/InputManager.cpp index abef4108ce..2ddb278fa5 100644 --- a/pcsx2/Frontend/InputManager.cpp +++ b/pcsx2/Frontend/InputManager.cpp @@ -1006,10 +1006,8 @@ void InputManager::ReloadBindings(SettingsInterface& si, SettingsInterface& bind { // From lilypad: 1 mouse pixel = 1/8th way down. const float default_scale = (axis <= static_cast(InputPointerAxis::Y)) ? 8.0f : 1.0f; - const float invert = - si.GetBoolValue("Pad", fmt::format("Pointer{}Invert", s_pointer_axis_names[axis]).c_str(), false) ? -1.0f : 1.0f; s_pointer_axis_scale[axis] = - invert / + 1.0f / std::max(si.GetFloatValue("Pad", fmt::format("Pointer{}Scale", s_pointer_axis_names[axis]).c_str(), default_scale), 1.0f); } } diff --git a/pcsx2/PAD/Host/KeyStatus.cpp b/pcsx2/PAD/Host/KeyStatus.cpp index c56c41d1de..f5c58b1e52 100644 --- a/pcsx2/PAD/Host/KeyStatus.cpp +++ b/pcsx2/PAD/Host/KeyStatus.cpp @@ -24,7 +24,7 @@ using namespace PAD; KeyStatus::KeyStatus() { - Init(); + std::memset(&m_analog, 0, sizeof(m_analog)); for (u32 pad = 0; pad < NUM_CONTROLLER_PORTS; pad++) { @@ -32,6 +32,8 @@ KeyStatus::KeyStatus() m_axis_scale[pad][1] = 1.0f; m_pressure_modifier[pad] = 0.5f; } + + Init(); } void KeyStatus::Init() @@ -70,22 +72,22 @@ void KeyStatus::Set(u32 pad, u32 index, float value) { case PAD_R_LEFT: case PAD_R_RIGHT: - m_analog[pad].rx = MERGE(pad, PAD_R_RIGHT, PAD_R_LEFT); + m_analog[pad].rx = m_analog[pad].invert_rx ? MERGE(pad, PAD_R_LEFT, PAD_R_RIGHT) : MERGE(pad, PAD_R_RIGHT, PAD_R_LEFT); break; case PAD_R_DOWN: case PAD_R_UP: - m_analog[pad].ry = MERGE(pad, PAD_R_DOWN, PAD_R_UP); + m_analog[pad].ry = m_analog[pad].invert_ry ? MERGE(pad, PAD_R_UP, PAD_R_DOWN) : MERGE(pad, PAD_R_DOWN, PAD_R_UP); break; case PAD_L_LEFT: case PAD_L_RIGHT: - m_analog[pad].lx = MERGE(pad, PAD_L_RIGHT, PAD_L_LEFT); + m_analog[pad].lx = m_analog[pad].invert_lx ? MERGE(pad, PAD_L_LEFT, PAD_L_RIGHT) : MERGE(pad, PAD_L_RIGHT, PAD_L_LEFT); break; case PAD_L_DOWN: case PAD_L_UP: - m_analog[pad].ly = MERGE(pad, PAD_L_DOWN, PAD_L_UP); + m_analog[pad].ly = m_analog[pad].invert_ly ? MERGE(pad, PAD_L_UP, PAD_L_DOWN) : MERGE(pad, PAD_L_DOWN, PAD_L_UP); break; default: @@ -98,7 +100,8 @@ void KeyStatus::Set(u32 pad, u32 index, float value) { // Don't affect L2/R2, since they are analog on most pads. const float pmod = ((m_button[pad] & (1u << PAD_PRESSURE)) == 0 && !IsTriggerKey(index)) ? m_pressure_modifier[pad] : 1.0f; - m_button_pressure[pad][index] = static_cast(std::clamp(value * pmod * 255.0f, 0.0f, 255.0f)); + const float dz_value = (value < m_button_deadzone[pad]) ? 0.0f : value; + m_button_pressure[pad][index] = static_cast(std::clamp(dz_value * pmod * 255.0f, 0.0f, 255.0f)); // Since we reordered the buttons for better UI, we need to remap them here. static constexpr std::array bitmask_mapping = {{ @@ -123,8 +126,7 @@ void KeyStatus::Set(u32 pad, u32 index, float value) // remainder are analogs and not used here }}; - // TODO: Deadzone here? - if (value > 0.0f) + if (dz_value > 0.0f) m_button[pad] &= ~(1u << bitmask_mapping[index]); else m_button[pad] |= (1u << bitmask_mapping[index]); diff --git a/pcsx2/PAD/Host/KeyStatus.h b/pcsx2/PAD/Host/KeyStatus.h index 3be985dffe..cbc9b661a0 100644 --- a/pcsx2/PAD/Host/KeyStatus.h +++ b/pcsx2/PAD/Host/KeyStatus.h @@ -30,6 +30,8 @@ namespace PAD { u8 lx, ly; u8 rx, ry; + u8 invert_lx, invert_ly; + u8 invert_rx, invert_ry; }; PAD::ControllerType m_type[NUM_CONTROLLER_PORTS] = {}; @@ -39,6 +41,7 @@ namespace PAD float m_axis_scale[NUM_CONTROLLER_PORTS][2]; float m_vibration_scale[NUM_CONTROLLER_PORTS][2]; float m_pressure_modifier[NUM_CONTROLLER_PORTS]; + float m_button_deadzone[NUM_CONTROLLER_PORTS]; public: KeyStatus(); @@ -58,6 +61,17 @@ namespace PAD __fi void SetVibrationScale(u32 pad, u32 motor, float scale) { m_vibration_scale[pad][motor] = scale; } __fi float GetPressureModifier(u32 pad) const { return m_pressure_modifier[pad]; } __fi void SetPressureModifier(u32 pad, float mod) { m_pressure_modifier[pad] = mod; } + __fi void SetButtonDeadzone(u32 pad, float deadzone) { m_button_deadzone[pad] = deadzone; } + __fi void SetAnalogInvertL(u32 pad, bool x, bool y) + { + m_analog[pad].invert_lx = x; + m_analog[pad].invert_ly = y; + } + __fi void SetAnalogInvertR(u32 pad, bool x, bool y) + { + m_analog[pad].invert_rx = x; + m_analog[pad].invert_ry = y; + } u32 GetButtons(u32 pad); u8 GetPressure(u32 pad, u32 index); diff --git a/pcsx2/PAD/Host/PAD.cpp b/pcsx2/PAD/Host/PAD.cpp index 6d9bcee58a..49292dcc07 100644 --- a/pcsx2/PAD/Host/PAD.cpp +++ b/pcsx2/PAD/Host/PAD.cpp @@ -268,7 +268,9 @@ void PAD::LoadConfig(const SettingsInterface& si) const float axis_deadzone = si.GetFloatValue(section.c_str(), "Deadzone", DEFAULT_STICK_DEADZONE); const float axis_scale = si.GetFloatValue(section.c_str(), "AxisScale", DEFAULT_STICK_SCALE); + const float button_deadzone = si.GetFloatValue(section.c_str(), "ButtonDeadzone", DEFAULT_BUTTON_DEADZONE); g_key_status.SetAxisScale(i, axis_deadzone, axis_scale); + g_key_status.SetButtonDeadzone(i, button_deadzone); if (ci->vibration_caps != VibrationCapabilities::NoVibration) { @@ -281,6 +283,11 @@ void PAD::LoadConfig(const SettingsInterface& si) const float pressure_modifier = si.GetFloatValue(section.c_str(), "PressureModifier", 1.0f); g_key_status.SetPressureModifier(i, pressure_modifier); + const int invert_l = si.GetIntValue(section.c_str(), "InvertL", 0); + const int invert_r = si.GetIntValue(section.c_str(), "InvertR", 0); + g_key_status.SetAnalogInvertL(i, (invert_l & 1) != 0, (invert_l & 2) != 0); + g_key_status.SetAnalogInvertR(i, (invert_r & 1) != 0, (invert_r & 2) != 0); + LoadMacroButtonConfig(si, i, type, section); } } @@ -310,20 +317,42 @@ void PAD::SetDefaultControllerConfig(SettingsInterface& si) si.SetBoolValue("Pad", "MultitapPort2", false); si.SetFloatValue("Pad", "PointerXScale", 8.0f); si.SetFloatValue("Pad", "PointerYScale", 8.0f); - si.SetBoolValue("Pad", "PointerXInvert", false); - si.SetBoolValue("Pad", "PointerYInvert", false); // PCSX2 Controller Settings - Default pad types and parameters. for (u32 i = 0; i < NUM_CONTROLLER_PORTS; i++) { + const char* type = GetDefaultPadType(i); const std::string section(GetConfigSection(i)); si.ClearSection(section.c_str()); - si.SetStringValue(section.c_str(), "Type", GetDefaultPadType(i)); - si.SetFloatValue(section.c_str(), "Deadzone", DEFAULT_STICK_DEADZONE); - si.SetFloatValue(section.c_str(), "AxisScale", DEFAULT_STICK_SCALE); - si.SetFloatValue(section.c_str(), "LargeMotorScale", DEFAULT_MOTOR_SCALE); - si.SetFloatValue(section.c_str(), "SmallMotorScale", DEFAULT_MOTOR_SCALE); - si.SetFloatValue(section.c_str(), "PressureModifier", DEFAULT_PRESSURE_MODIFIER); + si.SetStringValue(section.c_str(), "Type", type); + + const ControllerInfo* ci = GetControllerInfo(type); + if (ci) + { + for (u32 i = 0; i < ci->num_settings; i++) + { + const ControllerSettingInfo& csi = ci->settings[i]; + switch (csi.type) + { + case ControllerSettingInfo::Type::Boolean: + si.SetBoolValue(section.c_str(), csi.name, csi.BooleanDefaultValue()); + break; + case ControllerSettingInfo::Type::Integer: + case ControllerSettingInfo::Type::IntegerList: + si.SetIntValue(section.c_str(), csi.name, csi.IntegerDefaultValue()); + break; + case ControllerSettingInfo::Type::Float: + si.SetFloatValue(section.c_str(), csi.name, csi.FloatDefaultValue()); + break; + case ControllerSettingInfo::Type::String: + case ControllerSettingInfo::Type::Path: + si.SetStringValue(section.c_str(), csi.name, csi.StringDefaultValue()); + break; + default: + break; + } + } + } } // PCSX2 Controller Settings - Controller 1 / Controller 2 / ... @@ -415,26 +444,42 @@ static const PAD::ControllerBindingInfo s_dualshock2_binds[] = { {"SmallMotor", "Small (High Frequency) Motor", PAD::ControllerBindingType::Motor, GenericInputBinding::SmallMotor}, }; +static const char* s_dualshock2_invert_entries[] = { + "Not Inverted", + "Invert Left/Right", + "Invert Up/Down", + "Invert Left/Right + Up/Down", + nullptr}; + static const PAD::ControllerSettingInfo s_dualshock2_settings[] = { + {PAD::ControllerSettingInfo::Type::IntegerList, "InvertL", "Invert Left Stick", + "Inverts the direction of the left analog stick.", + "0", "0", "3", nullptr, nullptr, s_dualshock2_invert_entries, 0.0f}, + {PAD::ControllerSettingInfo::Type::IntegerList, "InvertR", "Invert Right Stick", + "Inverts the direction of the right analog stick.", + "0", "0", "3", nullptr, nullptr, s_dualshock2_invert_entries, 0.0f}, {PAD::ControllerSettingInfo::Type::Float, "Deadzone", "Analog Deadzone", "Sets the analog stick deadzone, i.e. the fraction of the stick movement which will be ignored.", - "0.00", "0.00", "1.00", "0.01", "%.0f%%", 100.0f}, + "0.00", "0.00", "1.00", "0.01", "%.0f%%", nullptr, 100.0f}, {PAD::ControllerSettingInfo::Type::Float, "AxisScale", "Analog Sensitivity", "Sets the analog stick axis scaling factor. A value between 1.30 and 1.40 is recommended when using recent " "controllers, e.g. DualShock 4, Xbox One Controller.", - "1.33", "0.01", "2.00", "0.01", "%.0f%%", 100.0f}, + "1.33", "0.01", "2.00", "0.01", "%.0f%%", nullptr, 100.0f}, {PAD::ControllerSettingInfo::Type::Float, "LargeMotorScale", "Large Motor Vibration Scale", "Increases or decreases the intensity of low frequency vibration sent by the game.", - "1.00", "0.00", "2.00", "0.01", "%.0f%%", 100.0f}, + "1.00", "0.00", "2.00", "0.01", "%.0f%%", nullptr, 100.0f}, {PAD::ControllerSettingInfo::Type::Float, "SmallMotorScale", "Small Motor Vibration Scale", "Increases or decreases the intensity of high frequency vibration sent by the game.", - "1.00", "0.00", "2.00", "0.01", "%.0f%%", 100.0f}, + "1.00", "0.00", "2.00", "0.01", "%.0f%%", nullptr, 100.0f}, + {PAD::ControllerSettingInfo::Type::Float, "ButtonDeadzone", "Button/Trigger Deadzone", + "Sets the deadzone for activating buttons/triggers, i.e. the fraction of the trigger which will be ignored.", + "0.00", "0.00", "1.00", "0.01", "%.0f%%", nullptr, 100.0f}, /*{PAD::ControllerSettingInfo::Type::Float, "InitialPressure", "Initial Pressure", "Sets the pressure when the modifier button isn't held.", - "1.00", "0.01", "1.00", "0.01", "%.0f%%", 100.0f},*/ + "1.00", "0.01", "1.00", "0.01", "%.0f%%", nullptr, 100.0f},*/ {PAD::ControllerSettingInfo::Type::Float, "PressureModifier", "Modifier Pressure", "Sets the pressure when the modifier button is held.", - "0.50", "0.01", "1.00", "0.01", "%.0f%%", 100.0f}, + "0.50", "0.01", "1.00", "0.01", "%.0f%%", nullptr, 100.0f}, }; static const PAD::ControllerInfo s_controller_info[] = { @@ -523,8 +568,6 @@ void PAD::CopyConfiguration(SettingsInterface* dest_si, const SettingsInterface& dest_si->CopyBoolValue(src_si, "Pad", "MultitapPort2"); dest_si->CopyFloatValue(src_si, "Pad", "PointerXScale"); dest_si->CopyFloatValue(src_si, "Pad", "PointerYScale"); - dest_si->CopyBoolValue(src_si, "Pad", "PointerXInvert"); - dest_si->CopyBoolValue(src_si, "Pad", "PointerYInvert"); for (u32 i = 0; i < static_cast(InputSourceType::Count); i++) { dest_si->CopyBoolValue(src_si, "InputSources", @@ -571,6 +614,30 @@ void PAD::CopyConfiguration(SettingsInterface* dest_si, const SettingsInterface& dest_si->CopyFloatValue(src_si, section.c_str(), "LargeMotorScale"); dest_si->CopyFloatValue(src_si, section.c_str(), "SmallMotorScale"); } + + for (u32 i = 0; i < info->num_settings; i++) + { + const ControllerSettingInfo& csi = info->settings[i]; + switch (csi.type) + { + case ControllerSettingInfo::Type::Boolean: + dest_si->CopyBoolValue(src_si, section.c_str(), csi.name); + break; + case ControllerSettingInfo::Type::Integer: + case ControllerSettingInfo::Type::IntegerList: + dest_si->CopyIntValue(src_si, section.c_str(), csi.name); + break; + case ControllerSettingInfo::Type::Float: + dest_si->CopyFloatValue(src_si, section.c_str(), csi.name); + break; + case ControllerSettingInfo::Type::String: + case ControllerSettingInfo::Type::Path: + dest_si->CopyStringValue(src_si, section.c_str(), csi.name); + break; + default: + break; + } + } } } diff --git a/pcsx2/PAD/Host/PAD.h b/pcsx2/PAD/Host/PAD.h index 93f231afc6..b3b15f9971 100644 --- a/pcsx2/PAD/Host/PAD.h +++ b/pcsx2/PAD/Host/PAD.h @@ -77,6 +77,7 @@ namespace PAD { Boolean, Integer, + IntegerList, Float, String, Path, @@ -91,6 +92,7 @@ namespace PAD const char* max_value; const char* step_value; const char* format; + const char** options; float multiplier; const char* StringDefaultValue() const; @@ -129,6 +131,7 @@ namespace PAD static constexpr float DEFAULT_STICK_SCALE = 1.33f; static constexpr float DEFAULT_MOTOR_SCALE = 1.0f; static constexpr float DEFAULT_PRESSURE_MODIFIER = 0.5f; + static constexpr float DEFAULT_BUTTON_DEADZONE = 0.0f; /// Returns the default type for the specified port. const char* GetDefaultPadType(u32 pad);