mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-21 23:01:05 +00:00
fix: adapt to a wider range of terminal cursor management through DECSET and DECRQM requests again
This commit is contained in:
parent
c086f2f34d
commit
f024ce03e7
3 changed files with 48 additions and 52 deletions
|
|
@ -130,7 +130,7 @@ impl Emulator {
|
||||||
RestorePosition
|
RestorePosition
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let resp = futures::executor::block_on(Self::read_until_da1())?;
|
let resp = futures::executor::block_on(Self::read_until_da1());
|
||||||
let names = [
|
let names = [
|
||||||
("kitty", Self::Kitty),
|
("kitty", Self::Kitty),
|
||||||
("Konsole", Self::Konsole),
|
("Konsole", Self::Konsole),
|
||||||
|
|
@ -189,31 +189,31 @@ impl Emulator {
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn read_until_da1() -> Result<String> {
|
pub async fn read_until_da1() -> String {
|
||||||
|
let mut buf: Vec<u8> = Vec::with_capacity(200);
|
||||||
let read = async {
|
let read = async {
|
||||||
let mut stdin = BufReader::new(tokio::io::stdin());
|
let mut stdin = BufReader::new(tokio::io::stdin());
|
||||||
let mut buf: Vec<u8> = Vec::with_capacity(200);
|
|
||||||
loop {
|
loop {
|
||||||
let mut c = [0; 1];
|
let mut c = [0; 1];
|
||||||
if stdin.read(&mut c).await? == 0 {
|
if stdin.read(&mut c).await? == 0 {
|
||||||
bail!("unexpected EOF");
|
bail!("unexpected EOF");
|
||||||
}
|
}
|
||||||
buf.push(c[0]);
|
buf.push(c[0]);
|
||||||
if c[0] != b'c' {
|
if c[0] != b'c' || !buf.contains(&b'\x1b') {
|
||||||
continue;
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(buf)
|
Ok(())
|
||||||
};
|
};
|
||||||
|
|
||||||
let timeout = timeout(Duration::from_secs(10), read).await;
|
match timeout(Duration::from_secs(10), read).await {
|
||||||
if let Err(ref e) = timeout {
|
Err(e) => error!("read_until_da1 timed out: {buf:?}, error: {e:?}"),
|
||||||
error!("read_until_da1: {e:?}");
|
Ok(Err(e)) => error!("read_until_da1 failed: {buf:?}, error: {e:?}"),
|
||||||
|
Ok(Ok(())) => {}
|
||||||
}
|
}
|
||||||
|
String::from_utf8_lossy(&buf).into_owned()
|
||||||
String::from_utf8(timeout??).map_err(Into::into)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,22 +14,18 @@ pub struct Plugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Plugin {
|
impl Plugin {
|
||||||
pub fn fetchers(
|
pub fn fetchers<'a>(
|
||||||
&self,
|
&'a self,
|
||||||
path: &Path,
|
path: &'a Path,
|
||||||
mime: Option<&str>,
|
mime: Option<&'a str>,
|
||||||
f: impl Fn(&str) -> bool + Copy,
|
factor: impl Fn(&str) -> bool + Copy,
|
||||||
) -> Vec<&Fetcher> {
|
) -> impl Iterator<Item = &'a Fetcher> {
|
||||||
let is_dir = mime == Some(MIME_DIR);
|
let is_dir = mime == Some(MIME_DIR);
|
||||||
self
|
self.fetchers.iter().filter(move |&f| {
|
||||||
.fetchers
|
f.if_.as_ref().and_then(|c| c.eval(factor)) != Some(false)
|
||||||
.iter()
|
&& (f.mime.as_ref().zip(mime).map_or(false, |(p, m)| p.match_mime(m))
|
||||||
.filter(|&p| {
|
|| f.name.as_ref().is_some_and(|p| p.match_path(path, is_dir)))
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn preloaders(&self, path: &Path, mime: Option<&str>) -> Vec<&Preloader> {
|
pub fn preloaders(&self, path: &Path, mime: Option<&str>) -> Vec<&Preloader> {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use std::{io::{self, stderr, BufWriter, Stderr}, ops::{Deref, DerefMut}, sync::atomic::{AtomicBool, AtomicU8, Ordering}};
|
use std::{io::{self, stderr, BufWriter, Stderr}, ops::{Deref, DerefMut}, sync::atomic::{AtomicBool, AtomicU8, Ordering}};
|
||||||
|
|
||||||
use anyhow::Result;
|
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 cursor::RestoreCursor;
|
||||||
use ratatui::{backend::CrosstermBackend, buffer::Buffer, layout::Rect, CompletedFrame, Frame, Terminal};
|
use ratatui::{backend::CrosstermBackend, buffer::Buffer, layout::Rect, CompletedFrame, Frame, Terminal};
|
||||||
use yazi_adapter::Emulator;
|
use yazi_adapter::Emulator;
|
||||||
|
|
@ -28,27 +28,24 @@ impl Term {
|
||||||
enable_raw_mode()?;
|
enable_raw_mode()?;
|
||||||
execute!(
|
execute!(
|
||||||
BufWriter::new(stderr()),
|
BufWriter::new(stderr()),
|
||||||
EnterAlternateScreen,
|
|
||||||
EnableBracketedPaste,
|
|
||||||
mouse::SetMouse(true),
|
|
||||||
SavePosition,
|
|
||||||
Print("\x1b[?12$p"), // Request cursor blink status (DECSET)
|
Print("\x1b[?12$p"), // Request cursor blink status (DECSET)
|
||||||
Print("\x1bP$q q\x1b\\"), // Request cursor shape (DECRQM)
|
Print("\x1bP$q q\x1b\\"), // Request cursor shape (DECRQM)
|
||||||
Print("\x1b[?u\x1b[c"), // Request keyboard enhancement flags (CSI u)
|
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()) {
|
let da = futures::executor::block_on(Emulator::read_until_da1());
|
||||||
CSI_U.store(s.contains("\x1b[?0u"), Ordering::Relaxed);
|
CSI_U.store(da.contains("\x1b[?0u"), Ordering::Relaxed);
|
||||||
BLINK.store(s.contains("\x1b[?12;1$y"), Ordering::Relaxed);
|
BLINK.store(da.contains("\x1b[?12;1$y"), Ordering::Relaxed);
|
||||||
SHAPE.store(
|
SHAPE.store(
|
||||||
s.split_once("\x1bP1$r")
|
da.split_once("\x1bP1$r")
|
||||||
.and_then(|(_, s)| s.bytes().next())
|
.and_then(|(_, s)| s.bytes().next())
|
||||||
.filter(|&b| matches!(b, b'0'..=b'6'))
|
.filter(|&b| matches!(b, b'0'..=b'6'))
|
||||||
.map_or(0, |b| b - b'0'),
|
.map_or(u8::MAX, |b| b - b'0'),
|
||||||
Ordering::Relaxed,
|
Ordering::Relaxed,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
if CSI_U.load(Ordering::Relaxed) {
|
if CSI_U.load(Ordering::Relaxed) {
|
||||||
queue!(
|
queue!(
|
||||||
|
|
@ -74,9 +71,9 @@ impl Term {
|
||||||
execute!(
|
execute!(
|
||||||
stderr(),
|
stderr(),
|
||||||
mouse::SetMouse(false),
|
mouse::SetMouse(false),
|
||||||
|
RestoreCursor,
|
||||||
DisableBracketedPaste,
|
DisableBracketedPaste,
|
||||||
LeaveAlternateScreen,
|
LeaveAlternateScreen,
|
||||||
RestoreCursor,
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
self.show_cursor()?;
|
self.show_cursor()?;
|
||||||
|
|
@ -90,11 +87,11 @@ impl Term {
|
||||||
|
|
||||||
execute!(
|
execute!(
|
||||||
stderr(),
|
stderr(),
|
||||||
SetTitle(""),
|
|
||||||
mouse::SetMouse(false),
|
mouse::SetMouse(false),
|
||||||
|
RestoreCursor,
|
||||||
|
SetTitle(""),
|
||||||
DisableBracketedPaste,
|
DisableBracketedPaste,
|
||||||
LeaveAlternateScreen,
|
LeaveAlternateScreen,
|
||||||
RestoreCursor,
|
|
||||||
crossterm::cursor::Show
|
crossterm::cursor::Show
|
||||||
)
|
)
|
||||||
.ok();
|
.ok();
|
||||||
|
|
@ -219,17 +216,20 @@ mod cursor {
|
||||||
|
|
||||||
impl crossterm::Command for RestoreCursor {
|
impl crossterm::Command for RestoreCursor {
|
||||||
fn write_ansi(&self, f: &mut impl std::fmt::Write) -> std::fmt::Result {
|
fn write_ansi(&self, f: &mut impl std::fmt::Write) -> std::fmt::Result {
|
||||||
let shape = SHAPE.load(Ordering::Relaxed).max(1);
|
let (shape, shape_blink) = match SHAPE.load(Ordering::Relaxed) {
|
||||||
let blink = BLINK.load(Ordering::Relaxed) ^ (shape & 1 == 0);
|
u8::MAX => (0, false),
|
||||||
|
n => ((n.max(1) + 1) / 2, n.max(1) & 1 == 1),
|
||||||
|
};
|
||||||
|
|
||||||
Ok(match (shape + 1) / 2 {
|
let blink = BLINK.load(Ordering::Relaxed) ^ shape_blink;
|
||||||
1 if blink => SetCursorStyle::BlinkingBlock.write_ansi(f)?,
|
Ok(match shape {
|
||||||
1 if !blink => SetCursorStyle::SteadyBlock.write_ansi(f)?,
|
|
||||||
2 if blink => SetCursorStyle::BlinkingUnderScore.write_ansi(f)?,
|
2 if blink => SetCursorStyle::BlinkingUnderScore.write_ansi(f)?,
|
||||||
2 if !blink => SetCursorStyle::SteadyUnderScore.write_ansi(f)?,
|
2 if !blink => SetCursorStyle::SteadyUnderScore.write_ansi(f)?,
|
||||||
3 if blink => SetCursorStyle::BlinkingBar.write_ansi(f)?,
|
3 if blink => SetCursorStyle::BlinkingBar.write_ansi(f)?,
|
||||||
3 if !blink => SetCursorStyle::SteadyBar.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!(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue