<NME> surface.rs
<BEF> //! EGL surface related structs

use super::{ffi, native, EGLContext, Error};
use crate::backend::graphics::SwapBuffersError;
use nix::libc::c_int;
use std::{
    cell::Cell,
    ops::{Deref, DerefMut},
    rc::{Rc, Weak},
};

/// EGL surface of a given EGL context for rendering
pub struct EGLSurface<N: native::NativeSurface> {
    context: Weak<ffi::egl::types::EGLContext>,
    display: Weak<ffi::egl::types::EGLDisplay>,
    native: N,
    surface: Cell<ffi::egl::types::EGLSurface>,
    config_id: ffi::egl::types::EGLConfig,
    surface_attributes: Vec<c_int>,
}

    native: Box<dyn EGLNativeSurface + Send + 'static>,
    pub(crate) surface: AtomicPtr<nix::libc::c_void>,
    config_id: ffi::egl::types::EGLConfig,
    pixel_format: PixelFormat,
    damage_impl: DamageSupport,
    logger: ::slog::Logger,
}

impl fmt::Debug for EGLSurface {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("EGLSurface")
}

impl<N: native::NativeSurface> EGLSurface<N> {
    pub(crate) fn new<B: native::Backend<Surface = N>, D: native::NativeDisplay<B>>(
        context: &EGLContext<B, D>,
        native: N,
    ) -> Result<EGLSurface<N>, Error> {
        let surface = unsafe {
            ffi::egl::CreateWindowSurface(
                *context.display,
                context.config_id,
                native.ptr(),
                context.surface_attributes.as_ptr(),
            )
        };

        if surface.is_null() {
            display: display.get_display_handle(),
        }

        Ok(EGLSurface {
            context: Rc::downgrade(&context.context),
            display: Rc::downgrade(&context.display),
            native,
            surface: Cell::new(surface),
            config_id: context.config_id,
            surface_attributes: context.surface_attributes.clone(),
        })
    }

        let mut age = 0;
        let ret = unsafe {
            ffi::egl::QuerySurface(
                **self.display,
                surface as *const _,
                ffi::egl::BUFFER_AGE_EXT as i32,
                &mut age as *mut _,
            )
        };
        if ret == ffi::egl::FALSE {
            slog::debug!(
                self.logger,
                "Failed to query buffer age value for surface {:?}: {}",
                self,
                EGLError::from_last_call().unwrap_err()
            );
            None
        } else {
            Some(age)
        }
    }

    /// Returns the size of the underlying back buffer
    pub fn get_size(&self) -> Option<Size<i32, Physical>> {
        let surface = self.surface.load(Ordering::SeqCst);
        let mut height = 0;
        let ret_h = unsafe {
            ffi::egl::QuerySurface(
                **self.display,
                surface as *const _,
                ffi::egl::HEIGHT as i32,
                &mut height as *mut _,
            )
        };
        let mut width = 0;
        Ok(())
    }

    /// Makes the OpenGL context the current context in the current thread.
    ///
    /// # Safety
    ///
    /// This function is marked unsafe, because the context cannot be made current
    /// on multiple threads.
    pub unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> {
        if let (Some(display), Some(context)) = (self.display.upgrade(), self.context.upgrade()) {
            let ret = ffi::egl::MakeCurrent(
                (*display) as *const _,
                self.surface.get() as *const _,
                self.surface.get() as *const _,
                (*context) as *const _,
            );

            if ret == 0 {
                match ffi::egl::GetError() as u32 {
                    ffi::egl::CONTEXT_LOST => Err(SwapBuffersError::ContextLost),
                    err => panic!("eglMakeCurrent failed (eglGetError returned 0x{:x})", err),
                }
            } else {
                Ok(())
            }
        } else {
            Err(SwapBuffersError::ContextLost)
        }
    }

    /// Returns true if the OpenGL surface is the current one in the thread.
    pub fn is_current(&self) -> bool {
        if self.context.upgrade().is_some() {
            unsafe {
                ffi::egl::GetCurrentSurface(ffi::egl::DRAW as _) == self.surface.get() as *const _
                    && ffi::egl::GetCurrentSurface(ffi::egl::READ as _) == self.surface.get() as *const _
            }
        } else {
            false
        }
    }
}

