aboutsummaryrefslogtreecommitdiff
path: root/src/pty/backend.rs
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-06-07 00:07:24 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-06-07 16:29:44 +0300
commit3c2b46d24aa6c8011a1e198143752f2aa414d585 (patch)
treea52280bccc824ca656f8bc986a756f30901974e4 /src/pty/backend.rs
parent5d7e56737ef30d9de5802b0684a9428c40131dda (diff)
feat: add native windows beta support
Diffstat (limited to 'src/pty/backend.rs')
-rw-r--r--src/pty/backend.rs74
1 files changed, 10 insertions, 64 deletions
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<dyn MasterPty + Send>,
pub child: Box<dyn Child + Send + Sync>,
}
+#[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<Mutex<()>> = OnceLock::new();
- LOCK.get_or_init(|| Mutex::new(()))
- }
-
- fn parent_pty_fd_targets() -> Vec<String> {
- let Ok(entries) = std::fs::read_dir("/proc/self/fd") else {
- return Vec::new();
- };
- let mut targets: Vec<String> = 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);
- }
-}