use crate::detect::{Row, Rows};
const KNOWN_WMS: &[(&str, &str)] = &[
("kwin_wayland", "KWin"),
("kwin_x11", "KWin"),
("Hyprland", "Hyprland"),
("hyprland", "Hyprland"),
("mutter", "Mutter"),
("sway", "Sway"),
("weston", "Weston"),
("river", "River"),
("wayfire", "Wayfire"),
("niri", "niri"),
("labwc", "labwc"),
("i3", "i3"),
("bspwm", "bspwm"),
("openbox", "Openbox"),
("xfwm4", "Xfwm4"),
("dwm", "dwm"),
("awesome", "awesome"),
("qtile", "qtile"),
];
pub fn detect() -> Rows {
let wm = match scan_processes() {
Some(name) => name,
None => match de_to_wm() {
Some(name) => name,
None => return Vec::new(),
},
};
let value = match session_suffix() {
Some(sess) => format!("{wm} ({sess})"),
None => wm.to_string(),
};
vec![Row::val(value)]
}
fn scan_processes() -> Option<&'static str> {
let dir = std::fs::read_dir("/proc").ok()?;
for entry in dir.flatten() {
let name = entry.file_name();
let Some(pid) = name.to_str() else { continue };
if pid.is_empty() || !pid.bytes().all(|b| b.is_ascii_digit()) {
continue;
}
let comm = match crate::util::read_trim(&format!("/proc/{pid}/comm")) {
Some(c) => c,
None => continue,
};
for (proc_name, pretty) in KNOWN_WMS {
if comm == *proc_name {
return Some(pretty);
}
}
}
None
}
fn de_to_wm() -> Option<&'static str> {
let desktop = std::env::var("XDG_CURRENT_DESKTOP").ok()?;
for token in desktop.split(':') {
let de = token.trim().to_ascii_uppercase();
let wm = match de.as_str() {
"GNOME" => "Mutter",
"KDE" => "KWin",
"XFCE" => "Xfwm4",
"LXQT" => "Openbox",
"CINNAMON" | "X-CINNAMON" => "Muffin",
"MATE" => "Marco",
_ => continue,
};
return Some(wm);
}
None
}
fn session_suffix() -> Option<&'static str> {
match std::env::var("XDG_SESSION_TYPE")
.ok()?
.to_ascii_lowercase()
.as_str()
{
"wayland" => Some("Wayland"),
"x11" => Some("X11"),
_ => None,
}
}