use objc2::{
msg_send,
rc::Retained,
runtime::{AnyClass, AnyObject},
};
use raw_window_handle::RawWindowHandle;
use super::window::RawWindow;
const OCCLUSION_STATE_VISIBLE: usize = 1 << 1;
fn ns_view(raw: &RawWindow) -> Option<&AnyObject> {
let RawWindowHandle::AppKit(handle) = raw.window else {
return None;
};
Some(unsafe { handle.ns_view.cast::<AnyObject>().as_ref() })
}
pub(crate) fn sync(raw: &RawWindow) {
let Some(view) = ns_view(raw) else {
return;
};
let root: Option<Retained<AnyObject>> = unsafe { msg_send![view, layer] };
if let Some(root) = root {
make_translucent(&root);
}
}
fn make_translucent(root: &AnyObject) {
let Some(metal) = AnyClass::get(c"CAMetalLayer") else {
return;
};
unsafe {
let sublayers: Option<Retained<AnyObject>> = msg_send![root, sublayers];
let Some(sublayers) = sublayers else { return };
let count: usize = msg_send![&*sublayers, count];
for index in 0..count {
let layer: Retained<AnyObject> = msg_send![&*sublayers, objectAtIndex: index];
let is_metal: bool = msg_send![&*layer, isKindOfClass: metal];
let opaque: bool = msg_send![&*layer, isOpaque];
if is_metal && opaque {
let _: () = msg_send![&*layer, setOpaque: false];
}
}
}
}
pub(crate) fn is_on_screen(raw: &RawWindow) -> bool {
let Some(view) = ns_view(raw) else {
return false;
};
unsafe {
let window: Option<Retained<AnyObject>> = msg_send![view, window];
let Some(window) = window else { return false };
let state: usize = msg_send![&*window, occlusionState];
state & OCCLUSION_STATE_VISIBLE != 0
}
}
#[cfg(test)]
mod tests {
use objc2::{
msg_send,
rc::Retained,
runtime::{AnyClass, AnyObject},
};
fn layer(class: &std::ffi::CStr) -> Retained<AnyObject> {
let class = AnyClass::get(class).expect("a CALayer class");
unsafe { msg_send![class, new] }
}
fn is_opaque(layer: &AnyObject) -> bool {
unsafe { msg_send![layer, isOpaque] }
}
#[test]
fn makes_only_metal_sublayers_translucent() {
let root = layer(c"CALayer");
let metal = layer(c"CAMetalLayer");
let other = layer(c"CALayer");
unsafe {
for sublayer in [&metal, &other] {
let _: () = msg_send![&**sublayer, setOpaque: true];
let _: () = msg_send![&*root, addSublayer: &**sublayer];
}
}
super::make_translucent(&root);
assert!(!is_opaque(&metal));
assert!(is_opaque(&other));
}
}