diff options
author | Canop <cano.petrole@gmail.com> | 2022-01-06 08:35:13 +0100 |
---|---|---|
committer | Canop <cano.petrole@gmail.com> | 2022-01-06 08:35:13 +0100 |
commit | 1e3d68f84cc9f10a79905a837c2e46467fa8a952 (patch) | |
tree | a6236b38e8501968ab2726533c7016b666ba3964 /src | |
parent | 6c7f16446fdc81ca4cc5371c5d7a324c952da0d9 (diff) |
Display images in high res on recent WezTerm
More specifically, this commit implements detection
of recent enough WezTerm versions with the $TERM_PROGRAM
and $TERM_PROGRAM_VERSION environment variables.
Fix #473
Diffstat (limited to 'src')
-rw-r--r-- | src/kitty/detect_support.rs | 80 | ||||
-rw-r--r-- | src/kitty/image_renderer.rs | 62 | ||||
-rw-r--r-- | src/kitty/mod.rs | 1 | ||||
-rw-r--r-- | src/skin/skin_entry.rs | 16 | ||||
-rw-r--r-- | src/syntactic/syntactic_view.rs | 14 |
5 files changed, 100 insertions, 73 deletions
diff --git a/src/kitty/detect_support.rs b/src/kitty/detect_support.rs new file mode 100644 index 0000000..db806d0 --- /dev/null +++ b/src/kitty/detect_support.rs @@ -0,0 +1,80 @@ +use { + cli_log::*, + std::{ + env, + }, +}; + +/// Determine whether Kitty's graphics protocol is supported +/// by the terminal running broot. +/// +/// This is called only once, and cached in the KittyManager's +/// MaybeRenderer state +#[allow(unreachable_code)] +pub fn is_kitty_graphics_protocol_supported() -> bool { + debug!("is_kitty_graphics_protocol_supported ?"); + + #[cfg(not(unix))] + { + // because cell_size_in_pixels isn't implemented on Windows + debug!("no kitty support yet on Windows"); + return false; + } + + // we detect Kitty by the $TERM or $TERMINAL env var + for env_var in ["TERM", "TERMINAL"] { + if let Ok(env_val) = env::var(env_var) { + debug!("${} = {:?}", env_var, env_val); + let env_val = env_val.to_ascii_lowercase(); + if env_val.contains("kitty") { + debug!(" -> this terminal seems to be Kitty"); + return true; + } + } + } + + // we detect Wezterm with the $TERM_PROGRAM env var and we + // check its version to be sure it's one with support + if let Ok(term_program) = env::var("TERM_PROGRAM") { + debug!("$TERM_PROGRAM = {:?}", term_program); + if term_program == "WezTerm" { + if let Ok(version) = env::var("TERM_PROGRAM_VERSION") { + debug!("$TERM_PROGRAM_VERSION = {:?}", version); + if &*version < "20220105-201556-91a423da" { + debug!("WezTerm's version predates Kitty Graphics protocol support"); + } else { + debug!("this looks like a compatible version"); + return true; + } + } else { + warn!("$TERM_PROGRAM_VERSION unexpectedly missing"); + } + } + } + + // Checking support with a proper CSI sequence should be the prefered way but + // it doesn't work reliably on wezterm and requires a wait on other terminals. + // As both Kitty and WezTerm set env vars allowing an easy detection, this + // CSI based querying isn't necessary right now. + // This feature is kept gated and should only be tried if other terminals + // appear and can't be detected without CSI sequence. + #[cfg(feature = "kitty-csi-check")] + { + let start = std::time::Instant::now(); + const TIMEOUT_MS: isize = 400; + let s = match xterm_query::query("\x1b_Gi=31,s=1,v=1,a=q,t=d,f=24;AAAA\x1b\\\x1b[c", TIMEOUT_MS) { + Err(e) => { + debug!("xterm querying failed: {}", e); + false + } + Ok(response) => { + response.starts_with("\x1b_Gi=31;OK\x1b") + } + }; + debug!("Xterm querying took {:?}", start.elapsed()); + debug!("kitty protocol support: {:?}", s); + return s; + } + false +} + diff --git a/src/kitty/image_renderer.rs b/src/kitty/image_renderer.rs index 7282c27..58dab57 100644 --- a/src/kitty/image_renderer.rs +++ b/src/kitty/image_renderer.rs @@ -1,4 +1,5 @@ use { + super::detect_support::is_kitty_graphics_protocol_supported, crate::{ display::{ cell_size_in_pixels, @@ -19,14 +20,12 @@ use { RgbaImage, }, std::{ - env, io::{self, Write}, }, tempfile, termimad::Area, }; - /// How to send the image to kitty /// /// Note that I didn't test yet the named shared memory @@ -84,63 +83,13 @@ impl<'i> ImageData<'i> { /// according to kitty's documentation const CHUNK_SIZE: usize = 4096; -/// this is called only once, and cached in the kitty manager's MaybeRenderer state -#[allow(unreachable_code)] -fn is_kitty_graphics_protocol_supported() -> bool { - debug!("is_kitty_graphics_protocol_supported ?"); - - #[cfg(not(unix))] - { - // because cell_size_in_pixels isn't implemented on Windows - debug!("no kitty support yet on Windows"); - return false; - } - - for env_var in ["TERM", "TERMINAL"] { - if let Ok(env_val) = env::var(env_var) { - debug!("{:?} = {:?}", env_var, env_val); - let env_val = env_val.to_ascii_lowercase(); - for name in ["kitty", "wezterm"] { - if env_val.contains(name) { - debug!(" -> env var indicates kitty support"); - return true; - } - } - } - } - - // Checking support with a proper CSI sequence should be the prefered way but - // it doesn't work reliably on wezterm and requires a wait on other terminal. - // Only Kitty does supports it perfectly and it's not even necessary on this - // terminal because we can just check the env var TERM. - #[cfg(feature = "kitty-csi-check")] - { - let start = std::time::Instant::now(); - const TIMEOUT_MS: isize = 400; - let s = match xterm_query::query("\x1b_Gi=31,s=1,v=1,a=q,t=d,f=24;AAAA\x1b\\\x1b[c", TIMEOUT_MS) { - Err(e) => { - debug!("xterm querying failed: {}", e); - false - } - Ok(response) => { - response.starts_with("\x1b_Gi=31;OK\x1b") - } - }; - debug!("Xterm querying took {:?}", start.elapsed()); - debug!("kitty protocol support: {:?}", s); - return s; - } - false -} - fn div_ceil(a: u32, b: u32) -> u32 { a / b + (0 != a % b) as u32 } -/// the image renderer, with knowledge of the -/// console cells dimensions, and built only on Kitty. -/// +/// The image renderer, with knowledge of the console cells +/// dimensions, and built only on a compatible terminal #[derive(Debug)] pub struct KittyImageRenderer { cell_width: u32, @@ -150,7 +99,6 @@ pub struct KittyImageRenderer { } /// An image prepared for a precise area on screen -/// struct KittyImage<'i> { id: usize, data: ImageData<'i>, @@ -176,7 +124,7 @@ impl<'i> KittyImage<'i> { area, } } - /// render the image by sending multiple kitty escape sequence, each + /// Render the image by sending multiple kitty escape sequences, each /// one with part of the image raw data (encoded as base64) fn print_with_chunks( &self, @@ -207,7 +155,7 @@ impl<'i> KittyImage<'i> { } Ok(()) } - /// render the image by writing the raw data in a temporary file + /// Render the image by writing the raw data in a temporary file /// then giving to kitty the path to this file in the payload of /// a unique kitty ecape sequence pub fn print_with_temp_file( diff --git a/src/kitty/mod.rs b/src/kitty/mod.rs index 9e98787..adc9e45 100644 --- a/src/kitty/mod.rs +++ b/src/kitty/mod.rs @@ -1,3 +1,4 @@ +mod detect_support; mod image_renderer; mod image_set; diff --git a/src/skin/skin_entry.rs b/src/skin/skin_entry.rs index 5279107..8079f0e 100644 --- a/src/skin/skin_entry.rs +++ b/src/skin/skin_entry.rs @@ -1,6 +1,6 @@ -/// Manage conversion of a user provided string -/// defining foreground and background colors into -/// a string with TTY colors +//! Manage conversion of a user provided string +//! defining foreground and background colors into +//! a string with TTY colors use { super::*, @@ -14,7 +14,7 @@ use { termimad::CompoundStyle, }; -/// parsed content of a [skin] line of the conf.toml file +/// Parsed content of a [skin] line of the conf.toml file #[derive(Clone, Debug)] pub struct SkinEntry { focused: CompoundStyle, @@ -31,7 +31,7 @@ impl SkinEntry { pub fn get_unfocused(&self) -> &CompoundStyle { self.unfocused.as_ref().unwrap_or(&self.focused) } - /// parse a string representation of a skin entry. + /// Parse a string representation of a skin entry. /// /// The general form is either "<focused>" or "<focused> / <unfocused>": /// It may be just the focused compound_style, or both @@ -44,7 +44,7 @@ impl SkinEntry { let mut parts = s.split('/'); let focused = parse_compound_style(parts.next().unwrap())?; let unfocused = parts.next() - .map(|p| parse_compound_style(p)) + .map(parse_compound_style) .transpose()?; Ok(Self { focused, unfocused }) } @@ -60,8 +60,6 @@ impl<'de> Deserialize<'de> for SkinEntry { } } - - fn parse_attribute(s: &str) -> Result<Attribute, InvalidSkinError> { match s { "bold" => Ok(Bold), @@ -82,7 +80,7 @@ fn parse_attribute(s: &str) -> Result<Attribute, InvalidSkinError> { /// parse a sequence of space separated style attributes fn parse_attributes(s: &str) -> Result<Vec<Attribute>, InvalidSkinError> { - s.split_whitespace().map(|t| parse_attribute(t)).collect() + s.split_whitespace().map(parse_attribute).collect() } fn parse_compound_style(s: &str) -> Result<CompoundStyle, InvalidSkinError> { diff --git a/src/syntactic/syntactic_view.rs b/src/syntactic/syntactic_view.rs index 2e48c7c..c64bbc2 100644 --- a/src/syntactic/syntactic_view.rs +++ b/src/syntactic/syntactic_view.rs @@ -26,7 +26,7 @@ use { termimad::{Area, CropWriter, SPACE_FILLING}, }; -/// a homogeneously colored piece of a line +/// Homogeneously colored piece of a line #[derive(Debug)] pub struct Region { pub fg: Color, @@ -71,7 +71,7 @@ pub struct SyntacticView { impl SyntacticView { - /// return a prepared text view with syntax coloring if possible. + /// Return a prepared text view with syntax coloring if possible. /// May return Ok(None) only when a pattern is given and there /// was an event before the end of filtering. pub fn new( @@ -97,7 +97,7 @@ impl SyntacticView { } } - /// return true when there was no interruption + /// Return true when there was no interruption fn read_lines( &mut self, dam: &mut Dam, @@ -150,7 +150,7 @@ impl SyntacticView { highlighter .highlight(&line, &SYNTAXER.syntax_set) .iter() - .map(|r| Region::from_syntect(r)) + .map(Region::from_syntect) .collect() } else { Vec::new() @@ -172,8 +172,8 @@ impl SyntacticView { Ok(true) } - /// (count of lines which can be seen when scrolling, - /// total count including filtered ones) + /// Give the count of lines which can be seen when scrolling, + /// total count including filtered ones pub fn line_counts(&self) -> (usize, usize) { (self.lines.len(), self.total_lines_count) } @@ -451,7 +451,7 @@ fn is_thumb(y: usize, scrollbar: Option<(u16, u16)>) -> bool { }) } -/// tell whether the character is normal enough to be displayed by the +/// Tell whether the character is normal enough to be displayed by the /// syntactic view (if not we'll use a hex view) fn is_char_printable(c: char) -> bool { // the tab is printable because it's replaced by spaces |