<NME> native.rs
<BEF> //! Type safe native types for safe context/surface creation

use super::{ffi, wrap_egl_call, EGLError, Error};

#[cfg(feature = "backend_winit")]
use std::ptr;

#[cfg(feature = "backend_winit")]
use wayland_egl as wegl;
#[cfg(feature = "backend_winit")]
#[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};
    /// but there is no way to test that.
    unsafe fn get_display<F: Fn(&str) -> bool>(
        display: ffi::NativeDisplayType,
        has_dp_extension: F,
        log: ::slog::Logger,
    ) -> Result<ffi::egl::types::EGLDisplay, EGLError>;
/// * `required_extensions` - The name of the required EGL Extension for this platform
///
/// # Optional Arguments
/// * `attrib_list` - A list of `ffi::EGLint` like defined in the EGL Extension
///
/// # Examples
///
/// ```ignore

    unsafe fn get_display<F>(
        display: ffi::NativeDisplayType,
        has_dp_extension: F,
        log: ::slog::Logger,
    ) -> Result<ffi::egl::types::EGLDisplay, EGLError>
macro_rules! egl_platform {
    ($platform:ident, $native_display:expr, $required_extensions:expr) => {
    {
        if has_dp_extension("EGL_KHR_platform_wayland") && ffi::egl::GetPlatformDisplay::is_loaded() {
            trace!(log, "EGL Display Initialization via EGL_KHR_platform_wayland");
            wrap_egl_call(|| {
                ffi::egl::GetPlatformDisplay(ffi::egl::PLATFORM_WAYLAND_KHR, display as *mut _, ptr::null())
            })
        } else if has_dp_extension("EGL_EXT_platform_wayland") && ffi::egl::GetPlatformDisplayEXT::is_loaded()
        {
            $required_extensions,
        )
                ffi::egl::GetPlatformDisplayEXT(
                    ffi::egl::PLATFORM_WAYLAND_EXT,
                    display as *mut _,
                    ptr::null(),
                )
            })
        } else {
    /// Human readable name of the platform
    pub platform_name: &'static str,
    /// Platform type used to call [`eglGetPlatformDisplayEXT`](https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_platform_base.txt)
    pub platform: ffi::egl::types::EGLenum,
    /// Raw native display handle used to call [`eglGetPlatformDisplayEXT`](https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_platform_base.txt)
    pub native_display: *mut c_void,
    /// Attributes used to call [`eglGetPlatformDisplayEXT`](https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_platform_base.txt)
    pub attrib_list: Vec<ffi::EGLint>,
    _phantom: PhantomData<&'a c_void>,
}

impl<'a> EGLPlatform<'a> {
    /// Create a `EGLPlatform<'a>` for the provided platform.
    ///
    /// # Arguments

    unsafe fn get_display<F>(
        display: ffi::NativeDisplayType,
        has_dp_extension: F,
        log: ::slog::Logger,
    ) -> Result<ffi::egl::types::EGLDisplay, EGLError>
    /// # Examples
    ///
    {
        if has_dp_extension("EGL_KHR_platform_x11") && ffi::egl::GetPlatformDisplay::is_loaded() {
            trace!(log, "EGL Display Initialization via EGL_KHR_platform_x11");
            wrap_egl_call(|| {
                ffi::egl::GetPlatformDisplay(ffi::egl::PLATFORM_X11_KHR, display as *mut _, ptr::null())
            })
        } else if has_dp_extension("EGL_EXT_platform_x11") && ffi::egl::GetPlatformDisplayEXT::is_loaded() {
            trace!(log, "EGL Display Initialization via EGL_EXT_platform_x11");
            wrap_egl_call(|| {
                ffi::egl::GetPlatformDisplayEXT(ffi::egl::PLATFORM_X11_EXT, display as *mut _, ptr::null())
            })
        } else {
            trace!(log, "Default EGL Display Initialization via GetDisplay");
            _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()
    }
}

/// Trait describing platform specific functionality to create a valid `EGLDisplay` using the
/// [`EGL_EXT_platform_base`](https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_platform_base.txt) extension.
pub trait EGLNativeDisplay: Send {
    fn is_backend(&self) -> bool;
    /// Return a raw pointer EGL will accept for context creation.
    fn ptr(&self) -> Result<ffi::NativeDisplayType, Error>;
    /// Create a surface
    fn create_surface(&mut self, args: Self::Arguments) -> Result<B::Surface, Self::Error>;
}
    fn supported_platforms(&self) -> Vec<EGLPlatform<'_>> {
        vec![
            // see: https://www.khronos.org/registry/EGL/extensions/KHR/EGL_KHR_platform_gbm.txt
            egl_platform!(PLATFORM_GBM_KHR, self.as_raw(), &["EGL_KHR_platform_gbm"]),
            // see: https://www.khronos.org/registry/EGL/extensions/MESA/EGL_MESA_platform_gbm.txt
            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![
                // see: https://www.khronos.org/registry/EGL/extensions/KHR/EGL_KHR_platform_wayland.txt
                egl_platform!(PLATFORM_WAYLAND_KHR, display, &["EGL_KHR_platform_wayland"]),
                // see: https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_platform_wayland.txt
                egl_platform!(PLATFORM_WAYLAND_EXT, display, &["EGL_EXT_platform_wayland"]),
            ]
        } else if let Some(display) = self.xlib_display() {
            vec![
                // see: https://www.khronos.org/registry/EGL/extensions/KHR/EGL_KHR_platform_x11.txt
                egl_platform!(PLATFORM_X11_KHR, display, &["EGL_KHR_platform_x11"]),
                // see: https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_platform_x11.txt
                egl_platform!(PLATFORM_X11_EXT, display, &["EGL_EXT_platform_x11"]),
            ]
        } else {
            unreachable!("No backends for winit other then Wayland and X11 are supported")
        }
    }
}

