diff --git a/src/core/analog_controller.cpp b/src/core/analog_controller.cpp index 46ace1e86..b5e239a27 100644 --- a/src/core/analog_controller.cpp +++ b/src/core/analog_controller.cpp @@ -136,7 +136,7 @@ void AnalogController::SetBindState(u32 index, float value) if (index == static_cast(Button::Analog)) { // analog toggle - if (value >= 0.5f) + if (value >= m_button_deadzone) { if (m_command == Command::Idle) ProcessAnalogModeToggle(); @@ -168,22 +168,30 @@ void AnalogController::SetBindState(u32 index, float value) { case HalfAxis::LLeft: case HalfAxis::LRight: - m_axis_state[static_cast(Axis::LeftX)] = MERGE(HalfAxis::LRight, HalfAxis::LLeft); + m_axis_state[static_cast(Axis::LeftX)] = ((m_invert_left_stick & 1u) != 0u) ? + MERGE(HalfAxis::LLeft, HalfAxis::LRight) : + MERGE(HalfAxis::LRight, HalfAxis::LLeft); break; case HalfAxis::LDown: case HalfAxis::LUp: - m_axis_state[static_cast(Axis::LeftY)] = MERGE(HalfAxis::LDown, HalfAxis::LUp); + m_axis_state[static_cast(Axis::LeftY)] = ((m_invert_left_stick & 2u) != 0u) ? + MERGE(HalfAxis::LUp, HalfAxis::LDown) : + MERGE(HalfAxis::LDown, HalfAxis::LUp); break; case HalfAxis::RLeft: case HalfAxis::RRight: - m_axis_state[static_cast(Axis::RightX)] = MERGE(HalfAxis::RRight, HalfAxis::RLeft); + m_axis_state[static_cast(Axis::RightX)] = ((m_invert_right_stick & 1u) != 0u) ? + MERGE(HalfAxis::RLeft, HalfAxis::RRight) : + MERGE(HalfAxis::RRight, HalfAxis::RLeft); break; case HalfAxis::RDown: case HalfAxis::RUp: - m_axis_state[static_cast(Axis::RightY)] = MERGE(HalfAxis::RDown, HalfAxis::RUp); + m_axis_state[static_cast(Axis::RightY)] = ((m_invert_right_stick & 2u) != 0u) ? + MERGE(HalfAxis::RUp, HalfAxis::RDown) : + MERGE(HalfAxis::RDown, HalfAxis::RUp); break; default: @@ -197,7 +205,7 @@ void AnalogController::SetBindState(u32 index, float value) const u16 bit = u16(1) << static_cast(index); - if (value >= 0.5f) + if (value >= m_button_deadzone) { if (m_button_state & bit) System::SetRunaheadReplayFlag(); @@ -779,6 +787,11 @@ static const Controller::ControllerBindingInfo s_binding_info[] = { #undef BUTTON }; +static const char* s_invert_settings[] = {TRANSLATABLE("AnalogController", "Not Inverted"), + TRANSLATABLE("AnalogController", "Invert Left/Right"), + TRANSLATABLE("AnalogController", "Invert Up/Down"), + TRANSLATABLE("AnalogController", "Invert Left/Right + Up/Down"), nullptr}; + static const SettingInfo s_settings[] = { {SettingInfo::Type::Boolean, "ForceAnalogOnReset", TRANSLATABLE("AnalogController", "Force Analog Mode on Reset"), TRANSLATABLE("AnalogController", "Forces the controller to analog mode when the console is reset/powered on. May " @@ -792,17 +805,26 @@ static const SettingInfo s_settings[] = { {SettingInfo::Type::Float, "AnalogDeadzone", TRANSLATABLE("AnalogController", "Analog Deadzone"), TRANSLATABLE("AnalogController", "Sets the analog stick deadzone, i.e. the fraction of the stick movement which will be ignored."), - "0.00f", "0.00f", "1.00f", "0.01f", "%.0f%%", 100.0f}, + "0.00f", "0.00f", "1.00f", "0.01f", "%.0f%%", nullptr, 100.0f}, {SettingInfo::Type::Float, "AnalogSensitivity", TRANSLATABLE("AnalogController", "Analog Sensitivity"), TRANSLATABLE( "AnalogController", "Sets the analog stick axis scaling factor. A value between 130% and 140% is recommended when using recent " "controllers, e.g. DualShock 4, Xbox One Controller."), - "1.33f", "0.01f", "2.00f", "0.01f", "%.0f%%", 100.0f}, + "1.33f", "0.01f", "2.00f", "0.01f", "%.0f%%", nullptr, 100.0f}, + {SettingInfo::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.25", + "0.00", "1.00", "0.01", "%.0f%%", nullptr, 100.0f}, {SettingInfo::Type::Integer, "VibrationBias", TRANSLATABLE("AnalogController", "Vibration Bias"), TRANSLATABLE("AnalogController", "Sets the rumble bias value. If rumble in some games is too weak or not " "functioning, try increasing this value."), - "8", "0", "255", "1", "%d", 1.0f}}; + "8", "0", "255", "1", "%d", nullptr, 1.0f}, + {SettingInfo::Type::IntegerList, "InvertLeftStick", "Invert Left Stick", + "Inverts the direction of the left analog stick.", "0", "0", "3", nullptr, nullptr, s_invert_settings, 0.0f}, + {SettingInfo::Type::IntegerList, "InvertRightStick", "Invert Right Stick", + "Inverts the direction of the right analog stick.", "0", "0", "3", nullptr, nullptr, s_invert_settings, 0.0f}, + +}; const Controller::ControllerInfo AnalogController::INFO = {ControllerType::AnalogController, "AnalogController", @@ -821,5 +843,8 @@ void AnalogController::LoadSettings(SettingsInterface& si, const char* section) m_analog_deadzone = std::clamp(si.GetFloatValue(section, "AnalogDeadzone", DEFAULT_STICK_DEADZONE), 0.0f, 1.0f); m_analog_sensitivity = std::clamp(si.GetFloatValue(section, "AnalogSensitivity", DEFAULT_STICK_SENSITIVITY), 0.01f, 3.0f); + m_button_deadzone = std::clamp(si.GetFloatValue(section, "ButtonDeadzone", DEFAULT_BUTTON_DEADZONE), 0.0f, 1.0f); m_rumble_bias = static_cast(std::min(si.GetIntValue(section, "VibrationBias", 8), 255)); + m_invert_left_stick = static_cast(si.GetIntValue(section, "InvertLeftStick", 0)); + m_invert_right_stick = static_cast(si.GetIntValue(section, "InvertRightStick", 0)); } diff --git a/src/core/analog_controller.h b/src/core/analog_controller.h index 6ab888f89..01d4d716e 100644 --- a/src/core/analog_controller.h +++ b/src/core/analog_controller.h @@ -121,7 +121,10 @@ private: bool m_analog_dpad_in_digital_mode = false; float m_analog_deadzone = 0.0f; float m_analog_sensitivity = 1.33f; + float m_button_deadzone = 0.0f; u8 m_rumble_bias = 8; + u8 m_invert_left_stick = 0; + u8 m_invert_right_stick = 0; bool m_analog_mode = false; bool m_analog_locked = false; diff --git a/src/core/analog_joystick.cpp b/src/core/analog_joystick.cpp index 873182a49..353a40eda 100644 --- a/src/core/analog_joystick.cpp +++ b/src/core/analog_joystick.cpp @@ -328,13 +328,13 @@ static const SettingInfo s_settings[] = { {SettingInfo::Type::Float, "AnalogDeadzone", TRANSLATABLE("AnalogJoystick", "Analog Deadzone"), TRANSLATABLE("AnalogJoystick", "Sets the analog stick deadzone, i.e. the fraction of the stick movement which will be ignored."), - "1.00f", "0.00f", "1.00f", "0.01f", "%.0f%%", 100.0f}, + "1.00f", "0.00f", "1.00f", "0.01f", "%.0f%%", nullptr, 100.0f}, {SettingInfo::Type::Float, "AnalogSensitivity", TRANSLATABLE("AnalogJoystick", "Analog Sensitivity"), TRANSLATABLE( "AnalogJoystick", "Sets the analog stick axis scaling factor. A value between 130% and 140% is recommended when using recent " "controllers, e.g. DualShock 4, Xbox One Controller."), - "1.33f", "0.01f", "2.00f", "0.01f", "%.0f%%", 100.0f}}; + "1.33f", "0.01f", "2.00f", "0.01f", "%.0f%%", nullptr, 100.0f}}; const Controller::ControllerInfo AnalogJoystick::INFO = {ControllerType::AnalogJoystick, "AnalogJoystick", diff --git a/src/core/controller.h b/src/core/controller.h index 2fd12a3d7..4cc12866d 100644 --- a/src/core/controller.h +++ b/src/core/controller.h @@ -60,6 +60,7 @@ public: /// Default stick deadzone/sensitivity. static constexpr float DEFAULT_STICK_DEADZONE = 0.0f; static constexpr float DEFAULT_STICK_SENSITIVITY = 1.33f; + static constexpr float DEFAULT_BUTTON_DEADZONE = 0.25f; Controller(u32 index); virtual ~Controller(); diff --git a/src/core/guncon.cpp b/src/core/guncon.cpp index 17dce7bd1..497e8db5b 100644 --- a/src/core/guncon.cpp +++ b/src/core/guncon.cpp @@ -220,10 +220,11 @@ static const SettingInfo s_settings[] = { {SettingInfo::Type::Path, "CrosshairImagePath", TRANSLATABLE("GunCon", "Crosshair Image Path"), TRANSLATABLE("GunCon", "Path to an image to use as a crosshair/cursor.")}, {SettingInfo::Type::Float, "CrosshairScale", TRANSLATABLE("GunCon", "Crosshair Image Scale"), - TRANSLATABLE("GunCon", "Scale of crosshair image on screen."), "1.0", "0.0001", "100.0", "0.10", "%.0f%%", 100.0f}, + TRANSLATABLE("GunCon", "Scale of crosshair image on screen."), "1.0", "0.0001", "100.0", "0.10", "%.0f%%", nullptr, + 100.0f}, {SettingInfo::Type::Float, "XScale", TRANSLATABLE("GunCon", "X Scale"), - TRANSLATABLE("GunCon", "Scales X coordinates relative to the center of the screen."), "1.0", "0.01", "2.0", - "0.01", "%.0f%%", 100.0f}}; + TRANSLATABLE("GunCon", "Scales X coordinates relative to the center of the screen."), "1.0", "0.01", "2.0", "0.01", + "%.0f%%", nullptr, 100.0f}}; const Controller::ControllerInfo GunCon::INFO = {ControllerType::GunCon, "GunCon", diff --git a/src/core/negcon.cpp b/src/core/negcon.cpp index 4e78319c6..0d8f0e9f9 100644 --- a/src/core/negcon.cpp +++ b/src/core/negcon.cpp @@ -249,9 +249,10 @@ static const Controller::ControllerBindingInfo s_binding_info[] = { #undef BUTTON }; -static const SettingInfo s_settings[] = { - {SettingInfo::Type::Float, "SteeringDeadzone", TRANSLATABLE("NeGcon", "Steering Axis Deadzone"), - TRANSLATABLE("NeGcon", "Sets deadzone size for steering axis."), "0.00f", "0.00f", "0.99f", "0.01f", "%.0f%%", 100.0f}}; +static const SettingInfo s_settings[] = {{SettingInfo::Type::Float, "SteeringDeadzone", + TRANSLATABLE("NeGcon", "Steering Axis Deadzone"), + TRANSLATABLE("NeGcon", "Sets deadzone size for steering axis."), "0.00f", + "0.00f", "0.99f", "0.01f", "%.0f%%", nullptr, 100.0f}}; const Controller::ControllerInfo NeGcon::INFO = {ControllerType::NeGcon, "NeGcon", diff --git a/src/core/settings.h b/src/core/settings.h index 7f4a42166..8ae4aaf28 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -17,6 +17,7 @@ struct SettingInfo { Boolean, Integer, + IntegerList, Float, String, Path, @@ -31,6 +32,7 @@ struct SettingInfo const char* max_value; const char* step_value; const char* format; + const char** options; float multiplier; const char* StringDefaultValue() const; diff --git a/src/duckstation-qt/controllerbindingwidgets.cpp b/src/duckstation-qt/controllerbindingwidgets.cpp index 6bb7a8459..ede69663c 100644 --- a/src/duckstation-qt/controllerbindingwidgets.cpp +++ b/src/duckstation-qt/controllerbindingwidgets.cpp @@ -554,6 +554,20 @@ void ControllerCustomSettingsWidget::createSettingWidgets(ControllerBindingWidge } break; + case SettingInfo::Type::IntegerList: + { + QComboBox* cb = new QComboBox(this); + cb->setObjectName(QString::fromUtf8(si.name)); + for (u32 j = 0; si.options[j] != nullptr; j++) + cb->addItem(qApp->translate(cinfo->name, si.options[j])); + SettingWidgetBinder::BindWidgetToIntSetting(sif, cb, section, std::move(key_name), si.IntegerDefaultValue(), + si.IntegerMinValue()); + layout->addWidget(new QLabel(qApp->translate(cinfo->name, si.display_name), this), current_row, 0); + layout->addWidget(cb, current_row, 1, 1, 3); + current_row++; + } + break; + case SettingInfo::Type::Float: { QDoubleSpinBox* sb = new QDoubleSpinBox(this); @@ -639,6 +653,14 @@ void ControllerCustomSettingsWidget::restoreDefaults() } break; + case SettingInfo::Type::IntegerList: + { + QComboBox* widget = findChild(QString::fromStdString(si.name)); + if (widget) + widget->setCurrentIndex(si.IntegerDefaultValue() - si.IntegerMinValue()); + } + break; + case SettingInfo::Type::Float: { QDoubleSpinBox* widget = findChild(QString::fromStdString(si.name)); diff --git a/src/duckstation-qt/controllerglobalsettingswidget.cpp b/src/duckstation-qt/controllerglobalsettingswidget.cpp index 5f5baaa3e..17c204159 100644 --- a/src/duckstation-qt/controllerglobalsettingswidget.cpp +++ b/src/duckstation-qt/controllerglobalsettingswidget.cpp @@ -27,10 +27,6 @@ ControllerGlobalSettingsWidget::ControllerGlobalSettingsWidget(QWidget* parent, SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.multitapMode, "ControllerPorts", "MultitapMode", &Settings::ParseMultitapModeName, &Settings::GetMultitapModeName, Settings::DEFAULT_MULTITAP_MODE); - ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.pointerXInvert, "ControllerPorts", - "PointerXInvert", false); - ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.pointerYInvert, "ControllerPorts", - "PointerYInvert", false); ControllerSettingWidgetBinder::BindWidgetToInputProfileFloat(sif, m_ui.pointerXScale, "ControllerPorts", "PointerXScale", 8.0f); ControllerSettingWidgetBinder::BindWidgetToInputProfileFloat(sif, m_ui.pointerYScale, "ControllerPorts", diff --git a/src/duckstation-qt/controllerglobalsettingswidget.ui b/src/duckstation-qt/controllerglobalsettingswidget.ui index da82550d9..fd3ed2b38 100644 --- a/src/duckstation-qt/controllerglobalsettingswidget.ui +++ b/src/duckstation-qt/controllerglobalsettingswidget.ui @@ -7,7 +7,7 @@ 0 0 902 - 677 + 673 @@ -26,6 +26,57 @@ 0 + + + + DInput Source + + + + + + The DInput source provides support for legacy controllers which do not support XInput. Accessing these controllers via SDL instead is recommended. + + + true + + + + + + + Enable DInput Input Source + + + + + + + + + + Detected Devices + + + + + + + 200 + 0 + + + + + 200 + 16777215 + + + + + + + @@ -76,32 +127,6 @@ - - - - DInput Source - - - - - - The DInput source provides support for legacy controllers which do not support XInput. Accessing these controllers via SDL instead is recommended. - - - true - - - - - - - Enable DInput Input Source - - - - - - @@ -135,180 +160,6 @@ - - - - Detected Devices - - - - - - - 200 - 0 - - - - - 200 - 16777215 - - - - - - - - - - - Mouse/Pointer Source - - - - - - - 0 - 0 - - - - - 20 - 0 - - - - 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. - - - true - - - - - - - Vertical Sensitivity: - - - - - - - Horizontal Sensitivity: - - - - - - - Enable Mouse Mapping - - - - - - - Use Raw Input - - - - - - @@ -361,6 +212,153 @@ + + + + Mouse/Pointer Source + + + + + + Using raw input improves precision when you bind controller sticks to the mouse pointer. Also enables multiple mice to be used. + + + true + + + + + + + Horizontal Sensitivity: + + + + + + + + + + 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 + + + + + + + Use Raw Input + + + + + + + + diff --git a/src/frontend-common/fullscreen_ui.cpp b/src/frontend-common/fullscreen_ui.cpp index b09c463c8..28b8cbd72 100644 --- a/src/frontend-common/fullscreen_ui.cpp +++ b/src/frontend-common/fullscreen_ui.cpp @@ -3018,6 +3018,10 @@ void FullscreenUI::DrawControllerSettingsPage() DrawIntRangeSetting(bsi, title, si.description, section.c_str(), si.name, si.IntegerDefaultValue(), si.IntegerMinValue(), si.IntegerMaxValue(), si.format, true); break; + case SettingInfo::Type::IntegerList: + DrawIntListSetting(bsi, title, si.description, section.c_str(), si.name, si.IntegerDefaultValue(), + si.options, 0, si.IntegerMinValue(), true); + break; case SettingInfo::Type::Float: DrawFloatRangeSetting(bsi, title, si.description, section.c_str(), si.name, si.FloatDefaultValue(), si.FloatMinValue(), si.FloatMaxValue(), si.format, si.multiplier, true); diff --git a/src/frontend-common/input_manager.cpp b/src/frontend-common/input_manager.cpp index 7edbe4ba4..6b6a50d4d 100644 --- a/src/frontend-common/input_manager.cpp +++ b/src/frontend-common/input_manager.cpp @@ -962,12 +962,28 @@ void InputManager::CopyConfiguration(SettingsInterface* dest_si, const SettingsI if (copy_pad_config) { - dest_si->CopyFloatValue(src_si, section.c_str(), "AxisScale"); - - if (info->vibration_caps != Controller::VibrationCapabilities::NoVibration) + for (u32 i = 0; i < info->num_settings; i++) { - dest_si->CopyFloatValue(src_si, section.c_str(), "LargeMotorScale"); - dest_si->CopyFloatValue(src_si, section.c_str(), "SmallMotorScale"); + const SettingInfo& csi = info->settings[i]; + switch (csi.type) + { + case SettingInfo::Type::Boolean: + dest_si->CopyBoolValue(src_si, section.c_str(), csi.name); + break; + case SettingInfo::Type::Integer: + case SettingInfo::Type::IntegerList: + dest_si->CopyIntValue(src_si, section.c_str(), csi.name); + break; + case SettingInfo::Type::Float: + dest_si->CopyFloatValue(src_si, section.c_str(), csi.name); + break; + case SettingInfo::Type::String: + case SettingInfo::Type::Path: + dest_si->CopyStringValue(src_si, section.c_str(), csi.name); + break; + default: + break; + } } } } @@ -1350,12 +1366,10 @@ 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 / std::max(si.GetFloatValue("Pad", fmt::format("Pointer{}Scale", s_pointer_axis_names[axis]).c_str(), - default_scale), - 1.0f); + 1.0f / std::max(si.GetFloatValue("Pad", fmt::format("Pointer{}Scale", s_pointer_axis_names[axis]).c_str(), + default_scale), + 1.0f); } }