raw.rs 3.39 KB
Newer Older
ticki's avatar
ticki committed
1 2 3 4 5 6 7 8 9 10 11 12 13
//! Managing raw mode.
//!
//! Raw mode is a particular state a TTY can have. It signifies that:
//!
//! 1. No line buffering (the input is given byte-by-byte).
//! 2. The input is not written out, instead it has to be done manually by the programmer.
//! 3. The output is not canonicalized (for example, `\n` means "go one line down", not "line
//!    break").
//!
//! It is essential to design terminal programs.
//!
//! # Example
//!
14
//! ```rust,no_run
ticki's avatar
ticki committed
15 16 17 18 19 20 21 22 23
//! use termion::raw::IntoRawMode;
//! use std::io::{Write, stdout};
//!
//! fn main() {
//!     let mut stdout = stdout().into_raw_mode().unwrap();
//!
//!     write!(stdout, "Hey there.").unwrap();
//! }
//! ```
24

Ticki's avatar
Ticki committed
25
use std::io::{self, Write};
ticki's avatar
ticki committed
26
use std::ops;
Ticki's avatar
Ticki committed
27

28 29 30
use sys::Termios;
use sys::attr::{get_terminal_attr, raw_terminal_attr, set_terminal_attr};

31 32 33
/// The timeout of an escape code control sequence, in milliseconds.
pub const CONTROL_SEQUENCE_TIMEOUT: u64 = 100;

Ticki's avatar
Ticki committed
34 35
/// A terminal restorer, which keeps the previous state of the terminal, and restores it, when
/// dropped.
ticki's avatar
ticki committed
36 37
///
/// Restoring will entirely bring back the old TTY state.
Jeremy Soller's avatar
Jeremy Soller committed
38
pub struct RawTerminal<W: Write> {
Ticki's avatar
Ticki committed
39 40
    prev_ios: Termios,
    output: W,
Ticki's avatar
Ticki committed
41 42
}

Jeremy Soller's avatar
Jeremy Soller committed
43
impl<W: Write> Drop for RawTerminal<W> {
Ticki's avatar
Ticki committed
44
    fn drop(&mut self) {
45
        set_terminal_attr(&self.prev_ios).unwrap();
Ticki's avatar
Ticki committed
46 47
    }
}
Ticki's avatar
Ticki committed
48

ticki's avatar
ticki committed
49
impl<W: Write> ops::Deref for RawTerminal<W> {
Ticki's avatar
Ticki committed
50 51 52 53 54 55
    type Target = W;

    fn deref(&self) -> &W {
        &self.output
    }
}
ticki's avatar
ticki committed
56

ticki's avatar
ticki committed
57
impl<W: Write> ops::DerefMut for RawTerminal<W> {
Ticki's avatar
Ticki committed
58 59 60 61 62
    fn deref_mut(&mut self) -> &mut W {
        &mut self.output
    }
}

63
impl<W: Write> Write for RawTerminal<W> {
Ticki's avatar
Ticki committed
64
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
65 66 67
        self.output.write(buf)
    }

Ticki's avatar
Ticki committed
68
    fn flush(&mut self) -> io::Result<()> {
69 70 71 72
        self.output.flush()
    }
}

73
/// Types which can be converted into "raw mode".
ticki's avatar
ticki committed
74 75 76 77 78
///
/// # Why is this type defined on writers and not readers?
///
/// TTYs has their state controlled by the writer, not the reader. You use the writer to clear the
/// screen, move the cursor and so on, so naturally you use the writer to change the mode as well.
Jeremy Soller's avatar
Jeremy Soller committed
79
pub trait IntoRawMode: Write + Sized {
Ticki's avatar
Ticki committed
80 81
    /// Switch to raw mode.
    ///
IGI-111's avatar
IGI-111 committed
82 83 84
    /// Raw mode means that stdin won't be printed (it will instead have to be written manually by
    /// the program). Furthermore, the input isn't canonicalised or buffered (that is, you can
    /// read from stdin one byte of a time). The output is neither modified in any way.
Ticki's avatar
Ticki committed
85
    fn into_raw_mode(self) -> io::Result<RawTerminal<Self>>;
Ticki's avatar
Ticki committed
86 87 88
}

impl<W: Write> IntoRawMode for W {
Ticki's avatar
Ticki committed
89
    fn into_raw_mode(self) -> io::Result<RawTerminal<W>> {
90
        let mut ios = get_terminal_attr()?;
91
        let prev_ios = ios;
ticki's avatar
ticki committed
92

93 94 95 96 97 98 99 100
        raw_terminal_attr(&mut ios);

        set_terminal_attr(&ios)?;

        Ok(RawTerminal {
            prev_ios: prev_ios,
            output: self,
        })
Ticki's avatar
Ticki committed
101 102
    }
}
Ticki's avatar
Ticki committed
103

104 105 106 107 108 109 110 111 112 113 114 115 116 117
impl<W: Write> RawTerminal<W> {
    pub fn suspend_raw_mode(&self) -> io::Result<()> {
        set_terminal_attr(&self.prev_ios)?;
        Ok(())
    }

    pub fn activate_raw_mode(&self) -> io::Result<()> {
        let mut ios = get_terminal_attr()?;
        raw_terminal_attr(&mut ios);
        set_terminal_attr(&ios)?;
        Ok(())
    }
}

Ticki's avatar
Ticki committed
118 119 120 121 122 123 124 125 126
#[cfg(test)]
mod test {
    use super::*;
    use std::io::{Write, stdout};

    #[test]
    fn test_into_raw_mode() {
        let mut out = stdout().into_raw_mode().unwrap();

127 128 129
        out.write_all(b"this is a test, muahhahahah\r\n").unwrap();

        drop(out);
Ticki's avatar
Ticki committed
130 131
    }
}