use crate::windows; use crate::{Button, Event, EventType, HookCallback, Robot}; use once_cell::sync::Lazy; use std::mem::{size_of, transmute}; use std::ptr::null_mut; use std::sync::atomic::{AtomicPtr, Ordering}; use std::time::SystemTime; use winapi::ctypes::c_int; use winapi::shared::minwindef::{DWORD, HIWORD, LPARAM, LRESULT, WPARAM}; use winapi::shared::windef::{HHOOK__, POINT}; use winapi::um::winuser::{ CallNextHookEx, GetAsyncKeyState, GetCursorPos, GetSystemMetrics, SendInput, HC_ACTION, INPUT, INPUT_MOUSE, LPINPUT, MOUSEEVENTF_ABSOLUTE, MOUSEEVENTF_HWHEEL, MOUSEEVENTF_LEFTDOWN, MOUSEEVENTF_LEFTUP, MOUSEEVENTF_MIDDLEDOWN, MOUSEEVENTF_MIDDLEUP, MOUSEEVENTF_MOVE, MOUSEEVENTF_RIGHTDOWN, MOUSEEVENTF_RIGHTUP, MOUSEEVENTF_VIRTUALDESK, MOUSEEVENTF_WHEEL, MOUSEINPUT, MSLLHOOKSTRUCT, SM_CXVIRTUALSCREEN, SM_CYVIRTUALSCREEN, SM_XVIRTUALSCREEN, SM_YVIRTUALSCREEN, VK_LBUTTON, VK_MBUTTON, VK_RBUTTON, VK_XBUTTON1, VK_XBUTTON2, WH_MOUSE_LL, WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MBUTTONDOWN, WM_MBUTTONUP, WM_MOUSEMOVE, WM_RBUTTONDOWN, WM_RBUTTONUP, WM_XBUTTONDOWN, WM_XBUTTONUP, XBUTTON1, XBUTTON2, }; fn default_callback(event: Event) { println!("Default : Event {:?}", event); } static MOUSE_HHOOK: Lazy> = Lazy::new(AtomicPtr::default); static mut HOOK_CALLBACK: HookCallback = default_callback; impl Robot { pub unsafe fn mouse_listen(callback: HookCallback) -> Result<(), windows::HookError> { if MOUSE_HHOOK.load(Ordering::Relaxed).is_null() { let r = windows::hook(WH_MOUSE_LL, &*MOUSE_HHOOK, Self::mouse_raw_callback); if r.is_err() { return Err(windows::HookError::Mouse(r.err().unwrap())); } } HOOK_CALLBACK = callback; Ok(()) } pub unsafe fn mouse_unlisten() -> Result<(), windows::HookError> { if !MOUSE_HHOOK.load(Ordering::Relaxed).is_null() { let r = windows::unhook(&*MOUSE_HHOOK); if r.is_err() { return Err(windows::HookError::Mouse(r.err().unwrap())); } } HOOK_CALLBACK = default_callback; Ok(()) } pub fn mouse_location() -> (i32, i32) { let mut point = POINT { x: 0, y: 0 }; let result = unsafe { GetCursorPos(&mut point) }; if result != 0 { (point.x, point.y) } else { (0, 0) } } pub fn mouse_move_to(dx: i32, dy: i32) { let width = unsafe { GetSystemMetrics(SM_CXVIRTUALSCREEN) }; let height = unsafe { GetSystemMetrics(SM_CYVIRTUALSCREEN) }; if width == 0 || height == 0 { return; } let pressed_left = Self::mouse_pressed(Button::Left); let pressed_right = Self::mouse_pressed(Button::Right); let pressed_middle = Self::mouse_pressed(Button::Middle); let mut dw_flags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK; if pressed_left { dw_flags = dw_flags | MOUSEEVENTF_LEFTDOWN; } if pressed_right { dw_flags = dw_flags | MOUSEEVENTF_RIGHTDOWN; } if pressed_middle { dw_flags = dw_flags | MOUSEEVENTF_MIDDLEDOWN; } Self::mouse_event( (dx - unsafe { GetSystemMetrics(SM_XVIRTUALSCREEN) }) * 65535 / unsafe { GetSystemMetrics(SM_CXVIRTUALSCREEN) }, (dy - unsafe { GetSystemMetrics(SM_YVIRTUALSCREEN) }) * 65535 / unsafe { GetSystemMetrics(SM_CYVIRTUALSCREEN) }, 0, dw_flags, ); } pub fn mouse_press(button: Button) { let btn = Self::mouse_button_event(EventType::MousePress(button)); let dw_flags = btn; Self::mouse_event(0, 0, 0, dw_flags); } pub fn mouse_release(button: Button) { let btn = Self::mouse_button_event(EventType::MouseRelease(button)); let dw_flags = btn; Self::mouse_event(0, 0, 0, dw_flags); } pub fn mouse_pressed(button: Button) -> bool { (unsafe { GetAsyncKeyState(Self::mouse_button_virtual_key(button)) } >> 15) != 0 } pub fn mouse_wheel_x(length: i32) { let dw_flags = MOUSEEVENTF_HWHEEL; Self::mouse_event(0, 0, unsafe { transmute(length * 120) }, dw_flags); } pub fn mouse_wheel_y(length: i32) { let dw_flags = MOUSEEVENTF_WHEEL; Self::mouse_event(0, 0, unsafe { transmute(length * 120) }, dw_flags); } fn mouse_event(dx: i32, dy: i32, mouse_data: u32, dw_flags: u32) { let mut input = INPUT { type_: INPUT_MOUSE, u: unsafe { transmute(MOUSEINPUT { dx, dy, mouseData: mouse_data, dwFlags: dw_flags, time: 0, dwExtraInfo: 0, }) }, }; unsafe { SendInput(1, &mut input as LPINPUT, size_of::() as c_int) }; } fn mouse_button_event(ev: EventType) -> DWORD { match ev { EventType::MousePress(button) => match button { Button::Left => MOUSEEVENTF_LEFTDOWN, Button::Middle => MOUSEEVENTF_MIDDLEDOWN, Button::Right => MOUSEEVENTF_RIGHTDOWN, _ => unimplemented!(), }, EventType::MouseRelease(button) => match button { Button::Left => MOUSEEVENTF_LEFTUP, Button::Middle => MOUSEEVENTF_MIDDLEUP, Button::Right => MOUSEEVENTF_RIGHTUP, _ => unimplemented!(), }, _ => unimplemented!(), } } fn mouse_button_virtual_key(button: Button) -> c_int { match button { Button::Left => VK_LBUTTON, Button::Middle => VK_MBUTTON, Button::Right => VK_RBUTTON, Button::X1 => VK_XBUTTON1, Button::X2 => VK_XBUTTON2, _ => unimplemented!(), } } unsafe fn get_mouse_point(lpdata: isize) -> (i32, i32) { let mouse = *(lpdata as *const MSLLHOOKSTRUCT); (mouse.pt.x, mouse.pt.y) } #[allow(non_snake_case)] unsafe extern "system" fn mouse_raw_callback( code: c_int, w_param: WPARAM, l_param: LPARAM, ) -> LRESULT { if code == HC_ACTION { let opt = match w_param as u32 { WM_LBUTTONDOWN => Some(EventType::MousePress(Button::Left)), WM_LBUTTONUP => Some(EventType::MouseRelease(Button::Left)), WM_MBUTTONDOWN => Some(EventType::MousePress(Button::Middle)), WM_MBUTTONUP => Some(EventType::MouseRelease(Button::Middle)), WM_RBUTTONDOWN => Some(EventType::MousePress(Button::Right)), WM_RBUTTONUP => Some(EventType::MouseRelease(Button::Right)), WM_XBUTTONDOWN => { let llhs = &*(l_param as *const MSLLHOOKSTRUCT); match HIWORD(llhs.mouseData) { XBUTTON1 => Some(EventType::MousePress(Button::X1)), XBUTTON2 => Some(EventType::MousePress(Button::X2)), _ => None, } } WM_XBUTTONUP => { let llhs = &*(l_param as *const MSLLHOOKSTRUCT); match HIWORD(llhs.mouseData) { XBUTTON1 => Some(EventType::MouseRelease(Button::X1)), XBUTTON2 => Some(EventType::MouseRelease(Button::X2)), _ => None, } } WM_MOUSEMOVE => { let (x, y) = Self::get_mouse_point(l_param); Some(EventType::MouseMoveTo { x: x as f64, y: y as f64, }) } _ => None, }; println!("mouse {:?}", opt); if let Some(event_type) = opt { let event = Event { event_type, time: SystemTime::now(), }; HOOK_CALLBACK(event); } } CallNextHookEx(null_mut(), code, w_param, l_param) } }