mirror of
https://github.com/ratatui-org/ratatui
synced 2024-11-10 07:04:17 +00:00
Limit Rect size to prevent u16 overflow
This commit is contained in:
parent
50fef0fb26
commit
5cee2afc6d
6 changed files with 92 additions and 32 deletions
|
@ -45,12 +45,7 @@ impl Backend for CrosstermBackend {
|
|||
fn size(&self) -> io::Result<Rect> {
|
||||
let terminal = crossterm::terminal::terminal(&self.screen);
|
||||
let (width, height) = terminal.terminal_size();
|
||||
Ok(Rect {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width,
|
||||
height,
|
||||
})
|
||||
Ok(Rect::new(0, 0, width, height))
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
|
|
|
@ -57,12 +57,23 @@ impl Backend for RustboxBackend {
|
|||
Ok(())
|
||||
}
|
||||
fn size(&self) -> Result<Rect, io::Error> {
|
||||
Ok(Rect {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: self.rustbox.width() as u16,
|
||||
height: self.rustbox.height() as u16,
|
||||
})
|
||||
let term_width = self.rustbox.width();
|
||||
let term_height = self.rustbox.height();
|
||||
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
|
||||
},
|
||||
))
|
||||
}
|
||||
fn flush(&mut self) -> Result<(), io::Error> {
|
||||
self.rustbox.present();
|
||||
|
|
|
@ -112,12 +112,7 @@ where
|
|||
/// Return the size of the terminal
|
||||
fn size(&self) -> io::Result<Rect> {
|
||||
let terminal = try!(termion::terminal_size());
|
||||
Ok(Rect {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: terminal.0,
|
||||
height: terminal.1,
|
||||
})
|
||||
Ok(Rect::new(0, 0, terminal.0, terminal.1))
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
|
|
|
@ -16,12 +16,7 @@ impl TestBackend {
|
|||
TestBackend {
|
||||
width,
|
||||
height,
|
||||
buffer: Buffer::empty(Rect {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width,
|
||||
height,
|
||||
}),
|
||||
buffer: Buffer::empty(Rect::new(0, 0, width, height)),
|
||||
cursor: false,
|
||||
}
|
||||
}
|
||||
|
@ -55,12 +50,7 @@ impl Backend for TestBackend {
|
|||
Ok(())
|
||||
}
|
||||
fn size(&self) -> Result<Rect, io::Error> {
|
||||
Ok(Rect {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: self.width,
|
||||
height: self.height,
|
||||
})
|
||||
Ok(Rect::new(0, 0, self.width, self.height))
|
||||
}
|
||||
fn flush(&mut self) -> Result<(), io::Error> {
|
||||
Ok(())
|
||||
|
|
|
@ -279,12 +279,25 @@ impl Default for Rect {
|
|||
}
|
||||
|
||||
impl Rect {
|
||||
/// Creates a new rect, with width and height limited to keep the area under max u16.
|
||||
/// If clipped, aspect ratio will be preserved.
|
||||
pub fn new(x: u16, y: u16, width: u16, height: u16) -> Rect {
|
||||
let max_area = u16::max_value();
|
||||
let (clipped_width, clipped_height) =
|
||||
if u32::from(width) * u32::from(height) > u32::from(max_area) {
|
||||
let aspect_ratio = f64::from(width) / f64::from(height);
|
||||
let max_area_f = f64::from(max_area);
|
||||
let height_f = (max_area_f / aspect_ratio).sqrt();
|
||||
let width_f = height_f * aspect_ratio;
|
||||
(width_f as u16, height_f as u16)
|
||||
} else {
|
||||
(width, height)
|
||||
};
|
||||
Rect {
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
width: clipped_width,
|
||||
height: clipped_height,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -354,3 +367,46 @@ impl Rect {
|
|||
&& self.y + self.height > other.y
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rect_size_truncation() {
|
||||
for width in 256u16..300u16 {
|
||||
for height in 256u16..300u16 {
|
||||
let rect = Rect::new(0, 0, width, height);
|
||||
rect.area(); // Should not panic.
|
||||
assert!(rect.width < width || rect.height < height);
|
||||
// The target dimensions are rounded down so the math will not be too precise
|
||||
// but let's make sure the ratios don't diverge crazily.
|
||||
assert!(
|
||||
(f64::from(rect.width) / f64::from(rect.height)
|
||||
- f64::from(width) / f64::from(height)).abs()
|
||||
< 1.0
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// One dimension below 255, one above. Area above max u16.
|
||||
let width = 900;
|
||||
let height = 100;
|
||||
let rect = Rect::new(0, 0, width, height);
|
||||
assert_ne!(rect.width, 900);
|
||||
assert_ne!(rect.height, 100);
|
||||
assert!(rect.width < width || rect.height < height);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rect_size_preservation() {
|
||||
for width in 0..256u16 {
|
||||
for height in 0..256u16 {
|
||||
let rect = Rect::new(0, 0, width, height);
|
||||
rect.area(); // Should not panic.
|
||||
assert_eq!(rect.width, width);
|
||||
assert_eq!(rect.height, height);
|
||||
}
|
||||
}
|
||||
|
||||
// One dimension below 255, one above. Area below max u16.
|
||||
let rect = Rect::new(0, 0, 300, 100);
|
||||
assert_eq!(rect.width, 300);
|
||||
assert_eq!(rect.height, 100);
|
||||
}
|
||||
|
|
13
tests/size.rs
Normal file
13
tests/size.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
extern crate tui;
|
||||
|
||||
use tui::backend::{Backend, TestBackend};
|
||||
use tui::Terminal;
|
||||
|
||||
#[test]
|
||||
fn buffer_size_limited() {
|
||||
let backend = TestBackend::new(400, 400);
|
||||
let terminal = Terminal::new(backend).unwrap();
|
||||
let size = terminal.backend().size().unwrap();
|
||||
assert_eq!(size.width, 255);
|
||||
assert_eq!(size.height, 255);
|
||||
}
|
Loading…
Reference in a new issue