From 7cacfcc18973d1d4141f35dfc6162a5f2cd81d87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Gro=C3=9Fe?= Date: Tue, 13 Jun 2023 07:44:25 +0100 Subject: [PATCH] feat(backend): backend provides window_size For image (sixel, iTerm2, Kitty...) support that handles graphics in terms of `Rect` so that the image area can be included in layouts. For example: an image is loaded with a known pixel-size, and drawn, but the image protocol has no mechanism of knowing the actual cell/character area that been drawn on. It is then impossible to skip overdrawing the area. Returning the window size in pixel-width / pixel-height, together with colums / rows, it can be possible to account the pixel size of each cell / character, and then known the `Rect` of a given image, and also resize the image so that it fits exactly in a `Rect`. --- src/backend/crossterm.rs | 8 ++++++++ src/backend/mod.rs | 3 +++ src/backend/termion.rs | 6 ++++++ src/backend/termwiz.rs | 44 ++++++++++++++++++++++++---------------- src/backend/test.rs | 6 ++++++ 5 files changed, 50 insertions(+), 17 deletions(-) diff --git a/src/backend/crossterm.rs b/src/backend/crossterm.rs index 297a964fb..1d9cf57b7 100644 --- a/src/backend/crossterm.rs +++ b/src/backend/crossterm.rs @@ -175,6 +175,14 @@ where Ok(Rect::new(0, 0, width, height)) } + fn window_size(&mut self) -> Result<(Rect, (u16, u16)), io::Error> { + let window_size = terminal::window_size()?; + Ok(( + Rect::new(0, 0, window_size.columns, window_size.rows), + (window_size.width, window_size.height), + )) + } + fn flush(&mut self) -> io::Result<()> { self.buffer.flush() } diff --git a/src/backend/mod.rs b/src/backend/mod.rs index a9e1d1198..e2ce7433b 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -112,6 +112,9 @@ pub trait Backend { /// Get the size of the terminal screen as a [`Rect`]. fn size(&self) -> Result; + /// Get the font size of the terminal screen as [`(width, height)`]. + fn window_size(&mut self) -> Result<(Rect, (u16, u16)), io::Error>; + /// Flush any buffered content to the terminal screen. fn flush(&mut self) -> Result<(), io::Error>; } diff --git a/src/backend/termion.rs b/src/backend/termion.rs index b662fa9eb..cb453c568 100644 --- a/src/backend/termion.rs +++ b/src/backend/termion.rs @@ -160,6 +160,12 @@ where Ok(Rect::new(0, 0, terminal.0, terminal.1)) } + fn window_size(&mut self) -> Result<(Rect, (u16, u16)), io::Error> { + let terminal = termion::terminal_size()?; + let pixels = termion::terminal_size_pixels()?; + Ok((Rect::new(0, 0, terminal.0, terminal.1), pixels)) + } + fn flush(&mut self) -> io::Result<()> { self.stdout.flush() } diff --git a/src/backend/termwiz.rs b/src/backend/termwiz.rs index 18e3d2668..062f1d707 100644 --- a/src/backend/termwiz.rs +++ b/src/backend/termwiz.rs @@ -11,7 +11,7 @@ use termwiz::{ cell::{AttributeChange, Blink, Intensity, Underline}, color::{AnsiColor, ColorAttribute, SrgbaTuple}, surface::{Change, CursorVisibility, Position}, - terminal::{buffered::BufferedTerminal, SystemTerminal, Terminal}, + terminal::{buffered::BufferedTerminal, ScreenSize, SystemTerminal, Terminal}, }; use crate::{ @@ -169,22 +169,21 @@ impl Backend for TermwizBackend { } fn size(&self) -> Result { - let (term_width, term_height) = self.buffered_terminal.dimensions(); - let max = u16::max_value(); - Ok(Rect::new( - 0, - 0, - if term_width > usize::from(max) { - max - } else { - term_width as u16 - }, - if term_height > usize::from(max) { - max - } else { - term_height as u16 - }, - )) + Ok(self.buffered_terminal.dimensions().into()) + } + + fn window_size(&mut self) -> Result<(Rect, (u16, u16)), io::Error> { + let ScreenSize { + cols, + rows, + xpixel, + ypixel, + } = self + .buffered_terminal + .terminal() + .get_screen_size() + .map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?; + Ok(((cols, rows).into(), (u16_max(xpixel), u16_max(ypixel)))) } fn flush(&mut self) -> Result<(), io::Error> { @@ -221,3 +220,14 @@ impl From for ColorAttribute { } } } + +impl From<(usize, usize)> for Rect { + fn from((cols, rows): (usize, usize)) -> Rect { + Rect::new(0, 0, u16_max(cols), u16_max(rows)) + } +} + +#[inline] +fn u16_max(i: usize) -> u16 { + u16::try_from(i).unwrap_or(u16::MAX) +} diff --git a/src/backend/test.rs b/src/backend/test.rs index b261d8cfa..c9116697d 100644 --- a/src/backend/test.rs +++ b/src/backend/test.rs @@ -177,6 +177,12 @@ impl Backend for TestBackend { Ok(Rect::new(0, 0, self.width, self.height)) } + fn window_size(&mut self) -> Result<(Rect, (u16, u16)), io::Error> { + // Some arbitrary window pixel size, probably doesn't need much testing. + static WINDOW_PIXEL_SIZE: (u16, u16) = (640, 480); + Ok((Rect::new(0, 0, self.width, self.height), WINDOW_PIXEL_SIZE)) + } + fn flush(&mut self) -> Result<(), io::Error> { Ok(()) }