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},
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::<LibinputSessionInterface<AutoSession>>(
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<S: SessionNotifier> {
id: S::Id,
restart_id: S::Id,
struct BackendData {
_restart_token: SignalToken,
event_source: Source<Generic<RenderDevice>>,
surfaces: Rc<RefCell<HashMap<crtc::Handle, Rc<GliumDrawer<RenderSurface>>>>>,
}
struct UdevHandlerImpl<S: SessionNotifier, Data: 'static> {
struct UdevHandlerImpl<Data: 'static> {
compositor_token: CompositorToken<Roles>,
#[cfg(feature = "egl")]
egl_buffer_reader: Rc<RefCell<Option<EGLBufferReader>>>,
session: AutoSession,
backends: HashMap<dev_t, BackendData<S>>,
backends: HashMap<dev_t, BackendData>,
display: Rc<RefCell<Display>>,
primary_gpu: Option<PathBuf>,
window_map: Rc<RefCell<MyWindowMap>>,
@ -277,11 +270,11 @@ struct UdevHandlerImpl<S: SessionNotifier, Data: 'static> {
cursor_status: Arc<Mutex<CursorImageStatus>>,
dnd_icon: Arc<Mutex<Option<wl_surface::WlSurface>>>,
loop_handle: LoopHandle<Data>,
notifier: S,
signaler: Signaler<SessionSignal>,
logger: ::slog::Logger,
}
impl<S: SessionNotifier, Data: 'static> UdevHandlerImpl<S, Data> {
impl<Data: 'static> UdevHandlerImpl<Data> {
#[cfg(feature = "egl")]
pub fn scan_connectors(
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) {
// Try to open the device
if let Some(mut device) = self
@ -425,14 +418,14 @@ impl<S: SessionNotifier, Data: 'static> UdevHandlerImpl<S, Data> {
}
#[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,
self.egl_buffer_reader.clone(),
&self.logger,
)));
#[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,
&self.logger,
)));
@ -449,16 +442,20 @@ impl<S: SessionNotifier, Data: 'static> UdevHandlerImpl<S, Data> {
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<S: SessionNotifier, Data: 'static> UdevHandlerImpl<S, Data> {
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<S: SessionNotifier, Data: 'static> UdevHandlerImpl<S, Data> {
.with_source(&backend_data.event_source, |source| {
let mut backends = backend_data.surfaces.borrow_mut();
#[cfg(feature = "egl")]
let new_backends = UdevHandlerImpl::<S, Data>::scan_connectors(
&mut source.file,
egl_buffer_reader,
logger,
);
let new_backends =
UdevHandlerImpl::<Data>::scan_connectors(&mut source.file, egl_buffer_reader, logger);
#[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;
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.notifier.unregister(backend_data.id);
self.notifier.unregister(backend_data.restart_id);
debug!(self.logger, "Dropping device");
}
}
@ -567,9 +557,8 @@ pub struct DrmRendererSessionListener<Data: 'static> {
loop_handle: LoopHandle<Data>,
}
impl<Data: 'static> SessionObserver for DrmRendererSessionListener<Data> {
fn pause(&mut self, _device: Option<(u32, u32)>) {}
fn activate(&mut self, _device: Option<(u32, u32, Option<RawFd>)>) {
impl<Data: 'static> DrmRendererSessionListener<Data> {
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();

View File

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

View File

@ -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<A: AsRawFd + 'static> {
logger: ::slog::Logger,
}
impl<A: AsRawFd + 'static> AsSessionObserver<AtomicDrmDeviceObserver<A>> for AtomicDrmDevice<A> {
fn observer(&mut self) -> AtomicDrmDeviceObserver<A> {
AtomicDrmDeviceObserver {
impl<A: AsRawFd + 'static> Linkable<SessionSignal> for AtomicDrmDevice<A> {
fn link(&mut self, signaler: Signaler<SessionSignal>) {
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<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)>) {
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<A: AsRawFd + 'static> SessionObserver for AtomicDrmDeviceObserver<A> {
// TODO call drm-handler::error
}
}
}
impl<A: AsRawFd + 'static> AtomicDrmDeviceObserver<A> {
fn reset_state(&mut self) -> Result<(), Error> {
if let Some(dev) = self.dev.upgrade() {
let res_handles = ControlDevice::resource_handles(&*dev)

View File

@ -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<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>
#[cfg(feature = "backend_session")]
impl<D1, D2> crate::signaling::Linkable<crate::backend::session::Signal> for FallbackDevice<D1, D2>
where
O1: SessionObserver + 'static,
O2: SessionObserver + 'static,
D1: Device + AsSessionObserver<O1> + 'static,
D2: Device + AsSessionObserver<O2> + 'static,
D1: Device + crate::signaling::Linkable<crate::backend::session::Signal> + 'static,
D2: Device + crate::signaling::Linkable<crate::backend::session::Signal> + 'static,
{
fn observer(&mut self) -> FallbackDeviceObserver<O1, O2> {
fn link(&mut self, signal: crate::signaling::Signaler<crate::backend::session::Signal>) {
match self {
FallbackDevice::Preference(dev) => FallbackDeviceObserver::Preference(dev.observer()),
FallbackDevice::Fallback(dev) => FallbackDeviceObserver::Fallback(dev.observer()),
}
}
}
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),
FallbackDevice::Preference(d) => d.link(signal),
FallbackDevice::Fallback(d) => d.link(signal),
}
}
}

