2019-06-21 11:33:15 -04:00
|
|
|
use std::{
|
|
|
|
|
collections::VecDeque,
|
|
|
|
|
ffi::c_void,
|
|
|
|
|
marker::PhantomData,
|
2022-09-08 21:03:25 +02:00
|
|
|
ptr,
|
2019-06-21 11:33:15 -04:00
|
|
|
sync::mpsc::{self, Receiver, Sender},
|
2019-05-25 18:10:41 -07:00
|
|
|
};
|
2019-06-21 11:33:15 -04:00
|
|
|
|
2022-09-08 21:03:25 +02:00
|
|
|
use core_foundation::base::{CFIndex, CFRelease};
|
|
|
|
|
use core_foundation::runloop::{
|
|
|
|
|
kCFRunLoopAfterWaiting, kCFRunLoopBeforeWaiting, kCFRunLoopCommonModes, kCFRunLoopDefaultMode,
|
2022-09-08 21:56:53 +02:00
|
|
|
kCFRunLoopExit, CFRunLoopActivity, CFRunLoopAddObserver, CFRunLoopAddSource, CFRunLoopGetMain,
|
|
|
|
|
CFRunLoopObserverCreate, CFRunLoopObserverRef, CFRunLoopSourceContext, CFRunLoopSourceCreate,
|
|
|
|
|
CFRunLoopSourceInvalidate, CFRunLoopSourceRef, CFRunLoopSourceSignal, CFRunLoopWakeUp,
|
2022-09-08 21:03:25 +02:00
|
|
|
};
|
2023-07-29 00:33:03 +02:00
|
|
|
use icrate::Foundation::{MainThreadMarker, NSString};
|
2022-12-28 18:36:32 +01:00
|
|
|
use objc2::ClassType;
|
2022-07-21 22:22:36 +03:00
|
|
|
|
2019-06-21 11:33:15 -04:00
|
|
|
use crate::{
|
2023-08-13 23:20:09 +04:00
|
|
|
error::EventLoopError,
|
2019-06-21 11:33:15 -04:00
|
|
|
event::Event,
|
|
|
|
|
event_loop::{
|
2024-01-31 17:29:59 +04:00
|
|
|
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents, EventLoopClosed,
|
2019-06-21 11:33:15 -04:00
|
|
|
},
|
|
|
|
|
platform::ios::Idiom,
|
2024-01-25 05:26:50 +01:00
|
|
|
platform_impl::platform::app_state::{EventLoopHandler, HandlePendingUserEvents},
|
2024-02-03 07:27:17 +04:00
|
|
|
window::{CustomCursor, CustomCursorSource},
|
2019-06-21 11:33:15 -04:00
|
|
|
};
|
|
|
|
|
|
2024-02-27 21:07:52 +01:00
|
|
|
use super::{app_delegate::AppDelegate, uikit::UIUserInterfaceIdiom};
|
2024-02-22 22:28:49 +01:00
|
|
|
use super::{app_state, monitor, MonitorHandle};
|
2023-09-07 08:25:04 +02:00
|
|
|
use super::{
|
|
|
|
|
app_state::AppState,
|
|
|
|
|
uikit::{UIApplication, UIApplicationMain, UIDevice, UIScreen},
|
|
|
|
|
};
|
2023-08-13 23:20:09 +04:00
|
|
|
|
2019-10-18 18:31:26 +03:00
|
|
|
#[derive(Debug)]
|
2024-01-31 17:29:59 +04:00
|
|
|
pub struct ActiveEventLoop {
|
2023-08-27 17:04:39 +02:00
|
|
|
pub(super) mtm: MainThreadMarker,
|
2019-05-25 18:10:41 -07:00
|
|
|
}
|
|
|
|
|
|
2024-01-31 17:29:59 +04:00
|
|
|
impl ActiveEventLoop {
|
2024-02-03 07:27:17 +04:00
|
|
|
pub fn create_custom_cursor(&self, source: CustomCursorSource) -> CustomCursor {
|
|
|
|
|
let _ = source.inner;
|
|
|
|
|
CustomCursor {
|
|
|
|
|
inner: super::PlatformCustomCursor,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-04 15:46:41 -04:00
|
|
|
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
|
2023-08-27 17:04:39 +02:00
|
|
|
monitor::uiscreens(self.mtm)
|
2020-07-04 15:46:41 -04:00
|
|
|
}
|
|
|
|
|
|
2022-09-21 10:04:28 +02:00
|
|
|
pub fn primary_monitor(&self) -> Option<MonitorHandle> {
|
2023-08-27 17:04:39 +02:00
|
|
|
Some(MonitorHandle::new(UIScreen::main(self.mtm)))
|
2020-07-04 15:46:41 -04:00
|
|
|
}
|
2022-07-21 22:22:36 +03:00
|
|
|
|
2023-09-01 23:14:16 +02:00
|
|
|
#[inline]
|
|
|
|
|
pub fn listen_device_events(&self, _allowed: DeviceEvents) {}
|
|
|
|
|
|
2023-10-14 19:07:39 -07:00
|
|
|
#[cfg(feature = "rwh_05")]
|
|
|
|
|
#[inline]
|
|
|
|
|
pub fn raw_display_handle_rwh_05(&self) -> rwh_05::RawDisplayHandle {
|
|
|
|
|
rwh_05::RawDisplayHandle::UiKit(rwh_05::UiKitDisplayHandle::empty())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(feature = "rwh_06")]
|
|
|
|
|
#[inline]
|
|
|
|
|
pub fn raw_display_handle_rwh_06(
|
|
|
|
|
&self,
|
|
|
|
|
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
|
|
|
|
|
Ok(rwh_06::RawDisplayHandle::UiKit(
|
|
|
|
|
rwh_06::UiKitDisplayHandle::new(),
|
|
|
|
|
))
|
2022-07-21 22:22:36 +03:00
|
|
|
}
|
2023-09-07 08:25:04 +02:00
|
|
|
|
|
|
|
|
pub(crate) fn set_control_flow(&self, control_flow: ControlFlow) {
|
|
|
|
|
AppState::get_mut(self.mtm).set_control_flow(control_flow)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(crate) fn control_flow(&self) -> ControlFlow {
|
|
|
|
|
AppState::get_mut(self.mtm).control_flow()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(crate) fn exit(&self) {
|
|
|
|
|
// https://developer.apple.com/library/archive/qa/qa1561/_index.html
|
2024-02-19 11:58:44 +07:00
|
|
|
// it is not possible to quit an iOS app gracefully and programmatically
|
2024-02-25 19:20:39 -08:00
|
|
|
tracing::warn!("`ControlFlow::Exit` ignored on iOS");
|
2023-09-07 08:25:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(crate) fn exiting(&self) -> bool {
|
|
|
|
|
false
|
|
|
|
|
}
|
2024-01-15 11:58:11 -08:00
|
|
|
|
|
|
|
|
pub(crate) fn owned_display_handle(&self) -> OwnedDisplayHandle {
|
|
|
|
|
OwnedDisplayHandle
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
|
pub(crate) struct OwnedDisplayHandle;
|
|
|
|
|
|
|
|
|
|
impl OwnedDisplayHandle {
|
|
|
|
|
#[cfg(feature = "rwh_05")]
|
|
|
|
|
#[inline]
|
|
|
|
|
pub fn raw_display_handle_rwh_05(&self) -> rwh_05::RawDisplayHandle {
|
|
|
|
|
rwh_05::UiKitDisplayHandle::empty().into()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(feature = "rwh_06")]
|
|
|
|
|
#[inline]
|
|
|
|
|
pub fn raw_display_handle_rwh_06(
|
|
|
|
|
&self,
|
|
|
|
|
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
|
|
|
|
|
Ok(rwh_06::UiKitDisplayHandle::new().into())
|
|
|
|
|
}
|
2020-07-04 15:46:41 -04:00
|
|
|
}
|
|
|
|
|
|
2024-01-25 05:26:50 +01:00
|
|
|
fn map_user_event<T: 'static>(
|
2024-01-31 17:29:59 +04:00
|
|
|
mut handler: impl FnMut(Event<T>, &RootActiveEventLoop),
|
2024-01-25 05:26:50 +01:00
|
|
|
receiver: mpsc::Receiver<T>,
|
2024-01-31 17:29:59 +04:00
|
|
|
) -> impl FnMut(Event<HandlePendingUserEvents>, &RootActiveEventLoop) {
|
2024-01-25 05:26:50 +01:00
|
|
|
move |event, window_target| match event.map_nonuser_event() {
|
|
|
|
|
Ok(event) => (handler)(event, window_target),
|
|
|
|
|
Err(_) => {
|
|
|
|
|
for event in receiver.try_iter() {
|
|
|
|
|
(handler)(Event::UserEvent(event), window_target);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-25 18:10:41 -07:00
|
|
|
pub struct EventLoop<T: 'static> {
|
2023-08-27 17:04:39 +02:00
|
|
|
mtm: MainThreadMarker,
|
|
|
|
|
sender: Sender<T>,
|
|
|
|
|
receiver: Receiver<T>,
|
2024-01-31 17:29:59 +04:00
|
|
|
window_target: RootActiveEventLoop,
|
2019-05-25 18:10:41 -07:00
|
|
|
}
|
|
|
|
|
|
2022-06-10 13:43:33 +03:00
|
|
|
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
2022-02-16 22:09:03 +01:00
|
|
|
pub(crate) struct PlatformSpecificEventLoopAttributes {}
|
|
|
|
|
|
2019-05-25 18:10:41 -07:00
|
|
|
impl<T: 'static> EventLoop<T> {
|
2023-08-13 23:20:09 +04:00
|
|
|
pub(crate) fn new(
|
|
|
|
|
_: &PlatformSpecificEventLoopAttributes,
|
|
|
|
|
) -> Result<EventLoop<T>, EventLoopError> {
|
2023-08-27 17:04:39 +02:00
|
|
|
let mtm = MainThreadMarker::new()
|
|
|
|
|
.expect("On iOS, `EventLoop` must be created on the main thread");
|
2022-09-02 15:48:02 +02:00
|
|
|
|
2019-05-25 18:10:41 -07:00
|
|
|
static mut SINGLETON_INIT: bool = false;
|
|
|
|
|
unsafe {
|
2019-06-21 11:33:15 -04:00
|
|
|
assert!(
|
|
|
|
|
!SINGLETON_INIT,
|
|
|
|
|
"Only one `EventLoop` is supported on iOS. \
|
|
|
|
|
`EventLoopProxy` might be helpful"
|
|
|
|
|
);
|
2019-05-25 18:10:41 -07:00
|
|
|
SINGLETON_INIT = true;
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-27 17:04:39 +02:00
|
|
|
let (sender, receiver) = mpsc::channel();
|
2019-05-25 18:10:41 -07:00
|
|
|
|
|
|
|
|
// this line sets up the main run loop before `UIApplicationMain`
|
|
|
|
|
setup_control_flow_observers();
|
|
|
|
|
|
2023-08-13 23:20:09 +04:00
|
|
|
Ok(EventLoop {
|
2023-08-27 17:04:39 +02:00
|
|
|
mtm,
|
|
|
|
|
sender,
|
|
|
|
|
receiver,
|
2024-01-31 17:29:59 +04:00
|
|
|
window_target: RootActiveEventLoop {
|
|
|
|
|
p: ActiveEventLoop { mtm },
|
2019-05-25 18:10:41 -07:00
|
|
|
_marker: PhantomData,
|
2019-06-21 11:33:15 -04:00
|
|
|
},
|
2023-08-13 23:20:09 +04:00
|
|
|
})
|
2019-05-25 18:10:41 -07:00
|
|
|
}
|
|
|
|
|
|
2024-01-25 05:26:50 +01:00
|
|
|
pub fn run<F>(self, handler: F) -> !
|
2019-05-25 18:10:41 -07:00
|
|
|
where
|
2024-01-31 17:29:59 +04:00
|
|
|
F: FnMut(Event<T>, &RootActiveEventLoop),
|
2019-05-25 18:10:41 -07:00
|
|
|
{
|
2024-01-25 05:26:50 +01:00
|
|
|
let application = UIApplication::shared(self.mtm);
|
|
|
|
|
assert!(
|
|
|
|
|
application.is_none(),
|
|
|
|
|
"\
|
2022-12-28 18:36:32 +01:00
|
|
|
`EventLoop` cannot be `run` after a call to `UIApplicationMain` on iOS\n\
|
|
|
|
|
Note: `EventLoop::run` calls `UIApplicationMain` on iOS",
|
2024-01-25 05:26:50 +01:00
|
|
|
);
|
2023-08-06 01:56:56 +04:00
|
|
|
|
2024-01-25 05:26:50 +01:00
|
|
|
let handler = map_user_event(handler, self.receiver);
|
2023-08-06 01:56:56 +04:00
|
|
|
|
2024-01-25 05:26:50 +01:00
|
|
|
let handler = unsafe {
|
|
|
|
|
std::mem::transmute::<
|
2024-01-31 17:29:59 +04:00
|
|
|
Box<dyn FnMut(Event<HandlePendingUserEvents>, &RootActiveEventLoop)>,
|
|
|
|
|
Box<dyn FnMut(Event<HandlePendingUserEvents>, &RootActiveEventLoop)>,
|
2024-01-25 05:26:50 +01:00
|
|
|
>(Box::new(handler))
|
|
|
|
|
};
|
2023-08-06 01:56:56 +04:00
|
|
|
|
2024-01-25 05:26:50 +01:00
|
|
|
let handler = EventLoopHandler {
|
|
|
|
|
handler,
|
|
|
|
|
event_loop: self.window_target,
|
|
|
|
|
};
|
2019-05-25 18:10:41 -07:00
|
|
|
|
2024-01-25 05:26:50 +01:00
|
|
|
app_state::will_launch(self.mtm, handler);
|
2022-09-08 20:30:34 +02:00
|
|
|
|
2024-01-25 05:26:50 +01:00
|
|
|
// Ensure application delegate is initialized
|
2024-02-22 22:28:49 +01:00
|
|
|
let _ = AppDelegate::class();
|
2024-01-25 05:26:50 +01:00
|
|
|
|
|
|
|
|
unsafe {
|
2019-06-21 11:33:15 -04:00
|
|
|
UIApplicationMain(
|
|
|
|
|
0,
|
|
|
|
|
ptr::null(),
|
2023-01-23 00:01:45 +01:00
|
|
|
None,
|
2024-02-22 22:28:49 +01:00
|
|
|
Some(&NSString::from_str(AppDelegate::NAME)),
|
2024-01-25 05:26:50 +01:00
|
|
|
)
|
|
|
|
|
};
|
|
|
|
|
unreachable!()
|
2019-05-25 18:10:41 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn create_proxy(&self) -> EventLoopProxy<T> {
|
2023-08-27 17:04:39 +02:00
|
|
|
EventLoopProxy::new(self.sender.clone())
|
2019-05-25 18:10:41 -07:00
|
|
|
}
|
|
|
|
|
|
2024-01-31 17:29:59 +04:00
|
|
|
pub fn window_target(&self) -> &RootActiveEventLoop {
|
2019-05-25 18:10:41 -07:00
|
|
|
&self.window_target
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// EventLoopExtIOS
|
|
|
|
|
impl<T: 'static> EventLoop<T> {
|
2019-05-29 21:29:54 -04:00
|
|
|
pub fn idiom(&self) -> Idiom {
|
2024-02-27 21:07:52 +01:00
|
|
|
match UIDevice::current(self.mtm).userInterfaceIdiom() {
|
|
|
|
|
UIUserInterfaceIdiom::Unspecified => Idiom::Unspecified,
|
|
|
|
|
UIUserInterfaceIdiom::Phone => Idiom::Phone,
|
|
|
|
|
UIUserInterfaceIdiom::Pad => Idiom::Pad,
|
|
|
|
|
UIUserInterfaceIdiom::TV => Idiom::TV,
|
|
|
|
|
UIUserInterfaceIdiom::CarPlay => Idiom::CarPlay,
|
|
|
|
|
_ => Idiom::Unspecified,
|
|
|
|
|
}
|
2019-05-25 18:10:41 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub struct EventLoopProxy<T> {
|
|
|
|
|
sender: Sender<T>,
|
|
|
|
|
source: CFRunLoopSourceRef,
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-09 09:13:14 +09:00
|
|
|
unsafe impl<T: Send> Send for EventLoopProxy<T> {}
|
2024-02-01 14:27:44 +01:00
|
|
|
unsafe impl<T: Send> Sync for EventLoopProxy<T> {}
|
2019-05-25 18:10:41 -07:00
|
|
|
|
|
|
|
|
impl<T> Clone for EventLoopProxy<T> {
|
|
|
|
|
fn clone(&self) -> EventLoopProxy<T> {
|
|
|
|
|
EventLoopProxy::new(self.sender.clone())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<T> Drop for EventLoopProxy<T> {
|
|
|
|
|
fn drop(&mut self) {
|
|
|
|
|
unsafe {
|
|
|
|
|
CFRunLoopSourceInvalidate(self.source);
|
|
|
|
|
CFRelease(self.source as _);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<T> EventLoopProxy<T> {
|
|
|
|
|
fn new(sender: Sender<T>) -> EventLoopProxy<T> {
|
|
|
|
|
unsafe {
|
2019-07-13 01:05:07 +02:00
|
|
|
// just wake up the eventloop
|
2022-09-08 21:03:25 +02:00
|
|
|
extern "C" fn event_loop_proxy_handler(_: *const c_void) {}
|
2019-05-25 18:10:41 -07:00
|
|
|
|
|
|
|
|
// adding a Source to the main CFRunLoop lets us wake it up and
|
|
|
|
|
// process user events through the normal OS EventLoop mechanisms.
|
|
|
|
|
let rl = CFRunLoopGetMain();
|
2022-09-08 21:03:25 +02:00
|
|
|
let mut context = CFRunLoopSourceContext {
|
|
|
|
|
version: 0,
|
|
|
|
|
info: ptr::null_mut(),
|
|
|
|
|
retain: None,
|
|
|
|
|
release: None,
|
|
|
|
|
copyDescription: None,
|
|
|
|
|
equal: None,
|
|
|
|
|
hash: None,
|
|
|
|
|
schedule: None,
|
|
|
|
|
cancel: None,
|
|
|
|
|
perform: event_loop_proxy_handler,
|
|
|
|
|
};
|
2019-06-21 11:33:15 -04:00
|
|
|
let source =
|
|
|
|
|
CFRunLoopSourceCreate(ptr::null_mut(), CFIndex::max_value() - 1, &mut context);
|
2019-05-25 18:10:41 -07:00
|
|
|
CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes);
|
|
|
|
|
CFRunLoopWakeUp(rl);
|
|
|
|
|
|
2019-06-21 11:33:15 -04:00
|
|
|
EventLoopProxy { sender, source }
|
2019-05-25 18:10:41 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-07 18:22:03 +01:00
|
|
|
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
|
|
|
|
|
self.sender
|
|
|
|
|
.send(event)
|
|
|
|
|
.map_err(|::std::sync::mpsc::SendError(x)| EventLoopClosed(x))?;
|
2019-05-25 18:10:41 -07:00
|
|
|
unsafe {
|
|
|
|
|
// let the main thread know there's a new event
|
|
|
|
|
CFRunLoopSourceSignal(self.source);
|
|
|
|
|
let rl = CFRunLoopGetMain();
|
|
|
|
|
CFRunLoopWakeUp(rl);
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn setup_control_flow_observers() {
|
|
|
|
|
unsafe {
|
|
|
|
|
// begin is queued with the highest priority to ensure it is processed before other observers
|
2019-06-21 11:33:15 -04:00
|
|
|
extern "C" fn control_flow_begin_handler(
|
2019-05-25 18:10:41 -07:00
|
|
|
_: CFRunLoopObserverRef,
|
|
|
|
|
activity: CFRunLoopActivity,
|
|
|
|
|
_: *mut c_void,
|
|
|
|
|
) {
|
2023-08-27 17:04:39 +02:00
|
|
|
let mtm = MainThreadMarker::new().unwrap();
|
|
|
|
|
#[allow(non_upper_case_globals)]
|
|
|
|
|
match activity {
|
|
|
|
|
kCFRunLoopAfterWaiting => app_state::handle_wakeup_transition(mtm),
|
|
|
|
|
_ => unreachable!(),
|
2019-05-25 18:10:41 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-04 14:23:11 -07:00
|
|
|
// Core Animation registers its `CFRunLoopObserver` that performs drawing operations in
|
|
|
|
|
// `CA::Transaction::ensure_implicit` with a priority of `0x1e8480`. We set the main_end
|
2023-07-28 17:37:56 +01:00
|
|
|
// priority to be 0, in order to send AboutToWait before RedrawRequested. This value was
|
2019-09-04 14:23:11 -07:00
|
|
|
// chosen conservatively to guard against apple using different priorities for their redraw
|
|
|
|
|
// observers in different OS's or on different devices. If it so happens that it's too
|
2023-07-28 17:37:56 +01:00
|
|
|
// conservative, the main symptom would be non-redraw events coming in after `AboutToWait`.
|
2019-09-04 14:23:11 -07:00
|
|
|
//
|
|
|
|
|
// The value of `0x1e8480` was determined by inspecting stack traces and the associated
|
|
|
|
|
// registers for every `CFRunLoopAddObserver` call on an iPad Air 2 running iOS 11.4.
|
|
|
|
|
//
|
|
|
|
|
// Also tested to be `0x1e8480` on iPhone 8, iOS 13 beta 4.
|
|
|
|
|
extern "C" fn control_flow_main_end_handler(
|
|
|
|
|
_: CFRunLoopObserverRef,
|
|
|
|
|
activity: CFRunLoopActivity,
|
|
|
|
|
_: *mut c_void,
|
|
|
|
|
) {
|
2023-08-27 17:04:39 +02:00
|
|
|
let mtm = MainThreadMarker::new().unwrap();
|
|
|
|
|
#[allow(non_upper_case_globals)]
|
|
|
|
|
match activity {
|
|
|
|
|
kCFRunLoopBeforeWaiting => app_state::handle_main_events_cleared(mtm),
|
2023-11-22 13:14:51 +01:00
|
|
|
kCFRunLoopExit => {} // may happen when running on macOS
|
2023-08-27 17:04:39 +02:00
|
|
|
_ => unreachable!(),
|
2019-09-04 14:23:11 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-25 18:10:41 -07:00
|
|
|
// end is queued with the lowest priority to ensure it is processed after other observers
|
2019-06-21 11:33:15 -04:00
|
|
|
extern "C" fn control_flow_end_handler(
|
2019-05-25 18:10:41 -07:00
|
|
|
_: CFRunLoopObserverRef,
|
|
|
|
|
activity: CFRunLoopActivity,
|
|
|
|
|
_: *mut c_void,
|
|
|
|
|
) {
|
2023-08-27 17:04:39 +02:00
|
|
|
let mtm = MainThreadMarker::new().unwrap();
|
|
|
|
|
#[allow(non_upper_case_globals)]
|
|
|
|
|
match activity {
|
|
|
|
|
kCFRunLoopBeforeWaiting => app_state::handle_events_cleared(mtm),
|
2023-11-22 13:14:51 +01:00
|
|
|
kCFRunLoopExit => {} // may happen when running on macOS
|
2023-08-27 17:04:39 +02:00
|
|
|
_ => unreachable!(),
|
2019-05-25 18:10:41 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let main_loop = CFRunLoopGetMain();
|
2019-09-04 14:23:11 -07:00
|
|
|
|
2019-05-25 18:10:41 -07:00
|
|
|
let begin_observer = CFRunLoopObserverCreate(
|
|
|
|
|
ptr::null_mut(),
|
2022-09-08 21:56:53 +02:00
|
|
|
kCFRunLoopAfterWaiting,
|
2019-05-25 18:10:41 -07:00
|
|
|
1, // repeat = true
|
|
|
|
|
CFIndex::min_value(),
|
|
|
|
|
control_flow_begin_handler,
|
|
|
|
|
ptr::null_mut(),
|
|
|
|
|
);
|
|
|
|
|
CFRunLoopAddObserver(main_loop, begin_observer, kCFRunLoopDefaultMode);
|
2019-09-04 14:23:11 -07:00
|
|
|
|
|
|
|
|
let main_end_observer = CFRunLoopObserverCreate(
|
|
|
|
|
ptr::null_mut(),
|
|
|
|
|
kCFRunLoopExit | kCFRunLoopBeforeWaiting,
|
|
|
|
|
1, // repeat = true
|
|
|
|
|
0, // see comment on `control_flow_main_end_handler`
|
|
|
|
|
control_flow_main_end_handler,
|
|
|
|
|
ptr::null_mut(),
|
|
|
|
|
);
|
|
|
|
|
CFRunLoopAddObserver(main_loop, main_end_observer, kCFRunLoopDefaultMode);
|
|
|
|
|
|
2019-05-25 18:10:41 -07:00
|
|
|
let end_observer = CFRunLoopObserverCreate(
|
|
|
|
|
ptr::null_mut(),
|
|
|
|
|
kCFRunLoopExit | kCFRunLoopBeforeWaiting,
|
|
|
|
|
1, // repeat = true
|
|
|
|
|
CFIndex::max_value(),
|
|
|
|
|
control_flow_end_handler,
|
|
|
|
|
ptr::null_mut(),
|
|
|
|
|
);
|
|
|
|
|
CFRunLoopAddObserver(main_loop, end_observer, kCFRunLoopDefaultMode);
|
|
|
|
|
}
|
|
|
|
|
}
|