diff --git a/src/event.rs b/src/event.rs new file mode 100644 index 0000000..6f4ed71 --- /dev/null +++ b/src/event.rs @@ -0,0 +1,258 @@ +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) +#[repr(C)] +#[derive(Clone, Copy, Debug)] +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, +} + +/// 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)] +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 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_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() { + 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 { + 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 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; + + /// 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); + + /// 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..32bbaef --- /dev/null +++ b/src/event_source.rs @@ -0,0 +1,97 @@ +use core_foundation::base::{CFRelease, CFRetain, CFTypeID, CFTypeRef, TCFType}; + +use std::mem; +use std::ptr; + +/// Possible source states of an event source. +#[repr(C)] +#[derive(Clone, Copy, Debug)] +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(state_id: CGEventSourceStateID) -> Result { + unsafe { + let event_source_ref = CGEventSourceCreate(state_id); + 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; -