1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
// Copyright (c) 2025 R3BL LLC. Licensed under Apache License, Version 2.0.
//! # PTY Module
//!
//! This module provides a high-level, async interface for spawning and controlling
//! processes in pseudo-terminals (PTYs). It supports both read-only and read-write
//! (read-write) sessions with optional OSC sequence capture for enhanced terminal
//! features.
//!
//! ## PTY Architecture Overview
//!
//! The PTY (pseudo-terminal) acts as a bridge between your program and spawned processes:
//!
//! ### Read-Only Mode
//!
//! ```text
//! ┌──────────────┐ ◄── events ◄── ┌───────────────────────────────┐
//! │ Your Program │ │ Spawned Task (1) in Read Only │
//! │ │ │ session │
//! │ │ │ ↓ │
//! │ Handle │ │ ◄─── PTY creates pair ───► │
//! │ events and │ │ ┊Controller┊ ┊Controlled┊ │
//! │ process │ │ ↓ ↓ │
//! │ completion │ │ Spawn Tokio Controlled │
//! │ from read │ │ blocking task spawns │
//! │ only session │ │ (2) to read child │
//! │ │ │ from process (3) │
//! │ │ │ Controller and │
//! │ │ │ generate events │
//! │ │ │ for your program │
//! └──────────────┘ └───────────────────────────────┘
//! ```
//!
//! ### Read-Write Mode
//!
//! ```text
//! ┌──────────────┐ ┌────────────┐ ┌───────────────────┐
//! │ Your Program │◄─►│ PTY │ │ Spawned Process │
//! │ Reads/writes │ │ Controller │ │ stdin/stdout/ │
//! │ through │ │ ↕ │ │ stderr redirected │
//! │ controller/ │ │ PTY │ │ to controlled │
//! │ master side │ │ ↕ │ │ side │
//! │ │ │ Controlled │◄─►│ │
//! └──────────────┘ └────────────┘ └───────────────────┘
//! ```
//!
//! ## Key Features
//!
//! - **Read-only sessions**: Capture command output with optional OSC sequence processing
//! - **Read-write sessions**: Full bidirectional communication with PTY processes
//! - **OSC sequence support**: Capture progress updates and terminal escape sequences
//! - **Flexible configuration**: Control what data is captured and processed
//! - **Async/await support**: Built on tokio for non-blocking operation
//!
//! ## Implementation Strategy
//!
//! Both read-only and read-write modes use a multi-task async architecture:
//!
//! - **Completion Task**: Manages PTY pair creation, child process lifecycle, and
//! coordinates shutdown
//! - **Reader Task**: Handles output from the spawned process using `spawn_blocking` (PTY
//! I/O is inherently synchronous)
//! - **Input Handler Task**: (Read-write only) Manages input to the spawned process
//! - **Bridge Task**: (Read-write only) Converts async input events to sync channel for
//! blocking I/O
//!
//! ### Task Coordination & Lifecycle
//!
//! | Time | Completion Task | Reader Task | Input Handler | Bridge Task |
//! |:-------|:-----------------------|:-----------------|:-----------------|:-----------------|
//! | 0 | 🛫 Spawn child | | | |
//! | 1 | 🛫 Spawn reader | 🛫 Start read | | |
//! | 2 | 🛫 Spawn input hdlr* | 📖 Read data | 🛫 Start* | |
//! | 3 | 🛫 Spawn bridge* | 📤 Send events | 📥 Wait input* | 🛫 Start* |
//! | 4 | 🛬 Wait `child.wait()` | 📖 Read data | ✍️ Write PTY* | 🔄 Bridge I/O* |
//! | 5 | 📤 Send Exit event | 📖 Read EOF | 📥 Wait input* | 🔄 Bridge I/O* |
//! | 6 | 💀 drop(controlled) | 🛬 Exit | 🛬 Exit* | 🛬 Exit* |
//! | 7 | 🛬 Wait all tasks | | | |
//! | 8 | ✅ Return status | | | |
//! *Read-write mode only
//!
//! ### Event Communication
//!
//! ```text
//! Your Program ◄─── MPSC Channel ◄─── Background Tasks
//! │ │
//! │ ├─ Reader Task → Output/OSC Events
//! │ └─ Completion Task → Exit Events
//! │
//! └─ MPSC Channel ──► Input Handler (read-write only)
//! ```
//!
//! Events flow through unbounded MPSC channels, allowing your program to receive output
//! asynchronously while background tasks handle the blocking PTY operations.
//!
//! ### Channel Architecture (Read-Write Mode)
//!
//! ```text
//! Your Program
//! │
//! │ (async input)
//! │
//! ┌────▼────────────────────────────────┐
//! │ Async Input Channel │
//! │ (unbounded MPSC) │
//! └─────────────────────────────────────┘
//! │
//! │ (Bridge Task converts async→sync)
//! │
//! ┌────▼────────────────────────────────┐
//! │ Sync Input Channel │
//! │ (std::sync::mpsc) │
//! └─────────────────────────────────────┘
//! │
//! │ (Input Handler Task - blocking)
//! │
//! ┌────▼────────────────────────────────┐
//! │ PTY Controller │
//! │ (write to spawned) │
//! └─────────────────────────────────────┘
//! │
//! ┌────▼────────────────────────────────┐
//! │ Spawned Process Input │
//! └─────────────────────────────────────┘
//!
//! ---------------------------------------
//!
//! ┌─────────────────────────────────────┐
//! │ Spawned Process Output │
//! └─────────────────────────────────────┘
//! │
//! │
//! ┌────▼────────────────────────────────┐
//! │ PTY Controller │
//! │ (read from spawned) │
//! └─────────────────────────────────────┘
//! │
//! │ (Reader Task - blocking)
//! │
//! ┌────▼────────────────────────────────┐
//! │ Async Output Channel │
//! │ (unbounded MPSC) │
//! └─────────────────────────────────────┘
//! │
//! │ (async output)
//! │
//! ▼
//! Your Program
//! ```
//!
//! ## Critical PTY Lifecycle Management
//!
//! **Understanding PTY file descriptor management is crucial for all implementations
//! in this module to avoid deadlocks.**
//!
//! ### The PTY File Descriptor Reference Counting Problem
//!
//! A PTY consists of two halves: master (controller) and slave (controlled). The
//! kernel's PTY implementation requires **BOTH** conditions for EOF:
//!
//! 1. The slave side must be closed (happens when the child process exits)
//! 2. The reader must be the ONLY remaining reference to the master
//!
//! ### Why Explicit Resource Management is Required
//!
//! Even though the child process has exited and closed its slave FD, our `controlled`
//! variable keeps the slave side open. The PTY won't send EOF to the master until ALL
//! slave file descriptors are closed. Without explicitly dropping `controlled`, it would
//! remain open until the entire function returns, causing the reader to block forever
//! waiting for EOF that never comes.
//!
//! ### The Solution Strategy (Applied in All Implementations)
//!
//! 1. Clone a reader from controller, keeping controller in scope
//! 2. **Explicitly drop controlled after process exits** - closes our controlled half FD
//! 3. Drop controller after process exits to release master FD
//! 4. This allows the reader to receive EOF and exit cleanly
//!
//! **This pattern is critical in both read-only and read-write implementations.**
//!
//! ## Main Types
//!
//! - [`PtyCommandBuilder`]: Builder for configuring and spawning PTY commands
//! - [`PtyReadWriteSession`]: Read-write PTY session handle
//! - [`PtyReadOnlySession`]: Read-only PTY session handle
//! - [`PtyReadWriteOutputEvent`]: Events received from PTY processes (output, OSC
//! sequences, exit)
//! - [`PtyInputEvent`]: Input types that can be sent to interactive sessions
//!
//! ## Quick Start
//!
//! ### Read-only session (capture command output):
//! ```rust
//! use r3bl_tui::{PtyCommandBuilder, PtyConfigOption, PtyReadOnlyOutputEvent};
//!
//! # #[tokio::main]
//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
//! let mut session = PtyCommandBuilder::new("ls")
//! .args(["-la"])
//! .spawn_read_only(PtyConfigOption::Output)?;
//!
//! while let Some(event) = session.output_evt_ch_rx_half.recv().await {
//! match event {
//! PtyReadOnlyOutputEvent::Output(data) => {
//! print!("{}", String::from_utf8_lossy(&data));
//! }
//! PtyReadOnlyOutputEvent::Exit(_) => break,
//! _ => {}
//! }
//! }
//! # Ok(())
//! # }
//! ```
//!
//! ### Interactive session (send input to process):
//! ```rust
//! use r3bl_tui::{PtyCommandBuilder, PtyReadWriteOutputEvent, PtyInputEvent, ControlSequence, CursorKeyMode};
//! use portable_pty::PtySize;
//!
//! # #[tokio::main]
//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
//! let mut session = PtyCommandBuilder::new("cat")
//! .spawn_read_write(PtySize { rows: 24, cols: 80, pixel_width: 0, pixel_height: 0 })?;
//!
//! // Send input
//! session.input_event_ch_tx_half.send(PtyInputEvent::WriteLine("Hello, PTY!".into()))?;
//! session.input_event_ch_tx_half.send(PtyInputEvent::SendControl(ControlSequence::CtrlD, CursorKeyMode::default()))?; // EOF
//!
//! // Read output
//! while let Some(event) = session.output_event_receiver_half.recv().await {
//! match event {
//! PtyReadWriteOutputEvent::Output(data) => {
//! print!("{}", String::from_utf8_lossy(&data));
//! }
//! PtyReadWriteOutputEvent::Exit(_) => break,
//! _ => {}
//! }
//! }
//! # Ok(())
//! # }
//! ```
// Attach.
// Re-export.
pub use *;
pub use *;
pub use *;