From 0be66494066aafbb59c789e70df36ee7dde717ea Mon Sep 17 00:00:00 2001 From: kiwi Date: Mon, 1 Jul 2024 04:01:18 +0800 Subject: [PATCH 1/3] provide stop_listen function for user to stop listen in another thread --- src/lib.rs | 9 ++++++++- src/macos/common.rs | 3 +-- src/macos/listen.rs | 18 +++++++++++++++--- src/macos/mod.rs | 1 + 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 64efbe40..d916cedd 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; @@ -272,6 +275,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..d0632ca1 100644 --- a/src/macos/common.rs +++ b/src/macos/common.rs @@ -75,9 +75,8 @@ 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; - } // TODO Remove this, this was added as the coded diff --git a/src/macos/listen.rs b/src/macos/listen.rs index 611ea8cc..1bb18352 100644 --- a/src/macos/listen.rs +++ b/src/macos/listen.rs @@ -32,6 +32,14 @@ unsafe extern "C" fn raw_callback( cg_event } +pub fn stop_listen() { + unsafe { + if let Some(stop_loop) = STOP_LOOP.as_ref() { + stop_loop(); + } + } +} + pub fn listen(callback: T) -> Result<(), ListenError> where T: FnMut(Event) + 'static, @@ -50,16 +58,20 @@ where if tap.is_null() { return Err(ListenError::EventTapError); } - let _loop = CFMachPortCreateRunLoopSource(nil, tap, 0); + let _loop: CFRunLoopSourceRef = CFMachPortCreateRunLoopSource(nil, tap, 0); if _loop.is_null() { return Err(ListenError::LoopSourceError); } - let current_loop = CFRunLoopGetCurrent(); CFRunLoopAddSource(current_loop, _loop, kCFRunLoopCommonModes); - CGEventTapEnable(tap, true); + STOP_LOOP = Some(Box::new(move || { + CFRunLoopStop(current_loop); + })); CFRunLoopRun(); } Ok(()) } + +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; From 3260e8666360dfda0185db7c407cf01f736807ad Mon Sep 17 00:00:00 2001 From: kiwi Date: Mon, 1 Jul 2024 14:01:40 +0800 Subject: [PATCH 2/3] stop listen on windows --- src/lib.rs | 8 ++++++-- src/windows/listen.rs | 30 +++++++++++++++++++++++++++--- src/windows/mod.rs | 1 + 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d916cedd..db622688 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,7 +9,7 @@ //! # Listening to global events //! //! ```no_run -//! use rdev::{listen, Event}; +//! use rdev::{listen, Event, stop_listen}; //! //! // This will block. //! if let Err(error) = listen(callback) { @@ -217,6 +217,7 @@ //! //! Event data returned by the `listen` and `grab` functions can be serialized and de-serialized with //! Serde if you install this library with the `serialize` feature. +#![feature(thread_id_value)] mod rdev; pub use crate::rdev::{ Button, DisplayError, Event, EventType, GrabCallback, GrabError, Key, KeyboardState, @@ -245,7 +246,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 diff --git a/src/windows/listen.rs b/src/windows/listen.rs index bff00d2c..ed2781af 100644 --- a/src/windows/listen.rs +++ b/src/windows/listen.rs @@ -2,9 +2,11 @@ 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::thread; 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; @@ -41,6 +43,14 @@ unsafe extern "system" fn raw_callback(code: c_int, param: WPARAM, lpdata: LPARA CallNextHookEx(HOOK, code, param, lpdata) } +pub fn stop_listen() { + unsafe { + if let Some(stop_loop) = STOP_LOOP.as_ref() { + stop_loop(); + } + } +} + pub fn listen(callback: T) -> Result<(), ListenError> where T: FnMut(Event) + 'static, @@ -49,8 +59,22 @@ where GLOBAL_CALLBACK = Some(Box::new(callback)); 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(()) } + +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; From bd32be80a2f1d664aec5d63b921780540ccb6aeb Mon Sep 17 00:00:00 2001 From: kiwi Date: Mon, 1 Jul 2024 14:22:01 +0800 Subject: [PATCH 3/3] remove the unstable feature --- src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index db622688..a9130de9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -217,7 +217,6 @@ //! //! Event data returned by the `listen` and `grab` functions can be serialized and de-serialized with //! Serde if you install this library with the `serialize` feature. -#![feature(thread_id_value)] mod rdev; pub use crate::rdev::{ Button, DisplayError, Event, EventType, GrabCallback, GrabError, Key, KeyboardState,