use std::io::ErrorKind;
use std::process::Command;
use std::time::Duration;
use super::capture;
const MODS_CLEAR_TIMEOUT_MS: u64 = 250;
#[derive(Debug, thiserror::Error)]
pub enum EmitError {
#[error(
"`wtype` is not installed — install it (e.g. `sudo pacman -S wtype`) so hyprcorrect can type corrections"
)]
WtypeMissing,
#[error("`wtype` failed to apply the correction")]
WtypeFailed,
}
const WTYPE_INTER_KEY_DELAY_MS: u32 = 8;
pub fn replace(backspaces: usize, text: &str) -> Result<(), EmitError> {
replace_with_delay(backspaces, text, 8)
}
pub fn replace_with_delay(
backspaces: usize,
text: &str,
pause_per_backspace_ms: u32,
) -> Result<(), EmitError> {
replace_around_caret_with_delay(backspaces, 0, text, pause_per_backspace_ms)
}
pub fn replace_around_caret_with_delay(
backspaces: usize,
deletes: usize,
text: &str,
pause_per_backspace_ms: u32,
) -> Result<(), EmitError> {
let _ = capture::wait_mods_clear(Duration::from_millis(MODS_CLEAR_TIMEOUT_MS));
if deletes > 0 {
let mut cmd = Command::new("wtype");
cmd.args(["-d", &WTYPE_INTER_KEY_DELAY_MS.to_string()]);
for _ in 0..deletes {
cmd.args(["-P", "Right", "-p", "Right"]);
}
run(cmd)?;
sleep_ms(pause_per_backspace_ms, deletes);
}
let total_backspaces = backspaces + deletes;
if total_backspaces > 0 {
let mut cmd = Command::new("wtype");
cmd.args(["-d", &WTYPE_INTER_KEY_DELAY_MS.to_string()]);
for _ in 0..total_backspaces {
cmd.args(["-P", "BackSpace", "-p", "BackSpace"]);
}
run(cmd)?;
sleep_ms(pause_per_backspace_ms, total_backspaces);
}
if !text.is_empty() {
let mut cmd = Command::new("wtype");
cmd.args(["-d", &WTYPE_INTER_KEY_DELAY_MS.to_string()]);
cmd.arg("--").arg(text);
run(cmd)?;
}
Ok(())
}
pub fn anchored_replace_with_delay(
chars_from_end: usize,
word_chars: usize,
insert: &str,
pause_per_backspace_ms: u32,
) -> Result<(), EmitError> {
let _ = capture::wait_mods_clear(Duration::from_millis(MODS_CLEAR_TIMEOUT_MS));
{
let mut cmd = Command::new("wtype");
cmd.args(["-d", &WTYPE_INTER_KEY_DELAY_MS.to_string()]);
cmd.args(["-P", "End", "-p", "End"]);
run(cmd)?;
sleep_ms(pause_per_backspace_ms, 1);
}
if chars_from_end > 0 {
let mut cmd = Command::new("wtype");
cmd.args(["-d", &WTYPE_INTER_KEY_DELAY_MS.to_string()]);
for _ in 0..chars_from_end {
cmd.args(["-P", "Left", "-p", "Left"]);
}
run(cmd)?;
sleep_ms(pause_per_backspace_ms, chars_from_end);
}
if word_chars > 0 {
let mut cmd = Command::new("wtype");
cmd.args(["-d", &WTYPE_INTER_KEY_DELAY_MS.to_string()]);
for _ in 0..word_chars {
cmd.args(["-P", "BackSpace", "-p", "BackSpace"]);
}
run(cmd)?;
sleep_ms(pause_per_backspace_ms, word_chars);
}
if !insert.is_empty() {
let mut cmd = Command::new("wtype");
cmd.args(["-d", &WTYPE_INTER_KEY_DELAY_MS.to_string()]);
cmd.arg("--").arg(insert);
run(cmd)?;
}
Ok(())
}
fn sleep_ms(pause_per_backspace_ms: u32, count: usize) {
let total = u64::from(pause_per_backspace_ms).saturating_mul(count as u64);
if total > 0 {
std::thread::sleep(std::time::Duration::from_millis(total));
}
}
fn run(mut cmd: Command) -> Result<(), EmitError> {
let status = cmd.status().map_err(|e| match e.kind() {
ErrorKind::NotFound => EmitError::WtypeMissing,
_ => EmitError::WtypeFailed,
})?;
if status.success() {
Ok(())
} else {
Err(EmitError::WtypeFailed)
}
}