Skip to content
Snippets Groups Projects
Commit dccd5c42 authored by gwenn's avatar gwenn
Browse files

Move enbale/disable_raw_mode in traits

parent cbbd858d
No related branches found
No related tags found
No related merge requests found
......@@ -46,7 +46,7 @@ use std::path::Path;
use std::result;
#[cfg(unix)]
use nix::sys::signal;
use tty::{RawReader, Terminal, Term};
use tty::{RawMode, RawReader, Terminal, Term};
use encode_unicode::CharExt;
use completion::{Completer, longest_common_prefix};
......@@ -931,9 +931,9 @@ fn readline_edit<C: Completer>(prompt: &str,
}
#[cfg(unix)]
KeyPress::Ctrl('Z') => {
try!(tty::disable_raw_mode(original_mode));
try!(original_mode.disable_raw_mode());
try!(signal::raise(signal::SIGSTOP));
try!(tty::enable_raw_mode()); // TODO original_mode may have changed
try!(s.term.enable_raw_mode()); // TODO original_mode may have changed
try!(s.refresh_line())
}
// TODO CTRL-_ // undo
......@@ -1024,14 +1024,14 @@ struct Guard(tty::Mode);
impl Drop for Guard {
fn drop(&mut self) {
let Guard(mode) = *self;
tty::disable_raw_mode(mode);
mode.disable_raw_mode();
}
}
/// Readline method that will enable RAW mode, call the `readline_edit()`
/// method and disable raw mode
fn readline_raw<C: Completer>(prompt: &str, editor: &mut Editor<C>) -> Result<String> {
let original_mode = try!(tty::enable_raw_mode());
let original_mode = try!(editor.term.enable_raw_mode());
let guard = Guard(original_mode);
let user_input = readline_edit(prompt, editor, original_mode);
drop(guard); // try!(disable_raw_mode(original_mode));
......
......@@ -3,6 +3,11 @@ use std::io::Write;
use ::Result;
use consts::KeyPress;
pub trait RawMode: Copy + Sized {
/// Disable RAW mode for the terminal.
fn disable_raw_mode(&self) -> Result<()>;
}
pub trait RawReader: Sized {
/// Blocking read of key pressed.
fn next_key(&mut self, timeout_ms: i32) -> Result<KeyPress>;
......@@ -14,6 +19,7 @@ pub trait RawReader: Sized {
/// Terminal contract
pub trait Term: Clone {
type Reader: RawReader;
type Mode;
fn new() -> Self;
/// Check if current terminal can provide a rich line-editing user interface.
......@@ -26,6 +32,8 @@ pub trait Term: Clone {
fn get_rows(&self) -> usize;
/// Check if a SIGWINCH signal has been received
fn sigwinch(&self) -> bool;
/// Enable RAW mode for the terminal.
fn enable_raw_mode(&self) -> Result<Self::Mode>;
/// Create a RAW reader
fn create_reader(&self) -> Result<Self::Reader>;
/// Clear the screen. Used to handle ctrl+l
......
......@@ -10,15 +10,14 @@ use winapi;
use consts::KeyPress;
use ::error::ReadlineError;
use ::Result;
use super::{RawReader, Term};
use super::{RawMode, RawReader, Term};
pub type Mode = ();
pub fn enable_raw_mode() -> Result<Mode> {
Ok(())
}
pub fn disable_raw_mode(_: Mode) -> Result<()> {
Ok(())
impl RawMode for Mode {
fn disable_raw_mode(&self) -> Result<()> {
Ok(())
}
}
impl<'a> RawReader for Iter<'a, KeyPress> {
......@@ -91,6 +90,7 @@ impl DummyTerminal {
impl Term for DummyTerminal {
type Reader = IntoIter<KeyPress>;
type Mode = Mode;
fn new() -> DummyTerminal {
DummyTerminal { keys: Vec::new() }
......@@ -125,6 +125,10 @@ impl Term for DummyTerminal {
false
}
fn enable_raw_mode(&self) -> Result<Mode> {
Ok(())
}
/// Create a RAW reader
fn create_reader(&self) -> Result<IntoIter<KeyPress>> {
Ok(self.keys.clone().into_iter())
......
......@@ -13,9 +13,8 @@ use char_iter;
use consts::{self, KeyPress};
use ::Result;
use ::error;
use super::{RawReader, Term};
use super::{RawMode, RawReader, Term};
pub type Mode = termios::Termios;
const STDIN_FILENO: libc::c_int = libc::STDIN_FILENO;
const STDOUT_FILENO: libc::c_int = libc::STDOUT_FILENO;
......@@ -31,20 +30,6 @@ const TIOCGWINSZ: libc::c_ulong = 0x5413;
#[cfg(all(target_os = "linux", target_env = "musl"))]
const TIOCGWINSZ: libc::c_int = 0x5413;
/// Try to get the number of columns in the current terminal,
/// or assume 80 if it fails.
fn get_columns() -> usize {
let (cols, _) = get_win_size();
cols
}
/// Try to get the number of rows in the current terminal,
/// or assume 24 if it fails.
fn get_rows() -> usize {
let (_, rows) = get_win_size();
rows
}
fn get_win_size() -> (usize, usize) {
use std::mem::zeroed;
use libc::c_ushort;
......@@ -89,40 +74,14 @@ fn is_a_tty(fd: libc::c_int) -> bool {
unsafe { libc::isatty(fd) != 0 }
}
/// Enable RAW mode for the terminal.
pub fn enable_raw_mode() -> Result<Mode> {
use nix::errno::Errno::ENOTTY;
use nix::sys::termios::{BRKINT, CS8, ECHO, ICANON, ICRNL, IEXTEN, INPCK, ISIG, ISTRIP, IXON,
/* OPOST, */ VMIN, VTIME};
if !is_a_tty(STDIN_FILENO) {
try!(Err(nix::Error::from_errno(ENOTTY)));
}
let original_mode = try!(termios::tcgetattr(STDIN_FILENO));
let mut raw = original_mode;
// disable BREAK interrupt, CR to NL conversion on input,
// input parity check, strip high bit (bit 8), output flow control
raw.c_iflag = raw.c_iflag & !(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
// we don't want raw output, it turns newlines into straight linefeeds
// raw.c_oflag = raw.c_oflag & !(OPOST); // disable all output processing
raw.c_cflag = raw.c_cflag | (CS8); // character-size mark (8 bits)
// disable echoing, canonical mode, extended input processing and signals
raw.c_lflag = raw.c_lflag & !(ECHO | ICANON | IEXTEN | ISIG);
raw.c_cc[VMIN] = 1; // One character-at-a-time input
raw.c_cc[VTIME] = 0; // with blocking read
try!(termios::tcsetattr(STDIN_FILENO, termios::TCSAFLUSH, &raw));
Ok(original_mode)
}
/// Disable RAW mode for the terminal.
pub fn disable_raw_mode(original_mode: Mode) -> Result<()> {
try!(termios::tcsetattr(STDIN_FILENO, termios::TCSAFLUSH, &original_mode));
Ok(())
}
pub type Mode = termios::Termios;
fn clear_screen(w: &mut Write) -> Result<()> {
try!(w.write_all(b"\x1b[H\x1b[2J"));
try!(w.flush());
Ok(())
impl RawMode for Mode {
/// Disable RAW mode for the terminal.
fn disable_raw_mode(&self) -> Result<()> {
try!(termios::tcsetattr(STDIN_FILENO, termios::TCSADRAIN, self));
Ok(())
}
}
// Rust std::io::Stdin is buffered with no way to know if bytes are available.
......@@ -285,6 +244,7 @@ pub struct PosixTerminal {
impl Term for PosixTerminal {
type Reader = PosixRawReader;
type Mode = Mode;
fn new() -> PosixTerminal {
let term = PosixTerminal {
......@@ -311,14 +271,41 @@ impl Term for PosixTerminal {
// Interactive loop:
/// Get the number of columns in the current terminal.
/// Try to get the number of columns in the current terminal,
/// or assume 80 if it fails.
fn get_columns(&self) -> usize {
get_columns()
let (cols, _) = get_win_size();
cols
}
/// Get the number of rows in the current terminal.
/// Try to get the number of rows in the current terminal,
/// or assume 24 if it fails.
fn get_rows(&self) -> usize {
get_rows()
let (_, rows) = get_win_size();
rows
}
fn enable_raw_mode(&self) -> Result<Mode> {
use nix::errno::Errno::ENOTTY;
use nix::sys::termios::{BRKINT, CS8, ECHO, ICANON, ICRNL, IEXTEN, INPCK, ISIG, ISTRIP,
IXON, /* OPOST, */ VMIN, VTIME};
if !self.stdin_isatty {
try!(Err(nix::Error::from_errno(ENOTTY)));
}
let original_mode = try!(termios::tcgetattr(STDIN_FILENO));
let mut raw = original_mode;
// disable BREAK interrupt, CR to NL conversion on input,
// input parity check, strip high bit (bit 8), output flow control
raw.c_iflag = raw.c_iflag & !(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
// we don't want raw output, it turns newlines into straight linefeeds
// raw.c_oflag = raw.c_oflag & !(OPOST); // disable all output processing
raw.c_cflag = raw.c_cflag | (CS8); // character-size mark (8 bits)
// disable echoing, canonical mode, extended input processing and signals
raw.c_lflag = raw.c_lflag & !(ECHO | ICANON | IEXTEN | ISIG);
raw.c_cc[VMIN] = 1; // One character-at-a-time input
raw.c_cc[VTIME] = 0; // with blocking read
try!(termios::tcsetattr(STDIN_FILENO, termios::TCSAFLUSH, &raw));
Ok(original_mode)
}
/// Create a RAW reader
......@@ -333,7 +320,9 @@ impl Term for PosixTerminal {
/// Clear the screen. Used to handle ctrl+l
fn clear_screen(&mut self, w: &mut Write) -> Result<()> {
clear_screen(w)
try!(w.write_all(b"\x1b[H\x1b[2J"));
try!(w.flush());
Ok(())
}
}
......
......@@ -10,9 +10,8 @@ use winapi;
use consts::{self, KeyPress};
use ::error;
use ::Result;
use super::{RawReader, Term};
use super::{RawMode, RawReader, Term};
pub type Mode = winapi::DWORD;
const STDIN_FILENO: winapi::DWORD = winapi::STD_INPUT_HANDLE;
const STDOUT_FILENO: winapi::DWORD = winapi::STD_OUTPUT_HANDLE;
......@@ -40,16 +39,6 @@ macro_rules! check {
};
}
fn get_columns(handle: winapi::HANDLE) -> usize {
let (cols, _) = get_win_size(handle);
cols
}
fn get_rows(handle: winapi::HANDLE) -> usize {
let (_, rows) = get_win_size(handle);
rows
}
fn get_win_size(handle: winapi::HANDLE) -> (usize, usize) {
let mut info = unsafe { mem::zeroed() };
match unsafe { kernel32::GetConsoleScreenBufferInfo(handle, &mut info) } {
......@@ -58,60 +47,26 @@ fn get_win_size(handle: winapi::HANDLE) -> (usize, usize) {
}
}
fn get_console_mode(handle: winapi::HANDLE) -> Result<Mode> {
fn get_console_mode(handle: winapi::HANDLE) -> Result<winapi::DWORD> {
let mut original_mode = 0;
check!(kernel32::GetConsoleMode(handle, &mut original_mode));
Ok(original_mode)
}
/// Return whether or not STDIN, STDOUT or STDERR is a TTY
fn is_a_tty(fd: winapi::DWORD) -> bool {
let handle = get_std_handle(fd);
match handle {
Ok(handle) => {
// If this function doesn't fail then fd is a TTY
get_console_mode(handle).is_ok()
}
Err(_) => false,
}
}
/// Enable raw mode for the TERM
pub fn enable_raw_mode() -> Result<Mode> {
let handle = try!(get_std_handle(STDIN_FILENO));
let original_mode = try!(get_console_mode(handle));
// Disable these modes
let raw = original_mode &
!(winapi::wincon::ENABLE_LINE_INPUT | winapi::wincon::ENABLE_ECHO_INPUT |
winapi::wincon::ENABLE_PROCESSED_INPUT);
// Enable these modes
let raw = raw | winapi::wincon::ENABLE_EXTENDED_FLAGS;
let raw = raw | winapi::wincon::ENABLE_INSERT_MODE;
let raw = raw | winapi::wincon::ENABLE_QUICK_EDIT_MODE;
let raw = raw | winapi::wincon::ENABLE_WINDOW_INPUT;
check!(kernel32::SetConsoleMode(handle, raw));
Ok(original_mode)
}
pub type Mode = ConsoleMode;
/// Disable Raw mode for the term
pub fn disable_raw_mode(original_mode: Mode) -> Result<()> {
let handle = try!(get_std_handle(STDIN_FILENO));
check!(kernel32::SetConsoleMode(handle, original_mode));
Ok(())
#[derive(Clone,Copy,Debug)]
pub struct ConsoleMode {
original_mode: winapi::DWORD,
stdin_handle: winapi::HANDLE,
}
/// Clear the screen. Used to handle ctrl+l
fn clear_screen(info: winapi::CONSOLE_SCREEN_BUFFER_INFO, handle: winapi::HANDLE) -> Result<()> {
let coord = winapi::COORD { X: 0, Y: 0 };
check!(kernel32::SetConsoleCursorPosition(handle, coord));
let mut _count = 0;
let n = info.dwSize.X as winapi::DWORD * info.dwSize.Y as winapi::DWORD;
check!(kernel32::FillConsoleOutputCharacterA(handle,
' ' as winapi::CHAR,
n,
coord,
&mut _count));
Ok(())
impl RawMode for Mode {
/// Disable RAW mode for the terminal.
fn disable_raw_mode(&self) -> Result<()> {
check!(kernel32::SetConsoleMode(self.stdin_handle, self.original_mode));
Ok(())
}
}
/// Console input reader
......@@ -229,6 +184,7 @@ pub type Terminal = Console;
#[derive(Clone,Debug)]
pub struct Console {
stdin_isatty: bool,
stdin_handle: winapi::HANDLE,
stdout_handle: winapi::HANDLE,
}
......@@ -260,12 +216,23 @@ impl Console {
impl Term for Console {
type Reader = ConsoleRawReader;
type Mode = Mode;
fn new() -> Console {
use std::ptr;
let stdin_handle = get_std_handle(STDIN_FILENO);
let stdin_isatty = match stdin_handle {
Ok(handle) => {
// If this function doesn't fail then fd is a TTY
get_console_mode(handle).is_ok()
}
Err(_) => false,
};
let stdout_handle = get_std_handle(STDOUT_FILENO).unwrap_or(ptr::null_mut());
Console {
stdin_isatty: is_a_tty(STDIN_FILENO),
stdin_isatty: stdin_isatty,
stdin_handle: stdin_handle.unwrap_or(ptr::null_mut()),
stdout_handle: stdout_handle,
}
}
......@@ -286,15 +253,41 @@ impl Term for Console {
/// Try to get the number of columns in the current terminal,
/// or assume 80 if it fails.
fn get_columns(&self) -> usize {
get_columns(self.stdout_handle)
let (cols, _) = get_win_size(self.stdout_handle);
cols
}
/// Try to get the number of rows in the current terminal,
/// or assume 24 if it fails.
fn get_rows(&self) -> usize {
get_rows(self.stdout_handle)
let (_, rows) = get_win_size(self.stdout_handle);
rows
}
/// Enable RAW mode for the terminal.
fn enable_raw_mode(&self) -> Result<Mode> {
if !self.stdin_isatty {
try!(Err(io::Error::new(io::ErrorKind::Other,
"no stdio handle available for this process")));
}
let original_mode = try!(get_console_mode(self.stdin_handle));
// Disable these modes
let raw = original_mode &
!(winapi::wincon::ENABLE_LINE_INPUT | winapi::wincon::ENABLE_ECHO_INPUT |
winapi::wincon::ENABLE_PROCESSED_INPUT);
// Enable these modes
let raw = raw | winapi::wincon::ENABLE_EXTENDED_FLAGS;
let raw = raw | winapi::wincon::ENABLE_INSERT_MODE;
let raw = raw | winapi::wincon::ENABLE_QUICK_EDIT_MODE;
let raw = raw | winapi::wincon::ENABLE_WINDOW_INPUT;
check!(kernel32::SetConsoleMode(self.stdin_handle, raw));
Ok(Mode {
original_mode: original_mode,
stdin_handle: self.stdin_handle,
})
}
fn create_reader(&self) -> Result<ConsoleRawReader> {
ConsoleRawReader::new()
}
......@@ -303,8 +296,18 @@ impl Term for Console {
SIGWINCH.compare_and_swap(true, false, atomic::Ordering::SeqCst)
}
/// Clear the screen. Used to handle ctrl+l
fn clear_screen(&mut self, _: &mut Write) -> Result<()> {
let info = try!(self.get_console_screen_buffer_info());
clear_screen(info, self.stdout_handle)
let coord = winapi::COORD { X: 0, Y: 0 };
check!(kernel32::SetConsoleCursorPosition(self.stdout_handle, coord));
let mut _count = 0;
let n = info.dwSize.X as winapi::DWORD * info.dwSize.Y as winapi::DWORD;
check!(kernel32::FillConsoleOutputCharacterA(self.stdout_handle,
' ' as winapi::CHAR,
n,
coord,
&mut _count));
Ok(())
}
}
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