From edb187a17c7d63977271cd939df91e3d0c1d6e90 Mon Sep 17 00:00:00 2001 From: Asaf Fisher Date: Fri, 22 Jan 2021 01:53:25 +0200 Subject: [PATCH 1/3] core-foundation: Add basic CFMachPort --- core-foundation-sys/src/lib.rs | 1 + core-foundation-sys/src/mach_port.rs | 20 ++++++++++++++++++++ core-foundation/src/lib.rs | 1 + core-foundation/src/mach_port.rs | 28 ++++++++++++++++++++++++++++ 4 files changed, 50 insertions(+) create mode 100644 core-foundation-sys/src/mach_port.rs create mode 100644 core-foundation/src/mach_port.rs diff --git a/core-foundation-sys/src/lib.rs b/core-foundation-sys/src/lib.rs index 9b1056b..f9a1884 100644 --- a/core-foundation-sys/src/lib.rs +++ b/core-foundation-sys/src/lib.rs @@ -29,3 +29,4 @@ pub mod string; pub mod timezone; pub mod url; pub mod uuid; +pub mod mach_port; diff --git a/core-foundation-sys/src/mach_port.rs b/core-foundation-sys/src/mach_port.rs new file mode 100644 index 0000000..2341fd4 --- /dev/null +++ b/core-foundation-sys/src/mach_port.rs @@ -0,0 +1,20 @@ +pub use base::{CFAllocatorRef, CFIndex, CFTypeID}; +use runloop::CFRunLoopSourceRef; +use std::os::raw::c_void; + +#[repr(C)] +pub struct __CFMachPort(c_void); +pub type CFMachPortRef = *const __CFMachPort; + +extern "C" { + /* + * CFMachPort.h + */ + pub fn CFMachPortCreateRunLoopSource( + allocator: CFAllocatorRef, + port: CFMachPortRef, + order: CFIndex, + ) -> CFRunLoopSourceRef; + + pub fn CFMachPortGetTypeID() -> CFTypeID; +} diff --git a/core-foundation/src/lib.rs b/core-foundation/src/lib.rs index eaab9a0..b935938 100644 --- a/core-foundation/src/lib.rs +++ b/core-foundation/src/lib.rs @@ -233,3 +233,4 @@ pub mod propertylist; pub mod runloop; pub mod timezone; pub mod uuid; +pub mod mach_port; diff --git a/core-foundation/src/mach_port.rs b/core-foundation/src/mach_port.rs new file mode 100644 index 0000000..6112e3a --- /dev/null +++ b/core-foundation/src/mach_port.rs @@ -0,0 +1,28 @@ +use base::TCFType; +use core_foundation_sys::base::kCFAllocatorDefault; +use runloop::CFRunLoopSource; +pub use core_foundation_sys::mach_port::*; + + +declare_TCFType! { + /// An immutable numeric value. + CFMachPort, CFMachPortRef +} +impl_TCFType!(CFMachPort, CFMachPortRef, CFMachPortGetTypeID); +impl_CFTypeDescription!(CFMachPort); + +impl CFMachPort { + pub fn create_runloop_source( + &self, + order: CFIndex, + ) -> Result { + unsafe { + let runloop_source_ref = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, self.0, order); + if runloop_source_ref.is_null() { + Err(()) + } else { + Ok(CFRunLoopSource::wrap_under_create_rule(runloop_source_ref)) + } + } + } +} From 19671d30c26886cc449cfe6d88915fad803c74d8 Mon Sep 17 00:00:00 2001 From: Asaf Fisher Date: Fri, 22 Jan 2021 02:00:54 +0200 Subject: [PATCH 2/3] core-graphics: Add `CGEventTap` object to create an event tap --- core-graphics/src/event.rs | 130 +++++++++++++++++++++++++++++++++++-- core-graphics/src/sys.rs | 2 + 2 files changed, 126 insertions(+), 6 deletions(-) diff --git a/core-graphics/src/event.rs b/core-graphics/src/event.rs index f28e293..2a43c55 100644 --- a/core-graphics/src/event.rs +++ b/core-graphics/src/event.rs @@ -1,12 +1,13 @@ #![allow(non_upper_case_globals)] - -use core_foundation::base::{CFRelease, CFRetain, CFTypeID}; -use geometry::CGPoint; +use core_foundation::{ + base::{CFRelease, CFRetain, CFTypeID, TCFType}, + mach_port::{CFMachPort, CFMachPortRef}, +}; use event_source::CGEventSource; - -use libc; - use foreign_types::ForeignType; +use geometry::CGPoint; +use libc::c_void; +use std::mem::ManuallyDrop; pub type CGEventField = u32; pub type CGKeyCode = u16; @@ -384,6 +385,110 @@ pub enum CGEventTapLocation { AnnotatedSession, } +// The next three enums are taken from: +// [Ref](https://github.com/phracker/MacOSX-SDKs/blob/ef9fe35d5691b6dd383c8c46d867a499817a01b6/MacOSX10.15.sdk/System/Library/Frameworks/CoreGraphics.framework/Versions/A/Headers/CGEventTypes.h) +/* Constants that specify where a new event tap is inserted into the list of +active event taps. */ +#[repr(u32)] +#[derive(Clone, Copy, Debug)] +pub enum CGEventTapPlacement { + HeadInsertEventTap = 0, + TailAppendEventTap, +} + +/* Constants that specify whether a new event tap is an active filter or a +passive listener. */ +#[repr(u32)] +#[derive(Clone, Copy, Debug)] +pub enum CGEventTapOptions { + Default = 0x00000000, + ListenOnly = 0x00000001, +} + +pub type CGEventMask = u64; +/* Generate an event mask for a single type of event. */ +macro_rules! CGEventMaskBit { + ($eventType:expr) => { + 1 << $eventType as CGEventMask + }; +} + +pub type CGEventTapProxy = *const c_void; +pub type CGEventTapCallBackFn<'tap_life> = + Box Option + 'tap_life>; +type CGEventTapCallBackInternal = unsafe extern "C" fn( + proxy: CGEventTapProxy, + etype: CGEventType, + event: ::sys::CGEventRef, + user_info: *const c_void, +) -> ::sys::CGEventRef; + +#[no_mangle] +unsafe extern "C" fn cg_event_tap_callback_internal( + _proxy: CGEventTapProxy, + _etype: CGEventType, + _event: ::sys::CGEventRef, + _user_info: *const c_void, +) -> ::sys::CGEventRef { + let callback = _user_info as *mut CGEventTapCallBackFn; + let event = CGEvent::from_ptr(_event); + let new_event = (*callback)(_proxy, _etype, &event); + let event = match new_event { + Some(new_event) => new_event, + None => event, + }; + ManuallyDrop::new(event).as_ptr() +} + + +pub struct CGEventTap<'tap_life> { + pub mach_port: CFMachPort, + pub callback_ref: + Box Option + 'tap_life>, +} + +impl<'tap_life> CGEventTap<'tap_life> { + pub fn new Option + 'tap_life>( + tap: CGEventTapLocation, + place: CGEventTapPlacement, + options: CGEventTapOptions, + events_of_interest: std::vec::Vec, + callback: F, + ) -> Result, ()> { + let event_mask: CGEventMask = events_of_interest + .iter() + .fold(CGEventType::Null as CGEventMask, |mask, &etype| { + mask | CGEventMaskBit!(etype) + }); + let cb = Box::new(Box::new(callback) as CGEventTapCallBackFn); + let cbr = Box::into_raw(cb); + unsafe { + let event_tap_ref = CGEventTapCreate( + tap, + place, + options, + event_mask, + cg_event_tap_callback_internal, + cbr as *const c_void, + ); + + if !event_tap_ref.is_null() { + Ok(Self { + mach_port: (CFMachPort::wrap_under_create_rule(event_tap_ref)), + callback_ref: Box::from_raw(cbr), + }) + } else { + Box::from_raw(cbr); + Err(()) + } + } + } + + pub fn enable(&self) { + unsafe { CGEventTapEnable(self.mach_port.as_concrete_TypeRef(), true) } + } +} + foreign_type! { #[doc(hidden)] type CType = ::sys::CGEvent; @@ -666,4 +771,17 @@ extern { /// fixed point number or integer, the value parameter is scaled as needed /// and converted to the appropriate type. fn CGEventSetDoubleValueField(event: ::sys::CGEventRef, field: CGEventField, value: f64); + + // ::sys::CGEventTapRef is actually an CFMachPortRef + fn CGEventTapCreate( + tap: CGEventTapLocation, + place: CGEventTapPlacement, + options: CGEventTapOptions, + eventsOfInterest: CGEventMask, + callback: CGEventTapCallBackInternal, + userInfo: *const c_void, + ) -> CFMachPortRef; + + fn CGEventTapEnable(tap: CFMachPortRef, enable: bool); + } diff --git a/core-graphics/src/sys.rs b/core-graphics/src/sys.rs index 2ae5261..85df442 100644 --- a/core-graphics/src/sys.rs +++ b/core-graphics/src/sys.rs @@ -28,6 +28,8 @@ pub type CGGradientRef = *mut CGGradient; #[cfg(target_os = "macos")] mod macos { + pub enum CGEventTap {} + pub type CGEventTapRef = core_foundation::mach_port::CFMachPortRef; pub enum CGEvent {} pub type CGEventRef = *mut CGEvent; From 532a3214beee5c33fe3f07802b55c3ba543031de Mon Sep 17 00:00:00 2001 From: Asaf Fisher Date: Fri, 22 Jan 2021 02:02:09 +0200 Subject: [PATCH 3/3] core-graphics: event: Add tests to `CGEventTap` --- core-graphics/src/event.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/core-graphics/src/event.rs b/core-graphics/src/event.rs index 2a43c55..8cf9c62 100644 --- a/core-graphics/src/event.rs +++ b/core-graphics/src/event.rs @@ -441,6 +441,33 @@ unsafe extern "C" fn cg_event_tap_callback_internal( } +/// ```no_run +///extern crate core_foundation; +///use core_foundation::runloop::{kCFRunLoopCommonModes, CFRunLoop}; +///use core_graphics::event::{CGEventTap, CGEventTapLocation, CGEventTapPlacement, CGEventTapOptions, CGEventType}; +///let current = CFRunLoop::get_current(); +///match CGEventTap::new( +/// CGEventTapLocation::HID, +/// CGEventTapPlacement::HeadInsertEventTap, +/// CGEventTapOptions::Default, +/// vec![CGEventType::MouseMoved], +/// |_a, _b, d| { +/// println!("{:?}", d.location()); +/// None +/// }, +/// ) { +/// Ok(tap) => unsafe { +/// let loop_source = tap +/// .mach_port +/// .create_runloop_source(0) +/// .expect("Somethings is bad "); +/// current.add_source(&loop_source, kCFRunLoopCommonModes); +/// tap.enable(); +/// CFRunLoop::run_current(); +/// }, +/// Err(_) => (assert!(false)), +/// } +/// ``` pub struct CGEventTap<'tap_life> { pub mach_port: CFMachPort, pub callback_ref: