CNA-GAP-067: ScrollWheelValue granularity depends on the platform backend: SDL3 drops sub-notch wheel motion, Win32 and Wayland keep it
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 009d40f5; 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 recorded in docs/input-fna-fidelity.md), so high-resolution wheels and touchpads can scroll without changing ScrollWheelValue, while the Win32 and Wayland backends accumulate 1/120 units as XNA does. SDL delivers the fractional amounts and CNA discards them, so this is a narrower contract on the SDL3 backend, not a host constraint.
- Identifier
CNA-GAP-067- Category
- Functional gap
- Subsystem
- Input
- Status
- Open
- Verified against
- CNA
009d40f5(009d40f5dd085c4e674d3479675fac84b12b3e0a) - 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
- SDL3:
Sdl3EventMapper.cpppasses SDL's floatwheel.x/wheel.y, andAccumulateWheelinSdl3InputServices.cppconverts 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 leaveScrollWheelValueunchanged for a whole gesture. - Win32:
Win32EventMapper::TranslateMouseWheel(Win32EventMapper.cpp) accumulates the rawWHEEL_DELTAunits, as XNA does. - Wayland:
AxisTo120inWaylandMouse.cppkeeps the compositor'svalue120, so half a notch is 60. - X11 core wheel buttons are whole notches by nature (
X11Mouse::AccumulateScroll).
input-fna-fidelity.md records the SDL3 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, and the Win32 and Wayland backends do not either.
Source locations
modules/platform/src/Sdl3/Sdl3InputServices.cpp— AccumulateWheel and Sdl3Mouse::ObserveEventmodules/platform/src/Sdl3/Sdl3EventMapper.cpp— SDL_EVENT_MOUSE_WHEEL mappingmodules/platform/src/Win32/Win32EventMapper.cpp— Win32EventMapper::TranslateMouseWheelmodules/platform/src/Wayland/WaylandMouse.cpp— AxisTo120, WaylandMouse::EmitFrame and Updatemodules/platform/src/X11/X11Mouse.cpp— X11Mouse::AccumulateScrolldocs/input-fna-fidelity.md— Mouse: the wheel truncation recorded as an intentional deviationmodules/platform/tests/CNA/Platform/Sdl3InputServicesTests.cpp— Sdl3MouseTest.WheelEventsAccumulateInXnaUnitsAndTruncateBeforeScalingmodules/platform/tests/CNA/Platform/WaylandMappingTests.cpp— WaylandWheelMapping.Value120IsTakenAsItIs
Evidence
Checked by reading at 009d40f5; not executed. The XNA behaviour was read from the decompiled XNA 4.0 MouseMessageHooker, FNA's from FNA-XNA/FNA b3551247. Each backend's rule is pinned by its own test (Sdl3MouseTest, Win32EventMapping.WheelTotalsAccumulateInXnaUnits, WaylandWheelMapping), so the difference is deliberate per backend, not accidental. Whether a given touchpad reports sub-notch events through SDL depends on the host and was not observed.
Independent re-verification: Checked by reading at 009d40f5; not executed. Each backend's rule is pinned by its own test (Sdl3MouseTest, Win32EventMapping.WheelTotalsAccumulateInXnaUnits, WaylandWheelMapping). 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), Win32EventMapperTests.cpp whole-notch accumulation and WaylandMappingTests.cpp the half notch. No test states a cross-backend rule.
Regression test
If one rule is chosen, a backend-neutral conformance case: two half-notch wheel events must change ScrollWheelValue by the same amount (120, or 60 and 60) on every backend.
Blast radius
Games that read ScrollWheelValue for zoom or scrolling on the SDL3 backend with touchpads or free-spinning wheels, and games whose behaviour differs between the SDL3 and native Win32 or Wayland builds.
Workaround
None inside the XNA surface; a game that needs smooth scrolling on SDL3 has to read wheel events itself.
Related pages
The same subject is explained at several altitudes. These are the neighbouring pages at each one.
- User guide
- Input: mouse
- Deep dives
- The input model: MouseState
- Known issues
- Functional gap index