<NME> display.rs
<BEF> //! Type safe native types for safe egl initialisation

use std::ffi::CStr;
use std::mem::MaybeUninit;
use std::ops::Deref;
use std::sync::Arc;
#[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))]
use std::sync::{Mutex, Weak};
use std::{
    collections::HashSet,
    os::unix::prelude::{AsRawFd, FromRawFd},
};

use io_lifetimes::OwnedFd;
use libc::c_void;
use nix::libc::c_int;
#[cfg(all(feature = "use_system_lib", feature = "wayland_frontend"))]
use wayland_server::{protocol::wl_buffer::WlBuffer, DisplayHandle, Resource};
#[cfg(feature = "use_system_lib")]
use wayland_sys::server::wl_display;

#[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))]
use crate::backend::egl::{BufferAccessError, EGLBuffer, Format};
use crate::{
    backend::{
        allocator::{dmabuf::Dmabuf, Buffer as _, Format as DrmFormat, Fourcc, Modifier},
        egl::{
            context::{GlAttributes, PixelFormatRequirements},
            ffi,
            ffi::egl::types::EGLImage,
            native::EGLNativeDisplay,
            wrap_egl_call, EGLError, Error,
        },
    },
    utils::{Buffer as BufferCoords, Size},
};

use slog::{debug, error, info, o, trace, warn};

#[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))]
lazy_static::lazy_static! {
    pub(crate) static ref BUFFER_READER: Mutex<Option<WeakBufferReader>> = Mutex::new(None);
}

/// Wrapper around [`ffi::EGLDisplay`](ffi::egl::types::EGLDisplay) to ensure display is only destroyed
/// once all resources bound to it have been dropped.
#[derive(Debug)]
pub struct EGLDisplayHandle {
    /// ffi EGLDisplay ptr
    pub handle: ffi::egl::types::EGLDisplay,
}
// EGLDisplay has an internal Mutex
unsafe impl Send for EGLDisplayHandle {}
unsafe impl Sync for EGLDisplayHandle {}

impl Deref for EGLDisplayHandle {
    type Target = ffi::egl::types::EGLDisplay;

    fn deref(&self) -> &Self::Target {
        &self.handle
    }
}

impl Drop for EGLDisplayHandle {
    fn drop(&mut self) {
        unsafe {
            // ignore errors on drop
            ffi::egl::Terminate(self.handle);
        }
    }
}

/// [`EGLDisplay`] represents an initialised EGL environment
#[derive(Debug, Clone)]
pub struct EGLDisplay {
    display: Arc<EGLDisplayHandle>,
    egl_version: (i32, i32),
    extensions: Vec<String>,
    dmabuf_import_formats: HashSet<DrmFormat>,
    dmabuf_render_formats: HashSet<DrmFormat>,
    surface_type: ffi::EGLint,
    logger: slog::Logger,
}

unsafe fn select_platform_display<N: EGLNativeDisplay + 'static>(
    native: &N,
    dp_extensions: &[String],
    log: &::slog::Logger,
) -> Result<*const c_void, Error> {
    for platform in native.supported_platforms() {
        debug!(log, "Trying EGL platform: {}", platform.platform_name);

        let log = log.new(o!("platform" => format!("{:?}", platform)));

        let missing_extensions = platform
            .required_extensions
            .iter()
            .filter(|ext| !dp_extensions.iter().any(|x| x == *ext))
            .collect::<Vec<_>>();

        if !missing_extensions.is_empty() {
            info!(
                log,
                "Skipping EGL platform because one or more required extensions are not supported. Missing extensions: {:?}", missing_extensions
            );
            continue;
        }

        let display = wrap_egl_call(|| {
            ffi::egl::GetPlatformDisplayEXT(
                platform.platform,
                platform.native_display,
                platform.attrib_list.as_ptr(),
            )
        })
        .map_err(Error::DisplayCreationError);

        let display = match display {
            Ok(display) => {
                if display == ffi::egl::NO_DISPLAY {
                    info!(log, "Skipping platform because the display is not supported");
                    continue;
                }

                display
            }
            Err(err) => {
                info!(
                    log,
                    "Skipping platform because of an display creation error: {:?}", err
                );
                continue;
            }
        };

        info!(
            log,
            "Successfully selected EGL platform: {}", platform.platform_name
        );
        return Ok(display);
    }

    error!(log, "Unable to find suitable EGL platform");
    Err(Error::DisplayNotSupported)
}

impl EGLDisplay {
    /// Create a new [`EGLDisplay`] from a given [`EGLNativeDisplay`]
    ///
    /// # Safety
    ///
    /// - `native`: The native display handle has to be kept alive for the lifetime of the
    ///   created [`EGLDisplay`]
    pub unsafe fn new<N, L>(native: &N, logger: L) -> Result<EGLDisplay, Error>
    where
        N: EGLNativeDisplay + 'static,
        L: Into<Option<::slog::Logger>>,
    {
        let log = crate::slog_or_fallback(logger.into()).new(o!("smithay_module" => "backend_egl"));

        let dp_extensions = ffi::make_sure_egl_is_loaded()?;
        debug!(log, "Supported EGL client extensions: {:?}", dp_extensions);
        // we create an EGLDisplay
        let display = select_platform_display(native, &dp_extensions, &log)?;

        // We can then query the egl api version
        let egl_version = {
            let mut major: MaybeUninit<ffi::egl::types::EGLint> = MaybeUninit::uninit();
            let mut minor: MaybeUninit<ffi::egl::types::EGLint> = MaybeUninit::uninit();

            wrap_egl_call(|| ffi::egl::Initialize(display, major.as_mut_ptr(), minor.as_mut_ptr()))
                .map_err(Error::InitFailed)?;

            let major = major.assume_init();
            let minor = minor.assume_init();

            info!(log, "EGL Initialized");
            info!(log, "EGL Version: {:?}", (major, minor));

            (major, minor)
        };

        // the list of extensions supported by the client once initialized is different from the
        // list of extensions obtained earlier
        let extensions = EGLDisplay::get_extensions(egl_version, display)?;
        info!(log, "Supported EGL display extensions: {:?}", extensions);

        let (dmabuf_import_formats, dmabuf_render_formats) =
            get_dmabuf_formats(&display, &extensions, &log).map_err(Error::DisplayCreationError)?;

        // egl <= 1.2 does not support OpenGL ES (maybe we want to support OpenGL in the future?)
        if egl_version <= (1, 2) {
            return Err(Error::OpenGlesNotSupported(None));
        }
        wrap_egl_call(|| ffi::egl::BindAPI(ffi::egl::OPENGL_ES_API))
            .map_err(|source| Error::OpenGlesNotSupported(Some(source)))?;

        Ok(EGLDisplay {
            display: Arc::new(EGLDisplayHandle { handle: display }),
            surface_type: native.surface_type(),
            egl_version,
            extensions,
            dmabuf_import_formats,
            dmabuf_render_formats,
            logger: log,
        })
    }

    /// Create a new [`EGLDisplay`] from an already initialized EGLDisplay and EGLConfig handle
    ///
    /// # Safety
    ///
    /// - The display must be created from the system default EGL library (`dlopen("libEGL.so")`)
    /// - The `display` and `config` must be valid for the lifetime of the returned display and any handles created by this display (using [`EGLDisplay::get_display_handle`])
    pub unsafe fn from_raw<L>(
        display: *const c_void,
        config_id: *const c_void,
        logger: L,
    ) -> Result<EGLDisplay, Error>
    where
        L: Into<Option<::slog::Logger>>,
    {
        assert!(!display.is_null(), "EGLDisplay pointer is null");
        assert!(!config_id.is_null(), "EGL configuration id pointer is null");

        let log = crate::slog_or_fallback(logger.into()).new(o!("smithay_module" => "backend_egl"));

        let dp_extensions = ffi::make_sure_egl_is_loaded()?;
        debug!(log, "Supported EGL client extensions: {:?}", dp_extensions);

        let egl_version = {
            let p = CStr::from_ptr(
                wrap_egl_call(|| ffi::egl::QueryString(display, ffi::egl::VERSION as i32))
                    .map_err(|_| Error::DisplayQueryResultInvalid)?,
            );

            let version_string = String::from_utf8(p.to_bytes().to_vec()).unwrap_or_else(|_| String::new());
            let mut version_iterator = version_string
                .split(' ')
                .next()
                .ok_or(Error::DisplayQueryResultInvalid)?
                .split('.');

            let major = version_iterator
                .next()
                .and_then(|v| v.parse::<i32>().ok())
                .ok_or(Error::DisplayQueryResultInvalid)?;
            let minor = version_iterator
                .next()
                .and_then(|v| v.parse::<i32>().ok())
                .ok_or(Error::DisplayQueryResultInvalid)?;

            info!(log, "EGL Version: {:?}", (major, minor));
            (major, minor)
        };

        let extensions = EGLDisplay::get_extensions(egl_version, display)?;
        info!(log, "Supported EGL display extensions: {:?}", extensions);

        let (dmabuf_import_formats, dmabuf_render_formats) =
            get_dmabuf_formats(&display, &extensions, &log).map_err(Error::DisplayCreationError)?;

        let egl_api =
            wrap_egl_call(|| ffi::egl::QueryAPI()).map_err(|_| Error::OpenGlesNotSupported(None))?;
        if egl_api != ffi::egl::OPENGL_ES_API {
            return Err(Error::OpenGlesNotSupported(None));
        }

        let surface_type = {
            let mut surface_type: MaybeUninit<ffi::egl::types::EGLint> = MaybeUninit::uninit();

            wrap_egl_call(|| {
                ffi::egl::GetConfigAttrib(
                    display,
                    config_id,
                    ffi::egl::SURFACE_TYPE as i32,
                    surface_type.as_mut_ptr(),
                )
            })
            .map_err(|_| Error::OpenGlesNotSupported(None))?;

            surface_type.assume_init()
        };

        Ok(EGLDisplay {
            display: Arc::new(EGLDisplayHandle { handle: display }),
            surface_type,
            egl_version,
            extensions,
            dmabuf_import_formats,
            dmabuf_render_formats,
            logger: log,
        })
    }

    fn get_extensions(egl_version: (i32, i32), display: *const c_void) -> Result<Vec<String>, Error> {
        if egl_version >= (1, 2) {
            let p = unsafe {
                CStr::from_ptr(
                    wrap_egl_call(|| ffi::egl::QueryString(display, ffi::egl::EXTENSIONS as i32))
                        .map_err(Error::InitFailed)?,
                )
            };
            let list = String::from_utf8(p.to_bytes().to_vec()).unwrap_or_else(|_| String::new());
            Ok(list.split(' ').map(|e| e.to_string()).collect::<Vec<_>>())
        } else {
            Ok(vec![])
        }
    }

