2019-09-04 14:23:11 -07:00
|
|
|
#![deny(unused_results)]
|
|
|
|
|
|
2024-08-15 23:19:57 +02:00
|
|
|
use std::cell::{OnceCell, RefCell, RefMut};
|
2019-09-04 14:23:11 -07:00
|
|
|
use std::collections::HashSet;
|
2019-06-21 11:33:15 -04:00
|
|
|
use std::os::raw::c_void;
|
2025-01-28 21:31:14 +01:00
|
|
|
use std::sync::{Arc, Mutex};
|
2019-06-21 11:33:15 -04:00
|
|
|
use std::time::Instant;
|
2024-08-15 23:19:57 +02:00
|
|
|
use std::{mem, ptr};
|
2019-06-21 11:33:15 -04:00
|
|
|
|
2025-01-28 21:31:14 +01:00
|
|
|
use dispatch2::MainThreadBound;
|
2024-05-27 14:49:22 +02:00
|
|
|
use objc2::rc::Retained;
|
2025-01-28 21:31:14 +01:00
|
|
|
use objc2::MainThreadMarker;
|
|
|
|
|
use objc2_core_foundation::{
|
|
|
|
|
kCFRunLoopCommonModes, CFAbsoluteTimeGetCurrent, CFRetained, CFRunLoop, CFRunLoopAddTimer,
|
|
|
|
|
CFRunLoopGetMain, CFRunLoopTimer, CFRunLoopTimerCreate, CFRunLoopTimerInvalidate,
|
|
|
|
|
CFRunLoopTimerSetNextFireDate, CGRect, CGSize,
|
2024-04-18 17:34:19 +02:00
|
|
|
};
|
2025-01-28 21:31:14 +01:00
|
|
|
use objc2_ui_kit::{UIApplication, UICoordinateSpace, UIView};
|
2019-08-26 15:47:23 -07:00
|
|
|
|
2024-08-15 23:19:57 +02:00
|
|
|
use super::super::event_handler::EventHandler;
|
2025-03-01 20:18:28 +01:00
|
|
|
use super::super::event_loop_proxy::EventLoopProxy;
|
2024-02-22 22:28:49 +01:00
|
|
|
use super::window::WinitUIWindow;
|
2025-03-01 20:18:28 +01:00
|
|
|
use super::ActiveEventLoop;
|
2024-08-15 23:19:57 +02:00
|
|
|
use crate::application::ApplicationHandler;
|
2023-08-27 17:04:39 +02:00
|
|
|
use crate::dpi::PhysicalSize;
|
2024-12-03 19:53:29 +01:00
|
|
|
use crate::event::{StartCause, SurfaceSizeWriter, WindowEvent};
|
2024-08-15 23:19:57 +02:00
|
|
|
use crate::event_loop::ControlFlow;
|
2024-12-03 19:53:29 +01:00
|
|
|
use crate::window::WindowId;
|
2019-05-25 18:10:41 -07:00
|
|
|
|
|
|
|
|
macro_rules! bug {
|
2019-09-04 14:23:11 -07:00
|
|
|
($($msg:tt)*) => {
|
|
|
|
|
panic!("winit iOS bug, file an issue: {}", format!($($msg)*))
|
2019-05-25 18:10:41 -07:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-04 14:23:11 -07:00
|
|
|
macro_rules! bug_assert {
|
|
|
|
|
($test:expr, $($msg:tt)*) => {
|
|
|
|
|
assert!($test, "winit iOS bug, file an issue: {}", format!($($msg)*))
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-15 23:19:57 +02:00
|
|
|
/// Get the global event handler for the application.
|
|
|
|
|
///
|
|
|
|
|
/// This is stored separately from AppState, since AppState needs to be accessible while the handler
|
|
|
|
|
/// is executing.
|
|
|
|
|
fn get_handler(mtm: MainThreadMarker) -> &'static EventHandler {
|
|
|
|
|
// SAFETY: Creating `StaticMainThreadBound` in a `const` context, where there is no concept
|
|
|
|
|
// of the main thread.
|
2025-01-28 21:31:14 +01:00
|
|
|
static GLOBAL: MainThreadBound<OnceCell<EventHandler>> =
|
|
|
|
|
MainThreadBound::new(OnceCell::new(), unsafe { MainThreadMarker::new_unchecked() });
|
2024-08-15 23:19:57 +02:00
|
|
|
|
|
|
|
|
GLOBAL.get(mtm).get_or_init(EventHandler::new)
|
2024-01-25 05:26:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub(crate) enum EventWrapper {
|
2024-12-03 19:53:29 +01:00
|
|
|
Window { window_id: WindowId, event: WindowEvent },
|
2023-08-27 17:04:39 +02:00
|
|
|
ScaleFactorChanged(ScaleFactorChanged),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub struct ScaleFactorChanged {
|
2024-05-27 14:49:22 +02:00
|
|
|
pub(super) window: Retained<WinitUIWindow>,
|
2023-08-27 17:04:39 +02:00
|
|
|
pub(super) suggested_size: PhysicalSize<u32>,
|
|
|
|
|
pub(super) scale_factor: f64,
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-03 19:53:29 +01:00
|
|
|
impl EventWrapper {
|
2019-09-04 14:23:11 -07:00
|
|
|
fn is_redraw(&self) -> bool {
|
2024-12-03 19:53:29 +01:00
|
|
|
matches!(self, Self::Window { event: WindowEvent::RedrawRequested, .. })
|
2019-09-04 14:23:11 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-25 18:10:41 -07:00
|
|
|
// this is the state machine for the app lifecycle
|
|
|
|
|
#[derive(Debug)]
|
2019-09-04 14:23:11 -07:00
|
|
|
#[must_use = "dropping `AppStateImpl` without inspecting it is probably a bug"]
|
2019-05-25 18:10:41 -07:00
|
|
|
enum AppStateImpl {
|
2024-08-15 23:19:57 +02:00
|
|
|
Initial {
|
2024-05-27 14:49:22 +02:00
|
|
|
queued_gpu_redraws: HashSet<Retained<WinitUIWindow>>,
|
2019-05-25 18:10:41 -07:00
|
|
|
},
|
|
|
|
|
ProcessingEvents {
|
2024-05-27 14:49:22 +02:00
|
|
|
queued_gpu_redraws: HashSet<Retained<WinitUIWindow>>,
|
2019-05-25 18:10:41 -07:00
|
|
|
active_control_flow: ControlFlow,
|
|
|
|
|
},
|
2019-09-04 14:23:11 -07:00
|
|
|
ProcessingRedraws {
|
|
|
|
|
active_control_flow: ControlFlow,
|
2019-05-25 18:10:41 -07:00
|
|
|
},
|
|
|
|
|
Waiting {
|
|
|
|
|
start: Instant,
|
|
|
|
|
},
|
2024-08-15 23:19:57 +02:00
|
|
|
PollFinished,
|
2019-05-25 18:10:41 -07:00
|
|
|
Terminated,
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-07 08:25:04 +02:00
|
|
|
pub(crate) struct AppState {
|
2019-09-04 14:23:11 -07:00
|
|
|
// This should never be `None`, except for briefly during a state transition.
|
|
|
|
|
app_state: Option<AppStateImpl>,
|
|
|
|
|
control_flow: ControlFlow,
|
|
|
|
|
waker: EventLoopWaker,
|
2024-11-12 10:56:20 +03:00
|
|
|
event_loop_proxy: Arc<EventLoopProxy>,
|
2024-12-03 19:53:29 +01:00
|
|
|
queued_events: Vec<EventWrapper>,
|
2019-09-04 14:23:11 -07:00
|
|
|
}
|
|
|
|
|
|
2019-05-25 18:10:41 -07:00
|
|
|
impl AppState {
|
2025-03-01 20:18:28 +01:00
|
|
|
pub(crate) fn get_mut(mtm: MainThreadMarker) -> RefMut<'static, AppState> {
|
2019-05-25 18:10:41 -07:00
|
|
|
// 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);
|
|
|
|
|
|
2024-09-23 22:26:21 +03:00
|
|
|
#[allow(unknown_lints)] // New lint below
|
|
|
|
|
#[allow(static_mut_refs)] // TODO: Use `MainThreadBound` instead.
|
2023-08-27 17:04:39 +02:00
|
|
|
let mut guard = unsafe { APP_STATE.borrow_mut() };
|
2019-05-25 18:10:41 -07:00
|
|
|
if guard.is_none() {
|
|
|
|
|
#[inline(never)]
|
|
|
|
|
#[cold]
|
2025-03-01 20:18:28 +01:00
|
|
|
fn init_guard(guard: &mut RefMut<'static, Option<AppState>>, mtm: MainThreadMarker) {
|
2025-01-28 21:31:14 +01:00
|
|
|
let waker = EventLoopWaker::new(unsafe { CFRunLoopGetMain().unwrap() });
|
2025-03-01 20:18:28 +01:00
|
|
|
let event_loop_proxy = Arc::new(EventLoopProxy::new(mtm, move || {
|
|
|
|
|
get_handler(mtm).handle(|app| app.proxy_wake_up(&ActiveEventLoop { mtm }));
|
|
|
|
|
}));
|
|
|
|
|
|
2019-05-25 18:10:41 -07:00
|
|
|
**guard = Some(AppState {
|
2024-12-03 19:53:29 +01:00
|
|
|
app_state: Some(AppStateImpl::Initial { queued_gpu_redraws: HashSet::new() }),
|
2019-05-25 18:10:41 -07:00
|
|
|
control_flow: ControlFlow::default(),
|
|
|
|
|
waker,
|
2025-03-01 20:18:28 +01:00
|
|
|
event_loop_proxy,
|
2024-12-03 19:53:29 +01:00
|
|
|
queued_events: Vec::new(),
|
2019-05-25 18:10:41 -07:00
|
|
|
});
|
|
|
|
|
}
|
2025-03-01 20:18:28 +01:00
|
|
|
init_guard(&mut guard, mtm);
|
2019-05-25 18:10:41 -07:00
|
|
|
}
|
2019-06-21 11:33:15 -04:00
|
|
|
RefMut::map(guard, |state| state.as_mut().unwrap())
|
2019-05-25 18:10:41 -07:00
|
|
|
}
|
2019-06-21 11:33:15 -04:00
|
|
|
|
2019-09-04 14:23:11 -07:00
|
|
|
fn state(&self) -> &AppStateImpl {
|
|
|
|
|
match &self.app_state {
|
|
|
|
|
Some(ref state) => state,
|
|
|
|
|
None => bug!("`AppState` previously failed a state transition"),
|
2019-05-25 18:10:41 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-04 14:23:11 -07:00
|
|
|
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
|
2019-06-21 11:33:15 -04:00
|
|
|
);
|
2019-09-04 14:23:11 -07:00
|
|
|
self.app_state = Some(new_state)
|
2019-05-25 18:10:41 -07:00
|
|
|
}
|
|
|
|
|
|
2019-09-04 14:23:11 -07:00
|
|
|
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"),
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-05-25 18:10:41 -07:00
|
|
|
|
2019-09-04 14:23:11 -07:00
|
|
|
fn has_launched(&self) -> bool {
|
2024-08-15 23:19:57 +02:00
|
|
|
!matches!(self.state(), AppStateImpl::Initial { .. })
|
2019-09-04 14:23:11 -07:00
|
|
|
}
|
2019-05-25 18:10:41 -07:00
|
|
|
|
2023-11-22 13:14:51 +01:00
|
|
|
fn has_terminated(&self) -> bool {
|
|
|
|
|
matches!(self.state(), AppStateImpl::Terminated)
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-03 19:53:29 +01:00
|
|
|
fn did_finish_launching_transition(&mut self) {
|
|
|
|
|
let queued_gpu_redraws = match self.take_state() {
|
|
|
|
|
AppStateImpl::Initial { queued_gpu_redraws } => queued_gpu_redraws,
|
2019-09-04 14:23:11 -07:00
|
|
|
s => bug!("unexpected state {:?}", s),
|
|
|
|
|
};
|
|
|
|
|
self.set_state(AppStateImpl::ProcessingEvents {
|
2023-10-10 22:46:08 +02:00
|
|
|
active_control_flow: self.control_flow,
|
2019-09-04 14:23:11 -07:00
|
|
|
queued_gpu_redraws,
|
|
|
|
|
});
|
|
|
|
|
}
|
2019-05-25 18:10:41 -07:00
|
|
|
|
2024-12-03 19:53:29 +01:00
|
|
|
fn wakeup_transition(&mut self) -> Option<StartCause> {
|
2019-09-04 14:23:11 -07:00
|
|
|
// before `AppState::did_finish_launching` is called, pretend there is no running
|
|
|
|
|
// event loop.
|
2023-11-22 13:14:51 +01:00
|
|
|
if !self.has_launched() || self.has_terminated() {
|
2019-09-04 14:23:11 -07:00
|
|
|
return None;
|
2019-05-25 18:10:41 -07:00
|
|
|
}
|
|
|
|
|
|
2024-12-03 19:53:29 +01:00
|
|
|
let start_cause = match (self.control_flow, self.take_state()) {
|
|
|
|
|
(ControlFlow::Poll, AppStateImpl::PollFinished) => StartCause::Poll,
|
2024-08-15 23:19:57 +02:00
|
|
|
(ControlFlow::Wait, AppStateImpl::Waiting { start }) => {
|
2024-12-03 19:53:29 +01:00
|
|
|
StartCause::WaitCancelled { start, requested_resume: None }
|
2024-08-15 23:19:57 +02:00
|
|
|
},
|
|
|
|
|
(ControlFlow::WaitUntil(requested_resume), AppStateImpl::Waiting { start }) => {
|
|
|
|
|
if Instant::now() >= requested_resume {
|
2024-12-03 19:53:29 +01:00
|
|
|
StartCause::ResumeTimeReached { start, requested_resume }
|
2019-09-04 14:23:11 -07:00
|
|
|
} else {
|
2024-12-03 19:53:29 +01:00
|
|
|
StartCause::WaitCancelled { start, requested_resume: Some(requested_resume) }
|
2024-08-15 23:19:57 +02:00
|
|
|
}
|
2019-09-04 14:23:11 -07:00
|
|
|
},
|
|
|
|
|
s => bug!("`EventHandler` unexpectedly woke up {:?}", s),
|
|
|
|
|
};
|
2019-05-25 18:10:41 -07:00
|
|
|
|
2019-09-04 14:23:11 -07:00
|
|
|
self.set_state(AppStateImpl::ProcessingEvents {
|
|
|
|
|
queued_gpu_redraws: Default::default(),
|
|
|
|
|
active_control_flow: self.control_flow,
|
|
|
|
|
});
|
2024-12-03 19:53:29 +01:00
|
|
|
Some(start_cause)
|
2019-05-25 18:10:41 -07:00
|
|
|
}
|
|
|
|
|
|
2024-05-27 14:49:22 +02:00
|
|
|
fn main_events_cleared_transition(&mut self) -> HashSet<Retained<WinitUIWindow>> {
|
2024-08-15 23:19:57 +02:00
|
|
|
let (queued_gpu_redraws, active_control_flow) = match self.take_state() {
|
|
|
|
|
AppStateImpl::ProcessingEvents { queued_gpu_redraws, active_control_flow } => {
|
|
|
|
|
(queued_gpu_redraws, active_control_flow)
|
2024-01-25 05:26:50 +01:00
|
|
|
},
|
2019-09-04 14:23:11 -07:00
|
|
|
s => bug!("unexpected state {:?}", s),
|
2019-05-25 18:10:41 -07:00
|
|
|
};
|
2024-08-15 23:19:57 +02:00
|
|
|
self.set_state(AppStateImpl::ProcessingRedraws { active_control_flow });
|
2019-09-04 14:23:11 -07:00
|
|
|
queued_gpu_redraws
|
|
|
|
|
}
|
2019-05-25 18:10:41 -07:00
|
|
|
|
2019-09-04 14:23:11 -07:00
|
|
|
fn events_cleared_transition(&mut self) {
|
2023-11-22 13:14:51 +01:00
|
|
|
if !self.has_launched() || self.has_terminated() {
|
2019-09-04 14:23:11 -07:00
|
|
|
return;
|
|
|
|
|
}
|
2024-08-15 23:19:57 +02:00
|
|
|
let old = match self.take_state() {
|
|
|
|
|
AppStateImpl::ProcessingRedraws { active_control_flow } => active_control_flow,
|
2019-09-04 14:23:11 -07:00
|
|
|
s => bug!("unexpected state {:?}", s),
|
2019-05-25 18:10:41 -07:00
|
|
|
};
|
|
|
|
|
|
2019-09-04 14:23:11 -07:00
|
|
|
let new = self.control_flow;
|
2019-05-25 18:10:41 -07:00
|
|
|
match (old, new) {
|
|
|
|
|
(ControlFlow::Wait, ControlFlow::Wait) => {
|
|
|
|
|
let start = Instant::now();
|
2024-08-15 23:19:57 +02:00
|
|
|
self.set_state(AppStateImpl::Waiting { start });
|
2025-02-06 10:56:10 +01:00
|
|
|
self.waker.stop()
|
2019-09-04 14:23:11 -07:00
|
|
|
},
|
2019-05-25 18:10:41 -07:00
|
|
|
(ControlFlow::WaitUntil(old_instant), ControlFlow::WaitUntil(new_instant))
|
2019-06-21 11:33:15 -04:00
|
|
|
if old_instant == new_instant =>
|
|
|
|
|
{
|
2019-05-25 18:10:41 -07:00
|
|
|
let start = Instant::now();
|
2024-08-15 23:19:57 +02:00
|
|
|
self.set_state(AppStateImpl::Waiting { start });
|
2019-09-04 14:23:11 -07:00
|
|
|
},
|
2019-05-25 18:10:41 -07:00
|
|
|
(_, ControlFlow::Wait) => {
|
|
|
|
|
let start = Instant::now();
|
2024-08-15 23:19:57 +02:00
|
|
|
self.set_state(AppStateImpl::Waiting { start });
|
2019-09-04 14:23:11 -07:00
|
|
|
self.waker.stop()
|
2019-06-24 12:14:55 -04:00
|
|
|
},
|
2019-05-25 18:10:41 -07:00
|
|
|
(_, ControlFlow::WaitUntil(new_instant)) => {
|
|
|
|
|
let start = Instant::now();
|
2024-08-15 23:19:57 +02:00
|
|
|
self.set_state(AppStateImpl::Waiting { start });
|
2019-09-04 14:23:11 -07:00
|
|
|
self.waker.start_at(new_instant)
|
2019-06-24 12:14:55 -04:00
|
|
|
},
|
2023-07-28 17:52:24 +02:00
|
|
|
// Unlike on macOS, handle Poll to Poll transition here to call the waker
|
2019-05-25 18:10:41 -07:00
|
|
|
(_, ControlFlow::Poll) => {
|
2024-08-15 23:19:57 +02:00
|
|
|
self.set_state(AppStateImpl::PollFinished);
|
2019-09-04 14:23:11 -07:00
|
|
|
self.waker.start()
|
2019-06-24 12:14:55 -04:00
|
|
|
},
|
2019-05-25 18:10:41 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-15 23:19:57 +02:00
|
|
|
fn terminated_transition(&mut self) {
|
2019-09-04 14:23:11 -07:00
|
|
|
match self.replace_state(AppStateImpl::Terminated) {
|
2024-08-15 23:19:57 +02:00
|
|
|
AppStateImpl::ProcessingEvents { .. } => {},
|
2024-12-03 19:53:29 +01:00
|
|
|
s => bug!("terminated while not processing events {:?}", s),
|
2019-09-04 14:23:11 -07:00
|
|
|
}
|
|
|
|
|
}
|
2023-09-07 08:25:04 +02:00
|
|
|
|
2024-11-12 10:56:20 +03:00
|
|
|
pub fn event_loop_proxy(&self) -> &Arc<EventLoopProxy> {
|
|
|
|
|
&self.event_loop_proxy
|
2024-06-29 17:19:09 +02:00
|
|
|
}
|
|
|
|
|
|
2023-09-07 08:25:04 +02:00
|
|
|
pub(crate) fn set_control_flow(&mut self, control_flow: ControlFlow) {
|
|
|
|
|
self.control_flow = control_flow;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(crate) fn control_flow(&self) -> ControlFlow {
|
|
|
|
|
self.control_flow
|
|
|
|
|
}
|
2019-09-04 14:23:11 -07:00
|
|
|
}
|
|
|
|
|
|
2024-05-27 14:49:22 +02:00
|
|
|
pub(crate) fn queue_gl_or_metal_redraw(mtm: MainThreadMarker, window: Retained<WinitUIWindow>) {
|
2023-08-27 17:04:39 +02:00
|
|
|
let mut this = AppState::get_mut(mtm);
|
2019-09-04 14:23:11 -07:00
|
|
|
match this.state_mut() {
|
2024-08-15 23:19:57 +02:00
|
|
|
&mut AppStateImpl::Initial { ref mut queued_gpu_redraws, .. }
|
2024-12-03 19:53:29 +01:00
|
|
|
| &mut AppStateImpl::ProcessingEvents { ref mut queued_gpu_redraws, .. } => {
|
2022-06-10 13:43:33 +03:00
|
|
|
let _ = queued_gpu_redraws.insert(window);
|
|
|
|
|
},
|
2019-09-04 14:23:11 -07:00
|
|
|
s @ &mut AppStateImpl::ProcessingRedraws { .. }
|
|
|
|
|
| s @ &mut AppStateImpl::Waiting { .. }
|
|
|
|
|
| s @ &mut AppStateImpl::PollFinished { .. } => bug!("unexpected state {:?}", s),
|
|
|
|
|
&mut AppStateImpl::Terminated => {
|
|
|
|
|
panic!("Attempt to create a `Window` after the app has terminated")
|
2019-05-25 18:10:41 -07:00
|
|
|
},
|
|
|
|
|
}
|
2019-09-04 14:23:11 -07:00
|
|
|
}
|
|
|
|
|
|
2024-08-15 23:19:57 +02:00
|
|
|
pub(crate) fn launch(mtm: MainThreadMarker, app: &mut dyn ApplicationHandler, run: impl FnOnce()) {
|
|
|
|
|
get_handler(mtm).set(app, run)
|
2019-09-04 14:23:11 -07:00
|
|
|
}
|
|
|
|
|
|
2023-08-27 17:04:39 +02:00
|
|
|
pub fn did_finish_launching(mtm: MainThreadMarker) {
|
|
|
|
|
let mut this = AppState::get_mut(mtm);
|
2019-09-04 14:23:11 -07:00
|
|
|
|
|
|
|
|
this.waker.start();
|
|
|
|
|
|
|
|
|
|
// have to drop RefMut because the window setup code below can trigger new events
|
|
|
|
|
drop(this);
|
|
|
|
|
|
2024-12-03 19:53:29 +01:00
|
|
|
AppState::get_mut(mtm).did_finish_launching_transition();
|
2019-09-04 14:23:11 -07:00
|
|
|
|
2024-12-03 19:53:29 +01:00
|
|
|
get_handler(mtm).handle(|app| app.new_events(&ActiveEventLoop { mtm }, StartCause::Init));
|
|
|
|
|
get_handler(mtm).handle(|app| app.can_create_surfaces(&ActiveEventLoop { mtm }));
|
|
|
|
|
handle_nonuser_events(mtm, []);
|
2019-09-04 14:23:11 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// AppState::did_finish_launching handles the special transition `Init`
|
2023-08-27 17:04:39 +02:00
|
|
|
pub fn handle_wakeup_transition(mtm: MainThreadMarker) {
|
|
|
|
|
let mut this = AppState::get_mut(mtm);
|
2024-12-03 19:53:29 +01:00
|
|
|
let cause = match this.wakeup_transition() {
|
2019-09-04 14:23:11 -07:00
|
|
|
None => return,
|
2024-12-03 19:53:29 +01:00
|
|
|
Some(cause) => cause,
|
2019-09-04 14:23:11 -07:00
|
|
|
};
|
|
|
|
|
drop(this);
|
|
|
|
|
|
2024-12-03 19:53:29 +01:00
|
|
|
get_handler(mtm).handle(|app| app.new_events(&ActiveEventLoop { mtm }, cause));
|
|
|
|
|
handle_nonuser_events(mtm, []);
|
2019-09-04 14:23:11 -07:00
|
|
|
}
|
|
|
|
|
|
2023-08-27 17:04:39 +02:00
|
|
|
pub(crate) fn handle_nonuser_event(mtm: MainThreadMarker, event: EventWrapper) {
|
|
|
|
|
handle_nonuser_events(mtm, std::iter::once(event))
|
2019-09-04 14:23:11 -07:00
|
|
|
}
|
|
|
|
|
|
2023-08-27 17:04:39 +02:00
|
|
|
pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
|
|
|
|
|
mtm: MainThreadMarker,
|
|
|
|
|
events: I,
|
|
|
|
|
) {
|
|
|
|
|
let mut this = AppState::get_mut(mtm);
|
2023-11-22 13:14:51 +01:00
|
|
|
if this.has_terminated() {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-03 19:53:29 +01:00
|
|
|
if !get_handler(mtm).ready() {
|
|
|
|
|
// Prevent re-entrancy; queue the events up for once we're done handling the event instead.
|
|
|
|
|
this.queued_events.extend(events);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let processing_redraws = matches!(this.state(), AppStateImpl::ProcessingRedraws { .. });
|
2019-09-04 14:23:11 -07:00
|
|
|
drop(this);
|
|
|
|
|
|
2024-12-03 19:53:29 +01:00
|
|
|
for event in events {
|
|
|
|
|
if !processing_redraws && event.is_redraw() {
|
|
|
|
|
tracing::info!("processing `RedrawRequested` during the main event loop");
|
|
|
|
|
} else if processing_redraws && !event.is_redraw() {
|
|
|
|
|
tracing::warn!(
|
|
|
|
|
"processing non `RedrawRequested` event after the main event loop: {:#?}",
|
|
|
|
|
event
|
|
|
|
|
);
|
2019-09-04 14:23:11 -07:00
|
|
|
}
|
2024-12-03 19:53:29 +01:00
|
|
|
handle_wrapped_event(mtm, event)
|
2019-09-04 14:23:11 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
loop {
|
2023-08-27 17:04:39 +02:00
|
|
|
let mut this = AppState::get_mut(mtm);
|
2024-12-03 19:53:29 +01:00
|
|
|
let queued_events = mem::take(&mut this.queued_events);
|
2019-09-04 14:23:11 -07:00
|
|
|
if queued_events.is_empty() {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
drop(this);
|
|
|
|
|
|
2024-12-03 19:53:29 +01:00
|
|
|
for event in queued_events {
|
|
|
|
|
if !processing_redraws && event.is_redraw() {
|
|
|
|
|
tracing::info!("processing `RedrawRequested` during the main event loop");
|
|
|
|
|
} else if processing_redraws && !event.is_redraw() {
|
|
|
|
|
tracing::warn!(
|
|
|
|
|
"processing non-`RedrawRequested` event after the main event loop: {:#?}",
|
|
|
|
|
event
|
|
|
|
|
);
|
2019-09-04 14:23:11 -07:00
|
|
|
}
|
2024-12-03 19:53:29 +01:00
|
|
|
handle_wrapped_event(mtm, event);
|
2019-09-04 14:23:11 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-27 17:04:39 +02:00
|
|
|
fn handle_user_events(mtm: MainThreadMarker) {
|
2024-12-03 19:53:29 +01:00
|
|
|
let this = AppState::get_mut(mtm);
|
|
|
|
|
if matches!(this.state(), AppStateImpl::ProcessingRedraws { .. }) {
|
2019-09-04 14:23:11 -07:00
|
|
|
bug!("user events attempted to be sent out while `ProcessingRedraws`");
|
|
|
|
|
}
|
|
|
|
|
drop(this);
|
|
|
|
|
|
|
|
|
|
loop {
|
2023-08-27 17:04:39 +02:00
|
|
|
let mut this = AppState::get_mut(mtm);
|
2024-12-03 19:53:29 +01:00
|
|
|
let queued_events = mem::take(&mut this.queued_events);
|
2019-09-04 14:23:11 -07:00
|
|
|
if queued_events.is_empty() {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
drop(this);
|
|
|
|
|
|
2024-12-03 19:53:29 +01:00
|
|
|
for event in queued_events {
|
|
|
|
|
handle_wrapped_event(mtm, event);
|
2019-09-04 14:23:11 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-06 14:39:31 +02:00
|
|
|
pub(crate) fn send_occluded_event_for_all_windows(application: &UIApplication, occluded: bool) {
|
|
|
|
|
let mtm = MainThreadMarker::from(application);
|
|
|
|
|
|
|
|
|
|
let mut events = Vec::new();
|
|
|
|
|
#[allow(deprecated)]
|
|
|
|
|
for window in application.windows().iter() {
|
2025-01-28 21:31:14 +01:00
|
|
|
if let Ok(window) = window.downcast::<WinitUIWindow>() {
|
2024-12-03 19:53:29 +01:00
|
|
|
events.push(EventWrapper::Window {
|
2024-10-08 15:29:40 +02:00
|
|
|
window_id: window.id(),
|
2024-06-06 14:39:31 +02:00
|
|
|
event: WindowEvent::Occluded(occluded),
|
2024-12-03 19:53:29 +01:00
|
|
|
});
|
2024-06-06 14:39:31 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
handle_nonuser_events(mtm, events);
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-27 17:04:39 +02:00
|
|
|
pub fn handle_main_events_cleared(mtm: MainThreadMarker) {
|
|
|
|
|
let mut this = AppState::get_mut(mtm);
|
2023-11-22 13:14:51 +01:00
|
|
|
if !this.has_launched() || this.has_terminated() {
|
2019-09-04 14:23:11 -07:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
match this.state_mut() {
|
2022-07-29 14:39:41 +03:00
|
|
|
AppStateImpl::ProcessingEvents { .. } => {},
|
2019-09-04 14:23:11 -07:00
|
|
|
_ => bug!("`ProcessingRedraws` happened unexpectedly"),
|
|
|
|
|
};
|
|
|
|
|
drop(this);
|
|
|
|
|
|
2023-08-27 17:04:39 +02:00
|
|
|
handle_user_events(mtm);
|
2019-09-04 14:23:11 -07:00
|
|
|
|
2023-08-27 17:04:39 +02:00
|
|
|
let mut this = AppState::get_mut(mtm);
|
2023-07-28 17:37:56 +01:00
|
|
|
let redraw_events: Vec<EventWrapper> = this
|
2019-09-04 14:23:11 -07:00
|
|
|
.main_events_cleared_transition()
|
|
|
|
|
.into_iter()
|
2024-12-03 19:53:29 +01:00
|
|
|
.map(|window| EventWrapper::Window {
|
|
|
|
|
window_id: window.id(),
|
|
|
|
|
event: WindowEvent::RedrawRequested,
|
2023-08-27 16:15:09 +02:00
|
|
|
})
|
2019-12-21 16:35:18 -08:00
|
|
|
.collect();
|
2019-09-04 14:23:11 -07:00
|
|
|
drop(this);
|
|
|
|
|
|
2023-08-27 17:04:39 +02:00
|
|
|
handle_nonuser_events(mtm, redraw_events);
|
2024-12-03 19:53:29 +01:00
|
|
|
get_handler(mtm).handle(|app| app.about_to_wait(&ActiveEventLoop { mtm }));
|
|
|
|
|
handle_nonuser_events(mtm, []);
|
2019-09-04 14:23:11 -07:00
|
|
|
}
|
|
|
|
|
|
2023-08-27 17:04:39 +02:00
|
|
|
pub fn handle_events_cleared(mtm: MainThreadMarker) {
|
|
|
|
|
AppState::get_mut(mtm).events_cleared_transition();
|
2019-09-04 14:23:11 -07:00
|
|
|
}
|
|
|
|
|
|
2024-12-03 19:53:29 +01:00
|
|
|
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, []);
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-06 14:39:31 +02:00
|
|
|
pub(crate) fn terminated(application: &UIApplication) {
|
|
|
|
|
let mtm = MainThreadMarker::from(application);
|
|
|
|
|
|
|
|
|
|
let mut events = Vec::new();
|
|
|
|
|
#[allow(deprecated)]
|
|
|
|
|
for window in application.windows().iter() {
|
2025-01-28 21:31:14 +01:00
|
|
|
if let Ok(window) = window.downcast::<WinitUIWindow>() {
|
2024-12-03 19:53:29 +01:00
|
|
|
events.push(EventWrapper::Window {
|
2024-10-08 15:29:40 +02:00
|
|
|
window_id: window.id(),
|
2024-06-06 14:39:31 +02:00
|
|
|
event: WindowEvent::Destroyed,
|
2024-12-03 19:53:29 +01:00
|
|
|
});
|
2024-06-06 14:39:31 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
handle_nonuser_events(mtm, events);
|
|
|
|
|
|
2023-08-27 17:04:39 +02:00
|
|
|
let mut this = AppState::get_mut(mtm);
|
2024-08-15 23:19:57 +02:00
|
|
|
this.terminated_transition();
|
2019-09-04 14:23:11 -07:00
|
|
|
drop(this);
|
|
|
|
|
|
2024-12-03 19:53:29 +01:00
|
|
|
get_handler(mtm).handle(|app| app.exiting(&ActiveEventLoop { mtm }));
|
2025-03-01 20:18:28 +01:00
|
|
|
|
|
|
|
|
let this = AppState::get_mut(mtm);
|
|
|
|
|
// Prevent EventLoopProxy from firing again.
|
|
|
|
|
this.event_loop_proxy.invalidate();
|
2024-12-03 19:53:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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),
|
|
|
|
|
}
|
2019-05-25 18:10:41 -07:00
|
|
|
}
|
|
|
|
|
|
2024-08-15 23:19:57 +02:00
|
|
|
fn handle_hidpi_proxy(mtm: MainThreadMarker, event: ScaleFactorChanged) {
|
2023-08-27 17:04:39 +02:00
|
|
|
let ScaleFactorChanged { suggested_size, scale_factor, window } = event;
|
2024-09-04 15:04:48 +02:00
|
|
|
let new_surface_size = Arc::new(Mutex::new(suggested_size));
|
2024-12-03 19:53:29 +01:00
|
|
|
get_handler(mtm).handle(|app| {
|
|
|
|
|
app.window_event(&ActiveEventLoop { mtm }, window.id(), WindowEvent::ScaleFactorChanged {
|
2020-01-03 14:52:27 -05:00
|
|
|
scale_factor,
|
2024-09-04 15:04:48 +02:00
|
|
|
surface_size_writer: SurfaceSizeWriter::new(Arc::downgrade(&new_surface_size)),
|
2024-12-03 19:53:29 +01:00
|
|
|
});
|
|
|
|
|
});
|
2022-12-28 18:36:32 +01:00
|
|
|
let (view, screen_frame) = get_view_and_screen_frame(&window);
|
2024-09-04 15:04:48 +02:00
|
|
|
let physical_size = *new_surface_size.lock().unwrap();
|
|
|
|
|
drop(new_surface_size);
|
2020-01-03 14:52:27 -05:00
|
|
|
let logical_size = physical_size.to_logical(scale_factor);
|
2022-12-28 18:36:32 +01:00
|
|
|
let size = CGSize::new(logical_size.width, logical_size.height);
|
2020-01-03 00:28:41 -05:00
|
|
|
let new_frame: CGRect = CGRect::new(screen_frame.origin, size);
|
2023-01-23 00:01:45 +01:00
|
|
|
view.setFrame(new_frame);
|
2019-10-18 18:31:26 +03:00
|
|
|
}
|
|
|
|
|
|
2024-05-27 14:49:22 +02:00
|
|
|
fn get_view_and_screen_frame(window: &WinitUIWindow) -> (Retained<UIView>, CGRect) {
|
2022-12-28 18:36:32 +01:00
|
|
|
let view_controller = window.rootViewController().unwrap();
|
2023-01-23 00:01:45 +01:00
|
|
|
let view = view_controller.view().unwrap();
|
2022-12-28 18:36:32 +01:00
|
|
|
let bounds = window.bounds();
|
|
|
|
|
let screen = window.screen();
|
|
|
|
|
let screen_space = screen.coordinateSpace();
|
|
|
|
|
let screen_frame = window.convertRect_toCoordinateSpace(bounds, &screen_space);
|
|
|
|
|
(view, screen_frame)
|
2019-10-18 18:31:26 +03:00
|
|
|
}
|
|
|
|
|
|
2019-05-25 18:10:41 -07:00
|
|
|
struct EventLoopWaker {
|
2025-01-28 21:31:14 +01:00
|
|
|
timer: CFRetained<CFRunLoopTimer>,
|
2019-05-25 18:10:41 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Drop for EventLoopWaker {
|
|
|
|
|
fn drop(&mut self) {
|
|
|
|
|
unsafe {
|
2025-01-28 21:31:14 +01:00
|
|
|
CFRunLoopTimerInvalidate(&self.timer);
|
2019-05-25 18:10:41 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl EventLoopWaker {
|
2025-01-28 21:31:14 +01:00
|
|
|
fn new(rl: CFRetained<CFRunLoop>) -> EventLoopWaker {
|
|
|
|
|
extern "C-unwind" fn wakeup_main_loop(_timer: *mut CFRunLoopTimer, _info: *mut c_void) {}
|
2019-05-25 18:10:41 -07:00
|
|
|
unsafe {
|
2019-07-13 01:05:07 +02: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-25 18:10:41 -07:00
|
|
|
let timer = CFRunLoopTimerCreate(
|
2025-01-28 21:31:14 +01:00
|
|
|
None,
|
2024-06-15 15:26:26 +03:00
|
|
|
f64::MAX,
|
2019-05-25 18:10:41 -07:00
|
|
|
0.000_000_1,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
2025-01-28 21:31:14 +01:00
|
|
|
Some(wakeup_main_loop),
|
2019-05-25 18:10:41 -07:00
|
|
|
ptr::null_mut(),
|
2025-01-28 21:31:14 +01:00
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
CFRunLoopAddTimer(&rl, Some(&timer), kCFRunLoopCommonModes);
|
2019-05-25 18:10:41 -07:00
|
|
|
|
|
|
|
|
EventLoopWaker { timer }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn stop(&mut self) {
|
2025-01-28 21:31:14 +01:00
|
|
|
unsafe { CFRunLoopTimerSetNextFireDate(&self.timer, f64::MAX) }
|
2019-05-25 18:10:41 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn start(&mut self) {
|
2025-01-28 21:31:14 +01:00
|
|
|
unsafe { CFRunLoopTimerSetNextFireDate(&self.timer, f64::MIN) }
|
2019-05-25 18:10:41 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
2025-01-28 21:31:14 +01:00
|
|
|
CFRunLoopTimerSetNextFireDate(&self.timer, current + fsecs);
|
2019-05-25 18:10:41 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-06-21 11:33:15 -04:00
|
|
|
}
|