From abe20b6e73c733c2eaa45c492fed63158180d687 Mon Sep 17 00:00:00 2001 From: Kaloyan Donev Date: Wed, 23 Sep 2026 10:52:33 +0300 Subject: [PATCH] Drop unused raw input registration on Windows 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) --- src/app.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/app.rs b/src/app.rs index 1b254a0..caaa896 100644 --- a/src/app.rs +++ b/src/app.rs @@ -18,7 +18,7 @@ use napi::Result; use napi_derive::napi; use tao::{ event::{ElementState, MouseButton, MouseScrollDelta, TouchPhase, WindowEvent}, - event_loop::EventLoop, + event_loop::{DeviceEventFilter, EventLoop}, keyboard::{Key, KeyCode, ModifiersState}, window::WindowId, }; @@ -743,6 +743,11 @@ impl Application { pub fn new(env: Env, _options: Option) -> Result { let event_loop = EventLoop::new(); + // Nothing here ever consumes a tao DeviceEvent (the pump closure drops + // them), so leaving raw input registered is pure cost - on Windows it + // delayed input into the WebView2 child by seconds while a key was held. + event_loop.set_device_event_filter(DeviceEventFilter::Always); + // On macOS install a default app menu immediately so the menu bar is // functional from the start. Store it in global_menu so the ObjC delegate // is kept alive (it would be freed if the Menu were dropped here).