Merge pull request #194 from Smithay/feature/atomic_modesetting

Atomic modesetting support
This commit is contained in:
Victor Brekenfeld 2020-04-21 20:30:05 +02:00 committed by GitHub
commit 27a73888a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 2255 additions and 239 deletions

View File

@ -45,9 +45,10 @@ slog-term = "2.3"
gl_generator = { version = "0.14", optional = true } gl_generator = { version = "0.14", optional = true }
[features] [features]
default = ["backend_winit", "backend_drm_legacy", "backend_drm_gbm", "backend_drm_egl", "backend_libinput", "backend_udev", "backend_session_logind", "renderer_glium", "xwayland", "wayland_frontend"] default = ["backend_winit", "backend_drm_legacy", "backend_drm_atomic", "backend_drm_gbm", "backend_drm_egl", "backend_libinput", "backend_udev", "backend_session_logind", "renderer_glium", "xwayland", "wayland_frontend"]
backend_winit = ["winit", "wayland-server/dlopen", "backend_egl", "wayland-egl", "renderer_gl", "use_system_lib"] backend_winit = ["winit", "wayland-server/dlopen", "backend_egl", "wayland-egl", "renderer_gl", "use_system_lib"]
backend_drm = ["drm", "failure"] backend_drm = ["drm", "failure"]
backend_drm_atomic = ["backend_drm"]
backend_drm_legacy = ["backend_drm"] backend_drm_legacy = ["backend_drm"]
backend_drm_gbm = ["backend_drm", "gbm", "image"] backend_drm_gbm = ["backend_drm", "gbm", "image"]
backend_drm_egl = ["backend_drm", "backend_egl"] backend_drm_egl = ["backend_drm", "backend_egl"]
@ -63,5 +64,5 @@ wayland_frontend = ["wayland-server", "wayland-commons", "wayland-protocols"]
xwayland = ["wayland_frontend"] xwayland = ["wayland_frontend"]
[[example]] [[example]]
name = "raw_drm" name = "raw_legacy_drm"
required-features = ["backend_drm_legacy"] required-features = ["backend_drm_legacy"]

View File

@ -29,5 +29,5 @@ gl_generator = "0.14"
default = [ "winit", "egl", "udev", "logind" ] default = [ "winit", "egl", "udev", "logind" ]
egl = [ "smithay/use_system_lib" ] egl = [ "smithay/use_system_lib" ]
winit = [ "smithay/backend_winit" ] winit = [ "smithay/backend_winit" ]
udev = [ "smithay/backend_libinput", "smithay/backend_udev", "smithay/backend_drm_legacy", "smithay/backend_drm_gbm", "smithay/backend_drm_egl", "smithay/backend_session", "input" ] udev = [ "smithay/backend_libinput", "smithay/backend_udev", "smithay/backend_drm_atomic", "smithay/backend_drm_legacy", "smithay/backend_drm_gbm", "smithay/backend_drm_egl", "smithay/backend_session", "input" ]
logind = [ "smithay/backend_session_logind" ] logind = [ "smithay/backend_session_logind" ]

View File

