Now supports Redox!
This commit is contained in:
parent
45b1136f75
commit
fd2d74d41a
|
@ -3,6 +3,8 @@ libterm
|
||||||
|
|
||||||
A pure Rust library for handling, manipulating and reading information about terminals. This provides a full-featured alternative to Termbox.
|
A pure Rust library for handling, manipulating and reading information about terminals. This provides a full-featured alternative to Termbox.
|
||||||
|
|
||||||
|
Supports Redox and POSIX. Untested on Windows.
|
||||||
|
|
||||||
Features
|
Features
|
||||||
--------
|
--------
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ fn main() {
|
||||||
stdout.write(b"password: ").unwrap();
|
stdout.write(b"password: ").unwrap();
|
||||||
stdout.flush().unwrap();
|
stdout.flush().unwrap();
|
||||||
|
|
||||||
let pass = stdin.read_passwd();
|
let pass = stdin.read_passwd(&mut stdout);
|
||||||
|
|
||||||
if let Some(pass) = pass {
|
if let Some(pass) = pass {
|
||||||
stdout.write(pass.as_bytes()).unwrap();
|
stdout.write(pass.as_bytes()).unwrap();
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
extern crate libterm;
|
extern crate libterm;
|
||||||
|
|
||||||
use libterm::{TermControl, raw_mode, Color, Mode, ReadExt};
|
use libterm::{TermControl, IntoRawMode, Color, Mode, ReadExt};
|
||||||
use std::io::{Read, Write, stdout, stdin};
|
use std::io::{Read, Write, stdout, stdin};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let _raw = raw_mode();
|
|
||||||
let stdout = stdout();
|
let stdout = stdout();
|
||||||
let mut stdout = stdout.lock();
|
let mut stdout = stdout.lock().into_raw_mode().unwrap();
|
||||||
let mut stdin = stdin();
|
let mut stdin = stdin();
|
||||||
|
|
||||||
stdout.goto(5, 5).unwrap();
|
stdout.goto(5, 5).unwrap();
|
||||||
|
|
|
@ -3,12 +3,15 @@ use {Color, Mode};
|
||||||
|
|
||||||
/// Controlling terminals.
|
/// Controlling terminals.
|
||||||
pub trait TermControl {
|
pub trait TermControl {
|
||||||
|
|
||||||
/// Print the CSI (control sequence introducer) followed by a byte string.
|
/// Print the CSI (control sequence introducer) followed by a byte string.
|
||||||
fn csi(&mut self, b: &[u8]) -> IoResult<usize>;
|
fn csi(&mut self, b: &[u8]) -> IoResult<usize>;
|
||||||
/// Print OSC (operating system command) followed by a byte string.
|
/// Print OSC (operating system command) followed by a byte string.
|
||||||
fn osc(&mut self, b: &[u8]) -> IoResult<usize>;
|
fn osc(&mut self, b: &[u8]) -> IoResult<usize>;
|
||||||
/// Print OSC (device control string) followed by a byte string.
|
/// Print OSC (device control string) followed by a byte string.
|
||||||
fn dsc(&mut self, b: &[u8]) -> IoResult<usize>;
|
fn dsc(&mut self, b: &[u8]) -> IoResult<usize>;
|
||||||
|
|
||||||
|
|
||||||
/// Clear the terminal.
|
/// Clear the terminal.
|
||||||
fn clear(&mut self) -> IoResult<usize> {
|
fn clear(&mut self) -> IoResult<usize> {
|
||||||
self.csi(b"2J")
|
self.csi(b"2J")
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fmt::{Display, Formatter, Error as FmtError};
|
||||||
|
|
||||||
|
/// An terminal error.
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
||||||
|
pub enum TerminalError {
|
||||||
|
/// Failed to load attributes.
|
||||||
|
LoadAttrError,
|
||||||
|
/// Failed to set attributes.
|
||||||
|
SetAttrError,
|
||||||
|
/// Failed to get terminal size.
|
||||||
|
TermSizeError,
|
||||||
|
/// Failed to write to stdout.
|
||||||
|
StdoutError,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TerminalError {
|
||||||
|
fn msg(self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
TerminalError::LoadAttrError => "Failed to load Terminal attributes.",
|
||||||
|
TerminalError::SetAttrError => "Failed to set Terminal attribute.",
|
||||||
|
TerminalError::TermSizeError => "Failed to get terminal size.",
|
||||||
|
TerminalError::StdoutError => "Failed to write to stdout.",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for TerminalError {
|
||||||
|
fn description(&self) -> &str {
|
||||||
|
self.msg()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for TerminalError {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
|
||||||
|
f.write_str(self.msg())
|
||||||
|
}
|
||||||
|
}
|
14
src/extra.rs
14
src/extra.rs
|
@ -1,15 +1,19 @@
|
||||||
use std::io::Read;
|
use std::io::{Read, Write};
|
||||||
use raw_mode;
|
use IntoRawMode;
|
||||||
|
|
||||||
/// Extension to `Read` trait.
|
/// Extension to `Read` trait.
|
||||||
pub trait ReadExt {
|
pub trait ReadExt {
|
||||||
/// Read a password.
|
/// Read a password.
|
||||||
fn read_passwd(&mut self) -> Option<String>;
|
fn read_passwd<W: Write>(&mut self, writer: &mut W) -> Option<String>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Read> ReadExt for R {
|
impl<R: Read> ReadExt for R {
|
||||||
fn read_passwd(&mut self) -> Option<String> {
|
fn read_passwd<W: Write>(&mut self, writer: &mut W) -> Option<String> {
|
||||||
let _raw = raw_mode();
|
let _raw = if let Ok(x) = writer.into_raw_mode() {
|
||||||
|
x
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
let mut string = String::with_capacity(30);
|
let mut string = String::with_capacity(30);
|
||||||
|
|
||||||
for c in self.chars() {
|
for c in self.chars() {
|
||||||
|
|
|
@ -3,15 +3,20 @@
|
||||||
|
|
||||||
#[warn(missing_docs)]
|
#[warn(missing_docs)]
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "redox"))]
|
||||||
extern crate libc;
|
extern crate libc;
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "redox"))]
|
||||||
mod termios;
|
mod termios;
|
||||||
|
|
||||||
mod control;
|
mod control;
|
||||||
pub use control::TermControl;
|
pub use control::TermControl;
|
||||||
|
|
||||||
|
mod error;
|
||||||
|
pub use error::TerminalError;
|
||||||
|
|
||||||
mod raw;
|
mod raw;
|
||||||
pub use raw::{raw_mode, TerminalRestorer};
|
pub use raw::{IntoRawMode, TerminalRestorer};
|
||||||
|
|
||||||
mod size;
|
mod size;
|
||||||
pub use size::termsize;
|
pub use size::termsize;
|
||||||
|
|
93
src/raw.rs
93
src/raw.rs
|
@ -1,42 +1,95 @@
|
||||||
use termios::{cfmakeraw, Termios, TermiosError, get_terminal_attr, set_terminal_attr};
|
use std::io::Write;
|
||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
|
use TerminalError;
|
||||||
|
|
||||||
|
/// A terminal restorer, which keeps the previous state of the terminal, and restores it, when
|
||||||
|
/// dropped.
|
||||||
|
#[cfg(target_os = "redox")]
|
||||||
|
pub struct TerminalRestorer<W> {
|
||||||
|
output: W,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "redox")]
|
||||||
|
impl<W: Write> Drop for TerminalRestorer<W> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
use TermControl;
|
||||||
|
self.csi(b"R");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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 TerminalRestorer<W> {
|
||||||
|
prev_ios: Termios,
|
||||||
|
output: W,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "redox"))]
|
||||||
|
impl<W> Drop for TerminalRestorer<W> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
use termios::set_terminal_attr;
|
||||||
|
set_terminal_attr(&mut self.prev_ios as *mut _);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W> Deref for TerminalRestorer<W> {
|
||||||
|
type Target = W;
|
||||||
|
|
||||||
|
fn deref(&self) -> &W {
|
||||||
|
&self.output
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<W> DerefMut for TerminalRestorer<W> {
|
||||||
|
fn deref_mut(&mut self) -> &mut W {
|
||||||
|
&mut self.output
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait IntoRawMode: Sized {
|
||||||
/// Switch to raw mode.
|
/// Switch to raw mode.
|
||||||
///
|
///
|
||||||
/// Raw mode means that stdin won't be printed (it will instead have to be written manually by the
|
/// 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
|
/// 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.
|
/// stdin one byte of a time). The output is neither modified in any way.
|
||||||
pub fn raw_mode() -> Result<TerminalRestorer, TermiosError> {
|
fn into_raw_mode(self) -> Result<TerminalRestorer<Self>, TerminalError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W: Write> IntoRawMode for W {
|
||||||
|
#[cfg(not(target_os = "redox"))]
|
||||||
|
fn into_raw_mode(self) -> Result<TerminalRestorer<W>, TerminalError> {
|
||||||
|
use termios::{cfmakeraw, get_terminal_attr, set_terminal_attr};
|
||||||
|
|
||||||
let (mut ios, err) = get_terminal_attr();
|
let (mut ios, err) = get_terminal_attr();
|
||||||
let prev_ios = ios.clone();
|
let prev_ios = ios.clone();
|
||||||
if err != 0 {
|
if err != 0 {
|
||||||
return Err(TermiosError::LoadAttrError);
|
return Err(TerminalError::LoadAttrError);
|
||||||
}
|
}
|
||||||
|
|
||||||
make_raw(&mut ios);
|
unsafe {
|
||||||
|
cfmakeraw(&mut ios);
|
||||||
|
}
|
||||||
|
|
||||||
if set_terminal_attr(&mut ios as *mut _) != 0 {
|
if set_terminal_attr(&mut ios as *mut _) != 0 {
|
||||||
Err(TermiosError::SetAttrError)
|
Err(TerminalError::SetAttrError)
|
||||||
} else {
|
} else {
|
||||||
Ok(TerminalRestorer {
|
Ok(TerminalRestorer {
|
||||||
prev_ios: prev_ios,
|
prev_ios: prev_ios,
|
||||||
|
output: self,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[cfg(target_os = "redox")]
|
||||||
fn make_raw(ios: &mut Termios) {
|
fn into_raw_mode(self) -> Result<TerminalRestorer<W>, TerminalError> {
|
||||||
unsafe {
|
if let Err(_) = self.csi("r") {
|
||||||
cfmakeraw(&mut *ios);
|
Err(TerminalError::StdoutError)
|
||||||
|
} else {
|
||||||
|
Ok(TerminalRestorer {
|
||||||
|
output: self,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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 _);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,8 @@ use libc::{c_ushort, STDOUT_FILENO};
|
||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
use termios::{TermiosError, tiocgwinsz};
|
use termios::tiocgwinsz;
|
||||||
|
use TerminalError;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
struct TermSize {
|
struct TermSize {
|
||||||
|
@ -15,14 +16,14 @@ struct TermSize {
|
||||||
|
|
||||||
/// Get the size of the terminal. If the program isn't running in a terminal, it will return
|
/// Get the size of the terminal. If the program isn't running in a terminal, it will return
|
||||||
/// `None`.
|
/// `None`.
|
||||||
pub fn termsize() -> Result<(usize, usize), TermiosError> {
|
pub fn termsize() -> Result<(usize, usize), TerminalError> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut size: TermSize = mem::zeroed();
|
let mut size: TermSize = mem::zeroed();
|
||||||
|
|
||||||
if ioctl(STDOUT_FILENO, tiocgwinsz as u64, &mut size as *mut _) == 0 {
|
if ioctl(STDOUT_FILENO, tiocgwinsz as u64, &mut size as *mut _) == 0 {
|
||||||
Ok((size.col as usize, size.row as usize))
|
Ok((size.col as usize, size.row as usize))
|
||||||
} else {
|
} else {
|
||||||
Err(TermiosError::TermSizeError)
|
Err(TerminalError::TermSizeError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
use libc::{c_int, c_uint, c_uchar};
|
use libc::{c_int, c_uint, c_uchar};
|
||||||
|
|
||||||
use std::error::Error;
|
|
||||||
use std::fmt::{Display, Formatter, Error as FmtError};
|
|
||||||
|
|
||||||
extern {
|
extern {
|
||||||
pub static tiocgwinsz: c_int;
|
pub static tiocgwinsz: c_int;
|
||||||
|
|
||||||
|
@ -47,36 +44,3 @@ pub fn set_terminal_attr(ios: *mut Termios) -> c_int {
|
||||||
tcsetattr(0, 0, ios)
|
tcsetattr(0, 0, ios)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Termios error.
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
|
||||||
pub enum TermiosError {
|
|
||||||
/// Failed to load attributes.
|
|
||||||
LoadAttrError,
|
|
||||||
/// Failed to set attributes.
|
|
||||||
SetAttrError,
|
|
||||||
/// Failed to get terminal size.
|
|
||||||
TermSizeError,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TermiosError {
|
|
||||||
fn msg(self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
TermiosError::LoadAttrError => "Failed to load Termios attributes.",
|
|
||||||
TermiosError::SetAttrError => "Failed to set Termios attribute.",
|
|
||||||
TermiosError::TermSizeError => "Failed to get terminal size.",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Error for TermiosError {
|
|
||||||
fn description(&self) -> &str {
|
|
||||||
self.msg()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for TermiosError {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
|
|
||||||
f.write_str(self.msg())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue