use std::sync::Arc;
use denise::{Frame, InputEvent, InputSource, PixelFormat, Rect, Size, Surface, SurfaceError};
use denise_macos::ViewSurface;
use objc2::rc::Retained;
use objc2::runtime::AnyObject;
use objc2_app_kit::NSView;
use objc2_core_foundation::CGFloat;
use objc2_io_surface::IOSurfaceRef;
use objc2_quartz_core::{CALayer, CATransaction};
use winit::raw_window_handle::{HasWindowHandle, RawWindowHandle};
use winit::window::Window;
use crate::Error;
pub struct MacSurface {
window: Arc<Window>,
layer: Retained<CALayer>,
surface: ViewSurface,
events: Vec<InputEvent>,
frames: u64,
}
impl MacSurface {
pub fn new(window: Arc<Window>) -> Result<Self, Error> {
let size = window.inner_size();
let size = Size::new(size.width, size.height);
let scale = window.scale_factor() as f32;
let layer = layer_of(&window)?;
let surface = ViewSurface::new(size, scale).map_err(|e| Error::Present(e.to_string()))?;
let this = Self {
window,
layer,
surface,
events: Vec::new(),
frames: 0,
};
this.attach();
Ok(this)
}
pub fn window(&self) -> &Arc<Window> {
&self.window
}
pub fn resize(&mut self, size: Size, scale_factor: f32) {
match self.surface.resize(size, scale_factor) {
Ok(false) => {}
Ok(true) => self.attach(),
Err(_) => {}
}
}
pub(crate) fn push_event(&mut self, event: InputEvent) {
self.events.push(event);
}
fn attach(&self) {
self.layer
.setContentsScale(CGFloat::from(self.surface.scale_factor()));
self.set_contents();
}
fn set_contents(&self) {
let surface: &IOSurfaceRef = self.surface.io_surface();
let contents: &AnyObject =
unsafe { &*(surface as *const IOSurfaceRef).cast::<AnyObject>() };
CATransaction::begin();
CATransaction::setDisableActions(true);
unsafe { self.layer.setContents(Some(contents)) };
CATransaction::commit();
CATransaction::flush();
}
}
fn layer_of(window: &Window) -> Result<Retained<CALayer>, Error> {
let handle = window
.window_handle()
.map_err(|e| Error::Present(e.to_string()))?;
let RawWindowHandle::AppKit(handle) = handle.as_raw() else {
return Err(Error::Present("not an AppKit window".into()));
};
let view: &NSView = unsafe { &*handle.ns_view.as_ptr().cast::<NSView>() };
view.setWantsLayer(true);
view.layer()
.ok_or_else(|| Error::Present("the view has no layer".into()))
}
impl MacSurface {
fn read_back(&self, frame: u64) {
if std::env::var_os("DENISE_MACOS_DEBUG").is_none()
|| (frame > 3 && !frame.is_multiple_of(60))
{
return;
}
let ours: *const AnyObject = (self.surface.io_surface() as *const IOSurfaceRef).cast();
let contents = unsafe { self.layer.contents() };
let held = match contents.as_deref() {
None => "nil".to_owned(),
Some(object) if core::ptr::eq(object as *const AnyObject, ours) => {
format!("ours @ {ours:p}")
}
Some(object) => format!("{:?} (not ours)", object.class()),
};
let same_layer = layer_of(&self.window)
.map(|current| core::ptr::eq(&*current, &*self.layer))
.unwrap_or(false);
eprintln!(
"denise-macos frame {frame}: contents: {held}, in_use: {}, use_count: {}, seed: {}, same layer: {same_layer}",
self.surface.io_surface().is_in_use(),
self.surface.io_surface().use_count(),
self.surface.io_surface().seed(),
);
}
}
impl Surface for MacSurface {
fn size(&self) -> Size {
self.surface.size()
}
fn scale_factor(&self) -> f32 {
self.surface.scale_factor()
}
fn format(&self) -> PixelFormat {
self.surface.format()
}
fn acquire(&mut self) -> Result<Frame<'_>, SurfaceError> {
self.surface.acquire()
}
fn present(&mut self, damage: &[Rect]) -> Result<(), SurfaceError> {
self.surface.present(damage)?;
self.set_contents();
self.frames += 1;
self.read_back(self.frames);
Ok(())
}
}
impl InputSource for MacSurface {
fn poll(&mut self, out: &mut Vec<InputEvent>) {
out.append(&mut self.events);
}
}