Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[breaking][core-graphics] Allow dropping event from CGEventTap callback #724

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 36 additions & 22 deletions core-graphics/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -503,8 +503,21 @@ macro_rules! CGEventMaskBit {
}

pub type CGEventTapProxy = *const c_void;
type CGEventTapCallBackFn<'tap_life> =
Box<dyn Fn(CGEventTapProxy, CGEventType, &CGEvent) -> Option<CGEvent> + '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<dyn Fn(CGEventTapProxy, CGEventType, &CGEvent) -> CallbackResult + 'tap_life>;
type CGEventTapCallBackInternal = unsafe extern "C" fn(
proxy: CGEventTapProxy,
etype: CGEventType,
Expand All @@ -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(
Expand All @@ -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
Expand All @@ -555,11 +569,11 @@ unsafe extern "C" fn cg_event_tap_callback_internal(
/// ```
pub struct CGEventTap<'tap_life> {
mach_port: CFMachPort,
_callback: Box<CGEventTapCallBackFn<'tap_life>>,
_callback: Box<CGEventTapCallbackFn<'tap_life>>,
}

impl CGEventTap<'static> {
pub fn new<F: Fn(CGEventTapProxy, CGEventType, &CGEvent) -> Option<CGEvent> + 'static>(
pub fn new<F: Fn(CGEventTapProxy, CGEventType, &CGEvent) -> CallbackResult + 'static>(
tap: CGEventTapLocation,
place: CGEventTapPlacement,
options: CGEventTapOptions,
Expand All @@ -578,7 +592,7 @@ impl<'tap_life> CGEventTap<'tap_life> {
place: CGEventTapPlacement,
options: CGEventTapOptions,
events_of_interest: std::vec::Vec<CGEventType>,
callback: impl Fn(CGEventTapProxy, CGEventType, &CGEvent) -> Option<CGEvent> + 'tap_life,
callback: impl Fn(CGEventTapProxy, CGEventType, &CGEvent) -> CallbackResult + 'tap_life,
with_fn: impl FnOnce(&Self) -> R,
) -> Result<R, ()> {
// SAFETY: We are okay to bypass the 'static restriction because the
Expand All @@ -596,14 +610,14 @@ impl<'tap_life> CGEventTap<'tap_life> {
place: CGEventTapPlacement,
options: CGEventTapOptions,
events_of_interest: std::vec::Vec<CGEventType>,
callback: impl Fn(CGEventTapProxy, CGEventType, &CGEvent) -> Option<CGEvent> + 'tap_life,
callback: impl Fn(CGEventTapProxy, CGEventType, &CGEvent) -> CallbackResult + 'tap_life,
) -> Result<Self, ()> {
let event_mask: CGEventMask = events_of_interest
.iter()
.fold(CGEventType::Null as CGEventMask, |mask, &etype| {
mask | CGEventMaskBit!(etype)
});
let cb: Box<CGEventTapCallBackFn> = Box::new(Box::new(callback));
let cb: Box<CGEventTapCallbackFn> = Box::new(Box::new(callback));
let cbr = Box::into_raw(cb);
unsafe {
let event_tap_ref = CGEventTapCreate(
Expand Down
Loading