@ -19,6 +19,8 @@ use smithay::backend::egl::{display::EGLBufferReader, EGLGraphicsBackend};
use smithay::{ use smithay::{
backend::{ backend::{
drm::{ drm::{
atomic::AtomicDrmDevice,
common::fallback::FallbackDevice,
device_bind, device_bind,
egl::{EglDevice, EglSurface}, egl::{EglDevice, EglSurface},
gbm::{egl::Gbm as EglGbmBackend, GbmDevice}, gbm::{egl::Gbm as EglGbmBackend, GbmDevice},
@ -68,6 +70,7 @@ use crate::shell::{init_shell, MyWindowMap, Roles};
use crate::AnvilState; use crate::AnvilState;
use smithay::backend::drm::gbm::GbmSurface; use smithay::backend::drm::gbm::GbmSurface;
#[derive(Clone)]
pub struct SessionFd(RawFd); pub struct SessionFd(RawFd);
impl AsRawFd for SessionFd { impl AsRawFd for SessionFd {
fn as_raw_fd(&self) -> RawFd { fn as_raw_fd(&self) -> RawFd {
@ -75,9 +78,12 @@ impl AsRawFd for SessionFd {
} }
} }
type RenderDevice = type RenderDevice = EglDevice<
EglDevice<EglGbmBackend<LegacyDrmDevice<SessionFd>>, GbmDevice<LegacyDrmDevice<SessionFd>>>; EglGbmBackend<FallbackDevice<AtomicDrmDevice<SessionFd>, LegacyDrmDevice<SessionFd>>>,
type RenderSurface = EglSurface<GbmSurface<LegacyDrmDevice<SessionFd>>>; GbmDevice<FallbackDevice<AtomicDrmDevice<SessionFd>, LegacyDrmDevice<SessionFd>>>,
>;
type RenderSurface =
EglSurface<GbmSurface<FallbackDevice<AtomicDrmDevice<SessionFd>, LegacyDrmDevice<SessionFd>>>>;
pub fn run_udev(mut display: Display, mut event_loop: EventLoop<AnvilState>, log: Logger) -> Result<(), ()> { pub fn run_udev(mut display: Display, mut event_loop: EventLoop<AnvilState>, log: Logger) -> Result<(), ()> {
let name = display.add_socket_auto().unwrap().into_string().unwrap(); let name = display.add_socket_auto().unwrap().into_string().unwrap();
@ -411,9 +417,29 @@ impl<S: SessionNotifier, Data: 'static> UdevHandler for UdevHandlerImpl<S, Data>
OFlag::O_RDWR | OFlag::O_CLOEXEC | OFlag::O_NOCTTY | OFlag::O_NONBLOCK, OFlag::O_RDWR | OFlag::O_CLOEXEC | OFlag::O_NOCTTY | OFlag::O_NONBLOCK,
) )
.ok() .ok()
.and_then(|fd| LegacyDrmDevice::new(SessionFd(fd), self.logger.clone()).ok()) .and_then(
.and_then(|drm| GbmDevice::new(drm, self.logger.clone()).ok()) |fd| match FallbackDevice::new(SessionFd(fd), self.logger.clone()) {
.and_then(|gbm| EglDevice::new(gbm, self.logger.clone()).ok()) Ok(drm) => Some(drm),
Err(err) => {
error!(self.logger, "Skipping drm device, because of error: {}", err);
None
}
},
)
.and_then(|drm| match GbmDevice::new(drm, self.logger.clone()) {
Ok(gbm) => Some(gbm),
Err(err) => {
error!(self.logger, "Skipping gbm device, because of error: {}", err);
None
}
})
.and_then(|gbm| match EglDevice::new(gbm, self.logger.clone()) {
Ok(egl) => Some(egl),
Err(err) => {
error!(self.logger, "Skipping egl device, because of error: {}", err);
None
}
})
{ {
// init hardware acceleration on the primary gpu. // init hardware acceleration on the primary gpu.
#[cfg(feature = "egl")] #[cfg(feature = "egl")]

183
examples/raw_atomic_drm.rs Normal file
View File

@ -0,0 +1,183 @@
#![warn(rust_2018_idioms)]
#[macro_use]
extern crate slog;
use slog::Drain;
use smithay::{
backend::drm::{
atomic::{AtomicDrmDevice, AtomicDrmSurface},
common::Error,
device_bind, Device, DeviceHandler, RawSurface, Surface,
},
reexports::{
calloop::EventLoop,
drm::{
buffer::format::PixelFormat,
control::{
connector::State as ConnectorState, crtc, dumbbuffer::DumbBuffer, framebuffer, property,
Device as ControlDevice, ResourceHandle,
},
},
},
};
use std::{
fs::{File, OpenOptions},
io::Error as IoError,
rc::Rc,
sync::Mutex,
};
fn get_property_by_name<'a, D: ControlDevice, T: ResourceHandle>(
dev: &'a D,
handle: T,
name: &'static str,
) -> Option<(property::ValueType, property::RawValue)> {
let props = dev.get_properties(handle).expect("Could not get props");
let (ids, vals) = props.as_props_and_values();
for (&id, &val) in ids.iter().zip(vals.iter()) {
let info = dev.get_property(id).unwrap();
if info.name().to_str().map(|x| x == name).unwrap_or(false) {
let val_ty = info.value_type();
return Some((val_ty, val));
}
}
None
}
fn main() {
let log = slog::Logger::root(Mutex::new(slog_term::term_full().fuse()).fuse(), o!());
/*
* Initialize the drm backend
*/
// "Find" a suitable drm device
let mut options = OpenOptions::new();
options.read(true);
options.write(true);
let mut device = AtomicDrmDevice::new(options.open("/dev/dri/card0").unwrap(), log.clone()).unwrap();
// Get a set of all modesetting resource handles (excluding planes):
let res_handles = Device::resource_handles(&device).unwrap();
// Use first connected connector
let connector_info = res_handles
.connectors()
.iter()
.map(|conn| device.get_connector_info(*conn).unwrap())
.find(|conn| conn.state() == ConnectorState::Connected)
.unwrap();
// use the connected crtc if any
let (val_ty, raw) = get_property_by_name(&device, connector_info.handle(), "CRTC_ID").unwrap();
let crtc = match val_ty.convert_value(raw) {
property::Value::CRTC(Some(handle)) => handle,
property::Value::CRTC(None) => {
// Use the first encoder
let encoder = connector_info
.encoders()
.iter()
.filter_map(|&e| e)
.next()
.unwrap();
let encoder_info = device.get_encoder_info(encoder).unwrap();
*res_handles
.filter_crtcs(encoder_info.possible_crtcs())
.iter()
.next()
.unwrap()
}
_ => unreachable!("CRTC_ID does not return another property type"),
};
// Assuming we found a good connector and loaded the info into `connector_info`
let mode = connector_info.modes()[0]; // Use first mode (usually highest resoltion, but in reality you should filter and sort and check and match with other connectors, if you use more then one.)
// Initialize the hardware backend
let surface = Rc::new(device.create_surface(crtc).unwrap());
surface.set_connectors(&[connector_info.handle()]).unwrap();
surface.use_mode(Some(mode)).unwrap();
for conn in surface.current_connectors().into_iter() {
if conn != connector_info.handle() {
surface.remove_connector(conn).unwrap();
}
}
surface.add_connector(connector_info.handle()).unwrap();
/*
* Lets create buffers and framebuffers.
* We use drm-rs DumbBuffers, because they always work and require little to no setup.
* But they are very slow, this is just for demonstration purposes.
*/
let (w, h) = mode.size();
let front_buffer = device
.create_dumb_buffer((w as u32, h as u32), PixelFormat::XRGB8888)
.unwrap();
let front_framebuffer = device.add_framebuffer(&front_buffer).unwrap();
let back_buffer = device
.create_dumb_buffer((w as u32, h as u32), PixelFormat::XRGB8888)
.unwrap();
let back_framebuffer = device.add_framebuffer(&back_buffer).unwrap();
device.set_handler(DrmHandlerImpl {
current: front_framebuffer,
front: (front_buffer, front_framebuffer),
back: (back_buffer, back_framebuffer),
surface: surface.clone(),
});
/*
* Register the DrmDevice on the EventLoop
*/
let mut event_loop = EventLoop::<()>::new().unwrap();
let _source = device_bind(&event_loop.handle(), device)
.map_err(|err| -> IoError { err.into() })
.unwrap();
// Start rendering
if surface.commit_pending() {
surface.commit(front_framebuffer).unwrap();
}
// Run
event_loop.run(None, &mut (), |_| {}).unwrap();
}
pub struct DrmHandlerImpl {
front: (DumbBuffer, framebuffer::Handle),
back: (DumbBuffer, framebuffer::Handle),
current: framebuffer::Handle,
surface: Rc<AtomicDrmSurface<File>>,
}
impl DeviceHandler for DrmHandlerImpl {
type Device = AtomicDrmDevice<File>;
fn vblank(&mut self, _crtc: crtc::Handle) {
{
// Swap and map buffer
let mut mapping = if self.current == self.front.1 {
self.current = self.back.1;
self.surface.map_dumb_buffer(&mut self.back.0).unwrap()
} else {
self.current = self.front.1;
self.surface.map_dumb_buffer(&mut self.front.0).unwrap()
};
// now we could render to the mapping via software rendering.
// this example just sets some grey color
for x in mapping.as_mut() {
*x = 128;
}
}
RawSurface::page_flip(&*self.surface, self.current).unwrap();
}
fn error(&mut self, error: Error) {
panic!("{:?}", error);
}
}

View File

@ -6,8 +6,9 @@ extern crate slog;
use slog::Drain; use slog::Drain;
use smithay::{ use smithay::{
backend::drm::{ backend::drm::{
common::Error,
device_bind, device_bind,
legacy::{Error, LegacyDrmDevice, LegacyDrmSurface}, legacy::{LegacyDrmDevice, LegacyDrmSurface},
Device, DeviceHandler, RawSurface, Surface, Device, DeviceHandler, RawSurface, Surface,
}, },
reexports::{ reexports::{
@ -80,13 +81,7 @@ fn main() {
let surface = Rc::new(device.create_surface(crtc).unwrap()); let surface = Rc::new(device.create_surface(crtc).unwrap());
surface.use_mode(Some(mode)).unwrap(); surface.use_mode(Some(mode)).unwrap();
for conn in surface.current_connectors().into_iter() { surface.set_connectors(&[connector_info.handle()]).unwrap();
if conn != connector_info.handle() {
surface.remove_connector(conn).unwrap();
}
}
surface.add_connector(connector_info.handle()).unwrap();
/* /*
* Lets create buffers and framebuffers. * Lets create buffers and framebuffers.
* We use drm-rs DumbBuffers, because they always work and require little to no setup. * We use drm-rs DumbBuffers, because they always work and require little to no setup.
@ -121,7 +116,6 @@ fn main() {
if surface.commit_pending() { if surface.commit_pending() {
surface.commit(front_framebuffer).unwrap(); surface.commit(front_framebuffer).unwrap();
} }
RawSurface::page_flip(&*surface, front_framebuffer).unwrap();
// Run // Run
event_loop.run(None, &mut (), |_| {}).unwrap(); event_loop.run(None, &mut (), |_| {}).unwrap();

View File

@ -0,0 +1,406 @@
//!
//! [`RawDevice`](RawDevice) and [`RawSurface`](RawSurface)
//! implementations using the atomic mode-setting infrastructure.
//!
//! Usually this implementation will wrapped into a [`GbmDevice`](::backend::drm::gbm::GbmDevice).
//! Take a look at `anvil`s source code for an example of this.
//!
//! For an example how to use this standalone, take a look at the raw_atomic_drm example.
//!
use std::cell::RefCell;
use std::collections::HashMap;
use std::os::unix::io::{AsRawFd, RawFd};
use std::rc::{Rc, Weak};
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};
use drm::control::{atomic::AtomicModeReq, AtomicCommitFlags, Device as ControlDevice, Event};
use drm::control::{
connector, crtc, encoder, framebuffer, plane, property, PropertyValueSet, ResourceHandle, ResourceHandles,
};
use drm::SystemError as DrmError;
use drm::{ClientCapability, Device as BasicDevice};
use failure::{Fail, ResultExt};
use nix::libc::dev_t;
use nix::sys::stat::fstat;
use super::{common::Error, DevPath, Device, DeviceHandler, RawDevice};
mod surface;
pub use self::surface::AtomicDrmSurface;
use self::surface::AtomicDrmSurfaceInternal;
#[cfg(feature = "backend_session")]
pub mod session;
/// Open raw drm device utilizing atomic mode-setting
pub struct AtomicDrmDevice<A: AsRawFd + 'static> {
dev: Rc<Dev<A>>,
dev_id: dev_t,
active: Arc<AtomicBool>,
backends: Rc<RefCell<HashMap<crtc::Handle, Weak<AtomicDrmSurfaceInternal<A>>>>>,
handler: Option<RefCell<Box<dyn DeviceHandler<Device = AtomicDrmDevice<A>>>>>,
logger: ::slog::Logger,
}
type OldState = (
Vec<(connector::Handle, PropertyValueSet)>,
Vec<(crtc::Handle, PropertyValueSet)>,
Vec<(framebuffer::Handle, PropertyValueSet)>,
Vec<(plane::Handle, PropertyValueSet)>,
);
type Mapping = (
HashMap<connector::Handle, HashMap<String, property::Handle>>,
HashMap<crtc::Handle, HashMap<String, property::Handle>>,
HashMap<framebuffer::Handle, HashMap<String, property::Handle>>,
HashMap<plane::Handle, HashMap<String, property::Handle>>,
);
struct Dev<A: AsRawFd + 'static> {
fd: A,
privileged: bool,
active: Arc<AtomicBool>,
old_state: OldState,
prop_mapping: Mapping,
logger: ::slog::Logger,
}
impl<A: AsRawFd + 'static> AsRawFd for Dev<A> {
fn as_raw_fd(&self) -> RawFd {
self.fd.as_raw_fd()
}
}
impl<A: AsRawFd + 'static> BasicDevice for Dev<A> {}
impl<A: AsRawFd + 'static> ControlDevice for Dev<A> {}
impl<A: AsRawFd + 'static> Drop for Dev<A> {
fn drop(&mut self) {
info!(self.logger, "Dropping device: {:?}", self.dev_path());
if self.active.load(Ordering::SeqCst) {
// Here we restore the card/tty's to it's previous state.
// In case e.g. getty was running on the tty sets the correct framebuffer again,
// so that getty will be visible.
let mut req = AtomicModeReq::new();
fn add_multiple_props<T: ResourceHandle>(
req: &mut AtomicModeReq,
old_state: &[(T, PropertyValueSet)],
) {
for (handle, set) in old_state {
let (prop_handles, values) = set.as_props_and_values();
for (&prop_handle, &val) in prop_handles.iter().zip(values.iter()) {
req.add_raw_property((*handle).into(), prop_handle, val);
}
}
};
add_multiple_props(&mut req, &self.old_state.0);
add_multiple_props(&mut req, &self.old_state.1);
add_multiple_props(&mut req, &self.old_state.2);
add_multiple_props(&mut req, &self.old_state.3);
if let Err(err) = self.atomic_commit(&[AtomicCommitFlags::AllowModeset], req) {
error!(self.logger, "Failed to restore previous state. Error: {}", err);
}
}
if self.privileged {
if let Err(err) = self.release_master_lock() {
error!(self.logger, "Failed to drop drm master state. Error: {}", err);
}
}
}
}
impl<A: AsRawFd + 'static> Dev<A> {
// Add all properties of given handles to a given drm resource type to state.
// You may use this to snapshot the current state of the drm device (fully or partially).
fn add_props<T>(&self, handles: &[T], state: &mut Vec<(T, PropertyValueSet)>) -> Result<(), Error>
where
A: AsRawFd + 'static,
T: ResourceHandle,
{
let iter = handles.iter().map(|x| (x, self.get_properties(*x)));
if let Some(len) = iter.size_hint().1 {
state.reserve_exact(len)
}
iter.map(|(x, y)| (*x, y))
.try_for_each(|(x, y)| match y {
Ok(y) => {
state.push((x, y));
Ok(())
}
Err(err) => Err(err),
})
.compat()
.map_err(|source| Error::Access {
errmsg: "Error reading properties",
dev: self.dev_path(),
source,
})
}
/// Create a mapping of property names and handles for given handles of a given drm resource type.
/// You may use this to easily lookup properties by name instead of going through this procedure manually.
fn map_props<T>(
&self,
handles: &[T],
mapping: &mut HashMap<T, HashMap<String, property::Handle>>,
) -> Result<(), Error>
where
A: AsRawFd + 'static,
T: ResourceHandle + Eq + std::hash::Hash,
{
handles
.iter()
.map(|x| (x, self.get_properties(*x)))
.try_for_each(|(handle, props)| {
let mut map = HashMap::new();
match props {
Ok(props) => {
let (prop_handles, _) = props.as_props_and_values();
for prop in prop_handles {
if let Ok(info) = self.get_property(*prop) {
let name = info.name().to_string_lossy().into_owned();
map.insert(name, *prop);
}
}
mapping.insert(*handle, map);
Ok(())
}
Err(err) => Err(err),
}
})
.compat()
.map_err(|source| Error::Access {
errmsg: "Error reading properties on {:?}",
dev: self.dev_path(),
source,
})
}
}
impl<A: AsRawFd + 'static> AtomicDrmDevice<A> {
/// Create a new [`AtomicDrmDevice`] from an open drm node
///
/// Returns an error if the file is no valid drm node or context creation was not
/// successful.
pub fn new<L>(fd: A, logger: L) -> Result<Self, Error>
where
L: Into<Option<::slog::Logger>>,
{
let log = crate::slog_or_stdlog(logger).new(o!("smithay_module" => "backend_drm"));
info!(log, "AtomicDrmDevice initializing");
let dev_id = fstat(fd.as_raw_fd()).map_err(Error::UnableToGetDeviceId)?.st_rdev;
let active = Arc::new(AtomicBool::new(true));
let mut dev = Dev {
fd,
privileged: true,
active: active.clone(),
old_state: (Vec::new(), Vec::new(), Vec::new(), Vec::new()),
prop_mapping: (HashMap::new(), HashMap::new(), HashMap::new(), HashMap::new()),
logger: log.clone(),
};
// we want to modeset, so we better be the master, if we run via a tty session
if dev.acquire_master_lock().is_err() {
warn!(log, "Unable to become drm master, assuming unprivileged mode");
dev.privileged = false;
};
// enable the features we need
dev.set_client_capability(ClientCapability::UniversalPlanes, true)
.compat()
.map_err(|source| Error::Access {
errmsg: "Error enabling UniversalPlanes",
dev: dev.dev_path(),
source,
})?;
dev.set_client_capability(ClientCapability::Atomic, true)
.compat()
.map_err(|source| Error::Access {
errmsg: "Error enabling AtomicModesetting",
dev: dev.dev_path(),
source,
})?;
// enumerate (and save) the current device state
let res_handles = ControlDevice::resource_handles(&dev)
.compat()
.map_err(|source| Error::Access {
errmsg: "Error loading drm resources",
dev: dev.dev_path(),
source,
})?;
let plane_handles = dev.plane_handles().compat().map_err(|source| Error::Access {
errmsg: "Error loading planes",
dev: dev.dev_path(),
source,
})?;
let planes = plane_handles.planes();
let mut old_state = dev.old_state.clone();
let mut mapping = dev.prop_mapping.clone();
dev.add_props(res_handles.connectors(), &mut old_state.0)?;
dev.add_props(res_handles.crtcs(), &mut old_state.1)?;
dev.add_props(res_handles.framebuffers(), &mut old_state.2)?;
dev.add_props(planes, &mut old_state.3)?;
dev.map_props(res_handles.connectors(), &mut mapping.0)?;
dev.map_props(res_handles.crtcs(), &mut mapping.1)?;
dev.map_props(res_handles.framebuffers(), &mut mapping.2)?;
dev.map_props(planes, &mut mapping.3)?;
dev.old_state = old_state;
dev.prop_mapping = mapping;
debug!(log, "Mapping: {:#?}", dev.prop_mapping);
Ok(AtomicDrmDevice {
dev: Rc::new(dev),
dev_id,
active,
backends: Rc::new(RefCell::new(HashMap::new())),
handler: None,
logger: log.clone(),
})
}
}
impl<A: AsRawFd + 'static> AsRawFd for AtomicDrmDevice<A> {
fn as_raw_fd(&self) -> RawFd {
self.dev.as_raw_fd()
}
}
impl<A: AsRawFd + 'static> BasicDevice for AtomicDrmDevice<A> {}
impl<A: AsRawFd + 'static> ControlDevice for AtomicDrmDevice<A> {}
impl<A: AsRawFd + 'static> Device for AtomicDrmDevice<A> {
type Surface = AtomicDrmSurface<A>;
fn device_id(&self) -> dev_t {
self.dev_id
}
fn set_handler(&mut self, handler: impl DeviceHandler<Device = Self> + 'static) {
self.handler = Some(RefCell::new(Box::new(handler)));
}
fn clear_handler(&mut self) {
let _ = self.handler.take();
}
fn create_surface(&mut self, crtc: crtc::Handle) -> Result<AtomicDrmSurface<A>, Error> {
if self.backends.borrow().contains_key(&crtc) {
return Err(Error::CrtcAlreadyInUse(crtc));
}
if !self.active.load(Ordering::SeqCst) {
return Err(Error::DeviceInactive);
}
let backend = Rc::new(AtomicDrmSurfaceInternal::new(
self.dev.clone(),
crtc,
self.logger.new(o!("crtc" => format!("{:?}", crtc))),
)?);
self.backends.borrow_mut().insert(crtc, Rc::downgrade(&backend));
Ok(AtomicDrmSurface(backend))
}
fn process_events(&mut self) {
match self.receive_events() {
Ok(events) => {
for event in events {
if let Event::PageFlip(event) = event {
trace!(self.logger, "Got a page-flip event for crtc ({:?})", event.crtc);
if self.active.load(Ordering::SeqCst) {
if self
.backends
.borrow()
.get(&event.crtc)
.iter()
.flat_map(|x| x.upgrade())
.next()
.is_some()
{
trace!(self.logger, "Handling event for backend {:?}", event.crtc);
if let Some(handler) = self.handler.as_ref() {
handler.borrow_mut().vblank(event.crtc);
}
} else {
self.backends.borrow_mut().remove(&event.crtc);
}
} else {
debug!(
self.logger,
"Device ({:?}) not active. Ignoring PageFlip",
self.dev_path()
);
}
} else {
trace!(
self.logger,
"Got a non-page-flip event of device '{:?}'.",
self.dev_path()
);
}
}
}
Err(source) => {
if let Some(handler) = self.handler.as_ref() {
handler.borrow_mut().error(Error::Access {
errmsg: "Error processing drm events",
dev: self.dev_path(),
source: source.compat(),
});
}
}
}
}
fn resource_handles(&self) -> Result<ResourceHandles, Error> {
ControlDevice::resource_handles(self)
.compat()
.map_err(|source| Error::Access {
errmsg: "Error loading resource info",
dev: self.dev_path(),
source,
})
}
fn get_connector_info(&self, conn: connector::Handle) -> Result<connector::Info, DrmError> {
self.get_connector(conn)
}
fn get_crtc_info(&self, crtc: crtc::Handle) -> Result<crtc::Info, DrmError> {
self.get_crtc(crtc)
}
fn get_encoder_info(&self, enc: encoder::Handle) -> Result<encoder::Info, DrmError> {
self.get_encoder(enc)
}
fn get_framebuffer_info(&self, fb: framebuffer::Handle) -> Result<framebuffer::Info, DrmError> {
self.get_framebuffer(fb)
}
fn get_plane_info(&self, plane: plane::Handle) -> Result<plane::Info, DrmError> {
self.get_plane(plane)
}
}
impl<A: AsRawFd + 'static> RawDevice for AtomicDrmDevice<A> {
type Surface = AtomicDrmSurface<A>;
}
impl<A: AsRawFd + 'static> Drop for AtomicDrmDevice<A> {
fn drop(&mut self) {
self.clear_handler();
}
}

View File

@ -0,0 +1,99 @@
//!
//! Support to register an open [`AtomicDrmDevice`](AtomicDrmDevice)
//! to an open [`Session`](::backend::session::Session).
//!
use drm::control::crtc;
use drm::Device as BasicDevice;
use nix::libc::dev_t;
use nix::sys::stat;
use std::cell::RefCell;
use std::collections::HashMap;
use std::os::unix::io::{AsRawFd, RawFd};
use std::rc::{Rc, Weak};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use super::{AtomicDrmDevice, AtomicDrmSurfaceInternal, Dev};
use crate::backend::session::{AsSessionObserver, SessionObserver};
/// [`SessionObserver`](SessionObserver)
/// linked to the [`AtomicDrmDevice`](AtomicDrmDevice)
/// it was created from.
pub struct AtomicDrmDeviceObserver<A: AsRawFd + 'static> {
dev: Weak<Dev<A>>,
dev_id: dev_t,
privileged: bool,
active: Arc<AtomicBool>,
backends: Weak<RefCell<HashMap<crtc::Handle, Weak<AtomicDrmSurfaceInternal<A>>>>>,
logger: ::slog::Logger,
}
impl<A: AsRawFd + 'static> AsSessionObserver<AtomicDrmDeviceObserver<A>> for AtomicDrmDevice<A> {
fn observer(&mut self) -> AtomicDrmDeviceObserver<A> {
AtomicDrmDeviceObserver {
dev: Rc::downgrade(&self.dev),
dev_id: self.dev_id,
active: self.active.clone(),
privileged: self.dev.privileged,
backends: Rc::downgrade(&self.backends),
logger: self.logger.clone(),
}
}
}
impl<A: AsRawFd + 'static> SessionObserver for AtomicDrmDeviceObserver<A> {
fn pause(&mut self, devnum: Option<(u32, u32)>) {
if let Some((major, minor)) = devnum {
if major as u64 != stat::major(self.dev_id) || minor as u64 != stat::minor(self.dev_id) {
return;
}
}
// TODO: Clear overlay planes (if we ever use them)
if let Some(backends) = self.backends.upgrade() {
for surface in backends.borrow().values().filter_map(Weak::upgrade) {
// other ttys that use no cursor, might not clear it themselves.
// This makes sure our cursor won't stay visible.
if let Err(err) = surface.clear_plane(surface.planes.cursor) {
warn!(
self.logger,
"Failed to clear cursor on {:?}: {}", surface.crtc, err
);
}
}
}
self.active.store(false, Ordering::SeqCst);
if self.privileged {
if let Some(device) = self.dev.upgrade() {
if let Err(err) = device.release_master_lock() {
error!(self.logger, "Failed to drop drm master state Error: {}", err);
}
}
}
}
fn activate(&mut self, devnum: Option<(u32, u32, Option<RawFd>)>) {
if let Some((major, minor, fd)) = devnum {
if major as u64 != stat::major(self.dev_id) || minor as u64 != stat::minor(self.dev_id) {
return;
} else if let Some(fd) = fd {
info!(self.logger, "Replacing fd");
if let Some(device) = self.dev.upgrade() {
::nix::unistd::dup2(device.as_raw_fd(), fd)
.expect("Failed to replace file descriptor of drm device");
}
}
}
if self.privileged {
if let Some(device) = self.dev.upgrade() {
if let Err(err) = device.acquire_master_lock() {
crit!(self.logger, "Failed to acquire drm master again. Error: {}", err);
}
}
}
self.active.store(true, Ordering::SeqCst);
}
}

View File

@ -0,0 +1,874 @@
use drm::buffer::Buffer;
use drm::control::atomic::AtomicModeReq;
use drm::control::Device as ControlDevice;
use drm::control::{connector, crtc, framebuffer, plane, property, AtomicCommitFlags, Mode, PlaneType};
use drm::Device as BasicDevice;
use std::cell::Cell;
use std::collections::HashSet;
use std::os::unix::io::{AsRawFd, RawFd};
use std::rc::Rc;
use std::sync::RwLock;
use failure::ResultExt as FailureResultExt;
use super::Dev;
use crate::backend::drm::{common::Error, DevPath, RawSurface, Surface};
use crate::backend::graphics::CursorBackend;
use crate::backend::graphics::SwapBuffersError;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CursorState {
position: Cell<Option<(u32, u32)>>,
hotspot: Cell<(u32, u32)>,
framebuffer: Cell<Option<framebuffer::Info>>,
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct State {
pub mode: Option<Mode>,
pub blob: Option<property::Value<'static>>,
pub connectors: HashSet<connector::Handle>,
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Planes {
pub primary: plane::Handle,
pub cursor: plane::Handle,
}
pub(super) struct AtomicDrmSurfaceInternal<A: AsRawFd + 'static> {
pub(super) dev: Rc<Dev<A>>,
pub(super) crtc: crtc::Handle,
pub(super) cursor: CursorState,
pub(super) planes: Planes,
pub(super) state: RwLock<State>,
pub(super) pending: RwLock<State>,
pub(super) logger: ::slog::Logger,
}
impl<A: AsRawFd + 'static> AsRawFd for AtomicDrmSurfaceInternal<A> {
fn as_raw_fd(&self) -> RawFd {
self.dev.as_raw_fd()
}
}
impl<A: AsRawFd + 'static> BasicDevice for AtomicDrmSurfaceInternal<A> {}
impl<A: AsRawFd + 'static> ControlDevice for AtomicDrmSurfaceInternal<A> {}
impl<A: AsRawFd + 'static> AtomicDrmSurfaceInternal<A> {
pub(crate) fn new(dev: Rc<Dev<A>>, crtc: crtc::Handle, logger: ::slog::Logger) -> Result<Self, Error> {
let crtc_info = dev.get_crtc(crtc).compat().map_err(|source| Error::Access {
errmsg: "Error loading crtc info",
dev: dev.dev_path(),
source,
})?;
let mode = crtc_info.mode();
let blob = match mode {
Some(mode) => Some(
dev.create_property_blob(mode)
.compat()
.map_err(|source| Error::Access {
errmsg: "Failed to create Property Blob for mode",
dev: dev.dev_path(),
source,
})?,
),
None => None,
};
let res_handles = ControlDevice::resource_handles(&*dev)
.compat()
.map_err(|source| Error::Access {
errmsg: "Error loading drm resources",
dev: dev.dev_path(),
source,
})?;
let mut state = State {
mode,
blob,
connectors: HashSet::new(),
};
for conn in res_handles.connectors() {
let crtc_prop = dev
.prop_mapping
.0
.get(&conn)
.expect("Unknown handle")
.get("CRTC_ID")
.ok_or_else(|| Error::UnknownProperty {
handle: (*conn).into(),
name: "CRTC_ID",
})
.map(|x| *x)?;
if let (Ok(crtc_prop_info), Ok(props)) = (dev.get_property(crtc_prop), dev.get_properties(*conn))
{
let (ids, vals) = props.as_props_and_values();
for (&id, &val) in ids.iter().zip(vals.iter()) {
if id == crtc_prop {
if let property::Value::CRTC(Some(conn_crtc)) =
crtc_prop_info.value_type().convert_value(val)
{
if conn_crtc == crtc {
state.connectors.insert(*conn);
}
}
break;
}
}
}
}
let (primary, cursor) =
AtomicDrmSurfaceInternal::find_planes(&dev, crtc).ok_or(Error::NoSuitablePlanes {
crtc,
dev: dev.dev_path(),
})?;
Ok(AtomicDrmSurfaceInternal {
dev,
crtc,
cursor: CursorState {
position: Cell::new(None),
framebuffer: Cell::new(None),
hotspot: Cell::new((0, 0)),
},
planes: Planes { primary, cursor },
state: RwLock::new(state.clone()),
pending: RwLock::new(state),
logger,
})
}
}
impl<A: AsRawFd + 'static> Surface for AtomicDrmSurfaceInternal<A> {
type Error = Error;
type Connectors = HashSet<connector::Handle>;
fn crtc(&self) -> crtc::Handle {
self.crtc
}
fn current_connectors(&self) -> Self::Connectors {
self.state.read().unwrap().connectors.clone()
}
fn pending_connectors(&self) -> Self::Connectors {
self.pending.read().unwrap().connectors.clone()
}
fn current_mode(&self) -> Option<Mode> {
self.state.read().unwrap().mode
}
fn pending_mode(&self) -> Option<Mode> {
self.pending.read().unwrap().mode
}
fn add_connector(&self, conn: connector::Handle) -> Result<(), Error> {
let info = self
.get_connector(conn)
.compat()
.map_err(|source| Error::Access {
errmsg: "Error loading connector info",
dev: self.dev_path(),
source,
})?;
let mut pending = self.pending.write().unwrap();
// check if the connector can handle the current mode
if info.modes().contains(pending.mode.as_ref().unwrap()) {
// check if config is supported
let req = self.build_request(
&mut [conn].iter(),
&mut [].iter(),
&self.planes,
None,
pending.mode,
pending.blob,
)?;
self.atomic_commit(
&[AtomicCommitFlags::AllowModeset, AtomicCommitFlags::TestOnly],
req,
)
.compat()
.map_err(|_| Error::TestFailed(self.crtc))?;
// seems to be, lets add the connector
pending.connectors.insert(conn);
Ok(())
} else {
Err(Error::ModeNotSuitable(pending.mode.unwrap()))
}
}
fn remove_connector(&self, conn: connector::Handle) -> Result<(), Error> {
let mut pending = self.pending.write().unwrap();
// check if new config is supported (should be)
let req = self.build_request(
&mut [].iter(),
&mut [conn].iter(),
&self.planes,
None,
pending.mode,
pending.blob,
)?;
self.atomic_commit(
&[AtomicCommitFlags::AllowModeset, AtomicCommitFlags::TestOnly],
req,
)
.compat()
.map_err(|_| Error::TestFailed(self.crtc))?;
// seems to be, lets remove the connector
pending.connectors.remove(&conn);
Ok(())
}
fn set_connectors(&self, connectors: &[connector::Handle]) -> Result<(), Error> {
let current = self.state.write().unwrap();
let mut pending = self.pending.write().unwrap();
let conns = connectors.iter().cloned().collect::<HashSet<_>>();
let mut added = conns.difference(&current.connectors);
let mut removed = current.connectors.difference(&conns);
let req = self.build_request(
&mut added,
&mut removed,
&self.planes,
None,
pending.mode,
pending.blob,
)?;
self.atomic_commit(
&[AtomicCommitFlags::AllowModeset, AtomicCommitFlags::TestOnly],
req,
)
.map_err(|_| Error::TestFailed(self.crtc))?;
pending.connectors = conns;
Ok(())
}
fn use_mode(&self, mode: Option<Mode>) -> Result<(), Error> {
let mut pending = self.pending.write().unwrap();
// check if new config is supported
let new_blob = Some(match mode {
Some(mode) => self
.dev
.create_property_blob(mode)
.compat()
.map_err(|source| Error::Access {
errmsg: "Failed to create Property Blob for mode",
dev: self.dev_path(),
source,
})?,
None => property::Value::Unknown(0),
});
let req = self.build_request(
&mut pending.connectors.iter(),
&mut [].iter(),
&self.planes,
None,
mode,
new_blob,
)?;
if let Err(err) = self
.atomic_commit(
&[AtomicCommitFlags::AllowModeset, AtomicCommitFlags::TestOnly],
req,
)
.compat()
.map_err(|_| Error::TestFailed(self.crtc))
{
let _ = self.dev.destroy_property_blob(new_blob.unwrap().into());
return Err(err);
}
// seems to be, lets change the mode
pending.mode = mode;
pending.blob = new_blob;
Ok(())
}
}
impl<A: AsRawFd + 'static> RawSurface for AtomicDrmSurfaceInternal<A> {
fn commit_pending(&self) -> bool {
*self.pending.read().unwrap() != *self.state.read().unwrap()
}
fn commit(&self, framebuffer: framebuffer::Handle) -> Result<(), Error> {
let mut current = self.state.write().unwrap();
let mut pending = self.pending.write().unwrap();
debug!(
self.logger,
"Preparing Commit.\n\tCurrent: {:?}\n\tPending: {:?}\n", *current, *pending
);
let current_conns = current.connectors.clone();
let pending_conns = pending.connectors.clone();
let mut removed = current_conns.difference(&pending_conns);
let mut added = pending_conns.difference(&current_conns);
for conn in removed.clone() {
if let Ok(info) = self.get_connector(*conn) {
info!(self.logger, "Removing connector: {:?}", info.interface());
} else {
info!(self.logger, "Removing unknown connector");
}
}
for conn in added.clone() {
if let Ok(info) = self.get_connector(*conn) {
info!(self.logger, "Adding connector: {:?}", info.interface());
} else {
info!(self.logger, "Adding unknown connector");
}
}
if current.mode != pending.mode {
info!(
self.logger,
"Setting new mode: {:?}",
pending.mode.as_ref().unwrap().name()
);
}
trace!(self.logger, "Testing screen config");
let req = {
let req = self.build_request(
&mut added,
&mut removed,
&self.planes,
Some(framebuffer),
pending.mode,
pending.blob,
)?;
if let Err(err) = self
.atomic_commit(
&[AtomicCommitFlags::AllowModeset, AtomicCommitFlags::TestOnly],
req.clone(),
)
.compat()
.map_err(|_| Error::TestFailed(self.crtc))
{
warn!(
self.logger,
"New screen configuration invalid!:\n\t{:#?}\n\t{}\n", req, err
);
info!(self.logger, "Reverting back to last know good state");
*pending = current.clone();
self.build_request(
&mut [].iter(),
&mut [].iter(),
&self.planes,
Some(framebuffer),
current.mode,
current.blob,
)?
} else {
if current.mode != pending.mode {
if let Some(blob) = current.blob {
if let Err(err) = self.dev.destroy_property_blob(blob.into()) {
warn!(self.logger, "Failed to destory old mode property blob: {}", err);
}
}
}
*current = pending.clone();
// new config
req
}
};
debug!(self.logger, "Setting screen: {:#?}", req);
self.atomic_commit(
&[
AtomicCommitFlags::PageFlipEvent,
AtomicCommitFlags::AllowModeset,
AtomicCommitFlags::Nonblock,
],
req,
)
.compat()
.map_err(|source| Error::Access {
errmsg: "Error setting crtc",
dev: self.dev_path(),
source,
})?;
Ok(())
}
fn page_flip(&self, framebuffer: framebuffer::Handle) -> Result<(), SwapBuffersError> {
let req = self
.build_request(
&mut [].iter(),
&mut [].iter(),
&self.planes,
Some(framebuffer),
None,
None,
) //current.mode)
.map_err(|_| SwapBuffersError::ContextLost)?;
trace!(self.logger, "Queueing page flip: {:#?}", req);
self.atomic_commit(
&[AtomicCommitFlags::PageFlipEvent, AtomicCommitFlags::Nonblock],
req,
)
.map_err(|_| SwapBuffersError::ContextLost)?;
Ok(())
}
}
impl<A: AsRawFd + 'static> CursorBackend for AtomicDrmSurfaceInternal<A> {
type CursorFormat = dyn Buffer;
type Error = Error;
fn set_cursor_position(&self, x: u32, y: u32) -> Result<(), Error> {
trace!(self.logger, "New cursor position ({},{}) pending", x, y);
self.cursor.position.set(Some((x, y)));
Ok(())
}
fn set_cursor_representation(
&self,
buffer: &Self::CursorFormat,
hotspot: (u32, u32),
) -> Result<(), Error> {
trace!(self.logger, "Setting the new imported cursor");
if let Some(fb) = self.cursor.framebuffer.get().take() {
let _ = self.destroy_framebuffer(fb.handle());
}
self.cursor.framebuffer.set(Some(
self.get_framebuffer(self.add_planar_framebuffer(buffer, &[0; 4], 0).compat().map_err(
|source| Error::Access {
errmsg: "Failed to import cursor",
dev: self.dev_path(),
source,
},
)?)
.compat()
.map_err(|source| Error::Access {
errmsg: "Failed to get framebuffer info",
dev: self.dev_path(),
source,
})?,
));
self.cursor.hotspot.set(hotspot);
Ok(())
}
}
impl<A: AsRawFd + 'static> AtomicDrmSurfaceInternal<A> {
fn conn_prop_handle(
&self,
handle: connector::Handle,
name: &'static str,
) -> Result<property::Handle, Error> {
(*self.dev)
.prop_mapping
.0
.get(&handle)
.expect("Unknown handle")
.get(name)
.ok_or_else(|| Error::UnknownProperty {
handle: handle.into(),
name,
})
.map(|x| *x)
}
fn crtc_prop_handle(&self, handle: crtc::Handle, name: &'static str) -> Result<property::Handle, Error> {
(*self.dev)
.prop_mapping
.1
.get(&handle)
.expect("Unknown handle")
.get(name)
.ok_or_else(|| Error::UnknownProperty {
handle: handle.into(),
name,
})
.map(|x| *x)
}
#[allow(dead_code)]
fn fb_prop_handle(
&self,
handle: framebuffer::Handle,
name: &'static str,
) -> Result<property::Handle, Error> {
(*self.dev)
.prop_mapping
.2
.get(&handle)
.expect("Unknown handle")
.get(name)
.ok_or_else(|| Error::UnknownProperty {
handle: handle.into(),
name,
})
.map(|x| *x)
}
fn plane_prop_handle(
&self,
handle: plane::Handle,
name: &'static str,
) -> Result<property::Handle, Error> {
(*self.dev)
.prop_mapping
.3
.get(&handle)
.expect("Unknown handle")
.get(name)
.ok_or_else(|| Error::UnknownProperty {
handle: handle.into(),
name,
})
.map(|x| *x)
}
// If a mode is set a matching blob needs to be set (the inverse is not true)
fn build_request(
&self,
new_connectors: &mut dyn Iterator<Item = &connector::Handle>,
removed_connectors: &mut dyn Iterator<Item = &connector::Handle>,
planes: &Planes,
framebuffer: Option<framebuffer::Handle>,
mode: Option<Mode>,
blob: Option<property::Value<'static>>,
) -> Result<AtomicModeReq, Error> {
let mut req = AtomicModeReq::new();
for conn in new_connectors {
req.add_property(
*conn,
self.conn_prop_handle(*conn, "CRTC_ID")?,
property::Value::CRTC(Some(self.crtc)),
);
}
for conn in removed_connectors {
req.add_property(
*conn,
self.conn_prop_handle(*conn, "CRTC_ID")?,
property::Value::CRTC(None),
);
}
if let Some(blob) = blob {
req.add_property(self.crtc, self.crtc_prop_handle(self.crtc, "MODE_ID")?, blob);
}
req.add_property(
self.crtc,
self.crtc_prop_handle(self.crtc, "ACTIVE")?,
property::Value::Boolean(true),
);
if let Some(fb) = framebuffer {
req.add_property(
planes.primary,
self.plane_prop_handle(planes.primary, "FB_ID")?,
property::Value::Framebuffer(Some(fb)),
);
}
if let Some(mode) = mode {
req.add_property(
planes.primary,
self.plane_prop_handle(planes.primary, "CRTC_ID")?,
property::Value::CRTC(Some(self.crtc)),
);
req.add_property(
planes.primary,
self.plane_prop_handle(planes.primary, "SRC_X")?,
property::Value::UnsignedRange(0),
);
req.add_property(
planes.primary,
self.plane_prop_handle(planes.primary, "SRC_Y")?,
property::Value::UnsignedRange(0),
);
req.add_property(
planes.primary,
self.plane_prop_handle(planes.primary, "SRC_W")?,
property::Value::UnsignedRange((mode.size().0 as u64) << 16),
);
req.add_property(
planes.primary,
self.plane_prop_handle(planes.primary, "SRC_H")?,
property::Value::UnsignedRange((mode.size().1 as u64) << 16),
);
req.add_property(
planes.primary,
self.plane_prop_handle(planes.primary, "CRTC_X")?,
property::Value::SignedRange(0),
);
req.add_property(
planes.primary,
self.plane_prop_handle(planes.primary, "CRTC_Y")?,
property::Value::SignedRange(0),
);
req.add_property(
planes.primary,
self.plane_prop_handle(planes.primary, "CRTC_W")?,
property::Value::UnsignedRange(mode.size().0 as u64),
);
req.add_property(
planes.primary,
self.plane_prop_handle(planes.primary, "CRTC_H")?,
property::Value::UnsignedRange(mode.size().1 as u64),
);
}
let cursor_pos = self.cursor.position.get();
let cursor_fb = self.cursor.framebuffer.get();
if let (Some(pos), Some(fb)) = (cursor_pos, cursor_fb) {
let hotspot = self.cursor.hotspot.get();
req.add_property(
planes.cursor,
self.plane_prop_handle(planes.cursor, "CRTC_ID")?,
property::Value::CRTC(Some(self.crtc)),
);
req.add_property(
planes.cursor,
self.plane_prop_handle(planes.cursor, "SRC_X")?,
property::Value::UnsignedRange(0),
);
req.add_property(
planes.cursor,
self.plane_prop_handle(planes.cursor, "SRC_Y")?,
property::Value::UnsignedRange(0),
);
req.add_property(
planes.cursor,
self.plane_prop_handle(planes.cursor, "SRC_W")?,
property::Value::UnsignedRange((fb.size().0 as u64) << 16),
);
req.add_property(
planes.cursor,
self.plane_prop_handle(planes.cursor, "SRC_H")?,
property::Value::UnsignedRange((fb.size().1 as u64) << 16),
);
req.add_property(
planes.cursor,
self.plane_prop_handle(planes.cursor, "CRTC_X")?,
property::Value::SignedRange(pos.0 as i64 - (hotspot.0 as i64)),
);
req.add_property(
planes.cursor,
self.plane_prop_handle(planes.cursor, "CRTC_Y")?,
property::Value::SignedRange(pos.1 as i64 - (hotspot.1 as i64)),
);
req.add_property(
planes.cursor,
self.plane_prop_handle(planes.cursor, "CRTC_W")?,
property::Value::UnsignedRange(fb.size().0 as u64),
);
req.add_property(
planes.cursor,
self.plane_prop_handle(planes.cursor, "CRTC_H")?,
property::Value::UnsignedRange(fb.size().1 as u64),
);
req.add_property(
planes.cursor,
self.plane_prop_handle(planes.cursor, "FB_ID")?,
property::Value::Framebuffer(Some(fb.handle())),
);
}
Ok(req)
}
fn find_planes(card: &Dev<A>, crtc: crtc::Handle) -> Option<(plane::Handle, plane::Handle)> {
let res = card.resource_handles().expect("Could not list resources");
let planes = card.plane_handles().expect("Could not list planes");
let vec: Vec<(PlaneType, plane::Handle)> = planes
.planes()
.iter()
.copied()
.filter(|plane| {
card.get_plane(*plane)
.map(|plane_info| {
let compatible_crtcs = res.filter_crtcs(plane_info.possible_crtcs());
compatible_crtcs.contains(&crtc)
})
.unwrap_or(false)
})
.filter_map(|plane| {
if let Ok(props) = card.get_properties(plane) {
let (ids, vals) = props.as_props_and_values();
for (&id, &val) in ids.iter().zip(vals.iter()) {
if let Ok(info) = card.get_property(id) {
if info.name().to_str().map(|x| x == "type").unwrap_or(false) {
if val == (PlaneType::Primary as u32).into() {
return Some((PlaneType::Primary, plane));
}
if val == (PlaneType::Cursor as u32).into() {
return Some((PlaneType::Cursor, plane));
}
}
}
}
}
None
})
.collect();
Some((
vec.iter().find_map(|(plane_type, plane)| {
if *plane_type == PlaneType::Primary {
Some(*plane)
} else {
None
}
})?,
vec.iter().find_map(|(plane_type, plane)| {
if *plane_type == PlaneType::Cursor {
Some(*plane)
} else {
None
}
})?,
))
}
pub(crate) fn clear_plane(&self, plane: plane::Handle) -> Result<(), Error> {
let mut req = AtomicModeReq::new();
req.add_property(
plane,
self.plane_prop_handle(plane, "CRTC_ID")?,
property::Value::CRTC(None),
);
req.add_property(
plane,
self.plane_prop_handle(plane, "FB_ID")?,
property::Value::Framebuffer(None),
);
self.atomic_commit(&[AtomicCommitFlags::TestOnly], req.clone())
.compat()
.map_err(|_| Error::TestFailed(self.crtc))?;
self.atomic_commit(&[AtomicCommitFlags::Nonblock], req)
.compat()
.map_err(|source| Error::Access {
errmsg: "Failed to commit on clear_plane",
dev: self.dev_path(),
source,
})
}
}
/// Open raw crtc utilizing atomic mode-setting
pub struct AtomicDrmSurface<A: AsRawFd + 'static>(pub(super) Rc<AtomicDrmSurfaceInternal<A>>);
impl<A: AsRawFd + 'static> AsRawFd for AtomicDrmSurface<A> {
fn as_raw_fd(&self) -> RawFd {
self.0.as_raw_fd()
}
}
impl<A: AsRawFd + 'static> BasicDevice for AtomicDrmSurface<A> {}
impl<A: AsRawFd + 'static> ControlDevice for AtomicDrmSurface<A> {}
impl<A: AsRawFd + 'static> CursorBackend for AtomicDrmSurface<A> {
type CursorFormat = dyn Buffer;
type Error = Error;
fn set_cursor_position(&self, x: u32, y: u32) -> Result<(), Error> {
self.0.set_cursor_position(x, y)
}
fn set_cursor_representation(
&self,
buffer: &Self::CursorFormat,
hotspot: (u32, u32),
) -> Result<(), Error> {
self.0.set_cursor_representation(buffer, hotspot)
}
}
impl<A: AsRawFd + 'static> Surface for AtomicDrmSurface<A> {
type Error = Error;
type Connectors = HashSet<connector::Handle>;
fn crtc(&self) -> crtc::Handle {
self.0.crtc()
}
fn current_connectors(&self) -> Self::Connectors {
self.0.current_connectors()
}
fn pending_connectors(&self) -> Self::Connectors {
self.0.pending_connectors()
}
fn current_mode(&self) -> Option<Mode> {
self.0.current_mode()
}
fn pending_mode(&self) -> Option<Mode> {
self.0.pending_mode()
}
fn add_connector(&self, connector: connector::Handle) -> Result<(), Error> {
self.0.add_connector(connector)
}
fn remove_connector(&self, connector: connector::Handle) -> Result<(), Error> {
self.0.remove_connector(connector)
}
fn set_connectors(&self, connectors: &[connector::Handle]) -> Result<(), Error> {
self.0.set_connectors(connectors)
}
fn use_mode(&self, mode: Option<Mode>) -> Result<(), Error> {
self.0.use_mode(mode)
}
}
impl<A: AsRawFd + 'static> RawSurface for AtomicDrmSurface<A> {
fn commit_pending(&self) -> bool {
self.0.commit_pending()
}
fn commit(&self, framebuffer: framebuffer::Handle) -> Result<(), Error> {
self.0.commit(framebuffer)
}
fn page_flip(&self, framebuffer: framebuffer::Handle) -> Result<(), SwapBuffersError> {
RawSurface::page_flip(&*self.0, framebuffer)
}
}

View File

@ -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<D1: Device + 'static, D2: Device + 'static> {
/// Variant for successful initialization of the preferred device
Preference(D1),
/// Variant for the fallback device
Fallback(D2),
}
struct FallbackDeviceHandlerD1<E, C, S1, S2, D1, D2>(
Box<dyn DeviceHandler<Device = FallbackDevice<D1, D2>> + 'static>,
)
where
E: std::error::Error + Send + 'static,
C: IntoIterator<Item = connector::Handle> + 'static,
S1: Surface<Error = E, Connectors = C> + 'static,
S2: Surface<Error = E, Connectors = C> + 'static,
D1: Device<Surface = S1> + 'static,
D2: Device<Surface = S2> + 'static;
impl<E, C, S1, S2, D1, D2> DeviceHandler for FallbackDeviceHandlerD1<E, C, S1, S2, D1, D2>
where
E: std::error::Error + Send + 'static,
C: IntoIterator<Item = connector::Handle> + 'static,
S1: Surface<Error = E, Connectors = C> + 'static,
S2: Surface<Error = E, Connectors = C> + 'static,
D1: Device<Surface = S1> + 'static,
D2: Device<Surface = S2> + '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<E, C, S1, S2, D1, D2>(
Box<dyn DeviceHandler<Device = FallbackDevice<D1, D2>> + 'static>,
)
where
E: std::error::Error + Send + 'static,
C: IntoIterator<Item = connector::Handle> + 'static,
S1: Surface<Error = E, Connectors = C> + 'static,
S2: Surface<Error = E, Connectors = C> + 'static,
D1: Device<Surface = S1> + 'static,
D2: Device<Surface = S2> + 'static;
impl<E, C, S1, S2, D1, D2> DeviceHandler for FallbackDeviceHandlerD2<E, C, S1, S2, D1, D2>
where
E: std::error::Error + Send + 'static,
C: IntoIterator<Item = connector::Handle> + 'static,
S1: Surface<Error = E, Connectors = C> + 'static,
S2: Surface<Error = E, Connectors = C> + 'static,
D1: Device<Surface = S1> + 'static,
D2: Device<Surface = S2> + '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<O1: SessionObserver + 'static, O2: SessionObserver + 'static> {
/// Variant for successful initialization of the preferred device
Preference(O1),
/// Variant for the fallback device
Fallback(O2),
}
impl<O1, O2, D1, D2> AsSessionObserver<FallbackDeviceObserver<O1, O2>> for FallbackDevice<D1, D2>
where
O1: SessionObserver + 'static,
O2: SessionObserver + 'static,
D1: Device + AsSessionObserver<O1> + 'static,
D2: Device + AsSessionObserver<O2> + 'static,
{
fn observer(&mut self) -> FallbackDeviceObserver<O1, O2> {
match self {
FallbackDevice::Preference(dev) => FallbackDeviceObserver::Preference(dev.observer()),
FallbackDevice::Fallback(dev) => FallbackDeviceObserver::Fallback(dev.observer()),
}
}
}
impl<O1: SessionObserver + 'static, O2: SessionObserver + 'static> SessionObserver
for FallbackDeviceObserver<O1, O2>
{
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<RawFd>)>) {
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<S1: Surface, S2: Surface> {
/// 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<A: AsRawFd + Clone + 'static> FallbackDevice<AtomicDrmDevice<A>, LegacyDrmDevice<A>> {
/// Try to initialize an [`AtomicDrmDevice`](::backend::drm:;atomic::AtomicDrmDevice)
/// and fall back to a [`LegacyDrmDevice`] if atomic-modesetting is not supported.
pub fn new<L>(fd: A, logger: L) -> Result<Self, Error>
where
L: Into<Option<::slog::Logger>>,
{
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<D1: Device, D2: Device> AsRawFd for FallbackDevice<D1, D2> {
fallback_device_impl!(as_raw_fd, &Self, RawFd);
}
impl<D1: Device + BasicDevice, D2: Device + BasicDevice> BasicDevice for FallbackDevice<D1, D2> {}
impl<D1: Device + ControlDevice, D2: Device + ControlDevice> ControlDevice for FallbackDevice<D1, D2> {}
impl<E, C, S1, S2, D1, D2> Device for FallbackDevice<D1, D2>
where
// Connectors and Error need to match for both Surfaces
E: std::error::Error + Send + 'static,
C: IntoIterator<Item = connector::Handle> + 'static,
S1: Surface<Error = E, Connectors = C> + 'static,
S2: Surface<Error = E, Connectors = C> + 'static,
D1: Device<Surface = S1> + 'static,
D2: Device<Surface = S2> + 'static,
{
type Surface = FallbackSurface<S1, S2>;
fallback_device_impl!(device_id, &Self, dev_t);
fn set_handler(&mut self, handler: impl DeviceHandler<Device = Self> + '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<Self::Surface, E> {
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<ResourceHandles, E>);
fallback_device_impl!(get_connector_info, &Self, Result<connector::Info, DrmError>, conn: connector::Handle);
fallback_device_impl!(get_crtc_info, &Self, Result<crtc::Info, DrmError>, crtc: crtc::Handle);
fallback_device_impl!(get_encoder_info, &Self, Result<encoder::Info, DrmError>, enc: encoder::Handle);
fallback_device_impl!(get_framebuffer_info, &Self, Result<framebuffer::Info, DrmError>, fb: framebuffer::Handle);
fallback_device_impl!(get_plane_info, &Self, Result<plane::Info, DrmError>, plane : plane::Handle);
}
// Impl RawDevice where underlying types implement RawDevice
impl<E, C, S1, S2, D1, D2> RawDevice for FallbackDevice<D1, D2>
where
// Connectors and Error need to match for both Surfaces
E: std::error::Error + Send + 'static,
C: IntoIterator<Item = connector::Handle> + 'static,
S1: RawSurface + Surface<Error = E, Connectors = C> + 'static,
S2: RawSurface + Surface<Error = E, Connectors = C> + 'static,
D1: RawDevice<Surface = S1> + 'static,
D2: RawDevice<Surface = S2> + 'static,
{
type Surface = FallbackSurface<S1, S2>;
}
#[cfg(feature = "use_system_lib")]
impl<D1: Device + EGLGraphicsBackend + 'static, D2: Device + EGLGraphicsBackend + 'static> EGLGraphicsBackend
for FallbackDevice<D1, D2>
{
fallback_device_impl!(bind_wl_display, &Self, Result<EGLBufferReader, EGLError>, display : &Display);
}
impl<E, C, S1, S2> Surface for FallbackSurface<S1, S2>
where
// Connectors and Error need to match for both Surfaces
E: std::error::Error + Send + 'static,
C: IntoIterator<Item = connector::Handle> + 'static,
S1: Surface<Error = E, Connectors = C> + 'static,
S2: Surface<Error = E, Connectors = C> + '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<Mode>);
fallback_surface_impl!(pending_mode, &Self, Option<Mode>);
fallback_surface_impl!(use_mode, &Self, Result<(), E>, mode: Option<Mode>);
}
impl<E, C, S1, S2> RawSurface for FallbackSurface<S1, S2>
where
E: std::error::Error + Send + 'static,
C: IntoIterator<Item = connector::Handle> + 'static,
S1: RawSurface + Surface<Error = E, Connectors = C> + 'static,
S2: RawSurface + Surface<Error = E, Connectors = C> + '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<S1: Surface + AsRawFd, S2: Surface + AsRawFd> AsRawFd for FallbackSurface<S1, S2> {
fallback_surface_impl!(as_raw_fd, &Self, RawFd);
}
impl<S1: Surface + BasicDevice, S2: Surface + BasicDevice> BasicDevice for FallbackSurface<S1, S2> {}
impl<S1: Surface + ControlDevice, S2: Surface + ControlDevice> ControlDevice for FallbackSurface<S1, S2> {}
impl<E1, E2, C, CF, S1, S2> CursorBackend for FallbackSurface<S1, S2>
where
E1: std::error::Error + Send + 'static,
E2: 'static,
CF: ?Sized,
C: IntoIterator<Item = connector::Handle> + 'static,
S1: Surface<Error = E1, Connectors = C> + CursorBackend<CursorFormat = CF, Error = E2> + 'static,
S2: Surface<Error = E1, Connectors = C> + CursorBackend<CursorFormat = CF, Error = E2> + '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<E, C, S1, S2> GLGraphicsBackend for FallbackSurface<S1, S2>
where
E: std::error::Error + Send + 'static,
C: IntoIterator<Item = connector::Handle> + 'static,
S1: Surface<Error = E, Connectors = C> + GLGraphicsBackend + 'static,
S2: Surface<Error = E, Connectors = C> + 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);
}

View File

@ -0,0 +1,70 @@
//!
//! 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)
/// and [`AtomicDrmSurface`](::backend::drm::atomic::AtomicDrmSurface).
#[derive(thiserror::Error, Debug)]
pub enum Error {
/// Unable to acquire DRM master
#[error("Failed to aquire DRM master")]
DrmMasterFailed,
/// The `DrmDevice` encountered an access error
#[error("DRM access error: {errmsg} on device `{dev:?}`")]
Access {
/// Error message associated to the access error
errmsg: &'static str,
/// Device on which the error was generated
dev: Option<PathBuf>,
/// Underlying device error
source: failure::Compat<drm::SystemError>,
},
/// Unable to determine device id of drm device
#[error("Unable to determine device id of drm device")]
UnableToGetDeviceId(#[source] nix::Error),
/// Device is currently paused
#[error("Device is currently paused, operation rejected")]
DeviceInactive,
/// Mode is not compatible with all given connectors
#[error("Mode `{0:?}` is not compatible with all given connectors")]
ModeNotSuitable(Mode),
/// The given crtc is already in use by another backend
#[error("Crtc `{0:?}` is already in use by another backend")]
CrtcAlreadyInUse(crtc::Handle),
/// No encoder was found for a given connector on the set crtc
#[error("No encoder found for the given connector '{connector:?}' on crtc `{crtc:?}`")]
NoSuitableEncoder {
/// Connector
connector: connector::Handle,
/// CRTC
crtc: crtc::Handle,
},
/// No matching primary and cursor plane could be found for the given crtc
#[error("No matching primary and cursor plane could be found for crtc {crtc:?} on {dev:?}")]
NoSuitablePlanes {
/// CRTC
crtc: crtc::Handle,
/// Device on which the error was generated
dev: Option<PathBuf>,
},
/// The DrmDevice is missing a required property
#[error("The DrmDevice is missing a required property '{name}' for handle ({handle:?})")]
UnknownProperty {
/// Property handle
handle: RawResourceHandle,
/// Property name
name: &'static str,
},
/// Atomic Test failed for new properties
#[error("Atomic Test failed for new properties on crtc ({0:?})")]
TestFailed(crtc::Handle),
}

View File

@ -49,6 +49,10 @@ where
.map_err(Error::Underlying) .map_err(Error::Underlying)
} }
fn set_connectors(&self, connectors: &[connector::Handle]) -> Result<(), Self::Error> {
self.surface.set_connectors(connectors).map_err(Error::Underlying)
}
fn current_mode(&self) -> Option<Mode> { fn current_mode(&self) -> Option<Mode> {
self.surface.current_mode() self.surface.current_mode()
} }
@ -62,25 +66,22 @@ where
} }
} }
impl<'a, N> CursorBackend<'a> for EglSurface<N> impl<N> CursorBackend for EglSurface<N>
where where
N: NativeSurface + Surface + CursorBackend<'a>, N: NativeSurface + Surface + CursorBackend,
{ {
type CursorFormat = <N as CursorBackend<'a>>::CursorFormat; type CursorFormat = <N as CursorBackend>::CursorFormat;
type Error = <N as CursorBackend<'a>>::Error; type Error = <N as CursorBackend>::Error;
fn set_cursor_position(&self, x: u32, y: u32) -> ::std::result::Result<(), Self::Error> { fn set_cursor_position(&self, x: u32, y: u32) -> ::std::result::Result<(), Self::Error> {
self.surface.set_cursor_position(x, y) self.surface.set_cursor_position(x, y)
} }
fn set_cursor_representation<'b>( fn set_cursor_representation(
&'b self, &self,
buffer: Self::CursorFormat, buffer: &Self::CursorFormat,
hotspot: (u32, u32), hotspot: (u32, u32),
) -> ::std::result::Result<(), Self::Error> ) -> ::std::result::Result<(), Self::Error> {
where
'a: 'b,
{
self.surface.set_cursor_representation(buffer, hotspot) self.surface.set_cursor_representation(buffer, hotspot)
} }
} }

View File

@ -56,13 +56,23 @@ impl<
for (crtc, backend) in backends.borrow().iter() { for (crtc, backend) in backends.borrow().iter() {
if let Some(backend) = backend.upgrade() { if let Some(backend) = backend.upgrade() {
// restart rendering loop, if it was previously running // restart rendering loop, if it was previously running
if let Some(Err(err)) = backend if let Some(fb) = backend.current_frame_buffer.get() {
.current_frame_buffer if backend.crtc.page_flip(fb).is_err() {
.get() // Try more!
.map(|fb| backend.crtc.page_flip(fb)) if let Err(err) = backend.recreate() {
{ error!(
warn!(self.logger, "Failed to restart rendering loop. Error: {}", err); self.logger,
"Failed to re-create gbm surface, is the device gone?\n\t{}", err
);
}
if let Err(err) = unsafe { backend.page_flip() } {
warn!(self.logger, "Failed to restart rendering loop. Error: {}", err);
// TODO bubble this up the user somehow
// maybe expose a "running" state from a surface?
}
}
} }
// reset cursor // reset cursor
{ {
use ::drm::control::Device; use ::drm::control::Device;

View File

@ -6,11 +6,8 @@ use gbm::{self, BufferObject, BufferObjectFlags, Format as GbmFormat, SurfaceBuf
use image::{ImageBuffer, Rgba}; use image::{ImageBuffer, Rgba};
use std::cell::{Cell, RefCell}; use std::cell::{Cell, RefCell};
use std::os::unix::io::AsRawFd;
use std::rc::Rc; use std::rc::Rc;
#[cfg(feature = "backend_drm_legacy")]
use crate::backend::drm::legacy::LegacyDrmDevice;
use crate::backend::graphics::CursorBackend; use crate::backend::graphics::CursorBackend;
use crate::backend::graphics::SwapBuffersError; use crate::backend::graphics::SwapBuffersError;
@ -78,24 +75,31 @@ impl<D: RawDevice + 'static> GbmSurfaceInternal<D> {
if self.recreated.get() { if self.recreated.get() {
debug!(self.logger, "Commiting new state"); debug!(self.logger, "Commiting new state");
self.crtc.commit(fb).map_err(|_| SwapBuffersError::ContextLost)?; if let Err(err) = self.crtc.commit(fb) {
error!(self.logger, "Error commiting crtc: {}", err);
return Err(SwapBuffersError::ContextLost);
}
self.recreated.set(false); self.recreated.set(false);
} else {
trace!(self.logger, "Queueing Page flip");
RawSurface::page_flip(&self.crtc, fb)?;
} }
trace!(self.logger, "Queueing Page flip");
RawSurface::page_flip(&self.crtc, fb)?;
self.current_frame_buffer.set(Some(fb)); self.current_frame_buffer.set(Some(fb));
Ok(()) Ok(())
} }
pub fn recreate(&self) -> Result<(), Error<<<D as Device>::Surface as Surface>::Error>> { pub fn recreate(&self) -> Result<(), Error<<<D as Device>::Surface as Surface>::Error>> {
let (w, h) = self.pending_mode().ok_or(Error::NoModeSet)?.size(); let (w, h) = self
.pending_mode()
.or_else(|| self.current_mode())
.ok_or(Error::NoModeSet)?
.size();
// Recreate the surface and the related resources to match the new // Recreate the surface and the related resources to match the new
// resolution. // resolution.
debug!(self.logger, "(Re-)Initializing surface for mode: {}:{}", w, h); debug!(self.logger, "(Re-)Initializing surface (with mode: {}:{})", w, h);
let surface = self let surface = self
.dev .dev
.borrow_mut() .borrow_mut()
@ -159,6 +163,10 @@ impl<D: RawDevice + 'static> Surface for GbmSurfaceInternal<D> {
self.crtc.remove_connector(connector).map_err(Error::Underlying) self.crtc.remove_connector(connector).map_err(Error::Underlying)
} }
fn set_connectors(&self, connectors: &[connector::Handle]) -> Result<(), Self::Error> {
self.crtc.set_connectors(connectors).map_err(Error::Underlying)
}
fn current_mode(&self) -> Option<Mode> { fn current_mode(&self) -> Option<Mode> {
self.crtc.current_mode() self.crtc.current_mode()
} }
@ -172,47 +180,24 @@ impl<D: RawDevice + 'static> Surface for GbmSurfaceInternal<D> {
} }
} }
// FIXME: #[cfg(feature = "backend_drm")]
// impl<D: RawDevice + 'static> CursorBackend for GbmSurfaceInternal<D>
// Option 1: When there is GAT support, impl `GraphicsBackend` for `LegacyDrmBackend`
// using a new generic `B: Buffer` and use this:
/*
impl<'a, D: RawDevice + 'static> CursorBackend<'a> for GbmSurfaceInternal<D>
where where
<D as RawDevice>::Surface: CursorBackend<'a>, <D as RawDevice>::Surface: CursorBackend<CursorFormat = dyn drm::buffer::Buffer>,
<<D as RawDevice>::Surface as CursorBackend<'a>>::CursorFormat: Buffer, <<D as RawDevice>::Surface as CursorBackend>::Error: ::std::error::Error + Send,
<<D as RawDevice>::Surface as CursorBackend<'a>>::Error: ::std::error::Error + Send
{ {
*/ type CursorFormat = ImageBuffer<Rgba<u8>, Vec<u8>>;
// type Error = Error<<<D as Device>::Surface as CursorBackend>::Error>;
// Option 2: When equality checks in where clauses are supported, we could at least do this:
/*
impl<'a, D: RawDevice + 'static> GraphicsBackend<'a> for GbmSurfaceInternal<D>
where
<D as RawDevice>::Surface: CursorBackend<'a>,
<<D as RawDevice>::Surface as CursorBackend<'a>>::CursorFormat=&'a Buffer,
<<D as RawDevice>::Surface as CursorBackend<'a>>::Error: ::std::error::Error + Send
{
*/
// But for now got to do this:
#[cfg(feature = "backend_drm_legacy")]
impl<'a, A: AsRawFd + 'static> CursorBackend<'a> for GbmSurfaceInternal<LegacyDrmDevice<A>> {
type CursorFormat = &'a ImageBuffer<Rgba<u8>, Vec<u8>>;
type Error = Error<<<LegacyDrmDevice<A> as Device>::Surface as Surface>::Error>;
fn set_cursor_position(&self, x: u32, y: u32) -> Result<(), Self::Error> { fn set_cursor_position(&self, x: u32, y: u32) -> Result<(), Self::Error> {
self.crtc.set_cursor_position(x, y).map_err(Error::Underlying) self.crtc.set_cursor_position(x, y).map_err(Error::Underlying)
} }
fn set_cursor_representation<'b>( fn set_cursor_representation(
&'b self, &self,
buffer: &ImageBuffer<Rgba<u8>, Vec<u8>>, buffer: &ImageBuffer<Rgba<u8>, Vec<u8>>,
hotspot: (u32, u32), hotspot: (u32, u32),
) -> Result<(), Self::Error> ) -> Result<(), Self::Error> {
where
'a: 'b,
{
let (w, h) = buffer.dimensions(); let (w, h) = buffer.dimensions();
debug!(self.logger, "Importing cursor"); debug!(self.logger, "Importing cursor");
@ -336,6 +321,10 @@ impl<D: RawDevice + 'static> Surface for GbmSurface<D> {
self.0.remove_connector(connector) self.0.remove_connector(connector)
} }
fn set_connectors(&self, connectors: &[connector::Handle]) -> Result<(), Self::Error> {
self.0.set_connectors(connectors)
}
fn current_mode(&self) -> Option<Mode> { fn current_mode(&self) -> Option<Mode> {
self.0.current_mode() self.0.current_mode()
} }
@ -349,23 +338,24 @@ impl<D: RawDevice + 'static> Surface for GbmSurface<D> {
} }
} }
#[cfg(feature = "backend_drm_legacy")] #[cfg(feature = "backend_drm")]
impl<'a, A: AsRawFd + 'static> CursorBackend<'a> for GbmSurface<LegacyDrmDevice<A>> { impl<D: RawDevice + 'static> CursorBackend for GbmSurface<D>
type CursorFormat = &'a ImageBuffer<Rgba<u8>, Vec<u8>>; where
type Error = <Self as Surface>::Error; <D as RawDevice>::Surface: CursorBackend<CursorFormat = dyn drm::buffer::Buffer>,
<<D as RawDevice>::Surface as CursorBackend>::Error: ::std::error::Error + Send,
{
type CursorFormat = ImageBuffer<Rgba<u8>, Vec<u8>>;
type Error = Error<<<D as Device>::Surface as CursorBackend>::Error>;
fn set_cursor_position(&self, x: u32, y: u32) -> Result<(), Self::Error> { fn set_cursor_position(&self, x: u32, y: u32) -> Result<(), Self::Error> {
self.0.set_cursor_position(x, y) self.0.set_cursor_position(x, y)
} }
fn set_cursor_representation<'b>( fn set_cursor_representation(
&'b self, &self,
buffer: &ImageBuffer<Rgba<u8>, Vec<u8>>, buffer: &ImageBuffer<Rgba<u8>, Vec<u8>>,
hotspot: (u32, u32), hotspot: (u32, u32),
) -> Result<(), Self::Error> ) -> Result<(), Self::Error> {
where
'a: 'b,
{
self.0.set_cursor_representation(buffer, hotspot) self.0.set_cursor_representation(buffer, hotspot)
} }
} }

View File

@ -5,13 +5,13 @@
//! Usually this implementation will be wrapped into a [`GbmDevice`](::backend::drm::gbm::GbmDevice). //! Usually this implementation will be wrapped into a [`GbmDevice`](::backend::drm::gbm::GbmDevice).
//! Take a look at `anvil`s source code for an example of this. //! Take a look at `anvil`s source code for an example of this.
//! //!
//! For an example how to use this standalone, take a look at the `raw_drm` example. //! For an example how to use this standalone, take a look at the `raw_legacy_drm` example.
//! //!
use super::{DevPath, Device, DeviceHandler, RawDevice}; use super::{common::Error, DevPath, Device, DeviceHandler, RawDevice};
use drm::control::{ use drm::control::{
connector, crtc, encoder, framebuffer, plane, Device as ControlDevice, Event, Mode, ResourceHandles, connector, crtc, encoder, framebuffer, plane, Device as ControlDevice, Event, ResourceHandles,
}; };
use drm::{Device as BasicDevice, SystemError as DrmError}; use drm::{Device as BasicDevice, SystemError as DrmError};
use nix::libc::dev_t; use nix::libc::dev_t;
@ -20,7 +20,6 @@ use nix::sys::stat::fstat;
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::os::unix::io::{AsRawFd, RawFd}; use std::os::unix::io::{AsRawFd, RawFd};
use std::path::PathBuf;
use std::rc::{Rc, Weak}; use std::rc::{Rc, Weak};
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, RwLock}; use std::sync::{Arc, RwLock};
@ -34,45 +33,6 @@ use self::surface::{LegacyDrmSurfaceInternal, State};
#[cfg(feature = "backend_session")] #[cfg(feature = "backend_session")]
pub mod session; pub mod session;
/// Errors thrown by the [`LegacyDrmDevice`](::backend::drm::legacy::LegacyDrmDevice)
/// and [`LegacyDrmSurface`](::backend::drm::legacy::LegacyDrmSurface).
#[derive(thiserror::Error, Debug)]
pub enum Error {
/// Unable to acquire DRM master
#[error("Failed to aquire DRM master")]
DrmMasterFailed,
/// The `DrmDevice` encountered an access error
#[error("DRM access error: {errmsg} on device `{dev:?}`")]
Access {
/// Error message associated to the access error
errmsg: &'static str,
/// Device on which the error was generated
dev: Option<PathBuf>,
/// Underlying device error
source: failure::Compat<drm::SystemError>,
},
/// Unable to determine device id of drm device
#[error("Unable to determine device id of drm device")]
UnableToGetDeviceId(#[source] nix::Error),
/// Device is currently paused
#[error("Device is currently paused, operation rejected")]
DeviceInactive,
/// Mode is not compatible with all given connectors
#[error("Mode `{0:?}` is not compatible with all given connectors")]
ModeNotSuitable(Mode),
/// The given crtc is already in use by another backend
#[error("Crtc `{0:?}` is already in use by another backend")]
CrtcAlreadyInUse(crtc::Handle),
/// No encoder was found for a given connector on the set crtc
#[error("No encoder found for the given connector '{connector:?}' on crtc `{crtc:?}`")]
NoSuitableEncoder {
/// Connector
connector: connector::Handle,
/// CRTC
crtc: crtc::Handle,
},
}
/// Open raw drm device utilizing legacy mode-setting /// Open raw drm device utilizing legacy mode-setting
pub struct LegacyDrmDevice<A: AsRawFd + 'static> { pub struct LegacyDrmDevice<A: AsRawFd + 'static> {
dev: Rc<Dev<A>>, dev: Rc<Dev<A>>,
@ -85,7 +45,7 @@ pub struct LegacyDrmDevice<A: AsRawFd + 'static> {
pub(in crate::backend::drm) struct Dev<A: AsRawFd + 'static> { pub(in crate::backend::drm) struct Dev<A: AsRawFd + 'static> {
fd: A, fd: A,
priviledged: bool, privileged: bool,
active: Arc<AtomicBool>, active: Arc<AtomicBool>,
old_state: HashMap<crtc::Handle, (crtc::Info, Vec<connector::Handle>)>, old_state: HashMap<crtc::Handle, (crtc::Info, Vec<connector::Handle>)>,
logger: ::slog::Logger, logger: ::slog::Logger,
@ -117,7 +77,7 @@ impl<A: AsRawFd + 'static> Drop for Dev<A> {
} }
} }
} }
if self.priviledged { if self.privileged {
if let Err(err) = self.release_master_lock() { if let Err(err) = self.release_master_lock() {
error!(self.logger, "Failed to drop drm master state. Error: {}", err); error!(self.logger, "Failed to drop drm master state. Error: {}", err);
} }
@ -135,7 +95,7 @@ impl<A: AsRawFd + 'static> LegacyDrmDevice<A> {
L: Into<Option<::slog::Logger>>, L: Into<Option<::slog::Logger>>,
{ {
let log = crate::slog_or_stdlog(logger).new(o!("smithay_module" => "backend_drm")); let log = crate::slog_or_stdlog(logger).new(o!("smithay_module" => "backend_drm"));
info!(log, "DrmDevice initializing"); info!(log, "LegacyDrmDevice initializing");
let dev_id = fstat(dev.as_raw_fd()) let dev_id = fstat(dev.as_raw_fd())
.map_err(Error::UnableToGetDeviceId)? .map_err(Error::UnableToGetDeviceId)?
@ -144,7 +104,7 @@ impl<A: AsRawFd + 'static> LegacyDrmDevice<A> {
let active = Arc::new(AtomicBool::new(true)); let active = Arc::new(AtomicBool::new(true));
let mut dev = Dev { let mut dev = Dev {
fd: dev, fd: dev,
priviledged: true, privileged: true,
old_state: HashMap::new(), old_state: HashMap::new(),
active: active.clone(), active: active.clone(),
logger: log.clone(), logger: log.clone(),
@ -152,8 +112,8 @@ impl<A: AsRawFd + 'static> LegacyDrmDevice<A> {
// we want to modeset, so we better be the master, if we run via a tty session // we want to modeset, so we better be the master, if we run via a tty session
if dev.acquire_master_lock().is_err() { if dev.acquire_master_lock().is_err() {
warn!(log, "Unable to become drm master, assuming unpriviledged mode"); warn!(log, "Unable to become drm master, assuming unprivileged mode");
dev.priviledged = false; dev.privileged = false;
}; };
// enumerate (and save) the current device state // enumerate (and save) the current device state
@ -308,7 +268,11 @@ impl<A: AsRawFd + 'static> Device for LegacyDrmDevice<A> {
} else { } else {
self.backends.borrow_mut().remove(&event.crtc); self.backends.borrow_mut().remove(&event.crtc);
} }
} else {
debug!(self.logger, "Device not active. Ignoring PageFlip");
} }
} else {
trace!(self.logger, "Unrelated event");
} }
} }
} }
@ -334,22 +298,19 @@ impl<A: AsRawFd + 'static> Device for LegacyDrmDevice<A> {
}) })
} }
fn get_connector_info(&self, conn: connector::Handle) -> std::result::Result<connector::Info, DrmError> { fn get_connector_info(&self, conn: connector::Handle) -> Result<connector::Info, DrmError> {
self.get_connector(conn) self.get_connector(conn)
} }
fn get_crtc_info(&self, crtc: crtc::Handle) -> std::result::Result<crtc::Info, DrmError> { fn get_crtc_info(&self, crtc: crtc::Handle) -> Result<crtc::Info, DrmError> {
self.get_crtc(crtc) self.get_crtc(crtc)
} }
fn get_encoder_info(&self, enc: encoder::Handle) -> std::result::Result<encoder::Info, DrmError> { fn get_encoder_info(&self, enc: encoder::Handle) -> Result<encoder::Info, DrmError> {
self.get_encoder(enc) self.get_encoder(enc)
} }
fn get_framebuffer_info( fn get_framebuffer_info(&self, fb: framebuffer::Handle) -> Result<framebuffer::Info, DrmError> {
&self,
fb: framebuffer::Handle,
) -> std::result::Result<framebuffer::Info, DrmError> {
self.get_framebuffer(fb) self.get_framebuffer(fb)
} }
fn get_plane_info(&self, plane: plane::Handle) -> std::result::Result<plane::Info, DrmError> { fn get_plane_info(&self, plane: plane::Handle) -> Result<plane::Info, DrmError> {
self.get_plane(plane) self.get_plane(plane)
} }
} }

