Skip to content

Commit 19671d3

Browse files
committed
core-graphics: Add CGEventTap object to create an event tap
1 parent edb187a commit 19671d3

File tree

2 files changed

+126
-6
lines changed

2 files changed

+126
-6
lines changed

core-graphics/src/event.rs

Lines changed: 124 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
#![allow(non_upper_case_globals)]
2-
3-
use core_foundation::base::{CFRelease, CFRetain, CFTypeID};
4-
use geometry::CGPoint;
2+
use core_foundation::{
3+
base::{CFRelease, CFRetain, CFTypeID, TCFType},
4+
mach_port::{CFMachPort, CFMachPortRef},
5+
};
56
use event_source::CGEventSource;
6-
7-
use libc;
8-
97
use foreign_types::ForeignType;
8+
use geometry::CGPoint;
9+
use libc::c_void;
10+
use std::mem::ManuallyDrop;
1011

1112
pub type CGEventField = u32;
1213
pub type CGKeyCode = u16;
@@ -384,6 +385,110 @@ pub enum CGEventTapLocation {
384385
AnnotatedSession,
385386
}
386387

388+
// The next three enums are taken from:
389+
// [Ref](https://github.com/phracker/MacOSX-SDKs/blob/ef9fe35d5691b6dd383c8c46d867a499817a01b6/MacOSX10.15.sdk/System/Library/Frameworks/CoreGraphics.framework/Versions/A/Headers/CGEventTypes.h)
390+
/* Constants that specify where a new event tap is inserted into the list of
391+
active event taps. */
392+
#[repr(u32)]
393+
#[derive(Clone, Copy, Debug)]
394+
pub enum CGEventTapPlacement {
395+
HeadInsertEventTap = 0,
396+
TailAppendEventTap,
397+
}
398+
399+
/* Constants that specify whether a new event tap is an active filter or a
400+
passive listener. */
401+
#[repr(u32)]
402+
#[derive(Clone, Copy, Debug)]
403+
pub enum CGEventTapOptions {
404+
Default = 0x00000000,
405+
ListenOnly = 0x00000001,
406+
}
407+
408+
pub type CGEventMask = u64;
409+
/* Generate an event mask for a single type of event. */
410+
macro_rules! CGEventMaskBit {
411+
($eventType:expr) => {
412+
1 << $eventType as CGEventMask
413+
};
414+
}
415+
416+
pub type CGEventTapProxy = *const c_void;
417+
pub type CGEventTapCallBackFn<'tap_life> =
418+
Box<dyn Fn(CGEventTapProxy, CGEventType, &CGEvent) -> Option<CGEvent> + 'tap_life>;
419+
type CGEventTapCallBackInternal = unsafe extern "C" fn(
420+
proxy: CGEventTapProxy,
421+
etype: CGEventType,
422+
event: ::sys::CGEventRef,
423+
user_info: *const c_void,
424+
) -> ::sys::CGEventRef;
425+
426+
#[no_mangle]
427+
unsafe extern "C" fn cg_event_tap_callback_internal(
428+
_proxy: CGEventTapProxy,
429+
_etype: CGEventType,
430+
_event: ::sys::CGEventRef,
431+
_user_info: *const c_void,
432+
) -> ::sys::CGEventRef {
433+
let callback = _user_info as *mut CGEventTapCallBackFn;
434+
let event = CGEvent::from_ptr(_event);
435+
let new_event = (*callback)(_proxy, _etype, &event);
436+
let event = match new_event {
437+
Some(new_event) => new_event,
438+
None => event,
439+
};
440+
ManuallyDrop::new(event).as_ptr()
441+
}
442+
443+
444+
pub struct CGEventTap<'tap_life> {
445+
pub mach_port: CFMachPort,
446+
pub callback_ref:
447+
Box<dyn Fn(CGEventTapProxy, CGEventType, &CGEvent) -> Option<CGEvent> + 'tap_life>,
448+
}
449+
450+
impl<'tap_life> CGEventTap<'tap_life> {
451+
pub fn new<F: Fn(CGEventTapProxy, CGEventType, &CGEvent) -> Option<CGEvent> + 'tap_life>(
452+
tap: CGEventTapLocation,
453+
place: CGEventTapPlacement,
454+
options: CGEventTapOptions,
455+
events_of_interest: std::vec::Vec<CGEventType>,
456+
callback: F,
457+
) -> Result<CGEventTap<'tap_life>, ()> {
458+
let event_mask: CGEventMask = events_of_interest
459+
.iter()
460+
.fold(CGEventType::Null as CGEventMask, |mask, &etype| {
461+
mask | CGEventMaskBit!(etype)
462+
});
463+
let cb = Box::new(Box::new(callback) as CGEventTapCallBackFn);
464+
let cbr = Box::into_raw(cb);
465+
unsafe {
466+
let event_tap_ref = CGEventTapCreate(
467+
tap,
468+
place,
469+
options,
470+
event_mask,
471+
cg_event_tap_callback_internal,
472+
cbr as *const c_void,
473+
);
474+
475+
if !event_tap_ref.is_null() {
476+
Ok(Self {
477+
mach_port: (CFMachPort::wrap_under_create_rule(event_tap_ref)),
478+
callback_ref: Box::from_raw(cbr),
479+
})
480+
} else {
481+
Box::from_raw(cbr);
482+
Err(())
483+
}
484+
}
485+
}
486+
487+
pub fn enable(&self) {
488+
unsafe { CGEventTapEnable(self.mach_port.as_concrete_TypeRef(), true) }
489+
}
490+
}
491+
387492
foreign_type! {
388493
#[doc(hidden)]
389494
type CType = ::sys::CGEvent;
@@ -666,4 +771,17 @@ extern {
666771
/// fixed point number or integer, the value parameter is scaled as needed
667772
/// and converted to the appropriate type.
668773
fn CGEventSetDoubleValueField(event: ::sys::CGEventRef, field: CGEventField, value: f64);
774+
775+
// ::sys::CGEventTapRef is actually an CFMachPortRef
776+
fn CGEventTapCreate(
777+
tap: CGEventTapLocation,
778+
place: CGEventTapPlacement,
779+
options: CGEventTapOptions,
780+
eventsOfInterest: CGEventMask,
781+
callback: CGEventTapCallBackInternal,
782+
userInfo: *const c_void,
783+
) -> CFMachPortRef;
784+
785+
fn CGEventTapEnable(tap: CFMachPortRef, enable: bool);
786+
669787
}

core-graphics/src/sys.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ pub type CGGradientRef = *mut CGGradient;
2828

2929
#[cfg(target_os = "macos")]
3030
mod macos {
31+
pub enum CGEventTap {}
32+
pub type CGEventTapRef = core_foundation::mach_port::CFMachPortRef;
3133
pub enum CGEvent {}
3234
pub type CGEventRef = *mut CGEvent;
3335

0 commit comments

Comments
 (0)