diff --git a/src/backend/drm/common/fallback.rs b/src/backend/drm/common/fallback.rs new file mode 100644 index 0000000..69679a9 --- /dev/null +++ b/src/backend/drm/common/fallback.rs @@ -0,0 +1,347 @@ +//! +//! Types to make fallback device initialization easier +//! + +#[cfg(all(feature = "backend_drm_atomic", feature = "backend_drm_legacy"))] +use crate::backend::drm::{atomic::AtomicDrmDevice, legacy::LegacyDrmDevice}; +use crate::backend::drm::{common::Error, Device, DeviceHandler, RawDevice, RawSurface, Surface}; +use crate::backend::egl::Error as EGLError; +#[cfg(feature = "use_system_lib")] +use crate::backend::egl::{display::EGLBufferReader, EGLGraphicsBackend}; +#[cfg(feature = "renderer_gl")] +use crate::backend::graphics::gl::GLGraphicsBackend; +#[cfg(feature = "renderer_gl")] +use crate::backend::graphics::PixelFormat; +use crate::backend::graphics::{CursorBackend, SwapBuffersError}; +use crate::backend::session::{AsSessionObserver, SessionObserver}; + +use drm::{ + control::{connector, crtc, encoder, framebuffer, plane, Device as ControlDevice, Mode, ResourceHandles}, + Device as BasicDevice, SystemError as DrmError, +}; +#[cfg(feature = "renderer_gl")] +use nix::libc::c_void; +use nix::libc::dev_t; +use std::os::unix::io::{AsRawFd, RawFd}; +#[cfg(feature = "use_system_lib")] +use wayland_server::Display; + +/// [`Device`](::backend::drm::Device) Wrapper to assist fallback +/// in case initialization of the preferred device type fails. +pub enum FallbackDevice { + /// Variant for successful initialization of the preferred device + Preference(D1), + /// Variant for the fallback device + Fallback(D2), +} + +struct FallbackDeviceHandlerD1( + Box> + 'static>, +) +where + E: std::error::Error + Send + 'static, + C: IntoIterator + 'static, + S1: Surface + 'static, + S2: Surface + 'static, + D1: Device + 'static, + D2: Device + 'static; + +impl DeviceHandler for FallbackDeviceHandlerD1 +where + E: std::error::Error + Send + 'static, + C: IntoIterator + 'static, + S1: Surface + 'static, + S2: Surface + 'static, + D1: Device + 'static, + D2: Device + 'static, +{ + type Device = D1; + + fn vblank(&mut self, crtc: crtc::Handle) { + self.0.vblank(crtc) + } + fn error(&mut self, error: E) { + self.0.error(error); + } +} + +struct FallbackDeviceHandlerD2( + Box> + 'static>, +) +where + E: std::error::Error + Send + 'static, + C: IntoIterator + 'static, + S1: Surface + 'static, + S2: Surface + 'static, + D1: Device + 'static, + D2: Device + 'static; + +impl DeviceHandler for FallbackDeviceHandlerD2 +where + E: std::error::Error + Send + 'static, + C: IntoIterator + 'static, + S1: Surface + 'static, + S2: Surface + 'static, + D1: Device + 'static, + D2: Device + 'static, +{ + type Device = D2; + + fn vblank(&mut self, crtc: crtc::Handle) { + self.0.vblank(crtc) + } + fn error(&mut self, error: E) { + self.0.error(error); + } +} + +/// [`SessionObserver`](::backend::session::SessionObserver) Wrapper to assist fallback +/// in case initialization of the preferred device type fails. +pub enum FallbackDeviceObserver { + /// Variant for successful initialization of the preferred device + Preference(O1), + /// Variant for the fallback device + Fallback(O2), +} + +impl AsSessionObserver> for FallbackDevice +where + O1: SessionObserver + 'static, + O2: SessionObserver + 'static, + D1: Device + AsSessionObserver + 'static, + D2: Device + AsSessionObserver + 'static, +{ + fn observer(&mut self) -> FallbackDeviceObserver { + match self { + FallbackDevice::Preference(dev) => FallbackDeviceObserver::Preference(dev.observer()), + FallbackDevice::Fallback(dev) => FallbackDeviceObserver::Fallback(dev.observer()), + } + } +} + +impl SessionObserver + for FallbackDeviceObserver +{ + fn pause(&mut self, device: Option<(u32, u32)>) { + match self { + FallbackDeviceObserver::Preference(dev) => dev.pause(device), + FallbackDeviceObserver::Fallback(dev) => dev.pause(device), + } + } + + fn activate(&mut self, device: Option<(u32, u32, Option)>) { + match self { + FallbackDeviceObserver::Preference(dev) => dev.activate(device), + FallbackDeviceObserver::Fallback(dev) => dev.activate(device), + } + } +} + +/// [`Surface`](::backend::drm::Surface) Wrapper to assist fallback +/// in case initialization of the preferred device type fails. +pub enum FallbackSurface { + /// Variant for successful initialization of the preferred device + Preference(S1), + /// Variant for the fallback device + Fallback(S2), +} + +#[cfg(all(feature = "backend_drm_atomic", feature = "backend_drm_legacy"))] +impl FallbackDevice, LegacyDrmDevice> { + /// Try to initialize an [`AtomicDrmDevice`](::backend::drm:;atomic::AtomicDrmDevice) + /// and fall back to a [`LegacyDrmDevice`] if atomic-modesetting is not supported. + pub fn new(fd: A, logger: L) -> Result + where + L: Into>, + { + let log = crate::slog_or_stdlog(logger).new(o!("smithay_module" => "backend_drm_fallback")); + info!(log, "Trying to initialize AtomicDrmDevice"); + + match AtomicDrmDevice::new(fd.clone(), log.clone()) { + Ok(dev) => Ok(FallbackDevice::Preference(dev)), + Err(err) => { + error!(log, "Failed to initialize preferred AtomicDrmDevice: {}", err); + info!(log, "Falling back to fallback LegacyyDrmDevice"); + Ok(FallbackDevice::Fallback(LegacyDrmDevice::new(fd, log)?)) + } + } + } +} + +macro_rules! fallback_device_impl { + ($func_name:ident, $self:ty, $return:ty, $($arg_name:ident : $arg_ty:ty),*) => { + fn $func_name(self: $self, $($arg_name : $arg_ty),*) -> $return { + match self { + FallbackDevice::Preference(dev) => dev.$func_name($($arg_name),*), + FallbackDevice::Fallback(dev) => dev.$func_name($($arg_name),*), + } + } + }; + ($func_name:ident, $self:ty, $return:ty) => { + fallback_device_impl!($func_name, $self, $return,); + }; + ($func_name:ident, $self:ty) => { + fallback_device_impl!($func_name, $self, ()); + }; +} + +macro_rules! fallback_surface_impl { + ($func_name:ident, $self:ty, $return:ty, $($arg_name:ident : $arg_ty:ty),*) => { + fn $func_name(self: $self, $($arg_name : $arg_ty),*) -> $return { + match self { + FallbackSurface::Preference(dev) => dev.$func_name($($arg_name),*), + FallbackSurface::Fallback(dev) => dev.$func_name($($arg_name),*), + } + } + }; + ($func_name:ident, $self:ty, $return:ty) => { + fallback_surface_impl!($func_name, $self, $return,); + }; + ($func_name:ident, $self:ty) => { + fallback_surface_impl!($func_name, $self, ()); + }; +} + +impl AsRawFd for FallbackDevice { + fallback_device_impl!(as_raw_fd, &Self, RawFd); +} +impl BasicDevice for FallbackDevice {} +impl ControlDevice for FallbackDevice {} + +impl Device for FallbackDevice +where + // Connectors and Error need to match for both Surfaces + E: std::error::Error + Send + 'static, + C: IntoIterator + 'static, + S1: Surface + 'static, + S2: Surface + 'static, + D1: Device + 'static, + D2: Device + 'static, +{ + type Surface = FallbackSurface; + + fallback_device_impl!(device_id, &Self, dev_t); + fn set_handler(&mut self, handler: impl DeviceHandler + 'static) { + match self { + FallbackDevice::Preference(dev) => dev.set_handler(FallbackDeviceHandlerD1(Box::new(handler))), + FallbackDevice::Fallback(dev) => dev.set_handler(FallbackDeviceHandlerD2(Box::new(handler))), + } + } + fallback_device_impl!(clear_handler, &mut Self); + fn create_surface(&mut self, crtc: crtc::Handle) -> Result { + match self { + FallbackDevice::Preference(dev) => Ok(FallbackSurface::Preference(dev.create_surface(crtc)?)), + FallbackDevice::Fallback(dev) => Ok(FallbackSurface::Fallback(dev.create_surface(crtc)?)), + } + } + fallback_device_impl!(process_events, &mut Self); + fallback_device_impl!(resource_handles, &Self, Result); + fallback_device_impl!(get_connector_info, &Self, Result, conn: connector::Handle); + fallback_device_impl!(get_crtc_info, &Self, Result, crtc: crtc::Handle); + fallback_device_impl!(get_encoder_info, &Self, Result, enc: encoder::Handle); + fallback_device_impl!(get_framebuffer_info, &Self, Result, fb: framebuffer::Handle); + fallback_device_impl!(get_plane_info, &Self, Result, plane : plane::Handle); +} + +// Impl RawDevice where underlying types implement RawDevice +impl RawDevice for FallbackDevice +where + // Connectors and Error need to match for both Surfaces + E: std::error::Error + Send + 'static, + C: IntoIterator + 'static, + S1: RawSurface + Surface + 'static, + S2: RawSurface + Surface + 'static, + D1: RawDevice + 'static, + D2: RawDevice + 'static, +{ + type Surface = FallbackSurface; +} + +#[cfg(feature = "use_system_lib")] +impl EGLGraphicsBackend + for FallbackDevice +{ + fallback_device_impl!(bind_wl_display, &Self, Result, display : &Display); +} + +impl Surface for FallbackSurface +where + // Connectors and Error need to match for both Surfaces + E: std::error::Error + Send + 'static, + C: IntoIterator + 'static, + S1: Surface + 'static, + S2: Surface + 'static, +{ + type Error = E; + type Connectors = C; + + fallback_surface_impl!(crtc, &Self, crtc::Handle); + fallback_surface_impl!(current_connectors, &Self, C); + fallback_surface_impl!(pending_connectors, &Self, C); + fallback_surface_impl!(add_connector, &Self, Result<(), E>, conn: connector::Handle); + fallback_surface_impl!(remove_connector, &Self, Result<(), E>, conn: connector::Handle); + fallback_surface_impl!(set_connectors, &Self, Result<(), E>, conns: &[connector::Handle]); + fallback_surface_impl!(current_mode, &Self, Option); + fallback_surface_impl!(pending_mode, &Self, Option); + fallback_surface_impl!(use_mode, &Self, Result<(), E>, mode: Option); +} + +impl RawSurface for FallbackSurface +where + E: std::error::Error + Send + 'static, + C: IntoIterator + 'static, + S1: RawSurface + Surface + 'static, + S2: RawSurface + Surface + 'static, +{ + fallback_surface_impl!(commit_pending, &Self, bool); + fallback_surface_impl!(commit, &Self, Result<(), E>, fb: framebuffer::Handle); + fn page_flip(&self, framebuffer: framebuffer::Handle) -> Result<(), SwapBuffersError> { + match self { + FallbackSurface::Preference(dev) => RawSurface::page_flip(dev, framebuffer), + FallbackSurface::Fallback(dev) => RawSurface::page_flip(dev, framebuffer), + } + } +} + +impl AsRawFd for FallbackSurface { + fallback_surface_impl!(as_raw_fd, &Self, RawFd); +} +impl BasicDevice for FallbackSurface {} +impl ControlDevice for FallbackSurface {} + +impl CursorBackend for FallbackSurface +where + E1: std::error::Error + Send + 'static, + E2: 'static, + CF: ?Sized, + C: IntoIterator + 'static, + S1: Surface + CursorBackend + 'static, + S2: Surface + CursorBackend + 'static, +{ + type CursorFormat = CF; + type Error = E2; + + fallback_surface_impl!(set_cursor_position, &Self, Result<(), E2>, x: u32, y: u32); + fallback_surface_impl!(set_cursor_representation, &Self, Result<(), E2>, buffer: &Self::CursorFormat, hotspot: (u32, u32)); +} + +#[cfg(feature = "renderer_gl")] +impl GLGraphicsBackend for FallbackSurface +where + E: std::error::Error + Send + 'static, + C: IntoIterator + 'static, + S1: Surface + GLGraphicsBackend + 'static, + S2: Surface + GLGraphicsBackend + 'static, +{ + fallback_surface_impl!(swap_buffers, &Self, Result<(), SwapBuffersError>); + fallback_surface_impl!(get_proc_address, &Self, *const c_void, symbol: &str); + fallback_surface_impl!(get_framebuffer_dimensions, &Self, (u32, u32)); + fallback_surface_impl!(is_current, &Self, bool); + unsafe fn make_current(&self) -> Result<(), SwapBuffersError> { + match self { + FallbackSurface::Preference(dev) => dev.make_current(), + FallbackSurface::Fallback(dev) => dev.make_current(), + } + } + fallback_surface_impl!(get_pixel_format, &Self, PixelFormat); +} diff --git a/src/backend/drm/common/mod.rs b/src/backend/drm/common/mod.rs index 9e89a0b..83ef7c2 100644 --- a/src/backend/drm/common/mod.rs +++ b/src/backend/drm/common/mod.rs @@ -1,12 +1,14 @@ //! //! Module for common/shared types of the various [`Device`](::backend::drm::Device) //! and [`Surface`](::backend::drm::Surface) implementations of the `backend::drm` module. -//! +//! use drm::control::{connector, crtc, Mode, RawResourceHandle}; use std::path::PathBuf; +pub mod fallback; + /// Errors thrown by the [`LegacyDrmDevice`](::backend::drm::legacy::LegacyDrmDevice), /// [`AtomicDrmDevice`](::backend::drm::atomic::AtomicDrmDevice) /// and their surfaces: [`LegacyDrmSurface`](::backend::drm::legacy::LegacyDrmSurface)