use std::{fmt, mem, ptr};
use std::os::raw;
use objc::runtime::Class;
use super::{CFObjectRef, CGEventType, CGEventSource, CGPoint, NS_EVENT};
extern {
fn CGEventCreateMouseEvent(
source: CGEventSource,
mouse_type: CGEventType,
mouse_cursor_position: CGPoint,
mouse_button: raw::c_int,
) -> *mut raw::c_void;
fn CGEventGetLocation(event: CFObjectRef) -> CGPoint;
fn CGEventGetUnflippedLocation(event: CFObjectRef) -> CGPoint;
fn CGEventSetLocation(event: CFObjectRef, location: CGPoint);
fn CGWarpMouseCursorPosition(new_cursor_position: CGPoint) -> CGPoint;
}
unsafe fn location_from(ns_event: &Class) -> Location {
From::<CGPoint>::from(msg_send![ns_event, mouseLocation])
}
pub fn location() -> Location {
unsafe { location_from(&NS_EVENT) }
}
pub fn location_iter() -> LocationIter {
LocationIter { ns_event: &NS_EVENT }
}
#[inline]
pub fn warp_location(location: Location) {
unsafe { CGWarpMouseCursorPosition(location.into()) };
}
pub type Location = (f64, f64);
impl From<CGPoint> for Location {
#[inline]
fn from(point: CGPoint) -> Location {
(point.x as _, point.y as _)
}
}
impl From<Location> for CGPoint {
#[inline]
fn from((x, y): Location) -> CGPoint {
CGPoint { x: x as _, y: y as _ }
}
}
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub enum Button {
Left,
Right,
}
impl From<(Button, EventKind)> for super::CGEventType {
fn from(pair: (Button, EventKind)) -> Self {
use super::CGEventType::*;
match pair {
(Button::Left, EventKind::Down) => LeftMouseDown,
(Button::Left, EventKind::Up) => LeftMouseUp,
(Button::Left, EventKind::Moved) => MouseMoved,
(Button::Left, EventKind::Dragged) => LeftMouseDragged,
(Button::Right, EventKind::Down) => RightMouseDown,
(Button::Right, EventKind::Up) => RightMouseUp,
(Button::Right, EventKind::Moved) => MouseMoved,
(Button::Right, EventKind::Dragged) => RightMouseDragged,
}
}
}
declare_event!("A mouse event that can be posted into the Quartz event stream.");
impl Event {
pub fn new(button: Button, kind: EventKind, location: Location) -> Event {
unsafe {
let event = CGEventCreateMouseEvent(
ptr::null(),
(button, kind).into(),
location.into(),
button as raw::c_int,
);
mem::transmute(event)
}
}
#[inline]
pub fn location(&self) -> Location {
unsafe { CGEventGetLocation(((self.0).0).0).into() }
}
#[inline]
pub fn location_unflipped(&self) -> Location {
unsafe { CGEventGetUnflippedLocation(((self.0).0).0).into() }
}
#[inline]
pub fn set_location(&mut self, location: Location) {
unsafe { CGEventSetLocation(((self.0).0).0, location.into()) }
}
}
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub enum EventKind {
Down,
Up,
Moved,
Dragged,
}
#[derive(Copy, Clone)]
pub struct LocationIter {
ns_event: &'static Class
}
impl fmt::Debug for LocationIter {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Location").finish()
}
}
impl Iterator for LocationIter {
type Item = Location;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
unsafe { Some(location_from(self.ns_event)) }
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
(usize::max_value(), None)
}
}
#[cfg(all(test, nightly))]
mod benches {
use super::*;
use test::{Bencher, black_box};
#[bench]
fn location_100(b: &mut Bencher) {
b.iter(|| {
for _ in 0..100 {
black_box(location());
}
});
}
#[bench]
fn location_iter_100(b: &mut Bencher) {
b.iter(|| {
for loc in location_iter().take(100) {
black_box(loc);
}
})
}
}