Bring back previous simpler impl read/write in Tty trait

This commit is contained in:
Marcin Kulik
2025-06-22 14:58:58 +02:00
parent 0f3474585f
commit abbd2a7ac6
3 changed files with 42 additions and 221 deletions

View File

@@ -1,11 +1,10 @@
use anyhow::Result;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::sync::mpsc;
use tokio::time::{self, Duration, Instant};
use crate::asciicast::{self, Event, EventData};
use crate::config::Key;
use crate::tty::DevTty;
use crate::tty::{DevTty, Tty};
pub struct KeyBindings {
pub quit: Key,

View File

@@ -139,7 +139,6 @@ impl<N: Notifier> Session<N> {
let mut input: Vec<u8> = Vec::with_capacity(BUF_SIZE);
let mut output: Vec<u8> = Vec::with_capacity(BUF_SIZE);
let mut wait_status = None;
let (mut tty_reader, mut tty_writer) = tty.split();
let (mut pty_reader, mut pty_writer) = pty.split();
loop {
@@ -160,7 +159,7 @@ impl<N: Notifier> Session<N> {
input.drain(..n);
}
result = tty_reader.read(&mut input_buf) => {
result = tty.read(&mut input_buf) => {
let n = result?;
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?;
output.drain(..n);
}
@@ -206,7 +205,7 @@ impl<N: Notifier> Session<N> {
if !output.is_empty() {
self.handle_output(&output).await;
let _ = tty_writer.write_all(&output).await;
let _ = tty.write_all(&output).await;
}
let wait_status = match wait_status {

View File

@@ -2,8 +2,6 @@ use std::fs::File;
use std::io::{Read, Write};
use std::os::fd::{AsFd, AsRawFd};
use std::os::unix::fs::OpenOptionsExt;
use std::pin::Pin;
use std::task::{ready, Context, Poll};
use async_trait::async_trait;
use nix::libc;
@@ -11,7 +9,7 @@ use nix::pty::Winsize;
use nix::sys::termios::{self, SetArg, Termios};
use rgb::RGB8;
use tokio::io::unix::AsyncFd;
use tokio::io::{self, AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, ReadBuf};
use tokio::io::{self, Interest};
use tokio::time::{self, Duration};
const QUERY_READ_TIMEOUT: u64 = 500;
@@ -33,45 +31,30 @@ pub struct DevTty {
settings: libc::termios,
}
pub struct DevTtyReadHalf<'a> {
tty: &'a DevTty,
}
pub struct DevTtyWriteHalf<'a> {
tty: &'a DevTty,
}
pub struct NullTty;
pub struct NullTtyReadHalf;
pub struct NullTtyWriteHalf;
pub struct FixedSizeTty<T> {
inner: T,
cols: Option<u16>,
rows: Option<u16>,
}
pub struct FixedSizeTtyReadHalf<'a> {
inner: Box<dyn AsyncRead + Send + Unpin + 'a>,
}
pub struct FixedSizeTtyWriteHalf<'a> {
inner: Box<dyn AsyncWrite + Send + Unpin + 'a>,
}
#[async_trait]
#[async_trait(?Send)]
pub trait Tty {
fn get_size(&self) -> Winsize;
async fn get_theme(&mut self) -> Option<TtyTheme>;
async fn get_version(&mut self) -> Option<String>;
fn split(
&self,
) -> (
Box<dyn AsyncRead + Send + Unpin + '_>,
Box<dyn AsyncWrite + Send + Unpin + '_>,
);
async fn read(&self, buf: &mut [u8]) -> io::Result<usize>;
async fn write(&self, buf: &[u8]) -> io::Result<usize>;
async fn write_all(&self, mut buf: &[u8]) -> io::Result<()> {
while !buf.is_empty() {
let n = self.write(buf).await?;
buf = &buf[n..];
}
Ok(())
}
}
impl Default for TtySize {
@@ -123,17 +106,16 @@ impl DevTty {
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();
query.extend_from_slice(b"\x1b[c");
let mut query = &query[..];
let mut response = Vec::new();
let mut buf = [0u8; 1024];
let (mut reader, mut writer) = self.split();
loop {
tokio::select! {
result = reader.read(&mut buf) => {
result = self.read(&mut buf) => {
let n = result?;
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?;
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 {
fn drop(&mut self) {
let termios = Termios::from(self.settings);
@@ -220,7 +154,7 @@ impl Drop for DevTty {
}
}
#[async_trait]
#[async_trait(?Send)]
impl Tty for DevTty {
fn get_size(&self) -> Winsize {
let mut winsize = Winsize {
@@ -264,64 +198,16 @@ impl Tty for DevTty {
}
}
fn split(
&self,
) -> (
Box<dyn AsyncRead + Send + Unpin + '_>,
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)),
Err(_would_block) => continue,
}
}
}
}
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,
}
}
async fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
self.file
.async_io(Interest::READABLE, |mut file| file.read(buf))
.await
}
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 fn write(&self, buf: &[u8]) -> io::Result<usize> {
self.file
.async_io(Interest::WRITABLE, |mut file| file.write(buf))
.await
}
}
@@ -331,7 +217,7 @@ impl<T: Tty> FixedSizeTty<T> {
}
}
#[async_trait]
#[async_trait(?Send)]
impl Tty for NullTty {
fn get_size(&self) -> Winsize {
Winsize {
@@ -350,46 +236,17 @@ impl Tty for NullTty {
None
}
fn split(
&self,
) -> (
Box<dyn AsyncRead + Send + Unpin + '_>,
Box<dyn AsyncWrite + Send + Unpin + '_>,
) {
(Box::new(NullTtyReadHalf), Box::new(NullTtyWriteHalf))
async fn read(&self, _buf: &mut [u8]) -> io::Result<usize> {
std::future::pending().await
}
async fn write(&self, buf: &[u8]) -> io::Result<usize> {
Ok(buf.len())
}
}
impl AsyncRead for NullTtyReadHalf {
fn poll_read(
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> {
#[async_trait(?Send)]
impl<T: Tty> Tty for FixedSizeTty<T> {
fn get_size(&self) -> Winsize {
let mut winsize = self.inner.get_size();
@@ -412,46 +269,12 @@ impl<T: Tty + Send> Tty for FixedSizeTty<T> {
self.inner.get_version().await
}
fn split(
&self,
) -> (
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)
async fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
self.inner.read(buf).await
}
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Pin::new(&mut self.inner).poll_flush(cx)
}
fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Pin::new(&mut self.inner).poll_shutdown(cx)
async fn write(&self, buf: &[u8]) -> io::Result<usize> {
self.inner.write(buf).await
}
}