<NME> mod.rs
<BEF> //! This module represents abstraction on top the linux direct rendering manager api (drm).
//!
//! ## [`DrmDevice`]
//!
//! A device exposes certain properties, which are directly derived
//! from the *device* as perceived by the direct rendering manager api (drm). These resources consists
//! out of connectors, encoders, framebuffers, planes and crtcs.
//!
//! [`crtc`](drm::control::crtc)s represent scanout engines of the device pointer to one framebuffer.
//! Their responsibility is to read the data of the framebuffer and export it into an "Encoder".
//! The number of crtc's represent the number of independent output devices the hardware may handle.
//!
//! On modern graphic cards it is better to think about the `crtc` as some sort of rendering engine.
//! You can only have so many different pictures, you may display, as you have `crtc`s, but a single image
//! may be put onto multiple displays.
//!
//! An [`encoder`](drm::control::encoder) encodes the data of connected crtcs into a video signal for a fixed set of connectors.
//! E.g. you might have an analog encoder based on a DAG for VGA ports, but another one for digital ones.
//! Also not every encoder might be connected to every crtc.
//!
//! A [`connector`](drm::control::connector) represents a port on your computer, possibly with a connected monitor, TV, capture card, etc.
//!
//! A [`framebuffer`](drm::control::framebuffer) represents a buffer you may display, see `DrmSurface` below.
//!
//! A [`plane`](drm::control::plane) adds another layer on top of the crtcs, which allow us to layer multiple images on top of each other more efficiently
//! then by combining the rendered images in the rendering phase, e.g. via OpenGL. Planes have to be explicitly used by the user to be useful.
//! Every device has at least one primary plane used to display an image to the whole crtc. Additionally cursor and overlay planes may be present.
//! Cursor planes are usually very restricted in size and meant to be used for hardware cursors, while overlay planes may
//! be used for performance reasons to display any overlay on top of the image, e.g. the top-most windows.
//! Overlay planes may have a bunch of weird limitation, that you cannot query, e.g. only working on round pixel coordinates.
//! You code should never rely on a fixed set of overlay planes, but always have a fallback solution in place.
//!
//! The main functionality of a `Device` in smithay is to give access to all these properties for the user to
//! choose an appropriate rendering configuration. What that means is defined by the requirements and constraints documented
//! in the specific device implementations. The second functionality is the creation of a `Surface`.
//! Surface creation requires a `crtc` (which cannot be the same as another existing `Surface`'s crtc), a plane,
//! as well as a `Mode` and a set of `connectors`.
//!
//! smithay does not make sure that `connectors` are not already in use by another `Surface`. Overlapping `connector`-Sets may
//! be an error or result in undefined rendering behavior depending on the `Surface` implementation.
//!
//! ## [`DrmSurface`]
//!
//! A surface is a part of a `Device` that may output a picture to a number of connectors. It pumps pictures of buffers to outputs.
//!
//! On surface creation a matching encoder for your `connector` is automatically selected, if one exists(!),
//! which means you still need to check your configuration beforehand, even if you do not need to provide an encoder.
//!
//! A surface consists of one `crtc` that is rendered to by the user. This is fixed for the `Surface`s lifetime and cannot be changed.
//! A surface also always needs at least one connector to output the resulting image to as well as a `Mode` that is valid for the given connectors.
//!
//! The state of a `Surface` is double-buffered, meaning all operations that chance the set of `connector`s or their `Mode` are stored and
//! only applied on the next commit. `Surface`s do their best to validate these changes, if possible.
//!
//! A commit/page_flip may be triggered to apply the pending state.
//!
//! ## Rendering
//!
//! The drm infrastructure makes no assumptions about the used renderer and does not interface with them directly.
//! It just provides a way to create framebuffers from various buffer types (mainly `DumbBuffer`s and hardware-backed gbm `BufferObject`s).
//!
//! Buffer management and details about the various types can be found in the [`allocator`-Module](crate::backend::allocator) and
//! rendering abstractions, which can target these buffers can be found in the [`renderer`-Module](crate::backend::renderer).
//!
//! ## [`DrmNode`]
//!
//! A drm node refers to a drm device and the capabilities that may be performed using the node.
//! Generally [`DrmNode`] is primarily used by clients (such as the output backends) which need
//! to allocate buffers for use in X11 or Wayland. If you need to do mode setting, you should use
//! [`DrmDevice`] instead.

