From 4baa460562ba632b8d66ed13b08c715e4d6c9968 Mon Sep 17 00:00:00 2001 From: Khoa Vo Date: Thu, 2 Jul 2026 12:03:21 +0700 Subject: [PATCH] fix: use xdotool for Unicode injection instead of clipboard paste MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On X11 (Linux Mint, Ubuntu), clipboard-based Unicode injection was failing — backspace was sent but the Vietnamese character never appeared because xclip paste via Ctrl+V wasn't reliable from a root uinput process. Now send_string tries xdotool type first (XTest-based, doesn't touch the user's clipboard), falling back to clipboard paste only on Wayland or when xdotool is unavailable. --- protocol/src/uinput_monitor.rs | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/protocol/src/uinput_monitor.rs b/protocol/src/uinput_monitor.rs index 9988b1c..fc01dfa 100644 --- a/protocol/src/uinput_monitor.rs +++ b/protocol/src/uinput_monitor.rs @@ -222,7 +222,12 @@ impl KeyInjector for UinputInjector { return InjectResult::Success; } - // Unicode text: clipboard copy + paste (reliable method) + // Unicode text: try xdotool type first (works on X11, doesn't touch clipboard) + if self.type_via_xdotool(s) { + return InjectResult::Success; + } + + // Fallback: clipboard copy + paste if !self.paste_via_clipboard(s) { eprintln!( "[vietc] send_string failed for '{}' (clipboard unavailable)", @@ -388,6 +393,27 @@ impl UinputInjector { InjectResult::Success } + /// Type Unicode text via xdotool (X11 only). Returns true on success. + /// More reliable than clipboard paste — doesn't overwrite the user's clipboard + /// and works with XTest directly for proper Unicode key injection. + fn type_via_xdotool(&self, text: &str) -> bool { + let is_wayland = std::env::var("WAYLAND_DISPLAY").is_ok(); + if is_wayland { + return false; + } + let mut cmd = Self::user_cmd("xdotool"); + cmd.args(["type", "--clearmodifiers", text]); + cmd.stdout(std::process::Stdio::null()); + cmd.stderr(std::process::Stdio::null()); + match cmd.output() { + Ok(output) => output.status.success(), + Err(e) => { + eprintln!("[vietc] xdotool type failed: {}", e); + false + } + } + } + /// Read the user's current clipboard contents (wl-paste on Wayland, xclip /// on X11). Returns None if no clipboard tool is available or it is empty. fn read_clipboard() -> Option {