aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2022-02-24 02:42:25 -0500
committerJesse Luehrs <doy@tozt.net>2022-02-24 02:50:33 -0500
commit246ae0894d4074c38ea2d2eb520e386b0d36d82d (patch)
treed2e681e4df655a63519193d0b797b09d0afdd4cc /src
parentcd617e021bed862ac3794cc71123959c8d7d7dd1 (diff)
downloadtextmode-246ae0894d4074c38ea2d2eb520e386b0d36d82d.tar.gz
textmode-246ae0894d4074c38ea2d2eb520e386b0d36d82d.zip
move to tokio
Diffstat (limited to 'src')
-rw-r--r--src/blocking/input.rs3
-rw-r--r--src/input.rs56
-rw-r--r--src/lib.rs5
-rw-r--r--src/output.rs31
4 files changed, 57 insertions, 38 deletions
diff --git a/src/blocking/input.rs b/src/blocking/input.rs
index 48d9ee8..63c9ac5 100644
--- a/src/blocking/input.rs
+++ b/src/blocking/input.rs
@@ -36,7 +36,8 @@ impl RawGuard {
/// Switch back from raw mode early.
///
/// # Errors
- /// * `Error::SetTerminalMode`: failed to return the terminal from raw mode
+ /// * `Error::SetTerminalMode`: failed to return the terminal from raw
+ /// mode
pub fn cleanup(&mut self) -> crate::error::Result<()> {
self.termios.take().map_or(Ok(()), |termios| {
let stdin = std::io::stdin().as_raw_fd();
diff --git a/src/input.rs b/src/input.rs
index 90213ec..7f434f5 100644
--- a/src/input.rs
+++ b/src/input.rs
@@ -1,5 +1,5 @@
-use futures_lite::io::AsyncReadExt as _;
use std::os::unix::io::AsRawFd as _;
+use tokio::io::AsyncReadExt as _;
use crate::private::Input as _;
@@ -16,16 +16,20 @@ impl RawGuard {
///
/// # Errors
/// * `Error::SetTerminalMode`: failed to put the terminal into raw mode
+ // spawn_blocking is uncancellable, and the tcgetattr/tcsetattr calls
+ // can't panic, so unwrap is safe here
+ #[allow(clippy::missing_panics_doc)]
pub async fn new() -> crate::error::Result<Self> {
let stdin = std::io::stdin().as_raw_fd();
- let termios = blocking::unblock(move || {
+ let termios = tokio::task::spawn_blocking(move || {
nix::sys::termios::tcgetattr(stdin)
.map_err(crate::error::Error::SetTerminalMode)
})
- .await?;
+ .await
+ .unwrap()?;
let mut termios_raw = termios.clone();
nix::sys::termios::cfmakeraw(&mut termios_raw);
- blocking::unblock(move || {
+ tokio::task::spawn_blocking(move || {
nix::sys::termios::tcsetattr(
stdin,
nix::sys::termios::SetArg::TCSANOW,
@@ -33,7 +37,8 @@ impl RawGuard {
)
.map_err(crate::error::Error::SetTerminalMode)
})
- .await?;
+ .await
+ .unwrap()?;
Ok(Self {
termios: Some(termios),
})
@@ -42,11 +47,15 @@ impl RawGuard {
/// Switch back from raw mode early.
///
/// # Errors
- /// * `Error::SetTerminalMode`: failed to return the terminal from raw mode
+ /// * `Error::SetTerminalMode`: failed to return the terminal from raw
+ /// mode
+ // spawn_blocking is uncancellable, and the tcsetattr call can't panic, so
+ // unwrap is safe here
+ #[allow(clippy::missing_panics_doc)]
pub async fn cleanup(&mut self) -> crate::error::Result<()> {
if let Some(termios) = self.termios.take() {
let stdin = std::io::stdin().as_raw_fd();
- blocking::unblock(move || {
+ tokio::task::spawn_blocking(move || {
nix::sys::termios::tcsetattr(
stdin,
nix::sys::termios::SetArg::TCSANOW,
@@ -55,6 +64,7 @@ impl RawGuard {
.map_err(crate::error::Error::SetTerminalMode)
})
.await
+ .unwrap()
} else {
Ok(())
}
@@ -66,11 +76,19 @@ impl Drop for RawGuard {
/// of an async drop mechanism. If this could be a problem, you should
/// call `cleanup` manually instead.
fn drop(&mut self) {
- futures_lite::future::block_on(async {
- // https://github.com/rust-lang/rust-clippy/issues/8003
- #[allow(clippy::let_underscore_drop)]
- let _ = self.cleanup().await;
- });
+ // doesn't literally call `cleanup`, because calling spawn_blocking
+ // while the tokio runtime is in the process of shutting down doesn't
+ // work (spawn_blocking tasks are cancelled if the runtime starts
+ // shutting down before the task body starts running), but should be
+ // kept in sync with the actual things that `cleanup` does.
+ if let Some(termios) = self.termios.take() {
+ let stdin = std::io::stdin().as_raw_fd();
+ let _ = nix::sys::termios::tcsetattr(
+ stdin,
+ nix::sys::termios::SetArg::TCSANOW,
+ &termios,
+ );
+ }
}
}
@@ -80,8 +98,16 @@ impl Drop for RawGuard {
/// additionally configure the types of keypresses you are interested in
/// through the `parse_*` methods. This configuration can be changed between
/// any two calls to [`read_key`](Input::read_key).
+///
+/// # Note
+///
+/// This is built on [`tokio::io::Stdin`], and inherits its caveats. In
+/// particular, it will likely cause a hang until one more newline is received
+/// when the tokio runtime shuts down. Because of this, it is generally
+/// recommended to spawn a thread and use
+/// [`textmode::blocking::Input`](crate::blocking::Input) instead.
pub struct Input {
- stdin: blocking::Unblock<std::io::Stdin>,
+ stdin: tokio::io::Stdin,
raw: Option<RawGuard>,
buf: Vec<u8>,
@@ -161,7 +187,7 @@ impl Input {
#[must_use]
pub fn new_without_raw() -> Self {
Self {
- stdin: blocking::Unblock::new(std::io::stdin()),
+ stdin: tokio::io::stdin(),
raw: None,
buf: Vec::with_capacity(4096),
pos: 0,
@@ -290,7 +316,7 @@ impl Input {
}
async fn read_stdin(
- stdin: &mut blocking::Unblock<std::io::Stdin>,
+ stdin: &mut tokio::io::Stdin,
buf: &mut [u8],
) -> crate::error::Result<usize> {
stdin
diff --git a/src/lib.rs b/src/lib.rs
index 7e89eed..3bfb9b1 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -8,11 +8,8 @@
//! ```no_run
//! use textmode::Textmode;
//! # #[cfg(feature = "async")]
+//! # #[tokio::main]
//! # fn main() -> textmode::Result<()> {
-//! # futures_lite::future::block_on(async { run().await })
-//! # }
-//! # #[cfg(feature = "async")]
-//! # async fn run() -> textmode::Result<()> {
//! let mut tm = textmode::Output::new().await?;
//! tm.clear();
//! tm.move_to(5, 5);
diff --git a/src/output.rs b/src/output.rs
index 9b72438..378b7d4 100644
--- a/src/output.rs
+++ b/src/output.rs
@@ -1,4 +1,4 @@
-use futures_lite::io::AsyncWriteExt as _;
+use tokio::io::AsyncWriteExt as _;
use crate::private::Output as _;
@@ -16,11 +16,7 @@ impl ScreenGuard {
/// # Errors
/// * `Error::WriteStdout`: failed to write initialization to stdout
pub async fn new() -> crate::error::Result<Self> {
- write_stdout(
- &mut blocking::Unblock::new(std::io::stdout()),
- crate::INIT,
- )
- .await?;
+ write_stdout(&mut tokio::io::stdout(), crate::INIT).await?;
Ok(Self { cleaned_up: false })
}
@@ -33,11 +29,7 @@ impl ScreenGuard {
return Ok(());
}
self.cleaned_up = true;
- write_stdout(
- &mut blocking::Unblock::new(std::io::stdout()),
- crate::DEINIT,
- )
- .await
+ write_stdout(&mut tokio::io::stdout(), crate::DEINIT).await
}
}
@@ -46,10 +38,12 @@ impl Drop for ScreenGuard {
/// of an async drop mechanism. If this could be a problem, you should
/// call `cleanup` manually instead.
fn drop(&mut self) {
- futures_lite::future::block_on(async {
- // https://github.com/rust-lang/rust-clippy/issues/8003
- #[allow(clippy::let_underscore_drop)]
- let _ = self.cleanup().await;
+ tokio::task::block_in_place(move || {
+ tokio::runtime::Handle::current().block_on(async {
+ // https://github.com/rust-lang/rust-clippy/issues/8003
+ #[allow(clippy::let_underscore_drop)]
+ let _ = self.cleanup().await;
+ });
});
}
}
@@ -61,7 +55,7 @@ impl Drop for ScreenGuard {
/// then call [`refresh`](Output::refresh) when you want to update the
/// terminal on `stdout`.
pub struct Output {
- stdout: blocking::Unblock<std::io::Stdout>,
+ stdout: tokio::io::Stdout,
screen: Option<ScreenGuard>,
cur: vt100::Parser,
@@ -112,8 +106,9 @@ impl Output {
};
let cur = vt100::Parser::new(rows, cols, 0);
let next = vt100::Parser::new(rows, cols, 0);
+
Self {
- stdout: blocking::Unblock::new(std::io::stdout()),
+ stdout: tokio::io::stdout(),
screen: None,
cur,
next,
@@ -158,7 +153,7 @@ impl Output {
}
async fn write_stdout(
- stdout: &mut blocking::Unblock<std::io::Stdout>,
+ stdout: &mut tokio::io::Stdout,
buf: &[u8],
) -> crate::error::Result<()> {
stdout