termion/src/control.rs

225 lines
6.5 KiB
Rust
Raw Normal View History

2016-03-07 15:01:20 +00:00
use std::io::{Write, Result as IoResult};
use {Color, Style};
2016-03-07 15:01:20 +00:00
2016-03-08 20:39:24 +00:00
/// Extension to the `Write` trait.
///
/// This extension to the `Write` trait is capable of producing the correct ANSI escape sequences
/// for given commands, effectively controlling the terminal.
pub trait TermWrite {
2016-03-07 21:19:35 +00:00
2016-03-07 15:01:20 +00:00
/// Print the CSI (control sequence introducer) followed by a byte string.
fn csi(&mut self, b: &[u8]) -> IoResult<usize>;
/// Print OSC (operating system command) followed by a byte string.
fn osc(&mut self, b: &[u8]) -> IoResult<usize>;
/// Print OSC (device control string) followed by a byte string.
fn dsc(&mut self, b: &[u8]) -> IoResult<usize>;
2016-03-07 21:19:35 +00:00
/// Clear the entire screen.
2016-03-07 15:01:20 +00:00
fn clear(&mut self) -> IoResult<usize> {
self.csi(b"2J")
}
/// Clear everything _after_ the cursor.
fn clear_after(&mut self) -> IoResult<usize> {
self.csi(b"J")
}
/// Clear everything _before_ the cursor.
fn clear_before(&mut self) -> IoResult<usize> {
self.csi(b"1J")
}
/// Clear the current line.
fn clear_line(&mut self) -> IoResult<usize> {
self.csi(b"2K")
}
/// Clear from the cursor until newline.
fn clear_until_newline(&mut self) -> IoResult<usize> {
self.csi(b"K")
}
2016-03-07 15:01:20 +00:00
/// Show the cursor.
fn show_cursor(&mut self) -> IoResult<usize> {
2016-03-07 15:01:20 +00:00
self.csi(b"?25h")
}
/// Hide the cursor.
fn hide_cursor(&mut self) -> IoResult<usize> {
2016-03-07 15:01:20 +00:00
self.csi(b"?25l")
}
2016-03-07 16:57:17 +00:00
/// Reset the rendition mode.
///
/// This will reset both the current style and color.
2016-03-07 16:57:17 +00:00
fn reset(&mut self) -> IoResult<usize> {
2016-03-07 15:01:20 +00:00
self.csi(b"m")
}
/// Restore the defaults.
///
/// This will reset color, position, cursor state, and so on. It is recommended that you use
/// this before you exit your program, to avoid messing up the user's terminal.
fn restore(&mut self) -> IoResult<usize> {
Ok(try!(self.reset()) + try!(self.clear()) + try!(self.goto(0, 0)) + try!(self.show_cursor()))
}
2016-03-07 15:01:20 +00:00
/// Go to a given position.
2016-03-08 18:07:39 +00:00
///
/// The position is 0-based.
fn goto(&mut self, mut x: u16, mut y: u16) -> IoResult<usize> {
x += 1;
y += 1;
2016-03-07 15:01:20 +00:00
self.csi(&[
2016-03-08 07:19:26 +00:00
b'0' + (y / 10000) as u8,
b'0' + (y / 1000) as u8 % 10,
b'0' + (y / 100) as u8 % 10,
b'0' + (y / 10) as u8 % 10,
b'0' + y as u8 % 10,
2016-03-08 18:07:39 +00:00
b';',
b'0' + (x / 10000) as u8,
b'0' + (x / 1000) as u8 % 10,
b'0' + (x / 100) as u8 % 10,
b'0' + (x / 10) as u8 % 10,
b'0' + x as u8 % 10,
2016-03-08 07:19:26 +00:00
b'H',
2016-03-07 15:01:20 +00:00
])
}
2016-03-07 15:23:05 +00:00
/// Set graphic rendition.
fn rendition(&mut self, r: u8) -> IoResult<usize> {
self.csi(&[
2016-03-08 07:19:26 +00:00
b'0' + r / 100,
b'0' + r / 10 % 10,
b'0' + r % 10,
b'm',
2016-03-07 15:23:05 +00:00
])
}
2016-03-07 16:39:25 +00:00
/// Set foreground color
fn color(&mut self, color: Color) -> IoResult<usize> {
let ansi = color.to_ansi_val();
self.csi(&[
b'3',
b'8',
b';',
b'5',
b';',
b'0' + ansi / 100,
b'0' + ansi / 10 % 10,
b'0' + ansi % 10,
b'm',
])
}
/// Set background color
fn bg_color(&mut self, color: Color) -> IoResult<usize> {
let ansi = color.to_ansi_val();
self.csi(&[
b'4',
b'8',
b';',
b'5',
b';',
b'0' + ansi / 100,
b'0' + ansi / 10 % 10,
b'0' + ansi % 10,
b'm',
])
}
2016-03-07 16:57:17 +00:00
/// Set rendition mode (SGR).
fn style(&mut self, mode: Style) -> IoResult<usize> {
2016-03-07 16:57:17 +00:00
self.rendition(mode as u8)
}
2016-03-07 15:01:20 +00:00
}
2016-03-08 20:39:24 +00:00
impl<W: Write> TermWrite for W {
2016-03-07 15:01:20 +00:00
fn csi(&mut self, b: &[u8]) -> IoResult<usize> {
Ok(try!(self.write(b"\x1B[")) + try!(self.write(b)))
2016-03-07 15:01:20 +00:00
}
fn osc(&mut self, b: &[u8]) -> IoResult<usize> {
Ok(try!(self.write(b"\x1B]")) + try!(self.write(b)))
2016-03-07 15:01:20 +00:00
}
fn dsc(&mut self, b: &[u8]) -> IoResult<usize> {
Ok(try!(self.write(b"\x1BP")) + try!(self.write(b)))
2016-03-07 15:01:20 +00:00
}
}
2016-03-09 16:18:31 +00:00
#[cfg(test)]
mod test {
use super::*;
use std::io::Cursor;
#[test]
fn test_csi() {
let mut buf = Cursor::new(Vec::new());
buf.csi(b"bluh").unwrap();
assert_eq!(buf.get_ref(), b"\x1B[bluh");
buf.csi(b"blah").unwrap();
assert_eq!(buf.get_ref(), b"\x1B[bluh\x1B[blah");
}
#[test]
fn test_csi_partial() {
let mut buf = [0; 3];
let mut buf = &mut buf[..];
assert_eq!(buf.csi(b"blu").unwrap(), 3);
assert_eq!(buf.csi(b"").unwrap(), 0);
assert_eq!(buf.csi(b"nooooo").unwrap(), 0);
}
#[test]
fn test_osc() {
let mut buf = Cursor::new(Vec::new());
buf.osc(b"bluh").unwrap();
assert_eq!(buf.get_ref(), b"\x1B]bluh");
buf.osc(b"blah").unwrap();
assert_eq!(buf.get_ref(), b"\x1B]bluh\x1B]blah");
}
#[test]
fn test_osc_partial() {
let mut buf = [0; 3];
let mut buf = &mut buf[..];
assert_eq!(buf.osc(b"blu").unwrap(), 3);
assert_eq!(buf.osc(b"").unwrap(), 0);
assert_eq!(buf.osc(b"nooooo").unwrap(), 0);
}
#[test]
fn test_dsc() {
let mut buf = Cursor::new(Vec::new());
buf.dsc(b"bluh").unwrap();
assert_eq!(buf.get_ref(), b"\x1BPbluh");
buf.dsc(b"blah").unwrap();
assert_eq!(buf.get_ref(), b"\x1BPbluh\x1BPblah");
}
#[test]
fn test_dsc_partial() {
let mut buf = [0; 3];
let mut buf = &mut buf[..];
assert_eq!(buf.dsc(b"blu").unwrap(), 3);
assert_eq!(buf.dsc(b"").unwrap(), 0);
assert_eq!(buf.dsc(b"nooooo").unwrap(), 0);
}
#[test]
fn test_clear() {
let mut buf = Cursor::new(Vec::new());
buf.clear().unwrap();
assert_eq!(buf.get_ref(), b"\x1B[2J");
buf.clear().unwrap();
assert_eq!(buf.get_ref(), b"\x1B[2J\x1B[2J");
}
#[test]
fn test_goto() {
let mut buf = Cursor::new(Vec::new());
buf.goto(34, 43).unwrap();
assert_eq!(buf.get_ref(), b"\x1B[00044;00035H");
buf.goto(24, 45).unwrap();
assert_eq!(buf.get_ref(), b"\x1B[00044;00035H\x1B[00046;00025H");
}
#[test]
fn test_style() {
use Style;
let mut buf = Cursor::new(Vec::new());
buf.style(Style::Bold).unwrap();
assert_eq!(buf.get_ref(), b"\x1B[001m");
buf.style(Style::Italic).unwrap();
assert_eq!(buf.get_ref(), b"\x1B[001m\x1B[003m");
}
}