Move ControlFlow to EventLoopWindowTarget

Fixes #3042.
This commit is contained in:
daxpedda 2023-09-07 08:25:04 +02:00 committed by GitHub
parent 8fdd81ecef
commit e648169861
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
60 changed files with 800 additions and 860 deletions

View file

@ -56,7 +56,7 @@ declare_class!(
fn will_terminate(&self, _sender: Option<&AnyObject>) {
trace_scope!("applicationWillTerminate:");
// TODO: Notify every window that it will be destroyed, like done in iOS?
AppState::exit();
AppState::internal_exit();
}
}
);

View file

@ -40,11 +40,11 @@ impl<Never> Event<Never> {
pub trait EventHandler: Debug {
// Not sure probably it should accept Event<'static, Never>
fn handle_nonuser_event(&mut self, event: Event<Never>, control_flow: &mut ControlFlow);
fn handle_user_events(&mut self, control_flow: &mut ControlFlow);
fn handle_nonuser_event(&mut self, event: Event<Never>);
fn handle_user_events(&mut self);
}
pub(crate) type Callback<T> = RefCell<dyn FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow)>;
pub(crate) type Callback<T> = RefCell<dyn FnMut(Event<T>, &RootWindowTarget<T>)>;
struct EventLoopHandler<T: 'static> {
callback: Weak<Callback<T>>,
@ -55,10 +55,7 @@ struct EventLoopHandler<T: 'static> {
impl<T> EventLoopHandler<T> {
fn with_callback<F>(&mut self, f: F)
where
F: FnOnce(
&mut EventLoopHandler<T>,
RefMut<'_, dyn FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow)>,
),
F: FnOnce(&mut EventLoopHandler<T>, RefMut<'_, dyn FnMut(Event<T>, &RootWindowTarget<T>)>),
{
// The `NSApp` and our `HANDLER` are global state and so it's possible that
// we could get a delegate callback after the application has exit an
@ -85,28 +82,16 @@ impl<T> Debug for EventLoopHandler<T> {
}
impl<T> EventHandler for EventLoopHandler<T> {
fn handle_nonuser_event(&mut self, event: Event<Never>, control_flow: &mut ControlFlow) {
fn handle_nonuser_event(&mut self, event: Event<Never>) {
self.with_callback(|this, mut callback| {
if let ControlFlow::ExitWithCode(code) = *control_flow {
// XXX: why isn't event dispatching simply skipped after control_flow = ExitWithCode?
let dummy = &mut ControlFlow::ExitWithCode(code);
(callback)(event.userify(), &this.window_target, dummy);
} else {
(callback)(event.userify(), &this.window_target, control_flow);
}
(callback)(event.userify(), &this.window_target);
});
}
fn handle_user_events(&mut self, control_flow: &mut ControlFlow) {
fn handle_user_events(&mut self) {
self.with_callback(|this, mut callback| {
for event in this.receiver.try_iter() {
if let ControlFlow::ExitWithCode(code) = *control_flow {
// XXX: why isn't event dispatching simply skipped after control_flow = ExitWithCode?
let dummy = &mut ControlFlow::ExitWithCode(code);
(callback)(Event::UserEvent(event), &this.window_target, dummy);
} else {
(callback)(Event::UserEvent(event), &this.window_target, control_flow);
}
(callback)(Event::UserEvent(event), &this.window_target);
}
});
}
@ -132,6 +117,7 @@ struct Handler {
running: AtomicBool,
in_callback: AtomicBool,
control_flow: Mutex<ControlFlow>,
exit: AtomicBool,
start_time: Mutex<Option<Instant>>,
callback: Mutex<Option<Box<dyn EventHandler>>>,
pending_events: Mutex<VecDeque<EventWrapper>>,
@ -189,13 +175,6 @@ impl Handler {
self.running.store(true, Ordering::Relaxed);
}
fn should_exit(&self) -> bool {
matches!(
*self.control_flow.lock().unwrap(),
ControlFlow::ExitWithCode(_)
)
}
/// Clears the `running` state and resets the `control_flow` state when an `EventLoop` exits
///
/// Since an `EventLoop` may be run more than once we need make sure to reset the
@ -206,7 +185,7 @@ impl Handler {
///
/// # Caveat
/// This is only intended to be called from the main thread
fn exit(&self) {
fn internal_exit(&self) {
// Relaxed ordering because we don't actually have multiple threads involved, we just want
// interiour mutability
//
@ -227,6 +206,14 @@ impl Handler {
self.set_wait_timeout(None);
}
pub fn exit(&self) {
self.exit.store(true, Ordering::Relaxed)
}
pub fn exiting(&self) -> bool {
self.exit.load(Ordering::Relaxed)
}
pub fn request_stop_app_on_launch(&self) {
// Relaxed ordering because we don't actually have multiple threads involved, we just want
// interior mutability
@ -287,6 +274,10 @@ impl Handler {
self.stop_app_on_redraw.load(Ordering::Relaxed)
}
fn set_control_flow(&self, new_control_flow: ControlFlow) {
*self.control_flow.lock().unwrap() = new_control_flow
}
fn control_flow(&self) -> ControlFlow {
*self.control_flow.lock().unwrap()
}
@ -321,13 +312,13 @@ impl Handler {
fn handle_nonuser_event(&self, event: Event<Never>) {
if let Some(ref mut callback) = *self.callback.lock().unwrap() {
callback.handle_nonuser_event(event, &mut self.control_flow.lock().unwrap())
callback.handle_nonuser_event(event)
}
}
fn handle_user_events(&self) {
if let Some(ref mut callback) = *self.callback.lock().unwrap() {
callback.handle_user_events(&mut self.control_flow.lock().unwrap());
callback.handle_user_events();
}
}
@ -347,7 +338,7 @@ impl Handler {
},
};
callback.handle_nonuser_event(event, &mut self.control_flow.lock().unwrap());
callback.handle_nonuser_event(event);
let physical_size = *new_inner_size.lock().unwrap();
drop(new_inner_size);
@ -418,21 +409,28 @@ impl AppState {
HANDLER.set_stop_app_on_redraw_requested(stop_on_redraw);
}
pub fn set_control_flow(control_flow: ControlFlow) {
HANDLER.set_control_flow(control_flow)
}
pub fn control_flow() -> ControlFlow {
HANDLER.control_flow()
}
pub fn exit() -> i32 {
pub fn internal_exit() {
HANDLER.set_in_callback(true);
HANDLER.handle_nonuser_event(Event::LoopExiting);
HANDLER.set_in_callback(false);
HANDLER.exit();
HANDLER.internal_exit();
Self::clear_callback();
if let ControlFlow::ExitWithCode(code) = HANDLER.control_flow() {
code
} else {
0
}
}
pub fn exit() {
HANDLER.exit()
}
pub fn exiting() -> bool {
HANDLER.exiting()
}
pub fn dispatch_init_events() {
@ -528,7 +526,6 @@ impl AppState {
}
}
}
ControlFlow::ExitWithCode(_) => StartCause::Poll, //panic!("unexpected `ControlFlow::Exit`"),
};
HANDLER.set_in_callback(true);
HANDLER.handle_nonuser_event(Event::NewEvents(cause));
@ -643,7 +640,7 @@ impl AppState {
HANDLER.handle_nonuser_event(Event::AboutToWait);
HANDLER.set_in_callback(false);
if HANDLER.should_exit() {
if HANDLER.exiting() {
Self::stop();
}
@ -654,7 +651,7 @@ impl AppState {
let wait_timeout = HANDLER.wait_timeout(); // configured by pump_events
let app_timeout = match HANDLER.control_flow() {
ControlFlow::Wait => None,
ControlFlow::Poll | ControlFlow::ExitWithCode(_) => Some(Instant::now()),
ControlFlow::Poll => Some(Instant::now()),
ControlFlow::WaitUntil(instant) => Some(instant),
};
HANDLER

View file

@ -93,6 +93,22 @@ impl<T: 'static> EventLoopWindowTarget<T> {
pub fn raw_display_handle(&self) -> RawDisplayHandle {
RawDisplayHandle::AppKit(AppKitDisplayHandle::empty())
}
pub(crate) fn set_control_flow(&self, control_flow: ControlFlow) {
AppState::set_control_flow(control_flow)
}
pub(crate) fn control_flow(&self) -> ControlFlow {
AppState::control_flow()
}
pub(crate) fn exit(&self) {
AppState::exit()
}
pub(crate) fn exiting(&self) -> bool {
AppState::exiting()
}
}
impl<T> EventLoopWindowTarget<T> {
@ -213,7 +229,7 @@ impl<T> EventLoop<T> {
pub fn run<F>(mut self, callback: F) -> Result<(), EventLoopError>
where
F: FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow),
F: FnMut(Event<T>, &RootWindowTarget<T>),
{
self.run_ondemand(callback)
}
@ -224,7 +240,7 @@ impl<T> EventLoop<T> {
// redundant wake ups.
pub fn run_ondemand<F>(&mut self, callback: F) -> Result<(), EventLoopError>
where
F: FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow),
F: FnMut(Event<T>, &RootWindowTarget<T>),
{
if AppState::is_running() {
return Err(EventLoopError::AlreadyRunning);
@ -241,14 +257,14 @@ impl<T> EventLoop<T> {
let callback = unsafe {
mem::transmute::<
Rc<RefCell<dyn FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow)>>,
Rc<RefCell<dyn FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow)>>,
Rc<RefCell<dyn FnMut(Event<T>, &RootWindowTarget<T>)>>,
Rc<RefCell<dyn FnMut(Event<T>, &RootWindowTarget<T>)>>,
>(Rc::new(RefCell::new(callback)))
};
self._callback = Some(Rc::clone(&callback));
let exit_code = autoreleasepool(|_| {
autoreleasepool(|_| {
// A bit of juggling with the callback references to make sure
// that `self.callback` is the only owner of the callback.
let weak_cb: Weak<_> = Rc::downgrade(&callback);
@ -288,7 +304,7 @@ impl<T> EventLoop<T> {
resume_unwind(panic);
}
AppState::exit()
AppState::internal_exit()
}));
// # Safety
@ -298,22 +314,17 @@ impl<T> EventLoop<T> {
drop(self._callback.take());
AppState::clear_callback();
match catch_result {
Ok(exit_code) => exit_code,
Err(payload) => resume_unwind(payload),
if let Err(payload) = catch_result {
resume_unwind(payload)
}
});
if exit_code == 0 {
Ok(())
} else {
Err(EventLoopError::ExitFailure(exit_code))
}
Ok(())
}
pub fn pump_events<F>(&mut self, timeout: Option<Duration>, callback: F) -> PumpStatus
where
F: FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow),
F: FnMut(Event<T>, &RootWindowTarget<T>),
{
// # Safety
// We are erasing the lifetime of the application callback here so that we
@ -326,8 +337,8 @@ impl<T> EventLoop<T> {
let callback = unsafe {
mem::transmute::<
Rc<RefCell<dyn FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow)>>,
Rc<RefCell<dyn FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow)>>,
Rc<RefCell<dyn FnMut(Event<T>, &RootWindowTarget<T>)>>,
Rc<RefCell<dyn FnMut(Event<T>, &RootWindowTarget<T>)>>,
>(Rc::new(RefCell::new(callback)))
};
@ -407,9 +418,9 @@ impl<T> EventLoop<T> {
resume_unwind(panic);
}
if let ControlFlow::ExitWithCode(code) = AppState::control_flow() {
AppState::exit();
PumpStatus::Exit(code)
if AppState::exiting() {
AppState::internal_exit();
PumpStatus::Exit(0)
} else {
PumpStatus::Continue
}