detached_shell/
terminal_state.rs

1use nix::sys::termios::{tcgetattr, tcsetattr, SetArg, Termios};
2use serde::{Deserialize, Serialize};
3use std::os::unix::io::{BorrowedFd, RawFd};
4
5use crate::error::{NdsError, Result};
6
7/// Stores terminal state for restoration
8#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct TerminalState {
10    pub window_size: (u16, u16),             // cols, rows
11    pub cursor_position: Option<(u16, u16)>, // x, y
12    #[serde(skip)]
13    pub termios: Option<Termios>,
14}
15
16impl TerminalState {
17    pub fn capture(fd: RawFd) -> Result<Self> {
18        // Get window size
19        let (cols, rows) = Self::get_window_size(fd)?;
20
21        // Get termios settings
22        let borrowed_fd = unsafe { BorrowedFd::borrow_raw(fd) };
23        let termios = tcgetattr(&borrowed_fd).ok();
24
25        Ok(TerminalState {
26            window_size: (cols, rows),
27            cursor_position: None, // Could be enhanced to capture cursor position
28            termios,
29        })
30    }
31
32    pub fn restore(&self, fd: RawFd) -> Result<()> {
33        // Restore window size
34        Self::set_window_size(fd, self.window_size.0, self.window_size.1)?;
35
36        // Restore termios if available
37        if let Some(ref termios) = self.termios {
38            let borrowed_fd = unsafe { BorrowedFd::borrow_raw(fd) };
39            tcsetattr(&borrowed_fd, SetArg::TCSANOW, termios).map_err(|e| {
40                NdsError::TerminalError(format!("Failed to restore termios: {}", e))
41            })?;
42        }
43
44        Ok(())
45    }
46
47    fn get_window_size(fd: RawFd) -> Result<(u16, u16)> {
48        unsafe {
49            let mut winsize = libc::winsize {
50                ws_row: 0,
51                ws_col: 0,
52                ws_xpixel: 0,
53                ws_ypixel: 0,
54            };
55
56            if libc::ioctl(fd, libc::TIOCGWINSZ as u64, &mut winsize) < 0 {
57                return Err(NdsError::TerminalError(
58                    "Failed to get window size".to_string(),
59                ));
60            }
61
62            Ok((winsize.ws_col, winsize.ws_row))
63        }
64    }
65
66    fn set_window_size(fd: RawFd, cols: u16, rows: u16) -> Result<()> {
67        unsafe {
68            let winsize = libc::winsize {
69                ws_row: rows,
70                ws_col: cols,
71                ws_xpixel: 0,
72                ws_ypixel: 0,
73            };
74
75            if libc::ioctl(fd, libc::TIOCSWINSZ as u64, &winsize) < 0 {
76                return Err(NdsError::TerminalError(
77                    "Failed to set window size".to_string(),
78                ));
79            }
80
81            Ok(())
82        }
83    }
84}
85
86/// Terminal commands for state management
87pub struct TerminalCommands;
88
89impl TerminalCommands {
90    /// Send terminal refresh sequence
91    pub fn refresh_display() -> &'static [u8] {
92        // Ctrl+L to refresh display
93        b"\x0c"
94    }
95
96    /// Request terminal to redraw
97    pub fn redraw_prompt() -> &'static [u8] {
98        // Send empty input to trigger prompt redraw
99        b"\n\x1b[A" // Newline then cursor up
100    }
101}