impl<N: native::NativeSurface> Drop for EGLSurface<N> {
        &self,
        damage: Option<&mut [Rectangle<i32, Physical>]>,
    ) -> ::std::result::Result<(), SwapBuffersError> {
        let surface = self.surface.load(Ordering::SeqCst);

        let result = if !surface.is_null() {
            self.native
                .swap_buffers(&self.display, surface, damage, self.damage_impl)
        } else {
            Err(SwapBuffersError::EGLSwapBuffers(EGLError::BadSurface))
        };

        // workaround for missing `PartialEq` impl
        let is_bad_surface = matches!(
            result,
            Err(SwapBuffersError::EGLSwapBuffers(EGLError::BadSurface))
        );

        if self.native.needs_recreation() || surface.is_null() || is_bad_surface {
            let previous = self
                .surface
                .compare_exchange(
                    surface,
                    self.native
                        .create(&self.display, self.config_id)
                        .map_err(SwapBuffersError::EGLCreateSurface)? as *mut _,
                    Ordering::SeqCst,
                    Ordering::SeqCst,
                )
                .expect("The surface pointer changed in between?");
            if previous == surface && !surface.is_null() {
                let _ = unsafe { ffi::egl::DestroySurface(**self.display, surface as *const _) };
            }

            // if a recreation is pending anyway, ignore page-flip errors.
            // lets see if we still fail after the next commit.
            result.map_err(|err| {
                debug!(self.logger, "Hiding page-flip error *before* recreation: {}", err);
                SwapBuffersError::EGLSwapBuffers(EGLError::BadSurface)
            })
        } else {
            result
        }
    }

    /// Returns true if the OpenGL surface is the current one in the thread.
    pub fn is_current(&self) -> bool {
        let surface = self.surface.load(Ordering::SeqCst);
        unsafe {
            ffi::egl::GetCurrentSurface(ffi::egl::DRAW as _) == surface as *const _
                && ffi::egl::GetCurrentSurface(ffi::egl::READ as _) == surface as *const _
        }
    }

    /// Returns the egl config for this context
    pub fn config_id(&self) -> ffi::egl::types::EGLConfig {
        self.config_id
    }

    /// Returns the pixel format of the main framebuffer of the context.
    pub fn pixel_format(&self) -> PixelFormat {
        self.pixel_format
    }

    /// Tries to resize the underlying native surface.
    ///
    /// 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.
    pub fn resize(&self, width: i32, height: i32, dx: i32, dy: i32) -> bool {
        self.native.resize(width, height, dx, dy)
    }

    /// Get a raw handle to the underlying surface
    ///
    /// *Note*: The surface might get dynamically recreated during swap-buffers
    /// causing the pointer to become invalid.
    ///
    /// The pointer will become invalid, when this struct is destroyed.
    pub fn get_surface_handle(&self) -> ffi::egl::types::EGLSurface {
        self.surface.load(Ordering::SeqCst)
    }
}

impl Drop for EGLSurface {
    fn drop(&mut self) {
        unsafe {
            ffi::egl::DestroySurface(**self.display, *self.surface.get_mut() as *const _);
        }
    }
}

<MSG> Refactor EGL backend


<DFF> @@ -1,21 +1,21 @@
 //! EGL surface related structs
 
-use super::{ffi, native, EGLContext, Error};
-use crate::backend::graphics::SwapBuffersError;
+use super::{ffi, native, Error};
+use crate::backend::graphics::{PixelFormat, SwapBuffersError};
 use nix::libc::c_int;
+use std::sync::{Arc, Weak};
 use std::{
     cell::Cell,
     ops::{Deref, DerefMut},
-    rc::{Rc, Weak},
 };
 
 /// EGL surface of a given EGL context for rendering
 pub struct EGLSurface<N: native::NativeSurface> {
-    context: Weak<ffi::egl::types::EGLContext>,
     display: Weak<ffi::egl::types::EGLDisplay>,
     native: N,
-    surface: Cell<ffi::egl::types::EGLSurface>,
+    pub(crate) surface: Cell<ffi::egl::types::EGLSurface>,
     config_id: ffi::egl::types::EGLConfig,
+    pixel_format: PixelFormat,
     surface_attributes: Vec<c_int>,
 }
 