pub(crate) mod device;
pub(self) mod error;
pub mod node;

#[cfg(feature = "backend_session")]
pub(self) mod session;
pub(self) mod surface;

pub use device::{DevPath, DrmDevice, DrmEvent, EventMetadata as DrmEventMetadata, Time as DrmEventTime};
pub use error::Error as DrmError;
pub use node::{CreateDrmNodeError, DrmNode, NodeType};
#[cfg(feature = "backend_gbm")]
pub use surface::gbm::{Error as GbmBufferedSurfaceError, GbmBufferedSurface};
pub use surface::DrmSurface;

use drm::control::{crtc, plane, Device as ControlDevice, PlaneType};

/// A set of planes as supported by a crtc
#[derive(Debug)]
pub struct Planes {
    /// The primary plane of the crtc (automatically selected for [DrmDevice::create_surface])
    pub primary: plane::Handle,
    /// The cursor plane of the crtc, if available
    pub cursor: Option<plane::Handle>,
    /// Overlay planes supported by the crtc, if available
    pub overlay: Vec<plane::Handle>,
}

fn planes(
    dev: &impl ControlDevice,
    crtc: &crtc::Handle,
    has_universal_planes: bool,
) -> Result<Planes, DrmError> {
    let mut primary = None;
    let mut cursor = None;
    let mut overlay = Vec::new();

    let planes = dev.plane_handles().map_err(|source| DrmError::Access {
        errmsg: "Error loading plane handles",
        dev: dev.dev_path(),
        source,
    })?;

    let resources = dev.resource_handles().map_err(|source| DrmError::Access {
        errmsg: "Error loading resource handles",
        dev: dev.dev_path(),
        source,
    })?;

    for plane in planes {
        let info = dev.get_plane(plane).map_err(|source| DrmError::Access {
            errmsg: "Failed to get plane information",
            dev: dev.dev_path(),
            source,
        })?;
        let filter = info.possible_crtcs();
        if resources.filter_crtcs(filter).contains(crtc) {
            match plane_type(dev, plane)? {
                PlaneType::Primary => {
                    primary = Some(plane);
                }
                PlaneType::Cursor => {
                    cursor = Some(plane);
                }
                PlaneType::Overlay => {
                    overlay.push(plane);
                }
            };
        }
    }

    Ok(Planes {
        primary: primary.expect("Crtc has no primary plane"),
        cursor: if has_universal_planes { cursor } else { None },
        overlay: if has_universal_planes { overlay } else { Vec::new() },
    })
}

fn plane_type(dev: &impl ControlDevice, plane: plane::Handle) -> Result<PlaneType, DrmError> {
    let props = dev.get_properties(plane).map_err(|source| DrmError::Access {
        errmsg: "Failed to get properties of plane",
        dev: dev.dev_path(),
        source,
    })?;
    let (ids, vals) = props.as_props_and_values();
    for (&id, &val) in ids.iter().zip(vals.iter()) {
        let info = dev.get_property(id).map_err(|source| DrmError::Access {
            errmsg: "Failed to get property info",
            dev: dev.dev_path(),
            source,
        })?;
        if info.name().to_str().map(|x| x == "type").unwrap_or(false) {
            return Ok(match val {
                x if x == (PlaneType::Primary as u64) => PlaneType::Primary,
                x if x == (PlaneType::Cursor as u64) => PlaneType::Cursor,
                _ => PlaneType::Overlay,
            });
        }
    }
    unreachable!()
}
use drm::control::Device as ControlDevice;
use gbm::Device as GbmDevice;
use nix;
use std::cell::RefCell;
use std::fs::File;
use std::io::{Error as IoError, Result as IoResult};
use std::os::unix::io::{AsRawFd, RawFd};
use std::rc::{Rc, Weak};
use std::time::Duration;
use wayland_server::EventLoopHandle;
use wayland_server::sources::{FdEventSource, FdEventSourceImpl, READ};

mod backend;
pub mod error;

