From f41e3c8d8b8342f311785b690c33d9ae588314c4 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: Wed, 22 May 2024 13:10:16 +0800 Subject: [PATCH] fix: cursor gets out of sync occasionally at image previewing through IIP under tmux (#1070) --- README.md | 2 +- yazi-adaptor/src/chafa.rs | 6 +++--- yazi-adaptor/src/emulator.rs | 32 ++++++++++++++++++++++++++++ yazi-adaptor/src/iterm2.rs | 6 +++--- yazi-adaptor/src/kitty.rs | 6 +++--- yazi-adaptor/src/kitty_old.rs | 5 ++--- yazi-adaptor/src/sixel.rs | 6 +++--- yazi-shared/src/term/cursor.rs | 38 ++-------------------------------- 8 files changed, 49 insertions(+), 52 deletions(-) diff --git a/README.md b/README.md index 9b446df0..9899d057 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ https://github.com/sxyazi/yazi/assets/17523360/92ff23fa-0cd5-4f04-b387-894c12265 | Platform | Protocol | Support | | ----------------- | ----------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | | kitty | [Kitty unicode placeholders](https://sw.kovidgoyal.net/kitty/graphics-protocol/#unicode-placeholders) | ✅ Built-in | -| Konsole | [Kitty old protocol](https://github.com/sxyazi/yazi/blob/main/yazi-adaptor/src/kitty_old.rs) | ✅ Built-in | +| Konsole | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in | | iTerm2 | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in | | WezTerm | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in | | Mintty (Git Bash) | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in | diff --git a/yazi-adaptor/src/chafa.rs b/yazi-adaptor/src/chafa.rs index 77b98727..fc117538 100644 --- a/yazi-adaptor/src/chafa.rs +++ b/yazi-adaptor/src/chafa.rs @@ -6,7 +6,7 @@ use ratatui::layout::Rect; use tokio::process::Command; use yazi_shared::term::Term; -use crate::Adaptor; +use crate::{Adaptor, Emulator}; pub(super) struct Chafa; @@ -54,7 +54,7 @@ impl Chafa { }; Adaptor::shown_store(area); - Term::move_lock((max.x, max.y), |stderr| { + Emulator::move_lock((max.x, max.y), |stderr| { for (i, line) in lines.into_iter().enumerate() { stderr.write_all(line)?; Term::move_to(stderr, max.x, max.y + i as u16 + 1)?; @@ -65,7 +65,7 @@ impl Chafa { pub(super) fn image_erase(area: Rect) -> Result<()> { let s = " ".repeat(area.width as usize); - Term::move_lock((0, 0), |stderr| { + Emulator::move_lock((0, 0), |stderr| { for y in area.top()..area.bottom() { Term::move_to(stderr, area.x, y)?; write!(stderr, "{s}")?; diff --git a/yazi-adaptor/src/emulator.rs b/yazi-adaptor/src/emulator.rs index 3b8bf9ce..f590187d 100644 --- a/yazi-adaptor/src/emulator.rs +++ b/yazi-adaptor/src/emulator.rs @@ -155,4 +155,36 @@ impl Emulator { Ok(Self::Unknown(adapters)) } + + pub fn move_lock((x, y): (u16, u16), cb: F) -> Result + where + F: FnOnce(&mut std::io::BufWriter) -> Result, + { + use std::{io::Write, thread, time::Duration}; + + use crossterm::{cursor::{Hide, MoveTo, RestorePosition, SavePosition, Show}, queue}; + + let mut buf = std::io::BufWriter::new(stderr().lock()); + + // I really don't want to add this, + // But tmux and ConPTY sometimes cause the cursor position to get out of sync. + if *TMUX || cfg!(windows) { + execute!(buf, SavePosition, MoveTo(x, y), Show)?; + execute!(buf, MoveTo(x, y), Show)?; + execute!(buf, MoveTo(x, y), Show)?; + thread::sleep(Duration::from_millis(1)); + } else { + queue!(buf, SavePosition, MoveTo(x, y))?; + } + + let result = cb(&mut buf); + if *TMUX || cfg!(windows) { + queue!(buf, Hide, RestorePosition)?; + } else { + queue!(buf, RestorePosition)?; + } + + buf.flush()?; + result + } } diff --git a/yazi-adaptor/src/iterm2.rs b/yazi-adaptor/src/iterm2.rs index 4c0538f3..34948ea3 100644 --- a/yazi-adaptor/src/iterm2.rs +++ b/yazi-adaptor/src/iterm2.rs @@ -7,7 +7,7 @@ use ratatui::layout::Rect; use yazi_shared::term::Term; use super::image::Image; -use crate::{adaptor::Adaptor, CLOSE, START}; +use crate::{adaptor::Adaptor, Emulator, CLOSE, START}; pub(super) struct Iterm2; @@ -19,7 +19,7 @@ impl Iterm2 { Adaptor::Iterm2.image_hide()?; Adaptor::shown_store(area); - Term::move_lock((max.x, max.y), |stderr| { + Emulator::move_lock((max.x, max.y), |stderr| { stderr.write_all(&b)?; Ok(area) }) @@ -27,7 +27,7 @@ impl Iterm2 { pub(super) fn image_erase(area: Rect) -> Result<()> { let s = " ".repeat(area.width as usize); - Term::move_lock((0, 0), |stderr| { + Emulator::move_lock((0, 0), |stderr| { for y in area.top()..area.bottom() { Term::move_to(stderr, area.x, y)?; write!(stderr, "{s}")?; diff --git a/yazi-adaptor/src/kitty.rs b/yazi-adaptor/src/kitty.rs index 46b6814d..8d056590 100644 --- a/yazi-adaptor/src/kitty.rs +++ b/yazi-adaptor/src/kitty.rs @@ -8,7 +8,7 @@ use ratatui::layout::Rect; use yazi_shared::term::Term; use super::image::Image; -use crate::{adaptor::Adaptor, CLOSE, ESCAPE, START}; +use crate::{adaptor::Adaptor, Emulator, CLOSE, ESCAPE, START}; static DIACRITICS: [char; 297] = [ '\u{0305}', @@ -322,7 +322,7 @@ impl Kitty { Adaptor::Kitty.image_hide()?; Adaptor::shown_store(area); - Term::move_lock((area.x, area.y), |stderr| { + Emulator::move_lock((area.x, area.y), |stderr| { stderr.write_all(&b1)?; stderr.write_all(&b2)?; Ok(area) @@ -331,7 +331,7 @@ impl Kitty { pub(super) fn image_erase(area: Rect) -> Result<()> { let s = " ".repeat(area.width as usize); - Term::move_lock((0, 0), |stderr| { + Emulator::move_lock((0, 0), |stderr| { for y in area.top()..area.bottom() { Term::move_to(stderr, area.x, y)?; write!(stderr, "{s}")?; diff --git a/yazi-adaptor/src/kitty_old.rs b/yazi-adaptor/src/kitty_old.rs index 1e2250e5..432765a7 100644 --- a/yazi-adaptor/src/kitty_old.rs +++ b/yazi-adaptor/src/kitty_old.rs @@ -5,10 +5,9 @@ use anyhow::Result; use base64::{engine::general_purpose, Engine}; use image::DynamicImage; use ratatui::layout::Rect; -use yazi_shared::term::Term; use super::image::Image; -use crate::{adaptor::Adaptor, CLOSE, ESCAPE, START}; +use crate::{adaptor::Adaptor, Emulator, CLOSE, ESCAPE, START}; pub(super) struct KittyOld; @@ -20,7 +19,7 @@ impl KittyOld { Adaptor::KittyOld.image_hide()?; Adaptor::shown_store(area); - Term::move_lock((area.x, area.y), |stderr| { + Emulator::move_lock((area.x, area.y), |stderr| { stderr.write_all(&b)?; Ok(area) }) diff --git a/yazi-adaptor/src/sixel.rs b/yazi-adaptor/src/sixel.rs index 91f6f1fa..993a4e7a 100644 --- a/yazi-adaptor/src/sixel.rs +++ b/yazi-adaptor/src/sixel.rs @@ -7,7 +7,7 @@ use ratatui::layout::Rect; use yazi_config::PREVIEW; use yazi_shared::term::Term; -use crate::{adaptor::Adaptor, Image, CLOSE, ESCAPE, START}; +use crate::{adaptor::Adaptor, Emulator, Image, CLOSE, ESCAPE, START}; pub(super) struct Sixel; @@ -19,7 +19,7 @@ impl Sixel { Adaptor::Sixel.image_hide()?; Adaptor::shown_store(area); - Term::move_lock((area.x, area.y), |stderr| { + Emulator::move_lock((area.x, area.y), |stderr| { stderr.write_all(&b)?; Ok(area) }) @@ -27,7 +27,7 @@ impl Sixel { pub(super) fn image_erase(area: Rect) -> Result<()> { let s = " ".repeat(area.width as usize); - Term::move_lock((0, 0), |stderr| { + Emulator::move_lock((0, 0), |stderr| { for y in area.top()..area.bottom() { Term::move_to(stderr, area.x, y)?; write!(stderr, "{s}")?; diff --git a/yazi-shared/src/term/cursor.rs b/yazi-shared/src/term/cursor.rs index 4cbbd0f2..c381f275 100644 --- a/yazi-shared/src/term/cursor.rs +++ b/yazi-shared/src/term/cursor.rs @@ -1,7 +1,7 @@ -use std::io::{stderr, BufWriter, StderrLock, Write}; +use std::io::{stderr, Write}; use anyhow::Result; -use crossterm::{cursor::{MoveTo, RestorePosition, SavePosition, SetCursorStyle}, queue}; +use crossterm::{cursor::{MoveTo, SetCursorStyle}, queue}; use super::Term; @@ -9,40 +9,6 @@ impl Term { #[inline] pub fn move_to(w: &mut impl Write, x: u16, y: u16) -> Result<()> { Ok(queue!(w, MoveTo(x, y))?) } - // FIXME: remove this function - #[inline] - pub fn move_lock((x, y): (u16, u16), cb: F) -> Result - where - F: FnOnce(&mut BufWriter) -> Result, - { - let mut buf = BufWriter::new(stderr().lock()); - #[cfg(windows)] - { - use std::{thread, time::Duration}; - - use crossterm::cursor::{Hide, Show}; - queue!(buf, SavePosition, MoveTo(x, y), Show)?; - - // I really don't want to add this, - // but on Windows the cursor position will not synchronize in time occasionally - buf.flush()?; - thread::sleep(Duration::from_millis(1)); - - let result = cb(&mut buf); - queue!(buf, Hide, RestorePosition)?; - buf.flush()?; - result - } - #[cfg(unix)] - { - queue!(buf, SavePosition, MoveTo(x, y))?; - let result = cb(&mut buf); - queue!(buf, RestorePosition)?; - buf.flush()?; - result - } - } - #[inline] pub fn set_cursor_block() -> Result<()> { Ok(queue!(stderr(), SetCursorStyle::BlinkingBlock)?) }