iOS: Never queue application-level events (#3905)

Events like `resumed`, `new_events`, `about_to_wait`, and so on will
never happen as a result of programmer action, so we'll never need to
queue those. This allows us to remove the need for the old `Event`
struct in the iOS backend.

Furthermore, we can now remove `InUserCallback`, since that state is
already stored inside `EventHandler`.

I've tried to otherwise keep the semantics as close to the original by
calling `handle_nonuser_events(mtm, [])`, which flushes pending events.
This commit is contained in:
Mads Marquart 2024-12-03 19:53:29 +01:00 committed by GitHub
parent 4a8b659228
commit 4d2a0dd2b3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 165 additions and 312 deletions

View file

@ -163,6 +163,7 @@ changelog entry.
- On X11, use bottom-right corner for IME hotspot in `Window::set_ime_cursor_area`. - On X11, use bottom-right corner for IME hotspot in `Window::set_ime_cursor_area`.
- On macOS and iOS, no longer emit `ScaleFactorChanged` upon window creation. - On macOS and iOS, no longer emit `ScaleFactorChanged` upon window creation.
- On macOS, no longer emit `Focused` upon window creation. - On macOS, no longer emit `Focused` upon window creation.
- On iOS, emit more events immediately, instead of queuing them.
### Removed ### Removed

View file

@ -106,7 +106,6 @@ impl EventHandler {
self.inner.try_borrow().is_err() self.inner.try_borrow().is_err()
} }
#[cfg(target_os = "macos")]
pub(crate) fn ready(&self) -> bool { pub(crate) fn ready(&self) -> bool {
matches!(self.inner.try_borrow().as_deref(), Ok(Some(_))) matches!(self.inner.try_borrow().as_deref(), Ok(Some(_)))
} }

View file

