Skip to content
Snippets Groups Projects
Commit bf7ca5c1 authored by Ticki's avatar Ticki
Browse files

Initial commit

parents
No related branches found
No related tags found
No related merge requests found
#![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)
})
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment