diff --git a/src/input/input_handler.cpp b/src/input/input_handler.cpp index 50b924ccc..de3239b84 100644 --- a/src/input/input_handler.cpp +++ b/src/input/input_handler.cpp @@ -46,7 +46,7 @@ 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; -std::list pressed_keys = std::list(); +std::list> pressed_keys; std::list connections = std::list(); void toggleMouseEnabled() { @@ -161,7 +161,7 @@ void parseInputConfig(const std::string game_id = "") { // Split the line by '=' std::size_t equal_pos = line.find('='); if (equal_pos == std::string::npos) { - LOG_ERROR(Input, "Invalid format at line: {}, data: \"{}\", skipping line.", lineCount, line); + LOG_DEBUG(Input, "Invalid format at line: {}, data: \"{}\", skipping line.", lineCount, line); continue; } @@ -185,14 +185,14 @@ void parseInputConfig(const std::string game_id = "") { if (before_equals == "modkey_toggle") { if (comma_pos != std::string::npos) { // handle key-to-key toggling (separate list?) - LOG_ERROR(Input, "todo: {}", line); + LOG_DEBUG(Input, "todo: {}", line); continue; } - LOG_ERROR(Input, "Invalid format at line: {}, data: \"{}\", skipping line.", lineCount, line); + LOG_DEBUG(Input, "Invalid format at line: {}, data: \"{}\", skipping line.", lineCount, line); continue; } if (before_equals == "mouse_movement_params") { - LOG_ERROR(Input, "todo: {}", line); + LOG_DEBUG(Input, "todo: {}", line); continue; } @@ -203,7 +203,7 @@ void parseInputConfig(const std::string game_id = "") { auto axis_it = string_to_axis_map.find(before_equals); if(binding.isEmpty()) { - LOG_ERROR(Input, "Invalid format at line: {}, data: \"{}\", skipping line.", lineCount, line); + LOG_DEBUG(Input, "Invalid format at line: {}, data: \"{}\", skipping line.", lineCount, line); continue; } if (button_it != string_to_cbutton_map.end()) { @@ -214,18 +214,19 @@ void parseInputConfig(const std::string game_id = "") { connection = BindingConnection(binding, getOutputPointer(ControllerOutput(0, axis_it->second.axis)), axis_it->second.value); connections.insert(connections.end(), connection); } else { - LOG_ERROR(Input, "Invalid format at line: {}, data: \"{}\", skipping line.", lineCount, line); + LOG_DEBUG(Input, "Invalid format at line: {}, data: \"{}\", skipping line.", lineCount, line); continue; } //LOG_INFO(Input, "Succesfully parsed line {}", lineCount); } file.close(); - LOG_INFO(Input, "Done parsing the input config!"); + connections.sort(); + 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_ERROR(Input, "Something went wrong with wheel input parsing!"); + LOG_DEBUG(Input, "Something went wrong with wheel input parsing!"); return 0; } if (event.wheel.y > 0) { @@ -273,10 +274,10 @@ void ControllerOutput::update(bool pressed, int a_value) { controller->CheckButton(0, button, pressed); break; case LEFTJOYSTICK_HALFMODE: - leftjoystick_halfmode ^= pressed; // toggle if pressed, don't change otherwise + leftjoystick_halfmode = pressed; break; case RIGHTJOYSTICK_HALFMODE: - rightjoystick_halfmode ^= pressed; + rightjoystick_halfmode = pressed; break; default: // is a normal key (hopefully) controller->CheckButton(0, button, pressed); @@ -307,7 +308,7 @@ void ControllerOutput::update(bool pressed, int a_value) { int ax = GetAxis(-0x80, 0x80, axis_value); controller->Axis(0, axis, ax); } else { - LOG_ERROR(Input, "Controller output with no values detected!"); + LOG_DEBUG(Input, "Controller output with no values detected!"); } } void ControllerOutput::addUpdate(bool pressed, int a_value) { @@ -359,77 +360,93 @@ void ControllerOutput::addUpdate(bool pressed, int a_value) { controller->Axis(0, axis, GetAxis(-0x80, 0x80, axis_value)); //LOG_INFO(Input, "Axis value delta: {} final value: {}", (pressed ? a_value : 0), axis_value); } else { - LOG_ERROR(Input, "Controller output with no values detected!"); + LOG_DEBUG(Input, "Controller output with no values detected!"); } } 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); - + auto it = std::lower_bound(pressed_keys.begin(), pressed_keys.end(), value, + [](const std::pair& pk, u32 v) { return pk.first < v; }); + // Insert only if 'value' is not already in the list - if (it == pressed_keys.end() || *it != value) { - pressed_keys.insert(it, value); + if (it == pressed_keys.end() || it->first != value) { + pressed_keys.insert(it, {value, false}); } } else { // Remove 'value' from the list if it's not pressed - pressed_keys.remove(value); + auto it = std::find_if(pressed_keys.begin(), pressed_keys.end(), + [value](const std::pair& pk) { return pk.first == value; }); + if (it != pressed_keys.end()) { + pressed_keys.erase(it); // Remove the key entirely from the list + } } } // 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(); + bool* flag1 = nullptr; + bool* flag2 = nullptr; + bool* flag3 = nullptr; - 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 + // First pass: locate each key and save pointers to their flags if found + for (auto& entry : pressed_keys) { + u32 key = entry.first; + bool& is_active = entry.second; + + if (i.key1 != 0 && key == i.key1 && !flag1) { + flag1 = &is_active; + } else if (i.key2 != 0 && key == i.key2 && !flag2) { + flag2 = &is_active; + } else if (i.key3 != 0 && key == i.key3 && !flag3) { + flag3 = &is_active; + break; + } else { + return false; // an all 0 input never gets activated + } } - if (i.key2 != 0) { - it = std::find(it, pressed_keys.end(), i.key2); - if (it == pressed_keys.end()) return false; - ++it; + // If any required key was not found, return false without updating flags + if ((i.key1 != 0 && !flag1) || (i.key2 != 0 && !flag2) || (i.key3 != 0 && !flag3)) { + return false; } - if (i.key3 != 0) { - it = std::find(it, pressed_keys.end(), i.key3); - if (it == pressed_keys.end()) return false; + // Check if all flags are already true, which indicates this input is overridden (only if the key is not 0) + if ((i.key1 == 0 || (flag1 && *flag1)) && + (i.key2 == 0 || (flag2 && *flag2)) && + (i.key3 == 0 || (flag3 && *flag3))) { + return false; // This input is overridden by another input } - // All required keys were found in order - LOG_INFO(Input, "A valid held input is found!"); + // Set flags to true only after confirming all keys are present and not overridden + if (flag1) *flag1 = true; + if (flag2) *flag2 = true; + if (flag3) *flag3 = true; + + LOG_DEBUG(Input, "A valid held input is found: {}, flag ptrs: {} {} {}", i.toString(), fmt::ptr(flag1), fmt::ptr(flag2), fmt::ptr(flag3)); return true; } void activateOutputsFromInputs() { + LOG_DEBUG(Input, "Starting input scan..."); // reset everything for(auto it = connections.begin(); it != connections.end(); it++) { if (it->output) { it->output->update(false, 0); } else { - // LOG_ERROR(Input, "Null output in BindingConnection at position {}\n data: {}: {}", - // std::distance(connections.begin(), it), - // it->binding.toString(), it->output->toString()); - LOG_ERROR(Input, "Null output in BindingConnection at position {}", std::distance(connections.begin(), it)); + LOG_DEBUG(Input, "Null output in BindingConnection at position {}", std::distance(connections.begin(), it)); } } + for (auto it : pressed_keys) { + it.second = false; + } // 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) { it->output->addUpdate(isInputActive(it->binding), it->axis_value); } else { - //LOG_ERROR(Input, "Null output in BindingConnection at position {}", std::distance(connections.begin(), it)); + //LOG_DEBUG(Input, "Null output in BindingConnection at position {}", std::distance(connections.begin(), it)); } } } diff --git a/src/input/input_handler.h b/src/input/input_handler.h index 175d2b23f..7001b7152 100644 --- a/src/input/input_handler.h +++ b/src/input/input_handler.h @@ -218,10 +218,17 @@ public: // 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); + } + // 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; } - std::string toString() { + std::string toString() const { return fmt::format("({}, {}, {})", key1, key2, key3); } @@ -269,7 +276,9 @@ public: // todo: check if out is in the allowed array output = out; } - + bool operator<(const BindingConnection& other) { + return binding < other.binding; + } }; // Check if the 3 key input is currently active. diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index 21f266931..62c202934 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -136,7 +136,6 @@ void WindowSDL::waitEvent() { void WindowSDL::initTimers() { SDL_AddTimer(100, &PollController, controller); - // todo: add back mouse polling here SDL_AddTimer(33, Input::mousePolling, (void*)controller); }