From 0b01bb3f4352cda7dcb07ea09f455779f05dd75a Mon Sep 17 00:00:00 2001 From: kiwi <578088820@qq.com> Date: Tue, 2 Jul 2024 16:10:00 +0800 Subject: [PATCH 01/13] add stop_listen for macos and windows --- src/lib.rs | 14 ++++++++++++-- src/macos/common.rs | 2 +- src/macos/listen.rs | 14 ++++++++++++++ src/macos/mod.rs | 1 + src/windows/listen.rs | 27 +++++++++++++++++++++++++-- src/windows/mod.rs | 1 + tests/stop_listen.rs | 23 +++++++++++++++++++++++ 7 files changed, 77 insertions(+), 5 deletions(-) create mode 100644 tests/stop_listen.rs diff --git a/src/lib.rs b/src/lib.rs index 64efbe40..8aba9396 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -228,7 +228,10 @@ mod macos; #[cfg(target_os = "macos")] pub use crate::macos::Keyboard; #[cfg(target_os = "macos")] -use crate::macos::{display_size as _display_size, listen as _listen, simulate as _simulate}; +use crate::macos::{ + display_size as _display_size, listen as _listen, simulate as _simulate, + stop_listen as _stop_listen, +}; #[cfg(target_os = "linux")] mod linux; @@ -242,7 +245,10 @@ mod windows; #[cfg(target_os = "windows")] pub use crate::windows::Keyboard; #[cfg(target_os = "windows")] -use crate::windows::{display_size as _display_size, listen as _listen, simulate as _simulate}; +use crate::windows::{ + display_size as _display_size, listen as _listen, simulate as _simulate, + stop_listen as _stop_listen, +}; /// Listening to global events. Caveat: On MacOS, you require the listen /// loop needs to be the primary app (no fork before) and need to have accessibility @@ -272,6 +278,10 @@ where _listen(callback) } +pub fn stop_listen() { + _stop_listen(); +} + /// Sending some events /// /// ```no_run diff --git a/src/macos/common.rs b/src/macos/common.rs index 6c837ab0..d3d35cd1 100644 --- a/src/macos/common.rs +++ b/src/macos/common.rs @@ -75,7 +75,7 @@ extern "C" { pub fn CFRunLoopGetCurrent() -> CFRunLoopRef; pub fn CGEventTapEnable(tap: CFMachPortRef, enable: bool); pub fn CFRunLoopRun(); - + pub fn CFRunLoopStop(rl: CFRunLoopRef); pub static kCFRunLoopCommonModes: CFRunLoopMode; } diff --git a/src/macos/listen.rs b/src/macos/listen.rs index 611ea8cc..1415c434 100644 --- a/src/macos/listen.rs +++ b/src/macos/listen.rs @@ -59,7 +59,21 @@ where CFRunLoopAddSource(current_loop, _loop, kCFRunLoopCommonModes); CGEventTapEnable(tap, true); + STOP_LOOP = Some(Box::new(move || { + CFRunLoopStop(current_loop); + })); CFRunLoopRun(); } Ok(()) } + +pub fn stop_listen() { + unsafe { + if let Some(stop_loop) = STOP_LOOP.as_ref() { + stop_loop(); + } + } +} + +type DynFn = dyn Fn() + 'static; +pub static mut STOP_LOOP: Option> = None; diff --git a/src/macos/mod.rs b/src/macos/mod.rs index 43ce23fb..67a94c85 100644 --- a/src/macos/mod.rs +++ b/src/macos/mod.rs @@ -12,4 +12,5 @@ pub use crate::macos::display::display_size; pub use crate::macos::grab::grab; pub use crate::macos::keyboard::Keyboard; pub use crate::macos::listen::listen; +pub use crate::macos::listen::stop_listen; pub use crate::macos::simulate::simulate; diff --git a/src/windows/listen.rs b/src/windows/listen.rs index bff00d2c..6e913806 100644 --- a/src/windows/listen.rs +++ b/src/windows/listen.rs @@ -4,7 +4,7 @@ use std::os::raw::c_int; use std::ptr::null_mut; use std::time::SystemTime; use winapi::shared::minwindef::{LPARAM, LRESULT, WPARAM}; -use winapi::um::winuser::{CallNextHookEx, GetMessageA, HC_ACTION}; +use winapi::um::winuser::{CallNextHookEx, PeekMessageA, HC_ACTION}; static mut GLOBAL_CALLBACK: Option> = None; @@ -50,7 +50,30 @@ where set_key_hook(raw_callback)?; set_mouse_hook(raw_callback)?; - GetMessageA(null_mut(), null_mut(), 0, 0); + let (sender, receiver) = mpsc::channel(); + STOP_LOOP = Some(Box::new(move || { + sender.send(true).unwrap(); + })); + loop { + if let Ok(stop_listen) = receiver.try_recv() { + if stop_listen { + println!("stop loop successed"); + break; + } + } + PeekMessageA(null_mut(), null_mut(), 0, 0, 0); + } } Ok(()) } + +pub fn stop_listen() { + unsafe { + if let Some(stop_loop) = STOP_LOOP.as_ref() { + stop_loop(); + } + } +} + +type DynFn = dyn Fn() + 'static; +pub static mut STOP_LOOP: Option> = None; diff --git a/src/windows/mod.rs b/src/windows/mod.rs index 8ef404f3..bc72299f 100644 --- a/src/windows/mod.rs +++ b/src/windows/mod.rs @@ -14,4 +14,5 @@ pub use crate::windows::display::display_size; pub use crate::windows::grab::grab; pub use crate::windows::keyboard::Keyboard; pub use crate::windows::listen::listen; +pub use crate::windows::listen::stop_listen; pub use crate::windows::simulate::simulate; diff --git a/tests/stop_listen.rs b/tests/stop_listen.rs new file mode 100644 index 00000000..ef3d82a8 --- /dev/null +++ b/tests/stop_listen.rs @@ -0,0 +1,23 @@ +use rdev::{listen, stop_listen}; +use serial_test::serial; +use std::{ + thread::{self, spawn}, + time::Duration, +}; +#[test] +#[serial] +fn test_stop() { + eprintln!("hello"); + spawn(|| { + if let Err(error) = listen(|event| { + println!("My callback {:?}", event); + }) { + println!("Error: {:?}", error) + } + }); + thread::sleep(Duration::from_secs(5)); + spawn(|| { + stop_listen(); + }); + thread::sleep(Duration::from_secs(5)); +} From 0399f44d90bee3214dfead671624a3daba65111e Mon Sep 17 00:00:00 2001 From: kiwi <578088820@qq.com> Date: Tue, 2 Jul 2024 16:27:34 +0800 Subject: [PATCH 02/13] fill the missing using --- src/windows/listen.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/windows/listen.rs b/src/windows/listen.rs index 6e913806..a4b7d2b6 100644 --- a/src/windows/listen.rs +++ b/src/windows/listen.rs @@ -2,6 +2,7 @@ use crate::rdev::{Event, EventType, ListenError}; use crate::windows::common::{convert, set_key_hook, set_mouse_hook, HookError, HOOK, KEYBOARD}; use std::os::raw::c_int; use std::ptr::null_mut; +use std::sync::mpsc; use std::time::SystemTime; use winapi::shared::minwindef::{LPARAM, LRESULT, WPARAM}; use winapi::um::winuser::{CallNextHookEx, PeekMessageA, HC_ACTION}; From cc45070534be9585a7d367e57921d624fc6c6cf8 Mon Sep 17 00:00:00 2001 From: kiwi <578088820@qq.com> Date: Tue, 2 Jul 2024 17:23:51 +0800 Subject: [PATCH 03/13] remove a warning --- src/macos/listen.rs | 2 +- src/windows/listen.rs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/macos/listen.rs b/src/macos/listen.rs index 1415c434..2e6d9e8d 100644 --- a/src/macos/listen.rs +++ b/src/macos/listen.rs @@ -22,7 +22,7 @@ unsafe extern "C" fn raw_callback( let opt = KEYBOARD_STATE.lock(); if let Ok(mut keyboard) = opt { if let Some(event) = convert(_type, &cg_event, &mut keyboard) { - if let Some(callback) = &mut GLOBAL_CALLBACK { + if let Some(ref mut callback) = GLOBAL_CALLBACK { callback(event); } } diff --git a/src/windows/listen.rs b/src/windows/listen.rs index a4b7d2b6..58271a08 100644 --- a/src/windows/listen.rs +++ b/src/windows/listen.rs @@ -34,7 +34,7 @@ unsafe extern "system" fn raw_callback(code: c_int, param: WPARAM, lpdata: LPARA time: SystemTime::now(), name, }; - if let Some(callback) = &mut GLOBAL_CALLBACK { + if let Some(ref mut callback) = GLOBAL_CALLBACK { callback(event); } } @@ -58,7 +58,6 @@ where loop { if let Ok(stop_listen) = receiver.try_recv() { if stop_listen { - println!("stop loop successed"); break; } } From 74bba4f7194aa16c769c3f83a460c485cf270a2e Mon Sep 17 00:00:00 2001 From: kiwi <578088820@qq.com> Date: Tue, 2 Jul 2024 18:28:04 +0800 Subject: [PATCH 04/13] add point infomation while mouse button pressed or released --- examples/simulate.rs | 12 ++++++-- src/macos/common.rs | 34 ++++++++++++++------- src/macos/simulate.rs | 18 ++++++++--- src/rdev.rs | 12 ++++++-- src/windows/common.rs | 58 +++++++++++++++++++++++++++--------- src/windows/simulate.rs | 44 +++++++++++++++++++-------- tests/listen_and_simulate.rs | 13 ++++++-- 7 files changed, 144 insertions(+), 47 deletions(-) diff --git a/examples/simulate.rs b/examples/simulate.rs index 1f50f042..ea935df0 100644 --- a/examples/simulate.rs +++ b/examples/simulate.rs @@ -19,8 +19,16 @@ fn main() { send(&EventType::MouseMove { x: 0.0, y: 0.0 }); send(&EventType::MouseMove { x: 400.0, y: 400.0 }); - send(&EventType::ButtonPress(Button::Left)); - send(&EventType::ButtonRelease(Button::Right)); + send(&EventType::ButtonPress { + button: Button::Left, + x: None, + y: None, + }); + send(&EventType::ButtonRelease { + button: Button::Right, + x: None, + y: None, + }); send(&EventType::Wheel { delta_x: 0, delta_y: 1, diff --git a/src/macos/common.rs b/src/macos/common.rs index d3d35cd1..9a11020b 100644 --- a/src/macos/common.rs +++ b/src/macos/common.rs @@ -3,6 +3,7 @@ use crate::macos::keyboard::Keyboard; use crate::rdev::{Button, Event, EventType}; use cocoa::base::id; use core_graphics::event::{CGEvent, CGEventFlags, CGEventTapLocation, CGEventType, EventField}; +use core_graphics::geometry::CGPoint; use lazy_static::lazy_static; use std::convert::TryInto; use std::os::raw::c_void; @@ -96,18 +97,29 @@ pub unsafe fn convert( cg_event: &CGEvent, keyboard_state: &mut Keyboard, ) -> Option { + let CGPoint { x, y } = cg_event.location(); let option_type = match _type { - CGEventType::LeftMouseDown => Some(EventType::ButtonPress(Button::Left)), - CGEventType::LeftMouseUp => Some(EventType::ButtonRelease(Button::Left)), - CGEventType::RightMouseDown => Some(EventType::ButtonPress(Button::Right)), - CGEventType::RightMouseUp => Some(EventType::ButtonRelease(Button::Right)), - CGEventType::MouseMoved => { - let point = cg_event.location(); - Some(EventType::MouseMove { - x: point.x, - y: point.y, - }) - } + CGEventType::LeftMouseDown => Some(EventType::ButtonPress { + button: Button::Left, + x: Some(x), + y: Some(y), + }), + CGEventType::LeftMouseUp => Some(EventType::ButtonRelease { + button: Button::Left, + x: Some(x), + y: Some(y), + }), + CGEventType::RightMouseDown => Some(EventType::ButtonPress { + button: Button::Right, + x: Some(x), + y: Some(y), + }), + CGEventType::RightMouseUp => Some(EventType::ButtonRelease { + button: Button::Right, + x: Some(x), + y: Some(y), + }), + CGEventType::MouseMoved => Some(EventType::MouseMove { x, y }), CGEventType::KeyDown => { let code = cg_event.get_integer_value_field(EventField::KEYBOARD_EVENT_KEYCODE); Some(EventType::KeyPress(key_from_code(code.try_into().ok()?))) diff --git a/src/macos/simulate.rs b/src/macos/simulate.rs index 6242e195..b0b89e8e 100644 --- a/src/macos/simulate.rs +++ b/src/macos/simulate.rs @@ -21,8 +21,13 @@ unsafe fn convert_native_with_source( let code = code_from_key(*key)?; CGEvent::new_keyboard_event(source, code, false).ok() } - EventType::ButtonPress(button) => { - let point = get_current_mouse_location()?; + EventType::ButtonPress { button, x, y } => { + let mut point = get_current_mouse_location()?; + if let Some(x) = x { + if let Some(y) = y { + point = CGPoint { x: *x, y: *y }; + } + } let event = match button { Button::Left => CGEventType::LeftMouseDown, Button::Right => CGEventType::RightMouseDown, @@ -36,8 +41,13 @@ unsafe fn convert_native_with_source( ) .ok() } - EventType::ButtonRelease(button) => { - let point = get_current_mouse_location()?; + EventType::ButtonRelease { button, x, y } => { + let mut point = get_current_mouse_location()?; + if let Some(x) = x { + if let Some(y) = y { + point = CGPoint { x: *x, y: *y }; + } + } let event = match button { Button::Left => CGEventType::LeftMouseUp, Button::Right => CGEventType::RightMouseUp, diff --git a/src/rdev.rs b/src/rdev.rs index 277be3b0..bd810651 100644 --- a/src/rdev.rs +++ b/src/rdev.rs @@ -233,8 +233,16 @@ pub enum EventType { KeyPress(Key), KeyRelease(Key), /// Mouse Button - ButtonPress(Button), - ButtonRelease(Button), + ButtonPress { + button: Button, + x: Option, + y: Option, + }, + ButtonRelease { + button: Button, + x: Option, + y: Option, + }, /// Values in pixels. `EventType::MouseMove{x: 0, y: 0}` corresponds to the /// top left corner, with x increasing downward and y increasing rightward MouseMove { diff --git a/src/windows/common.rs b/src/windows/common.rs index a7579c52..6124dc4f 100644 --- a/src/windows/common.rs +++ b/src/windows/common.rs @@ -49,6 +49,7 @@ pub unsafe fn get_button_code(lpdata: LPARAM) -> WORD { } pub unsafe fn convert(param: WPARAM, lpdata: LPARAM) -> Option { + let (x, y) = get_point(lpdata); match param.try_into() { Ok(WM_KEYDOWN) | Ok(WM_SYSKEYDOWN) => { let code = get_code(lpdata); @@ -60,27 +61,56 @@ pub unsafe fn convert(param: WPARAM, lpdata: LPARAM) -> Option { let key = key_from_code(code as u16); Some(EventType::KeyRelease(key)) } - Ok(WM_LBUTTONDOWN) => Some(EventType::ButtonPress(Button::Left)), - Ok(WM_LBUTTONUP) => Some(EventType::ButtonRelease(Button::Left)), - Ok(WM_MBUTTONDOWN) => Some(EventType::ButtonPress(Button::Middle)), - Ok(WM_MBUTTONUP) => Some(EventType::ButtonRelease(Button::Middle)), - Ok(WM_RBUTTONDOWN) => Some(EventType::ButtonPress(Button::Right)), - Ok(WM_RBUTTONUP) => Some(EventType::ButtonRelease(Button::Right)), + Ok(WM_LBUTTONDOWN) => Some(EventType::ButtonPress { + button: Button::Left, + x: Some(x as f64), + y: Some(y as f64), + }), + Ok(WM_LBUTTONUP) => Some(EventType::ButtonRelease { + button: Button::Left, + x: Some(x as f64), + y: Some(y as f64), + }), + Ok(WM_MBUTTONDOWN) => Some(EventType::ButtonPress { + button: Button::Middle, + x: Some(x as f64), + y: Some(y as f64), + }), + Ok(WM_MBUTTONUP) => Some(EventType::ButtonRelease { + button: Button::Middle, + x: Some(x as f64), + y: Some(y as f64), + }), + Ok(WM_RBUTTONDOWN) => Some(EventType::ButtonPress { + button: Button::Right, + x: Some(x as f64), + y: Some(y as f64), + }), + Ok(WM_RBUTTONUP) => Some(EventType::ButtonRelease { + button: Button::Right, + x: Some(x as f64), + y: Some(y as f64), + }), Ok(WM_XBUTTONDOWN) => { let code = get_button_code(lpdata) as u8; - Some(EventType::ButtonPress(Button::Unknown(code))) + Some(EventType::ButtonPress { + button: Button::Unknown(code), + x: Some(x as f64), + y: Some(y as f64), + }) } Ok(WM_XBUTTONUP) => { let code = get_button_code(lpdata) as u8; - Some(EventType::ButtonRelease(Button::Unknown(code))) - } - Ok(WM_MOUSEMOVE) => { - let (x, y) = get_point(lpdata); - Some(EventType::MouseMove { - x: x as f64, - y: y as f64, + Some(EventType::ButtonRelease { + button: Button::Unknown(code), + x: Some(x as f64), + y: Some(y as f64), }) } + Ok(WM_MOUSEMOVE) => Some(EventType::MouseMove { + x: x as f64, + y: y as f64, + }), Ok(WM_MOUSEWHEEL) => { let delta = get_delta(lpdata) as c_short; Some(EventType::Wheel { diff --git a/src/windows/simulate.rs b/src/windows/simulate.rs index cd62e220..320ad1b0 100644 --- a/src/windows/simulate.rs +++ b/src/windows/simulate.rs @@ -83,18 +83,38 @@ pub fn simulate(event_type: &EventType) -> Result<(), SimulateError> { let code = code_from_key(*key).ok_or(SimulateError)?; sim_keyboard_event(KEYEVENTF_KEYUP, code, 0) } - EventType::ButtonPress(button) => match button { - Button::Left => sim_mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0), - Button::Middle => sim_mouse_event(MOUSEEVENTF_MIDDLEDOWN, 0, 0, 0), - Button::Right => sim_mouse_event(MOUSEEVENTF_RIGHTDOWN, 0, 0, 0), - Button::Unknown(code) => sim_mouse_event(MOUSEEVENTF_XDOWN, (*code).into(), 0, 0), - }, - EventType::ButtonRelease(button) => match button { - Button::Left => sim_mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0), - Button::Middle => sim_mouse_event(MOUSEEVENTF_MIDDLEUP, 0, 0, 0), - Button::Right => sim_mouse_event(MOUSEEVENTF_RIGHTUP, 0, 0, 0), - Button::Unknown(code) => sim_mouse_event(MOUSEEVENTF_XUP, (*code).into(), 0, 0), - }, + EventType::ButtonPress { button, x, y } => { + let mut dx = 0; + let mut dy = 0; + if let Some(x) = x { + if let Some(y) = y { + dx = *x as i32; + dy = *y as i32; + } + } + match button { + Button::Left => sim_mouse_event(MOUSEEVENTF_LEFTDOWN, 0, dx, dy), + Button::Middle => sim_mouse_event(MOUSEEVENTF_MIDDLEDOWN, 0, dx, dy), + Button::Right => sim_mouse_event(MOUSEEVENTF_RIGHTDOWN, 0, dx, dy), + Button::Unknown(code) => sim_mouse_event(MOUSEEVENTF_XDOWN, (*code).into(), dx, dy), + } + } + EventType::ButtonRelease { button, x, y } => { + let mut dx = 0; + let mut dy = 0; + if let Some(x) = x { + if let Some(y) = y { + dx = *x as i32; + dy = *y as i32; + } + } + match button { + Button::Left => sim_mouse_event(MOUSEEVENTF_LEFTUP, 0, dx, dy), + Button::Middle => sim_mouse_event(MOUSEEVENTF_MIDDLEUP, 0, dx, dy), + Button::Right => sim_mouse_event(MOUSEEVENTF_RIGHTUP, 0, dx, dy), + Button::Unknown(code) => sim_mouse_event(MOUSEEVENTF_XUP, (*code).into(), dx, dy), + } + } EventType::Wheel { delta_x, delta_y } => { if *delta_x != 0 { sim_mouse_event( diff --git a/tests/listen_and_simulate.rs b/tests/listen_and_simulate.rs index 03b9809d..541298d3 100644 --- a/tests/listen_and_simulate.rs +++ b/tests/listen_and_simulate.rs @@ -53,8 +53,16 @@ fn test_listen_and_simulate() -> Result<(), Box> { //EventType::KeyPress(Key::ShiftLeft), EventType::KeyPress(Key::KeyS), EventType::KeyRelease(Key::KeyS), - EventType::ButtonPress(Button::Right), - EventType::ButtonRelease(Button::Right), + EventType::ButtonPress { + button: Button::Right, + x: None, + y: None, + }, + EventType::ButtonRelease { + button: Button::Right, + x: None, + y: None, + }, EventType::Wheel { delta_x: 0, delta_y: 1, @@ -70,5 +78,6 @@ fn test_listen_and_simulate() -> Result<(), Box> { y: pixel as f64, }); let mut events = events.chain(click_events); + //how to fix it? todo sim_then_listen(&mut events) } From 3989faefcf59bf710ee3c7da052a0e54f941ee48 Mon Sep 17 00:00:00 2001 From: kiwi <578088820@qq.com> Date: Thu, 4 Jul 2024 11:59:23 +0800 Subject: [PATCH 05/13] add drag event on macos, you can record the mouse coordinate while mouse drag, but there has no way to simulate it. so you need to save drag events as press, mousemove and release,it's fine to save like this, cause there also has no drag event on windows. --- src/macos/common.rs | 26 ++++++++++++++++++-------- src/macos/simulate.rs | 30 +++++++++++++++++------------- src/rdev.rs | 13 +++++++++---- 3 files changed, 44 insertions(+), 25 deletions(-) diff --git a/src/macos/common.rs b/src/macos/common.rs index 9a11020b..d1a4ce10 100644 --- a/src/macos/common.rs +++ b/src/macos/common.rs @@ -101,23 +101,23 @@ pub unsafe fn convert( let option_type = match _type { CGEventType::LeftMouseDown => Some(EventType::ButtonPress { button: Button::Left, - x: Some(x), - y: Some(y), + x, + y, }), CGEventType::LeftMouseUp => Some(EventType::ButtonRelease { button: Button::Left, - x: Some(x), - y: Some(y), + x, + y, }), CGEventType::RightMouseDown => Some(EventType::ButtonPress { button: Button::Right, - x: Some(x), - y: Some(y), + x, + y, }), CGEventType::RightMouseUp => Some(EventType::ButtonRelease { button: Button::Right, - x: Some(x), - y: Some(y), + x, + y, }), CGEventType::MouseMoved => Some(EventType::MouseMove { x, y }), CGEventType::KeyDown => { @@ -147,6 +147,16 @@ pub unsafe fn convert( cg_event.get_integer_value_field(EventField::SCROLL_WHEEL_EVENT_POINT_DELTA_AXIS_2); Some(EventType::Wheel { delta_x, delta_y }) } + CGEventType::LeftMouseDragged => Some(EventType::Drag { + button: Button::Left, + x, + y, + }), + CGEventType::RightMouseDragged => Some(EventType::Drag { + button: Button::Right, + x, + y, + }), _ => None, }; if let Some(event_type) = option_type { diff --git a/src/macos/simulate.rs b/src/macos/simulate.rs index b0b89e8e..bb78255a 100644 --- a/src/macos/simulate.rs +++ b/src/macos/simulate.rs @@ -22,12 +22,7 @@ unsafe fn convert_native_with_source( CGEvent::new_keyboard_event(source, code, false).ok() } EventType::ButtonPress { button, x, y } => { - let mut point = get_current_mouse_location()?; - if let Some(x) = x { - if let Some(y) = y { - point = CGPoint { x: *x, y: *y }; - } - } + let point = CGPoint { x: *x, y: *y }; let event = match button { Button::Left => CGEventType::LeftMouseDown, Button::Right => CGEventType::RightMouseDown, @@ -42,12 +37,7 @@ unsafe fn convert_native_with_source( .ok() } EventType::ButtonRelease { button, x, y } => { - let mut point = get_current_mouse_location()?; - if let Some(x) = x { - if let Some(y) = y { - point = CGPoint { x: *x, y: *y }; - } - } + let point = CGPoint { x: *x, y: *y }; let event = match button { Button::Left => CGEventType::LeftMouseUp, Button::Right => CGEventType::RightMouseUp, @@ -62,7 +52,7 @@ unsafe fn convert_native_with_source( .ok() } EventType::MouseMove { x, y } => { - let point = CGPoint { x: (*x), y: (*y) }; + let point = CGPoint { x: *x, y: *y }; CGEvent::new_mouse_event(source, CGEventType::MouseMoved, point, CGMouseButton::Left) .ok() } @@ -78,6 +68,17 @@ unsafe fn convert_native_with_source( ) .ok() } + EventType::Drag { + button: _, + x: _, + y: _, + } => { + //https://developer.apple.com/documentation/coregraphics/quartz_event_services?language=objc + //no drag event in quartz_event_services of coregraphics + //you need to use button press, mouse move and release event to simulate drag event + //there also has no drag event on windows, so it's fine to replace drag events into press, mouse move and release events. + None + } } } @@ -86,6 +87,9 @@ unsafe fn convert_native(event_type: &EventType) -> Option { convert_native_with_source(event_type, source) } +///cause all of button events contain coordinate which can be used when they be simulated. +///so you don't need this fn when button press/release anymore. +#[allow(dead_code)] unsafe fn get_current_mouse_location() -> Option { let source = CGEventSource::new(CGEventSourceStateID::HIDSystemState).ok()?; let event = CGEvent::new(source).ok()?; diff --git a/src/rdev.rs b/src/rdev.rs index bd810651..1e4dee1b 100644 --- a/src/rdev.rs +++ b/src/rdev.rs @@ -235,13 +235,18 @@ pub enum EventType { /// Mouse Button ButtonPress { button: Button, - x: Option, - y: Option, + x: f64, + y: f64, }, ButtonRelease { button: Button, - x: Option, - y: Option, + x: f64, + y: f64, + }, + Drag { + button: Button, + x: f64, + y: f64, }, /// Values in pixels. `EventType::MouseMove{x: 0, y: 0}` corresponds to the /// top left corner, with x increasing downward and y increasing rightward From 4def44970e579734d7cbca71b61c55e0be52e039 Mon Sep 17 00:00:00 2001 From: kiwi <578088820@qq.com> Date: Thu, 4 Jul 2024 15:11:34 +0800 Subject: [PATCH 06/13] adapt drag event on windows --- src/windows/common.rs | 34 ++++++++++++++++++---------------- src/windows/simulate.rs | 25 +++++++++---------------- 2 files changed, 27 insertions(+), 32 deletions(-) diff --git a/src/windows/common.rs b/src/windows/common.rs index 6124dc4f..6ab7a0a2 100644 --- a/src/windows/common.rs +++ b/src/windows/common.rs @@ -50,6 +50,8 @@ pub unsafe fn get_button_code(lpdata: LPARAM) -> WORD { pub unsafe fn convert(param: WPARAM, lpdata: LPARAM) -> Option { let (x, y) = get_point(lpdata); + let x = x as f64; + let y = y as f64; match param.try_into() { Ok(WM_KEYDOWN) | Ok(WM_SYSKEYDOWN) => { let code = get_code(lpdata); @@ -63,48 +65,48 @@ pub unsafe fn convert(param: WPARAM, lpdata: LPARAM) -> Option { } Ok(WM_LBUTTONDOWN) => Some(EventType::ButtonPress { button: Button::Left, - x: Some(x as f64), - y: Some(y as f64), + x, + y, }), Ok(WM_LBUTTONUP) => Some(EventType::ButtonRelease { button: Button::Left, - x: Some(x as f64), - y: Some(y as f64), + x, + y, }), Ok(WM_MBUTTONDOWN) => Some(EventType::ButtonPress { button: Button::Middle, - x: Some(x as f64), - y: Some(y as f64), + x, + y, }), Ok(WM_MBUTTONUP) => Some(EventType::ButtonRelease { button: Button::Middle, - x: Some(x as f64), - y: Some(y as f64), + x, + y, }), Ok(WM_RBUTTONDOWN) => Some(EventType::ButtonPress { button: Button::Right, - x: Some(x as f64), - y: Some(y as f64), + x, + y, }), Ok(WM_RBUTTONUP) => Some(EventType::ButtonRelease { button: Button::Right, - x: Some(x as f64), - y: Some(y as f64), + x, + y, }), Ok(WM_XBUTTONDOWN) => { let code = get_button_code(lpdata) as u8; Some(EventType::ButtonPress { button: Button::Unknown(code), - x: Some(x as f64), - y: Some(y as f64), + x, + y, }) } Ok(WM_XBUTTONUP) => { let code = get_button_code(lpdata) as u8; Some(EventType::ButtonRelease { button: Button::Unknown(code), - x: Some(x as f64), - y: Some(y as f64), + x, + y, }) } Ok(WM_MOUSEMOVE) => Some(EventType::MouseMove { diff --git a/src/windows/simulate.rs b/src/windows/simulate.rs index 320ad1b0..8ea8a13a 100644 --- a/src/windows/simulate.rs +++ b/src/windows/simulate.rs @@ -84,14 +84,8 @@ pub fn simulate(event_type: &EventType) -> Result<(), SimulateError> { sim_keyboard_event(KEYEVENTF_KEYUP, code, 0) } EventType::ButtonPress { button, x, y } => { - let mut dx = 0; - let mut dy = 0; - if let Some(x) = x { - if let Some(y) = y { - dx = *x as i32; - dy = *y as i32; - } - } + let dx = *x as i32; + let dy = *y as i32; match button { Button::Left => sim_mouse_event(MOUSEEVENTF_LEFTDOWN, 0, dx, dy), Button::Middle => sim_mouse_event(MOUSEEVENTF_MIDDLEDOWN, 0, dx, dy), @@ -100,14 +94,8 @@ pub fn simulate(event_type: &EventType) -> Result<(), SimulateError> { } } EventType::ButtonRelease { button, x, y } => { - let mut dx = 0; - let mut dy = 0; - if let Some(x) = x { - if let Some(y) = y { - dx = *x as i32; - dy = *y as i32; - } - } + let dx = *x as i32; + let dy = *y as i32; match button { Button::Left => sim_mouse_event(MOUSEEVENTF_LEFTUP, 0, dx, dy), Button::Middle => sim_mouse_event(MOUSEEVENTF_MIDDLEUP, 0, dx, dy), @@ -149,5 +137,10 @@ pub fn simulate(event_type: &EventType) -> Result<(), SimulateError> { (*y as i32 + 1) * 65535 / height, ) } + EventType::Drag { button: _, x, y } => { + //if someone copy events from macos to windows, in order to ensure the operation, run drag event as mousemove + let event_type = EventType::MouseMove { x: *x, y: *y }; + simulate(&event_type) + } } } From 1c6e7d807848251cf90ede47c665385ff3603c90 Mon Sep 17 00:00:00 2001 From: kiwi <578088820@qq.com> Date: Thu, 4 Jul 2024 15:20:03 +0800 Subject: [PATCH 07/13] complete simulate drag event on macos --- src/macos/simulate.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/macos/simulate.rs b/src/macos/simulate.rs index bb78255a..a244b416 100644 --- a/src/macos/simulate.rs +++ b/src/macos/simulate.rs @@ -68,16 +68,12 @@ unsafe fn convert_native_with_source( ) .ok() } - EventType::Drag { - button: _, - x: _, - y: _, - } => { + EventType::Drag { button: _, x, y } => { //https://developer.apple.com/documentation/coregraphics/quartz_event_services?language=objc //no drag event in quartz_event_services of coregraphics - //you need to use button press, mouse move and release event to simulate drag event - //there also has no drag event on windows, so it's fine to replace drag events into press, mouse move and release events. - None + //simulate drag event into mousemove event + let event_type = EventType::MouseMove { x: *x, y: *y }; + convert_native_with_source(&event_type, source) } } } From decd7880d8e1a1a12b4a4957410ab1f34448d703 Mon Sep 17 00:00:00 2001 From: kiwi <578088820@qq.com> Date: Thu, 4 Jul 2024 20:27:21 +0800 Subject: [PATCH 08/13] when stop_listen executed, STOP_LOOP should be reset to None --- src/macos/listen.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/macos/listen.rs b/src/macos/listen.rs index 2e6d9e8d..456b10ba 100644 --- a/src/macos/listen.rs +++ b/src/macos/listen.rs @@ -70,6 +70,7 @@ where pub fn stop_listen() { unsafe { if let Some(stop_loop) = STOP_LOOP.as_ref() { + STOP_LOOP = None; stop_loop(); } } From 54048cfa5ce196765673bb265a2be2bde5bb4b74 Mon Sep 17 00:00:00 2001 From: kiwi <578088820@qq.com> Date: Thu, 4 Jul 2024 20:42:42 +0800 Subject: [PATCH 09/13] correct the execute order --- src/macos/listen.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/macos/listen.rs b/src/macos/listen.rs index 456b10ba..d442b93e 100644 --- a/src/macos/listen.rs +++ b/src/macos/listen.rs @@ -70,8 +70,8 @@ where pub fn stop_listen() { unsafe { if let Some(stop_loop) = STOP_LOOP.as_ref() { - STOP_LOOP = None; stop_loop(); + STOP_LOOP = None; } } } From e1783002ac58f41a72f3eec6450efbf6d9330bc6 Mon Sep 17 00:00:00 2001 From: kiwi <578088820@qq.com> Date: Thu, 4 Jul 2024 21:27:14 +0800 Subject: [PATCH 10/13] correct the stop_listen execute order on windows --- src/windows/listen.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/windows/listen.rs b/src/windows/listen.rs index 58271a08..0ff59bb1 100644 --- a/src/windows/listen.rs +++ b/src/windows/listen.rs @@ -71,6 +71,7 @@ pub fn stop_listen() { unsafe { if let Some(stop_loop) = STOP_LOOP.as_ref() { stop_loop(); + STOP_LOOP = None; } } } From e1a3483b4b3246056c49933dca2797723c397f4b Mon Sep 17 00:00:00 2001 From: kiwi <578088820@qq.com> Date: Thu, 4 Jul 2024 22:35:28 +0800 Subject: [PATCH 11/13] implement drag simulate --- src/macos/simulate.rs | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/src/macos/simulate.rs b/src/macos/simulate.rs index a244b416..98ff59f7 100644 --- a/src/macos/simulate.rs +++ b/src/macos/simulate.rs @@ -68,12 +68,26 @@ unsafe fn convert_native_with_source( ) .ok() } - EventType::Drag { button: _, x, y } => { - //https://developer.apple.com/documentation/coregraphics/quartz_event_services?language=objc - //no drag event in quartz_event_services of coregraphics - //simulate drag event into mousemove event - let event_type = EventType::MouseMove { x: *x, y: *y }; - convert_native_with_source(&event_type, source) + EventType::Drag { button, x, y } => { + let point = CGPoint { x: *x, y: *y }; + match button { + Button::Left => { + let mouse_type = CGEventType::LeftMouseDragged; + CGEvent::new_mouse_event(source, mouse_type, point, CGMouseButton::Left).ok() + } + Button::Right => { + let mouse_type = CGEventType::RightMouseDragged; + CGEvent::new_mouse_event(source, mouse_type, point, CGMouseButton::Right).ok() + } + Button::Middle => { + let mouse_type = CGEventType::OtherMouseDragged; + CGEvent::new_mouse_event(source, mouse_type, point, CGMouseButton::Center).ok() + } + Button::Unknown(_) => { + let mouse_type = CGEventType::OtherMouseDragged; + CGEvent::new_mouse_event(source, mouse_type, point, CGMouseButton::Center).ok() + } + } } } } From 3f32434ba431822ccc290594d8fddaa307a84beb Mon Sep 17 00:00:00 2001 From: kiwi <578088820@qq.com> Date: Fri, 5 Jul 2024 21:43:12 +0800 Subject: [PATCH 12/13] add current_position fn for simulating --- src/lib.rs | 23 +++++++++++++++++++++-- src/windows/mod.rs | 1 + src/windows/simulate.rs | 23 +++++++++++++++++++++++ 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 8aba9396..cea5b1d3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -246,8 +246,8 @@ mod windows; pub use crate::windows::Keyboard; #[cfg(target_os = "windows")] use crate::windows::{ - display_size as _display_size, listen as _listen, simulate as _simulate, - stop_listen as _stop_listen, + display_size as _display_size, get_current_mouse_location as _get_current_mouse_location, + listen as _listen, simulate as _simulate, stop_listen as _stop_listen, }; /// Listening to global events. Caveat: On MacOS, you require the listen @@ -282,6 +282,25 @@ pub fn stop_listen() { _stop_listen(); } +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct Point { + pub x: f64, + pub y: f64, +} + +pub fn get_current_mouse_location() -> Option { + unsafe { + if let Some(point) = _get_current_mouse_location() { + Some(Point { + x: point.x as f64, + y: point.y as f64, + }) + } else { + None + } + } +} + /// Sending some events /// /// ```no_run diff --git a/src/windows/mod.rs b/src/windows/mod.rs index bc72299f..8f680a0e 100644 --- a/src/windows/mod.rs +++ b/src/windows/mod.rs @@ -15,4 +15,5 @@ pub use crate::windows::grab::grab; pub use crate::windows::keyboard::Keyboard; pub use crate::windows::listen::listen; pub use crate::windows::listen::stop_listen; +pub use crate::windows::simulate::get_current_mouse_location; pub use crate::windows::simulate::simulate; diff --git a/src/windows/simulate.rs b/src/windows/simulate.rs index 8ea8a13a..01fec296 100644 --- a/src/windows/simulate.rs +++ b/src/windows/simulate.rs @@ -5,6 +5,8 @@ use std::mem::size_of; use winapi::ctypes::{c_int, c_short}; use winapi::shared::minwindef::{DWORD, UINT, WORD}; use winapi::shared::ntdef::LONG; +use winapi::shared::windef::POINT; +use winapi::um::winuser::GetCursorPos; use winapi::um::winuser::{ GetSystemMetrics, INPUT_u, SendInput, INPUT, INPUT_KEYBOARD, INPUT_MOUSE, KEYBDINPUT, KEYEVENTF_KEYUP, MOUSEEVENTF_ABSOLUTE, MOUSEEVENTF_HWHEEL, MOUSEEVENTF_LEFTDOWN, @@ -13,6 +15,7 @@ use winapi::um::winuser::{ MOUSEEVENTF_XDOWN, MOUSEEVENTF_XUP, MOUSEINPUT, SM_CXVIRTUALSCREEN, SM_CYVIRTUALSCREEN, WHEEL_DELTA, }; + /// Not defined in win32 but define here for clarity static KEYEVENTF_KEYDOWN: DWORD = 0; @@ -144,3 +147,23 @@ pub fn simulate(event_type: &EventType) -> Result<(), SimulateError> { } } } + +type CGFloat = f64; +pub struct CGPoint { + pub x: CGFloat, + pub y: CGFloat, +} + +pub unsafe fn get_current_mouse_location() -> Option { + let mut point = POINT { x: 0, y: 0 }; + unsafe { + if GetCursorPos(&mut point as *mut POINT) == 0 { + return None; + } else { + return Some(CGPoint { + x: point.x as f64, + y: point.y as f64, + }); + } + } +} From de6b3ca50a62907318f0863f36b59de400c6e954 Mon Sep 17 00:00:00 2001 From: kiwi <578088820@qq.com> Date: Fri, 5 Jul 2024 21:52:50 +0800 Subject: [PATCH 13/13] add current_position fn for simulating on mac --- src/lib.rs | 4 ++-- src/macos/mod.rs | 1 + src/macos/simulate.rs | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index cea5b1d3..0cfcca10 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -229,8 +229,8 @@ mod macos; pub use crate::macos::Keyboard; #[cfg(target_os = "macos")] use crate::macos::{ - display_size as _display_size, listen as _listen, simulate as _simulate, - stop_listen as _stop_listen, + display_size as _display_size, get_current_mouse_location as _get_current_mouse_location, + listen as _listen, simulate as _simulate, stop_listen as _stop_listen, }; #[cfg(target_os = "linux")] diff --git a/src/macos/mod.rs b/src/macos/mod.rs index 67a94c85..82bc0ac1 100644 --- a/src/macos/mod.rs +++ b/src/macos/mod.rs @@ -13,4 +13,5 @@ pub use crate::macos::grab::grab; pub use crate::macos::keyboard::Keyboard; pub use crate::macos::listen::listen; pub use crate::macos::listen::stop_listen; +pub use crate::macos::simulate::get_current_mouse_location; pub use crate::macos::simulate::simulate; diff --git a/src/macos/simulate.rs b/src/macos/simulate.rs index 98ff59f7..7b5a4407 100644 --- a/src/macos/simulate.rs +++ b/src/macos/simulate.rs @@ -100,7 +100,7 @@ unsafe fn convert_native(event_type: &EventType) -> Option { ///cause all of button events contain coordinate which can be used when they be simulated. ///so you don't need this fn when button press/release anymore. #[allow(dead_code)] -unsafe fn get_current_mouse_location() -> Option { +pub unsafe fn get_current_mouse_location() -> Option { let source = CGEventSource::new(CGEventSourceStateID::HIDSystemState).ok()?; let event = CGEvent::new(source).ok()?; Some(event.location())