use objc2::declare::ClassBuilder;
use objc2::rc::Retained;
use objc2::runtime::{AnyObject, AnyProtocol, NSObject, Sel};
use objc2::{ClassType, msg_send, msg_send_id, sel};
use raw_window_handle::{HasWindowHandle, RawWindowHandle};
use std::cell::RefCell;
use std::sync::Once;
thread_local! {
static SCENE: RefCell<Option<Retained<AnyObject>>> = const { RefCell::new(None) };
static WINDOW: RefCell<Option<Retained<AnyObject>>> = const { RefCell::new(None) };
}
extern "C" fn will_connect(
_this: *mut AnyObject,
_cmd: Sel,
scene: *mut AnyObject,
_: *mut AnyObject,
_: *mut AnyObject,
) {
let scene = unsafe { Retained::retain(scene) };
SCENE.with(|slot| *slot.borrow_mut() = scene);
join();
}
pub fn register() {
static ONCE: Once = Once::new();
ONCE.call_once(|| {
let Some(mut builder) = ClassBuilder::new("AuraSceneDelegate", NSObject::class()) else { return };
if let Some(protocol) = AnyProtocol::get("UIWindowSceneDelegate") {
builder.add_protocol(protocol);
}
unsafe {
builder.add_method(
sel!(scene:willConnectToSession:options:),
will_connect as extern "C" fn(*mut AnyObject, Sel, *mut AnyObject, *mut AnyObject, *mut AnyObject),
);
}
builder.register();
});
}
pub fn adopt(window: &winit::window::Window) {
let Ok(handle) = window.window_handle() else { return };
let RawWindowHandle::UiKit(handle) = handle.as_raw() else { return };
let view = unsafe { &*(handle.ui_view.as_ptr() as *const AnyObject) };
let ui_window: Option<Retained<AnyObject>> = unsafe { msg_send_id![view, window] };
WINDOW.with(|slot| *slot.borrow_mut() = ui_window);
join();
}
fn join() {
let (Some(scene), Some(window)) =
(SCENE.with(|slot| slot.borrow().clone()), WINDOW.with(|slot| slot.borrow().clone()))
else {
return;
};
let current: Option<Retained<AnyObject>> = unsafe { msg_send_id![&*window, windowScene] };
if current.is_none() {
unsafe {
let _: () = msg_send![&*window, setWindowScene: &*scene];
let _: () = msg_send![&*window, makeKeyAndVisible];
}
}
}