pub use self::backend::{DrmBackend, Id};
use self::backend::DrmBackendInternal;
use self::error::*;

/// Internal struct as required by the drm crate
use self::devices::{Context, Devices};

/// Representation of an open drm device node to create rendering backends
pub struct DrmDevice {
    context: Rc<Context>,
    backends: Vec<Weak<RefCell<DrmBackendInternal>>>,
    logger: ::slog::Logger,
}

impl DrmDevice {
    /// Create a new `DrmDevice` from a raw file descriptor
    ///
    /// Returns an error of opening the device failed or context creation was not
            context: Rc::new(Context::try_new(
                Box::new(Devices::try_new(Box::new(drm), |drm| {
                    debug!(log, "Creating gbm device");
                    GbmDevice::new_from_drm::<DrmDevice>(drm).chain_err(|| ErrorKind::GbmInitFailed)
                })?),
                |devices| {
                    debug!(log, "Creating egl context from gbm device");
                    ).map_err(Error::from)
                },
            )?),
            backends: Vec::new(),
            logger: log,
        })
    }
    ///
    /// Errors if initialization fails or the mode is not available on all given
    /// connectors.
    pub fn create_backend<I>(&mut self, crtc: crtc::Handle, mode: Mode, connectors: I) -> Result<DrmBackend>
    where
        I: Into<Vec<connector::Handle>>,
    {
        for backend in self.backends.iter() {
            if let Some(backend) = backend.upgrade() {
                if backend.borrow().is_crtc(crtc) {
                    bail!(ErrorKind::CrtcAlreadyInUse(crtc))
                }
            }
        }

        // check if the given connectors and crtc match
        let connectors = connectors.into();

        // check if we have an encoder for every connector
        for connector in connectors.iter() {
            let con_info = connector::Info::load_from_device(self.context.head().head(), *connector)
                .chain_err(|| {
                    ErrorKind::DrmDev(format!("{:?}", self.context.head().head()))
                })?;

            // then check for every connector which encoders it does support
            let encoders = con_info
                .encoders()
                .iter()

        // configuration is valid, the kernel will figure out the rest

        let own_id = self.backends.len();
        let logger = self.logger.new(
            o!("id" => format!("{}", own_id), "crtc" => format!("{:?}", crtc)),
        );

        let backend = Rc::new(RefCell::new(DrmBackendInternal::new(
            self.context.clone(),
            crtc,
            mode,
            connectors,
            own_id,
            logger,
        )?));

        self.backends.push(Rc::downgrade(&backend));

        Ok(DrmBackend::new(backend))
    }
}

// for users convinience and FdEventSource registering
impl AsRawFd for DrmDevice {
    fn as_raw_fd(&self) -> RawFd {
        self.context.head().head().as_raw_fd()
    }
}
impl BasicDevice for DrmDevice {}
impl ControlDevice for DrmDevice {}

/// Handler for drm node events
///
/// See module-level documentation for its use
pub trait DrmHandler {
    /// A `DrmBackend` has finished swapping buffers and new frame can now
    /// (and should be immediately) be rendered.
    ///
    /// The `id` argument is the `Id` of the `DrmBackend` that finished rendering,
    /// check using `DrmBackend::is`.
    fn ready(&mut self, evlh: &mut EventLoopHandle, id: Id, frame: u32, duration: Duration);
    /// The `DrmDevice` has thrown an error.
    ///
    /// The related backends are most likely *not* usable anymore and
    /// the whole stack has to be recreated.
    fn error(&mut self, evlh: &mut EventLoopHandle, error: IoError);
}

/// Bind a `DrmDevice` to an EventLoop,
///
/// This will cause it to recieve events and feed them into an `DrmHandler`
pub fn drm_device_bind<H>(evlh: &mut EventLoopHandle, device: DrmDevice, handler: H)
                          -> IoResult<FdEventSource<(DrmDevice, H)>>
where
    H: DrmHandler + 'static,
{
    evlh.add_fd_event_source(
        device.as_raw_fd(),
    )
}

