diff --git a/keyfork-prompt/src/alternate_screen.rs b/keyfork-prompt/src/alternate_screen.rs deleted file mode 100644 index ce857dc..0000000 --- a/keyfork-prompt/src/alternate_screen.rs +++ /dev/null @@ -1,80 +0,0 @@ -use std::{ - io::Write, - os::fd::AsRawFd, -}; - -use keyfork_crossterm::{ - cursor::MoveTo, - terminal::{EnterAlternateScreen, LeaveAlternateScreen}, - ExecutableCommand, -}; - -use crate::Result; - -pub(crate) struct AlternateScreen<'a, W> -where - W: Write + AsRawFd + Sized, -{ - write: &'a mut W, -} - -impl<'a, W> AlternateScreen<'a, W> -where - W: Write + AsRawFd + Sized, -{ - pub(crate) fn new(write_handle: &'a mut W) -> Result { - write_handle.execute(EnterAlternateScreen)?.execute(MoveTo(0, 0))?; - Ok(Self { - write: write_handle, - }) - } -} - -impl Write for AlternateScreen<'_, W> -where - W: Write + AsRawFd + Sized, -{ - fn write(&mut self, buf: &[u8]) -> std::io::Result { - self.write.write(buf) - } - - fn flush(&mut self) -> std::io::Result<()> { - self.write.flush() - } -} - -impl AsRawFd for AlternateScreen<'_, W> -where - W: Write + AsRawFd + Sized, -{ - fn as_raw_fd(&self) -> std::os::fd::RawFd { - self.write.as_raw_fd() - } -} - -/* -impl ExecutableCommand for AlternateScreen -where - W: Write + AsRawFd + Sized, -{ - fn execute(&mut self, command: impl crossterm::Command) -> std::io::Result<&mut Self> { - let mut write = self.write.lock().unwrap(); - match write.execute(command) { - Ok(_) => { - drop(write); - Ok(self) - } - Err(e) => Err(e), - } - } -} -*/ - -impl Drop for AlternateScreen<'_, W> -where - W: Write + AsRawFd + Sized, -{ - fn drop(&mut self) { - self.write.execute(LeaveAlternateScreen).unwrap(); - } -} diff --git a/keyfork-prompt/src/lib.rs b/keyfork-prompt/src/lib.rs index 3577503..4fc00e2 100644 --- a/keyfork-prompt/src/lib.rs +++ b/keyfork-prompt/src/lib.rs @@ -10,16 +10,11 @@ use keyfork_crossterm::{ cursor, event::{read, DisableBracketedPaste, EnableBracketedPaste, Event, KeyCode, KeyModifiers}, style::{Print, PrintStyledContent, Stylize}, - terminal::{self, TerminalIoctl, FdTerminal}, + terminal::{self, TerminalIoctl, FdTerminal, EnterAlternateScreen, LeaveAlternateScreen}, tty::IsTty, - QueueableCommand, + QueueableCommand, ExecutableCommand }; -mod alternate_screen; -mod raw_mode; -use alternate_screen::AlternateScreen; -use raw_mode::RawMode; - pub mod validators; #[cfg(feature = "qrencode")] @@ -44,6 +39,89 @@ pub enum Message { Data(String), } +struct TerminalGuard<'a, R, W> where W: Write + AsRawFd { + read: &'a mut BufReader, + write: &'a mut W, + terminal: &'a mut FdTerminal, +} + +impl<'a, R, W> TerminalGuard<'a, R, W> where W: Write + AsRawFd, R: Read { + fn new(read: &'a mut BufReader, write: &'a mut W, terminal: &'a mut FdTerminal) -> Self { + Self { + read, + write, + terminal + } + } + + fn alternate_screen(mut self) -> std::io::Result { + self.execute(EnterAlternateScreen)?; + Ok(self) + } + + fn raw_mode(self) -> std::io::Result { + self.terminal.enable_raw_mode()?; + Ok(self) + } + + fn bracketed_paste(mut self) -> std::io::Result { + self.execute(EnableBracketedPaste)?; + Ok(self) + } +} + +impl TerminalIoctl for TerminalGuard<'_, R, W> where R: Read, W: Write + AsRawFd { + fn enable_raw_mode(&mut self) -> std::io::Result<()> { + self.terminal.enable_raw_mode() + } + + fn disable_raw_mode(&mut self) -> std::io::Result<()> { + self.terminal.disable_raw_mode() + } + + fn size(&self) -> std::io::Result<(u16, u16)> { + self.terminal.size() + } + + fn window_size(&self) -> std::io::Result { + self.terminal.window_size() + } +} + +impl Read for TerminalGuard<'_, R, W> where R: Read, W: Write + AsRawFd { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + self.read.read(buf) + } +} + +impl BufRead for TerminalGuard<'_, R, W> where R: Read, W: Write + AsRawFd { + fn fill_buf(&mut self) -> std::io::Result<&[u8]> { + self.read.fill_buf() + } + + fn consume(&mut self, amt: usize) { + self.read.consume(amt) + } +} + +impl Write for TerminalGuard<'_, R, W> where W: Write + AsRawFd { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.write.write(buf) + } + + fn flush(&mut self) -> std::io::Result<()> { + self.write.flush() + } +} + +impl Drop for TerminalGuard<'_, R, W> where W: Write + AsRawFd { + fn drop(&mut self) { + self.write.execute(DisableBracketedPaste).unwrap(); + self.write.execute(LeaveAlternateScreen).unwrap(); + self.terminal.disable_raw_mode().unwrap(); + } +} + pub struct Terminal { read: BufReader, write: W, @@ -66,8 +144,12 @@ where }) } + fn lock(&mut self) -> TerminalGuard<'_, R, W> { + TerminalGuard::new(&mut self.read, &mut self.write, &mut self.terminal) + } + pub fn prompt_input(&mut self, prompt: &str) -> Result { - let mut terminal = AlternateScreen::new(&mut self.write)?; + let mut terminal = self.lock().alternate_screen()?; terminal .queue(terminal::Clear(terminal::ClearType::All))? .queue(cursor::MoveTo(0, 0))?; @@ -83,7 +165,7 @@ where terminal.flush()?; let mut line = String::new(); - self.read.read_line(&mut line)?; + terminal.read.read_line(&mut line)?; Ok(line) } @@ -118,17 +200,14 @@ where )) } - // TODO: create a wrapper for bracketed paste similar to RawMode #[cfg(feature = "mnemonic")] #[allow(clippy::too_many_lines)] pub fn prompt_wordlist(&mut self, prompt: &str, wordlist: &Wordlist) -> Result { - let mut terminal = AlternateScreen::new(&mut self.write)?; - let mut terminal = RawMode::new(&mut terminal)?; + let mut terminal = self.lock().alternate_screen()?.raw_mode()?.bracketed_paste()?; terminal .queue(terminal::Clear(terminal::ClearType::All))? - .queue(cursor::MoveTo(0, 0))? - .queue(EnableBracketedPaste)?; + .queue(cursor::MoveTo(0, 0))?; let mut lines = prompt.lines().peekable(); let mut prefix_length = 0; while let Some(line) = lines.next() { @@ -142,7 +221,7 @@ where } terminal.flush()?; - let (mut cols, mut _rows) = self.terminal.size()?; + let (mut cols, mut _rows) = terminal.size()?; let mut input = String::new(); loop { @@ -236,8 +315,6 @@ where terminal.flush()?; } - terminal.queue(DisableBracketedPaste)?.flush()?; - Ok(input) } @@ -273,8 +350,7 @@ where // TODO: return secrecy::Secret pub fn prompt_passphrase(&mut self, prompt: &str) -> Result { - let mut terminal = AlternateScreen::new(&mut self.write)?; - let mut terminal = RawMode::new(&mut terminal)?; + let mut terminal = self.lock().alternate_screen()?.raw_mode()?; terminal .queue(terminal::Clear(terminal::ClearType::All))? @@ -292,7 +368,7 @@ where } terminal.flush()?; - let (mut cols, mut _rows) = self.terminal.size()?; + let (mut cols, mut _rows) = terminal.size()?; let mut passphrase = String::new(); loop { @@ -332,11 +408,10 @@ where } pub fn prompt_message(&mut self, prompt: &Message) -> Result<()> { - let mut terminal = AlternateScreen::new(&mut self.write)?; - let mut terminal = RawMode::new(&mut terminal)?; + let mut terminal = self.lock().alternate_screen()?.raw_mode()?; loop { - let (cols, rows) = self.terminal.size()?; + let (cols, rows) = terminal.size()?; terminal .queue(terminal::Clear(terminal::ClearType::All))? diff --git a/keyfork-prompt/src/raw_mode.rs b/keyfork-prompt/src/raw_mode.rs deleted file mode 100644 index a294350..0000000 --- a/keyfork-prompt/src/raw_mode.rs +++ /dev/null @@ -1,79 +0,0 @@ -use std::{ - io::Write, - os::fd::AsRawFd, -}; - -use keyfork_crossterm::terminal::{FdTerminal, TerminalIoctl}; - -use crate::Result; - -pub(crate) struct RawMode<'a, W> -where - W: Write + AsRawFd + Sized, -{ - write: &'a mut W, - terminal: FdTerminal, -} - -impl<'a, W> RawMode<'a, W> -where - W: Write + AsRawFd + Sized, -{ - pub(crate) fn new(write_handle: &'a mut W) -> Result { - let mut terminal = FdTerminal::from(write_handle.as_raw_fd()); - terminal.enable_raw_mode()?; - Ok(Self { - terminal, - write: write_handle, - }) - } -} - -impl Write for RawMode<'_, W> -where - W: Write + AsRawFd + Sized, -{ - fn write(&mut self, buf: &[u8]) -> std::io::Result { - self.write.write(buf) - } - - fn flush(&mut self) -> std::io::Result<()> { - self.write.flush() - } -} - -impl AsRawFd for RawMode<'_, W> -where - W: Write + AsRawFd + Sized, -{ - fn as_raw_fd(&self) -> std::os::fd::RawFd { - self.write.as_raw_fd() - } -} - -/* -impl ExecutableCommand for RawMode -where - W: Write + AsRawFd + Sized, -{ - fn execute(&mut self, command: impl crossterm::Command) -> std::io::Result<&mut Self> { - let mut write = self.write.lock().unwrap(); - match write.execute(command) { - Ok(_) => { - drop(write); - Ok(self) - } - Err(e) => Err(e), - } - } -} -*/ - -impl Drop for RawMode<'_, W> -where - W: Write + AsRawFd + Sized, -{ - fn drop(&mut self) { - self.terminal.disable_raw_mode().unwrap(); - } -}