From 1d652dd5db9124aba79e4f6292c3b292ee5403d7 Mon Sep 17 00:00:00 2001 From: Wilson Giese Date: Tue, 18 Oct 2016 23:03:53 -0700 Subject: [PATCH 1/5] Add CGEvent and CGEventSource Signed-off-by: Wilson Giese --- src/event.rs | 173 ++++++++++++++++++++++++++++++++++++++++++++ src/event_source.rs | 97 +++++++++++++++++++++++++ src/lib.rs | 3 +- 3 files changed, 272 insertions(+), 1 deletion(-) create mode 100644 src/event.rs create mode 100644 src/event_source.rs diff --git a/src/event.rs b/src/event.rs new file mode 100644 index 0000000..ff710b4 --- /dev/null +++ b/src/event.rs @@ -0,0 +1,173 @@ +use core_foundation::base::{CFRelease, CFRetain, CFTypeID, CFTypeRef, TCFType}; +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-308/IOHIDSystem/IOKit/hidsystem/IOLLEvent.h) +#[repr(C)] +pub enum CGEventFlags { + // Device-independent modifier key bits. + AlphaShift = 0x00010000, + Shift = 0x00020000, + Control = 0x00040000, + Alternate = 0x00080000, + Command = 0x00100000, + + // Special key identifiers. + Help = 0x00400000, + SecondaryFn = 0x00800000, + + // Identifies key events from numeric keypad area on extended keyboards. + NumericPad = 0x00200000, + + // Indicates if mouse/pen movement events are not being coalesced + NonCoalesced = 0x00000100, +} + +/// Possible tapping points for events. +#[repr(C)] +pub enum CGEventTapLocation { + HIDEventTap, + SessionEventTap, + AnnotatedSessionEventTap, +} + +#[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 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, keycode: CGKeyCode, keydown: bool) -> Result { + 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 post(&self, tapLocation: CGEventTapLocation) { + unsafe { + CGEventPost(tapLocation, self.as_concrete_TypeRef()); + } + } + + 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()) + } + } +} + +#[link(name = "ApplicationServices", kind = "framework")] +extern { + /// Return the type identifier for the opaque type `CGEventRef'. + fn CGEventGetTypeID() -> CFTypeID; + + /// 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; + + /// 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); + + /// 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; +} diff --git a/src/event_source.rs b/src/event_source.rs new file mode 100644 index 0000000..5a1ad34 --- /dev/null +++ b/src/event_source.rs @@ -0,0 +1,97 @@ +use core_foundation::base::{CFRelease, CFRetain, CFTypeID, CFTypeRef, TCFType}; + +use libc; +use std::mem; +use std::ptr; + +/// Possible source states of an event source. +#[repr(C)] +pub enum CGEventSourceStateID { + Private = -1, + CombinedSessionState = 0, + HIDSystemState = 1, +} + +#[repr(C)] +pub struct __CGEventSource; + +pub type CGEventSourceRef = *const __CGEventSource; + +pub struct CGEventSource { + obj: CGEventSourceRef, +} + +impl Clone for CGEventSource { + #[inline] + fn clone(&self) -> CGEventSource { + unsafe { + TCFType::wrap_under_get_rule(self.obj) + } + } +} + +impl Drop for CGEventSource { + fn drop(&mut self) { + unsafe { + let ptr = self.as_CFTypeRef(); + assert!(ptr != ptr::null()); + CFRelease(ptr); + } + } +} + +impl TCFType for CGEventSource { + #[inline] + fn as_concrete_TypeRef(&self) -> CGEventSourceRef { + self.obj + } + + #[inline] + unsafe fn wrap_under_get_rule(reference: CGEventSourceRef) -> CGEventSource { + let reference: CGEventSourceRef = 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: CGEventSourceRef) -> CGEventSource { + CGEventSource { + obj: obj, + } + } + + #[inline] + fn type_id() -> CFTypeID { + unsafe { + CGEventSourceGetTypeID() + } + } +} + +impl CGEventSource { + pub fn new(stateID: CGEventSourceStateID) -> Result { + unsafe { + let event_source_ref = CGEventSourceCreate(stateID); + if event_source_ref != ptr::null() { + Ok(TCFType::wrap_under_create_rule(event_source_ref)) + } else { + Err(()) + } + } + } +} + +#[link(name = "ApplicationServices", kind = "framework")] +extern { + /// Return the type identifier for the opaque type `CGEventSourceRef'. + fn CGEventSourceGetTypeID() -> CFTypeID; + + /// Return a Quartz event source created with a specified source state. + fn CGEventSourceCreate(stateID: CGEventSourceStateID) -> CGEventSourceRef; +} diff --git a/src/lib.rs b/src/lib.rs index a162a1c..fed16b2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,7 +16,8 @@ pub mod color_space; pub mod context; pub mod data_provider; pub mod display; +pub mod event; +pub mod event_source; pub mod font; pub mod geometry; pub mod private; - From 438e083fc31ce4b6b1ecf9cd4db2da694fbddd89 Mon Sep 17 00:00:00 2001 From: Wilson Giese Date: Sun, 23 Oct 2016 23:36:13 -0700 Subject: [PATCH 2/5] Derive clone, copy, debug for enums --- src/event.rs | 2 ++ src/event_source.rs | 1 + 2 files changed, 3 insertions(+) diff --git a/src/event.rs b/src/event.rs index ff710b4..4254055 100644 --- a/src/event.rs +++ b/src/event.rs @@ -11,6 +11,7 @@ pub type CGKeyCode = libc::uint16_t; /// /// [Ref] (http://opensource.apple.com//source/IOHIDFamily/IOHIDFamily-308/IOHIDSystem/IOKit/hidsystem/IOLLEvent.h) #[repr(C)] +#[derive(Clone, Copy, Debug)] pub enum CGEventFlags { // Device-independent modifier key bits. AlphaShift = 0x00010000, @@ -32,6 +33,7 @@ pub enum CGEventFlags { /// Possible tapping points for events. #[repr(C)] +#[derive(Clone, Copy, Debug)] pub enum CGEventTapLocation { HIDEventTap, SessionEventTap, diff --git a/src/event_source.rs b/src/event_source.rs index 5a1ad34..16fa86b 100644 --- a/src/event_source.rs +++ b/src/event_source.rs @@ -6,6 +6,7 @@ use std::ptr; /// Possible source states of an event source. #[repr(C)] +#[derive(Clone, Copy, Debug)] pub enum CGEventSourceStateID { Private = -1, CombinedSessionState = 0, From 179f4747a9c2ca24219684276e0fdb220997dbaf Mon Sep 17 00:00:00 2001 From: Wilson Giese Date: Thu, 27 Oct 2016 17:38:48 -0700 Subject: [PATCH 3/5] Add MouseEvent --- src/event.rs | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/src/event.rs b/src/event.rs index 4254055..7e016ba 100644 --- a/src/event.rs +++ b/src/event.rs @@ -1,4 +1,5 @@ use core_foundation::base::{CFRelease, CFRetain, CFTypeID, CFTypeRef, TCFType}; +use geometry::CGPoint; use event_source::{CGEventSource,CGEventSourceRef}; use libc; @@ -9,7 +10,7 @@ pub type CGKeyCode = libc::uint16_t; /// Flags for events /// -/// [Ref] (http://opensource.apple.com//source/IOHIDFamily/IOHIDFamily-308/IOHIDSystem/IOKit/hidsystem/IOLLEvent.h) +/// [Ref](http://opensource.apple.com/source/IOHIDFamily/IOHIDFamily-700/IOHIDSystem/IOKit/hidsystem/IOLLEvent.h) #[repr(C)] #[derive(Clone, Copy, Debug)] pub enum CGEventFlags { @@ -31,6 +32,51 @@ pub enum CGEventFlags { NonCoalesced = 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 = 1 << 0, + + // Mouse events. + LeftMouseDown = 1 << 1, + LeftMouseUp = 1 << 2, + RightMouseDown = 1 << 3, + RightMouseUp = 1 << 4, + MouseMoved = 1 << 5, + LeftMouseDragged = 1 << 6, + RightMouseDragged = 1 << 7, + + // Keyboard events. + KeyDown = 1 << 10, + KeyUp = 1 << 11, + FlagsChanged = 1 << 12, + + // Specialized control devices. + ScrollWheel = 1 << 22, + TabletPointer = 1 << 23, + TabletProximity = 1 << 24, + OtherMouseDown = 1 << 25, + OtherMouseUp = 1 << 26, + OtherMouseDragged = 1 << 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)] @@ -157,6 +203,22 @@ extern { 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 From a70192dae8e8ec21bc00ecd6f5c497baa7e8ee9a Mon Sep 17 00:00:00 2001 From: Wilson Giese Date: Thu, 27 Oct 2016 17:54:35 -0700 Subject: [PATCH 4/5] New for keyboard & mouse events --- src/event.rs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/event.rs b/src/event.rs index 7e016ba..3ddac10 100644 --- a/src/event.rs +++ b/src/event.rs @@ -149,7 +149,11 @@ impl TCFType for CGEvent { } impl CGEvent { - pub fn new(source: CGEventSource, keycode: CGKeyCode, keydown: bool) -> Result { + pub fn new_keyboard_event( + source: CGEventSource, + keycode: CGKeyCode, + keydown: bool + ) -> Result { unsafe { let event_ref = CGEventCreateKeyboardEvent(source.as_concrete_TypeRef(), keycode, keydown); if event_ref != ptr::null() { @@ -160,6 +164,23 @@ impl CGEvent { } } + pub fn new_mouse_event( + source: CGEventSource, + mouseType: CGEventType, + mouseCursorPosition: CGPoint, + mouseButton: CGMouseButton + ) -> Result { + unsafe { + let event_ref = CGEventCreateMouseEvent(source.as_concrete_TypeRef(), mouseType, + mouseCursorPosition, mouseButton); + if event_ref != ptr::null() { + Ok(TCFType::wrap_under_create_rule(event_ref)) + } else { + Err(()) + } + } + } + pub fn post(&self, tapLocation: CGEventTapLocation) { unsafe { CGEventPost(tapLocation, self.as_concrete_TypeRef()); From b88ff77b0dfa07617110b45d692225c8fdcae844 Mon Sep 17 00:00:00 2001 From: Wilson Giese Date: Fri, 28 Oct 2016 13:11:11 -0700 Subject: [PATCH 5/5] Style --- src/event.rs | 20 ++++++++++---------- src/event_source.rs | 5 ++--- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/event.rs b/src/event.rs index 3ddac10..6f4ed71 100644 --- a/src/event.rs +++ b/src/event.rs @@ -81,9 +81,9 @@ pub enum CGMouseButton { #[repr(C)] #[derive(Clone, Copy, Debug)] pub enum CGEventTapLocation { - HIDEventTap, - SessionEventTap, - AnnotatedSessionEventTap, + HID, + Session, + AnnotatedSession, } #[repr(C)] @@ -166,13 +166,13 @@ impl CGEvent { pub fn new_mouse_event( source: CGEventSource, - mouseType: CGEventType, - mouseCursorPosition: CGPoint, - mouseButton: CGMouseButton + mouse_type: CGEventType, + mouse_cursor_position: CGPoint, + mouse_button: CGMouseButton ) -> Result { unsafe { - let event_ref = CGEventCreateMouseEvent(source.as_concrete_TypeRef(), mouseType, - mouseCursorPosition, mouseButton); + 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 { @@ -181,9 +181,9 @@ impl CGEvent { } } - pub fn post(&self, tapLocation: CGEventTapLocation) { + pub fn post(&self, tap_location: CGEventTapLocation) { unsafe { - CGEventPost(tapLocation, self.as_concrete_TypeRef()); + CGEventPost(tap_location, self.as_concrete_TypeRef()); } } diff --git a/src/event_source.rs b/src/event_source.rs index 16fa86b..32bbaef 100644 --- a/src/event_source.rs +++ b/src/event_source.rs @@ -1,6 +1,5 @@ use core_foundation::base::{CFRelease, CFRetain, CFTypeID, CFTypeRef, TCFType}; -use libc; use std::mem; use std::ptr; @@ -76,9 +75,9 @@ impl TCFType for CGEventSource { } impl CGEventSource { - pub fn new(stateID: CGEventSourceStateID) -> Result { + pub fn new(state_id: CGEventSourceStateID) -> Result { unsafe { - let event_source_ref = CGEventSourceCreate(stateID); + let event_source_ref = CGEventSourceCreate(state_id); if event_source_ref != ptr::null() { Ok(TCFType::wrap_under_create_rule(event_source_ref)) } else {