mirror of
https://github.com/fish-shell/fish-shell.git
synced 2026-06-01 04:41:14 -03:00
termios: use nix wrapper
This commit is contained in:
@@ -6,7 +6,9 @@
|
||||
use crate::tokenizer::tok_command;
|
||||
use crate::wutil::perror;
|
||||
use crate::{env::EnvMode, tty_handoff::TtyHandoff};
|
||||
use libc::{STDIN_FILENO, TCSADRAIN};
|
||||
use libc::STDIN_FILENO;
|
||||
use nix::sys::termios;
|
||||
use std::os::fd::BorrowedFd;
|
||||
|
||||
use super::prelude::*;
|
||||
|
||||
@@ -142,9 +144,14 @@ pub fn fg(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) -> Built
|
||||
}
|
||||
let tmodes = job_group.tmodes.borrow();
|
||||
if job_group.wants_terminal() && tmodes.is_some() {
|
||||
let termios = tmodes.as_ref().unwrap();
|
||||
let res = unsafe { libc::tcsetattr(STDIN_FILENO, TCSADRAIN, termios) };
|
||||
if res < 0 {
|
||||
let tmodes = tmodes.as_ref().unwrap();
|
||||
if termios::tcsetattr(
|
||||
unsafe { BorrowedFd::borrow_raw(STDIN_FILENO) },
|
||||
termios::SetArg::TCSADRAIN,
|
||||
tmodes,
|
||||
)
|
||||
.is_err()
|
||||
{
|
||||
perror("tcsetattr");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ fn should_exit(
|
||||
|
||||
for evt in [VINTR, VEOF] {
|
||||
let modes = shell_modes();
|
||||
let cc = Key::from_single_byte(modes.c_cc[evt]);
|
||||
let cc = Key::from_single_byte(modes.control_chars[evt]);
|
||||
|
||||
if match_key_event_to_key(&key_evt, &cc).is_some() {
|
||||
if recent_keys
|
||||
@@ -61,7 +61,7 @@ fn should_exit(
|
||||
}
|
||||
streams.err.append(&wgettext_fmt!(
|
||||
"Press ctrl-%c again to exit\n",
|
||||
char::from(modes.c_cc[evt] + 0x60)
|
||||
char::from(modes.control_chars[evt] + 0x60)
|
||||
));
|
||||
return false;
|
||||
}
|
||||
@@ -162,8 +162,8 @@ fn setup_and_process_keys(
|
||||
let modes = shell_modes();
|
||||
streams.err.appendln(&wgettext_fmt!(
|
||||
"or press ctrl-%c or ctrl-%c twice in a row.",
|
||||
char::from(modes.c_cc[VINTR] + 0x60),
|
||||
char::from(modes.c_cc[VEOF] + 0x60)
|
||||
char::from(modes.control_chars[VINTR] + 0x60),
|
||||
char::from(modes.control_chars[VEOF] + 0x60)
|
||||
));
|
||||
streams.err.appendln(L!("\n"));
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
use fish_widestring::{
|
||||
ENCODE_DIRECT_END, decode_byte_from_char, encode_byte_to_char, subslice_position,
|
||||
};
|
||||
use nix::sys::termios::Termios;
|
||||
use std::env;
|
||||
use std::ffi::{CStr, CString, OsStr, OsString};
|
||||
use std::os::unix::prelude::*;
|
||||
@@ -884,7 +885,7 @@ pub fn read_unquoted_escape(
|
||||
Some(in_pos)
|
||||
}
|
||||
|
||||
pub fn shell_modes() -> MutexGuard<'static, libc::termios> {
|
||||
pub fn shell_modes() -> MutexGuard<'static, Termios> {
|
||||
crate::reader::SHELL_MODES.lock().unwrap()
|
||||
}
|
||||
|
||||
|
||||
@@ -878,7 +878,7 @@ fn readch(&mut self) -> CharEvent {
|
||||
self.push_back(evt);
|
||||
}
|
||||
});
|
||||
let vintr = shell_modes().c_cc[libc::VINTR];
|
||||
let vintr = shell_modes().control_chars[libc::VINTR];
|
||||
if vintr != 0
|
||||
&& key.is_some_and(|key| {
|
||||
match_key_event_to_key(&key, &Key::from_single_byte(vintr))
|
||||
@@ -1619,7 +1619,7 @@ fn prepare_to_select(&mut self) {}
|
||||
fn select_interrupted(&mut self) {}
|
||||
|
||||
fn enqueue_interrupt_key(&mut self) {
|
||||
let vintr = shell_modes().c_cc[libc::VINTR];
|
||||
let vintr = shell_modes().control_chars[libc::VINTR];
|
||||
if vintr != 0 {
|
||||
let interrupt_evt = CharEvent::from_key(KeyEvent::from_single_byte(vintr));
|
||||
if stop_query(self.blocking_query()) {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
use crate::prelude::*;
|
||||
use crate::proc::{JobGroupRef, Pid};
|
||||
use crate::signal::Signal;
|
||||
use nix::sys::termios::Termios;
|
||||
use std::cell::RefCell;
|
||||
use std::num::NonZeroU32;
|
||||
use std::sync::atomic::{AtomicI32, Ordering};
|
||||
@@ -60,7 +61,7 @@ fn to_arg(self) -> fish_printf::Arg<'a> {
|
||||
pub struct JobGroup {
|
||||
/// If set, the saved terminal modes of this job. This needs to be saved so that we can restore
|
||||
/// the terminal to the same state when resuming a stopped job.
|
||||
pub tmodes: RefCell<Option<libc::termios>>,
|
||||
pub tmodes: RefCell<Option<Termios>>,
|
||||
/// Whether job control is enabled in this `JobGroup` or not.
|
||||
///
|
||||
/// If this is set, then the first process in the root job must be external, as it will become
|
||||
|
||||
@@ -127,10 +127,10 @@
|
||||
};
|
||||
use fish_wcstringutil::{IsPrefix, is_prefix};
|
||||
use libc::{
|
||||
_POSIX_VDISABLE, ECHO, EINTR, EIO, EISDIR, ENOTTY, EPERM, ESRCH, FLUSHO, ICANON, ICRNL, IEXTEN,
|
||||
INLCR, IXOFF, IXON, O_NONBLOCK, O_RDONLY, ONLCR, OPOST, SIGINT, STDERR_FILENO, STDIN_FILENO,
|
||||
STDOUT_FILENO, TCSANOW, VMIN, VQUIT, VSUSP, VTIME, c_char,
|
||||
_POSIX_VDISABLE, EIO, EISDIR, ENOTTY, EPERM, ESRCH, O_NONBLOCK, O_RDONLY, SIGINT,
|
||||
STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO, VMIN, VQUIT, VSUSP, VTIME, c_char,
|
||||
};
|
||||
use nix::sys::termios::{self, SetArg, Termios, tcgetattr};
|
||||
use nix::{
|
||||
fcntl::OFlag,
|
||||
sys::{
|
||||
@@ -144,7 +144,6 @@
|
||||
cell::UnsafeCell,
|
||||
cmp,
|
||||
io::BufReader,
|
||||
mem::MaybeUninit,
|
||||
num::NonZeroUsize,
|
||||
ops::{ControlFlow, Range},
|
||||
os::fd::{AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd},
|
||||
@@ -169,16 +168,20 @@ enum ExitState {
|
||||
|
||||
static EXIT_STATE: AtomicU8 = AtomicU8::new(ExitState::None as u8);
|
||||
|
||||
pub static SHELL_MODES: LazyLock<Mutex<libc::termios>> =
|
||||
LazyLock::new(|| Mutex::new(unsafe { std::mem::zeroed() }));
|
||||
fn zeroed_termios() -> Termios {
|
||||
let termios: libc::termios = unsafe { std::mem::zeroed() };
|
||||
termios.into()
|
||||
}
|
||||
|
||||
pub static SHELL_MODES: LazyLock<Mutex<Termios>> = LazyLock::new(|| Mutex::new(zeroed_termios()));
|
||||
|
||||
/// The valid terminal modes on startup.
|
||||
/// Warning: this is read from the SIGTERM handler! Hence the raw global.
|
||||
static TERMINAL_MODE_ON_STARTUP: OnceLock<libc::termios> = OnceLock::new();
|
||||
|
||||
/// Mode we use to execute programs.
|
||||
static TTY_MODES_FOR_EXTERNAL_CMDS: LazyLock<Mutex<libc::termios>> =
|
||||
LazyLock::new(|| Mutex::new(unsafe { std::mem::zeroed() }));
|
||||
static TTY_MODES_FOR_EXTERNAL_CMDS: LazyLock<Mutex<Termios>> =
|
||||
LazyLock::new(|| Mutex::new(zeroed_termios()));
|
||||
|
||||
static RUN_COUNT: AtomicU64 = AtomicU64::new(0);
|
||||
|
||||
@@ -223,13 +226,11 @@ fn redirect_tty_after_sighup() {
|
||||
};
|
||||
let fd = devnull.as_raw_fd();
|
||||
for stdfd in [STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO] {
|
||||
let mut t = std::mem::MaybeUninit::uninit();
|
||||
unsafe {
|
||||
if libc::tcgetattr(stdfd, t.as_mut_ptr()) != 0
|
||||
&& matches!(errno::errno().0, EIO | ENOTTY)
|
||||
{
|
||||
libc::dup2(fd, stdfd);
|
||||
}
|
||||
if matches!(
|
||||
tcgetattr(unsafe { BorrowedFd::borrow_raw(stdfd) }),
|
||||
Err(e) if matches!(e, nix::Error::EIO | nix::Error::ENOTTY)
|
||||
) {
|
||||
unsafe { libc::dup2(fd, stdfd) };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -986,18 +987,24 @@ fn read_ni(parser: &Parser, fd: RawFd, io: &IoChain) -> Result<(), ErrorCode> {
|
||||
}
|
||||
}
|
||||
|
||||
const FLOW_CONTROL_FLAGS: libc::tcflag_t = IXON | IXOFF;
|
||||
const FLOW_CONTROL_FLAGS: termios::InputFlags = {
|
||||
use termios::InputFlags;
|
||||
InputFlags::IXON.union(InputFlags::IXOFF)
|
||||
};
|
||||
|
||||
/// Initialize the reader.
|
||||
pub fn reader_init(will_restore_foreground_pgroup: bool) {
|
||||
// Save the initial terminal mode.
|
||||
// Note this field is read by a signal handler, so do it atomically, with a leaked mode.
|
||||
let mut terminal_mode_on_startup = unsafe { std::mem::zeroed::<libc::termios>() };
|
||||
let ret = unsafe { libc::tcgetattr(libc::STDIN_FILENO, &mut terminal_mode_on_startup) };
|
||||
// TODO: rationalize behavior if initial tcgetattr() fails.
|
||||
if ret == 0 {
|
||||
TERMINAL_MODE_ON_STARTUP.get_or_init(|| terminal_mode_on_startup);
|
||||
}
|
||||
let terminal_mode_on_startup = match tcgetattr(unsafe { BorrowedFd::borrow_raw(STDIN_FILENO) })
|
||||
{
|
||||
Ok(modes) => {
|
||||
// Save the initial terminal mode.
|
||||
// Note this field is read by a signal handler, so do it atomically, with a leaked mode.
|
||||
// TODO: rationalize behavior if initial tcgetattr() fails.
|
||||
TERMINAL_MODE_ON_STARTUP.get_or_init(|| libc::termios::from(modes.clone()));
|
||||
modes
|
||||
}
|
||||
Err(_) => zeroed_termios(),
|
||||
};
|
||||
|
||||
if !cfg!(test) {
|
||||
assert!(AT_EXIT.get().is_none());
|
||||
@@ -1009,12 +1016,12 @@ pub fn reader_init(will_restore_foreground_pgroup: bool) {
|
||||
term_fix_external_modes(&mut external_modes);
|
||||
|
||||
// Disable flow control by default.
|
||||
external_modes.c_iflag &= !FLOW_CONTROL_FLAGS;
|
||||
external_modes.input_flags &= !FLOW_CONTROL_FLAGS;
|
||||
|
||||
// Set the mode used for the terminal, initialized to the current mode.
|
||||
{
|
||||
let mut shell_modes = shell_modes();
|
||||
*shell_modes = external_modes;
|
||||
*shell_modes = external_modes.clone();
|
||||
term_fix_shell_modes(&mut shell_modes);
|
||||
}
|
||||
|
||||
@@ -1044,7 +1051,7 @@ pub fn safe_restore_term_mode() {
|
||||
return;
|
||||
}
|
||||
if let Some(modes) = safe_get_terminal_mode_on_startup() {
|
||||
unsafe { libc::tcsetattr(STDIN_FILENO, TCSANOW, modes) };
|
||||
unsafe { libc::tcsetattr(STDIN_FILENO, libc::TCSANOW, modes) };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1206,7 +1213,7 @@ pub fn reader_reading_interrupted(data: &mut ReaderData) -> i32 {
|
||||
/// commandline.
|
||||
pub fn reader_readline(
|
||||
parser: &Parser,
|
||||
old_modes: Option<libc::termios>,
|
||||
old_modes: Option<Termios>,
|
||||
nchars: Option<NonZeroUsize>,
|
||||
) -> Option<WString> {
|
||||
let data = current_data().unwrap();
|
||||
@@ -2535,7 +2542,7 @@ impl<'a> Reader<'a> {
|
||||
/// Return the command, or none if we were asked to cancel (e.g. SIGHUP).
|
||||
fn readline(
|
||||
&mut self,
|
||||
old_modes: Option<libc::termios>,
|
||||
old_modes: Option<Termios>,
|
||||
nchars: Option<NonZeroUsize>,
|
||||
) -> Option<WString> {
|
||||
let mut tty = TtyHandoff::new(reader_save_screen_state);
|
||||
@@ -2633,7 +2640,12 @@ fn readline(
|
||||
// The order of the two conditions below is important. Try to restore the mode
|
||||
// in all cases, but only complain if interactive.
|
||||
if let Some(old_modes) = old_modes {
|
||||
if unsafe { libc::tcsetattr(self.conf.inputfd, TCSANOW, &old_modes) } == -1
|
||||
if termios::tcsetattr(
|
||||
unsafe { BorrowedFd::borrow_raw(self.conf.inputfd) },
|
||||
SetArg::TCSANOW,
|
||||
&old_modes,
|
||||
)
|
||||
.is_err()
|
||||
&& is_interactive_session()
|
||||
{
|
||||
perror("tcsetattr");
|
||||
@@ -4702,32 +4714,39 @@ fn select_completion_in_direction(
|
||||
|
||||
// Turning off OPOST or ONLCR breaks output (staircase effect), we don't allow it.
|
||||
// See #7133.
|
||||
fn term_fix_oflag(modes: &mut libc::termios) {
|
||||
modes.c_oflag |= {
|
||||
fn term_fix_oflag(modes: &mut Termios) {
|
||||
modes.output_flags |= {
|
||||
use termios::OutputFlags;
|
||||
// turn on "implementation-defined post processing" - this often changes how line breaks work.
|
||||
OPOST
|
||||
OutputFlags::OPOST
|
||||
// "translate newline to carriage return-newline" - without you see staircase output.
|
||||
| ONLCR
|
||||
| OutputFlags::ONLCR
|
||||
};
|
||||
}
|
||||
|
||||
/// Restore terminal settings we care about, to prevent a broken shell.
|
||||
fn term_fix_shell_modes(modes: &mut libc::termios) {
|
||||
modes.c_iflag &= {
|
||||
fn term_fix_shell_modes(modes: &mut Termios) {
|
||||
modes.input_flags &= {
|
||||
use termios::InputFlags;
|
||||
// disable mapping CR (\cM) to NL (\cJ)
|
||||
!ICRNL
|
||||
!InputFlags::ICRNL
|
||||
// disable mapping NL (\cJ) to CR (\cM)
|
||||
& !INLCR
|
||||
& !InputFlags::INLCR
|
||||
};
|
||||
modes.c_lflag &= {
|
||||
!ECHO
|
||||
& !ICANON
|
||||
& !IEXTEN // turn off handling of discard and lnext characters
|
||||
& !FLUSHO
|
||||
modes.local_flags &= {
|
||||
use termios::LocalFlags;
|
||||
let echo = LocalFlags::ECHO;
|
||||
let flusho = LocalFlags::FLUSHO;
|
||||
let icanon = LocalFlags::ICANON;
|
||||
let iexten = LocalFlags::IEXTEN;
|
||||
!echo
|
||||
& !icanon
|
||||
& !iexten // turn off handling of discard and lnext characters
|
||||
& !flusho
|
||||
};
|
||||
term_fix_oflag(modes);
|
||||
|
||||
let c_cc = &mut modes.c_cc;
|
||||
let c_cc = &mut modes.control_chars;
|
||||
c_cc[VMIN] = 1;
|
||||
c_cc[VTIME] = 0;
|
||||
|
||||
@@ -4741,57 +4760,76 @@ fn term_fix_shell_modes(modes: &mut libc::termios) {
|
||||
c_cc[VQUIT] = disabling_char;
|
||||
}
|
||||
|
||||
fn term_fix_external_modes(modes: &mut libc::termios) {
|
||||
fn term_fix_external_modes(modes: &mut Termios) {
|
||||
term_fix_oflag(modes);
|
||||
// These cause other ridiculous behaviors like input not being shown.
|
||||
modes.c_lflag = (modes.c_lflag | ECHO | ICANON | IEXTEN) & !FLUSHO;
|
||||
modes.c_iflag = (modes.c_iflag | ICRNL) & !INLCR;
|
||||
modes.local_flags = {
|
||||
use termios::LocalFlags;
|
||||
let echo = LocalFlags::ECHO;
|
||||
let flusho = LocalFlags::FLUSHO;
|
||||
let icanon = LocalFlags::ICANON;
|
||||
let iexten = LocalFlags::IEXTEN;
|
||||
(modes.local_flags | echo | icanon | iexten) & !flusho
|
||||
};
|
||||
modes.input_flags = {
|
||||
use termios::InputFlags;
|
||||
let icrnl = InputFlags::ICRNL;
|
||||
let inlcr = InputFlags::INLCR;
|
||||
(modes.input_flags | icrnl) & !inlcr
|
||||
};
|
||||
}
|
||||
|
||||
/// Give up control of terminal.
|
||||
fn term_donate(quiet: bool /* = false */) {
|
||||
while unsafe {
|
||||
libc::tcsetattr(
|
||||
STDIN_FILENO,
|
||||
TCSANOW,
|
||||
&*TTY_MODES_FOR_EXTERNAL_CMDS.lock().unwrap(),
|
||||
)
|
||||
} == -1
|
||||
{
|
||||
if errno().0 != EINTR {
|
||||
if !quiet {
|
||||
flog!(
|
||||
warning,
|
||||
wgettext!("Could not set terminal mode for new job")
|
||||
);
|
||||
perror("tcsetattr");
|
||||
loop {
|
||||
match termios::tcsetattr(
|
||||
unsafe { BorrowedFd::borrow_raw(STDIN_FILENO) },
|
||||
SetArg::TCSANOW,
|
||||
&TTY_MODES_FOR_EXTERNAL_CMDS.lock().unwrap(),
|
||||
) {
|
||||
Ok(_) => (),
|
||||
Err(nix::Error::EINTR) => continue,
|
||||
Err(_) => {
|
||||
if !quiet {
|
||||
flog!(
|
||||
warning,
|
||||
wgettext!("Could not set terminal mode for new job")
|
||||
);
|
||||
perror("tcsetattr");
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// Copy the (potentially changed) terminal modes and use them from now on.
|
||||
pub fn term_copy_modes() {
|
||||
let mut modes = MaybeUninit::uninit();
|
||||
unsafe { libc::tcgetattr(STDIN_FILENO, modes.as_mut_ptr()) };
|
||||
let mut external_modes = unsafe { modes.assume_init() };
|
||||
let mut external_modes = tcgetattr(unsafe { BorrowedFd::borrow_raw(STDIN_FILENO) })
|
||||
.unwrap_or_else(|_| zeroed_termios());
|
||||
// We still want to fix most egregious breakage.
|
||||
// E.g. OPOST is *not* something that should be set globally,
|
||||
// and 99% triggered by a crashed program.
|
||||
term_fix_external_modes(&mut external_modes);
|
||||
let external_flow_control = external_modes.input_flags & FLOW_CONTROL_FLAGS;
|
||||
*TTY_MODES_FOR_EXTERNAL_CMDS.lock().unwrap() = external_modes;
|
||||
|
||||
let mut shell_modes = shell_modes();
|
||||
shell_modes.c_iflag =
|
||||
(shell_modes.c_iflag & !FLOW_CONTROL_FLAGS) | (external_modes.c_iflag & FLOW_CONTROL_FLAGS);
|
||||
shell_modes.input_flags =
|
||||
(shell_modes.input_flags & !FLOW_CONTROL_FLAGS) | external_flow_control;
|
||||
}
|
||||
|
||||
pub fn set_shell_modes(fd: RawFd, whence: &str) -> bool {
|
||||
let ok = loop {
|
||||
let ok = unsafe { libc::tcsetattr(fd, TCSANOW, &*shell_modes()) } != -1;
|
||||
if ok || errno().0 != EINTR {
|
||||
break ok;
|
||||
match termios::tcsetattr(
|
||||
unsafe { BorrowedFd::borrow_raw(fd) },
|
||||
SetArg::TCSANOW,
|
||||
&shell_modes(),
|
||||
) {
|
||||
Ok(_) => break true,
|
||||
Err(nix::Error::EINTR) => continue,
|
||||
Err(_) => break false,
|
||||
}
|
||||
};
|
||||
if !ok {
|
||||
@@ -4804,18 +4842,14 @@ pub fn set_shell_modes(fd: RawFd, whence: &str) -> bool {
|
||||
ok
|
||||
}
|
||||
|
||||
pub fn set_shell_modes_temporarily(inputfd: RawFd) -> Option<libc::termios> {
|
||||
pub fn set_shell_modes_temporarily(inputfd: RawFd) -> Option<Termios> {
|
||||
// It may happen that a command we ran when job control was disabled nevertheless stole the tty
|
||||
// from us. In that case when we read from our fd, it will trigger SIGTTIN. So just
|
||||
// unconditionally reclaim the tty. See #9181.
|
||||
unsafe { libc::tcsetpgrp(inputfd, libc::getpgrp()) };
|
||||
|
||||
// Get the current terminal modes. These will be restored when the function returns.
|
||||
let old_modes = {
|
||||
let mut old_modes = MaybeUninit::uninit();
|
||||
let ok = unsafe { libc::tcgetattr(inputfd, old_modes.as_mut_ptr()) } == 0;
|
||||
ok.then(|| unsafe { old_modes.assume_init() })
|
||||
};
|
||||
let old_modes = tcgetattr(unsafe { BorrowedFd::borrow_raw(inputfd) }).ok();
|
||||
|
||||
// Set the new modes.
|
||||
set_shell_modes(inputfd, "readline");
|
||||
|
||||
@@ -19,7 +19,8 @@
|
||||
use std::sync::atomic::AtomicU32;
|
||||
use std::time::SystemTime;
|
||||
|
||||
use libc::{ONLCR, STDERR_FILENO, STDOUT_FILENO};
|
||||
use libc::{STDERR_FILENO, STDOUT_FILENO};
|
||||
use nix::sys::termios;
|
||||
|
||||
use crate::common::{
|
||||
get_ellipsis_char, get_omitted_newline_str, has_working_tty_timestamps, shell_modes, wcs2bytes,
|
||||
@@ -929,7 +930,9 @@ fn do_move(&mut self, new_x: usize, new_y: usize) {
|
||||
let s = if y_steps < 0 {
|
||||
Some(CursorUp)
|
||||
} else if y_steps > 0 {
|
||||
if (shell_modes().c_oflag & ONLCR) != 0
|
||||
if shell_modes()
|
||||
.output_flags
|
||||
.contains(termios::OutputFlags::ONLCR)
|
||||
&& (!use_terminfo()
|
||||
|| crate::terminal::term()
|
||||
.cursor_down
|
||||
|
||||
@@ -20,8 +20,9 @@
|
||||
use crate::wutil::{perror, wcstoi};
|
||||
use fish_widestring::ToWString;
|
||||
use libc::{EINVAL, ENOTTY, EPERM, STDIN_FILENO, WNOHANG};
|
||||
use nix::sys::termios::tcgetattr;
|
||||
use nix::unistd::getpgrp;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::os::fd::BorrowedFd;
|
||||
use std::sync::{
|
||||
OnceLock,
|
||||
atomic::{AtomicBool, AtomicPtr, Ordering},
|
||||
@@ -408,12 +409,17 @@ pub fn to_job_group(&mut self, jg: &JobGroupRef) {
|
||||
|
||||
/// Save the current tty modes into the owning job group, if we are transferred.
|
||||
pub fn save_tty_modes(&mut self) {
|
||||
if let Some(ref mut owner) = self.owner {
|
||||
let mut tmodes = MaybeUninit::uninit();
|
||||
if unsafe { libc::tcgetattr(STDIN_FILENO, tmodes.as_mut_ptr()) } == 0 {
|
||||
owner.tmodes.replace(Some(unsafe { tmodes.assume_init() }));
|
||||
} else if errno::errno().0 != ENOTTY {
|
||||
perror("tcgetattr");
|
||||
let Some(ref mut owner) = self.owner else {
|
||||
return;
|
||||
};
|
||||
match tcgetattr(unsafe { BorrowedFd::borrow_raw(STDIN_FILENO) }) {
|
||||
Ok(modes) => {
|
||||
owner.tmodes.replace(Some(modes));
|
||||
}
|
||||
Err(err) => {
|
||||
if err != nix::Error::ENOTTY {
|
||||
perror("tcgetattr");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user