<NME> mod.rs
<BEF> use std::{cell::RefCell, os::unix::io::RawFd, rc::Rc};

pub use wayland_protocols::unstable::linux_dmabuf::v1::server::zwp_linux_buffer_params_v1::Flags;
//!
//! // define your supported formats
//! let formats = vec![
//!     /* ... */
//! ];
//!
//! // And create the dmabuf global.

/// Representation of a Dmabuf format, as advertized to the client
pub struct Format {
    /// The format identifier
    pub format: u32,
    /// High part of the supported modifiers
    pub modifier_hi: u32,
    /// Low part of the supported modifiers
    pub modifier_lo: u32,
    /// Number of planes used by this format
    pub plane_count: u32,
}
use std::{
    collections::HashMap,
    convert::TryFrom,
    os::unix::prelude::AsRawFd,
    sync::{
        atomic::{AtomicBool, Ordering},
        Arc, Mutex,
    },
    pub offset: u32,
    /// Stride for this plane
    pub stride: u32,
    /// High part of the modifiers for this plane
    pub modifier_hi: u32,
    /// Low part of the modifiers for this plane
    pub modifier_lo: u32,
}

/// The complete information provided by the client to create a dmabuf buffer
use crate::{
    backend::allocator::{
        dmabuf::{Dmabuf, DmabufFlags, Plane},
        Format, Fourcc, Modifier,
    },
    utils::{ids::id_gen, UnmanagedResource},
};

use super::buffer::BufferHandler;

/// Delegate type for all dmabuf globals.
///
/// Dmabuf globals are created using this type and events will be forwarded to an instance of the dmabuf global.
#[derive(Debug)]
pub struct DmabufState {
    /// Globals managed by the dmabuf handler.
    globals: HashMap<usize, GlobalId>,
}

pub trait DmabufHandler {
    /// The data of a successfully imported dmabuf.
    ///
    /// This will be stored as the `user_data` of the `WlBuffer` associated with this dmabuf.
    type BufferData: 'static;
    /// Validate a dmabuf
    ///
    /// Creates a dmabuf global with the specified supported formats.
    pub fn create_global<D, L>(
        &mut self,
        display: &DisplayHandle,
        formats: Vec<Format>,
        logger: L,
    ) -> DmabufGlobal
    where
        D: GlobalDispatch<zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1, DmabufGlobalData>
            + BufferHandler
            + DmabufHandler
            + 'static,
        L: Into<Option<::slog::Logger>>,
    {
        self.create_global_with_filter::<D, _, L>(display, formats, |_| true, logger)
    }

    /// Creates a dmabuf global with the specified supported formats.
    ///
    /// This function unlike [`DmabufState::create_global`] also allows you to specify a filter function to
    /// determine which clients may see this global. This functionality may be used on multi-gpu systems in
    /// order to make a client choose the correct gpu.
    pub fn create_global_with_filter<D, F, L>(
        &mut self,
        display: &DisplayHandle,
        formats: Vec<Format>,
        filter: F,
        logger: L,
    ) -> DmabufGlobal
    where
        D: GlobalDispatch<zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1, DmabufGlobalData>
            + BufferHandler
            + DmabufHandler
            + 'static,
        F: for<'c> Fn(&'c Client) -> bool + Send + Sync + 'static,
    L: Into<Option<::slog::Logger>>,
    H: DmabufHandler + 'static,
{
    let log = crate::slog_or_stdlog(logger);

    let max_planes = formats.iter().map(|f| f.plane_count).max().unwrap_or(0);
    let formats = Rc::new(formats);
    let handler = Rc::new(RefCell::new(handler));

    display.create_global(3, move |new_dmabuf, version| {
        let dma_formats = formats.clone();
        let dma_handler = handler.clone();
        let dmabuf: zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1 = new_dmabuf.implement_closure(
            move |req, _| match req {
                zwp_linux_dmabuf_v1::Request::CreateParams { params_id } => {
    /// This operation is permanent and there is no way to re-enable a global.
    pub fn disable_global<D: 'static>(&mut self, display: &DisplayHandle, global: &DmabufGlobal) {
        display.disable_global::<D>(self.globals.get(&global.id).unwrap().clone())
    }
                            used: false,
                            formats: dma_formats.clone(),
                            handler: dma_handler.clone(),
                        },
                        None::<fn(_)>,
                        (),
            display.remove_global::<D>(self.globals.remove(&global.id).unwrap());
        }
    }
}

/// Data associated with a dmabuf global.
#[allow(missing_debug_implementations)]
pub struct DmabufGlobalData {
    filter: Box<dyn for<'c> Fn(&'c Client) -> bool + Send + Sync>,
        for f in &*formats {
            dmabuf.format(f.format);
            if version >= 3 {
                dmabuf.modifier(f.format, f.modifier_hi, f.modifier_lo);
            }
        }
    })
