From ae4f5469711705e2686fd2be65e4fbc700f93e12 Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Sun, 7 Mar 2021 18:38:22 -0500 Subject: separate out the guards from the main structs --- src/async.rs | 58 +++++++++++++++++++++++++++++++++++++++------------------ src/blocking.rs | 52 +++++++++++++++++++++++++++++++++++---------------- src/input.rs | 56 ++++++++++++++++++++++++++++++++++++------------------- src/lib.rs | 4 ++-- 4 files changed, 115 insertions(+), 55 deletions(-) (limited to 'src') diff --git a/src/async.rs b/src/async.rs index 0a858c8..9e8c220 100644 --- a/src/async.rs +++ b/src/async.rs @@ -2,6 +2,28 @@ use futures_lite::io::AsyncWriteExt as _; use super::private::TextmodeImpl as _; +pub struct ScreenGuard { + cleaned_up: bool, +} + +impl ScreenGuard { + pub async fn cleanup(&mut self) -> std::io::Result<()> { + if self.cleaned_up { + return Ok(()); + } + self.cleaned_up = true; + write_stdout(super::DEINIT).await + } +} + +impl Drop for ScreenGuard { + fn drop(&mut self) { + futures_lite::future::block_on(async { + let _ = self.cleanup().await; + }); + } +} + pub struct Output { cur: vt100::Parser, next: vt100::Parser, @@ -28,7 +50,16 @@ impl super::private::TextmodeImpl for Output { impl super::Textmode for Output {} impl Output { - pub async fn new() -> std::io::Result { + pub async fn new() -> std::io::Result<(Self, ScreenGuard)> { + write_stdout(super::INIT).await?; + + Ok(( + Self::new_without_screen(), + ScreenGuard { cleaned_up: false }, + )) + } + + pub fn new_without_screen() -> Self { let (rows, cols) = match terminal_size::terminal_size() { Some((terminal_size::Width(w), terminal_size::Height(h))) => { (h, w) @@ -37,16 +68,7 @@ impl Output { }; let cur = vt100::Parser::new(rows, cols, 0); let next = vt100::Parser::new(rows, cols, 0); - - let self_ = Self { cur, next }; - self_.write_stdout(super::INIT).await?; - Ok(self_) - } - - // TODO: without async drop or async closures, i'm not sure how to do - // better than this - pub async fn cleanup(&mut self) -> std::io::Result<()> { - self.write_stdout(super::DEINIT).await + Self { cur, next } } pub async fn refresh(&mut self) -> std::io::Result<()> { @@ -57,16 +79,16 @@ impl Output { self.next().screen().bells_diff(self.cur().screen()), ]; for diff in diffs { - self.write_stdout(&diff).await?; + write_stdout(&diff).await?; self.cur_mut().process(&diff); } Ok(()) } +} - async fn write_stdout(&self, buf: &[u8]) -> std::io::Result<()> { - let mut stdout = blocking::Unblock::new(std::io::stdout()); - stdout.write_all(buf).await?; - stdout.flush().await?; - Ok(()) - } +async fn write_stdout(buf: &[u8]) -> std::io::Result<()> { + let mut stdout = blocking::Unblock::new(std::io::stdout()); + stdout.write_all(buf).await?; + stdout.flush().await?; + Ok(()) } diff --git a/src/blocking.rs b/src/blocking.rs index 53cc54c..fe33e6e 100644 --- a/src/blocking.rs +++ b/src/blocking.rs @@ -2,6 +2,26 @@ use std::io::Write as _; use super::private::TextmodeImpl as _; +pub struct ScreenGuard { + cleaned_up: bool, +} + +impl ScreenGuard { + pub fn cleanup(&mut self) -> std::io::Result<()> { + if self.cleaned_up { + return Ok(()); + } + self.cleaned_up = true; + write_stdout(super::DEINIT) + } +} + +impl Drop for ScreenGuard { + fn drop(&mut self) { + let _ = self.cleanup(); + } +} + pub struct Output { cur: vt100::Parser, next: vt100::Parser, @@ -28,7 +48,15 @@ impl super::private::TextmodeImpl for Output { impl super::Textmode for Output {} impl Output { - pub fn new() -> std::io::Result { + pub fn new() -> std::io::Result<(Self, ScreenGuard)> { + write_stdout(super::INIT)?; + Ok(( + Self::new_without_screen(), + ScreenGuard { cleaned_up: false }, + )) + } + + pub fn new_without_screen() -> Self { let (rows, cols) = match terminal_size::terminal_size() { Some((terminal_size::Width(w), terminal_size::Height(h))) => { (h, w) @@ -38,9 +66,7 @@ impl Output { let cur = vt100::Parser::new(rows, cols, 0); let next = vt100::Parser::new(rows, cols, 0); - let self_ = Self { cur, next }; - self_.write_stdout(super::INIT)?; - Ok(self_) + Self { cur, next } } pub fn refresh(&mut self) -> std::io::Result<()> { @@ -51,22 +77,16 @@ impl Output { self.next().screen().bells_diff(self.cur().screen()), ]; for diff in diffs { - self.write_stdout(&diff)?; + write_stdout(&diff)?; self.cur_mut().process(&diff); } Ok(()) } - - fn write_stdout(&self, buf: &[u8]) -> std::io::Result<()> { - let mut stdout = std::io::stdout(); - stdout.write_all(buf)?; - stdout.flush()?; - Ok(()) - } } -impl Drop for Output { - fn drop(&mut self) { - let _ = self.write_stdout(super::DEINIT); - } +fn write_stdout(buf: &[u8]) -> std::io::Result<()> { + let mut stdout = std::io::stdout(); + stdout.write_all(buf)?; + stdout.flush()?; + Ok(()) } diff --git a/src/input.rs b/src/input.rs index fb0d80f..a61afa5 100644 --- a/src/input.rs +++ b/src/input.rs @@ -79,15 +79,39 @@ impl Key { } } -#[derive(Clone)] -pub struct Input { +pub struct RawGuard { termios: nix::sys::termios::Termios, + cleaned_up: bool, +} + +impl RawGuard { + pub fn cleanup(&mut self) { + if self.cleaned_up { + return; + } + self.cleaned_up = true; + let stdin = std::io::stdin().as_raw_fd(); + let _ = nix::sys::termios::tcsetattr( + stdin, + nix::sys::termios::SetArg::TCSANOW, + &self.termios, + ); + } +} + +impl Drop for RawGuard { + fn drop(&mut self) { + self.cleanup(); + } +} + +pub struct Input { buf: Vec, } #[allow(clippy::new_without_default)] impl Input { - pub fn new() -> Self { + pub fn new() -> (Self, RawGuard) { let stdin = std::io::stdin().as_raw_fd(); let termios = nix::sys::termios::tcgetattr(stdin).unwrap(); let mut termios_raw = termios.clone(); @@ -98,8 +122,17 @@ impl Input { &termios_raw, ) .unwrap(); + ( + Self::new_without_raw(), + RawGuard { + termios, + cleaned_up: false, + }, + ) + } + + pub fn new_without_raw() -> Self { Self { - termios, buf: Vec::with_capacity(4096), } } @@ -120,15 +153,6 @@ impl Input { self.real_read_key(false, true) } - pub fn cleanup(&mut self) { - let stdin = std::io::stdin().as_raw_fd(); - let _ = nix::sys::termios::tcsetattr( - stdin, - nix::sys::termios::SetArg::TCSANOW, - &self.termios, - ); - } - fn real_read_key( &mut self, combine: bool, @@ -389,9 +413,3 @@ impl std::io::Read for Input { std::io::stdin().read(buf) } } - -impl Drop for Input { - fn drop(&mut self) { - self.cleanup(); - } -} diff --git a/src/lib.rs b/src/lib.rs index 7e1842b..e0bd91f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,12 +4,12 @@ pub mod color; pub mod blocking; mod input; -pub use input::{Input, Key}; +pub use input::{Input, Key, RawGuard}; #[cfg(feature = "async")] pub mod r#async; #[cfg(feature = "async")] -pub use r#async::Output; +pub use r#async::{Output, ScreenGuard}; const INIT: &[u8] = b"\x1b7\x1b[?47h\x1b[2J\x1b[H\x1b[?25h"; const DEINIT: &[u8] = b"\x1b[?47l\x1b8\x1b[?25h"; -- cgit v1.2.3-54-g00ecf