From 12c2ff6ab38775da968c4324540873b144b1cf80 Mon Sep 17 00:00:00 2001 From: Serhii Tokariev Date: Sat, 29 Jan 2022 21:42:35 +0200 Subject: [PATCH 1/2] Add version of listen which doesn't run new CFRunLoop --- src/lib.rs | 48 ++++++++++++++++++++++++++++++++++++++++++++- src/macos/listen.rs | 33 +++++++++++++++++++++++++++++++ src/macos/mod.rs | 1 + 3 files changed, 81 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 4aee56d0..d61d6825 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, listen_non_blocking as _listen_non_blocking, + simulate as _simulate, +}; #[cfg(target_os = "linux")] mod linux; @@ -272,6 +275,49 @@ where _listen(callback) } +/// 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 +/// settings enabled. +/// +/// ```no_run +/// use rdev::{listen_non_blocking, Event}; +/// +/// #[derive(Default)] +/// pub struct BaseApp { +/// pub window: Window, +/// } +/// +/// fn callback(event: Event) { +/// println!("My callback {:?}", event); +/// match event.name{ +/// Some(string) => println!("User wrote {:?}", string), +/// None => () +/// } +/// } +/// impl AppDelegate for BaseApp { +/// fn did_finish_launching(&self) { +/// self.window.set_title("RDev"); +/// self.window.show(); +/// +/// // This will not block +/// if let Err(error) = listen(callback) { +/// println!("Error: {:?}", error) +/// } +/// } +/// +/// fn should_terminate_after_last_window_closed(&self) -> bool { +/// false +/// } +/// } +/// ``` +#[cfg(target_os = "macos")] +pub fn listen_non_blocking(callback: T) -> Result<(), ListenError> +where + T: FnMut(Event) + 'static, +{ + _listen_non_blocking(callback) +} + /// Sending some events /// /// ```no_run diff --git a/src/macos/listen.rs b/src/macos/listen.rs index 3da80f44..a5253821 100644 --- a/src/macos/listen.rs +++ b/src/macos/listen.rs @@ -61,3 +61,36 @@ where } Ok(()) } + + +#[link(name = "Cocoa", kind = "framework")] +pub fn listen_non_blocking(callback: T) -> Result<(), ListenError> +where + T: FnMut(Event) + 'static, +{ + unsafe { + GLOBAL_CALLBACK = Some(Box::new(callback)); + let _pool = NSAutoreleasePool::new(nil); + let tap = CGEventTapCreate( + CGEventTapLocation::HID, // HID, Session, AnnotatedSession, + kCGHeadInsertEventTap, + CGEventTapOption::ListenOnly, + kCGEventMaskForAllEvents, + raw_callback, + nil, + ); + if tap.is_null() { + return Err(ListenError::EventTapError); + } + let _loop = CFMachPortCreateRunLoopSource(nil, tap, 0); + if _loop.is_null() { + return Err(ListenError::LoopSourceError); + } + + let current_loop = CFRunLoopGetCurrent(); + CFRunLoopAddSource(current_loop, _loop, kCFRunLoopCommonModes); + + CGEventTapEnable(tap, true); + } + Ok(()) +} diff --git a/src/macos/mod.rs b/src/macos/mod.rs index 43ce23fb..c177a9e7 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::listen_non_blocking; pub use crate::macos::simulate::simulate; From e86e07b011269803354c70084029de811edf8e15 Mon Sep 17 00:00:00 2001 From: Serhii Tokariev Date: Sat, 29 Jan 2022 21:53:53 +0200 Subject: [PATCH 2/2] Add use for cacao crate --- src/lib.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index d61d6825..fdc1c2e5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -281,6 +281,15 @@ where /// /// ```no_run /// use rdev::{listen_non_blocking, Event}; +/// use cacao::{ +/// macos::{ +/// menu::{Menu, MenuItem}, +/// window::Window, +/// App, AppDelegate, +/// }, +/// notification_center::Dispatcher, +/// view::View, +/// }; /// /// #[derive(Default)] /// pub struct BaseApp { @@ -294,6 +303,7 @@ where /// None => () /// } /// } +/// /// impl AppDelegate for BaseApp { /// fn did_finish_launching(&self) { /// self.window.set_title("RDev");