1use std::io::Stdout;
2use std::sync::mpsc::{self, TryRecvError};
3use std::sync::Arc;
4use std::thread;
5use std::time::Duration;
6
7use anyhow::Result;
8use parking_lot::Mutex;
9use ratatui::backend::CrosstermBackend;
10use ratatui::Terminal;
11
12use crate::app::Status;
13use crate::io::Display;
14use crate::log_info;
15
16pub struct Displayer {
21 tx: mpsc::Sender<()>,
22 handle: thread::JoinHandle<Result<()>>,
23}
24
25impl Displayer {
26 const THIRTY_PER_SECONDS_IN_MILLIS: u64 = 33;
27
28 pub fn new(term: Terminal<CrosstermBackend<Stdout>>, status: Arc<Mutex<Status>>) -> Self {
29 let (tx, rx) = mpsc::channel();
30 let mut display = Display::new(term);
31
32 let handle = thread::spawn(move || -> Result<()> {
33 loop {
34 match rx.try_recv() {
35 Ok(_) | Err(TryRecvError::Disconnected) => {
36 log_info!("terminating displayer");
37 display.restore_terminal()?;
38 drop(display);
39 break;
40 }
41 Err(TryRecvError::Empty) => {}
42 }
43 let mut status = status.lock();
44 if !status.internal_settings.is_disabled() {
45 let frame = display.display_all(&status);
46 if status.wants_buffer() {
47 if let Ok(frame) = frame {
48 status.set_buffer(frame.buffer.clone())
49 }
50 }
51 }
52 if status.should_tabs_images_be_cleared() {
53 status.set_tabs_images_cleared();
54 }
55 if status.should_be_cleared() {
56 status.internal_settings.reset_clear()
57 }
58 drop(status);
59
60 thread::sleep(Duration::from_millis(Self::THIRTY_PER_SECONDS_IN_MILLIS));
61 }
62 Ok(())
63 });
64 Self { tx, handle }
65 }
66
67 pub fn quit(self) {
68 log_info!("stopping display loop");
69 match self.tx.send(()) {
70 Ok(()) => (),
71 Err(e) => log_info!("Displayer::quit error {e:?}"),
72 };
73 let _ = self.handle.join();
74 }
75}