pub struct DmabufData {
    formats: Arc<Vec<Format>>,
    id: usize,
    logger: slog::Logger,
}
    used: bool,
    formats: Rc<Vec<Format>>,
    handler: Rc<RefCell<H>>,
}

impl<H: DmabufHandler> ParamRequestHandler for ParamsHandler<H> {
    /// Whether the params protocol object has been used before to create a wl_buffer.
    used: AtomicBool,

    formats: Arc<Vec<Format>>,

    /// Pending planes for the params.
    planes: Mutex<Vec<Plane>>,

    logger: slog::Logger,
}

/// A handle to a registered dmabuf global.
///
/// This type may be used in equitability checks to determine which global a dmabuf is being imported to.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
pub struct DmabufGlobal {
    id: usize,
}

/// Handler trait for [`Dmabuf`] import from the compositor.
pub trait DmabufHandler: BufferHandler {
    /// Returns a mutable reference to the [`DmabufState`] delegate type.
    fn dmabuf_state(&mut self) -> &mut DmabufState;

    /// This function is called when a client has imported a [`Dmabuf`].
    ///
    /// The `global` indicates which [`DmabufGlobal`] the buffer was imported to. You should import the dmabuf
    /// into your renderer to ensure the dmabuf may be used later when rendering.
    ///
    /// The return value of this function indicates whether dmabuf import is successful. The renderer is
    /// responsible for determining whether the format and plane combinations are valid and should return
    /// [`ImportError::InvalidFormat`] if the format and planes are not correct.
    ///
    /// If the import fails due to an implementation specific reason, then [`ImportError::Failed`] should be
    /// returned.
    fn dmabuf_imported(&mut self, global: &DmabufGlobal, dmabuf: Dmabuf) -> Result<(), ImportError>;
}

/// Error that may occur when importing a [`Dmabuf`].
            plane_idx,
            offset,
            stride,
            modifier_hi,
            modifier_lo,
        });
    }

    Failed,

    /// The format and plane combination is not valid.
    ///
    /// This specific error will kill the client providing the dmabuf.
    #[error("format and plane combination is not valid")]
    InvalidFormat,
}

/// Gets the contents of a [`Dmabuf`] backed [`WlBuffer`].
///
/// If the buffer is managed by the dmabuf handler, the [`Dmabuf`] is returned.
///
/// If the buffer is not managed by the dmabuf handler (whether the buffer is a different kind of buffer,
/// such as an shm buffer or is not managed by smithay), this function will return an [`UnmanagedResource`]
            width,
            height,
        ) {
            return;
        }
        let info = BufferInfo {
/// Macro to delegate implementation of the linux dmabuf to [`DmabufState`].
///
/// You must also implement [`DmabufHandler`] to use this.
#[macro_export]
macro_rules! delegate_dmabuf {
    ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
        type __ZwpLinuxDmabufV1 =
            $crate::reexports::wayland_protocols::wp::linux_dmabuf::zv1::server::zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1;
        type __ZwpLinuxBufferParamsV1 =
            $crate::reexports::wayland_protocols::wp::linux_dmabuf::zv1::server::zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1;

                .and_then(|c| c.create_resource::<wl_buffer::WlBuffer>(1))
            {
                let buffer = handler.create_buffer(data, buffer);
                params.created(&buffer);
            }
        } else {
            params.failed();
        }
    }
            $crate::reexports::wayland_server::protocol::wl_buffer::WlBuffer: $crate::backend::allocator::dmabuf::Dmabuf
        ] => $crate::wayland::dmabuf::DmabufState);
    };
}

const GLOBAL_VERSION: u32 = 3;

impl DmabufParamsData {
    /// Emits a protocol error if the params have already been used to create a dmabuf.
    ///
    /// This returns true if the protocol object has not been used.
    fn ensure_unused(&self, params: &zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1) -> bool {
        if !self.used.load(Ordering::Relaxed) {
            return true;
        }

        params.post_error(
            zwp_linux_buffer_params_v1::Error::AlreadyUsed,
            "This buffer_params has already been used to create a buffer.",
        );

        false
    }

