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}