diff --git a/src/platform_impl/ios/app_state.rs b/src/platform_impl/ios/app_state.rs index 1847783355..de59db9306 100644 --- a/src/platform_impl/ios/app_state.rs +++ b/src/platform_impl/ios/app_state.rs @@ -16,7 +16,9 @@ use core_foundation::runloop::{ kCFRunLoopCommonModes, CFRunLoopAddTimer, CFRunLoopGetMain, CFRunLoopRef, CFRunLoopTimerCreate, CFRunLoopTimerInvalidate, CFRunLoopTimerRef, CFRunLoopTimerSetNextFireDate, }; -use icrate::Foundation::{CGRect, CGSize, NSInteger, NSOperatingSystemVersion, NSProcessInfo}; +use icrate::Foundation::{ + CGRect, CGSize, MainThreadMarker, NSInteger, NSOperatingSystemVersion, NSProcessInfo, +}; use objc2::rc::Id; use objc2::runtime::AnyObject; use objc2::{msg_send, sel}; @@ -121,24 +123,18 @@ struct AppState { } impl AppState { - // requires main thread - unsafe fn get_mut() -> RefMut<'static, AppState> { + fn get_mut(_mtm: MainThreadMarker) -> 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> = RefCell::new(None); - if cfg!(debug_assertions) { - assert_main_thread!( - "bug in winit: `AppState::get_mut()` can only be called on the main thread" - ); - } - let mut guard = APP_STATE.borrow_mut(); + let mut guard = unsafe { APP_STATE.borrow_mut() }; if guard.is_none() { #[inline(never)] #[cold] - unsafe fn init_guard(guard: &mut RefMut<'static, Option>) { - let waker = EventLoopWaker::new(CFRunLoopGetMain()); + fn init_guard(guard: &mut RefMut<'static, Option>) { + let waker = EventLoopWaker::new(unsafe { CFRunLoopGetMain() }); **guard = Some(AppState { app_state: Some(AppStateImpl::NotLaunched { queued_windows: Vec::new(), @@ -149,7 +145,7 @@ impl AppState { waker, }); } - init_guard(&mut guard) + init_guard(&mut guard); } RefMut::map(guard, |state| state.as_mut().unwrap()) } @@ -458,10 +454,8 @@ impl AppState { } } -// requires main thread and window is a UIWindow -// retains window -pub(crate) unsafe fn set_key_window(window: &Id) { - let mut this = AppState::get_mut(); +pub(crate) fn set_key_window(mtm: MainThreadMarker, window: &Id) { + let mut this = AppState::get_mut(mtm); match this.state_mut() { &mut AppStateImpl::NotLaunched { ref mut queued_windows, @@ -481,10 +475,8 @@ pub(crate) unsafe fn set_key_window(window: &Id) { window.makeKeyAndVisible(); } -// requires main thread and window is a UIWindow -// retains window -pub(crate) unsafe fn queue_gl_or_metal_redraw(window: Id) { - let mut this = AppState::get_mut(); +pub(crate) fn queue_gl_or_metal_redraw(mtm: MainThreadMarker, window: Id) { + let mut this = AppState::get_mut(mtm); match this.state_mut() { &mut AppStateImpl::NotLaunched { ref mut queued_gpu_redraws, @@ -513,14 +505,12 @@ pub(crate) unsafe fn queue_gl_or_metal_redraw(window: Id) { } } -// requires main thread -pub unsafe fn will_launch(queued_event_handler: Box) { - AppState::get_mut().will_launch_transition(queued_event_handler) +pub fn will_launch(mtm: MainThreadMarker, queued_event_handler: Box) { + AppState::get_mut(mtm).will_launch_transition(queued_event_handler) } -// requires main thread -pub unsafe fn did_finish_launching() { - let mut this = AppState::get_mut(); +pub fn did_finish_launching(mtm: MainThreadMarker) { + let mut this = AppState::get_mut(mtm); let windows = match this.state_mut() { AppStateImpl::Launching { queued_windows, .. } => mem::take(queued_windows), s => bug!("unexpected state {:?}", s), @@ -548,7 +538,7 @@ pub unsafe fn did_finish_launching() { // completed. This may result in incorrect visual appearance. // ``` let screen = window.screen(); - let _: () = msg_send![&window, setScreen: ptr::null::()]; + let _: () = unsafe { msg_send![&window, setScreen: ptr::null::()] }; window.setScreen(&screen); let controller = window.rootViewController(); @@ -558,13 +548,13 @@ pub unsafe fn did_finish_launching() { window.makeKeyAndVisible(); } - let (windows, events) = AppState::get_mut().did_finish_launching_transition(); + let (windows, events) = AppState::get_mut(mtm).did_finish_launching_transition(); let events = std::iter::once(EventWrapper::StaticEvent(Event::NewEvents( StartCause::Init, ))) .chain(events); - handle_nonuser_events(events); + handle_nonuser_events(mtm, 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 @@ -573,27 +563,27 @@ pub unsafe fn did_finish_launching() { } } -// requires main thread // AppState::did_finish_launching handles the special transition `Init` -pub unsafe fn handle_wakeup_transition() { - let mut this = AppState::get_mut(); +pub fn handle_wakeup_transition(mtm: MainThreadMarker) { + let mut this = AppState::get_mut(mtm); let wakeup_event = match this.wakeup_transition() { None => return, Some(wakeup_event) => wakeup_event, }; drop(this); - handle_nonuser_event(wakeup_event) + handle_nonuser_event(mtm, wakeup_event) } -// requires main thread -pub(crate) unsafe fn handle_nonuser_event(event: EventWrapper) { - handle_nonuser_events(std::iter::once(event)) +pub(crate) fn handle_nonuser_event(mtm: MainThreadMarker, event: EventWrapper) { + handle_nonuser_events(mtm, std::iter::once(event)) } -// requires main thread -pub(crate) unsafe fn handle_nonuser_events>(events: I) { - let mut this = AppState::get_mut(); +pub(crate) fn handle_nonuser_events>( + mtm: MainThreadMarker, + events: I, +) { + let mut this = AppState::get_mut(mtm); let (mut event_handler, active_control_flow, processing_redraws) = match this.try_user_callback_transition() { UserCallbackTransitionResult::ReentrancyPrevented { queued_events } => { @@ -629,7 +619,7 @@ pub(crate) unsafe fn handle_nonuser_events> } loop { - let mut this = AppState::get_mut(); + let mut this = AppState::get_mut(mtm); let queued_events = match this.state_mut() { &mut AppStateImpl::InUserCallback { ref mut queued_events, @@ -687,9 +677,8 @@ pub(crate) unsafe fn handle_nonuser_events> } } -// requires main thread -unsafe fn handle_user_events() { - let mut this = AppState::get_mut(); +fn handle_user_events(mtm: MainThreadMarker) { + let mut this = AppState::get_mut(mtm); let mut control_flow = this.control_flow; let (mut event_handler, active_control_flow, processing_redraws) = match this.try_user_callback_transition() { @@ -710,7 +699,7 @@ unsafe fn handle_user_events() { event_handler.handle_user_events(&mut control_flow); loop { - let mut this = AppState::get_mut(); + let mut this = AppState::get_mut(mtm); let queued_events = match this.state_mut() { &mut AppStateImpl::InUserCallback { ref mut queued_events, @@ -750,9 +739,8 @@ unsafe fn handle_user_events() { } } -// requires main thread -pub unsafe fn handle_main_events_cleared() { - let mut this = AppState::get_mut(); +pub fn handle_main_events_cleared(mtm: MainThreadMarker) { + let mut this = AppState::get_mut(mtm); if !this.has_launched() { return; } @@ -762,9 +750,9 @@ pub unsafe fn handle_main_events_cleared() { }; drop(this); - handle_user_events(); + handle_user_events(mtm); - let mut this = AppState::get_mut(); + let mut this = AppState::get_mut(mtm); let redraw_events: Vec = this .main_events_cleared_transition() .into_iter() @@ -772,18 +760,16 @@ pub unsafe fn handle_main_events_cleared() { .collect(); drop(this); - handle_nonuser_events(redraw_events); - handle_nonuser_event(EventWrapper::StaticEvent(Event::AboutToWait)); + handle_nonuser_events(mtm, redraw_events); + handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::AboutToWait)); } -// requires main thread -pub unsafe fn handle_events_cleared() { - AppState::get_mut().events_cleared_transition(); +pub fn handle_events_cleared(mtm: MainThreadMarker) { + AppState::get_mut(mtm).events_cleared_transition(); } -// requires main thread -pub unsafe fn terminated() { - let mut this = AppState::get_mut(); +pub fn terminated(mtm: MainThreadMarker) { + let mut this = AppState::get_mut(mtm); let mut event_handler = this.terminated_transition(); let mut control_flow = this.control_flow; drop(this); diff --git a/src/platform_impl/ios/event_loop.rs b/src/platform_impl/ios/event_loop.rs index 2104a2f129..e3ad63b5f7 100644 --- a/src/platform_impl/ios/event_loop.rs +++ b/src/platform_impl/ios/event_loop.rs @@ -32,18 +32,17 @@ use super::{app_state, monitor, view, MonitorHandle}; #[derive(Debug)] pub struct EventLoopWindowTarget { + pub(super) mtm: MainThreadMarker, p: PhantomData, } impl EventLoopWindowTarget { pub fn available_monitors(&self) -> VecDeque { - monitor::uiscreens(MainThreadMarker::new().unwrap()) + monitor::uiscreens(self.mtm) } pub fn primary_monitor(&self) -> Option { - Some(MonitorHandle::new(UIScreen::main( - MainThreadMarker::new().unwrap(), - ))) + Some(MonitorHandle::new(UIScreen::main(self.mtm))) } pub fn raw_display_handle(&self) -> RawDisplayHandle { @@ -52,6 +51,7 @@ impl EventLoopWindowTarget { } pub struct EventLoop { + mtm: MainThreadMarker, sender: Sender, receiver: Receiver, window_target: RootEventLoopWindowTarget, @@ -64,7 +64,8 @@ impl EventLoop { pub(crate) fn new( _: &PlatformSpecificEventLoopAttributes, ) -> Result, EventLoopError> { - assert_main_thread!("`EventLoop` can only be created on the main thread on iOS"); + let mtm = MainThreadMarker::new() + .expect("On iOS, `EventLoop` must be created on the main thread"); static mut SINGLETON_INIT: bool = false; unsafe { @@ -82,10 +83,14 @@ impl EventLoop { setup_control_flow_observers(); Ok(EventLoop { + mtm, sender, receiver, window_target: RootEventLoopWindowTarget { - p: EventLoopWindowTarget { p: PhantomData }, + p: EventLoopWindowTarget { + mtm, + p: PhantomData, + }, _marker: PhantomData, }, }) @@ -96,7 +101,7 @@ impl EventLoop { F: FnMut(Event, &RootEventLoopWindowTarget, &mut ControlFlow), { unsafe { - let application = UIApplication::shared(MainThreadMarker::new().unwrap()); + let application = UIApplication::shared(self.mtm); assert!( application.is_none(), "\ @@ -115,7 +120,7 @@ impl EventLoop { event_loop: self.window_target, }; - app_state::will_launch(Box::new(handler)); + app_state::will_launch(self.mtm, Box::new(handler)); // Ensure application delegate is initialized view::WinitApplicationDelegate::class(); @@ -142,9 +147,7 @@ impl EventLoop { // EventLoopExtIOS impl EventLoop { pub fn idiom(&self) -> Idiom { - UIDevice::current(MainThreadMarker::new().unwrap()) - .userInterfaceIdiom() - .into() + UIDevice::current(self.mtm).userInterfaceIdiom().into() } } @@ -222,12 +225,11 @@ fn setup_control_flow_observers() { activity: CFRunLoopActivity, _: *mut c_void, ) { - unsafe { - #[allow(non_upper_case_globals)] - match activity { - kCFRunLoopAfterWaiting => app_state::handle_wakeup_transition(), - _ => unreachable!(), - } + let mtm = MainThreadMarker::new().unwrap(); + #[allow(non_upper_case_globals)] + match activity { + kCFRunLoopAfterWaiting => app_state::handle_wakeup_transition(mtm), + _ => unreachable!(), } } @@ -247,13 +249,12 @@ fn setup_control_flow_observers() { activity: CFRunLoopActivity, _: *mut c_void, ) { - unsafe { - #[allow(non_upper_case_globals)] - match activity { - kCFRunLoopBeforeWaiting => app_state::handle_main_events_cleared(), - kCFRunLoopExit => unimplemented!(), // not expected to ever happen - _ => unreachable!(), - } + let mtm = MainThreadMarker::new().unwrap(); + #[allow(non_upper_case_globals)] + match activity { + kCFRunLoopBeforeWaiting => app_state::handle_main_events_cleared(mtm), + kCFRunLoopExit => unimplemented!(), // not expected to ever happen + _ => unreachable!(), } } @@ -263,13 +264,12 @@ fn setup_control_flow_observers() { activity: CFRunLoopActivity, _: *mut c_void, ) { - unsafe { - #[allow(non_upper_case_globals)] - match activity { - kCFRunLoopBeforeWaiting => app_state::handle_events_cleared(), - kCFRunLoopExit => unimplemented!(), // not expected to ever happen - _ => unreachable!(), - } + let mtm = MainThreadMarker::new().unwrap(); + #[allow(non_upper_case_globals)] + match activity { + kCFRunLoopBeforeWaiting => app_state::handle_events_cleared(mtm), + kCFRunLoopExit => unimplemented!(), // not expected to ever happen + _ => unreachable!(), } } diff --git a/src/platform_impl/ios/view.rs b/src/platform_impl/ios/view.rs index 2e04e4c1cc..6221340c86 100644 --- a/src/platform_impl/ios/view.rs +++ b/src/platform_impl/ios/view.rs @@ -40,17 +40,18 @@ declare_class!( unsafe impl WinitView { #[method(drawRect:)] fn draw_rect(&self, rect: CGRect) { + let mtm = MainThreadMarker::new().unwrap(); let window = self.window().unwrap(); - unsafe { - app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::RedrawRequested( - RootWindowId(window.id()), - ))); - } + app_state::handle_nonuser_event( + mtm, + EventWrapper::StaticEvent(Event::RedrawRequested(RootWindowId(window.id()))), + ); let _: () = unsafe { msg_send![super(self), drawRect: rect] }; } #[method(layoutSubviews)] fn layout_subviews(&self) { + let mtm = MainThreadMarker::new().unwrap(); let _: () = unsafe { msg_send![super(self), layoutSubviews] }; let window = self.window().unwrap(); @@ -73,16 +74,18 @@ declare_class!( self.setFrame(window_bounds); } - unsafe { - app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::WindowEvent { + app_state::handle_nonuser_event( + mtm, + EventWrapper::StaticEvent(Event::WindowEvent { window_id: RootWindowId(window.id()), event: WindowEvent::Resized(size), - })); - } + }), + ); } #[method(setContentScaleFactor:)] fn set_content_scale_factor(&self, untrusted_scale_factor: CGFloat) { + let mtm = MainThreadMarker::new().unwrap(); let _: () = unsafe { msg_send![super(self), setContentScaleFactor: untrusted_scale_factor] }; @@ -114,23 +117,22 @@ declare_class!( height: screen_frame.size.height as f64, }; let window_id = RootWindowId(window.id()); - unsafe { - app_state::handle_nonuser_events( - std::iter::once(EventWrapper::ScaleFactorChanged( - app_state::ScaleFactorChanged { - window, - scale_factor, - suggested_size: size.to_physical(scale_factor), - }, - )) - .chain(std::iter::once(EventWrapper::StaticEvent( - Event::WindowEvent { - window_id, - event: WindowEvent::Resized(size.to_physical(scale_factor)), - }, - ))), - ); - } + app_state::handle_nonuser_events( + mtm, + std::iter::once(EventWrapper::ScaleFactorChanged( + app_state::ScaleFactorChanged { + window, + scale_factor, + suggested_size: size.to_physical(scale_factor), + }, + )) + .chain(std::iter::once(EventWrapper::StaticEvent( + Event::WindowEvent { + window_id, + event: WindowEvent::Resized(size.to_physical(scale_factor)), + }, + ))), + ); } #[method(touchesBegan:withEvent:)] @@ -255,9 +257,8 @@ impl WinitView { }), })); } - unsafe { - app_state::handle_nonuser_events(touch_events); - } + let mtm = MainThreadMarker::new().unwrap(); + app_state::handle_nonuser_events(mtm, touch_events); } } @@ -431,23 +432,27 @@ declare_class!( unsafe impl WinitUIWindow { #[method(becomeKeyWindow)] fn become_key_window(&self) { - unsafe { - app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::WindowEvent { + let mtm = MainThreadMarker::new().unwrap(); + app_state::handle_nonuser_event( + mtm, + EventWrapper::StaticEvent(Event::WindowEvent { window_id: RootWindowId(self.id()), event: WindowEvent::Focused(true), - })); - } + }), + ); let _: () = unsafe { msg_send![super(self), becomeKeyWindow] }; } #[method(resignKeyWindow)] fn resign_key_window(&self) { - unsafe { - app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::WindowEvent { + let mtm = MainThreadMarker::new().unwrap(); + app_state::handle_nonuser_event( + mtm, + EventWrapper::StaticEvent(Event::WindowEvent { window_id: RootWindowId(self.id()), event: WindowEvent::Focused(false), - })); - } + }), + ); let _: () = unsafe { msg_send![super(self), resignKeyWindow] }; } } @@ -500,20 +505,20 @@ declare_class!( unsafe impl WinitApplicationDelegate { #[method(application:didFinishLaunchingWithOptions:)] fn did_finish_launching(&self, _application: &UIApplication, _: *mut NSObject) -> bool { - unsafe { - app_state::did_finish_launching(); - } + app_state::did_finish_launching(MainThreadMarker::new().unwrap()); true } #[method(applicationDidBecomeActive:)] fn did_become_active(&self, _application: &UIApplication) { - unsafe { app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::Resumed)) } + let mtm = MainThreadMarker::new().unwrap(); + app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::Resumed)) } #[method(applicationWillResignActive:)] fn will_resign_active(&self, _application: &UIApplication) { - unsafe { app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::Suspended)) } + let mtm = MainThreadMarker::new().unwrap(); + app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::Suspended)) } #[method(applicationWillEnterForeground:)] @@ -539,10 +544,9 @@ declare_class!( })); } } - unsafe { - app_state::handle_nonuser_events(events); - app_state::terminated(); - } + let mtm = MainThreadMarker::new().unwrap(); + app_state::handle_nonuser_events(mtm, events); + app_state::terminated(mtm); } } ); diff --git a/src/platform_impl/ios/window.rs b/src/platform_impl/ios/window.rs index a4187571e1..332637b0cc 100644 --- a/src/platform_impl/ios/window.rs +++ b/src/platform_impl/ios/window.rs @@ -52,20 +52,19 @@ impl Inner { } pub fn request_redraw(&self) { - unsafe { - if self.gl_or_metal_backed { - // `setNeedsDisplay` does nothing on UIViews which are directly backed by CAEAGLLayer or CAMetalLayer. - // Ordinarily the OS sets up a bunch of UIKit state before calling drawRect: on a UIView, but when using - // raw or gl/metal for drawing this work is completely avoided. - // - // The docs for `setNeedsDisplay` don't mention `CAMetalLayer`; however, this has been confirmed via - // testing. - // - // https://developer.apple.com/documentation/uikit/uiview/1622437-setneedsdisplay?language=objc - app_state::queue_gl_or_metal_redraw(self.window.clone()); - } else { - self.view.setNeedsDisplay(); - } + if self.gl_or_metal_backed { + let mtm = MainThreadMarker::new().unwrap(); + // `setNeedsDisplay` does nothing on UIViews which are directly backed by CAEAGLLayer or CAMetalLayer. + // Ordinarily the OS sets up a bunch of UIKit state before calling drawRect: on a UIView, but when using + // raw or gl/metal for drawing this work is completely avoided. + // + // The docs for `setNeedsDisplay` don't mention `CAMetalLayer`; however, this has been confirmed via + // testing. + // + // https://developer.apple.com/documentation/uikit/uiview/1622437-setneedsdisplay?language=objc + app_state::queue_gl_or_metal_redraw(mtm, self.window.clone()); + } else { + self.view.setNeedsDisplay(); } } @@ -364,11 +363,11 @@ pub struct Window { impl Window { pub(crate) fn new( - _event_loop: &EventLoopWindowTarget, + event_loop: &EventLoopWindowTarget, window_attributes: WindowAttributes, platform_attributes: PlatformSpecificWindowBuilderAttributes, ) -> Result { - let mtm = MainThreadMarker::new().unwrap(); + let mtm = event_loop.mtm; if window_attributes.min_inner_size.is_some() { warn!("`WindowAttributes::min_inner_size` is ignored on iOS"); @@ -423,7 +422,7 @@ impl Window { &view_controller, ); - unsafe { app_state::set_key_window(&window) }; + app_state::set_key_window(mtm, &window); // Like the Windows and macOS backends, we send a `ScaleFactorChanged` and `Resized` // event on window creation if the DPI factor != 1.0 @@ -439,23 +438,22 @@ impl Window { height: screen_frame.size.height as f64, }; let window_id = RootWindowId(window.id()); - unsafe { - app_state::handle_nonuser_events( - std::iter::once(EventWrapper::ScaleFactorChanged( - app_state::ScaleFactorChanged { - window: window.clone(), - scale_factor, - suggested_size: size.to_physical(scale_factor), - }, - )) - .chain(std::iter::once(EventWrapper::StaticEvent( - Event::WindowEvent { - window_id, - event: WindowEvent::Resized(size.to_physical(scale_factor)), - }, - ))), - ); - } + app_state::handle_nonuser_events( + mtm, + std::iter::once(EventWrapper::ScaleFactorChanged( + app_state::ScaleFactorChanged { + window: window.clone(), + scale_factor, + suggested_size: size.to_physical(scale_factor), + }, + )) + .chain(std::iter::once(EventWrapper::StaticEvent( + Event::WindowEvent { + window_id, + event: WindowEvent::Resized(size.to_physical(scale_factor)), + }, + ))), + ); } let inner = Inner {