    /// Finds a compatible EGLConfig for a given set of requirements
    pub fn choose_config(
        &self,
        attributes: GlAttributes,
        reqs: PixelFormatRequirements,
    ) -> Result<(PixelFormat, ffi::egl::types::EGLConfig), Error> {
        let descriptor = {
            let mut out: Vec<c_int> = Vec::with_capacity(37);

            if self.egl_version >= (1, 2) {
                trace!(self.logger, "Setting COLOR_BUFFER_TYPE to RGB_BUFFER");
                out.push(ffi::egl::COLOR_BUFFER_TYPE as c_int);
                out.push(ffi::egl::RGB_BUFFER as c_int);
            }

            trace!(self.logger, "Setting SURFACE_TYPE to {}", self.surface_type);

            out.push(ffi::egl::SURFACE_TYPE as c_int);
            out.push(self.surface_type);

            match attributes.version {
                (3, _) => {
                    if self.egl_version < (1, 3) {
                        error!(
                            self.logger,
                            "OpenglES 3.* is not supported on EGL Versions lower then 1.3"
                        );
                        return Err(Error::NoAvailablePixelFormat);
                    }
                    trace!(self.logger, "Setting RENDERABLE_TYPE to OPENGL_ES3");
                    out.push(ffi::egl::RENDERABLE_TYPE as c_int);
                    out.push(ffi::egl::OPENGL_ES3_BIT as c_int);
                    trace!(self.logger, "Setting CONFORMANT to OPENGL_ES3");
                    out.push(ffi::egl::CONFORMANT as c_int);
                    out.push(ffi::egl::OPENGL_ES3_BIT as c_int);
                }
                (2, _) => {
                    if self.egl_version < (1, 3) {
                        error!(
                            self.logger,
                            "OpenglES 2.* is not supported on EGL Versions lower then 1.3"
                        );
                        return Err(Error::NoAvailablePixelFormat);
                    }
                    trace!(self.logger, "Setting RENDERABLE_TYPE to OPENGL_ES2");
                    out.push(ffi::egl::RENDERABLE_TYPE as c_int);
                    out.push(ffi::egl::OPENGL_ES2_BIT as c_int);
                    trace!(self.logger, "Setting CONFORMANT to OPENGL_ES2");
                    out.push(ffi::egl::CONFORMANT as c_int);
                    out.push(ffi::egl::OPENGL_ES2_BIT as c_int);
                }
                ver => {
                    return Err(Error::OpenGlVersionNotSupported(ver));
                }
            };

            reqs.create_attributes(&mut out, &self.logger);
            out.push(ffi::egl::NONE as c_int);
            out
        };

        // Try to find configs that match out criteria
        let mut num_configs = 0;
        wrap_egl_call(|| unsafe {
            ffi::egl::ChooseConfig(
                **self.display,
                descriptor.as_ptr(),
                std::ptr::null_mut(),
                0,
                &mut num_configs,
            )
        })
        .map_err(Error::ConfigFailed)?;
        if num_configs == 0 {
            return Err(Error::NoAvailablePixelFormat);
        }

        let mut config_ids: Vec<ffi::egl::types::EGLConfig> = Vec::with_capacity(num_configs as usize);
        wrap_egl_call(|| unsafe {
            ffi::egl::ChooseConfig(
                **self.display,
                descriptor.as_ptr(),
                config_ids.as_mut_ptr(),
                num_configs,
                &mut num_configs,
            )
        })
        .map_err(Error::ConfigFailed)?;
        unsafe {
            config_ids.set_len(num_configs as usize);
        }

        if config_ids.is_empty() {
            return Err(Error::NoAvailablePixelFormat);
        }

        let desired_swap_interval = if attributes.vsync { 1 } else { 0 };
        // try to select a config with the desired_swap_interval
        // (but don't fail, as the margin might be very small on some cards and most configs are fine)
        let config_id = config_ids
            .iter()
            .copied()
            .map(|config| unsafe {
                let mut min_swap_interval = 0;
                wrap_egl_call(|| {
                    ffi::egl::GetConfigAttrib(
                        **self.display,
                        config,
                        ffi::egl::MIN_SWAP_INTERVAL as ffi::egl::types::EGLint,
                        &mut min_swap_interval,
                    )
                })?;

                if desired_swap_interval < min_swap_interval {
                    return Ok(None);
                }

                let mut max_swap_interval = 0;
                wrap_egl_call(|| {
                    ffi::egl::GetConfigAttrib(
                        **self.display,
                        config,
                        ffi::egl::MAX_SWAP_INTERVAL as ffi::egl::types::EGLint,
                        &mut max_swap_interval,
                    )
                })?;

                if desired_swap_interval > max_swap_interval {
                    return Ok(None);
                }

                Ok(Some(config))
            })
            .collect::<Result<Vec<Option<ffi::egl::types::EGLConfig>>, EGLError>>()
            .map_err(Error::ConfigFailed)?
            .into_iter()
            .flatten()
            .next()
            .unwrap_or_else(|| config_ids[0]);

        // return the format that was selected for our config
        let desc = unsafe { self.get_pixel_format(config_id)? };
        info!(self.logger, "Selected color format: {:?}", desc);

        Ok((desc, config_id))
    }

    /// Gets a PixelFormat from a configured EGLConfig
    pub(super) unsafe fn get_pixel_format(&self, config_id: *const c_void) -> Result<PixelFormat, Error> {
        // analyzing each config
        macro_rules! attrib {
            ($display:expr, $config:expr, $attr:expr) => {{
                let mut value = MaybeUninit::uninit();
                wrap_egl_call(|| {
                    ffi::egl::GetConfigAttrib(
                        **$display,
                        $config,
                        $attr as ffi::egl::types::EGLint,
                        value.as_mut_ptr(),
                    )
                })
                .map_err(Error::ConfigFailed)?;
                value.assume_init()
            }};
        }

        Ok(PixelFormat {
            hardware_accelerated: attrib!(self.display, config_id, ffi::egl::CONFIG_CAVEAT)
                != ffi::egl::SLOW_CONFIG as i32,
            color_bits: attrib!(self.display, config_id, ffi::egl::RED_SIZE) as u8
                + attrib!(self.display, config_id, ffi::egl::BLUE_SIZE) as u8
                + attrib!(self.display, config_id, ffi::egl::GREEN_SIZE) as u8,
            alpha_bits: attrib!(self.display, config_id, ffi::egl::ALPHA_SIZE) as u8,
            depth_bits: attrib!(self.display, config_id, ffi::egl::DEPTH_SIZE) as u8,
            stencil_bits: attrib!(self.display, config_id, ffi::egl::STENCIL_SIZE) as u8,
            stereoscopy: false,
            multisampling: match attrib!(self.display, config_id, ffi::egl::SAMPLES) {
                0 | 1 => None,
                a => Some(a as u16),
            },
            srgb: false, // TODO: use EGL_KHR_gl_colorspace to know that
        })
    }

    /// Get a handle to the underlying raw EGLDisplay handle
    pub fn get_display_handle(&self) -> Arc<EGLDisplayHandle> {
        self.display.clone()
    }

    /// Returns the runtime egl version of this display
    pub fn get_egl_version(&self) -> (i32, i32) {
        self.egl_version
    }

    /// Returns the supported extensions of this display
    pub fn extensions(&self) -> &[String] {
        &self.extensions[..]
    }

    /// Returns a list of formats for dmabufs that can be rendered to.
    pub fn dmabuf_render_formats(&self) -> &HashSet<DrmFormat> {
        &self.dmabuf_render_formats
    }

    /// Returns a list of formats for dmabufs that can be used as textures.
    pub fn dmabuf_texture_formats(&self) -> &HashSet<DrmFormat> {
        &self.dmabuf_import_formats
    }

    /// Returns when the display supports extensions required for smithays
    /// damage tracking helpers.
    pub fn supports_damage(&self) -> bool {
        self.supports_damage_impl().supported()
    }

    pub(super) fn supports_damage_impl(&self) -> DamageSupport {
        if self.extensions.iter().any(|ext| ext == "EGL_EXT_buffer_age") {
            if self
                .extensions
                .iter()
                .any(|ext| ext == "EGL_KHR_swap_buffers_with_damage")
            {
                return DamageSupport::KHR;
            } else if self
                .extensions
                .iter()
                .any(|ext| ext == "EGL_EXT_swap_buffers_with_damage")
            {
                return DamageSupport::EXT;
            }
        }

        DamageSupport::No
    }

    /// Exports an [`EGLImage`] as a [`Dmabuf`]
    #[allow(clippy::not_unsafe_ptr_arg_deref)]
    pub fn create_dmabuf_from_image(
        &self,
        image: EGLImage,
        size: Size<i32, BufferCoords>,
        y_inverted: bool,
    ) -> Result<Dmabuf, Error> {
        use crate::backend::allocator::dmabuf::DmabufFlags;

        if !self.extensions.iter().any(|s| s == "EGL_KHR_image_base")
            && !self
                .extensions
                .iter()
                .any(|s| s == "EGL_MESA_image_dma_buf_export")
        {
            return Err(Error::EglExtensionNotSupported(&[
                "EGL_KHR_image_base",
                "EGL_MESA_image_dma_buf_export",
            ]));
        }

        let mut format: nix::libc::c_int = 0;
        let mut num_planes: nix::libc::c_int = 0;
        let mut modifier: ffi::egl::types::EGLuint64KHR = 0;
        if unsafe {
            // TODO: clippy warns us here that we might dereference a raw pointer in a non unsafe public function
            // For now we add a allow rule, but this should be addressed in the future
            ffi::egl::ExportDMABUFImageQueryMESA(
                **self.display,
                image,
                &mut format as *mut _,
                &mut num_planes as *mut _,
                &mut modifier as *mut _,
            ) == ffi::egl::FALSE
        } {
            EGLError::from_last_call().map_err(Error::DmabufExportFailed)?;
        }

        let mut fds: Vec<nix::libc::c_int> = Vec::with_capacity(num_planes as usize);
        let mut strides: Vec<ffi::egl::types::EGLint> = Vec::with_capacity(num_planes as usize);
        let mut offsets: Vec<ffi::egl::types::EGLint> = Vec::with_capacity(num_planes as usize);
        unsafe {
            // TODO: clippy warns us here that we might dereference a raw pointer in a non unsafe public function
            // For now we add a allow rule, but this should be addressed in the future
            if ffi::egl::ExportDMABUFImageMESA(
                **self.display,
                image,
                fds.as_mut_ptr(),
                strides.as_mut_ptr(),
                offsets.as_mut_ptr(),
            ) == ffi::egl::FALSE
            {
                EGLError::from_last_call().map_err(Error::DmabufExportFailed)?;
            }
            fds.set_len(num_planes as usize);
            strides.set_len(num_planes as usize);
            offsets.set_len(num_planes as usize);
        }

        let mut dma = Dmabuf::builder(
            size,
            Fourcc::try_from(format as u32).expect("Unknown format"),
///
/// Can be created by using [`EGLGraphicsBackend::bind_wl_display`].
#[cfg(feature = "use_system_lib")]
pub struct EGLBufferReader {
    display: Arc<EGLDisplayHandle>,
    wayland: *mut wl_display,
}

// Gles2 does not implement debug, so we have to impl Debug manually
#[cfg(feature = "use_system_lib")]
impl fmt::Debug for EGLBufferReader {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("EGLBufferReader")
            .field("display", &self.display)
            .field("wayland", &self.wayland)
            .finish()
    }
}

#[cfg(feature = "use_system_lib")]
impl EGLBufferReader {
    fn new(display: Arc<EGLDisplayHandle>, wayland: *mut wl_display) -> Self {
        Self { display, wayland }
    }

    /// Try to receive [`EGLImages`] from a given [`WlBuffer`].
        }
        dma.build().ok_or(Error::DmabufExportFailed(EGLError::BadAlloc))
    }

