feat(linux): add ydotool support for text input (#557)
Add ydotool as a fallback option for direct text input and key combinations on Linux, after wtype/dotool (Wayland) and xdotool (X11). ydotool uses uinput and works on both Wayland and X11, making it a useful alternative when other tools are unavailable. Requirements: - ydotool installed - ydotoold daemon running - User in input group with uinput permissions
This commit is contained in:
parent
1b580d54e8
commit
3bd2ad841d
1 changed files with 77 additions and 4 deletions
|
|
@ -59,7 +59,7 @@ fn paste_via_clipboard(
|
|||
#[cfg(target_os = "linux")]
|
||||
fn try_send_key_combo_linux(paste_method: &PasteMethod) -> Result<bool, String> {
|
||||
if is_wayland() {
|
||||
// Wayland: prefer wtype, then dotool
|
||||
// Wayland: prefer wtype, then dotool, then ydotool
|
||||
if is_wtype_available() {
|
||||
info!("Using wtype for key combo");
|
||||
send_key_combo_via_wtype(paste_method)?;
|
||||
|
|
@ -70,13 +70,23 @@ fn try_send_key_combo_linux(paste_method: &PasteMethod) -> Result<bool, String>
|
|||
send_key_combo_via_dotool(paste_method)?;
|
||||
return Ok(true);
|
||||
}
|
||||
if is_ydotool_available() {
|
||||
info!("Using ydotool for key combo");
|
||||
send_key_combo_via_ydotool(paste_method)?;
|
||||
return Ok(true);
|
||||
}
|
||||
} else {
|
||||
// X11: prefer xdotool
|
||||
// X11: prefer xdotool, then ydotool
|
||||
if is_xdotool_available() {
|
||||
info!("Using xdotool for key combo");
|
||||
send_key_combo_via_xdotool(paste_method)?;
|
||||
return Ok(true);
|
||||
}
|
||||
if is_ydotool_available() {
|
||||
info!("Using ydotool for key combo");
|
||||
send_key_combo_via_ydotool(paste_method)?;
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(false)
|
||||
|
|
@ -87,7 +97,7 @@ fn try_send_key_combo_linux(paste_method: &PasteMethod) -> Result<bool, String>
|
|||
#[cfg(target_os = "linux")]
|
||||
fn try_direct_typing_linux(text: &str) -> Result<bool, String> {
|
||||
if is_wayland() {
|
||||
// Wayland: prefer wtype, then dotool
|
||||
// Wayland: prefer wtype, then dotool, then ydotool
|
||||
if is_wtype_available() {
|
||||
info!("Using wtype for direct text input");
|
||||
type_text_via_wtype(text)?;
|
||||
|
|
@ -98,13 +108,23 @@ fn try_direct_typing_linux(text: &str) -> Result<bool, String> {
|
|||
type_text_via_dotool(text)?;
|
||||
return Ok(true);
|
||||
}
|
||||
if is_ydotool_available() {
|
||||
info!("Using ydotool for direct text input");
|
||||
type_text_via_ydotool(text)?;
|
||||
return Ok(true);
|
||||
}
|
||||
} else {
|
||||
// X11: prefer xdotool
|
||||
// X11: prefer xdotool, then ydotool
|
||||
if is_xdotool_available() {
|
||||
info!("Using xdotool for direct text input");
|
||||
type_text_via_xdotool(text)?;
|
||||
return Ok(true);
|
||||
}
|
||||
if is_ydotool_available() {
|
||||
info!("Using ydotool for direct text input");
|
||||
type_text_via_ydotool(text)?;
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(false)
|
||||
|
|
@ -130,6 +150,16 @@ fn is_dotool_available() -> bool {
|
|||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Check if ydotool is available (uinput-based, works on both Wayland and X11)
|
||||
#[cfg(target_os = "linux")]
|
||||
fn is_ydotool_available() -> bool {
|
||||
Command::new("which")
|
||||
.arg("ydotool")
|
||||
.output()
|
||||
.map(|output| output.status.success())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn is_xdotool_available() -> bool {
|
||||
Command::new("which")
|
||||
|
|
@ -204,6 +234,24 @@ fn type_text_via_dotool(text: &str) -> Result<(), String> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Type text directly via ydotool (uinput-based, requires ydotoold daemon).
|
||||
#[cfg(target_os = "linux")]
|
||||
fn type_text_via_ydotool(text: &str) -> Result<(), String> {
|
||||
let output = Command::new("ydotool")
|
||||
.arg("type")
|
||||
.arg("--")
|
||||
.arg(text)
|
||||
.output()
|
||||
.map_err(|e| format!("Failed to execute ydotool: {}", e))?;
|
||||
|
||||
if !output.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
return Err(format!("ydotool failed: {}", stderr));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Send a key combination (e.g., Ctrl+V) via wtype on Wayland.
|
||||
#[cfg(target_os = "linux")]
|
||||
fn send_key_combo_via_wtype(paste_method: &PasteMethod) -> Result<(), String> {
|
||||
|
|
@ -250,6 +298,31 @@ fn send_key_combo_via_dotool(paste_method: &PasteMethod) -> Result<(), String> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Send a key combination (e.g., Ctrl+V) via ydotool (requires ydotoold daemon).
|
||||
#[cfg(target_os = "linux")]
|
||||
fn send_key_combo_via_ydotool(paste_method: &PasteMethod) -> Result<(), String> {
|
||||
// ydotool uses Linux input event keycodes with format <keycode>:<pressed>
|
||||
// where pressed is 1 for down, 0 for up. Keycodes: ctrl=29, shift=42, v=47, insert=110
|
||||
let args: Vec<&str> = match paste_method {
|
||||
PasteMethod::CtrlV => vec!["key", "29:1", "47:1", "47:0", "29:0"],
|
||||
PasteMethod::ShiftInsert => vec!["key", "42:1", "110:1", "110:0", "42:0"],
|
||||
PasteMethod::CtrlShiftV => vec!["key", "29:1", "42:1", "47:1", "47:0", "42:0", "29:0"],
|
||||
_ => return Err("Unsupported paste method".into()),
|
||||
};
|
||||
|
||||
let output = Command::new("ydotool")
|
||||
.args(&args)
|
||||
.output()
|
||||
.map_err(|e| format!("Failed to execute ydotool: {}", e))?;
|
||||
|
||||
if !output.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
return Err(format!("ydotool failed: {}", stderr));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Send a key combination (e.g., Ctrl+V) via xdotool on X11.
|
||||
#[cfg(target_os = "linux")]
|
||||
fn send_key_combo_via_xdotool(paste_method: &PasteMethod) -> Result<(), String> {
|
||||
|
|
|
|||
Loading…
Reference in a new issue