View File

@ -23,7 +23,7 @@ use crate::backend::session::{AsSessionObserver, SessionObserver};
pub struct LegacyDrmDeviceObserver<A: AsRawFd + 'static> { pub struct LegacyDrmDeviceObserver<A: AsRawFd + 'static> {
dev: Weak<Dev<A>>, dev: Weak<Dev<A>>,
dev_id: dev_t, dev_id: dev_t,
priviledged: bool, privileged: bool,
active: Arc<AtomicBool>, active: Arc<AtomicBool>,
backends: Weak<RefCell<HashMap<crtc::Handle, Weak<LegacyDrmSurfaceInternal<A>>>>>, backends: Weak<RefCell<HashMap<crtc::Handle, Weak<LegacyDrmSurfaceInternal<A>>>>>,
logger: ::slog::Logger, logger: ::slog::Logger,
@ -35,7 +35,7 @@ impl<A: AsRawFd + 'static> AsSessionObserver<LegacyDrmDeviceObserver<A>> for Leg
dev: Rc::downgrade(&self.dev), dev: Rc::downgrade(&self.dev),
dev_id: self.dev_id, dev_id: self.dev_id,
active: self.active.clone(), active: self.active.clone(),
priviledged: self.dev.priviledged, privileged: self.dev.privileged,
backends: Rc::downgrade(&self.backends), backends: Rc::downgrade(&self.backends),
logger: self.logger.clone(), logger: self.logger.clone(),
} }
@ -62,7 +62,7 @@ impl<A: AsRawFd + 'static> SessionObserver for LegacyDrmDeviceObserver<A> {
} }
} }
self.active.store(false, Ordering::SeqCst); self.active.store(false, Ordering::SeqCst);
if self.priviledged { if self.privileged {
if let Some(device) = self.dev.upgrade() { if let Some(device) = self.dev.upgrade() {
if let Err(err) = device.release_master_lock() { if let Err(err) = device.release_master_lock() {
error!(self.logger, "Failed to drop drm master state. Error: {}", err); error!(self.logger, "Failed to drop drm master state. Error: {}", err);
@ -84,7 +84,7 @@ impl<A: AsRawFd + 'static> SessionObserver for LegacyDrmDeviceObserver<A> {
} }
} }
self.active.store(true, Ordering::SeqCst); self.active.store(true, Ordering::SeqCst);
if self.priviledged { if self.privileged {
if let Some(device) = self.dev.upgrade() { if let Some(device) = self.dev.upgrade() {
if let Err(err) = device.acquire_master_lock() { if let Err(err) = device.acquire_master_lock() {
crit!(self.logger, "Failed to acquire drm master again. Error: {}", err); crit!(self.logger, "Failed to acquire drm master again. Error: {}", err);

View File

@ -10,13 +10,13 @@ use std::os::unix::io::{AsRawFd, RawFd};
use std::rc::Rc; use std::rc::Rc;
use std::sync::RwLock; use std::sync::RwLock;
use crate::backend::drm::{DevPath, RawSurface, Surface}; use crate::backend::drm::{common::Error, DevPath, RawSurface, Surface};
use crate::backend::graphics::CursorBackend; use crate::backend::graphics::CursorBackend;
use crate::backend::graphics::SwapBuffersError; use crate::backend::graphics::SwapBuffersError;
use super::{Dev, Error}; use super::Dev;
use failure::ResultExt; use failure::{Fail, ResultExt};
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Eq, Clone)]
pub struct State { pub struct State {
@ -41,8 +41,8 @@ impl<A: AsRawFd + 'static> AsRawFd for LegacyDrmSurfaceInternal<A> {
impl<A: AsRawFd + 'static> BasicDevice for LegacyDrmSurfaceInternal<A> {} impl<A: AsRawFd + 'static> BasicDevice for LegacyDrmSurfaceInternal<A> {}
impl<A: AsRawFd + 'static> ControlDevice for LegacyDrmSurfaceInternal<A> {} impl<A: AsRawFd + 'static> ControlDevice for LegacyDrmSurfaceInternal<A> {}
impl<'a, A: AsRawFd + 'static> CursorBackend<'a> for LegacyDrmSurfaceInternal<A> { impl<A: AsRawFd + 'static> CursorBackend for LegacyDrmSurfaceInternal<A> {
type CursorFormat = &'a dyn Buffer; type CursorFormat = dyn Buffer;
type Error = Error; type Error = Error;
fn set_cursor_position(&self, x: u32, y: u32) -> Result<(), Error> { fn set_cursor_position(&self, x: u32, y: u32) -> Result<(), Error> {
@ -56,14 +56,11 @@ impl<'a, A: AsRawFd + 'static> CursorBackend<'a> for LegacyDrmSurfaceInternal<A>
}) })
} }
fn set_cursor_representation<'b>( fn set_cursor_representation(
&'b self, &self,
buffer: Self::CursorFormat, buffer: &Self::CursorFormat,
hotspot: (u32, u32), hotspot: (u32, u32),
) -> Result<(), Error> ) -> Result<(), Error> {
where
'a: 'b,
{
trace!(self.logger, "Setting the new imported cursor"); trace!(self.logger, "Setting the new imported cursor");
if self if self
@ -108,58 +105,13 @@ impl<A: AsRawFd + 'static> Surface for LegacyDrmSurfaceInternal<A> {
} }
fn add_connector(&self, conn: connector::Handle) -> Result<(), Error> { fn add_connector(&self, conn: connector::Handle) -> Result<(), Error> {
let info = self
.get_connector(conn)
.compat()
.map_err(|source| Error::Access {
errmsg: "Error loading connector info",
dev: self.dev_path(),
source,
})?;
let mut pending = self.pending.write().unwrap(); let mut pending = self.pending.write().unwrap();
// check if the connector can handle the current mode if self.check_connector(conn, pending.mode.as_ref().unwrap())? {
if info.modes().contains(pending.mode.as_ref().unwrap()) {
// check if there is a valid encoder
let encoders = info
.encoders()
.iter()
.filter(|enc| enc.is_some())
.map(|enc| enc.unwrap())
.map(|encoder| {
self.get_encoder(encoder)
.compat()
.map_err(|source| Error::Access {
errmsg: "Error loading encoder info",
dev: self.dev_path(),
source,
})
})
.collect::<Result<Vec<encoder::Info>, _>>()?;
// and if any encoder supports the selected crtc
let resource_handles = self.resource_handles().compat().map_err(|source| Error::Access {
errmsg: "Error loading resources",
dev: self.dev_path(),
source,
})?;
if !encoders
.iter()
.map(|encoder| encoder.possible_crtcs())
.all(|crtc_list| resource_handles.filter_crtcs(crtc_list).contains(&self.crtc))
{
return Err(Error::NoSuitableEncoder {
connector: info.handle(),
crtc: self.crtc,
});
}
pending.connectors.insert(conn); pending.connectors.insert(conn);
Ok(())
} else {
Err(Error::ModeNotSuitable(pending.mode.unwrap()))
} }
Ok(())
} }
fn remove_connector(&self, connector: connector::Handle) -> Result<(), Error> { fn remove_connector(&self, connector: connector::Handle) -> Result<(), Error> {
@ -167,6 +119,22 @@ impl<A: AsRawFd + 'static> Surface for LegacyDrmSurfaceInternal<A> {
Ok(()) Ok(())
} }
fn set_connectors(&self, connectors: &[connector::Handle]) -> Result<(), Self::Error> {
let mut pending = self.pending.write().unwrap();
if connectors
.iter()
.map(|conn| self.check_connector(*conn, pending.mode.as_ref().unwrap()))
.collect::<Result<Vec<bool>, _>>()?
.iter()
.all(|v| *v)
{
pending.connectors = connectors.iter().cloned().collect();
}
Ok(())
}
fn use_mode(&self, mode: Option<Mode>) -> Result<(), Error> { fn use_mode(&self, mode: Option<Mode>) -> Result<(), Error> {
let mut pending = self.pending.write().unwrap(); let mut pending = self.pending.write().unwrap();
@ -208,12 +176,27 @@ impl<A: AsRawFd + 'static> RawSurface for LegacyDrmSurfaceInternal<A> {
let removed = current.connectors.difference(&pending.connectors); let removed = current.connectors.difference(&pending.connectors);
let added = pending.connectors.difference(&current.connectors); let added = pending.connectors.difference(&current.connectors);
let mut conn_removed = false;
for conn in removed { for conn in removed {
if let Ok(info) = self.get_connector(*conn) { if let Ok(info) = self.get_connector(*conn) {
info!(self.logger, "Removing connector: {:?}", info.interface()); info!(self.logger, "Removing connector: {:?}", info.interface());
} else { } else {
info!(self.logger, "Removing unknown connector"); info!(self.logger, "Removing unknown connector");
} }
// if the connector was mapped to our crtc, we need to ack the disconnect.
// the graphics pipeline will not be freed otherwise
conn_removed = true;
}
if conn_removed {
// We need to do a null commit to free graphics pipelines
self.set_crtc(self.crtc, None, (0, 0), &[], None)
.compat()
.map_err(|source| Error::Access {
errmsg: "Error setting crtc",
dev: self.dev_path(),
source,
})?;
} }
for conn in added { for conn in added {
@ -254,7 +237,18 @@ impl<A: AsRawFd + 'static> RawSurface for LegacyDrmSurfaceInternal<A> {
*current = pending.clone(); *current = pending.clone();
Ok(()) ControlDevice::page_flip(
self,
self.crtc,
framebuffer,
&[PageFlipFlags::PageFlipEvent],
None,
)
.map_err(|source| Error::Access {
errmsg: "Failed to queue page flip",
dev: self.dev_path(),
source: source.compat(),
})
} }
fn page_flip(&self, framebuffer: framebuffer::Handle) -> ::std::result::Result<(), SwapBuffersError> { fn page_flip(&self, framebuffer: framebuffer::Handle) -> ::std::result::Result<(), SwapBuffersError> {
@ -267,11 +261,61 @@ impl<A: AsRawFd + 'static> RawSurface for LegacyDrmSurfaceInternal<A> {
&[PageFlipFlags::PageFlipEvent], &[PageFlipFlags::PageFlipEvent],
None, None,
) )
.map_err(|x| dbg!(x))
.map_err(|_| SwapBuffersError::ContextLost) .map_err(|_| SwapBuffersError::ContextLost)
} }
} }
impl<A: AsRawFd + 'static> LegacyDrmSurfaceInternal<A> {
fn check_connector(&self, conn: connector::Handle, mode: &Mode) -> Result<bool, Error> {
let info = self
.get_connector(conn)
.compat()
.map_err(|source| Error::Access {
errmsg: "Error loading connector info",
dev: self.dev_path(),
source,
})?;
// check if the connector can handle the current mode
if info.modes().contains(mode) {
// check if there is a valid encoder
let encoders = info
.encoders()
.iter()
.filter(|enc| enc.is_some())
.map(|enc| enc.unwrap())
.map(|encoder| {
self.get_encoder(encoder)
.compat()
.map_err(|source| Error::Access {
errmsg: "Error loading encoder info",
dev: self.dev_path(),
source,
})
})
.collect::<Result<Vec<encoder::Info>, _>>()?;
// and if any encoder supports the selected crtc
let resource_handles = self.resource_handles().compat().map_err(|source| Error::Access {
errmsg: "Error loading resources",
dev: self.dev_path(),
source,
})?;
if !encoders
.iter()
.map(|encoder| encoder.possible_crtcs())
.all(|crtc_list| resource_handles.filter_crtcs(crtc_list).contains(&self.crtc))
{
Ok(false)
} else {
Ok(true)
}
} else {
Ok(false)
}
}
}
impl<A: AsRawFd + 'static> Drop for LegacyDrmSurfaceInternal<A> { impl<A: AsRawFd + 'static> Drop for LegacyDrmSurfaceInternal<A> {
fn drop(&mut self) { fn drop(&mut self) {
// ignore failure at this point // ignore failure at this point
@ -291,22 +335,19 @@ impl<A: AsRawFd + 'static> AsRawFd for LegacyDrmSurface<A> {
impl<A: AsRawFd + 'static> BasicDevice for LegacyDrmSurface<A> {} impl<A: AsRawFd + 'static> BasicDevice for LegacyDrmSurface<A> {}
impl<A: AsRawFd + 'static> ControlDevice for LegacyDrmSurface<A> {} impl<A: AsRawFd + 'static> ControlDevice for LegacyDrmSurface<A> {}
impl<'a, A: AsRawFd + 'static> CursorBackend<'a> for LegacyDrmSurface<A> { impl<A: AsRawFd + 'static> CursorBackend for LegacyDrmSurface<A> {
type CursorFormat = &'a dyn Buffer; type CursorFormat = dyn Buffer;
type Error = Error; type Error = Error;
fn set_cursor_position(&self, x: u32, y: u32) -> Result<(), Error> { fn set_cursor_position(&self, x: u32, y: u32) -> Result<(), Error> {
self.0.set_cursor_position(x, y) self.0.set_cursor_position(x, y)
} }
fn set_cursor_representation<'b>( fn set_cursor_representation(
&'b self, &self,
buffer: Self::CursorFormat, buffer: &Self::CursorFormat,
hotspot: (u32, u32), hotspot: (u32, u32),
) -> Result<(), Error> ) -> Result<(), Error> {
where
'a: 'b,
{
self.0.set_cursor_representation(buffer, hotspot) self.0.set_cursor_representation(buffer, hotspot)
} }
} }
@ -343,6 +384,10 @@ impl<A: AsRawFd + 'static> Surface for LegacyDrmSurface<A> {
self.0.remove_connector(connector) self.0.remove_connector(connector)
} }
fn set_connectors(&self, connectors: &[connector::Handle]) -> Result<(), Self::Error> {
self.0.set_connectors(connectors)
}
fn use_mode(&self, mode: Option<Mode>) -> Result<(), Error> { fn use_mode(&self, mode: Option<Mode>) -> Result<(), Error> {
self.0.use_mode(mode) self.0.use_mode(mode)
} }