    /// Imports a [`Dmabuf`] as an [`EGLImage`]
    pub fn create_image_from_dmabuf(&self, dmabuf: &Dmabuf) -> Result<EGLImage, Error> {
        if !self.extensions.iter().any(|s| s == "EGL_KHR_image_base")
            && !self
                .extensions
                .iter()
                .any(|s| s == "EGL_EXT_image_dma_buf_import")
        {
            return Err(Error::EglExtensionNotSupported(&[
                "EGL_KHR_image_base",
                "EGL_EXT_image_dma_buf_import",
            ]));
        }

        if dmabuf.has_modifier()
            && !self
                .extensions
                .iter()
                .any(|s| s == "EGL_EXT_image_dma_buf_import_modifiers")
        {
            return Err(Error::EglExtensionNotSupported(&[
                "EGL_EXT_image_dma_buf_import_modifiers",
            ]));
        };

        let mut out: Vec<c_int> = Vec::with_capacity(50);

        out.extend(&[
            ffi::egl::WIDTH as i32,
            dmabuf.width() as i32,
            ffi::egl::HEIGHT as i32,
            dmabuf.height() as i32,
            ffi::egl::LINUX_DRM_FOURCC_EXT as i32,
            dmabuf.format().code as u32 as i32,
        ]);

        let names = [
            [
                ffi::egl::DMA_BUF_PLANE0_FD_EXT,
                ffi::egl::DMA_BUF_PLANE0_OFFSET_EXT,
                ffi::egl::DMA_BUF_PLANE0_PITCH_EXT,
                ffi::egl::DMA_BUF_PLANE0_MODIFIER_LO_EXT,
                ffi::egl::DMA_BUF_PLANE0_MODIFIER_HI_EXT,
            ],
            [
                ffi::egl::DMA_BUF_PLANE1_FD_EXT,
                ffi::egl::DMA_BUF_PLANE1_OFFSET_EXT,
                ffi::egl::DMA_BUF_PLANE1_PITCH_EXT,
                ffi::egl::DMA_BUF_PLANE1_MODIFIER_LO_EXT,
                ffi::egl::DMA_BUF_PLANE1_MODIFIER_HI_EXT,
            ],
            [
                ffi::egl::DMA_BUF_PLANE2_FD_EXT,
                ffi::egl::DMA_BUF_PLANE2_OFFSET_EXT,
                ffi::egl::DMA_BUF_PLANE2_PITCH_EXT,
                ffi::egl::DMA_BUF_PLANE2_MODIFIER_LO_EXT,
                ffi::egl::DMA_BUF_PLANE2_MODIFIER_HI_EXT,
            ],
            [
                ffi::egl::DMA_BUF_PLANE3_FD_EXT,
                ffi::egl::DMA_BUF_PLANE3_OFFSET_EXT,
                ffi::egl::DMA_BUF_PLANE3_PITCH_EXT,
                ffi::egl::DMA_BUF_PLANE3_MODIFIER_LO_EXT,
                ffi::egl::DMA_BUF_PLANE3_MODIFIER_HI_EXT,
            ],
        ];

        for (i, ((fd, offset), stride)) in dmabuf
            .handles()
            .zip(dmabuf.offsets())
            .zip(dmabuf.strides())
            .enumerate()
        {
            out.extend(&[
                names[i][0] as i32,
                fd.as_raw_fd(),
                names[i][1] as i32,
                offset as i32,
                names[i][2] as i32,
                stride as i32,
            ]);
            if dmabuf.has_modifier() {
                out.extend(&[
                    names[i][3] as i32,
                    (Into::<u64>::into(dmabuf.format().modifier) & 0xFFFFFFFF) as i32,
                    names[i][4] as i32,
                    (Into::<u64>::into(dmabuf.format().modifier) >> 32) as i32,
                ])
            }
        }

        out.push(ffi::egl::NONE as i32);

        unsafe {
            let image = ffi::egl::CreateImageKHR(
                **self.display,
                ffi::egl::NO_CONTEXT,
                ffi::egl::LINUX_DMA_BUF_EXT,
                std::ptr::null(),
                out.as_ptr(),
            );

            if image == ffi::egl::NO_IMAGE_KHR {
                Err(Error::EGLImageCreationFailed)
            } else {
                Ok(image)
            }
        }
    }

    /// Binds this EGL display to the given Wayland display.
    ///
    /// This will allow clients to utilize EGL to create hardware-accelerated
    /// surfaces. The server will need to be able to handle EGL-[`WlBuffer`]s.
    ///
    /// ## Errors
    ///
    /// This might return [`EglExtensionNotSupported`](Error::EglExtensionNotSupported)
    /// if binding is not supported by the EGL implementation.
    ///
    /// This might return [`OtherEGLDisplayAlreadyBound`](Error::OtherEGLDisplayAlreadyBound)
    /// if called for the same [`Display`] multiple times, as only one egl display may be bound at any given time.
    #[cfg(all(feature = "use_system_lib", feature = "wayland_frontend"))]
    pub fn bind_wl_display(&self, display: &DisplayHandle) -> Result<EGLBufferReader, Error> {
        let display_ptr = display.backend_handle().display_ptr();
        if !self.extensions.iter().any(|s| s == "EGL_WL_bind_wayland_display") {
            return Err(Error::EglExtensionNotSupported(&["EGL_WL_bind_wayland_display"]));
        }
#[cfg(feature = "use_system_lib")]
impl Drop for EGLBufferReader {
    fn drop(&mut self) {
        if !self.wayland.is_null() {
            unsafe {
                // ignore errors on drop
                ffi::egl::UnbindWaylandDisplayWL(**self.display, self.wayland as _);
            }
        }
    }
            logger: self.logger.clone(),
        });
        Ok(reader)
    }
}

fn get_dmabuf_formats(
    display: &ffi::egl::types::EGLDisplay,
    extensions: &[String],
    log: &::slog::Logger,
) -> Result<(HashSet<DrmFormat>, HashSet<DrmFormat>), EGLError> {
    if !extensions.iter().any(|s| s == "EGL_EXT_image_dma_buf_import") {
        warn!(log, "Dmabuf import extension not available");
        return Ok((HashSet::new(), HashSet::new()));
    }

    let formats = {
        // when we only have the image_dmabuf_import extension we can't query
        // which formats are supported. These two are on almost always
        // supported; it's the intended way to just try to create buffers.
        // Just a guess but better than not supporting dmabufs at all,
        // given that the modifiers extension isn't supported everywhere.
        if !extensions
            .iter()
            .any(|s| s == "EGL_EXT_image_dma_buf_import_modifiers")
        {
            vec![Fourcc::Argb8888, Fourcc::Xrgb8888]
        } else {
            let mut num = 0i32;
            wrap_egl_call(|| unsafe {
                ffi::egl::QueryDmaBufFormatsEXT(*display, 0, std::ptr::null_mut(), &mut num as *mut _)
            })?;
            if num == 0 {
                return Ok((HashSet::new(), HashSet::new()));
            }
            let mut formats: Vec<u32> = Vec::with_capacity(num as usize);
            wrap_egl_call(|| unsafe {
                ffi::egl::QueryDmaBufFormatsEXT(
                    *display,
                    num,
                    formats.as_mut_ptr() as *mut _,
                    &mut num as *mut _,
                )
            })?;
            unsafe {
                formats.set_len(num as usize);
            }
            formats
                .into_iter()
                .flat_map(|x| Fourcc::try_from(x).ok())
                .collect::<Vec<_>>()
        }
    };

    let mut texture_formats = HashSet::new();
    let mut render_formats = HashSet::new();

    for fourcc in formats {
        let mut num = 0i32;
        // Some drivers return EGL_BAD_PARAMETER here for formats
        // they themselves returned for the query above.. *sigh*
        //
        // - NVIDIA proprietary since 520
        //
        // So lets ignore any errors of this call on purpose(!),
        // which will let `num` stay at `0` and handle the format
        // as unsupported by explicit modifiers.
        // Which is probably what the error is suppose to indicate
        // although the spec doesn't seem to demand it...
        match wrap_egl_call(|| unsafe {
            ffi::egl::QueryDmaBufModifiersEXT(
                *display,
                fourcc as i32,
                0,
                std::ptr::null_mut(),
                std::ptr::null_mut(),
                &mut num as *mut _,
            )
        }) {
            Ok(_) => {}
            Err(EGLError::BadParameter) => {
                num = 0;
            }
            Err(x) => {
                return Err(x);
            }
        };

        if num == 0 {
            texture_formats.insert(DrmFormat {
                code: fourcc,
                modifier: Modifier::Invalid,
            });
            render_formats.insert(DrmFormat {
                code: fourcc,
                modifier: Modifier::Invalid,
            });
        } else {
            let mut mods: Vec<u64> = Vec::with_capacity(num as usize);
            let mut external: Vec<ffi::egl::types::EGLBoolean> = Vec::with_capacity(num as usize);

            wrap_egl_call(|| unsafe {
                ffi::egl::QueryDmaBufModifiersEXT(
                    *display,
                    fourcc as i32,
                    num,
                    mods.as_mut_ptr(),
                    external.as_mut_ptr(),
                    &mut num as *mut _,
                )
            })?;

            unsafe {
                mods.set_len(num as usize);
                external.set_len(num as usize);
            }

            for (modifier, external_only) in mods.into_iter().zip(external.into_iter()) {
                let format = DrmFormat {
                    code: fourcc,
                    modifier: Modifier::from(modifier),
                };
                texture_formats.insert(format);
                if external_only == 0 {
                    render_formats.insert(format);
                }
            }
        }
    }

    trace!(log, "Supported dmabuf import formats: {:?}", texture_formats);
    trace!(log, "Supported dmabuf render formats: {:?}", render_formats);

    Ok((texture_formats, render_formats))
}

/// Type to receive [`EGLBuffer`] for EGL-based [`WlBuffer`]s.
///
/// Can be created by using [`EGLDisplay::bind_wl_display`].
#[cfg(feature = "use_system_lib")]
#[derive(Debug, Clone)]
pub struct EGLBufferReader {
    display: Arc<EGLDisplayHandle>,
    wayland: Option<Arc<*mut wl_display>>,
    #[allow(dead_code)]
    logger: ::slog::Logger,
}

#[cfg(feature = "use_system_lib")]
pub(crate) struct WeakBufferReader {
    display: Weak<EGLDisplayHandle>,
    logger: ::slog::Logger,
}

#[cfg(feature = "use_system_lib")]
impl WeakBufferReader {
    pub fn upgrade(&self) -> Option<EGLBufferReader> {
        Some(EGLBufferReader {
            display: self.display.upgrade()?,
            wayland: None,
            logger: self.logger.clone(),
        })
    }
}

#[cfg(feature = "use_system_lib")]
unsafe impl Send for EGLBufferReader {}

#[cfg(feature = "use_system_lib")]
impl EGLBufferReader {
    fn new<L>(display: Arc<EGLDisplayHandle>, wayland: *mut wl_display, log: L) -> Self
    where
        L: Into<Option<::slog::Logger>>,
    {
        let logger = crate::slog_or_fallback(log.into()).new(o!("smithay_module" => "egl_buffer_reader"));

        Self {
            display,
            wayland: Some(Arc::new(wayland)),
            logger,
        }
    }

