libseat support (#292)

This commit is contained in:
Poly 2021-06-15 23:35:16 +02:00 committed by GitHub
parent e9aef7caad
commit f9f77288c8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 360 additions and 38 deletions

View File

@ -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"]

View File

@ -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"]

View File

@ -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");
}
}

View File

@ -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,51 +80,53 @@ impl AutoSession {
let logger = crate::slog_or_fallback(logger)
.new(o!("smithay_module" => "backend_session_auto", "session_type" => "auto"));
info!(logger, "Trying to create logind session");
match LogindSession::new(logger.clone()) {
Ok((session, notifier)) => 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
}
#[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);
}
}
}
}
/// 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"));
#[cfg(feature = "backend_session_logind")]
{
info!(logger, "Trying to create logind session");
match LogindSession::new(logger.clone()) {
Ok((session, notifier)) => {
return Some((
AutoSession::Logind(session),
AutoSessionNotifier::Logind(notifier),
))
}
Err(err) => {
warn!(logger, "Failed to create logind session: {}", err);
}
}
}
info!(logger, "Trying to create tty session");
match DirectSession::new(None, logger.clone()) {
Ok((session, notifier)) => Some((
AutoSession::Direct(Rc::new(RefCell::new(session))),
AutoSessionNotifier::Direct(notifier),
)),
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
}
}
error!(logger, "Could not create any session, possibilities exhausted");
None
}
}
@ -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),

View File

@ -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,
}
}
}

View File

@ -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;