use crate::c_uint;
#[crate::macro_apply(crate::_unsafe_syscall_not_miri)]
use crate::{
LINUX_ERRNO, LINUX_FILENO, LINUX_IOCTL, Linux, LinuxError, LinuxResult as Result, TermSize, is,
};
use crate::{
LinuxTermiosCc as Cc, LinuxTermiosControlFlags as C, LinuxTermiosInputFlags as I,
LinuxTermiosLocalFlags as L, LinuxTermiosOutputFlags as O,
};
#[doc = crate::_tags!(linux term abi)]
#[doc = crate::_doc_meta!{
location("sys/os/linux/io"),
test_size_of(LinuxTermios = 36|288),
}]
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct LinuxTermios {
pub c_iflag: c_uint,
pub c_oflag: c_uint,
pub c_cflag: c_uint,
pub c_lflag: c_uint,
pub c_line: u8,
pub c_cc: [u8; 19],
}
impl LinuxTermios {
#[must_use]
pub const fn new() -> Self {
Self {
c_iflag: 0,
c_oflag: 0,
c_cflag: 0,
c_lflag: 0,
c_line: 0,
c_cc: [0; 19],
}
}
#[must_use]
pub const fn as_bytes_ptr(&self) -> *const u8 {
self as *const Self as *const u8
}
#[must_use]
pub const fn as_mut_bytes_ptr(&mut self) -> *mut u8 {
self as *mut Self as *mut u8
}
}
#[cfg(any_target_arch_linux)]
#[crate::macro_apply(crate::_unsafe_syscall_not_miri)]
impl LinuxTermios {
pub fn read_state() -> Result<LinuxTermios> {
let mut state = LinuxTermios::new();
let res = unsafe {
Linux::sys_ioctl(LINUX_FILENO::STDIN, LINUX_IOCTL::TCGETS, state.as_mut_bytes_ptr())
};
is![res >= 0, Ok(state), Err(LinuxError::Sys(res))]
}
pub fn write_state(mut self) -> Result<()> {
let res = unsafe {
Linux::sys_ioctl(LINUX_FILENO::STDIN, LINUX_IOCTL::TCSETS, self.as_mut_bytes_ptr())
};
is![res >= 0, Ok(()), Err(LinuxError::Sys(res))]
}
pub fn update_state(f: impl FnOnce(&mut Self)) -> Result<()> {
let mut state = Self::read_state()?;
f(&mut state);
state.write_state()
}
#[must_use]
pub fn is_terminal() -> bool {
match Self::read_state() {
Ok(_) => true,
Err(LinuxError::Sys(err)) => err != -LINUX_ERRNO::ENOTTY && err != -LINUX_ERRNO::EINVAL,
Err(_) => false, }
}
pub fn read_window_size() -> Result<TermSize> {
let mut winsize = TermSize::default();
let res = unsafe {
Linux::sys_ioctl(
LINUX_FILENO::STDIN,
LINUX_IOCTL::TIOCGWINSZ,
&mut winsize as *mut TermSize as *mut u8,
)
};
is![res >= 0, Ok(winsize), Err(LinuxError::Sys(res))]
}
pub fn enable_event_mode() -> Result<()> {
Self::update_state(|state| state.make_event())
}
pub fn enable_raw_mode() -> Result<()> {
Self::update_state(|state| state.make_raw())
}
pub fn reset_cooked_mode() -> Result<()> {
Self::update_state(|state| state.make_cooked_reset())
}
}
#[rustfmt::skip]
impl LinuxTermios {
pub const fn make_event(&mut self) {
self.remove_input_flags(I::IXON);
self.remove_local_flags(L::ECHO);
self.remove_local_flags(L::ICANON);
self.set_read_min_timeout(1, 0);
}
pub const fn make_raw(&mut self) {
self.remove_input_flags(I::BRKINT);
self.remove_input_flags(I::ICRNL);
self.remove_input_flags(I::INPCK);
self.remove_input_flags(I::ISTRIP);
self.set_software_flow(false);
self.remove_output_flags(O::OPOST);
self.remove_local_flags(L::ECHO);
self.remove_local_flags(L::ICANON);
self.remove_local_flags(L::IEXTEN);
self.remove_local_flags(L::ISIG);
self.set_char_size(LinuxTermiosCharSize::Bits8);
self.set_read_min_timeout(1, 0);
}
pub const fn make_cooked_reset(&mut self) {
self.set_software_flow(true);
self.set_input_cr_to_lf(true);
self.set_canonical(true);
self.set_echo(true);
self.set_signals(true);
self.set_extensions(true);
self.set_output_processing(true);
self.set_read_min_timeout(1, 0);
}
pub const fn set_echo(&mut self, enabled: bool) {
if enabled { self.insert_local_flags(L::ECHO); }
else { self.remove_local_flags(L::ECHO); }
}
pub const fn set_canonical(&mut self, enabled: bool) {
if enabled { self.insert_local_flags(L::ICANON); }
else { self.remove_local_flags(L::ICANON); }
}
pub const fn set_signals(&mut self, enabled: bool) {
if enabled { self.insert_local_flags(L::ISIG); }
else { self.remove_local_flags(L::ISIG); }
}
pub const fn set_extensions(&mut self, enabled: bool) {
if enabled { self.insert_local_flags(L::IEXTEN); }
else { self.remove_local_flags(L::IEXTEN); }
}
pub const fn set_break_interrupt(&mut self, enabled: bool) {
if enabled { self.insert_input_flags(I::BRKINT); }
else { self.remove_input_flags(I::BRKINT); }
}
pub const fn set_input_cr_to_lf(&mut self, enabled: bool) {
if enabled { self.insert_input_flags(I::ICRNL); }
else { self.remove_input_flags(I::ICRNL); }
}
pub const fn set_software_flow(&mut self, enabled: bool) {
if enabled {
self.insert_input_flags(I::IXON);
self.insert_input_flags(I::IXOFF);
} else {
self.remove_input_flags(I::IXON);
self.remove_input_flags(I::IXOFF);
}
}
pub const fn set_output_processing(&mut self, enabled: bool) {
if enabled { self.insert_output_flags(O::OPOST); }
else { self.remove_output_flags(O::OPOST); }
}
pub const fn set_read_min_timeout(&mut self, min: u8, timeout_deciseconds: u8) {
self.set_cc(Cc::VMIN, min);
self.set_cc(Cc::VTIME, timeout_deciseconds);
}
}
#[rustfmt::skip]
impl LinuxTermios {
#[must_use]
pub const fn input_flags(&self) -> I { I::from_c_uint(self.c_iflag) }
pub const fn set_input_flags(&mut self, flags: I) { self.c_iflag = flags.as_c_uint(); }
pub const fn insert_input_flags(&mut self, flags: I) { self.c_iflag |= flags.as_c_uint(); }
pub const fn remove_input_flags(&mut self, flags: I) { self.c_iflag &= !flags.as_c_uint(); }
#[must_use]
pub const fn has_input_flags(&self, flags: I) -> bool {
(self.c_iflag & flags.as_c_uint()) == flags.as_c_uint()
}
#[must_use]
pub const fn output_flags(&self) -> O { O::from_c_uint(self.c_oflag) }
pub const fn set_output_flags(&mut self, flags: O) { self.c_oflag = flags.as_c_uint(); }
pub const fn insert_output_flags(&mut self, flags: O) { self.c_oflag |= flags.as_c_uint(); }
pub const fn remove_output_flags(&mut self, flags: O) { self.c_oflag &= !flags.as_c_uint(); }
#[must_use]
pub const fn has_output_flags(&self, flags: O) -> bool {
(self.c_oflag & flags.as_c_uint()) == flags.as_c_uint()
}
#[must_use]
pub const fn control_flags(&self) -> C { C::from_c_uint(self.c_cflag) }
pub const fn set_control_flags(&mut self, flags: C) { self.c_cflag = flags.as_c_uint(); }
pub const fn insert_control_flags(&mut self, flags: C) { self.c_cflag |= flags.as_c_uint(); }
pub const fn remove_control_flags(&mut self, flags: C) { self.c_cflag &= !flags.as_c_uint(); }
#[must_use]
pub const fn has_control_flags(&self, flags: C) -> bool {
(self.c_cflag & flags.as_c_uint()) == flags.as_c_uint()
}
#[must_use]
pub const fn local_flags(&self) -> L { L::from_c_uint(self.c_lflag) }
pub const fn set_local_flags(&mut self, flags: L) { self.c_lflag = flags.as_c_uint(); }
pub const fn insert_local_flags(&mut self, flags: L) { self.c_lflag |= flags.as_c_uint(); }
pub const fn remove_local_flags(&mut self, flags: L) { self.c_lflag &= !flags.as_c_uint(); }
#[must_use]
pub const fn has_local_flags(&self, flags: L) -> bool {
(self.c_lflag & flags.as_c_uint()) == flags.as_c_uint()
}
}
impl LinuxTermios {
#[must_use]
pub const fn char_size(&self) -> LinuxTermiosCharSize {
let bits = self.c_cflag & C::CSIZE.as_c_uint();
if bits == C::CS6.as_c_uint() {
LinuxTermiosCharSize::Bits6
} else if bits == C::CS7.as_c_uint() {
LinuxTermiosCharSize::Bits7
} else if bits == C::CS8.as_c_uint() {
LinuxTermiosCharSize::Bits8
} else {
LinuxTermiosCharSize::Bits5
}
}
pub const fn set_char_size(&mut self, size: LinuxTermiosCharSize) {
let mask = C::CSIZE.as_c_uint();
self.c_cflag = (self.c_cflag & !mask) | size.to_control_flags().as_c_uint();
}
}
#[rustfmt::skip]
impl LinuxTermios {
#[must_use]
pub const fn cc(&self, cc: Cc) -> u8 { self.c_cc[cc.index()] }
pub const fn set_cc(&mut self, cc: Cc, value: u8) { self.c_cc[cc.index()] = value; }
#[must_use]
pub const fn cc_array(&self) -> &[u8; Cc::COUNT] { &self.c_cc }
#[must_use]
pub const fn cc_array_mut(&mut self) -> &mut [u8; Cc::COUNT] { &mut self.c_cc }
}
#[doc = crate::_tags!(linux term)]
#[doc = crate::_doc_meta!{location("sys/os/linux/io/term")}]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u8)]
pub enum LinuxTermiosCharSize {
Bits5 = 5,
Bits6 = 6,
Bits7 = 7,
#[default]
Bits8 = 8,
}
impl LinuxTermiosCharSize {
#[must_use]
pub const fn to_control_flags(self) -> C {
match self {
Self::Bits5 => C::CS5,
Self::Bits6 => C::CS6,
Self::Bits7 => C::CS7,
Self::Bits8 => C::CS8,
}
}
}