Skip to content

Commit 9d3c0c7

Browse files
authored
feat: fallback to CSI 16 t for certain terminals that do not support TIOCGWINSZ (#2004)
1 parent 11cf555 commit 9d3c0c7

File tree

5 files changed

+65
-38
lines changed

5 files changed

+65
-38
lines changed

.github/workflows/draft.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ on:
44
push:
55
branches: [main]
66
tags: ["v[0-9]+.[0-9]+.[0-9]+"]
7-
schedule:
8-
- cron: "0 */6 * * *"
97
workflow_dispatch:
108

119
env:

Cargo.lock

Lines changed: 20 additions & 20 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ strip = true
1111

1212
[workspace.dependencies]
1313
ansi-to-tui = "7.0.0"
14-
anyhow = "1.0.93"
14+
anyhow = "1.0.94"
1515
base64 = "0.22.1"
1616
bitflags = "2.6.0"
17-
clap = { version = "4.5.21", features = [ "derive" ] }
17+
clap = { version = "4.5.23", features = [ "derive" ] }
1818
crossterm = { version = "0.28.1", features = [ "event-stream" ] }
1919
dirs = "5.0.1"
2020
futures = "0.3.31"
@@ -29,9 +29,9 @@ scopeguard = "1.2.0"
2929
serde = { version = "1.0.215", features = [ "derive" ] }
3030
serde_json = "1.0.133"
3131
shell-words = "1.1.0"
32-
tokio = { version = "1.41.1", features = [ "full" ] }
33-
tokio-stream = "0.1.16"
34-
tokio-util = "0.7.12"
32+
tokio = { version = "1.42.0", features = [ "full" ] }
33+
tokio-stream = "0.1.17"
34+
tokio-util = "0.7.13"
3535
tracing = { version = "0.1.41", features = [ "max_level_debug", "release_max_level_debug" ] }
3636
unicode-width = "0.2.0"
3737
uzers = "0.12.1"

yazi-adapter/src/dimension.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ use std::mem;
22

33
use crossterm::terminal::WindowSize;
44

5+
use crate::EMULATOR;
6+
57
pub struct Dimension;
68

79
impl Dimension {
@@ -27,9 +29,10 @@ impl Dimension {
2729
#[inline]
2830
pub fn ratio() -> Option<(f64, f64)> {
2931
let s = Self::available();
30-
if s.width == 0 || s.height == 0 {
31-
return None;
32-
}
33-
Some((f64::from(s.width) / f64::from(s.columns), f64::from(s.height) / f64::from(s.rows)))
32+
Some(if s.width == 0 || s.height == 0 {
33+
(EMULATOR.cell_size?.0 as f64, EMULATOR.cell_size?.1 as f64)
34+
} else {
35+
(f64::from(s.width) / f64::from(s.columns), f64::from(s.height) / f64::from(s.rows))
36+
})
3437
}
3538
}

yazi-adapter/src/emulator.rs

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,21 @@ use crate::{Adapter, Brand, Mux, TMUX, Unknown};
1111

1212
#[derive(Clone, Copy, Debug)]
1313
pub struct Emulator {
14-
pub kind: Either<Brand, Unknown>,
15-
pub light: bool,
14+
pub kind: Either<Brand, Unknown>,
15+
pub light: bool,
16+
pub cell_size: Option<(u16, u16)>,
1617
}
1718

1819
impl Default for Emulator {
19-
fn default() -> Self { Self { kind: Either::Right(Unknown::default()), light: false } }
20+
fn default() -> Self {
21+
Self { kind: Either::Right(Unknown::default()), light: false, cell_size: None }
22+
}
2023
}
2124

2225
impl Emulator {
2326
pub fn detect() -> Self {
2427
if let Some(brand) = Brand::from_env() {
25-
Self { kind: Either::Left(brand), light: Self::detect_base().unwrap_or_default().light }
28+
Self { kind: Either::Left(brand), ..Self::detect_base().unwrap_or_default() }
2629
} else {
2730
Self::detect_full().unwrap_or_default()
2831
}
@@ -37,6 +40,7 @@ impl Emulator {
3740
SavePosition,
3841
Print(Mux::csi("\x1b_Gi=31,s=1,v=1,a=q,t=d,f=24;AAAA\x1b\\")), // Detect KGP
3942
Print(Mux::csi("\x1b[>q")), // Request terminal version
43+
Print("\x1b[16t"), // Request cell size
4044
Print("\x1b]11;?\x07"), // Request background color
4145
Print(Mux::csi("\x1b[0c")), // Request device attributes
4246
RestorePosition
@@ -52,7 +56,11 @@ impl Emulator {
5256
})
5357
};
5458

55-
Ok(Self { kind, light: Self::light_bg(&resp).unwrap_or_default() })
59+
Ok(Self {
60+
kind,
61+
light: Self::light_bg(&resp).unwrap_or_default(),
62+
cell_size: Self::cell_size(&resp),
63+
})
5664
}
5765

5866
pub fn adapters(self) -> &'static [Adapter] {
@@ -117,7 +125,7 @@ impl Emulator {
117125
match timeout(Duration::from_secs(10), read).await {
118126
Err(e) => error!("read_until_da1 timed out: {buf:?}, error: {e:?}"),
119127
Ok(Err(e)) => error!("read_until_da1 failed: {buf:?}, error: {e:?}"),
120-
Ok(Ok(())) => {}
128+
Ok(Ok(())) => debug!("read_until_da1: {buf:?}"),
121129
}
122130
String::from_utf8_lossy(&buf).into_owned()
123131
}
@@ -128,12 +136,30 @@ impl Emulator {
128136

129137
execute!(
130138
LineWriter::new(stderr()),
139+
Print("\x1b[16t"), // Request cell size
131140
Print("\x1b]11;?\x07"), // Request background color
132141
Print(Mux::csi("\x1b[0c")), // Request device attributes
133142
)?;
134143

135144
let resp = futures::executor::block_on(Self::read_until_da1());
136-
Ok(Self { light: Self::light_bg(&resp).unwrap_or_default(), ..Default::default() })
145+
Ok(Self {
146+
light: Self::light_bg(&resp).unwrap_or_default(),
147+
cell_size: Self::cell_size(&resp),
148+
..Default::default()
149+
})
150+
}
151+
152+
fn cell_size(resp: &str) -> Option<(u16, u16)> {
153+
let b = resp.split_at(resp.find("\x1b[6;")? + 4).1.as_bytes();
154+
155+
let h: Vec<_> = b.iter().copied().take_while(|&c| c.is_ascii_digit()).collect();
156+
b.get(h.len()).filter(|&&c| c == b';')?;
157+
158+
let w: Vec<_> = b[h.len() + 1..].iter().copied().take_while(|&c| c.is_ascii_digit()).collect();
159+
b.get(h.len() + 1 + w.len()).filter(|&&c| c == b't')?;
160+
161+
let (w, h) = unsafe { (String::from_utf8_unchecked(w), String::from_utf8_unchecked(h)) };
162+
Some((w.parse().ok()?, h.parse().ok()?))
137163
}
138164

139165
fn light_bg(resp: &str) -> Result<bool> {

0 commit comments

Comments
 (0)