use std::fmt;
use std::marker::PhantomData;
use std::num::NonZeroU32;
use dispatch2::{run_on_main, MainThreadBound};
use objc2::rc::Retained;
use objc2::MainThreadMarker;
use objc2_app_kit::{NSAppKitVersionNumber, NSAppKitVersionNumber10_12, NSView};
use raw_window_handle::RawWindowHandle;
use crate::config::GetGlConfig;
use crate::display::GetGlDisplay;
use crate::error::{ErrorKind, Result};
use crate::private::Sealed;
use crate::surface::{
AsRawSurface, GlSurface, PbufferSurface, PixmapSurface, RawSurface, SurfaceAttributes,
SurfaceTypeTrait, SwapInterval, WindowSurface,
};
use super::config::Config;
use super::context::PossiblyCurrentContext;
use super::display::Display;
impl Display {
pub(crate) unsafe fn create_pixmap_surface(
&self,
_config: &Config,
_surface_attributes: &SurfaceAttributes<PixmapSurface>,
) -> Result<Surface<PixmapSurface>> {
Err(ErrorKind::NotSupported("pixmaps are not supported with CGL").into())
}
pub(crate) unsafe fn create_pbuffer_surface(
&self,
_config: &Config,
_surface_attributes: &SurfaceAttributes<PbufferSurface>,
) -> Result<Surface<PbufferSurface>> {
Err(ErrorKind::NotSupported("pbuffers are not supported with CGL").into())
}
pub(crate) unsafe fn create_window_surface(
&self,
config: &Config,
surface_attributes: &SurfaceAttributes<WindowSurface>,
) -> Result<Surface<WindowSurface>> {
let native_window = match surface_attributes.raw_window_handle.unwrap() {
RawWindowHandle::AppKit(window) => window,
_ => {
return Err(
ErrorKind::NotSupported("provided native window is not supported").into()
)
},
};
let mtm = unsafe { MainThreadMarker::new_unchecked() };
let ns_view = if let Some(ns_view) =
unsafe { Retained::retain(native_window.ns_view.as_ptr().cast::<NSView>()) }
{
ns_view
} else {
return Err(ErrorKind::NotSupported("ns_view of provided native window is nil").into());
};
#[allow(deprecated)]
ns_view.setWantsBestResolutionOpenGLSurface(true);
if unsafe { NSAppKitVersionNumber }.floor() > NSAppKitVersionNumber10_12 {
ns_view.setWantsLayer(true);
}
let ns_view = MainThreadBound::new(ns_view, mtm);
let surface = Surface {
display: self.clone(),
config: config.clone(),
ns_view,
_nosync: PhantomData,
_ty: PhantomData,
};
Ok(surface)
}
}
pub struct Surface<T: SurfaceTypeTrait> {
display: Display,
config: Config,
pub(crate) ns_view: MainThreadBound<Retained<NSView>>,
_nosync: PhantomData<*const std::ffi::c_void>,
_ty: PhantomData<T>,
}
unsafe impl<T: SurfaceTypeTrait> Send for Surface<T> {}
impl<T: SurfaceTypeTrait> GlSurface<T> for Surface<T> {
type Context = PossiblyCurrentContext;
type SurfaceType = T;
fn buffer_age(&self) -> u32 {
0
}
fn width(&self) -> Option<u32> {
let view = &self.ns_view;
run_on_main(|mtm| {
let view = view.get(mtm);
let scale_factor = match view.window() {
Some(window) => window.backingScaleFactor(),
None => 1.0,
};
let frame = view.frame();
Some((frame.size.width * scale_factor) as u32)
})
}
fn height(&self) -> Option<u32> {
let view = &self.ns_view;
run_on_main(|mtm| {
let view = view.get(mtm);
let scale_factor = match view.window() {
Some(window) => window.backingScaleFactor(),
None => 1.0,
};
let frame = view.frame();
Some((frame.size.height * scale_factor) as u32)
})
}
fn is_single_buffered(&self) -> bool {
self.config.is_single_buffered()
}
fn swap_buffers(&self, context: &Self::Context) -> Result<()> {
context.inner.flush_buffer()
}
fn set_swap_interval(&self, context: &Self::Context, interval: SwapInterval) -> Result<()> {
context.inner.set_swap_interval(interval);
Ok(())
}
fn is_current(&self, context: &Self::Context) -> bool {
context.inner.is_view_current(&self.ns_view)
}
fn is_current_draw(&self, context: &Self::Context) -> bool {
self.is_current(context)
}
fn is_current_read(&self, context: &Self::Context) -> bool {
self.is_current(context)
}
fn resize(&self, context: &Self::Context, _width: NonZeroU32, _height: NonZeroU32) {
context.inner.update();
}
}
impl<T: SurfaceTypeTrait> GetGlConfig for Surface<T> {
type Target = Config;
fn config(&self) -> Self::Target {
self.config.clone()
}
}
impl<T: SurfaceTypeTrait> GetGlDisplay for Surface<T> {
type Target = Display;
fn display(&self) -> Self::Target {
self.display.clone()
}
}
impl<T: SurfaceTypeTrait> AsRawSurface for Surface<T> {
fn raw_surface(&self) -> RawSurface {
let mtm = unsafe { MainThreadMarker::new_unchecked() };
RawSurface::Cgl(Retained::as_ptr(self.ns_view.get(mtm)).cast())
}
}
impl<T: SurfaceTypeTrait> fmt::Debug for Surface<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Surface")
.field("config", &self.config.inner.raw)
.field("ns_view", &self.ns_view)
.field("type", &T::surface_type())
.finish()
}
}
impl<T: SurfaceTypeTrait> Sealed for Surface<T> {}