xref: /DragonOS/tools/debugging/logmonitor/src/tui.rs (revision cc5feaf67b914ecf701abcba70c01da149755491)
1 use crate::app::{App, AppResult};
2 use crate::event::EventHandler;
3 use crate::ui;
4 use crossterm::event::DisableMouseCapture;
5 use crossterm::terminal::{self, EnterAlternateScreen, LeaveAlternateScreen};
6 use std::io;
7 use std::panic;
8 
9 use ratatui::backend::Backend;
10 use ratatui::Terminal;
11 
12 /// Representation of a terminal user interface.
13 ///
14 /// It is responsible for setting up the terminal,
15 /// initializing the interface and handling the draw events.
16 #[derive(Debug)]
17 pub struct Tui<B: Backend> {
18     /// Interface to the Terminal.
19     terminal: Terminal<B>,
20     /// Terminal event handler.
21     pub events: EventHandler,
22 }
23 
24 impl<B: Backend> Tui<B> {
25     /// Constructs a new instance of [`Tui`].
26     pub fn new(terminal: Terminal<B>, events: EventHandler) -> Self {
27         Self { terminal, events }
28     }
29 
30     /// Initializes the terminal interface.
31     ///
32     /// It enables the raw mode and sets terminal properties.
33     pub fn init(&mut self) -> AppResult<()> {
34         terminal::enable_raw_mode()?;
35         crossterm::execute!(io::stderr(), EnterAlternateScreen, DisableMouseCapture)?;
36 
37         // Define a custom panic hook to reset the terminal properties.
38         // This way, you won't have your terminal messed up if an unexpected error happens.
39         let panic_hook = panic::take_hook();
40         panic::set_hook(Box::new(move |panic| {
41             Self::reset().expect("failed to reset the terminal");
42             panic_hook(panic);
43         }));
44 
45         self.terminal.hide_cursor()?;
46         self.terminal.clear()?;
47         Ok(())
48     }
49 
50     /// [`Draw`] the terminal interface by [`rendering`] the widgets.
51     ///
52     /// [`Draw`]: ratatui::Terminal::draw
53     /// [`rendering`]: crate::ui:render
54     pub fn draw(&mut self, app: &mut App) -> AppResult<()> {
55         self.terminal.draw(|frame| ui::render(app, frame))?;
56         Ok(())
57     }
58 
59     /// Resets the terminal interface.
60     ///
61     /// This function is also used for the panic hook to revert
62     /// the terminal properties if unexpected errors occur.
63     fn reset() -> AppResult<()> {
64         terminal::disable_raw_mode()?;
65         crossterm::execute!(io::stdout(), LeaveAlternateScreen, DisableMouseCapture)?;
66         Ok(())
67     }
68 
69     /// Exits the terminal interface.
70     ///
71     /// It disables the raw mode and reverts back the terminal properties.
72     pub fn exit(&mut self) -> AppResult<()> {
73         Self::reset()?;
74         self.terminal.show_cursor()?;
75         Ok(())
76     }
77 }
78