From 148a1d2151fff29633c37e7af6311a488f44679e Mon Sep 17 00:00:00 2001 From: Tyler Mandry Date: Sat, 15 Mar 2025 22:57:02 -0700 Subject: [PATCH] Allow dropping event from CGEventTap callback --- core-graphics/src/event.rs | 58 +++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 22 deletions(-) diff --git a/core-graphics/src/event.rs b/core-graphics/src/event.rs index 05a01742..9e9f6fa6 100644 --- a/core-graphics/src/event.rs +++ b/core-graphics/src/event.rs @@ -9,7 +9,7 @@ use core_foundation::{ mach_port::{CFMachPort, CFMachPortInvalidate, CFMachPortRef}, }; use foreign_types::{foreign_type, ForeignType}; -use std::mem::ManuallyDrop; +use std::{mem::ManuallyDrop, ptr}; pub type CGEventField = u32; pub type CGKeyCode = u16; @@ -503,8 +503,21 @@ macro_rules! CGEventMaskBit { } pub type CGEventTapProxy = *const c_void; -type CGEventTapCallBackFn<'tap_life> = - Box Option + 'tap_life>; + +/// What the system should do with the event passed to the callback. +/// +/// This value is ignored if [`CGEventTapOptions::ListenOnly`] is specified. +pub enum CallbackResult { + /// Pass the event unchanged to other consumers. + Keep, + /// Drop the event so it is not passed to later consumers. + Drop, + /// Replace the event with a different one. + Replace(CGEvent), +} + +type CGEventTapCallbackFn<'tap_life> = + Box CallbackResult + 'tap_life>; type CGEventTapCallBackInternal = unsafe extern "C" fn( proxy: CGEventTapProxy, etype: CGEventType, @@ -513,24 +526,25 @@ type CGEventTapCallBackInternal = unsafe extern "C" fn( ) -> crate::sys::CGEventRef; unsafe extern "C" fn cg_event_tap_callback_internal( - _proxy: CGEventTapProxy, - _etype: CGEventType, - _event: crate::sys::CGEventRef, - _user_info: *const c_void, + proxy: CGEventTapProxy, + etype: CGEventType, + event: crate::sys::CGEventRef, + user_info: *const c_void, ) -> crate::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() + let callback = user_info as *mut CGEventTapCallbackFn; + let event = ManuallyDrop::new(CGEvent::from_ptr(event)); + let response = (*callback)(proxy, etype, &event); + use CallbackResult::*; + match response { + Keep => event.as_ptr(), + Drop => ptr::null_mut(), + Replace(new_event) => ManuallyDrop::new(new_event).as_ptr(), + } } /// ```no_run /// use core_foundation::runloop::{kCFRunLoopCommonModes, CFRunLoop}; -/// use core_graphics::event::{CGEventTap, CGEventTapLocation, CGEventTapPlacement, CGEventTapOptions, CGEventType}; +/// use core_graphics::event::{CGEventTap, CGEventTapLocation, CGEventTapPlacement, CGEventTapOptions, CGEventType, CallbackResult}; /// let current = CFRunLoop::get_current(); /// /// CGEventTap::with( @@ -540,7 +554,7 @@ unsafe extern "C" fn cg_event_tap_callback_internal( /// vec![CGEventType::MouseMoved], /// |_proxy, _type, event| { /// println!("{:?}", event.location()); -/// None +/// CallbackResult::Keep /// }, /// |tap| { /// let loop_source = tap @@ -555,11 +569,11 @@ unsafe extern "C" fn cg_event_tap_callback_internal( /// ``` pub struct CGEventTap<'tap_life> { mach_port: CFMachPort, - _callback: Box>, + _callback: Box>, } impl CGEventTap<'static> { - pub fn new Option + 'static>( + pub fn new CallbackResult + 'static>( tap: CGEventTapLocation, place: CGEventTapPlacement, options: CGEventTapOptions, @@ -578,7 +592,7 @@ impl<'tap_life> CGEventTap<'tap_life> { place: CGEventTapPlacement, options: CGEventTapOptions, events_of_interest: std::vec::Vec, - callback: impl Fn(CGEventTapProxy, CGEventType, &CGEvent) -> Option + 'tap_life, + callback: impl Fn(CGEventTapProxy, CGEventType, &CGEvent) -> CallbackResult + 'tap_life, with_fn: impl FnOnce(&Self) -> R, ) -> Result { // SAFETY: We are okay to bypass the 'static restriction because the @@ -596,14 +610,14 @@ impl<'tap_life> CGEventTap<'tap_life> { place: CGEventTapPlacement, options: CGEventTapOptions, events_of_interest: std::vec::Vec, - callback: impl Fn(CGEventTapProxy, CGEventType, &CGEvent) -> Option + 'tap_life, + callback: impl Fn(CGEventTapProxy, CGEventType, &CGEvent) -> CallbackResult + 'tap_life, ) -> Result { let event_mask: CGEventMask = events_of_interest .iter() .fold(CGEventType::Null as CGEventMask, |mask, &etype| { mask | CGEventMaskBit!(etype) }); - let cb: Box = Box::new(Box::new(callback)); + let cb: Box = Box::new(Box::new(callback)); let cbr = Box::into_raw(cb); unsafe { let event_tap_ref = CGEventTapCreate(