diff --git a/src/input/input_handler.cpp b/src/input/input_handler.cpp index 8883d1099..c72689c07 100644 --- a/src/input/input_handler.cpp +++ b/src/input/input_handler.cpp @@ -6,6 +6,7 @@ #include "fstream" #include "iostream" #include "map" +#include "unordered_map" #include "list" #include "sstream" #include "string" @@ -45,19 +46,45 @@ float mouse_deadzone_offset = 0.5, mouse_speed = 1, mouse_speed_offset = 0.1250; Uint32 mouse_polling_id = 0; bool mouse_enabled = false, leftjoystick_halfmode = false, rightjoystick_halfmode = false; -// todo +std::list pressed_keys = std::list(); +std::list connections = std::list(); + +void toggleMouseEnabled() { + mouse_enabled ^= true; +} ControllerOutput output_array[] = { + // Button mappings + ControllerOutput(OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_TRIANGLE), + ControllerOutput(OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_CIRCLE), + ControllerOutput(OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_CROSS), + ControllerOutput(OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_SQUARE), + ControllerOutput(OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_L1), + ControllerOutput(OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_L2), + ControllerOutput(OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R1), + ControllerOutput(OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R2), + ControllerOutput(OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_L3), + ControllerOutput(OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R3), + ControllerOutput(OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_OPTIONS), + ControllerOutput(OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_TOUCH_PAD), ControllerOutput(OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_UP), ControllerOutput(OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_DOWN), + ControllerOutput(OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_LEFT), + ControllerOutput(OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_RIGHT), - ControllerOutput(0, Axis::TriggerLeft), - ControllerOutput(0, Axis::LeftY), - // etc. + // Axis mappings + ControllerOutput(0, Input::Axis::LeftX), + ControllerOutput(0, Input::Axis::LeftY), + ControllerOutput(0, Input::Axis::RightX), + ControllerOutput(0, Input::Axis::RightY), + ControllerOutput(0, Input::Axis::TriggerLeft), + ControllerOutput(0, Input::Axis::TriggerRight), - // signifies the end of the array - ControllerOutput(0, Axis::AxisMax), + ControllerOutput(LEFTJOYSTICK_HALFMODE), + ControllerOutput(RIGHTJOYSTICK_HALFMODE), + + // End marker to signify the end of the array + ControllerOutput(0, Input::Axis::AxisMax) }; -std::list connections = std::list(); // parsing related functions @@ -167,6 +194,10 @@ void parseInputConfig(const std::string game_id = "") { LOG_ERROR(Input, "Invalid format at line: {}, data: \"{}\", skipping line.", lineCount, line); continue; } + if (before_equals == "mouse_movement_params") { + LOG_ERROR(Input, "todo"); + continue; + } // normal cases InputBinding binding = getBindingFromString(after_equals); @@ -180,78 +211,15 @@ void parseInputConfig(const std::string game_id = "") { } if (button_it != string_to_cbutton_map.end()) { connection = BindingConnection(binding, getOutputPointer(ControllerOutput(button_it->second))); - connections.push_back(connection); + connections.insert(connections.end(), connection); } else if (axis_it != string_to_axis_map.end()) { connection = BindingConnection(binding, getOutputPointer(ControllerOutput(0, axis_it->second.axis)), axis_it->second.value); - connections.push_back(connection); + connections.insert(connections.end(), connection); } else { LOG_ERROR(Input, "Invalid format at line: {}, data: \"{}\", skipping line.", lineCount, line); continue; } - LOG_INFO(Input, "Succesfully parsed line {}", lineCount); - /* og parsing - // first we parse the binding, and if its wrong, we skip to the next line - if (comma_pos != std::string::npos) { - // Handle key + modifier - std::string key = after_equals.substr(0, comma_pos); - std::string mod = after_equals.substr(comma_pos + 1); - - auto key_it = string_to_keyboard_key_map.find(key); - auto mod_it = string_to_keyboard_mod_key_map.find(mod); - - if (key_it != string_to_keyboard_key_map.end() && - mod_it != string_to_keyboard_mod_key_map.end()) { - binding.key = key_it->second; - binding.modifier = mod_it->second; - } else if (before_equals == "mouse_movement_params") { - // handle mouse movement params - float p1 = 0.5, p2 = 1, p3 = 0.125; - std::size_t second_comma_pos = after_equals.find(','); - try { - p1 = std::stof(key); - p2 = std::stof(mod.substr(0, second_comma_pos)); - p3 = std::stof(mod.substr(second_comma_pos + 1)); - mouse_deadzone_offset = p1; - mouse_speed = p2; - mouse_speed_offset = p3; - } catch (...) { - // fallback to default values - mouse_deadzone_offset = 0.5; - mouse_speed = 1; - mouse_speed_offset = 0.125; - std::cerr << "Parsing error while parsing kbm inputs at line " << lineCount - << " line data: " << line << "\n"; - } - continue; - } else { - std::cerr << "Syntax error while parsing kbm inputs at line " << lineCount - << " line data: " << line << "\n"; - continue; // skip - } - } else { - // Just a key without modifier - auto key_it = string_to_keyboard_key_map.find(after_equals); - if (key_it != string_to_keyboard_key_map.end()) { - binding.key = key_it->second; - } else { - std::cerr << "Syntax error while parsing kbm inputs at line " << lineCount - << " line data: " << line << "\n"; - continue; // skip - } - } - - // Check for axis mapping (example: axis_left_x_plus) - auto axis_it = string_to_axis_map.find(before_equals); - auto button_it = string_to_cbutton_map.find(before_equals); - if (axis_it != string_to_axis_map.end()) { - old_axis_map[binding] = axis_it->second; - } else if (button_it != string_to_cbutton_map.end()) { - old_button_map[binding] = button_it->second; - } else { - std::cerr << "Syntax error while parsing kbm inputs at line " << lineCount - << " line data: " << line << "\n"; - } - */ + //LOG_INFO(Input, "Succesfully parsed line {}", lineCount); } file.close(); } @@ -262,18 +230,23 @@ void ControllerOutput::setControllerOutputController(GameController* c) { ControllerOutput::controller = c; } + + void ControllerOutput::update(bool pressed, int axis_value) { + if(controller == nullptr) { + LOG_ERROR(Input, "No controller found!"); + return; + } float touchpad_x = 0; if(button != 0){ switch (button) { - /* todo + // todo: check if l2 and r2 can be moved to the axis section case OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_L2: case OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R2: axis = (button == OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R2) ? Axis::TriggerRight : Axis::TriggerLeft; controller->Axis(0, axis, GetAxis(0, 0x80, pressed ? 128 : 0)); break; - */ case OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_TOUCH_PAD: touchpad_x = Config::getBackButtonBehavior() == "left" ? 0.25f : Config::getBackButtonBehavior() == "right" ? 0.75f @@ -318,9 +291,68 @@ void ControllerOutput::update(bool pressed, int axis_value) { } } +void updatePressedKeys(u32 value, bool is_pressed) { + if (is_pressed) { + // Find the correct position for insertion to maintain order + auto it = std::lower_bound(pressed_keys.begin(), pressed_keys.end(), value); + + // Insert only if 'value' is not already in the list + if (it == pressed_keys.end() || *it != value) { + pressed_keys.insert(it, value); + } + } else { + // Remove 'value' from the list if it's not pressed + pressed_keys.remove(value); + } +} + +// Check if a given binding's all keys are currently active. +bool isInputActive(const InputBinding& i) { + /* how to check if a binding is currently held down: + iterate until connection.InputBinding.key3 is found or we reach the end + iterate from that point until connection.InputBinding.key2 is found or we reach the end + iterate from that point until connection.InputBinding.key1 is found or we reach the end + if we ever reach the end, return false + if the next key to find would be 0, return true + if all three are found return true + */ + auto it = pressed_keys.begin(); + + // Check for key1 if it's set + if (i.key1 != 0) { + it = std::find(it, pressed_keys.end(), i.key1); + if (it == pressed_keys.end()) return false; + ++it; // Move to the next element for subsequent checks + } + + // Check for key2 if it's set + if (i.key2 != 0) { + it = std::find(it, pressed_keys.end(), i.key2); + if (it == pressed_keys.end()) return false; + ++it; + } + + // Check for key3 if it's set + if (i.key3 != 0) { + it = std::find(it, pressed_keys.end(), i.key3); + if (it == pressed_keys.end()) return false; + } + + // All required keys were found in order + LOG_INFO(Input, "A valid held input is found!"); + return true; +} + void activateOutputsFromInputs() { // iterates over the connections, and updates them depending on whether the corresponding input trio is found - + for(auto it = connections.begin(); it != connections.end(); it++) { + if (it->output) { // Check if output is not nullptr + it->output->update(isInputActive(it->binding), it->axis_value); + LOG_INFO(Input, "Updating an output"); + } else { + LOG_ERROR(Input, "Null output in BindingConnection at position {}", std::distance(connections.begin(), it)); + } + } } void updateMouse(GameController* controller) { diff --git a/src/input/input_handler.h b/src/input/input_handler.h index 2857d1c14..73ad461cb 100644 --- a/src/input/input_handler.h +++ b/src/input/input_handler.h @@ -168,6 +168,9 @@ const std::map string_to_keyboard_key_map = { {"capslock", SDLK_CAPSLOCK}, }; +// literally the only flag that needs external access +void toggleMouseEnabled(); + // i wrapped it in a function so I can collapse it std::string_view getDefaultKeyboardConfig(); @@ -267,17 +270,14 @@ public: }; -// todo -//extern ControllerOutput output_array[]; -//extern std::map new_binding_map; -extern u32 pressed_keys[]; - // Check if the 3 key input is currently active. bool checkForInputDown(InputBinding i); // Add/remove the input that generated the event to/from the held keys container. void updatePressedKeys(u32 button, bool is_pressed); +void activateOutputsFromInputs(); + void updateMouse(GameController* controller); diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index cfa0e3eb3..e7dfbae07 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -108,7 +108,9 @@ void WindowSDL::waitEvent() { is_shown = event.type == SDL_EVENT_WINDOW_EXPOSED; onResize(); break; - + case SDL_EVENT_MOUSE_BUTTON_DOWN: + case SDL_EVENT_MOUSE_BUTTON_UP: + case SDL_EVENT_MOUSE_WHEEL: case SDL_EVENT_KEY_DOWN: case SDL_EVENT_KEY_UP: onKeyboardMouseInput(&event); @@ -146,201 +148,41 @@ void WindowSDL::onKeyboardMouseInput(const SDL_Event* event) { using Libraries::Pad::OrbisPadButtonDataOffset; // get the event's id, if it's keyup or keydown + 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); - // add/remove it from the list - - // update bindings and buttons - - // update axes - -/* og function -#ifdef __APPLE__ - // Use keys that are more friendly for keyboards without a keypad. - // Once there are key binding options this won't be necessary. - constexpr SDL_Keycode CrossKey = SDLK_N; - constexpr SDL_Keycode CircleKey = SDLK_B; - constexpr SDL_Keycode SquareKey = SDLK_V; - constexpr SDL_Keycode TriangleKey = SDLK_C; -#else - constexpr SDL_Keycode CrossKey = SDLK_KP_2; - constexpr SDL_Keycode CircleKey = SDLK_KP_6; - constexpr SDL_Keycode SquareKey = SDLK_KP_4; - constexpr SDL_Keycode TriangleKey = SDLK_KP_8; -#endif - - u32 button = 0; - Input::Axis axis = Input::Axis::AxisMax; - int axisvalue = 0; - int ax = 0; - std::string backButtonBehavior = Config::getBackButtonBehavior(); - switch (event->key.key) { - case SDLK_UP: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_UP; - break; - case SDLK_DOWN: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_DOWN; - break; - case SDLK_LEFT: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_LEFT; - break; - case SDLK_RIGHT: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_RIGHT; - break; - case TriangleKey: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_TRIANGLE; - break; - case CircleKey: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_CIRCLE; - break; - case CrossKey: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_CROSS; - break; - case SquareKey: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_SQUARE; - break; - case SDLK_RETURN: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_OPTIONS; - break; - case SDLK_A: - axis = Input::Axis::LeftX; - if (event->type == SDL_EVENT_KEY_DOWN) { - axisvalue += -127; - } else { - axisvalue = 0; + // Handle window controls outside of the input maps + if (event->type == SDL_EVENT_KEY_DOWN) { + // Reparse kbm inputs + if (input_id == SDLK_F8) { + Input::parseInputConfig(std::string(Common::ElfInfo::Instance().GameSerial())); } - ax = Input::GetAxis(-0x80, 0x80, axisvalue); - break; - case SDLK_D: - axis = Input::Axis::LeftX; - if (event->type == SDL_EVENT_KEY_DOWN) { - axisvalue += 127; - } else { - axisvalue = 0; + // Toggle mouse capture and movement input + else if (input_id == SDLK_F7) { + Input::toggleMouseEnabled(); + SDL_SetWindowRelativeMouseMode(this->GetSdlWindow(), + !SDL_GetWindowRelativeMouseMode(this->GetSdlWindow())); } - ax = Input::GetAxis(-0x80, 0x80, axisvalue); - break; - case SDLK_W: - axis = Input::Axis::LeftY; - if (event->type == SDL_EVENT_KEY_DOWN) { - axisvalue += -127; - } else { - axisvalue = 0; + // Toggle fullscreen + else if (input_id == SDLK_F11) { + SDL_WindowFlags flag = SDL_GetWindowFlags(window); + bool is_fullscreen = flag & SDL_WINDOW_FULLSCREEN; + SDL_SetWindowFullscreen(window, !is_fullscreen); } - ax = Input::GetAxis(-0x80, 0x80, axisvalue); - break; - case SDLK_S: - axis = Input::Axis::LeftY; - if (event->type == SDL_EVENT_KEY_DOWN) { - axisvalue += 127; - } else { - axisvalue = 0; - } - ax = Input::GetAxis(-0x80, 0x80, axisvalue); - break; - case SDLK_J: - axis = Input::Axis::RightX; - if (event->type == SDL_EVENT_KEY_DOWN) { - axisvalue += -127; - } else { - axisvalue = 0; - } - ax = Input::GetAxis(-0x80, 0x80, axisvalue); - break; - case SDLK_L: - axis = Input::Axis::RightX; - if (event->type == SDL_EVENT_KEY_DOWN) { - axisvalue += 127; - } else { - axisvalue = 0; - } - ax = Input::GetAxis(-0x80, 0x80, axisvalue); - break; - case SDLK_I: - axis = Input::Axis::RightY; - if (event->type == SDL_EVENT_KEY_DOWN) { - axisvalue += -127; - } else { - axisvalue = 0; - } - ax = Input::GetAxis(-0x80, 0x80, axisvalue); - break; - case SDLK_K: - axis = Input::Axis::RightY; - if (event->type == SDL_EVENT_KEY_DOWN) { - axisvalue += 127; - } else { - axisvalue = 0; - } - ax = Input::GetAxis(-0x80, 0x80, axisvalue); - break; - case SDLK_X: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_L3; - break; - case SDLK_M: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R3; - break; - case SDLK_Q: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_L1; - break; - case SDLK_U: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R1; - break; - case SDLK_E: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_L2; - axis = Input::Axis::TriggerLeft; - if (event->type == SDL_EVENT_KEY_DOWN) { - axisvalue += 255; - } else { - axisvalue = 0; - } - ax = Input::GetAxis(0, 0x80, axisvalue); - break; - case SDLK_O: - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_R2; - axis = Input::Axis::TriggerRight; - if (event->type == SDL_EVENT_KEY_DOWN) { - axisvalue += 255; - } else { - axisvalue = 0; - } - ax = Input::GetAxis(0, 0x80, axisvalue); - break; - case SDLK_SPACE: - 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); - button = OrbisPadButtonDataOffset::ORBIS_PAD_BUTTON_TOUCH_PAD; - } else { - button = 0; - } - break; - case SDLK_F11: - if (event->type == SDL_EVENT_KEY_DOWN) { - { - SDL_WindowFlags flag = SDL_GetWindowFlags(window); - bool is_fullscreen = flag & SDL_WINDOW_FULLSCREEN; - SDL_SetWindowFullscreen(window, !is_fullscreen); - } - } - break; - case SDLK_F12: - if (event->type == SDL_EVENT_KEY_DOWN) { - // Trigger rdoc capture + // Trigger rdoc capture + else if (input_id == SDLK_F12) { VideoCore::TriggerCapture(); } - break; - default: - break; } - if (button != 0) { - controller->CheckButton(0, button, event->type == SDL_EVENT_KEY_DOWN); - } - if (axis != Input::Axis::AxisMax) { - controller->Axis(0, axis, ax); - } -*/ + + // add/remove it from the list + Input::updatePressedKeys(input_id, input_down); + + // update bindings + Input::activateOutputsFromInputs(); + } void WindowSDL::onGamepadEvent(const SDL_Event* event) {