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

use super::{error::*, ffi, native, EGLContext};
use backend::graphics::SwapBuffersError;
use std::{
    ops::{Deref, DerefMut},
    rc::{Rc, Weak},
};
    ffi,
    native::EGLNativeSurface,
    EGLError, SwapBuffersError,
    context: Weak<ffi::egl::types::EGLContext>,
    display: Weak<ffi::egl::types::EGLDisplay>,
    native: N,
    surface: ffi::egl::types::EGLSurface,
}

impl<N: native::NativeSurface> Deref for EGLSurface<N> {
    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()
    }
}

// safe because EGLConfig can be moved between threads
// and the other types are thread-safe
unsafe impl Send for EGLSurface {}

impl EGLSurface {
    /// 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()`)
            context: Rc::downgrade(&context.context),
            display: Rc::downgrade(&context.display),
            native,
            surface,
        })
    }

    /// Swaps buffers at the end of a frame.
    pub fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> {
        self.native.swap_buffers(|| {
            if let Some(display) = self.display.upgrade() {
                let ret = unsafe { ffi::egl::SwapBuffers((*display) as *const _, self.surface as *const _) };

                if ret == 0 {
                    match unsafe { ffi::egl::GetError() } as u32 {
                        ffi::egl::CONTEXT_LOST => Err(SwapBuffersError::ContextLost),
                        err => Err(SwapBuffersError::Unknown(err)),
                    }
                } else {
                    Ok(())
                }
            } else {
                Err(SwapBuffersError::ContextLost)
            }
        })
    }

    /// Makes the OpenGL context the current context in the current thread.
            );
            None
        } else {
            Some(age)
        }
    }
        if let (Some(display), Some(context)) = (self.display.upgrade(), self.context.upgrade()) {
            let ret = ffi::egl::MakeCurrent(
                (*display) as *const _,
                self.surface as *const _,
                self.surface as *const _,
                (*context) as *const _,
            );

                surface as *const _,
                ffi::egl::HEIGHT as i32,
                &mut height as *mut _,
            )
        };
        let mut width = 0;
        let ret_w = unsafe {
            ffi::egl::QuerySurface(
                **self.display,
                surface as *const _,
                ffi::egl::WIDTH as i32,
                &mut width as *mut _,
            )
        };
    pub fn is_current(&self) -> bool {
        if self.context.upgrade().is_some() {
            unsafe {
                ffi::egl::GetCurrentSurface(ffi::egl::DRAW as _) == self.surface as *const _
                    && ffi::egl::GetCurrentSurface(ffi::egl::READ as _) == self.surface as *const _
            }
        } else {
            false
        } else {
            Some(Size::from((width, height)))
        }
    }

    fn drop(&mut self) {
        if let Some(display) = self.display.upgrade() {
            unsafe {
                ffi::egl::DestroySurface((*display) as *const _, self.surface as *const _);
            }
        }
    }
        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> gbm: fix EGLSurface recreation

<DFF> @@ -2,7 +2,9 @@
 
 use super::{error::*, ffi, native, EGLContext};
 use backend::graphics::SwapBuffersError;
+use nix::libc::c_int;
 use std::{
+    cell::Cell,
     ops::{Deref, DerefMut},
     rc::{Rc, Weak},
 };
@@ -12,7 +14,9 @@ pub struct EGLSurface<N: native::NativeSurface> {
     context: Weak<ffi::egl::types::EGLContext>,
     display: Weak<ffi::egl::types::EGLDisplay>,
     native: N,
-    surface: ffi::egl::types::EGLSurface,
+    surface: Cell<ffi::egl::types::EGLSurface>,
+    config_id: ffi::egl::types::EGLConfig,
+    surface_attributes: Vec<c_int>,
 }
 
 impl<N: native::NativeSurface> Deref for EGLSurface<N> {
@@ -50,28 +54,49 @@ impl<N: native::NativeSurface> EGLSurface<N> {
             context: Rc::downgrade(&context.context),
             display: Rc::downgrade(&context.display),
             native,
-            surface,
+            surface: Cell::new(surface),
+            config_id: context.config_id,
+            surface_attributes: context.surface_attributes.clone(),
         })
     }
 
     /// Swaps buffers at the end of a frame.
     pub fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> {
-        self.native.swap_buffers(|| {
+        let surface = self.surface.get();
+
+        if !surface.is_null() {
             if let Some(display) = self.display.upgrade() {
-                let ret = unsafe { ffi::egl::SwapBuffers((*display) as *const _, self.surface as *const _) };
+                let ret = unsafe { ffi::egl::SwapBuffers((*display) as *const _, surface as *const _) };
 
                 if ret == 0 {
                     match unsafe { ffi::egl::GetError() } as u32 {
-                        ffi::egl::CONTEXT_LOST => Err(SwapBuffersError::ContextLost),
-                        err => Err(SwapBuffersError::Unknown(err)),
-                    }
+                        ffi::egl::CONTEXT_LOST => return Err(SwapBuffersError::ContextLost),
+                        err => return Err(SwapBuffersError::Unknown(err)),
+                    };
                 } else {
-                    Ok(())
+                    return Ok(());
                 }
             } else {
-                Err(SwapBuffersError::ContextLost)
+                return Err(SwapBuffersError::ContextLost);
             }
-        })
+            self.native.swap_buffers()?;
+        };
+
+        if self.native.needs_recreation() || surface.is_null() {
+            if let Some(display) = self.display.upgrade() {
+                self.native.recreate();
+                self.surface.set(unsafe {
+                    ffi::egl::CreateWindowSurface(
+                        *display,
+                        self.config_id,
+                        self.native.ptr(),
+                        self.surface_attributes.as_ptr(),
+                    )
+                });
+            }
+        }
+
+        Ok(())
     }
 
     /// Makes the OpenGL context the current context in the current thread.
@@ -84,8 +109,8 @@ impl<N: native::NativeSurface> EGLSurface<N> {
         if let (Some(display), Some(context)) = (self.display.upgrade(), self.context.upgrade()) {
             let ret = ffi::egl::MakeCurrent(
                 (*display) as *const _,
-                self.surface as *const _,
-                self.surface as *const _,
+                self.surface.get() as *const _,
+                self.surface.get() as *const _,
                 (*context) as *const _,
             );
 
@@ -106,8 +131,8 @@ impl<N: native::NativeSurface> EGLSurface<N> {
     pub fn is_current(&self) -> bool {
         if self.context.upgrade().is_some() {
             unsafe {
-                ffi::egl::GetCurrentSurface(ffi::egl::DRAW as _) == self.surface as *const _
-                    && ffi::egl::GetCurrentSurface(ffi::egl::READ as _) == self.surface as *const _
+                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
@@ -119,7 +144,7 @@ impl<N: native::NativeSurface> Drop for EGLSurface<N> {
     fn drop(&mut self) {
         if let Some(display) = self.display.upgrade() {
             unsafe {
-                ffi::egl::DestroySurface((*display) as *const _, self.surface as *const _);
+                ffi::egl::DestroySurface((*display) as *const _, self.surface.get() as *const _);
             }
         }
     }

gbm: fix EGLSurface recreation