1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
use core_foundation::base::{CFRelease, CFRetain, CFTypeID, CFTypeRef, TCFType};
use geometry::CGPoint;
use event_source::{CGEventSource,CGEventSourceRef};
use libc;
use std::mem;
use std::ptr;
pub type CGKeyCode = libc::uint16_t;
/// Flags for events
///
/// [Ref](http://opensource.apple.com/source/IOHIDFamily/IOHIDFamily-700/IOHIDSystem/IOKit/hidsystem/IOLLEvent.h)
bitflags! {
pub struct CGEventFlags: u64 {
const CGEventFlagNull = 0;
// Device-independent modifier key bits.
const CGEventFlagAlphaShift = 0x00010000;
const CGEventFlagShift = 0x00020000;
const CGEventFlagControl = 0x00040000;
const CGEventFlagAlternate = 0x00080000;
const CGEventFlagCommand = 0x00100000;
// Special key identifiers.
const CGEventFlagHelp = 0x00400000;
const CGEventFlagSecondaryFn = 0x00800000;
// Identifies key events from numeric keypad area on extended keyboards.
const CGEventFlagNumericPad = 0x00200000;
// Indicates if mouse/pen movement events are not being coalesced
const CGEventFlagNonCoalesced = 0x00000100;
}
}
/// Constants that specify the different types of input events.
///
/// [Ref](http://opensource.apple.com/source/IOHIDFamily/IOHIDFamily-700/IOHIDSystem/IOKit/hidsystem/IOLLEvent.h)
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub enum CGEventType {
Null = 0,
// Mouse events.
LeftMouseDown = 1,
LeftMouseUp = 2,
RightMouseDown = 3,
RightMouseUp = 4,
MouseMoved = 5,
LeftMouseDragged = 6,
RightMouseDragged = 7,
// Keyboard events.
KeyDown = 10,
KeyUp = 11,
FlagsChanged = 12,
// Specialized control devices.
ScrollWheel = 22,
TabletPointer = 23,
TabletProximity = 24,
OtherMouseDown = 25,
OtherMouseUp = 26,
OtherMouseDragged = 27,
// Out of band event types. These are delivered to the event tap callback
// to notify it of unusual conditions that disable the event tap.
TapDisabledByTimeout = 0xFFFFFFFE,
TapDisabledByUserInput = 0xFFFFFFFF,
}
// Constants that specify buttons on a one, two, or three-button mouse.
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub enum CGMouseButton {
Left,
Right,
Center,
}
/// Possible tapping points for events.
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub enum CGEventTapLocation {
HID,
Session,
AnnotatedSession,
}
#[repr(C)]
pub struct __CGEvent;
pub type CGEventRef = *const __CGEvent;
pub struct CGEvent {
obj: CGEventRef,
}
impl Clone for CGEvent {
#[inline]
fn clone(&self) -> CGEvent {
unsafe {
TCFType::wrap_under_get_rule(self.obj)
}
}
}
impl Drop for CGEvent {
fn drop(&mut self) {
unsafe {
let ptr = self.as_CFTypeRef();
assert!(ptr != ptr::null());
CFRelease(ptr);
}
}
}
impl TCFType<CGEventRef> for CGEvent {
#[inline]
fn as_concrete_TypeRef(&self) -> CGEventRef {
self.obj
}
#[inline]
unsafe fn wrap_under_get_rule(reference: CGEventRef) -> CGEvent {
let reference: CGEventRef = mem::transmute(CFRetain(mem::transmute(reference)));
TCFType::wrap_under_create_rule(reference)
}
#[inline]
fn as_CFTypeRef(&self) -> CFTypeRef {
unsafe {
mem::transmute(self.as_concrete_TypeRef())
}
}
#[inline]
unsafe fn wrap_under_create_rule(obj: CGEventRef) -> CGEvent {
CGEvent {
obj: obj,
}
}
#[inline]
fn type_id() -> CFTypeID {
unsafe {
CGEventGetTypeID()
}
}
}
impl CGEvent {
pub fn new(source: CGEventSource) -> Result<CGEvent, ()> {
unsafe {
let event_ref = CGEventCreate(source.as_concrete_TypeRef());
if event_ref != ptr::null() {
Ok(TCFType::wrap_under_create_rule(event_ref))
} else {
Err(())
}
}
}
pub fn new_keyboard_event(
source: CGEventSource,
keycode: CGKeyCode,
keydown: bool
) -> Result<CGEvent, ()> {
unsafe {
let event_ref = CGEventCreateKeyboardEvent(source.as_concrete_TypeRef(), keycode, keydown);
if event_ref != ptr::null() {
Ok(TCFType::wrap_under_create_rule(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_concrete_TypeRef(), mouse_type,
mouse_cursor_position, mouse_button);
if event_ref != ptr::null() {
Ok(TCFType::wrap_under_create_rule(event_ref))
} else {
Err(())
}
}
}
pub fn post(&self, tap_location: CGEventTapLocation) {
unsafe {
CGEventPost(tap_location, self.as_concrete_TypeRef());
}
}
pub fn location(&self) -> CGPoint {
unsafe {
CGEventGetLocation(self.as_concrete_TypeRef())
}
}
#[cfg(feature = "elcapitan")]
pub fn post_to_pid(&self, pid: libc::pid_t) {
unsafe {
CGEventPostToPid(pid, self.as_concrete_TypeRef());
}
}
pub fn set_flags(&self, flags: CGEventFlags) {
unsafe {
CGEventSetFlags(self.as_concrete_TypeRef(), flags);
}
}
pub fn get_flags(&self) -> CGEventFlags {
unsafe {
CGEventGetFlags(self.as_concrete_TypeRef())
}
}
pub fn set_type(&self, event_type: CGEventType) {
unsafe {
CGEventSetType(self.as_concrete_TypeRef(), event_type);
}
}
pub fn get_type(&self) -> CGEventType {
unsafe {
CGEventGetType(self.as_concrete_TypeRef())
}
}
pub fn set_string_from_utf16_unchecked(&self, buf: &[u16]) {
let buflen = buf.len() as libc::c_ulong;
unsafe {
CGEventKeyboardSetUnicodeString(self.as_concrete_TypeRef(), 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);
}
}
#[link(name = "ApplicationServices", kind = "framework")]
extern {
/// Return the type identifier for the opaque type `CGEventRef'.
fn CGEventGetTypeID() -> CFTypeID;
/// Return a new event using the event source `source'. If `source' is NULL,
/// the default source is used.
fn CGEventCreate(source: CGEventSourceRef) -> CGEventRef;
/// Return a new keyboard event.
///
/// The event source may be taken from another event, or may be NULL. Based
/// on the virtual key code values entered, the appropriate key down, key up,
/// or flags changed events are generated.
///
/// All keystrokes needed to generate a character must be entered, including
/// SHIFT, CONTROL, OPTION, and COMMAND keys. For example, to produce a 'Z',
/// the SHIFT key must be down, the 'z' key must go down, and then the SHIFT
/// and 'z' key must be released:
fn CGEventCreateKeyboardEvent(source: CGEventSourceRef, keycode: CGKeyCode,
keydown: bool) -> CGEventRef;
/// Return a new mouse event.
///
/// The event source may be taken from another event, or may be NULL.
/// `mouseType' should be one of the mouse event types. `mouseCursorPosition'
/// should be the position of the mouse cursor in global coordinates.
/// `mouseButton' should be the button that's changing state; `mouseButton'
/// is ignored unless `mouseType' is one of `kCGEventOtherMouseDown',
/// `kCGEventOtherMouseDragged', or `kCGEventOtherMouseUp'.
///
/// The current implemementation of the event system supports a maximum of
/// thirty-two buttons. Mouse button 0 is the primary button on the mouse.
/// Mouse button 1 is the secondary mouse button (right). Mouse button 2 is
/// the center button, and the remaining buttons are in USB device order.
fn CGEventCreateMouseEvent(source: CGEventSourceRef, mouseType: CGEventType,
mouseCursorPosition: CGPoint, mouseButton: CGMouseButton) -> CGEventRef;
/// Post an event into the event stream at a specified location.
///
/// This function posts the specified event immediately before any event taps
/// instantiated for that location, and the event passes through any such
/// taps.
fn CGEventPost(tapLocation: CGEventTapLocation, event: CGEventRef);
#[cfg(feature = "elcapitan")]
/// Post an event to a specified process ID
fn CGEventPostToPid(pid: libc::pid_t, event: CGEventRef);
/// Set the event flags of an event.
fn CGEventSetFlags(event: CGEventRef, flags: CGEventFlags);
/// Return the event flags of an event.
fn CGEventGetFlags(event: CGEventRef) -> CGEventFlags;
/// Return the location of an event in global display coordinates.
/// CGPointZero is returned if event is not a valid CGEventRef.
fn CGEventGetLocation(event: CGEventRef) -> CGPoint;
/// Set the event type of an event.
fn CGEventSetType(event: CGEventRef, eventType: CGEventType);
/// Return the event type of an event (left mouse down, for example).
fn CGEventGetType(event: CGEventRef) -> CGEventType;
/// Set the Unicode string associated with a keyboard event.
///
/// By default, the system translates the virtual key code in a keyboard
/// event into a Unicode string based on the keyboard ID in the event
/// source. This function allows you to manually override this string.
/// Note that application frameworks may ignore the Unicode string in a
/// keyboard event and do their own translation based on the virtual
/// keycode and perceived event state.
fn CGEventKeyboardSetUnicodeString(event: CGEventRef,
length: libc::c_ulong,
string: *const u16);
}