Refactor code

This commit is contained in:
kalaposfos13 2025-01-13 15:44:33 +01:00
parent c5cde12ee9
commit 9aec35b23e
3 changed files with 212 additions and 197 deletions

View File

@ -54,12 +54,10 @@ Joystick halfmode
Don't be an idiot and test only the changed part expecting everything else to not be broken Don't be an idiot and test only the changed part expecting everything else to not be broken
*/ */
// Flags and values for varying purposes
// todo: can we change these?
bool leftjoystick_halfmode = false, rightjoystick_halfmode = false; bool leftjoystick_halfmode = false, rightjoystick_halfmode = false;
std::list<std::pair<u32, bool>> pressed_keys; std::list<std::pair<InputEvent, bool>> pressed_keys;
std::list<u32> toggled_keys; std::list<InputID> toggled_keys;
static std::vector<BindingConnection> connections; static std::vector<BindingConnection> connections;
auto output_array = std::array{ auto output_array = std::array{
@ -92,7 +90,6 @@ auto output_array = std::array{
ControllerOutput(OrbisPadButtonDataOffset::None, Axis::TriggerLeft), ControllerOutput(OrbisPadButtonDataOffset::None, Axis::TriggerLeft),
ControllerOutput(OrbisPadButtonDataOffset::None, Axis::TriggerRight), ControllerOutput(OrbisPadButtonDataOffset::None, Axis::TriggerRight),
// the "sorry, you gave an incorrect value, now we bind it to nothing" output
ControllerOutput(OrbisPadButtonDataOffset::None, Axis::AxisMax), ControllerOutput(OrbisPadButtonDataOffset::None, Axis::AxisMax),
}; };
@ -152,52 +149,35 @@ u32 GetControllerButtonInputId(u32 cbutton) {
// syntax: 'name, name,name' or 'name,name' or 'name' // syntax: 'name, name,name' or 'name,name' or 'name'
InputBinding GetBindingFromString(std::string& line) { InputBinding GetBindingFromString(std::string& line) {
u32 key1 = 0, key2 = 0, key3 = 0; std::array<InputID, 3> keys = {InputID(), InputID(), InputID()};
// Check and process tokens // Check and process tokens
for (const auto token : std::views::split(line, ',')) { // Split by comma for (const auto token : std::views::split(line, ',')) { // Split by comma
const std::string t(token.begin(), token.end()); const std::string t(token.begin(), token.end());
InputID input;
if (string_to_keyboard_key_map.find(t) != string_to_keyboard_key_map.end()) { if (string_to_keyboard_key_map.find(t) != string_to_keyboard_key_map.end()) {
// Map to keyboard key input = InputID(InputType::KeyboardMouse, string_to_keyboard_key_map.at(t));
u32 key_id = string_to_keyboard_key_map.at(t);
if (!key1)
key1 = key_id;
else if (!key2)
key2 = key_id;
else if (!key3)
key3 = key_id;
} else if (string_to_axis_map.find(t) != string_to_axis_map.end()) { } else if (string_to_axis_map.find(t) != string_to_axis_map.end()) {
// Map to axis input ID input = InputID(InputType::Axis, (u32)string_to_axis_map.at(t).axis);
u32 axis_id = GetAxisInputId(string_to_axis_map.at(t));
if (axis_id == (u32)-1) {
return InputBinding(0, 0, 0);
}
if (!key1)
key1 = axis_id;
else if (!key2)
key2 = axis_id;
else if (!key3)
key3 = axis_id;
} else if (string_to_cbutton_map.find(t) != string_to_cbutton_map.end()) { } else if (string_to_cbutton_map.find(t) != string_to_cbutton_map.end()) {
// Map to controller button input ID input = InputID(InputType::Controller, (u32)string_to_cbutton_map.at(t));
u32 cbutton_id = GetControllerButtonInputId((u32)string_to_cbutton_map.at(t));
if (cbutton_id == (u32)-1) {
return InputBinding(0, 0, 0);
}
if (!key1)
key1 = cbutton_id;
else if (!key2)
key2 = cbutton_id;
else if (!key3)
key3 = cbutton_id;
} else { } else {
// Invalid token found; return default binding // Invalid token found; return default binding
LOG_DEBUG(Input, "Invalid token found: {}", t); LOG_DEBUG(Input, "Invalid token found: {}", t);
return InputBinding(0, 0, 0); return InputBinding();
}
// Assign to the first available slot
for (auto& key : keys) {
if (!key.IsValid()) {
key = input;
break;
}
} }
} }
LOG_DEBUG(Input, "Parsed line: {} {} {}", keys[0].ToString(), keys[1].ToString(), keys[2].ToString());
return InputBinding(key1, key2, key3); return InputBinding(keys[0], keys[1], keys[2]);
} }
void ParseInputConfig(const std::string game_id = "") { void ParseInputConfig(const std::string game_id = "") {
@ -278,8 +258,8 @@ void ParseInputConfig(const std::string game_id = "") {
} }
ControllerOutput* toggle_out = &*std::ranges::find( ControllerOutput* toggle_out = &*std::ranges::find(
output_array, ControllerOutput((OrbisPadButtonDataOffset)KEY_TOGGLE)); output_array, ControllerOutput((OrbisPadButtonDataOffset)KEY_TOGGLE));
BindingConnection toggle_connection = BindingConnection toggle_connection = BindingConnection(
BindingConnection(InputBinding(toggle_keys.key2), toggle_out, toggle_keys.key3); InputBinding(toggle_keys.keys[0]), toggle_out, 0, toggle_keys.keys[1]);
connections.insert(connections.end(), toggle_connection); connections.insert(connections.end(), toggle_connection);
continue; continue;
} }
@ -301,7 +281,7 @@ void ParseInputConfig(const std::string game_id = "") {
// normal cases // normal cases
InputBinding binding = GetBindingFromString(input_string); InputBinding binding = GetBindingFromString(input_string);
BindingConnection connection(0, nullptr); BindingConnection connection(InputID(), nullptr);
auto button_it = string_to_cbutton_map.find(output_string); auto button_it = string_to_cbutton_map.find(output_string);
auto axis_it = string_to_axis_map.find(output_string); auto axis_it = string_to_axis_map.find(output_string);
@ -316,7 +296,7 @@ void ParseInputConfig(const std::string game_id = "") {
connections.insert(connections.end(), connection); connections.insert(connections.end(), connection);
} else if (axis_it != string_to_axis_map.end()) { } else if (axis_it != string_to_axis_map.end()) {
int value_to_set = (binding.key3 & 0x80000000) != 0 ? 0 int value_to_set = binding.keys[2].type == InputType::Axis ? 0
: (axis_it->second.axis == Axis::TriggerLeft || : (axis_it->second.axis == Axis::TriggerLeft ||
axis_it->second.axis == Axis::TriggerRight) axis_it->second.axis == Axis::TriggerRight)
? 127 ? 127
@ -332,16 +312,19 @@ void ParseInputConfig(const std::string game_id = "") {
lineCount, line); lineCount, line);
continue; continue;
} }
// LOG_INFO(Input, "Succesfully parsed line {}", lineCount); LOG_DEBUG(Input, "Succesfully parsed line {}", lineCount);
} }
file.close(); file.close();
std::sort(connections.begin(), connections.end()); std::sort(connections.begin(), connections.end());
for (auto& c : connections) {
LOG_DEBUG(Input, "Binding: {}", c.binding.ToString());
}
LOG_DEBUG(Input, "Done parsing the input config!"); LOG_DEBUG(Input, "Done parsing the input config!");
} }
u32 GetMouseWheelEvent(const SDL_Event& event) { u32 GetMouseWheelEvent(const SDL_Event& event) {
if (event.type != SDL_EVENT_MOUSE_WHEEL && event.type != SDL_EVENT_MOUSE_WHEEL_OFF) { if (event.type != SDL_EVENT_MOUSE_WHEEL && event.type != SDL_EVENT_MOUSE_WHEEL_OFF) {
LOG_DEBUG(Input, "Something went wrong with wheel input parsing!"); LOG_WARNING(Input, "Something went wrong with wheel input parsing!");
return (u32)-1; return (u32)-1;
} }
if (event.wheel.y > 0) { if (event.wheel.y > 0) {
@ -356,31 +339,28 @@ u32 GetMouseWheelEvent(const SDL_Event& event) {
return (u32)-1; return (u32)-1;
} }
u32 InputBinding::GetInputIDFromEvent(const SDL_Event& e) { InputEvent InputBinding::GetInputEventFromSDLEvent(const SDL_Event& e) {
int value_mask; int value_mask;
switch (e.type) { switch (e.type) {
case SDL_EVENT_KEY_DOWN: case SDL_EVENT_KEY_DOWN:
case SDL_EVENT_KEY_UP: case SDL_EVENT_KEY_UP:
return e.key.key; return InputEvent(InputType::KeyboardMouse, e.key.key, e.type == SDL_EVENT_KEY_DOWN, 0);
case SDL_EVENT_MOUSE_BUTTON_DOWN: case SDL_EVENT_MOUSE_BUTTON_DOWN:
case SDL_EVENT_MOUSE_BUTTON_UP: case SDL_EVENT_MOUSE_BUTTON_UP:
return (u32)e.button.button; return InputEvent(InputType::KeyboardMouse, e.button.button,
e.type == SDL_EVENT_MOUSE_BUTTON_DOWN, 0);
case SDL_EVENT_MOUSE_WHEEL: case SDL_EVENT_MOUSE_WHEEL:
case SDL_EVENT_MOUSE_WHEEL_OFF: case SDL_EVENT_MOUSE_WHEEL_OFF:
return GetMouseWheelEvent(e); return InputEvent(InputType::KeyboardMouse, GetMouseWheelEvent(e),
e.type == SDL_EVENT_MOUSE_WHEEL, 0);
case SDL_EVENT_GAMEPAD_BUTTON_DOWN: case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
case SDL_EVENT_GAMEPAD_BUTTON_UP: case SDL_EVENT_GAMEPAD_BUTTON_UP:
return (u32)e.gbutton.button + 0x10000000; // I believe this range is unused return InputEvent(InputType::Controller, e.gbutton.button,
e.type == SDL_EVENT_GAMEPAD_BUTTON_DOWN, 0);
case SDL_EVENT_GAMEPAD_AXIS_MOTION: case SDL_EVENT_GAMEPAD_AXIS_MOTION:
// todo: somehow put this value into the correct connection return InputEvent(InputType::Controller, e.gaxis.axis, true, (s8)(e.gaxis.value / 256));
// solution 1: add it to the keycode as a 0x0FF00000 (a bit hacky but works I guess?)
// I guess in software developement, there really is nothing more permanent than a temporary
// solution
value_mask = (u32)((e.gaxis.value / 256 + 128) << 20); // +-32000 to +-128 to 0-255
return (u32)e.gaxis.axis + 0x80000000 +
value_mask; // they are pushed to the end of the sorted array
default: default:
return (u32)-1; return InputEvent(InputType::Count, (u32)-1, false, 0);
} }
} }
@ -389,18 +369,18 @@ void ControllerOutput::SetControllerOutputController(GameController* c) {
ControllerOutput::controller = c; ControllerOutput::controller = c;
} }
void ToggleKeyInList(u32 key) { void ToggleKeyInList(InputID input) {
if ((key & 0x80000000) != 0) { if (input.type == InputType::Axis) {
LOG_ERROR(Input, "Toggling analog inputs is not supported!"); LOG_ERROR(Input, "Toggling analog inputs is not supported!");
return; return;
} }
auto it = std::find(toggled_keys.begin(), toggled_keys.end(), key); auto it = std::find(toggled_keys.begin(), toggled_keys.end(), input);
if (it == toggled_keys.end()) { if (it == toggled_keys.end()) {
toggled_keys.insert(toggled_keys.end(), key); toggled_keys.insert(toggled_keys.end(), input);
LOG_DEBUG(Input, "Added {} to toggled keys", key); LOG_DEBUG(Input, "Added {} to toggled keys", input.ToString());
} else { } else {
toggled_keys.erase(it); toggled_keys.erase(it);
LOG_DEBUG(Input, "Removed {} from toggled keys", key); LOG_DEBUG(Input, "Removed {} from toggled keys", input.ToString());
} }
} }
@ -409,14 +389,19 @@ void ControllerOutput::ResetUpdate() {
new_button_state = false; new_button_state = false;
new_param = 0; new_param = 0;
} }
void ControllerOutput::AddUpdate(bool pressed, bool analog, u32 param) { void ControllerOutput::AddUpdate(InputEvent event) {
state_changed = true; state_changed = true;
if (button != OrbisPadButtonDataOffset::None) { if ((u32)button == KEY_TOGGLE) {
if (analog) { if (event.active) {
new_button_state |= abs((s32)param) > 0x40; LOG_DEBUG(Input, "Toggling a button...");
ToggleKeyInList(event.input); // todo: fix key toggle
}
} else if (button != OrbisPadButtonDataOffset::None) {
if (event.input.type == InputType::Axis) {
new_button_state |= abs((s32)event.axis_value) > 0x40;
} else { } else {
new_button_state |= pressed; new_button_state |= event.active;
new_param = param; new_param = event.axis_value;
} }
} else if (axis != Axis::AxisMax) { } else if (axis != Axis::AxisMax) {
@ -425,17 +410,18 @@ void ControllerOutput::AddUpdate(bool pressed, bool analog, u32 param) {
case Axis::TriggerRight: case Axis::TriggerRight:
// if it's a button input, then we know the value to set, so the param is 0. // if it's a button input, then we know the value to set, so the param is 0.
// if it's an analog input, then the param isn't 0 // if it's an analog input, then the param isn't 0
// warning: doesn't work yet new_param = (event.active ? (s32)event.axis_value : 0) + new_param;
new_param = SDL_clamp((pressed ? (s32)param : 0) + new_param, 0, 127);
break; break;
default: default:
// todo: do the same as above new_param = (event.active ? (s32)event.axis_value : 0) + new_param;
new_param = SDL_clamp((pressed ? (s32)param : 0) + new_param, -127, 127);
break; break;
} }
} }
} }
void ControllerOutput::FinalizeUpdate() { void ControllerOutput::FinalizeUpdate() {
if (!state_changed) {
// return;
}
old_button_state = new_button_state; old_button_state = new_button_state;
old_param = new_param; old_param = new_param;
float touchpad_x = 0; float touchpad_x = 0;
@ -454,12 +440,10 @@ void ControllerOutput::FinalizeUpdate() {
case RIGHTJOYSTICK_HALFMODE: case RIGHTJOYSTICK_HALFMODE:
rightjoystick_halfmode = new_button_state; rightjoystick_halfmode = new_button_state;
break; break;
case KEY_TOGGLE: // KEY_TOGGLE isn't handled here anymore, as this function doesn't have the necessary data
if (new_button_state) { // to do it, and it would be inconvenient to force it here, when AddUpdate does the job just
// LOG_DEBUG(Input, "Toggling a button..."); // fine, and a toggle doesn't have to checked against every input that's bound to it, it's
ToggleKeyInList(new_param); // enough that one is pressed
}
break;
default: // is a normal key (hopefully) default: // is a normal key (hopefully)
controller->CheckButton(0, (OrbisPadButtonDataOffset)button, new_button_state); controller->CheckButton(0, (OrbisPadButtonDataOffset)button, new_button_state);
break; break;
@ -493,44 +477,43 @@ void ControllerOutput::FinalizeUpdate() {
// Updates the list of pressed keys with the given input. // Updates the list of pressed keys with the given input.
// Returns whether the list was updated or not. // Returns whether the list was updated or not.
bool UpdatePressedKeys(u32 value, bool is_pressed) { bool UpdatePressedKeys(InputEvent event) {
// Skip invalid inputs // Skip invalid inputs
if (value == (u32)-1) { InputID input = event.input;
if (input.sdl_id == (u32)-1) {
return false; return false;
} }
if ((value & 0x80000000) != 0) { if (input.type == InputType::Axis) {
// analog input, it gets added when it first sends an event, // analog input, it gets added when it first sends an event,
// and from there, it only changes the parameter // and from there, it only changes the parameter
// reverse iterate until we get out of the 0x8000000 range, if found, const auto& it = std::find_if(
// update the parameter, if not, add it to the end pressed_keys.begin(), pressed_keys.end(),
u32 value_to_search = value & 0xF00FFFFF; [input](const std::pair<InputEvent, bool>& e) { return e.first.input == input; });
for (auto& it = --pressed_keys.end(); (it->first & 0x80000000) != 0; it--) { if (it == pressed_keys.end()) {
if ((it->first & 0xF00FFFFF) == value_to_search) { pressed_keys.push_back({event, false});
it->first = value; } else {
// LOG_DEBUG(Input, "New value for {:X}: {:x}", value, value); it->first.axis_value = event.axis_value;
return true;
}
} }
// LOG_DEBUG(Input, "Input activated for the first time, adding it to the list");
pressed_keys.insert(pressed_keys.end(), {value, false});
return true; return true;
} }
if (is_pressed) { if (event.active) {
// Find the correct position for insertion to maintain order // Find the correct position for insertion to maintain order
auto it = auto it = std::lower_bound(pressed_keys.begin(), pressed_keys.end(), input,
std::lower_bound(pressed_keys.begin(), pressed_keys.end(), value, [](const std::pair<InputEvent, bool>& e, InputID i) {
[](const std::pair<u32, bool>& pk, u32 v) { return pk.first < v; }); return e.first.input.type <= i.type &&
e.first.input.sdl_id < i.sdl_id;
});
// Insert only if 'value' is not already in the list // Insert only if 'value' is not already in the list
if (it == pressed_keys.end() || it->first != value) { if (it == pressed_keys.end() || it->first.input != input) {
pressed_keys.insert(it, {value, false}); pressed_keys.insert(it, {event, false});
return true; return true;
} }
} else { } else {
// Remove 'value' from the list if it's not pressed // Remove 'value' from the list if it's not pressed
auto it = auto it = std::find_if(
std::find_if(pressed_keys.begin(), pressed_keys.end(), pressed_keys.begin(), pressed_keys.end(),
[value](const std::pair<u32, bool>& pk) { return pk.first == value; }); [input](const std::pair<InputEvent, bool>& e) { return e.first.input == input; });
if (it != pressed_keys.end()) { if (it != pressed_keys.end()) {
pressed_keys.erase(it); pressed_keys.erase(it);
return true; return true;
@ -538,17 +521,28 @@ bool UpdatePressedKeys(u32 value, bool is_pressed) {
} }
return false; return false;
} }
// Check if a given binding's all keys are currently active. // Check if the binding's all keys are currently active.
// For now it also extracts the analog inputs' parameters. // It also extracts the analog inputs' parameters, and updates the input hierarchy flags.
void IsInputActive(BindingConnection& connection, bool& active, bool& analog) { InputEvent BindingConnection::ProcessBinding() {
// the last key is always set (if the connection isn't empty),
// and the analog inputs are always the last one due to how they are sorted,
// so this signifies whether or not the input is analog
InputEvent event = InputEvent(binding.keys[3]);
if (pressed_keys.empty()) { if (pressed_keys.empty()) {
active = false; return event;
return;
} }
InputBinding i = connection.binding; if (event.input.type != InputType::Axis) {
// Extract keys from InputBinding and ignore unused (0) or toggled keys // for button inputs
std::list<u32> input_keys = {i.key1, i.key2, i.key3}; event.axis_value = axis_param;
input_keys.remove(0); }
// it's a bit scuffed, but if the output is a toggle, then we put the key here
if ((u32)output->button == KEY_TOGGLE) {
event.input = toggle;
}
// Extract keys from InputBinding and ignore unused or toggled keys
std::list<InputID> input_keys = {binding.keys[0], binding.keys[1], binding.keys[2]};
input_keys.remove(InputID());
for (auto key = input_keys.begin(); key != input_keys.end();) { for (auto key = input_keys.begin(); key != input_keys.end();) {
if (std::find(toggled_keys.begin(), toggled_keys.end(), *key) != toggled_keys.end()) { if (std::find(toggled_keys.begin(), toggled_keys.end(), *key) != toggled_keys.end()) {
key = input_keys.erase(key); // Use the returned iterator key = input_keys.erase(key); // Use the returned iterator
@ -558,8 +552,8 @@ void IsInputActive(BindingConnection& connection, bool& active, bool& analog) {
} }
if (input_keys.empty()) { if (input_keys.empty()) {
LOG_DEBUG(Input, "No actual inputs to check, returning true"); LOG_DEBUG(Input, "No actual inputs to check, returning true");
active = true; event.active = true;
return; return event;
} }
// Iterator for pressed_keys, starting from the beginning // Iterator for pressed_keys, starting from the beginning
@ -569,74 +563,50 @@ void IsInputActive(BindingConnection& connection, bool& active, bool& analog) {
std::list<bool*> flags_to_set; std::list<bool*> flags_to_set;
// Check if all keys in input_keys are active // Check if all keys in input_keys are active
for (u32 key : input_keys) { for (InputID key : input_keys) {
bool key_found = false; bool key_found = false;
// Search for the current key in pressed_keys starting from the last checked position while (pressed_it != pressed_keys.end()) {
while (pressed_it != pressed_keys.end() && (pressed_it->first & 0x80000000) == 0) { if (pressed_it->first.input == key && pressed_it->second == false) {
if (pressed_it->first == key) {
key_found = true; key_found = true;
flags_to_set.push_back(&pressed_it->second); flags_to_set.push_back(&pressed_it->second);
if (pressed_it->first.input.type == InputType::Axis) {
++pressed_it; // Move to the next key in pressed_keys event.axis_value = pressed_it->first.axis_value;
}
++pressed_it;
break; break;
} }
++pressed_it; ++pressed_it;
} }
if (!key_found && (key & 0x80000000) != 0) {
// reverse iterate over the analog inputs, as they can't be sorted
auto& rev_it = --pressed_keys.end();
for (auto rev_it = --pressed_keys.end(); (rev_it->first & 0x80000000) != 0; rev_it--) {
if ((rev_it->first & 0xF00FFFFF) == (key & 0xF00FFFFF)) {
connection.parameter = (u32)((s32)((rev_it->first & 0x0FF00000) >> 20) - 128);
// LOG_DEBUG(Input, "Extracted the following param: {:X} from {:X}",
// (s32)connection.parameter, rev_it->first);
key_found = true;
analog = true;
flags_to_set.push_back(&rev_it->second);
break;
}
}
}
if (!key_found) { if (!key_found) {
return; return event;
} }
} }
bool is_fully_blocked = true;
for (bool* flag : flags_to_set) {
is_fully_blocked &= *flag;
}
if (is_fully_blocked) {
return;
}
for (bool* flag : flags_to_set) { for (bool* flag : flags_to_set) {
*flag = true; *flag = true;
} }
LOG_DEBUG(Input, "Input found: {}", i.ToString()); LOG_DEBUG(Input, "Input found: {}", binding.ToString());
active = true; event.active = true;
return; // All keys are active return event; // All keys are active
} }
void ActivateOutputsFromInputs() { void ActivateOutputsFromInputs() {
// LOG_DEBUG(Input, "Start of an input frame..."); // Reset values and flags
for (auto& it : pressed_keys) { for (auto& it : pressed_keys) {
it.second = false; it.second = false;
} }
// this is the cleanest looking code I've ever written, too bad it is not working
// (i left the last part in by accident, then it turnd out to still be true even after I thought
// everything is good) (but now it really should be fine)
for (auto& it : output_array) { for (auto& it : output_array) {
it.ResetUpdate(); it.ResetUpdate();
} }
// Iterate over all inputs, and update their respecive outputs accordingly
for (auto& it : connections) { for (auto& it : connections) {
bool active = false, analog_input_detected = false; it.output->AddUpdate(it.ProcessBinding());
IsInputActive(it, active, analog_input_detected);
it.output->AddUpdate(active, analog_input_detected, it.parameter);
} }
// Update all outputs
for (auto& it : output_array) { for (auto& it : output_array) {
it.FinalizeUpdate(); it.FinalizeUpdate();
} }

View File

@ -41,6 +41,42 @@ struct AxisMapping {
int value; // Value to set for key press (+127 or -127 for movement) int value; // Value to set for key press (+127 or -127 for movement)
}; };
enum class InputType { KeyboardMouse, Controller, Axis, Count };
class InputID {
public:
InputType type;
u32 sdl_id;
InputID(InputType d = InputType::Count, u32 i = (u32)-1) : type(d), sdl_id(i) {}
bool operator==(const InputID& o) const {
return type == o.type && sdl_id == o.sdl_id;
}
bool operator!=(const InputID& o) const {
return type != o.type || sdl_id != o.sdl_id;
}
bool operator<=(const InputID& o) const {
return type <= o.type && sdl_id <= o.sdl_id;
}
bool IsValid() const {
return *this != InputID();
}
std::string ToString() {
return fmt::format("({}: {:x})", (u8)type, sdl_id);
}
};
class InputEvent {
public:
InputID input;
bool active;
s8 axis_value;
InputEvent(InputID i = InputID(), bool a = false, s8 v = 0)
: input(i), active(a), axis_value(v) {}
InputEvent(InputType d, u32 i, bool a = false, s8 v = 0)
: input(d, i), active(a), axis_value(v) {}
};
// i strongly suggest you collapse these maps // i strongly suggest you collapse these maps
const std::map<std::string, OrbisPadButtonDataOffset> string_to_cbutton_map = { const std::map<std::string, OrbisPadButtonDataOffset> string_to_cbutton_map = {
{"triangle", OrbisPadButtonDataOffset::Triangle}, {"triangle", OrbisPadButtonDataOffset::Triangle},
@ -192,79 +228,84 @@ void ParseInputConfig(const std::string game_id);
class InputBinding { class InputBinding {
public: public:
u32 key1, key2, key3; InputID keys[3];
InputBinding(u32 k1 = SDLK_UNKNOWN, u32 k2 = SDLK_UNKNOWN, u32 k3 = SDLK_UNKNOWN) { InputBinding(InputID k1 = InputID(), InputID k2 = InputID(), InputID k3 = InputID()) {
// we format the keys so comparing them will be very fast, because we will only have to // we format the keys so comparing them will be very fast, because we will only have to
// compare 3 sorted elements, where the only possible duplicate item is 0 // compare 3 sorted elements, where the only possible duplicate item is 0
// duplicate entries get changed to one original, one null // duplicate entries get changed to one original, one null
if (k1 == k2 && k1 != SDLK_UNKNOWN) { if (k1 == k2 && k1 != InputID()) {
k2 = 0; k2 = InputID();
} }
if (k1 == k3 && k1 != SDLK_UNKNOWN) { if (k1 == k3 && k1 != InputID()) {
k3 = 0; k3 = InputID();
} }
if (k3 == k2 && k2 != SDLK_UNKNOWN) { if (k3 == k2 && k2 != InputID()) {
k2 = 0; k2 = InputID();
} }
// this sorts them // this sorts them
if (k1 <= k2 && k1 <= k3) { if (k1 <= k2 && k1 <= k3) {
key1 = k1; keys[0] = k1;
if (k2 <= k3) { if (k2 <= k3) {
key2 = k2; keys[1] = k2;
key3 = k3; keys[2] = k3;
} else { } else {
key2 = k3; keys[1] = k3;
key3 = k2; keys[2] = k2;
} }
} else if (k2 <= k1 && k2 <= k3) { } else if (k2 <= k1 && k2 <= k3) {
key1 = k2; keys[0] = k2;
if (k1 <= k3) { if (k1 <= k3) {
key2 = k1; keys[1] = k1;
key3 = k3; keys[2] = k3;
} else { } else {
key2 = k3; keys[1] = k3;
key3 = k1; keys[2] = k1;
} }
} else { } else {
key1 = k3; keys[0] = k3;
if (k1 <= k2) { if (k1 <= k2) {
key2 = k1; keys[1] = k1;
key3 = k2; keys[2] = k2;
} else { } else {
key2 = k2; keys[1] = k2;
key3 = k1; keys[3] = k1;
} }
} }
} }
// copy ctor // copy ctor
InputBinding(const InputBinding& o) : key1(o.key1), key2(o.key2), key3(o.key3) {} InputBinding(const InputBinding& o) {
keys[0] = o.keys[0];
keys[1] = o.keys[1];
keys[2] = o.keys[2];
}
inline bool operator==(const InputBinding& o) { inline bool operator==(const InputBinding& o) {
// 0 = SDLK_UNKNOWN aka unused slot // InputID() signifies an unused slot
return (key3 == o.key3 || key3 == 0 || o.key3 == 0) && return (keys[2] == o.keys[2] || keys[2] == InputID() || o.keys[2] == InputID()) &&
(key2 == o.key2 || key2 == 0 || o.key2 == 0) && (keys[1] == o.keys[1] || keys[1] == InputID() || o.keys[1] == InputID()) &&
(key1 == o.key1 || key1 == 0 || o.key1 == 0); (keys[0] == o.keys[0] || keys[0] == InputID() || o.keys[0] == InputID());
// it is already very fast, // it is already very fast,
// but reverse order makes it check the actual keys first instead of possible 0-s, // but reverse order makes it check the actual keys first instead of possible 0-s,
// potenially skipping the later expressions of the three-way AND // potenially skipping the later expressions of the three-way AND
} }
inline int KeyCount() const { inline int KeyCount() const {
return (key1 ? 1 : 0) + (key2 ? 1 : 0) + (key3 ? 1 : 0); return (keys[0].IsValid() ? 1 : 0) + (keys[1].IsValid() ? 1 : 0) +
(keys[2].IsValid() ? 1 : 0);
} }
// Sorts by the amount of non zero keys - left side is 'bigger' here // Sorts by the amount of non zero keys - left side is 'bigger' here
bool operator<(const InputBinding& other) const { bool operator<(const InputBinding& other) const {
return KeyCount() > other.KeyCount(); return KeyCount() > other.KeyCount();
} }
inline bool IsEmpty() { inline bool IsEmpty() {
return key1 == 0 && key2 == 0 && key3 == 0; return !(keys[0].IsValid() || keys[1].IsValid() || keys[2].IsValid());
} }
std::string ToString() { std::string ToString() { // todo add device type
return fmt::format("({:X}, {:X}, {:X})", key1, key2, key3); return fmt::format("({:X}, {:X}, {:X})", keys[0].sdl_id, keys[1].sdl_id, keys[2].sdl_id);
} }
// returns a u32 based on the event type (keyboard, mouse buttons, or wheel) // returns an InputEvent based on the event type (keyboard, mouse buttons/wheel, or controller)
static u32 GetInputIDFromEvent(const SDL_Event& e); static InputEvent GetInputEventFromSDLEvent(const SDL_Event& e);
}; };
class ControllerOutput { class ControllerOutput {
static GameController* controller; static GameController* controller;
@ -300,19 +341,21 @@ public:
} }
void ResetUpdate(); void ResetUpdate();
void AddUpdate(bool pressed, bool analog, u32 param = 0); void AddUpdate(InputEvent event);
void FinalizeUpdate(); void FinalizeUpdate();
}; };
class BindingConnection { class BindingConnection {
public: public:
InputBinding binding; InputBinding binding;
ControllerOutput* output; ControllerOutput* output;
u32 parameter; u32 axis_param;
BindingConnection(InputBinding b, ControllerOutput* out, u32 param = 0) { InputID toggle;
binding = b;
parameter = param;
BindingConnection(InputBinding b, ControllerOutput* out, u32 param = 0, InputID t = InputID()) {
binding = b;
axis_param = param;
output = out; output = out;
toggle = t;
} }
bool operator<(const BindingConnection& other) const { bool operator<(const BindingConnection& other) const {
// a button is a higher priority than an axis, as buttons can influence axes // a button is a higher priority than an axis, as buttons can influence axes
@ -327,11 +370,12 @@ public:
} }
return false; return false;
} }
InputEvent ProcessBinding();
}; };
// Updates the list of pressed keys with the given input. // Updates the list of pressed keys with the given input.
// Returns whether the list was updated or not. // Returns whether the list was updated or not.
bool UpdatePressedKeys(u32 button, bool is_pressed); bool UpdatePressedKeys(InputEvent event);
void ActivateOutputsFromInputs(); void ActivateOutputsFromInputs();

View File

@ -254,10 +254,11 @@ void WindowSDL::OnKeyboardMouseInput(const SDL_Event* event) {
const bool input_down = event->type == SDL_EVENT_KEY_DOWN || const bool input_down = event->type == SDL_EVENT_KEY_DOWN ||
event->type == SDL_EVENT_MOUSE_BUTTON_DOWN || event->type == SDL_EVENT_MOUSE_BUTTON_DOWN ||
event->type == SDL_EVENT_MOUSE_WHEEL; event->type == SDL_EVENT_MOUSE_WHEEL;
u32 input_id = Input::InputBinding::GetInputIDFromEvent(*event); Input::InputEvent input_event = Input::InputBinding::GetInputEventFromSDLEvent(*event);
// Handle window controls outside of the input maps // Handle window controls outside of the input maps
if (event->type == SDL_EVENT_KEY_DOWN) { if (event->type == SDL_EVENT_KEY_DOWN) {
u32 input_id = input_event.input.sdl_id;
// Reparse kbm inputs // Reparse kbm inputs
if (input_id == SDLK_F8) { if (input_id == SDLK_F8) {
Input::ParseInputConfig(std::string(Common::ElfInfo::Instance().GameSerial())); Input::ParseInputConfig(std::string(Common::ElfInfo::Instance().GameSerial()));
@ -291,7 +292,7 @@ void WindowSDL::OnKeyboardMouseInput(const SDL_Event* event) {
} }
// add/remove it from the list // add/remove it from the list
bool inputs_changed = Input::UpdatePressedKeys(input_id, input_down); bool inputs_changed = Input::UpdatePressedKeys(input_event);
// update bindings // update bindings
if (inputs_changed) { if (inputs_changed) {