View File

@ -53,6 +53,10 @@ use calloop::{LoopHandle, Source};
use super::graphics::SwapBuffersError; use super::graphics::SwapBuffersError;
#[cfg(feature = "backend_drm_atomic")]
pub mod atomic;
#[cfg(feature = "backend_drm")]
pub mod common;
#[cfg(feature = "backend_drm_egl")] #[cfg(feature = "backend_drm_egl")]
pub mod egl; pub mod egl;
#[cfg(feature = "backend_drm_gbm")] #[cfg(feature = "backend_drm_gbm")]
@ -99,7 +103,7 @@ pub trait Device: AsRawFd + DevPath {
/// The number of crtc's represent the number of independant output devices the hardware may handle. /// The number of crtc's represent the number of independant output devices the hardware may handle.
fn create_surface( fn create_surface(
&mut self, &mut self,
ctrc: crtc::Handle, crtc: crtc::Handle,
) -> Result<Self::Surface, <Self::Surface as Surface>::Error>; ) -> Result<Self::Surface, <Self::Surface as Surface>::Error>;
/// Processes any open events of the underlying file descriptor. /// Processes any open events of the underlying file descriptor.
@ -168,6 +172,13 @@ pub trait Surface {
/// Tries to mark a [`connector`](drm::control::connector) /// Tries to mark a [`connector`](drm::control::connector)
/// for removal on the next commit. /// for removal on the next commit.
fn remove_connector(&self, connector: connector::Handle) -> Result<(), Self::Error>; fn remove_connector(&self, connector: connector::Handle) -> Result<(), Self::Error>;
/// Tries to replace the current connector set with the newly provided one on the next commit.
///
/// Fails if one new `connector` is not compatible with the underlying [`crtc`](drm::control::crtc)
/// (e.g. no suitable [`encoder`](drm::control::encoder) may be found)
/// or is not compatible with the currently pending
/// [`Mode`](drm::control::Mode).
fn set_connectors(&self, connectors: &[connector::Handle]) -> Result<(), Self::Error>;
/// Returns the currently active [`Mode`](drm::control::Mode) /// Returns the currently active [`Mode`](drm::control::Mode)
/// of the underlying [`crtc`](drm::control::crtc) /// of the underlying [`crtc`](drm::control::crtc)
/// if any. /// if any.
@ -203,7 +214,10 @@ pub trait RawSurface: Surface + ControlDevice + BasicDevice {
/// potentially causing some flickering. Check before performing this /// potentially causing some flickering. Check before performing this
/// operation if a commit really is necessary using [`commit_pending`](RawSurface::commit_pending). /// operation if a commit really is necessary using [`commit_pending`](RawSurface::commit_pending).
/// ///
/// This operation is blocking until the crtc is in the desired state. /// This operation is not necessarily blocking until the crtc is in the desired state,
/// but will trigger a `vblank` event once done.
/// Make sure to [set a `DeviceHandler`](Device::set_handler) and
/// [register the belonging `Device`](device_bind) before to receive the event in time.
fn commit(&self, framebuffer: framebuffer::Handle) -> Result<(), <Self as Surface>::Error>; fn commit(&self, framebuffer: framebuffer::Handle) -> Result<(), <Self as Surface>::Error>;
/// Page-flip the underlying [`crtc`](drm::control::crtc) /// Page-flip the underlying [`crtc`](drm::control::crtc)
/// to a new given [`framebuffer`]. /// to a new given [`framebuffer`].

View File

@ -4,18 +4,18 @@
/// where possible. This may however be quite restrictive in terms of supported formats. /// where possible. This may however be quite restrictive in terms of supported formats.
/// ///
/// For those reasons you may always choose to render your cursor(s) (partially) in software instead. /// For those reasons you may always choose to render your cursor(s) (partially) in software instead.
pub trait CursorBackend<'a> { pub trait CursorBackend {
/// Format representing the image drawn for the cursor. /// Format representing the image drawn for the cursor.
type CursorFormat: 'a; type CursorFormat: ?Sized;
/// Error the underlying backend throws if operations fail /// Error the underlying backend throws if operations fail
type Error; type Error: 'static;
/// Sets the cursor position and therefore updates the drawn cursors position. /// Sets the cursor position and therefore updates the drawn cursors position.
/// Useful as well for e.g. pointer wrapping. /// Useful as well for e.g. pointer wrapping.
/// ///
/// Not guaranteed to be supported on every backend. The result usually /// Not guaranteed to be supported on every backend. The result usually
/// depends on the backend, the cursor might be "owned" by another more priviledged /// depends on the backend, the cursor might be "owned" by another more privileged
/// compositor (running nested). /// compositor (running nested).
/// ///
/// In these cases setting the position is actually not required, as movement is done /// In these cases setting the position is actually not required, as movement is done
@ -29,11 +29,9 @@ pub trait CursorBackend<'a> {
/// The format is entirely dictated by the concrete implementation and might range /// The format is entirely dictated by the concrete implementation and might range
/// from raw image buffers over a fixed list of possible cursor types to simply the /// from raw image buffers over a fixed list of possible cursor types to simply the
/// void type () to represent no possible customization of the cursor itself. /// void type () to represent no possible customization of the cursor itself.
fn set_cursor_representation<'b>( fn set_cursor_representation(
&'b self, &self,
cursor: Self::CursorFormat, cursor: &Self::CursorFormat,
hotspot: (u32, u32), hotspot: (u32, u32),
) -> Result<(), Self::Error> ) -> Result<(), Self::Error>;
where
'a: 'b;
} }

