Auto merge of #436 - AsafFisher:master, r=jrmuizel

Add CGEventTap

https://github.com/servo/core-graphics-rs/issues/111
This commit is contained in:
bors-servo
2021-05-26 13:18:34 -04:00
committed by GitHub
6 changed files with 203 additions and 6 deletions
+1
View File
@@ -29,3 +29,4 @@ pub mod string;
pub mod timezone;
pub mod url;
pub mod uuid;
pub mod mach_port;
+20
View File
@@ -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
View File
@@ -233,3 +233,4 @@ pub mod propertylist;
pub mod runloop;
pub mod timezone;
pub mod uuid;
pub mod mach_port;
+28
View File
@@ -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(())
} else {
Ok(CFRunLoopSource::wrap_under_create_rule(runloop_source_ref))
}
}
}
}
+151 -6
View File
@@ -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;
@@ -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()
}
/// ```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;
@@ -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
View File
@@ -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;