2019-06-22 01:33:15 +10:00
|
|
|
use std::{
|
|
|
|
cell::{RefCell, RefMut},
|
|
|
|
mem::{self, ManuallyDrop},
|
|
|
|
os::raw::c_void,
|
|
|
|
ptr,
|
|
|
|
time::Instant,
|
|
|
|
};
|
|
|
|
|
|
|
|
use crate::{
|
|
|
|
event::{Event, StartCause},
|
|
|
|
event_loop::ControlFlow,
|
|
|
|
};
|
|
|
|
|
|
|
|
use crate::platform_impl::platform::{
|
|
|
|
event_loop::{EventHandler, Never},
|
|
|
|
ffi::{
|
|
|
|
id, kCFRunLoopCommonModes, CFAbsoluteTimeGetCurrent, CFRelease, CFRunLoopAddTimer,
|
|
|
|
CFRunLoopGetMain, CFRunLoopRef, CFRunLoopTimerCreate, CFRunLoopTimerInvalidate,
|
|
|
|
CFRunLoopTimerRef, CFRunLoopTimerSetNextFireDate, NSUInteger,
|
|
|
|
},
|
2019-05-26 11:10:41 +10:00
|
|
|
};
|
|
|
|
|
|
|
|
macro_rules! bug {
|
|
|
|
($msg:expr) => {
|
|
|
|
panic!("winit iOS bug, file an issue: {}", $msg)
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
// this is the state machine for the app lifecycle
|
|
|
|
#[derive(Debug)]
|
|
|
|
enum AppStateImpl {
|
|
|
|
NotLaunched {
|
|
|
|
queued_windows: Vec<id>,
|
|
|
|
queued_events: Vec<Event<Never>>,
|
|
|
|
},
|
|
|
|
Launching {
|
|
|
|
queued_windows: Vec<id>,
|
|
|
|
queued_events: Vec<Event<Never>>,
|
2019-06-18 04:27:00 +10:00
|
|
|
queued_event_handler: Box<dyn EventHandler>,
|
2019-05-26 11:10:41 +10:00
|
|
|
},
|
|
|
|
ProcessingEvents {
|
2019-06-18 04:27:00 +10:00
|
|
|
event_handler: Box<dyn EventHandler>,
|
2019-05-26 11:10:41 +10:00
|
|
|
active_control_flow: ControlFlow,
|
|
|
|
},
|
|
|
|
// special state to deal with reentrancy and prevent mutable aliasing.
|
|
|
|
InUserCallback {
|
|
|
|
queued_events: Vec<Event<Never>>,
|
|
|
|
},
|
|
|
|
Waiting {
|
2019-06-18 04:27:00 +10:00
|
|
|
waiting_event_handler: Box<dyn EventHandler>,
|
2019-05-26 11:10:41 +10:00
|
|
|
start: Instant,
|
|
|
|
},
|
|
|
|
PollFinished {
|
2019-06-18 04:27:00 +10:00
|
|
|
waiting_event_handler: Box<dyn EventHandler>,
|
2019-05-26 11:10:41 +10:00
|
|
|
},
|
|
|
|
Terminated,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Drop for AppStateImpl {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
match self {
|
2019-06-22 01:33:15 +10:00
|
|
|
&mut AppStateImpl::NotLaunched {
|
|
|
|
ref mut queued_windows,
|
|
|
|
..
|
|
|
|
}
|
|
|
|
| &mut AppStateImpl::Launching {
|
|
|
|
ref mut queued_windows,
|
|
|
|
..
|
|
|
|
} => unsafe {
|
2019-05-26 11:10:41 +10:00
|
|
|
for &mut window in queued_windows {
|
|
|
|
let () = msg_send![window, release];
|
|
|
|
}
|
2019-06-22 01:33:15 +10:00
|
|
|
},
|
2019-06-25 02:14:55 +10:00
|
|
|
_ => {}
|
2019-05-26 11:10:41 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct AppState {
|
|
|
|
app_state: AppStateImpl,
|
|
|
|
control_flow: ControlFlow,
|
|
|
|
waker: EventLoopWaker,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl AppState {
|
|
|
|
// requires main thread
|
|
|
|
unsafe fn get_mut() -> RefMut<'static, AppState> {
|
|
|
|
// basically everything in UIKit requires the main thread, so it's pointless to use the
|
|
|
|
// std::sync APIs.
|
|
|
|
// must be mut because plain `static` requires `Sync`
|
|
|
|
static mut APP_STATE: RefCell<Option<AppState>> = RefCell::new(None);
|
|
|
|
|
|
|
|
if cfg!(debug_assertions) {
|
2019-06-22 01:33:15 +10:00
|
|
|
assert_main_thread!(
|
|
|
|
"bug in winit: `AppState::get_mut()` can only be called on the main thread"
|
|
|
|
);
|
2019-05-26 11:10:41 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
let mut guard = APP_STATE.borrow_mut();
|
|
|
|
if guard.is_none() {
|
|
|
|
#[inline(never)]
|
|
|
|
#[cold]
|
|
|
|
unsafe fn init_guard(guard: &mut RefMut<'static, Option<AppState>>) {
|
|
|
|
let waker = EventLoopWaker::new(CFRunLoopGetMain());
|
|
|
|
**guard = Some(AppState {
|
|
|
|
app_state: AppStateImpl::NotLaunched {
|
|
|
|
queued_windows: Vec::new(),
|
|
|
|
queued_events: Vec::new(),
|
|
|
|
},
|
|
|
|
control_flow: ControlFlow::default(),
|
|
|
|
waker,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
init_guard(&mut guard)
|
|
|
|
}
|
2019-06-22 01:33:15 +10:00
|
|
|
RefMut::map(guard, |state| state.as_mut().unwrap())
|
2019-05-26 11:10:41 +10:00
|
|
|
}
|
2019-06-22 01:33:15 +10:00
|
|
|
|
2019-05-26 11:10:41 +10:00
|
|
|
// requires main thread and window is a UIWindow
|
|
|
|
// retains window
|
|
|
|
pub unsafe fn set_key_window(window: id) {
|
|
|
|
let mut this = AppState::get_mut();
|
|
|
|
match &mut this.app_state {
|
2019-06-22 01:33:15 +10:00
|
|
|
&mut AppStateImpl::NotLaunched {
|
|
|
|
ref mut queued_windows,
|
|
|
|
..
|
|
|
|
} => {
|
2019-05-26 11:10:41 +10:00
|
|
|
queued_windows.push(window);
|
|
|
|
msg_send![window, retain];
|
|
|
|
return;
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
|
|
|
&mut AppStateImpl::ProcessingEvents { .. } => {}
|
|
|
|
&mut AppStateImpl::InUserCallback { .. } => {}
|
|
|
|
&mut AppStateImpl::Terminated => panic!(
|
|
|
|
"Attempt to create a `Window` \
|
|
|
|
after the app has terminated"
|
|
|
|
),
|
2019-06-22 01:33:15 +10:00
|
|
|
app_state => unreachable!("unexpected state: {:#?}", app_state), /* all other cases should be impossible */
|
2019-05-26 11:10:41 +10:00
|
|
|
}
|
|
|
|
drop(this);
|
|
|
|
msg_send![window, makeKeyAndVisible]
|
|
|
|
}
|
|
|
|
|
|
|
|
// requires main thread
|
2019-06-18 04:27:00 +10:00
|
|
|
pub unsafe fn will_launch(queued_event_handler: Box<dyn EventHandler>) {
|
2019-05-26 11:10:41 +10:00
|
|
|
let mut this = AppState::get_mut();
|
|
|
|
let (queued_windows, queued_events) = match &mut this.app_state {
|
|
|
|
&mut AppStateImpl::NotLaunched {
|
|
|
|
ref mut queued_windows,
|
|
|
|
ref mut queued_events,
|
|
|
|
} => {
|
|
|
|
let windows = ptr::read(queued_windows);
|
|
|
|
let events = ptr::read(queued_events);
|
|
|
|
(windows, events)
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
|
|
|
_ => panic!(
|
|
|
|
"winit iOS expected the app to be in a `NotLaunched` \
|
|
|
|
state, but was not - please file an issue"
|
|
|
|
),
|
2019-05-26 11:10:41 +10:00
|
|
|
};
|
2019-06-22 01:33:15 +10:00
|
|
|
ptr::write(
|
|
|
|
&mut this.app_state,
|
|
|
|
AppStateImpl::Launching {
|
|
|
|
queued_windows,
|
|
|
|
queued_events,
|
|
|
|
queued_event_handler,
|
|
|
|
},
|
|
|
|
);
|
2019-05-26 11:10:41 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
// requires main thread
|
|
|
|
pub unsafe fn did_finish_launching() {
|
|
|
|
let mut this = AppState::get_mut();
|
|
|
|
let windows = match &mut this.app_state {
|
|
|
|
&mut AppStateImpl::Launching {
|
|
|
|
ref mut queued_windows,
|
|
|
|
..
|
|
|
|
} => mem::replace(queued_windows, Vec::new()),
|
2019-06-25 02:14:55 +10:00
|
|
|
_ => panic!(
|
|
|
|
"winit iOS expected the app to be in a `Launching` \
|
|
|
|
state, but was not - please file an issue"
|
|
|
|
),
|
2019-05-26 11:10:41 +10:00
|
|
|
};
|
|
|
|
// have to drop RefMut because the window setup code below can trigger new events
|
|
|
|
drop(this);
|
|
|
|
|
|
|
|
for window in windows {
|
|
|
|
let count: NSUInteger = msg_send![window, retainCount];
|
|
|
|
// make sure the window is still referenced
|
|
|
|
if count > 1 {
|
|
|
|
// Do a little screen dance here to account for windows being created before
|
|
|
|
// `UIApplicationMain` is called. This fixes visual issues such as being
|
|
|
|
// offcenter and sized incorrectly. Additionally, to fix orientation issues, we
|
|
|
|
// gotta reset the `rootViewController`.
|
|
|
|
//
|
|
|
|
// relevant iOS log:
|
|
|
|
// ```
|
|
|
|
// [ApplicationLifecycle] Windows were created before application initialzation
|
|
|
|
// completed. This may result in incorrect visual appearance.
|
|
|
|
// ```
|
|
|
|
let screen: id = msg_send![window, screen];
|
|
|
|
let () = msg_send![screen, retain];
|
|
|
|
let () = msg_send![window, setScreen:0 as id];
|
2019-06-22 01:33:15 +10:00
|
|
|
let () = msg_send![window, setScreen: screen];
|
2019-05-26 11:10:41 +10:00
|
|
|
let () = msg_send![screen, release];
|
|
|
|
let controller: id = msg_send![window, rootViewController];
|
|
|
|
let () = msg_send![window, setRootViewController:ptr::null::<()>()];
|
2019-06-22 01:33:15 +10:00
|
|
|
let () = msg_send![window, setRootViewController: controller];
|
2019-05-26 11:10:41 +10:00
|
|
|
let () = msg_send![window, makeKeyAndVisible];
|
|
|
|
}
|
|
|
|
let () = msg_send![window, release];
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut this = AppState::get_mut();
|
|
|
|
let (windows, events, event_handler) = match &mut this.app_state {
|
|
|
|
&mut AppStateImpl::Launching {
|
|
|
|
ref mut queued_windows,
|
|
|
|
ref mut queued_events,
|
|
|
|
ref mut queued_event_handler,
|
|
|
|
} => {
|
|
|
|
let windows = ptr::read(queued_windows);
|
|
|
|
let events = ptr::read(queued_events);
|
|
|
|
let event_handler = ptr::read(queued_event_handler);
|
|
|
|
(windows, events, event_handler)
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
|
|
|
_ => panic!(
|
|
|
|
"winit iOS expected the app to be in a `Launching` \
|
|
|
|
state, but was not - please file an issue"
|
|
|
|
),
|
2019-05-26 11:10:41 +10:00
|
|
|
};
|
2019-06-22 01:33:15 +10:00
|
|
|
ptr::write(
|
|
|
|
&mut this.app_state,
|
|
|
|
AppStateImpl::ProcessingEvents {
|
|
|
|
event_handler,
|
|
|
|
active_control_flow: ControlFlow::Poll,
|
|
|
|
},
|
|
|
|
);
|
2019-05-26 11:10:41 +10:00
|
|
|
drop(this);
|
2019-06-22 01:33:15 +10:00
|
|
|
|
2019-05-26 11:10:41 +10:00
|
|
|
let events = std::iter::once(Event::NewEvents(StartCause::Init)).chain(events);
|
|
|
|
AppState::handle_nonuser_events(events);
|
|
|
|
|
|
|
|
// the above window dance hack, could possibly trigger new windows to be created.
|
|
|
|
// we can just set those windows up normally, as they were created after didFinishLaunching
|
|
|
|
for window in windows {
|
|
|
|
let count: NSUInteger = msg_send![window, retainCount];
|
|
|
|
// make sure the window is still referenced
|
|
|
|
if count > 1 {
|
|
|
|
let () = msg_send![window, makeKeyAndVisible];
|
|
|
|
}
|
|
|
|
let () = msg_send![window, release];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// requires main thread
|
|
|
|
// AppState::did_finish_launching handles the special transition `Init`
|
|
|
|
pub unsafe fn handle_wakeup_transition() {
|
|
|
|
let mut this = AppState::get_mut();
|
2019-06-22 01:33:15 +10:00
|
|
|
let event =
|
|
|
|
match this.control_flow {
|
|
|
|
ControlFlow::Poll => {
|
|
|
|
let event_handler = match &mut this.app_state {
|
|
|
|
&mut AppStateImpl::NotLaunched { .. }
|
|
|
|
| &mut AppStateImpl::Launching { .. } => return,
|
|
|
|
&mut AppStateImpl::PollFinished {
|
|
|
|
ref mut waiting_event_handler,
|
|
|
|
} => ptr::read(waiting_event_handler),
|
|
|
|
_ => bug!("`EventHandler` unexpectedly started polling"),
|
|
|
|
};
|
|
|
|
ptr::write(
|
|
|
|
&mut this.app_state,
|
|
|
|
AppStateImpl::ProcessingEvents {
|
|
|
|
event_handler,
|
|
|
|
active_control_flow: ControlFlow::Poll,
|
|
|
|
},
|
|
|
|
);
|
|
|
|
Event::NewEvents(StartCause::Poll)
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-06-22 01:33:15 +10:00
|
|
|
ControlFlow::Wait => {
|
|
|
|
let (event_handler, start) = match &mut this.app_state {
|
|
|
|
&mut AppStateImpl::NotLaunched { .. }
|
|
|
|
| &mut AppStateImpl::Launching { .. } => return,
|
|
|
|
&mut AppStateImpl::Waiting {
|
|
|
|
ref mut waiting_event_handler,
|
|
|
|
ref mut start,
|
|
|
|
} => (ptr::read(waiting_event_handler), *start),
|
|
|
|
_ => bug!("`EventHandler` unexpectedly woke up"),
|
|
|
|
};
|
|
|
|
ptr::write(
|
|
|
|
&mut this.app_state,
|
|
|
|
AppStateImpl::ProcessingEvents {
|
|
|
|
event_handler,
|
|
|
|
active_control_flow: ControlFlow::Wait,
|
|
|
|
},
|
|
|
|
);
|
2019-05-26 11:10:41 +10:00
|
|
|
Event::NewEvents(StartCause::WaitCancelled {
|
|
|
|
start,
|
2019-06-22 01:33:15 +10:00
|
|
|
requested_resume: None,
|
2019-05-26 11:10:41 +10:00
|
|
|
})
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-06-22 01:33:15 +10:00
|
|
|
ControlFlow::WaitUntil(requested_resume) => {
|
|
|
|
let (event_handler, start) = match &mut this.app_state {
|
|
|
|
&mut AppStateImpl::NotLaunched { .. }
|
|
|
|
| &mut AppStateImpl::Launching { .. } => return,
|
|
|
|
&mut AppStateImpl::Waiting {
|
|
|
|
ref mut waiting_event_handler,
|
|
|
|
ref mut start,
|
|
|
|
} => (ptr::read(waiting_event_handler), *start),
|
|
|
|
_ => bug!("`EventHandler` unexpectedly woke up"),
|
|
|
|
};
|
|
|
|
ptr::write(
|
|
|
|
&mut this.app_state,
|
|
|
|
AppStateImpl::ProcessingEvents {
|
|
|
|
event_handler,
|
|
|
|
active_control_flow: ControlFlow::WaitUntil(requested_resume),
|
|
|
|
},
|
|
|
|
);
|
|
|
|
if Instant::now() >= requested_resume {
|
|
|
|
Event::NewEvents(StartCause::ResumeTimeReached {
|
|
|
|
start,
|
|
|
|
requested_resume,
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
Event::NewEvents(StartCause::WaitCancelled {
|
|
|
|
start,
|
|
|
|
requested_resume: Some(requested_resume),
|
|
|
|
})
|
|
|
|
}
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-06-22 01:33:15 +10:00
|
|
|
ControlFlow::Exit => bug!("unexpected controlflow `Exit`"),
|
|
|
|
};
|
2019-05-26 11:10:41 +10:00
|
|
|
drop(this);
|
|
|
|
AppState::handle_nonuser_event(event)
|
|
|
|
}
|
|
|
|
|
|
|
|
// requires main thread
|
|
|
|
pub unsafe fn handle_nonuser_event(event: Event<Never>) {
|
|
|
|
AppState::handle_nonuser_events(std::iter::once(event))
|
|
|
|
}
|
|
|
|
|
|
|
|
// requires main thread
|
|
|
|
pub unsafe fn handle_nonuser_events<I: IntoIterator<Item = Event<Never>>>(events: I) {
|
|
|
|
let mut this = AppState::get_mut();
|
|
|
|
let mut control_flow = this.control_flow;
|
|
|
|
let (mut event_handler, active_control_flow) = match &mut this.app_state {
|
|
|
|
&mut AppStateImpl::Launching {
|
|
|
|
ref mut queued_events,
|
|
|
|
..
|
|
|
|
}
|
|
|
|
| &mut AppStateImpl::NotLaunched {
|
|
|
|
ref mut queued_events,
|
|
|
|
..
|
|
|
|
}
|
|
|
|
| &mut AppStateImpl::InUserCallback {
|
|
|
|
ref mut queued_events,
|
|
|
|
..
|
|
|
|
} => {
|
|
|
|
queued_events.extend(events);
|
2019-06-22 01:33:15 +10:00
|
|
|
return;
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-05-26 11:10:41 +10:00
|
|
|
&mut AppStateImpl::ProcessingEvents {
|
|
|
|
ref mut event_handler,
|
|
|
|
ref mut active_control_flow,
|
|
|
|
} => (ptr::read(event_handler), *active_control_flow),
|
|
|
|
&mut AppStateImpl::PollFinished { .. }
|
|
|
|
| &mut AppStateImpl::Waiting { .. }
|
|
|
|
| &mut AppStateImpl::Terminated => bug!("unexpected attempted to process an event"),
|
|
|
|
};
|
2019-06-22 01:33:15 +10:00
|
|
|
ptr::write(
|
|
|
|
&mut this.app_state,
|
|
|
|
AppStateImpl::InUserCallback {
|
|
|
|
queued_events: Vec::new(),
|
|
|
|
},
|
|
|
|
);
|
2019-05-26 11:10:41 +10:00
|
|
|
drop(this);
|
|
|
|
|
|
|
|
for event in events {
|
|
|
|
event_handler.handle_nonuser_event(event, &mut control_flow)
|
|
|
|
}
|
|
|
|
loop {
|
|
|
|
let mut this = AppState::get_mut();
|
|
|
|
let queued_events = match &mut this.app_state {
|
|
|
|
&mut AppStateImpl::InUserCallback {
|
|
|
|
ref mut queued_events,
|
|
|
|
} => mem::replace(queued_events, Vec::new()),
|
|
|
|
_ => bug!("unexpected `AppStateImpl`"),
|
|
|
|
};
|
|
|
|
if queued_events.is_empty() {
|
|
|
|
this.app_state = AppStateImpl::ProcessingEvents {
|
|
|
|
event_handler,
|
|
|
|
active_control_flow,
|
|
|
|
};
|
|
|
|
this.control_flow = control_flow;
|
2019-06-22 01:33:15 +10:00
|
|
|
break;
|
2019-05-26 11:10:41 +10:00
|
|
|
}
|
|
|
|
drop(this);
|
|
|
|
for event in queued_events {
|
|
|
|
event_handler.handle_nonuser_event(event, &mut control_flow)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// requires main thread
|
|
|
|
pub unsafe fn handle_user_events() {
|
|
|
|
let mut this = AppState::get_mut();
|
|
|
|
let mut control_flow = this.control_flow;
|
|
|
|
let (mut event_handler, active_control_flow) = match &mut this.app_state {
|
|
|
|
&mut AppStateImpl::NotLaunched { .. } | &mut AppStateImpl::Launching { .. } => return,
|
|
|
|
&mut AppStateImpl::ProcessingEvents {
|
|
|
|
ref mut event_handler,
|
|
|
|
ref mut active_control_flow,
|
|
|
|
} => (ptr::read(event_handler), *active_control_flow),
|
|
|
|
&mut AppStateImpl::InUserCallback { .. }
|
|
|
|
| &mut AppStateImpl::PollFinished { .. }
|
|
|
|
| &mut AppStateImpl::Waiting { .. }
|
|
|
|
| &mut AppStateImpl::Terminated => bug!("unexpected attempted to process an event"),
|
|
|
|
};
|
2019-06-22 01:33:15 +10:00
|
|
|
ptr::write(
|
|
|
|
&mut this.app_state,
|
|
|
|
AppStateImpl::InUserCallback {
|
|
|
|
queued_events: Vec::new(),
|
|
|
|
},
|
|
|
|
);
|
2019-05-26 11:10:41 +10:00
|
|
|
drop(this);
|
|
|
|
|
|
|
|
event_handler.handle_user_events(&mut control_flow);
|
|
|
|
loop {
|
|
|
|
let mut this = AppState::get_mut();
|
|
|
|
let queued_events = match &mut this.app_state {
|
|
|
|
&mut AppStateImpl::InUserCallback {
|
|
|
|
ref mut queued_events,
|
|
|
|
} => mem::replace(queued_events, Vec::new()),
|
|
|
|
_ => bug!("unexpected `AppStateImpl`"),
|
|
|
|
};
|
|
|
|
if queued_events.is_empty() {
|
|
|
|
this.app_state = AppStateImpl::ProcessingEvents {
|
|
|
|
event_handler,
|
|
|
|
active_control_flow,
|
|
|
|
};
|
|
|
|
this.control_flow = control_flow;
|
2019-06-22 01:33:15 +10:00
|
|
|
break;
|
2019-05-26 11:10:41 +10:00
|
|
|
}
|
|
|
|
drop(this);
|
|
|
|
for event in queued_events {
|
|
|
|
event_handler.handle_nonuser_event(event, &mut control_flow)
|
|
|
|
}
|
|
|
|
event_handler.handle_user_events(&mut control_flow);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// requires main thread
|
|
|
|
pub unsafe fn handle_events_cleared() {
|
|
|
|
let mut this = AppState::get_mut();
|
|
|
|
match &mut this.app_state {
|
|
|
|
&mut AppStateImpl::NotLaunched { .. } | &mut AppStateImpl::Launching { .. } => return,
|
2019-06-25 02:14:55 +10:00
|
|
|
&mut AppStateImpl::ProcessingEvents { .. } => {}
|
2019-05-26 11:10:41 +10:00
|
|
|
_ => unreachable!(),
|
|
|
|
};
|
|
|
|
drop(this);
|
|
|
|
|
|
|
|
AppState::handle_user_events();
|
|
|
|
AppState::handle_nonuser_event(Event::EventsCleared);
|
|
|
|
|
|
|
|
let mut this = AppState::get_mut();
|
|
|
|
let (event_handler, old) = match &mut this.app_state {
|
|
|
|
&mut AppStateImpl::ProcessingEvents {
|
|
|
|
ref mut event_handler,
|
|
|
|
ref mut active_control_flow,
|
2019-06-25 02:14:55 +10:00
|
|
|
} => (
|
|
|
|
ManuallyDrop::new(ptr::read(event_handler)),
|
|
|
|
*active_control_flow,
|
|
|
|
),
|
2019-05-26 11:10:41 +10:00
|
|
|
_ => unreachable!(),
|
|
|
|
};
|
|
|
|
|
|
|
|
let new = this.control_flow;
|
|
|
|
match (old, new) {
|
2019-06-25 02:14:55 +10:00
|
|
|
(ControlFlow::Poll, ControlFlow::Poll) => ptr::write(
|
|
|
|
&mut this.app_state,
|
|
|
|
AppStateImpl::PollFinished {
|
|
|
|
waiting_event_handler: ManuallyDrop::into_inner(event_handler),
|
|
|
|
},
|
|
|
|
),
|
2019-05-26 11:10:41 +10:00
|
|
|
(ControlFlow::Wait, ControlFlow::Wait) => {
|
|
|
|
let start = Instant::now();
|
|
|
|
ptr::write(
|
|
|
|
&mut this.app_state,
|
|
|
|
AppStateImpl::Waiting {
|
|
|
|
waiting_event_handler: ManuallyDrop::into_inner(event_handler),
|
|
|
|
start,
|
|
|
|
},
|
|
|
|
)
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-05-26 11:10:41 +10:00
|
|
|
(ControlFlow::WaitUntil(old_instant), ControlFlow::WaitUntil(new_instant))
|
2019-06-22 01:33:15 +10:00
|
|
|
if old_instant == new_instant =>
|
|
|
|
{
|
2019-05-26 11:10:41 +10:00
|
|
|
let start = Instant::now();
|
|
|
|
ptr::write(
|
|
|
|
&mut this.app_state,
|
|
|
|
AppStateImpl::Waiting {
|
|
|
|
waiting_event_handler: ManuallyDrop::into_inner(event_handler),
|
|
|
|
start,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
}
|
|
|
|
(_, ControlFlow::Wait) => {
|
|
|
|
let start = Instant::now();
|
|
|
|
ptr::write(
|
|
|
|
&mut this.app_state,
|
|
|
|
AppStateImpl::Waiting {
|
|
|
|
waiting_event_handler: ManuallyDrop::into_inner(event_handler),
|
|
|
|
start,
|
|
|
|
},
|
|
|
|
);
|
|
|
|
this.waker.stop()
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-05-26 11:10:41 +10:00
|
|
|
(_, ControlFlow::WaitUntil(new_instant)) => {
|
|
|
|
let start = Instant::now();
|
|
|
|
ptr::write(
|
|
|
|
&mut this.app_state,
|
|
|
|
AppStateImpl::Waiting {
|
|
|
|
waiting_event_handler: ManuallyDrop::into_inner(event_handler),
|
|
|
|
start,
|
|
|
|
},
|
|
|
|
);
|
|
|
|
this.waker.start_at(new_instant)
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-05-26 11:10:41 +10:00
|
|
|
(_, ControlFlow::Poll) => {
|
|
|
|
ptr::write(
|
|
|
|
&mut this.app_state,
|
|
|
|
AppStateImpl::PollFinished {
|
|
|
|
waiting_event_handler: ManuallyDrop::into_inner(event_handler),
|
|
|
|
},
|
|
|
|
);
|
|
|
|
this.waker.start()
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-05-26 11:10:41 +10:00
|
|
|
(_, ControlFlow::Exit) => {
|
|
|
|
// https://developer.apple.com/library/archive/qa/qa1561/_index.html
|
|
|
|
// it is not possible to quit an iOS app gracefully and programatically
|
|
|
|
warn!("`ControlFlow::Exit` ignored on iOS");
|
|
|
|
this.control_flow = old
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-05-26 11:10:41 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn terminated() {
|
|
|
|
let mut this = unsafe { AppState::get_mut() };
|
|
|
|
let mut old = mem::replace(&mut this.app_state, AppStateImpl::Terminated);
|
|
|
|
let mut control_flow = this.control_flow;
|
2019-06-22 01:33:15 +10:00
|
|
|
if let AppStateImpl::ProcessingEvents {
|
|
|
|
ref mut event_handler,
|
|
|
|
..
|
|
|
|
} = old
|
|
|
|
{
|
2019-05-26 11:10:41 +10:00
|
|
|
drop(this);
|
|
|
|
event_handler.handle_nonuser_event(Event::LoopDestroyed, &mut control_flow)
|
|
|
|
} else {
|
|
|
|
bug!("`LoopDestroyed` happened while not processing events")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct EventLoopWaker {
|
|
|
|
timer: CFRunLoopTimerRef,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Drop for EventLoopWaker {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
unsafe {
|
|
|
|
CFRunLoopTimerInvalidate(self.timer);
|
|
|
|
CFRelease(self.timer as _);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl EventLoopWaker {
|
|
|
|
fn new(rl: CFRunLoopRef) -> EventLoopWaker {
|
2019-06-22 01:33:15 +10:00
|
|
|
extern "C" fn wakeup_main_loop(_timer: CFRunLoopTimerRef, _info: *mut c_void) {}
|
2019-05-26 11:10:41 +10:00
|
|
|
unsafe {
|
2019-07-13 09:05:07 +10:00
|
|
|
// Create a timer with a 0.1µs interval (1ns does not work) to mimic polling.
|
|
|
|
// It is initially setup with a first fire time really far into the
|
|
|
|
// future, but that gets changed to fire immediately in did_finish_launching
|
2019-05-26 11:10:41 +10:00
|
|
|
let timer = CFRunLoopTimerCreate(
|
|
|
|
ptr::null_mut(),
|
|
|
|
std::f64::MAX,
|
|
|
|
0.000_000_1,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
wakeup_main_loop,
|
|
|
|
ptr::null_mut(),
|
|
|
|
);
|
|
|
|
CFRunLoopAddTimer(rl, timer, kCFRunLoopCommonModes);
|
|
|
|
|
|
|
|
EventLoopWaker { timer }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn stop(&mut self) {
|
|
|
|
unsafe { CFRunLoopTimerSetNextFireDate(self.timer, std::f64::MAX) }
|
|
|
|
}
|
|
|
|
|
|
|
|
fn start(&mut self) {
|
|
|
|
unsafe { CFRunLoopTimerSetNextFireDate(self.timer, std::f64::MIN) }
|
|
|
|
}
|
|
|
|
|
|
|
|
fn start_at(&mut self, instant: Instant) {
|
|
|
|
let now = Instant::now();
|
|
|
|
if now >= instant {
|
|
|
|
self.start();
|
|
|
|
} else {
|
|
|
|
unsafe {
|
|
|
|
let current = CFAbsoluteTimeGetCurrent();
|
|
|
|
let duration = instant - now;
|
|
|
|
let fsecs =
|
|
|
|
duration.subsec_nanos() as f64 / 1_000_000_000.0 + duration.as_secs() as f64;
|
|
|
|
CFRunLoopTimerSetNextFireDate(self.timer, current + fsecs)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-06-22 01:33:15 +10:00
|
|
|
}
|