<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;

//! let crtc = encoder_info.current_crtc()
//!     // or use the first one that is compatible with the encoder
//!     .unwrap_or_else(||
//!         *res_handles.crtcs()
//!         .iter()
//!         .find(|crtc| encoder_info.supports_crtc(**crtc))
//!         .unwrap());
//!
//! // Use first mode (usually the highest resolution)
//! let mode = connector_info.modes()[0];
#[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(),
//! #
//! # use drm::control::{Device as ControlDevice, ResourceInfo};
//! # use drm::control::connector::{Info as ConnectorInfo, State as ConnectorState};
//! use std::io::Error as IoError;
//! # use std::fs::OpenOptions;
//! # use std::time::Duration;
//! use smithay::backend::drm::{DrmDevice, DrmBackend, DrmHandler, drm_device_bind};
            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,
//!     fn error(&mut self,
//!              _: &mut EventLoopHandle,
//!              device: &mut DrmDevice<DrmBackend>,
//!              error: IoError)
//!     {
//!         panic!("DrmDevice errored: {}", error);
//!     }
use drm::Device as BasicDevice;
use drm::control::{connector, crtc, encoder, Mode, ResourceInfo};
use drm::control::Device as ControlDevice;
use gbm::Device as GbmDevice;
use nix;
use std::borrow::Borrow;
use std::collections::HashMap;
use std::fs::File;
use std::io::{Error as IoError, Result as IoResult};
use std::marker::PhantomData;
use std::os::unix::io::{AsRawFd, RawFd};
use std::rc::Rc;
use std::time::Duration;
            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<B>>(drm).chain_err(|| ErrorKind::GbmInitFailed)
                })?),
                |devices| {
                    debug!(log, "Creating egl context from gbm device");
                .collect::<Result<Vec<encoder::Info>>>()?;

            // and if any encoder supports the selected crtc
            if !encoders.iter().any(|encoder| encoder.supports_crtc(crtc)) {
                bail!(ErrorKind::NoSuitableEncoder(con_info, crtc))
            }
        }
    ///
    /// The related backends are most likely *not* usable anymore and
    /// the whole stack has to be recreated.
    fn error(&mut self, evlh: &mut EventLoopHandle, device: &mut DrmDevice<B>, error: IoError);
}

/// Bind a `DrmDevice` to an EventLoop,
{
    FdEventSourceImpl {
        ready: |evlh, id, _, _| {
            use std::any::Any;

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

            struct PageFlipHandler<'a, 'b, B: Borrow<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: Borrow<DrmBackend> + 'static,
                H: DrmHandler<B> + 'static,
            {
                fn handle_event(&mut self, device: &mut DrmDevice<B>, frame: u32, duration: Duration,
                                userdata: Box<Any>) {
                    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
                        self.evlh.state().get(&token).borrow().unlock_buffer();
                        trace!(device.logger, "Handling event for backend {:?}", crtc_id);
                        // and then call the user to render the next frame
                        self.handler
                            .ready(self.evlh, device, &token, frame, duration);
                    }
                }
            }

            crtc::handle_event(
                dev,
                2,
                None::<&mut ()>,
                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, &mut id.0, error);
        },
    }
}

<MSG> Update to stable drm-rs version

<DFF> @@ -80,10 +80,10 @@
 //! let crtc = encoder_info.current_crtc()
 //!     // or use the first one that is compatible with the encoder
 //!     .unwrap_or_else(||
-//!         *res_handles.crtcs()
-//!         .iter()
-//!         .find(|crtc| encoder_info.supports_crtc(**crtc))
-//!         .unwrap());
+//!         *res_handles.filter_crtcs(encoder_info.possible_crtcs())
+//!           .iter()
+//!           .next()
+//!           .unwrap());
 //!
 //! // Use first mode (usually the highest resolution)
 //! let mode = connector_info.modes()[0];
@@ -118,7 +118,7 @@
 //! #
 //! # use drm::control::{Device as ControlDevice, ResourceInfo};
 //! # use drm::control::connector::{Info as ConnectorInfo, State as ConnectorState};
-//! use std::io::Error as IoError;
+//! use drm::result::Error as DrmError;
 //! # use std::fs::OpenOptions;
 //! # use std::time::Duration;
 //! use smithay::backend::drm::{DrmDevice, DrmBackend, DrmHandler, drm_device_bind};
@@ -167,7 +167,7 @@
 //!     fn error(&mut self,
 //!              _: &mut EventLoopHandle,
 //!              device: &mut DrmDevice<DrmBackend>,
-//!              error: IoError)
+//!              error: DrmError)
 //!     {
 //!         panic!("DrmDevice errored: {}", error);
 //!     }
