Skip to content

Commit

Permalink
Use termios from termios crate
Browse files Browse the repository at this point in the history
The termios interface from the nix crate is lacking a thought through
design. Severely broken on at least one platform. Please see [#2071][]
for details.

The termios crate stays closer to the C API and thus both works with
illumos and reduces the risk of bugs on other platforms.

[#2071]: nix-rust/nix#2071
  • Loading branch information
nospam3089 committed Jul 14, 2023
1 parent a855443 commit c3a2d08
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 22 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ nix = { version = "0.26", 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"

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

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

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]);
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);
Expand Down Expand Up @@ -1326,38 +1333,33 @@ impl Term for PosixTerminal {

fn enable_raw_mode(&mut self) -> Result<(Self::Mode, PosixKeyMap)> {
use nix::errno::Errno::ENOTTY;
use nix::sys::termios::{ControlFlags, InputFlags, LocalFlags};
if !self.is_in_a_tty {
return Err(ENOTTY.into());
}
let original_mode = termios::tcgetattr(self.tty_in)?;
let mut raw = original_mode.clone();
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.input_flags &= !(InputFlags::BRKINT
| InputFlags::ICRNL
| InputFlags::INPCK
| InputFlags::ISTRIP
| InputFlags::IXON);
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.control_flags |= ControlFlags::CS8;
raw.c_cflag |= termios::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
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, 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);
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(self.tty_in, SetArg::TCSADRAIN, &raw)?;
tcsetattr(self.tty_in, termios::TCSADRAIN, &raw)?;

self.raw_mode.store(true, Ordering::SeqCst);
// enable bracketed paste
Expand Down

0 comments on commit c3a2d08

Please sign in to comment.