use std::{hint::black_box, path::Path}; use crossterm::terminal::WindowSize; use flume::{r#async::RecvStream, unbounded, Sender}; use futures_util::stream::StreamExt as _; use ratatui::layout::Rect; use ratatui_image::picker::{Picker, ProtocolType}; use tdf::{ converter::{run_conversion_loop, ConvertedPage, ConverterMsg}, renderer::{fill_default, start_rendering, RenderError, RenderInfo, RenderNotif} }; fn handle_renderer_msg( msg: Result, pages: &mut Vec>, to_converter_tx: &mut Sender ) { match msg { Ok(RenderInfo::NumPages(num)) => { fill_default(pages, num); to_converter_tx.send(ConverterMsg::NumPages(num)).unwrap(); } Ok(RenderInfo::Page(info)) => to_converter_tx.send(ConverterMsg::AddImg(info)).unwrap(), Err(e) => panic!("Got error from renderer: {e:?}") } } fn handle_converter_msg( msg: Result, pages: &mut [Option], to_converter_tx: &mut Sender ) { let page = msg.expect("Got error from converter"); let num = page.num; pages[num] = Some(page); let num_got = pages.iter().filter(|p| p.is_some()).count(); // we have to tell it to jump to a certain page so that it will actually render it (since // it only renders fanning out from the page that we currently have selected) to_converter_tx .send(ConverterMsg::GoToPage(num_got)) .unwrap(); } struct RenderState { from_render_rx: RecvStream<'static, Result>, from_converter_rx: RecvStream<'static, Result>, pages: Vec>, to_converter_tx: Sender, to_render_tx: Sender } fn start_all_rendering(path: impl AsRef) -> RenderState { let pathbuf = path.as_ref().canonicalize().unwrap(); let str_path = format!("file://{}", pathbuf.into_os_string().to_string_lossy()); let (to_render_tx, from_main_rx) = unbounded(); let (to_main_tx, from_render_rx) = unbounded(); let font_size = (8, 14); let (columns, rows) = (60, 180); let size = WindowSize { columns, rows, height: rows * font_size.1, width: columns * font_size.0 }; std::thread::spawn(move || start_rendering(str_path, to_main_tx, from_main_rx, size)); let (to_converter_tx, from_main_rx) = unbounded(); let (to_main_tx, from_converter_rx) = unbounded(); let mut picker = Picker::new(font_size); picker.protocol_type = ProtocolType::Kitty; tokio::spawn(run_conversion_loop(to_main_tx, from_main_rx, picker)); let pages: Vec> = Vec::new(); let main_area = Rect { x: 0, y: 0, width: columns - 2, height: rows - 6 }; to_render_tx.send(RenderNotif::Area(main_area)).unwrap(); let from_render_rx = from_render_rx.into_stream(); let from_converter_rx = from_converter_rx.into_stream(); RenderState { from_render_rx, from_converter_rx, pages, to_converter_tx, to_render_tx } } pub async fn render_doc(path: impl AsRef) { let RenderState { mut from_render_rx, mut from_converter_rx, mut pages, mut to_converter_tx, to_render_tx } = start_all_rendering(path); while pages.is_empty() || pages.iter().any(|p| p.is_none()) { tokio::select! { Some(renderer_msg) = from_render_rx.next() => { handle_renderer_msg(renderer_msg, &mut pages, &mut to_converter_tx); }, Some(converter_msg) = from_converter_rx.next() => { handle_converter_msg(converter_msg, &mut pages, &mut to_converter_tx); } } } black_box(pages); // we want to make sure this is kept around until the end of this function, or else the other // thread will see that this is disconnected and think that we're done communicating with them drop(to_render_tx); } #[cfg(test)] pub async fn render_first_page(path: impl AsRef) { let RenderState { mut from_render_rx, mut from_converter_rx, mut pages, mut to_converter_tx, to_render_tx } = start_all_rendering(path); // we only want to render until the first page is ready to be printed while pages.is_empty() { tokio::select! { Some(renderer_msg) = from_render_rx.next() => { handle_renderer_msg(renderer_msg, &mut pages, &mut to_converter_tx); }, Some(converter_msg) = from_converter_rx.next() => { handle_converter_msg(converter_msg, &mut pages, &mut to_converter_tx); } } } black_box(pages); // we want to make sure this is kept around until the end of this function, or else the other // thread will see that this is disconnected and think that we're done communicating with them drop(to_render_tx); }