diff --git a/src/platform_impl/macos/app_state.rs b/src/platform_impl/macos/app_state.rs index 2fbdc85632..593aa765f5 100644 --- a/src/platform_impl/macos/app_state.rs +++ b/src/platform_impl/macos/app_state.rs @@ -82,11 +82,13 @@ where #[derive(Default)] struct Handler { ready: AtomicBool, + in_callback: AtomicBool, control_flow: Mutex, control_flow_prev: Mutex, start_time: Mutex>, callback: Mutex>>, pending_events: Mutex>>, + deferred_events: Mutex>>, pending_redraw: Mutex>, waker: Mutex, } @@ -99,6 +101,10 @@ impl Handler { self.pending_events.lock().unwrap() } + fn deferred<'a>(&'a self) -> MutexGuard<'a, VecDeque>> { + self.deferred_events.lock().unwrap() + } + fn redraw<'a>(&'a self) -> MutexGuard<'a, Vec> { self.pending_redraw.lock().unwrap() } @@ -143,10 +149,22 @@ impl Handler { mem::replace(&mut *self.events(), Default::default()) } + fn take_deferred(&self) -> VecDeque> { + mem::replace(&mut *self.deferred(), Default::default()) + } + fn should_redraw(&self) -> Vec { mem::replace(&mut *self.redraw(), Default::default()) } + fn get_in_callback(&self) -> bool { + self.in_callback.load(Ordering::Acquire) + } + + fn set_in_callback(&self, in_callback: bool) { + self.in_callback.store(in_callback, Ordering::Release); + } + fn handle_nonuser_event(&self, event: Event) { if let Some(ref mut callback) = *self.callback.lock().unwrap() { callback.handle_nonuser_event( @@ -181,13 +199,17 @@ impl AppState { } pub fn exit() { + HANDLER.set_in_callback(true); HANDLER.handle_nonuser_event(Event::LoopDestroyed); + HANDLER.set_in_callback(false); } pub fn launched() { HANDLER.set_ready(); HANDLER.waker().start(); + HANDLER.set_in_callback(true); HANDLER.handle_nonuser_event(Event::NewEvents(StartCause::Init)); + HANDLER.set_in_callback(false); } pub fn wakeup() { @@ -214,7 +236,9 @@ impl AppState { }, ControlFlow::Exit => StartCause::Poll,//panic!("unexpected `ControlFlow::Exit`"), }; + HANDLER.set_in_callback(true); HANDLER.handle_nonuser_event(Event::NewEvents(cause)); + HANDLER.set_in_callback(false); } // This is called from multiple threads at present @@ -227,38 +251,49 @@ impl AppState { pub fn queue_event(event: Event) { if !unsafe { msg_send![class!(NSThread), isMainThread] } { - panic!("uh-oh"); + panic!("Event queued from different thread: {:#?}", event); } HANDLER.events().push_back(event); } pub fn queue_events(mut events: VecDeque>) { if !unsafe { msg_send![class!(NSThread), isMainThread] } { - panic!("uh-ohs"); + panic!("Events queued from different thread: {:#?}", events); } HANDLER.events().append(&mut events); } pub fn send_event_immediately(event: Event) { if !unsafe { msg_send![class!(NSThread), isMainThread] } { - panic!("uh-oh"); + panic!("Event sent from different thread: {:#?}", event); + } + HANDLER.deferred().push_back(event); + if !HANDLER.get_in_callback() { + HANDLER.set_in_callback(true); + for event in HANDLER.take_deferred() { + HANDLER.handle_nonuser_event(event); + } + HANDLER.set_in_callback(false); } - HANDLER.handle_nonuser_event(event); } pub fn cleared() { if !HANDLER.is_ready() { return } - HANDLER.handle_user_events(); - for event in HANDLER.take_events() { - HANDLER.handle_nonuser_event(event); - } - for window_id in HANDLER.should_redraw() { - HANDLER.handle_nonuser_event(Event::WindowEvent { - window_id, - event: WindowEvent::RedrawRequested, - }); + if !HANDLER.get_in_callback() { + HANDLER.set_in_callback(true); + HANDLER.handle_user_events(); + for event in HANDLER.take_events() { + HANDLER.handle_nonuser_event(event); + } + for window_id in HANDLER.should_redraw() { + HANDLER.handle_nonuser_event(Event::WindowEvent { + window_id, + event: WindowEvent::RedrawRequested, + }); + } + HANDLER.handle_nonuser_event(Event::EventsCleared); + HANDLER.set_in_callback(false); } - HANDLER.handle_nonuser_event(Event::EventsCleared); if HANDLER.should_exit() { let _: () = unsafe { msg_send![NSApp(), stop:nil] }; return