fm/app/
displayer.rs

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
16/// Is responsible for running the display thread.
17/// Rendering is done at 30 fps if possible.
18/// It holds a transmitter used to ask the thread to stop and an handle to this thread.
19/// It's usefull to ensure the terminal is reset properly which should always be the case.
20pub 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                    display.display_all(&status);
46                }
47                if status.should_tabs_images_be_cleared() {
48                    status.set_tabs_images_cleared();
49                }
50                if status.should_be_cleared() {
51                    status.internal_settings.reset_clear()
52                }
53                drop(status);
54
55                thread::sleep(Duration::from_millis(Self::THIRTY_PER_SECONDS_IN_MILLIS));
56            }
57            Ok(())
58        });
59        Self { tx, handle }
60    }
61
62    pub fn quit(self) {
63        log_info!("stopping display loop");
64        match self.tx.send(()) {
65            Ok(()) => (),
66            Err(e) => log_info!("Displayer::quit error {e:?}"),
67        };
68        let _ = self.handle.join();
69    }
70}