mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 02:11:36 -04:00
Preserve ordered mouse re-press after delayed release
(cherry picked from commit 50ec9191c2)
This commit is contained in:
@@ -45,6 +45,11 @@ routing/removal, invalid data, capture failure and >250 ms host stalls release
|
||||
capture and reset the policy. Stale, backwards and future motion cannot
|
||||
extend a pulse. Releases remain immediate even with anomalous timestamps;
|
||||
their watermark cannot admit old motion after a cleared held target. A
|
||||
valid ordered LEFT release while capture remains eligible uses its actual
|
||||
event timestamp as the acquisition boundary. A later physical press in the
|
||||
same delayed SDL drain can therefore acquire a neutral new session. Lifecycle,
|
||||
stall and malformed-release resets retain the processing-time barrier; a release
|
||||
after such a reset cannot lower it to admit queued input. A
|
||||
stall's backlog cannot acquire capture again. Eligibility recovery needs a
|
||||
release/repress of LEFT and fresh motion. Reset retains physical LEFT down-state,
|
||||
so holding it across an interruption cannot recapture. Debug input injection, headless, netplay, replay/resimulation and
|
||||
@@ -81,6 +86,9 @@ pure policy tests do not exercise the OS or game dispatcher.
|
||||
The reviewed implementation base is release framework commit
|
||||
065888f50f9131839bcbbc8814debf58b4624b16, also the canonical
|
||||
`release/ape-v0.5.0-hle-support` branch. Current upstream master has differing
|
||||
existing mod APIs and needs separate compatibility validation. Final game-pin
|
||||
reachability/CI and physical interruption checks are pending; this is not a
|
||||
existing mod APIs and needs separate compatibility validation. The linked game
|
||||
draft pins this runtime draft's reachable head through its configured framework
|
||||
upstream; final-pin checkout, build and local test validation are recorded with
|
||||
the drafts. Hosted CI status and physical interruption checks are reported
|
||||
separately; this is not a
|
||||
public-quality or merge-readiness claim.
|
||||
|
||||
@@ -86,9 +86,18 @@ bool LocalMousePolicy::control(uint64_t t, uint64_t now, MouseControl control,
|
||||
if (!down) {
|
||||
// Release is unconditional: a future/backwards event time cannot
|
||||
// leave capture or the reducer's separate direction hold active.
|
||||
// A valid ordered release while still eligible owns its event
|
||||
// boundary, rather than the later time at which SDL drains it.
|
||||
// After an interruption or malformed release, retain reset()'s
|
||||
// processing-time barrier so queued input cannot rearm capture.
|
||||
const bool ordered_release = captured_ && allowed() &&
|
||||
t <= now && now - t <= StallNs && t >= release_clock_ &&
|
||||
(!event_valid_ || t >= event_clock_) &&
|
||||
(!clock_valid_ || (now >= clock_ && now - clock_ <= StallNs));
|
||||
const bool owned = activation_owned_;
|
||||
activation_owned_ = false;
|
||||
if (captured_ || owned) reset();
|
||||
if (ordered_release) release_clock_ = t;
|
||||
return owned;
|
||||
}
|
||||
// reset() retains physical down-state. A held button after a menu,
|
||||
|
||||
@@ -173,6 +173,73 @@ int main() {
|
||||
CHECK(policy.control(t+15*Ms,t+15*Ms,psx::MouseControl::Left,false,false));
|
||||
t+=20*Ms;
|
||||
|
||||
// SDL may drain several ordered edges after their physical timestamps.
|
||||
// A normal release must not reject a later re-press in that same drain.
|
||||
for (uint64_t delay : {10*Ms, 50*Ms, 200*Ms}) {
|
||||
psx::LocalMousePolicy batch({nullptr,capture,suppress,notice});
|
||||
CHECK(batch.install(&callbacks));
|
||||
const uint64_t base=1000*Ms+delay*10;
|
||||
batch.update(base+10*Ms,host);
|
||||
CHECK(batch.control(base+10*Ms,base+10*Ms,psx::MouseControl::Left,true,false));
|
||||
batch.motion(base+15*Ms,base+15*Ms,30,0);
|
||||
batch.control(base+16*Ms,base+16*Ms,psx::MouseControl::Right,true,false);
|
||||
batch.motion(base+17*Ms,base+17*Ms,30,0); CHECK(active && held);
|
||||
batch.update(base+20*Ms+delay,host);
|
||||
CHECK(batch.control(base+20*Ms,base+20*Ms+delay,psx::MouseControl::Left,false,false));
|
||||
CHECK(!batch.captured() && !active && !held);
|
||||
x=99;y=77;batch.sample(base+20*Ms+delay,x,y);CHECK(x==99 && y==77);
|
||||
batch.update(base+21*Ms+delay,host);
|
||||
CHECK(batch.control(base+25*Ms,base+21*Ms+delay,psx::MouseControl::Left,true,false));
|
||||
CHECK(batch.captured() && !active && !held);
|
||||
batch.sample(base+21*Ms+delay,x,y);CHECK(x==128 && y==128);
|
||||
batch.motion(base+26*Ms,base+22*Ms+delay,30,0);CHECK(active && !held);
|
||||
batch.control(base+27*Ms,base+23*Ms+delay,psx::MouseControl::Right,false,false);
|
||||
CHECK(active && !held); // A physically held RIGHT cannot latch or cancel this flick.
|
||||
CHECK(batch.control(base+28*Ms,base+24*Ms+delay,psx::MouseControl::Left,false,false));
|
||||
CHECK(!batch.captured() && !active && !held);
|
||||
batch.clear();
|
||||
}
|
||||
|
||||
// A release after an interruption cannot lower its processing-time barrier.
|
||||
for (int gate=0;gate<4;++gate) {
|
||||
psx::LocalMousePolicy batch({nullptr,capture,suppress,notice});
|
||||
CHECK(batch.install(&callbacks));
|
||||
const uint64_t base=5000*Ms+gate*1000*Ms;
|
||||
batch.update(base+10*Ms,host);
|
||||
CHECK(batch.control(base+10*Ms,base+10*Ms,psx::MouseControl::Left,true,false));
|
||||
batch.motion(base+15*Ms,base+15*Ms,30,0);
|
||||
auto off=host;const uint64_t barrier=base+(gate==3?300:30)*Ms;
|
||||
if(gate==0)off.focused=false;if(gate==1)guest=false;if(gate==2)off.native_right=true;
|
||||
batch.update(barrier,off);CHECK(!batch.captured() && !active);
|
||||
guest=true;batch.update(barrier+Ms,host);
|
||||
CHECK(batch.control(base+20*Ms,barrier+2*Ms,psx::MouseControl::Left,false,false));
|
||||
CHECK(!batch.control(base+25*Ms,barrier+3*Ms,psx::MouseControl::Left,true,false));
|
||||
CHECK(!batch.captured() && !active && !held);
|
||||
batch.control(barrier+4*Ms,barrier+4*Ms,psx::MouseControl::Left,false,false);
|
||||
batch.update(barrier+5*Ms,host);
|
||||
CHECK(batch.control(barrier+5*Ms,barrier+5*Ms,psx::MouseControl::Left,true,false));
|
||||
batch.sample(barrier+5*Ms,x,y);CHECK(x==128 && y==128);
|
||||
batch.motion(barrier+6*Ms,barrier+6*Ms,30,0);CHECK(active);
|
||||
batch.clear();
|
||||
}
|
||||
|
||||
// Malformed releases are still immediate, but cannot admit a queued press.
|
||||
for (uint64_t bad : {UINT64_MAX, uint64_t(14*Ms), uint64_t(0)}) {
|
||||
psx::LocalMousePolicy batch({nullptr,capture,suppress,notice});
|
||||
CHECK(batch.install(&callbacks));batch.update(10*Ms,host);
|
||||
CHECK(batch.control(10*Ms,10*Ms,psx::MouseControl::Left,true,false));
|
||||
batch.motion(15*Ms,15*Ms,30,0);batch.update(30*Ms,host);
|
||||
CHECK(batch.control(bad,30*Ms,psx::MouseControl::Left,false,false));
|
||||
CHECK(!batch.captured() && !active && !held);
|
||||
batch.update(31*Ms,host);
|
||||
CHECK(!batch.control(25*Ms,31*Ms,psx::MouseControl::Left,true,false));
|
||||
CHECK(!batch.captured());
|
||||
batch.control(32*Ms,32*Ms,psx::MouseControl::Left,false,false);
|
||||
batch.update(33*Ms,host);
|
||||
CHECK(batch.control(33*Ms,33*Ms,psx::MouseControl::Left,true,false));
|
||||
batch.sample(33*Ms,x,y);CHECK(x==128 && y==128);batch.clear();
|
||||
}
|
||||
|
||||
policy.reset();host.hold_conflict=false;capture_ok=false;CHECK(acquire());CHECK(!policy.captured());
|
||||
const unsigned failure_notices=notices;CHECK(acquire());CHECK(notices==failure_notices);
|
||||
policy.clear(); CHECK(!policy.installed() && !policy.captured());
|
||||
|
||||
Reference in New Issue
Block a user