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

use super::{ffi, native, Error};
use crate::backend::egl::display::EGLDisplayHandle;
use crate::backend::graphics::{PixelFormat, SwapBuffersError};
use nix::libc::c_int;
use std::sync::Arc;
use std::{
use crate::backend::egl::{
    display::{DamageSupport, EGLDisplay, EGLDisplayHandle, PixelFormat},
    ffi,
    native::EGLNativeSurface,
    EGLError, SwapBuffersError,
};
use crate::utils::{Physical, Rectangle, Size};

use slog::{debug, o};

/// EGL surface of a given EGL context for rendering
pub struct EGLSurface {
    pub(crate) display: Arc<EGLDisplayHandle>,
    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")
            .field("display", &self.display)
            // native does not necessarily implement Debug
            .field("surface", &self.surface)
            .field("config_id", &self.config_id)
            .field("pixel_format", &self.pixel_format)
            .field("logger", &self.logger)
            .finish()
    }
        config: ffi::egl::types::EGLConfig,
        native: N,
        log: L,
    ) -> Result<EGLSurface<N>, Error>
    where
        L: Into<Option<::slog::Logger>>,
    {
    /// Create a new `EGLSurface`.
    ///
    /// Requires:
    /// - A EGLDisplay supported by the corresponding platform matching the surface type
    /// - A pixel format
    /// - A valid `EGLConfig` (see `EGLContext::config_id()`)
    /// - A native type backing the surface matching the used platform
    /// - An (optional) Logger
    pub fn new<N, L>(
        display: &EGLDisplay,
        pixel_format: PixelFormat,
        config: ffi::egl::types::EGLConfig,
        native: N,
        log: L,
    ) -> Result<EGLSurface, EGLError>
    where
        N: EGLNativeSurface + Send + 'static,
        L: Into<Option<::slog::Logger>>,
    {
        let log = crate::slog_or_fallback(log.into()).new(o!("smithay_module" => "renderer_egl"));
            out
        };

        let surface = unsafe {
            ffi::egl::CreateWindowSurface(**display, config, native.ptr(), surface_attributes.as_ptr())
        };

        if surface.is_null() {
            return Err(Error::SurfaceCreationFailed);
        }

        Ok(EGLSurface {
            display,
            surface: AtomicPtr::new(surface as *mut _),
            config_id: config,
            pixel_format,
            damage_impl: display.supports_damage_impl(),
            logger: log,
        })
    }

    /// Swaps buffers at the end of a frame.
    pub fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> {
        let surface = self.surface.get();

        if !surface.is_null() {
            let ret = unsafe { ffi::egl::SwapBuffers(**self.display, surface as *const _) };

            if ret == 0 {
                match unsafe { ffi::egl::GetError() } as u32 {
                    ffi::egl::CONTEXT_LOST => return Err(SwapBuffersError::ContextLost),
                    err => return Err(SwapBuffersError::Unknown(err)),
                };
            } else {
                self.native.swap_buffers()?;
            }
        };

        if self.native.needs_recreation() || surface.is_null() {
            self.native.recreate();
            self.surface.set(unsafe {
                ffi::egl::CreateWindowSurface(
                    **self.display,
                    self.config_id,
                    self.native.ptr(),
                    self.surface_attributes.as_ptr(),
                )
            });
        }

        Ok(())
    }

    /// Returns true if the OpenGL surface is the current one in the thread.
                surface as *const _,
                ffi::egl::WIDTH as i32,
                &mut width as *mut _,
            )
        };
        if ret_h == ffi::egl::FALSE || ret_w == ffi::egl::FALSE {
            slog::debug!(
                self.logger,
                "Failed to query size value for surface {:?}: {}",
                self,
                EGLError::from_last_call().unwrap_err()
            );
            None
        } else {
            Some(Size::from((width, height)))
        }
    }

    /// Swaps buffers at the end of a frame.
    pub fn swap_buffers(
        &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> Merge pull request #206 from Smithay/fix/drm_connector

Fix drm mode and connector handling
<DFF> @@ -1,8 +1,8 @@
 //! EGL surface related structs
 
-use super::{ffi, native, Error};
+use super::{ffi, native, wrap_egl_call, EGLError, SwapBuffersError};
 use crate::backend::egl::display::EGLDisplayHandle;
-use crate::backend::graphics::{PixelFormat, SwapBuffersError};
+use crate::backend::graphics::PixelFormat;
 use nix::libc::c_int;
 use std::sync::Arc;
 use std::{
@@ -41,7 +41,7 @@ impl<N: native::NativeSurface> EGLSurface<N> {
         config: ffi::egl::types::EGLConfig,
         native: N,
         log: L,
-    ) -> Result<EGLSurface<N>, Error>
+    ) -> Result<EGLSurface<N>, EGLError>
     where
         L: Into<Option<::slog::Logger>>,
     {
@@ -68,13 +68,9 @@ impl<N: native::NativeSurface> EGLSurface<N> {
             out
         };
 
-        let surface = unsafe {
+        let surface = wrap_egl_call(|| unsafe {
             ffi::egl::CreateWindowSurface(**display, config, native.ptr(), surface_attributes.as_ptr())
-        };
-
-        if surface.is_null() {
-            return Err(Error::SurfaceCreationFailed);
-        }
+        })?;
 
         Ok(EGLSurface {
             display,
@@ -87,35 +83,43 @@ impl<N: native::NativeSurface> EGLSurface<N> {
     }
 
     /// Swaps buffers at the end of a frame.
-    pub fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> {
+    pub fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError<N::Error>> {
         let surface = self.surface.get();
 
-        if !surface.is_null() {
-            let ret = unsafe { ffi::egl::SwapBuffers(**self.display, surface as *const _) };
+        let result = if !surface.is_null() {
+            wrap_egl_call(|| unsafe { ffi::egl::SwapBuffers(**self.display, surface as *const _) })
+                .map_err(SwapBuffersError::EGLSwapBuffers)
+                .and_then(|_| self.native.swap_buffers().map_err(SwapBuffersError::Underlying))
+        } else {
+            Ok(())
+        };
 
-            if ret == 0 {
-                match unsafe { ffi::egl::GetError() } as u32 {
-                    ffi::egl::CONTEXT_LOST => return Err(SwapBuffersError::ContextLost),
-                    err => return Err(SwapBuffersError::Unknown(err)),
-                };
-            } else {
-                self.native.swap_buffers()?;
-            }
+        // workaround for missing `PartialEq` impl
+        let is_bad_surface = if let Err(SwapBuffersError::EGLSwapBuffers(EGLError::BadSurface)) = result {
+            true
+        } else {
+            false
         };
 
-        if self.native.needs_recreation() || surface.is_null() {
-            self.native.recreate();
+        if self.native.needs_recreation() || surface.is_null() || is_bad_surface {
+            self.native.recreate().map_err(SwapBuffersError::Underlying)?;
+            if !surface.is_null() {
+                let _ = unsafe { ffi::egl::DestroySurface(**self.display, surface as *const _) };
+            }
             self.surface.set(unsafe {
-                ffi::egl::CreateWindowSurface(
-                    **self.display,
-                    self.config_id,
-                    self.native.ptr(),
-                    self.surface_attributes.as_ptr(),
-                )
+                wrap_egl_call(|| {
+                    ffi::egl::CreateWindowSurface(
+                        **self.display,
+                        self.config_id,
+                        self.native.ptr(),
+                        self.surface_attributes.as_ptr(),
+                    )
+                })
+                .map_err(SwapBuffersError::EGLCreateWindowSurface)?
             });
         }
 
-        Ok(())
+        result
     }
 
     /// Returns true if the OpenGL surface is the current one in the thread.

Merge pull request #206 from Smithay/fix/drm_connector