@@ -33,17 +33,42 @@ impl<N: native::NativeSurface> DerefMut for EGLSurface<N> {
 }
 
 impl<N: native::NativeSurface> EGLSurface<N> {
-    pub(crate) fn new<B: native::Backend<Surface = N>, D: native::NativeDisplay<B>>(
-        context: &EGLContext<B, D>,
+    pub(crate) fn new<L>(
+        display: &Arc<ffi::egl::types::EGLDisplay>,
+        pixel_format: PixelFormat,
+        double_buffered: Option<bool>,
+        config: ffi::egl::types::EGLConfig,
         native: N,
-    ) -> Result<EGLSurface<N>, Error> {
+        log: L,
+    ) -> Result<EGLSurface<N>, Error>
+    where
+        L: Into<Option<::slog::Logger>>,
+    {
+        let log = crate::slog_or_stdlog(log.into()).new(o!("smithay_module" => "renderer_egl"));
+
+        let surface_attributes = {
+            let mut out: Vec<c_int> = Vec::with_capacity(3);
+
+            match double_buffered {
+                Some(true) => {
+                    trace!(log, "Setting RENDER_BUFFER to BACK_BUFFER");
+                    out.push(ffi::egl::RENDER_BUFFER as c_int);
+                    out.push(ffi::egl::BACK_BUFFER as c_int);
+                }
+                Some(false) => {
+                    trace!(log, "Setting RENDER_BUFFER to SINGLE_BUFFER");
+                    out.push(ffi::egl::RENDER_BUFFER as c_int);
+                    out.push(ffi::egl::SINGLE_BUFFER as c_int);
+                }
+                None => {}
+            }
+
+            out.push(ffi::egl::NONE as i32);
+            out
+        };
+
         let surface = unsafe {
-            ffi::egl::CreateWindowSurface(
-                *context.display,
-                context.config_id,
-                native.ptr(),
-                context.surface_attributes.as_ptr(),
-            )
+            ffi::egl::CreateWindowSurface(**display, config, native.ptr(), surface_attributes.as_ptr())
         };
 
         if surface.is_null() {
@@ -51,12 +76,12 @@ impl<N: native::NativeSurface> EGLSurface<N> {
         }
 
         Ok(EGLSurface {
-            context: Rc::downgrade(&context.context),
-            display: Rc::downgrade(&context.display),
+            display: Arc::downgrade(display),
             native,
             surface: Cell::new(surface),
-            config_id: context.config_id,
-            surface_attributes: context.surface_attributes.clone(),
+            config_id: config,
+            pixel_format,
+            surface_attributes,
         })
     }
 
@@ -98,45 +123,23 @@ impl<N: native::NativeSurface> EGLSurface<N> {
         Ok(())
     }
 
-    /// Makes the OpenGL context the current context in the current thread.
-    ///
-    /// # Safety
-    ///
-    /// This function is marked unsafe, because the context cannot be made current
-    /// on multiple threads.
-    pub unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> {
-        if let (Some(display), Some(context)) = (self.display.upgrade(), self.context.upgrade()) {
-            let ret = ffi::egl::MakeCurrent(
-                (*display) as *const _,
-                self.surface.get() as *const _,
-                self.surface.get() as *const _,
-                (*context) as *const _,
-            );
-
-            if ret == 0 {
-                match ffi::egl::GetError() as u32 {
-                    ffi::egl::CONTEXT_LOST => Err(SwapBuffersError::ContextLost),
-                    err => panic!("eglMakeCurrent failed (eglGetError returned 0x{:x})", err),
-                }
-            } else {
-                Ok(())
-            }
-        } else {
-            Err(SwapBuffersError::ContextLost)
-        }
-    }
-
     /// Returns true if the OpenGL surface is the current one in the thread.
     pub fn is_current(&self) -> bool {
-        if self.context.upgrade().is_some() {
-            unsafe {
-                ffi::egl::GetCurrentSurface(ffi::egl::DRAW as _) == self.surface.get() as *const _
-                    && ffi::egl::GetCurrentSurface(ffi::egl::READ as _) == self.surface.get() as *const _
-            }
-        } else {
-            false
+        unsafe {
+            ffi::egl::GetCurrentSurface(ffi::egl::DRAW as _) == self.surface.get() as *const _
+                && ffi::egl::GetCurrentSurface(ffi::egl::READ as _) == self.surface.get() as *const _
         }
     }
+
+    /// Returns the egl config for this context
+    pub fn get_config_id(&self) -> ffi::egl::types::EGLConfig {
+        self.config_id
+    }
+
+    /// Returns the pixel format of the main framebuffer of the context.
+    pub fn get_pixel_format(&self) -> PixelFormat {
+        self.pixel_format
+    }
 }
 
 impl<N: native::NativeSurface> Drop for EGLSurface<N> {

Refactor EGL backend