Refactor user event handling on macOS/iOS (#3422)

Move user event handling to inside main event loop
This commit is contained in:
Mads Marquart 2024-01-25 05:26:50 +01:00 committed by GitHub
parent a5b08fc48c
commit b36d8d1e52
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 194 additions and 236 deletions

View file

@ -3,7 +3,7 @@
use std::{ use std::{
cell::{RefCell, RefMut}, cell::{RefCell, RefMut},
collections::HashSet, collections::HashSet,
mem, fmt, mem,
os::raw::c_void, os::raw::c_void,
ptr, ptr,
sync::{Arc, Mutex}, sync::{Arc, Mutex},
@ -24,13 +24,12 @@ use objc2::runtime::AnyObject;
use objc2::{msg_send, sel}; use objc2::{msg_send, sel};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use super::event_loop::{EventHandler, Never};
use super::uikit::UIView; use super::uikit::UIView;
use super::view::WinitUIWindow; use super::view::WinitUIWindow;
use crate::{ use crate::{
dpi::PhysicalSize, dpi::PhysicalSize,
event::{Event, InnerSizeWriter, StartCause, WindowEvent}, event::{Event, InnerSizeWriter, StartCause, WindowEvent},
event_loop::ControlFlow, event_loop::{ControlFlow, EventLoopWindowTarget as RootEventLoopWindowTarget},
window::WindowId as RootWindowId, window::WindowId as RootWindowId,
}; };
@ -47,8 +46,32 @@ macro_rules! bug_assert {
} }
#[derive(Debug)] #[derive(Debug)]
pub enum EventWrapper { pub(crate) struct HandlePendingUserEvents;
StaticEvent(Event<Never>),
pub(crate) struct EventLoopHandler {
#[allow(clippy::type_complexity)]
pub(crate) handler: Box<dyn FnMut(Event<HandlePendingUserEvents>, &RootEventLoopWindowTarget)>,
pub(crate) event_loop: RootEventLoopWindowTarget,
}
impl fmt::Debug for EventLoopHandler {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("EventLoopHandler")
.field("handler", &"...")
.field("event_loop", &self.event_loop)
.finish()
}
}
impl EventLoopHandler {
fn handle_event(&mut self, event: Event<HandlePendingUserEvents>) {
(self.handler)(event, &self.event_loop)
}
}
#[derive(Debug)]
pub(crate) enum EventWrapper {
StaticEvent(Event<HandlePendingUserEvents>),
ScaleFactorChanged(ScaleFactorChanged), ScaleFactorChanged(ScaleFactorChanged),
} }
@ -61,7 +84,7 @@ pub struct ScaleFactorChanged {
enum UserCallbackTransitionResult<'a> { enum UserCallbackTransitionResult<'a> {
Success { Success {
event_handler: Box<dyn EventHandler>, handler: EventLoopHandler,
active_control_flow: ControlFlow, active_control_flow: ControlFlow,
processing_redraws: bool, processing_redraws: bool,
}, },
@ -70,7 +93,7 @@ enum UserCallbackTransitionResult<'a> {
}, },
} }
impl Event<Never> { impl Event<HandlePendingUserEvents> {
fn is_redraw(&self) -> bool { fn is_redraw(&self) -> bool {
matches!( matches!(
self, self,
@ -94,11 +117,11 @@ enum AppStateImpl {
Launching { Launching {
queued_windows: Vec<Id<WinitUIWindow>>, queued_windows: Vec<Id<WinitUIWindow>>,
queued_events: Vec<EventWrapper>, queued_events: Vec<EventWrapper>,
queued_event_handler: Box<dyn EventHandler>, queued_handler: EventLoopHandler,
queued_gpu_redraws: HashSet<Id<WinitUIWindow>>, queued_gpu_redraws: HashSet<Id<WinitUIWindow>>,
}, },
ProcessingEvents { ProcessingEvents {
event_handler: Box<dyn EventHandler>, handler: EventLoopHandler,
queued_gpu_redraws: HashSet<Id<WinitUIWindow>>, queued_gpu_redraws: HashSet<Id<WinitUIWindow>>,
active_control_flow: ControlFlow, active_control_flow: ControlFlow,
}, },
@ -108,15 +131,15 @@ enum AppStateImpl {
queued_gpu_redraws: HashSet<Id<WinitUIWindow>>, queued_gpu_redraws: HashSet<Id<WinitUIWindow>>,
}, },
ProcessingRedraws { ProcessingRedraws {
event_handler: Box<dyn EventHandler>, handler: EventLoopHandler,
active_control_flow: ControlFlow, active_control_flow: ControlFlow,
}, },
Waiting { Waiting {
waiting_event_handler: Box<dyn EventHandler>, waiting_handler: EventLoopHandler,
start: Instant, start: Instant,
}, },
PollFinished { PollFinished {
waiting_event_handler: Box<dyn EventHandler>, waiting_handler: EventLoopHandler,
}, },
Terminated, Terminated,
} }
@ -204,7 +227,7 @@ impl AppState {
matches!(self.state(), AppStateImpl::Terminated) matches!(self.state(), AppStateImpl::Terminated)
} }
fn will_launch_transition(&mut self, queued_event_handler: Box<dyn EventHandler>) { fn will_launch_transition(&mut self, queued_handler: EventLoopHandler) {
let (queued_windows, queued_events, queued_gpu_redraws) = match self.take_state() { let (queued_windows, queued_events, queued_gpu_redraws) = match self.take_state() {
AppStateImpl::NotLaunched { AppStateImpl::NotLaunched {
queued_windows, queued_windows,
@ -216,28 +239,28 @@ impl AppState {
self.set_state(AppStateImpl::Launching { self.set_state(AppStateImpl::Launching {
queued_windows, queued_windows,
queued_events, queued_events,
queued_event_handler, queued_handler,
queued_gpu_redraws, queued_gpu_redraws,
}); });
} }
fn did_finish_launching_transition(&mut self) -> (Vec<Id<WinitUIWindow>>, Vec<EventWrapper>) { fn did_finish_launching_transition(&mut self) -> (Vec<Id<WinitUIWindow>>, Vec<EventWrapper>) {
let (windows, events, event_handler, queued_gpu_redraws) = match self.take_state() { let (windows, events, handler, queued_gpu_redraws) = match self.take_state() {
AppStateImpl::Launching { AppStateImpl::Launching {
queued_windows, queued_windows,
queued_events, queued_events,
queued_event_handler, queued_handler,
queued_gpu_redraws, queued_gpu_redraws,
} => ( } => (
queued_windows, queued_windows,
queued_events, queued_events,
queued_event_handler, queued_handler,
queued_gpu_redraws, queued_gpu_redraws,
), ),
s => bug!("unexpected state {:?}", s), s => bug!("unexpected state {:?}", s),
}; };
self.set_state(AppStateImpl::ProcessingEvents { self.set_state(AppStateImpl::ProcessingEvents {
event_handler, handler,
active_control_flow: self.control_flow, active_control_flow: self.control_flow,
queued_gpu_redraws, queued_gpu_redraws,
}); });
@ -251,24 +274,19 @@ impl AppState {
return None; return None;
} }
let (event_handler, event) = match (self.control_flow, self.take_state()) { let (handler, event) = match (self.control_flow, self.take_state()) {
( (ControlFlow::Poll, AppStateImpl::PollFinished { waiting_handler }) => (
ControlFlow::Poll, waiting_handler,
AppStateImpl::PollFinished {
waiting_event_handler,
},
) => (
waiting_event_handler,
EventWrapper::StaticEvent(Event::NewEvents(StartCause::Poll)), EventWrapper::StaticEvent(Event::NewEvents(StartCause::Poll)),
), ),
( (
ControlFlow::Wait, ControlFlow::Wait,
AppStateImpl::Waiting { AppStateImpl::Waiting {
waiting_event_handler, waiting_handler,
start, start,
}, },
) => ( ) => (
waiting_event_handler, waiting_handler,
EventWrapper::StaticEvent(Event::NewEvents(StartCause::WaitCancelled { EventWrapper::StaticEvent(Event::NewEvents(StartCause::WaitCancelled {
start, start,
requested_resume: None, requested_resume: None,
@ -277,7 +295,7 @@ impl AppState {
( (
ControlFlow::WaitUntil(requested_resume), ControlFlow::WaitUntil(requested_resume),
AppStateImpl::Waiting { AppStateImpl::Waiting {
waiting_event_handler, waiting_handler,
start, start,
}, },
) => { ) => {
@ -292,13 +310,13 @@ impl AppState {
requested_resume: Some(requested_resume), requested_resume: Some(requested_resume),
})) }))
}; };
(waiting_event_handler, event) (waiting_handler, event)
} }
s => bug!("`EventHandler` unexpectedly woke up {:?}", s), s => bug!("`EventHandler` unexpectedly woke up {:?}", s),
}; };
self.set_state(AppStateImpl::ProcessingEvents { self.set_state(AppStateImpl::ProcessingEvents {
event_handler, handler,
queued_gpu_redraws: Default::default(), queued_gpu_redraws: Default::default(),
active_control_flow: self.control_flow, active_control_flow: self.control_flow,
}); });
@ -343,25 +361,20 @@ impl AppState {
} }
} }
let (event_handler, queued_gpu_redraws, active_control_flow, processing_redraws) = let (handler, queued_gpu_redraws, active_control_flow, processing_redraws) =
match self.take_state() { match self.take_state() {
AppStateImpl::Launching { .. } AppStateImpl::Launching { .. }
| AppStateImpl::NotLaunched { .. } | AppStateImpl::NotLaunched { .. }
| AppStateImpl::InUserCallback { .. } => unreachable!(), | AppStateImpl::InUserCallback { .. } => unreachable!(),
AppStateImpl::ProcessingEvents { AppStateImpl::ProcessingEvents {
event_handler, handler,
queued_gpu_redraws, queued_gpu_redraws,
active_control_flow, active_control_flow,
} => ( } => (handler, queued_gpu_redraws, active_control_flow, false),
event_handler,
queued_gpu_redraws,
active_control_flow,
false,
),
AppStateImpl::ProcessingRedraws { AppStateImpl::ProcessingRedraws {
event_handler, handler,
active_control_flow, active_control_flow,
} => (event_handler, Default::default(), active_control_flow, true), } => (handler, Default::default(), active_control_flow, true),
AppStateImpl::PollFinished { .. } AppStateImpl::PollFinished { .. }
| AppStateImpl::Waiting { .. } | AppStateImpl::Waiting { .. }
| AppStateImpl::Terminated => unreachable!(), | AppStateImpl::Terminated => unreachable!(),
@ -371,23 +384,23 @@ impl AppState {
queued_gpu_redraws, queued_gpu_redraws,
}); });
UserCallbackTransitionResult::Success { UserCallbackTransitionResult::Success {
event_handler, handler,
active_control_flow, active_control_flow,
processing_redraws, processing_redraws,
} }
} }
fn main_events_cleared_transition(&mut self) -> HashSet<Id<WinitUIWindow>> { fn main_events_cleared_transition(&mut self) -> HashSet<Id<WinitUIWindow>> {
let (event_handler, queued_gpu_redraws, active_control_flow) = match self.take_state() { let (handler, queued_gpu_redraws, active_control_flow) = match self.take_state() {
AppStateImpl::ProcessingEvents { AppStateImpl::ProcessingEvents {
event_handler, handler,
queued_gpu_redraws, queued_gpu_redraws,
active_control_flow, active_control_flow,
} => (event_handler, queued_gpu_redraws, active_control_flow), } => (handler, queued_gpu_redraws, active_control_flow),
s => bug!("unexpected state {:?}", s), s => bug!("unexpected state {:?}", s),
}; };
self.set_state(AppStateImpl::ProcessingRedraws { self.set_state(AppStateImpl::ProcessingRedraws {
event_handler, handler,
active_control_flow, active_control_flow,
}); });
queued_gpu_redraws queued_gpu_redraws
@ -397,11 +410,11 @@ impl AppState {
if !self.has_launched() || self.has_terminated() { if !self.has_launched() || self.has_terminated() {
return; return;
} }
let (waiting_event_handler, old) = match self.take_state() { let (waiting_handler, old) = match self.take_state() {
AppStateImpl::ProcessingRedraws { AppStateImpl::ProcessingRedraws {
event_handler, handler,
active_control_flow, active_control_flow,
} => (event_handler, active_control_flow), } => (handler, active_control_flow),
s => bug!("unexpected state {:?}", s), s => bug!("unexpected state {:?}", s),
}; };
@ -410,7 +423,7 @@ impl AppState {
(ControlFlow::Wait, ControlFlow::Wait) => { (ControlFlow::Wait, ControlFlow::Wait) => {
let start = Instant::now(); let start = Instant::now();
self.set_state(AppStateImpl::Waiting { self.set_state(AppStateImpl::Waiting {
waiting_event_handler, waiting_handler,
start, start,
}); });
} }
@ -419,14 +432,14 @@ impl AppState {
{ {
let start = Instant::now(); let start = Instant::now();
self.set_state(AppStateImpl::Waiting { self.set_state(AppStateImpl::Waiting {
waiting_event_handler, waiting_handler,
start, start,
}); });
} }
(_, ControlFlow::Wait) => { (_, ControlFlow::Wait) => {
let start = Instant::now(); let start = Instant::now();
self.set_state(AppStateImpl::Waiting { self.set_state(AppStateImpl::Waiting {
waiting_event_handler, waiting_handler,
start, start,
}); });
self.waker.stop() self.waker.stop()
@ -434,24 +447,22 @@ impl AppState {
(_, ControlFlow::WaitUntil(new_instant)) => { (_, ControlFlow::WaitUntil(new_instant)) => {
let start = Instant::now(); let start = Instant::now();
self.set_state(AppStateImpl::Waiting { self.set_state(AppStateImpl::Waiting {
waiting_event_handler, waiting_handler,
start, start,
}); });
self.waker.start_at(new_instant) self.waker.start_at(new_instant)
} }
// Unlike on macOS, handle Poll to Poll transition here to call the waker // Unlike on macOS, handle Poll to Poll transition here to call the waker
(_, ControlFlow::Poll) => { (_, ControlFlow::Poll) => {
self.set_state(AppStateImpl::PollFinished { self.set_state(AppStateImpl::PollFinished { waiting_handler });
waiting_event_handler,
});
self.waker.start() self.waker.start()
} }
} }
} }
fn terminated_transition(&mut self) -> Box<dyn EventHandler> { fn terminated_transition(&mut self) -> EventLoopHandler {
match self.replace_state(AppStateImpl::Terminated) { match self.replace_state(AppStateImpl::Terminated) {
AppStateImpl::ProcessingEvents { event_handler, .. } => event_handler, AppStateImpl::ProcessingEvents { handler, .. } => handler,
s => bug!("`LoopExiting` happened while not processing events {:?}", s), s => bug!("`LoopExiting` happened while not processing events {:?}", s),
} }
} }
@ -516,8 +527,8 @@ pub(crate) fn queue_gl_or_metal_redraw(mtm: MainThreadMarker, window: Id<WinitUI
} }
} }
pub fn will_launch(mtm: MainThreadMarker, queued_event_handler: Box<dyn EventHandler>) { pub(crate) fn will_launch(mtm: MainThreadMarker, queued_handler: EventLoopHandler) {
AppState::get_mut(mtm).will_launch_transition(queued_event_handler) AppState::get_mut(mtm).will_launch_transition(queued_handler)
} }
pub fn did_finish_launching(mtm: MainThreadMarker) { pub fn did_finish_launching(mtm: MainThreadMarker) {
@ -594,17 +605,17 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
return; return;
} }
let (mut event_handler, active_control_flow, processing_redraws) = let (mut handler, active_control_flow, processing_redraws) =
match this.try_user_callback_transition() { match this.try_user_callback_transition() {
UserCallbackTransitionResult::ReentrancyPrevented { queued_events } => { UserCallbackTransitionResult::ReentrancyPrevented { queued_events } => {
queued_events.extend(events); queued_events.extend(events);
return; return;
} }
UserCallbackTransitionResult::Success { UserCallbackTransitionResult::Success {
event_handler, handler,
active_control_flow, active_control_flow,
processing_redraws, processing_redraws,
} => (event_handler, active_control_flow, processing_redraws), } => (handler, active_control_flow, processing_redraws),
}; };
drop(this); drop(this);
@ -619,11 +630,9 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
event event
); );
} }
event_handler.handle_nonuser_event(event) handler.handle_event(event)
}
EventWrapper::ScaleFactorChanged(event) => {
handle_hidpi_proxy(&mut event_handler, event)
} }
EventWrapper::ScaleFactorChanged(event) => handle_hidpi_proxy(&mut handler, event),
} }
} }
@ -650,12 +659,12 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
"redraw queued while processing redraws" "redraw queued while processing redraws"
); );
AppStateImpl::ProcessingRedraws { AppStateImpl::ProcessingRedraws {
event_handler, handler,
active_control_flow, active_control_flow,
} }
} else { } else {
AppStateImpl::ProcessingEvents { AppStateImpl::ProcessingEvents {
event_handler, handler,
queued_gpu_redraws, queued_gpu_redraws,
active_control_flow, active_control_flow,
} }
@ -675,11 +684,9 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
event event
); );
} }
event_handler.handle_nonuser_event(event) handler.handle_event(event)
}
EventWrapper::ScaleFactorChanged(event) => {
handle_hidpi_proxy(&mut event_handler, event)
} }
EventWrapper::ScaleFactorChanged(event) => handle_hidpi_proxy(&mut handler, event),
} }
} }
} }
@ -687,23 +694,23 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
fn handle_user_events(mtm: MainThreadMarker) { fn handle_user_events(mtm: MainThreadMarker) {
let mut this = AppState::get_mut(mtm); let mut this = AppState::get_mut(mtm);
let (mut event_handler, active_control_flow, processing_redraws) = let (mut handler, active_control_flow, processing_redraws) =
match this.try_user_callback_transition() { match this.try_user_callback_transition() {
UserCallbackTransitionResult::ReentrancyPrevented { .. } => { UserCallbackTransitionResult::ReentrancyPrevented { .. } => {
bug!("unexpected attempted to process an event") bug!("unexpected attempted to process an event")
} }
UserCallbackTransitionResult::Success { UserCallbackTransitionResult::Success {
event_handler, handler,
active_control_flow, active_control_flow,
processing_redraws, processing_redraws,
} => (event_handler, active_control_flow, processing_redraws), } => (handler, active_control_flow, processing_redraws),
}; };
if processing_redraws { if processing_redraws {
bug!("user events attempted to be sent out while `ProcessingRedraws`"); bug!("user events attempted to be sent out while `ProcessingRedraws`");
} }
drop(this); drop(this);
event_handler.handle_user_events(); handler.handle_event(Event::UserEvent(HandlePendingUserEvents));
loop { loop {
let mut this = AppState::get_mut(mtm); let mut this = AppState::get_mut(mtm);
@ -723,7 +730,7 @@ fn handle_user_events(mtm: MainThreadMarker) {
_ => unreachable!(), _ => unreachable!(),
}; };
this.app_state = Some(AppStateImpl::ProcessingEvents { this.app_state = Some(AppStateImpl::ProcessingEvents {
event_handler, handler,
queued_gpu_redraws, queued_gpu_redraws,
active_control_flow, active_control_flow,
}); });
@ -733,13 +740,12 @@ fn handle_user_events(mtm: MainThreadMarker) {
for wrapper in queued_events { for wrapper in queued_events {
match wrapper { match wrapper {
EventWrapper::StaticEvent(event) => event_handler.handle_nonuser_event(event), EventWrapper::StaticEvent(event) => handler.handle_event(event),
EventWrapper::ScaleFactorChanged(event) => { EventWrapper::ScaleFactorChanged(event) => handle_hidpi_proxy(&mut handler, event),
handle_hidpi_proxy(&mut event_handler, event)
}
} }
} }
event_handler.handle_user_events();
handler.handle_event(Event::UserEvent(HandlePendingUserEvents));
} }
} }
@ -779,13 +785,13 @@ pub fn handle_events_cleared(mtm: MainThreadMarker) {
pub fn terminated(mtm: MainThreadMarker) { pub fn terminated(mtm: MainThreadMarker) {
let mut this = AppState::get_mut(mtm); let mut this = AppState::get_mut(mtm);
let mut event_handler = this.terminated_transition(); let mut handler = this.terminated_transition();
drop(this); drop(this);
event_handler.handle_nonuser_event(Event::LoopExiting) handler.handle_event(Event::LoopExiting)
} }
fn handle_hidpi_proxy(event_handler: &mut Box<dyn EventHandler>, event: ScaleFactorChanged) { fn handle_hidpi_proxy(handler: &mut EventLoopHandler, event: ScaleFactorChanged) {
let ScaleFactorChanged { let ScaleFactorChanged {
suggested_size, suggested_size,
scale_factor, scale_factor,
@ -799,7 +805,7 @@ fn handle_hidpi_proxy(event_handler: &mut Box<dyn EventHandler>, event: ScaleFac
inner_size_writer: InnerSizeWriter::new(Arc::downgrade(&new_inner_size)), inner_size_writer: InnerSizeWriter::new(Arc::downgrade(&new_inner_size)),
}, },
}; };
event_handler.handle_nonuser_event(event); handler.handle_event(event);
let (view, screen_frame) = get_view_and_screen_frame(&window); let (view, screen_frame) = get_view_and_screen_frame(&window);
let physical_size = *new_inner_size.lock().unwrap(); let physical_size = *new_inner_size.lock().unwrap();
drop(new_inner_size); drop(new_inner_size);

View file

@ -1,7 +1,6 @@
use std::{ use std::{
collections::VecDeque, collections::VecDeque,
ffi::c_void, ffi::c_void,
fmt::{self, Debug},
marker::PhantomData, marker::PhantomData,
ptr, ptr,
sync::mpsc::{self, Receiver, Sender}, sync::mpsc::{self, Receiver, Sender},
@ -25,6 +24,7 @@ use crate::{
EventLoopWindowTarget as RootEventLoopWindowTarget, EventLoopWindowTarget as RootEventLoopWindowTarget,
}, },
platform::ios::Idiom, platform::ios::Idiom,
platform_impl::platform::app_state::{EventLoopHandler, HandlePendingUserEvents},
}; };
use super::{app_state, monitor, view, MonitorHandle}; use super::{app_state, monitor, view, MonitorHandle};
@ -108,6 +108,20 @@ impl OwnedDisplayHandle {
} }
} }
fn map_user_event<T: 'static>(
mut handler: impl FnMut(Event<T>, &RootEventLoopWindowTarget),
receiver: mpsc::Receiver<T>,
) -> impl FnMut(Event<HandlePendingUserEvents>, &RootEventLoopWindowTarget) {
move |event, window_target| match event.map_nonuser_event() {
Ok(event) => (handler)(event, window_target),
Err(_) => {
for event in receiver.try_iter() {
(handler)(Event::UserEvent(event), window_target);
}
}
}
}
pub struct EventLoop<T: 'static> { pub struct EventLoop<T: 'static> {
mtm: MainThreadMarker, mtm: MainThreadMarker,
sender: Sender<T>, sender: Sender<T>,
@ -151,43 +165,46 @@ impl<T: 'static> EventLoop<T> {
}) })
} }
pub fn run<F>(self, event_handler: F) -> ! pub fn run<F>(self, handler: F) -> !
where where
F: FnMut(Event<T>, &RootEventLoopWindowTarget), F: FnMut(Event<T>, &RootEventLoopWindowTarget),
{ {
unsafe { let application = UIApplication::shared(self.mtm);
let application = UIApplication::shared(self.mtm); assert!(
assert!( application.is_none(),
application.is_none(), "\
"\
`EventLoop` cannot be `run` after a call to `UIApplicationMain` on iOS\n\ `EventLoop` cannot be `run` after a call to `UIApplicationMain` on iOS\n\
Note: `EventLoop::run` calls `UIApplicationMain` on iOS", Note: `EventLoop::run` calls `UIApplicationMain` on iOS",
); );
let event_handler = std::mem::transmute::< let handler = map_user_event(handler, self.receiver);
Box<dyn FnMut(Event<T>, &RootEventLoopWindowTarget)>,
Box<EventHandlerCallback<T>>,
>(Box::new(event_handler));
let handler = EventLoopHandler { let handler = unsafe {
f: event_handler, std::mem::transmute::<
receiver: self.receiver, Box<dyn FnMut(Event<HandlePendingUserEvents>, &RootEventLoopWindowTarget)>,
event_loop: self.window_target, Box<dyn FnMut(Event<HandlePendingUserEvents>, &RootEventLoopWindowTarget)>,
}; >(Box::new(handler))
};
app_state::will_launch(self.mtm, Box::new(handler)); let handler = EventLoopHandler {
handler,
event_loop: self.window_target,
};
// Ensure application delegate is initialized app_state::will_launch(self.mtm, handler);
view::WinitApplicationDelegate::class();
// Ensure application delegate is initialized
view::WinitApplicationDelegate::class();
unsafe {
UIApplicationMain( UIApplicationMain(
0, 0,
ptr::null(), ptr::null(),
None, None,
Some(&NSString::from_str("WinitApplicationDelegate")), Some(&NSString::from_str("WinitApplicationDelegate")),
); )
unreachable!() };
} unreachable!()
} }
pub fn create_proxy(&self) -> EventLoopProxy<T> { pub fn create_proxy(&self) -> EventLoopProxy<T> {
@ -361,39 +378,3 @@ fn setup_control_flow_observers() {
CFRunLoopAddObserver(main_loop, end_observer, kCFRunLoopDefaultMode); CFRunLoopAddObserver(main_loop, end_observer, kCFRunLoopDefaultMode);
} }
} }
#[derive(Debug)]
pub enum Never {}
type EventHandlerCallback<T> = dyn FnMut(Event<T>, &RootEventLoopWindowTarget) + 'static;
pub trait EventHandler: Debug {
fn handle_nonuser_event(&mut self, event: Event<Never>);
fn handle_user_events(&mut self);
}
struct EventLoopHandler<T: 'static> {
f: Box<EventHandlerCallback<T>>,
receiver: Receiver<T>,
event_loop: RootEventLoopWindowTarget,
}
impl<T: 'static> Debug for EventLoopHandler<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("EventLoopHandler")
.field("event_loop", &self.event_loop)
.finish()
}
}
impl<T: 'static> EventHandler for EventLoopHandler<T> {
fn handle_nonuser_event(&mut self, event: Event<Never>) {
(self.f)(event.map_nonuser_event().unwrap(), &self.event_loop);
}
fn handle_user_events(&mut self) {
for event in self.receiver.try_iter() {
(self.f)(Event::UserEvent(event), &self.event_loop);
}
}
}

