use super::{display::EGLDisplayHandle, ffi, wrap_egl_call, SwapBuffersError};
#[cfg(feature = "backend_winit")]
use std::os::raw::c_int;
use std::os::raw::c_void;
#[cfg(feature = "backend_gbm")]
use std::os::unix::io::AsRawFd;
use std::{fmt::Debug, marker::PhantomData, sync::Arc};
#[cfg(feature = "backend_winit")]
use wayland_egl as wegl;
#[cfg(feature = "backend_winit")]
use winit::{platform::unix::WindowExtUnix, window::Window as WinitWindow};
#[cfg(feature = "backend_gbm")]
use gbm::{AsRaw, Device as GbmDevice};
#[macro_export]
macro_rules! egl_platform {
($platform:ident, $native_display:expr, $required_extensions:expr) => {
egl_platform!(
$platform,
$native_display,
$required_extensions,
vec![ffi::egl::NONE as ffi::EGLint]
);
};
($platform:ident, $native_display:expr, $required_extensions:expr, $attrib_list:expr) => {
EGLPlatform::new(
ffi::egl::$platform,
stringify!($platform),
$native_display as *mut _,
$attrib_list,
$required_extensions,
)
};
}
pub struct EGLPlatform<'a> {
pub required_extensions: &'static [&'static str],
pub platform_name: &'static str,
pub platform: ffi::egl::types::EGLenum,
pub native_display: *mut c_void,
pub attrib_list: Vec<ffi::EGLint>,
_phantom: PhantomData<&'a c_void>,
}
impl<'a> EGLPlatform<'a> {
pub fn new(
platform: ffi::egl::types::EGLenum,
platform_name: &'static str,
native_display: *mut c_void,
attrib_list: Vec<ffi::EGLint>,
required_extensions: &'static [&'static str],
) -> Self {
EGLPlatform {
platform,
platform_name,
native_display,
attrib_list,
required_extensions,
_phantom: PhantomData,
}
}
}
impl<'a> Debug for EGLPlatform<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("EGLPlatform")
.field("platform_name", &self.platform_name)
.field("required_extensions", &self.required_extensions)
.finish()
}
}
pub trait EGLNativeDisplay: Send {
fn supported_platforms(&self) -> Vec<EGLPlatform<'_>>;
fn surface_type(&self) -> ffi::EGLint {
ffi::egl::WINDOW_BIT as ffi::EGLint
}
}
#[cfg(feature = "backend_gbm")]
impl<A: AsRawFd + Send + 'static> EGLNativeDisplay for GbmDevice<A> {
fn supported_platforms(&self) -> Vec<EGLPlatform<'_>> {
vec![
egl_platform!(PLATFORM_GBM_KHR, self.as_raw(), &["EGL_KHR_platform_gbm"]),
egl_platform!(PLATFORM_GBM_MESA, self.as_raw(), &["EGL_MESA_platform_gbm"]),
]
}
}
#[cfg(feature = "backend_winit")]
impl EGLNativeDisplay for WinitWindow {
fn supported_platforms(&self) -> Vec<EGLPlatform<'_>> {
if let Some(display) = self.wayland_display() {
vec![
egl_platform!(PLATFORM_WAYLAND_KHR, display, &["EGL_KHR_platform_wayland"]),
egl_platform!(PLATFORM_WAYLAND_EXT, display, &["EGL_EXT_platform_wayland"]),
]
} else if let Some(display) = self.xlib_display() {
vec![
egl_platform!(PLATFORM_X11_KHR, display, &["EGL_KHR_platform_x11"]),
egl_platform!(PLATFORM_X11_EXT, display, &["EGL_EXT_platform_x11"]),
]
} else {
unreachable!("No backends for winit other then Wayland and X11 are supported")
}
}
}
pub unsafe trait EGLNativeSurface: Send + Sync {
fn create(
&self,
display: &Arc<EGLDisplayHandle>,
config_id: ffi::egl::types::EGLConfig,
) -> Result<*const c_void, super::EGLError>;
fn needs_recreation(&self) -> bool {
false
}
fn resize(&self, _width: i32, _height: i32, _dx: i32, _dy: i32) -> bool {
false
}
fn swap_buffers(
&self,
display: &Arc<EGLDisplayHandle>,
surface: ffi::egl::types::EGLSurface,
) -> Result<(), SwapBuffersError> {
wrap_egl_call(|| unsafe {
ffi::egl::SwapBuffers(***display, surface as *const _);
})
.map_err(SwapBuffersError::EGLSwapBuffers)
}
}
#[cfg(feature = "backend_winit")]
static WINIT_SURFACE_ATTRIBUTES: [c_int; 3] = [
ffi::egl::RENDER_BUFFER as c_int,
ffi::egl::BACK_BUFFER as c_int,
ffi::egl::NONE as c_int,
];
#[cfg(feature = "backend_winit")]
#[derive(Debug)]
pub struct XlibWindow(pub std::os::raw::c_ulong);
#[cfg(feature = "backend_winit")]
unsafe impl EGLNativeSurface for XlibWindow {
fn create(
&self,
display: &Arc<EGLDisplayHandle>,
config_id: ffi::egl::types::EGLConfig,
) -> Result<*const c_void, super::EGLError> {
wrap_egl_call(|| unsafe {
let mut id = self.0;
ffi::egl::CreatePlatformWindowSurfaceEXT(
display.handle,
config_id,
(&mut id) as *mut std::os::raw::c_ulong as *mut _,
WINIT_SURFACE_ATTRIBUTES.as_ptr(),
)
})
}
}
#[cfg(feature = "backend_winit")]
unsafe impl EGLNativeSurface for wegl::WlEglSurface {
fn create(
&self,
display: &Arc<EGLDisplayHandle>,
config_id: ffi::egl::types::EGLConfig,
) -> Result<*const c_void, super::EGLError> {
wrap_egl_call(|| unsafe {
ffi::egl::CreatePlatformWindowSurfaceEXT(
display.handle,
config_id,
self.ptr() as *mut _,
WINIT_SURFACE_ATTRIBUTES.as_ptr(),
)
})
}
fn resize(&self, width: i32, height: i32, dx: i32, dy: i32) -> bool {
wegl::WlEglSurface::resize(self, width, height, dx, dy);
true
}
}