/// Shallow type for EGL_PLATFORM_X11_EXT with the default X11 display
#[derive(Debug)]
pub struct X11DefaultDisplay;

impl EGLNativeDisplay for X11DefaultDisplay {
    fn supported_platforms(&self) -> Vec<EGLPlatform<'_>> {
        vec![egl_platform!(
            PLATFORM_X11_EXT,
            // We pass DEFAULT_DISPLAY (null pointer) because the driver should open a connection to the X server.
            ffi::egl::DEFAULT_DISPLAY,
            &["EGL_EXT_platform_x11"]
        )]
    }
}

impl EGLNativeDisplay for EGLDevice {
    fn supported_platforms(&self) -> Vec<EGLPlatform<'_>> {
        // see: https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_platform_device.txt
        vec![egl_platform!(
            PLATFORM_DEVICE_EXT,
            self.inner,
            &["EGL_EXT_platform_device"]
        )]
    }

    fn surface_type(&self) -> ffi::EGLint {
        // EGLDisplays based on EGLDevices do not support normal windowed surfaces.
        // But they may support streams, so lets allow users to create them themselves.
        ffi::egl::STREAM_BIT_KHR as ffi::EGLint
    }
}

/// Trait for types returning valid surface pointers for initializing egl.
///
/// ## Safety
///
/// The returned [`NativeWindowType`](ffi::NativeWindowType) must be valid for EGL
/// and there is no way to test that.
pub unsafe trait EGLNativeSurface: Send {
    /// Create an EGLSurface from the internal native type.
    ///
    /// Must be able to deal with re-creation of existing resources,
    /// if `needs_recreation` can return `true`.
    ///
    fn create(
        &self,
        display: &Arc<EGLDisplayHandle>,
        config_id: ffi::egl::types::EGLConfig,
    ) -> Result<*const c_void, super::EGLError>;

    /// Will be called to check if any internal resources will need
    /// to be recreated. Old resources must be used until `create`
    /// was called again and a new surface was obtained.
    ///
    /// Only needs to be recreated, if this may return true.
    /// The default implementation always returns false.
    fn needs_recreation(&self) -> bool {
        false
    }

    /// If the surface supports resizing you may implement and use this function.
    ///
    /// The two first arguments (width, height) are the new size of the surface,
    /// the two others (dx, dy) represent the displacement of the top-left corner of the surface.
    /// It allows you to control the direction of the resizing if necessary.
    ///
    /// Implementations may ignore the dx and dy arguments.
    ///
    /// Returns true if the resize was successful.
    fn resize(&self, _width: i32, _height: i32, _dx: i32, _dy: i32) -> bool {
        false
    }

