From 3c2b46d24aa6c8011a1e198143752f2aa414d585 Mon Sep 17 00:00:00 2001 From: Ogulcan Celik Date: Sun, 7 Jun 2026 00:07:24 +0300 Subject: feat: add native windows beta support --- src/pty/backend.rs | 74 ++++++++---------------------------------------------- 1 file changed, 10 insertions(+), 64 deletions(-) (limited to 'src/pty/backend.rs') diff --git a/src/pty/backend.rs b/src/pty/backend.rs index 5871004a..d09922b7 100644 --- a/src/pty/backend.rs +++ b/src/pty/backend.rs @@ -1,14 +1,19 @@ -use std::os::fd::{FromRawFd, OwnedFd}; +#[cfg(unix)] +mod unix; -use portable_pty::{native_pty_system, Child, CommandBuilder, PtySize}; +#[cfg(unix)] +pub(crate) use unix::*; -use crate::pty::fd; +#[cfg(windows)] +use portable_pty::{native_pty_system, Child, CommandBuilder, MasterPty, PtySize}; +#[cfg(windows)] pub(crate) struct SpawnedPty { - pub master_fd: OwnedFd, + pub master: Box, pub child: Box, } +#[cfg(windows)] pub(crate) fn spawn_with_portable_pty( rows: u16, cols: u16, @@ -23,72 +28,13 @@ pub(crate) fn spawn_with_portable_pty( pixel_height: 0, }) .map_err(|err| std::io::Error::other(err.to_string()))?; - let master_fd = pair - .master - .as_raw_fd() - .ok_or_else(|| std::io::Error::other("pty master fd is unavailable"))?; - let actor_fd = fd::duplicate_cloexec_fd(master_fd)?; - let actor_fd = unsafe { OwnedFd::from_raw_fd(actor_fd) }; let child = pair .slave .spawn_command(cmd) .map_err(|err| std::io::Error::other(err.to_string()))?; - drop(pair); Ok(SpawnedPty { - master_fd: actor_fd, + master: pair.master, child, }) } - -#[cfg(all(test, target_os = "linux"))] -mod tests { - use super::*; - use std::sync::{Mutex, OnceLock}; - - fn pty_fd_test_lock() -> &'static Mutex<()> { - static LOCK: OnceLock> = OnceLock::new(); - LOCK.get_or_init(|| Mutex::new(())) - } - - fn parent_pty_fd_targets() -> Vec { - let Ok(entries) = std::fs::read_dir("/proc/self/fd") else { - return Vec::new(); - }; - let mut targets: Vec = entries - .filter_map(Result::ok) - .filter_map(|entry| std::fs::read_link(entry.path()).ok()) - .map(|target| target.to_string_lossy().into_owned()) - .filter(|target| target.starts_with("/dev/pts/") || target == "/dev/ptmx") - .collect(); - targets.sort(); - targets - } - - fn parent_pty_fd_count() -> usize { - parent_pty_fd_targets().len() - } - - #[test] - fn portable_pty_setup_leaves_one_parent_pty_fd() { - let _guard = pty_fd_test_lock().lock().expect("pty fd test lock"); - let before = parent_pty_fd_count(); - let mut cmd = CommandBuilder::new("/bin/cat"); - cmd.env(crate::HERDR_ENV_VAR, crate::HERDR_ENV_VALUE); - - let mut spawned = - spawn_with_portable_pty(24, 80, cmd).expect("portable pty setup succeeds"); - let after_spawn = parent_pty_fd_count(); - - assert_eq!( - after_spawn, - before + 1, - "portable-pty setup should leave only the Herdr-owned master fd in the parent: {:?}", - parent_pty_fd_targets() - ); - - let _ = spawned.child.kill(); - let _ = spawned.child.wait(); - drop(spawned.master_fd); - } -} -- cgit v1.2.3