2016-09-07 10:39:32 +01:00
|
|
|
//! 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
|
|
|
|
//!
|
2016-12-16 17:20:18 +00:00
|
|
|
//! ```rust,no_run
|
2016-09-07 10:39:32 +01:00
|
|
|
//! use termion::raw::IntoRawMode;
|
|
|
|
//! use std::io::{Write, stdout};
|
|
|
|
//!
|
|
|
|
//! fn main() {
|
|
|
|
//! let mut stdout = stdout().into_raw_mode().unwrap();
|
|
|
|
//!
|
|
|
|
//! write!(stdout, "Hey there.").unwrap();
|
|
|
|
//! }
|
|
|
|
//! ```
|
2016-07-23 15:40:27 +01:00
|
|
|
|
2016-03-20 21:55:08 +00:00
|
|
|
use std::io::{self, Write};
|
2016-07-23 19:41:43 +01:00
|
|
|
use std::ops;
|
2016-03-07 15:01:20 +00:00
|
|
|
|
2017-03-12 20:18:32 +00:00
|
|
|
/// The timeout of an escape code control sequence, in milliseconds.
|
|
|
|
pub const CONTROL_SEQUENCE_TIMEOUT: u64 = 100;
|
|
|
|
|
2016-03-07 21:19:35 +00:00
|
|
|
/// A terminal restorer, which keeps the previous state of the terminal, and restores it, when
|
|
|
|
/// dropped.
|
2016-09-07 10:39:32 +01:00
|
|
|
///
|
|
|
|
/// Restoring will entirely bring back the old TTY state.
|
2016-03-07 21:19:35 +00:00
|
|
|
#[cfg(target_os = "redox")]
|
2016-03-16 18:59:12 +00:00
|
|
|
pub struct RawTerminal<W: Write> {
|
2016-03-07 21:19:35 +00:00
|
|
|
output: W,
|
2016-03-07 15:01:20 +00:00
|
|
|
}
|
|
|
|
|
2016-03-07 21:19:35 +00:00
|
|
|
#[cfg(target_os = "redox")]
|
2016-03-10 15:24:41 +00:00
|
|
|
impl<W: Write> Drop for RawTerminal<W> {
|
2016-03-07 21:19:35 +00:00
|
|
|
fn drop(&mut self) {
|
2016-07-24 14:07:21 +01:00
|
|
|
write!(self, csi!("?82l")).unwrap();
|
2016-03-07 15:01:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-07 21:19:35 +00:00
|
|
|
#[cfg(not(target_os = "redox"))]
|
|
|
|
use termios::Termios;
|
2016-03-07 15:01:20 +00:00
|
|
|
/// A terminal restorer, which keeps the previous state of the terminal, and restores it, when
|
|
|
|
/// dropped.
|
2016-03-07 21:19:35 +00:00
|
|
|
#[cfg(not(target_os = "redox"))]
|
2016-03-16 18:59:12 +00:00
|
|
|
pub struct RawTerminal<W: Write> {
|
2016-03-07 21:19:35 +00:00
|
|
|
prev_ios: Termios,
|
|
|
|
output: W,
|
2016-03-07 15:01:20 +00:00
|
|
|
}
|
|
|
|
|
2016-03-07 21:19:35 +00:00
|
|
|
#[cfg(not(target_os = "redox"))]
|
2016-03-16 18:59:12 +00:00
|
|
|
impl<W: Write> Drop for RawTerminal<W> {
|
2016-03-07 15:01:20 +00:00
|
|
|
fn drop(&mut self) {
|
2016-03-07 21:19:35 +00:00
|
|
|
use termios::set_terminal_attr;
|
2016-03-07 15:01:20 +00:00
|
|
|
set_terminal_attr(&mut self.prev_ios as *mut _);
|
|
|
|
}
|
|
|
|
}
|
2016-03-07 21:19:35 +00:00
|
|
|
|
2016-07-23 19:41:43 +01:00
|
|
|
impl<W: Write> ops::Deref for RawTerminal<W> {
|
2016-03-07 21:19:35 +00:00
|
|
|
type Target = W;
|
|
|
|
|
|
|
|
fn deref(&self) -> &W {
|
|
|
|
&self.output
|
|
|
|
}
|
|
|
|
}
|
2016-06-14 13:29:31 +01:00
|
|
|
|
2016-07-23 19:41:43 +01:00
|
|
|
impl<W: Write> ops::DerefMut for RawTerminal<W> {
|
2016-03-07 21:19:35 +00:00
|
|
|
fn deref_mut(&mut self) -> &mut W {
|
|
|
|
&mut self.output
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-13 10:55:24 +00:00
|
|
|
impl<W: Write> Write for RawTerminal<W> {
|
2016-03-20 21:55:08 +00:00
|
|
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
2016-03-13 10:55:24 +00:00
|
|
|
self.output.write(buf)
|
|
|
|
}
|
|
|
|
|
2016-03-20 21:55:08 +00:00
|
|
|
fn flush(&mut self) -> io::Result<()> {
|
2016-03-13 10:55:24 +00:00
|
|
|
self.output.flush()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-09 08:39:22 +00:00
|
|
|
/// Types which can be converted into "raw mode".
|
2016-09-07 10:39:32 +01:00
|
|
|
///
|
|
|
|
/// # 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.
|
2016-03-16 18:59:12 +00:00
|
|
|
pub trait IntoRawMode: Write + Sized {
|
2016-03-07 21:19:35 +00:00
|
|
|
/// Switch to raw mode.
|
|
|
|
///
|
2016-07-20 10:03:30 +01:00
|
|
|
/// 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.
|
2016-03-20 21:55:08 +00:00
|
|
|
fn into_raw_mode(self) -> io::Result<RawTerminal<Self>>;
|
2016-03-07 21:19:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<W: Write> IntoRawMode for W {
|
|
|
|
#[cfg(not(target_os = "redox"))]
|
2016-03-20 21:55:08 +00:00
|
|
|
fn into_raw_mode(self) -> io::Result<RawTerminal<W>> {
|
2016-03-07 21:19:35 +00:00
|
|
|
use termios::{cfmakeraw, get_terminal_attr, set_terminal_attr};
|
|
|
|
|
2016-03-13 10:55:24 +00:00
|
|
|
let (mut ios, exit) = get_terminal_attr();
|
2016-08-04 21:37:03 +01:00
|
|
|
let prev_ios = ios;
|
2016-03-13 10:55:24 +00:00
|
|
|
if exit != 0 {
|
2016-03-20 21:55:08 +00:00
|
|
|
return Err(io::Error::new(io::ErrorKind::Other, "Unable to get Termios attribute."));
|
2016-03-07 21:19:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
cfmakeraw(&mut ios);
|
|
|
|
}
|
|
|
|
|
|
|
|
if set_terminal_attr(&mut ios as *mut _) != 0 {
|
2016-03-20 21:55:08 +00:00
|
|
|
Err(io::Error::new(io::ErrorKind::Other, "Unable to set Termios attribute."))
|
2016-03-07 21:19:35 +00:00
|
|
|
} else {
|
2016-07-20 00:00:22 +01:00
|
|
|
let res = RawTerminal {
|
2016-03-07 21:19:35 +00:00
|
|
|
prev_ios: prev_ios,
|
|
|
|
output: self,
|
2016-07-19 01:31:34 +01:00
|
|
|
};
|
|
|
|
Ok(res)
|
2016-03-07 21:19:35 +00:00
|
|
|
}
|
|
|
|
}
|
2016-06-14 13:29:31 +01:00
|
|
|
|
2016-03-07 21:19:35 +00:00
|
|
|
#[cfg(target_os = "redox")]
|
2016-03-20 21:55:08 +00:00
|
|
|
fn into_raw_mode(mut self) -> io::Result<RawTerminal<W>> {
|
2017-03-24 20:53:05 +00:00
|
|
|
write!(self, csi!("?82h")).map(|_| RawTerminal { output: self })
|
2016-03-07 21:19:35 +00:00
|
|
|
}
|
|
|
|
}
|
2016-03-09 16:18:31 +00:00
|
|
|
|
|
|
|
#[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();
|
|
|
|
|
2016-12-21 13:20:48 +00:00
|
|
|
out.write_all(b"this is a test, muahhahahah").unwrap();
|
2016-03-09 16:18:31 +00:00
|
|
|
}
|
|
|
|
}
|