diff --git a/src/libs/controller.cpp b/src/libs/controller.cpp index e65b25c2a..0a5af9f2c 100644 --- a/src/libs/controller.cpp +++ b/src/libs/controller.cpp @@ -519,7 +519,20 @@ void GameController::SetDevices(int active_id) { m_trigger_state = {}; } +// Input that changes no button or touch contact updates the newest unread sample, so a burst of +// stick or sensor events between two reads cannot push unread button or touch changes out. void GameController::AddState() { + if (m_states_num != 0) { + const auto newest = (m_first_state + m_states_num - 1) % STATES_MAX; + auto& last = m_states[newest]; + if (!m_obtained[newest] && last.buttons == m_state.buttons && + last.touch[0].down == m_state.touch[0].down && + last.touch[1].down == m_state.touch[1].down) { + last = m_state; + return; + } + } + if (m_states_num >= STATES_MAX) { m_states_num = STATES_MAX - 1; m_first_state = (m_first_state + 1) % STATES_MAX; diff --git a/tests/ControllerSettingsTests.cpp b/tests/ControllerSettingsTests.cpp index 03b73026a..1827051e7 100644 --- a/tests/ControllerSettingsTests.cpp +++ b/tests/ControllerSettingsTests.cpp @@ -689,6 +689,27 @@ void TestTriggerEffectState() { check_state(0, 0, "switching controllers retained effect states"); } +void TestUnreadPressSurvivesStickBurst() { + Controller controller; + std::array data {}; + SetButton(1, PAD_BUTTON_CROSS, true); + for (int value = 0; value < 100; value++) { + SetAxis(1, Axis::LeftX, value); + } + SetButton(1, PAD_BUTTON_CROSS, false); + for (int value = 100; value < 200; value++) { + SetAxis(1, Axis::LeftX, value); + } + Check(PadRead(1, data.data(), 64) == 2 && data[0].buttons == PAD_BUTTON_CROSS && + data[0].left_stick_x == 99 && data[1].buttons == 0 && data[1].left_stick_x == 199, + "a stick burst pushed an unread press out of the pad history"); + SetAxis(1, Axis::LeftX, 50); + SetButton(1, PAD_BUTTON_CROSS, true); + Check(PadRead(1, data.data(), 64) == 2 && data[0].buttons == 0 && + data[0].left_stick_x == 50 && data[1].buttons == PAD_BUTTON_CROSS, + "a stick move after a read did not add a sample"); +} + } // namespace int main() { @@ -702,6 +723,7 @@ int main() { TestMaskedTriggersAndValidation(); TestIndependentOutputsAndPadSwitch(); TestTriggerEffectState(); + TestUnreadPressSurvivesStickBurst(); TestMultiplayerRouting(); TestDedicatedKeyboardOutputs(); Config::Shutdown();