diff --git a/src/backend/drm/mod.rs b/src/backend/drm/mod.rs index 82d3c54..b583279 100644 --- a/src/backend/drm/mod.rs +++ b/src/backend/drm/mod.rs @@ -1,3 +1,40 @@ +//! +//! +//! This module provides Traits reprensentating open devices +//! and their surfaces to render contents. +//! +//! --- +//! +//! Initialization of devices happens through an open file descriptor +//! of a drm device. +//! +//! --- +//! +//! Initialization of surfaces happens through the types provided by +//! [`drm-rs`](https://docs.rs/drm/0.3.4/drm/). +//! +//! Four entities are relevant for the initialization procedure. +//! +//! [`crtc`](https://docs.rs/drm/0.3.4/drm/control/crtc/index.html)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 independant output devices the hardware may handle. +//! +//! An [`encoder`](https://docs.rs/drm/0.3.4/drm/control/encoder/index.html) 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`](https://docs.rs/drm/0.3.4/drm/control/connector/index.html) represents a port +//! on your computer, possibly with a connected monitor, TV, capture card, etc. +//! +//! On surface creation a matching encoder for your `encoder`-`connector` is automatically selected, +//! if it exists, which means you still need to check your configuration. +//! +//! At last a [`Mode`](https://docs.rs/drm/0.3.4/drm/control/struct.Mode.html) needs to be selected, +//! supported by the `crtc` in question. +//! + pub use drm::{ control::{connector, crtc, framebuffer, Device as ControlDevice, Mode, ResourceHandles, ResourceInfo}, Device as BasicDevice, @@ -23,55 +60,155 @@ pub mod gbm; #[cfg(feature = "backend_drm_legacy")] pub mod legacy; +/// Trait to receive events of a bound [`Device`](trait.Device.html) +/// +/// See [`device_bind`](fn.device_bind.html) pub trait DeviceHandler { + /// The [`Device`](trait.Device.html) type this handler can handle type Device: Device + ?Sized; + + /// A vblank blank event on the provided crtc has happend fn vblank(&mut self, crtc: crtc::Handle); + /// An error happend while processing events fn error(&mut self, error: <<::Device as Device>::Surface as Surface>::Error); } +/// An open drm device pub trait Device: AsRawFd + DevPath { + /// Associated [`Surface`](trait.Surface.html) of this `Device` type type Surface: Surface; + /// Returns the `id` of this device node. fn device_id(&self) -> dev_t; + + /// Assigns a `DeviceHandler` called during event processing. + /// + /// See [`device_bind`](fn.device_bind.html) and [`DeviceHandler`](trait.DeviceHandler.html) fn set_handler(&mut self, handler: impl DeviceHandler + 'static); + /// Clear a set [`DeviceHandler`](trait.DeviceHandler.html), if any fn clear_handler(&mut self); + + /// Creates a new rendering surface. + /// + /// Initialization of surfaces happens through the types provided by + /// [`drm-rs`](https://docs.rs/drm/0.3.4/drm/). + /// + /// [`crtc`](https://docs.rs/drm/0.3.4/drm/control/crtc/index.html)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 independant output devices the hardware may handle. fn create_surface( &mut self, ctrc: crtc::Handle, ) -> Result::Error>; + + /// Processes any open events of the underlying file descriptor. + /// + /// You should not call this function manually, but rather use + /// [`device_bind`](fn.device_bind.html) to register the device + /// to an [`EventLoop`](https://docs.rs/calloop/0.4.2/calloop/struct.EventLoop.html) + /// to synchronize your rendering to the vblank events of the open crtc's fn process_events(&mut self); + + /// Load the resource from a `Device` given its + /// [`ResourceHandle`](https://docs.rs/drm/0.3.4/drm/control/trait.ResourceHandle.html) fn resource_info( &self, handle: T::Handle, ) -> Result::Error>; + + /// Attempts to acquire a copy of the `Device`'s + /// [`ResourceHandles`](https://docs.rs/drm/0.3.4/drm/control/struct.ResourceHandles.html) fn resource_handles(&self) -> Result::Error>; } +/// Marker trait for `Device`s able to provide [`RawSurface`](trait.RawSurface.html)s pub trait RawDevice: Device::Surface> { + /// Associated [`RawSurface`](trait.RawSurface.html) of this `RawDevice` type type Surface: RawSurface; } +/// An open crtc that can be used for rendering pub trait Surface { + /// Type repesenting a collection of + /// [`connector`](https://docs.rs/drm/0.3.4/drm/control/connector/index.html)s + /// returned by [`current_connectors`](#method.current_connectors) and + /// [`pending_connectors`](#method.pending_connectors) type Connectors: IntoIterator; + /// Error type returned by methods of this trait type Error: Error + Send; + /// Returns the underlying [`crtc`](https://docs.rs/drm/0.3.4/drm/control/crtc/index.html) of this surface fn crtc(&self) -> crtc::Handle; + /// Currently used [`connector`](https://docs.rs/drm/0.3.4/drm/control/connector/index.html)s of this `Surface` fn current_connectors(&self) -> Self::Connectors; + /// Returns the pending [`connector`](https://docs.rs/drm/0.3.4/drm/control/connector/index.html)s + /// used after the next `commit` of this `Surface` + /// + /// *Note*: Only on a [`RawSurface`](trait.RawSurface.html) you may directly trigger + /// a [`commit`](trait.RawSurface.html#method.commit). Other `Surface`s provide their + /// own methods that *may* trigger a commit, you will need to read their docs. fn pending_connectors(&self) -> Self::Connectors; + /// Tries to add a new [`connector`](https://docs.rs/drm/0.3.4/drm/control/connector/index.html) + /// to be used after the next commit. + /// + /// Fails if the `connector` is not compatible with the underlying [`crtc`](https://docs.rs/drm/0.3.4/drm/control/crtc/index.html) + /// (e.g. no suitable [`encoder`](https://docs.rs/drm/0.3.4/drm/control/encoder/index.html) may be found) + /// or is not compatible with the currently pending + /// [`Mode`](https://docs.rs/drm/0.3.4/drm/control/struct.Mode.html). fn add_connector(&self, connector: connector::Handle) -> Result<(), Self::Error>; + /// Tries to mark a [`connector`](https://docs.rs/drm/0.3.4/drm/control/connector/index.html) + /// for removal on the next commit. fn remove_connector(&self, connector: connector::Handle) -> Result<(), Self::Error>; + /// Returns the currently active [`Mode`](https://docs.rs/drm/0.3.4/drm/control/struct.Mode.html) + /// of the underlying [`crtc`](https://docs.rs/drm/0.3.4/drm/control/crtc/index.html) + /// if any. fn current_mode(&self) -> Option; + /// Returns the currently pending [`Mode`](https://docs.rs/drm/0.3.4/drm/control/struct.Mode.html) + /// to be used after the next commit, if any. fn pending_mode(&self) -> Option; + /// Tries to set a new [`Mode`](https://docs.rs/drm/0.3.4/drm/control/struct.Mode.html) + /// to be used after the next commit. + /// + /// Fails if the mode is not compatible with the underlying + /// [`crtc`](https://docs.rs/drm/0.3.4/drm/control/crtc/index.html) or any of the + /// pending [`connector`](https://docs.rs/drm/0.3.4/drm/control/connector/index.html)s. + /// + /// *Note*: Only on a [`RawSurface`](trait.RawSurface.html) you may directly trigger + /// a [`commit`](trait.RawSurface.html#method.commit). Other `Surface`s provide their + /// own methods that *may* trigger a commit, you will need to read their docs. fn use_mode(&self, mode: Option) -> Result<(), Self::Error>; } +/// An open bare crtc without any rendering abstractions pub trait RawSurface: Surface + ControlDevice + BasicDevice { + /// Returns true whenever any state changes are pending to be commited + /// + /// The following functions may trigger a pending commit: + /// - [`add_connector`](trait.Surface.html#method.add_connector) + /// - [`remove_connector`](trait.Surface.html#method.remove_connector) + /// - [`use_mode`](trait.Surface.html#method.use_mode) fn commit_pending(&self) -> bool; + /// Commit the pending state rendering a given framebuffer. + /// + /// *Note*: This will trigger a full modeset on the underlying device, + /// potentially causing some flickering. Check before performing this + /// operation if a commit really is necessary using [`commit_pending`](#method.commit_pending). + /// + /// This operation is blocking until the crtc is in the desired state. fn commit(&self, framebuffer: framebuffer::Handle) -> Result<(), ::Error>; + /// Page-flip the underlying [`crtc`](https://docs.rs/drm/0.3.4/drm/control/crtc/index.html) + /// to a new given [`framebuffer`]. + /// + /// This will not cause the crtc to modeset. + /// + /// This operation is not blocking and will produce a `vblank` event once swapping is done. + /// Make sure to [set a `DeviceHandler`](trait.Device.html#method.set_handler) and + /// [register the belonging `Device`](fn.device_bind.html) before to receive the event in time. fn page_flip(&self, framebuffer: framebuffer::Handle) -> Result<(), SwapBuffersError>; } -/// Trait for types representing open devices +/// Trait representing open devices that *may* return a `Path` pub trait DevPath { /// Returns the path of the open device if possible fn dev_path(&self) -> Option; @@ -87,7 +224,8 @@ impl DevPath for A { /// Bind a `Device` to an `EventLoop`, /// -/// This will cause it to recieve events and feed them into an `DeviceHandler` +/// This will cause it to recieve events and feed them into a previously +/// set [`DeviceHandler`](trait.DeviceHandler.html). pub fn device_bind( handle: &LoopHandle, device: D,