Add extra method to detect window size on terminals that don't handle ioctls correctly

This commit is contained in:
itsjunetime
2024-05-31 00:38:41 -06:00
parent 1e89a93879
commit 017596a8b0
+67 -2
View File
@@ -1,6 +1,10 @@
#![feature(if_let_guard)] #![feature(if_let_guard)]
use std::{io::stdout, path::PathBuf, str::FromStr}; use std::{
io::{stdout, Read, Write},
path::PathBuf,
str::FromStr
};
use converter::{run_conversion_loop, ConvertedPage, ConverterMsg}; use converter::{run_conversion_loop, ConvertedPage, ConverterMsg};
use crossterm::{ use crossterm::{
@@ -23,6 +27,18 @@ mod renderer;
mod skip; mod skip;
mod tui; mod tui;
// Dummy struct for easy errors in main
#[derive(Debug)]
struct BadTermSizeStdin(String);
impl std::fmt::Display for BadTermSizeStdin {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl std::error::Error for BadTermSizeStdin {}
#[tokio::main] #[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> { async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut args = std::env::args().skip(1); let mut args = std::env::args().skip(1);
@@ -49,7 +65,56 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let file_path = format!("file://{}", path.clone().into_os_string().to_string_lossy()); let file_path = format!("file://{}", path.clone().into_os_string().to_string_lossy());
let (render_tx, mut tui_rx) = tokio::sync::mpsc::unbounded_channel(); let (render_tx, mut tui_rx) = tokio::sync::mpsc::unbounded_channel();
let window_size = window_size()?; let mut window_size = window_size()?;
if window_size.width == 0 || window_size.height == 0 {
// send the command code to get the terminal window size
print!("\x1b[14t");
std::io::stdout().flush()?;
// we need to enable raw mode here since this bit of output won't print a newline; it'll
// just print the info it wants to tell us. So we want to get all characters as they come
enable_raw_mode()?;
// read in the returned size until we hit a 't' (which indicates to us it's done)
let input_vec = std::io::stdin()
.bytes()
.flat_map(|b| b.ok())
.take_while(|b| *b != b't')
.collect::<Vec<_>>();
// and then disable raw mode again in case we return an error in this next section
disable_raw_mode()?;
let input_line = String::from_utf8(input_vec)?;
if input_line.starts_with("\x1b[4;") {
// it should input it to us as `\e[4;<height>;<width>t`, so we need to split to get the h/w
let mut splits = input_line.split([';', 't']);
// ignore the first val
_ = splits.next();
window_size.height = splits
.next()
.ok_or_else(|| {
BadTermSizeStdin(format!(
"Terminal responded with unparseable size response '{input_line}'"
))
})?
.parse::<u16>()?;
window_size.width = splits
.next()
.ok_or_else(|| {
BadTermSizeStdin(format!(
"Terminal responded with unparseable size response '{input_line}'"
))
})?
.parse::<u16>()?;
} else {
return Err("Your terminal is falsely reporting a window size of 0; tdf needs an accurate window size to display graphics".into());
}
}
// We need to create `picker` on this thread because if we create it on the `renderer` thread, // We need to create `picker` on this thread because if we create it on the `renderer` thread,
// it messes up something with user input. Input never makes it to the crossterm thing // it messes up something with user input. Input never makes it to the crossterm thing