CNA-GAP-067: ScrollWheelValue drops sub-notch wheel motion: the SDL3 platform counts whole notches only, where XNA accumulates raw wheel units

CNA snapshot c1c316b9  ·  Known Issues › Functional gaps  ·  source links pinned to c1c316b9

✓

Evidence basis: source-verified at the pinned commit; tests exist (not executed for this page). Claims on this page were checked by reading the CNA source at commit c1c316b9; unless a sentence says otherwise, nothing here was built or executed. Nothing on this page was executed unless the Evidence section says so.

The SDL3 platform truncates every wheel event to whole notches before scaling by 120 (a deliberate FNA-parity rule in docs/input-fna-fidelity.md), so high-resolution wheels and touchpads can scroll without changing ScrollWheelValue; XNA accumulates 1/120 units.

Identifier
CNA-GAP-067
Category
Functional gap
Subsystem
Input
Status
Open
Verified against
CNA c1c316b9 (c1c316b9c7a846ce8002809c151fcd1af14942c9)
Evidence basis
Source-verified: read at TARGET, not executed
Tests touching this area
Yes: see Current tests
Affected contract
MouseState::getScrollWheelValueProperty() and getHorizontalScrollWheelValueEXTProperty() as reported by Mouse::GetState()

Expected behaviour

XNA 4.0 on Windows adds the raw signed WM_MOUSEWHEEL delta to the wheel value (MouseMessageHooker.WndProc, decompiled reference), so a high-resolution wheel moves it in steps smaller than 120. The SDL3 backend deliberately follows FNA instead (cast to int before multiplying by 120), which is the documented and tested behaviour; the contract on SDL3 is whole notches.

Actual behaviour at TARGET

Sdl3EventMapper.cpp passes SDL's float wheel.x/wheel.y, and AccumulateWheel in Sdl3InputServices.cpp converts each event to an integer number of notches before multiplying by 120. An event of 0.25 notch adds nothing, so a touchpad or smooth-scrolling wheel that reports fractions per event can leave ScrollWheelValue unchanged for a whole gesture. SDL delivers the fractional amounts and CNA discards them, so this is a narrower contract on CNA's side, not a host constraint.

input-fna-fidelity.md records the truncation as an intentional FNA match that keeps the value “a clean multiple of 120 exactly like XNA”; XNA's own hook does not truncate.

Source locations

Evidence

Checked by reading at c1c316b9; not executed. The XNA behaviour was read from the decompiled XNA 4.0 MouseMessageHooker, FNA's from FNA-XNA/FNA b3551247. The SDL3 rule is pinned by Sdl3MouseTest.WheelEventsAccumulateInXnaUnitsAndTruncateBeforeScaling, so the truncation is deliberate, not accidental. Whether a given touchpad reports sub-notch events through SDL depends on the host and was not observed.

Focused reproduction

No focused reproduction is known. Nothing has been invented here; the evidence above is what exists.

Current tests

Sdl3InputServicesTests.cpp pins the truncation (1.9 notches count as one). No test asserts XNA's sub-notch accumulation.

Regression test

If XNA's rule is chosen: two half-notch wheel events must change ScrollWheelValue by 60 each (120 together), replacing the truncation test.

Blast radius

Games that read ScrollWheelValue for zoom or scrolling with touchpads or free-spinning wheels.

Workaround

None inside the XNA surface; a game that needs smooth scrolling on SDL3 has to read wheel events itself.

The same subject is explained at several altitudes. These are the neighbouring pages at each one.

User guide
Input: mouse