use crate::backend::c; use crate::{backend, io}; use bitflags::bitflags; /// `struct termios` for use with [`tcgetattr`] and [`tcsetattr`]. /// /// [`tcgetattr`]: crate::termios::tcgetattr /// [`tcsetattr`]: crate::termios::tcsetattr #[repr(C)] #[derive(Clone)] pub struct Termios { /// How is input interpreted? #[doc(alias = "c_iflag")] pub input_modes: InputModes, /// How is output translated? #[doc(alias = "c_oflag")] pub output_modes: OutputModes, /// Low-level configuration flags. #[doc(alias = "c_cflag")] pub control_modes: ControlModes, /// High-level configuration flags. #[doc(alias = "c_lflag")] pub local_modes: LocalModes, /// Line discipline. #[doc(alias = "c_line")] #[cfg(not(all(linux_raw, any(target_arch = "powerpc", target_arch = "powerpc64"))))] #[cfg(any( linux_like, target_env = "newlib", target_os = "haiku", target_os = "fuchsia", target_os = "redox" ))] pub line_discipline: c::cc_t, /// How are various special control codes handled? #[doc(alias = "c_cc")] pub special_codes: SpecialCodes, /// Line discipline. // On PowerPC, this field comes after `c_cc`. #[doc(alias = "c_line")] #[cfg(all(linux_raw, any(target_arch = "powerpc", target_arch = "powerpc64")))] pub line_discipline: c::cc_t, /// See the `input_speed` and `set_input_seed` functions. /// /// On Linux and BSDs, this is the arbitrary integer speed value. On all /// other platforms, this is the encoded speed value. pub(crate) input_speed: c::speed_t, /// See the `output_speed` and `set_output_seed` functions. /// /// On Linux and BSDs, this is the integer speed value. On all other /// platforms, this is the encoded speed value. pub(crate) output_speed: c::speed_t, } impl Termios { /// `cfmakeraw(self)`—Set a `Termios` value to the settings for "raw" mode. /// /// In raw mode, input is available a byte at a time, echoing is disabled, /// and special terminal input and output codes are disabled. #[doc(alias = "cfmakeraw")] #[inline] pub fn make_raw(&mut self) { backend::termios::syscalls::cfmakeraw(self) } /// Return the input communication speed. /// /// Unlike the `c_ispeed` field in GLIBC and others, this returns the /// integer value of the speed, rather than the `B*` encoded constant /// value. #[doc(alias = "c_ispeed")] #[doc(alias = "cfgetispeed")] #[doc(alias = "cfgetspeed")] #[inline] pub fn input_speed(&self) -> u32 { // On Linux and BSDs, `input_speed` is the arbitrary integer speed. #[cfg(any(linux_kernel, bsd))] { debug_assert!(u32::try_from(self.input_speed).is_ok()); self.input_speed as u32 } // On other platforms, it's the encoded speed. #[cfg(not(any(linux_kernel, bsd)))] { speed::decode(self.input_speed).unwrap() } } /// Return the output communication speed. /// /// Unlike the `c_ospeed` field in GLIBC and others, this returns the /// arbitrary integer value of the speed, rather than the `B*` encoded /// constant value. #[inline] pub fn output_speed(&self) -> u32 { // On Linux and BSDs, `input_speed` is the arbitrary integer speed. #[cfg(any(linux_kernel, bsd))] { debug_assert!(u32::try_from(self.output_speed).is_ok()); self.output_speed as u32 } // On other platforms, it's the encoded speed. #[cfg(not(any(linux_kernel, bsd)))] { speed::decode(self.output_speed).unwrap() } } /// Set the input and output communication speeds. /// /// Unlike the `c_ispeed` and `c_ospeed` fields in GLIBC and others, this /// takes the arbitrary integer value of the speed, rather than the `B*` /// encoded constant value. Not all implementations support all integer /// values; use the constants in the [`speed`] module for likely-supported /// speeds. #[doc(alias = "cfsetspeed")] #[doc(alias = "CBAUD")] #[doc(alias = "CBAUDEX")] #[doc(alias = "CIBAUD")] #[doc(alias = "CIBAUDEX")] #[inline] pub fn set_speed(&mut self, new_speed: u32) -> io::Result<()> { backend::termios::syscalls::set_speed(self, new_speed) } /// Set the input communication speed. /// /// Unlike the `c_ispeed` field in GLIBC and others, this takes the /// arbitrary integer value of the speed, rather than the `B*` encoded /// constant value. Not all implementations support all integer values; use /// the constants in the [`speed`] module for known-supported speeds. /// /// On some platforms, changing the input speed changes the output speed /// to the same speed. #[doc(alias = "c_ispeed")] #[doc(alias = "cfsetispeed")] #[doc(alias = "CIBAUD")] #[doc(alias = "CIBAUDEX")] #[inline] pub fn set_input_speed(&mut self, new_speed: u32) -> io::Result<()> { backend::termios::syscalls::set_input_speed(self, new_speed) } /// Set the output communication speed. /// /// Unlike the `c_ospeed` field in GLIBC and others, this takes the /// arbitrary integer value of the speed, rather than the `B*` encoded /// constant value. Not all implementations support all integer values; use /// the constants in the [`speed`] module for known-supported speeds. /// /// On some platforms, changing the output speed changes the input speed /// to the same speed. #[doc(alias = "c_ospeed")] #[doc(alias = "cfsetospeed")] #[doc(alias = "CBAUD")] #[doc(alias = "CBAUDEX")] #[inline] pub fn set_output_speed(&mut self, new_speed: u32) -> io::Result<()> { backend::termios::syscalls::set_output_speed(self, new_speed) } } impl core::fmt::Debug for Termios { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { let mut d = f.debug_struct("Termios"); d.field("input_modes", &self.input_modes); d.field("output_modes", &self.output_modes); d.field("control_modes", &self.control_modes); d.field("local_modes", &self.local_modes); #[cfg(any( linux_like, target_env = "newlib", target_os = "haiku", target_os = "fuchsia", target_os = "redox" ))] { d.field("line_discipline", &self.line_discipline); } d.field("special_codes", &self.special_codes); d.field("input_speed", &self.input_speed()); d.field("output_speed", &self.output_speed()); d.finish() } } bitflags! { /// Flags controlling terminal input. #[repr(transparent)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct InputModes: c::tcflag_t { /// `IGNBRK` const IGNBRK = c::IGNBRK; /// `BRKINT` const BRKINT = c::BRKINT; /// `IGNPAR` const IGNPAR = c::IGNPAR; /// `PARMRK` const PARMRK = c::PARMRK; /// `INPCK` const INPCK = c::INPCK; /// `ISTRIP` const ISTRIP = c::ISTRIP; /// `INLCR` const INLCR = c::INLCR; /// `IGNCR` const IGNCR = c::IGNCR; /// `ICRNL` const ICRNL = c::ICRNL; /// `IUCLC` #[cfg(any(linux_kernel, solarish, target_os = "haiku"))] const IUCLC = c::IUCLC; /// `IXON` const IXON = c::IXON; /// `IXANY` #[cfg(not(target_os = "redox"))] const IXANY = c::IXANY; /// `IXOFF` const IXOFF = c::IXOFF; /// `IMAXBEL` #[cfg(not(any(target_os = "haiku", target_os = "redox")))] const IMAXBEL = c::IMAXBEL; /// `IUTF8` #[cfg(not(any( solarish, target_os = "aix", target_os = "dragonfly", target_os = "emscripten", target_os = "freebsd", target_os = "haiku", target_os = "netbsd", target_os = "openbsd", target_os = "redox", )))] const IUTF8 = c::IUTF8; } } bitflags! { /// Flags controlling terminal output. #[repr(transparent)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct OutputModes: c::tcflag_t { /// `OPOST` const OPOST = c::OPOST; /// `OLCUC` #[cfg(not(any( apple, target_os = "aix", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "redox", )))] const OLCUC = c::OLCUC; /// `ONLCR` const ONLCR = c::ONLCR; /// `OCRNL` const OCRNL = c::OCRNL; /// `ONOCR` const ONOCR = c::ONOCR; /// `ONLRET` const ONLRET = c::ONLRET; /// `OFILL` #[cfg(not(bsd))] const OFILL = c::OFILL; /// `OFDEL` #[cfg(not(bsd))] const OFDEL = c::OFDEL; /// `NLDLY` #[cfg(not(any(bsd, solarish, target_os = "redox")))] const NLDLY = c::NLDLY; /// `NL0` #[cfg(not(any(bsd, solarish, target_os = "fuchsia", target_os = "redox")))] const NL0 = c::NL0; /// `NL1` #[cfg(not(any(bsd, solarish, target_os = "fuchsia", target_os = "redox")))] const NL1 = c::NL1; /// `CRDLY` #[cfg(not(any(bsd, solarish, target_os = "redox")))] const CRDLY = c::CRDLY; /// `CR0` #[cfg(not(any(bsd, solarish, target_os = "fuchsia", target_os = "redox")))] const CR0 = c::CR0; /// `CR1` #[cfg(not(any( target_env = "musl", bsd, solarish, target_os = "emscripten", target_os = "fuchsia", target_os = "redox", )))] const CR1 = c::CR1; /// `CR2` #[cfg(not(any( target_env = "musl", bsd, solarish, target_os = "emscripten", target_os = "fuchsia", target_os = "redox", )))] const CR2 = c::CR2; /// `CR3` #[cfg(not(any( target_env = "musl", bsd, solarish, target_os = "emscripten", target_os = "fuchsia", target_os = "redox", )))] const CR3 = c::CR3; /// `TABDLY` #[cfg(not(any( netbsdlike, solarish, target_os = "dragonfly", target_os = "redox", )))] const TABDLY = c::TABDLY; /// `TAB0` #[cfg(not(any( netbsdlike, solarish, target_os = "dragonfly", target_os = "fuchsia", target_os = "redox", )))] const TAB0 = c::TAB0; /// `TAB1` #[cfg(not(any( target_env = "musl", bsd, solarish, target_os = "emscripten", target_os = "fuchsia", target_os = "redox", )))] const TAB1 = c::TAB1; /// `TAB2` #[cfg(not(any( target_env = "musl", bsd, solarish, target_os = "emscripten", target_os = "fuchsia", target_os = "redox", )))] const TAB2 = c::TAB2; /// `TAB3` #[cfg(not(any( target_env = "musl", bsd, solarish, target_os = "emscripten", target_os = "fuchsia", target_os = "redox", )))] const TAB3 = c::TAB3; /// `XTABS` #[cfg(not(any( bsd, solarish, target_os = "aix", target_os = "haiku", target_os = "redox", )))] const XTABS = c::XTABS; /// `BSDLY` #[cfg(not(any(bsd, solarish, target_os = "redox")))] const BSDLY = c::BSDLY; /// `BS0` #[cfg(not(any(bsd, solarish, target_os = "fuchsia", target_os = "redox")))] const BS0 = c::BS0; /// `BS1` #[cfg(not(any( target_env = "musl", bsd, solarish, target_os = "emscripten", target_os = "fuchsia", target_os = "redox", )))] const BS1 = c::BS1; /// `FFDLY` #[cfg(not(any(bsd, solarish, target_os = "redox")))] const FFDLY = c::FFDLY; /// `FF0` #[cfg(not(any(bsd, solarish, target_os = "fuchsia", target_os = "redox")))] const FF0 = c::FF0; /// `FF1` #[cfg(not(any( target_env = "musl", bsd, solarish, target_os = "emscripten", target_os = "fuchsia", target_os = "redox", )))] const FF1 = c::FF1; /// `VTDLY` #[cfg(not(any(bsd, solarish, target_os = "redox")))] const VTDLY = c::VTDLY; /// `VT0` #[cfg(not(any(bsd, solarish, target_os = "fuchsia", target_os = "redox")))] const VT0 = c::VT0; /// `VT1` #[cfg(not(any( target_env = "musl", bsd, solarish, target_os = "emscripten", target_os = "fuchsia", target_os = "redox", )))] const VT1 = c::VT1; } } bitflags! { /// Flags controlling special terminal modes. /// /// `CBAUD`, `CBAUDEX`, `CIBAUD`, and `CIBAUDEX` are not defined here, /// because they're handled automatically by [`Termios::set_speed`] and /// related functions. #[repr(transparent)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct ControlModes: c::tcflag_t { /// `CSIZE` const CSIZE = c::CSIZE; /// `CS5` const CS5 = c::CS5; /// `CS6` const CS6 = c::CS6; /// `CS7` const CS7 = c::CS7; /// `CS8` const CS8 = c::CS8; /// `CSTOPB` const CSTOPB = c::CSTOPB; /// `CREAD` const CREAD = c::CREAD; /// `PARENB` const PARENB = c::PARENB; /// `PARODD` const PARODD = c::PARODD; /// `HUPCL` const HUPCL = c::HUPCL; /// `CLOCAL` const CLOCAL = c::CLOCAL; /// `CRTSCTS` #[cfg(not(any(target_os = "aix", target_os = "redox")))] const CRTSCTS = c::CRTSCTS; /// `CMSPAR` #[cfg(not(any( bsd, solarish, target_os = "aix", target_os = "emscripten", target_os = "haiku", target_os = "redox", )))] const CMSPAR = c::CMSPAR; } } bitflags! { /// Flags controlling "local" terminal modes. #[repr(transparent)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct LocalModes: c::tcflag_t { /// `XCASE` #[cfg(any(linux_kernel, target_arch = "s390x", target_os = "haiku"))] const XCASE = c::XCASE; /// `ECHOCTL` #[cfg(not(target_os = "redox"))] const ECHOCTL = c::ECHOCTL; /// `ECHOPRT` #[cfg(not(target_os = "redox"))] const ECHOPRT = c::ECHOPRT; /// `ECHOKE` #[cfg(not(target_os = "redox"))] const ECHOKE = c::ECHOKE; /// `FLUSHO` #[cfg(not(target_os = "redox"))] const FLUSHO = c::FLUSHO; /// `PENDIN` #[cfg(not(target_os = "redox"))] const PENDIN = c::PENDIN; /// `EXTPROC` #[cfg(not(any(target_os = "aix", target_os = "haiku", target_os = "redox")))] const EXTPROC = c::EXTPROC; /// `ISIG` const ISIG = c::ISIG; /// `ICANON`—A flag for the `c_lflag` field of [`Termios`] indicating /// canonical mode. const ICANON = c::ICANON; /// `ECHO` const ECHO = c::ECHO; /// `ECHOE` const ECHOE = c::ECHOE; /// `ECHOK` const ECHOK = c::ECHOK; /// `ECHONL` const ECHONL = c::ECHONL; /// `NOFLSH` const NOFLSH = c::NOFLSH; /// `TOSTOP` const TOSTOP = c::TOSTOP; /// `IEXTEN` const IEXTEN = c::IEXTEN; } } /// Speeds for use with [`Termios::set_input_speed`] and /// [`Termios::set_output_speed`]. /// /// Unlike in some platforms' libc APIs, these always have the same numerical /// value as their names; for example, `B50` has the value `50`, and so on. /// Consequently, it's not necessary to use them. They are provided here /// because they help identify speeds which are likely to be supported, on /// platforms which don't support arbitrary speeds. pub mod speed { #[cfg(not(bsd))] use crate::backend::c; /// `B0` pub const B0: u32 = 0; /// `B50` pub const B50: u32 = 50; /// `B75` pub const B75: u32 = 75; /// `B110` pub const B110: u32 = 110; /// `B134` pub const B134: u32 = 134; /// `B150` pub const B150: u32 = 150; /// `B200` pub const B200: u32 = 200; /// `B300` pub const B300: u32 = 300; /// `B600` pub const B600: u32 = 600; /// `B1200` pub const B1200: u32 = 1200; /// `B1800` pub const B1800: u32 = 1800; /// `B2400` pub const B2400: u32 = 2400; /// `B4800` pub const B4800: u32 = 4800; /// `B9600` pub const B9600: u32 = 9600; /// `B19200` #[doc(alias = "EXTA")] pub const B19200: u32 = 19200; /// `B38400` #[doc(alias = "EXTB")] pub const B38400: u32 = 38400; /// `B57600` #[cfg(not(target_os = "aix"))] pub const B57600: u32 = 57600; /// `B115200` #[cfg(not(target_os = "aix"))] pub const B115200: u32 = 115_200; /// `B230400` #[cfg(not(target_os = "aix"))] pub const B230400: u32 = 230_400; /// `B460800` #[cfg(not(any( apple, target_os = "aix", target_os = "dragonfly", target_os = "haiku", target_os = "openbsd" )))] pub const B460800: u32 = 460_800; /// `B500000` #[cfg(not(any(bsd, solarish, target_os = "aix", target_os = "haiku")))] pub const B500000: u32 = 500_000; /// `B576000` #[cfg(not(any(bsd, solarish, target_os = "aix", target_os = "haiku")))] pub const B576000: u32 = 576_000; /// `B921600` #[cfg(not(any( apple, target_os = "aix", target_os = "dragonfly", target_os = "haiku", target_os = "openbsd" )))] pub const B921600: u32 = 921_600; /// `B1000000` #[cfg(not(any(bsd, target_os = "aix", target_os = "haiku", target_os = "solaris")))] pub const B1000000: u32 = 1_000_000; /// `B1152000` #[cfg(not(any(bsd, target_os = "aix", target_os = "haiku", target_os = "solaris")))] pub const B1152000: u32 = 1_152_000; /// `B1500000` #[cfg(not(any(bsd, target_os = "aix", target_os = "haiku", target_os = "solaris")))] pub const B1500000: u32 = 1_500_000; /// `B2000000` #[cfg(not(any(bsd, target_os = "aix", target_os = "haiku", target_os = "solaris")))] pub const B2000000: u32 = 2_000_000; /// `B2500000` #[cfg(not(any( target_arch = "sparc", target_arch = "sparc64", bsd, target_os = "aix", target_os = "haiku", target_os = "solaris", )))] pub const B2500000: u32 = 2_500_000; /// `B3000000` #[cfg(not(any( target_arch = "sparc", target_arch = "sparc64", bsd, target_os = "aix", target_os = "haiku", target_os = "solaris", )))] pub const B3000000: u32 = 3_000_000; /// `B3500000` #[cfg(not(any( target_arch = "sparc", target_arch = "sparc64", bsd, target_os = "aix", target_os = "haiku", target_os = "solaris", )))] pub const B3500000: u32 = 3_500_000; /// `B4000000` #[cfg(not(any( target_arch = "sparc", target_arch = "sparc64", bsd, target_os = "aix", target_os = "haiku", target_os = "solaris", )))] pub const B4000000: u32 = 4_000_000; /// Translate from a `c::speed_t` code to an arbitrary integer speed value /// `u32`. #[cfg(not(any(linux_kernel, bsd)))] pub(crate) fn decode(encoded_speed: c::speed_t) -> Option { match encoded_speed { c::B0 => Some(0), c::B50 => Some(50), c::B75 => Some(75), c::B110 => Some(110), c::B134 => Some(134), c::B150 => Some(150), c::B200 => Some(200), c::B300 => Some(300), c::B600 => Some(600), c::B1200 => Some(1200), c::B1800 => Some(1800), c::B2400 => Some(2400), c::B4800 => Some(4800), c::B9600 => Some(9600), c::B19200 => Some(19200), c::B38400 => Some(38400), #[cfg(not(target_os = "aix"))] c::B57600 => Some(57600), #[cfg(not(target_os = "aix"))] c::B115200 => Some(115_200), #[cfg(not(target_os = "aix"))] c::B230400 => Some(230_400), #[cfg(not(any( apple, target_os = "aix", target_os = "dragonfly", target_os = "haiku", target_os = "openbsd" )))] c::B460800 => Some(460_800), #[cfg(not(any(bsd, solarish, target_os = "aix", target_os = "haiku")))] c::B500000 => Some(500_000), #[cfg(not(any(bsd, solarish, target_os = "aix", target_os = "haiku")))] c::B576000 => Some(576_000), #[cfg(not(any( apple, target_os = "aix", target_os = "dragonfly", target_os = "haiku", target_os = "openbsd" )))] c::B921600 => Some(921_600), #[cfg(not(any(bsd, target_os = "aix", target_os = "haiku", target_os = "solaris")))] c::B1000000 => Some(1_000_000), #[cfg(not(any(bsd, target_os = "aix", target_os = "haiku", target_os = "solaris")))] c::B1152000 => Some(1_152_000), #[cfg(not(any(bsd, target_os = "aix", target_os = "haiku", target_os = "solaris")))] c::B1500000 => Some(1_500_000), #[cfg(not(any(bsd, target_os = "aix", target_os = "haiku", target_os = "solaris")))] c::B2000000 => Some(2_000_000), #[cfg(not(any( target_arch = "sparc", target_arch = "sparc64", bsd, target_os = "aix", target_os = "haiku", target_os = "solaris", )))] c::B2500000 => Some(2_500_000), #[cfg(not(any( target_arch = "sparc", target_arch = "sparc64", bsd, target_os = "aix", target_os = "haiku", target_os = "solaris", )))] c::B3000000 => Some(3_000_000), #[cfg(not(any( target_arch = "sparc", target_arch = "sparc64", bsd, target_os = "aix", target_os = "haiku", target_os = "solaris", )))] c::B3500000 => Some(3_500_000), #[cfg(not(any( target_arch = "sparc", target_arch = "sparc64", bsd, target_os = "aix", target_os = "haiku", target_os = "solaris", )))] c::B4000000 => Some(4_000_000), _ => None, } } /// Translate from an arbitrary `u32` arbitrary integer speed value to a /// `c::speed_t` code. #[cfg(not(bsd))] pub(crate) fn encode(speed: u32) -> Option { match speed { 0 => Some(c::B0), 50 => Some(c::B50), 75 => Some(c::B75), 110 => Some(c::B110), 134 => Some(c::B134), 150 => Some(c::B150), 200 => Some(c::B200), 300 => Some(c::B300), 600 => Some(c::B600), 1200 => Some(c::B1200), 1800 => Some(c::B1800), 2400 => Some(c::B2400), 4800 => Some(c::B4800), 9600 => Some(c::B9600), 19200 => Some(c::B19200), 38400 => Some(c::B38400), #[cfg(not(target_os = "aix"))] 57600 => Some(c::B57600), #[cfg(not(target_os = "aix"))] 115_200 => Some(c::B115200), #[cfg(not(target_os = "aix"))] 230_400 => Some(c::B230400), #[cfg(not(any( apple, target_os = "aix", target_os = "dragonfly", target_os = "haiku", target_os = "openbsd" )))] 460_800 => Some(c::B460800), #[cfg(not(any(bsd, solarish, target_os = "aix", target_os = "haiku")))] 500_000 => Some(c::B500000), #[cfg(not(any(bsd, solarish, target_os = "aix", target_os = "haiku")))] 576_000 => Some(c::B576000), #[cfg(not(any( apple, target_os = "aix", target_os = "dragonfly", target_os = "haiku", target_os = "openbsd" )))] 921_600 => Some(c::B921600), #[cfg(not(any(bsd, target_os = "aix", target_os = "haiku", target_os = "solaris")))] 1_000_000 => Some(c::B1000000), #[cfg(not(any(bsd, target_os = "aix", target_os = "haiku", target_os = "solaris")))] 1_152_000 => Some(c::B1152000), #[cfg(not(any(bsd, target_os = "aix", target_os = "haiku", target_os = "solaris")))] 1_500_000 => Some(c::B1500000), #[cfg(not(any(bsd, target_os = "aix", target_os = "haiku", target_os = "solaris")))] 2_000_000 => Some(c::B2000000), #[cfg(not(any( target_arch = "sparc", target_arch = "sparc64", bsd, target_os = "aix", target_os = "haiku", target_os = "solaris", )))] 2_500_000 => Some(c::B2500000), #[cfg(not(any( target_arch = "sparc", target_arch = "sparc64", bsd, target_os = "aix", target_os = "haiku", target_os = "solaris", )))] 3_000_000 => Some(c::B3000000), #[cfg(not(any( target_arch = "sparc", target_arch = "sparc64", bsd, target_os = "aix", target_os = "haiku", target_os = "solaris", )))] 3_500_000 => Some(c::B3500000), #[cfg(not(any( target_arch = "sparc", target_arch = "sparc64", bsd, target_os = "aix", target_os = "haiku", target_os = "solaris", )))] 4_000_000 => Some(c::B4000000), _ => None, } } } /// An array indexed by [`SpecialCodeIndex`] indicating the current values /// of various special control codes. #[repr(transparent)] #[derive(Clone, Debug)] pub struct SpecialCodes(pub(crate) [c::cc_t; c::NCCS as usize]); impl core::ops::Index for SpecialCodes { type Output = c::cc_t; fn index(&self, index: SpecialCodeIndex) -> &Self::Output { &self.0[index.0] } } impl core::ops::IndexMut for SpecialCodes { fn index_mut(&mut self, index: SpecialCodeIndex) -> &mut Self::Output { &mut self.0[index.0] } } /// Indices for use with [`Termios::special_codes`]. pub struct SpecialCodeIndex(usize); #[rustfmt::skip] impl SpecialCodeIndex { /// `VINTR` pub const VINTR: Self = Self(c::VINTR as usize); /// `VQUIT` pub const VQUIT: Self = Self(c::VQUIT as usize); /// `VERASE` pub const VERASE: Self = Self(c::VERASE as usize); /// `VKILL` pub const VKILL: Self = Self(c::VKILL as usize); /// `VEOF` pub const VEOF: Self = Self(c::VEOF as usize); /// `VTIME` pub const VTIME: Self = Self(c::VTIME as usize); /// `VMIN` pub const VMIN: Self = Self(c::VMIN as usize); /// `VSWTC` #[cfg(not(any( apple, solarish, target_os = "aix", target_os = "dragonfly", target_os = "freebsd", target_os = "haiku", target_os = "netbsd", target_os = "openbsd", )))] pub const VSWTC: Self = Self(c::VSWTC as usize); /// `VSTART` pub const VSTART: Self = Self(c::VSTART as usize); /// `VSTOP` pub const VSTOP: Self = Self(c::VSTOP as usize); /// `VSUSP` pub const VSUSP: Self = Self(c::VSUSP as usize); /// `VEOL` pub const VEOL: Self = Self(c::VEOL as usize); /// `VREPRINT` #[cfg(not(target_os = "haiku"))] pub const VREPRINT: Self = Self(c::VREPRINT as usize); /// `VDISCARD` #[cfg(not(any(target_os = "aix", target_os = "haiku")))] pub const VDISCARD: Self = Self(c::VDISCARD as usize); /// `VWERASE` #[cfg(not(any(target_os = "aix", target_os = "haiku")))] pub const VWERASE: Self = Self(c::VWERASE as usize); /// `VLNEXT` #[cfg(not(target_os = "haiku"))] pub const VLNEXT: Self = Self(c::VLNEXT as usize); /// `VEOL2` pub const VEOL2: Self = Self(c::VEOL2 as usize); } /// `TCSA*` values for use with [`tcsetattr`]. /// /// [`tcsetattr`]: crate::termios::tcsetattr #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] #[repr(u32)] pub enum OptionalActions { /// `TCSANOW`—Make the change immediately. #[doc(alias = "TCSANOW")] Now = c::TCSANOW as u32, /// `TCSADRAIN`—Make the change after all output has been transmitted. #[doc(alias = "TCSADRAIN")] Drain = c::TCSADRAIN as u32, /// `TCSAFLUSH`—Discard any pending input and then make the change /// after all output has been transmitted. #[doc(alias = "TCSAFLUSH")] Flush = c::TCSAFLUSH as u32, } /// `TC*` values for use with [`tcflush`]. /// /// [`tcflush`]: crate::termios::tcflush #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] #[repr(u32)] pub enum QueueSelector { /// `TCIFLUSH`—Flush data received but not read. #[doc(alias = "TCIFLUSH")] IFlush = c::TCIFLUSH as u32, /// `TCOFLUSH`—Flush data written but not transmitted. #[doc(alias = "TCOFLUSH")] OFlush = c::TCOFLUSH as u32, /// `TCIOFLUSH`—`IFlush` and `OFlush` combined. #[doc(alias = "TCIOFLUSH")] IOFlush = c::TCIOFLUSH as u32, } /// `TC*` values for use with [`tcflow`]. /// /// [`tcflow`]: crate::termios::tcflow #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] #[repr(u32)] pub enum Action { /// `TCOOFF`—Suspend output. #[doc(alias = "TCOOFF")] OOff = c::TCOOFF as u32, /// `TCOON`—Restart suspended output. #[doc(alias = "TCOON")] OOn = c::TCOON as u32, /// `TCIOFF`—Transmits a STOP byte. #[doc(alias = "TCIOFF")] IOff = c::TCIOFF as u32, /// `TCION`—Transmits a START byte. #[doc(alias = "TCION")] IOn = c::TCION as u32, } /// `struct winsize` for use with [`tcgetwinsize`]. /// /// [`tcgetwinsize`]: crate::termios::tcgetwinsize #[doc(alias = "winsize")] pub type Winsize = c::winsize; #[test] fn termios_layouts() { check_renamed_type!(InputModes, tcflag_t); check_renamed_type!(OutputModes, tcflag_t); check_renamed_type!(ControlModes, tcflag_t); check_renamed_type!(LocalModes, tcflag_t); // On platforms with a termios/termios2 split, check `termios`. #[cfg(linux_raw)] { check_renamed_type!(Termios, termios2); check_renamed_struct_renamed_field!(Termios, termios2, input_modes, c_iflag); check_renamed_struct_renamed_field!(Termios, termios2, output_modes, c_oflag); check_renamed_struct_renamed_field!(Termios, termios2, control_modes, c_cflag); check_renamed_struct_renamed_field!(Termios, termios2, local_modes, c_lflag); check_renamed_struct_renamed_field!(Termios, termios2, line_discipline, c_line); check_renamed_struct_renamed_field!(Termios, termios2, special_codes, c_cc); check_renamed_struct_renamed_field!(Termios, termios2, input_speed, c_ispeed); check_renamed_struct_renamed_field!(Termios, termios2, output_speed, c_ospeed); // We assume that `termios` has the same layout as `termios2` minus the // `c_ispeed` and `c_ospeed` fields. check_renamed_struct_renamed_field!(Termios, termios, input_modes, c_iflag); check_renamed_struct_renamed_field!(Termios, termios, output_modes, c_oflag); check_renamed_struct_renamed_field!(Termios, termios, control_modes, c_cflag); check_renamed_struct_renamed_field!(Termios, termios, local_modes, c_lflag); check_renamed_struct_renamed_field!(Termios, termios, special_codes, c_cc); // On everything except PowerPC, `termios` matches `termios2` except for // the addition of `c_ispeed` and `c_ospeed`. #[cfg(not(any(target_arch = "powerpc", target_arch = "powerpc64")))] assert_eq!( memoffset::offset_of!(Termios, input_speed), core::mem::size_of::() ); // On PowerPC, `termios2` is `termios`. #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))] assert_eq!( core::mem::size_of::(), core::mem::size_of::() ); } #[cfg(not(linux_raw))] { #[cfg(not(all( target_env = "gnu", any( target_arch = "sparc", target_arch = "sparc64", target_arch = "mips", target_arch = "mips64" ) )))] check_renamed_type!(Termios, termios); check_renamed_struct_renamed_field!(Termios, termios, input_modes, c_iflag); check_renamed_struct_renamed_field!(Termios, termios, output_modes, c_oflag); check_renamed_struct_renamed_field!(Termios, termios, control_modes, c_cflag); check_renamed_struct_renamed_field!(Termios, termios, local_modes, c_lflag); #[cfg(any( linux_like, target_env = "newlib", target_os = "haiku", target_os = "fuchsia", target_os = "redox" ))] check_renamed_struct_renamed_field!(Termios, termios, line_discipline, c_line); check_renamed_struct_renamed_field!(Termios, termios, special_codes, c_cc); #[cfg(not(any( linux_kernel, solarish, target_os = "emscripten", target_os = "fuchsia" )))] { check_renamed_struct_renamed_field!(Termios, termios, input_speed, c_ispeed); check_renamed_struct_renamed_field!(Termios, termios, output_speed, c_ospeed); } #[cfg(any(target_env = "musl", target_os = "fuchsia"))] { check_renamed_struct_renamed_field!(Termios, termios, input_speed, __c_ispeed); check_renamed_struct_renamed_field!(Termios, termios, output_speed, __c_ospeed); } } check_renamed_type!(OptionalActions, c_int); check_renamed_type!(QueueSelector, c_int); check_renamed_type!(Action, c_int); } #[test] #[cfg(not(any(solarish, target_os = "emscripten")))] fn termios_legacy() { // Check that our doc aliases above are correct. assert_eq!(c::EXTA, c::B19200); assert_eq!(c::EXTB, c::B38400); } #[cfg(bsd)] #[test] fn termios_bsd() { // On BSD platforms we can assume that the `B*` constants have their // arbitrary integer speed value. Confirm this. assert_eq!(c::B0, 0); assert_eq!(c::B50, 50); assert_eq!(c::B19200, 19200); assert_eq!(c::B38400, 38400); } #[test] #[cfg(not(bsd))] fn termios_speed_encoding() { assert_eq!(speed::encode(0), Some(c::B0)); assert_eq!(speed::encode(50), Some(c::B50)); assert_eq!(speed::encode(19200), Some(c::B19200)); assert_eq!(speed::encode(38400), Some(c::B38400)); assert_eq!(speed::encode(1), None); assert_eq!(speed::encode(!0), None); #[cfg(not(linux_kernel))] { assert_eq!(speed::decode(c::B0), Some(0)); assert_eq!(speed::decode(c::B50), Some(50)); assert_eq!(speed::decode(c::B19200), Some(19200)); assert_eq!(speed::decode(c::B38400), Some(38400)); } } #[cfg(linux_kernel)] #[test] fn termios_ioctl_contiguity() { // When using `termios2`, we assume that we can add the optional actions // value to the ioctl request code. Test this assumption. assert_eq!(c::TCSETS2, c::TCSETS2 + 0); assert_eq!(c::TCSETSW2, c::TCSETS2 + 1); assert_eq!(c::TCSETSF2, c::TCSETS2 + 2); assert_eq!(c::TCSANOW - c::TCSANOW, 0); assert_eq!(c::TCSADRAIN - c::TCSANOW, 1); assert_eq!(c::TCSAFLUSH - c::TCSANOW, 2); // MIPS is different here. #[cfg(any(target_arch = "mips", target_arch = "mips64"))] { assert_eq!(i128::from(c::TCSANOW) - i128::from(c::TCSETS), 0); assert_eq!(i128::from(c::TCSADRAIN) - i128::from(c::TCSETS), 1); assert_eq!(i128::from(c::TCSAFLUSH) - i128::from(c::TCSETS), 2); } #[cfg(not(any(target_arch = "mips", target_arch = "mips64")))] { assert_eq!(c::TCSANOW, 0); assert_eq!(c::TCSADRAIN, 1); assert_eq!(c::TCSAFLUSH, 2); } } #[cfg(linux_kernel)] #[test] fn termios_cibaud() { // Test an assumption. assert_eq!(c::CIBAUD, c::CBAUD << c::IBSHIFT); }