@ -27,8 +27,9 @@ use super::window::WinitUIWindow;
use super::{ActiveEventLoop, EventLoopProxy}; use super::{ActiveEventLoop, EventLoopProxy};
use crate::application::ApplicationHandler; use crate::application::ApplicationHandler;
use crate::dpi::PhysicalSize; use crate::dpi::PhysicalSize;
use crate::event::{Event, StartCause, SurfaceSizeWriter, WindowEvent}; use crate::event::{StartCause, SurfaceSizeWriter, WindowEvent};
use crate::event_loop::ControlFlow; use crate::event_loop::ControlFlow;
use crate::window::WindowId;
macro_rules! bug { macro_rules! bug {
($($msg:tt)*) => { ($($msg:tt)*) => {
@ -67,25 +68,9 @@ fn get_handler(mtm: MainThreadMarker) -> &'static EventHandler {
GLOBAL.get(mtm).get_or_init(EventHandler::new) GLOBAL.get(mtm).get_or_init(EventHandler::new)
} }
fn handle_event(mtm: MainThreadMarker, event: Event) {
let event_loop = &ActiveEventLoop { mtm };
get_handler(mtm).handle(|app| match event {
Event::NewEvents(cause) => app.new_events(event_loop, cause),
Event::WindowEvent { window_id, event } => app.window_event(event_loop, window_id, event),
Event::DeviceEvent { device_id, event } => app.device_event(event_loop, device_id, event),
Event::UserWakeUp => app.proxy_wake_up(event_loop),
Event::Suspended => app.suspended(event_loop),
Event::Resumed => app.resumed(event_loop),
Event::CreateSurfaces => app.can_create_surfaces(event_loop),
Event::AboutToWait => app.about_to_wait(event_loop),
Event::LoopExiting => app.exiting(event_loop),
Event::MemoryWarning => app.memory_warning(event_loop),
})
}
#[derive(Debug)] #[derive(Debug)]
pub(crate) enum EventWrapper { pub(crate) enum EventWrapper {
StaticEvent(Event), Window { window_id: WindowId, event: WindowEvent },
ScaleFactorChanged(ScaleFactorChanged), ScaleFactorChanged(ScaleFactorChanged),
} }
@ -96,14 +81,9 @@ pub struct ScaleFactorChanged {
pub(super) scale_factor: f64, pub(super) scale_factor: f64,
} }
enum UserCallbackTransitionResult<'a> { impl EventWrapper {
Success { active_control_flow: ControlFlow, processing_redraws: bool },
ReentrancyPrevented { queued_events: &'a mut Vec<EventWrapper> },
}
impl Event {
fn is_redraw(&self) -> bool { fn is_redraw(&self) -> bool {
matches!(self, Event::WindowEvent { event: WindowEvent::RedrawRequested, .. }) matches!(self, Self::Window { event: WindowEvent::RedrawRequested, .. })
} }
} }
@ -112,18 +92,12 @@ impl Event {
#[must_use = "dropping `AppStateImpl` without inspecting it is probably a bug"] #[must_use = "dropping `AppStateImpl` without inspecting it is probably a bug"]
enum AppStateImpl { enum AppStateImpl {
Initial { Initial {
queued_events: Vec<EventWrapper>,
queued_gpu_redraws: HashSet<Retained<WinitUIWindow>>, queued_gpu_redraws: HashSet<Retained<WinitUIWindow>>,
}, },
ProcessingEvents { ProcessingEvents {
queued_gpu_redraws: HashSet<Retained<WinitUIWindow>>, queued_gpu_redraws: HashSet<Retained<WinitUIWindow>>,
active_control_flow: ControlFlow, active_control_flow: ControlFlow,
}, },
// special state to deal with reentrancy and prevent mutable aliasing.
InUserCallback {
queued_events: Vec<EventWrapper>,
queued_gpu_redraws: HashSet<Retained<WinitUIWindow>>,
},
ProcessingRedraws { ProcessingRedraws {
active_control_flow: ControlFlow, active_control_flow: ControlFlow,
}, },
@ -140,6 +114,7 @@ pub(crate) struct AppState {
control_flow: ControlFlow, control_flow: ControlFlow,
waker: EventLoopWaker, waker: EventLoopWaker,
event_loop_proxy: Arc<EventLoopProxy>, event_loop_proxy: Arc<EventLoopProxy>,
queued_events: Vec<EventWrapper>,
} }
impl AppState { impl AppState {
@ -158,13 +133,11 @@ impl AppState {
fn init_guard(guard: &mut RefMut<'static, Option<AppState>>) { fn init_guard(guard: &mut RefMut<'static, Option<AppState>>) {
let waker = EventLoopWaker::new(unsafe { CFRunLoopGetMain() }); let waker = EventLoopWaker::new(unsafe { CFRunLoopGetMain() });
**guard = Some(AppState { **guard = Some(AppState {
app_state: Some(AppStateImpl::Initial { app_state: Some(AppStateImpl::Initial { queued_gpu_redraws: HashSet::new() }),
queued_events: Vec::new(),
queued_gpu_redraws: HashSet::new(),
}),
control_flow: ControlFlow::default(), control_flow: ControlFlow::default(),
waker, waker,
event_loop_proxy: Arc::new(EventLoopProxy::new()), event_loop_proxy: Arc::new(EventLoopProxy::new()),
queued_events: Vec::new(),
}); });
} }
init_guard(&mut guard); init_guard(&mut guard);
@ -217,48 +190,34 @@ impl AppState {
matches!(self.state(), AppStateImpl::Terminated) matches!(self.state(), AppStateImpl::Terminated)
} }
fn did_finish_launching_transition(&mut self) -> Vec<EventWrapper> { fn did_finish_launching_transition(&mut self) {
let (events, queued_gpu_redraws) = match self.take_state() { let queued_gpu_redraws = match self.take_state() {
AppStateImpl::Initial { queued_events, queued_gpu_redraws } => { AppStateImpl::Initial { queued_gpu_redraws } => queued_gpu_redraws,
(queued_events, queued_gpu_redraws)
},
s => bug!("unexpected state {:?}", s), s => bug!("unexpected state {:?}", s),
}; };
self.set_state(AppStateImpl::ProcessingEvents { self.set_state(AppStateImpl::ProcessingEvents {
active_control_flow: self.control_flow, active_control_flow: self.control_flow,
queued_gpu_redraws, queued_gpu_redraws,
}); });
events
} }
fn wakeup_transition(&mut self) -> Option<EventWrapper> { fn wakeup_transition(&mut self) -> Option<StartCause> {
// before `AppState::did_finish_launching` is called, pretend there is no running // before `AppState::did_finish_launching` is called, pretend there is no running
// event loop. // event loop.
if !self.has_launched() || self.has_terminated() { if !self.has_launched() || self.has_terminated() {
return None; return None;
} }
let event = match (self.control_flow, self.take_state()) { let start_cause = match (self.control_flow, self.take_state()) {
(ControlFlow::Poll, AppStateImpl::PollFinished) => { (ControlFlow::Poll, AppStateImpl::PollFinished) => StartCause::Poll,
EventWrapper::StaticEvent(Event::NewEvents(StartCause::Poll))
},
(ControlFlow::Wait, AppStateImpl::Waiting { start }) => { (ControlFlow::Wait, AppStateImpl::Waiting { start }) => {
EventWrapper::StaticEvent(Event::NewEvents(StartCause::WaitCancelled { StartCause::WaitCancelled { start, requested_resume: None }
start,
requested_resume: None,
}))
}, },
(ControlFlow::WaitUntil(requested_resume), AppStateImpl::Waiting { start }) => { (ControlFlow::WaitUntil(requested_resume), AppStateImpl::Waiting { start }) => {
if Instant::now() >= requested_resume { if Instant::now() >= requested_resume {
EventWrapper::StaticEvent(Event::NewEvents(StartCause::ResumeTimeReached { StartCause::ResumeTimeReached { start, requested_resume }
start,
requested_resume,
}))
} else { } else {
EventWrapper::StaticEvent(Event::NewEvents(StartCause::WaitCancelled { StartCause::WaitCancelled { start, requested_resume: Some(requested_resume) }
start,
requested_resume: Some(requested_resume),
}))
} }
}, },
s => bug!("`EventHandler` unexpectedly woke up {:?}", s), s => bug!("`EventHandler` unexpectedly woke up {:?}", s),
@ -268,55 +227,7 @@ impl AppState {
queued_gpu_redraws: Default::default(), queued_gpu_redraws: Default::default(),
active_control_flow: self.control_flow, active_control_flow: self.control_flow,
}); });
Some(event) Some(start_cause)
}
fn try_user_callback_transition(&mut self) -> UserCallbackTransitionResult<'_> {
// If we're not able to process an event due to recursion or `Init` not having been sent out
// yet, then queue the events up.
match self.state_mut() {
&mut AppStateImpl::Initial { ref mut queued_events, .. }
| &mut AppStateImpl::InUserCallback { ref mut queued_events, .. } => {
// A lifetime cast: early returns are not currently handled well with NLL, but
// polonius handles them well. This transmute is a safe workaround.
return unsafe {
mem::transmute::<
UserCallbackTransitionResult<'_>,
UserCallbackTransitionResult<'_>,
>(UserCallbackTransitionResult::ReentrancyPrevented {
queued_events,
})
};
},
&mut AppStateImpl::ProcessingEvents { .. }
| &mut AppStateImpl::ProcessingRedraws { .. } => {},
s @ &mut AppStateImpl::PollFinished { .. }
| s @ &mut AppStateImpl::Waiting { .. }
| s @ &mut AppStateImpl::Terminated => {
bug!("unexpected attempted to process an event {:?}", s)
},
}
let (queued_gpu_redraws, active_control_flow, processing_redraws) = match self.take_state()
{
AppStateImpl::Initial { .. } | AppStateImpl::InUserCallback { .. } => unreachable!(),
AppStateImpl::ProcessingEvents { queued_gpu_redraws, active_control_flow } => {
(queued_gpu_redraws, active_control_flow, false)
},
AppStateImpl::ProcessingRedraws { active_control_flow } => {
(Default::default(), active_control_flow, true)
},
AppStateImpl::PollFinished { .. }
| AppStateImpl::Waiting { .. }
| AppStateImpl::Terminated => unreachable!(),
};
self.set_state(AppStateImpl::InUserCallback {
queued_events: Vec::new(),
queued_gpu_redraws,
});
UserCallbackTransitionResult::Success { active_control_flow, processing_redraws }
} }
fn main_events_cleared_transition(&mut self) -> HashSet<Retained<WinitUIWindow>> { fn main_events_cleared_transition(&mut self) -> HashSet<Retained<WinitUIWindow>> {
@ -372,7 +283,7 @@ impl AppState {
fn terminated_transition(&mut self) { fn terminated_transition(&mut self) {
match self.replace_state(AppStateImpl::Terminated) { match self.replace_state(AppStateImpl::Terminated) {
AppStateImpl::ProcessingEvents { .. } => {}, AppStateImpl::ProcessingEvents { .. } => {},
s => bug!("`LoopExiting` happened while not processing events {:?}", s), s => bug!("terminated while not processing events {:?}", s),
} }
} }
@ -393,8 +304,7 @@ pub(crate) fn queue_gl_or_metal_redraw(mtm: MainThreadMarker, window: Retained<W
let mut this = AppState::get_mut(mtm); let mut this = AppState::get_mut(mtm);
match this.state_mut() { match this.state_mut() {
&mut AppStateImpl::Initial { ref mut queued_gpu_redraws, .. } &mut AppStateImpl::Initial { ref mut queued_gpu_redraws, .. }
| &mut AppStateImpl::ProcessingEvents { ref mut queued_gpu_redraws, .. } | &mut AppStateImpl::ProcessingEvents { ref mut queued_gpu_redraws, .. } => {
| &mut AppStateImpl::InUserCallback { ref mut queued_gpu_redraws, .. } => {
let _ = queued_gpu_redraws.insert(window); let _ = queued_gpu_redraws.insert(window);
}, },
s @ &mut AppStateImpl::ProcessingRedraws { .. } s @ &mut AppStateImpl::ProcessingRedraws { .. }
@ -418,27 +328,24 @@ pub fn did_finish_launching(mtm: MainThreadMarker) {
// have to drop RefMut because the window setup code below can trigger new events // have to drop RefMut because the window setup code below can trigger new events
drop(this); drop(this);
let events = AppState::get_mut(mtm).did_finish_launching_transition(); AppState::get_mut(mtm).did_finish_launching_transition();
let events = [ get_handler(mtm).handle(|app| app.new_events(&ActiveEventLoop { mtm }, StartCause::Init));
EventWrapper::StaticEvent(Event::NewEvents(StartCause::Init)), get_handler(mtm).handle(|app| app.can_create_surfaces(&ActiveEventLoop { mtm }));
EventWrapper::StaticEvent(Event::CreateSurfaces), handle_nonuser_events(mtm, []);
]
.into_iter()
.chain(events);
handle_nonuser_events(mtm, events);
} }
// AppState::did_finish_launching handles the special transition `Init` // AppState::did_finish_launching handles the special transition `Init`
pub fn handle_wakeup_transition(mtm: MainThreadMarker) { pub fn handle_wakeup_transition(mtm: MainThreadMarker) {
let mut this = AppState::get_mut(mtm); let mut this = AppState::get_mut(mtm);
let wakeup_event = match this.wakeup_transition() { let cause = match this.wakeup_transition() {
None => return, None => return,
Some(wakeup_event) => wakeup_event, Some(cause) => cause,
}; };
drop(this); drop(this);
handle_nonuser_event(mtm, wakeup_event) get_handler(mtm).handle(|app| app.new_events(&ActiveEventLoop { mtm }, cause));
handle_nonuser_events(mtm, []);
} }
pub(crate) fn handle_nonuser_event(mtm: MainThreadMarker, event: EventWrapper) { pub(crate) fn handle_nonuser_event(mtm: MainThreadMarker, event: EventWrapper) {
@ -454,132 +361,75 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
return; return;
} }
let (active_control_flow, processing_redraws) = match this.try_user_callback_transition() { if !get_handler(mtm).ready() {
UserCallbackTransitionResult::ReentrancyPrevented { queued_events } => { // Prevent re-entrancy; queue the events up for once we're done handling the event instead.
queued_events.extend(events); this.queued_events.extend(events);
return; return;
}, }
UserCallbackTransitionResult::Success { active_control_flow, processing_redraws } => {
(active_control_flow, processing_redraws) let processing_redraws = matches!(this.state(), AppStateImpl::ProcessingRedraws { .. });
},
};
drop(this); drop(this);
for wrapper in events { for event in events {
match wrapper { if !processing_redraws && event.is_redraw() {
EventWrapper::StaticEvent(event) => { tracing::info!("processing `RedrawRequested` during the main event loop");
if !processing_redraws && event.is_redraw() { } else if processing_redraws && !event.is_redraw() {
tracing::info!("processing `RedrawRequested` during the main event loop"); tracing::warn!(
} else if processing_redraws && !event.is_redraw() { "processing non `RedrawRequested` event after the main event loop: {:#?}",
tracing::warn!( event
"processing non `RedrawRequested` event after the main event loop: {:#?}", );
event
);
}
handle_event(mtm, event)
},
EventWrapper::ScaleFactorChanged(event) => handle_hidpi_proxy(mtm, event),
} }
handle_wrapped_event(mtm, event)
} }
loop { loop {
let mut this = AppState::get_mut(mtm); let mut this = AppState::get_mut(mtm);
let queued_events = match this.state_mut() { let queued_events = mem::take(&mut this.queued_events);
&mut AppStateImpl::InUserCallback { ref mut queued_events, queued_gpu_redraws: _ } => {
mem::take(queued_events)
},
s => bug!("unexpected state {:?}", s),
};
if queued_events.is_empty() { if queued_events.is_empty() {
let queued_gpu_redraws = match this.take_state() {
AppStateImpl::InUserCallback { queued_events: _, queued_gpu_redraws } => {
queued_gpu_redraws
},
_ => unreachable!(),
};
this.app_state = Some(if processing_redraws {
bug_assert!(
queued_gpu_redraws.is_empty(),
"redraw queued while processing redraws"
);
AppStateImpl::ProcessingRedraws { active_control_flow }
} else {
AppStateImpl::ProcessingEvents { queued_gpu_redraws, active_control_flow }
});
break; break;
} }
drop(this); drop(this);
for wrapper in queued_events { for event in queued_events {
match wrapper { if !processing_redraws && event.is_redraw() {
EventWrapper::StaticEvent(event) => { tracing::info!("processing `RedrawRequested` during the main event loop");
if !processing_redraws && event.is_redraw() { } else if processing_redraws && !event.is_redraw() {
tracing::info!("processing `RedrawRequested` during the main event loop"); tracing::warn!(
} else if processing_redraws && !event.is_redraw() { "processing non-`RedrawRequested` event after the main event loop: {:#?}",
tracing::warn!( event
"processing non-`RedrawRequested` event after the main event loop: \ );
{:#?}",
event
);
}
handle_event(mtm, event)
},
EventWrapper::ScaleFactorChanged(event) => handle_hidpi_proxy(mtm, event),
} }
handle_wrapped_event(mtm, event);
} }
} }
} }
fn handle_user_events(mtm: MainThreadMarker) { fn handle_user_events(mtm: MainThreadMarker) {
let mut this = AppState::get_mut(mtm); let this = AppState::get_mut(mtm);
let (active_control_flow, processing_redraws) = match this.try_user_callback_transition() { if matches!(this.state(), AppStateImpl::ProcessingRedraws { .. }) {
UserCallbackTransitionResult::ReentrancyPrevented { .. } => {
bug!("unexpected attempted to process an event")
},
UserCallbackTransitionResult::Success { active_control_flow, processing_redraws } => {
(active_control_flow, 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`");
} }
let event_loop_proxy = this.event_loop_proxy().clone(); let event_loop_proxy = this.event_loop_proxy().clone();
drop(this); drop(this);
if event_loop_proxy.wake_up.swap(false, Ordering::Relaxed) { if event_loop_proxy.wake_up.swap(false, Ordering::Relaxed) {
handle_event(mtm, Event::UserWakeUp); get_handler(mtm).handle(|app| app.proxy_wake_up(&ActiveEventLoop { mtm }));
} }
loop { loop {
let mut this = AppState::get_mut(mtm); let mut this = AppState::get_mut(mtm);
let queued_events = match this.state_mut() { let queued_events = mem::take(&mut this.queued_events);
&mut AppStateImpl::InUserCallback { ref mut queued_events, queued_gpu_redraws: _ } => {
mem::take(queued_events)
},
s => bug!("unexpected state {:?}", s),
};
if queued_events.is_empty() { if queued_events.is_empty() {
let queued_gpu_redraws = match this.take_state() {
AppStateImpl::InUserCallback { queued_events: _, queued_gpu_redraws } => {
queued_gpu_redraws
},
_ => unreachable!(),
};
this.app_state =
Some(AppStateImpl::ProcessingEvents { queued_gpu_redraws, active_control_flow });
break; break;
} }
drop(this); drop(this);
for wrapper in queued_events { for event in queued_events {
match wrapper { handle_wrapped_event(mtm, event);
EventWrapper::StaticEvent(event) => handle_event(mtm, event),
EventWrapper::ScaleFactorChanged(event) => handle_hidpi_proxy(mtm, event),
}
} }
if event_loop_proxy.wake_up.swap(false, Ordering::Relaxed) { if event_loop_proxy.wake_up.swap(false, Ordering::Relaxed) {
handle_event(mtm, Event::UserWakeUp); get_handler(mtm).handle(|app| app.proxy_wake_up(&ActiveEventLoop { mtm }));
} }
} }
} }
@ -597,10 +447,10 @@ pub(crate) fn send_occluded_event_for_all_windows(application: &UIApplication, o
let ptr: *const WinitUIWindow = ptr.cast(); let ptr: *const WinitUIWindow = ptr.cast();
&*ptr &*ptr
}; };
events.push(EventWrapper::StaticEvent(Event::WindowEvent { events.push(EventWrapper::Window {
window_id: window.id(), window_id: window.id(),
event: WindowEvent::Occluded(occluded), event: WindowEvent::Occluded(occluded),
})); });
} }
} }
handle_nonuser_events(mtm, events); handle_nonuser_events(mtm, events);
@ -623,23 +473,37 @@ pub fn handle_main_events_cleared(mtm: MainThreadMarker) {
let redraw_events: Vec<EventWrapper> = this let redraw_events: Vec<EventWrapper> = this
.main_events_cleared_transition() .main_events_cleared_transition()
.into_iter() .into_iter()
.map(|window| { .map(|window| EventWrapper::Window {
EventWrapper::StaticEvent(Event::WindowEvent { window_id: window.id(),
window_id: window.id(), event: WindowEvent::RedrawRequested,
event: WindowEvent::RedrawRequested,
})
}) })
.collect(); .collect();
drop(this); drop(this);
handle_nonuser_events(mtm, redraw_events); handle_nonuser_events(mtm, redraw_events);
handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::AboutToWait)); get_handler(mtm).handle(|app| app.about_to_wait(&ActiveEventLoop { mtm }));
handle_nonuser_events(mtm, []);
} }
pub fn handle_events_cleared(mtm: MainThreadMarker) { pub fn handle_events_cleared(mtm: MainThreadMarker) {
AppState::get_mut(mtm).events_cleared_transition(); AppState::get_mut(mtm).events_cleared_transition();
} }
pub(crate) fn handle_resumed(mtm: MainThreadMarker) {
get_handler(mtm).handle(|app| app.resumed(&ActiveEventLoop { mtm }));
handle_nonuser_events(mtm, []);
}
pub(crate) fn handle_suspended(mtm: MainThreadMarker) {
get_handler(mtm).handle(|app| app.suspended(&ActiveEventLoop { mtm }));
handle_nonuser_events(mtm, []);
}
pub(crate) fn handle_memory_warning(mtm: MainThreadMarker) {
get_handler(mtm).handle(|app| app.memory_warning(&ActiveEventLoop { mtm }));
handle_nonuser_events(mtm, []);
}
pub(crate) fn terminated(application: &UIApplication) { pub(crate) fn terminated(application: &UIApplication) {
let mtm = MainThreadMarker::from(application); let mtm = MainThreadMarker::from(application);
@ -653,10 +517,10 @@ pub(crate) fn terminated(application: &UIApplication) {
let ptr: *const WinitUIWindow = ptr.cast(); let ptr: *const WinitUIWindow = ptr.cast();
&*ptr &*ptr
}; };
events.push(EventWrapper::StaticEvent(Event::WindowEvent { events.push(EventWrapper::Window {
window_id: window.id(), window_id: window.id(),
event: WindowEvent::Destroyed, event: WindowEvent::Destroyed,
})); });
} }
} }
handle_nonuser_events(mtm, events); handle_nonuser_events(mtm, events);
@ -665,20 +529,26 @@ pub(crate) fn terminated(application: &UIApplication) {
this.terminated_transition(); this.terminated_transition();
drop(this); drop(this);
handle_event(mtm, Event::LoopExiting) get_handler(mtm).handle(|app| app.exiting(&ActiveEventLoop { mtm }));
}
fn handle_wrapped_event(mtm: MainThreadMarker, event: EventWrapper) {
match event {
EventWrapper::Window { window_id, event } => get_handler(mtm)
.handle(|app| app.window_event(&ActiveEventLoop { mtm }, window_id, event)),
EventWrapper::ScaleFactorChanged(event) => handle_hidpi_proxy(mtm, event),
}
} }
fn handle_hidpi_proxy(mtm: MainThreadMarker, event: ScaleFactorChanged) { fn handle_hidpi_proxy(mtm: MainThreadMarker, event: ScaleFactorChanged) {
let ScaleFactorChanged { suggested_size, scale_factor, window } = event; let ScaleFactorChanged { suggested_size, scale_factor, window } = event;
let new_surface_size = Arc::new(Mutex::new(suggested_size)); let new_surface_size = Arc::new(Mutex::new(suggested_size));
let event = Event::WindowEvent { get_handler(mtm).handle(|app| {
window_id: window.id(), app.window_event(&ActiveEventLoop { mtm }, window.id(), WindowEvent::ScaleFactorChanged {
event: WindowEvent::ScaleFactorChanged {
scale_factor, scale_factor,
surface_size_writer: SurfaceSizeWriter::new(Arc::downgrade(&new_surface_size)), surface_size_writer: SurfaceSizeWriter::new(Arc::downgrade(&new_surface_size)),
}, });
}; });
handle_event(mtm, 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_surface_size.lock().unwrap(); let physical_size = *new_surface_size.lock().unwrap();
drop(new_surface_size); drop(new_surface_size);

View file

@ -23,11 +23,10 @@ use objc2_ui_kit::{
use rwh_06::HasDisplayHandle; use rwh_06::HasDisplayHandle;
use super::super::notification_center::create_observer; use super::super::notification_center::create_observer;
use super::app_state::{send_occluded_event_for_all_windows, AppState, EventWrapper}; use super::app_state::{send_occluded_event_for_all_windows, AppState};
use super::{app_state, monitor, MonitorHandle}; use super::{app_state, monitor, MonitorHandle};
use crate::application::ApplicationHandler; use crate::application::ApplicationHandler;
use crate::error::{EventLoopError, NotSupportedError, RequestError}; use crate::error::{EventLoopError, NotSupportedError, RequestError};
use crate::event::Event;
use crate::event_loop::{ use crate::event_loop::{
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents, ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider, EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider,
@ -174,17 +173,13 @@ impl EventLoop {
&center, &center,
// `applicationDidBecomeActive:` // `applicationDidBecomeActive:`
unsafe { UIApplicationDidBecomeActiveNotification }, unsafe { UIApplicationDidBecomeActiveNotification },
move |_| { move |_| app_state::handle_resumed(mtm),
app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::Resumed));
},
); );
let _will_resign_active_observer = create_observer( let _will_resign_active_observer = create_observer(
&center, &center,
// `applicationWillResignActive:` // `applicationWillResignActive:`
unsafe { UIApplicationWillResignActiveNotification }, unsafe { UIApplicationWillResignActiveNotification },
move |_| { move |_| app_state::handle_suspended(mtm),
app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::Suspended));
},
); );
let _will_enter_foreground_observer = create_observer( let _will_enter_foreground_observer = create_observer(
&center, &center,
@ -231,12 +226,7 @@ impl EventLoop {
&center, &center,
// `applicationDidReceiveMemoryWarning:` // `applicationDidReceiveMemoryWarning:`
unsafe { UIApplicationDidReceiveMemoryWarningNotification }, unsafe { UIApplicationDidReceiveMemoryWarningNotification },
move |_| { move |_| app_state::handle_memory_warning(mtm),
app_state::handle_nonuser_event(
mtm,
EventWrapper::StaticEvent(Event::MemoryWarning),
);
},
); );
Ok(EventLoop { Ok(EventLoop {

View file

@ -17,8 +17,8 @@ use super::app_state::{self, EventWrapper};
use super::window::WinitUIWindow; use super::window::WinitUIWindow;
use crate::dpi::PhysicalPosition; use crate::dpi::PhysicalPosition;
use crate::event::{ use crate::event::{
ButtonSource, ElementState, Event, FingerId, Force, KeyEvent, PointerKind, PointerSource, ButtonSource, ElementState, FingerId, Force, KeyEvent, PointerKind, PointerSource, TouchPhase,
TouchPhase, WindowEvent, WindowEvent,
}; };
use crate::keyboard::{Key, KeyCode, KeyLocation, NamedKey, NativeKeyCode, PhysicalKey}; use crate::keyboard::{Key, KeyCode, KeyLocation, NamedKey, NativeKeyCode, PhysicalKey};
use crate::platform_impl::KeyEventExtra; use crate::platform_impl::KeyEventExtra;
@ -60,10 +60,10 @@ declare_class!(
let window = self.window().unwrap(); let window = self.window().unwrap();
app_state::handle_nonuser_event( app_state::handle_nonuser_event(
mtm, mtm,
EventWrapper::StaticEvent(Event::WindowEvent { EventWrapper::Window {
window_id: window.id(), window_id: window.id(),
event: WindowEvent::RedrawRequested, event: WindowEvent::RedrawRequested,
}), },
); );
let _: () = unsafe { msg_send![super(self), drawRect: rect] }; let _: () = unsafe { msg_send![super(self), drawRect: rect] };
} }
@ -84,10 +84,10 @@ declare_class!(
let window = self.window().unwrap(); let window = self.window().unwrap();
app_state::handle_nonuser_event( app_state::handle_nonuser_event(
mtm, mtm,
EventWrapper::StaticEvent(Event::WindowEvent { EventWrapper::Window {
window_id: window.id(), window_id: window.id(),
event: WindowEvent::SurfaceResized(size), event: WindowEvent::SurfaceResized(size),
}), },
); );
} }
@ -131,12 +131,11 @@ declare_class!(
suggested_size: size.to_physical(scale_factor), suggested_size: size.to_physical(scale_factor),
}, },
)) ))
.chain(std::iter::once(EventWrapper::StaticEvent( .chain(std::iter::once(EventWrapper::Window {
Event::WindowEvent {
window_id, window_id,
event: WindowEvent::SurfaceResized(size.to_physical(scale_factor)), event: WindowEvent::SurfaceResized(size.to_physical(scale_factor)),
}, },
))), )),
); );
} }
@ -192,14 +191,14 @@ declare_class!(
state => panic!("unexpected recognizer state: {state:?}"), state => panic!("unexpected recognizer state: {state:?}"),
}; };
let gesture_event = EventWrapper::StaticEvent(Event::WindowEvent { let gesture_event = EventWrapper::Window {
window_id: window.id(), window_id: window.id(),
event: WindowEvent::PinchGesture { event: WindowEvent::PinchGesture {
device_id: None, device_id: None,
delta: delta as f64, delta: delta as f64,
phase, phase,
}, },
}); };
let mtm = MainThreadMarker::new().unwrap(); let mtm = MainThreadMarker::new().unwrap();
app_state::handle_nonuser_event(mtm, gesture_event); app_state::handle_nonuser_event(mtm, gesture_event);
@ -210,12 +209,12 @@ declare_class!(
let window = self.window().unwrap(); let window = self.window().unwrap();
if recognizer.state() == UIGestureRecognizerState::Ended { if recognizer.state() == UIGestureRecognizerState::Ended {
let gesture_event = EventWrapper::StaticEvent(Event::WindowEvent { let gesture_event = EventWrapper::Window {
window_id: window.id(), window_id: window.id(),
event: WindowEvent::DoubleTapGesture { event: WindowEvent::DoubleTapGesture {
device_id: None, device_id: None,
}, },
}); };
let mtm = MainThreadMarker::new().unwrap(); let mtm = MainThreadMarker::new().unwrap();
app_state::handle_nonuser_event(mtm, gesture_event); app_state::handle_nonuser_event(mtm, gesture_event);
@ -252,14 +251,14 @@ declare_class!(
}; };
// Make delta negative to match macos, convert to degrees // Make delta negative to match macos, convert to degrees
let gesture_event = EventWrapper::StaticEvent(Event::WindowEvent { let gesture_event = EventWrapper::Window {
window_id: window.id(), window_id: window.id(),
event: WindowEvent::RotationGesture { event: WindowEvent::RotationGesture {
device_id: None, device_id: None,
delta: -delta.to_degrees() as _, delta: -delta.to_degrees() as _,
phase, phase,
}, },
}); };
let mtm = MainThreadMarker::new().unwrap(); let mtm = MainThreadMarker::new().unwrap();
app_state::handle_nonuser_event(mtm, gesture_event); app_state::handle_nonuser_event(mtm, gesture_event);
@ -303,14 +302,14 @@ declare_class!(
}; };
let gesture_event = EventWrapper::StaticEvent(Event::WindowEvent { let gesture_event = EventWrapper::Window {
window_id: window.id(), window_id: window.id(),
event: WindowEvent::PanGesture { event: WindowEvent::PanGesture {
device_id: None, device_id: None,
delta: PhysicalPosition::new(dx as _, dy as _), delta: PhysicalPosition::new(dx as _, dy as _),
phase, phase,
}, },
}); };
let mtm = MainThreadMarker::new().unwrap(); let mtm = MainThreadMarker::new().unwrap();
app_state::handle_nonuser_event(mtm, gesture_event); app_state::handle_nonuser_event(mtm, gesture_event);
@ -538,7 +537,7 @@ impl WinitView {
} }
}; };
touch_events.push(EventWrapper::StaticEvent(Event::WindowEvent { touch_events.push(EventWrapper::Window {
window_id, window_id,
event: WindowEvent::PointerEntered { event: WindowEvent::PointerEntered {
device_id: None, device_id: None,
@ -550,8 +549,8 @@ impl WinitView {
PointerKind::Touch(finger_id) PointerKind::Touch(finger_id)
}, },
}, },
})); });
touch_events.push(EventWrapper::StaticEvent(Event::WindowEvent { touch_events.push(EventWrapper::Window {
window_id, window_id,
event: WindowEvent::PointerButton { event: WindowEvent::PointerButton {
device_id: None, device_id: None,
@ -564,7 +563,7 @@ impl WinitView {
ButtonSource::Touch { finger_id, force } ButtonSource::Touch { finger_id, force }
}, },
}, },
})); });
}, },
UITouchPhase::Moved => { UITouchPhase::Moved => {
let (primary, source) = if let UITouchType::Pencil = touch_type { let (primary, source) = if let UITouchType::Pencil = touch_type {
@ -576,7 +575,7 @@ impl WinitView {
}) })
}; };
touch_events.push(EventWrapper::StaticEvent(Event::WindowEvent { touch_events.push(EventWrapper::Window {
window_id, window_id,
event: WindowEvent::PointerMoved { event: WindowEvent::PointerMoved {
device_id: None, device_id: None,
@ -584,7 +583,7 @@ impl WinitView {
position, position,
source, source,
}, },
})); });
}, },
// 2 is UITouchPhase::Stationary and is not expected here // 2 is UITouchPhase::Stationary and is not expected here
UITouchPhase::Ended | UITouchPhase::Cancelled => { UITouchPhase::Ended | UITouchPhase::Cancelled => {
@ -600,7 +599,7 @@ impl WinitView {
}; };
if let UITouchPhase::Ended = phase { if let UITouchPhase::Ended = phase {
touch_events.push(EventWrapper::StaticEvent(Event::WindowEvent { touch_events.push(EventWrapper::Window {
window_id, window_id,
event: WindowEvent::PointerButton { event: WindowEvent::PointerButton {
device_id: None, device_id: None,
@ -613,10 +612,10 @@ impl WinitView {
ButtonSource::Touch { finger_id, force } ButtonSource::Touch { finger_id, force }
}, },
}, },
})); });
} }
touch_events.push(EventWrapper::StaticEvent(Event::WindowEvent { touch_events.push(EventWrapper::Window {
window_id, window_id,
event: WindowEvent::PointerLeft { event: WindowEvent::PointerLeft {
device_id: None, device_id: None,
@ -628,7 +627,7 @@ impl WinitView {
PointerKind::Touch(finger_id) PointerKind::Touch(finger_id)
}, },
}, },
})); });
}, },
_ => panic!("unexpected touch phase: {phase:?}"), _ => panic!("unexpected touch phase: {phase:?}"),
} }
@ -647,29 +646,25 @@ impl WinitView {
text.to_string().chars().flat_map(|c| { text.to_string().chars().flat_map(|c| {
let text = smol_str::SmolStr::from_iter([c]); let text = smol_str::SmolStr::from_iter([c]);
// Emit both press and release events // Emit both press and release events
[ElementState::Pressed, ElementState::Released].map(|state| { [ElementState::Pressed, ElementState::Released].map(|state| EventWrapper::Window {
EventWrapper::StaticEvent(Event::WindowEvent { window_id,
window_id, event: WindowEvent::KeyboardInput {
event: WindowEvent::KeyboardInput { device_id: None,
event: KeyEvent { event: KeyEvent {
text: if state == ElementState::Pressed { text: if state == ElementState::Pressed {
Some(text.clone()) Some(text.clone())
} else { } else {
None None
},
state,
location: KeyLocation::Standard,
repeat: false,
logical_key: Key::Character(text.clone()),
physical_key: PhysicalKey::Unidentified(
NativeKeyCode::Unidentified,
),
platform_specific: KeyEventExtra {},
}, },
is_synthetic: false, state,
device_id: None, location: KeyLocation::Standard,
repeat: false,
logical_key: Key::Character(text.clone()),
physical_key: PhysicalKey::Unidentified(NativeKeyCode::Unidentified),
platform_specific: KeyEventExtra {},
}, },
}) is_synthetic: false,
},
}) })
}), }),
); );
@ -681,23 +676,21 @@ impl WinitView {
let mtm = MainThreadMarker::new().unwrap(); let mtm = MainThreadMarker::new().unwrap();
app_state::handle_nonuser_events( app_state::handle_nonuser_events(
mtm, mtm,
[ElementState::Pressed, ElementState::Released].map(|state| { [ElementState::Pressed, ElementState::Released].map(|state| EventWrapper::Window {
EventWrapper::StaticEvent(Event::WindowEvent { window_id,
window_id, event: WindowEvent::KeyboardInput {
event: WindowEvent::KeyboardInput { device_id: None,
device_id: None, event: KeyEvent {
event: KeyEvent { state,
state, logical_key: Key::Named(NamedKey::Backspace),
logical_key: Key::Named(NamedKey::Backspace), physical_key: PhysicalKey::Code(KeyCode::Backspace),
physical_key: PhysicalKey::Code(KeyCode::Backspace), platform_specific: KeyEventExtra {},
platform_specific: KeyEventExtra {}, repeat: false,
repeat: false, location: KeyLocation::Standard,
location: KeyLocation::Standard, text: None,
text: None,
},
is_synthetic: false,
}, },
}) is_synthetic: false,
},
}), }),
); );
} }

View file

@ -23,7 +23,7 @@ use crate::dpi::{
Position, Size, Position, Size,
}; };
use crate::error::{NotSupportedError, RequestError}; use crate::error::{NotSupportedError, RequestError};
use crate::event::{Event, WindowEvent}; use crate::event::WindowEvent;
use crate::icon::Icon; use crate::icon::Icon;
use crate::monitor::MonitorHandle as CoreMonitorHandle; use crate::monitor::MonitorHandle as CoreMonitorHandle;
use crate::platform::ios::{ScreenEdge, StatusBarStyle, ValidOrientations}; use crate::platform::ios::{ScreenEdge, StatusBarStyle, ValidOrientations};
@ -51,10 +51,10 @@ declare_class!(
let mtm = MainThreadMarker::new().unwrap(); let mtm = MainThreadMarker::new().unwrap();
app_state::handle_nonuser_event( app_state::handle_nonuser_event(
mtm, mtm,
EventWrapper::StaticEvent(Event::WindowEvent { EventWrapper::Window {
window_id: self.id(), window_id: self.id(),
event: WindowEvent::Focused(true), event: WindowEvent::Focused(true),
}), },
); );
let _: () = unsafe { msg_send![super(self), becomeKeyWindow] }; let _: () = unsafe { msg_send![super(self), becomeKeyWindow] };
} }
@ -64,10 +64,10 @@ declare_class!(
let mtm = MainThreadMarker::new().unwrap(); let mtm = MainThreadMarker::new().unwrap();
app_state::handle_nonuser_event( app_state::handle_nonuser_event(
mtm, mtm,
EventWrapper::StaticEvent(Event::WindowEvent { EventWrapper::Window {
window_id: self.id(), window_id: self.id(),
event: WindowEvent::Focused(false), event: WindowEvent::Focused(false),
}), },
); );
let _: () = unsafe { msg_send![super(self), resignKeyWindow] }; let _: () = unsafe { msg_send![super(self), resignKeyWindow] };
} }