1use std::{
2 io::Read,
3 sync::{
4 Arc,
5 Mutex,
6 RwLock,
7 },
8};
9
10use freya_core::{
11 notify::ArcNotify,
12 prelude::{
13 Platform,
14 UserEvent,
15 spawn_forever,
16 },
17};
18use futures_lite::StreamExt;
19use futures_util::FutureExt;
20use portable_pty::{
21 CommandBuilder,
22 PtySize,
23 native_pty_system,
24};
25use vt100::Parser;
26
27use crate::{
28 buffer::TerminalBuffer,
29 handle::{
30 TerminalCleaner,
31 TerminalError,
32 TerminalHandle,
33 TerminalId,
34 },
35 parser::check_for_terminal_queries,
36};
37
38pub(crate) fn spawn_pty(command: CommandBuilder) -> Result<TerminalHandle, TerminalError> {
40 let (update_tx, mut update_rx) = futures_channel::mpsc::unbounded::<()>();
41 let (resize_tx, mut resize_rx) = futures_channel::mpsc::unbounded::<(u16, u16)>();
42
43 let id = TerminalId::new();
44 let buffer = Arc::new(Mutex::new(TerminalBuffer::default()));
45 let parser = Arc::new(RwLock::new(Parser::new(24, 80, 1000)));
46 let writer = Arc::new(Mutex::new(None::<Box<dyn std::io::Write + Send>>));
47 let closer_notifier = ArcNotify::new();
48
49 let pty_system = native_pty_system();
50 let pair = pty_system
51 .openpty(PtySize::default())
52 .map_err(|_| TerminalError::NotInitialized)?;
53 let master_writer = pair
54 .master
55 .take_writer()
56 .map_err(|_| TerminalError::NotInitialized)?;
57 *writer.lock().unwrap() = Some(master_writer);
58
59 pair.slave
60 .spawn_command(command)
61 .map_err(|_| TerminalError::NotInitialized)?;
62 let mut reader = pair
63 .master
64 .try_clone_reader()
65 .map_err(|_| TerminalError::NotInitialized)?;
66 let platform = Platform::get();
67 let task = spawn_forever({
68 let parser = parser.clone();
69 let buffer = buffer.clone();
70 let closer_notifier = closer_notifier.clone();
71 let writer = writer.clone();
72 async move {
73 loop {
74 futures_util::select! {
75 update = update_rx.next().fuse() => {
76 if update.is_none() {
77 *writer.lock().unwrap() = None;
79 closer_notifier.notify();
80 platform.send(UserEvent::RequestRedraw);
81 break;
82 }
83 if let Ok(p) = parser.read() {
84 let (rows, cols) = p.screen().size();
85 let rows_vec: Vec<Vec<vt100::Cell>> = (0..rows)
86 .map(|r| {
87 (0..cols)
88 .map(|c| p.screen().cell(r, c).unwrap().clone())
89 .collect()
90 })
91 .collect();
92
93 let (cur_r, cur_c) = p.screen().cursor_position();
94 let new_buffer = TerminalBuffer {
95 rows: rows_vec,
96 cursor_row: cur_r as usize,
97 cursor_col: cur_c as usize,
98 cols: cols as usize,
99 rows_count: rows as usize,
100 };
101
102 if let Ok(mut buf) = buffer.lock() {
103 *buf = new_buffer;
104 platform.send(UserEvent::RequestRedraw);
105 }
106 }
107 }
108 resize = resize_rx.next().fuse() => {
109 if let Some((rows, cols)) = resize {
110 if let Ok(mut p) = parser.write() {
111 p.screen_mut().set_size(rows, cols);
112 }
113
114 if let Ok(p) = parser.read() {
116 let (rows, cols) = p.screen().size();
117 let rows_vec: Vec<Vec<vt100::Cell>> = (0..rows)
118 .map(|r| {
119 (0..cols)
120 .map(|c| p.screen().cell(r, c).unwrap().clone())
121 .collect()
122 })
123 .collect();
124
125 let (cur_r, cur_c) = p.screen().cursor_position();
126 let new_buffer = TerminalBuffer {
127 rows: rows_vec,
128 cursor_row: cur_r as usize,
129 cursor_col: cur_c as usize,
130 cols: cols as usize,
131 rows_count: rows as usize,
132 };
133
134 if let Ok(mut buf) = buffer.lock() {
135 *buf = new_buffer;
136 }
137 }
138
139 let size = PtySize {
141 rows,
142 cols,
143 pixel_width: 0,
144 pixel_height: 0,
145 };
146 let _ = pair.master.resize(size);
147 }
148 }
149 }
150 }
151 }
152 });
153
154 blocking::unblock({
155 let writer = writer.clone();
156 move || {
157 let mut buf = [0u8; 4096];
158 loop {
159 match reader.read(&mut buf) {
160 Ok(0) => break,
161 Ok(n) => {
162 let data = &buf[..n];
163
164 if let Ok(mut p) = parser.write() {
165 p.process(data);
166 }
167
168 let responses = check_for_terminal_queries(data, &parser);
169 if !responses.is_empty()
170 && let Ok(mut guard) = writer.lock()
171 && let Some(w) = guard.as_mut()
172 {
173 for response in responses {
174 let _ = w.write_all(&response);
175 }
176 let _ = w.flush();
177 }
178
179 let _ = update_tx.unbounded_send(());
180 }
181 Err(_) => break,
182 }
183 }
184 }
185 })
186 .detach();
187 Ok(TerminalHandle {
188 closer_notifier: closer_notifier.clone(),
189 cleaner: Arc::new(TerminalCleaner {
190 writer: writer.clone(),
191 task,
192 closer_notifier,
193 }),
194 id,
195 buffer,
196 writer,
197 resize_sender: Arc::new(resize_tx),
198 })
199}