mirror of
https://github.com/rust-windowing/winit.git
synced 2026-08-29 04:40:04 -04:00
iOS: Avoid RefCell and static mut (#4255)
* iOS: Refactor queued_gpu_redraws out from AppStateImpl To allow AppStateImpl to be Copy, and to move redraws into the window in the future. * iOS AppState: Avoid RefCell and static mut Instead, prefer Cell and Copy types, as those will never have crashes on re-entrancy / if forgetting to make a state transition.
This commit is contained in:
@@ -1,11 +1,11 @@
|
|||||||
#![deny(unused_results)]
|
#![deny(unused_results)]
|
||||||
|
|
||||||
use std::cell::{OnceCell, RefCell, RefMut};
|
use std::cell::{Cell, OnceCell};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::os::raw::c_void;
|
use std::os::raw::c_void;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use std::{mem, ptr};
|
use std::{fmt, ptr};
|
||||||
|
|
||||||
use dispatch2::MainThreadBound;
|
use dispatch2::MainThreadBound;
|
||||||
use dpi::PhysicalSize;
|
use dpi::PhysicalSize;
|
||||||
@@ -32,12 +32,6 @@ macro_rules! bug {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! bug_assert {
|
|
||||||
($test:expr, $($msg:tt)*) => {
|
|
||||||
assert!($test, "winit iOS bug, file an issue: {}", format!($($msg)*))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the global event handler for the application.
|
/// Get the global event handler for the application.
|
||||||
///
|
///
|
||||||
/// This is stored separately from AppState, since AppState needs to be accessible while the handler
|
/// This is stored separately from AppState, since AppState needs to be accessible while the handler
|
||||||
@@ -71,131 +65,90 @@ impl EventWrapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// this is the state machine for the app lifecycle
|
// this is the state machine for the app lifecycle
|
||||||
#[derive(Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
#[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_gpu_redraws: HashSet<Retained<WinitUIWindow>>,
|
ProcessingEvents { active_control_flow: ControlFlow },
|
||||||
},
|
ProcessingRedraws { active_control_flow: ControlFlow },
|
||||||
ProcessingEvents {
|
Waiting { start: Instant },
|
||||||
queued_gpu_redraws: HashSet<Retained<WinitUIWindow>>,
|
|
||||||
active_control_flow: ControlFlow,
|
|
||||||
},
|
|
||||||
ProcessingRedraws {
|
|
||||||
active_control_flow: ControlFlow,
|
|
||||||
},
|
|
||||||
Waiting {
|
|
||||||
start: Instant,
|
|
||||||
},
|
|
||||||
PollFinished,
|
PollFinished,
|
||||||
Terminated,
|
Terminated,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct AppState {
|
pub(crate) struct AppState {
|
||||||
// This should never be `None`, except for briefly during a state transition.
|
state: Cell<AppStateImpl>,
|
||||||
app_state: Option<AppStateImpl>,
|
control_flow: Cell<ControlFlow>,
|
||||||
control_flow: ControlFlow,
|
|
||||||
waker: EventLoopWaker,
|
waker: EventLoopWaker,
|
||||||
event_loop_proxy: Arc<EventLoopProxy>,
|
event_loop_proxy: Arc<EventLoopProxy>,
|
||||||
queued_events: Vec<EventWrapper>,
|
queued_events: Cell<Vec<EventWrapper>>,
|
||||||
|
queued_gpu_redraws: Cell<HashSet<Retained<WinitUIWindow>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for AppState {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_struct("AppState")
|
||||||
|
.field("control_flow", &self.control_flow)
|
||||||
|
.field("waker", &self.waker)
|
||||||
|
.field("event_loop_proxy", &self.event_loop_proxy)
|
||||||
|
.field("queued_events", &"Cell<...>")
|
||||||
|
.field("queued_gpu_redraws", &"Cell<...>")
|
||||||
|
.finish_non_exhaustive()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SAFETY: Creating `MainThreadBound` in a `const` context,
|
||||||
|
// where there is no concept of the main thread.
|
||||||
|
static GLOBAL: MainThreadBound<OnceCell<AppState>> =
|
||||||
|
MainThreadBound::new(OnceCell::new(), unsafe { MainThreadMarker::new_unchecked() });
|
||||||
|
|
||||||
impl AppState {
|
impl AppState {
|
||||||
pub(crate) fn get_mut(mtm: MainThreadMarker) -> RefMut<'static, AppState> {
|
pub(crate) fn setup_global(mtm: MainThreadMarker) -> bool {
|
||||||
// basically everything in UIKit requires the main thread, so it's pointless to use the
|
let event_loop_proxy = Arc::new(EventLoopProxy::new(mtm, move || {
|
||||||
// std::sync APIs.
|
get_handler(mtm).handle(|app| app.proxy_wake_up(&ActiveEventLoop { mtm }));
|
||||||
// must be mut because plain `static` requires `Sync`
|
}));
|
||||||
static mut APP_STATE: RefCell<Option<AppState>> = RefCell::new(None);
|
GLOBAL
|
||||||
|
.get(mtm)
|
||||||
#[allow(unknown_lints)] // New lint below
|
.set(Self {
|
||||||
#[allow(static_mut_refs)] // TODO: Use `MainThreadBound` instead.
|
state: Cell::new(AppStateImpl::Initial),
|
||||||
let mut guard = unsafe { APP_STATE.borrow_mut() };
|
control_flow: Cell::new(ControlFlow::default()),
|
||||||
if guard.is_none() {
|
waker: EventLoopWaker::new(CFRunLoop::main().unwrap()),
|
||||||
#[inline(never)]
|
event_loop_proxy,
|
||||||
#[cold]
|
queued_events: Cell::new(Vec::new()),
|
||||||
fn init_guard(guard: &mut RefMut<'static, Option<AppState>>, mtm: MainThreadMarker) {
|
queued_gpu_redraws: Cell::new(HashSet::new()),
|
||||||
let waker = EventLoopWaker::new(CFRunLoop::main().unwrap());
|
})
|
||||||
let event_loop_proxy = Arc::new(EventLoopProxy::new(mtm, move || {
|
.is_ok()
|
||||||
get_handler(mtm).handle(|app| app.proxy_wake_up(&ActiveEventLoop { mtm }));
|
|
||||||
}));
|
|
||||||
|
|
||||||
**guard = Some(AppState {
|
|
||||||
app_state: Some(AppStateImpl::Initial { queued_gpu_redraws: HashSet::new() }),
|
|
||||||
control_flow: ControlFlow::default(),
|
|
||||||
waker,
|
|
||||||
event_loop_proxy,
|
|
||||||
queued_events: Vec::new(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
init_guard(&mut guard, mtm);
|
|
||||||
}
|
|
||||||
RefMut::map(guard, |state| state.as_mut().unwrap())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn state(&self) -> &AppStateImpl {
|
pub(crate) fn get(mtm: MainThreadMarker) -> &'static Self {
|
||||||
match &self.app_state {
|
GLOBAL.get(mtm).get().expect("tried to get application state before it was registered")
|
||||||
Some(ref state) => state,
|
|
||||||
None => bug!("`AppState` previously failed a state transition"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn state_mut(&mut self) -> &mut AppStateImpl {
|
|
||||||
match &mut self.app_state {
|
|
||||||
Some(ref mut state) => state,
|
|
||||||
None => bug!("`AppState` previously failed a state transition"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn take_state(&mut self) -> AppStateImpl {
|
|
||||||
match self.app_state.take() {
|
|
||||||
Some(state) => state,
|
|
||||||
None => bug!("`AppState` previously failed a state transition"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_state(&mut self, new_state: AppStateImpl) {
|
|
||||||
bug_assert!(
|
|
||||||
self.app_state.is_none(),
|
|
||||||
"attempted to set an `AppState` without calling `take_state` first {:?}",
|
|
||||||
self.app_state
|
|
||||||
);
|
|
||||||
self.app_state = Some(new_state)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn replace_state(&mut self, new_state: AppStateImpl) -> AppStateImpl {
|
|
||||||
match &mut self.app_state {
|
|
||||||
Some(ref mut state) => mem::replace(state, new_state),
|
|
||||||
None => bug!("`AppState` previously failed a state transition"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_launched(&self) -> bool {
|
fn has_launched(&self) -> bool {
|
||||||
!matches!(self.state(), AppStateImpl::Initial { .. })
|
!matches!(self.state.get(), AppStateImpl::Initial)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_terminated(&self) -> bool {
|
fn has_terminated(&self) -> bool {
|
||||||
matches!(self.state(), AppStateImpl::Terminated)
|
matches!(self.state.get(), AppStateImpl::Terminated)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn did_finish_launching_transition(&mut self) {
|
fn did_finish_launching_transition(&self) {
|
||||||
let queued_gpu_redraws = match self.take_state() {
|
match self.state.get() {
|
||||||
AppStateImpl::Initial { queued_gpu_redraws } => queued_gpu_redraws,
|
AppStateImpl::Initial => {},
|
||||||
s => bug!("unexpected state {:?}", s),
|
s => bug!("unexpected state {:?}", s),
|
||||||
};
|
}
|
||||||
self.set_state(AppStateImpl::ProcessingEvents {
|
self.state
|
||||||
active_control_flow: self.control_flow,
|
.set(AppStateImpl::ProcessingEvents { active_control_flow: self.control_flow.get() });
|
||||||
queued_gpu_redraws,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wakeup_transition(&mut self) -> Option<StartCause> {
|
fn wakeup_transition(&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 start_cause = match (self.control_flow, self.take_state()) {
|
let start_cause = match (self.control_flow.get(), self.state.get()) {
|
||||||
(ControlFlow::Poll, AppStateImpl::PollFinished) => StartCause::Poll,
|
(ControlFlow::Poll, AppStateImpl::PollFinished) => StartCause::Poll,
|
||||||
(ControlFlow::Wait, AppStateImpl::Waiting { start }) => {
|
(ControlFlow::Wait, AppStateImpl::Waiting { start }) => {
|
||||||
StartCause::WaitCancelled { start, requested_resume: None }
|
StartCause::WaitCancelled { start, requested_resume: None }
|
||||||
@@ -210,66 +163,61 @@ impl AppState {
|
|||||||
s => bug!("`EventHandler` unexpectedly woke up {:?}", s),
|
s => bug!("`EventHandler` unexpectedly woke up {:?}", s),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.set_state(AppStateImpl::ProcessingEvents {
|
self.state
|
||||||
queued_gpu_redraws: Default::default(),
|
.set(AppStateImpl::ProcessingEvents { active_control_flow: self.control_flow.get() });
|
||||||
active_control_flow: self.control_flow,
|
|
||||||
});
|
|
||||||
Some(start_cause)
|
Some(start_cause)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main_events_cleared_transition(&mut self) -> HashSet<Retained<WinitUIWindow>> {
|
fn main_events_cleared_transition(&self) {
|
||||||
let (queued_gpu_redraws, active_control_flow) = match self.take_state() {
|
let active_control_flow = match self.state.get() {
|
||||||
AppStateImpl::ProcessingEvents { queued_gpu_redraws, active_control_flow } => {
|
AppStateImpl::ProcessingEvents { active_control_flow } => active_control_flow,
|
||||||
(queued_gpu_redraws, active_control_flow)
|
|
||||||
},
|
|
||||||
s => bug!("unexpected state {:?}", s),
|
s => bug!("unexpected state {:?}", s),
|
||||||
};
|
};
|
||||||
self.set_state(AppStateImpl::ProcessingRedraws { active_control_flow });
|
self.state.set(AppStateImpl::ProcessingRedraws { active_control_flow });
|
||||||
queued_gpu_redraws
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn events_cleared_transition(&mut self) {
|
fn events_cleared_transition(&self) {
|
||||||
if !self.has_launched() || self.has_terminated() {
|
if !self.has_launched() || self.has_terminated() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let old = match self.take_state() {
|
let old = match self.state.get() {
|
||||||
AppStateImpl::ProcessingRedraws { active_control_flow } => active_control_flow,
|
AppStateImpl::ProcessingRedraws { active_control_flow } => active_control_flow,
|
||||||
s => bug!("unexpected state {:?}", s),
|
s => bug!("unexpected state {:?}", s),
|
||||||
};
|
};
|
||||||
|
|
||||||
let new = self.control_flow;
|
let new = self.control_flow.get();
|
||||||
match (old, new) {
|
match (old, new) {
|
||||||
(ControlFlow::Wait, ControlFlow::Wait) => {
|
(ControlFlow::Wait, ControlFlow::Wait) => {
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
self.set_state(AppStateImpl::Waiting { start });
|
self.state.set(AppStateImpl::Waiting { start });
|
||||||
self.waker.stop()
|
self.waker.stop()
|
||||||
},
|
},
|
||||||
(ControlFlow::WaitUntil(old_instant), ControlFlow::WaitUntil(new_instant))
|
(ControlFlow::WaitUntil(old_instant), ControlFlow::WaitUntil(new_instant))
|
||||||
if old_instant == new_instant =>
|
if old_instant == new_instant =>
|
||||||
{
|
{
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
self.set_state(AppStateImpl::Waiting { start });
|
self.state.set(AppStateImpl::Waiting { start });
|
||||||
},
|
},
|
||||||
(_, ControlFlow::Wait) => {
|
(_, ControlFlow::Wait) => {
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
self.set_state(AppStateImpl::Waiting { start });
|
self.state.set(AppStateImpl::Waiting { start });
|
||||||
self.waker.stop()
|
self.waker.stop()
|
||||||
},
|
},
|
||||||
(_, ControlFlow::WaitUntil(new_instant)) => {
|
(_, ControlFlow::WaitUntil(new_instant)) => {
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
self.set_state(AppStateImpl::Waiting { start });
|
self.state.set(AppStateImpl::Waiting { 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.state.set(AppStateImpl::PollFinished);
|
||||||
self.waker.start()
|
self.waker.start()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn terminated_transition(&mut self) {
|
fn terminated_transition(&self) {
|
||||||
match self.replace_state(AppStateImpl::Terminated) {
|
match self.state.replace(AppStateImpl::Terminated) {
|
||||||
AppStateImpl::ProcessingEvents { .. } => {},
|
AppStateImpl::ProcessingEvents { .. } => {},
|
||||||
s => bug!("terminated while not processing events {:?}", s),
|
s => bug!("terminated while not processing events {:?}", s),
|
||||||
}
|
}
|
||||||
@@ -279,26 +227,27 @@ impl AppState {
|
|||||||
&self.event_loop_proxy
|
&self.event_loop_proxy
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn set_control_flow(&mut self, control_flow: ControlFlow) {
|
pub(crate) fn set_control_flow(&self, control_flow: ControlFlow) {
|
||||||
self.control_flow = control_flow;
|
self.control_flow.set(control_flow);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn control_flow(&self) -> ControlFlow {
|
pub(crate) fn control_flow(&self) -> ControlFlow {
|
||||||
self.control_flow
|
self.control_flow.get()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn queue_gl_or_metal_redraw(mtm: MainThreadMarker, window: Retained<WinitUIWindow>) {
|
pub(crate) fn queue_gl_or_metal_redraw(mtm: MainThreadMarker, window: Retained<WinitUIWindow>) {
|
||||||
let mut this = AppState::get_mut(mtm);
|
let this = AppState::get(mtm);
|
||||||
match this.state_mut() {
|
match this.state.get() {
|
||||||
&mut AppStateImpl::Initial { ref mut queued_gpu_redraws, .. }
|
AppStateImpl::Initial | AppStateImpl::ProcessingEvents { .. } => {
|
||||||
| &mut AppStateImpl::ProcessingEvents { ref mut queued_gpu_redraws, .. } => {
|
let mut queued_gpu_redraws = this.queued_gpu_redraws.take();
|
||||||
let _ = queued_gpu_redraws.insert(window);
|
let _ = queued_gpu_redraws.insert(window);
|
||||||
|
this.queued_gpu_redraws.set(queued_gpu_redraws);
|
||||||
},
|
},
|
||||||
s @ &mut AppStateImpl::ProcessingRedraws { .. }
|
s @ AppStateImpl::ProcessingRedraws { .. }
|
||||||
| s @ &mut AppStateImpl::Waiting { .. }
|
| s @ AppStateImpl::Waiting { .. }
|
||||||
| s @ &mut AppStateImpl::PollFinished => bug!("unexpected state {:?}", s),
|
| s @ AppStateImpl::PollFinished => bug!("unexpected state {:?}", s),
|
||||||
&mut AppStateImpl::Terminated => {
|
AppStateImpl::Terminated => {
|
||||||
panic!("Attempt to create a `Window` after the app has terminated")
|
panic!("Attempt to create a `Window` after the app has terminated")
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -313,14 +262,10 @@ pub(crate) fn launch<R>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn did_finish_launching(mtm: MainThreadMarker) {
|
pub fn did_finish_launching(mtm: MainThreadMarker) {
|
||||||
let mut this = AppState::get_mut(mtm);
|
let this = AppState::get(mtm);
|
||||||
|
|
||||||
this.waker.start();
|
this.waker.start();
|
||||||
|
this.did_finish_launching_transition();
|
||||||
// have to drop RefMut because the window setup code below can trigger new events
|
|
||||||
drop(this);
|
|
||||||
|
|
||||||
AppState::get_mut(mtm).did_finish_launching_transition();
|
|
||||||
|
|
||||||
get_handler(mtm).handle(|app| app.new_events(&ActiveEventLoop { mtm }, StartCause::Init));
|
get_handler(mtm).handle(|app| app.new_events(&ActiveEventLoop { mtm }, StartCause::Init));
|
||||||
get_handler(mtm).handle(|app| app.can_create_surfaces(&ActiveEventLoop { mtm }));
|
get_handler(mtm).handle(|app| app.can_create_surfaces(&ActiveEventLoop { mtm }));
|
||||||
@@ -329,12 +274,11 @@ pub fn did_finish_launching(mtm: MainThreadMarker) {
|
|||||||
|
|
||||||
// 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 this = AppState::get(mtm);
|
||||||
let cause = match this.wakeup_transition() {
|
let cause = match this.wakeup_transition() {
|
||||||
None => return,
|
None => return,
|
||||||
Some(cause) => cause,
|
Some(cause) => cause,
|
||||||
};
|
};
|
||||||
drop(this);
|
|
||||||
|
|
||||||
get_handler(mtm).handle(|app| app.new_events(&ActiveEventLoop { mtm }, cause));
|
get_handler(mtm).handle(|app| app.new_events(&ActiveEventLoop { mtm }, cause));
|
||||||
handle_nonuser_events(mtm, []);
|
handle_nonuser_events(mtm, []);
|
||||||
@@ -348,19 +292,20 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
|
|||||||
mtm: MainThreadMarker,
|
mtm: MainThreadMarker,
|
||||||
events: I,
|
events: I,
|
||||||
) {
|
) {
|
||||||
let mut this = AppState::get_mut(mtm);
|
let this = AppState::get(mtm);
|
||||||
if this.has_terminated() {
|
if this.has_terminated() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !get_handler(mtm).ready() {
|
if !get_handler(mtm).ready() {
|
||||||
// Prevent re-entrancy; queue the events up for once we're done handling the event instead.
|
// Prevent re-entrancy; queue the events up for once we're done handling the event instead.
|
||||||
this.queued_events.extend(events);
|
let mut queued_events = this.queued_events.take();
|
||||||
|
queued_events.extend(events);
|
||||||
|
this.queued_events.set(queued_events);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let processing_redraws = matches!(this.state(), AppStateImpl::ProcessingRedraws { .. });
|
let processing_redraws = matches!(this.state.get(), AppStateImpl::ProcessingRedraws { .. });
|
||||||
drop(this);
|
|
||||||
|
|
||||||
for event in events {
|
for event in events {
|
||||||
if !processing_redraws && event.is_redraw() {
|
if !processing_redraws && event.is_redraw() {
|
||||||
@@ -375,12 +320,10 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let mut this = AppState::get_mut(mtm);
|
let queued_events = this.queued_events.take();
|
||||||
let queued_events = mem::take(&mut this.queued_events);
|
|
||||||
if queued_events.is_empty() {
|
if queued_events.is_empty() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
drop(this);
|
|
||||||
|
|
||||||
for event in queued_events {
|
for event in queued_events {
|
||||||
if !processing_redraws && event.is_redraw() {
|
if !processing_redraws && event.is_redraw() {
|
||||||
@@ -397,19 +340,16 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn handle_user_events(mtm: MainThreadMarker) {
|
fn handle_user_events(mtm: MainThreadMarker) {
|
||||||
let this = AppState::get_mut(mtm);
|
let this = AppState::get(mtm);
|
||||||
if matches!(this.state(), AppStateImpl::ProcessingRedraws { .. }) {
|
if matches!(this.state.get(), AppStateImpl::ProcessingRedraws { .. }) {
|
||||||
bug!("user events attempted to be sent out while `ProcessingRedraws`");
|
bug!("user events attempted to be sent out while `ProcessingRedraws`");
|
||||||
}
|
}
|
||||||
drop(this);
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let mut this = AppState::get_mut(mtm);
|
let queued_events = this.queued_events.take();
|
||||||
let queued_events = mem::take(&mut this.queued_events);
|
|
||||||
if queued_events.is_empty() {
|
if queued_events.is_empty() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
drop(this);
|
|
||||||
|
|
||||||
for event in queued_events {
|
for event in queued_events {
|
||||||
handle_wrapped_event(mtm, event);
|
handle_wrapped_event(mtm, event);
|
||||||
@@ -434,28 +374,23 @@ pub(crate) fn send_occluded_event_for_all_windows(application: &UIApplication, o
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_main_events_cleared(mtm: MainThreadMarker) {
|
pub fn handle_main_events_cleared(mtm: MainThreadMarker) {
|
||||||
let mut this = AppState::get_mut(mtm);
|
let this = AppState::get(mtm);
|
||||||
if !this.has_launched() || this.has_terminated() {
|
if !this.has_launched() || this.has_terminated() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
match this.state_mut() {
|
match this.state.get() {
|
||||||
AppStateImpl::ProcessingEvents { .. } => {},
|
AppStateImpl::ProcessingEvents { .. } => {},
|
||||||
_ => bug!("`ProcessingRedraws` happened unexpectedly"),
|
_ => bug!("`ProcessingRedraws` happened unexpectedly"),
|
||||||
};
|
};
|
||||||
drop(this);
|
|
||||||
|
|
||||||
handle_user_events(mtm);
|
handle_user_events(mtm);
|
||||||
|
|
||||||
let mut this = AppState::get_mut(mtm);
|
this.main_events_cleared_transition();
|
||||||
let redraw_events: Vec<EventWrapper> = this
|
let queued_gpu_redraws = this.queued_gpu_redraws.take();
|
||||||
.main_events_cleared_transition()
|
let redraw_events = queued_gpu_redraws.into_iter().map(|window| EventWrapper::Window {
|
||||||
.into_iter()
|
window_id: window.id(),
|
||||||
.map(|window| EventWrapper::Window {
|
event: WindowEvent::RedrawRequested,
|
||||||
window_id: window.id(),
|
});
|
||||||
event: WindowEvent::RedrawRequested,
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
drop(this);
|
|
||||||
|
|
||||||
handle_nonuser_events(mtm, redraw_events);
|
handle_nonuser_events(mtm, redraw_events);
|
||||||
get_handler(mtm).handle(|app| app.about_to_wait(&ActiveEventLoop { mtm }));
|
get_handler(mtm).handle(|app| app.about_to_wait(&ActiveEventLoop { mtm }));
|
||||||
@@ -463,7 +398,7 @@ pub fn handle_main_events_cleared(mtm: MainThreadMarker) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_events_cleared(mtm: MainThreadMarker) {
|
pub fn handle_events_cleared(mtm: MainThreadMarker) {
|
||||||
AppState::get_mut(mtm).events_cleared_transition();
|
AppState::get(mtm).events_cleared_transition();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn handle_resumed(mtm: MainThreadMarker) {
|
pub(crate) fn handle_resumed(mtm: MainThreadMarker) {
|
||||||
@@ -496,11 +431,10 @@ pub(crate) fn terminated(application: &UIApplication) {
|
|||||||
}
|
}
|
||||||
handle_nonuser_events(mtm, events);
|
handle_nonuser_events(mtm, events);
|
||||||
|
|
||||||
let mut this = AppState::get_mut(mtm);
|
let this = AppState::get(mtm);
|
||||||
this.terminated_transition();
|
this.terminated_transition();
|
||||||
// Prevent EventLoopProxy from firing again.
|
// Prevent EventLoopProxy from firing again.
|
||||||
this.event_loop_proxy.invalidate();
|
this.event_loop_proxy.invalidate();
|
||||||
drop(this);
|
|
||||||
|
|
||||||
get_handler(mtm).terminate();
|
get_handler(mtm).terminate();
|
||||||
}
|
}
|
||||||
@@ -541,6 +475,7 @@ fn get_view_and_screen_frame(window: &WinitUIWindow) -> (Retained<UIView>, CGRec
|
|||||||
(view, screen_frame)
|
(view, screen_frame)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
struct EventLoopWaker {
|
struct EventLoopWaker {
|
||||||
timer: CFRetained<CFRunLoopTimer>,
|
timer: CFRetained<CFRunLoopTimer>,
|
||||||
}
|
}
|
||||||
@@ -574,15 +509,15 @@ impl EventLoopWaker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stop(&mut self) {
|
fn stop(&self) {
|
||||||
self.timer.set_next_fire_date(f64::MAX);
|
self.timer.set_next_fire_date(f64::MAX);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start(&mut self) {
|
fn start(&self) {
|
||||||
self.timer.set_next_fire_date(f64::MIN);
|
self.timer.set_next_fire_date(f64::MIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_at(&mut self, instant: Instant) {
|
fn start_at(&self, instant: Instant) {
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
if now >= instant {
|
if now >= instant {
|
||||||
self.start();
|
self.start();
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ pub struct ActiveEventLoop {
|
|||||||
|
|
||||||
impl RootActiveEventLoop for ActiveEventLoop {
|
impl RootActiveEventLoop for ActiveEventLoop {
|
||||||
fn create_proxy(&self) -> CoreEventLoopProxy {
|
fn create_proxy(&self) -> CoreEventLoopProxy {
|
||||||
CoreEventLoopProxy::new(AppState::get_mut(self.mtm).event_loop_proxy().clone())
|
CoreEventLoopProxy::new(AppState::get(self.mtm).event_loop_proxy().clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_window(
|
fn create_window(
|
||||||
@@ -73,7 +73,7 @@ impl RootActiveEventLoop for ActiveEventLoop {
|
|||||||
fn listen_device_events(&self, _allowed: DeviceEvents) {}
|
fn listen_device_events(&self, _allowed: DeviceEvents) {}
|
||||||
|
|
||||||
fn set_control_flow(&self, control_flow: ControlFlow) {
|
fn set_control_flow(&self, control_flow: ControlFlow) {
|
||||||
AppState::get_mut(self.mtm).set_control_flow(control_flow)
|
AppState::get(self.mtm).set_control_flow(control_flow)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn system_theme(&self) -> Option<Theme> {
|
fn system_theme(&self) -> Option<Theme> {
|
||||||
@@ -81,7 +81,7 @@ impl RootActiveEventLoop for ActiveEventLoop {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn control_flow(&self) -> ControlFlow {
|
fn control_flow(&self) -> ControlFlow {
|
||||||
AppState::get_mut(self.mtm).control_flow()
|
AppState::get(self.mtm).control_flow()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exit(&self) {
|
fn exit(&self) {
|
||||||
@@ -146,13 +146,9 @@ impl EventLoop {
|
|||||||
let mtm = MainThreadMarker::new()
|
let mtm = MainThreadMarker::new()
|
||||||
.expect("On iOS, `EventLoop` must be created on the main thread");
|
.expect("On iOS, `EventLoop` must be created on the main thread");
|
||||||
|
|
||||||
static mut SINGLETON_INIT: bool = false;
|
if !AppState::setup_global(mtm) {
|
||||||
unsafe {
|
// Required, AppState is global state, and event loop can only be run once.
|
||||||
if SINGLETON_INIT {
|
return Err(EventLoopError::RecreationAttempt);
|
||||||
// Required, AppState is global state, and event loop can only be run once.
|
|
||||||
return Err(EventLoopError::RecreationAttempt);
|
|
||||||
}
|
|
||||||
SINGLETON_INIT = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// this line sets up the main run loop before `UIApplicationMain`
|
// this line sets up the main run loop before `UIApplicationMain`
|
||||||
|
|||||||
Reference in New Issue
Block a user