            width,
            height,
        ) {
            return;
        }
        let info = BufferInfo {
        &self,
        params: &zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1,
        width: i32,
        height: i32,
        format: u32,
        };
        let mut handler = self.handler.borrow_mut();
        if let Ok(data) = handler.validate_dmabuf(info) {
            handler.create_buffer(data, buffer_id);
        } else {
            params.as_ref().post_error(
                ParamError::InvalidWlBuffer as u32,
                "create_immed resulted in an invalid buffer.".into(),
                    zwp_linux_buffer_params_v1::Error::InvalidFormat,
                    format!("Format {:x} is not supported", format),
                );

                return None;
            }
        };

        // Validate buffer parameters:
        // 1. Must have known format
        if !self.formats.iter().any(|f| f.code == format) {
            params.post_error(
                zwp_linux_buffer_params_v1::Error::InvalidFormat,
                format!("Format {:?}/{:x} is not supported.", format, format as u32),
            );
            return None;
        }

        // 2. Width and height must be positive
        if width < 1 {
            params.post_error(
                zwp_linux_buffer_params_v1::Error::InvalidDimensions,
                "invalid width",
            );
        }

        if height < 1 {
            params.post_error(
                zwp_linux_buffer_params_v1::Error::InvalidDimensions,
                "invalid height",
            );
        }

        // 3. Validate all the planes
        let mut planes = self.planes.lock().unwrap();

        for plane in &*planes {
            // Must not overflow
            let end = match plane
                .stride
                .checked_mul(height as u32)
                .and_then(|o| o.checked_add(plane.offset))
            {
                Some(e) => e,

                None => {
                    params.post_error(
                        zwp_linux_buffer_params_v1::Error::OutOfBounds,
                        format!("Size overflow for plane {}.", plane.plane_idx),
                    );

                    return None;
                }
            };

            if let Ok(size) = unistd::lseek(plane.fd.as_raw_fd(), 0, unistd::Whence::SeekEnd) {
                // Reset seek point
                let _ = unistd::lseek(plane.fd.as_raw_fd(), 0, unistd::Whence::SeekSet);

                if plane.offset as libc::off_t > size {
                    params.post_error(
                        zwp_linux_buffer_params_v1::Error::OutOfBounds,
                        format!("Invalid offset {} for plane {}.", plane.offset, plane.plane_idx),
                    );

                    return None;
                }

                if (plane.offset + plane.stride) as libc::off_t > size {
                    params.post_error(
                        zwp_linux_buffer_params_v1::Error::OutOfBounds,
                        format!("Invalid stride {} for plane {}.", plane.stride, plane.plane_idx),
                    );

                    return None;
                }

                // Planes > 0 can be subsampled, in which case 'size' will be smaller than expected.
                if plane.plane_idx == 0 && end as libc::off_t > size {
                    params.post_error(
                        zwp_linux_buffer_params_v1::Error::OutOfBounds,
                        format!(
                            "Invalid stride ({}) or height ({}) for plane {}.",
                            plane.stride, height, plane.plane_idx
                        ),
                    );

                    return None;
                }
            }
        }

        let mut buf = Dmabuf::builder(
            (width, height),
            format,
            DmabufFlags::from_bits_truncate(flags.into()),
        );

        for (i, plane) in planes.drain(..).enumerate() {
            let offset = plane.offset;
            let stride = plane.stride;
            let modi = plane.modifier;
            buf.add_plane(plane.into(), i as u32, offset, stride, modi);
        }

        let dmabuf = match buf.build() {
            Some(buf) => buf,

            None => {
                params.post_error(
                    zwp_linux_buffer_params_v1::Error::Incomplete as u32,
                    "Provided buffer is incomplete, it has zero planes",
                );
                return None;
            }
        };

        Some(dmabuf)
    }
}

id_gen!(next_global_id, DMABUF_GLOBAL_ID, DMABUF_GLOBAL_IDS);

<MSG> wayland.dmabuf: module docs & some logging

