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.
This commit is contained in:
Victor Berger 2020-05-20 13:27:04 +02:00 committed by Victor Berger
parent c3859d999b
commit a717fa36cd
19 changed files with 312 additions and 395 deletions

View File

@ -28,10 +28,7 @@ use smithay::{
}, },
graphics::{CursorBackend, SwapBuffersError}, graphics::{CursorBackend, SwapBuffersError},
libinput::{LibinputInputBackend, LibinputSessionInterface}, libinput::{LibinputInputBackend, LibinputSessionInterface},
session::{ session::{auto::AutoSession, Session, Signal as SessionSignal},
auto::AutoSession, notify_multiplexer, AsSessionObserver, Session, SessionNotifier,
SessionObserver,
},
udev::{primary_gpu, UdevBackend, UdevEvent}, udev::{primary_gpu, UdevBackend, UdevEvent},
}, },
reexports::{ reexports::{
@ -56,6 +53,7 @@ use smithay::{
Display, Display,
}, },
}, },
signaling::{Linkable, SignalToken, Signaler},
wayland::{ wayland::{
compositor::CompositorToken, compositor::CompositorToken,
output::{Mode, Output, PhysicalProperties}, output::{Mode, Output, PhysicalProperties},
@ -117,9 +115,8 @@ pub fn run_udev(
/* /*
* Initialize session * Initialize session
*/ */
let (session, mut notifier) = AutoSession::new(log.clone()).ok_or(())?; let (session, notifier) = AutoSession::new(log.clone()).ok_or(())?;
let (udev_observer, udev_notifier) = notify_multiplexer(); let session_signal = notifier.signaler();
let udev_session_id = notifier.register(udev_observer);
/* /*
* Initialize the compositor * Initialize the compositor
@ -154,7 +151,7 @@ pub fn run_udev(
cursor_status: state.cursor_status.clone(), cursor_status: state.cursor_status.clone(),
dnd_icon: state.dnd_icon.clone(), dnd_icon: state.dnd_icon.clone(),
loop_handle: event_loop.handle(), loop_handle: event_loop.handle(),
notifier: udev_notifier, signaler: session_signal.clone(),
logger: log.clone(), logger: log.clone(),
}; };
@ -196,9 +193,9 @@ pub fn run_udev(
let mut libinput_context = Libinput::new_with_udev::<LibinputSessionInterface<AutoSession>>( let mut libinput_context = Libinput::new_with_udev::<LibinputSessionInterface<AutoSession>>(
state.session.clone().unwrap().into(), state.session.clone().unwrap().into(),
); );
let libinput_session_id = notifier.register(libinput_context.observer());
libinput_context.udev_assign_seat(&state.seat_name).unwrap(); 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 * Bind all our objects that get driven by the event loop
@ -246,29 +243,25 @@ pub fn run_udev(
// Cleanup stuff // Cleanup stuff
state.window_map.borrow_mut().clear(); state.window_map.borrow_mut().clear();
let mut notifier = event_loop.handle().remove(session_event_source); event_loop.handle().remove(session_event_source);
notifier.unregister(libinput_session_id);
notifier.unregister(udev_session_id);
event_loop.handle().remove(libinput_event_source); event_loop.handle().remove(libinput_event_source);
event_loop.handle().remove(udev_event_source); event_loop.handle().remove(udev_event_source);
Ok(()) Ok(())
} }
struct BackendData<S: SessionNotifier> { struct BackendData {
id: S::Id, _restart_token: SignalToken,
restart_id: S::Id,
event_source: Source<Generic<RenderDevice>>, event_source: Source<Generic<RenderDevice>>,
surfaces: Rc<RefCell<HashMap<crtc::Handle, Rc<GliumDrawer<RenderSurface>>>>>, surfaces: Rc<RefCell<HashMap<crtc::Handle, Rc<GliumDrawer<RenderSurface>>>>>,
} }
struct UdevHandlerImpl<S: SessionNotifier, Data: 'static> { struct UdevHandlerImpl<Data: 'static> {
compositor_token: CompositorToken<Roles>, compositor_token: CompositorToken<Roles>,
#[cfg(feature = "egl")] #[cfg(feature = "egl")]
egl_buffer_reader: Rc<RefCell<Option<EGLBufferReader>>>, egl_buffer_reader: Rc<RefCell<Option<EGLBufferReader>>>,
session: AutoSession, session: AutoSession,
backends: HashMap<dev_t, BackendData<S>>, backends: HashMap<dev_t, BackendData>,
display: Rc<RefCell<Display>>, display: Rc<RefCell<Display>>,
primary_gpu: Option<PathBuf>, primary_gpu: Option<PathBuf>,
window_map: Rc<RefCell<MyWindowMap>>, window_map: Rc<RefCell<MyWindowMap>>,
@ -277,11 +270,11 @@ struct UdevHandlerImpl<S: SessionNotifier, Data: 'static> {
cursor_status: Arc<Mutex<CursorImageStatus>>, cursor_status: Arc<Mutex<CursorImageStatus>>,
dnd_icon: Arc<Mutex<Option<wl_surface::WlSurface>>>, dnd_icon: Arc<Mutex<Option<wl_surface::WlSurface>>>,
loop_handle: LoopHandle<Data>, loop_handle: LoopHandle<Data>,
notifier: S, signaler: Signaler<SessionSignal>,
logger: ::slog::Logger, logger: ::slog::Logger,
} }
impl<S: SessionNotifier, Data: 'static> UdevHandlerImpl<S, Data> { impl<Data: 'static> UdevHandlerImpl<Data> {
#[cfg(feature = "egl")] #[cfg(feature = "egl")]
pub fn scan_connectors( pub fn scan_connectors(
device: &mut RenderDevice, device: &mut RenderDevice,
@ -375,7 +368,7 @@ impl<S: SessionNotifier, Data: 'static> UdevHandlerImpl<S, Data> {
} }
} }
impl<S: SessionNotifier, Data: 'static> UdevHandlerImpl<S, Data> { impl<Data: 'static> UdevHandlerImpl<Data> {
fn device_added(&mut self, _device: dev_t, path: PathBuf) { fn device_added(&mut self, _device: dev_t, path: PathBuf) {
// Try to open the device // Try to open the device
if let Some(mut device) = self if let Some(mut device) = self
@ -425,14 +418,14 @@ impl<S: SessionNotifier, Data: 'static> UdevHandlerImpl<S, Data> {
} }
#[cfg(feature = "egl")] #[cfg(feature = "egl")]
let backends = Rc::new(RefCell::new(UdevHandlerImpl::<S, Data>::scan_connectors( let backends = Rc::new(RefCell::new(UdevHandlerImpl::<Data>::scan_connectors(
&mut device, &mut device,
self.egl_buffer_reader.clone(), self.egl_buffer_reader.clone(),
&self.logger, &self.logger,
))); )));
#[cfg(not(feature = "egl"))] #[cfg(not(feature = "egl"))]
let backends = Rc::new(RefCell::new(UdevHandlerImpl::<S, Data>::scan_connectors( let backends = Rc::new(RefCell::new(UdevHandlerImpl::<Data>::scan_connectors(
&mut device, &mut device,
&self.logger, &self.logger,
))); )));
@ -449,16 +442,20 @@ impl<S: SessionNotifier, Data: 'static> UdevHandlerImpl<S, Data> {
dnd_icon: self.dnd_icon.clone(), dnd_icon: self.dnd_icon.clone(),
logger: self.logger.clone(), logger: self.logger.clone(),
}); });
let restart_id = self.notifier.register(DrmRendererSessionListener { let mut listener = DrmRendererSessionListener {
renderer: renderer.clone(), renderer: renderer.clone(),
loop_handle: self.loop_handle.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 { device.set_handler(DrmHandlerImpl {
renderer, renderer,
loop_handle: self.loop_handle.clone(), 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 dev_id = device.device_id();
let event_source = device_bind(&self.loop_handle, device) let event_source = device_bind(&self.loop_handle, device)
.map_err(|e| -> IoError { e.into() }) .map_err(|e| -> IoError { e.into() })
@ -478,8 +475,7 @@ impl<S: SessionNotifier, Data: 'static> UdevHandlerImpl<S, Data> {
self.backends.insert( self.backends.insert(
dev_id, dev_id,
BackendData { BackendData {
id: device_session_id, _restart_token: restart_token,
restart_id,
event_source, event_source,
surfaces: backends, surfaces: backends,
}, },
@ -498,13 +494,10 @@ impl<S: SessionNotifier, Data: 'static> UdevHandlerImpl<S, Data> {
.with_source(&backend_data.event_source, |source| { .with_source(&backend_data.event_source, |source| {
let mut backends = backend_data.surfaces.borrow_mut(); let mut backends = backend_data.surfaces.borrow_mut();
#[cfg(feature = "egl")] #[cfg(feature = "egl")]
let new_backends = UdevHandlerImpl::<S, Data>::scan_connectors( let new_backends =
&mut source.file, UdevHandlerImpl::<Data>::scan_connectors(&mut source.file, egl_buffer_reader, logger);
egl_buffer_reader,
logger,
);
#[cfg(not(feature = "egl"))] #[cfg(not(feature = "egl"))]
let new_backends = UdevHandlerImpl::<S, Data>::scan_connectors(&mut source.file, logger); let new_backends = UdevHandlerImpl::<Data>::scan_connectors(&mut source.file, logger);
*backends = new_backends; *backends = new_backends;
for renderer in backends.values() { for renderer in backends.values() {
@ -537,9 +530,6 @@ impl<S: SessionNotifier, Data: 'static> UdevHandlerImpl<S, Data> {
*self.egl_buffer_reader.borrow_mut() = None; *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"); debug!(self.logger, "Dropping device");
} }
} }
@ -567,9 +557,8 @@ pub struct DrmRendererSessionListener<Data: 'static> {
loop_handle: LoopHandle<Data>, loop_handle: LoopHandle<Data>,
} }
impl<Data: 'static> SessionObserver for DrmRendererSessionListener<Data> { impl<Data: 'static> DrmRendererSessionListener<Data> {
fn pause(&mut self, _device: Option<(u32, u32)>) {} fn activate(&mut self) {
fn activate(&mut self, _device: Option<(u32, u32, Option<RawFd>)>) {
// we want to be called, after all session handling is done (TODO this is not so nice) // we want to be called, after all session handling is done (TODO this is not so nice)
let renderer = self.renderer.clone(); let renderer = self.renderer.clone();
let handle = self.loop_handle.clone(); let handle = self.loop_handle.clone();

View File

@ -44,6 +44,8 @@ pub struct AtomicDrmDevice<A: AsRawFd + 'static> {
active: Arc<AtomicBool>, active: Arc<AtomicBool>,
backends: Rc<RefCell<HashMap<crtc::Handle, Weak<AtomicDrmSurfaceInternal<A>>>>>, backends: Rc<RefCell<HashMap<crtc::Handle, Weak<AtomicDrmSurfaceInternal<A>>>>>,
handler: Option<RefCell<Box<dyn DeviceHandler<Device = AtomicDrmDevice<A>>>>>, handler: Option<RefCell<Box<dyn DeviceHandler<Device = AtomicDrmDevice<A>>>>>,
#[cfg(feature = "backend_session")]
links: Vec<crate::signaling::SignalToken>,
logger: ::slog::Logger, logger: ::slog::Logger,
} }
@ -324,6 +326,8 @@ impl<A: AsRawFd + 'static> AtomicDrmDevice<A> {
active, active,
backends: Rc::new(RefCell::new(HashMap::new())), backends: Rc::new(RefCell::new(HashMap::new())),
handler: None, handler: None,
#[cfg(feature = "backend_session")]
links: Vec::new(),
logger: log.clone(), logger: log.clone(),
}) })
} }

View File

@ -17,7 +17,10 @@ use std::sync::Arc;
use super::{AtomicDrmDevice, AtomicDrmSurfaceInternal, Dev}; use super::{AtomicDrmDevice, AtomicDrmSurfaceInternal, Dev};
use crate::backend::drm::{common::Error, DevPath, Surface}; 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) /// [`SessionObserver`](SessionObserver)
/// linked to the [`AtomicDrmDevice`](AtomicDrmDevice) /// linked to the [`AtomicDrmDevice`](AtomicDrmDevice)
@ -31,20 +34,34 @@ pub struct AtomicDrmDeviceObserver<A: AsRawFd + 'static> {
logger: ::slog::Logger, logger: ::slog::Logger,
} }
impl<A: AsRawFd + 'static> AsSessionObserver<AtomicDrmDeviceObserver<A>> for AtomicDrmDevice<A> { impl<A: AsRawFd + 'static> Linkable<SessionSignal> for AtomicDrmDevice<A> {
fn observer(&mut self) -> AtomicDrmDeviceObserver<A> { fn link(&mut self, signaler: Signaler<SessionSignal>) {
AtomicDrmDeviceObserver { let mut observer = AtomicDrmDeviceObserver {
dev: Rc::downgrade(&self.dev), dev: Rc::downgrade(&self.dev),
dev_id: self.dev_id, dev_id: self.dev_id,
active: self.active.clone(), active: self.active.clone(),
privileged: self.dev.privileged, privileged: self.dev.privileged,
backends: Rc::downgrade(&self.backends), backends: Rc::downgrade(&self.backends),
logger: self.logger.clone(), logger: self.logger.clone(),
};
let token = signaler.register(move |signal| observer.signal(*signal));
self.links.push(token);
}
}
impl<A: AsRawFd + 'static> AtomicDrmDeviceObserver<A> {
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)))
} }
} }
} }
impl<A: AsRawFd + 'static> SessionObserver for AtomicDrmDeviceObserver<A> {
fn pause(&mut self, devnum: Option<(u32, u32)>) { fn pause(&mut self, devnum: Option<(u32, u32)>) {
if let Some((major, minor)) = devnum { if let Some((major, minor)) = devnum {
if major as u64 != stat::major(self.dev_id) || minor as u64 != stat::minor(self.dev_id) { if major as u64 != stat::major(self.dev_id) || minor as u64 != stat::minor(self.dev_id) {
@ -104,9 +121,7 @@ impl<A: AsRawFd + 'static> SessionObserver for AtomicDrmDeviceObserver<A> {
// TODO call drm-handler::error // TODO call drm-handler::error
} }
} }
}
impl<A: AsRawFd + 'static> AtomicDrmDeviceObserver<A> {
fn reset_state(&mut self) -> Result<(), Error> { fn reset_state(&mut self) -> Result<(), Error> {
if let Some(dev) = self.dev.upgrade() { if let Some(dev) = self.dev.upgrade() {
let res_handles = ControlDevice::resource_handles(&*dev) let res_handles = ControlDevice::resource_handles(&*dev)

View File

@ -24,7 +24,6 @@ use crate::backend::graphics::gl::GLGraphicsBackend;
#[cfg(feature = "renderer_gl")] #[cfg(feature = "renderer_gl")]
use crate::backend::graphics::PixelFormat; use crate::backend::graphics::PixelFormat;
use crate::backend::graphics::{CursorBackend, SwapBuffersError}; use crate::backend::graphics::{CursorBackend, SwapBuffersError};
use crate::backend::session::{AsSessionObserver, SessionObserver};
use drm::{ use drm::{
control::{connector, crtc, encoder, framebuffer, plane, Device as ControlDevice, Mode, ResourceHandles}, control::{connector, crtc, encoder, framebuffer, plane, Device as ControlDevice, Mode, ResourceHandles},
@ -111,44 +110,16 @@ where
} }
} }
/// [`SessionObserver`](::backend::session::SessionObserver) Wrapper to assist fallback #[cfg(feature = "backend_session")]
/// in case initialization of the preferred device type fails. impl<D1, D2> crate::signaling::Linkable<crate::backend::session::Signal> for FallbackDevice<D1, D2>
pub enum FallbackDeviceObserver<O1: SessionObserver + 'static, O2: SessionObserver + 'static> {
/// Variant for successful initialization of the preferred device
Preference(O1),
/// Variant for the fallback device
Fallback(O2),
}
impl<O1, O2, D1, D2> AsSessionObserver<FallbackDeviceObserver<O1, O2>> for FallbackDevice<D1, D2>
where where
O1: SessionObserver + 'static, D1: Device + crate::signaling::Linkable<crate::backend::session::Signal> + 'static,
O2: SessionObserver + 'static, D2: Device + crate::signaling::Linkable<crate::backend::session::Signal> + 'static,
D1: Device + AsSessionObserver<O1> + 'static,
D2: Device + AsSessionObserver<O2> + 'static,
{ {
fn observer(&mut self) -> FallbackDeviceObserver<O1, O2> { fn link(&mut self, signal: crate::signaling::Signaler<crate::backend::session::Signal>) {
match self { match self {
FallbackDevice::Preference(dev) => FallbackDeviceObserver::Preference(dev.observer()), FallbackDevice::Preference(d) => d.link(signal),
FallbackDevice::Fallback(dev) => FallbackDeviceObserver::Fallback(dev.observer()), FallbackDevice::Fallback(d) => d.link(signal),
}
}
}
impl<O1: SessionObserver + 'static, O2: SessionObserver + 'static> SessionObserver
for FallbackDeviceObserver<O1, O2>
{
fn pause(&mut self, device: Option<(u32, u32)>) {
match self {
FallbackDeviceObserver::Preference(dev) => dev.pause(device),
FallbackDeviceObserver::Fallback(dev) => dev.pause(device),
}
}
fn activate(&mut self, device: Option<(u32, u32, Option<RawFd>)>) {
match self {
FallbackDeviceObserver::Preference(dev) => dev.activate(device),
FallbackDeviceObserver::Fallback(dev) => dev.activate(device),
} }
} }
} }

View File

@ -62,6 +62,8 @@ where
default_attributes: GlAttributes, default_attributes: GlAttributes,
default_requirements: PixelFormatRequirements, default_requirements: PixelFormatRequirements,
backends: Rc<RefCell<HashMap<crtc::Handle, BackendRef<D>>>>, backends: Rc<RefCell<HashMap<crtc::Handle, BackendRef<D>>>>,
#[cfg(feature = "backend_session")]
links: Vec<crate::signaling::SignalToken>,
} }
impl<B, D> AsRawFd for EglDevice<B, D> impl<B, D> AsRawFd for EglDevice<B, D>
@ -129,6 +131,8 @@ where
default_requirements, default_requirements,
backends: Rc::new(RefCell::new(HashMap::new())), backends: Rc::new(RefCell::new(HashMap::new())),
logger: log, logger: log,
#[cfg(feature = "backend_session")]
links: Vec::new(),
}) })
} }
} }

View File

@ -6,7 +6,6 @@
use drm::control::{connector, crtc, Mode}; use drm::control::{connector, crtc, Mode};
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::os::unix::io::RawFd;
use std::rc::{Rc, Weak}; use std::rc::{Rc, Weak};
use super::{EglDevice, EglSurfaceInternal}; use super::{EglDevice, EglSurfaceInternal};
@ -15,42 +14,52 @@ use crate::backend::egl::{
ffi, ffi,
native::{Backend, NativeDisplay, NativeSurface}, native::{Backend, NativeDisplay, NativeSurface},
}; };
use crate::backend::session::{AsSessionObserver, SessionObserver}; use crate::{
backend::session::Signal as SessionSignal,
signaling::{Linkable, Signaler},
};
/// [`SessionObserver`](SessionObserver) /// [`SessionObserver`](SessionObserver)
/// linked to the [`EglDevice`](EglDevice) it was /// linked to the [`EglDevice`](EglDevice) it was
/// created from. /// created from.
pub struct EglDeviceObserver<S: SessionObserver + 'static, N: NativeSurface + Surface> { pub struct EglDeviceObserver<N: NativeSurface + Surface> {
observer: S,
backends: Weak<RefCell<HashMap<crtc::Handle, Weak<EglSurfaceInternal<N>>>>>, backends: Weak<RefCell<HashMap<crtc::Handle, Weak<EglSurfaceInternal<N>>>>>,
} }
impl<S, B, D> AsSessionObserver<EglDeviceObserver<S, <D as Device>::Surface>> for EglDevice<B, D> impl<B, D> Linkable<SessionSignal> for EglDevice<B, D>
where where
S: SessionObserver + 'static,
B: Backend<Surface = <D as Device>::Surface, Error = <<D as Device>::Surface as Surface>::Error> B: Backend<Surface = <D as Device>::Surface, Error = <<D as Device>::Surface as Surface>::Error>
+ 'static, + 'static,
D: Device D: Device
+ NativeDisplay<B, Arguments = (crtc::Handle, Mode, Vec<connector::Handle>)> + NativeDisplay<B, Arguments = (crtc::Handle, Mode, Vec<connector::Handle>)>
+ AsSessionObserver<S> + Linkable<SessionSignal>
+ 'static, + 'static,
<D as Device>::Surface: NativeSurface<Error = <<D as Device>::Surface as Surface>::Error>, <D as Device>::Surface: NativeSurface<Error = <<D as Device>::Surface as Surface>::Error>,
{ {
fn observer(&mut self) -> EglDeviceObserver<S, <D as Device>::Surface> { fn link(&mut self, signaler: Signaler<SessionSignal>) {
EglDeviceObserver { let lower_signal = Signaler::new();
observer: self.dev.borrow_mut().observer(), self.dev.borrow_mut().link(lower_signal.clone());
let mut observer = EglDeviceObserver {
backends: Rc::downgrade(&self.backends), 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<S: SessionObserver + 'static, N: NativeSurface + Surface> SessionObserver for EglDeviceObserver<S, N> { impl<N: NativeSurface + Surface> EglDeviceObserver<N> {
fn pause(&mut self, devnum: Option<(u32, u32)>) { fn activate(&mut self) {
self.observer.pause(devnum);
}
fn activate(&mut self, devnum: Option<(u32, u32, Option<RawFd>)>) {
self.observer.activate(devnum);
if let Some(backends) = self.backends.upgrade() { if let Some(backends) = self.backends.upgrade() {
for (_crtc, backend) in backends.borrow().iter() { for (_crtc, backend) in backends.borrow().iter() {
if let Some(backend) = backend.upgrade() { if let Some(backend) = backend.upgrade() {

View File

@ -82,6 +82,8 @@ pub struct EglStreamDevice<D: RawDevice + ControlDevice + 'static> {
raw: D, raw: D,
backends: Rc<RefCell<HashMap<crtc::Handle, Weak<EglStreamSurfaceInternal<D>>>>>, backends: Rc<RefCell<HashMap<crtc::Handle, Weak<EglStreamSurfaceInternal<D>>>>>,
logger: ::slog::Logger, logger: ::slog::Logger,
#[cfg(feature = "backend_session")]
links: Vec<crate::signaling::SignalToken>,
} }
impl<D: RawDevice + ControlDevice + 'static> EglStreamDevice<D> { impl<D: RawDevice + ControlDevice + 'static> EglStreamDevice<D> {
@ -202,6 +204,8 @@ impl<D: RawDevice + ControlDevice + 'static> EglStreamDevice<D> {
raw, raw,
backends: Rc::new(RefCell::new(HashMap::new())), backends: Rc::new(RefCell::new(HashMap::new())),
logger: log, logger: log,
#[cfg(feature = "backend_session")]
links: Vec::new(),
}) })
} }
} }

View File

@ -6,11 +6,11 @@
use super::{EglStreamDevice, EglStreamSurfaceInternal}; use super::{EglStreamDevice, EglStreamSurfaceInternal};
use crate::backend::drm::{RawDevice, Surface}; use crate::backend::drm::{RawDevice, Surface};
use crate::backend::egl::ffi; 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::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::os::unix::io::RawFd;
use std::rc::{Rc, Weak}; use std::rc::{Rc, Weak};
use drm::control::{crtc, Device as ControlDevice}; use drm::control::{crtc, Device as ControlDevice};
@ -18,31 +18,42 @@ use drm::control::{crtc, Device as ControlDevice};
/// [`SessionObserver`](SessionObserver) /// [`SessionObserver`](SessionObserver)
/// linked to the [`EglStreamDevice`](EglStreamDevice) it was /// linked to the [`EglStreamDevice`](EglStreamDevice) it was
/// created from. /// created from.
pub struct EglStreamDeviceObserver< pub struct EglStreamDeviceObserver<D: RawDevice + 'static> {
O: SessionObserver + 'static,
D: RawDevice + AsSessionObserver<O> + 'static,
> {
observer: O,
backends: Weak<RefCell<HashMap<crtc::Handle, Weak<EglStreamSurfaceInternal<D>>>>>, backends: Weak<RefCell<HashMap<crtc::Handle, Weak<EglStreamSurfaceInternal<D>>>>>,
logger: ::slog::Logger, logger: ::slog::Logger,
} }
impl<O: SessionObserver + 'static, D: RawDevice + ControlDevice + AsSessionObserver<O> + 'static> impl<D> Linkable<SessionSignal> for EglStreamDevice<D>
AsSessionObserver<EglStreamDeviceObserver<O, D>> for EglStreamDevice<D> where
D: RawDevice + ControlDevice + Linkable<SessionSignal> + 'static,
{ {
fn observer(&mut self) -> EglStreamDeviceObserver<O, D> { fn link(&mut self, signaler: Signaler<SessionSignal>) {
EglStreamDeviceObserver { let lower_signal = Signaler::new();
observer: self.raw.observer(), self.raw.link(lower_signal.clone());
let mut observer = EglStreamDeviceObserver {
backends: Rc::downgrade(&self.backends), backends: Rc::downgrade(&self.backends),
logger: self.logger.clone(), 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<O: SessionObserver + 'static, D: RawDevice + AsSessionObserver<O> + 'static> SessionObserver impl<D: RawDevice + 'static> EglStreamDeviceObserver<D> {
for EglStreamDeviceObserver<O, D> fn pause(&mut self) {
{
fn pause(&mut self, devnum: Option<(u32, u32)>) {
if let Some(backends) = self.backends.upgrade() { if let Some(backends) = self.backends.upgrade() {
for (_, backend) in backends.borrow().iter() { for (_, backend) in backends.borrow().iter() {
if let Some(backend) = backend.upgrade() { if let Some(backend) = backend.upgrade() {
@ -60,12 +71,9 @@ impl<O: SessionObserver + 'static, D: RawDevice + AsSessionObserver<O> + 'static
} }
} }
} }
self.observer.pause(devnum);
} }
fn activate(&mut self, devnum: Option<(u32, u32, Option<RawFd>)>) { fn activate(&mut self) {
self.observer.activate(devnum);
if let Some(backends) = self.backends.upgrade() { if let Some(backends) = self.backends.upgrade() {
for (_, backend) in backends.borrow().iter() { for (_, backend) in backends.borrow().iter() {
if let Some(backend) = backend.upgrade() { if let Some(backend) = backend.upgrade() {

View File

@ -76,6 +76,8 @@ static LOAD: Once = Once::new();
pub struct GbmDevice<D: RawDevice + ControlDevice + 'static> { pub struct GbmDevice<D: RawDevice + ControlDevice + 'static> {
pub(self) dev: Rc<RefCell<gbm::Device<D>>>, pub(self) dev: Rc<RefCell<gbm::Device<D>>>,
backends: Rc<RefCell<HashMap<crtc::Handle, Weak<GbmSurfaceInternal<D>>>>>, backends: Rc<RefCell<HashMap<crtc::Handle, Weak<GbmSurfaceInternal<D>>>>>,
#[cfg(feature = "backend_session")]
links: Vec<crate::signaling::SignalToken>,
logger: ::slog::Logger, logger: ::slog::Logger,
} }
@ -110,6 +112,8 @@ impl<D: RawDevice + ControlDevice + 'static> GbmDevice<D> {
// Open the gbm device from the drm device // Open the gbm device from the drm device
dev: Rc::new(RefCell::new(gbm::Device::new(dev).map_err(Error::InitFailed)?)), dev: Rc::new(RefCell::new(gbm::Device::new(dev).map_err(Error::InitFailed)?)),
backends: Rc::new(RefCell::new(HashMap::new())), backends: Rc::new(RefCell::new(HashMap::new())),
#[cfg(feature = "backend_session")]
links: Vec::new(),
logger: log, logger: log,
}) })
} }

View File

@ -7,53 +7,57 @@ use drm::control::crtc;
use gbm::BufferObject; use gbm::BufferObject;
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::os::unix::io::RawFd;
use std::rc::{Rc, Weak}; use std::rc::{Rc, Weak};
use super::{GbmDevice, GbmSurfaceInternal}; use super::{GbmDevice, GbmSurfaceInternal};
use crate::backend::drm::{RawDevice, RawSurface}; use crate::backend::drm::{Device, RawDevice};
use crate::backend::graphics::CursorBackend; use crate::backend::graphics::CursorBackend;
use crate::backend::session::{AsSessionObserver, SessionObserver}; use crate::{
backend::session::Signal as SessionSignal,
signaling::{Linkable, Signaler},
};
/// [`SessionObserver`](SessionObserver) /// [`SessionObserver`](SessionObserver)
/// linked to the [`GbmDevice`](GbmDevice) it was /// linked to the [`GbmDevice`](GbmDevice) it was
/// created from. /// created from.
pub struct GbmDeviceObserver< pub(crate) struct GbmDeviceObserver<D: RawDevice + ::drm::control::Device + 'static> {
S: SessionObserver + 'static,
D: RawDevice + ::drm::control::Device + AsSessionObserver<S> + 'static,
> {
observer: S,
backends: Weak<RefCell<HashMap<crtc::Handle, Weak<GbmSurfaceInternal<D>>>>>, backends: Weak<RefCell<HashMap<crtc::Handle, Weak<GbmSurfaceInternal<D>>>>>,
logger: ::slog::Logger, logger: ::slog::Logger,
} }
impl< impl<D> Linkable<SessionSignal> for GbmDevice<D>
O: SessionObserver + 'static, where
S: CursorBackend<CursorFormat = dyn drm::buffer::Buffer> + RawSurface + 'static, D: RawDevice + drm::control::Device + Linkable<SessionSignal> + 'static,
D: RawDevice<Surface = S> + drm::control::Device + AsSessionObserver<O> + 'static, <D as Device>::Surface: CursorBackend<CursorFormat = dyn drm::buffer::Buffer>,
> AsSessionObserver<GbmDeviceObserver<O, D>> for GbmDevice<D>
{ {
fn observer(&mut self) -> GbmDeviceObserver<O, D> { fn link(&mut self, signaler: Signaler<SessionSignal>) {
GbmDeviceObserver { let lower_signal = Signaler::new();
observer: (**self.dev.borrow_mut()).observer(), self.dev.borrow_mut().link(lower_signal.clone());
let mut observer = GbmDeviceObserver {
backends: Rc::downgrade(&self.backends), backends: Rc::downgrade(&self.backends),
logger: self.logger.clone(), 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< impl<D: RawDevice + drm::control::Device + 'static> GbmDeviceObserver<D>
O: SessionObserver + 'static, where
S: CursorBackend<CursorFormat = dyn drm::buffer::Buffer> + RawSurface + 'static, <D as Device>::Surface: CursorBackend<CursorFormat = dyn drm::buffer::Buffer>,
D: RawDevice<Surface = S> + drm::control::Device + AsSessionObserver<O> + 'static,
> SessionObserver for GbmDeviceObserver<O, D>
{ {
fn pause(&mut self, devnum: Option<(u32, u32)>) { fn activate(&mut self) {
self.observer.pause(devnum);
}
fn activate(&mut self, devnum: Option<(u32, u32, Option<RawFd>)>) {
self.observer.activate(devnum);
let mut crtcs = Vec::new(); let mut crtcs = Vec::new();
if let Some(backends) = self.backends.upgrade() { if let Some(backends) = self.backends.upgrade() {
for (crtc, backend) in backends.borrow().iter() { for (crtc, backend) in backends.borrow().iter() {

View File

@ -40,6 +40,8 @@ pub struct LegacyDrmDevice<A: AsRawFd + 'static> {
active: Arc<AtomicBool>, active: Arc<AtomicBool>,
backends: Rc<RefCell<HashMap<crtc::Handle, Weak<LegacyDrmSurfaceInternal<A>>>>>, backends: Rc<RefCell<HashMap<crtc::Handle, Weak<LegacyDrmSurfaceInternal<A>>>>>,
handler: Option<RefCell<Box<dyn DeviceHandler<Device = LegacyDrmDevice<A>>>>>, handler: Option<RefCell<Box<dyn DeviceHandler<Device = LegacyDrmDevice<A>>>>>,
#[cfg(feature = "backend_session")]
links: Vec<crate::signaling::SignalToken>,
logger: ::slog::Logger, logger: ::slog::Logger,
} }
@ -184,6 +186,8 @@ impl<A: AsRawFd + 'static> LegacyDrmDevice<A> {
active, active,
backends: Rc::new(RefCell::new(HashMap::new())), backends: Rc::new(RefCell::new(HashMap::new())),
handler: None, handler: None,
#[cfg(feature = "backend_session")]
links: Vec::new(),
logger: log.clone(), logger: log.clone(),
}) })
} }

View File

@ -16,12 +16,15 @@ use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc; use std::sync::Arc;
use super::{Dev, DevPath, Error, LegacyDrmDevice, LegacyDrmSurfaceInternal}; 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) /// [`SessionObserver`](SessionObserver)
/// linked to the [`LegacyDrmDevice`](LegacyDrmDevice) /// linked to the [`LegacyDrmDevice`](LegacyDrmDevice)
/// it was created from. /// it was created from.
pub struct LegacyDrmDeviceObserver<A: AsRawFd + 'static> { pub(crate) struct LegacyDrmDeviceObserver<A: AsRawFd + 'static> {
dev: Weak<Dev<A>>, dev: Weak<Dev<A>>,
dev_id: dev_t, dev_id: dev_t,
privileged: bool, privileged: bool,
@ -30,20 +33,34 @@ pub struct LegacyDrmDeviceObserver<A: AsRawFd + 'static> {
logger: ::slog::Logger, logger: ::slog::Logger,
} }
impl<A: AsRawFd + 'static> AsSessionObserver<LegacyDrmDeviceObserver<A>> for LegacyDrmDevice<A> { impl<A: AsRawFd + 'static> Linkable<SessionSignal> for LegacyDrmDevice<A> {
fn observer(&mut self) -> LegacyDrmDeviceObserver<A> { fn link(&mut self, signaler: Signaler<SessionSignal>) {
LegacyDrmDeviceObserver { let mut observer = LegacyDrmDeviceObserver {
dev: Rc::downgrade(&self.dev), dev: Rc::downgrade(&self.dev),
dev_id: self.dev_id, dev_id: self.dev_id,
active: self.active.clone(), active: self.active.clone(),
privileged: self.dev.privileged, privileged: self.dev.privileged,
backends: Rc::downgrade(&self.backends), backends: Rc::downgrade(&self.backends),
logger: self.logger.clone(), logger: self.logger.clone(),
};
let token = signaler.register(move |signal| observer.signal(*signal));
self.links.push(token);
}
}
impl<A: AsRawFd + 'static> LegacyDrmDeviceObserver<A> {
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)))
} }
} }
} }
impl<A: AsRawFd + 'static> SessionObserver for LegacyDrmDeviceObserver<A> {
fn pause(&mut self, devnum: Option<(u32, u32)>) { fn pause(&mut self, devnum: Option<(u32, u32)>) {
if let Some((major, minor)) = devnum { if let Some((major, minor)) = devnum {
if major as u64 != stat::major(self.dev_id) || minor as u64 != stat::minor(self.dev_id) { if major as u64 != stat::major(self.dev_id) || minor as u64 != stat::minor(self.dev_id) {

View File

@ -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}; use crate::backend::input::{self as backend, Axis, InputBackend, InputEvent};
#[cfg(feature = "backend_session")] #[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 as libinput;
use input::event; use input::event;
@ -33,6 +36,7 @@ pub struct LibinputInputBackend {
context: libinput::Libinput, context: libinput::Libinput,
config: LibinputConfig, config: LibinputConfig,
seats: HashMap<libinput::Seat, backend::Seat>, seats: HashMap<libinput::Seat, backend::Seat>,
links: Vec<SignalToken>,
logger: ::slog::Logger, logger: ::slog::Logger,
} }
@ -49,11 +53,35 @@ impl LibinputInputBackend {
context, context,
config: LibinputConfig { devices: Vec::new() }, config: LibinputConfig { devices: Vec::new() },
seats: HashMap::new(), seats: HashMap::new(),
links: Vec::new(),
logger: log, logger: log,
} }
} }
} }
#[cfg(feature = "backend_session")]
impl Linkable<SessionSignal> for LibinputInputBackend {
fn link(&mut self, signaler: Signaler<SessionSignal>) {
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 { impl backend::Event for event::keyboard::KeyboardKeyEvent {
fn time(&self) -> u32 { fn time(&self) -> u32 {
event::keyboard::KeyboardEventTrait::time(self) event::keyboard::KeyboardEventTrait::time(self)
@ -390,25 +418,6 @@ impl From<event::pointer::ButtonState> 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<RawFd>)>) {
// 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 /// Wrapper for types implementing the [`Session`] trait to provide
/// a [`libinput::LibinputInterface`] implementation. /// a [`libinput::LibinputInterface`] implementation.
#[cfg(feature = "backend_session")] #[cfg(feature = "backend_session")]

View File

@ -35,8 +35,9 @@
use super::logind::{self, LogindSession, LogindSessionNotifier}; use super::logind::{self, LogindSession, LogindSessionNotifier};
use super::{ use super::{
direct::{self, DirectSession, DirectSessionNotifier}, direct::{self, DirectSession, DirectSessionNotifier},
AsErrno, Session, SessionNotifier, SessionObserver, AsErrno, Session, Signal as SessionSignal,
}; };
use crate::signaling::Signaler;
use nix::fcntl::OFlag; use nix::fcntl::OFlag;
use std::{cell::RefCell, io, os::unix::io::RawFd, path::Path, rc::Rc}; use std::{cell::RefCell, io, os::unix::io::RawFd, path::Path, rc::Rc};
@ -61,16 +62,6 @@ pub enum AutoSessionNotifier {
Direct(DirectSessionNotifier), 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 { impl AutoSession {
/// Tries to create a new session via the best available interface. /// Tries to create a new session via the best available interface.
#[cfg(feature = "backend_session_logind")] #[cfg(feature = "backend_session_logind")]
@ -171,33 +162,15 @@ impl Session for AutoSession {
} }
} }
impl SessionNotifier for AutoSessionNotifier { impl AutoSessionNotifier {
type Id = AutoId; /// Get a handle to the Signaler of this session.
///
fn register<S: SessionObserver + 'static>(&mut self, signal: S) -> Self::Id { /// You can use it to listen for signals generated by the session.
pub fn signaler(&self) -> Signaler<SessionSignal> {
match *self { match *self {
#[cfg(feature = "backend_session_logind")] #[cfg(feature = "backend_session_logind")]
AutoSessionNotifier::Logind(ref mut logind) => { AutoSessionNotifier::Logind(ref logind) => logind.signaler(),
AutoId(AutoIdInternal::Logind(SessionNotifier::register(logind, signal))) AutoSessionNotifier::Direct(ref direct) => direct.signaler(),
}
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!(),
} }
} }
} }

View File

@ -34,7 +34,10 @@
//! The [`LogindSessionNotifier`](::backend::session::dbus::logind::LogindSessionNotifier) is to be inserted into //! The [`LogindSessionNotifier`](::backend::session::dbus::logind::LogindSessionNotifier) is to be inserted into
//! a calloop event source to have its events processed. //! 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::{ use dbus::{
arg::{messageitem::MessageItem, OwnedFd}, arg::{messageitem::MessageItem, OwnedFd},
strings::{BusName, Interface, Member, Path as DbusPath}, strings::{BusName, Interface, Member, Path as DbusPath},
@ -62,7 +65,7 @@ struct LogindSessionImpl {
conn: RefCell<DBusConnection>, conn: RefCell<DBusConnection>,
session_path: DbusPath<'static>, session_path: DbusPath<'static>,
active: AtomicBool, active: AtomicBool,
signals: RefCell<Vec<Option<Box<dyn SessionObserver>>>>, signaler: Signaler<SessionSignal>,
seat: String, seat: String,
logger: ::slog::Logger, logger: ::slog::Logger,
} }
@ -165,7 +168,6 @@ impl LogindSession {
Some(vec![false.into()]), Some(vec![false.into()]),
)?; )?;
let signals = RefCell::new(Vec::new());
let conn = RefCell::new(conn); let conn = RefCell::new(conn);
let internal = Rc::new(LogindSessionImpl { let internal = Rc::new(LogindSessionImpl {
@ -173,7 +175,7 @@ impl LogindSession {
conn, conn,
session_path, session_path,
active: AtomicBool::new(true), active: AtomicBool::new(true),
signals, signaler: Signaler::new(),
seat: seat.clone(), seat: seat.clone(),
logger: logger.new(o!("id" => session_id, "seat" => seat.clone(), "vt" => format!("{:?}", &vt))), logger: logger.new(o!("id" => session_id, "seat" => seat.clone(), "vt" => format!("{:?}", &vt))),
}); });
@ -196,6 +198,13 @@ impl LogindSessionNotifier {
seat: self.internal.seat.clone(), 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<SessionSignal> {
self.internal.signaler.clone()
}
} }
impl LogindSessionImpl { impl LogindSessionImpl {
@ -256,11 +265,7 @@ impl LogindSessionImpl {
//Ok... now what? //Ok... now what?
//This session will never live again, but the user maybe has other sessions open //This session will never live again, but the user maybe has other sessions open
//So lets just put it to sleep.. forever //So lets just put it to sleep.. forever
for signal in &mut *self.signals.borrow_mut() { self.signaler.signal(SessionSignal::PauseSession);
if let Some(ref mut signal) = signal {
signal.pause(None);
}
}
self.active.store(false, Ordering::SeqCst); self.active.store(false, Ordering::SeqCst);
warn!(self.logger, "Session is now considered inactive"); warn!(self.logger, "Session is now considered inactive");
} else if &*message.interface().unwrap() == "org.freedesktop.login1.Session" { } else if &*message.interface().unwrap() == "org.freedesktop.login1.Session" {
@ -287,11 +292,7 @@ impl LogindSessionImpl {
// notifications about it. // notifications about it.
// This is handled via udev and is not part of our session api. // This is handled via udev and is not part of our session api.
if pause_type != "gone" { if pause_type != "gone" {
for signal in &mut *self.signals.borrow_mut() { self.signaler.signal(SessionSignal::PauseDevice { major, minor });
if let Some(ref mut signal) = signal {
signal.pause(Some((major, minor)));
}
}
} }
// the other possible types are "force" or "gone" (unplugged), // the other possible types are "force" or "gone" (unplugged),
// both expect no acknowledgement (note even this is not *really* necessary, // 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 minor = minor.ok_or(Error::UnexpectedMethodReturn)?;
let fd = fd.ok_or(Error::UnexpectedMethodReturn)?.into_fd(); let fd = fd.ok_or(Error::UnexpectedMethodReturn)?.into_fd();
debug!(self.logger, "Reactivating device ({},{})", major, minor); debug!(self.logger, "Reactivating device ({},{})", major, minor);
for signal in &mut *self.signals.borrow_mut() { self.signaler.signal(SessionSignal::ActivateDevice {
if let Some(ref mut signal) = signal { major,
signal.activate(Some((major, minor, Some(fd)))); minor,
} new_fd: Some(fd),
} });
} }
} else if &*message.interface().unwrap() == "org.freedesktop.DBus.Properties" } else if &*message.interface().unwrap() == "org.freedesktop.DBus.Properties"
&& &*message.member().unwrap() == "PropertiesChanged" && &*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<S: SessionObserver + 'static>(&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 { impl Drop for LogindSessionNotifier {
fn drop(&mut self) { fn drop(&mut self) {
info!(self.internal.logger, "Closing logind session"); info!(self.internal.logger, "Closing logind session");

View File

@ -45,7 +45,9 @@
//! The [`DirectSessionNotifier`](::backend::session::direct::DirectSessionNotifier) is to be inserted into //! The [`DirectSessionNotifier`](::backend::session::direct::DirectSessionNotifier) is to be inserted into
//! a calloop event source to have its events processed. //! 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 calloop::signals::{Signal, Signals};
use nix::{ use nix::{
fcntl::{self, open, OFlag}, fcntl::{self, open, OFlag},
@ -152,7 +154,7 @@ pub struct DirectSession {
pub struct DirectSessionNotifier { pub struct DirectSessionNotifier {
tty: RawFd, tty: RawFd,
active: Arc<AtomicBool>, active: Arc<AtomicBool>,
signals: Vec<Option<Box<dyn SessionObserver>>>, signaler: Signaler<SessionSignal>,
signal: Signal, signal: Signal,
logger: ::slog::Logger, logger: ::slog::Logger,
source: Option<Signals>, source: Option<Signals>,
@ -196,7 +198,7 @@ impl DirectSession {
DirectSessionNotifier { DirectSessionNotifier {
tty: fd, tty: fd,
active, active,
signals: Vec::new(), signaler: Signaler::new(),
signal, signal,
logger: logger.new(o!("vt" => format!("{}", vt), "component" => "session_notifier")), logger: logger.new(o!("vt" => format!("{}", vt), "component" => "session_notifier")),
source: None, source: None,
@ -347,27 +349,11 @@ impl Drop for DirectSession {
#[derive(Debug, PartialEq, Eq, Clone, Copy)] #[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct Id(usize); pub struct Id(usize);
impl SessionNotifier for DirectSessionNotifier {
type Id = Id;
fn register<S: SessionObserver + 'static>(&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 { impl DirectSessionNotifier {
fn signal_received(&mut self) { fn signal_received(&mut self) {
if self.active.load(Ordering::SeqCst) { if self.active.load(Ordering::SeqCst) {
info!(self.logger, "Session shall become inactive."); info!(self.logger, "Session shall become inactive.");
for signal in &mut self.signals { self.signaler.signal(SessionSignal::PauseSession);
if let Some(ref mut signal) = *signal {
signal.pause(None);
}
}
self.active.store(false, Ordering::SeqCst); self.active.store(false, Ordering::SeqCst);
unsafe { unsafe {
tty::vt_rel_disp(self.tty, 1).expect("Unable to release tty lock"); tty::vt_rel_disp(self.tty, 1).expect("Unable to release tty lock");
@ -378,15 +364,18 @@ impl DirectSessionNotifier {
unsafe { unsafe {
tty::vt_rel_disp(self.tty, tty::VT_ACKACQ).expect("Unable to acquire tty lock"); tty::vt_rel_disp(self.tty, tty::VT_ACKACQ).expect("Unable to acquire tty lock");
} }
for signal in &mut self.signals { self.signaler.signal(SessionSignal::ActivateSession);
if let Some(ref mut signal) = *signal {
signal.activate(None);
}
}
self.active.store(true, Ordering::SeqCst); self.active.store(true, Ordering::SeqCst);
info!(self.logger, "Session is now active again"); 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<SessionSignal> {
self.signaler.clone()
}
} }
impl calloop::EventSource for DirectSessionNotifier { impl calloop::EventSource for DirectSessionNotifier {

View File

@ -42,57 +42,39 @@ pub trait Session {
fn seat(&self) -> String; 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 /// Objects that need to be notifier about activation and deactivation
/// gets paused or becomes active again. Any object implementing the [`SessionObserver`] /// of devices need to be linked to the `Signaler` of the session providing
/// trait may be registered. /// these signals.
pub trait SessionNotifier { #[derive(Copy, Clone, Debug)]
/// Id type of registered observers pub enum Signal {
type Id: PartialEq + Eq; /// The whole session has been paused
/// Registers a given [`SessionObserver`].
/// ///
/// Returns an id of the inserted observer, can be used to remove it again. /// All devices should be considered as paused
fn register<S: SessionObserver + 'static>(&mut self, signal: S) -> Self::Id; PauseSession,
/// Removes an observer by its given id from [`SessionNotifier::register`]. /// A given device has been paused
fn unregister(&mut self, signal: Self::Id); PauseDevice {
} /// Major number identifying the device
major: u32,
/// Trait describing the ability to return a [`SessionObserver`] related to Self. /// 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
/// ///
/// The returned [`SessionObserver`] is responsible to handle the [`pause`](SessionObserver::pause) /// If it is not `None`, you should use this FD in place of the
/// and [`activate`](SessionObserver::activate) signals. /// previous one for accessing this device. In case the old FD
pub trait AsSessionObserver<S: SessionObserver + 'static> { /// is different from the new one, the old one should be closed.
/// Create a [`SessionObserver`] linked to this object new_fd: Option<RawFd>,
fn observer(&mut self) -> S; },
}
impl<T: SessionObserver + Clone + 'static> AsSessionObserver<T> 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<RawFd>)` 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<RawFd>)>);
} }
impl Session for () { impl Session for () {
@ -181,5 +163,3 @@ pub mod auto;
mod dbus; mod dbus;
pub mod direct; pub mod direct;
pub use self::dbus::*; pub use self::dbus::*;
mod multi;
pub use self::multi::*;

View File

@ -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<Mutex<HashMap<Id, Box<dyn SessionObserver>>>>,
}
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<RawFd>)>) {
let mut lock = self.observer.lock().unwrap();
for observer in lock.values_mut() {
observer.activate(device)
}
}
}
struct MultiNotifier {
observer: Arc<Mutex<HashMap<Id, Box<dyn SessionObserver>>>>,
}
impl SessionNotifier for MultiNotifier {
type Id = Id;
fn register<S: SessionObserver + 'static>(&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<Id = Id>) {
let observer = Arc::new(Mutex::new(HashMap::new()));
(
MultiObserver {
observer: observer.clone(),
},
MultiNotifier { observer },
)
}

View File

@ -25,11 +25,19 @@ use std::{
}; };
/// A signaler, main type for signaling /// A signaler, main type for signaling
#[derive(Clone)]
pub struct Signaler<S> { pub struct Signaler<S> {
inner: Rc<SignalInner<S>>, inner: Rc<SignalInner<S>>,
} }
// Manual clone impl because of type parameters
impl<S> Clone for Signaler<S> {
fn clone(&self) -> Signaler<S> {
Signaler {
inner: self.inner.clone(),
}
}
}
impl<S> Signaler<S> { impl<S> Signaler<S> {
/// Create a new signaler for given signal type /// Create a new signaler for given signal type
pub fn new() -> Signaler<S> { pub fn new() -> Signaler<S> {
@ -161,6 +169,15 @@ impl<S> SignalInner<S> {
} }
} }
/// 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<S> {
/// Make this object listen for signals from given signaler
fn link(&mut self, signaler: Signaler<S>);
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;