From f024ce03e7b97f55f3bfad9da4b33458cea765d5 Mon Sep 17 00:00:00 2001 From: sxyazi Date: Fri, 26 Jul 2024 09:15:20 +0800 Subject: [PATCH] fix: adapt to a wider range of terminal cursor management through DECSET and DECRQM requests again --- yazi-adapter/src/emulator.rs | 22 +++++++------- yazi-config/src/plugin/plugin.rs | 26 +++++++--------- yazi-fm/src/term.rs | 52 ++++++++++++++++---------------- 3 files changed, 48 insertions(+), 52 deletions(-) diff --git a/yazi-adapter/src/emulator.rs b/yazi-adapter/src/emulator.rs index 832233bf..73908dc8 100644 --- a/yazi-adapter/src/emulator.rs +++ b/yazi-adapter/src/emulator.rs @@ -130,7 +130,7 @@ impl Emulator { RestorePosition )?; - let resp = futures::executor::block_on(Self::read_until_da1())?; + let resp = futures::executor::block_on(Self::read_until_da1()); let names = [ ("kitty", Self::Kitty), ("Konsole", Self::Konsole), @@ -189,31 +189,31 @@ impl Emulator { result } - pub async fn read_until_da1() -> Result { + pub async fn read_until_da1() -> String { + let mut buf: Vec = Vec::with_capacity(200); let read = async { let mut stdin = BufReader::new(tokio::io::stdin()); - let mut buf: Vec = Vec::with_capacity(200); loop { let mut c = [0; 1]; if stdin.read(&mut c).await? == 0 { bail!("unexpected EOF"); } buf.push(c[0]); - if c[0] != b'c' { + if c[0] != b'c' || !buf.contains(&b'\x1b') { continue; } - if buf.rsplitn(2, |&b| b == b'\xb1').next().is_some_and(|s| s.starts_with(b"\x1b[?")) { + if buf.rsplitn(2, |&b| b == b'\x1b').next().is_some_and(|s| s.starts_with(b"[?")) { break; } } - Ok(buf) + Ok(()) }; - let timeout = timeout(Duration::from_secs(10), read).await; - if let Err(ref e) = timeout { - error!("read_until_da1: {e:?}"); + match timeout(Duration::from_secs(10), read).await { + Err(e) => error!("read_until_da1 timed out: {buf:?}, error: {e:?}"), + Ok(Err(e)) => error!("read_until_da1 failed: {buf:?}, error: {e:?}"), + Ok(Ok(())) => {} } - - String::from_utf8(timeout??).map_err(Into::into) + String::from_utf8_lossy(&buf).into_owned() } } diff --git a/yazi-config/src/plugin/plugin.rs b/yazi-config/src/plugin/plugin.rs index 9e51fa0e..7a9b96ff 100644 --- a/yazi-config/src/plugin/plugin.rs +++ b/yazi-config/src/plugin/plugin.rs @@ -14,22 +14,18 @@ pub struct Plugin { } impl Plugin { - pub fn fetchers( - &self, - path: &Path, - mime: Option<&str>, - f: impl Fn(&str) -> bool + Copy, - ) -> Vec<&Fetcher> { + pub fn fetchers<'a>( + &'a self, + path: &'a Path, + mime: Option<&'a str>, + factor: impl Fn(&str) -> bool + Copy, + ) -> impl Iterator { let is_dir = mime == Some(MIME_DIR); - self - .fetchers - .iter() - .filter(|&p| { - p.if_.as_ref().and_then(|c| c.eval(f)) != Some(false) - && (p.mime.as_ref().zip(mime).map_or(false, |(p, m)| p.match_mime(m)) - || p.name.as_ref().is_some_and(|p| p.match_path(path, is_dir))) - }) - .collect() + self.fetchers.iter().filter(move |&f| { + f.if_.as_ref().and_then(|c| c.eval(factor)) != Some(false) + && (f.mime.as_ref().zip(mime).map_or(false, |(p, m)| p.match_mime(m)) + || f.name.as_ref().is_some_and(|p| p.match_path(path, is_dir))) + }) } pub fn preloaders(&self, path: &Path, mime: Option<&str>) -> Vec<&Preloader> { diff --git a/yazi-fm/src/term.rs b/yazi-fm/src/term.rs index 4d59d396..ff5516fb 100644 --- a/yazi-fm/src/term.rs +++ b/yazi-fm/src/term.rs @@ -1,7 +1,7 @@ use std::{io::{self, stderr, BufWriter, Stderr}, ops::{Deref, DerefMut}, sync::atomic::{AtomicBool, AtomicU8, Ordering}}; use anyhow::Result; -use crossterm::{cursor::{RestorePosition, SavePosition}, event::{DisableBracketedPaste, EnableBracketedPaste, KeyboardEnhancementFlags, PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags}, execute, queue, style::Print, terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen, SetTitle}}; +use crossterm::{event::{DisableBracketedPaste, EnableBracketedPaste, KeyboardEnhancementFlags, PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags}, execute, queue, style::Print, terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen, SetTitle}}; use cursor::RestoreCursor; use ratatui::{backend::CrosstermBackend, buffer::Buffer, layout::Rect, CompletedFrame, Frame, Terminal}; use yazi_adapter::Emulator; @@ -28,27 +28,24 @@ impl Term { enable_raw_mode()?; execute!( BufWriter::new(stderr()), - EnterAlternateScreen, - EnableBracketedPaste, - mouse::SetMouse(true), - SavePosition, Print("\x1b[?12$p"), // Request cursor blink status (DECSET) Print("\x1bP$q q\x1b\\"), // Request cursor shape (DECRQM) Print("\x1b[?u\x1b[c"), // Request keyboard enhancement flags (CSI u) - RestorePosition + EnterAlternateScreen, + EnableBracketedPaste, + mouse::SetMouse(true), )?; - if let Ok(s) = futures::executor::block_on(Emulator::read_until_da1()) { - CSI_U.store(s.contains("\x1b[?0u"), Ordering::Relaxed); - BLINK.store(s.contains("\x1b[?12;1$y"), Ordering::Relaxed); - SHAPE.store( - s.split_once("\x1bP1$r") - .and_then(|(_, s)| s.bytes().next()) - .filter(|&b| matches!(b, b'0'..=b'6')) - .map_or(0, |b| b - b'0'), - Ordering::Relaxed, - ); - } + let da = futures::executor::block_on(Emulator::read_until_da1()); + CSI_U.store(da.contains("\x1b[?0u"), Ordering::Relaxed); + BLINK.store(da.contains("\x1b[?12;1$y"), Ordering::Relaxed); + SHAPE.store( + da.split_once("\x1bP1$r") + .and_then(|(_, s)| s.bytes().next()) + .filter(|&b| matches!(b, b'0'..=b'6')) + .map_or(u8::MAX, |b| b - b'0'), + Ordering::Relaxed, + ); if CSI_U.load(Ordering::Relaxed) { queue!( @@ -74,9 +71,9 @@ impl Term { execute!( stderr(), mouse::SetMouse(false), + RestoreCursor, DisableBracketedPaste, LeaveAlternateScreen, - RestoreCursor, )?; self.show_cursor()?; @@ -90,11 +87,11 @@ impl Term { execute!( stderr(), - SetTitle(""), mouse::SetMouse(false), + RestoreCursor, + SetTitle(""), DisableBracketedPaste, LeaveAlternateScreen, - RestoreCursor, crossterm::cursor::Show ) .ok(); @@ -219,17 +216,20 @@ mod cursor { impl crossterm::Command for RestoreCursor { fn write_ansi(&self, f: &mut impl std::fmt::Write) -> std::fmt::Result { - let shape = SHAPE.load(Ordering::Relaxed).max(1); - let blink = BLINK.load(Ordering::Relaxed) ^ (shape & 1 == 0); + let (shape, shape_blink) = match SHAPE.load(Ordering::Relaxed) { + u8::MAX => (0, false), + n => ((n.max(1) + 1) / 2, n.max(1) & 1 == 1), + }; - Ok(match (shape + 1) / 2 { - 1 if blink => SetCursorStyle::BlinkingBlock.write_ansi(f)?, - 1 if !blink => SetCursorStyle::SteadyBlock.write_ansi(f)?, + let blink = BLINK.load(Ordering::Relaxed) ^ shape_blink; + Ok(match shape { 2 if blink => SetCursorStyle::BlinkingUnderScore.write_ansi(f)?, 2 if !blink => SetCursorStyle::SteadyUnderScore.write_ansi(f)?, 3 if blink => SetCursorStyle::BlinkingBar.write_ansi(f)?, 3 if !blink => SetCursorStyle::SteadyBar.write_ansi(f)?, - _ => tracing::error!("Terminal didn't respond to the cursor shape request: {shape}"), + _ if blink => SetCursorStyle::DefaultUserShape.write_ansi(f)?, + _ if !blink => SetCursorStyle::SteadyBlock.write_ansi(f)?, + _ => unreachable!(), }) }