feat: bundle xdotool, use xdotool type for Unicode injection

- 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)
This commit is contained in:
Khoa Vo 2026-06-26 16:44:13 +07:00
parent 3ce274c9ae
commit 01fe7c4f1c

View file

@ -352,9 +352,7 @@ impl UinputInjector {
// If all ASCII, send keycodes directly — fast and reliable // If all ASCII, send keycodes directly — fast and reliable
if text.chars().all(|c| char_to_linux_keycode(c).is_some() || c == '\n') { if text.chars().all(|c| char_to_linux_keycode(c).is_some() || c == '\n') {
if backspaces > 0 { if backspaces > 0 {
for _ in 0..backspaces { for _ in 0..backspaces { let _ = self.send_backspace(); }
let _ = self.send_backspace();
}
} }
for ch in text.chars() { for ch in text.chars() {
if ch == '\n' { self.send_enter(); } if ch == '\n' { self.send_enter(); }
@ -363,22 +361,25 @@ impl UinputInjector {
return InjectResult::Success; return InjectResult::Success;
} }
// Unicode text: paste entire text via clipboard (includes spaces). // Unicode text: try xdotool first (reliable, no clipboard)
// Don't split — splitting causes space to arrive after next keystrokes. if !std::env::var("WAYLAND_DISPLAY").is_ok() {
// Send backspaces first, then clipboard paste everything at once. let result = std::process::Command::new("xdotool")
if backspaces > 0 { .args(["type", "--clearmodifiers", "--delay", "1", text])
for _ in 0..backspaces { .output();
let _ = self.send_backspace(); 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) { if self.copy_to_clipboard(text) {
self.send_ctrl_v_x11(); 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 InjectResult::Success