use crate::hosting::HostingView;
use crate::objc::{self, NSRect};
use crate::views::ViewHandle;
use core::ffi::c_void;
pub struct Window {
window: *mut c_void,
hosting: HostingView,
}
#[cfg(target_os = "macos")]
impl Window {
pub fn new(view: &ViewHandle, title: &str, width: f64, height: f64) -> Self {
unsafe {
let hosting = HostingView::new(view);
let app = objc::shared_app();
let win = objc::send(objc::class(c"OBJC_CLASS_$_NSWindow"), objc::sel(c"alloc"));
type InitWin =
unsafe extern "C" fn(*mut c_void, *const c_void, NSRect, u64, u64, bool) -> *mut c_void;
let init: InitWin = core::mem::transmute(objc::msg_send_addr());
let win = init(
win,
objc::sel(c"initWithContentRect:styleMask:backing:defer:"),
NSRect::new(0.0, 0.0, width, height),
1 | 2 | 4 | 8, 2, false,
);
objc::send_id(win, objc::sel(c"setTitle:"), objc::nsstring(title));
objc::send_id(win, objc::sel(c"setContentView:"), hosting.as_ptr());
objc::send(win, objc::sel(c"center"));
objc::send_id(win, objc::sel(c"makeKeyAndOrderFront:"), core::ptr::null_mut());
objc::send_i64(app, objc::sel(c"setActivationPolicy:"), 0);
objc::send_bool(app, objc::sel(c"activateIgnoringOtherApps:"), true);
Self { window: win, hosting }
}
}
pub fn set_root(&self, view: &ViewHandle) {
self.hosting.set_root(view);
}
pub fn hosting(&self) -> &HostingView {
&self.hosting
}
pub fn as_ptr(&self) -> *mut c_void {
self.window
}
pub fn run(&self) -> ! {
unsafe {
let app = objc::shared_app();
objc::send(app, objc::sel(c"run"));
}
std::process::exit(0)
}
}
#[cfg(not(target_os = "macos"))]
impl Window {
pub fn new(view: &ViewHandle, title: &str, width: f64, height: f64) -> Self {
let _ = title;
unsafe {
let hosting = HostingView::new(view);
let window = objc::send(objc::class(c"OBJC_CLASS_$_UIWindow"), objc::sel(c"alloc"));
type InitFrame =
unsafe extern "C" fn(*mut c_void, *const c_void, NSRect) -> *mut c_void;
let init: InitFrame = core::mem::transmute(objc::msg_send_addr());
let window = init(
window,
objc::sel(c"initWithFrame:"),
NSRect::new(0.0, 0.0, width, height),
);
objc::send_id(
window,
objc::sel(c"setRootViewController:"),
hosting.controller(),
);
objc::send(window, objc::sel(c"makeKeyAndVisible"));
Self { window, hosting }
}
}
pub fn set_root(&self, view: &ViewHandle) {
self.hosting.set_root(view);
}
pub fn hosting(&self) -> &HostingView {
&self.hosting
}
pub fn as_ptr(&self) -> *mut c_void {
self.window
}
pub fn run(&self) -> ! {
unsafe {
#[cfg(feature = "uikit-entry")]
if objc::shared_app().is_null() {
run_app(self.hosting.controller());
}
unsafe extern "C" {
fn CFRunLoopRun();
}
CFRunLoopRun();
}
std::process::exit(0)
}
}
#[cfg(all(not(target_os = "macos"), feature = "uikit-entry"))]
static PENDING_ROOT: std::sync::atomic::AtomicPtr<c_void> =
std::sync::atomic::AtomicPtr::new(core::ptr::null_mut());
#[cfg(all(not(target_os = "macos"), feature = "uikit-entry"))]
pub unsafe fn run_app(controller: *mut c_void) -> ! {
use std::sync::atomic::Ordering;
unsafe extern "C" {
fn UIApplicationMain(
argc: i32,
argv: *const *const core::ffi::c_char,
principal: *mut c_void,
delegate: *mut c_void,
) -> i32;
}
unsafe extern "C" fn did_finish_launching(
this: *mut c_void,
_cmd: *const c_void,
_app: *mut c_void,
_options: *mut c_void,
) -> bool {
unsafe {
let screen = objc::send(
objc::class(c"OBJC_CLASS_$_UIScreen"),
objc::sel(c"mainScreen"),
);
let bounds = objc::send_ret_rect(screen, objc::sel(c"bounds"));
let window = objc::send(objc::class(c"OBJC_CLASS_$_UIWindow"), objc::sel(c"alloc"));
type InitFrame =
unsafe extern "C" fn(*mut c_void, *const c_void, objc::NSRect) -> *mut c_void;
let init: InitFrame = core::mem::transmute(objc::msg_send_addr());
let window = init(window, objc::sel(c"initWithFrame:"), bounds);
let root = PENDING_ROOT.load(Ordering::Acquire);
if !root.is_null() {
objc::send_id(window, objc::sel(c"setRootViewController:"), root);
}
objc::send(window, objc::sel(c"makeKeyAndVisible"));
let _ = this;
WINDOW.store(window, Ordering::Release);
true
}
}
static WINDOW: std::sync::atomic::AtomicPtr<c_void> =
std::sync::atomic::AtomicPtr::new(core::ptr::null_mut());
unsafe {
PENDING_ROOT.store(controller, Ordering::Release);
const DELEGATE: &core::ffi::CStr = c"RustAppDelegate";
if objc::class_named(DELEGATE).is_null() {
objc::ClassBuilder::new(c"UIResponder", DELEGATE)
.expect("RustAppDelegate")
.method(
c"application:didFinishLaunchingWithOptions:",
did_finish_launching as *const c_void,
c"B@:@@",
)
.conforms(c"UIApplicationDelegate")
.register();
}
let name = objc::nsstring("RustAppDelegate");
let args: Vec<*const core::ffi::c_char> = std::env::args()
.map(|a| {
std::ffi::CString::new(a).unwrap_or_default().into_raw() as *const core::ffi::c_char
})
.collect();
UIApplicationMain(
args.len() as i32,
args.as_ptr(),
core::ptr::null_mut(),
name,
);
}
std::process::exit(0)
}
#[cfg(target_os = "macos")]
pub fn show_window(view: &ViewHandle, title: &str, width: f64, height: f64) {
Window::new(view, title, width, height).run()
}
#[cfg(all(not(target_os = "macos"), feature = "uikit-entry"))]
pub fn show_window(view: &ViewHandle, title: &str, width: f64, height: f64) {
use std::sync::atomic::Ordering;
let _ = (title, width, height);
let hosting = HostingView::new(view);
let controller = hosting.controller();
let hosting: &'static HostingView = Box::leak(Box::new(hosting));
HOSTING.store(hosting as *const HostingView as *mut c_void, Ordering::Release);
unsafe { run_app(controller) }
}
#[cfg(not(target_os = "macos"))]
static HOSTING: std::sync::atomic::AtomicPtr<c_void> =
std::sync::atomic::AtomicPtr::new(core::ptr::null_mut());
#[cfg(not(target_os = "macos"))]
pub fn set_root(view: &ViewHandle) {
use std::sync::atomic::Ordering;
let p = HOSTING.load(Ordering::Acquire);
if p.is_null() {
return;
}
unsafe { (*(p as *const HostingView)).set_root(view) }
}
#[cfg(not(target_os = "macos"))]
pub fn on_main(f: unsafe extern "C" fn(*mut c_void), ctx: *mut c_void) {
unsafe extern "C" {
fn dispatch_async_f(queue: *mut c_void, ctx: *mut c_void, work: unsafe extern "C" fn(*mut c_void));
static _dispatch_main_q: c_void;
}
unsafe {
dispatch_async_f(
&raw const _dispatch_main_q as *mut c_void,
ctx,
f,
);
}
}