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