From 5ea09213017dc2341cf7da2f2e6d739aca3db0ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 2 Nov 2026 08:52:24 +0100 Subject: [PATCH] Ctrl: Let a press shorter than the sample interval be seen once A press and release that both land between two sceCtrl samples were lost. Now the next sample sees the button pressed. Idea from Azahar. Co-Authored-By: Claude Opus 5.5 --- Core/HLE/sceCtrl.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/Core/HLE/sceCtrl.cpp b/Core/HLE/sceCtrl.cpp index c623d2b368..976edcc433 100644 --- a/Core/HLE/sceCtrl.cpp +++ b/Core/HLE/sceCtrl.cpp @@ -114,6 +114,10 @@ static u32 emuRapidFireInterval = 5; // no buttons and centered sticks, as impose does on a PSP. Host-side peeks still see the real state. static std::atomic gameInputBlocked{ false }; +// Buttons pressed since the last sample. A press and release that both land between two samples +// would otherwise never be seen by the game. Guarded by ctrlMutex. +static u32 ctrlUnsampledPresses = 0; + // These buttons are not affected by rapid fire (neither is analog.) const u32 CTRL_EMU_RAPIDFIRE_MASK = CTRL_UP | CTRL_DOWN | CTRL_LEFT | CTRL_RIGHT; @@ -122,7 +126,8 @@ static void __CtrlUpdateLatch() std::lock_guard guard(ctrlMutex); u64 t = CoreTiming::GetGlobalTimeUs(); - u32 buttons = ctrlCurrent.buttons; + u32 buttons = ctrlCurrent.buttons | ctrlUnsampledPresses; + ctrlUnsampledPresses = 0; if (emuRapidFire && emuRapidFireToggle) buttons &= CTRL_EMU_RAPIDFIRE_MASK; @@ -284,7 +289,8 @@ void __CtrlUpdateButtons(u32 bitsToSet, u32 bitsToClear) std::lock_guard guard(ctrlMutex); // There's no atomic operation for this, so mutex it is. - ctrlCurrent.buttons = (ctrlCurrent.buttons & ~bitsToClear) | bitsToSet; + ctrlUnsampledPresses |= bitsToSet & ~ctrlCurrent.buttons; + ctrlCurrent.buttons =(ctrlCurrent.buttons & ~bitsToClear) | bitsToSet; } void __CtrlSetAnalogXY(int stick, float x, float y) @@ -448,6 +454,7 @@ void __CtrlInit() { latch.btnRelease = 0xffffffff; gameInputBlocked = false; + ctrlUnsampledPresses = 0; memset(&ctrlCurrent, 0, sizeof(ctrlCurrent)); memset(ctrlCurrent.analog, CTRL_ANALOG_CENTER, sizeof(ctrlCurrent.analog)); analogEnabled = false; @@ -464,6 +471,9 @@ void __CtrlDoState(PointerWrap &p) if (!s) return; + // Host input from before the load shouldn't show up in the loaded state. + ctrlUnsampledPresses = 0; + Do(p, analogEnabled); Do(p, ctrlLatchBufs); Do(p, ctrlOldButtons);