View File

@ -247,8 +247,8 @@ impl WinitGraphicsBackend {
} }
} }
impl<'a> CursorBackend<'a> for WinitGraphicsBackend { impl CursorBackend for WinitGraphicsBackend {
type CursorFormat = &'a CursorIcon; type CursorFormat = CursorIcon;
type Error = (); type Error = ();
fn set_cursor_position(&self, x: u32, y: u32) -> ::std::result::Result<(), ()> { fn set_cursor_position(&self, x: u32, y: u32) -> ::std::result::Result<(), ()> {
@ -261,14 +261,11 @@ impl<'a> CursorBackend<'a> for WinitGraphicsBackend {
}) })
} }
fn set_cursor_representation<'b>( fn set_cursor_representation(
&'b self, &self,
cursor: Self::CursorFormat, cursor: &Self::CursorFormat,
_hotspot: (u32, u32), _hotspot: (u32, u32),
) -> ::std::result::Result<(), ()> ) -> ::std::result::Result<(), ()> {
where
'a: 'b,
{
// Cannot log this one, as `CursorFormat` is not `Debug` and should not be // Cannot log this one, as `CursorFormat` is not `Debug` and should not be
debug!(self.logger, "Changing cursor representation"); debug!(self.logger, "Changing cursor representation");
self.window.window().set_cursor_icon(*cursor); self.window.window().set_cursor_icon(*cursor);