From 8655cf019302dbd8739068ae3473f92dbf4305cc Mon Sep 17 00:00:00 2001 From: sxyazi Date: Sun, 16 Feb 2025 18:25:54 +0800 Subject: [PATCH] feat: terminal response detection under async stdin --- Cargo.lock | 10 +++-- yazi-adapter/Cargo.toml | 6 +++ yazi-adapter/src/lib.rs | 2 +- yazi-adapter/src/stdin.rs | 77 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 90 insertions(+), 5 deletions(-) create mode 100644 yazi-adapter/src/stdin.rs diff --git a/Cargo.lock b/Cargo.lock index 5f873add..ac14342e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -726,9 +726,9 @@ checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe" [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "erased-serde" @@ -2390,9 +2390,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.2" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" [[package]] name = "smawk" @@ -3324,10 +3324,12 @@ dependencies = [ "crossterm", "futures", "image", + "libc", "ratatui", "scopeguard", "tokio", "tracing", + "windows-sys 0.59.0", "yazi-config", "yazi-macro", "yazi-shared", diff --git a/yazi-adapter/Cargo.toml b/yazi-adapter/Cargo.toml index ff6d77db..db0c3ea6 100644 --- a/yazi-adapter/Cargo.toml +++ b/yazi-adapter/Cargo.toml @@ -26,5 +26,11 @@ scopeguard = { workspace = true } tokio = { workspace = true } tracing = { workspace = true } +[target."cfg(unix)".dependencies] +libc = { workspace = true } + +[target.'cfg(windows)'.dependencies] +windows-sys = { version = "0.59.0", features = [ "Win32_UI_Shell" ] } + [target.'cfg(target_os = "macos")'.dependencies] crossterm = { workspace = true, features = [ "use-dev-tty", "libc" ] } diff --git a/yazi-adapter/src/lib.rs b/yazi-adapter/src/lib.rs index f28b134b..be4b23e8 100644 --- a/yazi-adapter/src/lib.rs +++ b/yazi-adapter/src/lib.rs @@ -2,7 +2,7 @@ yazi_macro::mod_pub!(drivers); -yazi_macro::mod_flat!(adapter brand dimension emulator image info mux unknown); +yazi_macro::mod_flat!(adapter brand dimension emulator image info mux stdin unknown); use yazi_shared::{SyncCell, in_wsl}; diff --git a/yazi-adapter/src/stdin.rs b/yazi-adapter/src/stdin.rs new file mode 100644 index 00000000..a0b6583a --- /dev/null +++ b/yazi-adapter/src/stdin.rs @@ -0,0 +1,77 @@ +use std::{io, ops::{Deref, DerefMut}}; + +pub struct AsyncStdin { + inner: std::io::StdinLock<'static>, + #[cfg(unix)] + fds: libc::fd_set, +} + +impl Deref for AsyncStdin { + type Target = std::io::StdinLock<'static>; + + fn deref(&self) -> &Self::Target { &self.inner } +} + +impl DerefMut for AsyncStdin { + fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner } +} + +#[cfg(unix)] +impl AsyncStdin { + pub fn new(inner: std::io::StdinLock<'static>) -> Self { + let mut me = Self { inner, fds: unsafe { std::mem::MaybeUninit::zeroed().assume_init() } }; + me.reset(); + me + } + + pub fn poll(&mut self, timeout: std::time::Duration) -> std::io::Result { + use std::os::unix::io::AsRawFd; + + let mut tv = libc::timeval { + tv_sec: timeout.as_secs() as libc::time_t, + tv_usec: timeout.subsec_micros() as libc::suseconds_t, + }; + + let result = unsafe { + libc::select( + self.inner.as_raw_fd() + 1, + &mut self.fds, + std::ptr::null_mut(), + std::ptr::null_mut(), + &mut tv, + ) + }; + + match result { + -1 => Err(io::Error::last_os_error()), + 0 => Ok(false), + _ => { + self.reset(); + Ok(true) + } + } + } + + fn reset(&mut self) { + use std::os::unix::io::AsRawFd; + + unsafe { + libc::FD_ZERO(&mut self.fds); + libc::FD_SET(self.inner.as_raw_fd(), &mut self.fds); + } + } +} + +#[cfg(windows)] +impl AsyncStdin { + pub fn new(inner: std::io::StdinLock<'static>) -> Self { Self { inner } } + + pub fn poll(&mut self) -> std::io::Result { + let handle = HANDLE(self.inner.as_raw_handle() as isize); + + match unsafe { WaitForSingleObject(handle, 5000) } { + WAIT_TIMEOUT => Ok(false), + _ => Ok(true), + } + } +}