pub(crate) 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, } impl LaunchConfig { pub fn from_environment() -> Result { 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, graphical_session: bool, ) -> Result { 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, state: &mut Ferese, ) -> Result<(), Box> { match backend { BackendKind::Nested => crate::winit::init(event_loop, state), BackendKind::Drm => direct::init(event_loop, state), } } fn parse_backend(value: &OsString) -> Result, 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::::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")); } }