Drop unused raw input registration on Windows - #56
Merged
Merged
Conversation
Symptom: Ctrl+wheel input reaching a WebView2 child window lagged by up
to several seconds while Ctrl was held (growing the longer it was
held); plain wheel and Alt+wheel were unaffected.
Cause: by default (DeviceEventFilter::Unfocused, i.e. RIDEV_DEVNOTIFY)
tao registers raw input for all mice and keyboards, so while this
window has focus every keystroke - a held key's autorepeat included,
~30/sec - posts WM_INPUT to this thread. This binding never consumes a
tao DeviceEvent anywhere: the pump closures in app.rs match only
WindowEvent, MainEventsCleared and NewEvents, dropping everything else
via `_ => {}`. So that raw input registration was pure cost, and on
Windows the resulting WM_INPUT traffic backing up on this thread
delayed whatever else was waiting on the same queue, including input
headed for the WebView2 child.
Fix: call event_loop.set_device_event_filter(DeviceEventFilter::Always)
once, right after the event loop is created, so raw input is never
registered for this thread in the first place (tao issues RIDEV_REMOVE
for mice and keyboards on Windows).
Safe because the binding has no code path, on any platform, that reads
a DeviceEvent - there is no behaviour to lose. set_device_event_filter
is already a documented no-op on non-Windows targets (tao gates its own
body internally), so this changes behaviour only on Windows, and only
by no longer asking for input this binding never reads.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
4 tasks
kdonev
added a commit
to kdonev/termscape
that referenced
this pull request
Sep 27, 2026
0.4.7 carries webviewjs/webview#56, which unregisters the unused raw input that made Ctrl+wheel zoom lag. The pump went back to its default. Closes #37 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Environment
Windows 11 Pro 10.0.26200, WebView2 runtime 153.0.4234.48, Node 24.21.0,
@webviewjs/webview0.4.6.Symptom
Under sustained mouse-wheel input with Ctrl held, wheel events reached
the page progressively later - and the delay did not clear during idle time
between gestures. Plain wheel (no modifier) was unaffected under identical
conditions on the same machine.
Reproduction
Minimal reproduction, no external repo needed:
@webviewjs/webviewwindow with a page-sidewheellistener,hold Ctrl and spin the mouse wheel. Events arrive progressively later,
by up to several seconds, the longer the gesture continues.
release Ctrl, then immediately spin the wheel with no modifier. The
first event afterward is late by seconds; the rest arrive normally.
A standalone repro with a visual arrival timeline (a rectangle that steps
once per wheel event, plus a 15-second arrival chart) is attached below as
webview-ctrl-wheel-repro.zip. Its README covers reproducing the bug againstthe published 0.4.6 and verifying this fix against a local build of this
branch, including how to tell which binary actually loaded.
webview-ctrl-wheel-repro.zip
Isolating measurements
All of the following were measured on Windows / WebView2, in order:
1. The original symptom, via a low-level OS hook. A
WH_MOUSE_LLhooktimestamping each physical wheel notch at the OS level, correlated against
performance.now()in the page at the moment its ownwheelhandler ran,across six successive Ctrl+wheel gestures 2-3 seconds apart:
The delay grew monotonically across the session rather than resetting
between gestures.
2. The crucial control, from the same six gestures. The delay from the
browser's own
WheelEvent.timeStampto the page's handler running was0-1ms every time. Whatever was happening, it happened before the event
reached the browser's own event queue - nothing downstream (renderer, page
JS) was ever slow.
3. The pump loop's own health, instrumented directly. ~40 ticks/sec,
pumpEvents()under 3ms mean, identical whether the gesture was Ctrl+wheelor plain wheel. This ruled out the JS-driven pump loop itself as the cause -
it was never slow, under either gesture.
4. Modifier breakdown, physical wheel, one gesture at a time:
scroll (
deltaYstays 0) - inconclusive, not evidence either way.5. Held-modifier-alone test. Holding Ctrl for 5 seconds with no wheel
input at all, releasing, then immediately spinning plain wheel: the
first wheel event afterward lagged by seconds; the rest were immediate.
The held key alone was building a backlog, with no wheel combined with it at
all.
6. Real-browser control. The identical page, served to a plain Edge
window instead of the WebView2-hosted one: Ctrl+wheel and plain wheel
arrived at the same rate. This ruled out Chromium's own Ctrl+wheel
handling (e.g. its built-in zoom accelerator) as the cause - the divergence
is specific to this binding's host process, not to Chromium in general.
7. Two switches added to isolate the mechanism, tested one at a time:
(
SetAreBrowserAcceleratorKeysEnabled(false)): lag unchanged. Rules outthe "Ctrl is an accelerator key needing a host round trip" theory.
(
DeviceEventFilter::Always, i.e.RIDEV_REMOVE): lag gone entirely.Isolates the cause to raw input specifically.
8. A separate
pumpEvents(timeoutMs)prototype (blocking the pump onthe OS message queue instead of a fixed clock) also removed the lag, but by
pumping fast enough to mask the backlog rather than removing its cause. Not
shipped.
Cause
By default (
DeviceEventFilter::Unfocused, i.e.RIDEV_DEVNOTIFY) taoregisters raw input for all mice and keyboards, so while this window has
focus every keystroke - a held key's autorepeat included, roughly 30/sec -
posts
WM_INPUTto this thread.This binding never reads a tao
DeviceEventanywhere: the pump closures inapp.rs(both the Windows/Linuxrun_returnpath and the macOSpump_eventspath) match onlyEvent::WindowEvent,Event::MainEventsClearedand
Event::NewEvents, dropping everything else - including everyDeviceEvent- through a catch-all_ => {}.So the registration was pure cost: on Windows, the resulting
WM_INPUTtraffic - amplified by a held key's autorepeat - was backing up on this
thread's message queue, delaying whatever else was waiting on it, wheel
input included.
Fix
One call, right after the event loop is created in
src/app.rs:DeviceEventFilter::Alwaysmeans "always filter device events" - tao'sWindows implementation responds by issuing
RIDEV_REMOVE, so mice andkeyboards are never registered for raw input on this thread in the first
place.
Why this is safe
DeviceEvent.There is no behaviour to lose.
set_device_event_filteris tao's own public, documented API and alreadya no-op on non-Windows targets: its body is gated
#[cfg(target_os = "windows")]internally(
tao/src/event_loop.rs:285-288) - this change has zero effect on macOS,Linux, Android, or the FreeBSD stub build. It only stops asking for input
that was never consumed, on the one platform where that registration was
actually happening.
change to anything this binding actually uses.
Verified
cargo fmt -- --check,cargo clippy -- -D warnings,cargo check: clean.node --test __test__/*.test.*: 43/47 pass - the same 4 pre-existingfailures as an untouched build of the commit this branches from, confirmed
by building that commit standalone and running the identical suite; no new
failures from this change.
with plain wheel, and holding Ctrl alone no longer delays the next wheel
event. (It was first confirmed during isolation testing with a build that
made the identical call behind a diagnostic switch - measurement 7.)
Not yet explained
Why Ctrl specifically shows the symptom and Alt does not is still
open - tao's raw keyboard path has nothing Ctrl-specific in it, and this
asymmetry is unexplained. It doesn't change whether this fix is correct or
sufficient for the reported symptom, but it may be worth a note to tao as
well.
🤖 Generated with Claude Code