diff --git a/rpcs3/Emu/Io/PadHandler.cpp b/rpcs3/Emu/Io/PadHandler.cpp index 953e6be55f..f04239f15c 100644 --- a/rpcs3/Emu/Io/PadHandler.cpp +++ b/rpcs3/Emu/Io/PadHandler.cpp @@ -11,25 +11,35 @@ PadHandlerBase::PadHandlerBase(pad_handler type) : m_type(type) { } -std::set PadHandlerBase::FindKeyCodes(const std::unordered_map& map, const cfg::string& cfg_string, bool fallback) +std::vector> PadHandlerBase::find_key_combos(const std::unordered_map& map, const cfg::string& cfg_string, bool fallback) { - std::set key_codes; + std::vector> key_codes; const std::string& def = cfg_string.def; - const std::vector names = cfg_pad::get_buttons(cfg_string.to_string()); + const std::vector> combos = cfg_pad::get_buttons(cfg_string.to_string()); u32 def_code = umax; - for (const std::string& nam : names) + for (const std::vector& names : combos) { - for (const auto& [code, name] : map) - { - if (name == nam) - { - key_codes.insert(code); - } + std::set keys; - if (fallback && name == def) - def_code = code; + for (const std::string& nam : names) + { + for (const auto& [code, name] : map) + { + if (name == nam) + { + keys.insert(code); + } + + if (fallback && name == def) + def_code = code; + } + } + + if (!keys.empty()) + { + key_codes.push_back(std::move(keys)); } } @@ -40,19 +50,19 @@ std::set PadHandlerBase::FindKeyCodes(const std::unordered_map PadHandlerBase::FindKeyCodes(const std::unordered_map& map, const std::vector& names) +std::set PadHandlerBase::find_key_codes(const std::unordered_map& map, const std::vector& names) { std::set key_codes; @@ -512,7 +522,7 @@ bool PadHandlerBase::bindPadToDevice(std::shared_ptr pad) return false; } - std::array, button::button_count> mapping = get_mapped_key_codes(pad_device, config); + std::array>, button::button_count> mapping = get_mapped_key_codes(pad_device, config); u32 pclass_profile = 0x0; u32 capabilities = CELL_PAD_CAPABILITY_PS3_CONFORMITY | CELL_PAD_CAPABILITY_PRESS_MODE | CELL_PAD_CAPABILITY_HP_ANALOG_STICK | CELL_PAD_CAPABILITY_ACTUATOR | CELL_PAD_CAPABILITY_SENSOR_MODE; @@ -602,58 +612,58 @@ bool PadHandlerBase::bindPadToDevice(std::shared_ptr pad) return true; } -std::array, PadHandlerBase::button::button_count> PadHandlerBase::get_mapped_key_codes(const std::shared_ptr& device, const cfg_pad* cfg) +std::array>, PadHandlerBase::button::button_count> PadHandlerBase::get_mapped_key_codes(const std::shared_ptr& device, const cfg_pad* cfg) { - std::array, button::button_count> mapping{}; + std::array>, button::button_count> mapping{}; if (!device || !cfg) return mapping; - mapping[button::up] = FindKeyCodes(button_list, cfg->up); - mapping[button::down] = FindKeyCodes(button_list, cfg->down); - mapping[button::left] = FindKeyCodes(button_list, cfg->left); - mapping[button::right] = FindKeyCodes(button_list, cfg->right); - mapping[button::cross] = FindKeyCodes(button_list, cfg->cross); - mapping[button::square] = FindKeyCodes(button_list, cfg->square); - mapping[button::circle] = FindKeyCodes(button_list, cfg->circle); - mapping[button::triangle] = FindKeyCodes(button_list, cfg->triangle); - mapping[button::start] = FindKeyCodes(button_list, cfg->start); - mapping[button::select] = FindKeyCodes(button_list, cfg->select); - mapping[button::l1] = FindKeyCodes(button_list, cfg->l1); - mapping[button::l2] = FindKeyCodes(button_list, cfg->l2); - mapping[button::l3] = FindKeyCodes(button_list, cfg->l3); - mapping[button::r1] = FindKeyCodes(button_list, cfg->r1); - mapping[button::r2] = FindKeyCodes(button_list, cfg->r2); - mapping[button::r3] = FindKeyCodes(button_list, cfg->r3); - mapping[button::ls_left] = FindKeyCodes(button_list, cfg->ls_left); - mapping[button::ls_right] = FindKeyCodes(button_list, cfg->ls_right); - mapping[button::ls_down] = FindKeyCodes(button_list, cfg->ls_down); - mapping[button::ls_up] = FindKeyCodes(button_list, cfg->ls_up); - mapping[button::rs_left] = FindKeyCodes(button_list, cfg->rs_left); - mapping[button::rs_right] = FindKeyCodes(button_list, cfg->rs_right); - mapping[button::rs_down] = FindKeyCodes(button_list, cfg->rs_down); - mapping[button::rs_up] = FindKeyCodes(button_list, cfg->rs_up); - mapping[button::ps] = FindKeyCodes(button_list, cfg->ps); + mapping[button::up] = find_key_combos(button_list, cfg->up); + mapping[button::down] = find_key_combos(button_list, cfg->down); + mapping[button::left] = find_key_combos(button_list, cfg->left); + mapping[button::right] = find_key_combos(button_list, cfg->right); + mapping[button::cross] = find_key_combos(button_list, cfg->cross); + mapping[button::square] = find_key_combos(button_list, cfg->square); + mapping[button::circle] = find_key_combos(button_list, cfg->circle); + mapping[button::triangle] = find_key_combos(button_list, cfg->triangle); + mapping[button::start] = find_key_combos(button_list, cfg->start); + mapping[button::select] = find_key_combos(button_list, cfg->select); + mapping[button::l1] = find_key_combos(button_list, cfg->l1); + mapping[button::l2] = find_key_combos(button_list, cfg->l2); + mapping[button::l3] = find_key_combos(button_list, cfg->l3); + mapping[button::r1] = find_key_combos(button_list, cfg->r1); + mapping[button::r2] = find_key_combos(button_list, cfg->r2); + mapping[button::r3] = find_key_combos(button_list, cfg->r3); + mapping[button::ls_left] = find_key_combos(button_list, cfg->ls_left); + mapping[button::ls_right] = find_key_combos(button_list, cfg->ls_right); + mapping[button::ls_down] = find_key_combos(button_list, cfg->ls_down); + mapping[button::ls_up] = find_key_combos(button_list, cfg->ls_up); + mapping[button::rs_left] = find_key_combos(button_list, cfg->rs_left); + mapping[button::rs_right] = find_key_combos(button_list, cfg->rs_right); + mapping[button::rs_down] = find_key_combos(button_list, cfg->rs_down); + mapping[button::rs_up] = find_key_combos(button_list, cfg->rs_up); + mapping[button::ps] = find_key_combos(button_list, cfg->ps); - mapping[button::skateboard_ir_nose] = FindKeyCodes(button_list, cfg->ir_nose); - mapping[button::skateboard_ir_tail] = FindKeyCodes(button_list, cfg->ir_tail); - mapping[button::skateboard_ir_left] = FindKeyCodes(button_list, cfg->ir_left); - mapping[button::skateboard_ir_right] = FindKeyCodes(button_list, cfg->ir_right); - mapping[button::skateboard_tilt_left] = FindKeyCodes(button_list, cfg->tilt_left); - mapping[button::skateboard_tilt_right] = FindKeyCodes(button_list, cfg->tilt_right); + mapping[button::skateboard_ir_nose] = find_key_combos(button_list, cfg->ir_nose); + mapping[button::skateboard_ir_tail] = find_key_combos(button_list, cfg->ir_tail); + mapping[button::skateboard_ir_left] = find_key_combos(button_list, cfg->ir_left); + mapping[button::skateboard_ir_right] = find_key_combos(button_list, cfg->ir_right); + mapping[button::skateboard_tilt_left] = find_key_combos(button_list, cfg->tilt_left); + mapping[button::skateboard_tilt_right] = find_key_combos(button_list, cfg->tilt_right); if (b_has_pressure_intensity_button) { - mapping[button::pressure_intensity_button] = FindKeyCodes(button_list, cfg->pressure_intensity_button); + mapping[button::pressure_intensity_button] = find_key_combos(button_list, cfg->pressure_intensity_button); } if (b_has_analog_limiter_button) { - mapping[button::analog_limiter_button] = FindKeyCodes(button_list, cfg->analog_limiter_button); + mapping[button::analog_limiter_button] = find_key_combos(button_list, cfg->analog_limiter_button); } if (b_has_orientation) { - mapping[button::orientation_reset_button] = FindKeyCodes(button_list, cfg->orientation_reset_button); + mapping[button::orientation_reset_button] = find_key_combos(button_list, cfg->orientation_reset_button); } return mapping; @@ -685,30 +695,46 @@ void PadHandlerBase::get_mapping(const pad_ensemble& binding) bool pressed{}; u16 value{}; - for (u32 code : button.m_key_codes) + // The DS3 Button is considered pressed if any configured button combination is pressed + for (const std::set& combo : button.m_key_combos) { - bool press{}; - u16 val = button_values[code]; + bool combo_pressed = !combo.empty(); + u16 combo_val = 0; - TranslateButtonPress(device, code, press, val, analog_limiter_enabled); - - if (press) + // The button combination is only considered pressed if all the buttons are pressed + for (u32 code : combo) { + bool btn_pressed{}; + u16 btn_val = button_values[code]; + TranslateButtonPress(device, code, btn_pressed, btn_val, analog_limiter_enabled); + + if (btn_pressed == false) + { + combo_pressed = false; + break; + } + // Modify pressure if necessary if the button was pressed if (adjust_pressure) { - val = pad->m_pressure_intensity; + btn_val = pad->m_pressure_intensity; } else if (pressure_intensity_deadzone > 0) { // Ignore triggers, since they have their own deadzones if (!get_is_left_trigger(device, code) && !get_is_right_trigger(device, code)) { - val = NormalizeDirectedInput(val, pressure_intensity_deadzone, 255); + btn_val = NormalizeDirectedInput(btn_val, pressure_intensity_deadzone, 255); } } - value = std::max(value, val); + // Take minimum combo value. Otherwise we will always end up with the max value in case an actual button is part of the combo. + combo_val = (combo_val == 0) ? btn_val : std::min(combo_val, btn_val); + } + + if (combo_pressed) + { + value = std::max(value, combo_val); pressed = value > 0; } } @@ -727,31 +753,44 @@ void PadHandlerBase::get_mapping(const pad_ensemble& binding) u16 val_min{}; u16 val_max{}; - // m_key_codes_min are the mapped keys for left or down - for (u32 key_min : pad->m_sticks[i].m_key_codes_min) + // The DS3 Stick direction is considered pressed if any configured button combination is pressed + const auto get_stick_val = [this, &device, &button_values, &pressed, analog_limiter_enabled](const std::vector>& combos, u16& value) { - u16 val = button_values[key_min]; - - TranslateButtonPress(device, key_min, pressed, val, analog_limiter_enabled, true); - - if (pressed) + for (const std::set& combo : combos) { - val_min = std::max(val_min, val); + bool combo_pressed = !combo.empty(); + u16 combo_val = 0; + + for (u32 key_min : combo) + { + bool btn_pressed{}; + u16 btn_val = button_values[key_min]; + + TranslateButtonPress(device, key_min, btn_pressed, btn_val, analog_limiter_enabled, true); + + if (btn_pressed == false) + { + combo_pressed = false; + break; + } + + // Take minimum combo value. Otherwise we will always end up with the max value in case an actual button is part of the combo. + combo_val = (combo_val == 0) ? btn_val : std::min(combo_val, btn_val); + } + + if (combo_pressed) + { + value = std::max(value, combo_val); + pressed = value > 0; + } } - } + }; - // m_key_codes_max are the mapped keys for right or up - for (u32 key_max : pad->m_sticks[i].m_key_codes_max) - { - u16 val = button_values[key_max]; + // m_key_combos_min are the mapped keys for left or down + get_stick_val(pad->m_sticks[i].m_key_combos_min, val_min); - TranslateButtonPress(device, key_max, pressed, val, analog_limiter_enabled, true); - - if (pressed) - { - val_max = std::max(val_max, val); - } - } + // m_key_combos_max are the mapped keys for right or up + get_stick_val(pad->m_sticks[i].m_key_combos_max, val_max); // cancel out opposing values and get the resulting difference stick_val[i] = val_max - val_min; diff --git a/rpcs3/Emu/Io/PadHandler.h b/rpcs3/Emu/Io/PadHandler.h index f6d18a63ca..04fafee1a1 100644 --- a/rpcs3/Emu/Io/PadHandler.h +++ b/rpcs3/Emu/Io/PadHandler.h @@ -192,11 +192,11 @@ protected: std::shared_ptr m_pad_for_pad_settings; - // Search an unordered map for a string value and return found keycode - static std::set FindKeyCodes(const std::unordered_map& map, const cfg::string& cfg_string, bool fallback = true); + // Search an unordered map for a string value and return the found combo + static std::vector> find_key_combos(const std::unordered_map& map, const cfg::string& cfg_string, bool fallback = true); - // Search an unordered map for a string value and return found keycode - static std::set FindKeyCodes(const std::unordered_map& map, const std::vector& names); + // Search an unordered map for string values and return the found key codes + static std::set find_key_codes(const std::unordered_map& map, const std::vector& names); // Get normalized trigger value based on the range defined by a threshold u16 NormalizeTriggerInput(u16 value, u32 threshold) const; @@ -320,7 +320,7 @@ private: void get_orientation(const pad_ensemble& binding) const; protected: - virtual std::array, PadHandlerBase::button::button_count> get_mapped_key_codes(const std::shared_ptr& device, const cfg_pad* cfg); + 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, u32 keyCode, bool& pressed, u16& val, bool use_stick_multipliers, bool ignore_stick_threshold = false, bool ignore_trigger_threshold = false); void init_configs(); diff --git a/rpcs3/Emu/Io/pad_config.cpp b/rpcs3/Emu/Io/pad_config.cpp index 937626076e..ce64513659 100644 --- a/rpcs3/Emu/Io/pad_config.cpp +++ b/rpcs3/Emu/Io/pad_config.cpp @@ -5,30 +5,89 @@ extern std::string g_input_config_override; -std::vector cfg_pad::get_buttons(std::string_view str) +std::vector> cfg_pad::get_buttons(std::string_view str) { - std::vector vec = fmt::split(str, {","}); + if (str.empty()) + return {}; - // Handle special case: string contains separator itself as configured value - if (str == "," || str.find(",,") != umax) + // Handle special case: string contains separator itself as configured value (it's why I don't use fmt::split here) + const auto split = [](std::string_view str, char sep) { - vec.push_back(","); + std::vector vec; + bool was_sep = true; + usz btn_start = 0ULL; + usz i = 0ULL; + + for (; i < str.size(); i++) + { + const char c = str[i]; + + if (c == sep) + { + if (!was_sep) + { + was_sep = true; + vec.push_back(std::string(str.substr(btn_start, i - btn_start))); + continue; + } + } + + if (was_sep) + { + was_sep = false; + btn_start = i; + } + + if (i == (str.size() - 1)) + { + vec.push_back(std::string(str.substr(btn_start, i - btn_start + 1))); + } + } + + // Remove duplicates + std::sort(vec.begin(), vec.end()); + vec.erase(std::unique(vec.begin(), vec.end()), vec.end()); + + return vec; + }; + + std::vector> res; + + // Get all combos (seperated by ',') + const std::vector vec = split(str, ','); + + for (const std::string& combo : vec) + { + // Get all keys for this combo (seperated by '&') + std::vector keys = split(combo, '&'); + if (!keys.empty()) + { + res.push_back(std::move(keys)); + } } - // Remove duplicates - std::sort(vec.begin(), vec.end()); - vec.erase(std::unique(vec.begin(), vec.end()), vec.end()); - - return vec; + return res; } -std::string cfg_pad::get_buttons(std::vector vec) +std::string cfg_pad::get_buttons(std::vector> vec) { + std::vector combos; + // Remove duplicates std::sort(vec.begin(), vec.end()); vec.erase(std::unique(vec.begin(), vec.end()), vec.end()); - return fmt::merge(vec, ","); + for (std::vector& combo : vec) + { + std::sort(combo.begin(), combo.end()); + combo.erase(std::unique(combo.begin(), combo.end()), combo.end()); + + // Merge all keys for this combo (seperated by '&') + combos.push_back(fmt::merge(combo, "&")); + } + + // Merge combos (seperated by ',') + return fmt::merge(combos, ","); } u8 cfg_pad::get_motor_speed(VibrateMotor& motor, f32 multiplier) const diff --git a/rpcs3/Emu/Io/pad_config.h b/rpcs3/Emu/Io/pad_config.h index 7dd4bd6323..07de4a7299 100644 --- a/rpcs3/Emu/Io/pad_config.h +++ b/rpcs3/Emu/Io/pad_config.h @@ -25,8 +25,8 @@ struct cfg_pad final : cfg::node cfg_pad() {}; cfg_pad(node* owner, const std::string& name) : cfg::node(owner, name) {} - static std::vector get_buttons(std::string_view str); - static std::string get_buttons(std::vector vec); + static std::vector> get_buttons(std::string_view str); + static std::string get_buttons(std::vector> vec); u8 get_motor_speed(VibrateMotor& motor, f32 multiplier) const; u8 get_large_motor_speed(std::array& motors) const; diff --git a/rpcs3/Emu/Io/pad_types.cpp b/rpcs3/Emu/Io/pad_types.cpp index ad3369a7a7..d5ef26f0d7 100644 --- a/rpcs3/Emu/Io/pad_types.cpp +++ b/rpcs3/Emu/Io/pad_types.cpp @@ -203,7 +203,7 @@ bool Pad::get_analog_limiter_button_active(bool is_toggle_mode, u32 player_id) const Button& analog_limiter_button = m_buttons[m_analog_limiter_button_index]; - if (analog_limiter_button.m_key_codes.empty()) + if (analog_limiter_button.m_key_combos.empty()) { // Active by default if no button was assigned return true; diff --git a/rpcs3/Emu/Io/pad_types.h b/rpcs3/Emu/Io/pad_types.h index 5fd9c8973a..d2b1535a1a 100644 --- a/rpcs3/Emu/Io/pad_types.h +++ b/rpcs3/Emu/Io/pad_types.h @@ -386,18 +386,18 @@ struct Button u16 m_value = 0; bool m_pressed = false; - std::set m_key_codes{}; + std::vector> m_key_combos; - u16 m_actual_value = 0; // only used in keyboard_pad_handler - bool m_analog = false; // only used in keyboard_pad_handler - bool m_trigger = false; // only used in keyboard_pad_handler - std::map m_pressed_keys{}; // only used in keyboard_pad_handler + u16 m_actual_value = 0; // only used in keyboard_pad_handler + bool m_analog = false; // only used in keyboard_pad_handler + bool m_trigger = false; // only used in keyboard_pad_handler + std::map m_pressed_keys; // only used in keyboard_pad_handler Button(){} - Button(u32 offset, std::set key_codes, u32 outKeyCode) + Button(u32 offset, std::vector> key_combos, u32 outKeyCode) : m_offset(offset) , m_outKeyCode(outKeyCode) - , m_key_codes(std::move(key_codes)) + , m_key_combos(std::move(key_combos)) { if (offset == CELL_PAD_BTN_OFFSET_DIGITAL1) { @@ -426,17 +426,19 @@ struct AnalogStick u32 m_offset = 0; u16 m_value = 128; - std::set m_key_codes_min{}; - std::set m_key_codes_max{}; + std::vector> m_key_combos_min; + std::vector> m_key_combos_max; - std::map m_pressed_keys_min{}; // only used in keyboard_pad_handler - std::map m_pressed_keys_max{}; // only used in keyboard_pad_handler + std::map m_pressed_keys_min; // only used in keyboard_pad_handler + std::map m_pressed_keys_max; // only used in keyboard_pad_handler + std::map m_pressed_combos_min; // only used in keyboard_pad_handler + std::map m_pressed_combos_max; // only used in keyboard_pad_handler AnalogStick() {} - AnalogStick(u32 offset, std::set key_codes_min, std::set key_codes_max) + AnalogStick(u32 offset, std::vector> key_combos_min, std::vector> key_combos_max) : m_offset(offset) - , m_key_codes_min(std::move(key_codes_min)) - , m_key_codes_max(std::move(key_codes_max)) + , m_key_combos_min(std::move(key_combos_min)) + , m_key_combos_max(std::move(key_combos_max)) {} }; diff --git a/rpcs3/Input/evdev_joystick_handler.cpp b/rpcs3/Input/evdev_joystick_handler.cpp index 07c39aab26..e920a61d14 100644 --- a/rpcs3/Input/evdev_joystick_handler.cpp +++ b/rpcs3/Input/evdev_joystick_handler.cpp @@ -373,7 +373,7 @@ PadHandlerBase::connection evdev_joystick_handler::get_next_button_press(const s const auto find_value = [&, this](const std::string& str) { - const std::vector names = cfg_pad::get_buttons(str); + const std::vector> combos = cfg_pad::get_buttons(str); u16 value{}; @@ -385,19 +385,22 @@ PadHandlerBase::connection evdev_joystick_handler::get_next_button_press(const s } }; - for (const u32 code : FindKeyCodes(rev_axis_list, names)) + for (const std::vector& names : combos) { - set_value(code, true); - } + for (const u32 code : find_key_codes(rev_axis_list, names)) + { + set_value(code, true); + } - for (const u32 code : FindKeyCodes(axis_list, names)) - { - set_value(code, false); - } + for (const u32 code : find_key_codes(axis_list, names)) + { + set_value(code, false); + } - for (const u32 code : FindKeyCodes(button_list, names)) - { - set_value(code, false); + for (const u32 code : find_key_codes(button_list, names)) + { + set_value(code, false); + } } return value; @@ -1235,19 +1238,51 @@ void evdev_joystick_handler::apply_input_events(const std::shared_ptr& pad) } }; + const auto process_mapped_combo = [&](const std::vector>& combos, bool is_stick_value) + { + bool pressed{}; + u16 value = 0; + + for (const std::set& combo : combos) + { + bool combo_pressed = !combo.empty(); + u16 combo_val = 0; + + // The button combination is only considered pressed if all the buttons are pressed + for (u32 index : combo) + { + bool btn_pressed{}; + u16 btn_val = 0; + + process_mapped_button(index, btn_pressed, btn_val, is_stick_value); + + if (btn_pressed == false) + { + combo_pressed = false; + break; + } + + // Take minimum combo value. Otherwise we will always end up with the max value in case an actual button is part of the combo. + combo_val = (combo_val == 0) ? btn_val : std::min(combo_val, btn_val); + } + + if (combo_pressed) + { + value = std::max(value, combo_val); + pressed = value > 0; + } + } + + return std::make_pair(pressed, value); + }; + // Translate any corresponding keycodes to our normal DS3 buttons and triggers for (Button& button : pad->m_buttons) { - bool pressed{}; - u16 final_value{}; + const std::pair val = process_mapped_combo(button.m_key_combos, false); - for (u32 index : button.m_key_codes) - { - process_mapped_button(index, pressed, final_value, false); - } - - button.m_value = final_value; - button.m_pressed = pressed; + button.m_pressed = val.first; + button.m_value = val.second; } // used to get the absolute value of an axis @@ -1256,24 +1291,14 @@ void evdev_joystick_handler::apply_input_events(const std::shared_ptr& pad) // Translate any corresponding keycodes to our two sticks. (ignoring thresholds for now) for (usz i = 0; i < pad->m_sticks.size(); i++) { - bool pressed{}; // unused - u16 val_min{}; - u16 val_max{}; - // m_key_codes_min are the mapped keys for left or down - for (u32 index : pad->m_sticks[i].m_key_codes_min) - { - process_mapped_button(index, pressed, val_min, true); - } + const std::pair val_min = process_mapped_combo(pad->m_sticks[i].m_key_combos_min, true); // m_key_codes_max are the mapped keys for right or up - for (u32 index : pad->m_sticks[i].m_key_codes_max) - { - process_mapped_button(index, pressed, val_max, true); - } + const std::pair val_max = process_mapped_combo(pad->m_sticks[i].m_key_combos_max, true); // cancel out opposing values and get the resulting difference. if there was no change, use the old value. - stick_val[i] = val_max - val_min; + stick_val[i] = val_max.second - val_min.second; } u16 lx, ly, rx, ry; @@ -1344,29 +1369,39 @@ bool evdev_joystick_handler::bindPadToDevice(std::shared_ptr pad) return index; }; - const auto find_buttons = [&](const cfg::string& name) -> std::set + const auto find_buttons = [&](const cfg::string& name) -> std::vector> { - const std::vector names = cfg_pad::get_buttons(name.to_string()); + const std::vector> str_combos = cfg_pad::get_buttons(name.to_string()); // In evdev we store indices to an EvdevButton vector in our pad objects instead of the usual key codes. - std::set indices; + std::vector> combos; - for (const u32 code : FindKeyCodes(axis_list, names)) + for (const std::vector& names : str_combos) { - indices.insert(register_evdevbutton(code, true, false)); + std::set indices; + + for (const u32 code : find_key_codes(axis_list, names)) + { + indices.insert(register_evdevbutton(code, true, false)); + } + + for (const u32 code : find_key_codes(rev_axis_list, names)) + { + indices.insert(register_evdevbutton(code, true, true)); + } + + for (const u32 code : find_key_codes(button_list, names)) + { + indices.insert(register_evdevbutton(code, false, false)); + } + + if (!indices.empty()) + { + combos.push_back(std::move(indices)); + } } - for (const u32 code : FindKeyCodes(rev_axis_list, names)) - { - indices.insert(register_evdevbutton(code, true, true)); - } - - for (const u32 code : FindKeyCodes(button_list, names)) - { - indices.insert(register_evdevbutton(code, false, false)); - } - - return indices; + return combos; }; const auto find_motion_button = [&](const cfg_sensor& sensor) -> evdev_sensor @@ -1376,8 +1411,8 @@ bool evdev_joystick_handler::bindPadToDevice(std::shared_ptr pad) e_sensor.mirrored = sensor.mirrored.get(); e_sensor.shift = sensor.shift.get(); - const std::set keys = FindKeyCodes(motion_axis_list, sensor.axis); - if (!keys.empty()) e_sensor.code = *keys.begin(); // We should only have one key for each of our sensors + const std::vector> combos = find_key_combos(motion_axis_list, sensor.axis); + if (!combos.empty() && !combos.front().empty()) e_sensor.code = *combos.front().begin(); // We should only have one key for each of our sensors return e_sensor; }; @@ -1421,10 +1456,10 @@ bool evdev_joystick_handler::bindPadToDevice(std::shared_ptr pad) pad->m_orientation_reset_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); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2,find_buttons(cfg->cross), CELL_PAD_CTRL_CROSS); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2,find_buttons(cfg->square), CELL_PAD_CTRL_SQUARE); + 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); + pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, find_buttons(cfg->cross), CELL_PAD_CTRL_CROSS); + pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, find_buttons(cfg->square), CELL_PAD_CTRL_SQUARE); m_dev->trigger_left = find_buttons(cfg->l2); pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, m_dev->trigger_left, CELL_PAD_CTRL_L2); @@ -1528,29 +1563,34 @@ bool evdev_joystick_handler::check_button_set(const std::set& indices, cons return false; } -bool evdev_joystick_handler::check_button_sets(const std::array, 4>& sets, const u32 code) +bool evdev_joystick_handler::check_button_combos(const std::vector>& combos, const u32 code) { - return std::any_of(sets.begin(), sets.end(), [this, code](const std::set& indices) { return check_button_set(indices, code); }); -}; + return std::any_of(combos.begin(), combos.end(), [this, code](const std::set& indices) { return check_button_set(indices, code); }); +} + +bool evdev_joystick_handler::check_button_combos(const std::array>, 4>& combo_array, const u32 code) +{ + return std::any_of(combo_array.begin(), combo_array.end(), [this, code](const std::vector>& combos) { return check_button_combos(combos, code); }); +} bool evdev_joystick_handler::get_is_left_trigger(const std::shared_ptr& /*device*/, u32 keyCode) { - return check_button_set(m_dev->trigger_left, keyCode); + return check_button_combos(m_dev->trigger_left, keyCode); } bool evdev_joystick_handler::get_is_right_trigger(const std::shared_ptr& /*device*/, u32 keyCode) { - return check_button_set(m_dev->trigger_right, keyCode); + return check_button_combos(m_dev->trigger_right, keyCode); } bool evdev_joystick_handler::get_is_left_stick(const std::shared_ptr& /*device*/, u32 keyCode) { - return check_button_sets(m_dev->axis_left, keyCode); + return check_button_combos(m_dev->axis_left, keyCode); } bool evdev_joystick_handler::get_is_right_stick(const std::shared_ptr& /*device*/, u32 keyCode) { - return check_button_sets(m_dev->axis_right, keyCode); + return check_button_combos(m_dev->axis_right, keyCode); } #endif diff --git a/rpcs3/Input/evdev_joystick_handler.h b/rpcs3/Input/evdev_joystick_handler.h index a76426e3fb..40785e148f 100644 --- a/rpcs3/Input/evdev_joystick_handler.h +++ b/rpcs3/Input/evdev_joystick_handler.h @@ -162,7 +162,7 @@ class evdev_joystick_handler final : public PadHandlerBase { BTN_TOOL_AIRBRUSH , "Airbrush" }, { BTN_TOOL_FINGER , "Finger" }, { BTN_TOOL_MOUSE , "Mouse" }, - { BTN_TOOL_LENS , "Lense" }, + { BTN_TOOL_LENS , "Lens" }, { BTN_TOOL_QUINTTAP , "Quinttap" }, { 0x149 , "0x149" }, { BTN_TOUCH , "Touch" }, @@ -172,8 +172,8 @@ class evdev_joystick_handler final : public PadHandlerBase { BTN_TOOL_TRIPLETAP , "Tripletap" }, { BTN_TOOL_QUADTAP , "Quadtap" }, //{ BTN_WHEEL , "Wheel" }, same as BTN_GEAR_DOWN - { BTN_GEAR_DOWN , "Gear Up" }, - { BTN_GEAR_UP , "Gear Down" }, + { BTN_GEAR_DOWN , "Gear Down" }, + { BTN_GEAR_UP , "Gear Up" }, { BTN_DPAD_UP , "D-Pad Up" }, { BTN_DPAD_DOWN , "D-Pad Down" }, { BTN_DPAD_LEFT , "D-Pad Left" }, @@ -364,10 +364,10 @@ class evdev_joystick_handler final : public PadHandlerBase libevdev* device{ nullptr }; std::string path; std::vector all_buttons; - std::set trigger_left{}; - std::set trigger_right{}; - std::array, 4> axis_left{}; - std::array, 4> axis_right{}; + std::vector> trigger_left{}; + std::vector> trigger_right{}; + std::array>, 4> axis_left{}; + std::array>, 4> axis_right{}; std::array axis_motion{}; std::map> events_by_code; int cur_dir = 0; @@ -409,7 +409,8 @@ private: std::shared_ptr m_dev; bool check_button_set(const std::set& indices, const u32 code); - bool check_button_sets(const std::array, 4>& sets, const u32 code); + bool check_button_combos(const std::vector>& combos, const u32 code); + bool check_button_combos(const std::array>, 4>& combo_array, const u32 code); void apply_input_events(const std::shared_ptr& pad); diff --git a/rpcs3/Input/keyboard_pad_handler.cpp b/rpcs3/Input/keyboard_pad_handler.cpp index 3aca96100d..e7435b09ba 100644 --- a/rpcs3/Input/keyboard_pad_handler.cpp +++ b/rpcs3/Input/keyboard_pad_handler.cpp @@ -84,24 +84,44 @@ void keyboard_pad_handler::Key(const u32 code, bool pressed, u16 value) for (auto& pad : m_pads_internal) { - const auto register_new_button_value = [&](std::map& pressed_keys) -> u16 + const auto register_new_button_value = [code, pressed, value](Button& btn) -> u16 { u16 actual_value = 0; // Make sure we keep this button pressed until all related keys are released. if (pressed) { - pressed_keys[code] = value; + btn.m_pressed_keys[code] = value; } else { - pressed_keys.erase(code); + btn.m_pressed_keys.erase(code); } - // Get the max value of all pressed keys for this button - for (const auto& [key, val] : pressed_keys) + // Get the max value of all pressed keys for this DS3 button + for (const std::set& key_codes : btn.m_key_combos) { - actual_value = std::max(actual_value, val); + if (key_codes.empty()) continue; + + // Every key in this combination has to be pressed for this button to be considered pressed + u16 combo_value = 0; + const bool combo_pressed = std::all_of(key_codes.cbegin(), key_codes.cend(), [&btn, &combo_value](u32 key_code) + { + const auto it = btn.m_pressed_keys.find(key_code); + if (it == btn.m_pressed_keys.cend() || it->second == 0) + { + return false; // This combo button is not pressed, so the combo is not pressed either. + } + + // Take minimum combo value. Otherwise we will always end up with the max value. + combo_value = (combo_value == 0) ? it->second : std::min(combo_value, it->second); + return true; + }); + + if (combo_pressed) + { + actual_value = std::max(actual_value, combo_value); + } } return actual_value; @@ -109,18 +129,23 @@ void keyboard_pad_handler::Key(const u32 code, bool pressed, u16 value) // Find out if special buttons are pressed (introduced by RPCS3). // Activate the buttons here if possible since keys don't auto-repeat. This ensures that they are already pressed in the following loop. - if (pad.m_pressure_intensity_button_index >= 0) - { - Button& pressure_intensity_button = pad.m_buttons[pad.m_pressure_intensity_button_index]; - if (pressure_intensity_button.m_key_codes.contains(code)) + const auto update_special_button_press = [&pad, ®ister_new_button_value, code](s32 index) + { + if (index < 0) return; + + Button& pressure_intensity_button = pad.m_buttons[index]; + + if (std::any_of(pressure_intensity_button.m_key_combos.cbegin(), pressure_intensity_button.m_key_combos.cend(), [code](const std::set& key_codes){ return key_codes.contains(code); })) { - const u16 actual_value = register_new_button_value(pressure_intensity_button.m_pressed_keys); + const u16 actual_value = register_new_button_value(pressure_intensity_button); pressure_intensity_button.m_pressed = actual_value > 0; pressure_intensity_button.m_value = actual_value; } - } + }; + + update_special_button_press(pad.m_pressure_intensity_button_index); const bool adjust_pressure = pad.get_pressure_intensity_button_active(m_pressure_intensity_toggle_mode, pad.m_player_id); const bool adjust_pressure_changed = pad.m_adjust_pressure_last != adjust_pressure; @@ -130,18 +155,7 @@ 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; - } - } + update_special_button_press(pad.m_analog_limiter_button_index); 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; @@ -158,21 +172,22 @@ void keyboard_pad_handler::Key(const u32 code, bool pressed, u16 value) { // Ignore special buttons if (static_cast(i) == pad.m_pressure_intensity_button_index || - static_cast(i) == pad.m_analog_limiter_button_index) + static_cast(i) == pad.m_analog_limiter_button_index || + static_cast(i) == pad.m_orientation_reset_button_index) continue; Button& button = pad.m_buttons[i]; bool update_button = true; - if (!button.m_key_codes.contains(code)) + if (std::none_of(button.m_key_combos.cbegin(), button.m_key_combos.cend(), [code](const std::set& key_codes){ return key_codes.contains(code); })) { // Handle pressure changes anyway update_button = adjust_pressure_changed; } else { - button.m_actual_value = register_new_button_value(button.m_pressed_keys); + button.m_actual_value = register_new_button_value(button); // to get the fastest response time possible we don't wanna use any lerp with factor 1 if (button.m_analog) @@ -220,8 +235,8 @@ void keyboard_pad_handler::Key(const u32 code, bool pressed, u16 value) 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); + const bool is_max = std::any_of(stick.m_key_combos_max.cbegin(), stick.m_key_combos_max.cend(), [code](const std::set& combo) { return combo.contains(code); }); + const bool is_min = std::any_of(stick.m_key_combos_min.cbegin(), stick.m_key_combos_min.cend(), [code](const std::set& combo) { return combo.contains(code); }); if (!is_max && !is_min) { @@ -229,8 +244,8 @@ void keyboard_pad_handler::Key(const u32 code, bool pressed, u16 value) 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(); + const bool is_min_pressed = !stick.m_pressed_combos_min.empty(); + const bool is_max_pressed = !stick.m_pressed_combos_max.empty(); const u32 stick_multiplier = is_left_stick ? l_stick_multiplier : r_stick_multiplier; @@ -246,43 +261,87 @@ void keyboard_pad_handler::Key(const u32 code, bool pressed, u16 value) else { 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 auto register_new_stick_value = [&](std::map& pressed_keys, bool is_max) + const auto register_new_stick_value = [&](bool is_max) { + const std::vector>& key_combos = is_max ? stick.m_key_combos_max : stick.m_key_combos_min; + std::map& pressed_keys = is_max ? stick.m_pressed_keys_max : stick.m_pressed_keys_min; + std::map& pressed_combos = is_max ? stick.m_pressed_combos_max : stick.m_pressed_combos_min; + // Make sure we keep this stick pressed until all related keys are released. if (pressed) { - pressed_keys[code] = normalized_value; + pressed_keys[code] = actual_value; } else { pressed_keys.erase(code); } + bool any_combo_pressed = false; + u16 new_val = 0; + // Get the min/max value of all pressed keys for this stick - for (const auto& [key, val] : pressed_keys) + for (const std::set& key_codes : key_combos) { - normalized_value = is_max ? std::max(normalized_value, val) : std::min(normalized_value, val); + if (key_codes.empty()) continue; + + // Every key in this combination has to be pressed for this button to be considered pressed + u16 combo_value = 0; + const bool combo_pressed = std::all_of(key_codes.cbegin(), key_codes.cend(), [&pressed_keys, &combo_value, is_max, i, pressed](u32 key_code) + { + const auto it = pressed_keys.find(key_code); + if (it == pressed_keys.cend() || it->second == 0) + { + return false; // This combo button is not pressed, so the combo is not pressed either. + } + + // Take minimum combo value. Otherwise we will always end up with the max value. + combo_value = (combo_value == 0) ? it->second : std::min(combo_value, it->second); + return true; + }); + + if (combo_pressed) + { + // Take minimum combo value. Otherwise we will always end up with the max value. + new_val = (new_val == 0) ? combo_value : std::min(new_val, combo_value); + any_combo_pressed = true; + } } + + if (any_combo_pressed) + { + if (pressed_combos.contains(code)) + { + u16& pressed_val = pressed_combos[code]; + pressed_val = is_max ? std::max(new_val, pressed_val) : std::min(new_val, pressed_val); + new_val = pressed_val; + } + else + { + pressed_combos[code] = new_val; + } + } + else + { + pressed_combos.erase(code); + } + + return std::pair(any_combo_pressed, static_cast(std::ceil(new_val / 2.0f))); }; if (is_max) { - register_new_stick_value(stick.m_pressed_keys_max, true); + const std::pair stick_value = register_new_stick_value(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; + m_stick_max[i] = stick_value.first ? std::min(128 + stick_value.second, 255) : 128; } if (is_min) { - register_new_stick_value(stick.m_pressed_keys_min, false); + const std::pair stick_value = register_new_stick_value(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_min[i] = stick_value.first ? std::min(stick_value.second, 128) : 0; } } @@ -832,17 +891,29 @@ std::string keyboard_pad_handler::GetKeyName(const u32& keyCode) return QKeySequence(keyCode).toString(QKeySequence::NativeText).toStdString(); } -std::set keyboard_pad_handler::GetKeyCodes(const cfg::string& cfg_string) +std::vector> keyboard_pad_handler::GetKeyCombos(const cfg::string& cfg_string) { - std::set key_codes; - for (const std::string& key_name : cfg_pad::get_buttons(cfg_string.to_string())) + std::vector> res; + + for (const std::vector& combo : cfg_pad::get_buttons(cfg_string.to_string())) { - if (u32 code = GetKeyCode(QString::fromStdString(key_name)); code != Qt::NoButton) + std::set key_codes; + + for (const std::string& key_name : combo) { - key_codes.insert(code); + if (u32 code = GetKeyCode(QString::fromStdString(key_name)); code != Qt::NoButton) + { + key_codes.insert(code); + } + } + + if (!key_codes.empty()) + { + res.push_back(std::move(key_codes)); } } - return key_codes; + + return res; } u32 keyboard_pad_handler::GetKeyCode(const QString& keyName) @@ -978,23 +1049,37 @@ bool keyboard_pad_handler::bindPadToDevice(std::shared_ptr pad) m_pressure_intensity_toggle_mode = cfg->pressure_intensity_toggle_mode.get(); m_pressure_intensity_deadzone = cfg->pressure_intensity_deadzone.get(); - const auto find_keys = [this](const cfg::string& name) + const auto find_combos = [this](const cfg::string& name) { - std::set keys = FindKeyCodes(mouse_list, name, false); - for (const u32& key : GetKeyCodes(name)) keys.insert(key); + std::vector> combos = find_key_combos(mouse_list, name, false); + for (const std::set& combo : GetKeyCombos(name)) combos.push_back(combo); - if (!keys.empty()) + if (!combos.empty()) { - if (!m_mouse_move_used && (keys.contains(mouse::move_left) || keys.contains(mouse::move_right) || keys.contains(mouse::move_up) || keys.contains(mouse::move_down))) + if (!m_mouse_move_used) { - m_mouse_move_used = true; + if (std::any_of(combos.cbegin(), combos.cend(), [](const std::set& keys) + { + return keys.contains(mouse::move_left) || keys.contains(mouse::move_right) || keys.contains(mouse::move_up) || keys.contains(mouse::move_down); + })) + { + m_mouse_move_used = true; + } } - else if (!m_mouse_wheel_used && (keys.contains(mouse::wheel_left) || keys.contains(mouse::wheel_right) || keys.contains(mouse::wheel_up) || keys.contains(mouse::wheel_down))) + + if (!m_mouse_wheel_used) { - m_mouse_wheel_used = true; + if (std::any_of(combos.cbegin(), combos.cend(), [](const std::set& keys) + { + return keys.contains(mouse::wheel_left) || keys.contains(mouse::wheel_right) || keys.contains(mouse::wheel_up) || keys.contains(mouse::wheel_down); + })) + { + m_mouse_wheel_used = true; + } } } - return keys; + + return combos; }; u32 pclass_profile = 0x0; @@ -1022,48 +1107,48 @@ bool keyboard_pad_handler::bindPadToDevice(std::shared_ptr pad) 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_buttons.emplace_back(special_button_offset, find_combos(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_buttons.emplace_back(special_button_offset, find_combos(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); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, find_keys(cfg->right), CELL_PAD_CTRL_RIGHT); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, find_keys(cfg->up), CELL_PAD_CTRL_UP); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, find_keys(cfg->start), CELL_PAD_CTRL_START); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, find_keys(cfg->r3), CELL_PAD_CTRL_R3); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, find_keys(cfg->l3), CELL_PAD_CTRL_L3); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, find_keys(cfg->select), CELL_PAD_CTRL_SELECT); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, find_keys(cfg->ps), CELL_PAD_CTRL_PS); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, find_keys(cfg->square), CELL_PAD_CTRL_SQUARE); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, find_keys(cfg->cross), CELL_PAD_CTRL_CROSS); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, find_keys(cfg->circle), CELL_PAD_CTRL_CIRCLE); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, find_keys(cfg->triangle), CELL_PAD_CTRL_TRIANGLE); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, find_keys(cfg->r1), CELL_PAD_CTRL_R1); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, find_keys(cfg->l1), CELL_PAD_CTRL_L1); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, find_keys(cfg->r2), CELL_PAD_CTRL_R2); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, find_keys(cfg->l2), CELL_PAD_CTRL_L2); + pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, find_combos(cfg->left), CELL_PAD_CTRL_LEFT); + pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, find_combos(cfg->down), CELL_PAD_CTRL_DOWN); + pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, find_combos(cfg->right), CELL_PAD_CTRL_RIGHT); + pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, find_combos(cfg->up), CELL_PAD_CTRL_UP); + pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, find_combos(cfg->start), CELL_PAD_CTRL_START); + pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, find_combos(cfg->r3), CELL_PAD_CTRL_R3); + pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, find_combos(cfg->l3), CELL_PAD_CTRL_L3); + pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, find_combos(cfg->select), CELL_PAD_CTRL_SELECT); + pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, find_combos(cfg->ps), CELL_PAD_CTRL_PS); + pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, find_combos(cfg->square), CELL_PAD_CTRL_SQUARE); + pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, find_combos(cfg->cross), CELL_PAD_CTRL_CROSS); + pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, find_combos(cfg->circle), CELL_PAD_CTRL_CIRCLE); + pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, find_combos(cfg->triangle), CELL_PAD_CTRL_TRIANGLE); + pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, find_combos(cfg->r1), CELL_PAD_CTRL_R1); + pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, find_combos(cfg->l1), CELL_PAD_CTRL_L1); + pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, find_combos(cfg->r2), CELL_PAD_CTRL_R2); + pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, find_combos(cfg->l2), CELL_PAD_CTRL_L2); if (pad->m_class_type == CELL_PAD_PCLASS_TYPE_SKATEBOARD) { - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK, find_keys(cfg->ir_nose), CELL_PAD_CTRL_PRESS_TRIANGLE); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK, find_keys(cfg->ir_tail), CELL_PAD_CTRL_PRESS_CIRCLE); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK, find_keys(cfg->ir_left), CELL_PAD_CTRL_PRESS_CROSS); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK, find_keys(cfg->ir_right), CELL_PAD_CTRL_PRESS_SQUARE); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK, find_keys(cfg->tilt_left), CELL_PAD_CTRL_PRESS_L1); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK, find_keys(cfg->tilt_right), CELL_PAD_CTRL_PRESS_R1); + pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK, find_combos(cfg->ir_nose), CELL_PAD_CTRL_PRESS_TRIANGLE); + pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK, find_combos(cfg->ir_tail), CELL_PAD_CTRL_PRESS_CIRCLE); + pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK, find_combos(cfg->ir_left), CELL_PAD_CTRL_PRESS_CROSS); + pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK, find_combos(cfg->ir_right), CELL_PAD_CTRL_PRESS_SQUARE); + pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK, find_combos(cfg->tilt_left), CELL_PAD_CTRL_PRESS_L1); + pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK, find_combos(cfg->tilt_right), CELL_PAD_CTRL_PRESS_R1); } - pad->m_sticks[0] = AnalogStick(CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X, find_keys(cfg->ls_left), find_keys(cfg->ls_right)); - pad->m_sticks[1] = AnalogStick(CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y, find_keys(cfg->ls_up), find_keys(cfg->ls_down)); - pad->m_sticks[2] = AnalogStick(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X, find_keys(cfg->rs_left), find_keys(cfg->rs_right)); - pad->m_sticks[3] = AnalogStick(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y, find_keys(cfg->rs_up), find_keys(cfg->rs_down)); + pad->m_sticks[0] = AnalogStick(CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X, find_combos(cfg->ls_left), find_combos(cfg->ls_right)); + pad->m_sticks[1] = AnalogStick(CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y, find_combos(cfg->ls_up), find_combos(cfg->ls_down)); + pad->m_sticks[2] = AnalogStick(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X, find_combos(cfg->rs_left), find_combos(cfg->rs_right)); + pad->m_sticks[3] = AnalogStick(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y, find_combos(cfg->rs_up), find_combos(cfg->rs_down)); pad->m_sensors[0] = AnalogSensor(CELL_PAD_BTN_OFFSET_SENSOR_X, 0, 0, 0, DEFAULT_MOTION_X); pad->m_sensors[1] = AnalogSensor(CELL_PAD_BTN_OFFSET_SENSOR_Y, 0, 0, 0, DEFAULT_MOTION_Y); diff --git a/rpcs3/Input/keyboard_pad_handler.h b/rpcs3/Input/keyboard_pad_handler.h index 855f9709ec..74c973f388 100644 --- a/rpcs3/Input/keyboard_pad_handler.h +++ b/rpcs3/Input/keyboard_pad_handler.h @@ -93,7 +93,7 @@ public: static QStringList GetKeyNames(const QKeyEvent* keyEvent); static std::string GetKeyName(const QKeyEvent* keyEvent, bool with_modifiers); static std::string GetKeyName(const u32& keyCode); - static std::set GetKeyCodes(const cfg::string& cfg_string); + static std::vector> GetKeyCombos(const cfg::string& cfg_string); static u32 GetKeyCode(const QString& keyName); static int native_scan_code_from_string(const std::string& key); diff --git a/rpcs3/Input/mm_joystick_handler.cpp b/rpcs3/Input/mm_joystick_handler.cpp index 8861f74b0d..14b28a5fc5 100644 --- a/rpcs3/Input/mm_joystick_handler.cpp +++ b/rpcs3/Input/mm_joystick_handler.cpp @@ -145,58 +145,68 @@ std::vector mm_joystick_handler::list_devices() return devices; } -std::set mm_joystick_handler::find_keys(const cfg::string& cfg_string) const +std::vector> mm_joystick_handler::find_combos(const cfg::string& cfg_string) const { - return find_keys(cfg_pad::get_buttons(cfg_string.to_string())); + return find_combos(cfg_pad::get_buttons(cfg_string.to_string())); } -std::set mm_joystick_handler::find_keys(const std::vector& names) const +std::vector> mm_joystick_handler::find_combos(const std::vector>& combos) const { - std::set keys; + std::vector> res; - for (u32 k : FindKeyCodes(axis_list, names)) keys.insert(k); - for (u32 k : FindKeyCodes(pov_list, names)) keys.insert(k); - for (u32 k : FindKeyCodes(button_list, names)) keys.insert(k); + for (const std::vector& combo : combos) + { + std::set keys; - return keys; + for (u32 k : find_key_codes(axis_list, combo)) keys.insert(k); + for (u32 k : find_key_codes(pov_list, combo)) keys.insert(k); + for (u32 k : find_key_codes(button_list, combo)) keys.insert(k); + + if (!keys.empty()) + { + res.push_back(std::move(keys)); + } + } + + return res; } -std::array, PadHandlerBase::button::button_count> mm_joystick_handler::get_mapped_key_codes(const std::shared_ptr& device, const cfg_pad* cfg) +std::array>, PadHandlerBase::button::button_count> mm_joystick_handler::get_mapped_key_codes(const std::shared_ptr& device, const cfg_pad* cfg) { - std::array, button::button_count> mapping{}; + std::array>, button::button_count> mapping{}; MMJOYDevice* dev = static_cast(device.get()); if (!dev || !cfg) return mapping; - dev->trigger_code_left = find_keys(cfg->l2); - dev->trigger_code_right = find_keys(cfg->r2); - dev->axis_code_left[0] = find_keys(cfg->ls_left); - dev->axis_code_left[1] = find_keys(cfg->ls_right); - dev->axis_code_left[2] = find_keys(cfg->ls_down); - dev->axis_code_left[3] = find_keys(cfg->ls_up); - dev->axis_code_right[0] = find_keys(cfg->rs_left); - dev->axis_code_right[1] = find_keys(cfg->rs_right); - dev->axis_code_right[2] = find_keys(cfg->rs_down); - dev->axis_code_right[3] = find_keys(cfg->rs_up); + dev->trigger_code_left = find_combos(cfg->l2); + dev->trigger_code_right = find_combos(cfg->r2); + dev->axis_code_left[0] = find_combos(cfg->ls_left); + dev->axis_code_left[1] = find_combos(cfg->ls_right); + dev->axis_code_left[2] = find_combos(cfg->ls_down); + dev->axis_code_left[3] = find_combos(cfg->ls_up); + dev->axis_code_right[0] = find_combos(cfg->rs_left); + dev->axis_code_right[1] = find_combos(cfg->rs_right); + dev->axis_code_right[2] = find_combos(cfg->rs_down); + dev->axis_code_right[3] = find_combos(cfg->rs_up); - mapping[button::up] = find_keys(cfg->up); - mapping[button::down] = find_keys(cfg->down); - mapping[button::left] = find_keys(cfg->left); - mapping[button::right] = find_keys(cfg->right); - mapping[button::cross] = find_keys(cfg->cross); - mapping[button::square] = find_keys(cfg->square); - mapping[button::circle] = find_keys(cfg->circle); - mapping[button::triangle] = find_keys(cfg->triangle); - mapping[button::l1] = find_keys(cfg->l1); + mapping[button::up] = find_combos(cfg->up); + mapping[button::down] = find_combos(cfg->down); + mapping[button::left] = find_combos(cfg->left); + mapping[button::right] = find_combos(cfg->right); + mapping[button::cross] = find_combos(cfg->cross); + mapping[button::square] = find_combos(cfg->square); + mapping[button::circle] = find_combos(cfg->circle); + mapping[button::triangle] = find_combos(cfg->triangle); + mapping[button::l1] = find_combos(cfg->l1); mapping[button::l2] = dev->trigger_code_left; - mapping[button::l3] = find_keys(cfg->l3); - mapping[button::r1] = find_keys(cfg->r1); + mapping[button::l3] = find_combos(cfg->l3); + mapping[button::r1] = find_combos(cfg->r1); mapping[button::r2] = dev->trigger_code_right; - mapping[button::r3] = find_keys(cfg->r3); - mapping[button::start] = find_keys(cfg->start); - mapping[button::select] = find_keys(cfg->select); - mapping[button::ps] = find_keys(cfg->ps); + mapping[button::r3] = find_combos(cfg->r3); + mapping[button::start] = find_combos(cfg->start); + mapping[button::select] = find_combos(cfg->select); + mapping[button::ps] = find_combos(cfg->ps); mapping[button::ls_left] = dev->axis_code_left[0]; mapping[button::ls_right] = dev->axis_code_left[1]; mapping[button::ls_down] = dev->axis_code_left[2]; @@ -206,21 +216,21 @@ std::array, PadHandlerBase::button::button_count> mm_joystick_hand mapping[button::rs_down] = dev->axis_code_right[2]; mapping[button::rs_up] = dev->axis_code_right[3]; - mapping[button::skateboard_ir_nose] = find_keys(cfg->ir_nose); - mapping[button::skateboard_ir_tail] = find_keys(cfg->ir_tail); - mapping[button::skateboard_ir_left] = find_keys(cfg->ir_left); - mapping[button::skateboard_ir_right] = find_keys(cfg->ir_right); - mapping[button::skateboard_tilt_left] = find_keys(cfg->tilt_left); - mapping[button::skateboard_tilt_right] = find_keys(cfg->tilt_right); + mapping[button::skateboard_ir_nose] = find_combos(cfg->ir_nose); + mapping[button::skateboard_ir_tail] = find_combos(cfg->ir_tail); + mapping[button::skateboard_ir_left] = find_combos(cfg->ir_left); + mapping[button::skateboard_ir_right] = find_combos(cfg->ir_right); + mapping[button::skateboard_tilt_left] = find_combos(cfg->tilt_left); + mapping[button::skateboard_tilt_right] = find_combos(cfg->tilt_right); if (b_has_pressure_intensity_button) { - mapping[button::pressure_intensity_button] = find_keys(cfg->pressure_intensity_button); + mapping[button::pressure_intensity_button] = find_combos(cfg->pressure_intensity_button); } if (b_has_analog_limiter_button) { - mapping[button::analog_limiter_button] = find_keys(cfg->analog_limiter_button); + mapping[button::analog_limiter_button] = find_combos(cfg->analog_limiter_button); } return mapping; @@ -362,14 +372,38 @@ PadHandlerBase::connection mm_joystick_handler::get_next_button_press(const std: { const auto get_key_value = [this, &data](const std::string& str) -> u16 { + bool pressed{}; u16 value{}; - for (u32 key_code : find_keys(cfg_pad::get_buttons(str))) + + // The DS3 Button is considered pressed if any configured button combination is pressed + for (const std::set& codes : find_combos(cfg_pad::get_buttons(str))) { - if (const auto it = data.find(key_code); it != data.cend()) + bool combo_pressed = !codes.empty(); + u16 combo_val = 0; + + // The button combination is only considered pressed if all the buttons are pressed + for (u32 code : codes) { - value = std::max(value, it->second); + if (const auto it = data.find(code); it != data.cend()) + { + if (it->second == 0) + { + combo_pressed = false; + break; + } + + // Take minimum combo value. Otherwise we will always end up with the max value in case an actual button is part of the combo. + combo_val = (combo_val == 0) ? it->second : std::min(combo_val, it->second); + } + } + + if (combo_pressed) + { + value = std::max(value, combo_val); + pressed = value > 0; } } + return value; }; preview_values[0] = get_key_value(buttons[0]); @@ -579,25 +613,25 @@ std::shared_ptr mm_joystick_handler::get_device(const std::string& de bool mm_joystick_handler::get_is_left_trigger(const std::shared_ptr& device, u32 keyCode) { const MMJOYDevice* dev = static_cast(device.get()); - return dev && dev->trigger_code_left.contains(keyCode); + return dev && std::any_of(dev->trigger_code_left.cbegin(), dev->trigger_code_left.cend(), [keyCode](const std::set& combo) { return combo.contains(keyCode); }); } bool mm_joystick_handler::get_is_right_trigger(const std::shared_ptr& device, u32 keyCode) { const MMJOYDevice* dev = static_cast(device.get()); - return dev && dev->trigger_code_right.contains(keyCode); + return dev && std::any_of(dev->trigger_code_right.cbegin(), dev->trigger_code_right.cend(), [keyCode](const std::set& combo) { return combo.contains(keyCode); }); } bool mm_joystick_handler::get_is_left_stick(const std::shared_ptr& device, u32 keyCode) { const MMJOYDevice* dev = static_cast(device.get()); - return dev && std::any_of(dev->axis_code_left.cbegin(), dev->axis_code_left.cend(), [&keyCode](const std::set& s){ return s.contains(keyCode); }); + return dev && std::any_of(dev->axis_code_left.cbegin(), dev->axis_code_left.cend(), [keyCode](const std::vector>& combos){ return std::any_of(combos.cbegin(), combos.cend(), [keyCode](const std::set& s){ return s.contains(keyCode); });}); } bool mm_joystick_handler::get_is_right_stick(const std::shared_ptr& device, u32 keyCode) { const MMJOYDevice* dev = static_cast(device.get()); - return dev && std::any_of(dev->axis_code_right.cbegin(), dev->axis_code_right.cend(), [&keyCode](const std::set& s){ return s.contains(keyCode); }); + return dev && std::any_of(dev->axis_code_right.cbegin(), dev->axis_code_right.cend(), [keyCode](const std::vector>& combos){ return std::any_of(combos.cbegin(), combos.cend(), [keyCode](const std::set& s){ return s.contains(keyCode); });}); } PadHandlerBase::connection mm_joystick_handler::update_connection(const std::shared_ptr& device) diff --git a/rpcs3/Input/mm_joystick_handler.h b/rpcs3/Input/mm_joystick_handler.h index e8cad683b3..f35758205e 100644 --- a/rpcs3/Input/mm_joystick_handler.h +++ b/rpcs3/Input/mm_joystick_handler.h @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -106,10 +107,10 @@ class mm_joystick_handler final : public PadHandlerBase JOYCAPS device_caps{}; MMRESULT device_status = JOYERR_UNPLUGGED; steady_clock::time_point last_update{}; - std::set trigger_code_left{}; - std::set trigger_code_right{}; - std::array, 4> axis_code_left{}; - std::array, 4> axis_code_right{}; + std::vector> trigger_code_left{}; + std::vector> trigger_code_right{}; + std::array>, 4> axis_code_left{}; + std::array>, 4> axis_code_right{}; }; public: @@ -134,10 +135,10 @@ private: std::unordered_map m_min_button_values; std::map> m_devices; - std::set find_keys(const std::vector& names) const; - std::set find_keys(const cfg::string& cfg_string) const; + std::vector> find_combos(const std::vector>& combos) const; + std::vector> find_combos(const cfg::string& cfg_string) const; - std::array, PadHandlerBase::button::button_count> get_mapped_key_codes(const std::shared_ptr& device, const cfg_pad* cfg) override; + std::array>, PadHandlerBase::button::button_count> get_mapped_key_codes(const std::shared_ptr& device, const cfg_pad* cfg) override; std::shared_ptr get_device(const std::string& device) override; bool get_is_left_trigger(const std::shared_ptr& device, u32 keyCode) override; bool get_is_right_trigger(const std::shared_ptr& device, u32 keyCode) override; diff --git a/rpcs3/rpcs3qt/pad_settings_dialog.cpp b/rpcs3/rpcs3qt/pad_settings_dialog.cpp index 21be0db0af..84afc37a67 100644 --- a/rpcs3/rpcs3qt/pad_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/pad_settings_dialog.cpp @@ -50,17 +50,31 @@ inline bool CreateConfigFile(const QString& dir, const QString& name) return true; } -void pad_settings_dialog::pad_button::insert_key(const std::string& key, bool append_key) +void pad_settings_dialog::pad_button::insert_key(const std::string& key, binding_mode mode) { - std::vector buttons; - if (append_key) + std::vector> combos; + if (mode != binding_mode::single) { - buttons = cfg_pad::get_buttons(keys); + combos = cfg_pad::get_buttons(m_keys); } - buttons.push_back(key); - keys = cfg_pad::get_buttons(std::move(buttons)); - text = QString::fromStdString(keys).replace(",", ", "); + if (combos.empty() || mode != binding_mode::combo) + { + combos.push_back({key}); + } + else if (mode == binding_mode::combo) + { + combos.back().push_back(key); + } + + update(cfg_pad::get_buttons(combos)); +} + +void pad_settings_dialog::pad_button::update(const std::string& keys) +{ + m_keys = keys; + QString new_text = QString::fromStdString(keys); + m_text = new_text.replace(",", ", ").replace("&", " + "); } pad_settings_dialog::pad_settings_dialog(std::shared_ptr gui_settings, QWidget* parent, const GameInfo* game) @@ -546,7 +560,7 @@ void pad_settings_dialog::InitButtons() if (m_button_id > button_ids::id_pad_begin && m_button_id < button_ids::id_pad_end && m_button_id == values.button_id) { - m_cfg_entries[m_button_id].insert_key(values.button_name, m_enable_multi_binding); + m_cfg_entries[m_button_id].insert_key(values.button_name, m_binding_mode); ReactivateButtons(); } } @@ -580,16 +594,16 @@ void pad_settings_dialog::InitButtons() const std::vector buttons = { - m_cfg_entries[button_ids::id_pad_l2].keys, - m_cfg_entries[button_ids::id_pad_r2].keys, - m_cfg_entries[button_ids::id_pad_lstick_left].keys, - m_cfg_entries[button_ids::id_pad_lstick_right].keys, - m_cfg_entries[button_ids::id_pad_lstick_down].keys, - m_cfg_entries[button_ids::id_pad_lstick_up].keys, - m_cfg_entries[button_ids::id_pad_rstick_left].keys, - m_cfg_entries[button_ids::id_pad_rstick_right].keys, - m_cfg_entries[button_ids::id_pad_rstick_down].keys, - m_cfg_entries[button_ids::id_pad_rstick_up].keys + m_cfg_entries[button_ids::id_pad_l2].keys(), + m_cfg_entries[button_ids::id_pad_r2].keys(), + m_cfg_entries[button_ids::id_pad_lstick_left].keys(), + m_cfg_entries[button_ids::id_pad_lstick_right].keys(), + m_cfg_entries[button_ids::id_pad_lstick_down].keys(), + m_cfg_entries[button_ids::id_pad_lstick_up].keys(), + m_cfg_entries[button_ids::id_pad_rstick_left].keys(), + m_cfg_entries[button_ids::id_pad_rstick_right].keys(), + m_cfg_entries[button_ids::id_pad_rstick_down].keys(), + m_cfg_entries[button_ids::id_pad_rstick_up].keys() }; // Check if this is the first call during a remap @@ -723,10 +737,10 @@ void pad_settings_dialog::ReloadButtons() { m_cfg_entries.clear(); - auto updateButton = [this](int id, QPushButton* button, cfg::string* cfg_text) + const auto updateButton = [this](int id, QPushButton* button, cfg::string* cfg_text) { const QString text = QString::fromStdString(*cfg_text); - m_cfg_entries.insert(std::make_pair(id, pad_button{cfg_text, *cfg_text, text})); + m_cfg_entries.insert(std::make_pair(id, pad_button(cfg_text))); button->setText(text); }; @@ -775,7 +789,7 @@ void pad_settings_dialog::ReactivateButtons() { m_remap_timer.stop(); m_seconds = MAX_SECONDS; - m_enable_multi_binding = false; + m_binding_mode = binding_mode::single; if (m_button_id == button_ids::id_pad_begin) { @@ -929,7 +943,7 @@ void pad_settings_dialog::keyPressEvent(QKeyEvent *keyEvent) } else { - m_cfg_entries[m_button_id].insert_key(keyboard_pad_handler::GetKeyName(keyEvent, false), m_enable_multi_binding); + m_cfg_entries[m_button_id].insert_key(keyboard_pad_handler::GetKeyName(keyEvent, false), m_binding_mode); } ReactivateButtons(); @@ -956,7 +970,7 @@ void pad_settings_dialog::mouseReleaseEvent(QMouseEvent* event) } else { - m_cfg_entries[m_button_id].insert_key((static_cast(m_handler.get()))->GetMouseName(event), m_enable_multi_binding); + m_cfg_entries[m_button_id].insert_key((static_cast(m_handler.get()))->GetMouseName(event), m_binding_mode); } ReactivateButtons(); @@ -1018,7 +1032,7 @@ void pad_settings_dialog::wheelEvent(QWheelEvent *event) } } - m_cfg_entries[m_button_id].insert_key((static_cast(m_handler.get()))->GetMouseName(key), m_enable_multi_binding); + m_cfg_entries[m_button_id].insert_key((static_cast(m_handler.get()))->GetMouseName(key), m_binding_mode); ReactivateButtons(); } @@ -1069,7 +1083,7 @@ void pad_settings_dialog::mouseMoveEvent(QMouseEvent* event) if (key != 0) { - m_cfg_entries[m_button_id].insert_key((static_cast(m_handler.get()))->GetMouseName(key), m_enable_multi_binding); + m_cfg_entries[m_button_id].insert_key((static_cast(m_handler.get()))->GetMouseName(key), m_binding_mode); ReactivateButtons(); } } @@ -1090,8 +1104,7 @@ bool pad_settings_dialog::eventFilter(QObject* object, QEvent* event) if (const int button_id = m_pad_buttons->id(button); m_cfg_entries.contains(button_id)) { pad_button& button = m_cfg_entries[button_id]; - button.keys.clear(); - button.text.clear(); + button.update(""); UpdateLabels(); return true; @@ -1279,14 +1292,13 @@ void pad_settings_dialog::UpdateLabels(bool is_reset) { if (is_reset) { - button.keys = *button.cfg_text; - button.text = QString::fromStdString(button.keys); + button.update(*button.cfg_text()); } // The button has to contain at least one character, because it would be square'ish otherwise if (auto btn = m_pad_buttons->button(id)) { - btn->setText(button.text.isEmpty() ? QStringLiteral("-") : button.text); + btn->setText(button.text().isEmpty() ? QStringLiteral("-") : button.text()); } } } @@ -1358,7 +1370,11 @@ void pad_settings_dialog::OnPadButtonClicked(int id) // On shift+click or shift+space enable multi key binding if (QApplication::keyboardModifiers() & Qt::KeyboardModifier::ShiftModifier) { - m_enable_multi_binding = true; + m_binding_mode = binding_mode::multi; + } + else if (QApplication::keyboardModifiers() & Qt::KeyboardModifier::ControlModifier) + { + m_binding_mode = binding_mode::combo; } // On alt+click or alt+space allow to handle triggers as the entire stick axis @@ -2024,12 +2040,15 @@ void pad_settings_dialog::ApplyCurrentPlayerConfig(int new_player_id) continue; } - for (const std::string& key : cfg_pad::get_buttons(button.keys)) + for (const std::vector& combo : cfg_pad::get_buttons(button.keys())) { - if (const auto& [it, ok] = unique_keys.insert(key); !ok) + for (const std::string& key : combo) { - m_duplicate_buttons[m_last_player_id] = key; - break; + if (const auto& [it, ok] = unique_keys.insert(key); !ok) + { + m_duplicate_buttons[m_last_player_id] = key; + break; + } } } } @@ -2038,7 +2057,7 @@ void pad_settings_dialog::ApplyCurrentPlayerConfig(int new_player_id) // Apply buttons for (const auto& entry : m_cfg_entries) { - entry.second.cfg_text->from_string(entry.second.keys); + entry.second.cfg_text()->from_string(entry.second.keys()); } // Apply rest of config diff --git a/rpcs3/rpcs3qt/pad_settings_dialog.h b/rpcs3/rpcs3qt/pad_settings_dialog.h index 0e31b0a297..02b4cd6e79 100644 --- a/rpcs3/rpcs3qt/pad_settings_dialog.h +++ b/rpcs3/rpcs3qt/pad_settings_dialog.h @@ -78,13 +78,32 @@ class pad_settings_dialog : public QDialog id_remove_config_file }; + enum class binding_mode + { + single, + multi, + combo + }; + struct pad_button { - cfg::string* cfg_text = nullptr; - std::string keys; - QString text; + pad_button() {} + pad_button(cfg::string* cfg_text) : m_cfg_text(ensure(cfg_text)) + { + update(*cfg_text); + } - void insert_key(const std::string& key, bool append_key); + void insert_key(const std::string& key, binding_mode mode); + void update(const std::string& keys); + + cfg::string* cfg_text() const { return m_cfg_text; } + const std::string& keys() const { return m_keys; } + const QString& text() const { return m_text; } + + private: + cfg::string* m_cfg_text = nullptr; + std::string m_keys; + QString m_text; }; const QString Disconnected_suffix = tr(" (disconnected)"); @@ -161,7 +180,7 @@ private: static constexpr int MAX_SECONDS = 5; int m_seconds = MAX_SECONDS; QTimer m_remap_timer; - bool m_enable_multi_binding = false; + binding_mode m_binding_mode = binding_mode::single; // Mouse Move QPoint m_last_pos; diff --git a/rpcs3/rpcs3qt/tooltips.h b/rpcs3/rpcs3qt/tooltips.h index 6152506c1d..6f07c531a2 100644 --- a/rpcs3/rpcs3qt/tooltips.h +++ b/rpcs3/rpcs3qt/tooltips.h @@ -312,7 +312,7 @@ public: const QString mouse_deadzones = tr("The mouse deadzones represent the games' own deadzones on the x and y axes. Games usually enforce their own deadzones to filter out small unwanted stick movements. In consequence, mouse input feels unintuitive since it relies on immediate responsiveness. You can change these values temporarily during gameplay in order to find out the optimal values for your game (Alt+T and Alt+Y for x, Alt+U and Alt+I for y)."); const QString mouse_acceleration = tr("The mouse acceleration can be used to amplify your mouse movements on the x and y axes. Increase these values if your mouse movements feel too slow while playing a game. You can change these values temporarily during gameplay in order to find out the optimal values (Alt+G and Alt+H for x, Alt+J and Alt+K for y). Keep in mind that modern mice usually provide different modes and settings that can be used to change mouse movement speeds as well."); const QString mouse_movement = tr("The mouse movement mode determines how the mouse movement is translated to pad input.
Use the relative mode for traditional mouse movement.
Use the absolute mode to use the mouse's distance to the center of the screen as input value."); - const QString button_assignment = tr("Left-click: remap this button.
Shift + Left-click: add an additional button mapping.
Alt + Left-click: differentiate between trigger press and release (only XInput for now).
Right-click: clear this button mapping."); + const QString button_assignment = tr("Left-click: remap this button.
Shift + Left-click: add an additional button mapping.
Ctrl + Left-click: Create a combo by adding a button to the last mapping.
Alt + Left-click: differentiate between trigger press and release (only XInput for now).
Right-click: clear this button mapping."); } gamepad_settings; };