fn fd_event_source_implementation<H>() -> FdEventSourceImpl<(DrmDevice, H)>
where
    H: DrmHandler + 'static,
{
    FdEventSourceImpl {
        ready: |evlh, id, _, _| {
            use std::any::Any;

            let &mut (ref dev, ref mut handler) = id;

            struct PageFlipHandler<'a, 'b, H: DrmHandler + 'static>(&'a mut H, &'b mut EventLoopHandle);

            impl<'a, 'b, H: DrmHandler + 'static> crtc::PageFlipHandler<DrmDevice> for PageFlipHandler<'a, 'b, H> {
                fn handle_event(&mut self, device: &DrmDevice, frame: u32, duration: Duration,
                                userdata: Box<Any>) {
                    let id: Id = *userdata.downcast().unwrap();
                    if let Some(backend) = device.backends[id.raw()].upgrade() {
                        // we can now unlock the buffer
                        backend.borrow().unlock_buffer();
                        trace!(device.logger, "Handling event for backend {:?}", id.raw());
                        // and then call the user to render the next frame
                        self.0.ready(self.1, id, frame, duration);
                    }
                }
            }
                dev,
                2,
                None::<&mut ()>,
                Some(&mut PageFlipHandler(handler, evlh)),
                None::<&mut ()>,
            ).unwrap();
        },
        error: |evlh, id, _, error| {
            warn!(id.0.logger, "DrmDevice errored: {}", error);
            id.1.error(evlh, error);
        },
    }
}

<MSG> Store DrmBackend in EventLoop state

<DFF> @@ -191,20 +191,21 @@ use drm::control::{connector, crtc, encoder, Mode, ResourceInfo};
 use drm::control::Device as ControlDevice;
 use gbm::Device as GbmDevice;
 use nix;
-use std::cell::RefCell;
+use std::collections::HashMap;
 use std::fs::File;
 use std::io::{Error as IoError, Result as IoResult};
+use std::marker::PhantomData;
+use std::ops::Deref;
 use std::os::unix::io::{AsRawFd, RawFd};
-use std::rc::{Rc, Weak};
+use std::rc::Rc;
 use std::time::Duration;
-use wayland_server::EventLoopHandle;
+use wayland_server::{EventLoopHandle, StateToken};
 use wayland_server::sources::{FdEventSource, FdEventSourceImpl, READ};
 
 mod backend;
 pub mod error;
 
-pub use self::backend::{DrmBackend, Id};
-use self::backend::DrmBackendInternal;
+pub use self::backend::DrmBackend;
 use self::error::*;
 
 /// Internal struct as required by the drm crate
@@ -255,13 +256,13 @@ rental! {
 use self::devices::{Context, Devices};
 
 /// Representation of an open drm device node to create rendering backends
-pub struct DrmDevice {
+pub struct DrmDevice<B: Deref<Target = DrmBackend> + 'static> {
     context: Rc<Context>,
-    backends: Vec<Weak<RefCell<DrmBackendInternal>>>,
+    backends: HashMap<crtc::Handle, StateToken<B>>,
     logger: ::slog::Logger,
 }
 
-impl DrmDevice {
+impl<B: From<DrmBackend> + Deref<Target = DrmBackend> + 'static> DrmDevice<B> {
     /// Create a new `DrmDevice` from a raw file descriptor
     ///
     /// Returns an error of opening the device failed or context creation was not
@@ -358,7 +359,7 @@ impl DrmDevice {
             context: Rc::new(Context::try_new(
                 Box::new(Devices::try_new(Box::new(drm), |drm| {
                     debug!(log, "Creating gbm device");
-                    GbmDevice::new_from_drm::<DrmDevice>(drm).chain_err(|| ErrorKind::GbmInitFailed)
+                    GbmDevice::new_from_drm::<DrmDevice<B>>(drm).chain_err(|| ErrorKind::GbmInitFailed)
                 })?),
                 |devices| {
                     debug!(log, "Creating egl context from gbm device");
@@ -375,7 +376,7 @@ impl DrmDevice {
                     ).map_err(Error::from)
                 },
             )?),
-            backends: Vec::new(),
+            backends: HashMap::new(),
             logger: log,
         })
     }
