use super::{Capabilities, Graphics, Protocol};
impl Graphics {
pub fn detect() -> Option<Graphics> {
Capabilities::detect().best()
}
pub fn detect_for(destination: &impl std::io::IsTerminal) -> Option<Graphics> {
Capabilities::detect_for(destination).best()
}
}
pub(crate) fn named_protocols(value: &str) -> Option<Vec<Protocol>> {
Some(match value.trim().to_ascii_lowercase().as_str() {
"kitty" => vec![Protocol::Kitty],
"sixel" => vec![Protocol::Sixel],
"iterm2" | "iterm" => vec![Protocol::ITerm2],
"none" | "cells" => Vec::new(),
_ => return None,
})
}
pub(crate) fn sniff(variable: &impl Fn(&str) -> Option<String>) -> Vec<Protocol> {
if let Some(named) = variable("MALEVICH_GRAPHICS").and_then(|value| named_protocols(&value)) {
return named;
}
if variable("TMUX").is_some() {
return Vec::new();
}
let term = variable("TERM").unwrap_or_default();
if term == "dumb" || term == "unknown" || term.starts_with("screen") || term.starts_with("tmux")
{
return Vec::new();
}
if variable("KITTY_WINDOW_ID").is_some()
|| variable("KITTY_PID").is_some()
|| variable("GHOSTTY_BIN_DIR").is_some()
|| term.contains("kitty")
|| term.contains("ghostty")
{
return vec![Protocol::Kitty];
}
let program = variable("TERM_PROGRAM")
.unwrap_or_default()
.to_ascii_lowercase();
if program.contains("ghostty") {
return vec![Protocol::Kitty];
}
let iterm = program.contains("iterm")
|| variable("LC_TERMINAL")
.is_some_and(|value| value.to_ascii_lowercase().contains("iterm"));
if iterm || program.contains("wezterm") || variable("WEZTERM_EXECUTABLE").is_some() {
return vec![Protocol::ITerm2, Protocol::Sixel];
}
if program.contains("rio") {
return vec![Protocol::ITerm2, Protocol::Sixel];
}
if program.contains("warp") {
return vec![Protocol::ITerm2];
}
if term.contains("foot") || variable("MLTERM").is_some() {
return vec![Protocol::Sixel];
}
if variable("KONSOLE_VERSION").is_some_and(|v| v.parse::<u32>().is_ok_and(|v| v >= 220400)) {
return vec![Protocol::Sixel];
}
if variable("WT_SESSION").is_some() {
return vec![Protocol::Sixel];
}
Vec::new()
}
#[cfg(unix)]
pub(crate) fn cell_size() -> Option<(u16, u16)> {
use std::os::fd::AsFd;
fn from(fd: impl AsFd) -> Option<(u16, u16)> {
let size = rustix::termios::tcgetwinsize(fd).ok()?;
if size.ws_col == 0 || size.ws_row == 0 {
return None;
}
let cell = (size.ws_xpixel / size.ws_col, size.ws_ypixel / size.ws_row);
(cell.0 >= 2 && cell.1 >= 4).then_some(cell)
}
std::fs::File::open("/dev/tty")
.ok()
.and_then(from)
.or_else(|| from(std::io::stdout()))
}
#[cfg(not(unix))]
pub(crate) fn cell_size() -> Option<(u16, u16)> {
None
}
#[cfg(test)]
#[path = "tests/detect_tests.rs"]
mod tests;