termion/src/raw.rs

183 lines
4.7 KiB
Rust
Raw Normal View History

2016-03-20 21:55:08 +00:00
use std::io::{self, Write};
2016-03-07 21:19:35 +00:00
use std::ops::{Deref, DerefMut};
2016-03-07 15:01:20 +00:00
2016-03-07 21:19:35 +00:00
/// A terminal restorer, which keeps the previous state of the terminal, and restores it, when
/// dropped.
#[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-03-16 18:59:12 +00:00
use control::TermWrite;
2016-06-14 13:24:07 +01:00
self.csi(b"R").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
}
#[cfg(not(target_os = "redox"))]
impl<W> RawTerminal<W>
where W: Write
{
/// Enable mouse support.
pub fn with_mouse(mut self) -> io::Result<MouseTerminal<W>> {
try!(self.write(ENTER_MOUSE_SEQUENCE));
Ok(MouseTerminal { term: self })
}
}
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-03-16 18:59:12 +00:00
impl<W: Write> 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-03-16 18:59:12 +00:00
impl<W: Write> 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()
}
}
/// Types which can be converted into "raw mode".
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.
///
/// 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-03-07 21:19:35 +00:00
let prev_ios = ios.clone();
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 {
let res = RawTerminal {
2016-03-07 21:19:35 +00:00
prev_ios: prev_ios,
output: self,
};
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>> {
2016-03-16 18:59:12 +00:00
use control::TermWrite;
2016-03-07 21:22:25 +00:00
self.csi(b"r").map(|_| {
let mut res = RawTerminal {
output: self,
};
res
2016-03-13 10:55:24 +00:00
})
2016-03-07 21:19:35 +00:00
}
}
2016-03-09 16:18:31 +00:00
/// 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<W: Write> {
term: RawTerminal<W>,
}
#[cfg(not(target_os = "redox"))]
impl<W: Write> Drop for MouseTerminal<W> {
fn drop(&mut self) {
self.term.write(EXIT_MOUSE_SEQUENCE).unwrap();
}
}
#[cfg(not(target_os = "redox"))]
impl<W: Write> Deref for MouseTerminal<W> {
type Target = W;
fn deref(&self) -> &W {
self.term.deref()
}
}
#[cfg(not(target_os = "redox"))]
impl<W: Write> DerefMut for MouseTerminal<W> {
fn deref_mut(&mut self) -> &mut W {
self.term.deref_mut()
}
}
#[cfg(not(target_os = "redox"))]
impl<W: Write> Write for MouseTerminal<W> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.term.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.term.flush()
}
}
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();
out.write(b"this is a test, muahhahahah").unwrap();
}
}