diff -Naur fish-shell-bb7dce941acf51b11ee49994f799ba90911872ac/src/builtins/fg.rs fish-shell-55e925526480e9e22d659724660ae9e484bc77fd/src/builtins/fg.rs
--- fish-shell-bb7dce941acf51b11ee49994f799ba90911872ac/src/builtins/fg.rs	2026-02-01 04:13:13.000000000 -0500
+++ fish-shell-55e925526480e9e22d659724660ae9e484bc77fd/src/builtins/fg.rs	2026-02-01 03:58:51.000000000 -0500
@@ -6,9 +6,7 @@
 use crate::tokenizer::tok_command;
 use crate::wutil::perror;
 use crate::{env::EnvMode, tty_handoff::TtyHandoff};
-use libc::STDIN_FILENO;
-use nix::sys::termios::{self, tcsetattr};
-use std::os::fd::BorrowedFd;
+use libc::{STDIN_FILENO, TCSADRAIN};
 
 use super::prelude::*;
 
@@ -144,14 +142,9 @@
         }
         let tmodes = job_group.tmodes.borrow();
         if job_group.wants_terminal() && tmodes.is_some() {
-            let tmodes = tmodes.as_ref().unwrap();
-            if tcsetattr(
-                unsafe { BorrowedFd::borrow_raw(STDIN_FILENO) },
-                termios::SetArg::TCSADRAIN,
-                tmodes,
-            )
-            .is_err()
-            {
+            let termios = tmodes.as_ref().unwrap();
+            let res = unsafe { libc::tcsetattr(STDIN_FILENO, TCSADRAIN, termios) };
+            if res < 0 {
                 perror("tcsetattr");
             }
         }
diff -Naur fish-shell-bb7dce941acf51b11ee49994f799ba90911872ac/src/builtins/fish_key_reader.rs fish-shell-55e925526480e9e22d659724660ae9e484bc77fd/src/builtins/fish_key_reader.rs
--- fish-shell-bb7dce941acf51b11ee49994f799ba90911872ac/src/builtins/fish_key_reader.rs	2026-02-01 04:13:13.000000000 -0500
+++ fish-shell-55e925526480e9e22d659724660ae9e484bc77fd/src/builtins/fish_key_reader.rs	2026-02-01 03:58:51.000000000 -0500
@@ -47,7 +47,7 @@
 
     for evt in [VINTR, VEOF] {
         let modes = shell_modes();
-        let cc = Key::from_single_byte(modes.control_chars[evt]);
+        let cc = Key::from_single_byte(modes.c_cc[evt]);
 
         if match_key_event_to_key(&key_evt, &cc).is_some() {
             if recent_keys
@@ -61,7 +61,7 @@
             }
             streams.err.appendln(&wgettext_fmt!(
                 "Press ctrl-%c again to exit",
-                char::from(modes.control_chars[evt] + 0x60)
+                char::from(modes.c_cc[evt] + 0x60)
             ));
             return false;
         }
@@ -162,8 +162,8 @@
         let modes = shell_modes();
         streams.err.appendln(&wgettext_fmt!(
             "or press ctrl-%c or ctrl-%c twice in a row.",
-            char::from(modes.control_chars[VINTR] + 0x60),
-            char::from(modes.control_chars[VEOF] + 0x60)
+            char::from(modes.c_cc[VINTR] + 0x60),
+            char::from(modes.c_cc[VEOF] + 0x60)
         ));
         streams.err.appendln(L!("\n"));
     }
diff -Naur fish-shell-bb7dce941acf51b11ee49994f799ba90911872ac/src/common.rs fish-shell-55e925526480e9e22d659724660ae9e484bc77fd/src/common.rs
--- fish-shell-bb7dce941acf51b11ee49994f799ba90911872ac/src/common.rs	2026-02-01 04:13:13.000000000 -0500
+++ fish-shell-55e925526480e9e22d659724660ae9e484bc77fd/src/common.rs	2026-02-01 03:58:51.000000000 -0500
@@ -19,7 +19,6 @@
 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::*;
@@ -885,7 +884,7 @@
     Some(in_pos)
 }
 
-pub fn shell_modes() -> MutexGuard<'static, Termios> {
+pub fn shell_modes() -> MutexGuard<'static, libc::termios> {
     crate::reader::SHELL_MODES.lock().unwrap()
 }
 
