| //! The `termios` crate provides Rust bindings for the POSIX termios API that is implemented on |
| //! Unix operating systems. The termios API is defined in the [IEEE Std 1003.1 ("POSIX.1") |
| //! specification](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/termios.h.html). |
| //! |
| //! ## Getting Started |
| //! |
| //! The termios API operates on file descriptors that are associated with terminal devices, e.g., |
| //! `/dev/tty*`. When used with other file descriptors, termios functions return an error. All |
| //! functions that are part of the POSIX standard are included in the `termios` crate. Where file |
| //! descriptors are expected, the type `std::os::unix::io::RawFd` is used, and integer error codes |
| //! are translated to `std::io::Result`. |
| //! |
| //! A major feature of the termios API is configuring a terminal device's parameters. The POSIX |
| //! standard defines a `termios` structure that contains the parameters and several functions for |
| //! manipulating the parameters. The `termios` crate defines a safe constructor that returns a |
| //! [`Termios`](struct.Termios.html) struct populated with the parameters of an open terminal |
| //! device: |
| //! |
| //! ```no_run |
| //! use termios::*; |
| //! # let fd = 1; |
| //! let mut termios = Termios::from_fd(fd).unwrap(); |
| //! ``` |
| //! |
| //! The [`Termios`](struct.Termios.html) struct provides access to the fields defined in the POSIX |
| //! standard (`c_iflag`, `c_oflag`, `c_cflag`, `c_lflag`, and `c_cc`): |
| //! |
| //! ```no_run |
| //! # use termios::*; |
| //! # let fd = 1; |
| //! # let mut termios = Termios::from_fd(fd).unwrap(); |
| //! termios.c_cflag |= CREAD | CLOCAL; |
| //! termios.c_lflag &= !(ICANON | ECHO | ECHOE | ECHOK | ECHONL | ISIG | IEXTEN); |
| //! termios.c_oflag &= !OPOST; |
| //! termios.c_iflag &= !(INLCR | IGNCR | ICRNL | IGNBRK); |
| //! |
| //! termios.c_cc[VMIN] = 0; |
| //! termios.c_cc[VTIME] = 0; |
| //! ``` |
| //! |
| //! The [`Termios`](struct.Termios.html) struct can also be manipulated using any of the standard |
| //! termios API functions: |
| //! |
| //! ```no_run |
| //! # use termios::*; |
| //! # let fd = 1; |
| //! # let mut termios = Termios::from_fd(fd).unwrap(); |
| //! cfgetispeed(&termios); |
| //! cfgetospeed(&termios); |
| //! cfsetispeed(&mut termios, B9600).unwrap(); |
| //! cfsetospeed(&mut termios, B9600).unwrap(); |
| //! tcsetattr(fd, TCSANOW, &termios).unwrap(); |
| //! ``` |
| //! |
| //! ## Portability |
| //! |
| //! The `termios` crate is organized in a way to help write portable code, while also allowing |
| //! access to OS-specific functionality when necessary. |
| //! |
| //! The crate root contains types, constants, and function definitions that are common across Unix |
| //! operating systems. Most of the definitions in the crate root are from the POSIX standard; |
| //! however, support for the standard may differ across operating systems. A couple functions in |
| //! the crate root are not part of the POSIX standard, but are included in the crate root because |
| //! they are widely available across Unix operating systems. |
| //! |
| //! To write portable code, import the `termios` crate and use only the definitions from the crate |
| //! root. |
| //! |
| //! ### OS-Specific Extensions |
| //! |
| //! Each operating system may define extensions to the POSIX API. To make it clear when code |
| //! depends on OS-specific definitions, any non-standard definitions are exported in the |
| //! `termios::os` module. Programs that depend on OS-specific functionality must explicity opt-in. |
| //! When writing portable code that depends on OS-specific definitions, it will often be necessary |
| //! to use `#[cfg(...)]` attributes to support alternative implementations. The following is an |
| //! example of a portable function that sets the maximum speed on a `Termios` struct. |
| //! |
| //! ```no_run |
| //! use std::io; |
| //! use termios::{Termios,cfsetspeed}; |
| //! |
| //! #[cfg(target_os = "linux")] |
| //! fn set_fastest_speed(termios: &mut Termios) -> io::Result<()> { |
| //! cfsetspeed(termios, termios::os::linux::B4000000) |
| //! } |
| //! |
| //! #[cfg(target_os = "macos")] |
| //! fn set_fastest_speed(termios: &mut Termios) -> io::Result<()> { |
| //! cfsetspeed(termios, termios::os::macos::B230400) |
| //! } |
| //! |
| //! #[cfg(target_os = "freebsd")] |
| //! fn set_fastest_speed(termios: &mut Termios) -> io::Result<()> { |
| //! cfsetspeed(termios, termios::os::freebsd::B921600) |
| //! } |
| //! |
| //! #[cfg(target_os = "openbsd")] |
| //! fn set_fastest_speed(termios: &mut Termios) -> io::Result<()> { |
| //! cfsetspeed(termios, termios::os::openbsd::B921600) |
| //! } |
| //! |
| //! #[cfg(target_os = "netbsd")] |
| //! fn set_fastest_speed(termios: &mut Termios) -> io::Result<()> { |
| //! cfsetspeed(termios, termios::os::netbsd::B921600) |
| //! } |
| //! |
| //! #[cfg(target_os = "dragonfly")] |
| //! fn set_fastest_speed(termios: &mut Termios) -> io::Result<()> { |
| //! cfsetspeed(termios, termios::os::dragonfly::B230400) |
| //! } |
| //! |
| //! #[cfg(target_os = "solaris")] |
| //! fn set_fastest_speed(termios: &mut Termios) -> io::Result<()> { |
| //! cfsetspeed(termios, termios::os::solaris::B921600) |
| //! } |
| //! |
| //! # let fd = 1; |
| //! let mut termios = Termios::from_fd(fd).unwrap(); |
| //! set_fastest_speed(&mut termios).unwrap(); |
| //! ``` |
| |
| extern crate libc; |
| |
| use std::io; |
| use std::mem; |
| use std::ops::{Deref,DerefMut}; |
| use std::os::unix::io::RawFd; |
| |
| use libc::{c_int,pid_t}; |
| |
| pub use ::os::target::{cc_t,speed_t,tcflag_t}; // types |
| pub use ::os::target::{VEOF,VEOL,VERASE,VINTR,VKILL,VMIN,VQUIT,VSTART,VSTOP,VSUSP,VTIME}; // c_cc subscripts |
| pub use ::os::target::{BRKINT,ICRNL,IGNBRK,IGNCR,IGNPAR,INLCR,INPCK,ISTRIP,IXANY,IXOFF,IXON,PARMRK}; // input modes |
| pub use ::os::target::{OPOST,ONLCR,OCRNL,ONOCR,ONLRET}; // output modes |
| pub use ::os::target::{B0,B50,B75,B110,B134,B150,B200,B300,B600,B1200,B1800,B2400,B4800,B9600,B19200,B38400}; // baud rate selection |
| pub use ::os::target::{CSIZE,CS5,CS6,CS7,CS8,CSTOPB,CREAD,PARENB,PARODD,HUPCL,CLOCAL}; // control modes |
| pub use ::os::target::{ECHO,ECHOE,ECHOK,ECHONL,ICANON,IEXTEN,ISIG,NOFLSH,TOSTOP}; // local modes |
| pub use ::os::target::{TCSANOW,TCSADRAIN,TCSAFLUSH}; // attribute selection |
| pub use ::os::target::{TCIFLUSH,TCIOFLUSH,TCOFLUSH,TCIOFF,TCION,TCOOFF,TCOON}; // line control |
| |
| pub mod ffi; |
| pub mod os; |
| |
| |
| /// Unix terminal I/O control structure. |
| /// |
| /// The `Termios` structure is a thin wrapper for the OS-specific `termios` struct. The only safe |
| /// way to obtain a `Termios` structure is to fill one from a file descriptor with |
| /// [`Termios::from_fd()`](#method.from_fd), after which it can be treated just like the POSIX |
| /// `termios` struct. It provides access to the standard fields of the `termios` struct (`c_iflag`, |
| /// `c_oflag`, `c_cflag`, `c_lflag`, and `c_cc`) through the `Deref` and `DerefMut` traits. |
| /// |
| /// ## Example |
| /// |
| /// The following is an example of how one might setup a file descriptor for a serial port: |
| /// |
| /// ```no_run |
| /// use std::io; |
| /// use std::os::unix::io::RawFd; |
| /// |
| /// fn setup_serial(fd: RawFd) -> io::Result<()> { |
| /// use termios::*; |
| /// |
| /// let mut termios = try!(Termios::from_fd(fd)); |
| /// |
| /// termios.c_cflag |= CREAD | CLOCAL; |
| /// termios.c_lflag &= !(ICANON | ECHO | ECHOE | ECHOK | ECHONL | ISIG | IEXTEN); |
| /// termios.c_oflag &= !OPOST; |
| /// termios.c_iflag &= !(INLCR | IGNCR | ICRNL | IGNBRK); |
| /// |
| /// termios.c_cc[VMIN] = 0; |
| /// termios.c_cc[VTIME] = 0; |
| /// |
| /// try!(cfsetspeed(&mut termios, B9600)); |
| /// try!(tcsetattr(fd, TCSANOW, &mut termios)); |
| /// |
| /// Ok(()) |
| /// } |
| /// ``` |
| #[derive(Debug,Copy,Clone,Eq,PartialEq)] |
| pub struct Termios { |
| inner: ::os::target::termios |
| } |
| |
| impl Termios { |
| /// Creates a `Termios` structure based on the current settings of a file descriptor. |
| /// |
| /// `fd` must be an open file descriptor for a terminal device. |
| pub fn from_fd(fd: RawFd) -> io::Result<Self> { |
| let mut termios = unsafe { mem::uninitialized() }; |
| |
| match tcgetattr(fd, &mut termios) { |
| Ok(_) => Ok(termios), |
| Err(err) => Err(err) |
| } |
| } |
| |
| fn inner(&self) -> &::os::target::termios { |
| &self.inner |
| } |
| |
| fn inner_mut(&mut self) -> &mut ::os::target::termios { |
| &mut self.inner |
| } |
| } |
| |
| impl Deref for Termios { |
| type Target = ::os::target::termios; |
| |
| fn deref(&self) -> &::os::target::termios { |
| self.inner() |
| } |
| } |
| |
| impl DerefMut for Termios { |
| fn deref_mut(&mut self) -> &mut ::os::target::termios { |
| self.inner_mut() |
| } |
| } |
| |
| |
| /// Gets the input baud rate stored in a `Termios` structure. |
| /// |
| /// # Examples |
| /// |
| /// ``` |
| /// # use std::mem; |
| /// # use termios::{Termios,B9600,cfsetispeed,cfgetispeed}; |
| /// # let mut termios = unsafe { mem::uninitialized() }; |
| /// cfsetispeed(&mut termios, B9600).unwrap(); |
| /// assert_eq!(cfgetispeed(&termios), B9600); |
| /// ``` |
| pub fn cfgetispeed(termios: &Termios) -> speed_t { |
| unsafe { ffi::cfgetispeed(termios.inner()) } |
| } |
| |
| /// Gets the output baud rate stored in a `Termios` structure. |
| /// |
| /// # Examples |
| /// |
| /// ``` |
| /// # use std::mem; |
| /// # use termios::{Termios,B9600,cfsetospeed,cfgetospeed}; |
| /// # let mut termios = unsafe { mem::uninitialized() }; |
| /// cfsetospeed(&mut termios, B9600).unwrap(); |
| /// assert_eq!(cfgetospeed(&termios), B9600); |
| /// ``` |
| pub fn cfgetospeed(termios: &Termios) -> speed_t { |
| unsafe { ffi::cfgetospeed(termios.inner()) } |
| } |
| |
| /// Sets the input baud rate. |
| /// |
| /// This function only sets the necessary values on the given `Termios` structure. The settings are |
| /// applied by a subsequent call to [`tcsetattr()`](fn.tcsetattr.html). |
| /// |
| /// # Parameters |
| /// |
| /// * `termios` should be a mutable reference to a `Termios` structure. |
| /// * `speed` should be one of the baud rate constants: |
| /// - `B0` |
| /// - `B50` |
| /// - `B75` |
| /// - `B110` |
| /// - `B134` |
| /// - `B150` |
| /// - `B200` |
| /// - `B300` |
| /// - `B600` |
| /// - `B1200` |
| /// - `B1800` |
| /// - `B2400` |
| /// - `B4800` |
| /// - `B9600` |
| /// - `B19200` |
| /// - `B38400` |
| /// - any OS-specific baud rate defined in [`termios::os`](os/index.html). |
| /// |
| /// A value of `B0` for `speed` sets the input baud rate to be the same as the output baud rate. |
| /// |
| /// # Examples |
| /// |
| /// ``` |
| /// # use std::mem; |
| /// # use termios::{Termios,B9600,cfsetispeed,cfgetispeed}; |
| /// # let mut termios = unsafe { mem::uninitialized() }; |
| /// cfsetispeed(&mut termios, B9600).unwrap(); |
| /// assert_eq!(cfgetispeed(&termios), B9600); |
| /// ``` |
| pub fn cfsetispeed(termios: &mut Termios, speed: speed_t) -> io::Result<()> { |
| io_result(unsafe { ffi::cfsetispeed(termios.inner_mut(), speed) }) |
| } |
| |
| /// Sets the output baud rate. |
| /// |
| /// This function only sets the necessary values on the given `Termios` structure. The settings are |
| /// applied on a successful call to [`tcsetattr()`](fn.tcsetattr.html). |
| /// |
| /// # Parameters |
| /// |
| /// * `termios` should be a mutable reference to a `Termios` structure. |
| /// * `speed` should be one of the baud rate constants: |
| /// - `B0` (hang up) |
| /// - `B50` |
| /// - `B75` |
| /// - `B110` |
| /// - `B134` |
| /// - `B150` |
| /// - `B200` |
| /// - `B300` |
| /// - `B600` |
| /// - `B1200` |
| /// - `B1800` |
| /// - `B2400` |
| /// - `B4800` |
| /// - `B9600` |
| /// - `B19200` |
| /// - `B38400` |
| /// - any OS-specific baud rate defined in [`termios::os`](os/index.html). |
| /// |
| /// A value of `B0` for `speed` deasserts the modem control lines when applied with |
| /// [`tcsetattr()`](fn.tcsetattr.html). This normally has the effect of disconnecting the line. |
| /// |
| /// # Examples |
| /// |
| /// ``` |
| /// # use std::mem; |
| /// # use termios::{Termios,B9600,cfsetospeed,cfgetospeed}; |
| /// # let mut termios = unsafe { mem::uninitialized() }; |
| /// cfsetospeed(&mut termios, B9600).unwrap(); |
| /// assert_eq!(cfgetospeed(&termios), B9600); |
| /// ``` |
| pub fn cfsetospeed(termios: &mut Termios, speed: speed_t) -> io::Result<()> { |
| io_result(unsafe { ffi::cfsetospeed(termios.inner_mut(), speed) }) |
| } |
| |
| /// Sets input and output baud rates. |
| /// |
| /// This function only sets the necessary values on the given `Termios` structure. The settings are |
| /// applied on a successful call to [`tcsetattr()`](fn.tcsetattr.html). |
| /// |
| /// # Parameters |
| /// |
| /// * `termios` should be a mutable reference to a `Termios` structure. |
| /// * `speed` should be one of the baud rate constants: |
| /// - `B0` |
| /// - `B50` |
| /// - `B75` |
| /// - `B110` |
| /// - `B134` |
| /// - `B150` |
| /// - `B200` |
| /// - `B300` |
| /// - `B600` |
| /// - `B1200` |
| /// - `B1800` |
| /// - `B2400` |
| /// - `B4800` |
| /// - `B9600` |
| /// - `B19200` |
| /// - `B38400` |
| /// - any OS-specific baud rate defined in [`termios::os`](os/index.html). |
| /// |
| /// # Examples |
| /// |
| /// ``` |
| /// # use std::mem; |
| /// # use termios::{Termios,B9600,cfsetspeed,cfgetispeed,cfgetospeed}; |
| /// # let mut termios = unsafe { mem::uninitialized() }; |
| /// cfsetspeed(&mut termios, B9600).unwrap(); |
| /// assert_eq!(cfgetispeed(&termios), B9600); |
| /// assert_eq!(cfgetospeed(&termios), B9600); |
| /// ``` |
| /// |
| /// # Portability |
| /// |
| /// This function is not part of the IEEE Std 1003.1 ("POSIX.1") specification, but it is available |
| /// on Linux, BSD, and OS X. |
| pub fn cfsetspeed(termios: &mut Termios, speed: speed_t) -> io::Result<()> { |
| io_result(unsafe { ffi::cfsetspeed(termios.inner_mut(), speed) }) |
| } |
| |
| /// Sets flags to disable all input and output processing. |
| /// |
| /// This function only sets the necessary values on the given `Termios` structure. The settings are |
| /// applied on a successful call to [`tcsetattr()`](fn.tcsetattr.html). |
| /// |
| /// # Portability |
| /// |
| /// This function is not part of the IEEE Std 1003.1 ("POSIX.1") specification, but it is available |
| /// on Linux, BSD, and OS X. |
| pub fn cfmakeraw(termios: &mut Termios) { |
| unsafe { ffi::cfmakeraw(termios.inner_mut()) }; |
| } |
| |
| /// Blocks until all output written to the file descriptor is transmitted. |
| /// |
| /// # Parameters |
| /// |
| /// * `fd` should be an open file descriptor associated with a terminal. |
| pub fn tcdrain(fd: RawFd) -> io::Result<()> { |
| io_result(unsafe { ffi::tcdrain(fd) }) |
| } |
| |
| /// Suspends or restarts transmission or reception of data. |
| /// |
| /// # Parameters |
| /// |
| /// * `fd` should be an open file descriptor associated with a terminal. |
| /// * `action` should be one of the following constants: |
| /// - `TCOOFF` suspends output. |
| /// - `TCOON` restarts output. |
| /// - `TCIOFF` transmits a STOP character, intended to cause the remote device to stop |
| /// transmitting. |
| /// - `TCION` transmits a START character, intended to cause the remote device to resume |
| /// transmitting. |
| pub fn tcflow(fd: RawFd, action: c_int) -> io::Result<()> { |
| io_result(unsafe { ffi::tcflow(fd, action) }) |
| } |
| |
| /// Discards data waiting in the terminal device's buffers. |
| /// |
| /// `tcflush()` discards data that has been written to the device by an application but has not yet |
| /// been transmitted by the hardware or data that has been received by the hardware but has not yet |
| /// been read by an application. |
| /// |
| /// # Parameters |
| /// |
| /// * `fd` should be an open file descriptor associated with a terminal. |
| /// * `queue_selector` should be one of: |
| /// - `TCIFLUSH` to discard data received but not read. |
| /// - `TCOFLUSH` to discard data written but not transmitted. |
| /// - `TCIOFLUSH` to discard both data received but not read and data written but not |
| /// transmitted. |
| pub fn tcflush(fd: RawFd, queue_selector: c_int) -> io::Result<()> { |
| io_result(unsafe { ffi::tcflush(fd, queue_selector) }) |
| } |
| |
| /// Populates a `Termios` structure with parameters associated with a terminal. |
| /// |
| /// Upon successful completion, the `Termios` structure referred to by the `termios` parameter will |
| /// contain the parameters associated with the terminal device referred to by `fd`. |
| /// |
| /// # Parameters |
| /// |
| /// * `fd` should be an open file descriptor associated with a terminal. |
| /// * `termios` should be a mutable reference to the `Termios` structure that will hold the |
| /// terminal device's parameters. |
| pub fn tcgetattr(fd: RawFd, termios: &mut Termios) -> io::Result<()> { |
| io_result(unsafe { ffi::tcgetattr(fd, termios.inner_mut()) }) |
| } |
| |
| /// Sets a terminal device's parameters. |
| /// |
| /// `tcsetattr()` returns successfully if it was able to perform any of the requested actions, even |
| /// if other requested actions could not be performed. It will set all attributes that the |
| /// implementation supports and leave others unchanged. The `Termios` structure will not be updated |
| /// to reflect the changes that were applied. |
| /// |
| /// In order to determine which parameters were applied to the terminal device, an application |
| /// should use [`tcgetattr()`](fn.tcgetattr.html) to obtain the latest state of the terminal |
| /// device. In particular, when attempting to change baud rates, [`tcgetattr()`](fn.tcgetattr.html) |
| /// can be used to determine which baud rates were actually selected. |
| /// |
| /// If none of the requested actions could be performed, then `tcsetattr()` returns an error. |
| /// |
| /// # Parameters |
| /// |
| /// * `fd` should be an open file descriptor associated with a terminal. |
| /// * `action` should be one of the constants: |
| /// - `TCSANOW` applies the change immediately. |
| /// - `TCSADRAIN` applies the change after all output previously written to `fd` is transmitted. |
| /// This mode should be used when changing parameters that affect output. |
| /// - `TCSAFLUSH` applies the change after all output previously written to `fd` is transmitted. |
| /// All data received but not read is discarded before applying the change. |
| /// * `termios` should be a mutable reference to a `Termios` structure containing the parameters to |
| /// apply to the terminal device. |
| pub fn tcsetattr(fd: RawFd, action: c_int, termios: &Termios) -> io::Result<()> { |
| io_result(unsafe { ffi::tcsetattr(fd, action, termios.inner()) }) |
| } |
| |
| /// Returns the process group ID of the controlling terminal's session. |
| /// |
| /// # Parameters |
| /// |
| /// * `fd` should be an open file descriptor associated with a controlling terminal. |
| pub fn tcgetsid(fd: RawFd) -> pid_t { |
| unsafe { ffi::tcgetsid(fd) } |
| } |
| |
| /// Transmits data to generate a break condition. |
| /// |
| /// If the terminal device is using asynchronous data transmission, `tcsendbreak()` transmits a |
| /// continuous stream of zero bits for a specific duration. |
| /// |
| /// # Parameters |
| /// |
| /// * `fd` should be an open file descriptor associated with a terminal. |
| /// * `duration` controls the duration of the transmitted zero bits. A value of 0 causes a |
| /// transmission between 0.25 and 0.5 seconds. A value other than 0 causes a transmission for an |
| /// implementation-defined period of time. |
| pub fn tcsendbreak(fd: RawFd, duration: c_int) -> io::Result<()> { |
| io_result(unsafe { ffi::tcsendbreak(fd, duration) }) |
| } |
| |
| |
| #[inline] |
| fn io_result(result: c_int) -> io::Result<()> { |
| match result { |
| 0 => Ok(()), |
| _ => Err(io::Error::last_os_error()) |
| } |
| } |