View file

@ -1,9 +1,8 @@
use std::cell::{Cell, RefCell, RefMut}; use std::cell::{Cell, RefCell};
use std::collections::VecDeque; use std::collections::VecDeque;
use std::fmt;
use std::mem; use std::mem;
use std::rc::{Rc, Weak}; use std::rc::{Rc, Weak};
use std::sync::{mpsc, Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::time::Instant; use std::time::Instant;
use icrate::AppKit::{NSApplication, NSApplicationActivationPolicy, NSApplicationDelegate}; use icrate::AppKit::{NSApplication, NSApplicationActivationPolicy, NSApplicationDelegate};
@ -14,7 +13,6 @@ use objc2::{declare_class, msg_send_id, mutability, ClassType, DeclaredClass};
use super::event_loop::{stop_app_immediately, PanicInfo}; use super::event_loop::{stop_app_immediately, PanicInfo};
use super::observer::{EventLoopWaker, RunLoop}; use super::observer::{EventLoopWaker, RunLoop};
use super::util::Never;
use super::window::WinitWindow; use super::window::WinitWindow;
use super::{menu, WindowId, DEVICE_ID}; use super::{menu, WindowId, DEVICE_ID};
use crate::dpi::PhysicalSize; use crate::dpi::PhysicalSize;
@ -30,7 +28,7 @@ pub(super) struct State {
/// Whether the application is currently executing a callback. /// Whether the application is currently executing a callback.
in_callback: Cell<bool>, in_callback: Cell<bool>,
/// The lifetime-erased callback. /// The lifetime-erased callback.
callback: RefCell<Option<Box<dyn EventHandler>>>, callback: RefCell<Option<EventLoopHandler>>,
stop_on_launch: Cell<bool>, stop_on_launch: Cell<bool>,
stop_before_wait: Cell<bool>, stop_before_wait: Cell<bool>,
stop_after_wait: Cell<bool>, stop_after_wait: Cell<bool>,
@ -158,17 +156,16 @@ impl ApplicationDelegate {
/// All public APIs that take an event callback (`run`, `run_on_demand`, /// All public APIs that take an event callback (`run`, `run_on_demand`,
/// `pump_events`) _must_ pair a call to `set_callback` with /// `pump_events`) _must_ pair a call to `set_callback` with
/// a call to `clear_callback` before returning to avoid undefined behaviour. /// a call to `clear_callback` before returning to avoid undefined behaviour.
pub unsafe fn set_callback<T>( #[allow(clippy::type_complexity)]
pub unsafe fn set_callback(
&self, &self,
callback: Weak<Callback<T>>, callback: Weak<RefCell<dyn FnMut(Event<HandlePendingUserEvents>, &RootWindowTarget)>>,
window_target: Rc<RootWindowTarget>, window_target: Rc<RootWindowTarget>,
receiver: Rc<mpsc::Receiver<T>>,
) { ) {
*self.ivars().callback.borrow_mut() = Some(Box::new(EventLoopHandler { *self.ivars().callback.borrow_mut() = Some(EventLoopHandler {
callback, callback,
window_target, window_target,
receiver, });
}));
} }
pub fn clear_callback(&self) { pub fn clear_callback(&self) {
@ -208,7 +205,7 @@ impl ApplicationDelegate {
/// and we won't need to re-launch the app if subsequent EventLoops are run. /// and we won't need to re-launch the app if subsequent EventLoops are run.
pub fn internal_exit(&self) { pub fn internal_exit(&self) {
self.set_in_callback(true); self.set_in_callback(true);
self.handle_nonuser_event(Event::LoopExiting); self.handle_event(Event::LoopExiting);
self.set_in_callback(false); self.set_in_callback(false);
self.set_is_running(false); self.set_is_running(false);
@ -290,7 +287,7 @@ impl ApplicationDelegate {
// Redraw request might come out of order from the OS. // Redraw request might come out of order from the OS.
// -> Don't go back into the callback when our callstack originates from there // -> Don't go back into the callback when our callstack originates from there
if !self.ivars().in_callback.get() { if !self.ivars().in_callback.get() {
self.handle_nonuser_event(Event::WindowEvent { self.handle_event(Event::WindowEvent {
window_id: RootWindowId(window_id), window_id: RootWindowId(window_id),
event: WindowEvent::RedrawRequested, event: WindowEvent::RedrawRequested,
}); });
@ -313,19 +310,19 @@ impl ApplicationDelegate {
unsafe { RunLoop::get() }.wakeup(); unsafe { RunLoop::get() }.wakeup();
} }
fn handle_nonuser_event(&self, event: Event<Never>) { fn handle_event(&self, event: Event<HandlePendingUserEvents>) {
if let Some(ref mut callback) = *self.ivars().callback.borrow_mut() { if let Some(ref mut callback) = *self.ivars().callback.borrow_mut() {
callback.handle_nonuser_event(event) callback.handle_event(event)
} }
} }
/// dispatch `NewEvents(Init)` + `Resumed` /// dispatch `NewEvents(Init)` + `Resumed`
pub fn dispatch_init_events(&self) { pub fn dispatch_init_events(&self) {
self.set_in_callback(true); self.set_in_callback(true);
self.handle_nonuser_event(Event::NewEvents(StartCause::Init)); self.handle_event(Event::NewEvents(StartCause::Init));
// NB: For consistency all platforms must emit a 'resumed' event even though macOS // NB: For consistency all platforms must emit a 'resumed' event even though macOS
// applications don't themselves have a formal suspend/resume lifecycle. // applications don't themselves have a formal suspend/resume lifecycle.
self.handle_nonuser_event(Event::Resumed); self.handle_event(Event::Resumed);
self.set_in_callback(false); self.set_in_callback(false);
} }
@ -373,7 +370,7 @@ impl ApplicationDelegate {
}; };
self.set_in_callback(true); self.set_in_callback(true);
self.handle_nonuser_event(Event::NewEvents(cause)); self.handle_event(Event::NewEvents(cause));
self.set_in_callback(false); self.set_in_callback(false);
} }
@ -396,21 +393,19 @@ impl ApplicationDelegate {
} }
self.set_in_callback(true); self.set_in_callback(true);
if let Some(ref mut callback) = *self.ivars().callback.borrow_mut() { self.handle_event(Event::UserEvent(HandlePendingUserEvents));
callback.handle_user_events();
}
let events = mem::take(&mut *self.ivars().pending_events.borrow_mut()); let events = mem::take(&mut *self.ivars().pending_events.borrow_mut());
for event in events { for event in events {
match event { match event {
QueuedEvent::WindowEvent(window_id, event) => { QueuedEvent::WindowEvent(window_id, event) => {
self.handle_nonuser_event(Event::WindowEvent { self.handle_event(Event::WindowEvent {
window_id: RootWindowId(window_id), window_id: RootWindowId(window_id),
event, event,
}); });
} }
QueuedEvent::DeviceEvent(event) => { QueuedEvent::DeviceEvent(event) => {
self.handle_nonuser_event(Event::DeviceEvent { self.handle_event(Event::DeviceEvent {
device_id: DEVICE_ID, device_id: DEVICE_ID,
event, event,
}); });
@ -432,7 +427,7 @@ impl ApplicationDelegate {
}, },
}; };
callback.handle_nonuser_event(scale_factor_changed_event); callback.handle_event(scale_factor_changed_event);
let physical_size = *new_inner_size.lock().unwrap(); let physical_size = *new_inner_size.lock().unwrap();
drop(new_inner_size); drop(new_inner_size);
@ -444,7 +439,7 @@ impl ApplicationDelegate {
window_id: RootWindowId(window.id()), window_id: RootWindowId(window.id()),
event: WindowEvent::Resized(physical_size), event: WindowEvent::Resized(physical_size),
}; };
callback.handle_nonuser_event(resized_event); callback.handle_event(resized_event);
} }
} }
} }
@ -452,13 +447,13 @@ impl ApplicationDelegate {
let redraw = mem::take(&mut *self.ivars().pending_redraw.borrow_mut()); let redraw = mem::take(&mut *self.ivars().pending_redraw.borrow_mut());
for window_id in redraw { for window_id in redraw {
self.handle_nonuser_event(Event::WindowEvent { self.handle_event(Event::WindowEvent {
window_id: RootWindowId(window_id), window_id: RootWindowId(window_id),
event: WindowEvent::RedrawRequested, event: WindowEvent::RedrawRequested,
}); });
} }
self.handle_nonuser_event(Event::AboutToWait); self.handle_event(Event::AboutToWait);
self.set_in_callback(false); self.set_in_callback(false);
if self.exiting() { if self.exiting() {
@ -495,34 +490,18 @@ pub(crate) enum QueuedEvent {
}, },
} }
trait EventHandler: fmt::Debug { #[derive(Debug)]
// Not sure probably it should accept Event<'static, Never> pub(crate) struct HandlePendingUserEvents;
fn handle_nonuser_event(&mut self, event: Event<Never>);
fn handle_user_events(&mut self);
}
pub(super) type Callback<T> = RefCell<dyn FnMut(Event<T>, &RootWindowTarget)>; #[derive(Debug)]
struct EventLoopHandler {
struct EventLoopHandler<T: 'static> { #[allow(clippy::type_complexity)]
callback: Weak<Callback<T>>, callback: Weak<RefCell<dyn FnMut(Event<HandlePendingUserEvents>, &RootWindowTarget)>>,
window_target: Rc<RootWindowTarget>, window_target: Rc<RootWindowTarget>,
receiver: Rc<mpsc::Receiver<T>>,
} }
impl<T> fmt::Debug for EventLoopHandler<T> { impl EventLoopHandler {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { fn handle_event(&mut self, event: Event<HandlePendingUserEvents>) {
formatter
.debug_struct("EventLoopHandler")
.field("window_target", &self.window_target)
.finish_non_exhaustive()
}
}
impl<T> EventLoopHandler<T> {
fn with_callback<F>(&mut self, f: F)
where
F: FnOnce(&mut EventLoopHandler<T>, RefMut<'_, dyn FnMut(Event<T>, &RootWindowTarget)>),
{
// `NSApplication` and our app delegate are global state and so it's possible // `NSApplication` and our app delegate are global state and so it's possible
// that we could get a delegate callback after the application has exit an // that we could get a delegate callback after the application has exit an
// `EventLoop`. If the loop has been exit then our weak `self.callback` // `EventLoop`. If the loop has been exit then our weak `self.callback`
@ -532,32 +511,12 @@ impl<T> EventLoopHandler<T> {
// upgrade the weak reference since it might be valid that the application // upgrade the weak reference since it might be valid that the application
// re-starts the `NSApplication` after exiting a Winit `EventLoop` // re-starts the `NSApplication` after exiting a Winit `EventLoop`
if let Some(callback) = self.callback.upgrade() { if let Some(callback) = self.callback.upgrade() {
let callback = callback.borrow_mut(); let mut callback = callback.borrow_mut();
(f)(self, callback); (callback)(event, &self.window_target);
} }
} }
} }
impl<T> EventHandler for EventLoopHandler<T> {
fn handle_nonuser_event(&mut self, event: Event<Never>) {
// `Never` can't be constructed, so the `UserEvent` variant can't
// be present here.
let event = event.map_nonuser_event().unwrap_or_else(|_| unreachable!());
self.with_callback(|this, mut callback| {
(callback)(event, &this.window_target);
});
}
fn handle_user_events(&mut self) {
self.with_callback(|this, mut callback| {
for event in this.receiver.try_iter() {
(callback)(Event::UserEvent(event), &this.window_target);
}
});
}
}
/// Returns the minimum `Option<Instant>`, taking into account that `None` /// Returns the minimum `Option<Instant>`, taking into account that `None`
/// equates to an infinite timeout, not a zero timeout (so can't just use /// equates to an infinite timeout, not a zero timeout (so can't just use
/// `Option::min`) /// `Option::min`)

View file

@ -31,7 +31,7 @@ use objc2::{
use super::event::dummy_event; use super::event::dummy_event;
use super::{ use super::{
app::WinitApplication, app::WinitApplication,
app_delegate::{ApplicationDelegate, Callback}, app_delegate::{ApplicationDelegate, HandlePendingUserEvents},
monitor::{self, MonitorHandle}, monitor::{self, MonitorHandle},
observer::setup_control_flow_observers, observer::setup_control_flow_observers,
}; };
@ -152,6 +152,20 @@ impl EventLoopWindowTarget {
} }
} }
fn map_user_event<T: 'static>(
mut handler: impl FnMut(Event<T>, &RootWindowTarget),
receiver: Rc<mpsc::Receiver<T>>,
) -> impl FnMut(Event<HandlePendingUserEvents>, &RootWindowTarget) {
move |event, window_target| match event.map_nonuser_event() {
Ok(event) => (handler)(event, window_target),
Err(_) => {
for event in receiver.try_iter() {
(handler)(Event::UserEvent(event), window_target);
}
}
}
}
pub struct EventLoop<T: 'static> { pub struct EventLoop<T: 'static> {
/// Store a reference to the application for convenience. /// Store a reference to the application for convenience.
/// ///
@ -177,7 +191,8 @@ pub struct EventLoop<T: 'static> {
/// Every other reference should be a Weak reference which is only upgraded /// Every other reference should be a Weak reference which is only upgraded
/// into a strong reference in order to call the callback but then the /// into a strong reference in order to call the callback but then the
/// strong reference should be dropped as soon as possible. /// strong reference should be dropped as soon as possible.
_callback: Option<Rc<Callback<T>>>, #[allow(clippy::type_complexity)]
_callback: Option<Rc<RefCell<dyn FnMut(Event<HandlePendingUserEvents>, &RootWindowTarget)>>>,
} }
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
@ -268,6 +283,8 @@ impl<T> EventLoop<T> {
return Err(EventLoopError::AlreadyRunning); return Err(EventLoopError::AlreadyRunning);
} }
let callback = map_user_event(callback, self.receiver.clone());
// # Safety // # Safety
// We are erasing the lifetime of the application callback here so that we // We are erasing the lifetime of the application callback here so that we
// can (temporarily) store it within 'static app delegate that's // can (temporarily) store it within 'static app delegate that's
@ -276,11 +293,10 @@ impl<T> EventLoop<T> {
// The safety of this depends on on making sure to also clear the callback // The safety of this depends on on making sure to also clear the callback
// from the app delegate before we return from here, ensuring that we don't // from the app delegate before we return from here, ensuring that we don't
// retain a reference beyond the real lifetime of the callback. // retain a reference beyond the real lifetime of the callback.
let callback = unsafe { let callback = unsafe {
mem::transmute::< mem::transmute::<
Rc<RefCell<dyn FnMut(Event<T>, &RootWindowTarget)>>, Rc<RefCell<dyn FnMut(Event<HandlePendingUserEvents>, &RootWindowTarget)>>,
Rc<RefCell<dyn FnMut(Event<T>, &RootWindowTarget)>>, Rc<RefCell<dyn FnMut(Event<HandlePendingUserEvents>, &RootWindowTarget)>>,
>(Rc::new(RefCell::new(callback))) >(Rc::new(RefCell::new(callback)))
}; };
@ -295,11 +311,8 @@ impl<T> EventLoop<T> {
// # Safety // # Safety
// We make sure to call `delegate.clear_callback` before returning // We make sure to call `delegate.clear_callback` before returning
unsafe { unsafe {
self.delegate.set_callback( self.delegate
weak_cb, .set_callback(weak_cb, Rc::clone(&self.window_target));
Rc::clone(&self.window_target),
Rc::clone(&self.receiver),
);
} }
// catch panics to make sure we can't unwind without clearing the set callback // catch panics to make sure we can't unwind without clearing the set callback
@ -348,6 +361,8 @@ impl<T> EventLoop<T> {
where where
F: FnMut(Event<T>, &RootWindowTarget), F: FnMut(Event<T>, &RootWindowTarget),
{ {
let callback = map_user_event(callback, self.receiver.clone());
// # Safety // # Safety
// We are erasing the lifetime of the application callback here so that we // We are erasing the lifetime of the application callback here so that we
// can (temporarily) store it within 'static global app delegate that's // can (temporarily) store it within 'static global app delegate that's
@ -359,8 +374,8 @@ impl<T> EventLoop<T> {
let callback = unsafe { let callback = unsafe {
mem::transmute::< mem::transmute::<
Rc<RefCell<dyn FnMut(Event<T>, &RootWindowTarget)>>, Rc<RefCell<dyn FnMut(Event<HandlePendingUserEvents>, &RootWindowTarget)>>,
Rc<RefCell<dyn FnMut(Event<T>, &RootWindowTarget)>>, Rc<RefCell<dyn FnMut(Event<HandlePendingUserEvents>, &RootWindowTarget)>>,
>(Rc::new(RefCell::new(callback))) >(Rc::new(RefCell::new(callback)))
}; };
@ -377,11 +392,8 @@ impl<T> EventLoop<T> {
// to ensure that we don't hold on to the callback beyond its (erased) // to ensure that we don't hold on to the callback beyond its (erased)
// lifetime // lifetime
unsafe { unsafe {
self.delegate.set_callback( self.delegate
weak_cb, .set_callback(weak_cb, Rc::clone(&self.window_target));
Rc::clone(&self.window_target),
Rc::clone(&self.receiver),
);
} }
// catch panics to make sure we can't unwind without clearing the set callback // catch panics to make sure we can't unwind without clearing the set callback