mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-08-05 17:02:40 +00:00
Refactor code
This commit is contained in:
parent
c5cde12ee9
commit
9aec35b23e
@ -54,12 +54,10 @@ Joystick halfmode
|
||||
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;
|
||||
|
||||
std::list<std::pair<u32, bool>> pressed_keys;
|
||||
std::list<u32> toggled_keys;
|
||||
std::list<std::pair<InputEvent, bool>> pressed_keys;
|
||||
std::list<InputID> toggled_keys;
|
||||
static std::vector<BindingConnection> connections;
|
||||
|
||||
auto output_array = std::array{
|
||||
@ -92,7 +90,6 @@ auto output_array = std::array{
|
||||
ControllerOutput(OrbisPadButtonDataOffset::None, Axis::TriggerLeft),
|
||||
ControllerOutput(OrbisPadButtonDataOffset::None, Axis::TriggerRight),
|
||||
|
||||
// the "sorry, you gave an incorrect value, now we bind it to nothing" output
|
||||
ControllerOutput(OrbisPadButtonDataOffset::None, Axis::AxisMax),
|
||||
};
|
||||
|
||||
@ -152,52 +149,35 @@ u32 GetControllerButtonInputId(u32 cbutton) {
|
||||
|
||||
// syntax: 'name, name,name' or 'name,name' or 'name'
|
||||
InputBinding GetBindingFromString(std::string& line) {
|
||||
u32 key1 = 0, key2 = 0, key3 = 0;
|
||||
std::array<InputID, 3> keys = {InputID(), InputID(), InputID()};
|
||||
|
||||
// Check and process tokens
|
||||
for (const auto token : std::views::split(line, ',')) { // Split by comma
|
||||
const std::string t(token.begin(), token.end());
|
||||
InputID input;
|
||||
|
||||
if (string_to_keyboard_key_map.find(t) != string_to_keyboard_key_map.end()) {
|
||||
// Map to keyboard key
|
||||
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;
|
||||
input = InputID(InputType::KeyboardMouse, string_to_keyboard_key_map.at(t));
|
||||
} else if (string_to_axis_map.find(t) != string_to_axis_map.end()) {
|
||||
// Map to axis input ID
|
||||
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;
|
||||
input = InputID(InputType::Axis, (u32)string_to_axis_map.at(t).axis);
|
||||
} else if (string_to_cbutton_map.find(t) != string_to_cbutton_map.end()) {
|
||||
// Map to controller button input ID
|
||||
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;
|
||||
input = InputID(InputType::Controller, (u32)string_to_cbutton_map.at(t));
|
||||
} else {
|
||||
// Invalid token found; return default binding
|
||||
LOG_DEBUG(Input, "Invalid token found: {}", t);
|
||||
return InputBinding(0, 0, 0);
|
||||
}
|
||||
return InputBinding();
|
||||
}
|
||||
|
||||
return InputBinding(key1, key2, key3);
|
||||
// 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(keys[0], keys[1], keys[2]);
|
||||
}
|
||||
|
||||
void ParseInputConfig(const std::string game_id = "") {
|
||||
@ -278,8 +258,8 @@ void ParseInputConfig(const std::string game_id = "") {
|
||||
}
|
||||
ControllerOutput* toggle_out = &*std::ranges::find(
|
||||
output_array, ControllerOutput((OrbisPadButtonDataOffset)KEY_TOGGLE));
|
||||
BindingConnection toggle_connection =
|
||||
BindingConnection(InputBinding(toggle_keys.key2), toggle_out, toggle_keys.key3);
|
||||
BindingConnection toggle_connection = BindingConnection(
|
||||
InputBinding(toggle_keys.keys[0]), toggle_out, 0, toggle_keys.keys[1]);
|
||||
connections.insert(connections.end(), toggle_connection);
|
||||
continue;
|
||||
}
|
||||
@ -301,7 +281,7 @@ void ParseInputConfig(const std::string game_id = "") {
|
||||
|
||||
// normal cases
|
||||
InputBinding binding = GetBindingFromString(input_string);
|
||||
BindingConnection connection(0, nullptr);
|
||||
BindingConnection connection(InputID(), nullptr);
|
||||
auto button_it = string_to_cbutton_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);
|
||||
|
||||
} 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::TriggerRight)
|
||||
? 127
|
||||
@ -332,16 +312,19 @@ void ParseInputConfig(const std::string game_id = "") {
|
||||
lineCount, line);
|
||||
continue;
|
||||
}
|
||||
// LOG_INFO(Input, "Succesfully parsed line {}", lineCount);
|
||||
LOG_DEBUG(Input, "Succesfully parsed line {}", lineCount);
|
||||
}
|
||||
file.close();
|
||||
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!");
|
||||
}
|
||||
|
||||
u32 GetMouseWheelEvent(const SDL_Event& event) {
|
||||
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;
|
||||
}
|
||||
if (event.wheel.y > 0) {
|
||||
@ -356,31 +339,28 @@ u32 GetMouseWheelEvent(const SDL_Event& event) {
|
||||
return (u32)-1;
|
||||
}
|
||||
|
||||
u32 InputBinding::GetInputIDFromEvent(const SDL_Event& e) {
|
||||
InputEvent InputBinding::GetInputEventFromSDLEvent(const SDL_Event& e) {
|
||||
int value_mask;
|
||||
switch (e.type) {
|
||||
case SDL_EVENT_KEY_DOWN:
|
||||
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_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_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_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:
|
||||
// todo: somehow put this value into the correct connection
|
||||
// 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
|
||||
return InputEvent(InputType::Controller, e.gaxis.axis, true, (s8)(e.gaxis.value / 256));
|
||||
default:
|
||||
return (u32)-1;
|
||||
return InputEvent(InputType::Count, (u32)-1, false, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -389,18 +369,18 @@ void ControllerOutput::SetControllerOutputController(GameController* c) {
|
||||
ControllerOutput::controller = c;
|
||||
}
|
||||
|
||||
void ToggleKeyInList(u32 key) {
|
||||
if ((key & 0x80000000) != 0) {
|
||||
void ToggleKeyInList(InputID input) {
|
||||
if (input.type == InputType::Axis) {
|
||||
LOG_ERROR(Input, "Toggling analog inputs is not supported!");
|
||||
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()) {
|
||||
toggled_keys.insert(toggled_keys.end(), key);
|
||||
LOG_DEBUG(Input, "Added {} to toggled keys", key);
|
||||
toggled_keys.insert(toggled_keys.end(), input);
|
||||
LOG_DEBUG(Input, "Added {} to toggled keys", input.ToString());
|
||||
} else {
|
||||
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_param = 0;
|
||||
}
|
||||
void ControllerOutput::AddUpdate(bool pressed, bool analog, u32 param) {
|
||||
void ControllerOutput::AddUpdate(InputEvent event) {
|
||||
state_changed = true;
|
||||
if (button != OrbisPadButtonDataOffset::None) {
|
||||
if (analog) {
|
||||
new_button_state |= abs((s32)param) > 0x40;
|
||||
if ((u32)button == KEY_TOGGLE) {
|
||||
if (event.active) {
|
||||
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 {
|
||||
new_button_state |= pressed;
|
||||
new_param = param;
|
||||
new_button_state |= event.active;
|
||||
new_param = event.axis_value;
|
||||
}
|
||||
|
||||
} else if (axis != Axis::AxisMax) {
|
||||
@ -425,17 +410,18 @@ void ControllerOutput::AddUpdate(bool pressed, bool analog, u32 param) {
|
||||
case Axis::TriggerRight:
|
||||
// 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
|
||||
// warning: doesn't work yet
|
||||
new_param = SDL_clamp((pressed ? (s32)param : 0) + new_param, 0, 127);
|
||||
new_param = (event.active ? (s32)event.axis_value : 0) + new_param;
|
||||
break;
|
||||
default:
|
||||
// todo: do the same as above
|
||||
new_param = SDL_clamp((pressed ? (s32)param : 0) + new_param, -127, 127);
|
||||
new_param = (event.active ? (s32)event.axis_value : 0) + new_param;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
void ControllerOutput::FinalizeUpdate() {
|
||||
if (!state_changed) {
|
||||
// return;
|
||||
}
|
||||
old_button_state = new_button_state;
|
||||
old_param = new_param;
|
||||
float touchpad_x = 0;
|
||||
@ -454,12 +440,10 @@ void ControllerOutput::FinalizeUpdate() {
|
||||
case RIGHTJOYSTICK_HALFMODE:
|
||||
rightjoystick_halfmode = new_button_state;
|
||||
break;
|
||||
case KEY_TOGGLE:
|
||||
if (new_button_state) {
|
||||
// LOG_DEBUG(Input, "Toggling a button...");
|
||||
ToggleKeyInList(new_param);
|
||||
}
|
||||
break;
|
||||
// KEY_TOGGLE isn't handled here anymore, as this function doesn't have the necessary data
|
||||
// to do it, and it would be inconvenient to force it here, when AddUpdate does the job just
|
||||
// fine, and a toggle doesn't have to checked against every input that's bound to it, it's
|
||||
// enough that one is pressed
|
||||
default: // is a normal key (hopefully)
|
||||
controller->CheckButton(0, (OrbisPadButtonDataOffset)button, new_button_state);
|
||||
break;
|
||||
@ -493,44 +477,43 @@ void ControllerOutput::FinalizeUpdate() {
|
||||
|
||||
// Updates the list of pressed keys with the given input.
|
||||
// Returns whether the list was updated or not.
|
||||
bool UpdatePressedKeys(u32 value, bool is_pressed) {
|
||||
bool UpdatePressedKeys(InputEvent event) {
|
||||
// Skip invalid inputs
|
||||
if (value == (u32)-1) {
|
||||
InputID input = event.input;
|
||||
if (input.sdl_id == (u32)-1) {
|
||||
return false;
|
||||
}
|
||||
if ((value & 0x80000000) != 0) {
|
||||
if (input.type == InputType::Axis) {
|
||||
// analog input, it gets added when it first sends an event,
|
||||
// and from there, it only changes the parameter
|
||||
// reverse iterate until we get out of the 0x8000000 range, if found,
|
||||
// update the parameter, if not, add it to the end
|
||||
u32 value_to_search = value & 0xF00FFFFF;
|
||||
for (auto& it = --pressed_keys.end(); (it->first & 0x80000000) != 0; it--) {
|
||||
if ((it->first & 0xF00FFFFF) == value_to_search) {
|
||||
it->first = value;
|
||||
// LOG_DEBUG(Input, "New value for {:X}: {:x}", value, value);
|
||||
const auto& it = std::find_if(
|
||||
pressed_keys.begin(), pressed_keys.end(),
|
||||
[input](const std::pair<InputEvent, bool>& e) { return e.first.input == input; });
|
||||
if (it == pressed_keys.end()) {
|
||||
pressed_keys.push_back({event, false});
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
if (is_pressed) {
|
||||
if (event.active) {
|
||||
// Find the correct position for insertion to maintain order
|
||||
auto it =
|
||||
std::lower_bound(pressed_keys.begin(), pressed_keys.end(), value,
|
||||
[](const std::pair<u32, bool>& pk, u32 v) { return pk.first < v; });
|
||||
auto it = std::lower_bound(pressed_keys.begin(), pressed_keys.end(), input,
|
||||
[](const std::pair<InputEvent, bool>& e, InputID i) {
|
||||
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
|
||||
if (it == pressed_keys.end() || it->first != value) {
|
||||
pressed_keys.insert(it, {value, false});
|
||||
if (it == pressed_keys.end() || it->first.input != input) {
|
||||
pressed_keys.insert(it, {event, false});
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// Remove 'value' from the list if it's not pressed
|
||||
auto it =
|
||||
std::find_if(pressed_keys.begin(), pressed_keys.end(),
|
||||
[value](const std::pair<u32, bool>& pk) { return pk.first == value; });
|
||||
auto it = std::find_if(
|
||||
pressed_keys.begin(), pressed_keys.end(),
|
||||
[input](const std::pair<InputEvent, bool>& e) { return e.first.input == input; });
|
||||
if (it != pressed_keys.end()) {
|
||||
pressed_keys.erase(it);
|
||||
return true;
|
||||
@ -538,17 +521,28 @@ bool UpdatePressedKeys(u32 value, bool is_pressed) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// Check if a given binding's all keys are currently active.
|
||||
// For now it also extracts the analog inputs' parameters.
|
||||
void IsInputActive(BindingConnection& connection, bool& active, bool& analog) {
|
||||
// Check if the binding's all keys are currently active.
|
||||
// It also extracts the analog inputs' parameters, and updates the input hierarchy flags.
|
||||
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()) {
|
||||
active = false;
|
||||
return;
|
||||
return event;
|
||||
}
|
||||
InputBinding i = connection.binding;
|
||||
// Extract keys from InputBinding and ignore unused (0) or toggled keys
|
||||
std::list<u32> input_keys = {i.key1, i.key2, i.key3};
|
||||
input_keys.remove(0);
|
||||
if (event.input.type != InputType::Axis) {
|
||||
// for button inputs
|
||||
event.axis_value = axis_param;
|
||||
}
|
||||
// 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();) {
|
||||
if (std::find(toggled_keys.begin(), toggled_keys.end(), *key) != toggled_keys.end()) {
|
||||
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()) {
|
||||
LOG_DEBUG(Input, "No actual inputs to check, returning true");
|
||||
active = true;
|
||||
return;
|
||||
event.active = true;
|
||||
return event;
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
// Check if all keys in input_keys are active
|
||||
for (u32 key : input_keys) {
|
||||
for (InputID key : input_keys) {
|
||||
bool key_found = false;
|
||||
|
||||
// Search for the current key in pressed_keys starting from the last checked position
|
||||
while (pressed_it != pressed_keys.end() && (pressed_it->first & 0x80000000) == 0) {
|
||||
if (pressed_it->first == key) {
|
||||
|
||||
while (pressed_it != pressed_keys.end()) {
|
||||
if (pressed_it->first.input == key && pressed_it->second == false) {
|
||||
key_found = true;
|
||||
flags_to_set.push_back(&pressed_it->second);
|
||||
|
||||
++pressed_it; // Move to the next key in pressed_keys
|
||||
if (pressed_it->first.input.type == InputType::Axis) {
|
||||
event.axis_value = pressed_it->first.axis_value;
|
||||
}
|
||||
++pressed_it;
|
||||
break;
|
||||
}
|
||||
++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) {
|
||||
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) {
|
||||
*flag = true;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Input, "Input found: {}", i.ToString());
|
||||
active = true;
|
||||
return; // All keys are active
|
||||
LOG_DEBUG(Input, "Input found: {}", binding.ToString());
|
||||
event.active = true;
|
||||
return event; // All keys are active
|
||||
}
|
||||
|
||||
void ActivateOutputsFromInputs() {
|
||||
// LOG_DEBUG(Input, "Start of an input frame...");
|
||||
// Reset values and flags
|
||||
for (auto& it : pressed_keys) {
|
||||
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) {
|
||||
it.ResetUpdate();
|
||||
}
|
||||
|
||||
// Iterate over all inputs, and update their respecive outputs accordingly
|
||||
for (auto& it : connections) {
|
||||
bool active = false, analog_input_detected = false;
|
||||
IsInputActive(it, active, analog_input_detected);
|
||||
it.output->AddUpdate(active, analog_input_detected, it.parameter);
|
||||
it.output->AddUpdate(it.ProcessBinding());
|
||||
}
|
||||
|
||||
// Update all outputs
|
||||
for (auto& it : output_array) {
|
||||
it.FinalizeUpdate();
|
||||
}
|
||||
|
@ -41,6 +41,42 @@ struct AxisMapping {
|
||||
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
|
||||
const std::map<std::string, OrbisPadButtonDataOffset> string_to_cbutton_map = {
|
||||
{"triangle", OrbisPadButtonDataOffset::Triangle},
|
||||
@ -192,79 +228,84 @@ void ParseInputConfig(const std::string game_id);
|
||||
|
||||
class InputBinding {
|
||||
public:
|
||||
u32 key1, key2, key3;
|
||||
InputBinding(u32 k1 = SDLK_UNKNOWN, u32 k2 = SDLK_UNKNOWN, u32 k3 = SDLK_UNKNOWN) {
|
||||
InputID keys[3];
|
||||
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
|
||||
// compare 3 sorted elements, where the only possible duplicate item is 0
|
||||
|
||||
// duplicate entries get changed to one original, one null
|
||||
if (k1 == k2 && k1 != SDLK_UNKNOWN) {
|
||||
k2 = 0;
|
||||
if (k1 == k2 && k1 != InputID()) {
|
||||
k2 = InputID();
|
||||
}
|
||||
if (k1 == k3 && k1 != SDLK_UNKNOWN) {
|
||||
k3 = 0;
|
||||
if (k1 == k3 && k1 != InputID()) {
|
||||
k3 = InputID();
|
||||
}
|
||||
if (k3 == k2 && k2 != SDLK_UNKNOWN) {
|
||||
k2 = 0;
|
||||
if (k3 == k2 && k2 != InputID()) {
|
||||
k2 = InputID();
|
||||
}
|
||||
// this sorts them
|
||||
if (k1 <= k2 && k1 <= k3) {
|
||||
key1 = k1;
|
||||
keys[0] = k1;
|
||||
if (k2 <= k3) {
|
||||
key2 = k2;
|
||||
key3 = k3;
|
||||
keys[1] = k2;
|
||||
keys[2] = k3;
|
||||
} else {
|
||||
key2 = k3;
|
||||
key3 = k2;
|
||||
keys[1] = k3;
|
||||
keys[2] = k2;
|
||||
}
|
||||
} else if (k2 <= k1 && k2 <= k3) {
|
||||
key1 = k2;
|
||||
keys[0] = k2;
|
||||
if (k1 <= k3) {
|
||||
key2 = k1;
|
||||
key3 = k3;
|
||||
keys[1] = k1;
|
||||
keys[2] = k3;
|
||||
} else {
|
||||
key2 = k3;
|
||||
key3 = k1;
|
||||
keys[1] = k3;
|
||||
keys[2] = k1;
|
||||
}
|
||||
} else {
|
||||
key1 = k3;
|
||||
keys[0] = k3;
|
||||
if (k1 <= k2) {
|
||||
key2 = k1;
|
||||
key3 = k2;
|
||||
keys[1] = k1;
|
||||
keys[2] = k2;
|
||||
} else {
|
||||
key2 = k2;
|
||||
key3 = k1;
|
||||
keys[1] = k2;
|
||||
keys[3] = k1;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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) {
|
||||
// 0 = SDLK_UNKNOWN aka unused slot
|
||||
return (key3 == o.key3 || key3 == 0 || o.key3 == 0) &&
|
||||
(key2 == o.key2 || key2 == 0 || o.key2 == 0) &&
|
||||
(key1 == o.key1 || key1 == 0 || o.key1 == 0);
|
||||
// InputID() signifies an unused slot
|
||||
return (keys[2] == o.keys[2] || keys[2] == InputID() || o.keys[2] == InputID()) &&
|
||||
(keys[1] == o.keys[1] || keys[1] == InputID() || o.keys[1] == InputID()) &&
|
||||
(keys[0] == o.keys[0] || keys[0] == InputID() || o.keys[0] == InputID());
|
||||
// it is already very fast,
|
||||
// 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
|
||||
}
|
||||
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
|
||||
bool operator<(const InputBinding& other) const {
|
||||
return KeyCount() > other.KeyCount();
|
||||
}
|
||||
inline bool IsEmpty() {
|
||||
return key1 == 0 && key2 == 0 && key3 == 0;
|
||||
return !(keys[0].IsValid() || keys[1].IsValid() || keys[2].IsValid());
|
||||
}
|
||||
std::string ToString() {
|
||||
return fmt::format("({:X}, {:X}, {:X})", key1, key2, key3);
|
||||
std::string ToString() { // todo add device type
|
||||
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)
|
||||
static u32 GetInputIDFromEvent(const SDL_Event& e);
|
||||
// returns an InputEvent based on the event type (keyboard, mouse buttons/wheel, or controller)
|
||||
static InputEvent GetInputEventFromSDLEvent(const SDL_Event& e);
|
||||
};
|
||||
class ControllerOutput {
|
||||
static GameController* controller;
|
||||
@ -300,19 +341,21 @@ public:
|
||||
}
|
||||
|
||||
void ResetUpdate();
|
||||
void AddUpdate(bool pressed, bool analog, u32 param = 0);
|
||||
void AddUpdate(InputEvent event);
|
||||
void FinalizeUpdate();
|
||||
};
|
||||
class BindingConnection {
|
||||
public:
|
||||
InputBinding binding;
|
||||
ControllerOutput* output;
|
||||
u32 parameter;
|
||||
BindingConnection(InputBinding b, ControllerOutput* out, u32 param = 0) {
|
||||
binding = b;
|
||||
parameter = param;
|
||||
u32 axis_param;
|
||||
InputID toggle;
|
||||
|
||||
BindingConnection(InputBinding b, ControllerOutput* out, u32 param = 0, InputID t = InputID()) {
|
||||
binding = b;
|
||||
axis_param = param;
|
||||
output = out;
|
||||
toggle = t;
|
||||
}
|
||||
bool operator<(const BindingConnection& other) const {
|
||||
// a button is a higher priority than an axis, as buttons can influence axes
|
||||
@ -327,11 +370,12 @@ public:
|
||||
}
|
||||
return false;
|
||||
}
|
||||
InputEvent ProcessBinding();
|
||||
};
|
||||
|
||||
// Updates the list of pressed keys with the given input.
|
||||
// Returns whether the list was updated or not.
|
||||
bool UpdatePressedKeys(u32 button, bool is_pressed);
|
||||
bool UpdatePressedKeys(InputEvent event);
|
||||
|
||||
void ActivateOutputsFromInputs();
|
||||
|
||||
|
@ -254,10 +254,11 @@ void WindowSDL::OnKeyboardMouseInput(const SDL_Event* event) {
|
||||
const bool input_down = event->type == SDL_EVENT_KEY_DOWN ||
|
||||
event->type == SDL_EVENT_MOUSE_BUTTON_DOWN ||
|
||||
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
|
||||
if (event->type == SDL_EVENT_KEY_DOWN) {
|
||||
u32 input_id = input_event.input.sdl_id;
|
||||
// Reparse kbm inputs
|
||||
if (input_id == SDLK_F8) {
|
||||
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
|
||||
bool inputs_changed = Input::UpdatePressedKeys(input_id, input_down);
|
||||
bool inputs_changed = Input::UpdatePressedKeys(input_event);
|
||||
|
||||
// update bindings
|
||||
if (inputs_changed) {
|
||||
|
Loading…
Reference in New Issue
Block a user