Handy/src-tauri/src/input.rs
CJ Pais 0cb88994f0
refactor enigo input (#441)
* refactor enigo input

* format
2025-12-12 16:33:49 +07:00

123 lines
4.8 KiB
Rust

use enigo::{Enigo, Key, Keyboard, Mouse, Settings};
use std::sync::Mutex;
use tauri::{AppHandle, Manager};
/// Wrapper for Enigo to store in Tauri's managed state.
/// Enigo is wrapped in a Mutex since it requires mutable access.
pub struct EnigoState(pub Mutex<Enigo>);
impl EnigoState {
pub fn new() -> Result<Self, String> {
let enigo = Enigo::new(&Settings::default())
.map_err(|e| format!("Failed to initialize Enigo: {}", e))?;
Ok(Self(Mutex::new(enigo)))
}
}
/// Get the current mouse cursor position using the managed Enigo instance.
/// Returns None if the state is not available or if getting the location fails.
pub fn get_cursor_position(app_handle: &AppHandle) -> Option<(i32, i32)> {
let enigo_state = app_handle.try_state::<EnigoState>()?;
let enigo = enigo_state.0.lock().ok()?;
enigo.location().ok()
}
/// Sends a Ctrl+V or Cmd+V paste command using platform-specific virtual key codes.
/// This ensures the paste works regardless of keyboard layout (e.g., Russian, AZERTY, DVORAK).
/// Note: On Wayland, this may not work - callers should check for Wayland and use alternative methods.
pub fn send_paste_ctrl_v(enigo: &mut Enigo) -> Result<(), String> {
// Platform-specific key definitions
#[cfg(target_os = "macos")]
let (modifier_key, v_key_code) = (Key::Meta, Key::Other(9));
#[cfg(target_os = "windows")]
let (modifier_key, v_key_code) = (Key::Control, Key::Other(0x56)); // VK_V
#[cfg(target_os = "linux")]
let (modifier_key, v_key_code) = (Key::Control, Key::Unicode('v'));
// Press modifier + V
enigo
.key(modifier_key, enigo::Direction::Press)
.map_err(|e| format!("Failed to press modifier key: {}", e))?;
enigo
.key(v_key_code, enigo::Direction::Click)
.map_err(|e| format!("Failed to click V key: {}", e))?;
std::thread::sleep(std::time::Duration::from_millis(100));
enigo
.key(modifier_key, enigo::Direction::Release)
.map_err(|e| format!("Failed to release modifier key: {}", e))?;
Ok(())
}
/// Sends a Ctrl+Shift+V paste command.
/// This is commonly used in terminal applications on Linux to paste without formatting.
/// Note: On Wayland, this may not work - callers should check for Wayland and use alternative methods.
pub fn send_paste_ctrl_shift_v(enigo: &mut Enigo) -> Result<(), String> {
// Platform-specific key definitions
#[cfg(target_os = "macos")]
let (modifier_key, v_key_code) = (Key::Meta, Key::Other(9)); // Cmd+Shift+V on macOS
#[cfg(target_os = "windows")]
let (modifier_key, v_key_code) = (Key::Control, Key::Other(0x56)); // VK_V
#[cfg(target_os = "linux")]
let (modifier_key, v_key_code) = (Key::Control, Key::Unicode('v'));
// Press Ctrl/Cmd + Shift + V
enigo
.key(modifier_key, enigo::Direction::Press)
.map_err(|e| format!("Failed to press modifier key: {}", e))?;
enigo
.key(Key::Shift, enigo::Direction::Press)
.map_err(|e| format!("Failed to press Shift key: {}", e))?;
enigo
.key(v_key_code, enigo::Direction::Click)
.map_err(|e| format!("Failed to click V key: {}", e))?;
std::thread::sleep(std::time::Duration::from_millis(100));
enigo
.key(Key::Shift, enigo::Direction::Release)
.map_err(|e| format!("Failed to release Shift key: {}", e))?;
enigo
.key(modifier_key, enigo::Direction::Release)
.map_err(|e| format!("Failed to release modifier key: {}", e))?;
Ok(())
}
/// Sends a Shift+Insert paste command (Windows and Linux only).
/// This is more universal for terminal applications and legacy software.
/// Note: On Wayland, this may not work - callers should check for Wayland and use alternative methods.
pub fn send_paste_shift_insert(enigo: &mut Enigo) -> Result<(), String> {
#[cfg(target_os = "windows")]
let insert_key_code = Key::Other(0x2D); // VK_INSERT
#[cfg(not(target_os = "windows"))]
let insert_key_code = Key::Other(0x76); // XK_Insert (keycode 118 / 0x76, also used as fallback)
// Press Shift + Insert
enigo
.key(Key::Shift, enigo::Direction::Press)
.map_err(|e| format!("Failed to press Shift key: {}", e))?;
enigo
.key(insert_key_code, enigo::Direction::Click)
.map_err(|e| format!("Failed to click Insert key: {}", e))?;
std::thread::sleep(std::time::Duration::from_millis(100));
enigo
.key(Key::Shift, enigo::Direction::Release)
.map_err(|e| format!("Failed to release Shift key: {}", e))?;
Ok(())
}
/// Pastes text directly using the enigo text method.
/// This tries to use system input methods if possible, otherwise simulates keystrokes one by one.
pub fn paste_text_direct(enigo: &mut Enigo, text: &str) -> Result<(), String> {
enigo
.text(text)
.map_err(|e| format!("Failed to send text directly: {}", e))?;
Ok(())
}