<DFF> @@ -1,3 +1,70 @@
+//! Linux DMABUF protocol
+//!
+//! This module provides helper to handle the linux-dmabuf protocol, which allows clients to submit their
+//! contents as dmabuf file descriptors. These handlers automate the aggregation of the metadata associated
+//! with a dma buffer, and do some basic checking of the sanity of what the client sends.
+//!
+//! ## How to use
+//!
+//! To setup the dmabuf global, you will need to provide 2 things:
+//!
+//! - a list of the dmabuf formats you wish to support
+//! - an implementation of the `DmabufHandler` trait
+//!
+//! The list of supported format is just a `Vec<Format>`, where you will enter all the (format, modifier)
+//! couples you support.
+//!
+//! The implementation of the `DmabufHandler` trait will be called whenever a client has finished setting up
+//! a dma buffer. You will be handled the full details of the sclient's submission as a `BufferInfo` struct,
+//! and you need to validate it and maybe import it into your rendered. The `BufferData` associated type
+//! allows you to store any metadata of handle to the resource you need into the created `wl_buffer`,
+//! user data, to then retrieve it when it is attached to a surface to re-identify the dmabuf.
+//!
+//! ```
+//! # extern crate wayland_server;
+//! # extern crate smithay;
+//! use smithay::wayland::dmabuf::{DmabufHandler, BufferInfo, init_dmabuf_global};
+//!
+//! struct MyDmabufHandler;
+//!
+//! struct MyBufferData {
+//!     /* ... */
+//! }
+//!
+//! impl Drop for MyBufferData {
+//!     fn drop(&mut self) {
+//!         // This is called when all handles to this buffer have been dropped,
+//!         // both client-side and server side.
+//!         // You can now free the associated resource in your renderer.
+//!     }
+//! }
+//!
+//! impl DmabufHandler for MyDmabufHandler {
+//!     type BufferData = MyBufferData;
+//!     fn validate_dmabuf(&mut self, info: BufferInfo) -> Result<Self::BufferData, ()> {
+//!         /* validate the dmabuf and import it into your renderer state */
+//!         Ok(MyBufferData { /* ... */ })
+//!     }
+//! }
+//!
+//! // Once this is defined, you can in your setup initialize the dmabuf global:
+//!
+//! # fn main() {
+//! # let mut event_loop = wayland_server::calloop::EventLoop::<()>::new().unwrap();
+//! # let mut display = wayland_server::Display::new(event_loop.handle());
+//! // define your supported formats
+//! let formats = vec![
+//!     /* ... */
+//! ];
+//! let dmabuf_global = init_dmabuf_global(
+//!     &mut display,
+//!     formats,
+//!     MyDmabufHandler,
+//!     None // we don't provide a logger in this example
+//! );
+//! # }
+//! ```
+
 use std::{cell::RefCell, os::unix::io::RawFd, rc::Rc};
 
 pub use wayland_protocols::unstable::linux_dmabuf::v1::server::zwp_linux_buffer_params_v1::Flags;
@@ -11,12 +78,16 @@ use wayland_server::{protocol::wl_buffer, Display, Global, NewResource};
 
 /// Representation of a Dmabuf format, as advertized to the client
 pub struct Format {
-    /// The format identifier
+    /// The format identifier.
+    ///
+    /// It must be a `DRM_FORMAT` code, as defined by the libdrm's drm_fourcc.h. The Linux kernel's DRM
+    /// sub-system is the authoritative source on how the format codes should work.
     pub format: u32,
-    /// High part of the supported modifiers
-    pub modifier_hi: u32,
-    /// Low part of the supported modifiers
-    pub modifier_lo: u32,
+    /// The supported dmabuf layout modifier.
+    ///
+    /// This is an opaque token. Drivers use this token to express tiling, compression, etc. driver-specific
+    /// modifications to the base format defined by the DRM fourcc code.
+    pub modifier: u64,
     /// Number of planes used by this format
     pub plane_count: u32,
 }
@@ -31,10 +102,8 @@ pub struct Plane {
     pub offset: u32,
     /// Stride for this plane
     pub stride: u32,
-    /// High part of the modifiers for this plane
-    pub modifier_hi: u32,
-    /// Low part of the modifiers for this plane
-    pub modifier_lo: u32,
+    /// Modifier for this plane
+    pub modifier: u64,
 }
 
 /// The complete information provided by the client to create a dmabuf buffer
