iOS: Refactor event handling to share code with macOS (#3865)
Instead of storing the event handler within the AppState, and extracting it our every time we need it, we now use the same event handling implementation as for macOS that ensures we don't re-entrantly call the event handler, and that we un-register the handler again after we're done using it (`UIApplicationMain` won't return, but may still unwind, so this is very important for panic safety).
This commit is contained in:
parent
7fbc2118b6
commit
a61e7bb4f4
6 changed files with 147 additions and 219 deletions
|
|
@ -8,7 +8,7 @@ use std::time::Instant;
|
||||||
use objc2_app_kit::{NSApplication, NSApplicationActivationPolicy};
|
use objc2_app_kit::{NSApplication, NSApplicationActivationPolicy};
|
||||||
use objc2_foundation::{MainThreadMarker, NSNotification};
|
use objc2_foundation::{MainThreadMarker, NSNotification};
|
||||||
|
|
||||||
use super::event_handler::EventHandler;
|
use super::super::event_handler::EventHandler;
|
||||||
use super::event_loop::{stop_app_immediately, ActiveEventLoop, PanicInfo};
|
use super::event_loop::{stop_app_immediately, ActiveEventLoop, PanicInfo};
|
||||||
use super::observer::{EventLoopWaker, RunLoop};
|
use super::observer::{EventLoopWaker, RunLoop};
|
||||||
use super::{menu, WindowId};
|
use super::{menu, WindowId};
|
||||||
|
|
@ -291,7 +291,7 @@ impl AppState {
|
||||||
callback: impl FnOnce(&mut dyn ApplicationHandler, &ActiveEventLoop),
|
callback: impl FnOnce(&mut dyn ApplicationHandler, &ActiveEventLoop),
|
||||||
) {
|
) {
|
||||||
let event_loop = ActiveEventLoop { app_state: Rc::clone(self), mtm: self.mtm };
|
let event_loop = ActiveEventLoop { app_state: Rc::clone(self), mtm: self.mtm };
|
||||||
self.event_handler.handle(callback, &event_loop);
|
self.event_handler.handle(|app| callback(app, &event_loop));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// dispatch `NewEvents(Init)` + `Resumed`
|
/// dispatch `NewEvents(Init)` + `Resumed`
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ mod app;
|
||||||
mod app_state;
|
mod app_state;
|
||||||
mod cursor;
|
mod cursor;
|
||||||
mod event;
|
mod event;
|
||||||
mod event_handler;
|
|
||||||
mod event_loop;
|
mod event_loop;
|
||||||
mod ffi;
|
mod ffi;
|
||||||
mod menu;
|
mod menu;
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,9 @@ use std::cell::RefCell;
|
||||||
use std::{fmt, mem};
|
use std::{fmt, mem};
|
||||||
|
|
||||||
use crate::application::ApplicationHandler;
|
use crate::application::ApplicationHandler;
|
||||||
use crate::platform_impl::ActiveEventLoop;
|
|
||||||
|
|
||||||
|
/// A helper type for storing a reference to `ApplicationHandler`, allowing interior mutable access
|
||||||
|
/// to it within the execution of a closure.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub(crate) struct EventHandler {
|
pub(crate) struct EventHandler {
|
||||||
/// This can be in the following states:
|
/// This can be in the following states:
|
||||||
|
|
@ -100,19 +101,17 @@ impl EventHandler {
|
||||||
// soundness.
|
// soundness.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
pub(crate) fn in_use(&self) -> bool {
|
pub(crate) fn in_use(&self) -> bool {
|
||||||
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(_)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn handle(
|
pub(crate) fn handle(&self, callback: impl FnOnce(&mut dyn ApplicationHandler)) {
|
||||||
&self,
|
|
||||||
callback: impl FnOnce(&mut dyn ApplicationHandler, &ActiveEventLoop),
|
|
||||||
event_loop: &ActiveEventLoop,
|
|
||||||
) {
|
|
||||||
match self.inner.try_borrow_mut().as_deref_mut() {
|
match self.inner.try_borrow_mut().as_deref_mut() {
|
||||||
Ok(Some(user_app)) => {
|
Ok(Some(user_app)) => {
|
||||||
// It is important that we keep the reference borrowed here,
|
// It is important that we keep the reference borrowed here,
|
||||||
|
|
@ -121,7 +120,7 @@ impl EventHandler {
|
||||||
//
|
//
|
||||||
// If the handler unwinds, the `RefMut` will ensure that the
|
// If the handler unwinds, the `RefMut` will ensure that the
|
||||||
// handler is no longer borrowed.
|
// handler is no longer borrowed.
|
||||||
callback(*user_app, event_loop);
|
callback(*user_app);
|
||||||
},
|
},
|
||||||
Ok(None) => {
|
Ok(None) => {
|
||||||
// `NSApplication`, our app state and this handler are all
|
// `NSApplication`, our app state and this handler are all
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
mod appkit;
|
mod appkit;
|
||||||
|
mod event_handler;
|
||||||
mod notification_center;
|
mod notification_center;
|
||||||
#[cfg(not(target_os = "macos"))]
|
#[cfg(not(target_os = "macos"))]
|
||||||
mod uikit;
|
mod uikit;
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
#![deny(unused_results)]
|
#![deny(unused_results)]
|
||||||
|
|
||||||
use std::cell::{RefCell, RefMut};
|
use std::cell::{OnceCell, RefCell, RefMut};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::os::raw::c_void;
|
use std::os::raw::c_void;
|
||||||
use std::sync::atomic::AtomicBool;
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::{Arc, Mutex, OnceLock};
|
use std::sync::{Arc, Mutex, OnceLock};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use std::{fmt, mem, ptr};
|
use std::{mem, ptr};
|
||||||
|
|
||||||
use core_foundation::base::CFRelease;
|
use core_foundation::base::CFRelease;
|
||||||
use core_foundation::date::CFAbsoluteTimeGetCurrent;
|
use core_foundation::date::CFAbsoluteTimeGetCurrent;
|
||||||
|
|
@ -23,11 +23,13 @@ use objc2_foundation::{
|
||||||
};
|
};
|
||||||
use objc2_ui_kit::{UIApplication, UICoordinateSpace, UIView, UIWindow};
|
use objc2_ui_kit::{UIApplication, UICoordinateSpace, UIView, UIWindow};
|
||||||
|
|
||||||
|
use super::super::event_handler::EventHandler;
|
||||||
use super::window::WinitUIWindow;
|
use super::window::WinitUIWindow;
|
||||||
use super::ActiveEventLoop;
|
use super::ActiveEventLoop;
|
||||||
|
use crate::application::ApplicationHandler;
|
||||||
use crate::dpi::PhysicalSize;
|
use crate::dpi::PhysicalSize;
|
||||||
use crate::event::{Event, InnerSizeWriter, StartCause, WindowEvent};
|
use crate::event::{Event, InnerSizeWriter, StartCause, WindowEvent};
|
||||||
use crate::event_loop::{ActiveEventLoop as RootActiveEventLoop, ControlFlow};
|
use crate::event_loop::ControlFlow;
|
||||||
use crate::window::WindowId as RootWindowId;
|
use crate::window::WindowId as RootWindowId;
|
||||||
|
|
||||||
macro_rules! bug {
|
macro_rules! bug {
|
||||||
|
|
@ -42,25 +44,45 @@ macro_rules! bug_assert {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct EventLoopHandler {
|
/// Get the global event handler for the application.
|
||||||
#[allow(clippy::type_complexity)]
|
///
|
||||||
pub(crate) handler: Box<dyn FnMut(Event, &dyn RootActiveEventLoop)>,
|
/// This is stored separately from AppState, since AppState needs to be accessible while the handler
|
||||||
pub(crate) event_loop: ActiveEventLoop,
|
/// is executing.
|
||||||
|
fn get_handler(mtm: MainThreadMarker) -> &'static EventHandler {
|
||||||
|
// TODO(madsmtm): Use `MainThreadBound` once that is possible in `static`s.
|
||||||
|
struct StaticMainThreadBound<T>(T);
|
||||||
|
|
||||||
|
impl<T> StaticMainThreadBound<T> {
|
||||||
|
const fn get(&self, _mtm: MainThreadMarker) -> &T {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<T> Send for StaticMainThreadBound<T> {}
|
||||||
|
unsafe impl<T> Sync for StaticMainThreadBound<T> {}
|
||||||
|
|
||||||
|
// SAFETY: Creating `StaticMainThreadBound` in a `const` context, where there is no concept
|
||||||
|
// of the main thread.
|
||||||
|
static GLOBAL: StaticMainThreadBound<OnceCell<EventHandler>> =
|
||||||
|
StaticMainThreadBound(OnceCell::new());
|
||||||
|
|
||||||
|
GLOBAL.get(mtm).get_or_init(EventHandler::new)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for EventLoopHandler {
|
fn handle_event(mtm: MainThreadMarker, event: Event) {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
let event_loop = &ActiveEventLoop { mtm };
|
||||||
f.debug_struct("EventLoopHandler")
|
get_handler(mtm).handle(|app| match event {
|
||||||
.field("handler", &"...")
|
Event::NewEvents(cause) => app.new_events(event_loop, cause),
|
||||||
.field("event_loop", &self.event_loop)
|
Event::WindowEvent { window_id, event } => app.window_event(event_loop, window_id, event),
|
||||||
.finish()
|
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),
|
||||||
impl EventLoopHandler {
|
Event::CreateSurfaces => app.can_create_surfaces(event_loop),
|
||||||
fn handle_event(&mut self, event: Event) {
|
Event::AboutToWait => app.about_to_wait(event_loop),
|
||||||
(self.handler)(event, &self.event_loop)
|
Event::LoopExiting => app.exiting(event_loop),
|
||||||
}
|
Event::MemoryWarning => app.memory_warning(event_loop),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -77,14 +99,8 @@ pub struct ScaleFactorChanged {
|
||||||
}
|
}
|
||||||
|
|
||||||
enum UserCallbackTransitionResult<'a> {
|
enum UserCallbackTransitionResult<'a> {
|
||||||
Success {
|
Success { active_control_flow: ControlFlow, processing_redraws: bool },
|
||||||
handler: EventLoopHandler,
|
ReentrancyPrevented { queued_events: &'a mut Vec<EventWrapper> },
|
||||||
active_control_flow: ControlFlow,
|
|
||||||
processing_redraws: bool,
|
|
||||||
},
|
|
||||||
ReentrancyPrevented {
|
|
||||||
queued_events: &'a mut Vec<EventWrapper>,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Event {
|
impl Event {
|
||||||
|
|
@ -97,19 +113,12 @@ impl Event {
|
||||||
#[derive(Debug)]
|
#[derive(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 {
|
||||||
NotLaunched {
|
Initial {
|
||||||
queued_windows: Vec<Retained<WinitUIWindow>>,
|
queued_windows: Vec<Retained<WinitUIWindow>>,
|
||||||
queued_events: Vec<EventWrapper>,
|
queued_events: Vec<EventWrapper>,
|
||||||
queued_gpu_redraws: HashSet<Retained<WinitUIWindow>>,
|
queued_gpu_redraws: HashSet<Retained<WinitUIWindow>>,
|
||||||
},
|
},
|
||||||
Launching {
|
|
||||||
queued_windows: Vec<Retained<WinitUIWindow>>,
|
|
||||||
queued_events: Vec<EventWrapper>,
|
|
||||||
queued_handler: EventLoopHandler,
|
|
||||||
queued_gpu_redraws: HashSet<Retained<WinitUIWindow>>,
|
|
||||||
},
|
|
||||||
ProcessingEvents {
|
ProcessingEvents {
|
||||||
handler: EventLoopHandler,
|
|
||||||
queued_gpu_redraws: HashSet<Retained<WinitUIWindow>>,
|
queued_gpu_redraws: HashSet<Retained<WinitUIWindow>>,
|
||||||
active_control_flow: ControlFlow,
|
active_control_flow: ControlFlow,
|
||||||
},
|
},
|
||||||
|
|
@ -119,16 +128,12 @@ enum AppStateImpl {
|
||||||
queued_gpu_redraws: HashSet<Retained<WinitUIWindow>>,
|
queued_gpu_redraws: HashSet<Retained<WinitUIWindow>>,
|
||||||
},
|
},
|
||||||
ProcessingRedraws {
|
ProcessingRedraws {
|
||||||
handler: EventLoopHandler,
|
|
||||||
active_control_flow: ControlFlow,
|
active_control_flow: ControlFlow,
|
||||||
},
|
},
|
||||||
Waiting {
|
Waiting {
|
||||||
waiting_handler: EventLoopHandler,
|
|
||||||
start: Instant,
|
start: Instant,
|
||||||
},
|
},
|
||||||
PollFinished {
|
PollFinished,
|
||||||
waiting_handler: EventLoopHandler,
|
|
||||||
},
|
|
||||||
Terminated,
|
Terminated,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -154,7 +159,7 @@ 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::NotLaunched {
|
app_state: Some(AppStateImpl::Initial {
|
||||||
queued_windows: Vec::new(),
|
queued_windows: Vec::new(),
|
||||||
queued_events: Vec::new(),
|
queued_events: Vec::new(),
|
||||||
queued_gpu_redraws: HashSet::new(),
|
queued_gpu_redraws: HashSet::new(),
|
||||||
|
|
@ -207,42 +212,23 @@ impl AppState {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_launched(&self) -> bool {
|
fn has_launched(&self) -> bool {
|
||||||
!matches!(self.state(), AppStateImpl::NotLaunched { .. } | AppStateImpl::Launching { .. })
|
!matches!(self.state(), AppStateImpl::Initial { .. })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_terminated(&self) -> bool {
|
fn has_terminated(&self) -> bool {
|
||||||
matches!(self.state(), AppStateImpl::Terminated)
|
matches!(self.state(), AppStateImpl::Terminated)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn will_launch_transition(&mut self, queued_handler: EventLoopHandler) {
|
fn did_finish_launching_transition(
|
||||||
let (queued_windows, queued_events, queued_gpu_redraws) = match self.take_state() {
|
&mut self,
|
||||||
AppStateImpl::NotLaunched { queued_windows, queued_events, queued_gpu_redraws } => {
|
) -> (Vec<Retained<WinitUIWindow>>, Vec<EventWrapper>) {
|
||||||
|
let (windows, events, queued_gpu_redraws) = match self.take_state() {
|
||||||
|
AppStateImpl::Initial { queued_windows, queued_events, queued_gpu_redraws } => {
|
||||||
(queued_windows, queued_events, queued_gpu_redraws)
|
(queued_windows, queued_events, queued_gpu_redraws)
|
||||||
},
|
},
|
||||||
s => bug!("unexpected state {:?}", s),
|
s => bug!("unexpected state {:?}", s),
|
||||||
};
|
};
|
||||||
self.set_state(AppStateImpl::Launching {
|
|
||||||
queued_windows,
|
|
||||||
queued_events,
|
|
||||||
queued_handler,
|
|
||||||
queued_gpu_redraws,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn did_finish_launching_transition(
|
|
||||||
&mut self,
|
|
||||||
) -> (Vec<Retained<WinitUIWindow>>, Vec<EventWrapper>) {
|
|
||||||
let (windows, events, handler, queued_gpu_redraws) = match self.take_state() {
|
|
||||||
AppStateImpl::Launching {
|
|
||||||
queued_windows,
|
|
||||||
queued_events,
|
|
||||||
queued_handler,
|
|
||||||
queued_gpu_redraws,
|
|
||||||
} => (queued_windows, queued_events, queued_handler, queued_gpu_redraws),
|
|
||||||
s => bug!("unexpected state {:?}", s),
|
|
||||||
};
|
|
||||||
self.set_state(AppStateImpl::ProcessingEvents {
|
self.set_state(AppStateImpl::ProcessingEvents {
|
||||||
handler,
|
|
||||||
active_control_flow: self.control_flow,
|
active_control_flow: self.control_flow,
|
||||||
queued_gpu_redraws,
|
queued_gpu_redraws,
|
||||||
});
|
});
|
||||||
|
|
@ -256,22 +242,18 @@ impl AppState {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (handler, event) = match (self.control_flow, self.take_state()) {
|
let event = match (self.control_flow, self.take_state()) {
|
||||||
(ControlFlow::Poll, AppStateImpl::PollFinished { waiting_handler }) => {
|
(ControlFlow::Poll, AppStateImpl::PollFinished) => {
|
||||||
(waiting_handler, EventWrapper::StaticEvent(Event::NewEvents(StartCause::Poll)))
|
EventWrapper::StaticEvent(Event::NewEvents(StartCause::Poll))
|
||||||
},
|
},
|
||||||
(ControlFlow::Wait, AppStateImpl::Waiting { waiting_handler, start }) => (
|
(ControlFlow::Wait, AppStateImpl::Waiting { start }) => {
|
||||||
waiting_handler,
|
|
||||||
EventWrapper::StaticEvent(Event::NewEvents(StartCause::WaitCancelled {
|
EventWrapper::StaticEvent(Event::NewEvents(StartCause::WaitCancelled {
|
||||||
start,
|
start,
|
||||||
requested_resume: None,
|
requested_resume: None,
|
||||||
})),
|
}))
|
||||||
),
|
},
|
||||||
(
|
(ControlFlow::WaitUntil(requested_resume), AppStateImpl::Waiting { start }) => {
|
||||||
ControlFlow::WaitUntil(requested_resume),
|
if Instant::now() >= requested_resume {
|
||||||
AppStateImpl::Waiting { waiting_handler, start },
|
|
||||||
) => {
|
|
||||||
let event = if Instant::now() >= requested_resume {
|
|
||||||
EventWrapper::StaticEvent(Event::NewEvents(StartCause::ResumeTimeReached {
|
EventWrapper::StaticEvent(Event::NewEvents(StartCause::ResumeTimeReached {
|
||||||
start,
|
start,
|
||||||
requested_resume,
|
requested_resume,
|
||||||
|
|
@ -281,14 +263,12 @@ impl AppState {
|
||||||
start,
|
start,
|
||||||
requested_resume: Some(requested_resume),
|
requested_resume: Some(requested_resume),
|
||||||
}))
|
}))
|
||||||
};
|
}
|
||||||
(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 {
|
||||||
handler,
|
|
||||||
queued_gpu_redraws: Default::default(),
|
queued_gpu_redraws: Default::default(),
|
||||||
active_control_flow: self.control_flow,
|
active_control_flow: self.control_flow,
|
||||||
});
|
});
|
||||||
|
|
@ -299,8 +279,7 @@ impl AppState {
|
||||||
// If we're not able to process an event due to recursion or `Init` not having been sent out
|
// 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.
|
// yet, then queue the events up.
|
||||||
match self.state_mut() {
|
match self.state_mut() {
|
||||||
&mut AppStateImpl::Launching { ref mut queued_events, .. }
|
&mut AppStateImpl::Initial { ref mut queued_events, .. }
|
||||||
| &mut AppStateImpl::NotLaunched { ref mut queued_events, .. }
|
|
||||||
| &mut AppStateImpl::InUserCallback { ref mut queued_events, .. } => {
|
| &mut AppStateImpl::InUserCallback { ref mut queued_events, .. } => {
|
||||||
// A lifetime cast: early returns are not currently handled well with NLL, but
|
// A lifetime cast: early returns are not currently handled well with NLL, but
|
||||||
// polonius handles them well. This transmute is a safe workaround.
|
// polonius handles them well. This transmute is a safe workaround.
|
||||||
|
|
@ -324,17 +303,14 @@ impl AppState {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
let (handler, queued_gpu_redraws, active_control_flow, processing_redraws) = match self
|
let (queued_gpu_redraws, active_control_flow, processing_redraws) = match self.take_state()
|
||||||
.take_state()
|
|
||||||
{
|
{
|
||||||
AppStateImpl::Launching { .. }
|
AppStateImpl::Initial { .. } | AppStateImpl::InUserCallback { .. } => unreachable!(),
|
||||||
| AppStateImpl::NotLaunched { .. }
|
AppStateImpl::ProcessingEvents { queued_gpu_redraws, active_control_flow } => {
|
||||||
| AppStateImpl::InUserCallback { .. } => unreachable!(),
|
(queued_gpu_redraws, active_control_flow, false)
|
||||||
AppStateImpl::ProcessingEvents { handler, queued_gpu_redraws, active_control_flow } => {
|
|
||||||
(handler, queued_gpu_redraws, active_control_flow, false)
|
|
||||||
},
|
},
|
||||||
AppStateImpl::ProcessingRedraws { handler, active_control_flow } => {
|
AppStateImpl::ProcessingRedraws { active_control_flow } => {
|
||||||
(handler, Default::default(), active_control_flow, true)
|
(Default::default(), active_control_flow, true)
|
||||||
},
|
},
|
||||||
AppStateImpl::PollFinished { .. }
|
AppStateImpl::PollFinished { .. }
|
||||||
| AppStateImpl::Waiting { .. }
|
| AppStateImpl::Waiting { .. }
|
||||||
|
|
@ -344,17 +320,17 @@ impl AppState {
|
||||||
queued_events: Vec::new(),
|
queued_events: Vec::new(),
|
||||||
queued_gpu_redraws,
|
queued_gpu_redraws,
|
||||||
});
|
});
|
||||||
UserCallbackTransitionResult::Success { handler, active_control_flow, processing_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>> {
|
||||||
let (handler, queued_gpu_redraws, active_control_flow) = match self.take_state() {
|
let (queued_gpu_redraws, active_control_flow) = match self.take_state() {
|
||||||
AppStateImpl::ProcessingEvents { handler, queued_gpu_redraws, active_control_flow } => {
|
AppStateImpl::ProcessingEvents { queued_gpu_redraws, active_control_flow } => {
|
||||||
(handler, queued_gpu_redraws, active_control_flow)
|
(queued_gpu_redraws, active_control_flow)
|
||||||
},
|
},
|
||||||
s => bug!("unexpected state {:?}", s),
|
s => bug!("unexpected state {:?}", s),
|
||||||
};
|
};
|
||||||
self.set_state(AppStateImpl::ProcessingRedraws { handler, active_control_flow });
|
self.set_state(AppStateImpl::ProcessingRedraws { active_control_flow });
|
||||||
queued_gpu_redraws
|
queued_gpu_redraws
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -362,10 +338,8 @@ impl AppState {
|
||||||
if !self.has_launched() || self.has_terminated() {
|
if !self.has_launched() || self.has_terminated() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let (waiting_handler, old) = match self.take_state() {
|
let old = match self.take_state() {
|
||||||
AppStateImpl::ProcessingRedraws { handler, active_control_flow } => {
|
AppStateImpl::ProcessingRedraws { active_control_flow } => active_control_flow,
|
||||||
(handler, active_control_flow)
|
|
||||||
},
|
|
||||||
s => bug!("unexpected state {:?}", s),
|
s => bug!("unexpected state {:?}", s),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -373,35 +347,35 @@ impl AppState {
|
||||||
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 { waiting_handler, start });
|
self.set_state(AppStateImpl::Waiting { start });
|
||||||
},
|
},
|
||||||
(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 { waiting_handler, start });
|
self.set_state(AppStateImpl::Waiting { start });
|
||||||
},
|
},
|
||||||
(_, ControlFlow::Wait) => {
|
(_, ControlFlow::Wait) => {
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
self.set_state(AppStateImpl::Waiting { waiting_handler, start });
|
self.set_state(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 { waiting_handler, start });
|
self.set_state(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 { waiting_handler });
|
self.set_state(AppStateImpl::PollFinished);
|
||||||
self.waker.start()
|
self.waker.start()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn terminated_transition(&mut self) -> EventLoopHandler {
|
fn terminated_transition(&mut self) {
|
||||||
match self.replace_state(AppStateImpl::Terminated) {
|
match self.replace_state(AppStateImpl::Terminated) {
|
||||||
AppStateImpl::ProcessingEvents { handler, .. } => handler,
|
AppStateImpl::ProcessingEvents { .. } => {},
|
||||||
s => bug!("`LoopExiting` happened while not processing events {:?}", s),
|
s => bug!("`LoopExiting` happened while not processing events {:?}", s),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -422,15 +396,15 @@ impl AppState {
|
||||||
pub(crate) fn set_key_window(mtm: MainThreadMarker, window: &Retained<WinitUIWindow>) {
|
pub(crate) fn set_key_window(mtm: MainThreadMarker, window: &Retained<WinitUIWindow>) {
|
||||||
let mut this = AppState::get_mut(mtm);
|
let mut this = AppState::get_mut(mtm);
|
||||||
match this.state_mut() {
|
match this.state_mut() {
|
||||||
&mut AppStateImpl::NotLaunched { ref mut queued_windows, .. } => {
|
&mut AppStateImpl::Initial { ref mut queued_windows, .. } => {
|
||||||
return queued_windows.push(window.clone())
|
return queued_windows.push(window.clone())
|
||||||
},
|
},
|
||||||
&mut AppStateImpl::ProcessingEvents { .. }
|
&mut AppStateImpl::ProcessingEvents { .. }
|
||||||
| &mut AppStateImpl::InUserCallback { .. }
|
| &mut AppStateImpl::InUserCallback { .. }
|
||||||
| &mut AppStateImpl::ProcessingRedraws { .. } => {},
|
| &mut AppStateImpl::ProcessingRedraws { .. } => {},
|
||||||
s @ &mut AppStateImpl::Launching { .. }
|
s @ &mut AppStateImpl::Waiting { .. } | s @ &mut AppStateImpl::PollFinished { .. } => {
|
||||||
| s @ &mut AppStateImpl::Waiting { .. }
|
bug!("unexpected state {:?}", s)
|
||||||
| s @ &mut AppStateImpl::PollFinished { .. } => bug!("unexpected state {:?}", s),
|
},
|
||||||
&mut AppStateImpl::Terminated => {
|
&mut AppStateImpl::Terminated => {
|
||||||
panic!("Attempt to create a `Window` after the app has terminated")
|
panic!("Attempt to create a `Window` after the app has terminated")
|
||||||
},
|
},
|
||||||
|
|
@ -442,8 +416,7 @@ pub(crate) fn set_key_window(mtm: MainThreadMarker, window: &Retained<WinitUIWin
|
||||||
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 mut this = AppState::get_mut(mtm);
|
||||||
match this.state_mut() {
|
match this.state_mut() {
|
||||||
&mut AppStateImpl::NotLaunched { ref mut queued_gpu_redraws, .. }
|
&mut AppStateImpl::Initial { ref mut queued_gpu_redraws, .. }
|
||||||
| &mut AppStateImpl::Launching { 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, .. } => {
|
| &mut AppStateImpl::InUserCallback { ref mut queued_gpu_redraws, .. } => {
|
||||||
let _ = queued_gpu_redraws.insert(window);
|
let _ = queued_gpu_redraws.insert(window);
|
||||||
|
|
@ -457,14 +430,14 @@ pub(crate) fn queue_gl_or_metal_redraw(mtm: MainThreadMarker, window: Retained<W
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn will_launch(mtm: MainThreadMarker, queued_handler: EventLoopHandler) {
|
pub(crate) fn launch(mtm: MainThreadMarker, app: &mut dyn ApplicationHandler, run: impl FnOnce()) {
|
||||||
AppState::get_mut(mtm).will_launch_transition(queued_handler)
|
get_handler(mtm).set(app, run)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn did_finish_launching(mtm: MainThreadMarker) {
|
pub fn did_finish_launching(mtm: MainThreadMarker) {
|
||||||
let mut this = AppState::get_mut(mtm);
|
let mut this = AppState::get_mut(mtm);
|
||||||
let windows = match this.state_mut() {
|
let windows = match this.state_mut() {
|
||||||
AppStateImpl::Launching { queued_windows, .. } => mem::take(queued_windows),
|
AppStateImpl::Initial { queued_windows, .. } => mem::take(queued_windows),
|
||||||
s => bug!("unexpected state {:?}", s),
|
s => bug!("unexpected state {:?}", s),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -537,18 +510,15 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (mut handler, active_control_flow, processing_redraws) =
|
let (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 { active_control_flow, processing_redraws } => {
|
||||||
UserCallbackTransitionResult::Success {
|
(active_control_flow, processing_redraws)
|
||||||
handler,
|
},
|
||||||
active_control_flow,
|
};
|
||||||
processing_redraws,
|
|
||||||
} => (handler, active_control_flow, processing_redraws),
|
|
||||||
};
|
|
||||||
drop(this);
|
drop(this);
|
||||||
|
|
||||||
for wrapper in events {
|
for wrapper in events {
|
||||||
|
|
@ -562,9 +532,9 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
|
||||||
event
|
event
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
handler.handle_event(event)
|
handle_event(mtm, event)
|
||||||
},
|
},
|
||||||
EventWrapper::ScaleFactorChanged(event) => handle_hidpi_proxy(&mut handler, event),
|
EventWrapper::ScaleFactorChanged(event) => handle_hidpi_proxy(mtm, event),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -588,9 +558,9 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
|
||||||
queued_gpu_redraws.is_empty(),
|
queued_gpu_redraws.is_empty(),
|
||||||
"redraw queued while processing redraws"
|
"redraw queued while processing redraws"
|
||||||
);
|
);
|
||||||
AppStateImpl::ProcessingRedraws { handler, active_control_flow }
|
AppStateImpl::ProcessingRedraws { active_control_flow }
|
||||||
} else {
|
} else {
|
||||||
AppStateImpl::ProcessingEvents { handler, queued_gpu_redraws, active_control_flow }
|
AppStateImpl::ProcessingEvents { queued_gpu_redraws, active_control_flow }
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -608,9 +578,9 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
|
||||||
event
|
event
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
handler.handle_event(event)
|
handle_event(mtm, event)
|
||||||
},
|
},
|
||||||
EventWrapper::ScaleFactorChanged(event) => handle_hidpi_proxy(&mut handler, event),
|
EventWrapper::ScaleFactorChanged(event) => handle_hidpi_proxy(mtm, event),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -618,23 +588,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 handler, active_control_flow, processing_redraws) =
|
let (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 { active_control_flow, processing_redraws } => {
|
||||||
UserCallbackTransitionResult::Success {
|
(active_control_flow, processing_redraws)
|
||||||
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`");
|
||||||
}
|
}
|
||||||
|
let proxy_wake_up = this.proxy_wake_up.clone();
|
||||||
drop(this);
|
drop(this);
|
||||||
|
|
||||||
handler.handle_event(Event::UserWakeUp);
|
if proxy_wake_up.swap(false, Ordering::Relaxed) {
|
||||||
|
handle_event(mtm, Event::UserWakeUp);
|
||||||
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let mut this = AppState::get_mut(mtm);
|
let mut this = AppState::get_mut(mtm);
|
||||||
|
|
@ -651,23 +621,22 @@ fn handle_user_events(mtm: MainThreadMarker) {
|
||||||
},
|
},
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
this.app_state = Some(AppStateImpl::ProcessingEvents {
|
this.app_state =
|
||||||
handler,
|
Some(AppStateImpl::ProcessingEvents { queued_gpu_redraws, active_control_flow });
|
||||||
queued_gpu_redraws,
|
|
||||||
active_control_flow,
|
|
||||||
});
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
drop(this);
|
drop(this);
|
||||||
|
|
||||||
for wrapper in queued_events {
|
for wrapper in queued_events {
|
||||||
match wrapper {
|
match wrapper {
|
||||||
EventWrapper::StaticEvent(event) => handler.handle_event(event),
|
EventWrapper::StaticEvent(event) => handle_event(mtm, event),
|
||||||
EventWrapper::ScaleFactorChanged(event) => handle_hidpi_proxy(&mut handler, event),
|
EventWrapper::ScaleFactorChanged(event) => handle_hidpi_proxy(mtm, event),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handler.handle_event(Event::UserWakeUp);
|
if proxy_wake_up.swap(false, Ordering::Relaxed) {
|
||||||
|
handle_event(mtm, Event::UserWakeUp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -749,13 +718,13 @@ pub(crate) fn terminated(application: &UIApplication) {
|
||||||
handle_nonuser_events(mtm, events);
|
handle_nonuser_events(mtm, events);
|
||||||
|
|
||||||
let mut this = AppState::get_mut(mtm);
|
let mut this = AppState::get_mut(mtm);
|
||||||
let mut handler = this.terminated_transition();
|
this.terminated_transition();
|
||||||
drop(this);
|
drop(this);
|
||||||
|
|
||||||
handler.handle_event(Event::LoopExiting)
|
handle_event(mtm, Event::LoopExiting)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_hidpi_proxy(handler: &mut EventLoopHandler, 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_inner_size = Arc::new(Mutex::new(suggested_size));
|
let new_inner_size = Arc::new(Mutex::new(suggested_size));
|
||||||
let event = Event::WindowEvent {
|
let event = Event::WindowEvent {
|
||||||
|
|
@ -765,7 +734,7 @@ fn handle_hidpi_proxy(handler: &mut EventLoopHandler, event: ScaleFactorChanged)
|
||||||
inner_size_writer: InnerSizeWriter::new(Arc::downgrade(&new_inner_size)),
|
inner_size_writer: InnerSizeWriter::new(Arc::downgrade(&new_inner_size)),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
handler.handle_event(event);
|
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_inner_size.lock().unwrap();
|
let physical_size = *new_inner_size.lock().unwrap();
|
||||||
drop(new_inner_size);
|
drop(new_inner_size);
|
||||||
|
|
|
||||||
|
|
@ -22,9 +22,7 @@ use objc2_ui_kit::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::super::notification_center::create_observer;
|
use super::super::notification_center::create_observer;
|
||||||
use super::app_state::{
|
use super::app_state::{send_occluded_event_for_all_windows, AppState, EventWrapper};
|
||||||
send_occluded_event_for_all_windows, AppState, EventLoopHandler, EventWrapper,
|
|
||||||
};
|
|
||||||
use super::{app_state, monitor, MonitorHandle};
|
use super::{app_state, monitor, MonitorHandle};
|
||||||
use crate::application::ApplicationHandler;
|
use crate::application::ApplicationHandler;
|
||||||
use crate::error::{EventLoopError, ExternalError, NotSupportedError, OsError};
|
use crate::error::{EventLoopError, ExternalError, NotSupportedError, OsError};
|
||||||
|
|
@ -128,32 +126,6 @@ impl OwnedDisplayHandle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn map_user_event<'a, A: ApplicationHandler + 'a>(
|
|
||||||
mut app: A,
|
|
||||||
proxy_wake_up: Arc<AtomicBool>,
|
|
||||||
) -> impl FnMut(Event, &dyn RootActiveEventLoop) + 'a {
|
|
||||||
move |event, window_target| match event {
|
|
||||||
Event::NewEvents(cause) => app.new_events(window_target, cause),
|
|
||||||
Event::WindowEvent { window_id, event } => {
|
|
||||||
app.window_event(window_target, window_id, event)
|
|
||||||
},
|
|
||||||
Event::DeviceEvent { device_id, event } => {
|
|
||||||
app.device_event(window_target, device_id, event)
|
|
||||||
},
|
|
||||||
Event::UserWakeUp => {
|
|
||||||
if proxy_wake_up.swap(false, AtomicOrdering::Relaxed) {
|
|
||||||
app.proxy_wake_up(window_target);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Event::Suspended => app.suspended(window_target),
|
|
||||||
Event::Resumed => app.resumed(window_target),
|
|
||||||
Event::CreateSurfaces => app.can_create_surfaces(window_target),
|
|
||||||
Event::AboutToWait => app.about_to_wait(window_target),
|
|
||||||
Event::LoopExiting => app.exiting(window_target),
|
|
||||||
Event::MemoryWarning => app.memory_warning(window_target),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct EventLoop {
|
pub struct EventLoop {
|
||||||
mtm: MainThreadMarker,
|
mtm: MainThreadMarker,
|
||||||
window_target: ActiveEventLoop,
|
window_target: ActiveEventLoop,
|
||||||
|
|
@ -285,7 +257,7 @@ impl EventLoop {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_app<A: ApplicationHandler>(self, app: A) -> ! {
|
pub fn run_app<A: ApplicationHandler>(self, mut app: A) -> ! {
|
||||||
let application: Option<Retained<UIApplication>> =
|
let application: Option<Retained<UIApplication>> =
|
||||||
unsafe { msg_send_id![UIApplication::class(), sharedApplication] };
|
unsafe { msg_send_id![UIApplication::class(), sharedApplication] };
|
||||||
assert!(
|
assert!(
|
||||||
|
|
@ -295,35 +267,23 @@ impl EventLoop {
|
||||||
`EventLoop::run_app` calls `UIApplicationMain` on iOS",
|
`EventLoop::run_app` calls `UIApplicationMain` on iOS",
|
||||||
);
|
);
|
||||||
|
|
||||||
let handler = map_user_event(app, AppState::get_mut(self.mtm).proxy_wake_up());
|
|
||||||
|
|
||||||
let handler = unsafe {
|
|
||||||
std::mem::transmute::<
|
|
||||||
Box<dyn FnMut(Event, &dyn RootActiveEventLoop)>,
|
|
||||||
Box<dyn FnMut(Event, &dyn RootActiveEventLoop)>,
|
|
||||||
>(Box::new(handler))
|
|
||||||
};
|
|
||||||
|
|
||||||
let handler = EventLoopHandler { handler, event_loop: self.window_target };
|
|
||||||
|
|
||||||
app_state::will_launch(self.mtm, handler);
|
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
// These functions are in crt_externs.h.
|
// These functions are in crt_externs.h.
|
||||||
fn _NSGetArgc() -> *mut c_int;
|
fn _NSGetArgc() -> *mut c_int;
|
||||||
fn _NSGetArgv() -> *mut *mut *mut c_char;
|
fn _NSGetArgv() -> *mut *mut *mut c_char;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe {
|
app_state::launch(self.mtm, &mut app, || unsafe {
|
||||||
UIApplicationMain(
|
UIApplicationMain(
|
||||||
*_NSGetArgc(),
|
*_NSGetArgc(),
|
||||||
NonNull::new(*_NSGetArgv()).unwrap(),
|
NonNull::new(*_NSGetArgv()).unwrap(),
|
||||||
// We intentionally override neither the application nor the delegate, to allow the
|
// We intentionally override neither the application nor the delegate, to allow
|
||||||
// user to do so themselves!
|
// the user to do so themselves!
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
)
|
);
|
||||||
};
|
});
|
||||||
|
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue