mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-21 23:01:05 +00:00
feat: fall back to CSI 16 t when PowerShell OpenSSH returns a fake terminal size (#2636)
This commit is contained in:
parent
510cb14ab6
commit
c38be35250
10 changed files with 78 additions and 73 deletions
|
|
@ -4,36 +4,61 @@ use crossterm::terminal::WindowSize;
|
|||
|
||||
use crate::EMULATOR;
|
||||
|
||||
pub struct Dimension;
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Dimension {
|
||||
pub rows: u16,
|
||||
pub columns: u16,
|
||||
pub width: u16,
|
||||
pub height: u16,
|
||||
}
|
||||
|
||||
impl From<WindowSize> for Dimension {
|
||||
fn from(s: WindowSize) -> Self {
|
||||
Self { rows: s.rows, columns: s.columns, width: s.width, height: s.height }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Dimension> for WindowSize {
|
||||
fn from(d: Dimension) -> Self {
|
||||
Self { rows: d.rows, columns: d.columns, width: d.width, height: d.height }
|
||||
}
|
||||
}
|
||||
|
||||
impl Dimension {
|
||||
pub fn available() -> WindowSize {
|
||||
pub fn available() -> Self {
|
||||
let mut size = WindowSize { rows: 0, columns: 0, width: 0, height: 0 };
|
||||
if let Ok(s) = crossterm::terminal::window_size() {
|
||||
_ = mem::replace(&mut size, s);
|
||||
}
|
||||
|
||||
if size.rows == 0 || size.columns == 0 {
|
||||
if size.columns == 0 || size.rows == 0 {
|
||||
if let Ok((cols, rows)) = crossterm::terminal::size() {
|
||||
size.columns = cols;
|
||||
size.rows = rows;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Use `CSI 14 t` to get the actual size of the terminal
|
||||
// if size.width == 0 || size.height == 0 {}
|
||||
|
||||
size
|
||||
size.into()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn ratio() -> Option<(f64, f64)> {
|
||||
let s = Self::available();
|
||||
Some(if s.width == 0 || s.height == 0 {
|
||||
let s = EMULATOR.get().cell_size?;
|
||||
(s.0 as f64, s.1 as f64)
|
||||
pub fn ratio(self) -> Option<(f64, f64)> {
|
||||
if self.rows == 0 || self.columns == 0 || self.width == 0 || self.height == 0 {
|
||||
None
|
||||
} else {
|
||||
(f64::from(s.width) / f64::from(s.columns), f64::from(s.height) / f64::from(s.rows))
|
||||
Some((self.width as f64 / self.columns as f64, self.height as f64 / self.rows as f64))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cell_size() -> Option<(f64, f64)> {
|
||||
let emu = EMULATOR.get();
|
||||
Some(if emu.force_16t {
|
||||
(emu.csi_16t.0 as f64, emu.csi_16t.1 as f64)
|
||||
} else if let Some(r) = Self::available().ratio() {
|
||||
r
|
||||
} else if emu.csi_16t.0 != 0 && emu.csi_16t.1 != 0 {
|
||||
(emu.csi_16t.0 as f64, emu.csi_16t.1 as f64)
|
||||
} else {
|
||||
None?
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,12 +53,12 @@ impl Ueberzug {
|
|||
})
|
||||
.await??;
|
||||
|
||||
let area = Dimension::ratio()
|
||||
.map(|(r1, r2)| Rect {
|
||||
let area = Dimension::cell_size()
|
||||
.map(|(cw, ch)| Rect {
|
||||
x: max.x,
|
||||
y: max.y,
|
||||
width: max.width.min((w.min(YAZI.preview.max_width as _) as f64 / r1).ceil() as _),
|
||||
height: max.height.min((h.min(YAZI.preview.max_height as _) as f64 / r2).ceil() as _),
|
||||
width: max.width.min((w.min(YAZI.preview.max_width as _) as f64 / cw).ceil() as _),
|
||||
height: max.height.min((h.min(YAZI.preview.max_height as _) as f64 / ch).ceil() as _),
|
||||
})
|
||||
.unwrap_or(max);
|
||||
|
||||
|
|
|
|||
|
|
@ -7,13 +7,14 @@ use tokio::time::sleep;
|
|||
use tracing::{debug, error, warn};
|
||||
use yazi_shared::{Either, tty::{Handle, TTY}};
|
||||
|
||||
use crate::{Adapter, Brand, Mux, TMUX, Unknown};
|
||||
use crate::{Adapter, Brand, Dimension, Mux, TMUX, Unknown};
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Emulator {
|
||||
pub kind: Either<Brand, Unknown>,
|
||||
pub light: bool,
|
||||
pub cell_size: Option<(u16, u16)>,
|
||||
pub csi_16t: (u16, u16),
|
||||
pub force_16t: bool,
|
||||
}
|
||||
|
||||
impl Default for Emulator {
|
||||
|
|
@ -55,15 +56,22 @@ impl Emulator {
|
|||
})
|
||||
};
|
||||
|
||||
let csi_16t = Self::csi_16t(&resp).unwrap_or_default();
|
||||
Ok(Self {
|
||||
kind,
|
||||
light: Self::light_bg(&resp).unwrap_or_default(),
|
||||
cell_size: Self::cell_size(&resp),
|
||||
csi_16t,
|
||||
force_16t: Self::force_16t(csi_16t),
|
||||
})
|
||||
}
|
||||
|
||||
pub const fn unknown() -> Self {
|
||||
Self { kind: Either::Right(Unknown::default()), light: false, cell_size: None }
|
||||
Self {
|
||||
kind: Either::Right(Unknown::default()),
|
||||
light: false,
|
||||
csi_16t: (0, 0),
|
||||
force_16t: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn adapters(self) -> &'static [Adapter] {
|
||||
|
|
@ -160,7 +168,7 @@ impl Emulator {
|
|||
);
|
||||
}
|
||||
|
||||
fn cell_size(resp: &str) -> Option<(u16, u16)> {
|
||||
fn csi_16t(resp: &str) -> Option<(u16, u16)> {
|
||||
let b = resp.split_once("\x1b[6;")?.1.as_bytes();
|
||||
|
||||
let h: Vec<_> = b.iter().copied().take_while(|&c| c.is_ascii_digit()).collect();
|
||||
|
|
@ -189,4 +197,14 @@ impl Emulator {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn force_16t((w, h): (u16, u16)) -> bool {
|
||||
if w == 0 || h == 0 {
|
||||
return false;
|
||||
}
|
||||
|
||||
Dimension::available()
|
||||
.ratio()
|
||||
.is_none_or(|(rw, rh)| rw.floor() as u16 != w || rh.floor() as u16 != h)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,21 +65,21 @@ impl Image {
|
|||
}
|
||||
|
||||
pub(super) fn max_pixel(rect: Rect) -> (u32, u32) {
|
||||
Dimension::ratio()
|
||||
.map(|(r1, r2)| {
|
||||
let (w, h) = ((rect.width as f64 * r1) as u32, (rect.height as f64 * r2) as u32);
|
||||
Dimension::cell_size()
|
||||
.map(|(cw, ch)| {
|
||||
let (w, h) = ((rect.width as f64 * cw) as u32, (rect.height as f64 * ch) as u32);
|
||||
(w.min(YAZI.preview.max_width), h.min(YAZI.preview.max_height))
|
||||
})
|
||||
.unwrap_or((YAZI.preview.max_width, YAZI.preview.max_height))
|
||||
}
|
||||
|
||||
pub(super) fn pixel_area(size: (u32, u32), rect: Rect) -> Rect {
|
||||
Dimension::ratio()
|
||||
.map(|(r1, r2)| Rect {
|
||||
Dimension::cell_size()
|
||||
.map(|(cw, ch)| Rect {
|
||||
x: rect.x,
|
||||
y: rect.y,
|
||||
width: (size.0 as f64 / r1).ceil() as u16,
|
||||
height: (size.1 as f64 / r2).ceil() as u16,
|
||||
width: (size.0 as f64 / cw).ceil() as u16,
|
||||
height: (size.1 as f64 / ch).ceil() as u16,
|
||||
})
|
||||
.unwrap_or(rect)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ impl Mgr {
|
|||
if pos.origin == Origin::Hovered {
|
||||
self.active().hovered_rect_based(pos)
|
||||
} else {
|
||||
pos.rect(Dimension::available())
|
||||
pos.rect(Dimension::available().into())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ impl Tab {
|
|||
}
|
||||
|
||||
pub fn hovered_rect_based(&self, pos: Position) -> Rect {
|
||||
let ws = Dimension::available();
|
||||
let ws = Dimension::available().into();
|
||||
if let Some(r) = self.hovered_rect() {
|
||||
Position::sticky(ws, r, pos.offset)
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
use crossterm::terminal::WindowSize;
|
||||
use ratatui::layout::Rect;
|
||||
use yazi_adapter::Dimension;
|
||||
use yazi_shared::event::CmdCow;
|
||||
|
|
@ -7,7 +6,7 @@ use crate::{app::App, notify};
|
|||
|
||||
impl App {
|
||||
pub(crate) fn update_notify(&mut self, cmd: CmdCow) {
|
||||
let WindowSize { rows, columns, .. } = Dimension::available();
|
||||
let Dimension { rows, columns, .. } = Dimension::available();
|
||||
let area =
|
||||
notify::Notify::available(Rect { x: 0, y: 0, width: columns, height: rows });
|
||||
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ impl Widget for Cmp<'_> {
|
|||
.collect();
|
||||
|
||||
let input_area = self.cx.mgr.area(self.cx.input.position);
|
||||
let mut area = Position::sticky(Dimension::available(), input_area, Offset {
|
||||
let mut area = Position::sticky(Dimension::available().into(), input_area, Offset {
|
||||
x: 1,
|
||||
y: 0,
|
||||
width: input_area.width.saturating_sub(2),
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
#![allow(clippy::module_inception)]
|
||||
|
||||
yazi_macro::mod_flat!(cha chan icon image input layer mouse permit range window);
|
||||
yazi_macro::mod_flat!(cha chan icon image input layer mouse permit range);
|
||||
|
|
|
|||
|
|
@ -1,37 +0,0 @@
|
|||
use mlua::{FromLua, UserData};
|
||||
use yazi_adapter::Dimension;
|
||||
|
||||
#[derive(Debug, Clone, Copy, FromLua)]
|
||||
pub struct Window {
|
||||
pub rows: u16,
|
||||
pub cols: u16,
|
||||
pub width: u16,
|
||||
pub height: u16,
|
||||
}
|
||||
|
||||
impl Window {
|
||||
pub fn get() -> Self {
|
||||
let ws = Dimension::available();
|
||||
Self { rows: ws.rows, cols: ws.columns, width: ws.width, height: ws.height }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Window> for crossterm::terminal::WindowSize {
|
||||
fn from(value: Window) -> Self {
|
||||
crossterm::terminal::WindowSize {
|
||||
rows: value.rows,
|
||||
columns: value.cols,
|
||||
width: value.width,
|
||||
height: value.height,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UserData for Window {
|
||||
fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) {
|
||||
fields.add_field_method_get("rows", |_, me| Ok(me.rows));
|
||||
fields.add_field_method_get("cols", |_, me| Ok(me.cols));
|
||||
fields.add_field_method_get("width", |_, me| Ok(me.width));
|
||||
fields.add_field_method_get("height", |_, me| Ok(me.height));
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue