From c3446cd94103ef346f77cd492d0f888989a18ea0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20=C2=B7=20Misaki=20Masa?= Date: Tue, 14 Nov 2023 08:46:58 +0800 Subject: [PATCH] feat: new kitty graphics protocol implementation for better compatibility with `tmux` through Unicode placeholders (#365) --- README.md | 2 +- cspell.json | 2 +- yazi-adaptor/src/adaptor.rs | 30 ++- yazi-adaptor/src/iterm2.rs | 3 +- yazi-adaptor/src/kitty.rs | 345 ++++++++++++++++++++++++++++++++- yazi-adaptor/src/kitty_old.rs | 76 ++++++++ yazi-adaptor/src/lib.rs | 15 +- yazi-shared/src/fs.rs | 3 + yazi-shared/src/term/cursor.rs | 7 +- 9 files changed, 452 insertions(+), 31 deletions(-) create mode 100644 yazi-adaptor/src/kitty_old.rs diff --git a/README.md b/README.md index 8d33fdec..7176342b 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ https://github.com/sxyazi/yazi/assets/17523360/92ff23fa-0cd5-4f04-b387-894c12265 | Platform | Protocol | Support | | ----------------- | -------------------------------------------------------------------------------- | --------------------- | -| Kitty | [Terminal graphics protocol](https://sw.kovidgoyal.net/kitty/graphics-protocol/) | ✅ Built-in | +| kitty | [Terminal graphics protocol](https://sw.kovidgoyal.net/kitty/graphics-protocol/) | ✅ Built-in | | WezTerm | [Terminal graphics protocol](https://sw.kovidgoyal.net/kitty/graphics-protocol/) | ✅ Built-in | | Konsole | [Terminal graphics protocol](https://sw.kovidgoyal.net/kitty/graphics-protocol/) | ✅ Built-in | | iTerm2 | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in | diff --git a/cspell.json b/cspell.json index 1086be32..e723e662 100644 --- a/cspell.json +++ b/cspell.json @@ -1 +1 @@ -{"language":"en","flagWords":[],"version":"0.2","words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE"]} \ No newline at end of file +{"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim"],"version":"0.2","language":"en","flagWords":[]} diff --git a/yazi-adaptor/src/adaptor.rs b/yazi-adaptor/src/adaptor.rs index 00f04a88..e3b111bc 100644 --- a/yazi-adaptor/src/adaptor.rs +++ b/yazi-adaptor/src/adaptor.rs @@ -5,9 +5,9 @@ use ratatui::prelude::Rect; use tokio::{fs, sync::mpsc::UnboundedSender}; use tracing::warn; use yazi_config::PREVIEW; -use yazi_shared::RoCell; +use yazi_shared::{env_exists, RoCell}; -use super::{Iterm2, Kitty}; +use super::{Iterm2, Kitty, KittyOld}; use crate::{ueberzug::Ueberzug, Sixel, TMUX}; static IMAGE_SHOWN: AtomicBool = AtomicBool::new(false); @@ -18,6 +18,7 @@ static UEBERZUG: RoCell>>> = RoCe #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub enum Adaptor { Kitty, + KittyOld, Iterm2, Sixel, @@ -39,10 +40,15 @@ enum Emulator { VSCode, Hyper, Mintty, + Neovim, } impl Adaptor { fn emulator() -> Emulator { + if env_exists("NVIM_LOG_FILE") && env_exists("NVIM") { + return Emulator::Neovim; + } + let vars = [ ("KITTY_WINDOW_ID", Emulator::Kitty), ("KONSOLE_VERSION", Emulator::Konsole), @@ -50,7 +56,7 @@ impl Adaptor { ("WEZTERM_EXECUTABLE", Emulator::WezTerm), ("VSCODE_INJECTION", Emulator::VSCode), ]; - match vars.into_iter().find(|v| env::var_os(v.0).is_some_and(|s| !s.is_empty())) { + match vars.into_iter().find(|v| env_exists(v.0)) { Some(var) => return var.1, None => warn!("[Adaptor] No special environment variables detected"), } @@ -80,21 +86,22 @@ impl Adaptor { Emulator::Kitty => vec![Self::Kitty], Emulator::Konsole => vec![Self::Kitty, Self::Iterm2, Self::Sixel], Emulator::Iterm2 => vec![Self::Iterm2, Self::Sixel], - Emulator::WezTerm => vec![Self::Kitty, Self::Iterm2, Self::Sixel], + Emulator::WezTerm => vec![Self::KittyOld, Self::Iterm2, Self::Sixel], Emulator::Foot => vec![Self::Sixel], Emulator::BlackBox => vec![Self::Sixel], Emulator::VSCode => vec![Self::Sixel], Emulator::Hyper => vec![Self::Sixel], Emulator::Mintty => vec![Self::Iterm2], + Emulator::Neovim => vec![], }; #[cfg(windows)] protocols.retain(|p| *p == Self::Iterm2); - if env::var_os("ZELLIJ_SESSION_NAME").is_some_and(|s| !s.is_empty()) { + if env_exists("ZELLIJ_SESSION_NAME") { protocols.retain(|p| *p == Self::Sixel); } if *TMUX && protocols.len() > 1 { - protocols.retain(|p| *p != Self::Kitty); + protocols.retain(|p| *p != Self::KittyOld); } if let Some(p) = protocols.first() { return *p; @@ -105,10 +112,10 @@ impl Adaptor { "wayland" => return Self::Wayland, _ => warn!("[Adaptor] Could not identify XDG_SESSION_TYPE"), } - if env::var_os("WAYLAND_DISPLAY").is_some_and(|s| !s.is_empty()) { + if env_exists("WAYLAND_DISPLAY") { return Self::Wayland; } - if env::var_os("DISPLAY").is_some_and(|s| !s.is_empty()) { + if env_exists("DISPLAY") { return Self::X11; } if std::fs::symlink_metadata("/proc/sys/fs/binfmt_misc/WSLInterop").is_ok() { @@ -145,6 +152,7 @@ impl ToString for Adaptor { fn to_string(&self) -> String { match self { Self::Kitty => "kitty", + Self::KittyOld => "kitty", Self::Iterm2 => "iterm2", Self::Sixel => "sixel", Self::X11 => "x11", @@ -171,6 +179,7 @@ impl Adaptor { match self { Self::Kitty => Kitty::image_show(path, rect).await, + Self::KittyOld => KittyOld::image_show(path, rect).await, Self::Iterm2 => Iterm2::image_show(path, rect).await, Self::Sixel => Sixel::image_show(path, rect).await, _ => Ok(if let Some(tx) = &*UEBERZUG { @@ -185,7 +194,8 @@ impl Adaptor { } match self { - Self::Kitty => Kitty::image_hide(), + Self::Kitty => Kitty::image_hide(rect), + Self::KittyOld => KittyOld::image_hide(), Self::Iterm2 => Iterm2::image_hide(rect), Self::Sixel => Sixel::image_hide(rect), _ => Ok(if let Some(tx) = &*UEBERZUG { @@ -196,6 +206,6 @@ impl Adaptor { #[inline] pub(super) fn needs_ueberzug(self) -> bool { - !matches!(self, Self::Kitty | Self::Iterm2 | Self::Sixel) + !matches!(self, Self::Kitty | Self::KittyOld | Self::Iterm2 | Self::Sixel) } } diff --git a/yazi-adaptor/src/iterm2.rs b/yazi-adaptor/src/iterm2.rs index d419a9bc..81b47a3a 100644 --- a/yazi-adaptor/src/iterm2.rs +++ b/yazi-adaptor/src/iterm2.rs @@ -22,10 +22,11 @@ impl Iterm2 { pub(super) fn image_hide(rect: Rect) -> Result<()> { let stdout = BufWriter::new(stdout().lock()); + let s = " ".repeat(rect.width as usize); Term::move_lock(stdout, (0, 0), |stdout| { for y in rect.top()..rect.bottom() { Term::move_to(stdout, rect.x, y)?; - Term::kill_to_end(stdout)?; + stdout.write_all(s.as_bytes())?; } Ok(()) }) diff --git a/yazi-adaptor/src/kitty.rs b/yazi-adaptor/src/kitty.rs index c66cb35e..6640b957 100644 --- a/yazi-adaptor/src/kitty.rs +++ b/yazi-adaptor/src/kitty.rs @@ -1,4 +1,4 @@ -use std::{io::{stdout, Write}, path::Path}; +use std::{io::{stdout, BufWriter, Write}, path::Path}; use anyhow::Result; use base64::{engine::general_purpose, Engine}; @@ -9,6 +9,306 @@ use yazi_shared::Term; use super::image::Image; use crate::{CLOSE, ESCAPE, START}; +static DIACRITICS: [char; 297] = [ + '\u{0305}', + '\u{030D}', + '\u{030E}', + '\u{0310}', + '\u{0312}', + '\u{033D}', + '\u{033E}', + '\u{033F}', + '\u{0346}', + '\u{034A}', + '\u{034B}', + '\u{034C}', + '\u{0350}', + '\u{0351}', + '\u{0352}', + '\u{0357}', + '\u{035B}', + '\u{0363}', + '\u{0364}', + '\u{0365}', + '\u{0366}', + '\u{0367}', + '\u{0368}', + '\u{0369}', + '\u{036A}', + '\u{036B}', + '\u{036C}', + '\u{036D}', + '\u{036E}', + '\u{036F}', + '\u{0483}', + '\u{0484}', + '\u{0485}', + '\u{0486}', + '\u{0487}', + '\u{0592}', + '\u{0593}', + '\u{0594}', + '\u{0595}', + '\u{0597}', + '\u{0598}', + '\u{0599}', + '\u{059C}', + '\u{059D}', + '\u{059E}', + '\u{059F}', + '\u{05A0}', + '\u{05A1}', + '\u{05A8}', + '\u{05A9}', + '\u{05AB}', + '\u{05AC}', + '\u{05AF}', + '\u{05C4}', + '\u{0610}', + '\u{0611}', + '\u{0612}', + '\u{0613}', + '\u{0614}', + '\u{0615}', + '\u{0616}', + '\u{0617}', + '\u{0657}', + '\u{0658}', + '\u{0659}', + '\u{065A}', + '\u{065B}', + '\u{065D}', + '\u{065E}', + '\u{06D6}', + '\u{06D7}', + '\u{06D8}', + '\u{06D9}', + '\u{06DA}', + '\u{06DB}', + '\u{06DC}', + '\u{06DF}', + '\u{06E0}', + '\u{06E1}', + '\u{06E2}', + '\u{06E4}', + '\u{06E7}', + '\u{06E8}', + '\u{06EB}', + '\u{06EC}', + '\u{0730}', + '\u{0732}', + '\u{0733}', + '\u{0735}', + '\u{0736}', + '\u{073A}', + '\u{073D}', + '\u{073F}', + '\u{0740}', + '\u{0741}', + '\u{0743}', + '\u{0745}', + '\u{0747}', + '\u{0749}', + '\u{074A}', + '\u{07EB}', + '\u{07EC}', + '\u{07ED}', + '\u{07EE}', + '\u{07EF}', + '\u{07F0}', + '\u{07F1}', + '\u{07F3}', + '\u{0816}', + '\u{0817}', + '\u{0818}', + '\u{0819}', + '\u{081B}', + '\u{081C}', + '\u{081D}', + '\u{081E}', + '\u{081F}', + '\u{0820}', + '\u{0821}', + '\u{0822}', + '\u{0823}', + '\u{0825}', + '\u{0826}', + '\u{0827}', + '\u{0829}', + '\u{082A}', + '\u{082B}', + '\u{082C}', + '\u{082D}', + '\u{0951}', + '\u{0953}', + '\u{0954}', + '\u{0F82}', + '\u{0F83}', + '\u{0F86}', + '\u{0F87}', + '\u{135D}', + '\u{135E}', + '\u{135F}', + '\u{17DD}', + '\u{193A}', + '\u{1A17}', + '\u{1A75}', + '\u{1A76}', + '\u{1A77}', + '\u{1A78}', + '\u{1A79}', + '\u{1A7A}', + '\u{1A7B}', + '\u{1A7C}', + '\u{1B6B}', + '\u{1B6D}', + '\u{1B6E}', + '\u{1B6F}', + '\u{1B70}', + '\u{1B71}', + '\u{1B72}', + '\u{1B73}', + '\u{1CD0}', + '\u{1CD1}', + '\u{1CD2}', + '\u{1CDA}', + '\u{1CDB}', + '\u{1CE0}', + '\u{1DC0}', + '\u{1DC1}', + '\u{1DC3}', + '\u{1DC4}', + '\u{1DC5}', + '\u{1DC6}', + '\u{1DC7}', + '\u{1DC8}', + '\u{1DC9}', + '\u{1DCB}', + '\u{1DCC}', + '\u{1DD1}', + '\u{1DD2}', + '\u{1DD3}', + '\u{1DD4}', + '\u{1DD5}', + '\u{1DD6}', + '\u{1DD7}', + '\u{1DD8}', + '\u{1DD9}', + '\u{1DDA}', + '\u{1DDB}', + '\u{1DDC}', + '\u{1DDD}', + '\u{1DDE}', + '\u{1DDF}', + '\u{1DE0}', + '\u{1DE1}', + '\u{1DE2}', + '\u{1DE3}', + '\u{1DE4}', + '\u{1DE5}', + '\u{1DE6}', + '\u{1DFE}', + '\u{20D0}', + '\u{20D1}', + '\u{20D4}', + '\u{20D5}', + '\u{20D6}', + '\u{20D7}', + '\u{20DB}', + '\u{20DC}', + '\u{20E1}', + '\u{20E7}', + '\u{20E9}', + '\u{20F0}', + '\u{2CEF}', + '\u{2CF0}', + '\u{2CF1}', + '\u{2DE0}', + '\u{2DE1}', + '\u{2DE2}', + '\u{2DE3}', + '\u{2DE4}', + '\u{2DE5}', + '\u{2DE6}', + '\u{2DE7}', + '\u{2DE8}', + '\u{2DE9}', + '\u{2DEA}', + '\u{2DEB}', + '\u{2DEC}', + '\u{2DED}', + '\u{2DEE}', + '\u{2DEF}', + '\u{2DF0}', + '\u{2DF1}', + '\u{2DF2}', + '\u{2DF3}', + '\u{2DF4}', + '\u{2DF5}', + '\u{2DF6}', + '\u{2DF7}', + '\u{2DF8}', + '\u{2DF9}', + '\u{2DFA}', + '\u{2DFB}', + '\u{2DFC}', + '\u{2DFD}', + '\u{2DFE}', + '\u{2DFF}', + '\u{A66F}', + '\u{A67C}', + '\u{A67D}', + '\u{A6F0}', + '\u{A6F1}', + '\u{A8E0}', + '\u{A8E1}', + '\u{A8E2}', + '\u{A8E3}', + '\u{A8E4}', + '\u{A8E5}', + '\u{A8E6}', + '\u{A8E7}', + '\u{A8E8}', + '\u{A8E9}', + '\u{A8EA}', + '\u{A8EB}', + '\u{A8EC}', + '\u{A8ED}', + '\u{A8EE}', + '\u{A8EF}', + '\u{A8F0}', + '\u{A8F1}', + '\u{AAB0}', + '\u{AAB2}', + '\u{AAB3}', + '\u{AAB7}', + '\u{AAB8}', + '\u{AABE}', + '\u{AABF}', + '\u{AAC1}', + '\u{FE20}', + '\u{FE21}', + '\u{FE22}', + '\u{FE23}', + '\u{FE24}', + '\u{FE25}', + '\u{FE26}', + '\u{10A0F}', + '\u{10A38}', + '\u{1D185}', + '\u{1D186}', + '\u{1D187}', + '\u{1D188}', + '\u{1D189}', + '\u{1D1AA}', + '\u{1D1AB}', + '\u{1D1AC}', + '\u{1D1AD}', + '\u{1D242}', + '\u{1D243}', + '\u{1D244}', +]; + pub(super) struct Kitty; impl Kitty { @@ -16,16 +316,41 @@ impl Kitty { let img = Image::crop(path, (rect.width, rect.height)).await?; let b = Self::encode(img).await?; - Self::image_hide()?; - Term::move_lock(stdout().lock(), (rect.x, rect.y), |stdout| Ok(stdout.write_all(&b)?)) + Self::image_hide(rect)?; + Term::move_lock(stdout().lock(), (rect.x, rect.y), |stdout| { + stdout.write_all(&b)?; + + let mut buf = String::with_capacity(rect.width as usize * 3 + 20); + for y in 0..rect.height { + Term::move_to(stdout, rect.x, rect.y + y)?; + + buf.clear(); + buf.push_str("\x1b[38;5;1m"); + for x in 0..rect.width { + buf.push('\u{10EEEE}'); + buf.push(*DIACRITICS.get(y as usize).unwrap_or(&DIACRITICS[0])); + buf.push(*DIACRITICS.get(x as usize).unwrap_or(&DIACRITICS[0])); + } + buf.push_str("\x1b[0m"); + stdout.write_all(buf.as_bytes())?; + } + + Ok(()) + }) } - #[inline] - pub(super) fn image_hide() -> Result<()> { - let mut stdout = stdout().lock(); - stdout.write_all(format!("{}_Ga=d,d=A{}\\{}", START, ESCAPE, CLOSE).as_bytes())?; - stdout.flush()?; - Ok(()) + pub(super) fn image_hide(rect: Rect) -> Result<()> { + let stdout = BufWriter::new(stdout().lock()); + let s = " ".repeat(rect.width as usize); + Term::move_lock(stdout, (0, 0), |stdout| { + for y in rect.top()..rect.bottom() { + Term::move_to(stdout, rect.x, y)?; + stdout.write_all(s.as_bytes())?; + } + + stdout.write_all(format!("{}_Gq=1,a=d,d=A{}\\{}", START, ESCAPE, CLOSE).as_bytes())?; + Ok(()) + }) } async fn encode(img: DynamicImage) -> Result> { @@ -37,7 +362,7 @@ impl Kitty { if let Some(first) = it.next() { write!( buf, - "{}_Ga=T,f={},s={},v={},m={};{}{}\\{}", + "{}_Gq=1,a=T,i=1,C=1,U=1,f={},s={},v={},m={};{}{}\\{}", START, format, size.0, diff --git a/yazi-adaptor/src/kitty_old.rs b/yazi-adaptor/src/kitty_old.rs new file mode 100644 index 00000000..5cfe1bbc --- /dev/null +++ b/yazi-adaptor/src/kitty_old.rs @@ -0,0 +1,76 @@ +use std::{io::{stdout, Write}, path::Path}; + +use anyhow::Result; +use base64::{engine::general_purpose, Engine}; +use image::DynamicImage; +use ratatui::prelude::Rect; +use yazi_shared::Term; + +use super::image::Image; +use crate::{CLOSE, ESCAPE, START}; + +pub(super) struct KittyOld; + +impl KittyOld { + pub(super) async fn image_show(path: &Path, rect: Rect) -> Result<()> { + let img = Image::crop(path, (rect.width, rect.height)).await?; + let b = Self::encode(img).await?; + + Self::image_hide()?; + Term::move_lock(stdout().lock(), (rect.x, rect.y), |stdout| Ok(stdout.write_all(&b)?)) + } + + #[inline] + pub(super) fn image_hide() -> Result<()> { + let mut stdout = stdout().lock(); + stdout.write_all(format!("{}_Gq=1,a=d,d=A{}\\{}", START, ESCAPE, CLOSE).as_bytes())?; + stdout.flush()?; + Ok(()) + } + + async fn encode(img: DynamicImage) -> Result> { + fn output(raw: &[u8], format: u8, size: (u32, u32)) -> Result> { + let b64 = general_purpose::STANDARD.encode(raw).chars().collect::>(); + + let mut it = b64.chunks(4096).peekable(); + let mut buf = Vec::with_capacity(b64.len() + it.len() * 50); + if let Some(first) = it.next() { + write!( + buf, + "{}_Gq=1,a=T,z=-1,C=1,f={},s={},v={},m={};{}{}\\{}", + START, + format, + size.0, + size.1, + it.peek().is_some() as u8, + first.iter().collect::(), + ESCAPE, + CLOSE + )?; + } + + while let Some(chunk) = it.next() { + write!( + buf, + "{}_Gm={};{}{}\\{}", + START, + it.peek().is_some() as u8, + chunk.iter().collect::(), + ESCAPE, + CLOSE + )?; + } + + buf.write_all(CLOSE.as_bytes())?; + Ok(buf) + } + + let size = (img.width(), img.height()); + tokio::task::spawn_blocking(move || match img { + DynamicImage::ImageRgb8(v) => output(v.as_raw(), 24, size), + DynamicImage::ImageRgba8(v) => output(v.as_raw(), 32, size), + v => output(v.to_rgb8().as_raw(), 24, size), + }) + .await? + } +} diff --git a/yazi-adaptor/src/lib.rs b/yazi-adaptor/src/lib.rs index 668edb1d..de261e47 100644 --- a/yazi-adaptor/src/lib.rs +++ b/yazi-adaptor/src/lib.rs @@ -4,14 +4,16 @@ mod adaptor; mod image; mod iterm2; mod kitty; +mod kitty_old; mod sixel; mod ueberzug; use adaptor::*; use iterm2::*; use kitty::*; +use kitty_old::*; use sixel::*; -use yazi_shared::RoCell; +use yazi_shared::{env_exists, RoCell}; pub use crate::image::*; @@ -24,11 +26,20 @@ static START: RoCell<&'static str> = RoCell::new(); static CLOSE: RoCell<&'static str> = RoCell::new(); pub fn init() { - TMUX.init(std::env::var_os("TMUX").is_some()); + TMUX.init(env_exists("TMUX")); START.init(if *TMUX { "\x1bPtmux;\x1b\x1b" } else { "\x1b" }); CLOSE.init(if *TMUX { "\x1b\\" } else { "" }); ESCAPE.init(if *TMUX { "\x1b\x1b" } else { "\x1b" }); ADAPTOR.init(Adaptor::detect()); ADAPTOR.start(); + + if *TMUX { + _ = std::process::Command::new("tmux") + .args(["set", "-p", "allow-passthrough", "on"]) + .stdin(std::process::Stdio::null()) + .stdout(std::process::Stdio::null()) + .stderr(std::process::Stdio::null()) + .spawn(); + } } diff --git a/yazi-shared/src/fs.rs b/yazi-shared/src/fs.rs index cfd1f6d7..72e01be0 100644 --- a/yazi-shared/src/fs.rs +++ b/yazi-shared/src/fs.rs @@ -3,6 +3,9 @@ use std::{collections::VecDeque, path::{Path, PathBuf}}; use anyhow::Result; use tokio::{fs, io, select, sync::{mpsc, oneshot}, time}; +#[inline] +pub fn env_exists(name: &str) -> bool { std::env::var_os(name).is_some_and(|s| !s.is_empty()) } + pub async fn calculate_size(path: &Path) -> u64 { let mut total = 0; let mut stack = VecDeque::from([path.to_path_buf()]); diff --git a/yazi-shared/src/term/cursor.rs b/yazi-shared/src/term/cursor.rs index 821f5f70..ba1237a2 100644 --- a/yazi-shared/src/term/cursor.rs +++ b/yazi-shared/src/term/cursor.rs @@ -1,16 +1,11 @@ use std::io::{stdout, Write}; use anyhow::Result; -use crossterm::{cursor::{MoveTo, RestorePosition, SavePosition, SetCursorStyle}, execute, queue, terminal::{Clear, ClearType}}; +use crossterm::{cursor::{MoveTo, RestorePosition, SavePosition, SetCursorStyle}, execute, queue}; use crate::Term; impl Term { - #[inline] - pub fn kill_to_end(stdout: &mut impl Write) -> Result<()> { - Ok(queue!(stdout, Clear(ClearType::UntilNewLine))?) - } - #[inline] pub fn move_to(stdout: &mut impl Write, x: u16, y: u16) -> Result<()> { Ok(queue!(stdout, MoveTo(x, y))?)