Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions include/pad.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@

int startPads();
int readPads();
// While frozen, readPads() keeps sampling but does not advance the key-on baseline, so an edge
// raised now survives until it is consumed. Used across screen transitions, where the GUI polls
// every frame but runs no input handler -- without this, presses made during a fade are discarded.
void padFreezeEdgeBaseline(int freeze);
void unloadPads();

// Menu rumble (#172), gated by gEnableRumble. Tap/Bump arm a pulse on every capable pad and never
Expand Down
5 changes: 5 additions & 0 deletions src/gui.c
Original file line number Diff line number Diff line change
Expand Up @@ -2339,6 +2339,11 @@ static void guiDrawOverlays()

static void guiReadPads()
{
// A transition means handleInput() will not run this frame (see guiMainLoop). Hold the key-on
// baseline so a press made during the ~430ms fade is still pending when the new screen takes
// over, instead of being consumed by a poll that nobody is listening behind.
padFreezeEdgeBaseline(screenHandlerTarget != NULL);

if (readPads())
guiInactiveFrames = 0;
else {
Expand Down
30 changes: 29 additions & 1 deletion src/pad.c
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,31 @@ static u32 oldpaddata;
static u32 edgedata;
static u32 oldedgedata;

/*
While set, readPads() keeps sampling but does NOT advance the edge baseline, so a key-on raised
during this window survives until someone is actually listening.

guiMainLoop() polls the pads EVERY frame (gui.c) but only runs screenHandler->handleInput() when no
screen transition is in flight. A fade is 26 frames -- about 430 ms at 60 Hz -- and every poll in
that window still shifted oldedgedata, so the edge was consumed by a poll with no consumer behind
it. Any button pressed during a screen fade was silently discarded: press a direction right after
switching screens and nothing happens.

Freezing the BASELINE rather than skipping the poll is deliberate. readPads() also expires rumble
taps, drives the pad state machine and detects (dis)connects, so simply not calling it during a
transition would leave a motor spinning and a replug unnoticed.

Scope note: this is armed only while a transition is running, so it cannot affect ordinary
same-screen navigation -- deliberately, so it does not disturb the #271/#272 read-miss work being
tested on hardware.
*/
static int edgeBaselineFrozen = 0;

void padFreezeEdgeBaseline(int freeze)
{
edgeBaselineFrozen = freeze ? 1 : 0;
}

static int delaycnt[16];
static int paddelay[16];

Expand Down Expand Up @@ -965,7 +990,10 @@ int readPads()
paddata = 0;
// Same one-poll lifetime as paddata, so an edge still lasts exactly one poll and cannot re-fire;
// the difference is only in how a MISSED read contributes below (see the edgedata note above).
oldedgedata = edgedata;
// Unless the baseline is frozen -- then a pending key-on is held for whoever is not listening yet
// (padFreezeEdgeBaseline; screen transitions).
if (!edgeBaselineFrozen)
oldedgedata = edgedata;
edgedata = 0;

/*
Expand Down