devela/sys/os/linux/structs/
termios.rs

1// devela::sys::os::linux::structs::termios
2//
3//! Defines [`LinuxTermios`]
4//
5
6#![cfg_attr(not(feature = "unsafe_syscall"), allow(dead_code))]
7
8#[cfg(all(feature = "unsafe_syscall", not(miri)))]
9use crate::{
10    LINUX_ERRNO, LINUX_FILENO, LINUX_IOCTL, LINUX_TERMIOS_LFLAG, Linux, LinuxError,
11    LinuxResult as Result, is,
12};
13use crate::{TermSize, c_uint};
14
15/// Represents the [`termios`] structure from libc,
16/// used to control terminal I/O.
17///
18/// It has fields for input, output, control, and local modes,
19/// as well as a line discipline and control characters.
20///
21/// [`termios`]: https://man7.org/linux/man-pages/man3/termios.3.html
22#[derive(Clone, Copy, Debug, Default, PartialEq)]
23#[repr(C)]
24pub struct LinuxTermios {
25    /// Input flags.
26    pub c_iflag: c_uint,
27
28    /// Output flags.
29    pub c_oflag: c_uint,
30
31    /// Control flags.
32    pub c_cflag: c_uint,
33
34    /// Local flags.
35    pub c_lflag: c_uint,
36
37    /// …
38    pub c_line: u8,
39
40    /// …
41    pub c_cc: [u8; 19],
42}
43
44#[cfg_attr(nightly_doc, doc(cfg(all(feature = "unsafe_syscall", feature = "dep_bytemuck"))))]
45#[cfg(all(feature = "unsafe_syscall", feature = "dep_bytemuck"))]
46unsafe impl crate::_dep::bytemuck::NoUninit for LinuxTermios {}
47
48impl LinuxTermios {
49    /// Returns a new empty struct.
50    #[must_use]
51    pub const fn new() -> Self {
52        Self {
53            c_iflag: 0,
54            c_oflag: 0,
55            c_cflag: 0,
56            c_lflag: 0,
57            c_line: 0,
58            c_cc: [0; 19],
59        }
60    }
61
62    /// Returns a raw byte pointer to self.
63    #[must_use]
64    pub const fn as_bytes_ptr(&self) -> *const u8 {
65        self as *const Self as *const u8
66    }
67
68    /// Returns a raw mutable byte pointer to self.
69    #[must_use]
70    pub fn as_mut_bytes_ptr(&mut self) -> *mut u8 {
71        self as *mut Self as *mut u8
72    }
73}
74
75#[cfg(all(
76    any(
77        target_arch = "x86_64",
78        target_arch = "x86",
79        target_arch = "arm",
80        target_arch = "aarch64",
81        target_arch = "riscv32",
82        target_arch = "riscv64"
83    ),
84    feature = "unsafe_syscall",
85    not(miri),
86))]
87impl LinuxTermios {
88    /// Gets the current termios state into `state`.
89    pub fn get_state() -> Result<LinuxTermios> {
90        let mut state = LinuxTermios::new();
91        let res = unsafe {
92            Linux::sys_ioctl(LINUX_FILENO::STDIN, LINUX_IOCTL::TCGETS, state.as_mut_bytes_ptr())
93        };
94        is![res >= 0; Ok(state); Err(LinuxError::Sys(res))]
95    }
96
97    /// Sets the current termios `state`.
98    pub fn set_state(mut state: LinuxTermios) -> Result<()> {
99        let res = unsafe {
100            Linux::sys_ioctl(LINUX_FILENO::STDIN, LINUX_IOCTL::TCSETS, state.as_mut_bytes_ptr())
101        };
102        is![res >= 0; Ok(()); Err(LinuxError::Sys(res))]
103    }
104
105    /// Returns `true` if we're in a terminal context.
106    #[must_use]
107    pub fn is_terminal() -> bool {
108        match Self::get_state() {
109            Ok(_) => true,
110            Err(LinuxError::Sys(err)) => err != -LINUX_ERRNO::ENOTTY && err != -LINUX_ERRNO::EINVAL,
111            Err(_) => false, // Other errors are not related to terminal checks
112        }
113    }
114
115    /// Disables raw mode.
116    pub fn disable_raw_mode() -> Result<()> {
117        let mut state = LinuxTermios::get_state()?;
118        state.c_lflag |= LINUX_TERMIOS_LFLAG::ICANON;
119        state.c_lflag |= LINUX_TERMIOS_LFLAG::ECHO;
120        LinuxTermios::set_state(state)
121    }
122
123    /// Enables raw mode.
124    pub fn enable_raw_mode() -> Result<()> {
125        let mut state = Self::get_state()?;
126        state.c_lflag &= !LINUX_TERMIOS_LFLAG::ICANON;
127        state.c_lflag &= !LINUX_TERMIOS_LFLAG::ECHO;
128        LinuxTermios::set_state(state)
129    }
130
131    /// Returns the size of the window, in cells and pixels.
132    pub fn get_winsize() -> Result<TermSize> {
133        let mut winsize = TermSize::default();
134        let res = unsafe {
135            Linux::sys_ioctl(
136                LINUX_FILENO::STDIN,
137                LINUX_IOCTL::TIOCGWINSZ,
138                &mut winsize as *mut TermSize as *mut u8,
139            )
140        };
141        is![res >= 0; Ok(winsize); Err(LinuxError::Sys(res))]
142    }
143}