Skip to content

Commit

Permalink
Merge pull request #736 from gwenn/termios
Browse files Browse the repository at this point in the history
Make termios an optional dependency
  • Loading branch information
gwenn authored Oct 7, 2023
2 parents 64928b8 + 308e89f commit f3adec4
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 40 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ nix = { version = "0.27", default-features = false, features = ["fs", "ioctl", "
utf8parse = "0.2"
skim = { version = "0.10", optional = true, default-features = false }
signal-hook = { version = "0.3", optional = true, default-features = false }
termios = "0.3.3"
termios = { version = "0.3.3", optional = true }

[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["consoleapi", "handleapi", "synchapi", "minwindef", "processenv", "std", "winbase", "wincon", "winuser"] }
Expand Down
156 changes: 117 additions & 39 deletions src/tty/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@ use log::{debug, warn};
use nix::errno::Errno;
use nix::poll::{self, PollFlags};
use nix::sys::select::{self, FdSet};
#[cfg(not(feature = "termios"))]
use nix::sys::termios::Termios;
use nix::unistd::{close, isatty, read, write};
use termios::{tcsetattr, Termios};
#[cfg(feature = "termios")]
use termios::Termios;
use unicode_segmentation::UnicodeSegmentation;
use utf8parse::{Parser, Receiver};

Expand Down Expand Up @@ -106,7 +109,7 @@ pub type Mode = PosixMode;
impl RawMode for PosixMode {
/// Disable RAW mode for the terminal.
fn disable_raw_mode(&self) -> Result<()> {
tcsetattr(self.tty_in, termios::TCSADRAIN, &self.termios)?;
termios_::disable_raw_mode(self.tty_in, &self.termios)?;
// disable bracketed paste
if let Some(out) = self.tty_out {
write_all(out, BRACKETED_PASTE_OFF)?;
Expand Down Expand Up @@ -1199,19 +1202,6 @@ impl SigWinCh {
}
}

fn map_key(
key_map: &mut HashMap<KeyEvent, Cmd>,
raw: &Termios,
index: usize,
name: &str,
cmd: Cmd,
) {
let cc = char::from(raw.c_cc[index]);
let key = KeyEvent::new(cc, M::NONE);
debug!(target: "rustyline", "{}: {:?}", name, key);
key_map.insert(key, cmd);
}

#[cfg(not(test))]
pub type Terminal = PosixTerminal;

Expand Down Expand Up @@ -1332,30 +1322,7 @@ impl Term for PosixTerminal {
if !self.is_in_a_tty {
return Err(ENOTTY.into());
}
let original_mode = Termios::from_fd(self.tty_in)?;
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 &=
!(termios::BRKINT | termios::ICRNL | termios::INPCK | termios::ISTRIP | termios::IXON);
// we don't want raw output, it turns newlines into straight line feeds
// disable all output processing
// raw.c_oflag = raw.c_oflag & !(OutputFlags::OPOST);

// character-size mark (8 bits)
raw.c_cflag |= termios::CS8;
// disable echoing, canonical mode, extended input processing and signals
raw.c_lflag &= !(termios::ECHO | termios::ICANON | termios::IEXTEN | termios::ISIG);
raw.c_cc[termios::VMIN] = 1; // One character-at-a-time input
raw.c_cc[termios::VTIME] = 0; // with blocking read

let mut key_map: HashMap<KeyEvent, Cmd> = HashMap::with_capacity(4);
map_key(&mut key_map, &raw, termios::VEOF, "VEOF", Cmd::EndOfFile);
map_key(&mut key_map, &raw, termios::VINTR, "VINTR", Cmd::Interrupt);
map_key(&mut key_map, &raw, termios::VQUIT, "VQUIT", Cmd::Interrupt);
map_key(&mut key_map, &raw, termios::VSUSP, "VSUSP", Cmd::Suspend);

tcsetattr(self.tty_in, termios::TCSADRAIN, &raw)?;
let (original_mode, key_map) = termios_::enable_raw_mode(self.tty_in)?;

self.raw_mode.store(true, Ordering::SeqCst);
// enable bracketed paste
Expand Down Expand Up @@ -1488,6 +1455,117 @@ pub fn suspend() -> Result<()> {
Ok(())
}

#[cfg(not(feature = "termios"))]
mod termios_ {
use super::PosixKeyMap;
use crate::keys::{KeyEvent, Modifiers as M};
use crate::{Cmd, Result};
use nix::sys::termios::{self, SetArg, SpecialCharacterIndices as SCI, Termios};
use std::collections::HashMap;
use std::os::unix::io::{BorrowedFd, RawFd};
pub fn disable_raw_mode(tty_in: RawFd, termios: &Termios) -> Result<()> {
let fd = unsafe { BorrowedFd::borrow_raw(tty_in) };
Ok(termios::tcsetattr(fd, SetArg::TCSADRAIN, termios)?)
}
pub fn enable_raw_mode(tty_in: RawFd) -> Result<(Termios, PosixKeyMap)> {
use nix::sys::termios::{ControlFlags, InputFlags, LocalFlags};

let fd = unsafe { BorrowedFd::borrow_raw(tty_in) };
let original_mode = termios::tcgetattr(fd)?;
let mut raw = original_mode.clone();
// disable BREAK interrupt, CR to NL conversion on input,
// input parity check, strip high bit (bit 8), output flow control
raw.input_flags &= !(InputFlags::BRKINT
| InputFlags::ICRNL
| InputFlags::INPCK
| InputFlags::ISTRIP
| InputFlags::IXON);
// we don't want raw output, it turns newlines into straight line feeds
// disable all output processing
// raw.c_oflag = raw.c_oflag & !(OutputFlags::OPOST);

// character-size mark (8 bits)
raw.control_flags |= ControlFlags::CS8;
// disable echoing, canonical mode, extended input processing and signals
raw.local_flags &=
!(LocalFlags::ECHO | LocalFlags::ICANON | LocalFlags::IEXTEN | LocalFlags::ISIG);
raw.control_chars[SCI::VMIN as usize] = 1; // One character-at-a-time input
raw.control_chars[SCI::VTIME as usize] = 0; // with blocking read

let mut key_map: HashMap<KeyEvent, Cmd> = HashMap::with_capacity(4);
map_key(&mut key_map, &raw, SCI::VEOF, "VEOF", Cmd::EndOfFile);
map_key(&mut key_map, &raw, SCI::VINTR, "VINTR", Cmd::Interrupt);
map_key(&mut key_map, &raw, SCI::VQUIT, "VQUIT", Cmd::Interrupt);
map_key(&mut key_map, &raw, SCI::VSUSP, "VSUSP", Cmd::Suspend);

termios::tcsetattr(fd, SetArg::TCSADRAIN, &raw)?;
Ok((original_mode, key_map))
}
fn map_key(
key_map: &mut HashMap<KeyEvent, Cmd>,
raw: &Termios,
index: SCI,
name: &str,
cmd: Cmd,
) {
let cc = char::from(raw.control_chars[index as usize]);
let key = KeyEvent::new(cc, M::NONE);
log::debug!(target: "rustyline", "{}: {:?}", name, key);
key_map.insert(key, cmd);
}
}
#[cfg(feature = "termios")]
mod termios_ {
use super::PosixKeyMap;
use crate::keys::{KeyEvent, Modifiers as M};
use crate::{Cmd, Result};
use std::collections::HashMap;
use std::os::unix::io::RawFd;
use termios::{self, Termios};
pub fn disable_raw_mode(tty_in: RawFd, termios: &Termios) -> Result<()> {
Ok(termios::tcsetattr(tty_in, termios::TCSADRAIN, termios)?)
}
pub fn enable_raw_mode(tty_in: RawFd) -> Result<(Termios, PosixKeyMap)> {
let original_mode = Termios::from_fd(tty_in)?;
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 &=
!(termios::BRKINT | termios::ICRNL | termios::INPCK | termios::ISTRIP | termios::IXON);
// we don't want raw output, it turns newlines into straight line feeds
// disable all output processing
// raw.c_oflag = raw.c_oflag & !(OutputFlags::OPOST);

// character-size mark (8 bits)
raw.c_cflag |= termios::CS8;
// disable echoing, canonical mode, extended input processing and signals
raw.c_lflag &= !(termios::ECHO | termios::ICANON | termios::IEXTEN | termios::ISIG);
raw.c_cc[termios::VMIN] = 1; // One character-at-a-time input
raw.c_cc[termios::VTIME] = 0; // with blocking read

let mut key_map: HashMap<KeyEvent, Cmd> = HashMap::with_capacity(4);
map_key(&mut key_map, &raw, termios::VEOF, "VEOF", Cmd::EndOfFile);
map_key(&mut key_map, &raw, termios::VINTR, "VINTR", Cmd::Interrupt);
map_key(&mut key_map, &raw, termios::VQUIT, "VQUIT", Cmd::Interrupt);
map_key(&mut key_map, &raw, termios::VSUSP, "VSUSP", Cmd::Suspend);

termios::tcsetattr(tty_in, termios::TCSADRAIN, &raw)?;
Ok((original_mode, key_map))
}
fn map_key(
key_map: &mut HashMap<KeyEvent, Cmd>,
raw: &Termios,
index: usize,
name: &str,
cmd: Cmd,
) {
let cc = char::from(raw.c_cc[index]);
let key = KeyEvent::new(cc, M::NONE);
log::debug!(target: "rustyline", "{}: {:?}", name, key);
key_map.insert(key, cmd);
}
}

#[cfg(test)]
mod test {
use super::{Position, PosixRenderer, PosixTerminal, Renderer};
Expand Down

0 comments on commit f3adec4

Please sign in to comment.