Initial commit
This commit is contained in:
commit
bf7ca5c143
|
@ -0,0 +1,155 @@
|
|||
#![feature(libc)]
|
||||
extern crate libc;
|
||||
|
||||
use std::mem;
|
||||
use self::libc::{c_int, c_uint, c_ushort, c_uchar, STDOUT_FILENO};
|
||||
use self::libc::ioctl;
|
||||
|
||||
use std::io::{Write, Result as IoResult};
|
||||
|
||||
extern {
|
||||
static tiocgwinsz: c_int;
|
||||
|
||||
fn tcgetattr(filedes: c_int, termptr: *mut Termios) -> c_int;
|
||||
fn tcsetattr(filedes: c_int, opt: c_int, termptr: *mut Termios) -> c_int;
|
||||
fn cfmakeraw(termptr: *mut Termios);
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct TermSize {
|
||||
row: c_ushort,
|
||||
col: c_ushort,
|
||||
_x: c_ushort,
|
||||
_y: c_ushort,
|
||||
}
|
||||
|
||||
/// Get the size of the terminal. If the program isn't running in a terminal, it will return
|
||||
/// `None`.
|
||||
pub fn termsize() -> Option<(usize, usize)> {
|
||||
unsafe {
|
||||
let mut size: TermSize = mem::zeroed();
|
||||
|
||||
if ioctl(STDOUT_FILENO, tiocgwinsz as u64, &mut size as *mut _) == 0 {
|
||||
Some((size.col as usize, size.row as usize))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[repr(C)]
|
||||
struct Termios {
|
||||
c_iflag: c_uint,
|
||||
c_oflag: c_uint,
|
||||
c_cflag: c_uint,
|
||||
c_lflag: c_uint,
|
||||
c_line: c_uchar,
|
||||
c_cc: [c_uchar; 32],
|
||||
c_ispeed: c_uint,
|
||||
c_ospeed: c_uint,
|
||||
}
|
||||
|
||||
fn get_terminal_attr() -> (Termios, c_int) {
|
||||
unsafe {
|
||||
let mut ios = Termios {
|
||||
c_iflag: 0,
|
||||
c_oflag: 0,
|
||||
c_cflag: 0,
|
||||
c_lflag: 0,
|
||||
c_line: 0,
|
||||
c_cc: [0; 32],
|
||||
c_ispeed: 0,
|
||||
c_ospeed: 0
|
||||
};
|
||||
|
||||
let attr = tcgetattr(0, &mut ios);
|
||||
(ios, attr)
|
||||
}
|
||||
}
|
||||
|
||||
fn make_raw(ios: &mut Termios) {
|
||||
unsafe {
|
||||
cfmakeraw(&mut *ios);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_terminal_attr(ios: *mut Termios) -> c_int {
|
||||
unsafe {
|
||||
tcsetattr(0, 0, ios)
|
||||
}
|
||||
}
|
||||
|
||||
/// A terminal restorer, which keeps the previous state of the terminal, and restores it, when
|
||||
/// dropped.
|
||||
pub struct TerminalRestorer {
|
||||
prev_ios: Termios
|
||||
}
|
||||
|
||||
impl Drop for TerminalRestorer {
|
||||
fn drop(&mut self) {
|
||||
set_terminal_attr(&mut self.prev_ios as *mut _);
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// Panics
|
||||
/// ------
|
||||
///
|
||||
/// This may panic if the Termios settings can be set or loaded properly.
|
||||
pub fn raw_mode() -> TerminalRestorer {
|
||||
let (mut ios, err) = get_terminal_attr();
|
||||
let prev_ios = ios.clone();
|
||||
if err != 0 {
|
||||
panic!("Failed to load termios settings properly.");
|
||||
}
|
||||
|
||||
make_raw(&mut ios);
|
||||
|
||||
if set_terminal_attr(&mut ios as *mut _) != 0 {
|
||||
panic!("Failed to init termios raw mode properly.");
|
||||
}
|
||||
|
||||
TerminalRestorer {
|
||||
prev_ios: prev_ios,
|
||||
}
|
||||
}
|
||||
|
||||
/// Controlling terminals.
|
||||
pub trait TermControl {
|
||||
/// Print the CSI (control sequence introducer) followed by a byte string.
|
||||
fn csi(&mut self, b: &[u8]) -> IoResult<usize>;
|
||||
/// Clear the terminal.
|
||||
fn clear(&mut self) -> IoResult<usize> {
|
||||
self.csi(b"2J")
|
||||
}
|
||||
/// Show the cursor.
|
||||
fn show(&mut self) -> IoResult<usize> {
|
||||
self.csi(b"?25h")
|
||||
}
|
||||
/// Hide the cursor.
|
||||
fn hide(&mut self) -> IoResult<usize> {
|
||||
self.csi(b"?25l")
|
||||
}
|
||||
/// Reset the style of the cursor.
|
||||
fn reset_style(&mut self) -> IoResult<usize> {
|
||||
self.csi(b"0m")
|
||||
}
|
||||
/// Go to a given position.
|
||||
fn goto(&mut self, x: usize, y: usize) -> IoResult<usize> {
|
||||
self.csi(format!("{};{}H", x, y).as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> TermControl for W {
|
||||
fn csi(&mut self, b: &[u8]) -> IoResult<usize> {
|
||||
self.write(b"\x1b[").and_then(|x| {
|
||||
self.write(b).map(|y| x + y)
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue