Add documentation for logind/auto session

This commit is contained in:
Drakulix 2018-01-27 13:38:21 +01:00
parent 4501ca5fe1
commit 20e10612b9
3 changed files with 116 additions and 18 deletions

View File

@ -1,3 +1,34 @@
//!
//! Implementation of the `Session` trait through various implementations
//! automatically choosing the best available interface.
//!
//! ## How to use it
//!
//! ### Initialization
//!
//! To initialize a session just call `AutoSession::new`. A new session will be opened, if the
//! any available interface is successful and will be closed once the `AutoSessionNotifier` is dropped.
//!
//! ### Usage of the session
//!
//! The session may be used to open devices manually through the `Session` interface
//! or be passed to other objects that need it to open devices themselves.
//! The `AutoSession` is clonable and may be passed to multiple devices easily.
//!
//! Examples for those are e.g. the `LibinputInputBackend` (its context might be initialized through a
//! `Session` via the `LibinputSessionInterface`) or the `UdevBackend`.
//!
//! ### Usage of the session notifier
//!
//! The notifier might be used to pause device access, when the session gets paused (e.g. by
//! switching the tty via `AutoSession::change_vt`) and to automatically enable it again,
//! when the session becomes active again.
//!
//! It is crutial to avoid errors during that state. Examples for object that might be registered
//! for notifications are the `Libinput` context, the `UdevBackend` or a `DrmDevice` (handled
//! automatically by the `UdevBackend`, if not done manually).
//! ```
use std::io::{Result as IoResult};
use std::rc::Rc;
use std::cell::RefCell;
@ -12,25 +43,39 @@ use super::{Session, SessionNotifier, SessionObserver, AsErrno};
use super::logind::{self, LogindSession, LogindSessionNotifier, BoundLogindSession, logind_session_bind};
use super::direct::{self, DirectSession, DirectSessionNotifier, direct_session_bind};
/// `Session` using the best available inteface
#[derive(Clone)]
pub enum AutoSession {
/// Logind session
#[cfg(feature = "backend_session_logind")]
Logind(LogindSession),
/// Direct / tty session
Direct(Rc<RefCell<DirectSession>>),
}
/// `SessionNotifier` using the best available inteface
pub enum AutoSessionNotifier {
/// Logind session nofifier
#[cfg(feature = "backend_session_logind")]
Logind(LogindSessionNotifier),
/// Direct / tty session notifier
Direct(DirectSessionNotifier),
}
/// Bound session that is driven by the `wayland_server::EventLoop`.
///
/// See `auto_session_bind` for details.
///
/// Dropping this object will close the session just like the `AutoSessionNotifier`.
pub enum BoundAutoSession {
/// Bound logind session
#[cfg(feature = "backend_session_logind")]
Logind(BoundLogindSession),
/// Bound direct / tty session
Direct(SignalEventSource<DirectSessionNotifier>),
}
/// Id's used by the `AutoSessionNotifier` internally.
#[derive(PartialEq, Eq)]
pub struct AutoId(AutoIdInternal);
#[derive(PartialEq, Eq)]
@ -41,6 +86,7 @@ enum AutoIdInternal {
}
impl AutoSession {
/// Tries to create a new session via the best available interface.
#[cfg(feature = "backend_session_logind")]
pub fn new<L>(logger: L) -> Option<(AutoSession, AutoSessionNotifier)>
where L: Into<Option<::slog::Logger>>
@ -85,6 +131,11 @@ impl AutoSession {
}
}
/// Bind an `AutoSessionNotifier` to an `EventLoop`.
///
/// Allows the `AutoSessionNotifier` to listen for incoming signals signalling the session state.
/// If you don't use this function `AutoSessionNotifier` will not correctly tell you the
/// session state and call it's `SessionObservers`.
pub fn auto_session_bind(notifier: AutoSessionNotifier, evlh: &mut EventLoopHandle) -> IoResult<BoundAutoSession> {
Ok(match notifier {
#[cfg(feature = "backend_session_logind")]
@ -171,10 +222,11 @@ impl SessionNotifier for AutoSessionNotifier {
}
impl BoundAutoSession {
pub fn remove(self) -> AutoSessionNotifier {
/// Unbind the session from the `EventLoop` again
pub fn unbind(self) -> AutoSessionNotifier {
match self {
#[cfg(feature = "backend_session_logind")]
BoundAutoSession::Logind(logind) => AutoSessionNotifier::Logind(logind.close()),
BoundAutoSession::Logind(logind) => AutoSessionNotifier::Logind(logind.unbind()),
BoundAutoSession::Direct(source) => AutoSessionNotifier::Direct(source.remove()),
}
}
@ -182,11 +234,11 @@ impl BoundAutoSession {
error_chain! {
links {
Logind(logind::Error, logind::ErrorKind) #[cfg(feature = "backend_session_logind")];
Logind(logind::Error, logind::ErrorKind) #[cfg(feature = "backend_session_logind")] #[doc = "Underlying logind session error"];
}
foreign_links {
Direct(::nix::Error);
Direct(::nix::Error) #[doc = "Underlying direct tty session error"];
}
}

View File

@ -1,3 +1,35 @@
//!
//! Implementation of the `Session` trait through the logind dbus interface.
//!
//! This requires systemd and dbus to be available and started on the system.
//!
//! ## How to use it
//!
//! ### Initialization
//!
//! To initialize a session just call `LogindSession::new`. A new session will be opened, if the
//! call is successful and will be closed once the `LogindSessionNotifier` is dropped.
//!
//! ### Usage of the session
//!
//! The session may be used to open devices manually through the `Session` interface
//! or be passed to other objects that need it to open devices themselves.
//! The `LogindSession` is clonable and may be passed to multiple devices easily.
//!
//! Examples for those are e.g. the `LibinputInputBackend` (its context might be initialized through a
//! `Session` via the `LibinputSessionInterface`) or the `UdevBackend`.
//!
//! ### Usage of the session notifier
//!
//! The notifier might be used to pause device access, when the session gets paused (e.g. by
//! switching the tty via `LogindSession::change_vt`) and to automatically enable it again,
//! when the session becomes active again.
//!
//! It is crutial to avoid errors during that state. Examples for object that might be registered
//! for notifications are the `Libinput` context, the `UdevBackend` or a `DrmDevice` (handled
//! automatically by the `UdevBackend`, if not done manually).
//! ```
use ::backend::session::{AsErrno, Session, SessionNotifier, SessionObserver};
use nix::fcntl::OFlag;
use nix::sys::stat::{stat, fstat, major, minor};
@ -21,17 +53,20 @@ struct LogindSessionImpl {
logger: ::slog::Logger,
}
/// `Session` via the logind dbus interface
#[derive(Clone)]
pub struct LogindSession {
internal: Weak<LogindSessionImpl>,
seat: String,
}
/// `SessionNotifier` via the logind dbus interface
pub struct LogindSessionNotifier {
internal: Rc<LogindSessionImpl>
}
impl LogindSession {
/// Tries to create a new session via the logind dbus interface.
pub fn new<L>(logger: L) -> Result<(LogindSession, LogindSessionNotifier)>
where
L: Into<Option<::slog::Logger>>
@ -39,17 +74,14 @@ impl LogindSession {
let logger = ::slog_or_stdlog(logger)
.new(o!("smithay_module" => "backend_session", "session_type" => "logind"));
// Acquire session_id, seat and vt (if any) via libsystemd
let session_id = login::get_session(None).chain_err(|| ErrorKind::FailedToGetSession)?;
let seat = login::get_seat(session_id.clone()).chain_err(|| ErrorKind::FailedToGetSeat)?;
/*let vt = if seat == "seat0" {
Some(login::get_vt(session_id.clone()).chain_err(|| ErrorKind::FailedToGetVT)?)
} else {
None
};*/
let vt = login::get_vt(session_id.clone()).ok();
// Create dbus connection
let conn = Connection::get_private(BusType::System).chain_err(|| ErrorKind::FailedDbusConnection)?;
// and get the session path
let session_path = LogindSessionImpl::blocking_call(
&conn,
"org.freedesktop.login1",
@ -60,6 +92,7 @@ impl LogindSession {
)?.get1::<DbusPath<'static>>()
.chain_err(|| ErrorKind::UnexpectedMethodReturn)?;
// Match all signals that we want to receive and handle
let match1 = String::from("type='signal',\
sender='org.freedesktop.login1',\
interface='org.freedesktop.login1.Manager',\
@ -85,6 +118,7 @@ impl LogindSession {
path='{}'", &session_path);
conn.add_match(&match4).chain_err(|| ErrorKind::DbusMatchFailed(match4))?;
// Activate (switch to) the session and take control
LogindSessionImpl::blocking_call(
&conn,
"org.freedesktop.login1",
@ -93,7 +127,6 @@ impl LogindSession {
"Activate",
None,
)?;
LogindSessionImpl::blocking_call(
&conn,
"org.freedesktop.login1",
@ -128,6 +161,7 @@ impl LogindSession {
}
impl LogindSessionNotifier {
/// Creates a new session object beloging to this notifier.
pub fn session(&self) -> LogindSession {
LogindSession {
internal: Rc::downgrade(&self.internal),
@ -357,12 +391,22 @@ impl SessionNotifier for LogindSessionNotifier {
}
}
/// Bound logind session that is driven by the `wayland_server::EventLoop`.
///
/// See `logind_session_bind` for details.
///
/// Dropping this object will close the logind session just like the `LogindSessionNotifier`.
pub struct BoundLogindSession {
notifier: LogindSessionNotifier,
watches: Vec<Watch>,
_watches: Vec<Watch>,
sources: Vec<FdEventSource<Rc<LogindSessionImpl>>>,
}
/// Bind a `LogindSessionNotifier` to an `EventLoop`.
///
/// Allows the `LogindSessionNotifier` to listen for incoming signals signalling the session state.
/// If you don't use this function `LogindSessionNotifier` will not correctly tell you the logind
/// session state and call it's `SessionObservers`.
pub fn logind_session_bind(
notifier: LogindSessionNotifier, evlh: &mut EventLoopHandle
) -> IoResult<BoundLogindSession>
@ -382,13 +426,14 @@ pub fn logind_session_bind(
Ok(BoundLogindSession {
notifier,
watches,
_watches: watches,
sources,
})
}
impl BoundLogindSession {
pub fn close(self) -> LogindSessionNotifier {
/// Unbind the logind session from the `EventLoop`
pub fn unbind(self) -> LogindSessionNotifier {
for source in self.sources {
source.remove();
}
@ -399,6 +444,7 @@ impl BoundLogindSession {
impl Drop for LogindSessionNotifier {
fn drop(&mut self) {
info!(self.internal.logger, "Closing logind session");
// Release control again and drop everything closing the connection
let _ = LogindSessionImpl::blocking_call(
&*self.internal.conn.borrow(),
"org.freedesktop.login1",

View File

@ -27,7 +27,7 @@
//! ### Usage of the session
//!
//! The session may be used to open devices manually through the `Session` interface
//! or be passed to other object that need to open devices themselves.
//! or be passed to other objects that need it to open devices themselves.
//!
//! Examples for those are e.g. the `LibinputInputBackend` (its context might be initialized through a
//! `Session` via the `LibinputSessionInterface`) or the `UdevBackend`.
@ -160,7 +160,7 @@ pub struct DirectSessionNotifier {
}
impl DirectSession {
/// Tries to creates a new session via the legacy virtual terminal interface.
/// Tries to create a new session via the legacy virtual terminal interface.
///
/// If you do not provide a tty device path, it will try to open the currently active tty if any.
pub fn new<L>(tty: Option<&Path>, logger: L) -> Result<(DirectSession, DirectSessionNotifier)>
@ -362,9 +362,9 @@ impl SessionNotifier for DirectSessionNotifier {
/// Bind a `DirectSessionNotifier` to an `EventLoop`.
///
/// Allows the `DirectSessionNotifier` to listen for the incoming signals signalling the session state.
/// Allows the `DirectSessionNotifier` to listen for incoming signals signalling the session state.
/// If you don't use this function `DirectSessionNotifier` will not correctly tell you the current
/// session state.
/// session state and call it's `SessionObservers`.
pub fn direct_session_bind(
notifier: DirectSessionNotifier, evlh: &mut EventLoopHandle
) -> IoResult<SignalEventSource<DirectSessionNotifier>> {