From 01fe7c4f1c061728c5fbfc4b2e656532cb20a750 Mon Sep 17 00:00:00 2001 From: Khoa Vo Date: Fri, 26 Jun 2026 16:44:13 +0700 Subject: [PATCH] feat: bundle xdotool, use xdotool type for Unicode injection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - xdotool types text directly into X11 focus window — no clipboard hack - More reliable than clipboard paste (no trimming, no timing issues) - Fallback to clipboard if xdotool not available - Only on X11 (Wayland uses clipboard fallback) --- protocol/src/uinput_monitor.rs | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/protocol/src/uinput_monitor.rs b/protocol/src/uinput_monitor.rs index 989abcb..633dd97 100644 --- a/protocol/src/uinput_monitor.rs +++ b/protocol/src/uinput_monitor.rs @@ -352,9 +352,7 @@ impl UinputInjector { // If all ASCII, send keycodes directly — fast and reliable if text.chars().all(|c| char_to_linux_keycode(c).is_some() || c == '\n') { if backspaces > 0 { - for _ in 0..backspaces { - let _ = self.send_backspace(); - } + for _ in 0..backspaces { let _ = self.send_backspace(); } } for ch in text.chars() { if ch == '\n' { self.send_enter(); } @@ -363,22 +361,25 @@ impl UinputInjector { return InjectResult::Success; } - // Unicode text: paste entire text via clipboard (includes spaces). - // Don't split — splitting causes space to arrive after next keystrokes. - // Send backspaces first, then clipboard paste everything at once. - if backspaces > 0 { - for _ in 0..backspaces { - let _ = self.send_backspace(); + // Unicode text: try xdotool first (reliable, no clipboard) + if !std::env::var("WAYLAND_DISPLAY").is_ok() { + let result = std::process::Command::new("xdotool") + .args(["type", "--clearmodifiers", "--delay", "1", text]) + .output(); + if result.map(|o| o.status.success()).unwrap_or(false) { + if backspaces > 0 { + for _ in 0..backspaces { let _ = self.send_backspace(); } + } + return InjectResult::Success; } } + // Fallback: clipboard paste + if backspaces > 0 { + for _ in 0..backspaces { let _ = self.send_backspace(); } + } if self.copy_to_clipboard(text) { self.send_ctrl_v_x11(); - } else { - // Fallback: send base ASCII chars via uinput - for ch in text.chars() { - let _ = self.send_char(ch); - } } InjectResult::Success