From 8e6dfded7ac1f0b04c2a0a4e6a5395613ae200eb Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Sat, 14 Nov 2020 13:27:42 +0100 Subject: [PATCH] fix(ux): properly echo characters and do not mess up exit (#44) --- src/main.rs | 1 + src/os_input_output.rs | 32 ++++++++++++++++++++++++++------ src/tests/fakes.rs | 4 ++++ 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/src/main.rs b/src/main.rs index becdd457..1f30d334 100644 --- a/src/main.rs +++ b/src/main.rs @@ -341,6 +341,7 @@ pub fn start(mut os_input: Box, opts: Opt) { let goto_start_of_last_line = format!("\u{1b}[{};{}H", full_screen_ws.ws_row, 1); let goodbye_message = format!("{}\n{}Bye from Mosaic!", goto_start_of_last_line, reset_style); + os_input.unset_raw_mode(0); os_input.get_stdout_writer().write(goodbye_message.as_bytes()).unwrap(); os_input.get_stdout_writer().flush().unwrap(); } diff --git a/src/os_input_output.rs b/src/os_input_output.rs index 6363d2e6..14690cf5 100644 --- a/src/os_input_output.rs +++ b/src/os_input_output.rs @@ -1,6 +1,7 @@ use nix::unistd::{read, write, ForkResult, Pid}; use nix::fcntl::{fcntl, FcntlArg, OFlag}; use nix::sys::termios::{ + Termios, tcgetattr, cfmakeraw, tcsetattr, @@ -14,6 +15,7 @@ use std::os::unix::io::RawFd; use std::process::{Command, Child}; use std::io::{Read, Write}; use std::path::PathBuf; +use std::sync::{Arc, Mutex}; use std::env; @@ -26,6 +28,13 @@ fn into_raw_mode(pid: RawFd) { }; } +fn unset_raw_mode(pid: RawFd, mut orig_termios: Termios) { + match tcsetattr(pid, SetArg::TCSANOW, &mut orig_termios) { + Ok(_) => {}, + Err(e) => panic!("error {:?}", e) + }; +} + pub fn get_terminal_size_using_fd(fd: RawFd) -> Winsize { // TODO: do this with the nix ioctl use libc::ioctl; @@ -91,10 +100,9 @@ fn handle_command_exit(mut child: Child) { } } -fn spawn_terminal (file_to_open: Option) -> (RawFd, RawFd) { +fn spawn_terminal (file_to_open: Option, orig_termios: Termios) -> (RawFd, RawFd) { let (pid_primary, pid_secondary): (RawFd, RawFd) = { - let current_termios = tcgetattr(0).unwrap(); - match forkpty(None, Some(¤t_termios)) { + match forkpty(None, Some(&orig_termios)) { Ok(fork_pty_res) => { let pid_primary = fork_pty_res.master; let pid_secondary = match fork_pty_res.fork_result { @@ -134,12 +142,15 @@ fn spawn_terminal (file_to_open: Option) -> (RawFd, RawFd) { } #[derive(Clone)] -pub struct OsInputOutput {} +pub struct OsInputOutput { + orig_termios: Arc>, +} pub trait OsApi: Send + Sync { fn get_terminal_size_using_fd(&self, pid: RawFd) -> Winsize; fn set_terminal_size_using_fd(&mut self, pid: RawFd, cols: u16, rows: u16); fn into_raw_mode(&mut self, pid: RawFd); + fn unset_raw_mode(&mut self, pid: RawFd); fn spawn_terminal(&mut self, file_to_open: Option) -> (RawFd, RawFd); fn read_from_tty_stdout(&mut self, pid: RawFd, buf: &mut [u8]) -> Result; fn write_to_tty_stdin(&mut self, pid: RawFd, buf: &mut [u8]) -> Result; @@ -160,8 +171,13 @@ impl OsApi for OsInputOutput { fn into_raw_mode(&mut self, pid: RawFd) { into_raw_mode(pid); } + fn unset_raw_mode(&mut self, pid: RawFd) { + let orig_termios = self.orig_termios.lock().unwrap(); + unset_raw_mode(pid, orig_termios.clone()); + } fn spawn_terminal(&mut self, file_to_open: Option) -> (RawFd, RawFd) { - spawn_terminal(file_to_open) + let orig_termios = self.orig_termios.lock().unwrap(); + spawn_terminal(file_to_open, orig_termios.clone()) } fn read_from_tty_stdout(&mut self, pid: RawFd, buf: &mut [u8]) -> Result { read(pid, buf) @@ -201,5 +217,9 @@ impl Clone for Box } pub fn get_os_input () -> OsInputOutput { - OsInputOutput {} + let current_termios = tcgetattr(0).unwrap(); + let orig_termios = Arc::new(Mutex::new(current_termios)); + OsInputOutput { + orig_termios + } } diff --git a/src/tests/fakes.rs b/src/tests/fakes.rs index be43bde5..a9f34778 100644 --- a/src/tests/fakes.rs +++ b/src/tests/fakes.rs @@ -14,6 +14,7 @@ pub enum IoEvent { Kill(RawFd), SetTerminalSizeUsingFd(RawFd, u16, u16), IntoRawMode(RawFd), + UnsetRawMode(RawFd), TcDrain(RawFd), } @@ -123,6 +124,9 @@ impl OsApi for FakeInputOutput { fn into_raw_mode(&mut self, pid: RawFd) { self.io_events.lock().unwrap().push(IoEvent::IntoRawMode(pid)); } + fn unset_raw_mode(&mut self, pid: RawFd) { + self.io_events.lock().unwrap().push(IoEvent::UnsetRawMode(pid)); + } fn spawn_terminal(&mut self, _file_to_open: Option) -> (RawFd, RawFd) { let next_terminal_id = { self.read_buffers.lock().unwrap().keys().len() as RawFd + 1 }; self.add_terminal(next_terminal_id);