use std::{
collections::VecDeque, mem, os::raw::c_void, process, ptr, sync::mpsc, marker::PhantomData
};
use cocoa::{appkit::NSApp, base::{id, nil}, foundation::NSAutoreleasePool};
use {
event::Event,
event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootWindowTarget},
use platform_impl::platform::{
app::APP_CLASS, app_delegate::APP_DELEGATE_CLASS,
app_state::AppState, monitor::{self, MonitorHandle},
observer::*, util::IdRef,
pub struct EventLoopWindowTarget<T: 'static> {
pub sender: mpsc::Sender<T>, // this is only here to be cloned elsewhere
pub receiver: mpsc::Receiver<T>,
}
impl<T> Default for EventLoopWindowTarget<T> {
fn default() -> Self {
let (sender, receiver) = mpsc::channel();
EventLoopWindowTarget { sender, receiver }
pub struct EventLoop<T: 'static> {
window_target: RootWindowTarget<T>,
_delegate: IdRef,
impl<T> EventLoop<T> {
pub fn new() -> Self {
let delegate = unsafe {
if !msg_send![class!(NSThread), isMainThread] {
panic!("On macOS, `EventLoop` must be created on the main thread!");
// This must be done before `NSApp()` (equivalent to sending
// `sharedApplication`) is called anywhere else, or we'll end up
// with the wrong `NSApplication` class and the wrong thread could
// be marked as main.
let app: id = msg_send![APP_CLASS.0, sharedApplication];
let delegate = IdRef::new(msg_send![APP_DELEGATE_CLASS.0, new]);
let pool = NSAutoreleasePool::new(nil);
let _: () = msg_send![app, setDelegate:*delegate];
let _: () = msg_send![pool, drain];
delegate
setup_control_flow_observers();
EventLoop {
window_target: RootWindowTarget {
p: Default::default(),
_marker: PhantomData,
},
_delegate: delegate,
#[inline]
pub fn get_available_monitors(&self) -> VecDeque<MonitorHandle> {
monitor::get_available_monitors()
pub fn get_primary_monitor(&self) -> MonitorHandle {
monitor::get_primary_monitor()
pub fn window_target(&self) -> &RootWindowTarget<T> {
&self.window_target
pub fn run<F>(self, callback: F) -> !
where F: 'static + FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow),
{
unsafe {
let _pool = NSAutoreleasePool::new(nil);
let app = NSApp();
assert_ne!(app, nil);
AppState::set_callback(callback, self.window_target);
let _: () = msg_send![app, run];
AppState::exit();
process::exit(0)
pub fn run_return<F>(&mut self, _callback: F)
where F: FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow),
unimplemented!();
pub fn create_proxy(&self) -> Proxy<T> {
Proxy::new(self.window_target.p.sender.clone())
#[derive(Clone)]
pub struct Proxy<T> {
sender: mpsc::Sender<T>,
source: CFRunLoopSourceRef,
unsafe impl<T> Send for Proxy<T> {}
unsafe impl<T> Sync for Proxy<T> {}
impl<T> Proxy<T> {
fn new(sender: mpsc::Sender<T>) -> Self {
// just wakeup the eventloop
extern "C" fn event_loop_proxy_handler(_: *mut c_void) {}
// adding a Source to the main CFRunLoop lets us wake it up and
// process user events through the normal OS EventLoop mechanisms.
let rl = CFRunLoopGetMain();
let mut context: CFRunLoopSourceContext = mem::zeroed();
context.perform = event_loop_proxy_handler;
let source = CFRunLoopSourceCreate(
ptr::null_mut(),
CFIndex::max_value() - 1,
&mut context,
);
CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes);
CFRunLoopWakeUp(rl);
Proxy { sender, source }
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> {
self.sender.send(event).map_err(|_| EventLoopClosed)?;
// let the main thread know there's a new event
CFRunLoopSourceSignal(self.source);
Ok(())