rust_pty/
windows.rs

1//! Windows platform implementation for PTY operations.
2//!
3//! This module provides the Windows-specific PTY implementation using ConPTY
4//! (Console Pseudo Terminal), introduced in Windows 10 version 1809.
5//!
6//! # Platform Support
7//!
8//! ConPTY is only available on:
9//! - Windows 10 version 1809 (build 17763) and later
10//! - Windows Server 2019 and later
11//!
12//! On older Windows versions, PTY creation will fail with `PtyError::ConPtyNotAvailable`.
13//!
14//! # Example
15//!
16//! ```ignore
17//! use rust_pty::windows::WindowsPtySystem;
18//! use rust_pty::{PtySystem, PtyConfig};
19//!
20//! let config = PtyConfig::default();
21//! let (master, child) = WindowsPtySystem::spawn("cmd.exe", &[], &config).await?;
22//! ```
23
24mod async_adapter;
25mod child;
26mod conpty;
27mod pipes;
28
29use std::ffi::OsStr;
30use std::future::Future;
31use std::sync::Arc;
32
33pub use async_adapter::WindowsPtyMaster;
34pub use child::{WindowsPtyChild, spawn_child};
35pub use conpty::{ConPty, is_conpty_available};
36pub use pipes::{PipePair, create_input_pipe, create_output_pipe, set_inheritable};
37
38use crate::config::{PtyConfig, WindowSize};
39use crate::error::{PtyError, Result};
40use crate::traits::PtySystem;
41
42/// Windows PTY system implementation using ConPTY.
43///
44/// This struct provides the factory methods for creating PTY sessions on Windows.
45#[derive(Debug, Clone, Copy, Default)]
46pub struct WindowsPtySystem;
47
48impl PtySystem for WindowsPtySystem {
49    type Master = WindowsPtyMaster;
50    type Child = WindowsPtyChild;
51
52    fn spawn<S, I>(
53        program: S,
54        args: I,
55        config: &PtyConfig,
56    ) -> impl Future<Output = Result<(Self::Master, Self::Child)>> + Send
57    where
58        S: AsRef<OsStr> + Send,
59        I: IntoIterator + Send,
60        I::Item: AsRef<OsStr>,
61    {
62        async move {
63            // Check ConPTY availability
64            if !is_conpty_available() {
65                return Err(PtyError::ConPtyNotAvailable);
66            }
67
68            // Create pipes
69            let input_pipe = create_input_pipe()?;
70            let output_pipe = create_output_pipe()?;
71
72            // Create ConPTY
73            let window_size = WindowSize::from(config.window_size);
74            let mut conpty = ConPty::new(
75                window_size,
76                input_pipe.read,
77                output_pipe.write,
78                input_pipe.write,
79                output_pipe.read,
80            )?;
81
82            // Spawn child process
83            let child = spawn_child(conpty.handle(), program, args, config)?;
84
85            // Close the PTY pipe handles after CreateProcess per Microsoft docs.
86            // This signals to ConPTY that no other handles exist on the "other side"
87            // of the pipes, enabling proper channel detection.
88            conpty.close_pty_pipes();
89
90            // Duplicate handles for the master (Windows requires explicit handle duplication)
91            let input_handle = conpty.input().try_clone().map_err(|e| PtyError::Spawn(e))?;
92            let output_handle = conpty
93                .output()
94                .try_clone()
95                .map_err(|e| PtyError::Spawn(e))?;
96
97            // Now wrap in Arc for shared ownership
98            let conpty = Arc::new(conpty);
99            let conpty_for_resize = Arc::clone(&conpty);
100
101            // Create master wrapper
102            let master = WindowsPtyMaster::new(
103                input_handle,
104                output_handle,
105                move |size| conpty_for_resize.resize(size),
106                window_size,
107            );
108
109            Ok((master, child))
110        }
111    }
112}
113
114/// Convenience type alias for the default PTY system on Windows.
115pub type NativePtySystem = WindowsPtySystem;
116
117#[cfg(test)]
118mod tests {
119    use super::*;
120
121    #[test]
122    fn check_availability() {
123        // Just check that this doesn't panic
124        let _ = is_conpty_available();
125    }
126}