libseat support (#292)
This commit is contained in:
parent
e9aef7caad
commit
f9f77288c8
|
@ -15,6 +15,7 @@ bitflags = "1"
|
|||
calloop = "0.8.0"
|
||||
cgmath = "0.18.0"
|
||||
dbus = { version = "0.9.0", optional = true }
|
||||
libseat= { version = "0.1.1", optional = true }
|
||||
drm-fourcc = "^2.1.1"
|
||||
drm = { version = "0.4.0", optional = true }
|
||||
drm-ffi = { version = "0.1.0", optional = true }
|
||||
|
@ -57,6 +58,7 @@ backend_session = []
|
|||
backend_udev = ["udev"]
|
||||
backend_session_logind = ["dbus", "backend_session", "pkg-config"]
|
||||
backend_session_elogind = ["backend_session_logind"]
|
||||
backend_session_libseat = ["libseat"]
|
||||
renderer_gl = ["gl_generator", "backend_egl"]
|
||||
use_system_lib = ["wayland_frontend", "wayland-sys", "wayland-server/use_system_lib"]
|
||||
wayland_frontend = ["wayland-server", "wayland-commons", "wayland-protocols", "tempfile"]
|
||||
|
|
|
@ -37,5 +37,6 @@ winit = [ "smithay/backend_winit" ]
|
|||
udev = [ "smithay/backend_libinput", "smithay/backend_udev", "smithay/backend_drm", "smithay/backend_gbm", "smithay/backend_egl", "smithay/backend_session", "input", "image", "smithay/image"]
|
||||
logind = [ "smithay/backend_session_logind" ]
|
||||
elogind = ["logind", "smithay/backend_session_elogind" ]
|
||||
libseat = ["smithay/backend_session_libseat" ]
|
||||
xwayland = [ "smithay/xwayland", "x11rb" ]
|
||||
test_all_features = ["default"]
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
use std::env::var;
|
||||
|
||||
fn main() {
|
||||
if var("CARGO_FEATURE_LOGIND").ok().is_none() {
|
||||
println!("cargo:warning=You are compiling anvil without logind support.");
|
||||
if var("CARGO_FEATURE_LOGIND").ok().is_none() && var("CARGO_FEATURE_LIBSEAT").ok().is_none() {
|
||||
println!("cargo:warning=You are compiling anvil without logind/libseat support.");
|
||||
println!("cargo:warning=This means that you'll likely need to run it as root if you want to launch it from a tty.");
|
||||
println!("cargo:warning=To enable logind support add `--feature logind` to your cargo invocation:");
|
||||
println!("cargo:warning=To enable logind support add `--feature logind` to your cargo invocation.");
|
||||
println!("cargo:warning=$ cd anvil; cargo run --feature logind");
|
||||
println!("cargo:warning=To enable libseat support add `--feature libseat` to your cargo invocation.");
|
||||
println!("cargo:warning=$ cd anvil; cargo run --feature libseat");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
//! The [`AutoSessionNotifier`] is to be inserted into
|
||||
//! a calloop event source to have its events processed.
|
||||
|
||||
#[cfg(feature = "backend_session_libseat")]
|
||||
use super::libseat::{LibSeatSession, LibSeatSessionNotifier};
|
||||
#[cfg(feature = "backend_session_logind")]
|
||||
use super::logind::{self, LogindSession, LogindSessionNotifier};
|
||||
use super::{
|
||||
|
@ -51,6 +53,9 @@ pub enum AutoSession {
|
|||
Logind(LogindSession),
|
||||
/// Direct / tty session
|
||||
Direct(Rc<RefCell<DirectSession>>),
|
||||
/// LibSeat session
|
||||
#[cfg(feature = "backend_session_libseat")]
|
||||
LibSeat(LibSeatSession),
|
||||
}
|
||||
|
||||
/// Notifier using the best available interface
|
||||
|
@ -61,11 +66,13 @@ pub enum AutoSessionNotifier {
|
|||
Logind(LogindSessionNotifier),
|
||||
/// Direct / tty session notifier
|
||||
Direct(DirectSessionNotifier),
|
||||
/// LibSeat session notifier
|
||||
#[cfg(feature = "backend_session_libseat")]
|
||||
LibSeat(LibSeatSessionNotifier),
|
||||
}
|
||||
|
||||
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>>,
|
||||
|
@ -73,52 +80,54 @@ impl AutoSession {
|
|||
let logger = crate::slog_or_fallback(logger)
|
||||
.new(o!("smithay_module" => "backend_session_auto", "session_type" => "auto"));
|
||||
|
||||
#[cfg(feature = "backend_session_libseat")]
|
||||
{
|
||||
info!(logger, "Trying to create libseat session");
|
||||
match LibSeatSession::new(logger.clone()) {
|
||||
Ok((sesstion, notifier)) => {
|
||||
return Some((
|
||||
AutoSession::LibSeat(sesstion),
|
||||
AutoSessionNotifier::LibSeat(notifier),
|
||||
))
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(logger, "Failed to create libseat session: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "backend_session_logind")]
|
||||
{
|
||||
info!(logger, "Trying to create logind session");
|
||||
match LogindSession::new(logger.clone()) {
|
||||
Ok((session, notifier)) => Some((
|
||||
Ok((session, notifier)) => {
|
||||
return Some((
|
||||
AutoSession::Logind(session),
|
||||
AutoSessionNotifier::Logind(notifier),
|
||||
)),
|
||||
))
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(logger, "Failed to create logind session: {}", err);
|
||||
info!(logger, "Falling back to create tty session");
|
||||
match DirectSession::new(None, logger.clone()) {
|
||||
Ok((session, notifier)) => Some((
|
||||
AutoSession::Direct(Rc::new(RefCell::new(session))),
|
||||
AutoSessionNotifier::Direct(notifier),
|
||||
)),
|
||||
Err(err) => {
|
||||
warn!(logger, "Failed to create direct session: {}", err);
|
||||
error!(logger, "Could not create any session, possibilities exhausted");
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to create a new session via the best available interface.
|
||||
#[cfg(not(feature = "backend_session_logind"))]
|
||||
pub fn new<L>(logger: L) -> Option<(AutoSession, AutoSessionNotifier)>
|
||||
where
|
||||
L: Into<Option<::slog::Logger>>,
|
||||
{
|
||||
let logger = crate::slog_or_fallback(logger)
|
||||
.new(o!("smithay_module" => "backend_session_auto", "session_type" => "auto"));
|
||||
|
||||
info!(logger, "Trying to create tty session");
|
||||
match DirectSession::new(None, logger.clone()) {
|
||||
Ok((session, notifier)) => Some((
|
||||
Ok((session, notifier)) => {
|
||||
return Some((
|
||||
AutoSession::Direct(Rc::new(RefCell::new(session))),
|
||||
AutoSessionNotifier::Direct(notifier),
|
||||
)),
|
||||
))
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(logger, "Failed to create direct session: {}", err);
|
||||
}
|
||||
}
|
||||
|
||||
error!(logger, "Could not create any session, possibilities exhausted");
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Session for AutoSession {
|
||||
|
@ -129,6 +138,8 @@ impl Session for AutoSession {
|
|||
#[cfg(feature = "backend_session_logind")]
|
||||
AutoSession::Logind(ref mut logind) => logind.open(path, flags).map_err(|e| e.into()),
|
||||
AutoSession::Direct(ref mut direct) => direct.open(path, flags).map_err(|e| e.into()),
|
||||
#[cfg(feature = "backend_session_libseat")]
|
||||
AutoSession::LibSeat(ref mut logind) => logind.open(path, flags).map_err(|e| e.into()),
|
||||
}
|
||||
}
|
||||
fn close(&mut self, fd: RawFd) -> Result<(), Error> {
|
||||
|
@ -136,6 +147,8 @@ impl Session for AutoSession {
|
|||
#[cfg(feature = "backend_session_logind")]
|
||||
AutoSession::Logind(ref mut logind) => logind.close(fd).map_err(|e| e.into()),
|
||||
AutoSession::Direct(ref mut direct) => direct.close(fd).map_err(|e| e.into()),
|
||||
#[cfg(feature = "backend_session_libseat")]
|
||||
AutoSession::LibSeat(ref mut direct) => direct.close(fd).map_err(|e| e.into()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -144,6 +157,8 @@ impl Session for AutoSession {
|
|||
#[cfg(feature = "backend_session_logind")]
|
||||
AutoSession::Logind(ref mut logind) => logind.change_vt(vt).map_err(|e| e.into()),
|
||||
AutoSession::Direct(ref mut direct) => direct.change_vt(vt).map_err(|e| e.into()),
|
||||
#[cfg(feature = "backend_session_libseat")]
|
||||
AutoSession::LibSeat(ref mut direct) => direct.change_vt(vt).map_err(|e| e.into()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -152,6 +167,8 @@ impl Session for AutoSession {
|
|||
#[cfg(feature = "backend_session_logind")]
|
||||
AutoSession::Logind(ref logind) => logind.is_active(),
|
||||
AutoSession::Direct(ref direct) => direct.is_active(),
|
||||
#[cfg(feature = "backend_session_libseat")]
|
||||
AutoSession::LibSeat(ref direct) => direct.is_active(),
|
||||
}
|
||||
}
|
||||
fn seat(&self) -> String {
|
||||
|
@ -159,6 +176,8 @@ impl Session for AutoSession {
|
|||
#[cfg(feature = "backend_session_logind")]
|
||||
AutoSession::Logind(ref logind) => logind.seat(),
|
||||
AutoSession::Direct(ref direct) => direct.seat(),
|
||||
#[cfg(feature = "backend_session_libseat")]
|
||||
AutoSession::LibSeat(ref direct) => direct.seat(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -172,6 +191,8 @@ impl AutoSessionNotifier {
|
|||
#[cfg(feature = "backend_session_logind")]
|
||||
AutoSessionNotifier::Logind(ref logind) => logind.signaler(),
|
||||
AutoSessionNotifier::Direct(ref direct) => direct.signaler(),
|
||||
#[cfg(feature = "backend_session_libseat")]
|
||||
AutoSessionNotifier::LibSeat(ref direct) => direct.signaler(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -189,6 +210,8 @@ impl EventSource for AutoSessionNotifier {
|
|||
#[cfg(feature = "backend_session_logind")]
|
||||
AutoSessionNotifier::Logind(s) => s.process_events(readiness, token, callback),
|
||||
AutoSessionNotifier::Direct(s) => s.process_events(readiness, token, callback),
|
||||
#[cfg(feature = "backend_session_libseat")]
|
||||
AutoSessionNotifier::LibSeat(s) => s.process_events(readiness, token, callback),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -197,6 +220,8 @@ impl EventSource for AutoSessionNotifier {
|
|||
#[cfg(feature = "backend_session_logind")]
|
||||
AutoSessionNotifier::Logind(s) => EventSource::register(s, poll, token),
|
||||
AutoSessionNotifier::Direct(s) => EventSource::register(s, poll, token),
|
||||
#[cfg(feature = "backend_session_libseat")]
|
||||
AutoSessionNotifier::LibSeat(s) => EventSource::register(s, poll, token),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -205,6 +230,8 @@ impl EventSource for AutoSessionNotifier {
|
|||
#[cfg(feature = "backend_session_logind")]
|
||||
AutoSessionNotifier::Logind(s) => EventSource::reregister(s, poll, token),
|
||||
AutoSessionNotifier::Direct(s) => EventSource::reregister(s, poll, token),
|
||||
#[cfg(feature = "backend_session_libseat")]
|
||||
AutoSessionNotifier::LibSeat(s) => EventSource::reregister(s, poll, token),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -213,6 +240,8 @@ impl EventSource for AutoSessionNotifier {
|
|||
#[cfg(feature = "backend_session_logind")]
|
||||
AutoSessionNotifier::Logind(s) => EventSource::unregister(s, poll),
|
||||
AutoSessionNotifier::Direct(s) => EventSource::unregister(s, poll),
|
||||
#[cfg(feature = "backend_session_libseat")]
|
||||
AutoSessionNotifier::LibSeat(s) => EventSource::unregister(s, poll),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -227,6 +256,11 @@ pub enum Error {
|
|||
/// Direct session error
|
||||
#[error("Direct session error: {0}")]
|
||||
Direct(#[from] direct::Error),
|
||||
/// LibSeat session error
|
||||
#[cfg(feature = "backend_session_libseat")]
|
||||
#[error("LibSeat session error: {0}")]
|
||||
LibSeat(#[from] super::libseat::Error),
|
||||
|
||||
/// Nix error
|
||||
#[error("Nix error: {0}")]
|
||||
Nix(#[from] nix::Error),
|
||||
|
|
|
@ -0,0 +1,281 @@
|
|||
//!
|
||||
//! Implementation of the [`Session`](::backend::session::Session) trait through the libseat.
|
||||
//!
|
||||
//! This requires libseat to be available on the system.
|
||||
|
||||
use libseat::{Seat, SeatEvent};
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
collections::HashMap,
|
||||
os::unix::io::RawFd,
|
||||
path::Path,
|
||||
rc::{Rc, Weak},
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
},
|
||||
};
|
||||
|
||||
use nix::{errno::Errno, fcntl::OFlag, unistd::close};
|
||||
|
||||
use calloop::{EventSource, Poll, Readiness, Token};
|
||||
|
||||
use crate::{
|
||||
backend::session::{AsErrno, Session, Signal as SessionSignal},
|
||||
signaling::Signaler,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct LibSeatSessionImpl {
|
||||
seat: RefCell<Seat>,
|
||||
active: Arc<AtomicBool>,
|
||||
devices: RefCell<HashMap<RawFd, i32>>,
|
||||
logger: ::slog::Logger,
|
||||
}
|
||||
|
||||
impl Drop for LibSeatSessionImpl {
|
||||
fn drop(&mut self) {
|
||||
debug!(self.logger, "Closing seat")
|
||||
}
|
||||
}
|
||||
|
||||
/// [`Session`] via the libseat
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LibSeatSession {
|
||||
internal: Weak<LibSeatSessionImpl>,
|
||||
seat_name: String,
|
||||
}
|
||||
|
||||
/// [`SessionNotifier`] via the libseat
|
||||
#[derive(Debug)]
|
||||
pub struct LibSeatSessionNotifier {
|
||||
internal: Rc<LibSeatSessionImpl>,
|
||||
signaler: Signaler<SessionSignal>,
|
||||
}
|
||||
|
||||
impl LibSeatSession {
|
||||
/// Tries to create a new session via libseat.
|
||||
pub fn new<L>(logger: L) -> Result<(LibSeatSession, LibSeatSessionNotifier), Error>
|
||||
where
|
||||
L: Into<Option<::slog::Logger>>,
|
||||
{
|
||||
let logger = crate::slog_or_fallback(logger)
|
||||
.new(o!("smithay_module" => "backend_session", "session_type" => "libseat"));
|
||||
|
||||
let active = Arc::new(AtomicBool::new(false));
|
||||
let signaler = Signaler::new();
|
||||
|
||||
let seat = {
|
||||
let log = logger.clone();
|
||||
let active = active.clone();
|
||||
let signaler = signaler.clone();
|
||||
|
||||
Seat::open(
|
||||
move |seat, event| match event {
|
||||
SeatEvent::Enable => {
|
||||
debug!(log, "Enable callback called");
|
||||
active.store(true, Ordering::SeqCst);
|
||||
signaler.signal(SessionSignal::ActivateSession);
|
||||
}
|
||||
SeatEvent::Disable => {
|
||||
debug!(log, "Disable callback called");
|
||||
active.store(false, Ordering::SeqCst);
|
||||
signaler.signal(SessionSignal::PauseSession);
|
||||
seat.disable().unwrap();
|
||||
}
|
||||
},
|
||||
logger.clone(),
|
||||
)
|
||||
};
|
||||
|
||||
seat.map(|mut seat| {
|
||||
// In some cases enable_seat event is avalible right after startup
|
||||
// so, we can dispatch it
|
||||
seat.dispatch(0).unwrap();
|
||||
|
||||
let seat_name = seat.name().to_owned();
|
||||
|
||||
let internal = Rc::new(LibSeatSessionImpl {
|
||||
seat: RefCell::new(seat),
|
||||
active,
|
||||
devices: RefCell::new(HashMap::new()),
|
||||
logger,
|
||||
});
|
||||
|
||||
(
|
||||
LibSeatSession {
|
||||
internal: Rc::downgrade(&internal),
|
||||
seat_name,
|
||||
},
|
||||
LibSeatSessionNotifier { internal, signaler },
|
||||
)
|
||||
})
|
||||
.map_err(|err| Error::FailedToOpenSession(Errno::from_i32(err.into())))
|
||||
}
|
||||
}
|
||||
|
||||
impl Session for LibSeatSession {
|
||||
type Error = Error;
|
||||
|
||||
fn open(&mut self, path: &Path, _flags: OFlag) -> Result<RawFd, Self::Error> {
|
||||
if let Some(session) = self.internal.upgrade() {
|
||||
debug!(session.logger, "Opening device: {:?}", path);
|
||||
|
||||
session
|
||||
.seat
|
||||
.borrow_mut()
|
||||
.open_device(&path)
|
||||
.map(|(id, fd)| {
|
||||
session.devices.borrow_mut().insert(fd, id);
|
||||
fd
|
||||
})
|
||||
.map_err(|err| Error::FailedToOpenDevice(Errno::from_i32(err.into())))
|
||||
} else {
|
||||
Err(Error::SessionLost)
|
||||
}
|
||||
}
|
||||
|
||||
fn close(&mut self, fd: RawFd) -> Result<(), Self::Error> {
|
||||
if let Some(session) = self.internal.upgrade() {
|
||||
debug!(session.logger, "Closing device: {:?}", fd);
|
||||
|
||||
let dev = session.devices.borrow().get(&fd).map(|fd| *fd);
|
||||
|
||||
let out = if let Some(dev) = dev {
|
||||
session
|
||||
.seat
|
||||
.borrow_mut()
|
||||
.close_device(dev)
|
||||
.map_err(|err| Error::FailedToCloseDevice(Errno::from_i32(err.into())))
|
||||
} else {
|
||||
Ok(())
|
||||
};
|
||||
|
||||
close(fd).unwrap();
|
||||
|
||||
out
|
||||
} else {
|
||||
Err(Error::SessionLost)
|
||||
}
|
||||
}
|
||||
|
||||
fn change_vt(&mut self, vt: i32) -> Result<(), Self::Error> {
|
||||
if let Some(session) = self.internal.upgrade() {
|
||||
debug!(session.logger, "Session switch: {:?}", vt);
|
||||
session
|
||||
.seat
|
||||
.borrow_mut()
|
||||
.switch_session(vt)
|
||||
.map_err(|err| Error::FailedToChangeVt(Errno::from_i32(err.into())))
|
||||
} else {
|
||||
Err(Error::SessionLost)
|
||||
}
|
||||
}
|
||||
|
||||
fn is_active(&self) -> bool {
|
||||
if let Some(internal) = self.internal.upgrade() {
|
||||
internal.active.load(Ordering::SeqCst)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn seat(&self) -> String {
|
||||
self.seat_name.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl LibSeatSessionNotifier {
|
||||
/// Creates a new session object belonging to this notifier.
|
||||
pub fn session(&self) -> LibSeatSession {
|
||||
LibSeatSession {
|
||||
internal: Rc::downgrade(&self.internal),
|
||||
seat_name: self.internal.seat.borrow_mut().name().to_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 EventSource for LibSeatSessionNotifier {
|
||||
type Event = ();
|
||||
type Metadata = ();
|
||||
type Ret = ();
|
||||
|
||||
fn process_events<F>(&mut self, _readiness: Readiness, _token: Token, _: F) -> std::io::Result<()>
|
||||
where
|
||||
F: FnMut((), &mut ()),
|
||||
{
|
||||
self.internal.seat.borrow_mut().dispatch(0).unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn register(&mut self, poll: &mut Poll, token: Token) -> std::io::Result<()> {
|
||||
poll.register(
|
||||
self.internal.seat.borrow_mut().get_fd().unwrap(),
|
||||
calloop::Interest::READ,
|
||||
calloop::Mode::Level,
|
||||
token,
|
||||
)
|
||||
.unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn reregister(&mut self, poll: &mut Poll, token: Token) -> std::io::Result<()> {
|
||||
poll.reregister(
|
||||
self.internal.seat.borrow_mut().get_fd().unwrap(),
|
||||
calloop::Interest::READ,
|
||||
calloop::Mode::Level,
|
||||
token,
|
||||
)
|
||||
.unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unregister(&mut self, poll: &mut Poll) -> std::io::Result<()> {
|
||||
poll.unregister(self.internal.seat.borrow_mut().get_fd().unwrap())
|
||||
.unwrap();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Errors related to direct/tty sessions
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum Error {
|
||||
/// Failed to open session
|
||||
#[error("Failed to open session: {0}")]
|
||||
FailedToOpenSession(Errno),
|
||||
|
||||
/// Failed to open device
|
||||
#[error("Failed to open device: {0}")]
|
||||
FailedToOpenDevice(Errno),
|
||||
|
||||
/// Failed to close device
|
||||
#[error("Failed to close device: {0}")]
|
||||
FailedToCloseDevice(Errno),
|
||||
|
||||
/// Failed to close device
|
||||
#[error("Failed to change vt: {0}")]
|
||||
FailedToChangeVt(Errno),
|
||||
|
||||
/// Session is already closed,
|
||||
#[error("Session is already closed")]
|
||||
SessionLost,
|
||||
}
|
||||
|
||||
impl AsErrno for Error {
|
||||
fn as_errno(&self) -> Option<i32> {
|
||||
match self {
|
||||
&Self::FailedToOpenSession(errno)
|
||||
| &Self::FailedToOpenDevice(errno)
|
||||
| &Self::FailedToCloseDevice(errno)
|
||||
| &Self::FailedToChangeVt(errno) => Some(errno as i32),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -161,6 +161,8 @@ impl AsErrno for () {
|
|||
|
||||
pub mod auto;
|
||||
pub mod direct;
|
||||
#[cfg(feature = "backend_session_libseat")]
|
||||
pub mod libseat;
|
||||
|
||||
#[cfg(feature = "backend_session_logind")]
|
||||
mod dbus;
|
||||
|
|
Loading…
Reference in New Issue