From 02ff709718b08176778be8f7deb39f28a771ff18 Mon Sep 17 00:00:00 2001 From: Pihkaal Date: Sat, 23 Mar 2024 02:51:50 +0100 Subject: [PATCH] fix(rendering): fix out of bounds render crashes app --- src/modes/chrono.rs | 12 +++++------ src/modes/clock.rs | 8 +++---- src/modes/timer.rs | 12 +++++------ src/rendering/mod.rs | 51 +++++++++++++++++++++++++++++++++----------- 4 files changed, 54 insertions(+), 29 deletions(-) diff --git a/src/modes/chrono.rs b/src/modes/chrono.rs index b3624f5..80a09fe 100644 --- a/src/modes/chrono.rs +++ b/src/modes/chrono.rs @@ -162,8 +162,8 @@ fn render_frame( rendering::draw_time(&elapsed, color)?; // Display lapses - let (width, height) = terminal::size()?; - let y = height / 2 + symbols::SYMBOL_HEIGHT as u16 / 2 + 2; + let (width, height) = rendering::get_terminal_size()?; + let y = height / 2 + symbols::SYMBOL_HEIGHT as i16 / 2 + 2; let max_items = min(10, height - y - 1) as usize; if lapses.len() <= max_items { @@ -188,18 +188,18 @@ fn render_frame( delta, time ); - let x = width / 2 - (lapse.len() as u16) / 2; - rendering::draw_text(&lapse, x, y + i as u16, color)?; + let x = width / 2 - (lapse.len() as i16) / 2; + rendering::draw_text(&lapse, x, y + i as i16, color)?; } // Display pause state if chronometer.is_paused() { let text = "[PAUSE]"; - let x = width / 2 - (text.len() as u16) / 2; + let x = width / 2 - (text.len() as i16) / 2; rendering::draw_text( text, x, - y - symbols::SYMBOL_HEIGHT as u16 - symbols::SYMBOL_HEIGHT as u16 / 2, + y - symbols::SYMBOL_HEIGHT as i16 - symbols::SYMBOL_HEIGHT as i16 / 2, color, )?; } diff --git a/src/modes/clock.rs b/src/modes/clock.rs index d534016..c0a9bde 100644 --- a/src/modes/clock.rs +++ b/src/modes/clock.rs @@ -68,10 +68,10 @@ fn render_frame(config: &Config) -> io::Result<()> { .format(&config.date_format.to_owned()) .to_string(); - let (width, height) = terminal::size()?; - let x = width / 2 - (date.len() as u16) / 2; - let y = height / 2 + symbols::SYMBOL_HEIGHT as u16 / 2 + 2; - rendering::draw_text(&date, x, y, color)?; + let (width, height) = rendering::get_terminal_size()?; + let x = width / 2 - (date.len() as i16) / 2; + let y = height / 2 + symbols::SYMBOL_HEIGHT as i16 / 2 + 2; + rendering::draw_text(&date, x, y - 1, color)?; return Ok(()); } diff --git a/src/modes/timer.rs b/src/modes/timer.rs index a3222b4..fb458ed 100644 --- a/src/modes/timer.rs +++ b/src/modes/timer.rs @@ -127,28 +127,28 @@ fn render_frame(config: &Config, timer: &Timer) -> io::Result<()> { rendering::draw_time(&remaining, color)?; // Display pause state - let (width, height) = terminal::size()?; - let y = height / 2 + symbols::SYMBOL_HEIGHT as u16 / 2 + 2; + let (width, height) = rendering::get_terminal_size()?; + let y = height / 2 + symbols::SYMBOL_HEIGHT as i16 / 2 + 2; if timer.is_paused() { let text = "[PAUSE]"; - let x = width / 2 - (text.len() as u16) / 2; + let x = width / 2 - (text.len() as i16) / 2; rendering::draw_text( text, x, - y - symbols::SYMBOL_HEIGHT as u16 - symbols::SYMBOL_HEIGHT as u16 / 2, + y - symbols::SYMBOL_HEIGHT as i16 - symbols::SYMBOL_HEIGHT as i16 / 2, color, )?; } // Display finish state else if timer.is_finished() { let text = "[FINISHED]"; - let x = width / 2 - (text.len() as u16) / 2; + let x = width / 2 - (text.len() as i16) / 2; rendering::draw_text( text, x, - y - symbols::SYMBOL_HEIGHT as u16 - symbols::SYMBOL_HEIGHT as u16 / 2, + y - symbols::SYMBOL_HEIGHT as i16 - symbols::SYMBOL_HEIGHT as i16 / 2, color, )?; } diff --git a/src/rendering/mod.rs b/src/rendering/mod.rs index 49ab288..63eed8b 100644 --- a/src/rendering/mod.rs +++ b/src/rendering/mod.rs @@ -1,4 +1,8 @@ -use std::io::{self, Write}; +use std::{ + cmp::min, + i16, + io::{self, Write}, +}; use crossterm::{ cursor, queue, @@ -9,14 +13,20 @@ use crossterm::{ pub mod color; pub mod symbols; -pub fn draw_time(time: &str, color: Color) -> io::Result<()> { +pub fn get_terminal_size() -> io::Result<(i16, i16)> { let (width, height) = terminal::size()?; + Ok((width as i16, height as i16)) +} + +pub fn draw_time(time: &str, color: Color) -> io::Result<()> { + let (width, height) = get_terminal_size()?; + let text_width = draw_time_width(&time); let text_height = 5; - let mut x = width / 2 - text_width / 2; - let y = height / 2 - text_height / 2; + let mut x = width / 2 - text_width / 2 - 1; + let y = height / 2 - text_height / 2 - 1; for c in time.chars() { if c == ':' { x -= 1; @@ -33,21 +43,30 @@ pub fn draw_time(time: &str, color: Color) -> io::Result<()> { return Ok(()); } -pub fn draw_text(string: &str, x: u16, y: u16, color: Color) -> io::Result<()> { +pub fn draw_text(mut string: &str, mut x: i16, y: i16, color: Color) -> io::Result<()> { let mut stdout = io::stdout(); + let (width, _) = get_terminal_size()?; + + let offset = if x < 0 { + let ret = -x as usize; + x = 0; + ret + } else { + 0 + }; + string = &string[offset..min(string.len(), offset + width as usize)]; queue!( stdout, - cursor::MoveTo(x, y), + cursor::MoveTo(x as u16, y as u16), style::SetForegroundColor(color), style::SetAttribute(Attribute::Bold) )?; write!(stdout, "{}", string)?; - return Ok(()); } -fn draw_time_width(time: &str) -> u16 { +fn draw_time_width(time: &str) -> i16 { if time.len() == 0 { return 0; } @@ -62,24 +81,30 @@ fn draw_time_width(time: &str) -> u16 { } w -= if time.len() == 1 { 1 } else { 2 }; - return w.try_into().unwrap(); + + w.try_into().unwrap() } -fn draw_time_symbol(symbol: char, x: u16, y: u16, color: Color) -> io::Result<()> { +fn draw_time_symbol(symbol: char, x: i16, y: i16, color: Color) -> io::Result<()> { let mut stdout = io::stdout(); + let (width, height) = get_terminal_size()?; let data = symbols::symbol_to_render_data(symbol); for oy in 0..data.len() { for ox in 0..data[oy].len() { if data[oy][ox] { - let cx = ox as u16; - let cy = oy as u16; + let cx = x + ox as i16; + let cy = y + oy as i16; + + if cx < 0 || cx >= width || cy < 0 || cy >= height { + continue; + } // Render cursor at position by setting background color and using space queue!( stdout, - cursor::MoveTo(x + cx, y + cy), + cursor::MoveTo(cx as u16, cy as u16), style::SetBackgroundColor(color) )?; write!(stdout, " ")?;