feat: initialize direct session backend

This commit is contained in:
Samuel Onoja committed 2026-09-24 16:15:11 +01:00
1 parent 1b8ba7570c
commit c50dd98685
4 files changed
+226 -8

No files matched your search

+65
View File
@@ -0,0 +1,65 @@
use std::{error::Error, io};
use smithay::{
backend::{
libinput::{LibinputInputBackend, LibinputSessionInterface},
session::{Event as SessionEvent, Session, libseat::LibSeatSession},
udev::{UdevBackend, UdevEvent},
},
reexports::{calloop::EventLoop, input::Libinput},
};
use crate::Ferese;
pub fn init(event_loop: &mut EventLoop<Ferese>, state: &mut Ferese) -> Result<(), Box<dyn Error>> {
let (session, notifier) = LibSeatSession::new()?;
let seat_name = session.seat();
let udev_backend = UdevBackend::new(&seat_name)?;
let mut libinput_context =
Libinput::new_with_udev::<LibinputSessionInterface<LibSeatSession>>(session.clone().into());
libinput_context
.udev_assign_seat(&seat_name)
.map_err(|()| io::Error::other(format!("failed to assign libinput seat {seat_name}")))?;
let libinput_backend = LibinputInputBackend::new(libinput_context.clone());
state.direct_session = Some(session);
state.session_active = true;
event_loop
.handle()
.insert_source(libinput_backend, |event, _, state| {
state.process_input_event(event);
})?;
event_loop
.handle()
.insert_source(notifier, move |event, _, state| match event {
SessionEvent::PauseSession => {
state.session_active = false;
libinput_context.suspend();
tracing::info!("direct session paused");
}
SessionEvent::ActivateSession => {
if libinput_context.resume().is_err() {
tracing::error!("failed to resume libinput");
}
state.session_active = true;
tracing::info!("direct session activated");
}
})?;
event_loop
.handle()
.insert_source(udev_backend, |event, _, _state| match event {
UdevEvent::Added { device_id, path } => {
tracing::info!(?device_id, ?path, "DRM device added");
}
UdevEvent::Changed { device_id } => {
tracing::debug!(?device_id, "DRM device changed");
}
UdevEvent::Removed { device_id } => {
tracing::info!(?device_id, "DRM device removed");
}
})?;
tracing::info!(seat = %seat_name, "initialized direct session input and device discovery");
Ok(())
}
+143
View File
@@ -0,0 +1,143 @@
mod direct;
use std::ffi::OsString;
use smithay::reexports::calloop::EventLoop;
use crate::Ferese;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum BackendKind {
Nested,
Drm,
}
#[derive(Debug, Eq, PartialEq)]
pub struct LaunchConfig {
pub backend: BackendKind,
pub client: Vec<OsString>,
}
impl LaunchConfig {
pub fn from_environment() -> Result<Self, String> {
let graphical_session =
std::env::var_os("WAYLAND_DISPLAY").is_some() || std::env::var_os("DISPLAY").is_some();
Self::parse(std::env::args_os().skip(1), graphical_session)
}
fn parse(
args: impl IntoIterator<Item = OsString>,
graphical_session: bool,
) -> Result<Self, String> {
let mut args = args.into_iter().peekable();
let mut requested_backend = None;
let mut client = Vec::new();
while let Some(argument) = args.next() {
if argument == "--" {
client.extend(args);
break;
}
if argument == "--backend" {
let value = args
.next()
.ok_or_else(|| "--backend requires auto, nested, or drm".to_owned())?;
requested_backend = Some(parse_backend(&value)?);
continue;
}
if let Some(value) = argument
.to_str()
.and_then(|argument| argument.strip_prefix("--backend="))
{
requested_backend = Some(parse_backend(&OsString::from(value))?);
continue;
}
client.push(argument);
client.extend(args);
break;
}
let backend = requested_backend.flatten().unwrap_or(if graphical_session {
BackendKind::Nested
} else {
BackendKind::Drm
});
Ok(Self { backend, client })
}
}
pub fn init(
backend: BackendKind,
event_loop: &mut EventLoop<Ferese>,
state: &mut Ferese,
) -> Result<(), Box<dyn std::error::Error>> {
match backend {
BackendKind::Nested => crate::winit::init(event_loop, state),
BackendKind::Drm => direct::init(event_loop, state),
}
}
fn parse_backend(value: &OsString) -> Result<Option<BackendKind>, String> {
match value.to_str() {
Some("auto") => Ok(None),
Some("nested") => Ok(Some(BackendKind::Nested)),
Some("drm") => Ok(Some(BackendKind::Drm)),
_ => Err(format!(
"unknown backend {:?}; expected auto, nested, or drm",
value
)),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn auto_selects_nested_inside_a_graphical_session() {
let config = LaunchConfig::parse([OsString::from("foot")], true).unwrap();
assert_eq!(config.backend, BackendKind::Nested);
assert_eq!(config.client, [OsString::from("foot")]);
}
#[test]
fn auto_selects_drm_without_a_graphical_session() {
let config = LaunchConfig::parse(Vec::<OsString>::new(), false).unwrap();
assert_eq!(config.backend, BackendKind::Drm);
assert!(config.client.is_empty());
}
#[test]
fn explicit_backend_and_separator_are_consumed() {
let config = LaunchConfig::parse(
[
OsString::from("--backend=drm"),
OsString::from("--"),
OsString::from("foot"),
OsString::from("--title=Ferese"),
],
true,
)
.unwrap();
assert_eq!(config.backend, BackendKind::Drm);
assert_eq!(
config.client,
[OsString::from("foot"), OsString::from("--title=Ferese")]
);
}
#[test]
fn rejects_an_unknown_backend() {
let error =
LaunchConfig::parse([OsString::from("--backend"), OsString::from("other")], true)
.unwrap_err();
assert!(error.contains("unknown backend"));
}
}