aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2019-10-27 13:33:47 -0400
committerJesse Luehrs <doy@tozt.net>2019-10-27 15:37:32 -0400
commitf88153f62b024d1c2ef82c5ac7ae9002e4fe2967 (patch)
treef5d9485a0e06072a22e8ae9e3a2089ee799ec948
parente74e5f1d2a1e091af214e39166b6a17d9052325c (diff)
downloadtokio-pty-process-stream-f88153f62b024d1c2ef82c5ac7ae9002e4fe2967.tar.gz
tokio-pty-process-stream-f88153f62b024d1c2ef82c5ac7ae9002e4fe2967.zip
add terminal resizing functionality
to keep the process's pty size in sync with the size of the user's terminal
-rw-r--r--Cargo.toml1
-rw-r--r--examples/interhack.rs12
-rw-r--r--examples/shell.rs2
-rw-r--r--src/error.rs5
-rw-r--r--src/lib.rs4
-rw-r--r--src/process.rs4
-rw-r--r--src/resize.rs78
7 files changed, 101 insertions, 5 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 8bc303c..dfd7067 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -18,6 +18,7 @@ log = "0.4"
snafu = "0.5"
tokio = "0.1.22"
tokio-pty-process = "0.4"
+tokio-terminal-resize = "0.1"
[dev-dependencies]
crossterm = "0.11"
diff --git a/examples/interhack.rs b/examples/interhack.rs
index 2e753b5..88a3349 100644
--- a/examples/interhack.rs
+++ b/examples/interhack.rs
@@ -7,7 +7,7 @@ use tokio::io::AsyncRead as _;
mod input;
struct Interhack {
- process: tokio_pty_process_stream::Process<input::buf::Stdin>,
+ process: tokio_pty_process_stream::ResizingProcess<input::buf::Stdin>,
stdin: input::evented_stdin::Stdin,
read_buf: [u8; 4096],
}
@@ -15,10 +15,12 @@ struct Interhack {
impl Interhack {
fn new() -> Self {
Self {
- process: tokio_pty_process_stream::Process::new(
- "nethack",
- &[],
- input::buf::Stdin::new(),
+ process: tokio_pty_process_stream::ResizingProcess::new(
+ tokio_pty_process_stream::Process::new(
+ "nethack",
+ &[],
+ input::buf::Stdin::new(),
+ ),
),
stdin: input::evented_stdin::Stdin::new(),
read_buf: [0; 4096],
diff --git a/examples/shell.rs b/examples/shell.rs
index 682c008..2cd7ba2 100644
--- a/examples/shell.rs
+++ b/examples/shell.rs
@@ -15,6 +15,7 @@ fn main() {
&args,
input::evented_stdin::Stdin::new(),
);
+ let process = tokio_pty_process_stream::ResizingProcess::new(process);
let _raw = crossterm::RawScreen::into_raw_mode().unwrap();
tokio::run(
@@ -33,6 +34,7 @@ fn main() {
tokio_pty_process_stream::Event::CommandExit {
..
} => {}
+ tokio_pty_process_stream::Event::Resize { .. } => {}
}
futures::future::ok(())
})
diff --git a/src/error.rs b/src/error.rs
index c8fc902..2259015 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -22,6 +22,11 @@ pub enum Error {
#[snafu(display("failed to resize pty: {}", source))]
ResizePty { source: std::io::Error },
+ #[snafu(display("failed to poll for terminal resizing: {}", source))]
+ Resize {
+ source: tokio_terminal_resize::Error,
+ },
+
/// failed to spawn process
#[snafu(display("failed to spawn process for `{}`: {}", cmd, source))]
SpawnProcess { cmd: String, source: std::io::Error },
diff --git a/src/lib.rs b/src/lib.rs
index 4ad3960..9a19ed8 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -31,6 +31,7 @@
//!
//! let process =
//! tokio_pty_process_stream::Process::new(&cmd, &args, Stdin::new());
+//! let process = tokio_pty_process_stream::ResizingProcess::new(process);
//!
//! let _raw = crossterm::RawScreen::into_raw_mode().unwrap();
//! tokio::run(
@@ -49,6 +50,7 @@
//! tokio_pty_process_stream::Event::CommandExit {
//! ..
//! } => {}
+//! tokio_pty_process_stream::Event::Resize { .. } => {}
//! }
//! futures::future::ok(())
//! })
@@ -151,3 +153,5 @@ pub use error::Error;
mod process;
pub use process::Event;
pub use process::Process;
+mod resize;
+pub use resize::ResizingProcess;
diff --git a/src/process.rs b/src/process.rs
index ed3344c..f5c76ed 100644
--- a/src/process.rs
+++ b/src/process.rs
@@ -20,6 +20,9 @@ pub enum Event {
/// Emitted when the command has exited.
CommandExit { status: std::process::ExitStatus },
+
+ /// Emitted by a `ResizingProcess` when a resize event happens
+ Resize { size: (u16, u16) },
}
struct State {
@@ -354,6 +357,7 @@ mod test {
assert!(status.success());
exited = true;
}
+ Event::Resize { .. } => {}
}
}
assert!(exited);
diff --git a/src/resize.rs b/src/resize.rs
new file mode 100644
index 0000000..5b59f9e
--- /dev/null
+++ b/src/resize.rs
@@ -0,0 +1,78 @@
+use futures::future::Future as _;
+use futures::stream::Stream as _;
+use snafu::futures01::StreamExt as _;
+
+pub struct ResizingProcess<R: tokio::io::AsyncRead + 'static> {
+ process: crate::process::Process<R>,
+ resizer: Box<
+ dyn futures::stream::Stream<
+ Item = (u16, u16),
+ Error = crate::error::Error,
+ > + Send,
+ >,
+}
+
+impl<R: tokio::io::AsyncRead + 'static> ResizingProcess<R> {
+ pub fn new(process: crate::process::Process<R>) -> Self {
+ Self {
+ process,
+ resizer: Box::new(
+ tokio_terminal_resize::resizes()
+ .flatten_stream()
+ .context(crate::error::Resize),
+ ),
+ }
+ }
+
+ pub fn input(&mut self) -> &mut R {
+ self.process.input()
+ }
+}
+
+impl<R: tokio::io::AsyncRead + 'static> ResizingProcess<R> {
+ const POLL_FNS:
+ &'static [&'static dyn for<'a> Fn(
+ &'a mut Self,
+ )
+ -> component_future::Poll<
+ Option<crate::process::Event>,
+ crate::error::Error,
+ >] = &[&Self::poll_resize, &Self::poll_process];
+
+ fn poll_resize(
+ &mut self,
+ ) -> component_future::Poll<
+ Option<crate::process::Event>,
+ crate::error::Error,
+ > {
+ let (rows, cols) =
+ component_future::try_ready!(self.resizer.poll()).unwrap();
+ self.process.resize(rows, cols);
+ Ok(component_future::Async::Ready(Some(
+ crate::process::Event::Resize { size: (rows, cols) },
+ )))
+ }
+
+ fn poll_process(
+ &mut self,
+ ) -> component_future::Poll<
+ Option<crate::process::Event>,
+ crate::error::Error,
+ > {
+ Ok(component_future::Async::Ready(
+ component_future::try_ready!(self.process.poll()),
+ ))
+ }
+}
+
+#[must_use = "streams do nothing unless polled"]
+impl<R: tokio::io::AsyncRead + 'static> futures::stream::Stream
+ for ResizingProcess<R>
+{
+ type Item = crate::process::Event;
+ type Error = crate::error::Error;
+
+ fn poll(&mut self) -> futures::Poll<Option<Self::Item>, Self::Error> {
+ component_future::poll_stream(self, Self::POLL_FNS)
+ }
+}