use crate::detect::{Row, Rows};
pub fn detect() -> Rows {
let raw = desktop_hint();
let Some(raw) = raw else {
return Vec::new();
};
let token = raw.split(':').map(str::trim).rfind(|t| !t.is_empty());
let Some(token) = token else {
return Vec::new();
};
let name = normalize(token);
let value = match version_for(&name) {
Some(v) => format!("{name} {v}"),
None => name,
};
vec![Row::val(value)]
}
fn desktop_hint() -> Option<String> {
for key in [
"XDG_CURRENT_DESKTOP",
"XDG_SESSION_DESKTOP",
"DESKTOP_SESSION",
] {
if let Ok(v) = std::env::var(key) {
let v = v.trim();
if !v.is_empty() {
return Some(v.to_string());
}
}
}
None
}
fn normalize(token: &str) -> String {
let lower = token.to_ascii_lowercase();
let name = if lower.contains("gnome") {
"GNOME"
} else if lower.contains("kde") || lower.contains("plasma") {
"KDE Plasma"
} else if lower.contains("xfce") {
"XFCE"
} else if lower.contains("cinnamon") {
"Cinnamon"
} else if lower.contains("mate") {
"MATE"
} else if lower.contains("lxqt") {
"LXQt"
} else if lower.contains("lxde") {
"LXDE"
} else if lower.contains("budgie") {
"Budgie"
} else if lower.contains("deepin") {
"Deepin"
} else if lower.contains("pantheon") {
"Pantheon"
} else if lower.contains("enlightenment") {
"Enlightenment"
} else if lower.contains("unity") {
"Unity"
} else {
return token.to_string();
};
name.to_string()
}
fn version_for(name: &str) -> Option<String> {
match name {
"GNOME" => {
crate::util::cmd("gnome-shell", &["--version"]).and_then(|s| first_num_token(&s))
}
"KDE Plasma" => match std::env::var("KDE_SESSION_VERSION") {
Ok(v) if !v.trim().is_empty() => Some(v.trim().to_string()),
_ => crate::util::cmd("plasmashell", &["--version"]).and_then(|s| first_num_token(&s)),
},
"XFCE" => {
crate::util::cmd("xfce4-session", &["--version"]).and_then(|s| first_num_token(&s))
}
"MATE" => {
crate::util::cmd("mate-session", &["--version"]).and_then(|s| first_num_token(&s))
}
"Cinnamon" => {
crate::util::cmd("cinnamon", &["--version"]).and_then(|s| first_num_token(&s))
}
_ => None,
}
}
fn first_num_token(s: &str) -> Option<String> {
s.split_whitespace()
.find(|t| t.chars().next().is_some_and(|c| c.is_ascii_digit()))
.map(|t| t.to_string())
}