@@ -385,29 +386,32 @@ impl DrmDevice {
     ///
     /// Errors if initialization fails or the mode is not available on all given
     /// connectors.
-    pub fn create_backend<I>(&mut self, crtc: crtc::Handle, mode: Mode, connectors: I) -> Result<DrmBackend>
+    pub fn create_backend<I>(&mut self, evlh: &mut EventLoopHandle, crtc: crtc::Handle, mode: Mode,
+                             connectors: I)
+                             -> Result<StateToken<B>>
     where
         I: Into<Vec<connector::Handle>>,
     {
-        for backend in self.backends.iter() {
-            if let Some(backend) = backend.upgrade() {
-                if backend.borrow().is_crtc(crtc) {
-                    bail!(ErrorKind::CrtcAlreadyInUse(crtc))
-                }
-            }
+        if self.backends.contains_key(&crtc) {
+            bail!(ErrorKind::CrtcAlreadyInUse(crtc));
         }
 
         // check if the given connectors and crtc match
         let connectors = connectors.into();
 
-        // check if we have an encoder for every connector
+        // check if we have an encoder for every connector and the mode mode
         for connector in connectors.iter() {
             let con_info = connector::Info::load_from_device(self.context.head().head(), *connector)
                 .chain_err(|| {
                     ErrorKind::DrmDev(format!("{:?}", self.context.head().head()))
                 })?;
 
-            // then check for every connector which encoders it does support
+            // check the mode
+            if !con_info.modes().contains(&mode) {
+                bail!(ErrorKind::ModeNotSuitable(mode));
+            }
+
+            // check for every connector which encoders it does support
             let encoders = con_info
                 .encoders()
                 .iter()
@@ -426,59 +430,50 @@ impl DrmDevice {
 
         // configuration is valid, the kernel will figure out the rest
 
-        let own_id = self.backends.len();
-        let logger = self.logger.new(
-            o!("id" => format!("{}", own_id), "crtc" => format!("{:?}", crtc)),
-        );
-
-        let backend = Rc::new(RefCell::new(DrmBackendInternal::new(
-            self.context.clone(),
-            crtc,
-            mode,
-            connectors,
-            own_id,
-            logger,
-        )?));
-
-        self.backends.push(Rc::downgrade(&backend));
+        let logger = self.logger.new(o!("crtc" => format!("{:?}", crtc)));
+        let backend = DrmBackend::new(self.context.clone(), crtc, mode, connectors, logger)?;
+        let token = evlh.state().insert(backend.into());
+        self.backends.insert(crtc, token.clone());
 
-        Ok(DrmBackend::new(backend))
+        Ok(token)
     }
 }
 
 // for users convinience and FdEventSource registering
-impl AsRawFd for DrmDevice {
+impl<B: Deref<Target = DrmBackend> + 'static> AsRawFd for DrmDevice<B> {
     fn as_raw_fd(&self) -> RawFd {
         self.context.head().head().as_raw_fd()
     }
 }
-impl BasicDevice for DrmDevice {}
-impl ControlDevice for DrmDevice {}
+impl<B: Deref<Target = DrmBackend> + 'static> BasicDevice for DrmDevice<B> {}
+impl<B: Deref<Target = DrmBackend> + 'static> ControlDevice for DrmDevice<B> {}
 
 /// Handler for drm node events
 ///
 /// See module-level documentation for its use
-pub trait DrmHandler {
+pub trait DrmHandler<B: Deref<Target = DrmBackend> + 'static> {
     /// A `DrmBackend` has finished swapping buffers and new frame can now
     /// (and should be immediately) be rendered.
     ///
     /// The `id` argument is the `Id` of the `DrmBackend` that finished rendering,
     /// check using `DrmBackend::is`.
-    fn ready(&mut self, evlh: &mut EventLoopHandle, id: Id, frame: u32, duration: Duration);
+    fn ready(&mut self, evlh: &mut EventLoopHandle, device: &mut DrmDevice<B>, backend: &StateToken<B>,
+             frame: u32, duration: Duration);
     /// The `DrmDevice` has thrown an error.
     ///
     /// The related backends are most likely *not* usable anymore and
     /// the whole stack has to be recreated.
-    fn error(&mut self, evlh: &mut EventLoopHandle, error: IoError);
+    fn error(&mut self, evlh: &mut EventLoopHandle, device: &mut DrmDevice<B>, error: IoError);
 }
 
 /// Bind a `DrmDevice` to an EventLoop,
 ///
 /// This will cause it to recieve events and feed them into an `DrmHandler`
-pub fn drm_device_bind<H>(evlh: &mut EventLoopHandle, device: DrmDevice, handler: H)
-                          -> IoResult<FdEventSource<(DrmDevice, H)>>
+pub fn drm_device_bind<B, H>(evlh: &mut EventLoopHandle, device: DrmDevice<B>, handler: H)
+                             -> IoResult<FdEventSource<(DrmDevice<B>, H)>>
 where
-    H: DrmHandler + 'static,
+    B: Deref<Target = DrmBackend> + 'static,
+    H: DrmHandler<B> + 'static,
 {
     evlh.add_fd_event_source(
         device.as_raw_fd(),
@@ -488,28 +483,44 @@ where
     )
 }
 
-fn fd_event_source_implementation<H>() -> FdEventSourceImpl<(DrmDevice, H)>
+fn fd_event_source_implementation<B, H>() -> FdEventSourceImpl<(DrmDevice<B>, H)>
 where
-    H: DrmHandler + 'static,
+    B: Deref<Target = DrmBackend> + 'static,
+    H: DrmHandler<B> + 'static,
 {
     FdEventSourceImpl {
         ready: |evlh, id, _, _| {
             use std::any::Any;
 
-            let &mut (ref dev, ref mut handler) = id;
-
-            struct PageFlipHandler<'a, 'b, H: DrmHandler + 'static>(&'a mut H, &'b mut EventLoopHandle);
-
-            impl<'a, 'b, H: DrmHandler + 'static> crtc::PageFlipHandler<DrmDevice> for PageFlipHandler<'a, 'b, H> {
-                fn handle_event(&mut self, device: &DrmDevice, frame: u32, duration: Duration,
+            let &mut (ref mut dev, ref mut handler) = id;
+
+            struct PageFlipHandler<
+                'a,
+                'b,
+                B: Deref<Target = DrmBackend> + 'static,
+                H: DrmHandler<B> + 'static,
+            > {
+                handler: &'a mut H,
+                evlh: &'b mut EventLoopHandle,
+                _marker: PhantomData<B>,
+            };
+
+            impl<'a, 'b, B, H> crtc::PageFlipHandler<DrmDevice<B>> for PageFlipHandler<'a, 'b, B, H>
+            where
+                B: Deref<Target = DrmBackend> + 'static,
+                H: DrmHandler<B> + 'static,
+            {
+                fn handle_event(&mut self, device: &mut DrmDevice<B>, frame: u32, duration: Duration,
                                 userdata: Box<Any>) {
-                    let id: Id = *userdata.downcast().unwrap();
-                    if let Some(backend) = device.backends[id.raw()].upgrade() {
+                    let crtc_id: crtc::Handle = *userdata.downcast().unwrap();
+                    let token = device.backends.get(&crtc_id).cloned();
+                    if let Some(token) = token {
                         // we can now unlock the buffer
-                        backend.borrow().unlock_buffer();
-                        trace!(device.logger, "Handling event for backend {:?}", id.raw());
+                        (**self.evlh.state().get(&token)).unlock_buffer();
+                        trace!(device.logger, "Handling event for backend {:?}", crtc_id);
                         // and then call the user to render the next frame
-                        self.0.ready(self.1, id, frame, duration);
+                        self.handler
+                            .ready(self.evlh, device, &token, frame, duration);
                     }
                 }
             }
@@ -518,13 +529,17 @@ where
                 dev,
                 2,
                 None::<&mut ()>,
-                Some(&mut PageFlipHandler(handler, evlh)),
+                Some(&mut PageFlipHandler {
+                    handler,
+                    evlh,
+                    _marker: PhantomData,
+                }),
                 None::<&mut ()>,
             ).unwrap();
         },
         error: |evlh, id, _, error| {
             warn!(id.0.logger, "DrmDevice errored: {}", error);
-            id.1.error(evlh, error);
+            id.1.error(evlh, &mut id.0, error);
         },
     }
 }

Store DrmBackend in EventLoop state