From 5f0f0591390831536b75ab5329bf970e9810f2cd Mon Sep 17 00:00:00 2001 From: Khoa Vo Date: Sat, 4 Jul 2026 16:29:48 +0700 Subject: [PATCH] =?UTF-8?q?fix:=20non-grabbed=20evdev=20on=20X11=20?= =?UTF-8?q?=E2=80=94=20switch=20to=20XTest=20injection=20for=20instant=20c?= =?UTF-8?q?orrection?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On X11, uinput injection has high latency (kernel → libinput → X server) compared to XTest' which writes events directly into the X11 event queue. Physical key events reach the app before the uinput backspace correction, causing the VNI control key digit to flash on screen. Fix: when non-grabbed mode is entered on X11 (grab failed or ungrab), replace the uinput injector with X11Injector (XTest). XTest events are processed synchronously by X server and take effect before subsequent physical events. --- daemon/src/main.rs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/daemon/src/main.rs b/daemon/src/main.rs index 6516c4b..630eee3 100644 --- a/daemon/src/main.rs +++ b/daemon/src/main.rs @@ -1309,7 +1309,7 @@ fn run_with_evdev( _engine_enabled: Arc, display: display::DisplayServer, ) -> Result<(), Box> { - let injector = create_injector(display)?; + let mut injector = create_injector(display)?; // Use the first device for grab (only one device can be grabbed at a time) let primary_idx = 0usize; @@ -1336,6 +1336,17 @@ fn run_with_evdev( false }; + // Non-grabbed on X11: use XTest injection for fast, synchronous correction + if !grabbed { + #[cfg(feature = "x11")] + if display != display::DisplayServer::Wayland { + if let Ok(x11_inj) = vietc_protocol::x11_inject::X11Injector::new() { + injector = Box::new(x11_inj); + log_info("[vietc] Non-grabbed: using X11 injection (faster than uinput)"); + } + } + } + let mut consumed_keys: HashSet = HashSet::new(); let mut last_active_window = String::new(); let mut last_window_class = String::new(); @@ -1374,6 +1385,14 @@ fn run_with_evdev( let _ = devices[primary_idx].0.ungrab(); grabbed = false; log_info("[vietc] Non-grabbed mode: polling all evdev devices for keystrokes"); + // Switch to XTest injection for fast synchronous non-grabbed correction + #[cfg(feature = "x11")] + if display != display::DisplayServer::Wayland { + if let Ok(x11_inj) = vietc_protocol::x11_inject::X11Injector::new() { + injector = Box::new(x11_inj); + log_info("[vietc] Non-grabbed: using X11 injection (faster than uinput)"); + } + } continue; }