brush_core/
terminal.rs

1use crate::{error, sys};
2
3/// Encapsulates the state of a controlled terminal.
4#[allow(clippy::module_name_repetitions)]
5pub struct TerminalControl {
6    prev_fg_pid: Option<sys::process::ProcessId>,
7}
8
9impl TerminalControl {
10    /// Acquire the terminal for the shell.
11    pub fn acquire() -> Result<Self, error::Error> {
12        let prev_fg_pid = sys::terminal::get_foreground_pid();
13
14        // Break out into new process group.
15        // TODO: jobs: Investigate why this sometimes fails with EPERM.
16        let _ = sys::signal::lead_new_process_group();
17
18        // Take ownership.
19        sys::terminal::move_self_to_foreground()?;
20
21        // Mask out SIGTTOU.
22        sys::signal::mask_sigttou()?;
23
24        Ok(Self { prev_fg_pid })
25    }
26
27    fn try_release(&mut self) {
28        // Restore the previous foreground process group.
29        if let Some(pid) = self.prev_fg_pid {
30            if sys::terminal::move_to_foreground(pid).is_ok() {
31                self.prev_fg_pid = None;
32            }
33        }
34    }
35}
36
37impl Drop for TerminalControl {
38    fn drop(&mut self) {
39        self.try_release();
40    }
41}