use crate::error::{AumateError, Result};
use enigo::{Axis, Button, Coordinate, Direction, Enigo, Mouse as MouseTrait};
use std::str::FromStr;
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MouseButton {
Left,
Right,
Middle,
}
impl FromStr for MouseButton {
type Err = AumateError;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"left" => Ok(MouseButton::Left),
"right" => Ok(MouseButton::Right),
"middle" => Ok(MouseButton::Middle),
_ => Err(AumateError::Input(format!("Invalid button: {}", s))),
}
}
}
impl MouseButton {
fn to_enigo_button(self) -> Button {
match self {
MouseButton::Left => Button::Left,
MouseButton::Right => Button::Right,
MouseButton::Middle => Button::Middle,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MousePosition {
pub x: i32,
pub y: i32,
}
pub struct Mouse {
enigo: Arc<Mutex<Enigo>>,
delay_ms: Arc<Mutex<u32>>,
}
impl Mouse {
pub fn new() -> Result<Self> {
let enigo = Enigo::new(&enigo::Settings::default())
.map_err(|e| AumateError::Input(format!("Failed to create Enigo: {}", e)))?;
Ok(Self { enigo: Arc::new(Mutex::new(enigo)), delay_ms: Arc::new(Mutex::new(10)) })
}
pub fn move_mouse(&self, x: i32, y: i32) -> Result<()> {
let mut enigo =
self.enigo.lock().map_err(|e| AumateError::Input(format!("Lock error: {}", e)))?;
let _ = enigo.move_mouse(x, y, Coordinate::Abs);
self.apply_delay();
Ok(())
}
pub fn move_mouse_smooth(&self, x: i32, y: i32) -> Result<()> {
self.move_mouse_smooth_with_speed(x, y, 3.0)
}
pub fn move_mouse_smooth_with_speed(&self, x: i32, y: i32, speed: f64) -> Result<()> {
let current_pos = self.get_mouse_pos()?;
let start_x = current_pos.x as f64;
let start_y = current_pos.y as f64;
let end_x = x as f64;
let end_y = y as f64;
let distance = ((end_x - start_x).powi(2) + (end_y - start_y).powi(2)).sqrt();
let steps = (distance / speed).max(1.0) as u32;
for i in 0..=steps {
let t = i as f64 / steps as f64;
let eased = t * t * (3.0 - 2.0 * t);
let current_x = (start_x + (end_x - start_x) * eased) as i32;
let current_y = (start_y + (end_y - start_y) * eased) as i32;
self.move_mouse(current_x, current_y)?;
thread::sleep(Duration::from_millis(1));
}
Ok(())
}
pub fn get_mouse_pos(&self) -> Result<MousePosition> {
let enigo =
self.enigo.lock().map_err(|e| AumateError::Input(format!("Lock error: {}", e)))?;
let (x, y) = enigo
.location()
.map_err(|e| AumateError::Input(format!("Failed to get mouse position: {}", e)))?;
Ok(MousePosition { x, y })
}
pub fn click(&self, button: MouseButton) -> Result<()> {
let mut enigo =
self.enigo.lock().map_err(|e| AumateError::Input(format!("Lock error: {}", e)))?;
let _ = enigo.button(button.to_enigo_button(), Direction::Click);
self.apply_delay();
Ok(())
}
pub fn double_click(&self, button: MouseButton) -> Result<()> {
let mut enigo =
self.enigo.lock().map_err(|e| AumateError::Input(format!("Lock error: {}", e)))?;
let enigo_button = button.to_enigo_button();
let _ = enigo.button(enigo_button, Direction::Click);
thread::sleep(Duration::from_millis(50));
let _ = enigo.button(enigo_button, Direction::Click);
self.apply_delay();
Ok(())
}
pub fn mouse_click(&self, button: Option<&str>, double: Option<bool>) -> Result<()> {
let button_str = button.unwrap_or("left");
let is_double = double.unwrap_or(false);
let mouse_button = MouseButton::from_str(button_str)?;
if is_double { self.double_click(mouse_button) } else { self.click(mouse_button) }
}
pub fn press(&self, button: MouseButton) -> Result<()> {
let mut enigo =
self.enigo.lock().map_err(|e| AumateError::Input(format!("Lock error: {}", e)))?;
let _ = enigo.button(button.to_enigo_button(), Direction::Press);
self.apply_delay();
Ok(())
}
pub fn release(&self, button: MouseButton) -> Result<()> {
let mut enigo =
self.enigo.lock().map_err(|e| AumateError::Input(format!("Lock error: {}", e)))?;
let _ = enigo.button(button.to_enigo_button(), Direction::Release);
self.apply_delay();
Ok(())
}
pub fn mouse_toggle(&self, down: &str, button: Option<&str>) -> Result<()> {
let button_str = button.unwrap_or("left");
let mouse_button = MouseButton::from_str(button_str)?;
match down {
"down" => self.press(mouse_button),
"up" => self.release(mouse_button),
_ => Err(AumateError::Input(format!("Invalid direction: {}", down))),
}
}
pub fn drag_mouse(&self, x: i32, y: i32) -> Result<()> {
let mut enigo =
self.enigo.lock().map_err(|e| AumateError::Input(format!("Lock error: {}", e)))?;
let _ = enigo.button(Button::Left, Direction::Press);
let _ = enigo.move_mouse(x, y, Coordinate::Abs);
let _ = enigo.button(Button::Left, Direction::Release);
self.apply_delay();
Ok(())
}
pub fn scroll(&self, x: i32, y: i32) -> Result<()> {
let mut enigo =
self.enigo.lock().map_err(|e| AumateError::Input(format!("Lock error: {}", e)))?;
if x != 0 {
let _ = enigo.scroll(x, Axis::Horizontal);
}
if y != 0 {
let _ = enigo.scroll(y, Axis::Vertical);
}
self.apply_delay();
Ok(())
}
pub fn scroll_mouse(&self, x: i32, y: i32) -> Result<()> {
self.scroll(x, y)
}
pub fn set_delay(&self, delay_ms: u32) -> Result<()> {
let mut delay =
self.delay_ms.lock().map_err(|e| AumateError::Input(format!("Lock error: {}", e)))?;
*delay = delay_ms;
Ok(())
}
pub fn set_mouse_delay(&self, delay_ms: u32) -> Result<()> {
self.set_delay(delay_ms)
}
fn apply_delay(&self) {
let delay = self.delay_ms.lock().ok().map(|d| *d).unwrap_or(10);
if delay > 0 {
thread::sleep(Duration::from_millis(delay as u64));
}
}
}
impl Default for Mouse {
fn default() -> Self {
Self::new().expect("Failed to create Mouse")
}
}