fix: cursor gets out of sync occasionally at image previewing through IIP under tmux (#1070)

This commit is contained in:
三咲雅 · Misaki Masa 2024-05-22 13:10:16 +08:00 committed by GitHub
parent 0ebfacb677
commit f41e3c8d8b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 49 additions and 52 deletions

View File

@ -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 |

View File

@ -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}")?;

View File

@ -155,4 +155,36 @@ impl Emulator {
Ok(Self::Unknown(adapters))
}
pub fn move_lock<F, T>((x, y): (u16, u16), cb: F) -> Result<T>
where
F: FnOnce(&mut std::io::BufWriter<std::io::StderrLock>) -> Result<T>,
{
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
}
}

View File

@ -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}")?;

View File

@ -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}")?;

View File

@ -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)
})

View File

@ -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}")?;

View File

@ -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<F, T>((x, y): (u16, u16), cb: F) -> Result<T>
where
F: FnOnce(&mut BufWriter<StderrLock>) -> Result<T>,
{
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)?) }