Move system specific features into sys module
This commit is contained in:
parent
fafd891966
commit
cd455e8358
|
@ -2,7 +2,7 @@ use std::io::{self, Read};
|
|||
use std::sync::mpsc;
|
||||
use std::thread;
|
||||
|
||||
use tty;
|
||||
use sys::tty::get_tty;
|
||||
|
||||
/// Construct an asynchronous handle to the TTY standard input.
|
||||
///
|
||||
|
@ -17,7 +17,7 @@ use tty;
|
|||
pub fn async_stdin() -> AsyncReader {
|
||||
let (send, recv) = mpsc::channel();
|
||||
|
||||
thread::spawn(move || for i in tty::get_tty().unwrap().bytes() {
|
||||
thread::spawn(move || for i in get_tty().unwrap().bytes() {
|
||||
if send.send(i).is_err() {
|
||||
return;
|
||||
}
|
||||
|
|
46
src/lib.rs
46
src/lib.rs
|
@ -11,27 +11,20 @@
|
|||
//! For more information refer to the [README](https://github.com/ticki/termion).
|
||||
#![warn(missing_docs)]
|
||||
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
extern crate libc;
|
||||
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
mod termios;
|
||||
|
||||
#[cfg(target_os = "redox")]
|
||||
extern crate redox_termios;
|
||||
#[path="sys/redox/mod.rs"]
|
||||
mod sys;
|
||||
|
||||
#[cfg(target_os = "redox")]
|
||||
extern crate syscall;
|
||||
#[cfg(unix)]
|
||||
#[path="sys/unix/mod.rs"]
|
||||
mod sys;
|
||||
|
||||
pub use sys::size::terminal_size;
|
||||
pub use sys::tty::{is_tty, get_tty};
|
||||
|
||||
mod async;
|
||||
pub use async::{AsyncReader, async_stdin};
|
||||
|
||||
mod size;
|
||||
pub use size::terminal_size;
|
||||
|
||||
mod tty;
|
||||
pub use tty::{is_tty, get_tty};
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
pub mod clear;
|
||||
|
@ -43,3 +36,26 @@ pub mod raw;
|
|||
pub mod screen;
|
||||
pub mod scroll;
|
||||
pub mod style;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::sys;
|
||||
|
||||
#[test]
|
||||
fn test_get_terminal_attr() {
|
||||
sys::attr::get_terminal_attr().unwrap();
|
||||
sys::attr::get_terminal_attr().unwrap();
|
||||
sys::attr::get_terminal_attr().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set_terminal_attr() {
|
||||
let ios = sys::attr::get_terminal_attr().unwrap();
|
||||
sys::attr::set_terminal_attr(&ios).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_size() {
|
||||
sys::size::terminal_size().unwrap();
|
||||
}
|
||||
}
|
||||
|
|
59
src/raw.rs
59
src/raw.rs
|
@ -25,6 +25,9 @@
|
|||
use std::io::{self, Write};
|
||||
use std::ops;
|
||||
|
||||
use sys::Termios;
|
||||
use sys::attr::{get_terminal_attr, raw_terminal_attr, set_terminal_attr};
|
||||
|
||||
/// The timeout of an escape code control sequence, in milliseconds.
|
||||
pub const CONTROL_SEQUENCE_TIMEOUT: u64 = 100;
|
||||
|
||||
|
@ -32,34 +35,14 @@ pub const CONTROL_SEQUENCE_TIMEOUT: u64 = 100;
|
|||
/// dropped.
|
||||
///
|
||||
/// Restoring will entirely bring back the old TTY state.
|
||||
#[cfg(target_os = "redox")]
|
||||
pub struct RawTerminal<W: Write> {
|
||||
output: W,
|
||||
}
|
||||
|
||||
#[cfg(target_os = "redox")]
|
||||
impl<W: Write> Drop for RawTerminal<W> {
|
||||
fn drop(&mut self) {
|
||||
let _ = write!(self, csi!("?82l"));
|
||||
let _ = self.flush();
|
||||
}
|
||||
}
|
||||
|
||||
#[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 RawTerminal<W: Write> {
|
||||
prev_ios: Termios,
|
||||
output: W,
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
impl<W: Write> Drop for RawTerminal<W> {
|
||||
fn drop(&mut self) {
|
||||
use termios::set_terminal_attr;
|
||||
set_terminal_attr(&mut self.prev_ios as *mut _);
|
||||
set_terminal_attr(&self.prev_ios).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -103,36 +86,18 @@ pub trait IntoRawMode: Write + Sized {
|
|||
}
|
||||
|
||||
impl<W: Write> IntoRawMode for W {
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
fn into_raw_mode(self) -> io::Result<RawTerminal<W>> {
|
||||
use termios::{cfmakeraw, get_terminal_attr, set_terminal_attr};
|
||||
|
||||
let (mut ios, exit) = get_terminal_attr();
|
||||
let mut ios = get_terminal_attr()?;
|
||||
let prev_ios = ios;
|
||||
if exit != 0 {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "Unable to get Termios attribute."));
|
||||
}
|
||||
|
||||
unsafe {
|
||||
cfmakeraw(&mut ios);
|
||||
}
|
||||
raw_terminal_attr(&mut ios);
|
||||
|
||||
if set_terminal_attr(&mut ios as *mut _) != 0 {
|
||||
Err(io::Error::new(io::ErrorKind::Other, "Unable to set Termios attribute."))
|
||||
} else {
|
||||
let res = RawTerminal {
|
||||
set_terminal_attr(&ios)?;
|
||||
|
||||
Ok(RawTerminal {
|
||||
prev_ios: prev_ios,
|
||||
output: self,
|
||||
};
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "redox")]
|
||||
fn into_raw_mode(mut self) -> io::Result<RawTerminal<W>> {
|
||||
write!(self, csi!("?82h"))?;
|
||||
self.flush()?;
|
||||
Ok(RawTerminal { output: self })
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -145,6 +110,8 @@ mod test {
|
|||
fn test_into_raw_mode() {
|
||||
let mut out = stdout().into_raw_mode().unwrap();
|
||||
|
||||
out.write_all(b"this is a test, muahhahahah").unwrap();
|
||||
out.write_all(b"this is a test, muahhahahah\r\n").unwrap();
|
||||
|
||||
drop(out);
|
||||
}
|
||||
}
|
||||
|
|
85
src/size.rs
85
src/size.rs
|
@ -1,85 +0,0 @@
|
|||
use std::io;
|
||||
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
use libc::c_ushort;
|
||||
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
#[repr(C)]
|
||||
struct TermSize {
|
||||
row: c_ushort,
|
||||
col: c_ushort,
|
||||
_x: c_ushort,
|
||||
_y: c_ushort,
|
||||
}
|
||||
|
||||
// Since attributes on non-item statements is not stable yet, we use a function.
|
||||
#[cfg(not(target_os = "android"))]
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
#[cfg(not(target_env = "musl"))]
|
||||
fn tiocgwinsz() -> u64 {
|
||||
use termios::TIOCGWINSZ;
|
||||
TIOCGWINSZ as u64
|
||||
}
|
||||
#[cfg(not(target_os = "android"))]
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
#[cfg(not(target_env = "musl"))]
|
||||
fn tiocgwinsz() -> u32 {
|
||||
use termios::TIOCGWINSZ;
|
||||
TIOCGWINSZ as u32
|
||||
}
|
||||
|
||||
#[cfg(any(target_env = "musl", target_os = "android"))]
|
||||
fn tiocgwinsz() -> i32 {
|
||||
use termios::TIOCGWINSZ;
|
||||
TIOCGWINSZ as i32
|
||||
}
|
||||
|
||||
/// Get the size of the terminal.
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
pub fn terminal_size() -> io::Result<(u16, u16)> {
|
||||
use libc::ioctl;
|
||||
use libc::STDOUT_FILENO;
|
||||
|
||||
use std::mem;
|
||||
unsafe {
|
||||
let mut size: TermSize = mem::zeroed();
|
||||
|
||||
if ioctl(STDOUT_FILENO, tiocgwinsz(), &mut size as *mut _) == 0 {
|
||||
Ok((size.col as u16, size.row as u16))
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::Other, "Unable to get the terminal size."))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the size of the terminal.
|
||||
#[cfg(target_os = "redox")]
|
||||
pub fn terminal_size() -> io::Result<(u16, u16)> {
|
||||
use redox_termios;
|
||||
use syscall;
|
||||
|
||||
if let Ok(fd) = syscall::dup(1, b"winsize") {
|
||||
let mut winsize = redox_termios::Winsize::default();
|
||||
let res = syscall::read(fd, &mut winsize);
|
||||
let _ = syscall::close(fd);
|
||||
if let Ok(count) = res {
|
||||
if count == winsize.len() {
|
||||
return Ok((winsize.ws_col, winsize.ws_row));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(io::Error::new(io::ErrorKind::Other, "Unable to get the terminal size."))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_size() {
|
||||
assert!(terminal_size().is_ok());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
use std::io;
|
||||
|
||||
use super::{cvt, syscall, Termios};
|
||||
|
||||
pub fn get_terminal_attr() -> io::Result<Termios> {
|
||||
let mut termios = Termios::default();
|
||||
|
||||
let fd = cvt(syscall::dup(0, b"termios"))?;
|
||||
let res = cvt(syscall::read(fd, &mut termios));
|
||||
let _ = syscall::close(fd);
|
||||
|
||||
if res? == termios.len() {
|
||||
Ok(termios)
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::Other, "Unable to get the terminal attributes."))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_terminal_attr(termios: &Termios) -> io::Result<()> {
|
||||
let fd = cvt(syscall::dup(0, b"termios"))?;
|
||||
let res = cvt(syscall::write(fd, termios));
|
||||
let _ = syscall::close(fd);
|
||||
|
||||
if res? == termios.len() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::Other, "Unable to set the terminal attributes."))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn raw_terminal_attr(ios: &mut Termios) {
|
||||
ios.make_raw()
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
extern crate redox_termios;
|
||||
extern crate syscall;
|
||||
|
||||
use std::io;
|
||||
|
||||
pub use self::redox_termios::Termios;
|
||||
|
||||
pub mod attr;
|
||||
pub mod size;
|
||||
pub mod tty;
|
||||
|
||||
// Support function for converting syscall error to io error
|
||||
fn cvt(result: Result<usize, syscall::Error>) -> io::Result<usize> {
|
||||
result.map_err(|err| io::Error::from_raw_os_error(err.errno))
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
use std::io;
|
||||
|
||||
use super::{cvt, redox_termios, syscall};
|
||||
|
||||
/// Get the size of the terminal.
|
||||
pub fn terminal_size() -> io::Result<(u16, u16)> {
|
||||
let mut winsize = redox_termios::Winsize::default();
|
||||
|
||||
let fd = cvt(syscall::dup(1, b"winsize"))?;
|
||||
let res = cvt(syscall::read(fd, &mut winsize));
|
||||
let _ = syscall::close(fd);
|
||||
|
||||
if res? == winsize.len() {
|
||||
Ok((winsize.ws_col, winsize.ws_row))
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::Other, "Unable to get the terminal size."))
|
||||
}
|
||||
}
|
|
@ -1,19 +1,10 @@
|
|||
use std::{fs, io};
|
||||
use std::{env, fs, io};
|
||||
use std::os::unix::io::AsRawFd;
|
||||
|
||||
/// Is this stream a TTY?
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
pub fn is_tty<T: AsRawFd>(stream: &T) -> bool {
|
||||
use libc;
|
||||
|
||||
unsafe { libc::isatty(stream.as_raw_fd()) == 1 }
|
||||
}
|
||||
use super::syscall;
|
||||
|
||||
/// Is this stream a TTY?
|
||||
#[cfg(target_os = "redox")]
|
||||
pub fn is_tty<T: AsRawFd>(stream: &T) -> bool {
|
||||
use syscall;
|
||||
|
||||
if let Ok(fd) = syscall::dup(stream.as_raw_fd(), b"termios") {
|
||||
let _ = syscall::close(fd);
|
||||
true
|
||||
|
@ -25,17 +16,7 @@ pub fn is_tty<T: AsRawFd>(stream: &T) -> bool {
|
|||
/// Get the TTY device.
|
||||
///
|
||||
/// This allows for getting stdio representing _only_ the TTY, and not other streams.
|
||||
#[cfg(target_os = "redox")]
|
||||
pub fn get_tty() -> io::Result<fs::File> {
|
||||
use std::env;
|
||||
let tty = try!(env::var("TTY").map_err(|x| io::Error::new(io::ErrorKind::NotFound, x)));
|
||||
fs::OpenOptions::new().read(true).write(true).open(tty)
|
||||
}
|
||||
|
||||
/// Get the TTY device.
|
||||
///
|
||||
/// This allows for getting stdio representing _only_ the TTY, and not other streams.
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
pub fn get_tty() -> io::Result<fs::File> {
|
||||
fs::OpenOptions::new().read(true).write(true).open("/dev/tty")
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
use std::{io, mem};
|
||||
|
||||
use super::{cvt, Termios};
|
||||
use super::libc::c_int;
|
||||
|
||||
pub fn get_terminal_attr() -> io::Result<Termios> {
|
||||
extern "C" {
|
||||
pub fn tcgetattr(fd: c_int, termptr: *mut Termios) -> c_int;
|
||||
}
|
||||
unsafe {
|
||||
let mut termios = mem::zeroed();
|
||||
cvt(tcgetattr(0, &mut termios))?;
|
||||
Ok(termios)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_terminal_attr(termios: &Termios) -> io::Result<()> {
|
||||
extern "C" {
|
||||
pub fn tcsetattr(fd: c_int, opt: c_int, termptr: *const Termios) -> c_int;
|
||||
}
|
||||
cvt(unsafe { tcsetattr(0, 0, termios) }).and(Ok(()))
|
||||
}
|
||||
|
||||
pub fn raw_terminal_attr(termios: &mut Termios) {
|
||||
extern "C" {
|
||||
pub fn cfmakeraw(termptr: *mut Termios);
|
||||
}
|
||||
unsafe { cfmakeraw(termios) }
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
extern crate libc;
|
||||
|
||||
use std::io;
|
||||
|
||||
pub use self::libc::termios as Termios;
|
||||
|
||||
pub mod attr;
|
||||
pub mod size;
|
||||
pub mod tty;
|
||||
|
||||
// Support functions for converting libc return values to io errors {
|
||||
trait IsMinusOne {
|
||||
fn is_minus_one(&self) -> bool;
|
||||
}
|
||||
|
||||
macro_rules! impl_is_minus_one {
|
||||
($($t:ident)*) => ($(impl IsMinusOne for $t {
|
||||
fn is_minus_one(&self) -> bool {
|
||||
*self == -1
|
||||
}
|
||||
})*)
|
||||
}
|
||||
|
||||
impl_is_minus_one! { i8 i16 i32 i64 isize }
|
||||
|
||||
fn cvt<T: IsMinusOne>(t: T) -> io::Result<T> {
|
||||
if t.is_minus_one() {
|
||||
Err(io::Error::last_os_error())
|
||||
} else {
|
||||
Ok(t)
|
||||
}
|
||||
}
|
||||
// } End of support functions
|
|
@ -0,0 +1,48 @@
|
|||
use std::{io, mem};
|
||||
|
||||
use super::cvt;
|
||||
use super::libc::{c_ushort, ioctl, STDOUT_FILENO};
|
||||
|
||||
#[repr(C)]
|
||||
struct TermSize {
|
||||
row: c_ushort,
|
||||
col: c_ushort,
|
||||
_x: c_ushort,
|
||||
_y: c_ushort,
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub const TIOCGWINSZ: usize = 0x00005413;
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
pub const TIOCGWINSZ: usize = 0x40087468;
|
||||
|
||||
// Since attributes on non-item statements is not stable yet, we use a function.
|
||||
#[cfg(not(target_os = "android"))]
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
#[cfg(not(target_env = "musl"))]
|
||||
fn tiocgwinsz() -> u64 {
|
||||
TIOCGWINSZ as u64
|
||||
}
|
||||
#[cfg(not(target_os = "android"))]
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
#[cfg(not(target_env = "musl"))]
|
||||
fn tiocgwinsz() -> u32 {
|
||||
TIOCGWINSZ as u32
|
||||
}
|
||||
|
||||
#[cfg(any(target_env = "musl", target_os = "android"))]
|
||||
fn tiocgwinsz() -> i32 {
|
||||
TIOCGWINSZ as i32
|
||||
}
|
||||
|
||||
/// Get the size of the terminal.
|
||||
pub fn terminal_size() -> io::Result<(u16, u16)> {
|
||||
unsafe {
|
||||
let mut size: TermSize = mem::zeroed();
|
||||
cvt(ioctl(STDOUT_FILENO, tiocgwinsz(), &mut size as *mut _))?;
|
||||
Ok((size.col as u16, size.row as u16))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
use std::{fs, io};
|
||||
use std::os::unix::io::AsRawFd;
|
||||
|
||||
use super::libc;
|
||||
|
||||
|
||||
/// Is this stream a TTY?
|
||||
pub fn is_tty<T: AsRawFd>(stream: &T) -> bool {
|
||||
unsafe { libc::isatty(stream.as_raw_fd()) == 1 }
|
||||
}
|
||||
|
||||
/// Get the TTY device.
|
||||
///
|
||||
/// This allows for getting stdio representing _only_ the TTY, and not other streams.
|
||||
pub fn get_tty() -> io::Result<fs::File> {
|
||||
fs::OpenOptions::new().read(true).write(true).open("/dev/tty")
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
use libc::c_int;
|
||||
use std::mem;
|
||||
|
||||
pub use libc::termios as Termios;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub const TIOCGWINSZ: usize = 0x00005413;
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
pub const TIOCGWINSZ: usize = 0x40087468;
|
||||
|
||||
extern "C" {
|
||||
pub fn tcgetattr(fd: c_int, termptr: *mut Termios) -> c_int;
|
||||
pub fn tcsetattr(fd: c_int, opt: c_int, termptr: *mut Termios) -> c_int;
|
||||
pub fn cfmakeraw(termptr: *mut Termios);
|
||||
}
|
||||
|
||||
pub fn get_terminal_attr() -> (Termios, c_int) {
|
||||
unsafe {
|
||||
let mut ios = mem::zeroed();
|
||||
let attr = tcgetattr(0, &mut ios);
|
||||
(ios, attr)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_terminal_attr(ios: *mut Termios) -> c_int {
|
||||
unsafe { tcsetattr(0, 0, ios) }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_get_terminal_attr() {
|
||||
get_terminal_attr();
|
||||
get_terminal_attr();
|
||||
get_terminal_attr();
|
||||
}
|
||||
#[test]
|
||||
fn test_set_terminal_attr() {
|
||||
let mut ios = get_terminal_attr().0;
|
||||
set_terminal_attr(&mut ios as *mut _);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue