input: implement button combos

This commit is contained in:
Megamouse 2026-03-15 11:01:58 +01:00
parent 2eba7da8e6
commit 22fe14d31d
15 changed files with 672 additions and 373 deletions

View file

@ -11,25 +11,35 @@ PadHandlerBase::PadHandlerBase(pad_handler type) : m_type(type)
{
}
std::set<u32> PadHandlerBase::FindKeyCodes(const std::unordered_map<u32, std::string>& map, const cfg::string& cfg_string, bool fallback)
std::vector<std::set<u32>> PadHandlerBase::find_key_combos(const std::unordered_map<u32, std::string>& map, const cfg::string& cfg_string, bool fallback)
{
std::set<u32> key_codes;
std::vector<std::set<u32>> key_codes;
const std::string& def = cfg_string.def;
const std::vector<std::string> names = cfg_pad::get_buttons(cfg_string.to_string());
const std::vector<std::vector<std::string>> combos = cfg_pad::get_buttons(cfg_string.to_string());
u32 def_code = umax;
for (const std::string& nam : names)
for (const std::vector<std::string>& names : combos)
{
for (const auto& [code, name] : map)
{
if (name == nam)
{
key_codes.insert(code);
}
std::set<u32> 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<u32> PadHandlerBase::FindKeyCodes(const std::unordered_map<u32, std::st
if (fallback)
{
if (!names.empty())
if (!combos.empty())
input_log.error("FindKeyCode for [name = %s] returned with [def_code = %d] for [def = %s]", cfg_string.to_string(), def_code, def);
if (def_code != umax)
{
return { def_code };
return {{ def_code }};
}
}
return {};
}
std::set<u32> PadHandlerBase::FindKeyCodes(const std::unordered_map<u32, std::string>& map, const std::vector<std::string>& names)
std::set<u32> PadHandlerBase::find_key_codes(const std::unordered_map<u32, std::string>& map, const std::vector<std::string>& names)
{
std::set<u32> key_codes;
@ -512,7 +522,7 @@ bool PadHandlerBase::bindPadToDevice(std::shared_ptr<Pad> pad)
return false;
}
std::array<std::set<u32>, button::button_count> mapping = get_mapped_key_codes(pad_device, config);
std::array<std::vector<std::set<u32>>, 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> pad)
return true;
}
std::array<std::set<u32>, PadHandlerBase::button::button_count> PadHandlerBase::get_mapped_key_codes(const std::shared_ptr<PadDevice>& device, const cfg_pad* cfg)
std::array<std::vector<std::set<u32>>, PadHandlerBase::button::button_count> PadHandlerBase::get_mapped_key_codes(const std::shared_ptr<PadDevice>& device, const cfg_pad* cfg)
{
std::array<std::set<u32>, button::button_count> mapping{};
std::array<std::vector<std::set<u32>>, 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<u32>& 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<std::set<u32>>& 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<u32>& 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;

View file

@ -192,11 +192,11 @@ protected:
std::shared_ptr<Pad> m_pad_for_pad_settings;
// Search an unordered map for a string value and return found keycode
static std::set<u32> FindKeyCodes(const std::unordered_map<u32, std::string>& 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<std::set<u32>> find_key_combos(const std::unordered_map<u32, std::string>& map, const cfg::string& cfg_string, bool fallback = true);
// Search an unordered map for a string value and return found keycode
static std::set<u32> FindKeyCodes(const std::unordered_map<u32, std::string>& map, const std::vector<std::string>& names);
// Search an unordered map for string values and return the found key codes
static std::set<u32> find_key_codes(const std::unordered_map<u32, std::string>& map, const std::vector<std::string>& 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<std::set<u32>, PadHandlerBase::button::button_count> get_mapped_key_codes(const std::shared_ptr<PadDevice>& device, const cfg_pad* cfg);
virtual std::array<std::vector<std::set<u32>>, PadHandlerBase::button::button_count> get_mapped_key_codes(const std::shared_ptr<PadDevice>& device, const cfg_pad* cfg);
virtual void get_mapping(const pad_ensemble& binding);
void TranslateButtonPress(const std::shared_ptr<PadDevice>& device, u32 keyCode, bool& pressed, u16& val, bool use_stick_multipliers, bool ignore_stick_threshold = false, bool ignore_trigger_threshold = false);
void init_configs();

View file

@ -5,30 +5,89 @@
extern std::string g_input_config_override;
std::vector<std::string> cfg_pad::get_buttons(std::string_view str)
std::vector<std::vector<std::string>> cfg_pad::get_buttons(std::string_view str)
{
std::vector<std::string> 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<std::string> 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<std::vector<std::string>> res;
// Get all combos (seperated by ',')
const std::vector<std::string> vec = split(str, ',');
for (const std::string& combo : vec)
{
// Get all keys for this combo (seperated by '&')
std::vector<std::string> 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<std::string> vec)
std::string cfg_pad::get_buttons(std::vector<std::vector<std::string>> vec)
{
std::vector<std::string> 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<std::string>& 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

View file

@ -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<std::string> get_buttons(std::string_view str);
static std::string get_buttons(std::vector<std::string> vec);
static std::vector<std::vector<std::string>> get_buttons(std::string_view str);
static std::string get_buttons(std::vector<std::vector<std::string>> vec);
u8 get_motor_speed(VibrateMotor& motor, f32 multiplier) const;
u8 get_large_motor_speed(std::array<VibrateMotor, 2>& motors) const;

View file

@ -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;

View file

@ -386,18 +386,18 @@ struct Button
u16 m_value = 0;
bool m_pressed = false;
std::set<u32> m_key_codes{};
std::vector<std::set<u32>> 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<u32, u16> 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<u32, u16> m_pressed_keys; // only used in keyboard_pad_handler
Button(){}
Button(u32 offset, std::set<u32> key_codes, u32 outKeyCode)
Button(u32 offset, std::vector<std::set<u32>> 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<u32> m_key_codes_min{};
std::set<u32> m_key_codes_max{};
std::vector<std::set<u32>> m_key_combos_min;
std::vector<std::set<u32>> m_key_combos_max;
std::map<u32, u16> m_pressed_keys_min{}; // only used in keyboard_pad_handler
std::map<u32, u16> m_pressed_keys_max{}; // only used in keyboard_pad_handler
std::map<u32, u16> m_pressed_keys_min; // only used in keyboard_pad_handler
std::map<u32, u16> m_pressed_keys_max; // only used in keyboard_pad_handler
std::map<u32, u16> m_pressed_combos_min; // only used in keyboard_pad_handler
std::map<u32, u16> m_pressed_combos_max; // only used in keyboard_pad_handler
AnalogStick() {}
AnalogStick(u32 offset, std::set<u32> key_codes_min, std::set<u32> key_codes_max)
AnalogStick(u32 offset, std::vector<std::set<u32>> key_combos_min, std::vector<std::set<u32>> 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))
{}
};

View file

@ -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<std::string> names = cfg_pad::get_buttons(str);
const std::vector<std::vector<std::string>> 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<std::string>& 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>& pad)
}
};
const auto process_mapped_combo = [&](const std::vector<std::set<u32>>& combos, bool is_stick_value)
{
bool pressed{};
u16 value = 0;
for (const std::set<u32>& 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<bool, u16> 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>& 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<bool, u16> 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<bool, u16> 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> pad)
return index;
};
const auto find_buttons = [&](const cfg::string& name) -> std::set<u32>
const auto find_buttons = [&](const cfg::string& name) -> std::vector<std::set<u32>>
{
const std::vector<std::string> names = cfg_pad::get_buttons(name.to_string());
const std::vector<std::vector<std::string>> 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<u32> indices;
std::vector<std::set<u32>> combos;
for (const u32 code : FindKeyCodes(axis_list, names))
for (const std::vector<std::string>& names : str_combos)
{
indices.insert(register_evdevbutton(code, true, false));
std::set<u32> 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> pad)
e_sensor.mirrored = sensor.mirrored.get();
e_sensor.shift = sensor.shift.get();
const std::set<u32> 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<std::set<u32>> 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)
pad->m_orientation_reset_button_index = static_cast<s32>(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<u32>& indices, cons
return false;
}
bool evdev_joystick_handler::check_button_sets(const std::array<std::set<u32>, 4>& sets, const u32 code)
bool evdev_joystick_handler::check_button_combos(const std::vector<std::set<u32>>& combos, const u32 code)
{
return std::any_of(sets.begin(), sets.end(), [this, code](const std::set<u32>& indices) { return check_button_set(indices, code); });
};
return std::any_of(combos.begin(), combos.end(), [this, code](const std::set<u32>& indices) { return check_button_set(indices, code); });
}
bool evdev_joystick_handler::check_button_combos(const std::array<std::vector<std::set<u32>>, 4>& combo_array, const u32 code)
{
return std::any_of(combo_array.begin(), combo_array.end(), [this, code](const std::vector<std::set<u32>>& combos) { return check_button_combos(combos, code); });
}
bool evdev_joystick_handler::get_is_left_trigger(const std::shared_ptr<PadDevice>& /*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<PadDevice>& /*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<PadDevice>& /*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<PadDevice>& /*device*/, u32 keyCode)
{
return check_button_sets(m_dev->axis_right, keyCode);
return check_button_combos(m_dev->axis_right, keyCode);
}
#endif

View file

@ -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<EvdevButton> all_buttons;
std::set<u32> trigger_left{};
std::set<u32> trigger_right{};
std::array<std::set<u32>, 4> axis_left{};
std::array<std::set<u32>, 4> axis_right{};
std::vector<std::set<u32>> trigger_left{};
std::vector<std::set<u32>> trigger_right{};
std::array<std::vector<std::set<u32>>, 4> axis_left{};
std::array<std::vector<std::set<u32>>, 4> axis_right{};
std::array<evdev_sensor, 4> axis_motion{};
std::map<u32, std::shared_ptr<input_event_wrapper>> events_by_code;
int cur_dir = 0;
@ -409,7 +409,8 @@ private:
std::shared_ptr<EvdevDevice> m_dev;
bool check_button_set(const std::set<u32>& indices, const u32 code);
bool check_button_sets(const std::array<std::set<u32>, 4>& sets, const u32 code);
bool check_button_combos(const std::vector<std::set<u32>>& combos, const u32 code);
bool check_button_combos(const std::array<std::vector<std::set<u32>>, 4>& combo_array, const u32 code);
void apply_input_events(const std::shared_ptr<Pad>& pad);

View file

@ -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<u32, u16>& 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<u32>& 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, &register_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<u32>& 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<s32>(i) == pad.m_pressure_intensity_button_index ||
static_cast<s32>(i) == pad.m_analog_limiter_button_index)
static_cast<s32>(i) == pad.m_analog_limiter_button_index ||
static_cast<s32>(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<u32>& 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<u32>& 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<u32>& 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<u32, u16>& pressed_keys, bool is_max)
const auto register_new_stick_value = [&](bool is_max)
{
const std::vector<std::set<u32>>& key_combos = is_max ? stick.m_key_combos_max : stick.m_key_combos_min;
std::map<u32, u16>& pressed_keys = is_max ? stick.m_pressed_keys_max : stick.m_pressed_keys_min;
std::map<u32, u16>& 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<u32>& 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<u16>(std::ceil(new_val / 2.0f)));
};
if (is_max)
{
register_new_stick_value(stick.m_pressed_keys_max, true);
const std::pair<bool, u16> 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<int>(128 + normalized_value, 255) : 128;
m_stick_max[i] = stick_value.first ? std::min<int>(128 + stick_value.second, 255) : 128;
}
if (is_min)
{
register_new_stick_value(stick.m_pressed_keys_min, false);
const std::pair<bool, u16> 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<u8>(normalized_value, 128) : 0;
m_stick_min[i] = stick_value.first ? std::min<u8>(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<u32> keyboard_pad_handler::GetKeyCodes(const cfg::string& cfg_string)
std::vector<std::set<u32>> keyboard_pad_handler::GetKeyCombos(const cfg::string& cfg_string)
{
std::set<u32> key_codes;
for (const std::string& key_name : cfg_pad::get_buttons(cfg_string.to_string()))
std::vector<std::set<u32>> res;
for (const std::vector<std::string>& combo : cfg_pad::get_buttons(cfg_string.to_string()))
{
if (u32 code = GetKeyCode(QString::fromStdString(key_name)); code != Qt::NoButton)
std::set<u32> 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> 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<u32> keys = FindKeyCodes(mouse_list, name, false);
for (const u32& key : GetKeyCodes(name)) keys.insert(key);
std::vector<std::set<u32>> combos = find_key_combos(mouse_list, name, false);
for (const std::set<u32>& 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<u32>& 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<u32>& 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> 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<s32>(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<s32>(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);

View file

@ -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<u32> GetKeyCodes(const cfg::string& cfg_string);
static std::vector<std::set<u32>> GetKeyCombos(const cfg::string& cfg_string);
static u32 GetKeyCode(const QString& keyName);
static int native_scan_code_from_string(const std::string& key);

View file

@ -145,58 +145,68 @@ std::vector<pad_list_entry> mm_joystick_handler::list_devices()
return devices;
}
std::set<u32> mm_joystick_handler::find_keys(const cfg::string& cfg_string) const
std::vector<std::set<u32>> 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<u32> mm_joystick_handler::find_keys(const std::vector<std::string>& names) const
std::vector<std::set<u32>> mm_joystick_handler::find_combos(const std::vector<std::vector<std::string>>& combos) const
{
std::set<u32> keys;
std::vector<std::set<u32>> 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<std::string>& combo : combos)
{
std::set<u32> 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<std::set<u32>, PadHandlerBase::button::button_count> mm_joystick_handler::get_mapped_key_codes(const std::shared_ptr<PadDevice>& device, const cfg_pad* cfg)
std::array<std::vector<std::set<u32>>, PadHandlerBase::button::button_count> mm_joystick_handler::get_mapped_key_codes(const std::shared_ptr<PadDevice>& device, const cfg_pad* cfg)
{
std::array<std::set<u32>, button::button_count> mapping{};
std::array<std::vector<std::set<u32>>, button::button_count> mapping{};
MMJOYDevice* dev = static_cast<MMJOYDevice*>(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<std::set<u32>, 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<u32>& 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<PadDevice> mm_joystick_handler::get_device(const std::string& de
bool mm_joystick_handler::get_is_left_trigger(const std::shared_ptr<PadDevice>& device, u32 keyCode)
{
const MMJOYDevice* dev = static_cast<MMJOYDevice*>(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<u32>& combo) { return combo.contains(keyCode); });
}
bool mm_joystick_handler::get_is_right_trigger(const std::shared_ptr<PadDevice>& device, u32 keyCode)
{
const MMJOYDevice* dev = static_cast<MMJOYDevice*>(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<u32>& combo) { return combo.contains(keyCode); });
}
bool mm_joystick_handler::get_is_left_stick(const std::shared_ptr<PadDevice>& device, u32 keyCode)
{
const MMJOYDevice* dev = static_cast<MMJOYDevice*>(device.get());
return dev && std::any_of(dev->axis_code_left.cbegin(), dev->axis_code_left.cend(), [&keyCode](const std::set<u32>& s){ return s.contains(keyCode); });
return dev && std::any_of(dev->axis_code_left.cbegin(), dev->axis_code_left.cend(), [keyCode](const std::vector<std::set<u32>>& combos){ return std::any_of(combos.cbegin(), combos.cend(), [keyCode](const std::set<u32>& s){ return s.contains(keyCode); });});
}
bool mm_joystick_handler::get_is_right_stick(const std::shared_ptr<PadDevice>& device, u32 keyCode)
{
const MMJOYDevice* dev = static_cast<MMJOYDevice*>(device.get());
return dev && std::any_of(dev->axis_code_right.cbegin(), dev->axis_code_right.cend(), [&keyCode](const std::set<u32>& s){ return s.contains(keyCode); });
return dev && std::any_of(dev->axis_code_right.cbegin(), dev->axis_code_right.cend(), [keyCode](const std::vector<std::set<u32>>& combos){ return std::any_of(combos.cbegin(), combos.cend(), [keyCode](const std::set<u32>& s){ return s.contains(keyCode); });});
}
PadHandlerBase::connection mm_joystick_handler::update_connection(const std::shared_ptr<PadDevice>& device)

View file

@ -10,6 +10,7 @@
#include <mmsystem.h>
#include <string>
#include <set>
#include <vector>
#include <memory>
#include <unordered_map>
@ -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<u32> trigger_code_left{};
std::set<u32> trigger_code_right{};
std::array<std::set<u32>, 4> axis_code_left{};
std::array<std::set<u32>, 4> axis_code_right{};
std::vector<std::set<u32>> trigger_code_left{};
std::vector<std::set<u32>> trigger_code_right{};
std::array<std::vector<std::set<u32>>, 4> axis_code_left{};
std::array<std::vector<std::set<u32>>, 4> axis_code_right{};
};
public:
@ -134,10 +135,10 @@ private:
std::unordered_map<u32, u16> m_min_button_values;
std::map<std::string, std::shared_ptr<MMJOYDevice>> m_devices;
std::set<u32> find_keys(const std::vector<std::string>& names) const;
std::set<u32> find_keys(const cfg::string& cfg_string) const;
std::vector<std::set<u32>> find_combos(const std::vector<std::vector<std::string>>& combos) const;
std::vector<std::set<u32>> find_combos(const cfg::string& cfg_string) const;
std::array<std::set<u32>, PadHandlerBase::button::button_count> get_mapped_key_codes(const std::shared_ptr<PadDevice>& device, const cfg_pad* cfg) override;
std::array<std::vector<std::set<u32>>, PadHandlerBase::button::button_count> get_mapped_key_codes(const std::shared_ptr<PadDevice>& device, const cfg_pad* cfg) override;
std::shared_ptr<PadDevice> get_device(const std::string& device) override;
bool get_is_left_trigger(const std::shared_ptr<PadDevice>& device, u32 keyCode) override;
bool get_is_right_trigger(const std::shared_ptr<PadDevice>& device, u32 keyCode) override;

View file

@ -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<std::string> buttons;
if (append_key)
std::vector<std::vector<std::string>> 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> 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<std::string> 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<keyboard_pad_handler*>(m_handler.get()))->GetMouseName(event), m_enable_multi_binding);
m_cfg_entries[m_button_id].insert_key((static_cast<keyboard_pad_handler*>(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<keyboard_pad_handler*>(m_handler.get()))->GetMouseName(key), m_enable_multi_binding);
m_cfg_entries[m_button_id].insert_key((static_cast<keyboard_pad_handler*>(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<keyboard_pad_handler*>(m_handler.get()))->GetMouseName(key), m_enable_multi_binding);
m_cfg_entries[m_button_id].insert_key((static_cast<keyboard_pad_handler*>(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<std::string>& 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

View file

@ -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;

View file

@ -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.<br>Use the relative mode for traditional mouse movement.<br>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.<br>Shift + Left-click: add an additional button mapping.<br>Alt + Left-click: differentiate between trigger press and release (only XInput for now).<br>Right-click: clear this button mapping.");
const QString button_assignment = tr("Left-click: remap this button.<br>Shift + Left-click: add an additional button mapping.<br>Ctrl + Left-click: Create a combo by adding a button to the last mapping.<br>Alt + Left-click: differentiate between trigger press and release (only XInput for now).<br>Right-click: clear this button mapping.");
} gamepad_settings;
};