Skip to main content

tui/runtime/
terminal.rs

1use crossterm::event::{
2    DisableBracketedPaste, DisableMouseCapture, EnableBracketedPaste, EnableMouseCapture, KeyboardEnhancementFlags,
3    PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags,
4};
5use crossterm::execute;
6use crossterm::terminal::{disable_raw_mode, enable_raw_mode};
7use std::io;
8
9pub fn terminal_size() -> io::Result<(u16, u16)> {
10    crossterm::terminal::size()
11}
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum MouseCapture {
15    Disabled,
16    Enabled,
17}
18
19#[doc = include_str!("../docs/terminal_session.md")]
20pub struct TerminalSession {
21    enable_bracketed_paste: bool,
22    keyboard_enhancement_enabled: bool,
23}
24
25impl TerminalSession {
26    pub fn new(enable_bracketed_paste: bool, mouse_capture: MouseCapture) -> io::Result<Self> {
27        enable_raw_mode()?;
28        let mut stdout = io::stdout();
29        let flags = KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES
30            | KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS
31            | KeyboardEnhancementFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES;
32        let keyboard_enhancement_enabled = execute!(stdout, PushKeyboardEnhancementFlags(flags)).is_ok();
33
34        if enable_bracketed_paste {
35            execute!(stdout, EnableBracketedPaste)?;
36        }
37        if mouse_capture == MouseCapture::Enabled {
38            execute!(stdout, EnableMouseCapture)?;
39        }
40
41        Ok(Self { enable_bracketed_paste, keyboard_enhancement_enabled })
42    }
43}
44
45impl Drop for TerminalSession {
46    fn drop(&mut self) {
47        let mut stdout = io::stdout();
48        // Always attempt to disable mouse capture defensively: callers may
49        // toggle capture via RendererCommand after session creation, so the
50        // initial `mouse_capture` field may no longer reflect terminal state.
51        let _ = execute!(stdout, DisableMouseCapture);
52        if self.keyboard_enhancement_enabled {
53            let _ = execute!(stdout, PopKeyboardEnhancementFlags);
54        }
55        if self.enable_bracketed_paste {
56            let _ = execute!(stdout, DisableBracketedPaste);
57        }
58        let _ = disable_raw_mode();
59    }
60}