use serde::{Deserialize, Serialize};
use std::{env, fmt, str::FromStr};
const DETECTION_SIGNATURES: &[(&str, Desktop)] = &[
("HYPRLAND_INSTANCE_SIGNATURE", Desktop::Hyprland),
("NIRI_SOCKET", Desktop::Niri),
("LABWC_PID", Desktop::Labwc),
("MANGO_PID", Desktop::Mango),
("GNOME_DESKTOP_SESSION_ID", Desktop::Gnome),
("GNOME_SHELL_SESSION_MODE", Desktop::Gnome),
("XFCE_DESKTOP_SESSION_ID", Desktop::Xfce),
("WAYLAND_DISPLAY", Desktop::Wayland),
];
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Desktop {
Gnome,
Xfce,
Hyprland,
Niri,
Labwc, Mango, Wayland, #[serde(other)]
Openbox,
}
impl FromStr for Desktop {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.to_lowercase();
if s.contains("gnome") {
Ok(Desktop::Gnome)
} else if s.contains("xfce") {
Ok(Desktop::Xfce)
} else if s.contains("hyprland") {
Ok(Desktop::Hyprland)
} else if s.contains("niri") {
Ok(Desktop::Niri)
} else if s.contains("labwc") {
Ok(Desktop::Labwc)
} else if s.contains("mango") {
Ok(Desktop::Mango)
} else if s.contains("wayland") {
Ok(Desktop::Wayland)
} else {
Ok(Desktop::Openbox)
}
}
}
impl fmt::Display for Desktop {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let s = match self {
Desktop::Gnome => "gnome",
Desktop::Xfce => "xfce",
Desktop::Hyprland => "hyprland",
Desktop::Niri => "niri",
Desktop::Labwc => "labwc",
Desktop::Mango => "mango",
Desktop::Wayland => "wayland",
Desktop::Openbox => "openbox",
};
write!(f, "{s}")
}
}
impl Desktop {
pub fn detect() -> Self {
for key in [
"XDG_CURRENT_DESKTOP",
"XDG_SESSION_DESKTOP",
"DESKTOP_SESSION",
] {
if let Ok(val) = env::var(key)
&& let Ok(desktop) = Self::from_str(&val)
&& desktop != Desktop::Openbox
{
return desktop;
}
}
for &(var, desktop) in DETECTION_SIGNATURES {
if env::var(var).is_ok() {
return desktop;
}
}
Desktop::Openbox
}
}
#[cfg(test)]
mod tests_desktop {
use super::*;
#[test]
fn test_from_str_conversions() {
assert_eq!("GNOME".parse::<Desktop>(), Ok(Desktop::Gnome));
assert_eq!("Xfce".parse::<Desktop>(), Ok(Desktop::Xfce));
assert_eq!("ubuntu:gnome".parse::<Desktop>(), Ok(Desktop::Gnome));
assert_eq!(
"Hyprland (Wayland)".parse::<Desktop>(),
Ok(Desktop::Hyprland)
);
assert_eq!("random_wm_name".parse::<Desktop>(), Ok(Desktop::Openbox));
}
#[test]
fn test_display_formatting() {
assert_eq!(Desktop::Gnome.to_string(), "gnome");
assert_eq!(Desktop::Xfce.to_string(), "xfce");
assert_eq!(Desktop::Hyprland.to_string(), "hyprland");
assert_eq!(Desktop::Openbox.to_string(), "openbox");
}
#[test]
fn test_serde_serialization_and_deserialization() {
let serialized = serde_json::to_string(&Desktop::Hyprland).unwrap();
assert_eq!(serialized, "\"hyprland\"");
let deserialized: Desktop = serde_json::from_str("\"xfce\"").unwrap();
assert_eq!(deserialized, Desktop::Xfce);
let unknown: Desktop = serde_json::from_str("\"unknown_compositor\"").unwrap();
assert_eq!(unknown, Desktop::Openbox);
}
}