#![allow(improper_ctypes)]
#![allow(non_snake_case)]
use std::fmt;
use std::ffi::CStr;
use std::os::raw;
use std::ptr;
use objc::runtime::{Class, Object};
use objc::{Encode, Encoding};
#[link(name = "Cocoa", kind = "framework")]
extern {
fn CFRelease(_: CFObjectRef);
fn CGEventPost(tap_location: raw::c_int, event: CFObjectRef);
fn CGEventCreateCopy(event: CFObjectRef) -> CFObject;
fn CGEventGetFlags(event: CFObjectRef) -> EventFlags;
fn CGEventSetFlags(event: CFObjectRef, flags: EventFlags);
}
#[macro_use]
mod macros;
pub mod app;
pub mod keyboard;
pub mod mouse;
pub mod screen;
pub mod wheel;
lazy_static! {
static ref NS_EVENT: &'static Class = Class::get("NSEvent").unwrap();
}
unsafe fn ns_url_encode_utf8(ns_url: Option<NSObject>) -> Option<String> {
ns_url.and_then(|url| {
ns_string_encode_utf8(msg_send![url.inner(), absoluteString])
})
}
unsafe fn ns_string_encode_utf8(ns_string: Option<NSObject>) -> Option<String> {
if let Some(s) = ns_string {
let s = CStr::from_ptr(msg_send![s.inner(), UTF8String]);
Some(s.to_string_lossy().into())
} else {
None
}
}
type CFObjectRef = ptr::NonNull<raw::c_void>;
type NSObjectRef = ptr::NonNull<Object>;
macro_rules! impl_object {
($obj:ident, $inner:ty, $($drop:tt)+) => {
#[repr(C)]
#[derive(PartialEq, Eq, Hash)]
struct $obj($inner);
impl Drop for $obj {
#[inline]
$($drop)+
}
impl fmt::Debug for $obj {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
unsafe impl Send for $obj {}
unsafe impl Sync for $obj {}
}
}
impl_object!(CFObject, CFObjectRef, fn drop(&mut self) {
unsafe { CFRelease(self.0) };
});
impl_object!(NSObject, NSObjectRef, fn drop(&mut self) {
let ptr = self.0.as_ptr();
unsafe { msg_send![ptr, release] };
});
impl NSObject {
fn alloc(cls: &Class) -> NSObject {
unsafe { msg_send![cls, alloc] }
}
fn inner(&self) -> &Object {
unsafe { self.0.as_ref() }
}
}
cfg_if! {
if #[cfg(target_pointer_width = "64")] {
type CGFloat = raw::c_double;
} else {
type CGFloat = raw::c_float;
}
}
#[repr(C)]
#[derive(Copy, Clone)]
struct CGPoint {
x: CGFloat,
y: CGFloat,
}
#[repr(C)]
#[derive(Copy, Clone)]
struct CGSize {
width: CGFloat,
height: CGFloat,
}
#[repr(C)]
#[derive(Copy, Clone)]
struct CGRect {
origin: CGPoint,
size: CGSize,
}
impl CGRect {
fn new(x: CGFloat, y: CGFloat, width: CGFloat, height: CGFloat) -> CGRect {
CGRect { origin: CGPoint { x, y }, size: CGSize { width, height } }
}
}
type CGEvent = CFObject;
type CGEventSource = *const Object;
#[repr(C)]
#[derive(Copy, Clone)]
#[allow(dead_code)]
enum CGEventType {
Null,
LeftMouseDown,
LeftMouseUp,
RightMouseDown,
RightMouseUp,
MouseMoved,
LeftMouseDragged,
RightMouseDragged,
KeyDown,
KeyUp,
FlagsChanged,
ScrollWheel,
TabletPointer,
TabletProximity,
OtherMouseDown,
OtherMouseUp,
OtherMouseDragged,
TapDisabledByTimeout,
TapDisabledByUserInput,
}
unsafe impl Encode for CGPoint {
fn encode() -> Encoding {
let inner = f64::encode();
let encoding = format!("{{CGPoint={0}{0}}}", inner.as_str());
unsafe { Encoding::from_str(&encoding) }
}
}
#[derive(Debug, Hash, PartialEq, Eq)]
pub struct Event(CGEvent);
impl Clone for Event {
#[inline]
fn clone(&self) -> Event {
unsafe { Event(CGEventCreateCopy((self.0).0)) }
}
}
impl Event {
#[inline]
pub fn post(&self, location: EventLocation) {
unsafe { CGEventPost(location as raw::c_int, (self.0).0) };
}
#[inline]
pub fn flags(&self) -> EventFlags {
unsafe { CGEventGetFlags((self.0).0) }
}
#[inline]
pub fn set_flags(&mut self, flags: EventFlags) {
unsafe { CGEventSetFlags((self.0).0, flags) };
}
#[inline]
pub fn enable_flags(&mut self, flags: EventFlags) {
let prev = self.flags();
self.set_flags(prev | flags);
}
}
bitflags! {
#[repr(C)]
pub struct EventFlags: u64 {
const ALPHA_SHIFT = 0x10000;
const SHIFT = 0x20000;
const CONTROL = 0x40000;
const ALTERNATE = 0x80000;
const COMMAND = 0x100000;
const HELP = 0x400000;
const SECONDARY_FN = 0x800000;
const NUMERIC_PAD = 0x200000;
const NON_COALESCED = 0x100;
}
}
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub enum EventLocation {
Hid,
Session,
AnnotatedSession,
}