    /// Adds additional semantics when calling
    /// [EGLSurface::swap_buffers](crate::backend::egl::surface::EGLSurface::swap_buffers)
    ///
    /// Only implement if required by the backend.
    fn swap_buffers(
        &self,
        display: &Arc<EGLDisplayHandle>,
        surface: ffi::egl::types::EGLSurface,
        damage: Option<&mut [Rectangle<i32, Physical>]>,
        damage_impl: DamageSupport,
    ) -> Result<(), SwapBuffersError> {
        wrap_egl_call(|| unsafe {
            if let Some(damage) = damage {
                match damage_impl {
                    DamageSupport::KHR => ffi::egl::SwapBuffersWithDamageKHR(
                        ***display,
                        surface as *const _,
                        damage.as_mut_ptr() as *mut _,
                        damage.len() as i32,
                    ),
                    DamageSupport::EXT => ffi::egl::SwapBuffersWithDamageEXT(
                        ***display,
                        surface as *const _,
                        damage.as_mut_ptr() as *mut _,
                        damage.len() as i32,
                    ),
                    DamageSupport::No => ffi::egl::SwapBuffers(***display, surface as *const _),
                };
            } else {
                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")]
/// Typed Xlib window for the `X11` backend
#[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
    }
}

<MSG> egl: allow display creation to specify attributes

<DFF> @@ -2,9 +2,6 @@
 
 use super::{ffi, wrap_egl_call, EGLError, Error};
 
-#[cfg(feature = "backend_winit")]
-use std::ptr;
-
 #[cfg(feature = "backend_winit")]
 use wayland_egl as wegl;
 #[cfg(feature = "backend_winit")]
@@ -25,6 +22,7 @@ pub trait Backend {
     /// but there is no way to test that.
     unsafe fn get_display<F: Fn(&str) -> bool>(
         display: ffi::NativeDisplayType,
+        attribs: &[ffi::EGLint],
         has_dp_extension: F,
         log: ::slog::Logger,
     ) -> Result<ffi::egl::types::EGLDisplay, EGLError>;
@@ -39,6 +37,7 @@ impl Backend for Wayland {
 
     unsafe fn get_display<F>(
         display: ffi::NativeDisplayType,
+        attribs: &[ffi::EGLint],
         has_dp_extension: F,
         log: ::slog::Logger,
     ) -> Result<ffi::egl::types::EGLDisplay, EGLError>
@@ -47,8 +46,13 @@ impl Backend for Wayland {
     {
         if has_dp_extension("EGL_KHR_platform_wayland") && ffi::egl::GetPlatformDisplay::is_loaded() {
             trace!(log, "EGL Display Initialization via EGL_KHR_platform_wayland");
+            let attribs = attribs.iter().map(|x| *x as isize).collect::<Vec<_>>();
             wrap_egl_call(|| {
-                ffi::egl::GetPlatformDisplay(ffi::egl::PLATFORM_WAYLAND_KHR, display as *mut _, ptr::null())
+                ffi::egl::GetPlatformDisplay(
+                    ffi::egl::PLATFORM_WAYLAND_KHR,
+                    display as *mut _,
+                    attribs.as_ptr(),
+                )
             })
         } else if has_dp_extension("EGL_EXT_platform_wayland") && ffi::egl::GetPlatformDisplayEXT::is_loaded()
         {
@@ -57,7 +61,7 @@ impl Backend for Wayland {
                 ffi::egl::GetPlatformDisplayEXT(
                     ffi::egl::PLATFORM_WAYLAND_EXT,
                     display as *mut _,
-                    ptr::null(),
+                    attribs.as_ptr(),
                 )
             })
         } else {
@@ -79,6 +83,7 @@ impl Backend for X11 {
 
     unsafe fn get_display<F>(
         display: ffi::NativeDisplayType,
+        attribs: &[ffi::EGLint],
         has_dp_extension: F,
         log: ::slog::Logger,
     ) -> Result<ffi::egl::types::EGLDisplay, EGLError>
@@ -87,13 +92,18 @@ impl Backend for X11 {
     {
         if has_dp_extension("EGL_KHR_platform_x11") && ffi::egl::GetPlatformDisplay::is_loaded() {
             trace!(log, "EGL Display Initialization via EGL_KHR_platform_x11");
+            let attribs = attribs.iter().map(|x| *x as isize).collect::<Vec<_>>();
             wrap_egl_call(|| {
-                ffi::egl::GetPlatformDisplay(ffi::egl::PLATFORM_X11_KHR, display as *mut _, ptr::null())
+                ffi::egl::GetPlatformDisplay(ffi::egl::PLATFORM_X11_KHR, display as *mut _, attribs.as_ptr())
             })
         } else if has_dp_extension("EGL_EXT_platform_x11") && ffi::egl::GetPlatformDisplayEXT::is_loaded() {
             trace!(log, "EGL Display Initialization via EGL_EXT_platform_x11");
             wrap_egl_call(|| {
-                ffi::egl::GetPlatformDisplayEXT(ffi::egl::PLATFORM_X11_EXT, display as *mut _, ptr::null())
+                ffi::egl::GetPlatformDisplayEXT(
+                    ffi::egl::PLATFORM_X11_EXT,
+                    display as *mut _,
+                    attribs.as_ptr(),
+                )
             })
         } else {
             trace!(log, "Default EGL Display Initialization via GetDisplay");
@@ -117,6 +127,12 @@ pub unsafe trait NativeDisplay<B: Backend> {
     fn is_backend(&self) -> bool;
     /// Return a raw pointer EGL will accept for context creation.
     fn ptr(&self) -> Result<ffi::NativeDisplayType, Error>;
+    /// Return attributes that might be used by `B::get_display`
+    ///
+    /// Default implementation returns an empty list
+    fn attributes(&self) -> Vec<ffi::EGLint> {
+        vec![ffi::egl::NONE as ffi::EGLint]
+    }
     /// Create a surface
     fn create_surface(&mut self, args: Self::Arguments) -> Result<B::Surface, Self::Error>;
 }

egl: allow display creation to specify attributes