freya_terminal/
pty.rs

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
38/// Spawn a PTY and return a TerminalHandle
39pub(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                            // Channel closed - PTY exited
78                            *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                            // Resize parser
115                            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                            // Resize PTY
140                            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}