@@ -60,7 +129,9 @@ pub struct BufferInfo {
 pub trait DmabufHandler {
     /// The data of a successfully imported dmabuf.
     ///
-    /// This will be stored as the `user_data` of the `WlBuffer` associated with this dmabuf.
+    /// This will be stored as the `user_data` of the `WlBuffer` associated with this dmabuf. If it has a
+    /// destructor, it will be run when the client has destroyed the buffer and your compositor has dropped
+    /// all of its `WlBuffer` handles to it.
     type BufferData: 'static;
     /// Validate a dmabuf
     ///
@@ -102,15 +173,22 @@ where
     L: Into<Option<::slog::Logger>>,
     H: DmabufHandler + 'static,
 {
-    let log = crate::slog_or_stdlog(logger);
+    let log = crate::slog_or_stdlog(logger).new(o!("smithay_module" => "dmabuf_handler"));
 
     let max_planes = formats.iter().map(|f| f.plane_count).max().unwrap_or(0);
     let formats = Rc::new(formats);
     let handler = Rc::new(RefCell::new(handler));
 
+    trace!(
+        log,
+        "Initializing DMABUF handler with {} supported formats",
+        formats.len()
+    );
+
     display.create_global(3, move |new_dmabuf, version| {
         let dma_formats = formats.clone();
         let dma_handler = handler.clone();
+        let dma_log = log.clone();
         let dmabuf: zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1 = new_dmabuf.implement_closure(
             move |req, _| match req {
                 zwp_linux_dmabuf_v1::Request::CreateParams { params_id } => {
@@ -121,6 +199,7 @@ where
                             used: false,
                             formats: dma_formats.clone(),
                             handler: dma_handler.clone(),
+                            log: dma_log.clone(),
                         },
                         None::<fn(_)>,
                         (),
@@ -136,7 +215,7 @@ where
         for f in &*formats {
             dmabuf.format(f.format);
             if version >= 3 {
-                dmabuf.modifier(f.format, f.modifier_hi, f.modifier_lo);
+                dmabuf.modifier(f.format, (f.modifier >> 32) as u32, f.modifier as u32);
             }
         }
     })
@@ -148,6 +227,7 @@ struct ParamsHandler<H: DmabufHandler> {
     used: bool,
     formats: Rc<Vec<Format>>,
     handler: Rc<RefCell<H>>,
+    log: ::slog::Logger,
 }
 
 impl<H: DmabufHandler> ParamRequestHandler for ParamsHandler<H> {
@@ -193,8 +273,7 @@ impl<H: DmabufHandler> ParamRequestHandler for ParamsHandler<H> {
             plane_idx,
             offset,
             stride,
-            modifier_hi,
-            modifier_lo,
+            modifier: ((modifier_hi as u64) << 32) + (modifier_lo as u64),
         });
     }
 
@@ -216,6 +295,7 @@ impl<H: DmabufHandler> ParamRequestHandler for ParamsHandler<H> {
             width,
             height,
         ) {
+            trace!(self.log, "Killing client providing bogus dmabuf buffer params.");
             return;
         }
         let info = BufferInfo {
@@ -233,9 +313,11 @@ impl<H: DmabufHandler> ParamRequestHandler for ParamsHandler<H> {
                 .and_then(|c| c.create_resource::<wl_buffer::WlBuffer>(1))
             {
                 let buffer = handler.create_buffer(data, buffer);
+                trace!(self.log, "Creating a new validated dma wl_buffer.");
                 params.created(&buffer);
             }
         } else {
+            trace!(self.log, "Refusing creation of an invalid dma wl_buffer.");
             params.failed();
         }
     }
@@ -266,6 +348,7 @@ impl<H: DmabufHandler> ParamRequestHandler for ParamsHandler<H> {
             width,
             height,
         ) {
+            trace!(self.log, "Killing client providing bogus dmabuf buffer params.");
             return;
         }
         let info = BufferInfo {
@@ -277,8 +360,13 @@ impl<H: DmabufHandler> ParamRequestHandler for ParamsHandler<H> {
         };
         let mut handler = self.handler.borrow_mut();
         if let Ok(data) = handler.validate_dmabuf(info) {
+            trace!(self.log, "Creating a new validated immediate dma wl_buffer.");
             handler.create_buffer(data, buffer_id);
         } else {
+            trace!(
+                self.log,
+                "Refusing creation of an invalid immediate dma wl_buffer, killing client."
+            );
             params.as_ref().post_error(
                 ParamError::InvalidWlBuffer as u32,
                 "create_immed resulted in an invalid buffer.".into(),

wayland.dmabuf: module docs & some logging