use smithay::{backend::session::Session, reexports::rustix::fs::OFlags, utils::DeviceFd}; use std::{io, os::fd::OwnedFd, path::Path}; pub(super) struct SessionDevice { session: S, fd: Option, } impl SessionDevice { pub fn open(mut session: S, path: &Path) -> Result { let fd = session.open(path, OFlags::RDWR | OFlags::CLOEXEC | OFlags::NOCTTY | OFlags::NONBLOCK)?; Ok(Self { session, fd: Some(fd) }) } pub fn device_fd(&self) -> io::Result { self.fd.as_ref().unwrap().try_clone().map(DeviceFd::from) } } impl Drop for SessionDevice { fn drop(&mut self) { if let Err(error) = self.session.close(self.fd.take().unwrap()) { tracing::warn!(?error, "could not release DRM device through the session manager"); } } } #[cfg(test)] mod tests { use super::*; use std::{cell::Cell, fs::File, os::fd::AsRawFd, rc::Rc}; #[derive(Clone, Default)] struct TestSession(Rc>>); impl Session for TestSession { type Error = (); fn open(&mut self, _: &Path, _: OFlags) -> Result { if self.0.get().is_some() { return Err(()); } let fd: OwnedFd = File::open("/dev/null").unwrap().into(); self.0.set(Some(fd.as_raw_fd())); Ok(fd) } fn close(&mut self, fd: OwnedFd) -> Result<(), ()> { assert_eq!(self.0.replace(None), Some(fd.as_raw_fd())); Ok(()) } fn change_vt(&mut self, _: i32) -> Result<(), ()> { Ok(()) } fn is_active(&self) -> bool { true } fn seat(&self) -> String { "seat0".into() } } #[test] fn teardown_releases_original_session_lease_before_reopening() { let session = TestSession::default(); for _ in 0..3 { let lease = SessionDevice::open(session.clone(), Path::new("/dev/null")).unwrap(); let rendering = lease.device_fd().unwrap(); assert_ne!(Some(rendering.as_raw_fd()), session.0.get()); drop(rendering); assert!(session.0.get().is_some()); drop(lease); assert!(session.0.get().is_none()); } } #[test] fn failed_renderer_initialization_releases_lease() { fn initialize(session: TestSession) -> Result<(), ()> { let lease = SessionDevice::open(session, Path::new("/dev/null"))?; let _rendering = lease.device_fd().unwrap(); Err(()) } let session = TestSession::default(); assert!(initialize(session.clone()).is_err()); assert!(session.0.get().is_none()); assert!(SessionDevice::open(session, Path::new("/dev/null")).is_ok()); } }