use std::io::{self, Write}; use std::ops::{Deref, DerefMut}; /// A terminal restorer, which keeps the previous state of the terminal, and restores it, when /// dropped. #[cfg(target_os = "redox")] pub struct RawTerminal { output: W, } #[cfg(target_os = "redox")] impl Drop for RawTerminal { fn drop(&mut self) { use control::TermWrite; self.csi(b"R").unwrap(); } } #[cfg(not(target_os = "redox"))] use termios::Termios; /// A terminal restorer, which keeps the previous state of the terminal, and restores it, when /// dropped. #[cfg(not(target_os = "redox"))] pub struct RawTerminal { prev_ios: Termios, output: W, } #[cfg(not(target_os = "redox"))] impl RawTerminal where W: Write { /// Enable mouse support. pub fn with_mouse(mut self) -> io::Result> { try!(self.write(ENTER_MOUSE_SEQUENCE)); Ok(MouseTerminal { term: self }) } } #[cfg(not(target_os = "redox"))] impl Drop for RawTerminal { fn drop(&mut self) { use termios::set_terminal_attr; set_terminal_attr(&mut self.prev_ios as *mut _); } } impl Deref for RawTerminal { type Target = W; fn deref(&self) -> &W { &self.output } } impl DerefMut for RawTerminal { fn deref_mut(&mut self) -> &mut W { &mut self.output } } impl Write for RawTerminal { fn write(&mut self, buf: &[u8]) -> io::Result { self.output.write(buf) } fn flush(&mut self) -> io::Result<()> { self.output.flush() } } /// Types which can be converted into "raw mode". pub trait IntoRawMode: Write + Sized { /// Switch to raw mode. /// /// 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. fn into_raw_mode(self) -> io::Result>; } impl IntoRawMode for W { #[cfg(not(target_os = "redox"))] fn into_raw_mode(self) -> io::Result> { use termios::{cfmakeraw, get_terminal_attr, set_terminal_attr}; let (mut ios, exit) = get_terminal_attr(); let prev_ios = ios.clone(); if exit != 0 { return Err(io::Error::new(io::ErrorKind::Other, "Unable to get Termios attribute.")); } unsafe { cfmakeraw(&mut ios); } if set_terminal_attr(&mut ios as *mut _) != 0 { Err(io::Error::new(io::ErrorKind::Other, "Unable to set Termios attribute.")) } else { let res = RawTerminal { prev_ios: prev_ios, output: self, }; Ok(res) } } #[cfg(target_os = "redox")] fn into_raw_mode(mut self) -> io::Result> { use control::TermWrite; self.csi(b"r").map(|_| { let mut res = RawTerminal { output: self }; res }) } } /// A sequence of escape codes to enable terminal mouse support. const ENTER_MOUSE_SEQUENCE: &'static [u8] = b"\x1b[?1000h\x1b[?1002h\x1b[?1015h\x1b[?1006h"; /// A sequence of escape codes to disable terminal mouse support. const EXIT_MOUSE_SEQUENCE: &'static [u8] = b"\x1b[?1006l\x1b[?1015l\x1b[?1002l\x1b[?1000l"; /// A `RawTerminal` with added mouse support. /// /// To get such a terminal handle use `RawTerminal`'s /// [`with_mouse()`](../termion/struct.RawTerminal.html#method.with_mouse) method. #[cfg(not(target_os = "redox"))] pub struct MouseTerminal { term: RawTerminal, } #[cfg(not(target_os = "redox"))] impl Drop for MouseTerminal { fn drop(&mut self) { self.term.write(EXIT_MOUSE_SEQUENCE).unwrap(); } } #[cfg(not(target_os = "redox"))] impl Deref for MouseTerminal { type Target = W; fn deref(&self) -> &W { self.term.deref() } } #[cfg(not(target_os = "redox"))] impl DerefMut for MouseTerminal { fn deref_mut(&mut self) -> &mut W { self.term.deref_mut() } } #[cfg(not(target_os = "redox"))] impl Write for MouseTerminal { fn write(&mut self, buf: &[u8]) -> io::Result { self.term.write(buf) } fn flush(&mut self) -> io::Result<()> { self.term.flush() } } #[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(); out.write(b"this is a test, muahhahahah").unwrap(); } }