diff --git a/winit-uikit/Cargo.toml b/winit-uikit/Cargo.toml index 3784bc3237..6927e4dc49 100644 --- a/winit-uikit/Cargo.toml +++ b/winit-uikit/Cargo.toml @@ -68,6 +68,8 @@ objc2-ui-kit = { workspace = true, features = [ "UIView", "UIViewController", "UIWindow", + "UIScene", + "UIWindowScene" ] } winit-common = { workspace = true, features = ["core-foundation", "event-handler"] } diff --git a/winit-uikit/src/app_state.rs b/winit-uikit/src/app_state.rs index 7876e34fe1..6f0f71e07a 100644 --- a/winit-uikit/src/app_state.rs +++ b/winit-uikit/src/app_state.rs @@ -451,10 +451,14 @@ fn handle_hidpi_proxy(mtm: MainThreadMarker, event: ScaleFactorChanged) { let ScaleFactorChanged { suggested_size, scale_factor, window } = event; let new_surface_size = Arc::new(Mutex::new(suggested_size)); get_handler(mtm).handle(|app| { - app.window_event(&ActiveEventLoop { mtm }, window.id(), WindowEvent::ScaleFactorChanged { - scale_factor, - surface_size_writer: SurfaceSizeWriter::new(Arc::downgrade(&new_surface_size)), - }); + app.window_event( + &ActiveEventLoop { mtm }, + window.id(), + WindowEvent::ScaleFactorChanged { + scale_factor, + surface_size_writer: SurfaceSizeWriter::new(Arc::downgrade(&new_surface_size)), + }, + ); }); let (view, screen_frame) = get_view_and_screen_frame(&window); let physical_size = *new_surface_size.lock().unwrap(); diff --git a/winit-uikit/src/event_loop.rs b/winit-uikit/src/event_loop.rs index c5897edc6b..2f0c5d141d 100644 --- a/winit-uikit/src/event_loop.rs +++ b/winit-uikit/src/event_loop.rs @@ -139,13 +139,12 @@ pub struct EventLoop { } #[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub struct PlatformSpecificEventLoopAttributes {} +pub struct PlatformSpecificEventLoopAttributes { + pub mtm: Option, +} impl EventLoop { - pub fn new(_: &PlatformSpecificEventLoopAttributes) -> Result { - let mtm = MainThreadMarker::new() - .expect("On iOS, `EventLoop` must be created on the main thread"); - + pub fn new_with_mtm(mtm: MainThreadMarker) -> Result { if !AppState::setup_global(mtm) { // Required, AppState is global state, and event loop can only be run once. return Err(EventLoopError::RecreationAttempt); @@ -238,17 +237,36 @@ impl EventLoop { }) } + pub fn new( + attributes: &PlatformSpecificEventLoopAttributes, + ) -> Result { + // if the user provided a MainThreadMarker, use it; otherwise, create one + let mtm = match attributes.mtm { + Some(mtm) => mtm, + None => MainThreadMarker::new() + .expect("On iOS, `EventLoop` must be created on the main thread"), + }; + + EventLoop::new_with_mtm(mtm) + } + // Require `'static` for correctness, we won't be able to `Drop` the user's state otherwise. pub fn run_app_never_return(self, app: A) -> ! { let application: Option> = unsafe { msg_send![UIApplication::class(), sharedApplication] }; - assert!( - application.is_none(), - "\ - `EventLoop` cannot be `run` after a call to `UIApplicationMain` on iOS\nNote: \ - `EventLoop::run_app` calls `UIApplicationMain` on iOS", - ); - + if let Some(_) = application { + // launch without calling `UIApplicationMain`, then park the thread forever + // it would probably be better to turn this into a `register_app`-style function + // and just return immediately + app_state::launch(self.mtm, app, || { + loop { + std::thread::park(); + } + }); + + // manually call did_finish_launching(mtm); + app_state::did_finish_launching(self.mtm); + } // We intentionally override neither the application nor the delegate, // to allow the user to do so themselves! // diff --git a/winit-uikit/src/lib.rs b/winit-uikit/src/lib.rs index 7ae806f2e3..ee99f951f2 100644 --- a/winit-uikit/src/lib.rs +++ b/winit-uikit/src/lib.rs @@ -110,6 +110,8 @@ mod window; use std::os::raw::c_void; +pub use objc2; + #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use winit_core::monitor::{MonitorHandle, VideoMode}; @@ -294,6 +296,12 @@ impl WindowExtIOS for dyn Window + '_ { } } +pub trait EventLoopBuilderExtIOS { + /// Sets the main thread marker to be used by the event loop. + fn with_main_thread_marker(&mut self, main_thread_marker: objc2::MainThreadMarker) + -> &mut Self; +} + /// Ios specific window attributes. #[derive(Clone, Debug, Default, PartialEq)] pub struct WindowAttributesIos { diff --git a/winit-uikit/src/window.rs b/winit-uikit/src/window.rs index 788babcb71..3eab4759c4 100644 --- a/winit-uikit/src/window.rs +++ b/winit-uikit/src/window.rs @@ -47,20 +47,20 @@ define_class!( #[unsafe(method(becomeKeyWindow))] fn become_key_window(&self) { let mtm = MainThreadMarker::new().unwrap(); - app_state::handle_nonuser_event(mtm, EventWrapper::Window { - window_id: self.id(), - event: WindowEvent::Focused(true), - }); + app_state::handle_nonuser_event( + mtm, + EventWrapper::Window { window_id: self.id(), event: WindowEvent::Focused(true) }, + ); let _: () = unsafe { msg_send![super(self), becomeKeyWindow] }; } #[unsafe(method(resignKeyWindow))] fn resign_key_window(&self) { let mtm = MainThreadMarker::new().unwrap(); - app_state::handle_nonuser_event(mtm, EventWrapper::Window { - window_id: self.id(), - event: WindowEvent::Focused(false), - }); + app_state::handle_nonuser_event( + mtm, + EventWrapper::Window { window_id: self.id(), event: WindowEvent::Focused(false) }, + ); let _: () = unsafe { msg_send![super(self), resignKeyWindow] }; } } @@ -490,6 +490,22 @@ impl Window { ) -> Result { let mtm = event_loop.mtm; + // check if there is already one or more scenes using .connectedScenes + let mut window_scene: Option> = None; + + unsafe { + let app = UIApplication::sharedApplication(mtm); + let connected_scenes = app.connectedScenes(); + for scene in connected_scenes.iter() { + // check if UIWindowScene + if scene.isKindOfClass(class!(UIWindowScene)) { + window_scene = Some(Retained::cast_unchecked::( + scene.clone(), + )); + } + } + }; + if window_attributes.min_surface_size.is_some() { warn!("`WindowAttributes::min_surface_size` is ignored on iOS"); } @@ -539,6 +555,10 @@ impl Window { let view_controller = WinitViewController::new(mtm, &ios_attributes, &view); let window = WinitUIWindow::new(mtm, &window_attributes, frame, &view_controller); window.makeKeyAndVisible(); + unsafe { + window.setWindowLevel(10000.0); + window.setWindowScene(window_scene.as_deref()); + } let inner = Inner { window, diff --git a/winit/src/event_loop.rs b/winit/src/event_loop.rs index 6882ebf163..b578dfc525 100644 --- a/winit/src/event_loop.rs +++ b/winit/src/event_loop.rs @@ -383,6 +383,14 @@ impl winit_android::EventLoopBuilderExtAndroid for EventLoopBuilder { } } +#[cfg(ios_platform)] +impl winit_uikit::EventLoopBuilderExtIOS for EventLoopBuilder { + fn with_main_thread_marker(&mut self, mtm: winit_uikit::objc2::MainThreadMarker) -> &mut Self { + self.platform_specific.mtm = Some(mtm); + self + } +} + #[cfg(macos_platform)] impl winit_appkit::EventLoopBuilderExtMacOS for EventLoopBuilder { #[inline]