diff --git a/Source/Core/DolphinQt/Config/Mapping/MappingCommon.cpp b/Source/Core/DolphinQt/Config/Mapping/MappingCommon.cpp index bbd15ad831..7963fc22d1 100644 --- a/Source/Core/DolphinQt/Config/Mapping/MappingCommon.cpp +++ b/Source/Core/DolphinQt/Config/Mapping/MappingCommon.cpp @@ -68,7 +68,7 @@ QString DetectExpression(QPushButton* button, ciface::Core::DeviceContainer& dev // Avoid that the button press itself is registered as an event Common::SleepCurrentThread(50); - const auto [device, input] = device_container.DetectInput(INPUT_DETECT_TIME, device_strings); + const auto detections = device_container.DetectInput(INPUT_DETECT_TIME, device_strings); const auto timer = new QTimer(button); @@ -83,14 +83,24 @@ QString DetectExpression(QPushButton* button, ciface::Core::DeviceContainer& dev button->setText(old_text); - if (!input) - return {}; + QString full_expression; - ciface::Core::DeviceQualifier device_qualifier; - device_qualifier.FromDevice(device.get()); + for (auto [device, input] : detections) + { + ciface::Core::DeviceQualifier device_qualifier; + device_qualifier.FromDevice(device.get()); - return MappingCommon::GetExpressionForControl(QString::fromStdString(input->GetName()), - device_qualifier, default_device, quote); + if (!full_expression.isEmpty()) + full_expression += QChar::fromLatin1('+'); + + full_expression += MappingCommon::GetExpressionForControl( + QString::fromStdString(input->GetName()), device_qualifier, default_device, quote); + } + + if (detections.size() > 1) + return QStringLiteral("@(%1)").arg(std::move(full_expression)); + + return full_expression; } void TestOutput(QPushButton* button, OutputReference* reference) diff --git a/Source/Core/InputCommon/ControllerInterface/Device.cpp b/Source/Core/InputCommon/ControllerInterface/Device.cpp index ebed1ad421..cd056af0d8 100644 --- a/Source/Core/InputCommon/ControllerInterface/Device.cpp +++ b/Source/Core/InputCommon/ControllerInterface/Device.cpp @@ -18,6 +18,7 @@ namespace ciface::Core { // Compared to an input's current state (ideally 1.0) minus abs(initial_state) (ideally 0.0). +// Note: Detect() logic assumes this is greater than 0.5. constexpr ControlState INPUT_DETECT_THRESHOLD = 0.55; Device::~Device() @@ -253,13 +254,14 @@ bool DeviceContainer::HasConnectedDevice(const DeviceQualifier& qualifier) const // Inputs are considered if they are first seen in a neutral state. // This is useful for crazy flightsticks that have certain buttons that are always held down // and also properly handles detection when using "FullAnalogSurface" inputs. -// Upon input, return the detected Device and Input, else return nullptrs -std::pair, Device::Input*> +// Detects multiple inputs if they are pressed before others are released. +// Upon input, return the detected Device and Input pairs, else return an empty container +std::vector, Device::Input*>> DeviceContainer::DetectInput(u32 wait_ms, const std::vector& device_strings) const { struct InputState { - ciface::Core::Device::Input& input; + ciface::Core::Device::Input* input; ControlState initial_state; }; @@ -291,7 +293,7 @@ DeviceContainer::DetectInput(u32 wait_ms, const std::vector& device // Undesirable axes will have negative values here when trying to map a // "FullAnalogSurface". - input_states.push_back({*input, input->GetState()}); + input_states.push_back({input, input->GetState()}); } if (!input_states.empty()) @@ -301,6 +303,8 @@ DeviceContainer::DetectInput(u32 wait_ms, const std::vector& device if (device_states.empty()) return {}; + std::vector, Device::Input*>> detections; + u32 time = 0; while (time < wait_ms) { @@ -309,16 +313,31 @@ DeviceContainer::DetectInput(u32 wait_ms, const std::vector& device for (auto& device_state : device_states) { - for (auto& input_state : device_state.input_states) + for (std::size_t i = 0; i != device_state.input_states.size(); ++i) { + auto& input_state = device_state.input_states[i]; + // We want an input that was initially 0.0 and currently 1.0. const auto detection_score = - (input_state.input.GetState() - std::abs(input_state.initial_state)); + (input_state.input->GetState() - std::abs(input_state.initial_state)); if (detection_score > INPUT_DETECT_THRESHOLD) - return {device_state.device, &input_state.input}; + { + // We found an input. Add it to our detections. + detections.emplace_back(device_state.device, input_state.input); + + // And remove from input_states to prevent more detections. + device_state.input_states.erase(device_state.input_states.begin() + i--); + } } } + + for (auto& detection : detections) + { + // If one of our detected inputs is released we are done. + if (detection.second->GetState() < (1 - INPUT_DETECT_THRESHOLD)) + return detections; + } } // No input was detected. :'( diff --git a/Source/Core/InputCommon/ControllerInterface/Device.h b/Source/Core/InputCommon/ControllerInterface/Device.h index d923d97076..43e61e53fd 100644 --- a/Source/Core/InputCommon/ControllerInterface/Device.h +++ b/Source/Core/InputCommon/ControllerInterface/Device.h @@ -194,7 +194,7 @@ public: bool HasConnectedDevice(const DeviceQualifier& qualifier) const; - std::pair, Device::Input*> + std::vector, Device::Input*>> DetectInput(u32 wait_ms, const std::vector& device_strings) const; protected: