Merge pull request #194 from Smithay/feature/atomic_modesetting
Atomic modesetting support
This commit is contained in:
commit
27a73888a8
|
@ -45,9 +45,10 @@ slog-term = "2.3"
|
|||
gl_generator = { version = "0.14", optional = true }
|
||||
|
||||
[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_drm = ["drm", "failure"]
|
||||
backend_drm_atomic = ["backend_drm"]
|
||||
backend_drm_legacy = ["backend_drm"]
|
||||
backend_drm_gbm = ["backend_drm", "gbm", "image"]
|
||||
backend_drm_egl = ["backend_drm", "backend_egl"]
|
||||
|
@ -63,5 +64,5 @@ wayland_frontend = ["wayland-server", "wayland-commons", "wayland-protocols"]
|
|||
xwayland = ["wayland_frontend"]
|
||||
|
||||
[[example]]
|
||||
name = "raw_drm"
|
||||
name = "raw_legacy_drm"
|
||||
required-features = ["backend_drm_legacy"]
|
||||
|
|
|
@ -29,5 +29,5 @@ gl_generator = "0.14"
|
|||
default = [ "winit", "egl", "udev", "logind" ]
|
||||
egl = [ "smithay/use_system_lib" ]
|
||||
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" ]
|
||||
|
|
|
@ -19,6 +19,8 @@ use smithay::backend::egl::{display::EGLBufferReader, EGLGraphicsBackend};
|
|||
use smithay::{
|
||||
backend::{
|
||||
drm::{
|
||||
atomic::AtomicDrmDevice,
|
||||
common::fallback::FallbackDevice,
|
||||
device_bind,
|
||||
egl::{EglDevice, EglSurface},
|
||||
gbm::{egl::Gbm as EglGbmBackend, GbmDevice},
|
||||
|
@ -68,6 +70,7 @@ use crate::shell::{init_shell, MyWindowMap, Roles};
|
|||
use crate::AnvilState;
|
||||
use smithay::backend::drm::gbm::GbmSurface;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SessionFd(RawFd);
|
||||
impl AsRawFd for SessionFd {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
|
@ -75,9 +78,12 @@ impl AsRawFd for SessionFd {
|
|||
}
|
||||
}
|
||||
|
||||
type RenderDevice =
|
||||
EglDevice<EglGbmBackend<LegacyDrmDevice<SessionFd>>, GbmDevice<LegacyDrmDevice<SessionFd>>>;
|
||||
type RenderSurface = EglSurface<GbmSurface<LegacyDrmDevice<SessionFd>>>;
|
||||
type RenderDevice = EglDevice<
|
||||
EglGbmBackend<FallbackDevice<AtomicDrmDevice<SessionFd>, 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<(), ()> {
|
||||
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,
|
||||
)
|
||||
.ok()
|
||||
.and_then(|fd| LegacyDrmDevice::new(SessionFd(fd), self.logger.clone()).ok())
|
||||
.and_then(|drm| GbmDevice::new(drm, self.logger.clone()).ok())
|
||||
.and_then(|gbm| EglDevice::new(gbm, self.logger.clone()).ok())
|
||||
.and_then(
|
||||
|fd| match FallbackDevice::new(SessionFd(fd), self.logger.clone()) {
|
||||
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.
|
||||
#[cfg(feature = "egl")]
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -6,8 +6,9 @@ extern crate slog;
|
|||
use slog::Drain;
|
||||
use smithay::{
|
||||
backend::drm::{
|
||||
common::Error,
|
||||
device_bind,
|
||||
legacy::{Error, LegacyDrmDevice, LegacyDrmSurface},
|
||||
legacy::{LegacyDrmDevice, LegacyDrmSurface},
|
||||
Device, DeviceHandler, RawSurface, Surface,
|
||||
},
|
||||
reexports::{
|
||||
|
@ -80,13 +81,7 @@ fn main() {
|
|||
let surface = Rc::new(device.create_surface(crtc).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();
|
||||
|
||||
surface.set_connectors(&[connector_info.handle()]).unwrap();
|
||||
/*
|
||||
* Lets create buffers and framebuffers.
|
||||
* 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() {
|
||||
surface.commit(front_framebuffer).unwrap();
|
||||
}
|
||||
RawSurface::page_flip(&*surface, front_framebuffer).unwrap();
|
||||
|
||||
// Run
|
||||
event_loop.run(None, &mut (), |_| {}).unwrap();
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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(¤t.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(¤t_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)
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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),
|
||||
}
|
|
@ -49,6 +49,10 @@ where
|
|||
.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> {
|
||||
self.surface.current_mode()
|
||||
}
|
||||
|
@ -62,25 +66,22 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, N> CursorBackend<'a> for EglSurface<N>
|
||||
impl<N> CursorBackend for EglSurface<N>
|
||||
where
|
||||
N: NativeSurface + Surface + CursorBackend<'a>,
|
||||
N: NativeSurface + Surface + CursorBackend,
|
||||
{
|
||||
type CursorFormat = <N as CursorBackend<'a>>::CursorFormat;
|
||||
type Error = <N as CursorBackend<'a>>::Error;
|
||||
type CursorFormat = <N as CursorBackend>::CursorFormat;
|
||||
type Error = <N as CursorBackend>::Error;
|
||||
|
||||
fn set_cursor_position(&self, x: u32, y: u32) -> ::std::result::Result<(), Self::Error> {
|
||||
self.surface.set_cursor_position(x, y)
|
||||
}
|
||||
|
||||
fn set_cursor_representation<'b>(
|
||||
&'b self,
|
||||
buffer: Self::CursorFormat,
|
||||
fn set_cursor_representation(
|
||||
&self,
|
||||
buffer: &Self::CursorFormat,
|
||||
hotspot: (u32, u32),
|
||||
) -> ::std::result::Result<(), Self::Error>
|
||||
where
|
||||
'a: 'b,
|
||||
{
|
||||
) -> ::std::result::Result<(), Self::Error> {
|
||||
self.surface.set_cursor_representation(buffer, hotspot)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,13 +56,23 @@ impl<
|
|||
for (crtc, backend) in backends.borrow().iter() {
|
||||
if let Some(backend) = backend.upgrade() {
|
||||
// restart rendering loop, if it was previously running
|
||||
if let Some(Err(err)) = backend
|
||||
.current_frame_buffer
|
||||
.get()
|
||||
.map(|fb| backend.crtc.page_flip(fb))
|
||||
{
|
||||
warn!(self.logger, "Failed to restart rendering loop. Error: {}", err);
|
||||
if let Some(fb) = backend.current_frame_buffer.get() {
|
||||
if backend.crtc.page_flip(fb).is_err() {
|
||||
// Try more!
|
||||
if let Err(err) = backend.recreate() {
|
||||
error!(
|
||||
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
|
||||
{
|
||||
use ::drm::control::Device;
|
||||
|
|
|
@ -6,11 +6,8 @@ use gbm::{self, BufferObject, BufferObjectFlags, Format as GbmFormat, SurfaceBuf
|
|||
use image::{ImageBuffer, Rgba};
|
||||
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[cfg(feature = "backend_drm_legacy")]
|
||||
use crate::backend::drm::legacy::LegacyDrmDevice;
|
||||
use crate::backend::graphics::CursorBackend;
|
||||
use crate::backend::graphics::SwapBuffersError;
|
||||
|
||||
|
@ -78,12 +75,15 @@ impl<D: RawDevice + 'static> GbmSurfaceInternal<D> {
|
|||
|
||||
if self.recreated.get() {
|
||||
debug!(self.logger, "Commiting new state");
|
||||
self.crtc.commit(fb).map_err(|_| SwapBuffersError::ContextLost)?;
|
||||
self.recreated.set(false);
|
||||
if let Err(err) = self.crtc.commit(fb) {
|
||||
error!(self.logger, "Error commiting crtc: {}", err);
|
||||
return Err(SwapBuffersError::ContextLost);
|
||||
}
|
||||
|
||||
self.recreated.set(false);
|
||||
} else {
|
||||
trace!(self.logger, "Queueing Page flip");
|
||||
RawSurface::page_flip(&self.crtc, fb)?;
|
||||
}
|
||||
|
||||
self.current_frame_buffer.set(Some(fb));
|
||||
|
||||
|
@ -91,11 +91,15 @@ impl<D: RawDevice + 'static> GbmSurfaceInternal<D> {
|
|||
}
|
||||
|
||||
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
|
||||
// resolution.
|
||||
debug!(self.logger, "(Re-)Initializing surface for mode: {}:{}", w, h);
|
||||
debug!(self.logger, "(Re-)Initializing surface (with mode: {}:{})", w, h);
|
||||
let surface = self
|
||||
.dev
|
||||
.borrow_mut()
|
||||
|
@ -159,6 +163,10 @@ impl<D: RawDevice + 'static> Surface for GbmSurfaceInternal<D> {
|
|||
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> {
|
||||
self.crtc.current_mode()
|
||||
}
|
||||
|
@ -172,47 +180,24 @@ impl<D: RawDevice + 'static> Surface for GbmSurfaceInternal<D> {
|
|||
}
|
||||
}
|
||||
|
||||
// FIXME:
|
||||
//
|
||||
// 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>
|
||||
#[cfg(feature = "backend_drm")]
|
||||
impl<D: RawDevice + 'static> CursorBackend for GbmSurfaceInternal<D>
|
||||
where
|
||||
<D as RawDevice>::Surface: CursorBackend<'a>,
|
||||
<<D as RawDevice>::Surface as CursorBackend<'a>>::CursorFormat: Buffer,
|
||||
<<D as RawDevice>::Surface as CursorBackend<'a>>::Error: ::std::error::Error + Send
|
||||
<D as RawDevice>::Surface: CursorBackend<CursorFormat = dyn drm::buffer::Buffer>,
|
||||
<<D as RawDevice>::Surface as CursorBackend>::Error: ::std::error::Error + Send,
|
||||
{
|
||||
*/
|
||||
//
|
||||
// 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>;
|
||||
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> {
|
||||
self.crtc.set_cursor_position(x, y).map_err(Error::Underlying)
|
||||
}
|
||||
|
||||
fn set_cursor_representation<'b>(
|
||||
&'b self,
|
||||
fn set_cursor_representation(
|
||||
&self,
|
||||
buffer: &ImageBuffer<Rgba<u8>, Vec<u8>>,
|
||||
hotspot: (u32, u32),
|
||||
) -> Result<(), Self::Error>
|
||||
where
|
||||
'a: 'b,
|
||||
{
|
||||
) -> Result<(), Self::Error> {
|
||||
let (w, h) = buffer.dimensions();
|
||||
debug!(self.logger, "Importing cursor");
|
||||
|
||||
|
@ -336,6 +321,10 @@ impl<D: RawDevice + 'static> Surface for GbmSurface<D> {
|
|||
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> {
|
||||
self.0.current_mode()
|
||||
}
|
||||
|
@ -349,23 +338,24 @@ impl<D: RawDevice + 'static> Surface for GbmSurface<D> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "backend_drm_legacy")]
|
||||
impl<'a, A: AsRawFd + 'static> CursorBackend<'a> for GbmSurface<LegacyDrmDevice<A>> {
|
||||
type CursorFormat = &'a ImageBuffer<Rgba<u8>, Vec<u8>>;
|
||||
type Error = <Self as Surface>::Error;
|
||||
#[cfg(feature = "backend_drm")]
|
||||
impl<D: RawDevice + 'static> CursorBackend for GbmSurface<D>
|
||||
where
|
||||
<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> {
|
||||
self.0.set_cursor_position(x, y)
|
||||
}
|
||||
|
||||
fn set_cursor_representation<'b>(
|
||||
&'b self,
|
||||
fn set_cursor_representation(
|
||||
&self,
|
||||
buffer: &ImageBuffer<Rgba<u8>, Vec<u8>>,
|
||||
hotspot: (u32, u32),
|
||||
) -> Result<(), Self::Error>
|
||||
where
|
||||
'a: 'b,
|
||||
{
|
||||
) -> Result<(), Self::Error> {
|
||||
self.0.set_cursor_representation(buffer, hotspot)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,13 +5,13 @@
|
|||
//! 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.
|
||||
//!
|
||||
//! 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::{
|
||||
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 nix::libc::dev_t;
|
||||
|
@ -20,7 +20,6 @@ use nix::sys::stat::fstat;
|
|||
use std::cell::RefCell;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use std::path::PathBuf;
|
||||
use std::rc::{Rc, Weak};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
@ -34,45 +33,6 @@ use self::surface::{LegacyDrmSurfaceInternal, State};
|
|||
#[cfg(feature = "backend_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
|
||||
pub struct LegacyDrmDevice<A: AsRawFd + 'static> {
|
||||
dev: Rc<Dev<A>>,
|
||||
|
@ -85,7 +45,7 @@ pub struct LegacyDrmDevice<A: AsRawFd + 'static> {
|
|||
|
||||
pub(in crate::backend::drm) struct Dev<A: AsRawFd + 'static> {
|
||||
fd: A,
|
||||
priviledged: bool,
|
||||
privileged: bool,
|
||||
active: Arc<AtomicBool>,
|
||||
old_state: HashMap<crtc::Handle, (crtc::Info, Vec<connector::Handle>)>,
|
||||
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() {
|
||||
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>>,
|
||||
{
|
||||
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())
|
||||
.map_err(Error::UnableToGetDeviceId)?
|
||||
|
@ -144,7 +104,7 @@ impl<A: AsRawFd + 'static> LegacyDrmDevice<A> {
|
|||
let active = Arc::new(AtomicBool::new(true));
|
||||
let mut dev = Dev {
|
||||
fd: dev,
|
||||
priviledged: true,
|
||||
privileged: true,
|
||||
old_state: HashMap::new(),
|
||||
active: active.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
|
||||
if dev.acquire_master_lock().is_err() {
|
||||
warn!(log, "Unable to become drm master, assuming unpriviledged mode");
|
||||
dev.priviledged = false;
|
||||
warn!(log, "Unable to become drm master, assuming unprivileged mode");
|
||||
dev.privileged = false;
|
||||
};
|
||||
|
||||
// enumerate (and save) the current device state
|
||||
|
@ -308,7 +268,11 @@ impl<A: AsRawFd + 'static> Device for LegacyDrmDevice<A> {
|
|||
} else {
|
||||
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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
fn get_framebuffer_info(
|
||||
&self,
|
||||
fb: framebuffer::Handle,
|
||||
) -> std::result::Result<framebuffer::Info, DrmError> {
|
||||
fn get_framebuffer_info(&self, fb: framebuffer::Handle) -> Result<framebuffer::Info, DrmError> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ use crate::backend::session::{AsSessionObserver, SessionObserver};
|
|||
pub struct LegacyDrmDeviceObserver<A: AsRawFd + 'static> {
|
||||
dev: Weak<Dev<A>>,
|
||||
dev_id: dev_t,
|
||||
priviledged: bool,
|
||||
privileged: bool,
|
||||
active: Arc<AtomicBool>,
|
||||
backends: Weak<RefCell<HashMap<crtc::Handle, Weak<LegacyDrmSurfaceInternal<A>>>>>,
|
||||
logger: ::slog::Logger,
|
||||
|
@ -35,7 +35,7 @@ impl<A: AsRawFd + 'static> AsSessionObserver<LegacyDrmDeviceObserver<A>> for Leg
|
|||
dev: Rc::downgrade(&self.dev),
|
||||
dev_id: self.dev_id,
|
||||
active: self.active.clone(),
|
||||
priviledged: self.dev.priviledged,
|
||||
privileged: self.dev.privileged,
|
||||
backends: Rc::downgrade(&self.backends),
|
||||
logger: self.logger.clone(),
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ impl<A: AsRawFd + 'static> SessionObserver for LegacyDrmDeviceObserver<A> {
|
|||
}
|
||||
}
|
||||
self.active.store(false, Ordering::SeqCst);
|
||||
if self.priviledged {
|
||||
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);
|
||||
|
@ -84,7 +84,7 @@ impl<A: AsRawFd + 'static> SessionObserver for LegacyDrmDeviceObserver<A> {
|
|||
}
|
||||
}
|
||||
self.active.store(true, Ordering::SeqCst);
|
||||
if self.priviledged {
|
||||
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);
|
||||
|
|
|
@ -10,13 +10,13 @@ use std::os::unix::io::{AsRawFd, RawFd};
|
|||
use std::rc::Rc;
|
||||
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::SwapBuffersError;
|
||||
|
||||
use super::{Dev, Error};
|
||||
use super::Dev;
|
||||
|
||||
use failure::ResultExt;
|
||||
use failure::{Fail, ResultExt};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
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> ControlDevice for LegacyDrmSurfaceInternal<A> {}
|
||||
|
||||
impl<'a, A: AsRawFd + 'static> CursorBackend<'a> for LegacyDrmSurfaceInternal<A> {
|
||||
type CursorFormat = &'a dyn Buffer;
|
||||
impl<A: AsRawFd + 'static> CursorBackend for LegacyDrmSurfaceInternal<A> {
|
||||
type CursorFormat = dyn Buffer;
|
||||
type Error = 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>(
|
||||
&'b self,
|
||||
buffer: Self::CursorFormat,
|
||||
fn set_cursor_representation(
|
||||
&self,
|
||||
buffer: &Self::CursorFormat,
|
||||
hotspot: (u32, u32),
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
'a: 'b,
|
||||
{
|
||||
) -> Result<(), Error> {
|
||||
trace!(self.logger, "Setting the new imported cursor");
|
||||
|
||||
if self
|
||||
|
@ -108,58 +105,13 @@ impl<A: AsRawFd + 'static> Surface for LegacyDrmSurfaceInternal<A> {
|
|||
}
|
||||
|
||||
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 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,
|
||||
});
|
||||
}
|
||||
|
||||
if self.check_connector(conn, pending.mode.as_ref().unwrap())? {
|
||||
pending.connectors.insert(conn);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::ModeNotSuitable(pending.mode.unwrap()))
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn remove_connector(&self, connector: connector::Handle) -> Result<(), Error> {
|
||||
|
@ -167,6 +119,22 @@ impl<A: AsRawFd + 'static> Surface for LegacyDrmSurfaceInternal<A> {
|
|||
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> {
|
||||
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 added = pending.connectors.difference(¤t.connectors);
|
||||
|
||||
let mut conn_removed = false;
|
||||
for conn in removed {
|
||||
if let Ok(info) = self.get_connector(*conn) {
|
||||
info!(self.logger, "Removing connector: {:?}", info.interface());
|
||||
} else {
|
||||
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 {
|
||||
|
@ -254,7 +237,18 @@ impl<A: AsRawFd + 'static> RawSurface for LegacyDrmSurfaceInternal<A> {
|
|||
|
||||
*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> {
|
||||
|
@ -267,11 +261,61 @@ impl<A: AsRawFd + 'static> RawSurface for LegacyDrmSurfaceInternal<A> {
|
|||
&[PageFlipFlags::PageFlipEvent],
|
||||
None,
|
||||
)
|
||||
.map_err(|x| dbg!(x))
|
||||
.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> {
|
||||
fn drop(&mut self) {
|
||||
// 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> ControlDevice for LegacyDrmSurface<A> {}
|
||||
|
||||
impl<'a, A: AsRawFd + 'static> CursorBackend<'a> for LegacyDrmSurface<A> {
|
||||
type CursorFormat = &'a dyn Buffer;
|
||||
impl<A: AsRawFd + 'static> CursorBackend for LegacyDrmSurface<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<'b>(
|
||||
&'b self,
|
||||
buffer: Self::CursorFormat,
|
||||
fn set_cursor_representation(
|
||||
&self,
|
||||
buffer: &Self::CursorFormat,
|
||||
hotspot: (u32, u32),
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
'a: 'b,
|
||||
{
|
||||
) -> Result<(), Error> {
|
||||
self.0.set_cursor_representation(buffer, hotspot)
|
||||
}
|
||||
}
|
||||
|
@ -343,6 +384,10 @@ impl<A: AsRawFd + 'static> Surface for LegacyDrmSurface<A> {
|
|||
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> {
|
||||
self.0.use_mode(mode)
|
||||
}
|
||||
|
|
|
@ -53,6 +53,10 @@ use calloop::{LoopHandle, Source};
|
|||
|
||||
use super::graphics::SwapBuffersError;
|
||||
|
||||
#[cfg(feature = "backend_drm_atomic")]
|
||||
pub mod atomic;
|
||||
#[cfg(feature = "backend_drm")]
|
||||
pub mod common;
|
||||
#[cfg(feature = "backend_drm_egl")]
|
||||
pub mod egl;
|
||||
#[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.
|
||||
fn create_surface(
|
||||
&mut self,
|
||||
ctrc: crtc::Handle,
|
||||
crtc: crtc::Handle,
|
||||
) -> Result<Self::Surface, <Self::Surface as Surface>::Error>;
|
||||
|
||||
/// Processes any open events of the underlying file descriptor.
|
||||
|
@ -168,6 +172,13 @@ pub trait Surface {
|
|||
/// Tries to mark a [`connector`](drm::control::connector)
|
||||
/// for removal on the next commit.
|
||||
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)
|
||||
/// of the underlying [`crtc`](drm::control::crtc)
|
||||
/// if any.
|
||||
|
@ -203,7 +214,10 @@ pub trait RawSurface: Surface + ControlDevice + BasicDevice {
|
|||
/// potentially causing some flickering. Check before performing this
|
||||
/// 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>;
|
||||
/// Page-flip the underlying [`crtc`](drm::control::crtc)
|
||||
/// to a new given [`framebuffer`].
|
||||
|
|
|
@ -4,18 +4,18 @@
|
|||
/// 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.
|
||||
pub trait CursorBackend<'a> {
|
||||
pub trait CursorBackend {
|
||||
/// Format representing the image drawn for the cursor.
|
||||
type CursorFormat: 'a;
|
||||
type CursorFormat: ?Sized;
|
||||
|
||||
/// Error the underlying backend throws if operations fail
|
||||
type Error;
|
||||
type Error: 'static;
|
||||
|
||||
/// Sets the cursor position and therefore updates the drawn cursors position.
|
||||
/// Useful as well for e.g. pointer wrapping.
|
||||
///
|
||||
/// 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).
|
||||
///
|
||||
/// 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
|
||||
/// 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.
|
||||
fn set_cursor_representation<'b>(
|
||||
&'b self,
|
||||
cursor: Self::CursorFormat,
|
||||
fn set_cursor_representation(
|
||||
&self,
|
||||
cursor: &Self::CursorFormat,
|
||||
hotspot: (u32, u32),
|
||||
) -> Result<(), Self::Error>
|
||||
where
|
||||
'a: 'b;
|
||||
) -> Result<(), Self::Error>;
|
||||
}
|
||||
|
|
|
@ -247,8 +247,8 @@ impl WinitGraphicsBackend {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> CursorBackend<'a> for WinitGraphicsBackend {
|
||||
type CursorFormat = &'a CursorIcon;
|
||||
impl CursorBackend for WinitGraphicsBackend {
|
||||
type CursorFormat = CursorIcon;
|
||||
type Error = ();
|
||||
|
||||
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>(
|
||||
&'b self,
|
||||
cursor: Self::CursorFormat,
|
||||
fn set_cursor_representation(
|
||||
&self,
|
||||
cursor: &Self::CursorFormat,
|
||||
_hotspot: (u32, u32),
|
||||
) -> ::std::result::Result<(), ()>
|
||||
where
|
||||
'a: 'b,
|
||||
{
|
||||
) -> ::std::result::Result<(), ()> {
|
||||
// Cannot log this one, as `CursorFormat` is not `Debug` and should not be
|
||||
debug!(self.logger, "Changing cursor representation");
|
||||
self.window.window().set_cursor_icon(*cursor);
|
||||
|
|
Loading…
Reference in New Issue