Skip to content

Commit f7e0b97

Browse files
authored
Auto merge of #436 - AsafFisher:master, r=jrmuizel
Add CGEventTap servo/core-graphics-rs#111
2 parents ea68a18 + 532a321 commit f7e0b97

File tree

6 files changed

+203
-6
lines changed

6 files changed

+203
-6
lines changed

core-foundation-sys/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,4 @@ pub mod string;
2929
pub mod timezone;
3030
pub mod url;
3131
pub mod uuid;
32+
pub mod mach_port;

core-foundation-sys/src/mach_port.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
pub use base::{CFAllocatorRef, CFIndex, CFTypeID};
2+
use runloop::CFRunLoopSourceRef;
3+
use std::os::raw::c_void;
4+
5+
#[repr(C)]
6+
pub struct __CFMachPort(c_void);
7+
pub type CFMachPortRef = *const __CFMachPort;
8+
9+
extern "C" {
10+
/*
11+
* CFMachPort.h
12+
*/
13+
pub fn CFMachPortCreateRunLoopSource(
14+
allocator: CFAllocatorRef,
15+
port: CFMachPortRef,
16+
order: CFIndex,
17+
) -> CFRunLoopSourceRef;
18+
19+
pub fn CFMachPortGetTypeID() -> CFTypeID;
20+
}

core-foundation/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,3 +233,4 @@ pub mod propertylist;
233233
pub mod runloop;
234234
pub mod timezone;
235235
pub mod uuid;
236+
pub mod mach_port;

core-foundation/src/mach_port.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
use base::TCFType;
2+
use core_foundation_sys::base::kCFAllocatorDefault;
3+
use runloop::CFRunLoopSource;
4+
pub use core_foundation_sys::mach_port::*;
5+
6+
7+
declare_TCFType! {
8+
/// An immutable numeric value.
9+
CFMachPort, CFMachPortRef
10+
}
11+
impl_TCFType!(CFMachPort, CFMachPortRef, CFMachPortGetTypeID);
12+
impl_CFTypeDescription!(CFMachPort);
13+
14+
impl CFMachPort {
15+
pub fn create_runloop_source(
16+
&self,
17+
order: CFIndex,
18+
) -> Result<CFRunLoopSource, ()> {
19+
unsafe {
20+
let runloop_source_ref = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, self.0, order);
21+
if runloop_source_ref.is_null() {
22+
Err(())
23+
} else {
24+
Ok(CFRunLoopSource::wrap_under_create_rule(runloop_source_ref))
25+
}
26+
}
27+
}
28+
}

core-graphics/src/event.rs

Lines changed: 151 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,137 @@ 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+
/// ```no_run
445+
///extern crate core_foundation;
446+
///use core_foundation::runloop::{kCFRunLoopCommonModes, CFRunLoop};
447+
///use core_graphics::event::{CGEventTap, CGEventTapLocation, CGEventTapPlacement, CGEventTapOptions, CGEventType};
448+
///let current = CFRunLoop::get_current();
449+
///match CGEventTap::new(
450+
/// CGEventTapLocation::HID,
451+
/// CGEventTapPlacement::HeadInsertEventTap,
452+
/// CGEventTapOptions::Default,
453+
/// vec![CGEventType::MouseMoved],
454+
/// |_a, _b, d| {
455+
/// println!("{:?}", d.location());
456+
/// None
457+
/// },
458+
/// ) {
459+
/// Ok(tap) => unsafe {
460+
/// let loop_source = tap
461+
/// .mach_port
462+
/// .create_runloop_source(0)
463+
/// .expect("Somethings is bad ");
464+
/// current.add_source(&loop_source, kCFRunLoopCommonModes);
465+
/// tap.enable();
466+
/// CFRunLoop::run_current();
467+
/// },
468+
/// Err(_) => (assert!(false)),
469+
/// }
470+
/// ```
471+
pub struct CGEventTap<'tap_life> {
472+
pub mach_port: CFMachPort,
473+
pub callback_ref:
474+
Box<dyn Fn(CGEventTapProxy, CGEventType, &CGEvent) -> Option<CGEvent> + 'tap_life>,
475+
}
476+
477+
impl<'tap_life> CGEventTap<'tap_life> {
478+
pub fn new<F: Fn(CGEventTapProxy, CGEventType, &CGEvent) -> Option<CGEvent> + 'tap_life>(
479+
tap: CGEventTapLocation,
480+
place: CGEventTapPlacement,
481+
options: CGEventTapOptions,
482+
events_of_interest: std::vec::Vec<CGEventType>,
483+
callback: F,
484+
) -> Result<CGEventTap<'tap_life>, ()> {
485+
let event_mask: CGEventMask = events_of_interest
486+
.iter()
487+
.fold(CGEventType::Null as CGEventMask, |mask, &etype| {
488+
mask | CGEventMaskBit!(etype)
489+
});
490+
let cb = Box::new(Box::new(callback) as CGEventTapCallBackFn);
491+
let cbr = Box::into_raw(cb);
492+
unsafe {
493+
let event_tap_ref = CGEventTapCreate(
494+
tap,
495+
place,
496+
options,
497+
event_mask,
498+
cg_event_tap_callback_internal,
499+
cbr as *const c_void,
500+
);
501+
502+
if !event_tap_ref.is_null() {
503+
Ok(Self {
504+
mach_port: (CFMachPort::wrap_under_create_rule(event_tap_ref)),
505+
callback_ref: Box::from_raw(cbr),
506+
})
507+
} else {
508+
Box::from_raw(cbr);
509+
Err(())
510+
}
511+
}
512+
}
513+
514+
pub fn enable(&self) {
515+
unsafe { CGEventTapEnable(self.mach_port.as_concrete_TypeRef(), true) }
516+
}
517+
}
518+
387519
foreign_type! {
388520
#[doc(hidden)]
389521
type CType = ::sys::CGEvent;
@@ -666,4 +798,17 @@ extern {
666798
/// fixed point number or integer, the value parameter is scaled as needed
667799
/// and converted to the appropriate type.
668800
fn CGEventSetDoubleValueField(event: ::sys::CGEventRef, field: CGEventField, value: f64);
801+
802+
// ::sys::CGEventTapRef is actually an CFMachPortRef
803+
fn CGEventTapCreate(
804+
tap: CGEventTapLocation,
805+
place: CGEventTapPlacement,
806+
options: CGEventTapOptions,
807+
eventsOfInterest: CGEventMask,
808+
callback: CGEventTapCallBackInternal,
809+
userInfo: *const c_void,
810+
) -> CFMachPortRef;
811+
812+
fn CGEventTapEnable(tap: CFMachPortRef, enable: bool);
813+
669814
}

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)