@@ -186,13 +186,13 @@ use backend::graphics::egl::{EGLContext, GlAttributes, PixelFormatRequirements};
 use drm::Device as BasicDevice;
 use drm::control::{connector, crtc, encoder, Mode, ResourceInfo};
 use drm::control::Device as ControlDevice;
+use drm::result::Error as DrmError;
 use gbm::Device as GbmDevice;
 use nix;
 use std::borrow::Borrow;
 use std::collections::HashMap;
 use std::fs::File;
-use std::io::{Error as IoError, Result as IoResult};
-use std::marker::PhantomData;
+use std::io::Result as IoResult;
 use std::os::unix::io::{AsRawFd, RawFd};
 use std::rc::Rc;
 use std::time::Duration;
@@ -356,7 +356,7 @@ impl<B: From<DrmBackend> + Borrow<DrmBackend> + 'static> DrmDevice<B> {
             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<B>>(drm).chain_err(|| ErrorKind::GbmInitFailed)
+                    GbmDevice::new_from_drm(drm).chain_err(|| ErrorKind::GbmInitFailed)
                 })?),
                 |devices| {
                     debug!(log, "Creating egl context from gbm device");
@@ -420,7 +420,15 @@ impl<B: From<DrmBackend> + Borrow<DrmBackend> + 'static> DrmDevice<B> {
                 .collect::<Result<Vec<encoder::Info>>>()?;
 
             // and if any encoder supports the selected crtc
-            if !encoders.iter().any(|encoder| encoder.supports_crtc(crtc)) {
+            let resource_handles = self.resource_handles().chain_err(|| {
+                ErrorKind::DrmDev(format!("{:?}", self.context.head().head()))
+            })?;
+            if !encoders
+                .iter()
+                .map(|encoder| encoder.possible_crtcs())
+                .all(|crtc_list| {
+                    resource_handles.filter_crtcs(crtc_list).contains(&crtc)
+                }) {
                 bail!(ErrorKind::NoSuitableEncoder(con_info, crtc))
             }
         }
@@ -460,7 +468,7 @@ pub trait DrmHandler<B: Borrow<DrmBackend> + 'static> {
     ///
     /// The related backends are most likely *not* usable anymore and
     /// the whole stack has to be recreated.
-    fn error(&mut self, evlh: &mut EventLoopHandle, device: &mut DrmDevice<B>, error: IoError);
+    fn error(&mut self, evlh: &mut EventLoopHandle, device: &mut DrmDevice<B>, error: DrmError);
 }
 
 /// Bind a `DrmDevice` to an EventLoop,
@@ -487,51 +495,31 @@ where
 {
     FdEventSourceImpl {
         ready: |evlh, id, _, _| {
-            use std::any::Any;
-
             let &mut (ref mut dev, ref mut handler) = id;
 
-            struct PageFlipHandler<'a, 'b, B: Borrow<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: Borrow<DrmBackend> + 'static,
-                H: DrmHandler<B> + 'static,
-            {
-                fn handle_event(&mut self, device: &mut DrmDevice<B>, frame: u32, duration: Duration,
-                                userdata: Box<Any>) {
-                    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
-                        self.evlh.state().get(&token).borrow().unlock_buffer();
-                        trace!(device.logger, "Handling event for backend {:?}", crtc_id);
-                        // and then call the user to render the next frame
-                        self.handler
-                            .ready(self.evlh, device, &token, frame, duration);
+            let events = crtc::receive_events(dev);
+            match events {
+                Ok(events) => for event in events {
+                    match event {
+                        crtc::Event::PageFlip(event) => {
+                            let token = dev.backends.get(&event.crtc).cloned();
+                            if let Some(token) = token {
+                                // we can now unlock the buffer
+                                evlh.state().get(&token).borrow().unlock_buffer();
+                                trace!(dev.logger, "Handling event for backend {:?}", event.crtc);
+                                // and then call the user to render the next frame
+                                handler.ready(evlh, dev, &token, event.frame, event.duration);
+                            }
+                        }
+                        _ => {}
                     }
-                }
-            }
-
-            crtc::handle_event(
-                dev,
-                2,
-                None::<&mut ()>,
-                Some(&mut PageFlipHandler {
-                    handler,
-                    evlh,
-                    _marker: PhantomData,
-                }),
-                None::<&mut ()>,
-            ).unwrap();
+                },
+                Err(err) => return handler.error(evlh, dev, err),
+            };
         },
         error: |evlh, id, _, error| {
             warn!(id.0.logger, "DrmDevice errored: {}", error);
-            id.1.error(evlh, &mut id.0, error);
+            id.1.error(evlh, &mut id.0, error.into());
         },
     }
 }

Update to stable drm-rs version