    /// Try to receive [`EGLBuffer`] from a given [`WlBuffer`].
    ///
    /// In case the buffer is not managed by EGL (but e.g. the [`wayland::shm` module](crate::wayland::shm))
    /// a [`BufferAccessError::NotManaged`](crate::backend::egl::BufferAccessError::NotManaged) is returned.
    pub fn egl_buffer_contents(
        &self,
        buffer: &WlBuffer,
    ) -> ::std::result::Result<EGLBuffer, BufferAccessError> {
        if !buffer.is_alive() {
            return Err(BufferAccessError::Destroyed);
        }

        let mut format: i32 = 0;
        let query = wrap_egl_call(|| unsafe {
            ffi::egl::QueryWaylandBufferWL(
                **self.display,
                buffer.id().as_ptr() as _,
                ffi::egl::EGL_TEXTURE_FORMAT,
                &mut format,
            )
        })
        .map_err(BufferAccessError::NotManaged)?;
        if query == ffi::egl::FALSE {
            return Err(BufferAccessError::NotManaged(EGLError::BadParameter));
        }

        let format = match format {
            x if x == ffi::egl::TEXTURE_RGB as i32 => Format::RGB,
            x if x == ffi::egl::TEXTURE_RGBA as i32 => Format::RGBA,
            ffi::egl::TEXTURE_EXTERNAL_WL => Format::External,
            ffi::egl::TEXTURE_Y_UV_WL => {
                return Err(BufferAccessError::UnsupportedMultiPlanarFormat(Format::Y_UV))
            }
            ffi::egl::TEXTURE_Y_U_V_WL => {
                return Err(BufferAccessError::UnsupportedMultiPlanarFormat(Format::Y_U_V))
            }
            ffi::egl::TEXTURE_Y_XUXV_WL => {
                return Err(BufferAccessError::UnsupportedMultiPlanarFormat(Format::Y_XUXV))
            }
            x => panic!("EGL returned invalid texture type: {}", x),
        };

        let mut width: i32 = 0;
        wrap_egl_call(|| unsafe {
            ffi::egl::QueryWaylandBufferWL(
                **self.display,
                buffer.id().as_ptr() as _,
                ffi::egl::WIDTH as i32,
                &mut width,
            )
        })
        .map_err(BufferAccessError::NotManaged)?;

        let mut height: i32 = 0;
        wrap_egl_call(|| unsafe {
            ffi::egl::QueryWaylandBufferWL(
                **self.display,
                buffer.id().as_ptr() as _,
                ffi::egl::HEIGHT as i32,
                &mut height,
            )
        })
        .map_err(BufferAccessError::NotManaged)?;

        let y_inverted = {
            let mut inverted: i32 = 0;

            // Query the egl buffer with EGL_WAYLAND_Y_INVERTED_WL to retrieve the
            // buffer orientation.
            // The call can either fail, succeed with EGL_TRUE or succeed with EGL_FALSE.
            // The specification for eglQuery defines that unsupported attributes shall return
            // EGL_FALSE. In case of EGL_WAYLAND_Y_INVERTED_WL the specification defines that
            // if EGL_FALSE is returned the value of inverted should be assumed as EGL_TRUE.
            //
            // see: https://www.khronos.org/registry/EGL/extensions/WL/EGL_WL_bind_wayland_display.txt
            match wrap_egl_call(|| unsafe {
                ffi::egl::QueryWaylandBufferWL(
                    **self.display,
                    buffer.id().as_ptr() as _,
                    ffi::egl::WAYLAND_Y_INVERTED_WL,
                    &mut inverted,
                )
            })
            .map_err(BufferAccessError::NotManaged)?
            {
                ffi::egl::TRUE => inverted != 0,
                ffi::egl::FALSE => true,
                _ => unreachable!(),
            }
        };

        let mut images = Vec::with_capacity(format.num_planes());
        for i in 0..format.num_planes() {
            let out = [ffi::egl::WAYLAND_PLANE_WL as i32, i as i32, ffi::egl::NONE as i32];

            images.push({
                wrap_egl_call(|| unsafe {
                    ffi::egl::CreateImageKHR(
                        **self.display,
                        ffi::egl::NO_CONTEXT,
                        ffi::egl::WAYLAND_BUFFER_WL,
                        buffer.id().as_ptr() as *mut _,
                        out.as_ptr(),
                    )
                })
                .map_err(BufferAccessError::EGLImageCreationFailed)?
            });
        }

        Ok(EGLBuffer {
            display: self.display.clone(),
            size: (width, height).into(),
            // y_inverted is negated here because the gles2 renderer
            // already inverts the buffer during rendering.
            y_inverted: !y_inverted,
            format,
            images,
        })
    }

    /// Try to receive the dimensions of a given [`WlBuffer`].
    ///
    /// In case the buffer is not managed by EGL (but e.g. the [`wayland::shm` module](crate::wayland::shm)) or the
    /// context has been lost, `None` is returned.
    pub fn egl_buffer_dimensions(
        &self,
        buffer: &WlBuffer,
    ) -> Option<crate::utils::Size<i32, crate::utils::Buffer>> {
        if !buffer.is_alive() {
            return None;
        }

        let mut width: i32 = 0;
        if unsafe {
            ffi::egl::QueryWaylandBufferWL(
                **self.display,
                buffer.id().as_ptr() as _,
                ffi::egl::WIDTH as _,
                &mut width,
            ) == 0
        } {
            return None;
        }

        let mut height: i32 = 0;
        if unsafe {
            ffi::egl::QueryWaylandBufferWL(
                **self.display,
                buffer.id().as_ptr() as _,
                ffi::egl::HEIGHT as _,
                &mut height,
            ) == 0
        } {
            return None;
        }

        Some((width, height).into())
    }

    /// Returns if the [`EGLBuffer`] has an alpha channel
    ///
    /// Note: This will always return `true` for buffers with a
    /// `TEXTURE_EXTERNAL_WL` format
    pub fn egl_buffer_has_alpha(format: Format) -> bool {
        !matches!(
            format,
            Format::RGB | Format::Y_UV | Format::Y_U_V | Format::Y_XUXV
        )
    }
}

#[cfg(feature = "use_system_lib")]
impl Drop for EGLBufferReader {
    fn drop(&mut self) {
        if let Some(wayland) = self.wayland.take().and_then(|x| Arc::try_unwrap(x).ok()) {
            if !wayland.is_null() {
                unsafe {
                    // ignore errors on drop
                    ffi::egl::UnbindWaylandDisplayWL(**self.display, wayland as _);
                }
            }
        }
    }
}

/// Describes the pixel format of a framebuffer
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PixelFormat {
    /// is the format hardware accelerated
    pub hardware_accelerated: bool,
    /// number of bits used for colors
    pub color_bits: u8,
    /// number of bits used for alpha channel
    pub alpha_bits: u8,
    /// number of bits used for depth channel
    pub depth_bits: u8,
    /// number of bits used for stencil buffer
    pub stencil_bits: u8,
    /// is stereoscopy enabled
    pub stereoscopy: bool,
    /// number of samples used for multisampling if enabled
    pub multisampling: Option<u16>,
    /// is srgb enabled
    pub srgb: bool,
}

/// Denotes if damage tracking is supported.
///
/// Additionally notes which variant of the `EGL_*_swap_buffers_with_damage` extension was found.
/// Prefers `KHR` over `EXT`, if both are available.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum DamageSupport {
    /// `EGL_KHR_swap_buffers_with_damage`
    KHR,
    /// `EGL_EXT_swap_buffers_with_damage`
    EXT,
    /// Some required extensions are missing
    No,
}

impl DamageSupport {
    /// Returns true, if any valid combination of required extensions was found.
    pub fn supported(&self) -> bool {
        self != &DamageSupport::No
    }
}

<MSG> egl: Make EGLBufferReader clonable