diff -Naur fish-shell-bb7dce941acf51b11ee49994f799ba90911872ac/src/input_common.rs fish-shell-55e925526480e9e22d659724660ae9e484bc77fd/src/input_common.rs
--- fish-shell-bb7dce941acf51b11ee49994f799ba90911872ac/src/input_common.rs	2026-02-01 04:13:13.000000000 -0500
+++ fish-shell-55e925526480e9e22d659724660ae9e484bc77fd/src/input_common.rs	2026-02-01 03:58:51.000000000 -0500
@@ -878,7 +878,7 @@
                                 self.push_back(evt);
                             }
                         });
-                        let vintr = shell_modes().control_chars[libc::VINTR];
+                        let vintr = shell_modes().c_cc[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 select_interrupted(&mut self) {}
 
     fn enqueue_interrupt_key(&mut self) {
-        let vintr = shell_modes().control_chars[libc::VINTR];
+        let vintr = shell_modes().c_cc[libc::VINTR];
         if vintr != 0 {
             let interrupt_evt = CharEvent::from_key(KeyEvent::from_single_byte(vintr));
             if stop_query(self.blocking_query()) {
diff -Naur fish-shell-bb7dce941acf51b11ee49994f799ba90911872ac/src/job_group.rs fish-shell-55e925526480e9e22d659724660ae9e484bc77fd/src/job_group.rs
--- fish-shell-bb7dce941acf51b11ee49994f799ba90911872ac/src/job_group.rs	2026-02-01 04:13:13.000000000 -0500
+++ fish-shell-55e925526480e9e22d659724660ae9e484bc77fd/src/job_group.rs	2026-02-01 03:58:51.000000000 -0500
@@ -2,7 +2,6 @@
 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};
@@ -61,7 +60,7 @@
 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<Termios>>,
+    pub tmodes: RefCell<Option<libc::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
diff -Naur fish-shell-bb7dce941acf51b11ee49994f799ba90911872ac/src/reader/reader.rs fish-shell-55e925526480e9e22d659724660ae9e484bc77fd/src/reader/reader.rs
--- fish-shell-bb7dce941acf51b11ee49994f799ba90911872ac/src/reader/reader.rs	2026-02-01 04:13:13.000000000 -0500
+++ fish-shell-55e925526480e9e22d659724660ae9e484bc77fd/src/reader/reader.rs	2026-02-01 03:58:51.000000000 -0500
@@ -127,10 +127,10 @@
 };
 use fish_wcstringutil::{IsPrefix, is_prefix};
 use libc::{
-    _POSIX_VDISABLE, EIO, EISDIR, ENOTTY, EPERM, ESRCH, O_NONBLOCK, O_RDONLY, SIGINT,
-    STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO, VMIN, VQUIT, VSUSP, VTIME, c_char,
+    _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,
 };
-use nix::sys::termios::{self, SetArg, Termios, tcgetattr, tcsetattr};
 use nix::{
     fcntl::OFlag,
     sys::{
@@ -144,6 +144,7 @@
     cell::UnsafeCell,
     cmp,
     io::BufReader,
+    mem::MaybeUninit,
     num::NonZeroUsize,
     ops::{ControlFlow, Range},
     os::fd::{AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd},
@@ -168,20 +169,16 @@
 
 static EXIT_STATE: AtomicU8 = AtomicU8::new(ExitState::None as u8);
 
-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()));
+pub static SHELL_MODES: LazyLock<Mutex<libc::termios>> =
+    LazyLock::new(|| Mutex::new(unsafe { std::mem::zeroed() }));
 
 /// 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<Termios>> =
-    LazyLock::new(|| Mutex::new(zeroed_termios()));
+static TTY_MODES_FOR_EXTERNAL_CMDS: LazyLock<Mutex<libc::termios>> =
+    LazyLock::new(|| Mutex::new(unsafe { std::mem::zeroed() }));
 
 static RUN_COUNT: AtomicU64 = AtomicU64::new(0);
 
@@ -226,11 +223,13 @@
     };
     let fd = devnull.as_raw_fd();
     for stdfd in [STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO] {
-        if matches!(
-            tcgetattr(unsafe { BorrowedFd::borrow_raw(stdfd) }),
-            Err(nix::Error::EIO | nix::Error::ENOTTY)
-        ) {
-            unsafe { libc::dup2(fd, stdfd) };
+        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);
+            }
         }
     }
 }
@@ -987,24 +986,18 @@
     }
 }
 
