use std::{env, fmt};
use anyhow::Result;
use clap::ValueEnum;
use log::debug;
use crate::{DesktopFrame, Scene};
mod generic;
mod hyprland;
mod ipc;
mod mango;
mod niri;
mod sway;
pub trait SceneReader {
fn scene(&mut self) -> Result<Scene>;
fn refine_scene(&mut self, _scene: &mut Scene, _frame: &DesktopFrame) -> Result<()> {
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
pub enum Compositor {
Niri,
Sway,
Hyprland,
Mango,
Generic,
}
impl Compositor {
const fn env_var(self) -> Option<&'static str> {
match self {
Self::Niri => Some("NIRI_SOCKET"),
Self::Sway => Some("SWAYSOCK"),
Self::Hyprland => Some("HYPRLAND_INSTANCE_SIGNATURE"),
Self::Mango => Some("MANGO_INSTANCE_SIGNATURE"),
Self::Generic => None,
}
}
#[must_use]
pub fn detect() -> Option<Self> {
for backend in Self::value_variants() {
let Some(env_var) = backend.env_var() else {
continue;
};
if env::var_os(env_var).is_some() {
debug!("env {env_var} matched {backend}");
return Some(*backend);
}
}
env::var("XDG_CURRENT_DESKTOP").ok().and_then(|desktop| {
desktop
.split(':')
.find_map(|name| Self::from_str(name, true).ok())
.filter(|backend| *backend != Self::Generic)
})
}
pub fn connect(self) -> Result<Box<dyn SceneReader>> {
match self {
Self::Niri => Ok(Box::new(niri::Niri::connect()?)),
Self::Sway => Ok(Box::new(sway::Sway::connect()?)),
Self::Hyprland => Ok(Box::new(hyprland::Hyprland::connect()?)),
Self::Mango => Ok(Box::new(mango::Mango::connect()?)),
Self::Generic => {
debug!("using generic Wayland scene discovery");
Ok(Box::new(generic::Generic::connect()?))
}
}
}
}
impl fmt::Display for Compositor {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(self.to_possible_value().unwrap().get_name())
}
}