1use std::mem::ManuallyDrop;
2
3use core_foundation::{
4 base::{kCFAllocatorDefault, CFAllocatorRef, CFTypeID, TCFType},
5 data::{CFData, CFDataRef},
6 declare_TCFType, impl_CFTypeDescription, impl_TCFType,
7 mach_port::{CFMachPort, CFMachPortRef},
8};
9use libc::{c_double, c_ulong, c_void, pid_t};
10#[cfg(feature = "objc")]
11use objc2::encode::{Encoding, RefEncode};
12
13use crate::{error::CGError, geometry::CGPoint};
14pub use crate::{event_source::CGEventSource, event_types::*, remote_operation::CGKeyCode};
15
16extern "C" {
17 pub fn CGEventGetTypeID() -> CFTypeID;
18 pub fn CGEventCreate(source: CGEventSourceRef) -> CGEventRef;
19 pub fn CGEventCreateData(allocator: CFAllocatorRef, event: CGEventRef) -> CFDataRef;
20 pub fn CGEventCreateFromData(allocator: CFAllocatorRef, data: CFDataRef) -> CGEventRef;
21 pub fn CGEventCreateMouseEvent(
22 source: CGEventSourceRef,
23 mouseType: CGEventType,
24 mouseCursorPosition: CGPoint,
25 mouseButton: CGMouseButton,
26 ) -> CGEventRef;
27 pub fn CGEventCreateKeyboardEvent(source: CGEventSourceRef, virtualKey: CGKeyCode, keydown: bool) -> CGEventRef;
28 pub fn CGEventCreateScrollWheelEvent2(
29 source: CGEventSourceRef,
30 units: CGScrollEventUnit,
31 wheelCount: u32,
32 wheel1: i32,
33 wheel2: i32,
34 wheel3: i32,
35 ) -> CGEventRef;
36 pub fn CGEventCreateCopy(event: CGEventRef) -> CGEventRef;
37 pub fn CGEventCreateSourceFromEvent(event: CGEventRef) -> CGEventSourceRef;
38 pub fn CGEventSetSource(event: CGEventRef, source: CGEventSourceRef);
39 pub fn CGEventGetType(event: CGEventRef) -> CGEventType;
40 pub fn CGEventSetType(event: CGEventRef, type_: CGEventType);
41 pub fn CGEventGetTimestamp(event: CGEventRef) -> CGEventTimestamp;
42 pub fn CGEventSetTimestamp(event: CGEventRef, timestamp: CGEventTimestamp);
43 pub fn CGEventGetLocation(event: CGEventRef) -> CGPoint;
44 pub fn CGEventGetUnflippedLocation(event: CGEventRef) -> CGPoint;
45 pub fn CGEventSetLocation(event: CGEventRef, location: CGPoint);
46 pub fn CGEventGetFlags(event: CGEventRef) -> CGEventFlags;
47 pub fn CGEventSetFlags(event: CGEventRef, flags: CGEventFlags);
48 pub fn CGEventKeyboardGetUnicodeString(
49 event: CGEventRef,
50 maxStringLength: c_ulong,
51 actualStringLength: *mut c_ulong,
52 unicodeString: *mut u16,
53 ) -> bool;
54 pub fn CGEventKeyboardSetUnicodeString(event: CGEventRef, stringLength: c_ulong, unicodeString: *const u16);
55 pub fn CGEventGetIntegerValueField(event: CGEventRef, field: CGEventField) -> i64;
56 pub fn CGEventSetIntegerValueField(event: CGEventRef, field: CGEventField, value: i64);
57 pub fn CGEventGetDoubleValueField(event: CGEventRef, field: CGEventField) -> c_double;
58 pub fn CGEventSetDoubleValueField(event: CGEventRef, field: CGEventField, value: c_double);
59
60 pub fn CGEventTapCreate(
61 tap: CGEventTapLocation,
62 place: CGEventTapPlacement,
63 options: CGEventTapOptions,
64 eventsOfInterest: CGEventMask,
65 callback: CGEventTapCallBack,
66 userInfo: *mut c_void,
67 ) -> CFMachPortRef;
68 pub fn CGEventTapCreateForPSN(
69 processSerialNumber: *const c_void,
70 place: CGEventTapPlacement,
71 options: CGEventTapOptions,
72 eventsOfInterest: CGEventMask,
73 callback: CGEventTapCallBack,
74 userInfo: *mut c_void,
75 ) -> CFMachPortRef;
76 pub fn CGEventTapCreateForPid(
77 pid: pid_t,
78 place: CGEventTapPlacement,
79 options: CGEventTapOptions,
80 eventsOfInterest: CGEventMask,
81 callback: CGEventTapCallBack,
82 userInfo: *mut c_void,
83 ) -> CFMachPortRef;
84 pub fn CGEventTapEnable(tap: CFMachPortRef, enable: bool);
85 pub fn CGEventTapIsEnabled(tap: CFMachPortRef) -> bool;
86 pub fn CGEventTapPostEvent(proxy: CGEventTapProxy, event: CGEventRef);
87
88 pub fn CGEventPost(tap: CGEventTapLocation, event: CGEventRef);
89 pub fn CGEventPostToPSN(processSerialNumber: *mut c_void, event: CGEventRef);
90 pub fn CGEventPostToPid(pid: pid_t, event: CGEventRef);
91 pub fn CGGetEventTapList(maxNumberOfTaps: u32, tapList: *mut CGEventTapInformation, eventTapCount: *mut u32) -> CGError;
92 pub fn CGPreflightListenEventAccess() -> bool;
93 pub fn CGRequestListenEventAccess() -> bool;
94 pub fn CGPreflightPostEventAccess() -> bool;
95 pub fn CGRequestPostEventAccess() -> bool;
96}
97
98#[macro_export]
99macro_rules! CGEventMaskBit {
100 ($event_type:expr) => {
101 1 << $event_type as CGEventMask
102 };
103}
104
105pub type CGEventTapCallbackBox<'a> = Box<dyn FnMut(CGEventTapProxy, CGEventType, &CGEvent) -> Option<CGEvent> + 'a>;
106
107pub struct CGEventTap<'a> {
108 pub mach_port: CFMachPort,
109 pub callback: Box<dyn FnMut(CGEventTapProxy, CGEventType, &CGEvent) -> Option<CGEvent> + 'a>,
110}
111
112impl<'a> CGEventTap<'a> {
113 pub fn new<F>(
114 tap: CGEventTapLocation,
115 place: CGEventTapPlacement,
116 options: CGEventTapOptions,
117 events_of_interest: Vec<CGEventType>,
118 closure: F,
119 ) -> Result<CGEventTap<'a>, ()>
120 where
121 F: FnMut(CGEventTapProxy, CGEventType, &CGEvent) -> Option<CGEvent> + 'a,
122 {
123 let event_mask: CGEventMask = events_of_interest.iter().fold(CGEventType::Null as CGEventMask, |mask, &etype| mask | CGEventMaskBit!(etype));
124 let cb = Box::into_raw(Box::new(Box::new(closure) as CGEventTapCallbackBox));
125 unsafe {
126 let event_tap = CGEventTapCreate(tap, place, options, event_mask, callback, cb as *mut c_void);
127 if event_tap.is_null() {
128 let _ = Box::from_raw(cb);
129 return Err(());
130 } else {
131 return Ok(Self {
132 mach_port: CFMachPort::wrap_under_create_rule(event_tap),
133 callback: Box::from_raw(cb),
134 });
135 }
136 }
137
138 extern "C" fn callback(proxy: CGEventTapProxy, etype: CGEventType, event: CGEventRef, user_info: *mut c_void) -> CGEventRef {
139 unsafe {
140 let callback = user_info as *mut CGEventTapCallbackBox;
141 let event = CGEvent::wrap_under_get_rule(event);
142 let new_event = (*callback)(proxy, etype, &event);
143 let event = new_event.unwrap_or(event);
144 ManuallyDrop::new(event).as_concrete_TypeRef()
145 }
146 }
147 }
148
149 pub fn enable(&self, enable: bool) {
150 unsafe { CGEventTapEnable(self.mach_port.as_concrete_TypeRef(), enable) }
151 }
152
153 pub fn is_enabled(&self) -> bool {
154 unsafe { CGEventTapIsEnabled(self.mach_port.as_concrete_TypeRef()) }
155 }
156}
157
158declare_TCFType!(CGEvent, CGEventRef);
159impl_TCFType!(CGEvent, CGEventRef, CGEventGetTypeID);
160impl_CFTypeDescription!(CGEvent);
161
162impl CGEvent {
163 pub fn new(source: CGEventSource) -> Option<CGEvent> {
164 unsafe {
165 let event = CGEventCreate(source.as_concrete_TypeRef());
166 if event.is_null() {
167 None
168 } else {
169 Some(TCFType::wrap_under_create_rule(event))
170 }
171 }
172 }
173
174 pub fn new_data(&self) -> Option<CFData> {
175 unsafe {
176 let data = CGEventCreateData(kCFAllocatorDefault, self.as_concrete_TypeRef());
177 if data.is_null() {
178 None
179 } else {
180 Some(TCFType::wrap_under_create_rule(data))
181 }
182 }
183 }
184
185 pub fn new_mouse_event(source: CGEventSource, mouse_type: CGEventType, cursor_position: CGPoint, button: CGMouseButton) -> Option<CGEvent> {
186 unsafe {
187 let event = CGEventCreateMouseEvent(source.as_concrete_TypeRef(), mouse_type, cursor_position, button);
188 if event.is_null() {
189 None
190 } else {
191 Some(TCFType::wrap_under_create_rule(event))
192 }
193 }
194 }
195
196 pub fn new_keyboard_event(source: CGEventSource, keycode: CGKeyCode, keydown: bool) -> Option<CGEvent> {
197 unsafe {
198 let event = CGEventCreateKeyboardEvent(source.as_concrete_TypeRef(), keycode, keydown);
199 if event.is_null() {
200 None
201 } else {
202 Some(TCFType::wrap_under_create_rule(event))
203 }
204 }
205 }
206
207 pub fn new_scroll_event(
208 source: CGEventSource,
209 units: CGScrollEventUnit,
210 wheel_count: u32,
211 wheel1: i32,
212 wheel2: i32,
213 wheel3: i32,
214 ) -> Option<CGEvent> {
215 unsafe {
216 let event = CGEventCreateScrollWheelEvent2(source.as_concrete_TypeRef(), units, wheel_count, wheel1, wheel2, wheel3);
217 if event.is_null() {
218 None
219 } else {
220 Some(TCFType::wrap_under_create_rule(event))
221 }
222 }
223 }
224
225 pub fn new_copy(&self) -> Option<CGEvent> {
226 unsafe {
227 let event = CGEventCreateCopy(self.as_concrete_TypeRef());
228 if event.is_null() {
229 None
230 } else {
231 Some(TCFType::wrap_under_create_rule(event))
232 }
233 }
234 }
235
236 pub fn new_source(&self) -> Option<CGEventSource> {
237 unsafe {
238 let source = CGEventCreateSourceFromEvent(self.as_concrete_TypeRef());
239 if source.is_null() {
240 None
241 } else {
242 Some(TCFType::wrap_under_create_rule(source))
243 }
244 }
245 }
246
247 pub fn from_data(data: CFData) -> Option<CGEvent> {
248 unsafe {
249 let event = CGEventCreateFromData(kCFAllocatorDefault, data.as_concrete_TypeRef());
250 if event.is_null() {
251 None
252 } else {
253 Some(TCFType::wrap_under_create_rule(event))
254 }
255 }
256 }
257
258 pub fn set_source(&self, source: CGEventSource) {
259 unsafe {
260 CGEventSetSource(self.as_concrete_TypeRef(), source.as_concrete_TypeRef());
261 }
262 }
263
264 pub fn get_type(&self) -> CGEventType {
265 unsafe { CGEventGetType(self.as_concrete_TypeRef()) }
266 }
267
268 pub fn set_type(&self, event_type: CGEventType) {
269 unsafe {
270 CGEventSetType(self.as_concrete_TypeRef(), event_type);
271 }
272 }
273
274 pub fn get_timestamp(&self) -> CGEventTimestamp {
275 unsafe { CGEventGetTimestamp(self.as_concrete_TypeRef()) }
276 }
277
278 pub fn set_timestamp(&self, timestamp: CGEventTimestamp) {
279 unsafe {
280 CGEventSetTimestamp(self.as_concrete_TypeRef(), timestamp);
281 }
282 }
283
284 pub fn get_location(&self) -> CGPoint {
285 unsafe { CGEventGetLocation(self.as_concrete_TypeRef()) }
286 }
287
288 pub fn get_unflipped_location(&self) -> CGPoint {
289 unsafe { CGEventGetUnflippedLocation(self.as_concrete_TypeRef()) }
290 }
291
292 pub fn set_location(&self, location: CGPoint) {
293 unsafe {
294 CGEventSetLocation(self.as_concrete_TypeRef(), location);
295 }
296 }
297
298 pub fn get_flags(&self) -> CGEventFlags {
299 unsafe { CGEventGetFlags(self.as_concrete_TypeRef()) }
300 }
301
302 pub fn set_flags(&self, flags: CGEventFlags) {
303 unsafe {
304 CGEventSetFlags(self.as_concrete_TypeRef(), flags);
305 }
306 }
307
308 pub fn get_integer_value_field(&self, field: CGEventField) -> i64 {
309 unsafe { CGEventGetIntegerValueField(self.as_concrete_TypeRef(), field) }
310 }
311
312 pub fn set_integer_value_field(&self, field: CGEventField, value: i64) {
313 unsafe { CGEventSetIntegerValueField(self.as_concrete_TypeRef(), field, value) }
314 }
315
316 pub fn get_double_value_field(&self, field: CGEventField) -> f64 {
317 unsafe { CGEventGetDoubleValueField(self.as_concrete_TypeRef(), field) }
318 }
319
320 pub fn set_double_value_field(&self, field: CGEventField, value: f64) {
321 unsafe { CGEventSetDoubleValueField(self.as_concrete_TypeRef(), field, value) }
322 }
323
324 pub fn set_string(&self, string: &str) {
325 let utf16: Vec<u16> = string.encode_utf16().collect();
326 unsafe {
327 CGEventKeyboardSetUnicodeString(self.as_concrete_TypeRef(), utf16.len() as c_ulong, utf16.as_ptr());
328 }
329 }
330
331 pub fn post(&self, tap: CGEventTapLocation) {
332 unsafe {
333 CGEventPost(tap, self.as_concrete_TypeRef());
334 }
335 }
336
337 pub fn post_to_pid(&self, pid: pid_t) {
338 unsafe {
339 CGEventPostToPid(pid, self.as_concrete_TypeRef());
340 }
341 }
342
343 pub fn post_from_tap(&self, proxy: CGEventTapProxy) {
344 unsafe {
345 CGEventTapPostEvent(proxy, self.as_concrete_TypeRef());
346 }
347 }
348}
349
350#[cfg(feature = "objc")]
351unsafe impl RefEncode for __CGEvent {
352 const ENCODING_REF: Encoding = Encoding::Pointer(&Encoding::Struct("CGEvent", &[]));
353}