use std::collections::HashSet;
use std::ffi::{self, CStr};
use std::ops::Deref;
use std::os::raw::c_char;
use std::sync::Arc;
use std::{fmt, ptr};
use glutin_egl_sys::egl;
use glutin_egl_sys::egl::types::{EGLAttrib, EGLDisplay, EGLint};
use once_cell::sync::OnceCell;
use raw_window_handle::RawDisplayHandle;
use crate::config::ConfigTemplate;
use crate::context::Version;
use crate::display::{AsRawDisplay, DisplayFeatures, GetDisplayExtensions, RawDisplay};
use crate::error::{ErrorKind, Result};
use crate::prelude::*;
use crate::private::Sealed;
use crate::surface::{PbufferSurface, PixmapSurface, SurfaceAttributes, WindowSurface};
use super::config::Config;
use super::context::NotCurrentContext;
use super::device::Device;
use super::surface::Surface;
use super::{Egl, EGL};
pub(crate) static NO_DISPLAY_EXTENSIONS: OnceCell<HashSet<&'static str>> = OnceCell::new();
#[derive(Debug, Clone)]
pub struct Display {
pub(crate) inner: Arc<DisplayInner>,
}
impl Display {
pub unsafe fn new(raw_display: RawDisplayHandle) -> Result<Self> {
let egl = match EGL.as_ref() {
Some(egl) => egl,
None => return Err(ErrorKind::NotFound.into()),
};
NO_DISPLAY_EXTENSIONS.get_or_init(|| get_extensions(egl, egl::NO_DISPLAY));
let display = Self::get_platform_display(egl, raw_display)
.or_else(|err| {
if err.error_kind() == ErrorKind::BadAttribute {
Err(err)
} else {
Self::get_platform_display_ext(egl, raw_display)
}
})
.or_else(|err| {
if err.error_kind() == ErrorKind::BadAttribute {
Err(err)
} else {
Self::get_display(egl, raw_display)
}
})?;
Self::initialize_display(egl, display)
}
pub unsafe fn with_device(
device: &Device,
raw_display: Option<RawDisplayHandle>,
) -> Result<Self> {
let egl = match EGL.as_ref() {
Some(egl) => egl,
None => return Err(ErrorKind::NotFound.into()),
};
if raw_display.is_some() {
return Err(ErrorKind::NotSupported(
"Display::with_device does not support a `raw_display` argument yet",
)
.into());
}
if !egl.GetPlatformDisplayEXT.is_loaded() {
return Err(ErrorKind::NotSupported("eglGetPlatformDisplayEXT is not supported").into());
}
let extensions = NO_DISPLAY_EXTENSIONS.get().unwrap();
if !extensions.contains("EGL_EXT_platform_base")
&& !extensions.contains("EGL_EXT_platform_device")
{
return Err(ErrorKind::NotSupported(
"Creating a display from a device is not supported",
)
.into());
}
let mut attrs = Vec::<EGLint>::with_capacity(2);
attrs.push(egl::NONE as EGLint);
let display = Self::check_display_error(unsafe {
egl.GetPlatformDisplayEXT(
egl::PLATFORM_DEVICE_EXT,
device.raw_device() as *mut _,
attrs.as_ptr(),
)
})?;
Self::initialize_display(egl, display)
}
pub fn device(&self) -> Result<Device> {
let no_display_extensions = NO_DISPLAY_EXTENSIONS.get().unwrap();
if !no_display_extensions.contains("EGL_EXT_device_query")
|| !no_display_extensions.contains("EGL_EXT_device_base")
{
return Err(ErrorKind::NotSupported(
"Querying the device from a display is not supported",
)
.into());
}
let device = ptr::null_mut();
if unsafe {
self.inner.egl.QueryDisplayAttribEXT(
self.inner.raw.0,
egl::DEVICE_EXT as EGLint,
device as *mut _,
)
} == egl::FALSE
{
super::check_error()?;
return Err(ErrorKind::NotSupported("Failed to query device from display").into());
}
Device::from_ptr(self.inner.egl, device)
}
fn get_platform_display(egl: &Egl, display: RawDisplayHandle) -> Result<EGLDisplay> {
if !egl.GetPlatformDisplay.is_loaded() {
return Err(ErrorKind::NotSupported("eglGetPlatformDisplay is not supported").into());
}
let extensions = NO_DISPLAY_EXTENSIONS.get().unwrap();
let mut attrs = Vec::<EGLAttrib>::new();
let (platform, mut display) = match display {
#[cfg(wayland_platform)]
RawDisplayHandle::Wayland(handle)
if extensions.contains("EGL_KHR_platform_wayland") =>
{
(egl::PLATFORM_WAYLAND_KHR, handle.display)
},
#[cfg(x11_platform)]
RawDisplayHandle::Xlib(handle) if extensions.contains("EGL_KHR_platform_x11") => {
attrs.push(egl::PLATFORM_X11_SCREEN_KHR as EGLAttrib);
attrs.push(handle.screen as EGLAttrib);
(egl::PLATFORM_X11_KHR, handle.display)
},
RawDisplayHandle::Gbm(handle) if extensions.contains("EGL_KHR_platform_gbm") => {
(egl::PLATFORM_GBM_KHR, handle.gbm_device)
},
RawDisplayHandle::Android(_) if extensions.contains("EGL_KHR_platform_android") => {
(egl::PLATFORM_ANDROID_KHR, egl::DEFAULT_DISPLAY as *mut _)
},
_ => {
return Err(
ErrorKind::NotSupported("provided display handle is not supported").into()
)
},
};
if display.is_null() {
display = egl::DEFAULT_DISPLAY as *mut _;
}
attrs.push(egl::NONE as EGLAttrib);
let display =
unsafe { egl.GetPlatformDisplay(platform, display as *mut _, attrs.as_ptr()) };
Self::check_display_error(display)
}
fn get_platform_display_ext(egl: &Egl, display: RawDisplayHandle) -> Result<EGLDisplay> {
if !egl.GetPlatformDisplayEXT.is_loaded() {
return Err(ErrorKind::NotSupported("eglGetPlatformDisplayEXT is not supported").into());
}
let extensions = NO_DISPLAY_EXTENSIONS.get().unwrap();
let mut attrs = Vec::<EGLint>::new();
let (platform, mut display) = match display {
#[cfg(wayland_platform)]
RawDisplayHandle::Wayland(handle)
if extensions.contains("EGL_EXT_platform_wayland") =>
{
(egl::PLATFORM_WAYLAND_EXT, handle.display)
},
#[cfg(x11_platform)]
RawDisplayHandle::Xlib(handle) if extensions.contains("EGL_EXT_platform_x11") => {
attrs.push(egl::PLATFORM_X11_SCREEN_EXT as EGLint);
attrs.push(handle.screen as EGLint);
(egl::PLATFORM_X11_EXT, handle.display)
},
#[cfg(x11_platform)]
RawDisplayHandle::Xcb(handle)
if extensions.contains("EGL_MESA_platform_xcb")
|| extensions.contains("EGL_EXT_platform_xcb") =>
{
attrs.push(egl::PLATFORM_XCB_EXT as EGLint);
attrs.push(handle.screen as EGLint);
(egl::PLATFORM_XCB_EXT, handle.connection)
},
RawDisplayHandle::Gbm(handle) if extensions.contains("EGL_MESA_platform_gbm") => {
(egl::PLATFORM_GBM_MESA, handle.gbm_device)
},
_ => {
return Err(
ErrorKind::NotSupported("provided display handle is not supported").into()
)
},
};
if display.is_null() {
display = egl::DEFAULT_DISPLAY as *mut _;
}
attrs.push(egl::NONE as EGLint);
let display =
unsafe { egl.GetPlatformDisplayEXT(platform, display as *mut _, attrs.as_ptr()) };
Self::check_display_error(display)
}
fn get_display(egl: &Egl, display: RawDisplayHandle) -> Result<EGLDisplay> {
let mut display = match display {
RawDisplayHandle::Gbm(handle) => handle.gbm_device,
#[cfg(x11_platform)]
RawDisplayHandle::Xlib(handle) => handle.display,
RawDisplayHandle::Android(_) => egl::DEFAULT_DISPLAY as *mut _,
_ => {
return Err(
ErrorKind::NotSupported("provided display handle is not supported").into()
)
},
};
if display.is_null() {
display = egl::DEFAULT_DISPLAY as *mut _;
}
let display = unsafe { egl.GetDisplay(display) };
Self::check_display_error(display)
}
fn extract_display_features(
extensions: &HashSet<&'static str>,
version: Version,
) -> DisplayFeatures {
let mut supported_features = DisplayFeatures::CREATE_ES_CONTEXT
| DisplayFeatures::MULTISAMPLING_PIXEL_FORMATS
| DisplayFeatures::SWAP_CONTROL;
supported_features.set(
DisplayFeatures::FLOAT_PIXEL_FORMAT,
extensions.contains("EGL_EXT_pixel_format_float"),
);
supported_features
.set(DisplayFeatures::SRGB_FRAMEBUFFERS, extensions.contains("EGL_KHR_gl_colorspace"));
supported_features.set(
DisplayFeatures::CONTEXT_ROBUSTNESS,
version > Version::new(1, 5)
|| extensions.contains("EGL_EXT_create_context_robustness"),
);
supported_features.set(
DisplayFeatures::CONTEXT_NO_ERROR,
extensions.contains("EGL_KHR_create_context_no_error"),
);
supported_features
}
fn check_display_error(display: EGLDisplay) -> Result<EGLDisplay> {
if display == egl::NO_DISPLAY {
Err(super::check_error().err().unwrap())
} else {
Ok(display)
}
}
fn initialize_display(egl: &'static Egl, display: EGLDisplay) -> Result<Self> {
let version = unsafe {
let (mut major, mut minor) = (0, 0);
if egl.Initialize(display, &mut major, &mut minor) == egl::FALSE {
return Err(super::check_error().err().unwrap());
}
Version::new(major as u8, minor as u8)
};
let client_extensions = get_extensions(egl, display);
let features = Self::extract_display_features(&client_extensions, version);
let inner = Arc::new(DisplayInner {
egl,
raw: EglDisplay(display),
_native_display: None,
version,
client_extensions,
features,
});
Ok(Self { inner })
}
}
impl GlDisplay for Display {
type Config = Config;
type NotCurrentContext = NotCurrentContext;
type PbufferSurface = Surface<PbufferSurface>;
type PixmapSurface = Surface<PixmapSurface>;
type WindowSurface = Surface<WindowSurface>;
unsafe fn find_configs(
&self,
template: ConfigTemplate,
) -> Result<Box<dyn Iterator<Item = Self::Config> + '_>> {
unsafe { Self::find_configs(self, template) }
}
unsafe fn create_window_surface(
&self,
config: &Self::Config,
surface_attributes: &SurfaceAttributes<WindowSurface>,
) -> Result<Self::WindowSurface> {
unsafe { Self::create_window_surface(self, config, surface_attributes) }
}
unsafe fn create_pbuffer_surface(
&self,
config: &Self::Config,
surface_attributes: &SurfaceAttributes<PbufferSurface>,
) -> Result<Self::PbufferSurface> {
unsafe { Self::create_pbuffer_surface(self, config, surface_attributes) }
}
unsafe fn create_context(
&self,
config: &Self::Config,
context_attributes: &crate::context::ContextAttributes,
) -> Result<Self::NotCurrentContext> {
unsafe { Self::create_context(self, config, context_attributes) }
}
unsafe fn create_pixmap_surface(
&self,
config: &Self::Config,
surface_attributes: &SurfaceAttributes<PixmapSurface>,
) -> Result<Self::PixmapSurface> {
unsafe { Self::create_pixmap_surface(self, config, surface_attributes) }
}
fn get_proc_address(&self, addr: &CStr) -> *const ffi::c_void {
unsafe { self.inner.egl.GetProcAddress(addr.as_ptr()) as *const _ }
}
fn version_string(&self) -> String {
format!("EGL {}.{}", self.inner.version.major, self.inner.version.minor)
}
fn supported_features(&self) -> DisplayFeatures {
self.inner.features
}
}
impl GetDisplayExtensions for Display {
fn extensions(&self) -> &HashSet<&'static str> {
&self.inner.client_extensions
}
}
impl AsRawDisplay for Display {
fn raw_display(&self) -> RawDisplay {
RawDisplay::Egl(*self.inner.raw)
}
}
impl Sealed for Display {}
pub(crate) struct DisplayInner {
pub(crate) egl: &'static Egl,
pub(crate) raw: EglDisplay,
pub(crate) version: Version,
pub(crate) client_extensions: HashSet<&'static str>,
pub(crate) features: DisplayFeatures,
pub(crate) _native_display: Option<NativeDisplay>,
}
impl fmt::Debug for DisplayInner {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Display")
.field("raw", &self.raw)
.field("version", &self.version)
.field("features", &self.features)
.field("extensions", &self.client_extensions)
.finish()
}
}
impl Drop for DisplayInner {
fn drop(&mut self) {
}
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct NativeDisplay(RawDisplayHandle);
unsafe impl Send for NativeDisplay {}
unsafe impl Sync for NativeDisplay {}
impl Deref for NativeDisplay {
type Target = RawDisplayHandle;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Debug, Clone)]
pub(crate) struct EglDisplay(EGLDisplay);
unsafe impl Send for EglDisplay {}
unsafe impl Sync for EglDisplay {}
impl Deref for EglDisplay {
type Target = EGLDisplay;
fn deref(&self) -> &Self::Target {
&self.0
}
}
pub(crate) fn get_extensions(egl: &Egl, display: EGLDisplay) -> HashSet<&'static str> {
unsafe {
let extensions = egl.QueryString(display, egl::EXTENSIONS as i32);
extensions_from_ptr(extensions)
}
}
pub(crate) unsafe fn extensions_from_ptr(extensions: *const c_char) -> HashSet<&'static str> {
if extensions.is_null() {
return HashSet::new();
}
if let Ok(extensions) = unsafe { CStr::from_ptr(extensions) }.to_str() {
extensions.split(' ').collect::<HashSet<&'static str>>()
} else {
HashSet::new()
}
}