Skip to content
Merged
Show file tree
Hide file tree
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
1 change: 1 addition & 0 deletions core-foundation-sys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ pub mod string;
pub mod timezone;
pub mod url;
pub mod uuid;
pub mod mach_port;
20 changes: 20 additions & 0 deletions core-foundation-sys/src/mach_port.rs
Original file line number Diff line number Diff line change
@@ -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;
}
1 change: 1 addition & 0 deletions core-foundation/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,3 +233,4 @@ pub mod propertylist;
pub mod runloop;
pub mod timezone;
pub mod uuid;
pub mod mach_port;
28 changes: 28 additions & 0 deletions core-foundation/src/mach_port.rs
Original file line number Diff line number Diff line change
@@ -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<CFRunLoopSource, ()> {
unsafe {
let runloop_source_ref = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, self.0, order);
if runloop_source_ref.is_null() {
Err(())
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Swipe Err and Ok

} else {
Ok(CFRunLoopSource::wrap_under_create_rule(runloop_source_ref))
}
}
}
}
157 changes: 151 additions & 6 deletions core-graphics/src/event.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -384,6 +385,137 @@ 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<dyn Fn(CGEventTapProxy, CGEventType, &CGEvent) -> Option<CGEvent> + '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()
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add comments, why do I do that?

}


Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Derive Debug

/// ```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:
Box<dyn Fn(CGEventTapProxy, CGEventType, &CGEvent) -> Option<CGEvent> + 'tap_life>,
}

impl<'tap_life> CGEventTap<'tap_life> {
pub fn new<F: Fn(CGEventTapProxy, CGEventType, &CGEvent) -> Option<CGEvent> + 'tap_life>(
tap: CGEventTapLocation,
place: CGEventTapPlacement,
options: CGEventTapOptions,
events_of_interest: std::vec::Vec<CGEventType>,
callback: F,
) -> Result<CGEventTap<'tap_life>, ()> {
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;
Expand Down Expand Up @@ -666,4 +798,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);

}
2 changes: 2 additions & 0 deletions core-graphics/src/sys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down