diff --git a/yazi-adapter/src/stdin.rs b/yazi-adapter/src/stdin.rs
index 00f5ecd1..f8fd2031 100644
--- a/yazi-adapter/src/stdin.rs
+++ b/yazi-adapter/src/stdin.rs
@@ -1,10 +1,27 @@
-use std::{io::{Error, ErrorKind}, time::{Duration, Instant}};
+use std::{io::{Error, ErrorKind}, ops::Deref, time::{Duration, Instant}};
pub struct AsyncStdin {
+ fd: Fd,
#[cfg(unix)]
fds: libc::fd_set,
}
+impl Default for AsyncStdin {
+ fn default() -> Self {
+ let fd = Fd::new().expect("failed to open stdin");
+ #[cfg(unix)]
+ {
+ let mut me = Self { fd, fds: unsafe { std::mem::MaybeUninit::zeroed().assume_init() } };
+ me.reset();
+ me
+ }
+ #[cfg(windows)]
+ {
+ Self { fd }
+ }
+ }
+}
+
impl AsyncStdin {
pub fn read_until
(&mut self, timeout: Duration, predicate: P) -> (Vec, std::io::Result<()>)
where
@@ -21,7 +38,7 @@ impl AsyncStdin {
continue;
}
- let b = Self::read_u8()?;
+ let b = self.read_u8()?;
buf.push(b);
if predicate(b, &buf) {
@@ -36,15 +53,7 @@ impl AsyncStdin {
}
}
-#[cfg(unix)]
-impl Default for AsyncStdin {
- fn default() -> Self {
- let mut me = Self { fds: unsafe { std::mem::MaybeUninit::zeroed().assume_init() } };
- me.reset();
- me
- }
-}
-
+// --- Unix
#[cfg(unix)]
impl AsyncStdin {
pub fn poll(&mut self, timeout: Duration) -> std::io::Result {
@@ -54,13 +63,7 @@ impl AsyncStdin {
};
let result = unsafe {
- libc::select(
- libc::STDIN_FILENO + 1,
- &mut self.fds,
- std::ptr::null_mut(),
- std::ptr::null_mut(),
- &mut tv,
- )
+ libc::select(*self.fd + 1, &mut self.fds, std::ptr::null_mut(), std::ptr::null_mut(), &mut tv)
};
match result {
@@ -73,9 +76,9 @@ impl AsyncStdin {
}
}
- pub fn read_u8() -> std::io::Result {
+ pub fn read_u8(&mut self) -> std::io::Result {
let mut b = 0;
- match unsafe { libc::read(libc::STDIN_FILENO, &mut b as *mut _ as *mut _, 1) } {
+ match unsafe { libc::read(*self.fd, &mut b as *mut _ as *mut _, 1) } {
-1 => Err(Error::last_os_error()),
0 => Err(Error::new(ErrorKind::UnexpectedEof, "unexpected EOF")),
_ => Ok(b),
@@ -85,42 +88,31 @@ impl AsyncStdin {
fn reset(&mut self) {
unsafe {
libc::FD_ZERO(&mut self.fds);
- libc::FD_SET(libc::STDIN_FILENO, &mut self.fds);
+ libc::FD_SET(*self.fd, &mut self.fds);
}
}
}
-#[cfg(windows)]
-impl Default for AsyncStdin {
- fn default() -> Self { Self {} }
-}
-
+// --- Windows
#[cfg(windows)]
impl AsyncStdin {
pub fn poll(&mut self, timeout: Duration) -> std::io::Result {
- use std::os::windows::io::AsRawHandle;
-
use windows_sys::Win32::{Foundation::{WAIT_FAILED, WAIT_OBJECT_0}, System::Threading::WaitForSingleObject};
- let handle = std::io::stdin().as_raw_handle();
let millis = timeout.as_millis();
- match unsafe { WaitForSingleObject(handle, millis as u32) } {
+ match unsafe { WaitForSingleObject(*self.fd, millis as u32) } {
WAIT_FAILED => Err(Error::last_os_error()),
WAIT_OBJECT_0 => Ok(true),
_ => Ok(false),
}
}
- pub fn read_u8() -> std::io::Result {
- use std::os::windows::io::AsRawHandle;
-
+ pub fn read_u8(&mut self) -> std::io::Result {
use windows_sys::Win32::Storage::FileSystem::ReadFile;
let mut buf = 0;
let mut bytes = 0;
- let success = unsafe {
- ReadFile(std::io::stdin().as_raw_handle(), &mut buf, 1, &mut bytes, std::ptr::null_mut())
- };
+ let success = unsafe { ReadFile(*self.fd, &mut buf, 1, &mut bytes, std::ptr::null_mut()) };
if success == 0 {
return Err(Error::last_os_error());
@@ -130,3 +122,74 @@ impl AsyncStdin {
Ok(buf)
}
}
+
+// --- Fd
+struct Fd {
+ #[cfg(unix)]
+ inner: std::os::fd::RawFd,
+ #[cfg(windows)]
+ inner: std::os::windows::io::RawHandle,
+ close: bool,
+}
+
+impl Deref for Fd {
+ #[cfg(unix)]
+ type Target = std::os::fd::RawFd;
+ #[cfg(windows)]
+ type Target = std::os::windows::io::RawHandle;
+
+ fn deref(&self) -> &Self::Target { &self.inner }
+}
+
+impl Drop for Fd {
+ fn drop(&mut self) {
+ #[cfg(unix)]
+ if self.close {
+ unsafe { libc::close(self.inner) };
+ }
+ #[cfg(windows)]
+ if self.close {
+ unsafe { windows_sys::Win32::Foundation::CloseHandle(self.inner) };
+ }
+ }
+}
+
+impl Fd {
+ #[cfg(unix)]
+ fn new() -> std::io::Result {
+ use std::{fs::OpenOptions, os::fd::IntoRawFd};
+
+ Ok(if unsafe { libc::isatty(libc::STDIN_FILENO) } == 1 {
+ Self { inner: libc::STDIN_FILENO, close: false }
+ } else {
+ Self {
+ inner: OpenOptions::new().read(true).write(true).open("/dev/tty")?.into_raw_fd(),
+ close: true,
+ }
+ })
+ }
+
+ #[cfg(windows)]
+ fn new() -> std::io::Result {
+ use windows_sys::Win32::{Foundation::{GENERIC_READ, GENERIC_WRITE, INVALID_HANDLE_VALUE}, Storage::FileSystem::{CreateFileW, FILE_SHARE_READ, FILE_SHARE_WRITE, OPEN_EXISTING}};
+
+ let name: Vec = "CONIN$\0".encode_utf16().collect();
+ let result = unsafe {
+ CreateFileW(
+ name.as_ptr(),
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ std::ptr::null_mut(),
+ OPEN_EXISTING,
+ 0,
+ std::ptr::null_mut(),
+ )
+ };
+
+ if result == INVALID_HANDLE_VALUE {
+ Err(std::io::Error::last_os_error())
+ } else {
+ Ok(Self { inner: result, close: true })
+ }
+ }
+}