use std::{
thread::sleep,
time::{Duration, Instant},
};
use crate::core::mouse::{MouseClick, MouseScroll};
use crate::errors::AutoGuiError;
use core_graphics::{
event::{CGEvent, CGEventTapLocation, CGEventType, CGMouseButton, ScrollEventUnit},
event_source::{CGEventSource, CGEventSourceStateID},
geometry::CGPoint,
};
pub struct Mouse {}
impl Mouse {
pub fn new() -> Self {
Self {}
}
pub fn move_mouse_to_pos(x: i32, y: i32, moving_time: f32) -> Result<(), AutoGuiError> {
if moving_time <= 0.0 {
Mouse::move_mouse(x, y)
} else {
let start_location = Mouse::get_mouse_position()?;
let distance_x = x - start_location.0;
let distance_y = y - start_location.1;
let start = Instant::now();
loop {
let duration = start.elapsed().as_secs_f32();
let time_passed_percentage = duration / moving_time;
if time_passed_percentage > 10.0 {
continue;
}
let new_x = start_location.0 as f32 + (time_passed_percentage * distance_x as f32);
let new_y = start_location.1 as f32 + (time_passed_percentage * distance_y as f32);
if time_passed_percentage >= 1.0 {
Mouse::move_mouse(x, y)?;
break;
} else {
Mouse::move_mouse(new_x as i32, new_y as i32)?;
}
}
Ok(())
}
}
pub fn drag_mouse(x: i32, y: i32, moving_time: f32) -> Result<(), AutoGuiError> {
let (cg_button, down, up) = (
CGMouseButton::Left,
CGEventType::LeftMouseDown,
CGEventType::LeftMouseUp,
);
let drag = CGEventType::LeftMouseDragged;
let mouse_pos = Mouse::get_mouse_position()?;
let cg_event_source =
CGEventSource::new(CGEventSourceStateID::HIDSystemState).map_err(|_| {
AutoGuiError::OSFailure(
"Error creating CGEventSource on mouse movement".to_string(),
)
})?;
let click_down = CGEvent::new_mouse_event(
cg_event_source.clone(),
down,
CGPoint::new(mouse_pos.0 as f64, mouse_pos.1 as f64),
cg_button,
)
.map_err(|_| AutoGuiError::OSFailure("Failed the mouse click down CGevent".to_string()))?;
click_down.post(CGEventTapLocation::HID);
sleep(Duration::from_millis(100));
let distance = (((x - mouse_pos.0).pow(2) + (y - mouse_pos.1).pow(2)) as f32).sqrt();
let steps = distance / 20.0;
let dx = (x - mouse_pos.0) as f64 / steps as f64;
let dy = (y - mouse_pos.1) as f64 / steps as f64;
for i in 1..=steps as i32 {
let new_x = mouse_pos.0 as f64 + dx * i as f64;
let new_y = mouse_pos.1 as f64 + dy * i as f64;
let drag_event = CGEvent::new_mouse_event(
cg_event_source.clone(),
drag, CGPoint::new(new_x, new_y),
cg_button,
)
.map_err(|_| AutoGuiError::OSFailure("Failed to create drag CGEvent".to_string()))?;
drag_event.post(CGEventTapLocation::HID);
sleep(Duration::from_millis(
(moving_time * 1000.0 / steps as f32) as u64,
));
}
let mouse_pos = Mouse::get_mouse_position()?;
let cg_event_source =
CGEventSource::new(CGEventSourceStateID::HIDSystemState).map_err(|_| {
AutoGuiError::OSFailure(
"Error creating CGEventSource on mouse movement".to_string(),
)
})?;
let click_up = CGEvent::new_mouse_event(
cg_event_source,
up,
CGPoint::new(mouse_pos.0 as f64, mouse_pos.1 as f64),
cg_button,
)
.map_err(|_| AutoGuiError::OSFailure("Failed the mouse click up CGevent".to_string()))?;
click_up.post(CGEventTapLocation::HID);
sleep(Duration::from_millis(20));
Ok(())
}
fn move_mouse(x: i32, y: i32) -> Result<(), AutoGuiError> {
let gc_event_source =
CGEventSource::new(CGEventSourceStateID::HIDSystemState).map_err(|_| {
AutoGuiError::OSFailure(
"Error creating CGEventSource on mouse movement".to_string(),
)
})?;
let event = CGEvent::new_mouse_event(
gc_event_source,
CGEventType::MouseMoved,
CGPoint::new(x as f64, y as f64),
CGMouseButton::Left,
)
.map_err(|_| AutoGuiError::OSFailure("Failed creating CGEvent".to_string()))?;
event.post(CGEventTapLocation::HID);
sleep(Duration::from_millis(20));
Ok(())
}
pub fn get_mouse_position() -> Result<(i32, i32), AutoGuiError> {
let gc_event_source =
CGEventSource::new(CGEventSourceStateID::HIDSystemState).map_err(|_| {
AutoGuiError::OSFailure(
"Error creating CGEventSource on mouse movement".to_string(),
)
})?;
let event = CGEvent::new(gc_event_source)
.map_err(|_| AutoGuiError::OSFailure("Failed creating CGevent".to_string()))?;
let point = event.location();
Ok((point.x as i32, point.y as i32))
}
pub fn mouse_click(button: MouseClick) -> Result<(), AutoGuiError> {
let (cg_button, down, up) = match button {
MouseClick::LEFT => (
CGMouseButton::Left,
CGEventType::LeftMouseDown,
CGEventType::LeftMouseUp,
),
MouseClick::RIGHT => (
CGMouseButton::Right,
CGEventType::RightMouseDown,
CGEventType::RightMouseUp,
),
MouseClick::MIDDLE => (
CGMouseButton::Center,
CGEventType::OtherMouseDown,
CGEventType::OtherMouseUp,
),
};
let mouse_pos = Mouse::get_mouse_position()?;
let cg_event_source =
CGEventSource::new(CGEventSourceStateID::HIDSystemState).map_err(|_| {
AutoGuiError::OSFailure(
"Error creating CGEventSource on mouse movement".to_string(),
)
})?;
let click_down = CGEvent::new_mouse_event(
cg_event_source,
down,
CGPoint::new(mouse_pos.0 as f64, mouse_pos.1 as f64),
cg_button,
)
.map_err(|_| AutoGuiError::OSFailure("Failed the mouse click down CGevent".to_string()))?;
click_down.post(CGEventTapLocation::HID);
sleep(Duration::from_millis(20));
let cg_event_source =
CGEventSource::new(CGEventSourceStateID::HIDSystemState).map_err(|_| {
AutoGuiError::OSFailure(
"Error creating CGEventSource on mouse movement".to_string(),
)
})?;
let click_up = CGEvent::new_mouse_event(
cg_event_source,
up,
CGPoint::new(mouse_pos.0 as f64, mouse_pos.1 as f64),
cg_button,
)
.map_err(|_| AutoGuiError::OSFailure("Failed the mouse click up CGevent".to_string()))?;
click_up.post(CGEventTapLocation::HID);
sleep(Duration::from_millis(20));
Ok(())
}
pub fn mouse_down(button: MouseClick) -> Result<(), AutoGuiError> {
let (cg_button, down) = match button {
MouseClick::LEFT => (CGMouseButton::Left, CGEventType::LeftMouseDown),
MouseClick::RIGHT => (CGMouseButton::Right, CGEventType::RightMouseDown),
MouseClick::MIDDLE => (CGMouseButton::Center, CGEventType::OtherMouseDown),
};
let mouse_pos = Mouse::get_mouse_position()?;
let cg_event_source =
CGEventSource::new(CGEventSourceStateID::HIDSystemState).map_err(|_| {
AutoGuiError::OSFailure(
"Error creating CGEventSource on mouse movement".to_string(),
)
})?;
let click_down = CGEvent::new_mouse_event(
cg_event_source,
down,
CGPoint::new(mouse_pos.0 as f64, mouse_pos.1 as f64),
cg_button,
)
.map_err(|_| AutoGuiError::OSFailure("Failed the mouse click down CGevent".to_string()))?;
click_down.post(CGEventTapLocation::HID);
sleep(Duration::from_millis(20));
Ok(())
}
pub fn mouse_up(button: MouseClick) -> Result<(), AutoGuiError> {
let (cg_button, up) = match button {
MouseClick::LEFT => (CGMouseButton::Left, CGEventType::LeftMouseUp),
MouseClick::RIGHT => (CGMouseButton::Right, CGEventType::RightMouseUp),
MouseClick::MIDDLE => (CGMouseButton::Center, CGEventType::OtherMouseUp),
};
let mouse_pos = Mouse::get_mouse_position()?;
let cg_event_source =
CGEventSource::new(CGEventSourceStateID::HIDSystemState).map_err(|_| {
AutoGuiError::OSFailure(
"Error creating CGEventSource on mouse movement".to_string(),
)
})?;
let click_up = CGEvent::new_mouse_event(
cg_event_source,
up,
CGPoint::new(mouse_pos.0 as f64, mouse_pos.1 as f64),
cg_button,
)
.map_err(|_| AutoGuiError::OSFailure("Failed the mouse click up CGevent".to_string()))?;
click_up.post(CGEventTapLocation::HID);
sleep(Duration::from_millis(20));
Ok(())
}
pub fn scroll(direction: MouseScroll, intensity: u32) -> Result<(), AutoGuiError> {
let delta = match direction {
MouseScroll::UP => (10 * intensity as i32, 0),
MouseScroll::DOWN => (-10 * intensity as i32, 0),
MouseScroll::LEFT => (0, 10 * intensity as i32),
MouseScroll::RIGHT => (0, -10 * intensity as i32),
};
let cg_event_source =
CGEventSource::new(CGEventSourceStateID::HIDSystemState).map_err(|_| {
AutoGuiError::OSFailure(
"Error creating CGEventSource on mouse movement".to_string(),
)
})?;
let scroll = CGEvent::new_scroll_event(
cg_event_source,
ScrollEventUnit::PIXEL,
2,
delta.0,
delta.1,
0,
)
.map_err(|_| AutoGuiError::OSFailure("Failed creating mouse scroll CGevent".to_string()))?;
scroll.post(CGEventTapLocation::HID);
sleep(Duration::from_millis(20));
Ok(())
}
pub fn double_click() -> Result<(), AutoGuiError> {
let source = CGEventSource::new(CGEventSourceStateID::HIDSystemState).map_err(|_| {
AutoGuiError::OSFailure(
"Failed creating CGEventSource on mouse double click".to_string(),
)
})?;
let pos = Mouse::get_mouse_position()?;
Self::mouse_click(MouseClick::LEFT)?;
sleep(Duration::from_millis(50));
let mouse_down = CGEvent::new_mouse_event(
source.clone(),
CGEventType::LeftMouseDown,
CGPoint::new(pos.0 as f64, pos.1 as f64),
CGMouseButton::Left,
)
.map_err(|_| {
AutoGuiError::OSFailure(
"Failed creating CGevent for mouse click down action".to_string(),
)
})?;
mouse_down.set_integer_value_field(1, 2);
let mouse_up = CGEvent::new_mouse_event(
source.clone(),
CGEventType::LeftMouseUp,
CGPoint::new(pos.0 as f64, pos.1 as f64),
CGMouseButton::Left,
)
.map_err(|_| {
AutoGuiError::OSFailure("Failed creating CGevent for mouse up click".to_string())
})?;
mouse_up.set_integer_value_field(1, 2);
mouse_down.post(CGEventTapLocation::HID);
sleep(Duration::from_millis(10));
mouse_up.post(CGEventTapLocation::HID);
sleep(Duration::from_millis(50));
Ok(())
}
}