feat(input): wire native mouse-wheel scroll on Wayland + Win32, normalize all backends to ±1/detent (#32)

Window::onMouseScroll now fires on every backend and speaks one unit:
signed whole detents packed in the uint32 payload, +1 = wheel down
(DOM deltaY sign).

- Wayland: implement the previously-stubbed PointerListenerHandleAxis —
  vertical axis only, wl_fixed_to_double / 15 (libinput's units-per-
  detent), sub-detent remainder accumulated and reset on pointer leave.
- Win32: add the missing WM_MOUSEWHEEL case — -GET_WHEEL_DELTA_WPARAM /
  WHEEL_DELTA with a remainder accumulator for free-spinning wheels.
- DOM: dom-env.js normalizes WheelEvent.deltaY per deltaMode (100px /
  3 lines / 1 page per detent) with the same remainder scheme, so wasm
  delivers the identical unit instead of raw browser-specific deltas.
- Document the contract on Window::onMouseScroll.
- tests/MouseScroll: compositor-free regression test driving the
  Wayland axis handler directly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
catbot 2026-06-12 15:02:28 +00:00
commit 419730f46b
8 changed files with 240 additions and 8 deletions

View file

@ -103,6 +103,15 @@ export namespace Crafter {
Event<void> onMouseMove;
Event<void> onMouseEnter;
Event<void> onMouseLeave;
// Mouse wheel, in whole detents per event, sign packed into the
// uint32 payload (reinterpret as int32 to read it). Positive =
// wheel down / toward the user — the DOM deltaY sign. Every
// backend normalizes to ±1 per notch: dom-env.js divides
// WheelEvent deltas by the per-deltaMode detent size, Wayland
// divides the axis value by libinput's 15-units-per-detent,
// Win32 divides by WHEEL_DELTA. Sub-detent motion (smooth-scroll
// touchpads, free-spinning wheels) is accumulated backend-side
// until a whole detent is reached.
Event<std::uint32_t> onMouseScroll;
Vector<float, 2> currentMousePos;
Vector<float, 2> lastMousePos;