use std::ops::Deref; use std::rc::Rc; use objc2::rc::Id; use objc2::runtime::ProtocolObject; use objc2::{declare_class, msg_send_id, mutability, ClassType, DeclaredClass}; use objc2_app_kit::{NSApplication, NSApplicationActivationPolicy, NSApplicationDelegate}; use objc2_foundation::{MainThreadMarker, NSNotification, NSObject, NSObjectProtocol}; use super::Platform; mod proxy; pub use proxy::*; #[derive(Clone, Copy)] pub struct AppRef<'a>(pub &'a NSApplication); impl<'a> Deref for AppRef<'a> { type Target = NSApplication; fn deref(&self) -> &Self::Target { self.0 } } impl<'a> types::AppRef for AppRef<'a> { fn terminate(&self) { unsafe { self.0.terminate(None) }; } fn proxy(&self) -> Option { if let Some(delegate) = unsafe { self.0.delegate() } { let proxy: Id = unsafe { msg_send_id![&delegate, proxy] }; Some(AppProxy(proxy)) } else { None } } } pub struct AppHandle(pub Id); impl types::AppHandle for AppHandle { fn singleton() -> Option { let mtm = MainThreadMarker::new().unwrap(); let app = NSApplication::sharedApplication(mtm); for win in &app.windows() { win.contentView().map(|view| unsafe { view.setNeedsDisplay(true) }); } Some(AppHandle(app)) } fn with(&self, mut fun: impl FnMut(::AppRef<'_>)) { fun(AppRef(&self.0)); } } #[allow(unused)] pub struct Vars { delegate: Rc>, proxy: Id, } declare_class!( pub(crate) struct AppDelegate; unsafe impl ClassType for AppDelegate { type Super = NSObject; type Mutability = mutability::MainThreadOnly; const NAME: &'static str = "XLoopAppDelegate"; } impl DeclaredClass for AppDelegate { type Ivars = Vars; } unsafe impl NSObjectProtocol for AppDelegate {} unsafe impl NSApplicationDelegate for AppDelegate { #[method_id(proxy)] fn proxy(&self) -> Id { self.ivars().proxy.clone() } #[method(applicationDidFinishLaunching:)] fn did_finish_launching(&self, _notification: &NSNotification) { self.did_finish_launching_(_notification); } #[method(applicationShouldTerminateAfterLastWindowClosed:)] fn application_should_terminate_after_last_window_closed(&self, _sender: &NSApplication) -> bool { true } #[method(applicationWillTerminate:)] fn application_will_terminate(&self, notification: &NSNotification) { self.application_will_terminate_(notification); } } ); impl AppDelegate { pub fn build(delegate: impl types::AppDelegate + 'static) -> Id { let delegate = Rc::new(delegate); let mtm = MainThreadMarker::new().unwrap(); let app = NSApplication::sharedApplication(mtm); app.setActivationPolicy(NSApplicationActivationPolicy::Regular); let this = mtm.alloc().set_ivars(Vars { delegate, proxy: AppProxyObj::new(), }); let this: Id = unsafe { msg_send_id![super(this), init] }; app.setDelegate(Some(ProtocolObject::from_ref(&*this))); app } fn did_finish_launching_(&self, notification: &NSNotification) { if let Some(app) = unsafe { notification.object() } { let app = from_anyobject::(&app); self.ivars().delegate.on_launch(AppRef(app)); unsafe { app.activate() }; } } fn application_will_terminate_(&self, notification: &NSNotification) { if let Some(app) = unsafe { notification.object() } { let app = from_anyobject::(&app); self.ivars().delegate.on_terminate(AppRef(app)); } } } fn from_anyobject(value: &objc2::runtime::AnyObject) -> &T { use objc2::runtime::AnyObject; use std::ffi::c_void; unsafe { let value = value as *const AnyObject; let value = value as *const c_void; let value = value as *const T; &*value } }