devela/sys/os/linux/
terminal.rs

1// devela::sys::os::linux::terminal
2//
3//! Linux terminal related items.
4//
5
6use super::{LinuxTerminalSize, LinuxTermios};
7
8#[cfg(all(feature = "dep_atomic", feature = "dep_bytemuck"))]
9use crate::_dep::atomic::{Atomic, Ordering as AtomicOrdering};
10
11/// State of the terminal saved globally, that can be restored from anywhere.
12///
13/// This allows to restore the initial terminal state from a panic handler. E.g.:
14///
15/// ```ignore
16/// # use devela::LinuxTerminal;
17/// #[panic_handler]
18/// fn panic(_info: &core::panic::PanicInfo) -> ! {
19///     LinuxTerminal::restore_saved_state().unwrap();
20/// }
21/// ```
22///
23/// # Features
24/// Makes use of [`atomic`] and [`bytemuck`] dependencies to save the
25/// terminal state in an [`Atomic`].
26///
27/// [`atomic`]: crate::_dep::atomic
28/// [`bytemuck`]: crate::_dep::bytemuck
29#[cfg_attr(
30    feature = "nightly_doc",
31    doc(cfg(all(feature = "dep_atomic", feature = "dep_bytemuck")))
32)]
33#[cfg(all(feature = "dep_atomic", feature = "dep_bytemuck"))]
34pub static LINUX_TERMINAL_STATE: Atomic<LinuxTermios> = Atomic::new(LinuxTermios::new());
35
36/// Linux terminal manager.
37///
38/// # Features
39/// With `atomic` and `bytemuck` enabled,
40/// the terminal state is saved in [`LINUX_TERMINAL_STATE`] and restored on drop.
41#[allow(rustdoc::broken_intra_doc_links, reason = "LINUX_TERMINAL_STATE")]
42#[derive(Debug, Default)]
43pub struct LinuxTerminal;
44
45#[cfg_attr(
46    feature = "nightly_doc",
47    doc(cfg(all(feature = "dep_atomic", feature = "dep_bytemuck")))
48)]
49#[cfg(all(feature = "dep_atomic", feature = "dep_bytemuck"))]
50impl Drop for LinuxTerminal {
51    fn drop(&mut self) {
52        // If we are here, this should work
53        Self::restore_saved_state().unwrap();
54    }
55}
56
57#[allow(rustdoc::broken_intra_doc_links, reason = "LINUX_TERMINAL_STATE")]
58impl LinuxTerminal {
59    /// Returns a new linux terminal configured in canonical (cooked) mode.
60    ///
61    /// # Features
62    /// With `atomic` and `bytemuck` enabled,
63    /// it saves the initial terminal state in [`LINUX_TERMINAL_STATE`].
64    pub fn new() -> Result<Self, isize> {
65        #[cfg(all(feature = "dep_atomic", feature = "dep_bytemuck"))]
66        Self::save_state()?;
67        Ok(Self)
68    }
69
70    /// Returns a new linux terminal configured in raw mode.
71    ///
72    /// *Raw* mode is a mode where the terminal's input is processed character
73    /// by character, rather than line by line.
74    ///
75    /// # Features
76    /// With `atomic` and `bytemuck` enabled,
77    /// it saves the initial terminal state in [`LINUX_TERMINAL_STATE`].
78    pub fn new_raw() -> Result<Self, isize> {
79        #[cfg(all(feature = "dep_atomic", feature = "dep_bytemuck"))]
80        Self::save_state()?;
81
82        let new = Self::new()?;
83        new.enable_raw_mode()?;
84        Ok(new)
85    }
86
87    /// Saves the current terminal state into [`LINUX_TERMINAL_STATE`].
88    #[cfg_attr(
89        feature = "nightly_doc",
90        doc(cfg(all(feature = "dep_bytemuck", feature = "dep_atomic")))
91    )]
92    #[cfg(all(feature = "dep_atomic", feature = "dep_bytemuck"))]
93    pub fn save_state() -> Result<(), isize> {
94        LINUX_TERMINAL_STATE.store(LinuxTermios::get_state()?, AtomicOrdering::Relaxed);
95        Ok(())
96    }
97
98    /// Restores the current terminal state into [`LINUX_TERMINAL_STATE`].
99    #[cfg_attr(
100        feature = "nightly_doc",
101        doc(cfg(all(feature = "dep_bytemuck", feature = "dep_atomic")))
102    )]
103    #[cfg(all(feature = "dep_atomic", feature = "dep_bytemuck"))]
104    pub fn restore_saved_state() -> Result<(), isize> {
105        LinuxTermios::set_state(LINUX_TERMINAL_STATE.load(AtomicOrdering::Relaxed))
106    }
107
108    /// Returns `true` if we are in a terminal context.
109    #[must_use]
110    pub fn is_terminal(&self) -> bool {
111        LinuxTermios::is_terminal()
112    }
113
114    /// Returns the terminal dimensions.
115    pub fn size(&self) -> Result<LinuxTerminalSize, isize> {
116        LinuxTermios::get_winsize()
117    }
118
119    /// Enables raw mode.
120    ///
121    /// Raw mode is a way to configure the terminal so that it does not process or
122    /// interpret any of the input but instead passes it directly to the program.
123    pub fn enable_raw_mode(&self) -> Result<(), isize> {
124        LinuxTermios::enable_raw_mode()
125    }
126
127    /// Disables raw mode.
128    pub fn disable_raw_mode(&self) -> Result<(), isize> {
129        LinuxTermios::disable_raw_mode()
130    }
131}