Skip to main content

termion/
raw.rs

1//! Managing raw mode.
2//!
3//! Raw mode is a particular state a TTY can have. It signifies that:
4//!
5//! 1. No line buffering (the input is given byte-by-byte).
6//! 2. The input is not written out, instead it has to be done manually by the programmer.
7//! 3. The output is not canonicalized (for example, `\n` means "go one line down", not "line
8//!    break").
9//!
10//! It is essential to design terminal programs.
11//!
12//! # Example
13//!
14//! ```rust,no_run
15//! use termion::raw::IntoRawMode;
16//! use std::io::{Write, stdout};
17//!
18//! let mut stdout = stdout().into_raw_mode()?;
19//! write!(stdout, "Hey there.").unwrap();
20//! # std::io::Result::Ok(())
21//! ```
22
23use std::{
24    io::{self, Write},
25    ops,
26    os::fd::AsFd,
27};
28
29use sys::attr::{get_terminal_attr, raw_terminal_attr, set_terminal_attr};
30use sys::Termios;
31
32/// The timeout of an escape code control sequence, in milliseconds.
33pub const CONTROL_SEQUENCE_TIMEOUT: u64 = 100;
34
35/// A terminal restorer, which keeps the previous state of the terminal, and restores it, when
36/// dropped.
37///
38/// Restoring will entirely bring back the old TTY state.
39pub struct RawTerminal<W: Write + AsFd> {
40    prev_ios: Termios,
41    output: W,
42}
43
44impl<W: Write + AsFd> Drop for RawTerminal<W> {
45    fn drop(&mut self) {
46        let _ = set_terminal_attr(self.output.as_fd(), &self.prev_ios);
47    }
48}
49
50impl<W: Write + AsFd> ops::Deref for RawTerminal<W> {
51    type Target = W;
52
53    fn deref(&self) -> &W {
54        &self.output
55    }
56}
57
58impl<W: Write + AsFd> ops::DerefMut for RawTerminal<W> {
59    fn deref_mut(&mut self) -> &mut W {
60        &mut self.output
61    }
62}
63
64impl<W: Write + AsFd> Write for RawTerminal<W> {
65    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
66        self.output.write(buf)
67    }
68
69    fn flush(&mut self) -> io::Result<()> {
70        self.output.flush()
71    }
72}
73
74#[cfg(unix)]
75mod unix_impl {
76    use super::*;
77    use std::os::unix::io::{AsFd, BorrowedFd};
78
79    impl<W: Write + AsFd> AsFd for RawTerminal<W> {
80        fn as_fd(&self) -> BorrowedFd {
81            self.output.as_fd()
82        }
83    }
84}
85
86/// Types which can be converted into "raw mode".
87///
88/// # Why is this type defined on writers and not readers?
89///
90/// TTYs has their state controlled by the writer, not the reader. You use the writer to clear the
91/// screen, move the cursor and so on, so naturally you use the writer to change the mode as well.
92pub trait IntoRawMode: Write + AsFd + Sized {
93    /// Switch to raw mode.
94    ///
95    /// Raw mode means that stdin won't be printed (it will instead have to be written manually by
96    /// the program). Furthermore, the input isn't canonicalised or buffered (that is, you can
97    /// read from stdin one byte of a time). The output is neither modified in any way.
98    fn into_raw_mode(self) -> io::Result<RawTerminal<Self>>;
99}
100
101impl<W: Write + AsFd> IntoRawMode for W {
102    fn into_raw_mode(self) -> io::Result<RawTerminal<W>> {
103        let mut ios = get_terminal_attr(self.as_fd())?;
104        let prev_ios = ios;
105
106        raw_terminal_attr(&mut ios);
107
108        set_terminal_attr(self.as_fd(), &ios)?;
109
110        Ok(RawTerminal {
111            prev_ios,
112            output: self,
113        })
114    }
115}
116
117impl<W: Write + AsFd> RawTerminal<W> {
118    /// Temporarily switch to original mode
119    pub fn suspend_raw_mode(&self) -> io::Result<()> {
120        set_terminal_attr(self.as_fd(), &self.prev_ios)?;
121        Ok(())
122    }
123
124    /// Temporarily switch to raw mode
125    pub fn activate_raw_mode(&self) -> io::Result<()> {
126        let mut ios = get_terminal_attr(self.as_fd())?;
127        raw_terminal_attr(&mut ios);
128        set_terminal_attr(self.as_fd(), &ios)?;
129        Ok(())
130    }
131}
132
133#[cfg(test)]
134mod test {
135    use super::*;
136    use std::io::{stdout, Write};
137
138    #[test]
139    fn test_into_raw_mode() {
140        let mut out = stdout().into_raw_mode().unwrap();
141
142        out.write_all(b"this is a test, muahhahahah\r\n").unwrap();
143
144        drop(out);
145    }
146}