<NME> gbm.rs
<BEF> use std::collections::HashSet;
use std::os::unix::io::AsRawFd;
use std::sync::Arc;

use drm::buffer::PlanarBuffer;
use drm::control::{connector, crtc, framebuffer, plane, Device, Mode};
use gbm::{BufferObject, Device as GbmDevice};

use crate::backend::allocator::{
    dmabuf::{AsDmabuf, Dmabuf},
    gbm::GbmConvertError,
    Format, Fourcc, Modifier, Slot, Swapchain,
};
use crate::backend::drm::{device::DevPath, surface::DrmSurfaceInternal, DrmError, DrmSurface};
use crate::backend::SwapBuffersError;

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

/// Simplified abstraction of a swapchain for gbm-buffers displayed on a [`DrmSurface`].
#[derive(Debug)]
pub struct GbmBufferedSurface<D: AsRawFd + 'static> {
    current_fb: Slot<BufferObject<()>>,
    pending_fb: Option<Slot<BufferObject<()>>>,
    queued_fb: Option<Slot<BufferObject<()>>>,
    next_fb: Option<Slot<BufferObject<()>>>,
    swapchain: Swapchain<GbmDevice<D>, BufferObject<()>>,
    drm: Arc<DrmSurface<D>>,
}

impl<D> GbmBufferedSurface<D>
where
    D: AsRawFd + 'static,
{
    /// Create a new `GbmBufferedSurface` from a given compatible combination
// we need to have a more sophisticated picker.
// (Or maybe just select A/XRGB2101010, if available, we will see.)
const SUPPORTED_FORMATS: &[Fourcc] = &[Fourcc::Argb8888, Fourcc::Xrgb8888];

impl<A, D> GbmBufferedSurface<A, D>
    #[allow(clippy::type_complexity)]
    pub fn new<L>(
        drm: DrmSurface<D>,
        allocator: GbmDevice<D>,
        mut renderer_formats: HashSet<Format>,
        log: L,
    ) -> Result<GbmBufferedSurface<D>, Error>
    where
        L: Into<Option<::slog::Logger>>,
    {
    /// buffers of a supported format for rendering.
    pub fn new<L>(
        drm: DrmSurface<D>,
        mut allocator: A,
        renderer_formats: HashSet<Format>,
        log: L,
    ) -> Result<GbmBufferedSurface<A, D>, Error<A::Error>>
    where
        L: Into<Option<::slog::Logger>>,
    {
        let mut error = None;
        let drm = Arc::new(drm);

        let log = crate::slog_or_fallback(log).new(o!("backend" => "drm_render"));
        for format in SUPPORTED_FORMATS {
            debug!(log, "Testing color format: {}", format);
            match Self::new_internal(
                drm.clone(),
                allocator,
                renderer_formats.clone(),
                *format,
                log.clone(),
            ) {
                Ok((current_fb, swapchain)) => {
                    return Ok(GbmBufferedSurface {
                        current_fb,
                        pending_fb: None,
                        queued_fb: None,
                        next_fb: None,
                        swapchain,
                        drm,
                    })
                }
                Err((alloc, err)) => {
                    warn!(log, "Preferred format {} not available: {:?}", format, err);
                    allocator = alloc;
                    error = Some(err);
                }
            }
        }
        Err(error.unwrap())
    }

    #[allow(clippy::type_complexity)]
    fn new_internal(
        drm: Arc<DrmSurface<D>>,
        allocator: A,
        mut renderer_formats: HashSet<Format>,
        code: Fourcc,
        logger: slog::Logger,
    ) -> Result<(Slot<BufferObject<()>>, Swapchain<A, BufferObject<()>>), (A, Error<A::Error>)> {
        // select a format
        let mut plane_formats = match drm.supported_formats(drm.plane()) {
            Ok(formats) => formats.iter().cloned().collect::<HashSet<_>>(),
            Err(err) => return Err((allocator, err.into())),
        };

        if !plane_formats.iter().any(|fmt| fmt.code == code) {
            return Err((allocator, Error::NoSupportedPlaneFormat));
        }
        plane_formats.retain(|fmt| fmt.code == code);
        renderer_formats.retain(|fmt| fmt.code == code);

        trace!(logger, "Plane formats: {:?}", plane_formats);
        trace!(logger, "Renderer formats: {:?}", renderer_formats);
        debug!(
            logger,
            "Remaining intersected formats: {:?}",
            plane_formats
                .intersection(&renderer_formats)
                .collect::<HashSet<_>>()

        let mode = drm.pending_mode();

        let mut swapchain: Swapchain<GbmDevice<D>, BufferObject<()>> = Swapchain::new(
            allocator,
            mode.size().0 as u32,
            mode.size().1 as u32,

        let formats = {
        );

        // Test format
        let buffer = swapchain.acquire()?.unwrap();
        let format = Format {
            code,
            modifier: buffer.modifier().unwrap(), // no guarantee
                && renderer_formats.iter().any(|x| x.modifier == Modifier::Linear))
                || (renderer_formats.len() == 1
                    && renderer_formats.iter().next().unwrap().modifier == Modifier::Invalid
                    && plane_formats.iter().all(|x| x.modifier != Modifier::Invalid)
                    && plane_formats.iter().any(|x| x.modifier == Modifier::Linear))
            {
                vec![Format {
                    code,
                    modifier: Modifier::Invalid,
                }]
            } else {
                plane_formats
                    .intersection(&renderer_formats)
                    .cloned()
                    .collect::<Vec<_>>()
            }
        };
        debug!(logger, "Testing Formats: {:?}", formats);

        let modifiers = formats.iter().map(|x| x.modifier).collect::<Vec<_>>();
        let mode = drm.pending_mode();

        let mut swapchain: Swapchain<A, BufferObject<()>> = Swapchain::new(
            allocator,
            mode.size().0 as u32,
            mode.size().1 as u32,
            code,
            modifiers,
        );

        // Test format
        let buffer = match swapchain.acquire() {
            Ok(buffer) => buffer.unwrap(),
            Err(err) => return Err((swapchain.allocator, Error::GbmError(err))),
    ///
    /// *Note*: This function can be called multiple times and
    /// will return the same buffer until it is queued (see [`GbmBufferedSurface::queue_buffer`]).
    pub fn next_buffer(&mut self) -> Result<(Dmabuf, u8), Error> {
        if self.next_fb.is_none() {
            let slot = self.swapchain.acquire()?.ok_or(Error::NoFreeSlotsError)?;

            let maybe_buffer = slot.userdata().get::<Dmabuf>().cloned();
            if maybe_buffer.is_none() {
        let dmabuf = match buffer.export() {
            Ok(dmabuf) => dmabuf,
            Err(err) => return Err((swapchain.allocator, err.into())),
        };
        let handle = fb.fb;
        buffer.userdata().insert_if_missing(|| dmabuf);
        buffer.userdata().insert_if_missing(|| fb);

        match drm.test_buffer(handle, &mode, true) {
            Ok(_) => {
                debug!(logger, "Choosen format: {:?}", format);
                Ok((buffer, swapchain))
            }
            Err(err) => {
                warn!(
                    logger,
                    "Mode-setting failed with automatically selected buffer format {:?}: {}", format, err
    /// *Note*: This function needs to be followed up with [`GbmBufferedSurface::frame_submitted`]
    /// when a vblank event is received, that denotes successful scanout of the buffer.
    /// Otherwise the underlying swapchain will eventually run out of buffers.
    pub fn queue_buffer(&mut self) -> Result<(), Error> {
        self.queued_fb = self.next_fb.take();
        if self.pending_fb.is_none() && self.queued_fb.is_some() {
            self.submit()?;
    ///
    /// *Note*: This function can be called multiple times and
    /// will return the same buffer until it is queued (see [`GbmBufferedSurface::queue_buffer`]).
    pub fn next_buffer(&mut self) -> Result<(Dmabuf, u8), Error<A::Error>> {
        if self.next_fb.is_none() {
            let slot = self
    /// *Note*: Needs to be called, after the vblank event of the matching [`DrmDevice`](super::super::DrmDevice)
    /// was received after calling [`GbmBufferedSurface::queue_buffer`] on this surface.
    /// Otherwise the underlying swapchain will run out of buffers eventually.
    pub fn frame_submitted(&mut self) -> Result<(), Error> {
        if let Some(mut pending) = self.pending_fb.take() {
            std::mem::swap(&mut pending, &mut self.current_fb);
            if self.queued_fb.is_some() {
                let dmabuf = slot.export().map_err(Error::AsDmabufError)?;
                let fb_handle = attach_framebuffer(&self.drm, &*slot)?;

                let userdata = slot.userdata();
        Ok(())
    }

    fn submit(&mut self) -> Result<(), Error> {
        // yes it does not look like it, but both of these lines should be safe in all cases.
        let slot = self.queued_fb.take().unwrap();
        let fb = slot.userdata().get::<FbHandle<D>>().unwrap().fb;
        let slot = self.next_fb.as_ref().unwrap();
        Ok((slot.userdata().get::<Dmabuf>().unwrap().clone(), slot.age()))
    }

    /// Queues the current buffer for rendering.
    ///
    /// *Note*: This function needs to be followed up with [`GbmBufferedSurface::frame_submitted`]
    /// when a vblank event is received, that denotes successful scanout of the buffer.
    /// Otherwise the underlying swapchain will eventually run out of buffers.
    pub fn queue_buffer(&mut self) -> Result<(), Error<A::Error>> {
        self.queued_fb = self.next_fb.take();
        if self.pending_fb.is_none() && self.queued_fb.is_some() {
            self.submit()?;
        }
        Ok(())
    }

    /// Marks the current frame as submitted.
    ///
    /// *Note*: Needs to be called, after the vblank event of the matching [`DrmDevice`](super::super::DrmDevice)
    /// was received after calling [`GbmBufferedSurface::queue_buffer`] on this surface.
    /// Otherwise the underlying swapchain will run out of buffers eventually.
    pub fn frame_submitted(&mut self) -> Result<(), Error<A::Error>> {
        if let Some(mut pending) = self.pending_fb.take() {
            std::mem::swap(&mut pending, &mut self.current_fb);
            if self.queued_fb.is_some() {
                self.submit()?;
            }
        }

        Ok(())
    }

    fn submit(&mut self) -> Result<(), Error<A::Error>> {
        // yes it does not look like it, but both of these lines should be safe in all cases.
        let slot = self.queued_fb.take().unwrap();
        let fb = slot.userdata().get::<FbHandle<D>>().unwrap().fb;

        let flip = if self.drm.commit_pending() {
            self.drm.commit([(fb, self.drm.plane())].iter(), true)
        } else {
            self.drm.page_flip([(fb, self.drm.plane())].iter(), true)
        };
        if flip.is_ok() {
            self.swapchain.submitted(&slot);
            self.pending_fb = Some(slot);
        }
        flip.map_err(Error::DrmError)
    /// (e.g. no suitable [`encoder`](drm::control::encoder) may be found)
    /// or is not compatible with the currently pending
    /// [`Mode`](drm::control::Mode).
    pub fn add_connector(&self, connector: connector::Handle) -> Result<(), Error> {
        self.drm.add_connector(connector).map_err(Error::DrmError)
    }

    /// Tries to mark a [`connector`](drm::control::connector)
    /// for removal on the next commit.    
    pub fn remove_connector(&self, connector: connector::Handle) -> Result<(), Error> {
        self.drm.remove_connector(connector).map_err(Error::DrmError)
    }

    pub fn plane(&self) -> plane::Handle {
        self.drm.plane()
    }
    /// (e.g. no suitable [`encoder`](drm::control::encoder) may be found)
    /// or is not compatible with the currently pending
    /// [`Mode`](drm::control::Mode).    
    pub fn set_connectors(&self, connectors: &[connector::Handle]) -> Result<(), Error> {
        self.drm.set_connectors(connectors).map_err(Error::DrmError)
    }

    /// used for the next frame queued via [`queue_buffer`](GbmBufferedSurface::queue_buffer).
    pub fn pending_connectors(&self) -> impl IntoIterator<Item = connector::Handle> {
        self.drm.pending_connectors()
    }

    /// Tries to add a new [`connector`](drm::control::connector)
    /// to be used after the next commit.
    ///
    /// **Warning**: You need to make sure, that the connector is not used with another surface
    /// or was properly removed via `remove_connector` + `commit` before adding it to another surface.
    /// Behavior if failing to do so is undefined, but might result in rendering errors or the connector
    /// getting removed from the other surface without updating it's internal state.
    ///
    /// Fails if the `connector` is not compatible with the underlying [`crtc`](drm::control::crtc)
    /// (e.g. no suitable [`encoder`](drm::control::encoder) may be found)
    /// Fails if the mode is not compatible with the underlying
    /// [`crtc`](drm::control::crtc) or any of the
    /// pending [`connector`](drm::control::connector)s.
    pub fn use_mode(&mut self, mode: Mode) -> Result<(), Error> {
        self.drm.use_mode(mode).map_err(Error::DrmError)?;
        let (w, h) = mode.size();
        self.swapchain.resize(w as _, h as _);
    /// for removal on the next commit.    
    pub fn remove_connector(&self, connector: connector::Handle) -> Result<(), Error<A::Error>> {
        self.drm.remove_connector(connector).map_err(Error::DrmError)
    }

    /// Tries to replace the current connector set with the newly provided one on the next commit.
    ///
    /// Fails if one new `connector` is not compatible with the underlying [`crtc`](drm::control::crtc)
    /// (e.g. no suitable [`encoder`](drm::control::encoder) may be found)
    /// or is not compatible with the currently pending
    /// [`Mode`](drm::control::Mode).    
    pub fn set_connectors(&self, connectors: &[connector::Handle]) -> Result<(), Error<A::Error>> {
        self.drm.set_connectors(connectors).map_err(Error::DrmError)
    }
}

fn attach_framebuffer<A>(drm: &Arc<DrmSurface<A>>, bo: &BufferObject<()>) -> Result<FbHandle<A>, Error>
where
    A: AsRawFd + 'static,
{
    let modifier = match bo.modifier().unwrap() {
        Modifier::Invalid => None,
    pub fn pending_mode(&self) -> Mode {
        self.drm.pending_mode()
    }

    /// Tries to set a new [`Mode`](drm::control::Mode)
    /// to be used after the next commit.
    ///
    /// Fails if the mode is not compatible with the underlying
    /// [`crtc`](drm::control::crtc) or any of the
    /// pending [`connector`](drm::control::connector)s.
    pub fn use_mode(&mut self, mode: Mode) -> Result<(), Error<A::Error>> {
        self.drm.use_mode(mode).map_err(Error::DrmError)?;
        let (w, h) = mode.size();
        self.swapchain.resize(w as _, h as _);
        Ok(())
    }
}

#[derive(Debug)]
struct FbHandle<D: AsRawFd + 'static> {
    drm: Arc<DrmSurface<D>>,
    fb: framebuffer::Handle,
}

impl<A: AsRawFd + 'static> Drop for FbHandle<A> {
    fn drop(&mut self) {
        let _ = self.drm.destroy_framebuffer(self.fb);
    }
}

fn attach_framebuffer<E, D>(drm: &Arc<DrmSurface<D>>, bo: &BufferObject<()>) -> Result<FbHandle<D>, Error<E>>
where
    E: std::error::Error + Send + Sync,
    D: AsRawFd + 'static,
{
    let modifier = match bo.modifier().unwrap() {
        Modifier::Invalid => None,
        x => Some(x),
    };

    let logger = match &*(*drm).internal {
        DrmSurfaceInternal::Atomic(surf) => surf.logger.clone(),

/// Errors thrown by a [`GbmBufferedSurface`]
#[derive(Debug, thiserror::Error)]
pub enum Error {
    /// No supported pixel format for the given plane could be determined
    #[error("No supported plane buffer format found")]
    NoSupportedPlaneFormat,
            if num > 1 { modifier } else { None },
            if num > 2 { modifier } else { None },
            if num > 3 { modifier } else { None },
        ];
        drm.add_planar_framebuffer(bo, &modifiers, drm_ffi::DRM_MODE_FB_MODIFIERS)
    } else {
        drm.add_planar_framebuffer(bo, &[None, None, None, None], 0)
    } {
        Ok(fb) => fb,
        Err(source) => {
            // We only support this as a fallback of last resort like xf86-video-modesetting does.
            if bo.plane_count().unwrap() > 1 {
                return Err(Error::DrmError(DrmError::Access {
                    errmsg: "Failed to add framebuffer",
    DrmError(#[from] DrmError),
    /// Error importing the rendered buffer to libgbm for scan-out
    #[error("The underlying gbm device encounted an error: {0}")]
    GbmError(#[from] std::io::Error),
    /// Error exporting as Dmabuf
    #[error("The allocated buffer could not be exported as a dmabuf: {0}")]
    AsDmabufError(#[from] GbmConvertError),
}

impl From<Error> for SwapBuffersError {
    fn from(err: Error) -> SwapBuffersError {
        match err {
            x @ Error::NoSupportedPlaneFormat
            | x @ Error::NoSupportedRendererFormat
                })?;
            drm.add_framebuffer(bo, depth as u32, bpp as u32)
                .map_err(|source| DrmError::Access {
                    errmsg: "Failed to add framebuffer",
                    dev: drm.dev_path(),
                    source,
                })?
        }
    };
    Ok(FbHandle { drm: drm.clone(), fb })
}

/// Errors thrown by a [`GbmBufferedSurface`]
#[derive(Debug, thiserror::Error)]
pub enum Error<E: std::error::Error + Send + Sync + 'static> {
    /// No supported pixel format for the given plane could be determined
    #[error("No supported plane buffer format found")]
    NoSupportedPlaneFormat,
    /// No supported pixel format for the given renderer could be determined
    #[error("No supported renderer buffer format found")]
    NoSupportedRendererFormat,
    /// The supported pixel formats of the renderer and plane are incompatible
    #[error("Supported plane and renderer buffer formats are incompatible")]
    FormatsNotCompatible,
    /// The swapchain is exhausted, you need to call `frame_submitted`
    #[error("Failed to allocate a new buffer")]
    NoFreeSlotsError,
    /// Failed to renderer using the given renderer
    #[error("Failed to render test frame")]
    InitialRenderingError,
    /// Error accessing the drm device
    #[error("The underlying drm surface encounted an error: {0}")]
    DrmError(#[from] DrmError),
    /// Error importing the rendered buffer to libgbm for scan-out
    #[error("The underlying gbm device encounted an error: {0}")]
    GbmError(#[source] E),
    /// Error exporting as Dmabuf
    #[error("The allocated buffer could not be exported as a dmabuf: {0}")]
    AsDmabufError(#[from] GbmConvertError),
}

impl<E: std::error::Error + Send + Sync + 'static> From<Error<E>> for SwapBuffersError {
    fn from(err: Error<E>) -> SwapBuffersError {
        match err {
            x @ Error::NoSupportedPlaneFormat
            | x @ Error::NoSupportedRendererFormat
            | x @ Error::FormatsNotCompatible
            | x @ Error::InitialRenderingError => SwapBuffersError::ContextLost(Box::new(x)),
            x @ Error::NoFreeSlotsError => SwapBuffersError::TemporaryFailure(Box::new(x)),
            Error::DrmError(err) => err.into(),
            Error::GbmError(err) => SwapBuffersError::ContextLost(Box::new(err)),
            Error::AsDmabufError(err) => SwapBuffersError::ContextLost(Box::new(err)),
        }
    }
}

<MSG> gbm: Allow usage of other allocators for GbmBufferedSurface

<DFF> @@ -4,12 +4,12 @@ use std::sync::Arc;
 
 use drm::buffer::PlanarBuffer;
 use drm::control::{connector, crtc, framebuffer, plane, Device, Mode};
-use gbm::{BufferObject, Device as GbmDevice};
+use gbm::BufferObject;
 
 use crate::backend::allocator::{
     dmabuf::{AsDmabuf, Dmabuf},
     gbm::GbmConvertError,
-    Format, Fourcc, Modifier, Slot, Swapchain,
+    Allocator, Format, Fourcc, Modifier, Slot, Swapchain,
 };
 use crate::backend::drm::{device::DevPath, surface::DrmSurfaceInternal, DrmError, DrmSurface};
 use crate::backend::SwapBuffersError;
@@ -18,17 +18,19 @@ use slog::{debug, error, o, trace, warn};
 
 /// Simplified abstraction of a swapchain for gbm-buffers displayed on a [`DrmSurface`].
 #[derive(Debug)]
-pub struct GbmBufferedSurface<D: AsRawFd + 'static> {
+pub struct GbmBufferedSurface<A: Allocator<BufferObject<()>> + 'static, D: AsRawFd + 'static> {
     current_fb: Slot<BufferObject<()>>,
     pending_fb: Option<Slot<BufferObject<()>>>,
     queued_fb: Option<Slot<BufferObject<()>>>,
     next_fb: Option<Slot<BufferObject<()>>>,
-    swapchain: Swapchain<GbmDevice<D>, BufferObject<()>>,
+    swapchain: Swapchain<A, BufferObject<()>>,
     drm: Arc<DrmSurface<D>>,
 }
 
-impl<D> GbmBufferedSurface<D>
+impl<A, D> GbmBufferedSurface<A, D>
 where
+    A: Allocator<BufferObject<()>>,
+    A::Error: std::error::Error + Send + Sync,
     D: AsRawFd + 'static,
 {
     /// Create a new `GbmBufferedSurface` from a given compatible combination
@@ -40,10 +42,10 @@ where
     #[allow(clippy::type_complexity)]
     pub fn new<L>(
         drm: DrmSurface<D>,
-        allocator: GbmDevice<D>,
+        allocator: A,
         mut renderer_formats: HashSet<Format>,
         log: L,
-    ) -> Result<GbmBufferedSurface<D>, Error>
+    ) -> Result<GbmBufferedSurface<A, D>, Error<A::Error>>
     where
         L: Into<Option<::slog::Logger>>,
     {
@@ -121,7 +123,7 @@ where
 
         let mode = drm.pending_mode();
 
-        let mut swapchain: Swapchain<GbmDevice<D>, BufferObject<()>> = Swapchain::new(
+        let mut swapchain: Swapchain<A, BufferObject<()>> = Swapchain::new(
             allocator,
             mode.size().0 as u32,
             mode.size().1 as u32,
@@ -130,7 +132,7 @@ where
         );
 
         // Test format
-        let buffer = swapchain.acquire()?.unwrap();
+        let buffer = swapchain.acquire().map_err(Error::GbmError)?.unwrap();
         let format = Format {
             code,
             modifier: buffer.modifier().unwrap(), // no guarantee
@@ -171,9 +173,13 @@ where
     ///
     /// *Note*: This function can be called multiple times and
     /// will return the same buffer until it is queued (see [`GbmBufferedSurface::queue_buffer`]).
-    pub fn next_buffer(&mut self) -> Result<(Dmabuf, u8), Error> {
+    pub fn next_buffer(&mut self) -> Result<(Dmabuf, u8), Error<A::Error>> {
         if self.next_fb.is_none() {
-            let slot = self.swapchain.acquire()?.ok_or(Error::NoFreeSlotsError)?;
+            let slot = self
+                .swapchain
+                .acquire()
+                .map_err(Error::GbmError)?
+                .ok_or(Error::NoFreeSlotsError)?;
 
             let maybe_buffer = slot.userdata().get::<Dmabuf>().cloned();
             if maybe_buffer.is_none() {
@@ -197,7 +203,7 @@ where
     /// *Note*: This function needs to be followed up with [`GbmBufferedSurface::frame_submitted`]
     /// when a vblank event is received, that denotes successful scanout of the buffer.
     /// Otherwise the underlying swapchain will eventually run out of buffers.
-    pub fn queue_buffer(&mut self) -> Result<(), Error> {
+    pub fn queue_buffer(&mut self) -> Result<(), Error<A::Error>> {
         self.queued_fb = self.next_fb.take();
         if self.pending_fb.is_none() && self.queued_fb.is_some() {
             self.submit()?;
@@ -210,7 +216,7 @@ where
     /// *Note*: Needs to be called, after the vblank event of the matching [`DrmDevice`](super::super::DrmDevice)
     /// was received after calling [`GbmBufferedSurface::queue_buffer`] on this surface.
     /// Otherwise the underlying swapchain will run out of buffers eventually.
-    pub fn frame_submitted(&mut self) -> Result<(), Error> {
+    pub fn frame_submitted(&mut self) -> Result<(), Error<A::Error>> {
         if let Some(mut pending) = self.pending_fb.take() {
             std::mem::swap(&mut pending, &mut self.current_fb);
             if self.queued_fb.is_some() {
@@ -221,7 +227,7 @@ where
         Ok(())
     }
 
-    fn submit(&mut self) -> Result<(), Error> {
+    fn submit(&mut self) -> Result<(), Error<A::Error>> {
         // yes it does not look like it, but both of these lines should be safe in all cases.
         let slot = self.queued_fb.take().unwrap();
         let fb = slot.userdata().get::<FbHandle<D>>().unwrap().fb;
@@ -276,13 +282,13 @@ where
     /// (e.g. no suitable [`encoder`](drm::control::encoder) may be found)
     /// or is not compatible with the currently pending
     /// [`Mode`](drm::control::Mode).
-    pub fn add_connector(&self, connector: connector::Handle) -> Result<(), Error> {
+    pub fn add_connector(&self, connector: connector::Handle) -> Result<(), Error<A::Error>> {
         self.drm.add_connector(connector).map_err(Error::DrmError)
     }
 
     /// Tries to mark a [`connector`](drm::control::connector)
     /// for removal on the next commit.    
-    pub fn remove_connector(&self, connector: connector::Handle) -> Result<(), Error> {
+    pub fn remove_connector(&self, connector: connector::Handle) -> Result<(), Error<A::Error>> {
         self.drm.remove_connector(connector).map_err(Error::DrmError)
     }
 
@@ -292,7 +298,7 @@ where
     /// (e.g. no suitable [`encoder`](drm::control::encoder) may be found)
     /// or is not compatible with the currently pending
     /// [`Mode`](drm::control::Mode).    
-    pub fn set_connectors(&self, connectors: &[connector::Handle]) -> Result<(), Error> {
+    pub fn set_connectors(&self, connectors: &[connector::Handle]) -> Result<(), Error<A::Error>> {
         self.drm.set_connectors(connectors).map_err(Error::DrmError)
     }
 
@@ -314,7 +320,7 @@ where
     /// Fails if the mode is not compatible with the underlying
     /// [`crtc`](drm::control::crtc) or any of the
     /// pending [`connector`](drm::control::connector)s.
-    pub fn use_mode(&mut self, mode: Mode) -> Result<(), Error> {
+    pub fn use_mode(&mut self, mode: Mode) -> Result<(), Error<A::Error>> {
         self.drm.use_mode(mode).map_err(Error::DrmError)?;
         let (w, h) = mode.size();
         self.swapchain.resize(w as _, h as _);
@@ -334,9 +340,10 @@ impl<A: AsRawFd + 'static> Drop for FbHandle<A> {
     }
 }
 
-fn attach_framebuffer<A>(drm: &Arc<DrmSurface<A>>, bo: &BufferObject<()>) -> Result<FbHandle<A>, Error>
+fn attach_framebuffer<E, D>(drm: &Arc<DrmSurface<D>>, bo: &BufferObject<()>) -> Result<FbHandle<D>, Error<E>>
 where
-    A: AsRawFd + 'static,
+    E: std::error::Error + Send + Sync,
+    D: AsRawFd + 'static,
 {
     let modifier = match bo.modifier().unwrap() {
         Modifier::Invalid => None,
@@ -385,7 +392,7 @@ where
 
 /// Errors thrown by a [`GbmBufferedSurface`]
 #[derive(Debug, thiserror::Error)]
-pub enum Error {
+pub enum Error<E: std::error::Error + Send + Sync + 'static> {
     /// No supported pixel format for the given plane could be determined
     #[error("No supported plane buffer format found")]
     NoSupportedPlaneFormat,
@@ -406,14 +413,14 @@ pub enum Error {
     DrmError(#[from] DrmError),
     /// Error importing the rendered buffer to libgbm for scan-out
     #[error("The underlying gbm device encounted an error: {0}")]
-    GbmError(#[from] std::io::Error),
+    GbmError(#[source] E),
     /// Error exporting as Dmabuf
     #[error("The allocated buffer could not be exported as a dmabuf: {0}")]
     AsDmabufError(#[from] GbmConvertError),
 }
 
-impl From<Error> for SwapBuffersError {
-    fn from(err: Error) -> SwapBuffersError {
+impl<E: std::error::Error + Send + Sync + 'static> From<Error<E>> for SwapBuffersError {
+    fn from(err: Error<E>) -> SwapBuffersError {
         match err {
             x @ Error::NoSupportedPlaneFormat
             | x @ Error::NoSupportedRendererFormat

gbm: Allow usage of other allocators for GbmBufferedSurface