diff --git a/.travis.yml b/.travis.yml index 6779588..f714af3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -39,7 +39,6 @@ env: - FEATURES="backend_libinput" - FEATURES="backend_udev" - FEATURES="backend_session" - - FEATURES="backend_session_udev" - FEATURES="backend_session_logind" - FEATURES="renderer_glium" - FEATURES="xwayland" diff --git a/Cargo.toml b/Cargo.toml index 39730bd..ce3a8db 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,9 +43,8 @@ backend_drm = ["drm", "backend_egl"] backend_egl = ["gl_generator"] backend_libinput = ["input"] backend_session = [] -backend_session_udev = ["udev", "backend_session"] +backend_udev = ["udev"] backend_session_logind = ["dbus", "systemd", "backend_session"] -backend_udev = ["udev", "backend_drm", "backend_session_udev"] renderer_gl = ["gl_generator"] renderer_glium = ["renderer_gl", "glium"] xwayland = [] \ No newline at end of file diff --git a/src/backend/udev.rs b/src/backend/udev.rs index 4055697..3f94d95 100644 --- a/src/backend/udev.rs +++ b/src/backend/udev.rs @@ -9,258 +9,91 @@ //! See also `examples/udev.rs` for pure hardware backed example of a compositor utilizing this //! backend. -use backend::{ - drm::{drm_device_bind, DrmDevice, DrmHandler}, - session::{AsSessionObserver, Session, SessionObserver}, -}; -use drm::{control::Device as ControlDevice, Device as BasicDevice}; -use nix::{fcntl, sys::stat::dev_t}; +use nix::sys::stat::{dev_t, stat}; use std::{ - cell::RefCell, - collections::HashMap, + collections::HashSet, ffi::OsString, - io::Error as IoError, - mem::drop, os::unix::io::{AsRawFd, RawFd}, path::{Path, PathBuf}, - rc::{Rc, Weak}, }; -use udev::{Context, Enumerator, Event, EventType, MonitorBuilder, MonitorSocket, Result as UdevResult}; +use udev::{Context, Enumerator, EventType, MonitorBuilder, MonitorSocket, Result as UdevResult}; use wayland_server::calloop::{ - generic::{EventedRawFd, Generic}, + generic::{EventedFd, Generic}, mio::Ready, - LoopHandle, Source, + LoopHandle, Source, InsertError, }; -/// Udev's `DrmDevice` type based on the underlying session -pub struct SessionFdDrmDevice(RawFd); - -impl AsRawFd for SessionFdDrmDevice { - fn as_raw_fd(&self) -> RawFd { - self.0 - } -} -impl BasicDevice for SessionFdDrmDevice {} -impl ControlDevice for SessionFdDrmDevice {} - -/// Graphical backend that monitors available DRM devices. +/// Backend to monitor available drm devices. /// -/// Provides a way to automatically initialize a `DrmDevice` for available GPUs and notifies the -/// given handler of any changes. Can be used to provide hot-plug functionality for GPUs and +/// Provides a way to automatically scan for available gpus and notifies the +/// given handler of any changes. Can be used to provide hot-plug functionality for gpus and /// attached monitors. -pub struct UdevBackend< - H: DrmHandler + 'static, - S: Session + 'static, - T: UdevHandler + 'static, - Data: 'static, -> { - _handler: ::std::marker::PhantomData, - devices: Rc< - RefCell< - HashMap< - dev_t, - ( - Source>, - Rc>>, - ), - >, - >, - >, +pub struct UdevBackend { + devices: HashSet, monitor: MonitorSocket, - session: S, handler: T, logger: ::slog::Logger, - handle: LoopHandle, } -impl< - H: DrmHandler + 'static, - S: Session + 'static, - T: UdevHandler + 'static, - Data: 'static, - > UdevBackend -{ +impl AsRawFd for UdevBackend { + fn as_raw_fd(&self) -> RawFd { + self.monitor.as_raw_fd() + } +} + +impl UdevBackend { /// Creates a new `UdevBackend` and adds it to the given `EventLoop`'s state. /// /// ## Arguments - /// `evlh` - An event loop to use for binding `DrmDevices` /// `context` - An initialized udev context - /// `session` - A session used to open and close devices as they become available /// `handler` - User-provided handler to respond to any detected changes + /// `seat` - /// `logger` - slog Logger to be used by the backend and its `DrmDevices`. - pub fn new( - handle: LoopHandle, + pub fn new>( context: &Context, - mut session: S, mut handler: T, + seat: S, logger: L, - ) -> Result> + ) -> UdevResult> where L: Into>, { - let logger = ::slog_or_stdlog(logger).new(o!("smithay_module" => "backend_udev")); - let seat = session.seat(); - let devices = all_gpus(context, seat) - .chain_err(|| ErrorKind::FailedToScan)? + let log = ::slog_or_stdlog(logger).new(o!("smithay_module" => "backend_udev")); + + let devices = all_gpus(context, seat)? .into_iter() // Create devices - .flat_map(|path| { - match DrmDevice::new( - { - match session.open( - &path, - fcntl::OFlag::O_RDWR - | fcntl::OFlag::O_CLOEXEC - | fcntl::OFlag::O_NOCTTY - | fcntl::OFlag::O_NONBLOCK, - ) { - Ok(fd) => SessionFdDrmDevice(fd), - Err(err) => { - warn!( - logger, - "Unable to open drm device {:?}, Error: {:?}. Skipping", path, err - ); - return None; - } - } - }, - logger.clone(), - ) { - // Call the handler, which might add it to the runloop - Ok(mut device) => { - let devnum = device.device_id(); - let fd = device.as_raw_fd(); - match handler.device_added(&mut device) { - Some(drm_handler) => match drm_device_bind(&handle, device, drm_handler) { - Ok((event_source, device)) => Some((devnum, (event_source, device))), - Err((err, mut device)) => { - warn!(logger, "Failed to bind device. Error: {:?}.", err); - handler.device_removed(&mut device); - drop(device); - if let Err(err) = session.close(fd) { - warn!( - logger, - "Failed to close dropped device. Error: {:?}. Ignoring", err - ); - }; - None - } - }, - None => { - drop(device); //drops master - if let Err(err) = session.close(fd) { - warn!(logger, "Failed to close device. Error: {:?}. Ignoring", err); - } - None - } - } - } - Err(err) => { - warn!( - logger, - "Failed to initialize device {:?}. Error: {:?}. Skipping", path, err - ); - None - } + .flat_map(|path| match stat(&path) { + Ok(stat) => { + handler.device_added(stat.st_rdev, path); + Some(stat.st_rdev) + }, + Err(err) => { + warn!(log, "Unable to get id of {:?}, Error: {:?}. Skipping", path, err); + None } - }).collect::>(); + }) + .collect(); - let mut builder = MonitorBuilder::new(context).chain_err(|| ErrorKind::FailedToInitMonitor)?; - builder - .match_subsystem("drm") - .chain_err(|| ErrorKind::FailedToInitMonitor)?; - let monitor = builder.listen().chain_err(|| ErrorKind::FailedToInitMonitor)?; + let mut builder = MonitorBuilder::new(context)?; + builder.match_subsystem("drm")?; + let monitor = builder.listen()?; Ok(UdevBackend { - _handler: ::std::marker::PhantomData, - devices: Rc::new(RefCell::new(devices)), + devices, monitor, - session, handler, - logger, - handle, + logger: log, }) } - - /// Closes the udev backend and frees all remaining open devices. - pub fn close(&mut self) { - let mut devices = self.devices.borrow_mut(); - for (_, (event_source, device)) in devices.drain() { - event_source.remove(); - let mut device = Rc::try_unwrap(device) - .unwrap_or_else(|_| unreachable!()) - .into_inner(); - self.handler.device_removed(&mut device); - let fd = device.as_raw_fd(); - drop(device); - if let Err(err) = self.session.close(fd) { - warn!(self.logger, "Failed to close device. Error: {:?}. Ignoring", err); - }; - } - info!(self.logger, "All devices closed"); - } } -impl< - H: DrmHandler + 'static, - S: Session + 'static, - T: UdevHandler + 'static, - Data: 'static, - > Drop for UdevBackend +impl Drop for UdevBackend { fn drop(&mut self) { - self.close(); - } -} - -/// `SessionObserver` linked to the `UdevBackend` it was created from. -pub struct UdevBackendObserver { - devices: Weak< - RefCell< - HashMap< - dev_t, - ( - Source>, - Rc>>, - ), - >, - >, - >, - logger: ::slog::Logger, -} - -impl< - H: DrmHandler + 'static, - S: Session + 'static, - T: UdevHandler + 'static, - Data: 'static, - > AsSessionObserver for UdevBackend -{ - fn observer(&mut self) -> UdevBackendObserver { - UdevBackendObserver { - devices: Rc::downgrade(&self.devices), - logger: self.logger.clone(), - } - } -} - -impl SessionObserver for UdevBackendObserver { - fn pause(&mut self, devnum: Option<(u32, u32)>) { - if let Some(devices) = self.devices.upgrade() { - for &mut (_, ref device) in devices.borrow_mut().values_mut() { - info!(self.logger, "changed successful"); - device.borrow_mut().observer().pause(devnum); - } - } - } - - fn activate(&mut self, devnum: Option<(u32, u32, Option)>) { - if let Some(devices) = self.devices.upgrade() { - for &mut (_, ref device) in devices.borrow_mut().values_mut() { - info!(self.logger, "changed successful"); - device.borrow_mut().observer().activate(devnum); - } + for device in &self.devices { + self.handler.device_removed(*device); } } } @@ -269,122 +102,39 @@ impl SessionObserver for UdevBackendObserver { /// /// Allows the backend to receive kernel events and thus to drive the `UdevHandler`. /// No runtime functionality can be provided without using this function. -pub fn udev_backend_bind( - mut udev: UdevBackend, -) -> ::std::result::Result>, IoError> -where - H: DrmHandler + 'static, - T: UdevHandler + 'static, - S: Session + 'static, +pub fn udev_backend_bind( + handle: &LoopHandle, + udev: UdevBackend, +) -> Result>>>, InsertError>>>> { - let fd = udev.monitor.as_raw_fd(); - let handle = udev.handle.clone(); - let mut source = Generic::from_raw_fd(fd); + let mut source = Generic::from_fd_source(udev); source.set_interest(Ready::readable()); - handle - .insert_source(source, move |_, _| { - udev.process_events(); - }).map_err(Into::into) + + handle.insert_source(source, |evt, _| { + evt.source.borrow_mut().0.process_events(); + }) } -impl UdevBackend -where - H: DrmHandler + 'static, - T: UdevHandler + 'static, - S: Session + 'static, - Data: 'static, -{ +impl UdevBackend { fn process_events(&mut self) { - let events = self.monitor.clone().collect::>(); - for event in events { + let monitor = self.monitor.clone(); + for event in monitor { match event.event_type() { // New device EventType::Add => { info!(self.logger, "Device Added"); if let (Some(path), Some(devnum)) = (event.devnode(), event.devnum()) { - let mut device = { - match DrmDevice::new( - { - let logger = self.logger.clone(); - match self.session.open( - path, - fcntl::OFlag::O_RDWR - | fcntl::OFlag::O_CLOEXEC - | fcntl::OFlag::O_NOCTTY - | fcntl::OFlag::O_NONBLOCK, - ) { - Ok(fd) => SessionFdDrmDevice(fd), - Err(err) => { - warn!( - logger, - "Unable to open drm device {:?}, Error: {:?}. Skipping", - path, - err - ); - continue; - } - } - }, - self.logger.clone(), - ) { - Ok(dev) => dev, - Err(err) => { - warn!( - self.logger, - "Failed to initialize device {:?}. Error: {}. Skipping", path, err - ); - continue; - } - } - }; - let fd = device.as_raw_fd(); - match self.handler.device_added(&mut device) { - Some(drm_handler) => match drm_device_bind(&self.handle, device, drm_handler) { - Ok(fd_event_source) => { - self.devices.borrow_mut().insert(devnum, fd_event_source); - } - Err((err, mut device)) => { - warn!(self.logger, "Failed to bind device. Error: {:?}.", err); - self.handler.device_removed(&mut device); - drop(device); - if let Err(err) = self.session.close(fd) { - warn!( - self.logger, - "Failed to close dropped device. Error: {:?}. Ignoring", err - ); - }; - } - }, - None => { - self.handler.device_removed(&mut device); - drop(device); - if let Err(err) = self.session.close(fd) { - warn!(self.logger, "Failed to close unused device. Error: {:?}", err); - } - } - }; + if self.devices.insert(devnum) { + self.handler.device_added(devnum, path.to_path_buf()); + } } } // Device removed EventType::Remove => { info!(self.logger, "Device Remove"); if let Some(devnum) = event.devnum() { - if let Some((fd_event_source, device)) = self.devices.borrow_mut().remove(&devnum) { - fd_event_source.remove(); - let mut device = Rc::try_unwrap(device) - .unwrap_or_else(|_| unreachable!()) - .into_inner(); - self.handler.device_removed(&mut device); - let fd = device.as_raw_fd(); - drop(device); - if let Err(err) = self.session.close(fd) { - warn!( - self.logger, - "Failed to close device {:?}. Error: {:?}. Ignoring", - event.sysname(), - err - ); - }; + if self.devices.remove(&devnum) { + self.handler.device_removed(devnum); } } } @@ -393,9 +143,8 @@ where info!(self.logger, "Device Changed"); if let Some(devnum) = event.devnum() { info!(self.logger, "Devnum: {:b}", devnum); - if let Some(&(_, ref device)) = self.devices.borrow_mut().get(&devnum) { - let handler = &mut self.handler; - handler.device_changed(&mut device.borrow_mut()); + if self.devices.contains(&devnum) { + self.handler.device_changed(devnum); } else { info!(self.logger, "changed, but device not tracked by backend"); }; @@ -409,36 +158,21 @@ where } } -/// Handler for the `UdevBackend`, allows to open, close and update DRM devices as they change during runtime. -pub trait UdevHandler + 'static> { - /// Called on initialization for every known device and when a new device is detected. - /// - /// Returning a `DrmHandler` will initialize the device, returning `None` will ignore the device. - /// - /// ## Panics - /// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`. - fn device_added(&mut self, device: &mut DrmDevice) -> Option; +/// Handler for the `UdevBackend`, allows to open, close and update drm devices as they change during runtime. +pub trait UdevHandler { + /// Called when a new device is detected. + fn device_added(&mut self, device: dev_t, path: PathBuf); /// Called when an open device is changed. /// /// This usually indicates that some connectors did become available or were unplugged. The handler /// should scan again for connected monitors and mode switch accordingly. - /// - /// ## Panics - /// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`. - fn device_changed(&mut self, device: &mut DrmDevice); + fn device_changed(&mut self, device: dev_t); /// Called when a device was removed. /// - /// The device will not accept any operations anymore and its file descriptor will be closed once - /// this function returns, any open references/tokens to this device need to be released. - /// - /// ## Panics - /// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`. - fn device_removed(&mut self, device: &mut DrmDevice); - /// Called when the udev context has encountered and error. - /// - /// ## Panics - /// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`. - fn error(&mut self, error: IoError); + /// The corresponding `UdevRawFd` will never return a valid `RawFd` anymore + /// and its file descriptor will be closed once this function returns, + /// any open references/tokens to this device need to be released. + fn device_removed(&mut self, device: dev_t); } /// Returns the path of the primary GPU device if any @@ -489,22 +223,3 @@ pub fn all_gpus>(context: &Context, seat: S) -> UdevResult