use std::os::raw::c_void;
use std::sync::mpsc::{channel, Receiver, Sender};
use cocoa::{
appkit::NSView,
base::id,
foundation::{NSPoint, NSRect, NSSize},
};
use objc::{
class,
declare::ClassDecl,
msg_send,
runtime::{Class, Object, Sel},
sel, sel_impl,
};
use super::window::EditorWindowImpl;
use crate::event::{MouseButton, WindowEvent};
use crate::platform::EventSourceBackend;
const EVENT_DELEGATE_IVAR: &str = "EVENT_DELEGATE_IVAR";
pub(in crate::platform) struct EventSourceImpl {
event_subview: id,
incoming_events: Receiver<WindowEvent>,
}
impl EventSourceBackend for EventSourceImpl {
fn new(window: &EditorWindowImpl, size_xy: (i32, i32)) -> Self {
unsafe {
let event_subview: id = msg_send![EVENT_SUBVIEW_DECL.class, alloc];
event_subview.initWithFrame_(NSRect::new(
NSPoint::new(0., 0.),
NSSize::new(size_xy.0 as f64, size_xy.1 as f64),
));
let _: id = msg_send![window.ns_view, addSubview: event_subview];
let (event_sender, incoming_events) = channel();
let event_delegate = EventDelegate {
sender: event_sender,
size_xy,
};
let event_delegate = Box::into_raw(Box::new(event_delegate));
(*event_subview).set_ivar(EVENT_DELEGATE_IVAR, event_delegate as *mut c_void);
Self {
event_subview,
incoming_events,
}
}
}
fn poll_event(&self) -> Option<WindowEvent> {
self.incoming_events.try_recv().ok()
}
}
impl Drop for EventSourceImpl {
fn drop(&mut self) {
let _: id = unsafe { msg_send![self.event_subview, dealloc] };
}
}
struct EventDelegate {
sender: Sender<WindowEvent>,
size_xy: (i32, i32),
}
impl EventDelegate {
#[allow(clippy::mut_from_ref)]
fn from_field(obj: &Object) -> &mut EventDelegate {
unsafe {
let delegate_ptr: *mut c_void = *obj.get_ivar(EVENT_DELEGATE_IVAR);
&mut *(delegate_ptr as *mut EventDelegate)
}
}
fn send(&mut self, event: WindowEvent) {
self.sender.send(event).unwrap();
}
}
struct EventSubview {
class: *const Class,
}
unsafe impl Send for EventSubview {}
unsafe impl Sync for EventSubview {}
static EVENT_SUBVIEW_DECL: once_cell::sync::Lazy<EventSubview> =
once_cell::sync::Lazy::new(|| unsafe {
let mut class = ClassDecl::new("EventSubview", class!(NSView)).unwrap();
class.add_method(sel!(dealloc), dealloc as extern "C" fn(&Object, Sel));
class.add_method(
sel!(mouseDown:),
mouse_down as extern "C" fn(&Object, Sel, id),
);
class.add_method(sel!(mouseUp:), mouse_up as extern "C" fn(&Object, Sel, id));
class.add_method(
sel!(rightMouseDown:),
right_mouse_down as extern "C" fn(&Object, Sel, id),
);
class.add_method(
sel!(rightMouseUp:),
right_mouse_up as extern "C" fn(&Object, Sel, id),
);
class.add_method(
sel!(otherMouseDown:),
other_mouse_down as extern "C" fn(&Object, Sel, id),
);
class.add_method(
sel!(otherMouseUp:),
other_mouse_up as extern "C" fn(&Object, Sel, id),
);
class.add_method(
sel!(mouseMoved:),
mouse_moved as extern "C" fn(&Object, Sel, id),
);
class.add_method(
sel!(mouseDragged:),
mouse_dragged as extern "C" fn(&Object, Sel, id),
);
class.add_ivar::<*mut c_void>(EVENT_DELEGATE_IVAR);
EventSubview {
class: class.register(),
}
});
extern "C" fn dealloc(this: &Object, _sel: Sel) {
unsafe {
let delegate_ptr: *mut c_void = *this.get_ivar(EVENT_DELEGATE_IVAR);
Box::from_raw(delegate_ptr as *mut EventDelegate);
}
}
extern "C" fn mouse_down(this: &Object, _sel: Sel, event: id) {
let location = unsafe { cocoa::appkit::NSEvent::locationInWindow(event) };
let delegate = EventDelegate::from_field(this);
delegate.send(WindowEvent::CursorMovement(
(location.x / delegate.size_xy.0 as f64) as f32,
1. - (location.y / delegate.size_xy.1 as f64) as f32,
));
delegate.send(WindowEvent::MouseClick(MouseButton::Left));
}
extern "C" fn mouse_up(this: &Object, _sel: Sel, event: id) {
let location = unsafe { cocoa::appkit::NSEvent::locationInWindow(event) };
let delegate = EventDelegate::from_field(this);
delegate.send(WindowEvent::CursorMovement(
(location.x / delegate.size_xy.0 as f64) as f32,
1. - (location.y / delegate.size_xy.1 as f64) as f32,
));
delegate.send(WindowEvent::MouseRelease(MouseButton::Left));
}
extern "C" fn right_mouse_down(this: &Object, _sel: Sel, event: id) {
let location = unsafe { cocoa::appkit::NSEvent::locationInWindow(event) };
let delegate = EventDelegate::from_field(this);
delegate.send(WindowEvent::CursorMovement(
(location.x / delegate.size_xy.0 as f64) as f32,
1. - (location.y / delegate.size_xy.1 as f64) as f32,
));
delegate.send(WindowEvent::MouseClick(MouseButton::Right));
}
extern "C" fn right_mouse_up(this: &Object, _sel: Sel, event: id) {
let location = unsafe { cocoa::appkit::NSEvent::locationInWindow(event) };
let delegate = EventDelegate::from_field(this);
delegate.send(WindowEvent::CursorMovement(
(location.x / delegate.size_xy.0 as f64) as f32,
1. - (location.y / delegate.size_xy.1 as f64) as f32,
));
delegate.send(WindowEvent::MouseRelease(MouseButton::Right));
}
extern "C" fn other_mouse_down(this: &Object, _sel: Sel, event: id) {
let location = unsafe { cocoa::appkit::NSEvent::locationInWindow(event) };
let delegate = EventDelegate::from_field(this);
delegate.send(WindowEvent::CursorMovement(
(location.x / delegate.size_xy.0 as f64) as f32,
1. - (location.y / delegate.size_xy.1 as f64) as f32,
));
delegate.send(WindowEvent::MouseClick(MouseButton::Middle));
}
extern "C" fn other_mouse_up(this: &Object, _sel: Sel, event: id) {
let location = unsafe { cocoa::appkit::NSEvent::locationInWindow(event) };
let delegate = EventDelegate::from_field(this);
delegate.send(WindowEvent::CursorMovement(
(location.x / delegate.size_xy.0 as f64) as f32,
1. - (location.y / delegate.size_xy.1 as f64) as f32,
));
delegate.send(WindowEvent::MouseRelease(MouseButton::Middle));
}
extern "C" fn mouse_moved(this: &Object, _sel: Sel, event: id) {
let location = unsafe { cocoa::appkit::NSEvent::locationInWindow(event) };
let delegate = EventDelegate::from_field(this);
delegate.send(WindowEvent::CursorMovement(
(location.x / delegate.size_xy.0 as f64) as f32,
1. - (location.y / delegate.size_xy.1 as f64) as f32,
));
}
extern "C" fn mouse_dragged(this: &Object, sel: Sel, event: id) {
mouse_moved(this, sel, event)
}