input: refactor combos into a struct for future use

Also update some terminology in the code
This commit is contained in:
Megamouse 2026-03-17 21:30:18 +01:00
parent 6523afa69a
commit 704d8764af
9 changed files with 121 additions and 140 deletions

View File

@ -11,30 +11,15 @@ PadHandlerBase::PadHandlerBase(pad_handler type) : m_type(type)
{
}
std::vector<std::set<u32>> PadHandlerBase::find_key_combos(const std::unordered_map<u32, std::string>& map, const std::string& cfg_string, const std::string& fallback)
std::vector<std::set<u32>> PadHandlerBase::find_key_combos(const std::unordered_map<u32, std::string>& map, const std::string& cfg_string)
{
std::vector<std::set<u32>> key_codes;
const std::vector<std::vector<std::string>> combos = cfg_pad::get_buttons(cfg_string);
u32 def_code = umax;
const std::vector<pad::combo> combos = cfg_pad::get_combos(cfg_string);
for (const std::vector<std::string>& names : combos)
for (const pad::combo& combo : combos)
{
std::set<u32> keys;
for (const std::string& nam : names)
{
for (const auto& [code, name] : map)
{
if (name == nam)
{
keys.insert(code);
}
if (!fallback.empty() && name == fallback)
def_code = code;
}
}
std::set<u32> keys = find_key_codes(map, combo);
if (!keys.empty())
{
@ -42,39 +27,18 @@ std::vector<std::set<u32>> PadHandlerBase::find_key_combos(const std::unordered_
}
}
if (!key_codes.empty())
{
return key_codes;
}
if (!fallback.empty())
{
if (!combos.empty())
input_log.error("FindKeyCode for [name = %s] returned with [def_code = %d] for [fallback = %s]", cfg_string, def_code, fallback);
if (def_code != umax)
{
return {{ def_code }};
}
}
return {};
return key_codes;
}
std::vector<std::set<u32>> PadHandlerBase::find_key_combos(const std::unordered_map<u32, std::string>& map, const cfg::string& cfg_string, bool fallback)
{
return find_key_combos(map, cfg_string.to_string(), fallback ? cfg_string.def : "");
}
std::set<u32> PadHandlerBase::find_key_codes(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 pad::combo& combo)
{
std::set<u32> key_codes;
for (const std::string& name : names)
for (const std::string& button_name : combo.buttons())
{
for (const auto& [code, nam] : map)
for (const auto& [code, name] : map)
{
if (nam == name)
if (button_name == name)
{
key_codes.insert(code);
break;
@ -82,12 +46,7 @@ std::set<u32> PadHandlerBase::find_key_codes(const std::unordered_map<u32, std::
}
}
if (!key_codes.empty())
{
return key_codes;
}
return {};
return key_codes;
}
s32 PadHandlerBase::MultipliedInput(s32 raw_value, s32 multiplier)

View File

@ -192,14 +192,11 @@ protected:
std::shared_ptr<Pad> m_pad_for_pad_settings;
// 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 std::string& cfg_string, const std::string& fallback);
// Search an unordered map for a string value and return the found combos
static std::vector<std::set<u32>> find_key_combos(const std::unordered_map<u32, std::string>& map, const std::string& cfg_string);
// 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 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);
// Search an unordered map for a combo and return the found key codes
static std::set<u32> find_key_codes(const std::unordered_map<u32, std::string>& map, const pad::combo& combo);
// Get normalized trigger value based on the range defined by a threshold
u16 NormalizeTriggerInput(u16 value, u32 threshold) const;

View File

@ -5,15 +5,15 @@
extern std::string g_input_config_override;
std::vector<std::vector<std::string>> cfg_pad::get_buttons(std::string_view str)
std::vector<pad::combo> cfg_pad::get_combos(std::string_view button_string)
{
if (str.empty())
if (button_string.empty())
return {};
// 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)
{
std::vector<std::string> vec;
std::set<std::string> buttons;
bool was_sep = true;
usz btn_start = 0ULL;
usz i = 0ULL;
@ -27,7 +27,7 @@ std::vector<std::vector<std::string>> cfg_pad::get_buttons(std::string_view str)
if (!was_sep)
{
was_sep = true;
vec.push_back(std::string(str.substr(btn_start, i - btn_start)));
buttons.insert(std::string(str.substr(btn_start, i - btn_start)));
continue;
}
}
@ -40,54 +40,47 @@ std::vector<std::vector<std::string>> cfg_pad::get_buttons(std::string_view str)
if (i == (str.size() - 1))
{
vec.push_back(std::string(str.substr(btn_start, i - btn_start + 1)));
buttons.insert(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;
return buttons;
};
std::vector<std::vector<std::string>> res;
std::vector<pad::combo> combos;
// Get all combos (seperated by ',')
const std::vector<std::string> vec = split(str, ',');
const std::set<std::string> combo_strings = split(button_string, ',');
for (const std::string& combo : vec)
for (const std::string& combo_string : combo_strings)
{
// Get all keys for this combo (seperated by '&')
std::vector<std::string> keys = split(combo, '&');
if (!keys.empty())
std::set<std::string> combo = split(combo_string, '&');
if (!combo.empty())
{
res.push_back(std::move(keys));
combos.push_back(pad::combo{std::move(combo)});
}
}
return res;
return combos;
}
std::string cfg_pad::get_buttons(std::vector<std::vector<std::string>> vec)
std::string cfg_pad::get_button_string(std::vector<pad::combo>& combos)
{
std::vector<std::string> combos;
std::vector<std::string> combo_strings;
// Remove duplicates
std::sort(vec.begin(), vec.end());
vec.erase(std::unique(vec.begin(), vec.end()), vec.end());
std::sort(combos.begin(), combos.end());
combos.erase(std::unique(combos.begin(), combos.end()), combos.end());
for (std::vector<std::string>& combo : vec)
for (const pad::combo& combo : combos)
{
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, "&"));
combo_strings.push_back(fmt::merge(combo.buttons(), "&"));
}
// Merge combos (seperated by ',')
return fmt::merge(combos, ",");
return fmt::merge(combo_strings, ",");
}
u8 cfg_pad::get_motor_speed(VibrateMotor& motor, f32 multiplier) const

View File

@ -5,10 +5,42 @@
#include "Utilities/Config.h"
#include <array>
#include <vector>
namespace pad
{
constexpr static std::string_view keyboard_device_name = "Keyboard";
struct combo
{
public:
combo() = default;
combo(std::set<std::string> buttons) : m_buttons(std::move(buttons)) {}
const std::set<std::string>& buttons() const
{
return m_buttons;
}
void add_button(const std::string& button)
{
if (button.empty()) return;
m_buttons.insert(button);
}
bool operator==(const combo& other) const
{
return m_buttons == other.m_buttons;
}
bool operator<(const combo& other) const
{
return m_buttons < other.m_buttons;
}
private:
std::set<std::string> m_buttons;
};
}
struct cfg_sensor final : cfg::node
@ -25,8 +57,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::vector<std::string>> get_buttons(std::string_view str);
static std::string get_buttons(std::vector<std::vector<std::string>> vec);
static std::vector<pad::combo> get_combos(std::string_view button_string);
static std::string get_button_string(std::vector<pad::combo>& combos);
u8 get_motor_speed(VibrateMotor& motor, f32 multiplier) const;
u8 get_large_motor_speed(std::array<VibrateMotor, 2>& motors) const;

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::vector<std::string>> combos = cfg_pad::get_buttons(str);
const std::vector<pad::combo> combos = cfg_pad::get_combos(str);
u16 value{};
@ -385,19 +385,19 @@ PadHandlerBase::connection evdev_joystick_handler::get_next_button_press(const s
}
};
for (const std::vector<std::string>& names : combos)
for (const pad::combo& combo : combos)
{
for (const u32 code : find_key_codes(rev_axis_list, names))
for (const u32 code : find_key_codes(rev_axis_list, combo))
{
set_value(code, true);
}
for (const u32 code : find_key_codes(axis_list, names))
for (const u32 code : find_key_codes(axis_list, combo))
{
set_value(code, false);
}
for (const u32 code : find_key_codes(button_list, names))
for (const u32 code : find_key_codes(button_list, combo))
{
set_value(code, false);
}
@ -1379,37 +1379,37 @@ bool evdev_joystick_handler::bindPadToDevice(std::shared_ptr<Pad> pad)
const auto find_buttons = [&](const cfg::string& name) -> std::vector<std::set<u32>>
{
const std::vector<std::vector<std::string>> str_combos = cfg_pad::get_buttons(name.to_string());
const std::vector<pad::combo> combos = cfg_pad::get_combos(name.to_string());
// In evdev we store indices to an EvdevButton vector in our pad objects instead of the usual key codes.
std::vector<std::set<u32>> combos;
std::vector<std::set<u32>> index_combos;
for (const std::vector<std::string>& names : str_combos)
for (const pad::combo& combo : combos)
{
std::set<u32> indices;
for (const u32 code : find_key_codes(axis_list, names))
for (const u32 code : find_key_codes(axis_list, combo))
{
indices.insert(register_evdevbutton(code, true, false));
}
for (const u32 code : find_key_codes(rev_axis_list, names))
for (const u32 code : find_key_codes(rev_axis_list, combo))
{
indices.insert(register_evdevbutton(code, true, true));
}
for (const u32 code : find_key_codes(button_list, names))
for (const u32 code : find_key_codes(button_list, combo))
{
indices.insert(register_evdevbutton(code, false, false));
}
if (!indices.empty())
{
combos.push_back(std::move(indices));
index_combos.push_back(std::move(indices));
}
}
return combos;
return index_combos;
};
const auto find_motion_button = [&](const cfg_sensor& sensor) -> evdev_sensor

View File

@ -895,13 +895,13 @@ std::vector<std::set<u32>> keyboard_pad_handler::GetKeyCombos(const cfg::string&
{
std::vector<std::set<u32>> res;
for (const std::vector<std::string>& combo : cfg_pad::get_buttons(cfg_string.to_string()))
for (const pad::combo& combo : cfg_pad::get_combos(cfg_string.to_string()))
{
std::set<u32> key_codes;
for (const std::string& key_name : combo)
for (const std::string& button : combo.buttons())
{
if (u32 code = GetKeyCode(QString::fromStdString(key_name)); code != Qt::NoButton)
if (u32 code = GetKeyCode(QString::fromStdString(button)); code != Qt::NoButton)
{
key_codes.insert(code);
}
@ -1051,7 +1051,7 @@ bool keyboard_pad_handler::bindPadToDevice(std::shared_ptr<Pad> pad)
const auto find_combos = [this](const cfg::string& name)
{
std::vector<std::set<u32>> combos = find_key_combos(mouse_list, name, false);
std::vector<std::set<u32>> combos = find_key_combos(mouse_list, name);
for (const std::set<u32>& combo : GetKeyCombos(name)) combos.push_back(combo);
if (!combos.empty())

View File

@ -232,7 +232,7 @@ pad_preview_values mm_joystick_handler::get_preview_values(const std::unordered_
u16 value{};
// The DS3 Button is considered pressed if any configured button combination is pressed
for (const std::set<u32>& codes : find_key_combos(button_list, str, std::string()))
for (const std::set<u32>& codes : find_key_combos(button_list, str))
{
bool combo_pressed = !codes.empty();
u16 combo_val = 0;

View File

@ -51,30 +51,30 @@ inline bool CreateConfigFile(const QString& dir, const QString& name)
return true;
}
void pad_settings_dialog::pad_button::insert_key(const std::string& key, binding_mode mode)
void pad_settings_dialog::pad_button::insert_button(const std::string& button, binding_mode mode)
{
std::vector<std::vector<std::string>> combos;
std::vector<pad::combo> combos;
if (mode != binding_mode::single)
{
combos = cfg_pad::get_buttons(m_keys);
combos = cfg_pad::get_combos(m_button_string);
}
if (combos.empty() || mode != binding_mode::combo)
{
combos.push_back({key});
combos.push_back(pad::combo({button}));
}
else if (mode == binding_mode::combo)
{
combos.back().push_back(key);
combos.back().add_button(button);
}
update(cfg_pad::get_buttons(combos));
update(cfg_pad::get_button_string(combos));
}
void pad_settings_dialog::pad_button::update(const std::string& keys)
void pad_settings_dialog::pad_button::update(const std::string& button_string)
{
m_keys = keys;
QString new_text = QString::fromStdString(keys);
m_button_string = button_string;
QString new_text = QString::fromStdString(button_string);
m_text = new_text.replace(",", ", ").replace("&", " + ");
}
@ -622,7 +622,7 @@ void pad_settings_dialog::InitButtons()
{
if (value == 0) continue;
m_cfg_entries[m_button_id].insert_key(key, mode);
m_cfg_entries[m_button_id].insert_button(key, mode);
// Switch to combo mode for all further keys
mode = binding_mode::combo;
@ -632,7 +632,7 @@ void pad_settings_dialog::InitButtons()
{
if (value == 0) continue;
m_cfg_entries[m_button_id].insert_key(key, mode);
m_cfg_entries[m_button_id].insert_button(key, mode);
// Switch to combo mode for all further keys
mode = binding_mode::combo;
@ -674,16 +674,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].button_string(),
m_cfg_entries[button_ids::id_pad_r2].button_string(),
m_cfg_entries[button_ids::id_pad_lstick_left].button_string(),
m_cfg_entries[button_ids::id_pad_lstick_right].button_string(),
m_cfg_entries[button_ids::id_pad_lstick_down].button_string(),
m_cfg_entries[button_ids::id_pad_lstick_up].button_string(),
m_cfg_entries[button_ids::id_pad_rstick_left].button_string(),
m_cfg_entries[button_ids::id_pad_rstick_right].button_string(),
m_cfg_entries[button_ids::id_pad_rstick_down].button_string(),
m_cfg_entries[button_ids::id_pad_rstick_up].button_string()
};
// Check if this is the first call during a remap
@ -1023,7 +1023,7 @@ void pad_settings_dialog::keyPressEvent(QKeyEvent* keyEvent)
}
else
{
m_cfg_entries[m_button_id].insert_key(keyboard_pad_handler::GetKeyName(keyEvent, false), m_binding_mode);
m_cfg_entries[m_button_id].insert_button(keyboard_pad_handler::GetKeyName(keyEvent, false), m_binding_mode);
}
ReactivateButtons();
@ -1050,7 +1050,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_binding_mode);
m_cfg_entries[m_button_id].insert_button((static_cast<keyboard_pad_handler*>(m_handler.get()))->GetMouseName(event), m_binding_mode);
}
ReactivateButtons();
@ -1112,7 +1112,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_binding_mode);
m_cfg_entries[m_button_id].insert_button((static_cast<keyboard_pad_handler*>(m_handler.get()))->GetMouseName(key), m_binding_mode);
ReactivateButtons();
}
@ -1163,7 +1163,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_binding_mode);
m_cfg_entries[m_button_id].insert_button((static_cast<keyboard_pad_handler*>(m_handler.get()))->GetMouseName(key), m_binding_mode);
ReactivateButtons();
}
}
@ -2110,7 +2110,7 @@ void pad_settings_dialog::ApplyCurrentPlayerConfig(int new_player_id)
// Check for duplicate button choices
if (m_handler->m_type != pad_handler::null)
{
std::set<std::string> unique_keys;
std::set<std::string> unique_button_strings;
for (const auto& [id, button] : m_cfg_entries)
{
// Let's ignore special keys, unless we're using a keyboard
@ -2120,13 +2120,13 @@ void pad_settings_dialog::ApplyCurrentPlayerConfig(int new_player_id)
continue;
}
for (const std::vector<std::string>& combo : cfg_pad::get_buttons(button.keys()))
for (const pad::combo& combo : cfg_pad::get_combos(button.button_string()))
{
for (const std::string& key : combo)
for (const std::string& button_string : combo.buttons())
{
if (const auto& [it, ok] = unique_keys.insert(key); !ok)
if (const auto& [it, ok] = unique_button_strings.insert(button_string); !ok)
{
m_duplicate_buttons[m_last_player_id] = key;
m_duplicate_buttons[m_last_player_id] = button_string;
break;
}
}
@ -2137,7 +2137,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.button_string());
}
// Apply rest of config

View File

@ -93,16 +93,16 @@ class pad_settings_dialog : public QDialog
update(*cfg_text);
}
void insert_key(const std::string& key, binding_mode mode);
void update(const std::string& keys);
void insert_button(const std::string& button, binding_mode mode);
void update(const std::string& button_string);
cfg::string* cfg_text() const { return m_cfg_text; }
const std::string& keys() const { return m_keys; }
const std::string& button_string() const { return m_button_string; }
const QString& text() const { return m_text; }
private:
cfg::string* m_cfg_text = nullptr;
std::string m_keys;
std::string m_button_string;
QString m_text;
};