Initial controller support

This commit is contained in:
kalaposfos13 2024-11-24 21:00:17 +01:00
parent 4c0c3d30d7
commit 675559b1ba
4 changed files with 238 additions and 148 deletions

View File

@ -704,19 +704,21 @@ void setDefaultValues() {
constexpr std::string_view GetDefaultKeyboardConfig() { constexpr std::string_view GetDefaultKeyboardConfig() {
return R"(#Feeling lost? Check out the Help section! return R"(#Feeling lost? Check out the Help section!
#Keyboard bindings
triangle = f triangle = f
circle = space circle = space
cross = e cross = e
square = r square = r
up = w, lalt pad_up = w, lalt
up = mousewheelup pad_up = mousewheelup
down = s, lalt pad_down = s, lalt
down = mousewheeldown pad_down = mousewheeldown
left = a, lalt pad_left = a, lalt
left = mousewheelleft pad_left = mousewheelleft
right = d, lalt pad_right = d, lalt
right = mousewheelright pad_right = mousewheelright
l1 = rightbutton, lshift l1 = rightbutton, lshift
r1 = leftbutton r1 = leftbutton
@ -726,21 +728,45 @@ l3 = x
r3 = q r3 = q
r3 = middlebutton r3 = middlebutton
key_toggle = i, lalt
options = escape options = escape
touchpad = g touchpad = g
key_toggle = i, lalt
mouse_to_joystick = right mouse_to_joystick = right
mouse_movement_params = 0.5, 1, 0.125 mouse_movement_params = 0.5, 1, 0.125
leftjoystick_halfmode = lctrl leftjoystick_halfmode = lctrl
axis_left_x_minus = a axis_left_x_minus = a
axis_left_x_plus = d axis_left_x_plus = d
axis_left_y_minus = w axis_left_y_minus = w
axis_left_y_plus = s axis_left_y_plus = s
#Controller bindings
triangle = triangle
cross = cross
square = square
circle = circle
l1 = l1
l2 = l2
l3 = l3
r1 = r1
r2 = r2
r3 = r3
pad_up = pad_up
pad_down = pad_down
pad_left = pad_left
pad_right = pad_right
options = options
axis_left_x = axis_left_x
axis_left_y = axis_left_y
axis_right_x = axis_right_x
axis_right_y = axis_right_y
)"; )";
} }
std::filesystem::path GetFoolproofKbmConfigFile(const std::string& game_id) { std::filesystem::path GetFoolproofKbmConfigFile(const std::string& game_id) {
@ -748,7 +774,7 @@ std::filesystem::path GetFoolproofKbmConfigFile(const std::string& game_id) {
// If that doesn't exist either, generate that from getDefaultConfig() and try again // If that doesn't exist either, generate that from getDefaultConfig() and try again
// If even the folder is missing, we start with that. // If even the folder is missing, we start with that.
const auto config_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "kbmConfig"; const auto config_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "inputConfig";
const auto config_file = config_dir / (game_id + ".ini"); const auto config_file = config_dir / (game_id + ".ini");
const auto default_config_file = config_dir / "default.ini"; const auto default_config_file = config_dir / "default.ini";

View File

@ -98,7 +98,62 @@ ControllerOutput output_array[] = {
void ToggleMouseEnabled() { void ToggleMouseEnabled() {
mouse_enabled ^= true; mouse_enabled ^= true;
} }
// parsing related functions // parsing related functions
u32 GetAxisInputId(AxisMapping a) {
//LOG_INFO(Input, "Parsing an axis...");
if (a.axis == Axis::AxisMax || a.value != 0) {
LOG_ERROR(Input, "Invalid axis given!");
return 0;
}
u32 value = (u32)a.axis + 0x80000000;
LOG_DEBUG(Input, "Listening to {0:X}", value);
return value;
}
u32 GetOrbisToSdlButtonKeycode(u32 cbutton) {
switch (cbutton) {
case OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_CIRCLE:
return SDL_GAMEPAD_BUTTON_EAST;
case OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_TRIANGLE:
return SDL_GAMEPAD_BUTTON_NORTH;
case OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_SQUARE:
return SDL_GAMEPAD_BUTTON_WEST;
case OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_CROSS:
return SDL_GAMEPAD_BUTTON_SOUTH;
case OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_L1:
return SDL_GAMEPAD_BUTTON_LEFT_SHOULDER;
case OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R1:
return SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER;
case OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_L3:
return SDL_GAMEPAD_BUTTON_LEFT_STICK;
case OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R3:
return SDL_GAMEPAD_BUTTON_RIGHT_STICK;
case OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_UP:
return SDL_GAMEPAD_BUTTON_DPAD_UP;
case OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_DOWN:
return SDL_GAMEPAD_BUTTON_DPAD_DOWN;
case OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_LEFT:
return SDL_GAMEPAD_BUTTON_DPAD_LEFT;
case OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_RIGHT:
return SDL_GAMEPAD_BUTTON_DPAD_RIGHT;
case OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_OPTIONS:
return SDL_GAMEPAD_BUTTON_START;
default:
return ((u32)-1) - 0x10000000;
}
}
u32 GetControllerButtonInputId(u32 cbutton) {
if((cbutton & (OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_TOUCH_PAD |
LEFTJOYSTICK_HALFMODE |
RIGHTJOYSTICK_HALFMODE)) != 0) {
//LOG_ERROR(Input, "You can't use this as a button input!");
return (u32)-1;
}
return GetOrbisToSdlButtonKeycode(cbutton) + 0x10000000;
}
// 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) {
@ -113,21 +168,41 @@ InputBinding GetBindingFromString(std::string& line) {
tokens.push_back(token); tokens.push_back(token);
} }
// Check for invalid tokens and map valid ones to keys // Check and process tokens
for (const auto& t : tokens) { for (const auto& t : tokens) {
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()) {
return InputBinding(0, 0, 0); // Skip by setting all keys to 0 // 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;
}
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;
}
else if (string_to_cbutton_map.find(t) != string_to_cbutton_map.end()) {
// Map to controller button input ID
u32 cbutton_id = GetControllerButtonInputId(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 {
// Invalid token found; return default binding
return InputBinding(0, 0, 0);
} }
} }
// Assign values to keys if all tokens were valid
if (tokens.size() > 0)
key1 = string_to_keyboard_key_map.at(tokens[0]);
if (tokens.size() > 1)
key2 = string_to_keyboard_key_map.at(tokens[1]);
if (tokens.size() > 2)
key3 = string_to_keyboard_key_map.at(tokens[2]);
return InputBinding(key1, key2, key3); return InputBinding(key1, key2, key3);
} }
@ -176,8 +251,8 @@ void ParseInputConfig(const std::string game_id = "") {
// Split the line by '=' // Split the line by '='
std::size_t equal_pos = line.find('='); std::size_t equal_pos = line.find('=');
if (equal_pos == std::string::npos) { if (equal_pos == std::string::npos) {
LOG_ERROR(Input, "Invalid format at line: {}, data: \"{}\", skipping line.", lineCount, LOG_WARNING(Input, "Invalid format at line: {}, data: \"{}\", skipping line.",
line); lineCount, line);
continue; continue;
} }
@ -185,7 +260,6 @@ void ParseInputConfig(const std::string game_id = "") {
std::string input_string = line.substr(equal_pos + 1); std::string input_string = line.substr(equal_pos + 1);
std::size_t comma_pos = input_string.find(','); std::size_t comma_pos = input_string.find(',');
// special check for mouse to joystick input
if (output_string == "mouse_to_joystick") { if (output_string == "mouse_to_joystick") {
if (input_string == "left") { if (input_string == "left") {
mouse_joystick_binding = 1; mouse_joystick_binding = 1;
@ -196,13 +270,12 @@ void ParseInputConfig(const std::string game_id = "") {
} }
continue; continue;
} }
// key toggle
if (output_string == "key_toggle") { if (output_string == "key_toggle") {
if (comma_pos != std::string::npos) { if (comma_pos != std::string::npos) {
// handle key-to-key toggling (separate list?) // handle key-to-key toggling (separate list?)
InputBinding toggle_keys = GetBindingFromString(input_string); InputBinding toggle_keys = GetBindingFromString(input_string);
if (toggle_keys.KeyCount() != 2) { if (toggle_keys.KeyCount() != 2) {
LOG_ERROR(Input, LOG_WARNING(Input,
"Syntax error: Please provide exactly 2 keys: " "Syntax error: Please provide exactly 2 keys: "
"first is the toggler, the second is the key to toggle: {}", "first is the toggler, the second is the key to toggle: {}",
line); line);
@ -214,7 +287,7 @@ void ParseInputConfig(const std::string game_id = "") {
connections.insert(connections.end(), toggle_connection); connections.insert(connections.end(), toggle_connection);
continue; continue;
} }
LOG_ERROR(Input, "Invalid format at line: {}, data: \"{}\", skipping line.", lineCount, LOG_WARNING(Input, "Invalid format at line: {}, data: \"{}\", skipping line.", lineCount,
line); line);
continue; continue;
} }
@ -225,12 +298,8 @@ void ParseInputConfig(const std::string game_id = "") {
// Check for invalid input (in case there's an unexpected format) // Check for invalid input (in case there's an unexpected format)
if (ss.fail()) { if (ss.fail()) {
LOG_ERROR(Input, "Failed to parse mouse movement parameters from line: {}", line); LOG_WARNING(Input, "Failed to parse mouse movement parameters from line: {}", line);
} else {
// LOG_DEBUG(Input, "Mouse movement parameters parsed: {} {} {}",
// mouse_deadzone_offset, mouse_speed, mouse_speed_offset);
} }
continue; continue;
} }
@ -241,7 +310,7 @@ void ParseInputConfig(const std::string game_id = "") {
auto axis_it = string_to_axis_map.find(output_string); auto axis_it = string_to_axis_map.find(output_string);
if (binding.IsEmpty()) { if (binding.IsEmpty()) {
LOG_DEBUG(Input, "Invalid format at line: {}, data: \"{}\", skipping line.", lineCount, LOG_WARNING(Input, "Invalid format at line: {}, data: \"{}\", skipping line.", lineCount,
line); line);
continue; continue;
} }
@ -251,12 +320,15 @@ 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 :
(axis_it->second.axis == Axis::TriggerLeft || axis_it->second.axis == Axis::TriggerRight) ?
127 : axis_it->second.value;
connection = BindingConnection( connection = BindingConnection(
binding, GetOutputPointer(ControllerOutput(0, axis_it->second.axis)), binding, GetOutputPointer(ControllerOutput(0, axis_it->second.axis)),
axis_it->second.value); value_to_set);
connections.insert(connections.end(), connection); connections.insert(connections.end(), connection);
} else { } else {
LOG_DEBUG(Input, "Invalid format at line: {}, data: \"{}\", skipping line.", lineCount, LOG_WARNING(Input, "Invalid format at line: {}, data: \"{}\", skipping line.", lineCount,
line); line);
continue; continue;
} }
@ -270,7 +342,7 @@ void ParseInputConfig(const std::string game_id = "") {
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_DEBUG(Input, "Something went wrong with wheel input parsing!");
return 0; return (u32)-1;
} }
if (event.wheel.y > 0) { if (event.wheel.y > 0) {
return SDL_MOUSE_WHEEL_UP; return SDL_MOUSE_WHEEL_UP;
@ -285,6 +357,7 @@ u32 GetMouseWheelEvent(const SDL_Event& event) {
} }
u32 InputBinding::GetInputIDFromEvent(const SDL_Event& e) { u32 InputBinding::GetInputIDFromEvent(const SDL_Event& e) {
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:
@ -295,6 +368,15 @@ u32 InputBinding::GetInputIDFromEvent(const SDL_Event& e) {
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 GetMouseWheelEvent(e);
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
case SDL_EVENT_GAMEPAD_BUTTON_UP:
return (u32)e.gbutton.button + 0x10000000; // I believe this range is unused
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
default: default:
return (u32)-1; return (u32)-1;
} }
@ -306,6 +388,10 @@ void ControllerOutput::SetControllerOutputController(GameController* c) {
} }
void ToggleKeyInList(u32 key) { void ToggleKeyInList(u32 key) {
if ((key & 0x80000000) != 0) {
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(), key);
if (it == toggled_keys.end()) { if (it == toggled_keys.end()) {
toggled_keys.insert(toggled_keys.end(), key); toggled_keys.insert(toggled_keys.end(), key);
@ -327,13 +413,16 @@ void ControllerOutput::AddUpdate(bool pressed, u32 param) {
new_button_state |= pressed; new_button_state |= pressed;
new_param = param; new_param = param;
} else if (axis != Axis::AxisMax) { } else if (axis != Axis::AxisMax) {
float multiplier = 1.0;
switch (axis) { switch (axis) {
case Axis::TriggerLeft: case Axis::TriggerLeft:
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 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 = SDL_clamp((pressed ? (s32)param : 0) + new_param, 0, 127);
break; break;
default: default:
// todo: do the same as above
new_param = SDL_clamp((pressed ? (s32)param : 0) + new_param, -127, 127); new_param = SDL_clamp((pressed ? (s32)param : 0) + new_param, -127, 127);
break; break;
} }
@ -401,6 +490,28 @@ 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(u32 value, bool is_pressed) {
// Skip invalid inputs
if (value == (u32)-1) {
return false;
}
if ((value & 0x80000000) != 0) {
// 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
//LOG_DEBUG(Input, "Updating an analog input...");
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);
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 (is_pressed) {
// Find the correct position for insertion to maintain order // Find the correct position for insertion to maintain order
auto it = auto it =
@ -425,9 +536,11 @@ bool UpdatePressedKeys(u32 value, bool is_pressed) {
return false; return false;
} }
// Check if a given binding's all keys are currently active. // Check if a given binding's all keys are currently active.
bool IsInputActive(const InputBinding& i) { // For now it also extracts the analog inputs' parameters.
// Extract keys from InputBinding and ignore unused (0) or virtually pressed keys bool IsInputActive(BindingConnection& connection) {
std::list<uint32_t> input_keys = {i.key1, i.key2, i.key3}; 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); input_keys.remove(0);
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()) {
@ -436,20 +549,23 @@ bool IsInputActive(const InputBinding& i) {
++key; // Increment only if no erase happened ++key; // Increment only if no erase happened
} }
} }
if (input_keys.empty()) {
LOG_DEBUG(Input, "No actual inputs to check, returning true");
return true;
}
// Iterator for pressed_keys, starting from the beginning // Iterator for pressed_keys, starting from the beginning
auto pressed_it = pressed_keys.begin(); auto pressed_it = pressed_keys.begin();
auto pressed_end = pressed_keys.end();
// Store pointers to flags in pressed_keys that need to be set if all keys are active // Store pointers to flags in pressed_keys that need to be set if all keys are active
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 (uint32_t key : input_keys) { for (u32 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 // Search for the current key in pressed_keys starting from the last checked position
while (pressed_it != pressed_end && pressed_it->first <= key) { while (pressed_it != pressed_keys.end() && (pressed_it->first & 0x80000000) == 0) {
if (pressed_it->first == key) { if (pressed_it->first == key) {
key_found = true; key_found = true;
@ -460,6 +576,20 @@ bool IsInputActive(const InputBinding& i) {
} }
++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;
flags_to_set.push_back(&rev_it->second);
break;
}
}
}
if (!key_found) { if (!key_found) {
return false; return false;
} }
@ -493,7 +623,8 @@ void ActivateOutputsFromInputs() {
it.ResetUpdate(); it.ResetUpdate();
} }
for (auto& it : connections) { for (auto& it : connections) {
it.output->AddUpdate(IsInputActive(it.binding), it.parameter); bool active = IsInputActive(it);
it.output->AddUpdate(active, it.parameter);
} }
for (auto& it : output_array) { for (auto& it : output_array) {
it.FinalizeUpdate(); it.FinalizeUpdate();

View File

@ -49,12 +49,14 @@ const std::map<std::string, u32> string_to_cbutton_map = {
{"r1", OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R1}, {"r1", OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R1},
{"l3", OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_L3}, {"l3", OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_L3},
{"r3", OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R3}, {"r3", OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R3},
{"pad_up", OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_UP},
{"pad_down", OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_DOWN},
{"pad_left", OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_LEFT},
{"pad_right", OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_RIGHT},
{"options", OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_OPTIONS}, {"options", OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_OPTIONS},
// these are outputs only (touchpad can only be bound to itself)
{"touchpad", OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_TOUCH_PAD}, {"touchpad", OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_TOUCH_PAD},
{"up", OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_UP},
{"down", OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_DOWN},
{"left", OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_LEFT},
{"right", OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_RIGHT},
{"leftjoystick_halfmode", LEFTJOYSTICK_HALFMODE}, {"leftjoystick_halfmode", LEFTJOYSTICK_HALFMODE},
{"rightjoystick_halfmode", RIGHTJOYSTICK_HALFMODE}, {"rightjoystick_halfmode", RIGHTJOYSTICK_HALFMODE},
}; };
@ -67,8 +69,15 @@ const std::map<std::string, AxisMapping> string_to_axis_map = {
{"axis_right_x_minus", {Input::Axis::RightX, -127}}, {"axis_right_x_minus", {Input::Axis::RightX, -127}},
{"axis_right_y_plus", {Input::Axis::RightY, 127}}, {"axis_right_y_plus", {Input::Axis::RightY, 127}},
{"axis_right_y_minus", {Input::Axis::RightY, -127}}, {"axis_right_y_minus", {Input::Axis::RightY, -127}},
{"l2", {Axis::TriggerLeft, 127}},
{"r2", {Axis::TriggerRight, 127}}, {"l2", {Axis::TriggerLeft, 0}},
{"r2", {Axis::TriggerRight, 0}},
// should only use these to bind analog inputs to analog outputs
{"axis_left_x", {Input::Axis::LeftX, 0}},
{"axis_left_y", {Input::Axis::LeftY, 0}},
{"axis_right_x", {Input::Axis::RightX, 0}},
{"axis_right_y", {Input::Axis::RightY, 0}},
}; };
const std::map<std::string, u32> string_to_keyboard_key_map = { const std::map<std::string, u32> string_to_keyboard_key_map = {
{"a", SDLK_A}, {"a", SDLK_A},
@ -248,8 +257,8 @@ public:
inline bool IsEmpty() { inline bool IsEmpty() {
return key1 == 0 && key2 == 0 && key3 == 0; return key1 == 0 && key2 == 0 && key3 == 0;
} }
std::string ToString() const { std::string ToString() {
return fmt::format("({}, {}, {})", key1, key2, key3); return fmt::format("({:X}, {:X}, {:X})", key1, key2, key3);
} }
// returns a u32 based on the event type (keyboard, mouse buttons, or wheel) // returns a u32 based on the event type (keyboard, mouse buttons, or wheel)

View File

@ -116,14 +116,20 @@ void WindowSDL::waitEvent() {
case SDL_EVENT_KEY_UP: case SDL_EVENT_KEY_UP:
OnKeyboardMouseInput(&event); OnKeyboardMouseInput(&event);
break; break;
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
case SDL_EVENT_GAMEPAD_BUTTON_UP:
case SDL_EVENT_GAMEPAD_AXIS_MOTION:
case SDL_EVENT_GAMEPAD_ADDED: case SDL_EVENT_GAMEPAD_ADDED:
case SDL_EVENT_GAMEPAD_REMOVED: case SDL_EVENT_GAMEPAD_REMOVED:
controller->TryOpenSDLController();
break;
case SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN: case SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN:
case SDL_EVENT_GAMEPAD_TOUCHPAD_UP: case SDL_EVENT_GAMEPAD_TOUCHPAD_UP:
case SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION: case SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION:
controller->SetTouchpadState(event.gtouchpad.finger,
event.type != SDL_EVENT_GAMEPAD_TOUCHPAD_UP,
event.gtouchpad.x, event.gtouchpad.y);
break;
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
case SDL_EVENT_GAMEPAD_BUTTON_UP:
case SDL_EVENT_GAMEPAD_AXIS_MOTION:
OnGamepadEvent(&event); OnGamepadEvent(&event);
break; break;
case SDL_EVENT_QUIT: case SDL_EVENT_QUIT:
@ -205,99 +211,17 @@ void WindowSDL::OnKeyboardMouseInput(const SDL_Event* event) {
} }
void WindowSDL::OnGamepadEvent(const SDL_Event* event) { void WindowSDL::OnGamepadEvent(const SDL_Event* event) {
using Libraries::Pad::OrbisPadButtonDataOffset;
u32 button = 0; bool input_down = event->type == SDL_EVENT_GAMEPAD_AXIS_MOTION ||
Input::Axis axis = Input::Axis::AxisMax; event->type == SDL_EVENT_GAMEPAD_BUTTON_DOWN ||
switch (event->type) { event->type == SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN ||
case SDL_EVENT_GAMEPAD_ADDED: event->type == SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION;
case SDL_EVENT_GAMEPAD_REMOVED: u32 input_id = Input::InputBinding::GetInputIDFromEvent(*event);
controller->TryOpenSDLController();
break;
case SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN:
case SDL_EVENT_GAMEPAD_TOUCHPAD_UP:
case SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION:
controller->SetTouchpadState(event->gtouchpad.finger,
event->type != SDL_EVENT_GAMEPAD_TOUCHPAD_UP,
event->gtouchpad.x, event->gtouchpad.y);
break;
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
case SDL_EVENT_GAMEPAD_BUTTON_UP:
button = sdlGamepadToOrbisButton(event->gbutton.button);
if (button != 0) {
if (event->gbutton.button == SDL_GAMEPAD_BUTTON_BACK) {
std::string backButtonBehavior = Config::getBackButtonBehavior();
if (backButtonBehavior != "none") {
float x = backButtonBehavior == "left"
? 0.25f
: (backButtonBehavior == "right" ? 0.75f : 0.5f);
// trigger a touchpad event so that the touchpad emulation for back button works
controller->SetTouchpadState(0, true, x, 0.5f);
controller->CheckButton(0, button,
event->type == SDL_EVENT_GAMEPAD_BUTTON_DOWN);
}
} else {
controller->CheckButton(0, button, event->type == SDL_EVENT_GAMEPAD_BUTTON_DOWN);
}
}
break;
case SDL_EVENT_GAMEPAD_AXIS_MOTION:
axis = event->gaxis.axis == SDL_GAMEPAD_AXIS_LEFTX ? Input::Axis::LeftX
: event->gaxis.axis == SDL_GAMEPAD_AXIS_LEFTY ? Input::Axis::LeftY
: event->gaxis.axis == SDL_GAMEPAD_AXIS_RIGHTX ? Input::Axis::RightX
: event->gaxis.axis == SDL_GAMEPAD_AXIS_RIGHTY ? Input::Axis::RightY
: event->gaxis.axis == SDL_GAMEPAD_AXIS_LEFT_TRIGGER ? Input::Axis::TriggerLeft
: event->gaxis.axis == SDL_GAMEPAD_AXIS_RIGHT_TRIGGER ? Input::Axis::TriggerRight
: Input::Axis::AxisMax;
if (axis != Input::Axis::AxisMax) {
if (event->gaxis.axis == SDL_GAMEPAD_AXIS_LEFT_TRIGGER ||
event->gaxis.axis == SDL_GAMEPAD_AXIS_RIGHT_TRIGGER) {
controller->Axis(0, axis, Input::GetAxis(0, 0x8000, event->gaxis.value));
} else { bool inputs_changed = Input::UpdatePressedKeys(input_id, input_down);
controller->Axis(0, axis, Input::GetAxis(-0x8000, 0x8000, event->gaxis.value));
}
}
break;
}
}
int WindowSDL::sdlGamepadToOrbisButton(u8 button) { if (inputs_changed) {
using Libraries::Pad::OrbisPadButtonDataOffset; Input::ActivateOutputsFromInputs();
switch (button) {
case SDL_GAMEPAD_BUTTON_DPAD_DOWN:
return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_DOWN;
case SDL_GAMEPAD_BUTTON_DPAD_UP:
return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_UP;
case SDL_GAMEPAD_BUTTON_DPAD_LEFT:
return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_LEFT;
case SDL_GAMEPAD_BUTTON_DPAD_RIGHT:
return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_RIGHT;
case SDL_GAMEPAD_BUTTON_SOUTH:
return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_CROSS;
case SDL_GAMEPAD_BUTTON_NORTH:
return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_TRIANGLE;
case SDL_GAMEPAD_BUTTON_WEST:
return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_SQUARE;
case SDL_GAMEPAD_BUTTON_EAST:
return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_CIRCLE;
case SDL_GAMEPAD_BUTTON_START:
return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_OPTIONS;
case SDL_GAMEPAD_BUTTON_TOUCHPAD:
return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_TOUCH_PAD;
case SDL_GAMEPAD_BUTTON_BACK:
return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_TOUCH_PAD;
case SDL_GAMEPAD_BUTTON_LEFT_SHOULDER:
return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_L1;
case SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER:
return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R1;
case SDL_GAMEPAD_BUTTON_LEFT_STICK:
return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_L3;
case SDL_GAMEPAD_BUTTON_RIGHT_STICK:
return OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R3;
default:
return 0;
} }
} }