diff --git a/anvil/src/udev.rs b/anvil/src/udev.rs index cc5a2d8..b1cdfa3 100644 --- a/anvil/src/udev.rs +++ b/anvil/src/udev.rs @@ -28,10 +28,7 @@ use smithay::{ }, graphics::{CursorBackend, SwapBuffersError}, libinput::{LibinputInputBackend, LibinputSessionInterface}, - session::{ - auto::AutoSession, notify_multiplexer, AsSessionObserver, Session, SessionNotifier, - SessionObserver, - }, + session::{auto::AutoSession, Session, Signal as SessionSignal}, udev::{primary_gpu, UdevBackend, UdevEvent}, }, reexports::{ @@ -56,6 +53,7 @@ use smithay::{ Display, }, }, + signaling::{Linkable, SignalToken, Signaler}, wayland::{ compositor::CompositorToken, output::{Mode, Output, PhysicalProperties}, @@ -117,9 +115,8 @@ pub fn run_udev( /* * Initialize session */ - let (session, mut notifier) = AutoSession::new(log.clone()).ok_or(())?; - let (udev_observer, udev_notifier) = notify_multiplexer(); - let udev_session_id = notifier.register(udev_observer); + let (session, notifier) = AutoSession::new(log.clone()).ok_or(())?; + let session_signal = notifier.signaler(); /* * Initialize the compositor @@ -154,7 +151,7 @@ pub fn run_udev( cursor_status: state.cursor_status.clone(), dnd_icon: state.dnd_icon.clone(), loop_handle: event_loop.handle(), - notifier: udev_notifier, + signaler: session_signal.clone(), logger: log.clone(), }; @@ -196,9 +193,9 @@ pub fn run_udev( let mut libinput_context = Libinput::new_with_udev::>( state.session.clone().unwrap().into(), ); - let libinput_session_id = notifier.register(libinput_context.observer()); libinput_context.udev_assign_seat(&state.seat_name).unwrap(); - let libinput_backend = LibinputInputBackend::new(libinput_context, log.clone()); + let mut libinput_backend = LibinputInputBackend::new(libinput_context, log.clone()); + libinput_backend.link(session_signal); /* * Bind all our objects that get driven by the event loop @@ -246,29 +243,25 @@ pub fn run_udev( // Cleanup stuff state.window_map.borrow_mut().clear(); - let mut notifier = event_loop.handle().remove(session_event_source); - notifier.unregister(libinput_session_id); - notifier.unregister(udev_session_id); - + event_loop.handle().remove(session_event_source); event_loop.handle().remove(libinput_event_source); event_loop.handle().remove(udev_event_source); Ok(()) } -struct BackendData { - id: S::Id, - restart_id: S::Id, +struct BackendData { + _restart_token: SignalToken, event_source: Source>, surfaces: Rc>>>>, } -struct UdevHandlerImpl { +struct UdevHandlerImpl { compositor_token: CompositorToken, #[cfg(feature = "egl")] egl_buffer_reader: Rc>>, session: AutoSession, - backends: HashMap>, + backends: HashMap, display: Rc>, primary_gpu: Option, window_map: Rc>, @@ -277,11 +270,11 @@ struct UdevHandlerImpl { cursor_status: Arc>, dnd_icon: Arc>>, loop_handle: LoopHandle, - notifier: S, + signaler: Signaler, logger: ::slog::Logger, } -impl UdevHandlerImpl { +impl UdevHandlerImpl { #[cfg(feature = "egl")] pub fn scan_connectors( device: &mut RenderDevice, @@ -375,7 +368,7 @@ impl UdevHandlerImpl { } } -impl UdevHandlerImpl { +impl UdevHandlerImpl { fn device_added(&mut self, _device: dev_t, path: PathBuf) { // Try to open the device if let Some(mut device) = self @@ -425,14 +418,14 @@ impl UdevHandlerImpl { } #[cfg(feature = "egl")] - let backends = Rc::new(RefCell::new(UdevHandlerImpl::::scan_connectors( + let backends = Rc::new(RefCell::new(UdevHandlerImpl::::scan_connectors( &mut device, self.egl_buffer_reader.clone(), &self.logger, ))); #[cfg(not(feature = "egl"))] - let backends = Rc::new(RefCell::new(UdevHandlerImpl::::scan_connectors( + let backends = Rc::new(RefCell::new(UdevHandlerImpl::::scan_connectors( &mut device, &self.logger, ))); @@ -449,16 +442,20 @@ impl UdevHandlerImpl { dnd_icon: self.dnd_icon.clone(), logger: self.logger.clone(), }); - let restart_id = self.notifier.register(DrmRendererSessionListener { + let mut listener = DrmRendererSessionListener { renderer: renderer.clone(), loop_handle: self.loop_handle.clone(), + }; + let restart_token = self.signaler.register(move |signal| match signal { + SessionSignal::ActivateSession | SessionSignal::ActivateDevice { .. } => listener.activate(), + _ => {} }); device.set_handler(DrmHandlerImpl { renderer, loop_handle: self.loop_handle.clone(), }); - let device_session_id = self.notifier.register(device.observer()); + device.link(self.signaler.clone()); let dev_id = device.device_id(); let event_source = device_bind(&self.loop_handle, device) .map_err(|e| -> IoError { e.into() }) @@ -478,8 +475,7 @@ impl UdevHandlerImpl { self.backends.insert( dev_id, BackendData { - id: device_session_id, - restart_id, + _restart_token: restart_token, event_source, surfaces: backends, }, @@ -498,13 +494,10 @@ impl UdevHandlerImpl { .with_source(&backend_data.event_source, |source| { let mut backends = backend_data.surfaces.borrow_mut(); #[cfg(feature = "egl")] - let new_backends = UdevHandlerImpl::::scan_connectors( - &mut source.file, - egl_buffer_reader, - logger, - ); + let new_backends = + UdevHandlerImpl::::scan_connectors(&mut source.file, egl_buffer_reader, logger); #[cfg(not(feature = "egl"))] - let new_backends = UdevHandlerImpl::::scan_connectors(&mut source.file, logger); + let new_backends = UdevHandlerImpl::::scan_connectors(&mut source.file, logger); *backends = new_backends; for renderer in backends.values() { @@ -537,9 +530,6 @@ impl UdevHandlerImpl { *self.egl_buffer_reader.borrow_mut() = None; } } - - self.notifier.unregister(backend_data.id); - self.notifier.unregister(backend_data.restart_id); debug!(self.logger, "Dropping device"); } } @@ -567,9 +557,8 @@ pub struct DrmRendererSessionListener { loop_handle: LoopHandle, } -impl SessionObserver for DrmRendererSessionListener { - fn pause(&mut self, _device: Option<(u32, u32)>) {} - fn activate(&mut self, _device: Option<(u32, u32, Option)>) { +impl DrmRendererSessionListener { + fn activate(&mut self) { // we want to be called, after all session handling is done (TODO this is not so nice) let renderer = self.renderer.clone(); let handle = self.loop_handle.clone(); diff --git a/src/backend/drm/atomic/mod.rs b/src/backend/drm/atomic/mod.rs index 13d7c45..a67ecd9 100644 --- a/src/backend/drm/atomic/mod.rs +++ b/src/backend/drm/atomic/mod.rs @@ -44,6 +44,8 @@ pub struct AtomicDrmDevice { active: Arc, backends: Rc>>>>, handler: Option>>>>, + #[cfg(feature = "backend_session")] + links: Vec, logger: ::slog::Logger, } @@ -324,6 +326,8 @@ impl AtomicDrmDevice { active, backends: Rc::new(RefCell::new(HashMap::new())), handler: None, + #[cfg(feature = "backend_session")] + links: Vec::new(), logger: log.clone(), }) } diff --git a/src/backend/drm/atomic/session.rs b/src/backend/drm/atomic/session.rs index 8f15c13..9d4b39e 100644 --- a/src/backend/drm/atomic/session.rs +++ b/src/backend/drm/atomic/session.rs @@ -17,7 +17,10 @@ use std::sync::Arc; use super::{AtomicDrmDevice, AtomicDrmSurfaceInternal, Dev}; use crate::backend::drm::{common::Error, DevPath, Surface}; -use crate::backend::session::{AsSessionObserver, SessionObserver}; +use crate::{ + backend::session::Signal as SessionSignal, + signaling::{Linkable, Signaler}, +}; /// [`SessionObserver`](SessionObserver) /// linked to the [`AtomicDrmDevice`](AtomicDrmDevice) @@ -31,20 +34,34 @@ pub struct AtomicDrmDeviceObserver { logger: ::slog::Logger, } -impl AsSessionObserver> for AtomicDrmDevice { - fn observer(&mut self) -> AtomicDrmDeviceObserver { - AtomicDrmDeviceObserver { +impl Linkable for AtomicDrmDevice { + fn link(&mut self, signaler: Signaler) { + let mut observer = 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(), - } + }; + + let token = signaler.register(move |signal| observer.signal(*signal)); + self.links.push(token); } } -impl SessionObserver for AtomicDrmDeviceObserver { +impl AtomicDrmDeviceObserver { + fn signal(&mut self, signal: SessionSignal) { + match signal { + SessionSignal::PauseSession => self.pause(None), + SessionSignal::PauseDevice { major, minor } => self.pause(Some((major, minor))), + SessionSignal::ActivateSession => self.activate(None), + SessionSignal::ActivateDevice { major, minor, new_fd } => { + self.activate(Some((major, minor, new_fd))) + } + } + } + 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) { @@ -104,9 +121,7 @@ impl SessionObserver for AtomicDrmDeviceObserver { // TODO call drm-handler::error } } -} -impl AtomicDrmDeviceObserver { fn reset_state(&mut self) -> Result<(), Error> { if let Some(dev) = self.dev.upgrade() { let res_handles = ControlDevice::resource_handles(&*dev) diff --git a/src/backend/drm/common/fallback.rs b/src/backend/drm/common/fallback.rs index 597864b..c1ddef7 100644 --- a/src/backend/drm/common/fallback.rs +++ b/src/backend/drm/common/fallback.rs @@ -24,7 +24,6 @@ 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}, @@ -111,44 +110,16 @@ where } } -/// [`SessionObserver`](::backend::session::SessionObserver) Wrapper to assist fallback -/// in case initialization of the preferred device type fails. -pub enum FallbackDeviceObserver { - /// Variant for successful initialization of the preferred device - Preference(O1), - /// Variant for the fallback device - Fallback(O2), -} - -impl AsSessionObserver> for FallbackDevice +#[cfg(feature = "backend_session")] +impl crate::signaling::Linkable for FallbackDevice where - O1: SessionObserver + 'static, - O2: SessionObserver + 'static, - D1: Device + AsSessionObserver + 'static, - D2: Device + AsSessionObserver + 'static, + D1: Device + crate::signaling::Linkable + 'static, + D2: Device + crate::signaling::Linkable + 'static, { - fn observer(&mut self) -> FallbackDeviceObserver { + fn link(&mut self, signal: crate::signaling::Signaler) { match self { - FallbackDevice::Preference(dev) => FallbackDeviceObserver::Preference(dev.observer()), - FallbackDevice::Fallback(dev) => FallbackDeviceObserver::Fallback(dev.observer()), - } - } -} - -impl SessionObserver - for FallbackDeviceObserver -{ - fn pause(&mut self, device: Option<(u32, u32)>) { - match self { - FallbackDeviceObserver::Preference(dev) => dev.pause(device), - FallbackDeviceObserver::Fallback(dev) => dev.pause(device), - } - } - - fn activate(&mut self, device: Option<(u32, u32, Option)>) { - match self { - FallbackDeviceObserver::Preference(dev) => dev.activate(device), - FallbackDeviceObserver::Fallback(dev) => dev.activate(device), + FallbackDevice::Preference(d) => d.link(signal), + FallbackDevice::Fallback(d) => d.link(signal), } } } diff --git a/src/backend/drm/egl/mod.rs b/src/backend/drm/egl/mod.rs index 70f9934..2f17d41 100644 --- a/src/backend/drm/egl/mod.rs +++ b/src/backend/drm/egl/mod.rs @@ -62,6 +62,8 @@ where default_attributes: GlAttributes, default_requirements: PixelFormatRequirements, backends: Rc>>>, + #[cfg(feature = "backend_session")] + links: Vec, } impl AsRawFd for EglDevice @@ -129,6 +131,8 @@ where default_requirements, backends: Rc::new(RefCell::new(HashMap::new())), logger: log, + #[cfg(feature = "backend_session")] + links: Vec::new(), }) } } diff --git a/src/backend/drm/egl/session.rs b/src/backend/drm/egl/session.rs index 9a6c774..cdcbf64 100644 --- a/src/backend/drm/egl/session.rs +++ b/src/backend/drm/egl/session.rs @@ -6,7 +6,6 @@ use drm::control::{connector, crtc, Mode}; use std::cell::RefCell; use std::collections::HashMap; -use std::os::unix::io::RawFd; use std::rc::{Rc, Weak}; use super::{EglDevice, EglSurfaceInternal}; @@ -15,42 +14,52 @@ use crate::backend::egl::{ ffi, native::{Backend, NativeDisplay, NativeSurface}, }; -use crate::backend::session::{AsSessionObserver, SessionObserver}; +use crate::{ + backend::session::Signal as SessionSignal, + signaling::{Linkable, Signaler}, +}; /// [`SessionObserver`](SessionObserver) /// linked to the [`EglDevice`](EglDevice) it was /// created from. -pub struct EglDeviceObserver { - observer: S, +pub struct EglDeviceObserver { backends: Weak>>>>, } -impl AsSessionObserver::Surface>> for EglDevice +impl Linkable for EglDevice where - S: SessionObserver + 'static, B: Backend::Surface, Error = <::Surface as Surface>::Error> + 'static, D: Device + NativeDisplay)> - + AsSessionObserver + + Linkable + 'static, ::Surface: NativeSurface::Surface as Surface>::Error>, { - fn observer(&mut self) -> EglDeviceObserver::Surface> { - EglDeviceObserver { - observer: self.dev.borrow_mut().observer(), + fn link(&mut self, signaler: Signaler) { + let lower_signal = Signaler::new(); + self.dev.borrow_mut().link(lower_signal.clone()); + let mut observer = EglDeviceObserver { backends: Rc::downgrade(&self.backends), - } + }; + + let token = signaler.register(move |&signal| match signal { + SessionSignal::ActivateSession | SessionSignal::ActivateDevice { .. } => { + // Activate lower *before* we process the event + lower_signal.signal(signal); + observer.activate() + } + _ => { + lower_signal.signal(signal); + } + }); + + self.links.push(token); } } -impl SessionObserver for EglDeviceObserver { - fn pause(&mut self, devnum: Option<(u32, u32)>) { - self.observer.pause(devnum); - } - - fn activate(&mut self, devnum: Option<(u32, u32, Option)>) { - self.observer.activate(devnum); +impl EglDeviceObserver { + fn activate(&mut self) { if let Some(backends) = self.backends.upgrade() { for (_crtc, backend) in backends.borrow().iter() { if let Some(backend) = backend.upgrade() { diff --git a/src/backend/drm/eglstream/mod.rs b/src/backend/drm/eglstream/mod.rs index 24e2bfe..c8bc001 100644 --- a/src/backend/drm/eglstream/mod.rs +++ b/src/backend/drm/eglstream/mod.rs @@ -82,6 +82,8 @@ pub struct EglStreamDevice { raw: D, backends: Rc>>>>, logger: ::slog::Logger, + #[cfg(feature = "backend_session")] + links: Vec, } impl EglStreamDevice { @@ -202,6 +204,8 @@ impl EglStreamDevice { raw, backends: Rc::new(RefCell::new(HashMap::new())), logger: log, + #[cfg(feature = "backend_session")] + links: Vec::new(), }) } } diff --git a/src/backend/drm/eglstream/session.rs b/src/backend/drm/eglstream/session.rs index 8b5d7fc..8904a24 100644 --- a/src/backend/drm/eglstream/session.rs +++ b/src/backend/drm/eglstream/session.rs @@ -6,11 +6,11 @@ use super::{EglStreamDevice, EglStreamSurfaceInternal}; use crate::backend::drm::{RawDevice, Surface}; use crate::backend::egl::ffi; -use crate::backend::session::{AsSessionObserver, SessionObserver}; +use crate::backend::session::Signal as SessionSignal; +use crate::signaling::{Linkable, Signaler}; use std::cell::RefCell; use std::collections::HashMap; -use std::os::unix::io::RawFd; use std::rc::{Rc, Weak}; use drm::control::{crtc, Device as ControlDevice}; @@ -18,31 +18,42 @@ use drm::control::{crtc, Device as ControlDevice}; /// [`SessionObserver`](SessionObserver) /// linked to the [`EglStreamDevice`](EglStreamDevice) it was /// created from. -pub struct EglStreamDeviceObserver< - O: SessionObserver + 'static, - D: RawDevice + AsSessionObserver + 'static, -> { - observer: O, +pub struct EglStreamDeviceObserver { backends: Weak>>>>, logger: ::slog::Logger, } -impl + 'static> - AsSessionObserver> for EglStreamDevice +impl Linkable for EglStreamDevice +where + D: RawDevice + ControlDevice + Linkable + 'static, { - fn observer(&mut self) -> EglStreamDeviceObserver { - EglStreamDeviceObserver { - observer: self.raw.observer(), + fn link(&mut self, signaler: Signaler) { + let lower_signal = Signaler::new(); + self.raw.link(lower_signal.clone()); + let mut observer = EglStreamDeviceObserver { backends: Rc::downgrade(&self.backends), logger: self.logger.clone(), - } + }; + + let token = signaler.register(move |&signal| match signal { + SessionSignal::ActivateSession | SessionSignal::ActivateDevice { .. } => { + // activate lower device *before* we process the signal + lower_signal.signal(signal); + observer.activate(); + } + SessionSignal::PauseSession | SessionSignal::PauseDevice { .. } => { + // pause lower device *after* we process the signal + observer.pause(); + lower_signal.signal(signal); + } + }); + + self.links.push(token); } } -impl + 'static> SessionObserver - for EglStreamDeviceObserver -{ - fn pause(&mut self, devnum: Option<(u32, u32)>) { +impl EglStreamDeviceObserver { + fn pause(&mut self) { if let Some(backends) = self.backends.upgrade() { for (_, backend) in backends.borrow().iter() { if let Some(backend) = backend.upgrade() { @@ -60,12 +71,9 @@ impl + 'static } } } - - self.observer.pause(devnum); } - fn activate(&mut self, devnum: Option<(u32, u32, Option)>) { - self.observer.activate(devnum); + fn activate(&mut self) { if let Some(backends) = self.backends.upgrade() { for (_, backend) in backends.borrow().iter() { if let Some(backend) = backend.upgrade() { diff --git a/src/backend/drm/gbm/mod.rs b/src/backend/drm/gbm/mod.rs index 2cd9523..89e3305 100644 --- a/src/backend/drm/gbm/mod.rs +++ b/src/backend/drm/gbm/mod.rs @@ -76,6 +76,8 @@ static LOAD: Once = Once::new(); pub struct GbmDevice { pub(self) dev: Rc>>, backends: Rc>>>>, + #[cfg(feature = "backend_session")] + links: Vec, logger: ::slog::Logger, } @@ -110,6 +112,8 @@ impl GbmDevice { // Open the gbm device from the drm device dev: Rc::new(RefCell::new(gbm::Device::new(dev).map_err(Error::InitFailed)?)), backends: Rc::new(RefCell::new(HashMap::new())), + #[cfg(feature = "backend_session")] + links: Vec::new(), logger: log, }) } diff --git a/src/backend/drm/gbm/session.rs b/src/backend/drm/gbm/session.rs index 4ae20c8..46112ca 100644 --- a/src/backend/drm/gbm/session.rs +++ b/src/backend/drm/gbm/session.rs @@ -7,53 +7,57 @@ use drm::control::crtc; use gbm::BufferObject; use std::cell::RefCell; use std::collections::HashMap; -use std::os::unix::io::RawFd; use std::rc::{Rc, Weak}; use super::{GbmDevice, GbmSurfaceInternal}; -use crate::backend::drm::{RawDevice, RawSurface}; +use crate::backend::drm::{Device, RawDevice}; use crate::backend::graphics::CursorBackend; -use crate::backend::session::{AsSessionObserver, SessionObserver}; +use crate::{ + backend::session::Signal as SessionSignal, + signaling::{Linkable, Signaler}, +}; /// [`SessionObserver`](SessionObserver) /// linked to the [`GbmDevice`](GbmDevice) it was /// created from. -pub struct GbmDeviceObserver< - S: SessionObserver + 'static, - D: RawDevice + ::drm::control::Device + AsSessionObserver + 'static, -> { - observer: S, +pub(crate) struct GbmDeviceObserver { backends: Weak>>>>, logger: ::slog::Logger, } -impl< - O: SessionObserver + 'static, - S: CursorBackend + RawSurface + 'static, - D: RawDevice + drm::control::Device + AsSessionObserver + 'static, - > AsSessionObserver> for GbmDevice +impl Linkable for GbmDevice +where + D: RawDevice + drm::control::Device + Linkable + 'static, + ::Surface: CursorBackend, { - fn observer(&mut self) -> GbmDeviceObserver { - GbmDeviceObserver { - observer: (**self.dev.borrow_mut()).observer(), + fn link(&mut self, signaler: Signaler) { + let lower_signal = Signaler::new(); + self.dev.borrow_mut().link(lower_signal.clone()); + let mut observer = GbmDeviceObserver { backends: Rc::downgrade(&self.backends), logger: self.logger.clone(), - } + }; + + let token = signaler.register(move |&signal| match signal { + SessionSignal::ActivateSession | SessionSignal::ActivateDevice { .. } => { + // Activate lower *before* we process the event + lower_signal.signal(signal); + observer.activate() + } + _ => { + lower_signal.signal(signal); + } + }); + + self.links.push(token); } } -impl< - O: SessionObserver + 'static, - S: CursorBackend + RawSurface + 'static, - D: RawDevice + drm::control::Device + AsSessionObserver + 'static, - > SessionObserver for GbmDeviceObserver +impl GbmDeviceObserver +where + ::Surface: CursorBackend, { - fn pause(&mut self, devnum: Option<(u32, u32)>) { - self.observer.pause(devnum); - } - - fn activate(&mut self, devnum: Option<(u32, u32, Option)>) { - self.observer.activate(devnum); + fn activate(&mut self) { let mut crtcs = Vec::new(); if let Some(backends) = self.backends.upgrade() { for (crtc, backend) in backends.borrow().iter() { diff --git a/src/backend/drm/legacy/mod.rs b/src/backend/drm/legacy/mod.rs index d99294f..1897222 100644 --- a/src/backend/drm/legacy/mod.rs +++ b/src/backend/drm/legacy/mod.rs @@ -40,6 +40,8 @@ pub struct LegacyDrmDevice { active: Arc, backends: Rc>>>>, handler: Option>>>>, + #[cfg(feature = "backend_session")] + links: Vec, logger: ::slog::Logger, } @@ -184,6 +186,8 @@ impl LegacyDrmDevice { active, backends: Rc::new(RefCell::new(HashMap::new())), handler: None, + #[cfg(feature = "backend_session")] + links: Vec::new(), logger: log.clone(), }) } diff --git a/src/backend/drm/legacy/session.rs b/src/backend/drm/legacy/session.rs index a211bce..5ceda28 100644 --- a/src/backend/drm/legacy/session.rs +++ b/src/backend/drm/legacy/session.rs @@ -16,12 +16,15 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use super::{Dev, DevPath, Error, LegacyDrmDevice, LegacyDrmSurfaceInternal}; -use crate::backend::session::{AsSessionObserver, SessionObserver}; +use crate::{ + backend::session::Signal as SessionSignal, + signaling::{Linkable, Signaler}, +}; /// [`SessionObserver`](SessionObserver) /// linked to the [`LegacyDrmDevice`](LegacyDrmDevice) /// it was created from. -pub struct LegacyDrmDeviceObserver { +pub(crate) struct LegacyDrmDeviceObserver { dev: Weak>, dev_id: dev_t, privileged: bool, @@ -30,20 +33,34 @@ pub struct LegacyDrmDeviceObserver { logger: ::slog::Logger, } -impl AsSessionObserver> for LegacyDrmDevice { - fn observer(&mut self) -> LegacyDrmDeviceObserver { - LegacyDrmDeviceObserver { +impl Linkable for LegacyDrmDevice { + fn link(&mut self, signaler: Signaler) { + let mut observer = LegacyDrmDeviceObserver { 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(), - } + }; + + let token = signaler.register(move |signal| observer.signal(*signal)); + self.links.push(token); } } -impl SessionObserver for LegacyDrmDeviceObserver { +impl LegacyDrmDeviceObserver { + fn signal(&mut self, signal: SessionSignal) { + match signal { + SessionSignal::PauseSession => self.pause(None), + SessionSignal::PauseDevice { major, minor } => self.pause(Some((major, minor))), + SessionSignal::ActivateSession => self.activate(None), + SessionSignal::ActivateDevice { major, minor, new_fd } => { + self.activate(Some((major, minor, new_fd))) + } + } + } + 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) { diff --git a/src/backend/libinput/mod.rs b/src/backend/libinput/mod.rs index 8284e30..3ec0566 100644 --- a/src/backend/libinput/mod.rs +++ b/src/backend/libinput/mod.rs @@ -5,7 +5,10 @@ use helpers::{on_device_event, on_keyboard_event, on_pointer_event, on_touch_eve use crate::backend::input::{self as backend, Axis, InputBackend, InputEvent}; #[cfg(feature = "backend_session")] -use crate::backend::session::{AsErrno, Session, SessionObserver}; +use crate::{ + backend::session::{AsErrno, Session, Signal as SessionSignal}, + signaling::{Linkable, SignalToken, Signaler}, +}; use input as libinput; use input::event; @@ -33,6 +36,7 @@ pub struct LibinputInputBackend { context: libinput::Libinput, config: LibinputConfig, seats: HashMap, + links: Vec, logger: ::slog::Logger, } @@ -49,11 +53,35 @@ impl LibinputInputBackend { context, config: LibinputConfig { devices: Vec::new() }, seats: HashMap::new(), + links: Vec::new(), logger: log, } } } +#[cfg(feature = "backend_session")] +impl Linkable for LibinputInputBackend { + fn link(&mut self, signaler: Signaler) { + let mut input = self.context.clone(); + let log = self.logger.clone(); + let token = signaler.register(move |s| match s { + SessionSignal::PauseSession + | SessionSignal::PauseDevice { + major: INPUT_MAJOR, .. + } => { + input.suspend(); + } + SessionSignal::ActivateSession | SessionSignal::ActivateDevice { .. } => { + if input.resume().is_err() { + error!(log, "Failed to resume libinput context"); + } + } + _ => {} + }); + self.links.push(token); + } +} + impl backend::Event for event::keyboard::KeyboardKeyEvent { fn time(&self) -> u32 { event::keyboard::KeyboardEventTrait::time(self) @@ -390,25 +418,6 @@ impl From for backend::MouseButtonState { } } -#[cfg(feature = "backend_session")] -impl SessionObserver for libinput::Libinput { - fn pause(&mut self, device: Option<(u32, u32)>) { - if let Some((major, _)) = device { - if major != INPUT_MAJOR { - return; - } - } - // lets hope multiple suspends are okay in case of logind? - self.suspend() - } - - fn activate(&mut self, _device: Option<(u32, u32, Option)>) { - // libinput closes the devices on suspend, so we should not get any INPUT_MAJOR calls - // also lets hope multiple resumes are okay in case of logind - self.resume().expect("Unable to resume libinput context"); - } -} - /// Wrapper for types implementing the [`Session`] trait to provide /// a [`libinput::LibinputInterface`] implementation. #[cfg(feature = "backend_session")] diff --git a/src/backend/session/auto.rs b/src/backend/session/auto.rs index 9e332d3..0e4ca21 100644 --- a/src/backend/session/auto.rs +++ b/src/backend/session/auto.rs @@ -35,8 +35,9 @@ use super::logind::{self, LogindSession, LogindSessionNotifier}; use super::{ direct::{self, DirectSession, DirectSessionNotifier}, - AsErrno, Session, SessionNotifier, SessionObserver, + AsErrno, Session, Signal as SessionSignal, }; +use crate::signaling::Signaler; use nix::fcntl::OFlag; use std::{cell::RefCell, io, os::unix::io::RawFd, path::Path, rc::Rc}; @@ -61,16 +62,6 @@ pub enum AutoSessionNotifier { Direct(DirectSessionNotifier), } -/// Id's used by the [`AutoSessionNotifier`] internally. -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub struct AutoId(AutoIdInternal); -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -enum AutoIdInternal { - #[cfg(feature = "backend_session_logind")] - Logind(logind::Id), - Direct(direct::Id), -} - impl AutoSession { /// Tries to create a new session via the best available interface. #[cfg(feature = "backend_session_logind")] @@ -171,33 +162,15 @@ impl Session for AutoSession { } } -impl SessionNotifier for AutoSessionNotifier { - type Id = AutoId; - - fn register(&mut self, signal: S) -> Self::Id { +impl AutoSessionNotifier { + /// Get a handle to the Signaler of this session. + /// + /// You can use it to listen for signals generated by the session. + pub fn signaler(&self) -> Signaler { match *self { #[cfg(feature = "backend_session_logind")] - AutoSessionNotifier::Logind(ref mut logind) => { - AutoId(AutoIdInternal::Logind(SessionNotifier::register(logind, signal))) - } - AutoSessionNotifier::Direct(ref mut direct) => { - AutoId(AutoIdInternal::Direct(SessionNotifier::register(direct, signal))) - } - } - } - - fn unregister(&mut self, signal: Self::Id) { - #[allow(unreachable_patterns)] - match (self, signal) { - #[cfg(feature = "backend_session_logind")] - (&mut AutoSessionNotifier::Logind(ref mut logind), AutoId(AutoIdInternal::Logind(signal))) => { - SessionNotifier::unregister(logind, signal) - } - (&mut AutoSessionNotifier::Direct(ref mut direct), AutoId(AutoIdInternal::Direct(signal))) => { - SessionNotifier::unregister(direct, signal) - } - // this pattern is needed when the logind backend is activated - _ => unreachable!(), + AutoSessionNotifier::Logind(ref logind) => logind.signaler(), + AutoSessionNotifier::Direct(ref direct) => direct.signaler(), } } } diff --git a/src/backend/session/dbus/logind.rs b/src/backend/session/dbus/logind.rs index 2507413..7cf258c 100644 --- a/src/backend/session/dbus/logind.rs +++ b/src/backend/session/dbus/logind.rs @@ -34,7 +34,10 @@ //! The [`LogindSessionNotifier`](::backend::session::dbus::logind::LogindSessionNotifier) is to be inserted into //! a calloop event source to have its events processed. -use crate::backend::session::{AsErrno, Session, SessionNotifier, SessionObserver}; +use crate::{ + backend::session::{AsErrno, Session, Signal as SessionSignal}, + signaling::Signaler, +}; use dbus::{ arg::{messageitem::MessageItem, OwnedFd}, strings::{BusName, Interface, Member, Path as DbusPath}, @@ -62,7 +65,7 @@ struct LogindSessionImpl { conn: RefCell, session_path: DbusPath<'static>, active: AtomicBool, - signals: RefCell>>>, + signaler: Signaler, seat: String, logger: ::slog::Logger, } @@ -165,7 +168,6 @@ impl LogindSession { Some(vec![false.into()]), )?; - let signals = RefCell::new(Vec::new()); let conn = RefCell::new(conn); let internal = Rc::new(LogindSessionImpl { @@ -173,7 +175,7 @@ impl LogindSession { conn, session_path, active: AtomicBool::new(true), - signals, + signaler: Signaler::new(), seat: seat.clone(), logger: logger.new(o!("id" => session_id, "seat" => seat.clone(), "vt" => format!("{:?}", &vt))), }); @@ -196,6 +198,13 @@ impl LogindSessionNotifier { seat: self.internal.seat.clone(), } } + + /// Get a handle to the Signaler of this session. + /// + /// You can use it to listen for signals generated by the session. + pub fn signaler(&self) -> Signaler { + self.internal.signaler.clone() + } } impl LogindSessionImpl { @@ -256,11 +265,7 @@ impl LogindSessionImpl { //Ok... now what? //This session will never live again, but the user maybe has other sessions open //So lets just put it to sleep.. forever - for signal in &mut *self.signals.borrow_mut() { - if let Some(ref mut signal) = signal { - signal.pause(None); - } - } + self.signaler.signal(SessionSignal::PauseSession); self.active.store(false, Ordering::SeqCst); warn!(self.logger, "Session is now considered inactive"); } else if &*message.interface().unwrap() == "org.freedesktop.login1.Session" { @@ -287,11 +292,7 @@ impl LogindSessionImpl { // notifications about it. // This is handled via udev and is not part of our session api. if pause_type != "gone" { - for signal in &mut *self.signals.borrow_mut() { - if let Some(ref mut signal) = signal { - signal.pause(Some((major, minor))); - } - } + self.signaler.signal(SessionSignal::PauseDevice { major, minor }); } // the other possible types are "force" or "gone" (unplugged), // both expect no acknowledgement (note even this is not *really* necessary, @@ -313,11 +314,11 @@ impl LogindSessionImpl { let minor = minor.ok_or(Error::UnexpectedMethodReturn)?; let fd = fd.ok_or(Error::UnexpectedMethodReturn)?.into_fd(); debug!(self.logger, "Reactivating device ({},{})", major, minor); - for signal in &mut *self.signals.borrow_mut() { - if let Some(ref mut signal) = signal { - signal.activate(Some((major, minor, Some(fd)))); - } - } + self.signaler.signal(SessionSignal::ActivateDevice { + major, + minor, + new_fd: Some(fd), + }); } } else if &*message.interface().unwrap() == "org.freedesktop.DBus.Properties" && &*message.member().unwrap() == "PropertiesChanged" @@ -421,22 +422,6 @@ impl Session for LogindSession { } } -/// Ids of registered [`SessionObserver`]s of the [`LogindSessionNotifier`] -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub struct Id(usize); - -impl SessionNotifier for LogindSessionNotifier { - type Id = Id; - - fn register(&mut self, signal: S) -> Self::Id { - self.internal.signals.borrow_mut().push(Some(Box::new(signal))); - Id(self.internal.signals.borrow().len() - 1) - } - fn unregister(&mut self, signal: Id) { - self.internal.signals.borrow_mut()[signal.0] = None; - } -} - impl Drop for LogindSessionNotifier { fn drop(&mut self) { info!(self.internal.logger, "Closing logind session"); diff --git a/src/backend/session/direct.rs b/src/backend/session/direct.rs index 9d270c9..a15b4f2 100644 --- a/src/backend/session/direct.rs +++ b/src/backend/session/direct.rs @@ -45,7 +45,9 @@ //! The [`DirectSessionNotifier`](::backend::session::direct::DirectSessionNotifier) is to be inserted into //! a calloop event source to have its events processed. -use super::{AsErrno, Session, SessionNotifier, SessionObserver}; +use super::{AsErrno, Session, Signal as SessionSignal}; +use crate::signaling::Signaler; + use calloop::signals::{Signal, Signals}; use nix::{ fcntl::{self, open, OFlag}, @@ -152,7 +154,7 @@ pub struct DirectSession { pub struct DirectSessionNotifier { tty: RawFd, active: Arc, - signals: Vec>>, + signaler: Signaler, signal: Signal, logger: ::slog::Logger, source: Option, @@ -196,7 +198,7 @@ impl DirectSession { DirectSessionNotifier { tty: fd, active, - signals: Vec::new(), + signaler: Signaler::new(), signal, logger: logger.new(o!("vt" => format!("{}", vt), "component" => "session_notifier")), source: None, @@ -347,27 +349,11 @@ impl Drop for DirectSession { #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub struct Id(usize); -impl SessionNotifier for DirectSessionNotifier { - type Id = Id; - - fn register(&mut self, signal: S) -> Self::Id { - self.signals.push(Some(Box::new(signal))); - Id(self.signals.len() - 1) - } - fn unregister(&mut self, signal: Id) { - self.signals[signal.0] = None; - } -} - impl DirectSessionNotifier { fn signal_received(&mut self) { if self.active.load(Ordering::SeqCst) { info!(self.logger, "Session shall become inactive."); - for signal in &mut self.signals { - if let Some(ref mut signal) = *signal { - signal.pause(None); - } - } + self.signaler.signal(SessionSignal::PauseSession); self.active.store(false, Ordering::SeqCst); unsafe { tty::vt_rel_disp(self.tty, 1).expect("Unable to release tty lock"); @@ -378,15 +364,18 @@ impl DirectSessionNotifier { unsafe { tty::vt_rel_disp(self.tty, tty::VT_ACKACQ).expect("Unable to acquire tty lock"); } - for signal in &mut self.signals { - if let Some(ref mut signal) = *signal { - signal.activate(None); - } - } + self.signaler.signal(SessionSignal::ActivateSession); self.active.store(true, Ordering::SeqCst); info!(self.logger, "Session is now active again"); } } + + /// Get a handle to the Signaler of this session. + /// + /// You can use it to listen for signals generated by the session. + pub fn signaler(&self) -> Signaler { + self.signaler.clone() + } } impl calloop::EventSource for DirectSessionNotifier { diff --git a/src/backend/session/mod.rs b/src/backend/session/mod.rs index ce69c42..231081d 100644 --- a/src/backend/session/mod.rs +++ b/src/backend/session/mod.rs @@ -42,57 +42,39 @@ pub trait Session { fn seat(&self) -> String; } -/// Interface for registering for notifications for a given session. +/// Signals that can be generated by a session /// -/// Part of the session API which allows to get notified, when the given session -/// gets paused or becomes active again. Any object implementing the [`SessionObserver`] -/// trait may be registered. -pub trait SessionNotifier { - /// Id type of registered observers - type Id: PartialEq + Eq; - - /// Registers a given [`SessionObserver`]. +/// Objects that need to be notifier about activation and deactivation +/// of devices need to be linked to the `Signaler` of the session providing +/// these signals. +#[derive(Copy, Clone, Debug)] +pub enum Signal { + /// The whole session has been paused /// - /// Returns an id of the inserted observer, can be used to remove it again. - fn register(&mut self, signal: S) -> Self::Id; - /// Removes an observer by its given id from [`SessionNotifier::register`]. - fn unregister(&mut self, signal: Self::Id); -} - -/// Trait describing the ability to return a [`SessionObserver`] related to Self. -/// -/// The returned [`SessionObserver`] is responsible to handle the [`pause`](SessionObserver::pause) -/// and [`activate`](SessionObserver::activate) signals. -pub trait AsSessionObserver { - /// Create a [`SessionObserver`] linked to this object - fn observer(&mut self) -> S; -} - -impl AsSessionObserver for T { - fn observer(&mut self) -> T { - self.clone() - } -} - -/// Trait describing the ability to be notified when the session pauses or becomes active again. -/// -/// It might be impossible to interact with devices while the session is disabled. -/// This interface provides callbacks for when that happens. -pub trait SessionObserver { - /// Session/Device is about to be paused. - /// - /// If only a specific device shall be closed a device number in the form of - /// (major, minor) is provided. All observers not using the specified device should - /// ignore the signal in that case. - fn pause(&mut self, device: Option<(u32, u32)>); - /// Session/Device got active again - /// - /// If only a specific device shall be activated again a device number in the form of - /// `(major, major, Option)` is provided. Optionally the session may decide to replace - /// the currently open file descriptor of the device with a new one. In that case the old one - /// should not be used anymore and be closed. All observers not using the specified device should - /// ignore the signal in that case. - fn activate(&mut self, device: Option<(u32, u32, Option)>); + /// All devices should be considered as paused + PauseSession, + /// A given device has been paused + PauseDevice { + /// Major number identifying the device + major: u32, + /// Minor number identifying the device + minor: u32, + }, + /// The whole session has been activated + ActivateSession, + /// A given device has been activated + ActivateDevice { + /// Major number identifying the device + major: u32, + /// Minor number identifying the device + minor: u32, + /// New file descriptor for accessing the device + /// + /// If it is not `None`, you should use this FD in place of the + /// previous one for accessing this device. In case the old FD + /// is different from the new one, the old one should be closed. + new_fd: Option, + }, } impl Session for () { @@ -181,5 +163,3 @@ pub mod auto; mod dbus; pub mod direct; pub use self::dbus::*; -mod multi; -pub use self::multi::*; diff --git a/src/backend/session/multi.rs b/src/backend/session/multi.rs deleted file mode 100644 index 18c088a..0000000 --- a/src/backend/session/multi.rs +++ /dev/null @@ -1,69 +0,0 @@ -use std::{ - collections::HashMap, - os::unix::io::RawFd, - sync::{ - atomic::{AtomicUsize, Ordering}, - Arc, Mutex, - }, -}; - -use super::{SessionNotifier, SessionObserver}; - -static ID_COUNTER: AtomicUsize = AtomicUsize::new(0); - -/// Ids of registered [`SessionObserver`](SessionObserver)s -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] -pub struct Id(usize); - -struct MultiObserver { - observer: Arc>>>, -} - -impl SessionObserver for MultiObserver { - fn pause(&mut self, device: Option<(u32, u32)>) { - let mut lock = self.observer.lock().unwrap(); - for observer in lock.values_mut() { - observer.pause(device) - } - } - fn activate(&mut self, device: Option<(u32, u32, Option)>) { - let mut lock = self.observer.lock().unwrap(); - for observer in lock.values_mut() { - observer.activate(device) - } - } -} - -struct MultiNotifier { - observer: Arc>>>, -} - -impl SessionNotifier for MultiNotifier { - type Id = Id; - - fn register(&mut self, signal: S) -> Self::Id { - let id = Id(ID_COUNTER.fetch_add(1, Ordering::SeqCst)); - self.observer.lock().unwrap().insert(id, Box::new(signal)); - id - } - - fn unregister(&mut self, signal: Self::Id) { - self.observer.lock().unwrap().remove(&signal); - } -} - -/// Create a pair of a linked [`SessionObserver`] and a -/// [`SessionNotifier`](SessionNotifier). -/// -/// Observers added to the returned notifier are notified, -/// when the returned observer is notified. -pub fn notify_multiplexer() -> (impl SessionObserver, impl SessionNotifier) { - let observer = Arc::new(Mutex::new(HashMap::new())); - - ( - MultiObserver { - observer: observer.clone(), - }, - MultiNotifier { observer }, - ) -} diff --git a/src/signaling.rs b/src/signaling.rs index 4fe9109..6a12a2e 100644 --- a/src/signaling.rs +++ b/src/signaling.rs @@ -25,11 +25,19 @@ use std::{ }; /// A signaler, main type for signaling -#[derive(Clone)] pub struct Signaler { inner: Rc>, } +// Manual clone impl because of type parameters +impl Clone for Signaler { + fn clone(&self) -> Signaler { + Signaler { + inner: self.inner.clone(), + } + } +} + impl Signaler { /// Create a new signaler for given signal type pub fn new() -> Signaler { @@ -161,6 +169,15 @@ impl SignalInner { } } +/// Trait representing the capability of an object to listen for some signals +/// +/// It is provided so that the signaling system can play nicely into generic +/// constructs. +pub trait Linkable { + /// Make this object listen for signals from given signaler + fn link(&mut self, signaler: Signaler); +} + #[cfg(test)] mod tests { use super::*;