diff --git a/Cargo.lock b/Cargo.lock index 44ae6e9..604e158 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -260,6 +260,12 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + [[package]] name = "cpufeatures" version = "0.2.9" @@ -360,12 +366,27 @@ dependencies = [ "phf", ] +[[package]] +name = "dataview" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50eb3a329e19d78c3a3dfa4ec5a51ecb84fa3a20c06edad04be25356018218f9" +dependencies = [ + "derive_pod", +] + [[package]] name = "deltae" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5729f5117e208430e437df2f4843f5e5952997175992d1414f94c57d61e270b4" +[[package]] +name = "derive_pod" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2ea6706d74fca54e15f1d40b5cf7fe7f764aaec61352a9fcec58fe27e042fc8" + [[package]] name = "digest" version = "0.10.7" @@ -899,6 +920,12 @@ dependencies = [ "memoffset 0.9.1", ] +[[package]] +name = "no-std-compat" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c" + [[package]] name = "nom" version = "7.1.3" @@ -915,6 +942,15 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" +[[package]] +name = "ntapi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" +dependencies = [ + "winapi", +] + [[package]] name = "num-bigint" version = "0.4.4" @@ -1029,7 +1065,7 @@ dependencies = [ "libc", "redox_syscall 0.3.5", "smallvec", - "windows-targets", + "windows-targets 0.48.1", ] [[package]] @@ -1038,6 +1074,25 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +[[package]] +name = "pelite" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88dccf4bd32294364aeb7bd55d749604450e9db54605887551f21baea7617685" +dependencies = [ + "dataview", + "libc", + "no-std-compat", + "pelite-macros", + "winapi", +] + +[[package]] +name = "pelite-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a7cf3f8ecebb0f4895f4892a8be0a0dc81b498f9d56735cb769dc31bf00815b" + [[package]] name = "pest" version = "2.7.1" @@ -1278,10 +1333,13 @@ dependencies = [ "dyn-clone", "icy_sixel", "image", + "pelite", "rand", "ratatui", "rustix", + "semver 1.0.23", "serde", + "sysinfo", "termion", "termwiz", ] @@ -1478,6 +1536,12 @@ dependencies = [ "semver-parser", ] +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + [[package]] name = "semver-parser" version = "0.10.2" @@ -1659,6 +1723,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sysinfo" +version = "0.31.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "355dbe4f8799b304b05e1b0f05fc59b2a18d36645cf169607da45bde2f69a1be" +dependencies = [ + "core-foundation-sys", + "libc", + "memchr", + "ntapi", + "windows", +] + [[package]] name = "system-deps" version = "6.2.2" @@ -1750,7 +1827,7 @@ dependencies = [ "pest", "pest_derive", "phf", - "semver", + "semver 0.11.0", "sha2", "signal-hook", "siphasher", @@ -2085,13 +2162,66 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" +dependencies = [ + "windows-core", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-result", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-implement" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.65", +] + +[[package]] +name = "windows-interface" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.65", +] + +[[package]] +name = "windows-result" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.1", ] [[package]] @@ -2100,13 +2230,29 @@ version = "0.48.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -2115,42 +2261,90 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + [[package]] name = "windows_aarch64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + [[package]] name = "windows_i686_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + [[package]] name = "windows_i686_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + [[package]] name = "windows_x86_64_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + [[package]] name = "windows_x86_64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + [[package]] name = "winnow" version = "0.6.8" diff --git a/Cargo.toml b/Cargo.toml index 6720016..fb878a5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,6 +37,11 @@ base64 = { version = "^0.22.1" } rand = { version = "0.8.5" } ratatui = { version = ">=0.23", default-features = false, features = [] } +[target.'cfg(windows)'.dependencies] +semver = "1.0" +pelite = "0.10.0" +sysinfo = { version = "0.31.4", default-features = false, features = ["system"] } + [[bin]] name = "ratatui-image" path = "./src/bin/ratatui-image/main.rs" # cargo readme needs this for some reason diff --git a/examples/async.rs b/examples/async.rs index adda57d..bc2b7f6 100644 --- a/examples/async.rs +++ b/examples/async.rs @@ -42,7 +42,13 @@ fn main() -> Result<(), Box> { let backend = CrosstermBackend::new(stdout); let mut terminal = Terminal::new(backend)?; + #[cfg(all(feature = "rustix", unix))] let mut picker = Picker::from_termios()?; + #[cfg(not(all(feature = "rustix", unix)))] + let mut picker = { + let font_size = (8, 16); + Picker::new(font_size) + }; picker.guess_protocol(); picker.background_color = Some(Rgb::([255, 0, 255])); let dyn_img = image::io::Reader::open("./assets/Ada.png")?.decode()?; diff --git a/examples/demo/main.rs b/examples/demo/main.rs index bc0e704..95e8a71 100644 --- a/examples/demo/main.rs +++ b/examples/demo/main.rs @@ -74,7 +74,13 @@ impl<'a> App<'a> { let ada = "./assets/Ada.png"; let dyn_img = image::io::Reader::open(ada).unwrap().decode().unwrap(); + #[cfg(all(feature = "rustix", unix))] let mut picker = Picker::from_termios().unwrap(); + #[cfg(not(all(feature = "rustix", unix)))] + let mut picker = { + let font_size = (8, 16); + Picker::new(font_size) + }; picker.guess_protocol(); let image_static = picker diff --git a/examples/screenshot.rs b/examples/screenshot.rs index d7c9ca3..825b0dc 100644 --- a/examples/screenshot.rs +++ b/examples/screenshot.rs @@ -37,7 +37,13 @@ fn main() -> Result<(), Box> { let backend = CrosstermBackend::new(stdout); let mut terminal = Terminal::new(backend)?; + #[cfg(all(feature = "rustix", unix))] let mut picker = Picker::from_termios()?; + #[cfg(not(all(feature = "rustix", unix)))] + let mut picker = { + let font_size = (8, 16); + Picker::new(font_size) + }; picker.guess_protocol(); picker.background_color = Some(Rgb::([255, 0, 255])); if false { diff --git a/src/picker.rs b/src/picker.rs index b5fd909..ef462e7 100644 --- a/src/picker.rs +++ b/src/picker.rs @@ -6,8 +6,12 @@ use image::{DynamicImage, Rgb}; use ratatui::layout::Rect; #[cfg(all(feature = "rustix", unix))] use rustix::termios::Winsize; +#[cfg(windows)] +use semver::Version; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; +#[cfg(windows)] +use sysinfo::{ProcessRefreshKind, RefreshKind, System}; use crate::{ protocol::{ @@ -225,6 +229,11 @@ fn guess_protocol() -> (ProtocolType, bool) { return (ProtocolType::Iterm2, is_tmux); } } + // WT_SESSION see https://github.com/microsoft/terminal/pull/897 + #[cfg(windows)] + if env::var("WT_SESSION").is_ok() && support_sixels().is_ok_and(|flag| flag) { + return (ProtocolType::Sixel, is_tmux); + } if is_tmux { let _ = std::process::Command::new("tmux") @@ -252,6 +261,46 @@ fn guess_protocol() -> (ProtocolType, bool) { (ProtocolType::Halfblocks, is_tmux) } +// Since only the latest preview version of Windows Terminal supports Sixels, +// it is necessary to get the version and determine whether it supports Sixels or not. +#[cfg(windows)] +fn support_sixels() -> Result { + let mut support_sixels = false; + // The earliest version of Windows Terminal to support Sixels + // https://github.com/microsoft/terminal/releases/tag/v1.22.2362.0 + let base_version = Version::parse("1.22.2362").unwrap(); + + let system = System::new_with_specifics( + RefreshKind::new().with_processes(ProcessRefreshKind::everything()), + ); + + // TODO Since there is no easy way to get the Windows Terminal version, + // here the file version of the running Windows Terminal process is detected. + // When two or more different versions of Windows Terminal are running at the same time, + // the version detection may not be correct. + // Need to find a better way to implement this. + for process in system.processes_by_exact_name("WindowsTerminal.exe".as_ref()) { + if let Some(exe) = process.exe() { + let file_map = pelite::FileMap::open(exe)?; + let image = pelite::PeFile::from_bytes(file_map.as_ref())?; + let resources = image.resources()?; + + if let Some(fixed) = resources.version_info()?.file_info().fixed { + let version = Version::parse(&format!( + "{}.{}.{}", + fixed.dwFileVersion.Major, fixed.dwFileVersion.Minor, fixed.dwFileVersion.Patch, + ))?; + + if version >= base_version { + support_sixels = true; + } + } + } + } + + Ok(support_sixels) +} + /// Crude guess based on the *existance* of some magic program specific env vars. /// Produces false positives, for example xterm started from kitty inherits KITTY_WINDOW_ID. /// Furthermore, tmux shares env vars from the first session, for example tmux started in xterm @@ -394,7 +443,7 @@ pub fn read_stdin( } } -#[cfg(all(test, feature = "rustix"))] +#[cfg(all(test, feature = "rustix", unix))] mod tests { use std::{ assert_eq,