<DFF> @@ -609,26 +609,16 @@ fn get_dmabuf_formats(
 ///
 /// Can be created by using [`EGLGraphicsBackend::bind_wl_display`].
 #[cfg(feature = "use_system_lib")]
+#[derive(Debug, Clone)]
 pub struct EGLBufferReader {
     display: Arc<EGLDisplayHandle>,
-    wayland: *mut wl_display,
-}
-
-// Gles2 does not implement debug, so we have to impl Debug manually
-#[cfg(feature = "use_system_lib")]
-impl fmt::Debug for EGLBufferReader {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("EGLBufferReader")
-            .field("display", &self.display)
-            .field("wayland", &self.wayland)
-            .finish()
-    }
+    wayland: Option<Arc<*mut wl_display>>,
 }
 
 #[cfg(feature = "use_system_lib")]
 impl EGLBufferReader {
     fn new(display: Arc<EGLDisplayHandle>, wayland: *mut wl_display) -> Self {
-        Self { display, wayland }
+        Self { display, wayland: Some(Arc::new(wayland)) }
     }
 
     /// Try to receive [`EGLImages`] from a given [`WlBuffer`].
@@ -767,10 +757,12 @@ impl EGLBufferReader {
 #[cfg(feature = "use_system_lib")]
 impl Drop for EGLBufferReader {
     fn drop(&mut self) {
-        if !self.wayland.is_null() {
-            unsafe {
-                // ignore errors on drop
-                ffi::egl::UnbindWaylandDisplayWL(**self.display, self.wayland as _);
+        if let Ok(wayland) = Arc::try_unwrap(self.wayland.take().unwrap()) {
+            if !wayland.is_null() {
+                unsafe {
+                    // ignore errors on drop
+                    ffi::egl::UnbindWaylandDisplayWL(**self.display, wayland as _);
+                }
             }
         }
     }

{'commit': 'diff --git a/src/backend/egl/display.rs b/src/backend/egl/display.rs\nindex f99e945e..1ee8ee65 100644\n--- a/src/backend/egl/display.rs\n+++ b/src/backend/egl/display.rs\n@@ -609,26 +609,16 @@ fn get_dmabuf_formats(\n ///\n /// Can be created by using [`EGLGraphicsBackend::bind_wl_display`].\n #[cfg(feature = "use_system_lib")]\n+#[derive(Debug, Clone)]\n pub struct EGLBufferReader {\n     display: Arc<EGLDisplayHandle>,\n-    wayland: *mut wl_display,\n-}\n-\n-// Gles2 does not implement debug, so we have to impl Debug manually\n-#[cfg(feature = "use_system_lib")]\n-impl fmt::Debug for EGLBufferReader {\n-    fn fmt(&self, f: &mut fmt::Formatter<\'_>) -> fmt::Result {\n-        f.debug_struct("EGLBufferReader")\n-            .field("display", &self.display)\n-            .field("wayland", &self.wayland)\n-            .finish()\n-    }\n+    wayland: Option<Arc<*mut wl_display>>,\n }\n \n #[cfg(feature = "use_system_lib")]\n impl EGLBufferReader {\n     fn new(display: Arc<EGLDisplayHandle>, wayland: *mut wl_display) -> Self {\n-        Self { display, wayland }\n+        Self { display, wayland: Some(Arc::new(wayland)) }\n     }\n \n     /// Try to receive [`EGLImages`] from a given [`WlBuffer`].\n@@ -767,10 +757,12 @@ impl EGLBufferReader {\n #[cfg(feature = "use_system_lib")]\n impl Drop for EGLBufferReader {\n     fn drop(&mut self) {\n-        if !self.wayland.is_null() {\n-            unsafe {\n-                // ignore errors on drop\n-                ffi::egl::UnbindWaylandDisplayWL(**self.display, self.wayland as _);\n+        if let Ok(wayland) = Arc::try_unwrap(self.wayland.take().unwrap()) {\n+            if !wayland.is_null() {\n+                unsafe {\n+                    // ignore errors on drop\n+                    ffi::egl::UnbindWaylandDisplayWL(**self.display, wayland as _);\n+                }\n             }\n         }\n     }\n', 'commit_subject': 'egl: Make EGLBufferReader clonable', 'repo_name': 'Smithay/smithay', 'committer': {'name': 'Victor Brekenfeld', 'email': '64b2b6d12bfe4baae7dad3d018f8cbf6b0e7a044@drakulix.de', 'time_sec': '1621087933', 'tz_offset': '120', 'date': {'seconds': '1621087933'}}, 'commit_tree': '722e1a756772a4570641b2bb81066b525a115c5e', 'commit_message': 'egl: Make EGLBufferReader clonable\n', 'language_name': 'C', 'license': 'mit', 'hunks': '@@ -609,26 +609,16 @@ fn get_dmabuf_formats(\n ///\n /// Can be created by using [`EGLGraphicsBackend::bind_wl_display`].\n #[cfg(feature = "use_system_lib")]\n+#[derive(Debug, Clone)]\n pub struct EGLBufferReader {\n     display: Arc<EGLDisplayHandle>,\n-    wayland: *mut wl_display,\n-}\n-\n-// Gles2 does not implement debug, so we have to impl Debug manually\n-#[cfg(feature = "use_system_lib")]\n-impl fmt::Debug for EGLBufferReader {\n-    fn fmt(&self, f: &mut fmt::Formatter<\'_>) -> fmt::Result {\n-        f.debug_struct("EGLBufferReader")\n-            .field("display", &self.display)\n-            .field("wayland", &self.wayland)\n-            .finish()\n-    }\n+    wayland: Option<Arc<*mut wl_display>>,\n }\n \n #[cfg(feature = "use_system_lib")]\n impl EGLBufferReader {\n     fn new(display: Arc<EGLDisplayHandle>, wayland: *mut wl_display) -> Self {\n-        Self { display, wayland }\n+        Self { display, wayland: Some(Arc::new(wayland)) }\n     }\n \n     /// Try to receive [`EGLImages`] from a given [`WlBuffer`].\n@@ -767,10 +757,12 @@ impl EGLBufferReader {\n #[cfg(feature = "use_system_lib")]\n impl Drop for EGLBufferReader {\n     fn drop(&mut self) {\n-        if !self.wayland.is_null() {\n-            unsafe {\n-                // ignore errors on drop\n-                ffi::egl::UnbindWaylandDisplayWL(**self.display, self.wayland as _);\n+        if let Ok(wayland) = Arc::try_unwrap(self.wayland.take().unwrap()) {\n+            if !wayland.is_null() {\n+                unsafe {\n+                    // ignore errors on drop\n+                    ffi::egl::UnbindWaylandDisplayWL(**self.display, wayland as _);\n+                }\n             }\n         }\n     }\n', 'addition_count': 9, 'deletion_count': 17, 'src_file': 'a/src/backend/egl/display.rs', 'tgt_file': 'b/src/backend/egl/display.rs', 'file_extension': '.rs', 'before_file': '//! Type safe native types for safe egl initialisation\n\nuse std::ffi::CStr;\nuse std::mem::MaybeUninit;\nuse std::ops::Deref;\nuse std::sync::Arc;\n#[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))]\nuse std::sync::{Mutex, Weak};\nuse std::{\n    collections::HashSet,\n    os::unix::prelude::{AsRawFd, FromRawFd},\n};\n\nuse io_lifetimes::OwnedFd;\nuse libc::c_void;\nuse nix::libc::c_int;\n#[cfg(all(feature = "use_system_lib", feature = "wayland_frontend"))]\nuse wayland_server::{protocol::wl_buffer::WlBuffer, DisplayHandle, Resource};\n#[cfg(feature = "use_system_lib")]\nuse wayland_sys::server::wl_display;\n\n#[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))]\nuse crate::backend::egl::{BufferAccessError, EGLBuffer, Format};\nuse crate::{\n    backend::{\n        allocator::{dmabuf::Dmabuf, Buffer as _, Format as DrmFormat, Fourcc, Modifier},\n        egl::{\n            context::{GlAttributes, PixelFormatRequirements},\n            ffi,\n            ffi::egl::types::EGLImage,\n            native::EGLNativeDisplay,\n            wrap_egl_call, EGLError, Error,\n        },\n    },\n    utils::{Buffer as BufferCoords, Size},\n};\n\nuse slog::{debug, error, info, o, trace, warn};\n\n#[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))]\nlazy_static::lazy_static! {\n    pub(crate) static ref BUFFER_READER: Mutex<Option<WeakBufferReader>> = Mutex::new(None);\n}\n\n/// Wrapper around [`ffi::EGLDisplay`](ffi::egl::types::EGLDisplay) to ensure display is only destroyed\n/// once all resources bound to it have been dropped.\n#[derive(Debug)]\npub struct EGLDisplayHandle {\n    /// ffi EGLDisplay ptr\n    pub handle: ffi::egl::types::EGLDisplay,\n}\n// EGLDisplay has an internal Mutex\nunsafe impl Send for EGLDisplayHandle {}\nunsafe impl Sync for EGLDisplayHandle {}\n\nimpl Deref for EGLDisplayHandle {\n    type Target = ffi::egl::types::EGLDisplay;\n\n    fn deref(&self) -> &Self::Target {\n        &self.handle\n    }\n}\n\nimpl Drop for EGLDisplayHandle {\n    fn drop(&mut self) {\n        unsafe {\n            // ignore errors on drop\n            ffi::egl::Terminate(self.handle);\n        }\n    }\n}\n\n/// [`EGLDisplay`] represents an initialised EGL environment\n#[derive(Debug, Clone)]\npub struct EGLDisplay {\n    display: Arc<EGLDisplayHandle>,\n    egl_version: (i32, i32),\n    extensions: Vec<String>,\n    dmabuf_import_formats: HashSet<DrmFormat>,\n    dmabuf_render_formats: HashSet<DrmFormat>,\n    surface_type: ffi::EGLint,\n    logger: slog::Logger,\n}\n\nunsafe fn select_platform_display<N: EGLNativeDisplay + \'static>(\n    native: &N,\n    dp_extensions: &[String],\n    log: &::slog::Logger,\n) -> Result<*const c_void, Error> {\n    for platform in native.supported_platforms() {\n        debug!(log, "Trying EGL platform: {}", platform.platform_name);\n\n        let log = log.new(o!("platform" => format!("{:?}", platform)));\n\n        let missing_extensions = platform\n            .required_extensions\n            .iter()\n            .filter(|ext| !dp_extensions.iter().any(|x| x == *ext))\n            .collect::<Vec<_>>();\n\n        if !missing_extensions.is_empty() {\n            info!(\n                log,\n                "Skipping EGL platform because one or more required extensions are not supported. Missing extensions: {:?}", missing_extensions\n            );\n            continue;\n        }\n\n        let display = wrap_egl_call(|| {\n            ffi::egl::GetPlatformDisplayEXT(\n                platform.platform,\n                platform.native_display,\n                platform.attrib_list.as_ptr(),\n            )\n        })\n        .map_err(Error::DisplayCreationError);\n\n        let display = match display {\n            Ok(display) => {\n                if display == ffi::egl::NO_DISPLAY {\n                    info!(log, "Skipping platform because the display is not supported");\n                    continue;\n                }\n\n                display\n            }\n            Err(err) => {\n                info!(\n                    log,\n                    "Skipping platform because of an display creation error: {:?}", err\n                );\n                continue;\n            }\n        };\n\n        info!(\n            log,\n            "Successfully selected EGL platform: {}", platform.platform_name\n        );\n        return Ok(display);\n    }\n\n    error!(log, "Unable to find suitable EGL platform");\n    Err(Error::DisplayNotSupported)\n}\n\nimpl EGLDisplay {\n    /// Create a new [`EGLDisplay`] from a given [`EGLNativeDisplay`]\n    ///\n    /// # Safety\n    ///\n    /// - `native`: The native display handle has to be kept alive for the lifetime of the\n    ///   created [`EGLDisplay`]\n    pub unsafe fn new<N, L>(native: &N, logger: L) -> Result<EGLDisplay, Error>\n    where\n        N: EGLNativeDisplay + \'static,\n        L: Into<Option<::slog::Logger>>,\n    {\n        let log = crate::slog_or_fallback(logger.into()).new(o!("smithay_module" => "backend_egl"));\n\n        let dp_extensions = ffi::make_sure_egl_is_loaded()?;\n        debug!(log, "Supported EGL client extensions: {:?}", dp_extensions);\n        // we create an EGLDisplay\n        let display = select_platform_display(native, &dp_extensions, &log)?;\n\n        // We can then query the egl api version\n        let egl_version = {\n            let mut major: MaybeUninit<ffi::egl::types::EGLint> = MaybeUninit::uninit();\n            let mut minor: MaybeUninit<ffi::egl::types::EGLint> = MaybeUninit::uninit();\n\n            wrap_egl_call(|| ffi::egl::Initialize(display, major.as_mut_ptr(), minor.as_mut_ptr()))\n                .map_err(Error::InitFailed)?;\n\n            let major = major.assume_init();\n            let minor = minor.assume_init();\n\n            info!(log, "EGL Initialized");\n            info!(log, "EGL Version: {:?}", (major, minor));\n\n            (major, minor)\n        };\n\n        // the list of extensions supported by the client once initialized is different from the\n        // list of extensions obtained earlier\n        let extensions = EGLDisplay::get_extensions(egl_version, display)?;\n        info!(log, "Supported EGL display extensions: {:?}", extensions);\n\n        let (dmabuf_import_formats, dmabuf_render_formats) =\n            get_dmabuf_formats(&display, &extensions, &log).map_err(Error::DisplayCreationError)?;\n\n        // egl <= 1.2 does not support OpenGL ES (maybe we want to support OpenGL in the future?)\n        if egl_version <= (1, 2) {\n            return Err(Error::OpenGlesNotSupported(None));\n        }\n        wrap_egl_call(|| ffi::egl::BindAPI(ffi::egl::OPENGL_ES_API))\n            .map_err(|source| Error::OpenGlesNotSupported(Some(source)))?;\n\n        Ok(EGLDisplay {\n            display: Arc::new(EGLDisplayHandle { handle: display }),\n            surface_type: native.surface_type(),\n            egl_version,\n            extensions,\n            dmabuf_import_formats,\n            dmabuf_render_formats,\n            logger: log,\n        })\n    }\n\n    /// Create a new [`EGLDisplay`] from an already initialized EGLDisplay and EGLConfig handle\n    ///\n    /// # Safety\n    ///\n    /// - The display must be created from the system default EGL library (`dlopen("libEGL.so")`)\n    /// - The `display` and `config` must be valid for the lifetime of the returned display and any handles created by this display (using [`EGLDisplay::get_display_handle`])\n    pub unsafe fn from_raw<L>(\n        display: *const c_void,\n        config_id: *const c_void,\n        logger: L,\n    ) -> Result<EGLDisplay, Error>\n    where\n        L: Into<Option<::slog::Logger>>,\n    {\n        assert!(!display.is_null(), "EGLDisplay pointer is null");\n        assert!(!config_id.is_null(), "EGL configuration id pointer is null");\n\n        let log = crate::slog_or_fallback(logger.into()).new(o!("smithay_module" => "backend_egl"));\n\n        let dp_extensions = ffi::make_sure_egl_is_loaded()?;\n        debug!(log, "Supported EGL client extensions: {:?}", dp_extensions);\n\n        let egl_version = {\n            let p = CStr::from_ptr(\n                wrap_egl_call(|| ffi::egl::QueryString(display, ffi::egl::VERSION as i32))\n                    .map_err(|_| Error::DisplayQueryResultInvalid)?,\n            );\n\n            let version_string = String::from_utf8(p.to_bytes().to_vec()).unwrap_or_else(|_| String::new());\n            let mut version_iterator = version_string\n                .split(\' \')\n                .next()\n                .ok_or(Error::DisplayQueryResultInvalid)?\n                .split(\'.\');\n\n            let major = version_iterator\n                .next()\n                .and_then(|v| v.parse::<i32>().ok())\n                .ok_or(Error::DisplayQueryResultInvalid)?;\n            let minor = version_iterator\n                .next()\n                .and_then(|v| v.parse::<i32>().ok())\n                .ok_or(Error::DisplayQueryResultInvalid)?;\n\n            info!(log, "EGL Version: {:?}", (major, minor));\n            (major, minor)\n        };\n\n        let extensions = EGLDisplay::get_extensions(egl_version, display)?;\n        info!(log, "Supported EGL display extensions: {:?}", extensions);\n\n        let (dmabuf_import_formats, dmabuf_render_formats) =\n            get_dmabuf_formats(&display, &extensions, &log).map_err(Error::DisplayCreationError)?;\n\n        let egl_api =\n            wrap_egl_call(|| ffi::egl::QueryAPI()).map_err(|_| Error::OpenGlesNotSupported(None))?;\n        if egl_api != ffi::egl::OPENGL_ES_API {\n            return Err(Error::OpenGlesNotSupported(None));\n        }\n\n        let surface_type = {\n            let mut surface_type: MaybeUninit<ffi::egl::types::EGLint> = MaybeUninit::uninit();\n\n            wrap_egl_call(|| {\n                ffi::egl::GetConfigAttrib(\n                    display,\n                    config_id,\n                    ffi::egl::SURFACE_TYPE as i32,\n                    surface_type.as_mut_ptr(),\n                )\n            })\n            .map_err(|_| Error::OpenGlesNotSupported(None))?;\n\n            surface_type.assume_init()\n        };\n\n        Ok(EGLDisplay {\n            display: Arc::new(EGLDisplayHandle { handle: display }),\n            surface_type,\n            egl_version,\n            extensions,\n            dmabuf_import_formats,\n            dmabuf_render_formats,\n            logger: log,\n        })\n    }\n\n    fn get_extensions(egl_version: (i32, i32), display: *const c_void) -> Result<Vec<String>, Error> {\n        if egl_version >= (1, 2) {\n            let p = unsafe {\n                CStr::from_ptr(\n                    wrap_egl_call(|| ffi::egl::QueryString(display, ffi::egl::EXTENSIONS as i32))\n                        .map_err(Error::InitFailed)?,\n                )\n            };\n            let list = String::from_utf8(p.to_bytes().to_vec()).unwrap_or_else(|_| String::new());\n            Ok(list.split(\' \').map(|e| e.to_string()).collect::<Vec<_>>())\n        } else {\n            Ok(vec![])\n        }\n    }\n\n    /// Finds a compatible EGLConfig for a given set of requirements\n    pub fn choose_config(\n        &self,\n        attributes: GlAttributes,\n        reqs: PixelFormatRequirements,\n    ) -> Result<(PixelFormat, ffi::egl::types::EGLConfig), Error> {\n        let descriptor = {\n            let mut out: Vec<c_int> = Vec::with_capacity(37);\n\n            if self.egl_version >= (1, 2) {\n                trace!(self.logger, "Setting COLOR_BUFFER_TYPE to RGB_BUFFER");\n                out.push(ffi::egl::COLOR_BUFFER_TYPE as c_int);\n                out.push(ffi::egl::RGB_BUFFER as c_int);\n            }\n\n            trace!(self.logger, "Setting SURFACE_TYPE to {}", self.surface_type);\n\n            out.push(ffi::egl::SURFACE_TYPE as c_int);\n            out.push(self.surface_type);\n\n            match attributes.version {\n                (3, _) => {\n                    if self.egl_version < (1, 3) {\n                        error!(\n                            self.logger,\n                            "OpenglES 3.* is not supported on EGL Versions lower then 1.3"\n                        );\n                        return Err(Error::NoAvailablePixelFormat);\n                    }\n                    trace!(self.logger, "Setting RENDERABLE_TYPE to OPENGL_ES3");\n                    out.push(ffi::egl::RENDERABLE_TYPE as c_int);\n                    out.push(ffi::egl::OPENGL_ES3_BIT as c_int);\n                    trace!(self.logger, "Setting CONFORMANT to OPENGL_ES3");\n                    out.push(ffi::egl::CONFORMANT as c_int);\n                    out.push(ffi::egl::OPENGL_ES3_BIT as c_int);\n                }\n                (2, _) => {\n                    if self.egl_version < (1, 3) {\n                        error!(\n                            self.logger,\n                            "OpenglES 2.* is not supported on EGL Versions lower then 1.3"\n                        );\n                        return Err(Error::NoAvailablePixelFormat);\n                    }\n                    trace!(self.logger, "Setting RENDERABLE_TYPE to OPENGL_ES2");\n                    out.push(ffi::egl::RENDERABLE_TYPE as c_int);\n                    out.push(ffi::egl::OPENGL_ES2_BIT as c_int);\n                    trace!(self.logger, "Setting CONFORMANT to OPENGL_ES2");\n                    out.push(ffi::egl::CONFORMANT as c_int);\n                    out.push(ffi::egl::OPENGL_ES2_BIT as c_int);\n                }\n                ver => {\n                    return Err(Error::OpenGlVersionNotSupported(ver));\n                }\n            };\n\n            reqs.create_attributes(&mut out, &self.logger);\n            out.push(ffi::egl::NONE as c_int);\n            out\n        };\n\n        // Try to find configs that match out criteria\n        let mut num_configs = 0;\n        wrap_egl_call(|| unsafe {\n            ffi::egl::ChooseConfig(\n                **self.display,\n                descriptor.as_ptr(),\n                std::ptr::null_mut(),\n                0,\n                &mut num_configs,\n            )\n        })\n        .map_err(Error::ConfigFailed)?;\n        if num_configs == 0 {\n            return Err(Error::NoAvailablePixelFormat);\n        }\n\n        let mut config_ids: Vec<ffi::egl::types::EGLConfig> = Vec::with_capacity(num_configs as usize);\n        wrap_egl_call(|| unsafe {\n            ffi::egl::ChooseConfig(\n                **self.display,\n                descriptor.as_ptr(),\n                config_ids.as_mut_ptr(),\n                num_configs,\n                &mut num_configs,\n            )\n        })\n        .map_err(Error::ConfigFailed)?;\n        unsafe {\n            config_ids.set_len(num_configs as usize);\n        }\n\n        if config_ids.is_empty() {\n            return Err(Error::NoAvailablePixelFormat);\n        }\n\n        let desired_swap_interval = if attributes.vsync { 1 } else { 0 };\n        // try to select a config with the desired_swap_interval\n        // (but don\'t fail, as the margin might be very small on some cards and most configs are fine)\n        let config_id = config_ids\n            .iter()\n            .copied()\n            .map(|config| unsafe {\n                let mut min_swap_interval = 0;\n                wrap_egl_call(|| {\n                    ffi::egl::GetConfigAttrib(\n                        **self.display,\n                        config,\n                        ffi::egl::MIN_SWAP_INTERVAL as ffi::egl::types::EGLint,\n                        &mut min_swap_interval,\n                    )\n                })?;\n\n                if desired_swap_interval < min_swap_interval {\n                    return Ok(None);\n                }\n\n                let mut max_swap_interval = 0;\n                wrap_egl_call(|| {\n                    ffi::egl::GetConfigAttrib(\n                        **self.display,\n                        config,\n                        ffi::egl::MAX_SWAP_INTERVAL as ffi::egl::types::EGLint,\n                        &mut max_swap_interval,\n                    )\n                })?;\n\n                if desired_swap_interval > max_swap_interval {\n                    return Ok(None);\n                }\n\n                Ok(Some(config))\n            })\n            .collect::<Result<Vec<Option<ffi::egl::types::EGLConfig>>, EGLError>>()\n            .map_err(Error::ConfigFailed)?\n            .into_iter()\n            .flatten()\n            .next()\n            .unwrap_or_else(|| config_ids[0]);\n\n        // return the format that was selected for our config\n        let desc = unsafe { self.get_pixel_format(config_id)? };\n        info!(self.logger, "Selected color format: {:?}", desc);\n\n        Ok((desc, config_id))\n    }\n\n    /// Gets a PixelFormat from a configured EGLConfig\n    pub(super) unsafe fn get_pixel_format(&self, config_id: *const c_void) -> Result<PixelFormat, Error> {\n        // analyzing each config\n        macro_rules! attrib {\n            ($display:expr, $config:expr, $attr:expr) => {{\n                let mut value = MaybeUninit::uninit();\n                wrap_egl_call(|| {\n                    ffi::egl::GetConfigAttrib(\n                        **$display,\n                        $config,\n                        $attr as ffi::egl::types::EGLint,\n                        value.as_mut_ptr(),\n                    )\n                })\n                .map_err(Error::ConfigFailed)?;\n                value.assume_init()\n            }};\n        }\n\n        Ok(PixelFormat {\n            hardware_accelerated: attrib!(self.display, config_id, ffi::egl::CONFIG_CAVEAT)\n                != ffi::egl::SLOW_CONFIG as i32,\n            color_bits: attrib!(self.display, config_id, ffi::egl::RED_SIZE) as u8\n                + attrib!(self.display, config_id, ffi::egl::BLUE_SIZE) as u8\n                + attrib!(self.display, config_id, ffi::egl::GREEN_SIZE) as u8,\n            alpha_bits: attrib!(self.display, config_id, ffi::egl::ALPHA_SIZE) as u8,\n            depth_bits: attrib!(self.display, config_id, ffi::egl::DEPTH_SIZE) as u8,\n            stencil_bits: attrib!(self.display, config_id, ffi::egl::STENCIL_SIZE) as u8,\n            stereoscopy: false,\n            multisampling: match attrib!(self.display, config_id, ffi::egl::SAMPLES) {\n                0 | 1 => None,\n                a => Some(a as u16),\n            },\n            srgb: false, // TODO: use EGL_KHR_gl_colorspace to know that\n        })\n    }\n\n    /// Get a handle to the underlying raw EGLDisplay handle\n    pub fn get_display_handle(&self) -> Arc<EGLDisplayHandle> {\n        self.display.clone()\n    }\n\n    /// Returns the runtime egl version of this display\n    pub fn get_egl_version(&self) -> (i32, i32) {\n        self.egl_version\n    }\n\n    /// Returns the supported extensions of this display\n    pub fn extensions(&self) -> &[String] {\n        &self.extensions[..]\n    }\n\n    /// Returns a list of formats for dmabufs that can be rendered to.\n    pub fn dmabuf_render_formats(&self) -> &HashSet<DrmFormat> {\n        &self.dmabuf_render_formats\n    }\n\n    /// Returns a list of formats for dmabufs that can be used as textures.\n    pub fn dmabuf_texture_formats(&self) -> &HashSet<DrmFormat> {\n        &self.dmabuf_import_formats\n    }\n\n    /// Returns when the display supports extensions required for smithays\n    /// damage tracking helpers.\n    pub fn supports_damage(&self) -> bool {\n        self.supports_damage_impl().supported()\n    }\n\n    pub(super) fn supports_damage_impl(&self) -> DamageSupport {\n        if self.extensions.iter().any(|ext| ext == "EGL_EXT_buffer_age") {\n            if self\n                .extensions\n                .iter()\n                .any(|ext| ext == "EGL_KHR_swap_buffers_with_damage")\n            {\n                return DamageSupport::KHR;\n            } else if self\n                .extensions\n                .iter()\n                .any(|ext| ext == "EGL_EXT_swap_buffers_with_damage")\n            {\n                return DamageSupport::EXT;\n            }\n        }\n\n        DamageSupport::No\n    }\n\n    /// Exports an [`EGLImage`] as a [`Dmabuf`]\n    #[allow(clippy::not_unsafe_ptr_arg_deref)]\n    pub fn create_dmabuf_from_image(\n        &self,\n        image: EGLImage,\n        size: Size<i32, BufferCoords>,\n        y_inverted: bool,\n    ) -> Result<Dmabuf, Error> {\n        use crate::backend::allocator::dmabuf::DmabufFlags;\n\n        if !self.extensions.iter().any(|s| s == "EGL_KHR_image_base")\n            && !self\n                .extensions\n                .iter()\n                .any(|s| s == "EGL_MESA_image_dma_buf_export")\n        {\n            return Err(Error::EglExtensionNotSupported(&[\n                "EGL_KHR_image_base",\n                "EGL_MESA_image_dma_buf_export",\n            ]));\n        }\n\n        let mut format: nix::libc::c_int = 0;\n        let mut num_planes: nix::libc::c_int = 0;\n        let mut modifier: ffi::egl::types::EGLuint64KHR = 0;\n        if unsafe {\n            // TODO: clippy warns us here that we might dereference a raw pointer in a non unsafe public function\n            // For now we add a allow rule, but this should be addressed in the future\n            ffi::egl::ExportDMABUFImageQueryMESA(\n                **self.display,\n                image,\n                &mut format as *mut _,\n                &mut num_planes as *mut _,\n                &mut modifier as *mut _,\n            ) == ffi::egl::FALSE\n        } {\n            EGLError::from_last_call().map_err(Error::DmabufExportFailed)?;\n        }\n\n        let mut fds: Vec<nix::libc::c_int> = Vec::with_capacity(num_planes as usize);\n        let mut strides: Vec<ffi::egl::types::EGLint> = Vec::with_capacity(num_planes as usize);\n        let mut offsets: Vec<ffi::egl::types::EGLint> = Vec::with_capacity(num_planes as usize);\n        unsafe {\n            // TODO: clippy warns us here that we might dereference a raw pointer in a non unsafe public function\n            // For now we add a allow rule, but this should be addressed in the future\n            if ffi::egl::ExportDMABUFImageMESA(\n                **self.display,\n                image,\n                fds.as_mut_ptr(),\n                strides.as_mut_ptr(),\n                offsets.as_mut_ptr(),\n            ) == ffi::egl::FALSE\n            {\n                EGLError::from_last_call().map_err(Error::DmabufExportFailed)?;\n            }\n            fds.set_len(num_planes as usize);\n            strides.set_len(num_planes as usize);\n            offsets.set_len(num_planes as usize);\n        }\n\n        let mut dma = Dmabuf::builder(\n            size,\n            Fourcc::try_from(format as u32).expect("Unknown format"),\n///\n/// Can be created by using [`EGLGraphicsBackend::bind_wl_display`].\n#[cfg(feature = "use_system_lib")]\npub struct EGLBufferReader {\n    display: Arc<EGLDisplayHandle>,\n    wayland: *mut wl_display,\n}\n\n// Gles2 does not implement debug, so we have to impl Debug manually\n#[cfg(feature = "use_system_lib")]\nimpl fmt::Debug for EGLBufferReader {\n    fn fmt(&self, f: &mut fmt::Formatter<\'_>) -> fmt::Result {\n        f.debug_struct("EGLBufferReader")\n            .field("display", &self.display)\n            .field("wayland", &self.wayland)\n            .finish()\n    }\n}\n\n#[cfg(feature = "use_system_lib")]\nimpl EGLBufferReader {\n    fn new(display: Arc<EGLDisplayHandle>, wayland: *mut wl_display) -> Self {\n        Self { display, wayland }\n    }\n\n    /// Try to receive [`EGLImages`] from a given [`WlBuffer`].\n        }\n        dma.build().ok_or(Error::DmabufExportFailed(EGLError::BadAlloc))\n    }\n\n    /// Imports a [`Dmabuf`] as an [`EGLImage`]\n    pub fn create_image_from_dmabuf(&self, dmabuf: &Dmabuf) -> Result<EGLImage, Error> {\n        if !self.extensions.iter().any(|s| s == "EGL_KHR_image_base")\n            && !self\n                .extensions\n                .iter()\n                .any(|s| s == "EGL_EXT_image_dma_buf_import")\n        {\n            return Err(Error::EglExtensionNotSupported(&[\n                "EGL_KHR_image_base",\n                "EGL_EXT_image_dma_buf_import",\n            ]));\n        }\n\n        if dmabuf.has_modifier()\n            && !self\n                .extensions\n                .iter()\n                .any(|s| s == "EGL_EXT_image_dma_buf_import_modifiers")\n        {\n            return Err(Error::EglExtensionNotSupported(&[\n                "EGL_EXT_image_dma_buf_import_modifiers",\n            ]));\n        };\n\n        let mut out: Vec<c_int> = Vec::with_capacity(50);\n\n        out.extend(&[\n            ffi::egl::WIDTH as i32,\n            dmabuf.width() as i32,\n            ffi::egl::HEIGHT as i32,\n            dmabuf.height() as i32,\n            ffi::egl::LINUX_DRM_FOURCC_EXT as i32,\n            dmabuf.format().code as u32 as i32,\n        ]);\n\n        let names = [\n            [\n                ffi::egl::DMA_BUF_PLANE0_FD_EXT,\n                ffi::egl::DMA_BUF_PLANE0_OFFSET_EXT,\n                ffi::egl::DMA_BUF_PLANE0_PITCH_EXT,\n                ffi::egl::DMA_BUF_PLANE0_MODIFIER_LO_EXT,\n                ffi::egl::DMA_BUF_PLANE0_MODIFIER_HI_EXT,\n            ],\n            [\n                ffi::egl::DMA_BUF_PLANE1_FD_EXT,\n                ffi::egl::DMA_BUF_PLANE1_OFFSET_EXT,\n                ffi::egl::DMA_BUF_PLANE1_PITCH_EXT,\n                ffi::egl::DMA_BUF_PLANE1_MODIFIER_LO_EXT,\n                ffi::egl::DMA_BUF_PLANE1_MODIFIER_HI_EXT,\n            ],\n            [\n                ffi::egl::DMA_BUF_PLANE2_FD_EXT,\n                ffi::egl::DMA_BUF_PLANE2_OFFSET_EXT,\n                ffi::egl::DMA_BUF_PLANE2_PITCH_EXT,\n                ffi::egl::DMA_BUF_PLANE2_MODIFIER_LO_EXT,\n                ffi::egl::DMA_BUF_PLANE2_MODIFIER_HI_EXT,\n            ],\n            [\n                ffi::egl::DMA_BUF_PLANE3_FD_EXT,\n                ffi::egl::DMA_BUF_PLANE3_OFFSET_EXT,\n                ffi::egl::DMA_BUF_PLANE3_PITCH_EXT,\n                ffi::egl::DMA_BUF_PLANE3_MODIFIER_LO_EXT,\n                ffi::egl::DMA_BUF_PLANE3_MODIFIER_HI_EXT,\n            ],\n        ];\n\n        for (i, ((fd, offset), stride)) in dmabuf\n            .handles()\n            .zip(dmabuf.offsets())\n            .zip(dmabuf.strides())\n            .enumerate()\n        {\n            out.extend(&[\n                names[i][0] as i32,\n                fd.as_raw_fd(),\n                names[i][1] as i32,\n                offset as i32,\n                names[i][2] as i32,\n                stride as i32,\n            ]);\n            if dmabuf.has_modifier() {\n                out.extend(&[\n                    names[i][3] as i32,\n                    (Into::<u64>::into(dmabuf.format().modifier) & 0xFFFFFFFF) as i32,\n                    names[i][4] as i32,\n                    (Into::<u64>::into(dmabuf.format().modifier) >> 32) as i32,\n                ])\n            }\n        }\n\n        out.push(ffi::egl::NONE as i32);\n\n        unsafe {\n            let image = ffi::egl::CreateImageKHR(\n                **self.display,\n                ffi::egl::NO_CONTEXT,\n                ffi::egl::LINUX_DMA_BUF_EXT,\n                std::ptr::null(),\n                out.as_ptr(),\n            );\n\n            if image == ffi::egl::NO_IMAGE_KHR {\n                Err(Error::EGLImageCreationFailed)\n            } else {\n                Ok(image)\n            }\n        }\n    }\n\n    /// Binds this EGL display to the given Wayland display.\n    ///\n    /// This will allow clients to utilize EGL to create hardware-accelerated\n    /// surfaces. The server will need to be able to handle EGL-[`WlBuffer`]s.\n    ///\n    /// ## Errors\n    ///\n    /// This might return [`EglExtensionNotSupported`](Error::EglExtensionNotSupported)\n    /// if binding is not supported by the EGL implementation.\n    ///\n    /// This might return [`OtherEGLDisplayAlreadyBound`](Error::OtherEGLDisplayAlreadyBound)\n    /// if called for the same [`Display`] multiple times, as only one egl display may be bound at any given time.\n    #[cfg(all(feature = "use_system_lib", feature = "wayland_frontend"))]\n    pub fn bind_wl_display(&self, display: &DisplayHandle) -> Result<EGLBufferReader, Error> {\n        let display_ptr = display.backend_handle().display_ptr();\n        if !self.extensions.iter().any(|s| s == "EGL_WL_bind_wayland_display") {\n            return Err(Error::EglExtensionNotSupported(&["EGL_WL_bind_wayland_display"]));\n        }\n#[cfg(feature = "use_system_lib")]\nimpl Drop for EGLBufferReader {\n    fn drop(&mut self) {\n        if !self.wayland.is_null() {\n            unsafe {\n                // ignore errors on drop\n                ffi::egl::UnbindWaylandDisplayWL(**self.display, self.wayland as _);\n            }\n        }\n    }\n            logger: self.logger.clone(),\n        });\n        Ok(reader)\n    }\n}\n\nfn get_dmabuf_formats(\n    display: &ffi::egl::types::EGLDisplay,\n    extensions: &[String],\n    log: &::slog::Logger,\n) -> Result<(HashSet<DrmFormat>, HashSet<DrmFormat>), EGLError> {\n    if !extensions.iter().any(|s| s == "EGL_EXT_image_dma_buf_import") {\n        warn!(log, "Dmabuf import extension not available");\n        return Ok((HashSet::new(), HashSet::new()));\n    }\n\n    let formats = {\n        // when we only have the image_dmabuf_import extension we can\'t query\n        // which formats are supported. These two are on almost always\n        // supported; it\'s the intended way to just try to create buffers.\n        // Just a guess but better than not supporting dmabufs at all,\n        // given that the modifiers extension isn\'t supported everywhere.\n        if !extensions\n            .iter()\n            .any(|s| s == "EGL_EXT_image_dma_buf_import_modifiers")\n        {\n            vec![Fourcc::Argb8888, Fourcc::Xrgb8888]\n        } else {\n            let mut num = 0i32;\n            wrap_egl_call(|| unsafe {\n                ffi::egl::QueryDmaBufFormatsEXT(*display, 0, std::ptr::null_mut(), &mut num as *mut _)\n            })?;\n            if num == 0 {\n                return Ok((HashSet::new(), HashSet::new()));\n            }\n            let mut formats: Vec<u32> = Vec::with_capacity(num as usize);\n            wrap_egl_call(|| unsafe {\n                ffi::egl::QueryDmaBufFormatsEXT(\n                    *display,\n                    num,\n                    formats.as_mut_ptr() as *mut _,\n                    &mut num as *mut _,\n                )\n            })?;\n            unsafe {\n                formats.set_len(num as usize);\n            }\n            formats\n                .into_iter()\n                .flat_map(|x| Fourcc::try_from(x).ok())\n                .collect::<Vec<_>>()\n        }\n    };\n\n    let mut texture_formats = HashSet::new();\n    let mut render_formats = HashSet::new();\n\n    for fourcc in formats {\n        let mut num = 0i32;\n        // Some drivers return EGL_BAD_PARAMETER here for formats\n        // they themselves returned for the query above.. *sigh*\n        //\n        // - NVIDIA proprietary since 520\n        //\n        // So lets ignore any errors of this call on purpose(!),\n        // which will let `num` stay at `0` and handle the format\n        // as unsupported by explicit modifiers.\n        // Which is probably what the error is suppose to indicate\n        // although the spec doesn\'t seem to demand it...\n        match wrap_egl_call(|| unsafe {\n            ffi::egl::QueryDmaBufModifiersEXT(\n                *display,\n                fourcc as i32,\n                0,\n                std::ptr::null_mut(),\n                std::ptr::null_mut(),\n                &mut num as *mut _,\n            )\n        }) {\n            Ok(_) => {}\n            Err(EGLError::BadParameter) => {\n                num = 0;\n            }\n            Err(x) => {\n                return Err(x);\n            }\n        };\n\n        if num == 0 {\n            texture_formats.insert(DrmFormat {\n                code: fourcc,\n                modifier: Modifier::Invalid,\n            });\n            render_formats.insert(DrmFormat {\n                code: fourcc,\n                modifier: Modifier::Invalid,\n            });\n        } else {\n            let mut mods: Vec<u64> = Vec::with_capacity(num as usize);\n            let mut external: Vec<ffi::egl::types::EGLBoolean> = Vec::with_capacity(num as usize);\n\n            wrap_egl_call(|| unsafe {\n                ffi::egl::QueryDmaBufModifiersEXT(\n                    *display,\n                    fourcc as i32,\n                    num,\n                    mods.as_mut_ptr(),\n                    external.as_mut_ptr(),\n                    &mut num as *mut _,\n                )\n            })?;\n\n            unsafe {\n                mods.set_len(num as usize);\n                external.set_len(num as usize);\n            }\n\n            for (modifier, external_only) in mods.into_iter().zip(external.into_iter()) {\n                let format = DrmFormat {\n                    code: fourcc,\n                    modifier: Modifier::from(modifier),\n                };\n                texture_formats.insert(format);\n                if external_only == 0 {\n                    render_formats.insert(format);\n                }\n            }\n        }\n    }\n\n    trace!(log, "Supported dmabuf import formats: {:?}", texture_formats);\n    trace!(log, "Supported dmabuf render formats: {:?}", render_formats);\n\n    Ok((texture_formats, render_formats))\n}\n\n/// Type to receive [`EGLBuffer`] for EGL-based [`WlBuffer`]s.\n///\n/// Can be created by using [`EGLDisplay::bind_wl_display`].\n#[cfg(feature = "use_system_lib")]\n#[derive(Debug, Clone)]\npub struct EGLBufferReader {\n    display: Arc<EGLDisplayHandle>,\n    wayland: Option<Arc<*mut wl_display>>,\n    #[allow(dead_code)]\n    logger: ::slog::Logger,\n}\n\n#[cfg(feature = "use_system_lib")]\npub(crate) struct WeakBufferReader {\n    display: Weak<EGLDisplayHandle>,\n    logger: ::slog::Logger,\n}\n\n#[cfg(feature = "use_system_lib")]\nimpl WeakBufferReader {\n    pub fn upgrade(&self) -> Option<EGLBufferReader> {\n        Some(EGLBufferReader {\n            display: self.display.upgrade()?,\n            wayland: None,\n            logger: self.logger.clone(),\n        })\n    }\n}\n\n#[cfg(feature = "use_system_lib")]\nunsafe impl Send for EGLBufferReader {}\n\n#[cfg(feature = "use_system_lib")]\nimpl EGLBufferReader {\n    fn new<L>(display: Arc<EGLDisplayHandle>, wayland: *mut wl_display, log: L) -> Self\n    where\n        L: Into<Option<::slog::Logger>>,\n    {\n        let logger = crate::slog_or_fallback(log.into()).new(o!("smithay_module" => "egl_buffer_reader"));\n\n        Self {\n            display,\n            wayland: Some(Arc::new(wayland)),\n            logger,\n        }\n    }\n\n    /// Try to receive [`EGLBuffer`] from a given [`WlBuffer`].\n    ///\n    /// In case the buffer is not managed by EGL (but e.g. the [`wayland::shm` module](crate::wayland::shm))\n    /// a [`BufferAccessError::NotManaged`](crate::backend::egl::BufferAccessError::NotManaged) is returned.\n    pub fn egl_buffer_contents(\n        &self,\n        buffer: &WlBuffer,\n    ) -> ::std::result::Result<EGLBuffer, BufferAccessError> {\n        if !buffer.is_alive() {\n            return Err(BufferAccessError::Destroyed);\n        }\n\n        let mut format: i32 = 0;\n        let query = wrap_egl_call(|| unsafe {\n            ffi::egl::QueryWaylandBufferWL(\n                **self.display,\n                buffer.id().as_ptr() as _,\n                ffi::egl::EGL_TEXTURE_FORMAT,\n                &mut format,\n            )\n        })\n        .map_err(BufferAccessError::NotManaged)?;\n        if query == ffi::egl::FALSE {\n            return Err(BufferAccessError::NotManaged(EGLError::BadParameter));\n        }\n\n        let format = match format {\n            x if x == ffi::egl::TEXTURE_RGB as i32 => Format::RGB,\n            x if x == ffi::egl::TEXTURE_RGBA as i32 => Format::RGBA,\n            ffi::egl::TEXTURE_EXTERNAL_WL => Format::External,\n            ffi::egl::TEXTURE_Y_UV_WL => {\n                return Err(BufferAccessError::UnsupportedMultiPlanarFormat(Format::Y_UV))\n            }\n            ffi::egl::TEXTURE_Y_U_V_WL => {\n                return Err(BufferAccessError::UnsupportedMultiPlanarFormat(Format::Y_U_V))\n            }\n            ffi::egl::TEXTURE_Y_XUXV_WL => {\n                return Err(BufferAccessError::UnsupportedMultiPlanarFormat(Format::Y_XUXV))\n            }\n            x => panic!("EGL returned invalid texture type: {}", x),\n        };\n\n        let mut width: i32 = 0;\n        wrap_egl_call(|| unsafe {\n            ffi::egl::QueryWaylandBufferWL(\n                **self.display,\n                buffer.id().as_ptr() as _,\n                ffi::egl::WIDTH as i32,\n                &mut width,\n            )\n        })\n        .map_err(BufferAccessError::NotManaged)?;\n\n        let mut height: i32 = 0;\n        wrap_egl_call(|| unsafe {\n            ffi::egl::QueryWaylandBufferWL(\n                **self.display,\n                buffer.id().as_ptr() as _,\n                ffi::egl::HEIGHT as i32,\n                &mut height,\n            )\n        })\n        .map_err(BufferAccessError::NotManaged)?;\n\n        let y_inverted = {\n            let mut inverted: i32 = 0;\n\n            // Query the egl buffer with EGL_WAYLAND_Y_INVERTED_WL to retrieve the\n            // buffer orientation.\n            // The call can either fail, succeed with EGL_TRUE or succeed with EGL_FALSE.\n            // The specification for eglQuery defines that unsupported attributes shall return\n            // EGL_FALSE. In case of EGL_WAYLAND_Y_INVERTED_WL the specification defines that\n            // if EGL_FALSE is returned the value of inverted should be assumed as EGL_TRUE.\n            //\n            // see: https://www.khronos.org/registry/EGL/extensions/WL/EGL_WL_bind_wayland_display.txt\n            match wrap_egl_call(|| unsafe {\n                ffi::egl::QueryWaylandBufferWL(\n                    **self.display,\n                    buffer.id().as_ptr() as _,\n                    ffi::egl::WAYLAND_Y_INVERTED_WL,\n                    &mut inverted,\n                )\n            })\n            .map_err(BufferAccessError::NotManaged)?\n            {\n                ffi::egl::TRUE => inverted != 0,\n                ffi::egl::FALSE => true,\n                _ => unreachable!(),\n            }\n        };\n\n        let mut images = Vec::with_capacity(format.num_planes());\n        for i in 0..format.num_planes() {\n            let out = [ffi::egl::WAYLAND_PLANE_WL as i32, i as i32, ffi::egl::NONE as i32];\n\n            images.push({\n                wrap_egl_call(|| unsafe {\n                    ffi::egl::CreateImageKHR(\n                        **self.display,\n                        ffi::egl::NO_CONTEXT,\n                        ffi::egl::WAYLAND_BUFFER_WL,\n                        buffer.id().as_ptr() as *mut _,\n                        out.as_ptr(),\n                    )\n                })\n                .map_err(BufferAccessError::EGLImageCreationFailed)?\n            });\n        }\n\n        Ok(EGLBuffer {\n            display: self.display.clone(),\n            size: (width, height).into(),\n            // y_inverted is negated here because the gles2 renderer\n            // already inverts the buffer during rendering.\n            y_inverted: !y_inverted,\n            format,\n            images,\n        })\n    }\n\n    /// Try to receive the dimensions of a given [`WlBuffer`].\n    ///\n    /// In case the buffer is not managed by EGL (but e.g. the [`wayland::shm` module](crate::wayland::shm)) or the\n    /// context has been lost, `None` is returned.\n    pub fn egl_buffer_dimensions(\n        &self,\n        buffer: &WlBuffer,\n    ) -> Option<crate::utils::Size<i32, crate::utils::Buffer>> {\n        if !buffer.is_alive() {\n            return None;\n        }\n\n        let mut width: i32 = 0;\n        if unsafe {\n            ffi::egl::QueryWaylandBufferWL(\n                **self.display,\n                buffer.id().as_ptr() as _,\n                ffi::egl::WIDTH as _,\n                &mut width,\n            ) == 0\n        } {\n            return None;\n        }\n\n        let mut height: i32 = 0;\n        if unsafe {\n            ffi::egl::QueryWaylandBufferWL(\n                **self.display,\n                buffer.id().as_ptr() as _,\n                ffi::egl::HEIGHT as _,\n                &mut height,\n            ) == 0\n        } {\n            return None;\n        }\n\n        Some((width, height).into())\n    }\n\n    /// Returns if the [`EGLBuffer`] has an alpha channel\n    ///\n    /// Note: This will always return `true` for buffers with a\n    /// `TEXTURE_EXTERNAL_WL` format\n    pub fn egl_buffer_has_alpha(format: Format) -> bool {\n        !matches!(\n            format,\n            Format::RGB | Format::Y_UV | Format::Y_U_V | Format::Y_XUXV\n        )\n    }\n}\n\n#[cfg(feature = "use_system_lib")]\nimpl Drop for EGLBufferReader {\n    fn drop(&mut self) {\n        if let Some(wayland) = self.wayland.take().and_then(|x| Arc::try_unwrap(x).ok()) {\n            if !wayland.is_null() {\n                unsafe {\n                    // ignore errors on drop\n                    ffi::egl::UnbindWaylandDisplayWL(**self.display, wayland as _);\n                }\n            }\n        }\n    }\n}\n\n/// Describes the pixel format of a framebuffer\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub struct PixelFormat {\n    /// is the format hardware accelerated\n    pub hardware_accelerated: bool,\n    /// number of bits used for colors\n    pub color_bits: u8,\n    /// number of bits used for alpha channel\n    pub alpha_bits: u8,\n    /// number of bits used for depth channel\n    pub depth_bits: u8,\n    /// number of bits used for stencil buffer\n    pub stencil_bits: u8,\n    /// is stereoscopy enabled\n    pub stereoscopy: bool,\n    /// number of samples used for multisampling if enabled\n    pub multisampling: Option<u16>,\n    /// is srgb enabled\n    pub srgb: bool,\n}\n\n/// Denotes if damage tracking is supported.\n///\n/// Additionally notes which variant of the `EGL_*_swap_buffers_with_damage` extension was found.\n/// Prefers `KHR` over `EXT`, if both are available.\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]\npub enum DamageSupport {\n    /// `EGL_KHR_swap_buffers_with_damage`\n    KHR,\n    /// `EGL_EXT_swap_buffers_with_damage`\n    EXT,\n    /// Some required extensions are missing\n    No,\n}\n\nimpl DamageSupport {\n    /// Returns true, if any valid combination of required extensions was found.\n    pub fn supported(&self) -> bool {\n        self != &DamageSupport::No\n    }\n}\n', 'message': 'egl: Make EGLBufferReader clonable\n', 'author_name': 'Victor Brekenfeld', 'lang': 'rs'}