#![allow(non_upper_case_globals)]
use crate::event_source::CGEventSource;
use crate::geometry::CGPoint;
use core_foundation::{
base::{CFRelease, CFRetain, CFTypeID, TCFType},
mach_port::{CFMachPort, CFMachPortRef},
};
use foreign_types::ForeignType;
use libc::c_void;
use std::mem::ManuallyDrop;
pub type CGEventField = u32;
pub type CGKeyCode = u16;
pub type CGScrollEventUnit = u32;
bitflags! {
#[repr(C)]
pub struct CGEventFlags: u64 {
const CGEventFlagNull = 0;
const CGEventFlagAlphaShift = 0x00010000;
const CGEventFlagShift = 0x00020000;
const CGEventFlagControl = 0x00040000;
const CGEventFlagAlternate = 0x00080000;
const CGEventFlagCommand = 0x00100000;
const CGEventFlagHelp = 0x00400000;
const CGEventFlagSecondaryFn = 0x00800000;
const CGEventFlagNumericPad = 0x00200000;
const CGEventFlagNonCoalesced = 0x00000100;
}
}
#[repr(C)]
pub struct KeyCode;
impl KeyCode {
pub const RETURN: CGKeyCode = 0x24;
pub const TAB: CGKeyCode = 0x30;
pub const SPACE: CGKeyCode = 0x31;
pub const DELETE: CGKeyCode = 0x33;
pub const ESCAPE: CGKeyCode = 0x35;
pub const COMMAND: CGKeyCode = 0x37;
pub const SHIFT: CGKeyCode = 0x38;
pub const CAPS_LOCK: CGKeyCode = 0x39;
pub const OPTION: CGKeyCode = 0x3A;
pub const CONTROL: CGKeyCode = 0x3B;
pub const RIGHT_COMMAND: CGKeyCode = 0x36;
pub const RIGHT_SHIFT: CGKeyCode = 0x3C;
pub const RIGHT_OPTION: CGKeyCode = 0x3D;
pub const RIGHT_CONTROL: CGKeyCode = 0x3E;
pub const FUNCTION: CGKeyCode = 0x3F;
pub const VOLUME_UP: CGKeyCode = 0x48;
pub const VOLUME_DOWN: CGKeyCode = 0x49;
pub const MUTE: CGKeyCode = 0x4A;
pub const F1: CGKeyCode = 0x7A;
pub const F2: CGKeyCode = 0x78;
pub const F3: CGKeyCode = 0x63;
pub const F4: CGKeyCode = 0x76;
pub const F5: CGKeyCode = 0x60;
pub const F6: CGKeyCode = 0x61;
pub const F7: CGKeyCode = 0x62;
pub const F8: CGKeyCode = 0x64;
pub const F9: CGKeyCode = 0x65;
pub const F10: CGKeyCode = 0x6D;
pub const F11: CGKeyCode = 0x67;
pub const F12: CGKeyCode = 0x6F;
pub const F13: CGKeyCode = 0x69;
pub const F14: CGKeyCode = 0x6B;
pub const F15: CGKeyCode = 0x71;
pub const F16: CGKeyCode = 0x6A;
pub const F17: CGKeyCode = 0x40;
pub const F18: CGKeyCode = 0x4F;
pub const F19: CGKeyCode = 0x50;
pub const F20: CGKeyCode = 0x5A;
pub const HELP: CGKeyCode = 0x72;
pub const HOME: CGKeyCode = 0x73;
pub const PAGE_UP: CGKeyCode = 0x74;
pub const FORWARD_DELETE: CGKeyCode = 0x75;
pub const END: CGKeyCode = 0x77;
pub const PAGE_DOWN: CGKeyCode = 0x79;
pub const LEFT_ARROW: CGKeyCode = 0x7B;
pub const RIGHT_ARROW: CGKeyCode = 0x7C;
pub const DOWN_ARROW: CGKeyCode = 0x7D;
pub const UP_ARROW: CGKeyCode = 0x7E;
}
#[repr(C)]
pub struct ScrollEventUnit {}
impl ScrollEventUnit {
pub const PIXEL: CGScrollEventUnit = 0;
pub const LINE: CGScrollEventUnit = 1;
}
#[repr(u32)]
#[derive(Clone, Copy, Debug)]
pub enum CGEventType {
Null = 0,
LeftMouseDown = 1,
LeftMouseUp = 2,
RightMouseDown = 3,
RightMouseUp = 4,
MouseMoved = 5,
LeftMouseDragged = 6,
RightMouseDragged = 7,
KeyDown = 10,
KeyUp = 11,
FlagsChanged = 12,
ScrollWheel = 22,
TabletPointer = 23,
TabletProximity = 24,
OtherMouseDown = 25,
OtherMouseUp = 26,
OtherMouseDragged = 27,
TapDisabledByTimeout = 0xFFFFFFFE,
TapDisabledByUserInput = 0xFFFFFFFF,
}
pub struct EventField;
impl EventField {
pub const MOUSE_EVENT_NUMBER: CGEventField = 0;
pub const MOUSE_EVENT_CLICK_STATE: CGEventField = 1;
pub const MOUSE_EVENT_PRESSURE: CGEventField = 2;
pub const MOUSE_EVENT_BUTTON_NUMBER: CGEventField = 3;
pub const MOUSE_EVENT_DELTA_X: CGEventField = 4;
pub const MOUSE_EVENT_DELTA_Y: CGEventField = 5;
pub const MOUSE_EVENT_INSTANT_MOUSER: CGEventField = 6;
pub const MOUSE_EVENT_SUB_TYPE: CGEventField = 7;
pub const KEYBOARD_EVENT_AUTOREPEAT: CGEventField = 8;
pub const KEYBOARD_EVENT_KEYCODE: CGEventField = 9;
pub const KEYBOARD_EVENT_KEYBOARD_TYPE: CGEventField = 10;
pub const SCROLL_WHEEL_EVENT_DELTA_AXIS_1: CGEventField = 11;
pub const SCROLL_WHEEL_EVENT_DELTA_AXIS_2: CGEventField = 12;
pub const SCROLL_WHEEL_EVENT_FIXED_POINT_DELTA_AXIS_1: CGEventField = 93;
pub const SCROLL_WHEEL_EVENT_FIXED_POINT_DELTA_AXIS_2: CGEventField = 94;
pub const SCROLL_WHEEL_EVENT_POINT_DELTA_AXIS_1: CGEventField = 96;
pub const SCROLL_WHEEL_EVENT_POINT_DELTA_AXIS_2: CGEventField = 97;
pub const SCROLL_WHEEL_EVENT_INSTANT_MOUSER: CGEventField = 14;
pub const TABLET_EVENT_POINT_X: CGEventField = 15;
pub const TABLET_EVENT_POINT_Y: CGEventField = 16;
pub const TABLET_EVENT_POINT_Z: CGEventField = 17;
pub const TABLET_EVENT_POINT_BUTTONS: CGEventField = 18;
pub const TABLET_EVENT_POINT_PRESSURE: CGEventField = 19;
pub const TABLET_EVENT_TILT_X: CGEventField = 20;
pub const TABLET_EVENT_TILT_Y: CGEventField = 21;
pub const TABLET_EVENT_ROTATION: CGEventField = 22;
pub const TABLET_EVENT_TANGENTIAL_PRESSURE: CGEventField = 23;
pub const TABLET_EVENT_DEVICE_ID: CGEventField = 24;
pub const TABLET_EVENT_VENDOR_1: CGEventField = 25;
pub const TABLET_EVENT_VENDOR_2: CGEventField = 26;
pub const TABLET_EVENT_VENDOR_3: CGEventField = 27;
pub const TABLET_PROXIMITY_EVENT_VENDOR_ID: CGEventField = 28;
pub const TABLET_PROXIMITY_EVENT_TABLET_ID: CGEventField = 29;
pub const TABLET_PROXIMITY_EVENT_POINTER_ID: CGEventField = 30;
pub const TABLET_PROXIMITY_EVENT_DEVICE_ID: CGEventField = 31;
pub const TABLET_PROXIMITY_EVENT_SYSTEM_TABLET_ID: CGEventField = 32;
pub const TABLET_PROXIMITY_EVENT_VENDOR_POINTER_TYPE: CGEventField = 33;
pub const TABLET_PROXIMITY_EVENT_VENDOR_POINTER_SERIAL_NUMBER: CGEventField = 34;
pub const TABLET_PROXIMITY_EVENT_VENDOR_UNIQUE_ID: CGEventField = 35;
pub const TABLET_PROXIMITY_EVENT_CAPABILITY_MASK: CGEventField = 36;
pub const TABLET_PROXIMITY_EVENT_POINTER_TYPE: CGEventField = 37;
pub const TABLET_PROXIMITY_EVENT_ENTER_PROXIMITY: CGEventField = 38;
pub const EVENT_TARGET_PROCESS_SERIAL_NUMBER: CGEventField = 39;
pub const EVENT_TARGET_UNIX_PROCESS_ID: CGEventField = 40;
pub const EVENT_SOURCE_UNIX_PROCESS_ID: CGEventField = 41;
pub const EVENT_SOURCE_USER_DATA: CGEventField = 42;
pub const EVENT_SOURCE_USER_ID: CGEventField = 43;
pub const EVENT_SOURCE_GROUP_ID: CGEventField = 44;
pub const EVENT_SOURCE_STATE_ID: CGEventField = 45;
pub const SCROLL_WHEEL_EVENT_IS_CONTINUOUS: CGEventField = 88;
pub const MOUSE_EVENT_WINDOW_UNDER_MOUSE_POINTER: CGEventField = 91;
pub const MOUSE_EVENT_WINDOW_UNDER_MOUSE_POINTER_THAT_CAN_HANDLE_THIS_EVENT: CGEventField = 92;
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub enum CGMouseButton {
Left,
Right,
Center,
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub enum CGEventTapLocation {
HID,
Session,
AnnotatedSession,
}
#[repr(u32)]
#[derive(Clone, Copy, Debug)]
pub enum CGEventTapPlacement {
HeadInsertEventTap = 0,
TailAppendEventTap,
}
#[repr(u32)]
#[derive(Clone, Copy, Debug)]
pub enum CGEventTapOptions {
Default = 0x00000000,
ListenOnly = 0x00000001,
}
pub type CGEventMask = u64;
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: crate::sys::CGEventRef,
user_info: *const c_void,
) -> 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,
) -> 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()
}
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 {
let _ = Box::from_raw(cbr);
Err(())
}
}
}
pub fn enable(&self) {
unsafe { CGEventTapEnable(self.mach_port.as_concrete_TypeRef(), true) }
}
}
foreign_type! {
#[doc(hidden)]
pub unsafe type CGEvent {
type CType = crate::sys::CGEvent;
fn drop = |p| CFRelease(p as *mut _);
fn clone = |p| CFRetain(p as *const _) as *mut _;
}
}
impl CGEvent {
pub fn type_id() -> CFTypeID {
unsafe { CGEventGetTypeID() }
}
pub fn new(source: CGEventSource) -> Result<CGEvent, ()> {
unsafe {
let event_ref = CGEventCreate(source.as_ptr());
if !event_ref.is_null() {
Ok(Self::from_ptr(event_ref))
} else {
Err(())
}
}
}
pub fn new_keyboard_event(
source: CGEventSource,
keycode: CGKeyCode,
keydown: bool,
) -> Result<CGEvent, ()> {
unsafe {
let event_ref = CGEventCreateKeyboardEvent(source.as_ptr(), keycode, keydown);
if !event_ref.is_null() {
Ok(Self::from_ptr(event_ref))
} else {
Err(())
}
}
}
pub fn new_mouse_event(
source: CGEventSource,
mouse_type: CGEventType,
mouse_cursor_position: CGPoint,
mouse_button: CGMouseButton,
) -> Result<CGEvent, ()> {
unsafe {
let event_ref = CGEventCreateMouseEvent(
source.as_ptr(),
mouse_type,
mouse_cursor_position,
mouse_button,
);
if !event_ref.is_null() {
Ok(Self::from_ptr(event_ref))
} else {
Err(())
}
}
}
#[cfg(feature = "highsierra")]
pub fn new_scroll_event(
source: CGEventSource,
units: CGScrollEventUnit,
wheel_count: u32,
wheel1: i32,
wheel2: i32,
wheel3: i32,
) -> Result<CGEvent, ()> {
unsafe {
let event_ref = CGEventCreateScrollWheelEvent2(
source.as_ptr(),
units,
wheel_count,
wheel1,
wheel2,
wheel3,
);
if !event_ref.is_null() {
Ok(Self::from_ptr(event_ref))
} else {
Err(())
}
}
}
pub fn post(&self, tap_location: CGEventTapLocation) {
unsafe {
CGEventPost(tap_location, self.as_ptr());
}
}
pub fn post_from_tap(&self, tap_proxy: CGEventTapProxy) {
unsafe {
CGEventTapPostEvent(tap_proxy, self.as_ptr());
}
}
pub fn location(&self) -> CGPoint {
unsafe { CGEventGetLocation(self.as_ptr()) }
}
#[cfg(feature = "elcapitan")]
pub fn post_to_pid(&self, pid: libc::pid_t) {
unsafe {
CGEventPostToPid(pid, self.as_ptr());
}
}
pub fn set_flags(&self, flags: CGEventFlags) {
unsafe {
CGEventSetFlags(self.as_ptr(), flags);
}
}
pub fn get_flags(&self) -> CGEventFlags {
unsafe { CGEventGetFlags(self.as_ptr()) }
}
pub fn set_type(&self, event_type: CGEventType) {
unsafe {
CGEventSetType(self.as_ptr(), event_type);
}
}
pub fn get_type(&self) -> CGEventType {
unsafe { CGEventGetType(self.as_ptr()) }
}
pub fn set_string_from_utf16_unchecked(&self, buf: &[u16]) {
let buflen = buf.len() as libc::c_ulong;
unsafe {
CGEventKeyboardSetUnicodeString(self.as_ptr(), buflen, buf.as_ptr());
}
}
pub fn set_string(&self, string: &str) {
let buf: Vec<u16> = string.encode_utf16().collect();
self.set_string_from_utf16_unchecked(&buf);
}
pub fn get_integer_value_field(&self, field: CGEventField) -> i64 {
unsafe { CGEventGetIntegerValueField(self.as_ptr(), field) }
}
pub fn set_integer_value_field(&self, field: CGEventField, value: i64) {
unsafe { CGEventSetIntegerValueField(self.as_ptr(), field, value) }
}
pub fn get_double_value_field(&self, field: CGEventField) -> f64 {
unsafe { CGEventGetDoubleValueField(self.as_ptr(), field) }
}
pub fn set_double_value_field(&self, field: CGEventField, value: f64) {
unsafe { CGEventSetDoubleValueField(self.as_ptr(), field, value) }
}
}
#[cfg_attr(feature = "link", link(name = "CoreGraphics", kind = "framework"))]
extern "C" {
fn CGEventGetTypeID() -> CFTypeID;
fn CGEventCreate(source: crate::sys::CGEventSourceRef) -> crate::sys::CGEventRef;
fn CGEventCreateKeyboardEvent(
source: crate::sys::CGEventSourceRef,
keycode: CGKeyCode,
keydown: bool,
) -> crate::sys::CGEventRef;
fn CGEventCreateMouseEvent(
source: crate::sys::CGEventSourceRef,
mouseType: CGEventType,
mouseCursorPosition: CGPoint,
mouseButton: CGMouseButton,
) -> crate::sys::CGEventRef;
#[cfg(feature = "highsierra")]
fn CGEventCreateScrollWheelEvent2(
source: crate::sys::CGEventSourceRef,
units: CGScrollEventUnit,
wheelCount: u32,
wheel1: i32,
wheel2: i32,
wheel3: i32,
) -> crate::sys::CGEventRef;
fn CGEventPost(tapLocation: CGEventTapLocation, event: crate::sys::CGEventRef);
fn CGEventTapPostEvent(tapProxy: CGEventTapProxy, event: crate::sys::CGEventRef);
#[cfg(feature = "elcapitan")]
fn CGEventPostToPid(pid: libc::pid_t, event: crate::sys::CGEventRef);
fn CGEventSetFlags(event: crate::sys::CGEventRef, flags: CGEventFlags);
fn CGEventGetFlags(event: crate::sys::CGEventRef) -> CGEventFlags;
fn CGEventGetLocation(event: crate::sys::CGEventRef) -> CGPoint;
fn CGEventSetType(event: crate::sys::CGEventRef, eventType: CGEventType);
fn CGEventGetType(event: crate::sys::CGEventRef) -> CGEventType;
fn CGEventKeyboardSetUnicodeString(
event: crate::sys::CGEventRef,
length: libc::c_ulong,
string: *const u16,
);
fn CGEventGetIntegerValueField(event: crate::sys::CGEventRef, field: CGEventField) -> i64;
fn CGEventSetIntegerValueField(event: crate::sys::CGEventRef, field: CGEventField, value: i64);
fn CGEventGetDoubleValueField(event: crate::sys::CGEventRef, field: CGEventField) -> f64;
fn CGEventSetDoubleValueField(event: crate::sys::CGEventRef, field: CGEventField, value: f64);
fn CGEventTapCreate(
tap: CGEventTapLocation,
place: CGEventTapPlacement,
options: CGEventTapOptions,
eventsOfInterest: CGEventMask,
callback: CGEventTapCallBackInternal,
userInfo: *const c_void,
) -> CFMachPortRef;
fn CGEventTapEnable(tap: CFMachPortRef, enable: bool);
}