From a717fa36cde180e11c20435c9817076dc2fba91e Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Wed, 20 May 2020 13:27:04 +0200 Subject: [PATCH] backend.session: Migrate to using Signaler Change the session backend to rely on Signaler to propagate its signals. Also introduce the Linkable trait to allow generic composition of objects needing to listen for signals. --- anvil/src/udev.rs | 69 ++++++++++------------- src/backend/drm/atomic/mod.rs | 4 ++ src/backend/drm/atomic/session.rs | 31 ++++++++--- src/backend/drm/common/fallback.rs | 43 +++------------ src/backend/drm/egl/mod.rs | 4 ++ src/backend/drm/egl/session.rs | 45 +++++++++------ src/backend/drm/eglstream/mod.rs | 4 ++ src/backend/drm/eglstream/session.rs | 50 ++++++++++------- src/backend/drm/gbm/mod.rs | 4 ++ src/backend/drm/gbm/session.rs | 60 ++++++++++---------- src/backend/drm/legacy/mod.rs | 4 ++ src/backend/drm/legacy/session.rs | 31 ++++++++--- src/backend/libinput/mod.rs | 49 ++++++++++------- src/backend/session/auto.rs | 45 +++------------ src/backend/session/dbus/logind.rs | 55 +++++++------------ src/backend/session/direct.rs | 39 +++++-------- src/backend/session/mod.rs | 82 +++++++++++----------------- src/backend/session/multi.rs | 69 ----------------------- src/signaling.rs | 19 ++++++- 19 files changed, 312 insertions(+), 395 deletions(-) delete mode 100644 src/backend/session/multi.rs 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::*;