mirror of
https://github.com/asciinema/asciinema.git
synced 2025-12-16 11:48:13 +01:00
Bring back previous simpler impl read/write in Tty trait
This commit is contained in:
@@ -1,11 +1,10 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
use tokio::time::{self, Duration, Instant};
|
use tokio::time::{self, Duration, Instant};
|
||||||
|
|
||||||
use crate::asciicast::{self, Event, EventData};
|
use crate::asciicast::{self, Event, EventData};
|
||||||
use crate::config::Key;
|
use crate::config::Key;
|
||||||
use crate::tty::DevTty;
|
use crate::tty::{DevTty, Tty};
|
||||||
|
|
||||||
pub struct KeyBindings {
|
pub struct KeyBindings {
|
||||||
pub quit: Key,
|
pub quit: Key,
|
||||||
|
|||||||
@@ -139,7 +139,6 @@ impl<N: Notifier> Session<N> {
|
|||||||
let mut input: Vec<u8> = Vec::with_capacity(BUF_SIZE);
|
let mut input: Vec<u8> = Vec::with_capacity(BUF_SIZE);
|
||||||
let mut output: Vec<u8> = Vec::with_capacity(BUF_SIZE);
|
let mut output: Vec<u8> = Vec::with_capacity(BUF_SIZE);
|
||||||
let mut wait_status = None;
|
let mut wait_status = None;
|
||||||
let (mut tty_reader, mut tty_writer) = tty.split();
|
|
||||||
let (mut pty_reader, mut pty_writer) = pty.split();
|
let (mut pty_reader, mut pty_writer) = pty.split();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
@@ -160,7 +159,7 @@ impl<N: Notifier> Session<N> {
|
|||||||
input.drain(..n);
|
input.drain(..n);
|
||||||
}
|
}
|
||||||
|
|
||||||
result = tty_reader.read(&mut input_buf) => {
|
result = tty.read(&mut input_buf) => {
|
||||||
let n = result?;
|
let n = result?;
|
||||||
|
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
@@ -172,7 +171,7 @@ impl<N: Notifier> Session<N> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result = tty_writer.write(&output), if !output.is_empty() => {
|
result = tty.write(&output), if !output.is_empty() => {
|
||||||
let n = result?;
|
let n = result?;
|
||||||
output.drain(..n);
|
output.drain(..n);
|
||||||
}
|
}
|
||||||
@@ -206,7 +205,7 @@ impl<N: Notifier> Session<N> {
|
|||||||
|
|
||||||
if !output.is_empty() {
|
if !output.is_empty() {
|
||||||
self.handle_output(&output).await;
|
self.handle_output(&output).await;
|
||||||
let _ = tty_writer.write_all(&output).await;
|
let _ = tty.write_all(&output).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
let wait_status = match wait_status {
|
let wait_status = match wait_status {
|
||||||
|
|||||||
253
src/tty.rs
253
src/tty.rs
@@ -2,8 +2,6 @@ use std::fs::File;
|
|||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
use std::os::fd::{AsFd, AsRawFd};
|
use std::os::fd::{AsFd, AsRawFd};
|
||||||
use std::os::unix::fs::OpenOptionsExt;
|
use std::os::unix::fs::OpenOptionsExt;
|
||||||
use std::pin::Pin;
|
|
||||||
use std::task::{ready, Context, Poll};
|
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use nix::libc;
|
use nix::libc;
|
||||||
@@ -11,7 +9,7 @@ use nix::pty::Winsize;
|
|||||||
use nix::sys::termios::{self, SetArg, Termios};
|
use nix::sys::termios::{self, SetArg, Termios};
|
||||||
use rgb::RGB8;
|
use rgb::RGB8;
|
||||||
use tokio::io::unix::AsyncFd;
|
use tokio::io::unix::AsyncFd;
|
||||||
use tokio::io::{self, AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, ReadBuf};
|
use tokio::io::{self, Interest};
|
||||||
use tokio::time::{self, Duration};
|
use tokio::time::{self, Duration};
|
||||||
|
|
||||||
const QUERY_READ_TIMEOUT: u64 = 500;
|
const QUERY_READ_TIMEOUT: u64 = 500;
|
||||||
@@ -33,45 +31,30 @@ pub struct DevTty {
|
|||||||
settings: libc::termios,
|
settings: libc::termios,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DevTtyReadHalf<'a> {
|
|
||||||
tty: &'a DevTty,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct DevTtyWriteHalf<'a> {
|
|
||||||
tty: &'a DevTty,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct NullTty;
|
pub struct NullTty;
|
||||||
|
|
||||||
pub struct NullTtyReadHalf;
|
|
||||||
|
|
||||||
pub struct NullTtyWriteHalf;
|
|
||||||
|
|
||||||
pub struct FixedSizeTty<T> {
|
pub struct FixedSizeTty<T> {
|
||||||
inner: T,
|
inner: T,
|
||||||
cols: Option<u16>,
|
cols: Option<u16>,
|
||||||
rows: Option<u16>,
|
rows: Option<u16>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FixedSizeTtyReadHalf<'a> {
|
#[async_trait(?Send)]
|
||||||
inner: Box<dyn AsyncRead + Send + Unpin + 'a>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct FixedSizeTtyWriteHalf<'a> {
|
|
||||||
inner: Box<dyn AsyncWrite + Send + Unpin + 'a>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
pub trait Tty {
|
pub trait Tty {
|
||||||
fn get_size(&self) -> Winsize;
|
fn get_size(&self) -> Winsize;
|
||||||
async fn get_theme(&mut self) -> Option<TtyTheme>;
|
async fn get_theme(&mut self) -> Option<TtyTheme>;
|
||||||
async fn get_version(&mut self) -> Option<String>;
|
async fn get_version(&mut self) -> Option<String>;
|
||||||
fn split(
|
async fn read(&self, buf: &mut [u8]) -> io::Result<usize>;
|
||||||
&self,
|
async fn write(&self, buf: &[u8]) -> io::Result<usize>;
|
||||||
) -> (
|
|
||||||
Box<dyn AsyncRead + Send + Unpin + '_>,
|
async fn write_all(&self, mut buf: &[u8]) -> io::Result<()> {
|
||||||
Box<dyn AsyncWrite + Send + Unpin + '_>,
|
while !buf.is_empty() {
|
||||||
);
|
let n = self.write(buf).await?;
|
||||||
|
buf = &buf[n..];
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for TtySize {
|
impl Default for TtySize {
|
||||||
@@ -123,17 +106,16 @@ impl DevTty {
|
|||||||
Ok(Self { file, settings })
|
Ok(Self { file, settings })
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn query(&mut self, query: &str) -> anyhow::Result<Vec<u8>> {
|
async fn query(&self, query: &str) -> anyhow::Result<Vec<u8>> {
|
||||||
let mut query = query.to_string().into_bytes();
|
let mut query = query.to_string().into_bytes();
|
||||||
query.extend_from_slice(b"\x1b[c");
|
query.extend_from_slice(b"\x1b[c");
|
||||||
let mut query = &query[..];
|
let mut query = &query[..];
|
||||||
let mut response = Vec::new();
|
let mut response = Vec::new();
|
||||||
let mut buf = [0u8; 1024];
|
let mut buf = [0u8; 1024];
|
||||||
let (mut reader, mut writer) = self.split();
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
result = reader.read(&mut buf) => {
|
result = self.read(&mut buf) => {
|
||||||
let n = result?;
|
let n = result?;
|
||||||
response.extend_from_slice(&buf[..n]);
|
response.extend_from_slice(&buf[..n]);
|
||||||
|
|
||||||
@@ -143,7 +125,7 @@ impl DevTty {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result = writer.write(query), if !query.is_empty() => {
|
result = self.write(query), if !query.is_empty() => {
|
||||||
let n = result?;
|
let n = result?;
|
||||||
query = &query[n..];
|
query = &query[n..];
|
||||||
}
|
}
|
||||||
@@ -165,54 +147,6 @@ impl DevTty {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsyncRead for DevTty {
|
|
||||||
fn poll_read(
|
|
||||||
self: Pin<&mut Self>,
|
|
||||||
cx: &mut Context<'_>,
|
|
||||||
buf: &mut ReadBuf<'_>,
|
|
||||||
) -> Poll<io::Result<()>> {
|
|
||||||
loop {
|
|
||||||
let mut guard = ready!(self.file.poll_read_ready(cx))?;
|
|
||||||
let unfilled = buf.initialize_unfilled();
|
|
||||||
|
|
||||||
match guard.try_io(|inner| inner.get_ref().read(unfilled)) {
|
|
||||||
Ok(Ok(len)) => {
|
|
||||||
buf.advance(len);
|
|
||||||
return Poll::Ready(Ok(()));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Err(err)) => return Poll::Ready(Err(err)),
|
|
||||||
Err(_would_block) => continue,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsyncWrite for DevTty {
|
|
||||||
fn poll_write(
|
|
||||||
self: Pin<&mut Self>,
|
|
||||||
cx: &mut Context<'_>,
|
|
||||||
buf: &[u8],
|
|
||||||
) -> Poll<io::Result<usize>> {
|
|
||||||
loop {
|
|
||||||
let mut guard = ready!(self.file.poll_write_ready(cx))?;
|
|
||||||
|
|
||||||
match guard.try_io(|inner| inner.get_ref().write(buf)) {
|
|
||||||
Ok(result) => return Poll::Ready(result),
|
|
||||||
Err(_would_block) => continue,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
|
||||||
Poll::Ready(Ok(()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
|
||||||
Poll::Ready(Ok(()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for DevTty {
|
impl Drop for DevTty {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
let termios = Termios::from(self.settings);
|
let termios = Termios::from(self.settings);
|
||||||
@@ -220,7 +154,7 @@ impl Drop for DevTty {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait(?Send)]
|
||||||
impl Tty for DevTty {
|
impl Tty for DevTty {
|
||||||
fn get_size(&self) -> Winsize {
|
fn get_size(&self) -> Winsize {
|
||||||
let mut winsize = Winsize {
|
let mut winsize = Winsize {
|
||||||
@@ -264,64 +198,16 @@ impl Tty for DevTty {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn split(
|
async fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
&self,
|
self.file
|
||||||
) -> (
|
.async_io(Interest::READABLE, |mut file| file.read(buf))
|
||||||
Box<dyn AsyncRead + Send + Unpin + '_>,
|
.await
|
||||||
Box<dyn AsyncWrite + Send + Unpin + '_>,
|
|
||||||
) {
|
|
||||||
let reader = DevTtyReadHalf { tty: self };
|
|
||||||
let writer = DevTtyWriteHalf { tty: self };
|
|
||||||
|
|
||||||
(Box::new(reader), Box::new(writer))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsyncRead for DevTtyReadHalf<'_> {
|
|
||||||
fn poll_read(
|
|
||||||
self: Pin<&mut Self>,
|
|
||||||
cx: &mut Context<'_>,
|
|
||||||
buf: &mut ReadBuf<'_>,
|
|
||||||
) -> Poll<io::Result<()>> {
|
|
||||||
loop {
|
|
||||||
let mut guard = ready!(self.tty.file.poll_read_ready(cx))?;
|
|
||||||
let unfilled = buf.initialize_unfilled();
|
|
||||||
|
|
||||||
match guard.try_io(|inner| inner.get_ref().read(unfilled)) {
|
|
||||||
Ok(Ok(len)) => {
|
|
||||||
buf.advance(len);
|
|
||||||
return Poll::Ready(Ok(()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Err(err)) => return Poll::Ready(Err(err)),
|
async fn write(&self, buf: &[u8]) -> io::Result<usize> {
|
||||||
Err(_would_block) => continue,
|
self.file
|
||||||
}
|
.async_io(Interest::WRITABLE, |mut file| file.write(buf))
|
||||||
}
|
.await
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsyncWrite for DevTtyWriteHalf<'_> {
|
|
||||||
fn poll_write(
|
|
||||||
self: Pin<&mut Self>,
|
|
||||||
cx: &mut Context<'_>,
|
|
||||||
buf: &[u8],
|
|
||||||
) -> Poll<io::Result<usize>> {
|
|
||||||
loop {
|
|
||||||
let mut guard = ready!(self.tty.file.poll_write_ready(cx))?;
|
|
||||||
|
|
||||||
match guard.try_io(|inner| inner.get_ref().write(buf)) {
|
|
||||||
Ok(result) => return Poll::Ready(result),
|
|
||||||
Err(_would_block) => continue,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
|
||||||
Poll::Ready(Ok(()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
|
||||||
Poll::Ready(Ok(()))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -331,7 +217,7 @@ impl<T: Tty> FixedSizeTty<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait(?Send)]
|
||||||
impl Tty for NullTty {
|
impl Tty for NullTty {
|
||||||
fn get_size(&self) -> Winsize {
|
fn get_size(&self) -> Winsize {
|
||||||
Winsize {
|
Winsize {
|
||||||
@@ -350,46 +236,17 @@ impl Tty for NullTty {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn split(
|
async fn read(&self, _buf: &mut [u8]) -> io::Result<usize> {
|
||||||
&self,
|
std::future::pending().await
|
||||||
) -> (
|
}
|
||||||
Box<dyn AsyncRead + Send + Unpin + '_>,
|
|
||||||
Box<dyn AsyncWrite + Send + Unpin + '_>,
|
async fn write(&self, buf: &[u8]) -> io::Result<usize> {
|
||||||
) {
|
Ok(buf.len())
|
||||||
(Box::new(NullTtyReadHalf), Box::new(NullTtyWriteHalf))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsyncRead for NullTtyReadHalf {
|
#[async_trait(?Send)]
|
||||||
fn poll_read(
|
impl<T: Tty> Tty for FixedSizeTty<T> {
|
||||||
self: Pin<&mut Self>,
|
|
||||||
_cx: &mut Context<'_>,
|
|
||||||
_buf: &mut ReadBuf<'_>,
|
|
||||||
) -> Poll<io::Result<()>> {
|
|
||||||
Poll::Pending
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsyncWrite for NullTtyWriteHalf {
|
|
||||||
fn poll_write(
|
|
||||||
self: Pin<&mut Self>,
|
|
||||||
_cx: &mut Context<'_>,
|
|
||||||
buf: &[u8],
|
|
||||||
) -> Poll<io::Result<usize>> {
|
|
||||||
Poll::Ready(Ok(buf.len()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
|
||||||
Poll::Ready(Ok(()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
|
||||||
Poll::Ready(Ok(()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl<T: Tty + Send> Tty for FixedSizeTty<T> {
|
|
||||||
fn get_size(&self) -> Winsize {
|
fn get_size(&self) -> Winsize {
|
||||||
let mut winsize = self.inner.get_size();
|
let mut winsize = self.inner.get_size();
|
||||||
|
|
||||||
@@ -412,46 +269,12 @@ impl<T: Tty + Send> Tty for FixedSizeTty<T> {
|
|||||||
self.inner.get_version().await
|
self.inner.get_version().await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn split(
|
async fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
&self,
|
self.inner.read(buf).await
|
||||||
) -> (
|
|
||||||
Box<dyn AsyncRead + Send + Unpin + '_>,
|
|
||||||
Box<dyn AsyncWrite + Send + Unpin + '_>,
|
|
||||||
) {
|
|
||||||
let (reader, writer) = self.inner.split();
|
|
||||||
|
|
||||||
(
|
|
||||||
Box::new(FixedSizeTtyReadHalf { inner: reader }),
|
|
||||||
Box::new(FixedSizeTtyWriteHalf { inner: writer }),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsyncRead for FixedSizeTtyReadHalf<'_> {
|
|
||||||
fn poll_read(
|
|
||||||
mut self: Pin<&mut Self>,
|
|
||||||
cx: &mut Context<'_>,
|
|
||||||
buf: &mut ReadBuf<'_>,
|
|
||||||
) -> Poll<io::Result<()>> {
|
|
||||||
Pin::new(&mut self.inner).poll_read(cx, buf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsyncWrite for FixedSizeTtyWriteHalf<'_> {
|
|
||||||
fn poll_write(
|
|
||||||
mut self: Pin<&mut Self>,
|
|
||||||
cx: &mut Context<'_>,
|
|
||||||
buf: &[u8],
|
|
||||||
) -> Poll<io::Result<usize>> {
|
|
||||||
Pin::new(&mut self.inner).poll_write(cx, buf)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
async fn write(&self, buf: &[u8]) -> io::Result<usize> {
|
||||||
Pin::new(&mut self.inner).poll_flush(cx)
|
self.inner.write(buf).await
|
||||||
}
|
|
||||||
|
|
||||||
fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
|
||||||
Pin::new(&mut self.inner).poll_shutdown(cx)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user