Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make termios an optional dependency #736

Merged
merged 1 commit into from
Oct 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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