Add documentation
This commit is contained in:
parent
0350dca972
commit
13be5b1634
|
@ -375,6 +375,7 @@ impl DrmBackend {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the crtc id used by this backend
|
||||||
pub fn crtc(&self) -> crtc::Handle {
|
pub fn crtc(&self) -> crtc::Handle {
|
||||||
self.crtc
|
self.crtc
|
||||||
}
|
}
|
||||||
|
|
|
@ -457,14 +457,20 @@ impl<B: From<DrmBackend> + Borrow<DrmBackend> + 'static> DrmDevice<B> {
|
||||||
Ok(self.backends.get(&crtc).unwrap())
|
Ok(self.backends.get(&crtc).unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the current backend for a given crtc if any
|
||||||
pub fn backend_for_crtc(&self, crtc: &crtc::Handle) -> Option<&StateToken<B>> {
|
pub fn backend_for_crtc(&self, crtc: &crtc::Handle) -> Option<&StateToken<B>> {
|
||||||
self.backends.get(crtc)
|
self.backends.get(crtc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get all belonging backends
|
||||||
pub fn current_backends(&self) -> Vec<&StateToken<B>> {
|
pub fn current_backends(&self) -> Vec<&StateToken<B>> {
|
||||||
self.backends.values().collect()
|
self.backends.values().collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Destroy the backend using a given crtc if any
|
||||||
|
///
|
||||||
|
/// ## Panics
|
||||||
|
/// Panics if the backend is already borrowed from the state
|
||||||
pub fn destroy_backend<'a, S>(&mut self, state: S, crtc: &crtc::Handle)
|
pub fn destroy_backend<'a, S>(&mut self, state: S, crtc: &crtc::Handle)
|
||||||
where
|
where
|
||||||
S: Into<StateProxy<'a>>
|
S: Into<StateProxy<'a>>
|
||||||
|
@ -474,6 +480,11 @@ impl<B: From<DrmBackend> + Borrow<DrmBackend> + 'static> DrmDevice<B> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Close the device
|
||||||
|
///
|
||||||
|
/// ## Warning
|
||||||
|
/// Never call this function if the device is managed by another backend e.g. the `UdevBackend`.
|
||||||
|
/// Only use this function for manually initialized devices.
|
||||||
pub fn close(self) -> NixResult<()> {
|
pub fn close(self) -> NixResult<()> {
|
||||||
let fd = self.as_raw_fd();
|
let fd = self.as_raw_fd();
|
||||||
mem::drop(self);
|
mem::drop(self);
|
||||||
|
@ -517,12 +528,20 @@ pub trait DrmHandler<B: Borrow<DrmBackend> + 'static> {
|
||||||
///
|
///
|
||||||
/// The `id` argument is the `Id` of the `DrmBackend` that finished rendering,
|
/// The `id` argument is the `Id` of the `DrmBackend` that finished rendering,
|
||||||
/// check using `DrmBackend::is`.
|
/// check using `DrmBackend::is`.
|
||||||
|
///
|
||||||
|
/// ## Panics
|
||||||
|
/// The device is already borrowed from the given `state`. Borrowing it again will panic
|
||||||
|
/// and is not necessary as it is already provided via the `device` parameter.
|
||||||
fn ready<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &mut DrmDevice<B>, backend: &StateToken<B>,
|
fn ready<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &mut DrmDevice<B>, backend: &StateToken<B>,
|
||||||
crtc: crtc::Handle, frame: u32, duration: Duration);
|
crtc: crtc::Handle, frame: u32, duration: Duration);
|
||||||
/// The `DrmDevice` has thrown an error.
|
/// The `DrmDevice` has thrown an error.
|
||||||
///
|
///
|
||||||
/// The related backends are most likely *not* usable anymore and
|
/// The related backends are most likely *not* usable anymore and
|
||||||
/// the whole stack has to be recreated.
|
/// the whole stack has to be recreated..
|
||||||
|
///
|
||||||
|
/// ## Panics
|
||||||
|
/// The device is already borrowed from the given `state`. Borrowing it again will panic
|
||||||
|
/// and is not necessary as it is already provided via the `device` parameter.
|
||||||
fn error<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &mut DrmDevice<B>, error: DrmError);
|
fn error<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &mut DrmDevice<B>, error: DrmError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -610,6 +629,5 @@ impl<B: Borrow<DrmBackend> + 'static> SessionObserver for StateToken<DrmDevice<B
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -566,6 +566,8 @@ impl SessionObserver for libinput::Libinput {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Wrapper for types implementing the `Session` trait to provide
|
||||||
|
/// a `LibinputInterface` implementation.
|
||||||
pub struct LibinputSessionInterface<S: Session>(S);
|
pub struct LibinputSessionInterface<S: Session>(S);
|
||||||
|
|
||||||
impl<S: Session> From<S> for LibinputSessionInterface<S> {
|
impl<S: Session> From<S> for LibinputSessionInterface<S> {
|
||||||
|
@ -585,6 +587,10 @@ impl<S: Session> libinput::LibinputInterface for LibinputSessionInterface<S> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Binds a `LibinputInputBackend` to a given `EventLoop`.
|
||||||
|
///
|
||||||
|
/// Automatically feeds the backend with incoming events without any manual calls to
|
||||||
|
/// `dispatch_new_events`. Should be used to achieve the smallest possible latency.
|
||||||
pub fn libinput_bind(backend: LibinputInputBackend, evlh: &mut EventLoopHandle)
|
pub fn libinput_bind(backend: LibinputInputBackend, evlh: &mut EventLoopHandle)
|
||||||
-> IoResult<FdEventSource<LibinputInputBackend>>
|
-> IoResult<FdEventSource<LibinputInputBackend>>
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,3 +1,51 @@
|
||||||
|
//!
|
||||||
|
//! Implementation of the `Session` trait through the legacy vt kernel interface.
|
||||||
|
//!
|
||||||
|
//! This requires write permissions for the given tty device and any devices opened through this
|
||||||
|
//! interface. This means it will almost certainly require root permissions and not allow to run
|
||||||
|
//! the compositor as an unpriviledged user. Use this session type *only* as a fallback or for testing,
|
||||||
|
//! if anything better is available.
|
||||||
|
//!
|
||||||
|
//! ## How to use it
|
||||||
|
//!
|
||||||
|
//! ### Initialization
|
||||||
|
//!
|
||||||
|
//! To initialize the session you may pass the path to any tty device, that shall be used.
|
||||||
|
//! If no path is given the tty used to start this compositor (if any) will be used.
|
||||||
|
//! A new session and its notifier will be returned.
|
||||||
|
//!
|
||||||
|
//! ```rust,no_run
|
||||||
|
//! extern crate smithay;
|
||||||
|
//!
|
||||||
|
//! use smithay::backend::session::direct::DirectSession;
|
||||||
|
//!
|
||||||
|
//! # fn main() {
|
||||||
|
//! let (session, mut notifier) = DirectSession::new(None, None).unwrap();
|
||||||
|
//! # }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! ### 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.
|
||||||
|
//!
|
||||||
|
//! Examples for those are e.g. the `LibinputInputBackend` (its context might be initialized through a
|
||||||
|
//! `Session` via the `LibinputSessionInterface`) or the `UdevBackend`.
|
||||||
|
//!
|
||||||
|
//! In case you want to pass the same `Session` to multiple objects, `Session` is implement for
|
||||||
|
//! every `Rc<RefCell<Session>>` or `Arc<Mutex<Session>>`.
|
||||||
|
//!
|
||||||
|
//! ### 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 `DirectSession::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::io::Result as IoResult;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::os::unix::io::RawFd;
|
use std::os::unix::io::RawFd;
|
||||||
|
@ -17,6 +65,7 @@ use libudev::Context;
|
||||||
|
|
||||||
use super::{AsErrno, Session, SessionNotifier, SessionObserver};
|
use super::{AsErrno, Session, SessionNotifier, SessionObserver};
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
mod tty {
|
mod tty {
|
||||||
ioctl!(bad read kd_get_mode with 0x4B3B; i16);
|
ioctl!(bad read kd_get_mode with 0x4B3B; i16);
|
||||||
ioctl!(bad write_int kd_set_mode with 0x4B3A);
|
ioctl!(bad write_int kd_set_mode with 0x4B3A);
|
||||||
|
@ -59,60 +108,17 @@ mod tty {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// on freebsd and dragonfly
|
|
||||||
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
|
|
||||||
const DRM_MAJOR: u64 = 145;
|
|
||||||
|
|
||||||
// on netbsd
|
|
||||||
#[cfg(target_os = "netbsd")]
|
|
||||||
const DRM_MAJOR: u64 = 34;
|
|
||||||
|
|
||||||
// on openbsd (32 & 64 bit)
|
|
||||||
#[cfg(all(target_os = "openbsd", target_pointer_width = "32"))]
|
|
||||||
const DRM_MAJOR: u64 = 88;
|
|
||||||
#[cfg(all(target_os = "openbsd", target_pointer_width = "64"))]
|
|
||||||
const DRM_MAJOR: u64 = 87;
|
|
||||||
|
|
||||||
// on linux/android
|
|
||||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
|
||||||
const DRM_MAJOR: u64 = 226;
|
|
||||||
|
|
||||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
const TTY_MAJOR: u64 = 4;
|
const TTY_MAJOR: u64 = 4;
|
||||||
|
|
||||||
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
||||||
const TTY_MAJOR: u64 = 0;
|
const TTY_MAJOR: u64 = 0;
|
||||||
|
|
||||||
#[cfg(not(feature = "backend_session_udev"))]
|
|
||||||
fn is_drm_device(dev: dev_t, _path: &Path) -> bool {
|
|
||||||
major(dev) == DRM_MAJOR
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "backend_session_udev"))]
|
#[cfg(not(feature = "backend_session_udev"))]
|
||||||
fn is_tty_device(dev: dev_t, _path: Option<&Path>) -> bool {
|
fn is_tty_device(dev: dev_t, _path: Option<&Path>) -> bool {
|
||||||
major(dev) == TTY_MAJOR
|
major(dev) == TTY_MAJOR
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "backend_session_udev")]
|
|
||||||
fn is_drm_device(dev: dev_t, path: &Path) -> bool {
|
|
||||||
let udev = match Context::new() {
|
|
||||||
Ok(context) => context,
|
|
||||||
Err(_) => return major(dev) == DRM_MAJOR,
|
|
||||||
};
|
|
||||||
|
|
||||||
let device = match udev.device_from_syspath(path) {
|
|
||||||
Ok(device) => device,
|
|
||||||
Err(_) => return major(dev) == DRM_MAJOR,
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(subsystem) = device.subsystem() {
|
|
||||||
subsystem == "drm"
|
|
||||||
} else {
|
|
||||||
major(dev) == DRM_MAJOR
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "backend_session_udev")]
|
#[cfg(feature = "backend_session_udev")]
|
||||||
fn is_tty_device(dev: dev_t, path: Option<&Path>) -> bool {
|
fn is_tty_device(dev: dev_t, path: Option<&Path>) -> bool {
|
||||||
match path {
|
match path {
|
||||||
|
@ -138,6 +144,7 @@ fn is_tty_device(dev: dev_t, path: Option<&Path>) -> bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `Session` via the virtual terminal direct kernel interface
|
||||||
pub struct DirectSession {
|
pub struct DirectSession {
|
||||||
tty: RawFd,
|
tty: RawFd,
|
||||||
active: Arc<AtomicBool>,
|
active: Arc<AtomicBool>,
|
||||||
|
@ -146,6 +153,7 @@ pub struct DirectSession {
|
||||||
logger: ::slog::Logger,
|
logger: ::slog::Logger,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `SessionNotifier` via the virtual terminal direct kernel interface
|
||||||
pub struct DirectSessionNotifier {
|
pub struct DirectSessionNotifier {
|
||||||
tty: RawFd,
|
tty: RawFd,
|
||||||
active: Arc<AtomicBool>,
|
active: Arc<AtomicBool>,
|
||||||
|
@ -155,6 +163,9 @@ pub struct DirectSessionNotifier {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DirectSession {
|
impl DirectSession {
|
||||||
|
/// Tries to creates 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)>
|
pub fn new<L>(tty: Option<&Path>, logger: L) -> Result<(DirectSession, DirectSessionNotifier)>
|
||||||
where
|
where
|
||||||
L: Into<Option<::slog::Logger>>
|
L: Into<Option<::slog::Logger>>
|
||||||
|
@ -244,6 +255,11 @@ impl DirectSession {
|
||||||
|
|
||||||
Ok((vt_num, old_keyboard_mode, signal))
|
Ok((vt_num, old_keyboard_mode, signal))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the number of the virtual terminal used by this session
|
||||||
|
pub fn vt(&self) -> i32 {
|
||||||
|
self.vt
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Session for DirectSession {
|
impl Session for DirectSession {
|
||||||
|
@ -323,6 +339,11 @@ impl SessionNotifier for DirectSessionNotifier {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Bind a `DirectSessionNotifier` to an `EventLoop`.
|
||||||
|
///
|
||||||
|
/// Allows the `DirectSessionNotifier` to listen for the incoming signals signalling the session state.
|
||||||
|
/// If you don't use this function `DirectSessionNotifier` will not correctly tell you the current
|
||||||
|
/// session state.
|
||||||
pub fn direct_session_bind<L>(notifier: DirectSessionNotifier, evlh: &mut EventLoopHandle, _logger: L)
|
pub fn direct_session_bind<L>(notifier: DirectSessionNotifier, evlh: &mut EventLoopHandle, _logger: L)
|
||||||
-> IoResult<SignalEventSource<DirectSessionNotifier>>
|
-> IoResult<SignalEventSource<DirectSessionNotifier>>
|
||||||
where
|
where
|
||||||
|
@ -332,6 +353,7 @@ where
|
||||||
|
|
||||||
evlh.add_signal_event_source(|evlh, notifier, _| {
|
evlh.add_signal_event_source(|evlh, notifier, _| {
|
||||||
if notifier.is_active() {
|
if notifier.is_active() {
|
||||||
|
info!(notifier.logger, "Session shall become inactive");
|
||||||
for signal in &mut notifier.signals {
|
for signal in &mut notifier.signals {
|
||||||
if let &mut Some(ref mut signal) = signal {signal.pause(&mut evlh.state().as_proxy()); }
|
if let &mut Some(ref mut signal) = signal {signal.pause(&mut evlh.state().as_proxy()); }
|
||||||
}
|
}
|
||||||
|
@ -339,7 +361,9 @@ where
|
||||||
unsafe {
|
unsafe {
|
||||||
tty::vt_rel_disp(notifier.tty, 1).expect("Unable to release tty lock");
|
tty::vt_rel_disp(notifier.tty, 1).expect("Unable to release tty lock");
|
||||||
}
|
}
|
||||||
|
debug!(notifier.logger, "Session is now inactive");
|
||||||
} else {
|
} else {
|
||||||
|
debug!(notifier.logger, "Session will become active again");
|
||||||
unsafe {
|
unsafe {
|
||||||
tty::vt_rel_disp(notifier.tty, tty::VT_ACKACQ).expect("Unable to acquire tty lock");
|
tty::vt_rel_disp(notifier.tty, tty::VT_ACKACQ).expect("Unable to acquire tty lock");
|
||||||
}
|
}
|
||||||
|
@ -347,6 +371,7 @@ where
|
||||||
if let &mut Some(ref mut signal) = signal { signal.activate(&mut evlh.state().as_proxy()); }
|
if let &mut Some(ref mut signal) = signal { signal.activate(&mut evlh.state().as_proxy()); }
|
||||||
}
|
}
|
||||||
notifier.active.store(true, Ordering::SeqCst);
|
notifier.active.store(true, Ordering::SeqCst);
|
||||||
|
info!(notifier.logger, "Session is now active again");
|
||||||
}
|
}
|
||||||
}, notifier, signal)
|
}, notifier, signal)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
use dbus::{BusType, Connection as DbusConnection};
|
||||||
|
use systemd::login as logind;
|
||||||
|
|
||||||
|
pub struct LogindSession {
|
||||||
|
dbus: DbusConnection,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Session for LogindSession {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LogindSession {
|
||||||
|
pub fn new() -> Result<LogindSession> {
|
||||||
|
let session = logind::get_session(None)?;
|
||||||
|
let vt = logind::get_vt(&session)?;
|
||||||
|
let seat = logind::get_seat(&session)?;
|
||||||
|
|
||||||
|
let dbus = DbusConnection::get_private(BusType::System)?;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
error_chain! {
|
||||||
|
errors {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,15 @@
|
||||||
|
//!
|
||||||
|
//! Abstraction of different session apis.
|
||||||
|
//!
|
||||||
|
//! Sessions provide a way for multiple graphical systems to run in parallel by providing
|
||||||
|
//! mechanisms to switch between and handle device access and permissions for every running
|
||||||
|
//! instance.
|
||||||
|
//!
|
||||||
|
//! They are crutial to allow unpriviledged processes to use graphical or input devices.
|
||||||
|
//!
|
||||||
|
//! The following mechanisms are currently provided:
|
||||||
|
//! - direct - legacy tty / virtual terminal kernel api
|
||||||
|
//!
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
@ -6,28 +18,62 @@ use std::os::unix::io::RawFd;
|
||||||
use nix::fcntl::OFlag;
|
use nix::fcntl::OFlag;
|
||||||
use wayland_server::StateProxy;
|
use wayland_server::StateProxy;
|
||||||
|
|
||||||
|
/// General session interface.
|
||||||
|
///
|
||||||
|
/// Provides a way to open and close devices and change the active vt.
|
||||||
pub trait Session {
|
pub trait Session {
|
||||||
|
/// Error type of the implementation
|
||||||
type Error: AsErrno;
|
type Error: AsErrno;
|
||||||
|
|
||||||
|
/// Opens a device at the given `path` with the given flags.
|
||||||
|
///
|
||||||
|
/// Returns a raw file descriptor
|
||||||
fn open(&mut self, path: &Path, flags: OFlag) -> Result<RawFd, Self::Error>;
|
fn open(&mut self, path: &Path, flags: OFlag) -> Result<RawFd, Self::Error>;
|
||||||
|
/// Close a previously opened file descriptor
|
||||||
fn close(&mut self, fd: RawFd) -> Result<(), Self::Error>;
|
fn close(&mut self, fd: RawFd) -> Result<(), Self::Error>;
|
||||||
|
|
||||||
|
/// Change the currently active virtual terminal
|
||||||
fn change_vt(&mut self, vt: i32) -> Result<(), Self::Error>;
|
fn change_vt(&mut self, vt: i32) -> Result<(), Self::Error>;
|
||||||
|
|
||||||
|
/// Check if this session is currently active
|
||||||
fn is_active(&self) -> bool;
|
fn is_active(&self) -> bool;
|
||||||
|
/// Which seat this session is on
|
||||||
fn seat(&self) -> String;
|
fn seat(&self) -> String;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Interface for registering for notifications for a given 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 {
|
pub trait SessionNotifier {
|
||||||
|
/// Registers a given `SessionObserver`.
|
||||||
|
///
|
||||||
|
/// Returns an id of the inserted observer, can be used to remove it again.
|
||||||
fn register<S: SessionObserver + 'static>(&mut self, signal: S) -> usize;
|
fn register<S: SessionObserver + 'static>(&mut self, signal: S) -> usize;
|
||||||
|
/// Removes an observer by its given id from `SessionNotifier::register`.
|
||||||
fn unregister(&mut self, signal: usize);
|
fn unregister(&mut self, signal: usize);
|
||||||
|
|
||||||
|
/// Check if this session is currently active
|
||||||
fn is_active(&self) -> bool;
|
fn is_active(&self) -> bool;
|
||||||
|
/// Which seat this session is on
|
||||||
fn seat(&self) -> &str;
|
fn seat(&self) -> &str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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 {
|
pub trait SessionObserver {
|
||||||
|
/// Session is about to be paused.
|
||||||
|
///
|
||||||
|
/// In case the implementor is a `StateToken` the state of the `EventLoop`
|
||||||
|
/// is provided via a `StateProxy`.
|
||||||
fn pause<'a>(&mut self, state: &mut StateProxy<'a>);
|
fn pause<'a>(&mut self, state: &mut StateProxy<'a>);
|
||||||
|
/// Session got active again
|
||||||
|
///
|
||||||
|
/// In case the implementor is a `StateToken` the state of the `EventLoop`
|
||||||
|
/// is provided via a `StateProxy`.
|
||||||
fn activate<'a>(&mut self, state: &mut StateProxy<'a>);
|
fn activate<'a>(&mut self, state: &mut StateProxy<'a>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,7 +137,9 @@ impl<S: Session> Session for Arc<Mutex<S>> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Allows errors to be described by an error number
|
||||||
pub trait AsErrno: ::std::fmt::Debug {
|
pub trait AsErrno: ::std::fmt::Debug {
|
||||||
|
/// Returns the error number representing this error if any
|
||||||
fn as_errno(&self) -> Option<i32>;
|
fn as_errno(&self) -> Option<i32>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,14 @@
|
||||||
|
//!
|
||||||
|
//! Provides `udev` related functionality for automated device scanning.
|
||||||
|
//!
|
||||||
|
//! This module mainly provides the `UdevBackend`, which constantly monitors available drm devices
|
||||||
|
//! and notifies a user supplied `UdevHandler` of any changes.
|
||||||
|
//!
|
||||||
|
//! Additionally this contains some utility functions related to scanning.
|
||||||
|
//!
|
||||||
|
//! See also `examples/udev.rs` for pure hardware backed example of a compositor utilizing this
|
||||||
|
//! backend.
|
||||||
|
|
||||||
use libudev::{Context, MonitorBuilder, MonitorSocket, Event, EventType, Enumerator, Result as UdevResult};
|
use libudev::{Context, MonitorBuilder, MonitorSocket, Event, EventType, Enumerator, Result as UdevResult};
|
||||||
use nix::fcntl;
|
use nix::fcntl;
|
||||||
use nix::sys::stat::{dev_t, fstat};
|
use nix::sys::stat::{dev_t, fstat};
|
||||||
|
@ -14,6 +25,11 @@ use wayland_server::sources::{FdEventSource, FdEventSourceImpl, FdInterest};
|
||||||
use ::backend::drm::{DrmDevice, DrmBackend, DrmHandler, drm_device_bind};
|
use ::backend::drm::{DrmDevice, DrmBackend, DrmHandler, drm_device_bind};
|
||||||
use ::backend::session::{Session, SessionObserver};
|
use ::backend::session::{Session, SessionObserver};
|
||||||
|
|
||||||
|
/// Graphical backend that monitors available drm devices.
|
||||||
|
///
|
||||||
|
/// Provides a way to automatically initialize a `DrmDevice` for available gpus and notifies the
|
||||||
|
/// given handler of any changes. Can be used to provide hot-plug functionality for gpus and
|
||||||
|
/// attached monitors.
|
||||||
pub struct UdevBackend<B: Borrow<DrmBackend> + 'static, H: DrmHandler<B> + 'static, S: Session + 'static, T: UdevHandler<B, H> + 'static> {
|
pub struct UdevBackend<B: Borrow<DrmBackend> + 'static, H: DrmHandler<B> + 'static, S: Session + 'static, T: UdevHandler<B, H> + 'static> {
|
||||||
devices: HashMap<dev_t, (StateToken<DrmDevice<B>>, FdEventSource<(StateToken<DrmDevice<B>>, H)>)>,
|
devices: HashMap<dev_t, (StateToken<DrmDevice<B>>, FdEventSource<(StateToken<DrmDevice<B>>, H)>)>,
|
||||||
monitor: MonitorSocket,
|
monitor: MonitorSocket,
|
||||||
|
@ -23,6 +39,14 @@ pub struct UdevBackend<B: Borrow<DrmBackend> + 'static, H: DrmHandler<B> + 'stat
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B: From<DrmBackend> + Borrow<DrmBackend> + 'static, H: DrmHandler<B> + 'static, S: Session + 'static, T: UdevHandler<B, H> + 'static> UdevBackend<B, H, S, T> {
|
impl<B: From<DrmBackend> + Borrow<DrmBackend> + 'static, H: DrmHandler<B> + 'static, S: Session + 'static, T: UdevHandler<B, H> + 'static> UdevBackend<B, H, S, T> {
|
||||||
|
/// Creates a new `UdevBackend` and adds it to the given `EventLoop`'s state.
|
||||||
|
///
|
||||||
|
/// ## Arguments
|
||||||
|
/// `evlh` - An event loop to use for binding `DrmDevices`
|
||||||
|
/// `context` - An initialized udev context
|
||||||
|
/// `session` - A session used to open and close devices as they become available
|
||||||
|
/// `handler` - User-provided handler to respond to any detected changes
|
||||||
|
/// `logger` - slog Logger to be used by the backend and its `DrmDevices`.
|
||||||
pub fn new<'a, L>(mut evlh: &mut EventLoopHandle,
|
pub fn new<'a, L>(mut evlh: &mut EventLoopHandle,
|
||||||
context: &Context,
|
context: &Context,
|
||||||
mut session: S,
|
mut session: S,
|
||||||
|
@ -113,6 +137,14 @@ impl<B: From<DrmBackend> + Borrow<DrmBackend> + 'static, H: DrmHandler<B> + 'sta
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Closes the udev backend and frees all remaining open devices.
|
||||||
|
///
|
||||||
|
/// Needs to be called after the `FdEventSource` was removed and the backend was removed from
|
||||||
|
/// the `EventLoop`'s `State`.
|
||||||
|
///
|
||||||
|
/// ## Panics
|
||||||
|
/// The given state might be passed to the registered `UdevHandler::device_removed` callback.
|
||||||
|
/// Make sure not to borrow any tokens twice.
|
||||||
pub fn close<'a, ST: Into<StateProxy<'a>>>(mut self, state: ST) {
|
pub fn close<'a, ST: Into<StateProxy<'a>>>(mut self, state: ST) {
|
||||||
let mut state = state.into();
|
let mut state = state.into();
|
||||||
for (_, (mut device, event_source)) in self.devices.drain() {
|
for (_, (mut device, event_source)) in self.devices.drain() {
|
||||||
|
@ -147,6 +179,10 @@ impl<B: Borrow<DrmBackend> + 'static, H: DrmHandler<B> + 'static, S: Session + '
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Binds a `UdevBackend` to a given `EventLoop`.
|
||||||
|
///
|
||||||
|
/// Allows the backend to recieve kernel events and thus to drive the `UdevHandler`.
|
||||||
|
/// No runtime functionality can be provided without using this function.
|
||||||
pub fn udev_backend_bind<B, S, H, T>(evlh: &mut EventLoopHandle, udev: StateToken<UdevBackend<B, H, S, T>>)
|
pub fn udev_backend_bind<B, S, H, T>(evlh: &mut EventLoopHandle, udev: StateToken<UdevBackend<B, H, S, T>>)
|
||||||
-> IoResult<FdEventSource<StateToken<UdevBackend<B, H, S, T>>>>
|
-> IoResult<FdEventSource<StateToken<UdevBackend<B, H, S, T>>>>
|
||||||
where
|
where
|
||||||
|
@ -275,13 +311,41 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Handler for the `UdevBackend`, allows to open, close and update drm devices as they change during runtime.
|
||||||
pub trait UdevHandler<B: Borrow<DrmBackend> + 'static, H: DrmHandler<B> + 'static> {
|
pub trait UdevHandler<B: Borrow<DrmBackend> + 'static, H: DrmHandler<B> + 'static> {
|
||||||
|
/// Called on initialization for every known device and when a new device is detected.
|
||||||
|
///
|
||||||
|
/// Returning a `DrmHandler` will initialize the device, returning `None` will ignore the device.
|
||||||
|
///
|
||||||
|
/// ## Panics
|
||||||
|
/// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`.
|
||||||
fn device_added<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &mut DrmDevice<B>) -> Option<H>;
|
fn device_added<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &mut DrmDevice<B>) -> Option<H>;
|
||||||
|
/// Called when an open device is changed.
|
||||||
|
///
|
||||||
|
/// This usually indicates that some connectors did become available or were unplugged. The handler
|
||||||
|
/// should scan again for connected monitors and mode switch accordingly.
|
||||||
|
///
|
||||||
|
/// ## Panics
|
||||||
|
/// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`.
|
||||||
fn device_changed<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &StateToken<DrmDevice<B>>);
|
fn device_changed<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &StateToken<DrmDevice<B>>);
|
||||||
|
/// Called when a device was removed.
|
||||||
|
///
|
||||||
|
/// The device will not accept any operations anymore and its file descriptor will be closed once
|
||||||
|
/// this function returns, any open references/tokens to this device need to be released.
|
||||||
|
///
|
||||||
|
/// ## Panics
|
||||||
|
/// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`.
|
||||||
fn device_removed<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &StateToken<DrmDevice<B>>);
|
fn device_removed<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &StateToken<DrmDevice<B>>);
|
||||||
|
/// Called when the udev context has encountered and error.
|
||||||
|
///
|
||||||
|
/// ## Panics
|
||||||
|
/// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`.
|
||||||
fn error<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, error: IoError);
|
fn error<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, error: IoError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the path of the primary gpu device if any
|
||||||
|
///
|
||||||
|
/// Might be used for filtering in `UdevHandler::device_added` or for manual `DrmDevice` initialization
|
||||||
pub fn primary_gpu<S: AsRef<str>>(context: &Context, seat: S) -> UdevResult<Option<PathBuf>> {
|
pub fn primary_gpu<S: AsRef<str>>(context: &Context, seat: S) -> UdevResult<Option<PathBuf>> {
|
||||||
let mut enumerator = Enumerator::new(context)?;
|
let mut enumerator = Enumerator::new(context)?;
|
||||||
enumerator.match_subsystem("drm")?;
|
enumerator.match_subsystem("drm")?;
|
||||||
|
@ -304,6 +368,9 @@ pub fn primary_gpu<S: AsRef<str>>(context: &Context, seat: S) -> UdevResult<Opti
|
||||||
Ok(result.and_then(|device| device.devnode().map(PathBuf::from)))
|
Ok(result.and_then(|device| device.devnode().map(PathBuf::from)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the paths of all available gpu devices
|
||||||
|
///
|
||||||
|
/// Might be used for manual `DrmDevice` initialization
|
||||||
pub fn all_gpus<S: AsRef<str>>(context: &Context, seat: S) -> UdevResult<Vec<PathBuf>> {
|
pub fn all_gpus<S: AsRef<str>>(context: &Context, seat: S) -> UdevResult<Vec<PathBuf>> {
|
||||||
let mut enumerator = Enumerator::new(context)?;
|
let mut enumerator = Enumerator::new(context)?;
|
||||||
enumerator.match_subsystem("drm")?;
|
enumerator.match_subsystem("drm")?;
|
||||||
|
|
Loading…
Reference in New Issue