From f2ef0966eb8b97e8997ad5c39d446dfbe732ef1f Mon Sep 17 00:00:00 2001 From: Megamouse Date: Fri, 9 Aug 2024 23:44:45 +0200 Subject: [PATCH] input: add analog limiter --- rpcs3/Emu/Io/Null/NullPadHandler.h | 1 + rpcs3/Emu/Io/PadHandler.cpp | 36 +++-- rpcs3/Emu/Io/PadHandler.h | 6 +- rpcs3/Emu/Io/pad_config.h | 2 + rpcs3/Emu/Io/pad_types.cpp | 40 ++++++ rpcs3/Emu/Io/pad_types.h | 9 +- rpcs3/Emu/localized_string_id.h | 2 + rpcs3/Emu/system_config.h | 1 + rpcs3/Input/ds3_pad_handler.cpp | 1 + rpcs3/Input/ds4_pad_handler.cpp | 1 + rpcs3/Input/dualsense_pad_handler.cpp | 1 + rpcs3/Input/evdev_joystick_handler.cpp | 17 ++- rpcs3/Input/keyboard_pad_handler.cpp | 173 ++++++++++++++++--------- rpcs3/Input/keyboard_pad_handler.h | 1 + rpcs3/Input/mm_joystick_handler.cpp | 11 +- rpcs3/Input/sdl_pad_handler.cpp | 1 + rpcs3/Input/xinput_pad_handler.cpp | 1 + rpcs3/rpcs3qt/emu_settings_type.h | 2 + rpcs3/rpcs3qt/localized_emu.h | 2 + rpcs3/rpcs3qt/pad_settings_dialog.cpp | 18 ++- rpcs3/rpcs3qt/pad_settings_dialog.h | 2 + rpcs3/rpcs3qt/pad_settings_dialog.ui | 63 +++++++++ rpcs3/rpcs3qt/settings_dialog.cpp | 3 + rpcs3/rpcs3qt/settings_dialog.ui | 7 + rpcs3/rpcs3qt/tooltips.h | 2 + 25 files changed, 327 insertions(+), 76 deletions(-) diff --git a/rpcs3/Emu/Io/Null/NullPadHandler.h b/rpcs3/Emu/Io/Null/NullPadHandler.h index bc5dd1afe4..63b14a5c34 100644 --- a/rpcs3/Emu/Io/Null/NullPadHandler.h +++ b/rpcs3/Emu/Io/Null/NullPadHandler.h @@ -47,6 +47,7 @@ public: cfg->l3.def = ""; cfg->pressure_intensity_button.def = ""; + cfg->analog_limiter_button.def = ""; // Apply defaults cfg->from_default(); diff --git a/rpcs3/Emu/Io/PadHandler.cpp b/rpcs3/Emu/Io/PadHandler.cpp index dc683cf584..6739bac2b6 100644 --- a/rpcs3/Emu/Io/PadHandler.cpp +++ b/rpcs3/Emu/Io/PadHandler.cpp @@ -371,7 +371,7 @@ void PadHandlerBase::convert_stick_values(u16& x_out, u16& y_out, s32 x_in, s32 } // Update the pad button values based on their type and thresholds. With this you can use axis or triggers as buttons or vice versa -void PadHandlerBase::TranslateButtonPress(const std::shared_ptr& device, u64 keyCode, bool& pressed, u16& val, bool ignore_stick_threshold, bool ignore_trigger_threshold) +void PadHandlerBase::TranslateButtonPress(const std::shared_ptr& device, u64 keyCode, bool& pressed, u16& val, bool use_stick_multipliers, bool ignore_stick_threshold, bool ignore_trigger_threshold) { if (!device || !device->config) { @@ -391,12 +391,12 @@ void PadHandlerBase::TranslateButtonPress(const std::shared_ptr& devi else if (get_is_left_stick(device, keyCode)) { pressed = val > (ignore_stick_threshold ? 0 : device->config->lstickdeadzone); - val = pressed ? NormalizeStickInput(val, device->config->lstickdeadzone, device->config->lstickmultiplier, ignore_stick_threshold) : 0; + val = pressed ? NormalizeStickInput(val, device->config->lstickdeadzone, use_stick_multipliers ? device->config->lstickmultiplier : 100, ignore_stick_threshold) : 0; } else if (get_is_right_stick(device, keyCode)) { pressed = val > (ignore_stick_threshold ? 0 : device->config->rstickdeadzone); - val = pressed ? NormalizeStickInput(val, device->config->rstickdeadzone, device->config->rstickmultiplier, ignore_stick_threshold) : 0; + val = pressed ? NormalizeStickInput(val, device->config->rstickdeadzone, use_stick_multipliers ? device->config->rstickmultiplier : 100, ignore_stick_threshold) : 0; } else // normal button (should in theory also support sensitive buttons) { @@ -461,8 +461,17 @@ bool PadHandlerBase::bindPadToDevice(std::shared_ptr pad) config->pressure_intensity ); - pad->m_buttons.emplace_back(special_button_offset, mapping[button::pressure_intensity_button], special_button_value::pressure_intensity); - pad->m_pressure_intensity_button_index = static_cast(pad->m_buttons.size()) - 1; + if (b_has_pressure_intensity_button) + { + pad->m_buttons.emplace_back(special_button_offset, mapping[button::pressure_intensity_button], special_button_value::pressure_intensity); + pad->m_pressure_intensity_button_index = static_cast(pad->m_buttons.size()) - 1; + } + + if (b_has_analog_limiter_button) + { + pad->m_buttons.emplace_back(special_button_offset, mapping[button::analog_limiter_button], special_button_value::analog_limiter); + pad->m_analog_limiter_button_index = static_cast(pad->m_buttons.size()) - 1; + } pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, mapping[button::up], CELL_PAD_CTRL_UP); pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, mapping[button::down], CELL_PAD_CTRL_DOWN); @@ -560,7 +569,15 @@ std::array, PadHandlerBase::button::button_count> PadHandlerBase:: mapping[button::skateboard_tilt_left] = FindKeyCodes(button_list, cfg->tilt_left); mapping[button::skateboard_tilt_right] = FindKeyCodes(button_list, cfg->tilt_right); - mapping[button::pressure_intensity_button] = FindKeyCodes(button_list, cfg->pressure_intensity_button); + if (b_has_pressure_intensity_button) + { + mapping[button::pressure_intensity_button] = FindKeyCodes(button_list, cfg->pressure_intensity_button); + } + + if (b_has_analog_limiter_button) + { + mapping[button::analog_limiter_button] = FindKeyCodes(button_list, cfg->analog_limiter_button); + } return mapping; } @@ -581,6 +598,7 @@ void PadHandlerBase::get_mapping(const pad_ensemble& binding) // Find out if special buttons are pressed (introduced by RPCS3). // These buttons will have a delay of one cycle, but whatever. + const bool analog_limiter_enabled = pad->get_analog_limiter_button_active(cfg->analog_limiter_toggle_mode.get(), pad->m_player_id); const bool adjust_pressure = pad->get_pressure_intensity_button_active(cfg->pressure_intensity_toggle_mode.get(), pad->m_player_id); const u32 pressure_intensity_deadzone = cfg->pressure_intensity_deadzone.get(); @@ -595,7 +613,7 @@ void PadHandlerBase::get_mapping(const pad_ensemble& binding) bool press{}; u16 val = button_values[code]; - TranslateButtonPress(device, code, press, val); + TranslateButtonPress(device, code, press, val, analog_limiter_enabled); if (press) { @@ -637,7 +655,7 @@ void PadHandlerBase::get_mapping(const pad_ensemble& binding) { u16 val = button_values[key_min]; - TranslateButtonPress(device, key_min, pressed, val, true); + TranslateButtonPress(device, key_min, pressed, val, analog_limiter_enabled, true); if (pressed) { @@ -650,7 +668,7 @@ void PadHandlerBase::get_mapping(const pad_ensemble& binding) { u16 val = button_values[key_max]; - TranslateButtonPress(device, key_max, pressed, val, true); + TranslateButtonPress(device, key_max, pressed, val, analog_limiter_enabled, true); if (pressed) { diff --git a/rpcs3/Emu/Io/PadHandler.h b/rpcs3/Emu/Io/PadHandler.h index 798b289d49..44089d601c 100644 --- a/rpcs3/Emu/Io/PadHandler.h +++ b/rpcs3/Emu/Io/PadHandler.h @@ -106,6 +106,7 @@ protected: skateboard_tilt_right, pressure_intensity_button, + analog_limiter_button, button_count }; @@ -131,6 +132,8 @@ protected: bool b_has_motion = false; bool b_has_config = false; bool b_has_pressure_intensity_button = true; + bool b_has_analog_limiter_button = true; + std::array m_pad_configs; std::vector m_bindings; std::unordered_map button_list; @@ -266,6 +269,7 @@ public: bool has_battery() const { return b_has_battery; } bool has_battery_led() const { return b_has_battery_led; } bool has_pressure_intensity_button() const { return b_has_pressure_intensity_button; } + bool has_analog_limiter_button() const { return b_has_analog_limiter_button; } u16 NormalizeStickInput(u16 raw_value, s32 threshold, s32 multiplier, bool ignore_threshold = false) const; void convert_stick_values(u16& x_out, u16& y_out, s32 x_in, s32 y_in, u32 deadzone, u32 anti_deadzone, u32 padsquircling) const; @@ -303,7 +307,7 @@ private: protected: virtual std::array, PadHandlerBase::button::button_count> get_mapped_key_codes(const std::shared_ptr& device, const cfg_pad* cfg); virtual void get_mapping(const pad_ensemble& binding); - void TranslateButtonPress(const std::shared_ptr& device, u64 keyCode, bool& pressed, u16& val, bool ignore_stick_threshold = false, bool ignore_trigger_threshold = false); + void TranslateButtonPress(const std::shared_ptr& device, u64 keyCode, bool& pressed, u16& val, bool use_stick_multipliers, bool ignore_stick_threshold = false, bool ignore_trigger_threshold = false); void init_configs(); cfg_pad* get_config(const std::string& pad_id); }; diff --git a/rpcs3/Emu/Io/pad_config.h b/rpcs3/Emu/Io/pad_config.h index 5e2b94bfd3..f27228ccf0 100644 --- a/rpcs3/Emu/Io/pad_config.h +++ b/rpcs3/Emu/Io/pad_config.h @@ -71,6 +71,8 @@ struct cfg_pad final : cfg::node cfg::_bool pressure_intensity_toggle_mode{ this, "Pressure Intensity Toggle Mode", false }; cfg::uint<0, 255> pressure_intensity_deadzone{ this, "Pressure Intensity Deadzone", 0 }; + cfg::string analog_limiter_button{ this, "Analog Limiter Button", "" }; + cfg::_bool analog_limiter_toggle_mode{ this, "Analog Limiter Toggle Mode", false }; cfg::uint<0, 200> lstickmultiplier{ this, "Left Stick Multiplier", 100 }; cfg::uint<0, 200> rstickmultiplier{ this, "Right Stick Multiplier", 100 }; cfg::uint<0, 1000000> lstickdeadzone{ this, "Left Stick Deadzone", 0 }; diff --git a/rpcs3/Emu/Io/pad_types.cpp b/rpcs3/Emu/Io/pad_types.cpp index 8d265dac23..9100e28596 100644 --- a/rpcs3/Emu/Io/pad_types.cpp +++ b/rpcs3/Emu/Io/pad_types.cpp @@ -198,3 +198,43 @@ bool Pad::get_pressure_intensity_button_active(bool is_toggle_mode, u32 player_i return pressure_intensity_button.m_pressed; } + +bool Pad::get_analog_limiter_button_active(bool is_toggle_mode, u32 player_id) +{ + if (m_analog_limiter_button_index < 0) + { + return false; + } + + const Button& analog_limiter_button = m_buttons[m_analog_limiter_button_index]; + + if (is_toggle_mode) + { + const bool pressed = analog_limiter_button.m_pressed; + + if (std::exchange(m_analog_limiter_button_pressed, pressed) != pressed) + { + if (pressed) + { + m_analog_limiter_toggled = !m_analog_limiter_toggled; + + if (g_cfg.misc.show_analog_limiter_toggle_hint) + { + const std::string player_id_string = std::to_string(player_id + 1); + if (m_analog_limiter_toggled) + { + rsx::overlays::queue_message(get_localized_string(localized_string_id::RSX_OVERLAYS_ANALOG_LIMITER_TOGGLED_ON, player_id_string.c_str()), 3'000'000); + } + else + { + rsx::overlays::queue_message(get_localized_string(localized_string_id::RSX_OVERLAYS_ANALOG_LIMITER_TOGGLED_OFF, player_id_string.c_str()), 3'000'000); + } + } + } + } + + return m_analog_limiter_toggled; + } + + return analog_limiter_button.m_pressed; +} diff --git a/rpcs3/Emu/Io/pad_types.h b/rpcs3/Emu/Io/pad_types.h index 28d1b1eba6..75b9122137 100644 --- a/rpcs3/Emu/Io/pad_types.h +++ b/rpcs3/Emu/Io/pad_types.h @@ -363,7 +363,8 @@ constexpr u32 special_button_offset = 666; // Must not conflict with other CELL enum special_button_value { - pressure_intensity + pressure_intensity, + analog_limiter }; struct Button @@ -474,6 +475,12 @@ struct Pad bool m_adjust_pressure_last{}; // only used in keyboard_pad_handler bool get_pressure_intensity_button_active(bool is_toggle_mode, u32 player_id); + s32 m_analog_limiter_button_index{-1}; // Special button index. -1 if not set. + bool m_analog_limiter_button_pressed{}; // Last sensitivity button press state, used for toggle. + bool m_analog_limiter_toggled{}; // Whether the sensitivity is toggled on or off. + bool m_analog_limiter_enabled_last{}; // only used in keyboard_pad_handler + bool get_analog_limiter_button_active(bool is_toggle_mode, u32 player_id); + // Cable State: 0 - 1 plugged in ? u8 m_cable_state{0}; diff --git a/rpcs3/Emu/localized_string_id.h b/rpcs3/Emu/localized_string_id.h index c41d7eb002..2012730f1f 100644 --- a/rpcs3/Emu/localized_string_id.h +++ b/rpcs3/Emu/localized_string_id.h @@ -33,6 +33,8 @@ enum class localized_string_id RSX_OVERLAYS_LIST_DENY, RSX_OVERLAYS_PRESSURE_INTENSITY_TOGGLED_OFF, RSX_OVERLAYS_PRESSURE_INTENSITY_TOGGLED_ON, + RSX_OVERLAYS_ANALOG_LIMITER_TOGGLED_OFF, + RSX_OVERLAYS_ANALOG_LIMITER_TOGGLED_ON, CELL_GAME_ERROR_BROKEN_GAMEDATA, CELL_GAME_ERROR_BROKEN_HDDGAME, diff --git a/rpcs3/Emu/system_config.h b/rpcs3/Emu/system_config.h index 9028772dd8..b7d15da534 100644 --- a/rpcs3/Emu/system_config.h +++ b/rpcs3/Emu/system_config.h @@ -340,6 +340,7 @@ struct cfg_root : cfg::node cfg::_bool show_shader_compilation_hint{ this, "Show shader compilation hint", true, true }; cfg::_bool show_ppu_compilation_hint{ this, "Show PPU compilation hint", true, true }; cfg::_bool show_pressure_intensity_toggle_hint{ this, "Show pressure intensity toggle hint", true, true }; + cfg::_bool show_analog_limiter_toggle_hint{ this, "Show analog limiter toggle hint", true, true }; cfg::_bool use_native_interface{ this, "Use native user interface", true }; cfg::string gdb_server{ this, "GDB Server", "127.0.0.1:2345" }; cfg::_bool silence_all_logs{ this, "Silence All Logs", false, true }; diff --git a/rpcs3/Input/ds3_pad_handler.cpp b/rpcs3/Input/ds3_pad_handler.cpp index 5279b92d53..7b689e2c23 100644 --- a/rpcs3/Input/ds3_pad_handler.cpp +++ b/rpcs3/Input/ds3_pad_handler.cpp @@ -191,6 +191,7 @@ void ds3_pad_handler::init_config(cfg_pad* cfg) cfg->l3.def = ::at32(button_list, DS3KeyCodes::L3); cfg->pressure_intensity_button.def = ::at32(button_list, DS3KeyCodes::None); + cfg->analog_limiter_button.def = ::at32(button_list, DS3KeyCodes::None); // Set default misc variables cfg->lstick_anti_deadzone.def = 0; diff --git a/rpcs3/Input/ds4_pad_handler.cpp b/rpcs3/Input/ds4_pad_handler.cpp index 2b80d17c0e..50ede0a091 100644 --- a/rpcs3/Input/ds4_pad_handler.cpp +++ b/rpcs3/Input/ds4_pad_handler.cpp @@ -158,6 +158,7 @@ void ds4_pad_handler::init_config(cfg_pad* cfg) cfg->l3.def = ::at32(button_list, DS4KeyCodes::L3); cfg->pressure_intensity_button.def = ::at32(button_list, DS4KeyCodes::None); + cfg->analog_limiter_button.def = ::at32(button_list, DS4KeyCodes::None); // Set default misc variables cfg->lstick_anti_deadzone.def = static_cast(0.13 * thumb_max); // 13% diff --git a/rpcs3/Input/dualsense_pad_handler.cpp b/rpcs3/Input/dualsense_pad_handler.cpp index fcda473cbd..ddcdbf1544 100644 --- a/rpcs3/Input/dualsense_pad_handler.cpp +++ b/rpcs3/Input/dualsense_pad_handler.cpp @@ -251,6 +251,7 @@ void dualsense_pad_handler::init_config(cfg_pad* cfg) cfg->l3.def = ::at32(button_list, DualSenseKeyCodes::L3); cfg->pressure_intensity_button.def = ::at32(button_list, DualSenseKeyCodes::None); + cfg->analog_limiter_button.def = ::at32(button_list, DualSenseKeyCodes::None); // Set default misc variables cfg->lstick_anti_deadzone.def = static_cast(0.13 * thumb_max); // 13% diff --git a/rpcs3/Input/evdev_joystick_handler.cpp b/rpcs3/Input/evdev_joystick_handler.cpp index d531bdd3a0..a1c651da70 100644 --- a/rpcs3/Input/evdev_joystick_handler.cpp +++ b/rpcs3/Input/evdev_joystick_handler.cpp @@ -83,6 +83,7 @@ void evdev_joystick_handler::init_config(cfg_pad* cfg) cfg->motion_sensor_g.axis.def = ::at32(motion_axis_list, ABS_RY); // DS3 uses the yaw axis for gyros cfg->pressure_intensity_button.def = ::at32(button_list, NO_BUTTON); + cfg->analog_limiter_button.def = ::at32(button_list, NO_BUTTON); // Set default misc variables cfg->lstick_anti_deadzone.def = static_cast(0.13 * thumb_max); // 13% @@ -1087,13 +1088,14 @@ void evdev_joystick_handler::apply_input_events(const std::shared_ptr& pad) // Find out if special buttons are pressed (introduced by RPCS3). // These buttons will have a delay of one cycle, but whatever. + const bool analog_limiter_enabled = pad->get_analog_limiter_button_active(cfg->analog_limiter_toggle_mode.get(), pad->m_player_id); const bool adjust_pressure = pad->get_pressure_intensity_button_active(cfg->pressure_intensity_toggle_mode.get(), pad->m_player_id); const u32 pressure_intensity_deadzone = cfg->pressure_intensity_deadzone.get(); const auto update_values = [&](bool& pressed, u16& final_value, bool is_stick_value, u32 code, u16 val) { bool press{}; - TranslateButtonPress(m_dev, code, press, val, is_stick_value); + TranslateButtonPress(m_dev, code, press, val, analog_limiter_enabled, is_stick_value); if (press) { @@ -1341,8 +1343,17 @@ bool evdev_joystick_handler::bindPadToDevice(std::shared_ptr pad) cfg->pressure_intensity ); - pad->m_buttons.emplace_back(special_button_offset, find_buttons(cfg->pressure_intensity_button), special_button_value::pressure_intensity); - pad->m_pressure_intensity_button_index = static_cast(pad->m_buttons.size()) - 1; + if (b_has_pressure_intensity_button) + { + pad->m_buttons.emplace_back(special_button_offset, find_buttons(cfg->pressure_intensity_button), special_button_value::pressure_intensity); + pad->m_pressure_intensity_button_index = static_cast(pad->m_buttons.size()) - 1; + } + + if (b_has_analog_limiter_button) + { + pad->m_buttons.emplace_back(special_button_offset, find_buttons(cfg->analog_limiter_button), special_button_value::analog_limiter); + pad->m_analog_limiter_button_index = static_cast(pad->m_buttons.size()) - 1; + } pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2,find_buttons(cfg->triangle), CELL_PAD_CTRL_TRIANGLE); pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2,find_buttons(cfg->circle), CELL_PAD_CTRL_CIRCLE); diff --git a/rpcs3/Input/keyboard_pad_handler.cpp b/rpcs3/Input/keyboard_pad_handler.cpp index 7af3d33d1d..943969bbd9 100644 --- a/rpcs3/Input/keyboard_pad_handler.cpp +++ b/rpcs3/Input/keyboard_pad_handler.cpp @@ -59,6 +59,7 @@ void keyboard_pad_handler::init_config(cfg_pad* cfg) cfg->l3.def = GetKeyName(Qt::Key_F); cfg->pressure_intensity_button.def = GetKeyName(Qt::NoButton); + cfg->analog_limiter_button.def = GetKeyName(Qt::NoButton); cfg->lstick_anti_deadzone.def = 0; cfg->rstick_anti_deadzone.def = 0; @@ -130,11 +131,35 @@ void keyboard_pad_handler::Key(const u32 code, bool pressed, u16 value) pad.m_adjust_pressure_last = adjust_pressure; } + if (pad.m_analog_limiter_button_index >= 0) + { + Button& analog_limiter_button = pad.m_buttons[pad.m_analog_limiter_button_index]; + + if (analog_limiter_button.m_key_codes.contains(code)) + { + const u16 actual_value = register_new_button_value(analog_limiter_button.m_pressed_keys); + + analog_limiter_button.m_pressed = actual_value > 0; + analog_limiter_button.m_value = actual_value; + } + } + + const bool analog_limiter_enabled = pad.get_analog_limiter_button_active(m_analog_limiter_toggle_mode, pad.m_player_id); + const bool analog_limiter_changed = pad.m_analog_limiter_enabled_last != analog_limiter_enabled; + const u32 l_stick_multiplier = analog_limiter_enabled ? m_l_stick_multiplier : 100; + const u32 r_stick_multiplier = analog_limiter_enabled ? m_r_stick_multiplier : 100; + + if (analog_limiter_changed) + { + pad.m_analog_limiter_enabled_last = analog_limiter_enabled; + } + // Handle buttons for (usz i = 0; i < pad.m_buttons.size(); i++) { - // Ignore pressure intensity button - if (static_cast(i) == pad.m_pressure_intensity_button_index) + // Ignore special buttons + if (static_cast(i) == pad.m_pressure_intensity_button_index || + static_cast(i) == pad.m_analog_limiter_button_index) continue; Button& button = pad.m_buttons[i]; @@ -161,31 +186,31 @@ void keyboard_pad_handler::Key(const u32 code, bool pressed, u16 value) } } - if (update_button) - { - if (button.m_actual_value > 0) - { - // Modify pressure if necessary if the button was pressed - if (adjust_pressure) - { - button.m_value = pad.m_pressure_intensity; - } - else if (m_pressure_intensity_deadzone > 0) - { - button.m_value = NormalizeDirectedInput(button.m_actual_value, m_pressure_intensity_deadzone, 255); - } - else - { - button.m_value = button.m_actual_value; - } + if (!update_button) + continue; - button.m_pressed = button.m_value > 0; + if (button.m_actual_value > 0) + { + // Modify pressure if necessary if the button was pressed + if (adjust_pressure) + { + button.m_value = pad.m_pressure_intensity; + } + else if (m_pressure_intensity_deadzone > 0) + { + button.m_value = NormalizeDirectedInput(button.m_actual_value, m_pressure_intensity_deadzone, 255); } else { - button.m_value = 0; - button.m_pressed = false; + button.m_value = button.m_actual_value; } + + button.m_pressed = button.m_value > 0; + } + else + { + button.m_value = 0; + button.m_pressed = false; } } @@ -194,54 +219,72 @@ void keyboard_pad_handler::Key(const u32 code, bool pressed, u16 value) { AnalogStick& stick = pad.m_sticks[i]; + const bool is_left_stick = i < 2; + const bool is_max = stick.m_key_codes_max.contains(code); const bool is_min = stick.m_key_codes_min.contains(code); if (!is_max && !is_min) { - continue; - } - - const bool is_left_stick = i < 2; - - const u16 actual_value = pressed ? MultipliedInput(value, is_left_stick ? m_l_stick_multiplier : m_r_stick_multiplier) : value; - u16 normalized_value = std::ceil(actual_value / 2.0); - - const auto register_new_stick_value = [&](std::map& pressed_keys, bool is_max) - { - // Make sure we keep this stick pressed until all related keys are released. - if (pressed) - { - pressed_keys[code] = normalized_value; - } - else - { - pressed_keys.erase(code); - } - - // Get the min/max value of all pressed keys for this stick - for (const auto& [key, val] : pressed_keys) - { - normalized_value = is_max ? std::max(normalized_value, val) : std::min(normalized_value, val); - } - }; - - if (is_max) - { - register_new_stick_value(stick.m_pressed_keys_max, true); + if (!analog_limiter_changed) + continue; + // Update already pressed sticks + const bool is_min_pressed = !stick.m_pressed_keys_min.empty(); const bool is_max_pressed = !stick.m_pressed_keys_max.empty(); - m_stick_max[i] = is_max_pressed ? std::min(128 + normalized_value, 255) : 128; + const u32 stick_multiplier = is_left_stick ? l_stick_multiplier : r_stick_multiplier; + + const u16 actual_min_value = is_min_pressed ? MultipliedInput(255, stick_multiplier) : 255; + const u16 normalized_min_value = std::ceil(actual_min_value / 2.0); + + const u16 actual_max_value = is_max_pressed ? MultipliedInput(255, stick_multiplier) : 255; + const u16 normalized_max_value = std::ceil(actual_max_value / 2.0); + + m_stick_min[i] = is_min_pressed ? std::min(normalized_min_value, 128) : 0; + m_stick_max[i] = is_max_pressed ? std::min(128 + normalized_max_value, 255) : 128; } - - if (is_min) + else { - register_new_stick_value(stick.m_pressed_keys_min, false); + const u16 actual_value = pressed ? MultipliedInput(value, is_left_stick ? l_stick_multiplier : r_stick_multiplier) : value; + u16 normalized_value = std::ceil(actual_value / 2.0); - const bool is_min_pressed = !stick.m_pressed_keys_min.empty(); + const auto register_new_stick_value = [&](std::map& pressed_keys, bool is_max) + { + // Make sure we keep this stick pressed until all related keys are released. + if (pressed) + { + pressed_keys[code] = normalized_value; + } + else + { + pressed_keys.erase(code); + } - m_stick_min[i] = is_min_pressed ? std::min(normalized_value, 128) : 0; + // Get the min/max value of all pressed keys for this stick + for (const auto& [key, val] : pressed_keys) + { + normalized_value = is_max ? std::max(normalized_value, val) : std::min(normalized_value, val); + } + }; + + if (is_max) + { + register_new_stick_value(stick.m_pressed_keys_max, true); + + const bool is_max_pressed = !stick.m_pressed_keys_max.empty(); + + m_stick_max[i] = is_max_pressed ? std::min(128 + normalized_value, 255) : 128; + } + + if (is_min) + { + register_new_stick_value(stick.m_pressed_keys_min, false); + + const bool is_min_pressed = !stick.m_pressed_keys_min.empty(); + + m_stick_min[i] = is_min_pressed ? std::min(normalized_value, 128) : 0; + } } m_stick_val[i] = m_stick_max[i] - m_stick_min[i]; @@ -920,6 +963,7 @@ bool keyboard_pad_handler::bindPadToDevice(std::shared_ptr pad) m_trigger_lerp_factor = cfg->trigger_lerp_factor / 100.0f; m_l_stick_multiplier = cfg->lstickmultiplier; m_r_stick_multiplier = cfg->rstickmultiplier; + m_analog_limiter_toggle_mode = cfg->analog_limiter_toggle_mode.get(); m_pressure_intensity_toggle_mode = cfg->pressure_intensity_toggle_mode.get(); m_pressure_intensity_deadzone = cfg->pressure_intensity_deadzone.get(); @@ -965,8 +1009,17 @@ bool keyboard_pad_handler::bindPadToDevice(std::shared_ptr pad) cfg->pressure_intensity ); - pad->m_buttons.emplace_back(special_button_offset, find_keys(cfg->pressure_intensity_button), special_button_value::pressure_intensity); - pad->m_pressure_intensity_button_index = static_cast(pad->m_buttons.size()) - 1; + if (b_has_pressure_intensity_button) + { + pad->m_buttons.emplace_back(special_button_offset, find_keys(cfg->pressure_intensity_button), special_button_value::pressure_intensity); + pad->m_pressure_intensity_button_index = static_cast(pad->m_buttons.size()) - 1; + } + + if (b_has_analog_limiter_button) + { + pad->m_buttons.emplace_back(special_button_offset, find_keys(cfg->analog_limiter_button), special_button_value::analog_limiter); + pad->m_analog_limiter_button_index = static_cast(pad->m_buttons.size()) - 1; + } pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, find_keys(cfg->left), CELL_PAD_CTRL_LEFT); pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, find_keys(cfg->down), CELL_PAD_CTRL_DOWN); diff --git a/rpcs3/Input/keyboard_pad_handler.h b/rpcs3/Input/keyboard_pad_handler.h index 47fc8afc2b..98578d6d34 100644 --- a/rpcs3/Input/keyboard_pad_handler.h +++ b/rpcs3/Input/keyboard_pad_handler.h @@ -116,6 +116,7 @@ private: steady_clock::time_point m_button_time; f32 m_analog_lerp_factor = 1.0f; f32 m_trigger_lerp_factor = 1.0f; + bool m_analog_limiter_toggle_mode = false; bool m_pressure_intensity_toggle_mode = false; u32 m_pressure_intensity_deadzone = 0; diff --git a/rpcs3/Input/mm_joystick_handler.cpp b/rpcs3/Input/mm_joystick_handler.cpp index e2f5f9171d..6f03c8a0f8 100644 --- a/rpcs3/Input/mm_joystick_handler.cpp +++ b/rpcs3/Input/mm_joystick_handler.cpp @@ -53,6 +53,7 @@ void mm_joystick_handler::init_config(cfg_pad* cfg) cfg->l3.def = ::at32(button_list, JOY_BUTTON11); cfg->pressure_intensity_button.def = ::at32(button_list, NO_BUTTON); + cfg->analog_limiter_button.def = ::at32(button_list, NO_BUTTON); // Set default misc variables cfg->lstick_anti_deadzone.def = static_cast(0.13 * thumb_max); // 13% @@ -216,7 +217,15 @@ std::array, PadHandlerBase::button::button_count> mm_joystick_hand mapping[button::skateboard_tilt_left] = find_keys(cfg->tilt_left); mapping[button::skateboard_tilt_right] = find_keys(cfg->tilt_right); - mapping[button::pressure_intensity_button] = find_keys(cfg->pressure_intensity_button); + if (b_has_pressure_intensity_button) + { + mapping[button::pressure_intensity_button] = find_keys(cfg->pressure_intensity_button); + } + + if (b_has_analog_limiter_button) + { + mapping[button::analog_limiter_button] = find_keys(cfg->analog_limiter_button); + } return mapping; } diff --git a/rpcs3/Input/sdl_pad_handler.cpp b/rpcs3/Input/sdl_pad_handler.cpp index ca3b72f889..7c8d5deb81 100644 --- a/rpcs3/Input/sdl_pad_handler.cpp +++ b/rpcs3/Input/sdl_pad_handler.cpp @@ -228,6 +228,7 @@ void sdl_pad_handler::init_config(cfg_pad* cfg) cfg->l3.def = ::at32(button_list, SDLKeyCodes::LS); cfg->pressure_intensity_button.def = ::at32(button_list, SDLKeyCodes::None); + cfg->analog_limiter_button.def = ::at32(button_list, SDLKeyCodes::None); // Set default misc variables cfg->lstick_anti_deadzone.def = static_cast(0.13 * thumb_max); // 13% diff --git a/rpcs3/Input/xinput_pad_handler.cpp b/rpcs3/Input/xinput_pad_handler.cpp index 6184877140..5850461116 100644 --- a/rpcs3/Input/xinput_pad_handler.cpp +++ b/rpcs3/Input/xinput_pad_handler.cpp @@ -114,6 +114,7 @@ void xinput_pad_handler::init_config(cfg_pad* cfg) cfg->l3.def = ::at32(button_list, XInputKeyCodes::LS); cfg->pressure_intensity_button.def = ::at32(button_list, XInputKeyCodes::None); + cfg->analog_limiter_button.def = ::at32(button_list, XInputKeyCodes::None); // Set default misc variables cfg->lstick_anti_deadzone.def = static_cast(0.13 * thumb_max); // 13% diff --git a/rpcs3/rpcs3qt/emu_settings_type.h b/rpcs3/rpcs3qt/emu_settings_type.h index 3885dfa9a3..59820995ec 100644 --- a/rpcs3/rpcs3qt/emu_settings_type.h +++ b/rpcs3/rpcs3qt/emu_settings_type.h @@ -174,6 +174,7 @@ enum class emu_settings_type ShowShaderCompilationHint, ShowPPUCompilationHint, ShowPressureIntensityToggleHint, + ShowAnalogLimiterToggleHint, WindowTitleFormat, PauseDuringHomeMenu, @@ -363,6 +364,7 @@ inline static const QMap settings_location = { emu_settings_type::ShowShaderCompilationHint, { "Miscellaneous", "Show shader compilation hint"}}, { emu_settings_type::ShowPPUCompilationHint, { "Miscellaneous", "Show PPU compilation hint"}}, { emu_settings_type::ShowPressureIntensityToggleHint, { "Miscellaneous", "Show pressure intensity toggle hint"}}, + { emu_settings_type::ShowAnalogLimiterToggleHint, { "Miscellaneous", "Show analog limiter toggle hint"}}, { emu_settings_type::SilenceAllLogs, { "Miscellaneous", "Silence All Logs" }}, { emu_settings_type::WindowTitleFormat, { "Miscellaneous", "Window Title Format" }}, { emu_settings_type::PauseDuringHomeMenu, { "Miscellaneous", "Pause Emulation During Home Menu" }}, diff --git a/rpcs3/rpcs3qt/localized_emu.h b/rpcs3/rpcs3qt/localized_emu.h index e1dba15956..e95e2b1703 100644 --- a/rpcs3/rpcs3qt/localized_emu.h +++ b/rpcs3/rpcs3qt/localized_emu.h @@ -67,6 +67,8 @@ private: case localized_string_id::RSX_OVERLAYS_LIST_DENY: return tr("Deny", "Deny Dialog List"); case localized_string_id::RSX_OVERLAYS_PRESSURE_INTENSITY_TOGGLED_OFF: return tr("Pressure intensity mode of player %0 disabled", "Pressure intensity toggled off").arg(std::forward(args)...); case localized_string_id::RSX_OVERLAYS_PRESSURE_INTENSITY_TOGGLED_ON: return tr("Pressure intensity mode of player %0 enabled", "Pressure intensity toggled on").arg(std::forward(args)...); + case localized_string_id::RSX_OVERLAYS_ANALOG_LIMITER_TOGGLED_OFF: return tr("Analog limiter of player %0 disabled", "Analog limiter toggled off").arg(std::forward(args)...); + case localized_string_id::RSX_OVERLAYS_ANALOG_LIMITER_TOGGLED_ON: return tr("Analog limiter of player %0 enabled", "Analog limiter toggled on").arg(std::forward(args)...); case localized_string_id::CELL_GAME_ERROR_BROKEN_GAMEDATA: return tr("ERROR: Game data is corrupted. The application will continue.", "Game Error"); case localized_string_id::CELL_GAME_ERROR_BROKEN_HDDGAME: return tr("ERROR: HDD boot game is corrupted. The application will continue.", "Game Error"); case localized_string_id::CELL_GAME_ERROR_BROKEN_EXIT_GAMEDATA: return tr("ERROR: Game data is corrupted. The application will be terminated.", "Game Error"); diff --git a/rpcs3/rpcs3qt/pad_settings_dialog.cpp b/rpcs3/rpcs3qt/pad_settings_dialog.cpp index c73a64abfb..f4967e7973 100644 --- a/rpcs3/rpcs3qt/pad_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/pad_settings_dialog.cpp @@ -312,6 +312,7 @@ void pad_settings_dialog::InitButtons() insert_button(button_ids::id_pad_rstick_up, ui->b_rstick_up); insert_button(button_ids::id_pressure_intensity, ui->b_pressure_intensity); + insert_button(button_ids::id_analog_limiter, ui->b_analog_limiter); m_pad_buttons->addButton(ui->b_refresh, button_ids::id_refresh); m_pad_buttons->addButton(ui->b_addConfig, button_ids::id_add_config_file); @@ -748,6 +749,7 @@ void pad_settings_dialog::ReloadButtons() updateButton(button_ids::id_pad_rstick_up, ui->b_rstick_up, &cfg.rs_up); updateButton(button_ids::id_pressure_intensity, ui->b_pressure_intensity, &cfg.pressure_intensity_button); + updateButton(button_ids::id_analog_limiter, ui->b_analog_limiter, &cfg.analog_limiter_button); UpdateLabels(true); } @@ -1226,6 +1228,9 @@ void pad_settings_dialog::UpdateLabels(bool is_reset) RepaintPreviewLabel(ui->preview_stick_left, ui->slider_stick_left->value(), ui->anti_deadzone_slider_stick_left->value(), ui->slider_stick_left->size().width(), m_lx, m_ly, cfg.lpadsquircling, cfg.lstickmultiplier / 100.0); RepaintPreviewLabel(ui->preview_stick_right, ui->slider_stick_right->value(), ui->anti_deadzone_slider_stick_right->value(), ui->slider_stick_right->size().width(), m_rx, m_ry, cfg.rpadsquircling, cfg.rstickmultiplier / 100.0); + // Update analog limiter toggle mode + ui->cb_analog_limiter_toggle_mode->setChecked(cfg.analog_limiter_toggle_mode.get()); + // Update pressure sensitivity factors range = cfg.pressure_intensity.to_list(); ui->sb_pressure_intensity->setRange(std::stoi(range.front()), std::stoi(range.back())); @@ -1278,6 +1283,7 @@ void pad_settings_dialog::SwitchButtons(bool is_enabled) ui->squircle_right->setEnabled(is_enabled); ui->gb_pressure_intensity_deadzone->setEnabled(is_enabled); ui->gb_pressure_intensity->setEnabled(is_enabled && m_enable_pressure_intensity_button); + ui->gb_analog_limiter->setEnabled(is_enabled && m_enable_analog_limiter_button); ui->gb_vibration->setEnabled(is_enabled && m_enable_rumble); ui->gb_motion_controls->setEnabled(is_enabled && m_enable_motion); ui->gb_stick_deadzones->setEnabled(is_enabled && m_enable_deadzones); @@ -1489,10 +1495,14 @@ void pad_settings_dialog::ChangeHandler() // Enable Pressure Sensitivity Settings m_enable_pressure_intensity_button = m_handler->has_pressure_intensity_button(); + // Enable Analog Limiter Settings + m_enable_analog_limiter_button = m_handler->has_analog_limiter_button(); + // Change our contextual widgets ui->left_stack->setCurrentIndex((m_handler->m_type == pad_handler::keyboard) ? 1 : 0); ui->right_stack->setCurrentIndex((m_handler->m_type == pad_handler::keyboard) ? 1 : 0); ui->gb_pressure_intensity->setVisible(m_handler->has_pressure_intensity_button()); + ui->gb_analog_limiter->setVisible(m_handler->has_analog_limiter_button()); // Update device dropdown and block signals while doing so ui->chooseDevice->blockSignals(true); @@ -1849,7 +1859,7 @@ void pad_settings_dialog::ApplyCurrentPlayerConfig(int new_player_id) for (const auto& [id, button] : m_cfg_entries) { // Let's ignore special keys, unless we're using a keyboard - if (id == button_ids::id_pressure_intensity && m_handler->m_type != pad_handler::keyboard) + if ((id == button_ids::id_pressure_intensity || id == button_ids::id_analog_limiter) && m_handler->m_type != pad_handler::keyboard) continue; for (const std::string& key : cfg_pad::get_buttons(button.keys)) @@ -1903,6 +1913,11 @@ void pad_settings_dialog::ApplyCurrentPlayerConfig(int new_player_id) cfg.rstick_anti_deadzone.set(ui->anti_deadzone_slider_stick_right->value()); } + if (m_handler->has_analog_limiter_button()) + { + cfg.analog_limiter_toggle_mode.set(ui->cb_analog_limiter_toggle_mode->isChecked()); + } + if (m_handler->has_pressure_intensity_button()) { cfg.pressure_intensity.set(ui->sb_pressure_intensity->value()); @@ -2098,6 +2113,7 @@ void pad_settings_dialog::SubscribeTooltips() // Localized tooltips const Tooltips tooltips; + SubscribeTooltip(ui->gb_analog_limiter, tooltips.gamepad_settings.analog_limiter); SubscribeTooltip(ui->gb_pressure_intensity, tooltips.gamepad_settings.pressure_intensity); SubscribeTooltip(ui->gb_pressure_intensity_deadzone, tooltips.gamepad_settings.pressure_deadzone); SubscribeTooltip(ui->gb_squircle, tooltips.gamepad_settings.squircle_factor); diff --git a/rpcs3/rpcs3qt/pad_settings_dialog.h b/rpcs3/rpcs3qt/pad_settings_dialog.h index c5535597b3..c5fcc922a2 100644 --- a/rpcs3/rpcs3qt/pad_settings_dialog.h +++ b/rpcs3/rpcs3qt/pad_settings_dialog.h @@ -65,6 +65,7 @@ class pad_settings_dialog : public QDialog id_pad_rstick_up, id_pressure_intensity, // Special button for pressure intensity + id_analog_limiter, // Special button for analog limiter id_pad_end, // end @@ -121,6 +122,7 @@ private: bool m_enable_battery_led{ false }; bool m_enable_motion{ false }; bool m_enable_pressure_intensity_button{ true }; + bool m_enable_analog_limiter_button{ true }; // Button Mapping QButtonGroup* m_pad_buttons = nullptr; diff --git a/rpcs3/rpcs3qt/pad_settings_dialog.ui b/rpcs3/rpcs3qt/pad_settings_dialog.ui index e7cf852fd7..94f78f01b7 100644 --- a/rpcs3/rpcs3qt/pad_settings_dialog.ui +++ b/rpcs3/rpcs3qt/pad_settings_dialog.ui @@ -669,6 +669,57 @@ + + + + Analog Limiter + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + - + + + + + + + Qt::Horizontal + + + QSizePolicy::MinimumExpanding + + + + 0 + 0 + + + + + + + + Toggle + + + + + + @@ -723,6 +774,18 @@ Pressure Sensitivity Deadzone + + 5 + + + 5 + + + 5 + + + 5 + diff --git a/rpcs3/rpcs3qt/settings_dialog.cpp b/rpcs3/rpcs3qt/settings_dialog.cpp index 8ee45e5aee..7edce0cad8 100644 --- a/rpcs3/rpcs3qt/settings_dialog.cpp +++ b/rpcs3/rpcs3qt/settings_dialog.cpp @@ -1797,6 +1797,9 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std m_emu_settings->EnhanceCheckBox(ui->showPressureIntensityToggleHint, emu_settings_type::ShowPressureIntensityToggleHint); SubscribeTooltip(ui->showPressureIntensityToggleHint, tooltips.settings.show_pressure_intensity_toggle_hint); + m_emu_settings->EnhanceCheckBox(ui->showAnalogLimiterToggleHint, emu_settings_type::ShowAnalogLimiterToggleHint); + SubscribeTooltip(ui->showAnalogLimiterToggleHint, tooltips.settings.show_analog_limiter_toggle_hint); + m_emu_settings->EnhanceCheckBox(ui->pauseDuringHomeMenu, emu_settings_type::PauseDuringHomeMenu); SubscribeTooltip(ui->pauseDuringHomeMenu, tooltips.settings.pause_during_home_menu); diff --git a/rpcs3/rpcs3qt/settings_dialog.ui b/rpcs3/rpcs3qt/settings_dialog.ui index 86b54687ae..fc9f33fde3 100644 --- a/rpcs3/rpcs3qt/settings_dialog.ui +++ b/rpcs3/rpcs3qt/settings_dialog.ui @@ -2967,6 +2967,13 @@ + + + + Show analog limiter toggle hint + + + diff --git a/rpcs3/rpcs3qt/tooltips.h b/rpcs3/rpcs3qt/tooltips.h index 0b7c4fb815..ba2fce2487 100644 --- a/rpcs3/rpcs3qt/tooltips.h +++ b/rpcs3/rpcs3qt/tooltips.h @@ -141,6 +141,7 @@ public: const QString show_shader_compilation_hint = tr("Shows 'Compiling shaders' hint using the native overlay."); const QString show_ppu_compilation_hint = tr("Shows 'Compiling PPU modules' hint using the native overlay."); const QString show_pressure_intensity_toggle_hint = tr("Shows pressure intensity toggle hint using the native overlay."); + const QString show_analog_limiter_toggle_hint = tr("Shows analog limiter toggle hint using the native overlay."); const QString use_native_interface = tr("Enables use of native HUD within the game window that can interact with game controllers.\nWhen disabled, regular Qt dialogs are used instead.\nCurrently, the on-screen keyboard only supports the English key layout."); const QString pause_during_home_menu = tr("When enabled, opening the home menu will also pause emulation.\nWhile most games pause themselves while the home menu is shown, some do not.\nIn that case it can be helpful to pause the emulation whenever the home menu is open."); @@ -278,6 +279,7 @@ public: const QString mmjoy = tr("The MMJoystick handler should work with almost any controller recognized by Windows. However, it is recommended that you use the more specific handlers if you have a controller that supports them."); const QString sdl = tr("The SDL handler supports a variety of controllers across different platforms."); + const QString analog_limiter = tr("Applies the stick multipliers while this special button is pressed.
Enable \"Toggle\" if you want to toggle the analog limiter on button press instead."); const QString pressure_intensity = tr("Controls the intensity of pressure sensitive buttons while this special button is pressed.
Enable \"Toggle\" if you want to toggle the intensity on button press instead.
Use the percentage to change how hard you want to press a button."); const QString pressure_deadzone = tr("Controls the deadzone of pressure sensitive buttons. It determines how far the button has to be pressed until it is recognized by the game. The resulting range will be projected onto the full button sensitivity range."); const QString squircle_factor = tr("The actual DualShock 3's stick range is not circular but formed like a rounded square (or squircle) which represents the maximum range of the emulated sticks. You can use the squircle values to modify the stick input if your sticks can't reach the corners of that range. A value of 0 does not apply any so called squircling. A value of 8000 is usually recommended.");