feat: XI2.1 smooth scrolling via xf86-input-neko scroll valuators#196
Open
hiroTamada wants to merge 6 commits intomainfrom
Open
feat: XI2.1 smooth scrolling via xf86-input-neko scroll valuators#196hiroTamada wants to merge 6 commits intomainfrom
hiroTamada wants to merge 6 commits intomainfrom
Conversation
Replace the XTest button-event scroll path (fixed ~100px quantum per click) with XInput2.1 scroll valuators on the xf86-input-neko driver. This gives pixel-precise 1:1 mapping between client trackpad deltas and browser scroll pixels, eliminating the jumpiness of discrete scroll notches. Changes: - neko.c: add vertical/horizontal scroll valuator axes (3, 4) with SetScrollValuator; handle NEKO_SCROLL messages via xf86PostMotionEventM instead of button events; change device type from XI_TOUCHSCREEN to XI_MOUSE so Chromium respects valuators - video.vue: remove PIXELS_PER_TICK quantization and 100ms throttle; send raw pixel deltas batched per rAF; use document-level wheel listener with passive:false for reliable preventDefault - base.ts: extend wheel binary message to include controlKey byte (PayloadScrollWithCtrl, length=5) - neko.yaml: enable xinput driver with socket path - Dockerfile: use neko base image with XI2 scroll support Requires: kernel/neko branch hiro/xi2-scroll Made-with: Cursor
- Scope wheel listener to overlay element instead of document to avoid blocking scrolling in chat/emoji/files panels - Store handler reference and remove it in beforeDestroy to prevent leaks - Re-integrate scroll sensitivity setting (1-100 range, 50 = 1x) - Remove unused scroll_x/scroll_y fields from xf86-input-neko driver - Revert Dockerfile FROM to existing neko base tag for CI (pending neko#12) Made-with: Cursor
hiroTamada
commented
Apr 1, 2026
|
|
||
| FROM ghcr.io/kernel/neko/base:3.0.8-v1.4.0 AS neko | ||
| # ^--- now has event.SYSTEM_PONG with legacy support to keepalive | ||
| # TODO: update to xi2-scroll tag once kernel/neko#12 is merged and tagged |
Contributor
Author
There was a problem hiding this comment.
I will update this once neko PR is merged
- Cancel any pending requestAnimationFrame in beforeDestroy to prevent accessing torn-down Vue instance - Restore sendMousePos call in onWheel so cursor position is synced before scroll events (trackpad scrolling doesn't fire mousemove) Made-with: Cursor
The default scroll setting is 10, so the divisor should be 10 (not 50) to produce 1x sensitivity at the default value. Range: 1=0.1x to 100=10x. Made-with: Cursor
Call preventDefault() before the locked check so the page doesn't scroll underneath the video overlay when hosting but locked. Made-with: Cursor
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
- Subtract sent dx/dy from accumulators instead of resetting to zero, preserving fractional remainders for accurate sensitivity scaling - Use assignment for ctrl flag to reflect latest event state, avoiding false positives from transient ctrl events within a RAF batch Made-with: Cursor
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Checklist
ghcr.io/kernel/neko/base:xi2-scrollimage to be publishedSummary
Replace the XTest button-event scroll path with XInput2.1 smooth scroll valuators on the
xf86-input-nekoXorg driver. This fixes the fundamental scrolling problem: XTest could only fire discrete "notch" events (~100px per click in Chromium), making it impossible to achieve smooth trackpad scrolling. With XI2.1, we get pixel-precise 1:1 mapping between client trackpad deltas and browser scroll pixels.Problem
The old XTest approach (
xf86PostButtonEventwith buttons 4/5) had a fixed scroll quantum — each button press scrolled ~100px in Chromium with no sub-notch precision. This meant:pixelsPerNotchtoo low → fires many button clicks → scrolls too fastpixelsPerNotchtoo high → needs lots of delta before firing → feels jumpy/unresponsiveSolution
Use XInput2.1 scroll valuators which provide continuous, sub-pixel scroll precision:
xf86-input-neko driver (
neko.c):SetScrollValuatorNEKO_SCROLL(0x80) messages viaxf86PostMotionEventMinstead of button eventsXI_TOUCHSCREENtoXI_MOUSEso Chromium respects scroll valuatorsClient (
video.vue/base.ts):PIXELS_PER_TICKquantization and 100ms throttlerequestAnimationFramepassive: falsefor reliablepreventDefault()controlKeybyte (length=5)Config:
neko.yamlwith socket pathTest results
Programmatic tests confirm exact 1:1 pixel mapping:
Depends on
kernel/neko@hiro/xi2-scroll— server-side changes to send raw pixel deltas to xinput driverMade with Cursor
Note
Medium Risk
Changes the low-level mouse wheel pipeline end-to-end (client event handling, binary protocol, and Xorg input driver), so regressions could break scrolling or input behavior across browsers/hosts.
Overview
Adds an XI2.1 smooth-scrolling path by extending
xf86-input-nekowith vertical/horizontal scroll valuators and handling a newNEKO_SCROLLmessage type via motion events, including switching the device type toXI_MOUSE.Updates the web client wheel handling to prevent default scrolling via a non-passive wheel listener, batch raw deltas per
requestAnimationFrame(removing quantization/throttle), and sends an updated wheel payload including acontrolKeybyte.Enables the new input driver via
neko.yamlconfiguration (input socket), and annotates the container build to track the required upstreamneko/baseimage tag.Written by Cursor Bugbot for commit 11c1ba1. This will update automatically on new commits. Configure here.