diff --git a/CHANGELOG.md b/CHANGELOG.md index 43c11793..b8594254 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Unreleased +- On macOS, implement `run_return`. + # 0.20.0 Alpha 3 (2019-08-14) - On macOS, drop the run closure on exit. diff --git a/src/platform_impl/macos/app_state.rs b/src/platform_impl/macos/app_state.rs index fb020d72..4a18ed83 100644 --- a/src/platform_impl/macos/app_state.rs +++ b/src/platform_impl/macos/app_state.rs @@ -3,6 +3,7 @@ use std::{ fmt::{self, Debug}, hint::unreachable_unchecked, mem, + rc::Rc, sync::{ atomic::{AtomicBool, Ordering}, Mutex, MutexGuard, @@ -37,13 +38,13 @@ pub trait EventHandler: Debug { fn handle_user_events(&mut self, control_flow: &mut ControlFlow); } -struct EventLoopHandler { - callback: F, +struct EventLoopHandler { + callback: Box, &RootWindowTarget, &mut ControlFlow)>, will_exit: bool, - window_target: RootWindowTarget, + window_target: Rc>, } -impl Debug for EventLoopHandler { +impl Debug for EventLoopHandler { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter .debug_struct("EventLoopHandler") @@ -52,11 +53,7 @@ impl Debug for EventLoopHandler { } } -impl EventHandler for EventLoopHandler -where - F: 'static + FnMut(Event, &RootWindowTarget, &mut ControlFlow), - T: 'static, -{ +impl EventHandler for EventLoopHandler { fn handle_nonuser_event(&mut self, event: Event, control_flow: &mut ControlFlow) { (self.callback)(event.userify(), &self.window_target, control_flow); self.will_exit |= *control_flow == ControlFlow::Exit; @@ -180,13 +177,20 @@ impl Handler { pub enum AppState {} impl AppState { - pub fn set_callback(callback: F, window_target: RootWindowTarget) + // This function extends lifetime of `callback` to 'static as its side effect + pub unsafe fn set_callback(callback: F, window_target: Rc>) where - F: 'static + FnMut(Event, &RootWindowTarget, &mut ControlFlow), - T: 'static, + F: FnMut(Event, &RootWindowTarget, &mut ControlFlow), { *HANDLER.callback.lock().unwrap() = Some(Box::new(EventLoopHandler { - callback, + // This transmute is always safe, in case it was reached through `run`, since our + // lifetime will be already 'static. In other cases caller should ensure that all data + // they passed to callback will actually outlive it, some apps just can't move + // everything to event loop, so this is something that they should care about. + callback: mem::transmute::< + Box, &RootWindowTarget, &mut ControlFlow)>, + Box, &RootWindowTarget, &mut ControlFlow)>, + >(Box::new(callback)), will_exit: false, window_target, })); @@ -299,7 +303,7 @@ impl AppState { } HANDLER.update_start_time(); match HANDLER.get_old_and_new_control_flow() { - (ControlFlow::Exit, _) | (_, ControlFlow::Exit) => unreachable!(), + (ControlFlow::Exit, _) | (_, ControlFlow::Exit) => (), (old, new) if old == new => (), (_, ControlFlow::Wait) => HANDLER.waker().stop(), (_, ControlFlow::WaitUntil(instant)) => HANDLER.waker().start_at(instant), diff --git a/src/platform_impl/macos/event_loop.rs b/src/platform_impl/macos/event_loop.rs index b5fea9d1..f3df1b52 100644 --- a/src/platform_impl/macos/event_loop.rs +++ b/src/platform_impl/macos/event_loop.rs @@ -1,5 +1,6 @@ use std::{ - collections::VecDeque, marker::PhantomData, mem, os::raw::c_void, process, ptr, sync::mpsc, + collections::VecDeque, marker::PhantomData, mem, os::raw::c_void, process, ptr, rc::Rc, + sync::mpsc, }; use cocoa::{ @@ -34,7 +35,7 @@ impl Default for EventLoopWindowTarget { } pub struct EventLoop { - window_target: RootWindowTarget, + window_target: Rc>, _delegate: IdRef, } @@ -59,10 +60,10 @@ impl EventLoop { }; setup_control_flow_observers(); EventLoop { - window_target: RootWindowTarget { + window_target: Rc::new(RootWindowTarget { p: Default::default(), _marker: PhantomData, - }, + }), _delegate: delegate, } } @@ -81,28 +82,28 @@ impl EventLoop { &self.window_target } - pub fn run(self, callback: F) -> ! + pub fn run(mut self, callback: F) -> ! where F: 'static + FnMut(Event, &RootWindowTarget, &mut ControlFlow), + { + self.run_return(callback); + process::exit(0); + } + + pub fn run_return(&mut self, callback: F) + where + F: FnMut(Event, &RootWindowTarget, &mut ControlFlow), { unsafe { let _pool = NSAutoreleasePool::new(nil); let app = NSApp(); assert_ne!(app, nil); - AppState::set_callback(callback, self.window_target); + AppState::set_callback(callback, Rc::clone(&self.window_target)); let _: () = msg_send![app, run]; AppState::exit(); - process::exit(0) } } - pub fn run_return(&mut self, _callback: F) - where - F: FnMut(Event, &RootWindowTarget, &mut ControlFlow), - { - unimplemented!(); - } - pub fn create_proxy(&self) -> Proxy { Proxy::new(self.window_target.p.sender.clone()) }