#![cfg(target_os = "macos")]
#![cfg(feature = "macos")]
#![allow(unused_unsafe)]
use objc2::rc::Retained;
use objc2::runtime::AnyObject;
use objc2::MainThreadMarker;
use objc2::{msg_send, sel};
use objc2_app_kit::{NSApplication, NSBackingStoreType, NSWindow, NSWindowStyleMask};
use objc2_foundation::{NSRect, NSString};
use std::collections::HashMap;
use std::sync::LazyLock;
use std::sync::Mutex;
#[derive(Clone, Copy)]
struct NativePtr(*mut std::ffi::c_void);
unsafe impl Send for NativePtr {}
static NATIVE_VIEWS: LazyLock<Mutex<HashMap<u64, NativePtr>>> =
LazyLock::new(|| Mutex::new(HashMap::new()));
pub(crate) fn store_native_view(widget_id: u64, view: *mut std::ffi::c_void) {
if view.is_null() {
return;
}
remove_native_view(widget_id);
unsafe {
let object = view as *mut AnyObject;
let _: *mut AnyObject = msg_send![object, retain];
}
NATIVE_VIEWS.lock().unwrap().insert(widget_id, NativePtr(view));
}
pub(crate) fn remove_native_view(widget_id: u64) {
let removed = NATIVE_VIEWS.lock().unwrap().remove(&widget_id);
if let Some(ptr) = removed {
unsafe {
let object = ptr.0 as *mut AnyObject;
let has_superview: bool =
msg_send![object, respondsToSelector: sel!(removeFromSuperview)];
if has_superview {
let _: () = msg_send![object, removeFromSuperview];
}
let _: () = msg_send![object, release];
}
}
}
fn make_rect(x: i32, y: i32, width: u32, height: u32) -> NSRect {
NSRect::new(
objc2_foundation::NSPoint::new(x as f64, y as f64),
objc2_foundation::NSSize::new(width.max(1) as f64, height.max(1) as f64),
)
}
pub(crate) fn create_ns_window(
mtm: MainThreadMarker,
title: &str,
x: i32,
y: i32,
width: u32,
height: u32,
) -> Retained<NSWindow> {
let style_mask = NSWindowStyleMask::Titled
| NSWindowStyleMask::Closable
| NSWindowStyleMask::Miniaturizable
| NSWindowStyleMask::Resizable;
let rect = make_rect(x, y, width, height);
let window = unsafe {
NSWindow::initWithContentRect_styleMask_backing_defer(
mtm.alloc(),
rect,
style_mask,
NSBackingStoreType::Buffered,
false,
)
};
window.setTitle(&NSString::from_str(title));
window.makeKeyAndOrderFront(None);
window
}
pub(crate) fn bootstrap_ns_application() -> bool {
let Some(mtm) = MainThreadMarker::new() else {
return false;
};
let app = NSApplication::sharedApplication(mtm);
app.finishLaunching();
true
}