-const FLOW_CONTROL_FLAGS: termios::InputFlags = {
-    use termios::InputFlags;
-    InputFlags::IXON.union(InputFlags::IXOFF)
-};
+const FLOW_CONTROL_FLAGS: libc::tcflag_t = IXON | IXOFF;
 
 /// Initialize the reader.
 pub fn reader_init(will_restore_foreground_pgroup: bool) {
-    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(),
-    };
+    // 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);
+    }
 
     if !cfg!(test) {
         assert!(AT_EXIT.get().is_none());
@@ -1016,12 +1009,12 @@
     term_fix_external_modes(&mut external_modes);
 
     // Disable flow control by default.
-    external_modes.input_flags &= !FLOW_CONTROL_FLAGS;
+    external_modes.c_iflag &= !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.clone();
+        *shell_modes = external_modes;
         term_fix_shell_modes(&mut shell_modes);
     }
 
@@ -1051,7 +1044,7 @@
         return;
     }
     if let Some(modes) = safe_get_terminal_mode_on_startup() {
-        unsafe { libc::tcsetattr(STDIN_FILENO, libc::TCSANOW, modes) };
+        unsafe { libc::tcsetattr(STDIN_FILENO, TCSANOW, modes) };
     }
 }
 
@@ -1213,7 +1206,7 @@
 /// commandline.
 pub fn reader_readline(
     parser: &Parser,
-    old_modes: Option<Termios>,
+    old_modes: Option<libc::termios>,
     nchars: Option<NonZeroUsize>,
 ) -> Option<WString> {
     let data = current_data().unwrap();
@@ -2542,7 +2535,7 @@
     /// Return the command, or none if we were asked to cancel (e.g. SIGHUP).
     fn readline(
         &mut self,
-        old_modes: Option<Termios>,
+        old_modes: Option<libc::termios>,
         nchars: Option<NonZeroUsize>,
     ) -> Option<WString> {
         let mut tty = TtyHandoff::new(reader_save_screen_state);
@@ -2640,12 +2633,7 @@
             // 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 tcsetattr(
-                    unsafe { BorrowedFd::borrow_raw(self.conf.inputfd) },
-                    SetArg::TCSANOW,
-                    &old_modes,
-                )
-                .is_err()
+                if unsafe { libc::tcsetattr(self.conf.inputfd, TCSANOW, &old_modes) } == -1
                     && is_interactive_session()
                 {
                     perror("tcsetattr");
@@ -4714,39 +4702,32 @@
 
 // Turning off OPOST or ONLCR breaks output (staircase effect), we don't allow it.
 // See #7133.
-fn term_fix_oflag(modes: &mut Termios) {
-    modes.output_flags |= {
-        use termios::OutputFlags;
+fn term_fix_oflag(modes: &mut libc::termios) {
+    modes.c_oflag |= {
         // turn on "implementation-defined post processing" - this often changes how line breaks work.
-        OutputFlags::OPOST
+        OPOST
         // "translate newline to carriage return-newline" - without you see staircase output.
-        | OutputFlags::ONLCR
+        | ONLCR
     };
 }
 
 /// Restore terminal settings we care about, to prevent a broken shell.
-fn term_fix_shell_modes(modes: &mut Termios) {
-    modes.input_flags &= {
-        use termios::InputFlags;
+fn term_fix_shell_modes(modes: &mut libc::termios) {
+    modes.c_iflag &= {
         // disable mapping CR (\cM) to NL (\cJ)
-        !InputFlags::ICRNL
+        !ICRNL
         // disable mapping NL (\cJ) to CR (\cM)
-        & !InputFlags::INLCR
+        & !INLCR
     };
-    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
+    modes.c_lflag &= {
+        !ECHO
+        & !ICANON
+        & !IEXTEN // turn off handling of discard and lnext characters
+        & !FLUSHO
     };
     term_fix_oflag(modes);
 
-    let c_cc = &mut modes.control_chars;
+    let c_cc = &mut modes.c_cc;
     c_cc[VMIN] = 1;
     c_cc[VTIME] = 0;
 
@@ -4760,76 +4741,57 @@
     c_cc[VQUIT] = disabling_char;
 }
 
-fn term_fix_external_modes(modes: &mut Termios) {
+fn term_fix_external_modes(modes: &mut libc::termios) {
     term_fix_oflag(modes);
     // These cause other ridiculous behaviors like input not being shown.
-    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
-    };
+    modes.c_lflag = (modes.c_lflag | ECHO | ICANON | IEXTEN) & !FLUSHO;
+    modes.c_iflag = (modes.c_iflag | ICRNL) & !INLCR;
 }
 
 /// Give up control of terminal.
 fn term_donate(quiet: bool /* = false */) {
-    loop {
-        match 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;
+    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");
             }
+            break;
         }
-        break;
     }
 }
 
 /// Copy the (potentially changed) terminal modes and use them from now on.
 pub fn term_copy_modes() {
-    let mut external_modes = tcgetattr(unsafe { BorrowedFd::borrow_raw(STDIN_FILENO) })
-        .unwrap_or_else(|_| zeroed_termios());
+    let mut modes = MaybeUninit::uninit();
+    unsafe { libc::tcgetattr(STDIN_FILENO, modes.as_mut_ptr()) };
+    let mut external_modes = unsafe { modes.assume_init() };
     // 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.input_flags =
-        (shell_modes.input_flags & !FLOW_CONTROL_FLAGS) | external_flow_control;
+    shell_modes.c_iflag =
+        (shell_modes.c_iflag & !FLOW_CONTROL_FLAGS) | (external_modes.c_iflag & FLOW_CONTROL_FLAGS);
 }
 
 pub fn set_shell_modes(fd: RawFd, whence: &str) -> bool {
     let ok = loop {
-        match tcsetattr(
-            unsafe { BorrowedFd::borrow_raw(fd) },
-            SetArg::TCSANOW,
-            &shell_modes(),
-        ) {
-            Ok(_) => break true,
-            Err(nix::Error::EINTR) => continue,
-            Err(_) => break false,
+        let ok = unsafe { libc::tcsetattr(fd, TCSANOW, &*shell_modes()) } != -1;
+        if ok || errno().0 != EINTR {
+            break ok;
         }
     };
     if !ok {
@@ -4842,14 +4804,18 @@
     ok
 }
 
-pub fn set_shell_modes_temporarily(inputfd: RawFd) -> Option<Termios> {
+pub fn set_shell_modes_temporarily(inputfd: RawFd) -> Option<libc::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 = tcgetattr(unsafe { BorrowedFd::borrow_raw(inputfd) }).ok();
+    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() })
+    };
 
     // Set the new modes.
     set_shell_modes(inputfd, "readline");
diff -Naur fish-shell-bb7dce941acf51b11ee49994f799ba90911872ac/src/screen.rs fish-shell-55e925526480e9e22d659724660ae9e484bc77fd/src/screen.rs
--- fish-shell-bb7dce941acf51b11ee49994f799ba90911872ac/src/screen.rs	2026-02-01 04:13:13.000000000 -0500
+++ fish-shell-55e925526480e9e22d659724660ae9e484bc77fd/src/screen.rs	2026-02-01 03:58:51.000000000 -0500
@@ -19,8 +19,7 @@
 use std::sync::atomic::AtomicU32;
 use std::time::SystemTime;
 
-use libc::{STDERR_FILENO, STDOUT_FILENO};
-use nix::sys::termios;
+use libc::{ONLCR, STDERR_FILENO, STDOUT_FILENO};
 
 use crate::common::{
     get_ellipsis_char, get_omitted_newline_str, has_working_tty_timestamps, shell_modes, wcs2bytes,
@@ -891,9 +891,7 @@
         let s = if y_steps < 0 {
             Some(CursorUp)
         } else if y_steps > 0 {
-            if shell_modes()
-                .output_flags
-                .contains(termios::OutputFlags::ONLCR)
+            if (shell_modes().c_oflag & ONLCR) != 0
             {
                 // See GitHub issue #4505.
                 // Most consoles use a simple newline as the cursor down escape.
diff -Naur fish-shell-bb7dce941acf51b11ee49994f799ba90911872ac/src/tty_handoff.rs fish-shell-55e925526480e9e22d659724660ae9e484bc77fd/src/tty_handoff.rs
--- fish-shell-bb7dce941acf51b11ee49994f799ba90911872ac/src/tty_handoff.rs	2026-02-01 04:13:13.000000000 -0500
+++ fish-shell-55e925526480e9e22d659724660ae9e484bc77fd/src/tty_handoff.rs	2026-02-01 03:58:51.000000000 -0500
@@ -20,9 +20,8 @@
 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::os::fd::BorrowedFd;
+use std::mem::MaybeUninit;
 use std::sync::{
     OnceLock,
     atomic::{AtomicBool, AtomicPtr, Ordering},
@@ -409,17 +408,12 @@
 
     /// Save the current tty modes into the owning job group, if we are transferred.
     pub fn save_tty_modes(&mut self) {
-        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");
-                }
+        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");
             }
         }
     }
