use super::{GraphicsMode, capabilities::TerminalCapabilities, image::protocols::kitty::KittyMode};
use std::{env, sync::OnceLock};
use strum::IntoEnumIterator;
static CAPABILITIES: OnceLock<TerminalCapabilities> = OnceLock::new();
#[derive(Debug, strum::EnumIter)]
pub enum TerminalEmulator {
Iterm2,
WezTerm,
Ghostty,
Mintty,
Kitty,
Konsole,
Foot,
Yaft,
Mlterm,
St,
Xterm,
Unknown,
}
impl TerminalEmulator {
pub fn detect() -> Self {
let term = env::var("TERM").unwrap_or_default();
let term_program = env::var("TERM_PROGRAM").unwrap_or_default();
for emulator in Self::iter() {
if emulator.is_detected(&term, &term_program) {
return emulator;
}
}
TerminalEmulator::Unknown
}
pub(crate) fn capabilities() -> TerminalCapabilities {
CAPABILITIES.get_or_init(|| TerminalCapabilities::query().unwrap_or_default()).clone()
}
pub(crate) fn disable_capability_detection() {
CAPABILITIES.get_or_init(TerminalCapabilities::default);
}
pub fn preferred_protocol(&self) -> GraphicsMode {
let capabilities = Self::capabilities();
let modes = [
GraphicsMode::Iterm2,
GraphicsMode::Iterm2Multipart,
GraphicsMode::Kitty { mode: KittyMode::Local },
GraphicsMode::Kitty { mode: KittyMode::Remote },
GraphicsMode::Sixel,
GraphicsMode::AsciiBlocks,
];
for mode in modes {
if self.supports_graphics_mode(&mode, &capabilities) {
return mode;
}
}
unreachable!("ascii blocks is always supported")
}
fn is_detected(&self, term: &str, term_program: &str) -> bool {
match self {
TerminalEmulator::Iterm2 => {
term_program.contains("iTerm") || env::var("LC_TERMINAL").is_ok_and(|c| c.contains("iTerm"))
}
TerminalEmulator::WezTerm => term_program.contains("WezTerm") || env::var("WEZTERM_EXECUTABLE").is_ok(),
TerminalEmulator::Mintty => term_program.contains("mintty"),
TerminalEmulator::Ghostty => term_program.contains("ghostty"),
TerminalEmulator::Kitty => term.contains("kitty"),
TerminalEmulator::Konsole => env::var("KONSOLE_VERSION").is_ok(),
TerminalEmulator::Foot => ["foot", "foot-extra"].contains(&term),
TerminalEmulator::Yaft => term == "yaft-256color",
TerminalEmulator::Mlterm => term == "mlterm",
TerminalEmulator::St => term == "st-256color",
TerminalEmulator::Xterm => ["xterm", "xterm-256color"].contains(&term),
TerminalEmulator::Unknown => true,
}
}
fn supports_graphics_mode(&self, mode: &GraphicsMode, capabilities: &TerminalCapabilities) -> bool {
match (mode, self) {
(GraphicsMode::Kitty { mode, .. }, _) => match mode {
KittyMode::Local => capabilities.kitty_local,
KittyMode::Remote => capabilities.kitty_remote,
},
(GraphicsMode::Iterm2, Self::Iterm2 | Self::WezTerm | Self::Mintty | Self::Konsole) => true,
(GraphicsMode::Iterm2Multipart, Self::Iterm2) => true,
(GraphicsMode::AsciiBlocks, _) => true,
(GraphicsMode::Sixel, Self::Foot | Self::Yaft | Self::Mlterm) => true,
(GraphicsMode::Sixel, Self::St | Self::Xterm | Self::Unknown) => capabilities.sixel,
_ => false,
}
}
}