View File

@ -62,6 +62,8 @@ where
default_attributes: GlAttributes,
default_requirements: PixelFormatRequirements,
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>
@ -129,6 +131,8 @@ where
default_requirements,
backends: Rc::new(RefCell::new(HashMap::new())),
logger: log,
#[cfg(feature = "backend_session")]
links: Vec::new(),
})
}
}

View File

@ -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<S: SessionObserver + 'static, N: NativeSurface + Surface> {
observer: S,
pub struct EglDeviceObserver<N: NativeSurface + Surface> {
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
S: SessionObserver + 'static,
B: Backend<Surface = <D as Device>::Surface, Error = <<D as Device>::Surface as Surface>::Error>
+ 'static,
D: Device
+ NativeDisplay<B, Arguments = (crtc::Handle, Mode, Vec<connector::Handle>)>
+ AsSessionObserver<S>
+ Linkable<SessionSignal>
+ 'static,
<D as Device>::Surface: NativeSurface<Error = <<D as Device>::Surface as Surface>::Error>,
{
fn observer(&mut self) -> EglDeviceObserver<S, <D as Device>::Surface> {
EglDeviceObserver {
observer: self.dev.borrow_mut().observer(),
fn link(&mut self, signaler: Signaler<SessionSignal>) {
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<S: SessionObserver + 'static, N: NativeSurface + Surface> SessionObserver for EglDeviceObserver<S, N> {
fn pause(&mut self, devnum: Option<(u32, u32)>) {
self.observer.pause(devnum);
}
fn activate(&mut self, devnum: Option<(u32, u32, Option<RawFd>)>) {
self.observer.activate(devnum);
impl<N: NativeSurface + Surface> EglDeviceObserver<N> {
fn activate(&mut self) {
if let Some(backends) = self.backends.upgrade() {
for (_crtc, backend) in backends.borrow().iter() {
if let Some(backend) = backend.upgrade() {

View File

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

View File

@ -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<O> + 'static,
> {
observer: O,
pub struct EglStreamDeviceObserver<D: RawDevice + 'static> {
backends: Weak<RefCell<HashMap<crtc::Handle, Weak<EglStreamSurfaceInternal<D>>>>>,
logger: ::slog::Logger,
}
impl<O: SessionObserver + 'static, D: RawDevice + ControlDevice + AsSessionObserver<O> + 'static>
AsSessionObserver<EglStreamDeviceObserver<O, D>> for EglStreamDevice<D>
impl<D> Linkable<SessionSignal> for EglStreamDevice<D>
where
D: RawDevice + ControlDevice + Linkable<SessionSignal> + 'static,
{
fn observer(&mut self) -> EglStreamDeviceObserver<O, D> {
EglStreamDeviceObserver {
observer: self.raw.observer(),
fn link(&mut self, signaler: Signaler<SessionSignal>) {
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<O: SessionObserver + 'static, D: RawDevice + AsSessionObserver<O> + 'static> SessionObserver
for EglStreamDeviceObserver<O, D>
{
fn pause(&mut self, devnum: Option<(u32, u32)>) {
impl<D: RawDevice + 'static> EglStreamDeviceObserver<D> {
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<O: SessionObserver + 'static, D: RawDevice + AsSessionObserver<O> + 'static
}
}
}
self.observer.pause(devnum);
}
fn activate(&mut self, devnum: Option<(u32, u32, Option<RawFd>)>) {
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() {

View File

@ -76,6 +76,8 @@ static LOAD: Once = Once::new();
pub struct GbmDevice<D: RawDevice + ControlDevice + 'static> {
pub(self) dev: Rc<RefCell<gbm::Device<D>>>,
backends: Rc<RefCell<HashMap<crtc::Handle, Weak<GbmSurfaceInternal<D>>>>>,
#[cfg(feature = "backend_session")]
links: Vec<crate::signaling::SignalToken>,
logger: ::slog::Logger,
}
@ -110,6 +112,8 @@ impl<D: RawDevice + ControlDevice + 'static> GbmDevice<D> {
// 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,
})
}

View File

@ -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<S> + 'static,
> {
observer: S,
pub(crate) struct GbmDeviceObserver<D: RawDevice + ::drm::control::Device + 'static> {
backends: Weak<RefCell<HashMap<crtc::Handle, Weak<GbmSurfaceInternal<D>>>>>,
logger: ::slog::Logger,
}
impl<
O: SessionObserver + 'static,
S: CursorBackend<CursorFormat = dyn drm::buffer::Buffer> + RawSurface + 'static,
D: RawDevice<Surface = S> + drm::control::Device + AsSessionObserver<O> + 'static,
> AsSessionObserver<GbmDeviceObserver<O, D>> for GbmDevice<D>
impl<D> Linkable<SessionSignal> for GbmDevice<D>
where
D: RawDevice + drm::control::Device + Linkable<SessionSignal> + 'static,
<D as Device>::Surface: CursorBackend<CursorFormat = dyn drm::buffer::Buffer>,
{
fn observer(&mut self) -> GbmDeviceObserver<O, D> {
GbmDeviceObserver {
observer: (**self.dev.borrow_mut()).observer(),
fn link(&mut self, signaler: Signaler<SessionSignal>) {
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<CursorFormat = dyn drm::buffer::Buffer> + RawSurface + 'static,
D: RawDevice<Surface = S> + drm::control::Device + AsSessionObserver<O> + 'static,
> SessionObserver for GbmDeviceObserver<O, D>
impl<D: RawDevice + drm::control::Device + 'static> GbmDeviceObserver<D>
where
<D as Device>::Surface: CursorBackend<CursorFormat = dyn drm::buffer::Buffer>,
{
fn pause(&mut self, devnum: Option<(u32, u32)>) {
self.observer.pause(devnum);
}
fn activate(&mut self, devnum: Option<(u32, u32, Option<RawFd>)>) {
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() {

View File

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

View File

@ -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<A: AsRawFd + 'static> {
pub(crate) struct LegacyDrmDeviceObserver<A: AsRawFd + 'static> {
dev: Weak<Dev<A>>,
dev_id: dev_t,
privileged: bool,
@ -30,20 +33,34 @@ pub struct LegacyDrmDeviceObserver<A: AsRawFd + 'static> {
logger: ::slog::Logger,
}
impl<A: AsRawFd + 'static> AsSessionObserver<LegacyDrmDeviceObserver<A>> for LegacyDrmDevice<A> {
fn observer(&mut self) -> LegacyDrmDeviceObserver<A> {
LegacyDrmDeviceObserver {
impl<A: AsRawFd + 'static> Linkable<SessionSignal> for LegacyDrmDevice<A> {
fn link(&mut self, signaler: Signaler<SessionSignal>) {
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<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)>) {
if let Some((major, minor)) = devnum {
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};
#[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<libinput::Seat, backend::Seat>,
links: Vec<SignalToken>,
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<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 {
fn time(&self) -> u32 {
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
/// a [`libinput::LibinputInterface`] implementation.
#[cfg(feature = "backend_session")]

View File

@ -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<S: SessionObserver + 'static>(&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<SessionSignal> {
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(),
}
}
}

View File

@ -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<DBusConnection>,
session_path: DbusPath<'static>,
active: AtomicBool,
signals: RefCell<Vec<Option<Box<dyn SessionObserver>>>>,
signaler: Signaler<SessionSignal>,
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<SessionSignal> {
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<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 {
fn drop(&mut self) {
info!(self.internal.logger, "Closing logind session");

View File

@ -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<AtomicBool>,
signals: Vec<Option<Box<dyn SessionObserver>>>,
signaler: Signaler<SessionSignal>,
signal: Signal,
logger: ::slog::Logger,
source: Option<Signals>,
@ -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<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 {
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<SessionSignal> {
self.signaler.clone()
}
}
impl calloop::EventSource for DirectSessionNotifier {

View File

@ -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<S: SessionObserver + 'static>(&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.
/// 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
///
/// The returned [`SessionObserver`] is responsible to handle the [`pause`](SessionObserver::pause)
/// and [`activate`](SessionObserver::activate) signals.
pub trait AsSessionObserver<S: SessionObserver + 'static> {
/// Create a [`SessionObserver`] linked to this object
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>)>);
/// 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<RawFd>,
},
}
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::*;

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
#[derive(Clone)]
pub struct Signaler<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> {
/// Create a new signaler for given signal type
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)]
mod tests {
use super::*;