Skip to main content

romm_cli/tui/
runtime.rs

1//! Shared terminal session setup for TUI frontends.
2
3use anyhow::Result;
4use crossterm::event::{DisableMouseCapture, EnableMouseCapture};
5use crossterm::execute;
6use crossterm::terminal::{
7    disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen,
8};
9use ratatui::backend::CrosstermBackend;
10use ratatui::Terminal;
11
12/// Options for terminal setup/teardown.
13#[derive(Debug, Clone, Copy, Default)]
14pub struct RuntimeOptions {
15    pub bracketed_paste: bool,
16}
17
18/// Control flow for the shared TUI loop.
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum LoopControl {
21    Continue,
22    Break,
23}
24
25/// Owns raw-mode terminal state until [`TuiSession::leave`].
26pub struct TuiSession {
27    terminal: Terminal<CrosstermBackend<std::io::Stdout>>,
28    options: RuntimeOptions,
29}
30
31impl TuiSession {
32    pub fn enter(options: RuntimeOptions) -> Result<Self> {
33        enable_raw_mode()?;
34        let mut stdout = std::io::stdout();
35        if options.bracketed_paste {
36            execute!(
37                stdout,
38                EnterAlternateScreen,
39                EnableMouseCapture,
40                crossterm::event::EnableBracketedPaste
41            )?;
42        } else {
43            execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
44        }
45        let backend = CrosstermBackend::new(stdout);
46        let terminal = Terminal::new(backend)?;
47        Ok(Self { terminal, options })
48    }
49
50    pub fn terminal_mut(&mut self) -> &mut Terminal<CrosstermBackend<std::io::Stdout>> {
51        &mut self.terminal
52    }
53
54    pub fn leave(mut self) -> Result<()> {
55        disable_raw_mode()?;
56        if self.options.bracketed_paste {
57            execute!(
58                self.terminal.backend_mut(),
59                crossterm::event::DisableBracketedPaste,
60                LeaveAlternateScreen,
61                DisableMouseCapture
62            )?;
63        } else {
64            execute!(
65                self.terminal.backend_mut(),
66                LeaveAlternateScreen,
67                DisableMouseCapture
68            )?;
69        }
70        self.terminal.show_cursor()?;
71        Ok(())
72    }
73}