1use crate::buffer::Cell;
2use crossterm::style::Color;
3use portable_pty::{native_pty_system, CommandBuilder, PtySize};
4use std::cell::RefCell;
5use std::io::{Read, Write};
6use std::rc::Rc;
7use std::sync::mpsc::{self, Receiver};
8use std::thread;
9use vte::{Params, Perform};
10
11#[derive(Clone)]
13pub struct TerminalBuffer {
14 cells: Vec<Vec<Cell>>,
15 rows: usize,
16 cols: usize,
17 cursor_row: usize,
18 cursor_col: usize,
19 cursor_visible: bool,
20 current_fg: Color,
22 current_bg: Color,
23 current_bold: bool,
24 current_italic: bool,
25 current_underline: bool,
26 current_dim: bool,
27}
28
29impl TerminalBuffer {
30 pub fn new(rows: usize, cols: usize) -> Self {
31 let cells = vec![vec![Cell::default(); cols]; rows];
32 Self {
33 cells,
34 rows,
35 cols,
36 cursor_row: 0,
37 cursor_col: 0,
38 cursor_visible: true,
39 current_fg: Color::Reset,
40 current_bg: Color::Reset,
41 current_bold: false,
42 current_italic: false,
43 current_underline: false,
44 current_dim: false,
45 }
46 }
47
48 pub fn rows(&self) -> usize {
49 self.rows
50 }
51
52 pub fn cols(&self) -> usize {
53 self.cols
54 }
55
56 pub fn cursor_row(&self) -> usize {
57 self.cursor_row
58 }
59
60 pub fn cursor_col(&self) -> usize {
61 self.cursor_col
62 }
63
64 pub fn cursor_visible(&self) -> bool {
65 self.cursor_visible
66 }
67
68 pub fn cells(&self) -> &Vec<Vec<Cell>> {
69 &self.cells
70 }
71
72 pub fn get_cell(&self, row: usize, col: usize) -> Option<&Cell> {
73 self.cells.get(row).and_then(|r| r.get(col))
74 }
75
76 pub fn set_cell(&mut self, row: usize, col: usize, cell: Cell) {
77 if row < self.rows && col < self.cols {
78 self.cells[row][col] = cell;
79 }
80 }
81
82 pub fn write_char(&mut self, ch: char) {
83 if self.cursor_row < self.rows && self.cursor_col < self.cols {
84 let cell = Cell::styled(
85 ch,
86 self.current_fg,
87 self.current_bg,
88 self.current_bold,
89 self.current_italic,
90 self.current_underline,
91 self.current_dim,
92 );
93 self.cells[self.cursor_row][self.cursor_col] = cell;
94 self.cursor_col += 1;
95
96 if self.cursor_col >= self.cols {
98 self.cursor_col = 0;
99 self.cursor_row += 1;
100 if self.cursor_row >= self.rows {
102 self.scroll_up();
103 self.cursor_row = self.rows - 1;
104 }
105 }
106 }
107 }
108
109 pub fn move_cursor(&mut self, row: usize, col: usize) {
110 self.cursor_row = row.min(self.rows.saturating_sub(1));
111 self.cursor_col = col.min(self.cols.saturating_sub(1));
112 }
113
114 pub fn move_cursor_relative(&mut self, row_delta: isize, col_delta: isize) {
115 let new_row = (self.cursor_row as isize + row_delta).max(0) as usize;
116 let new_col = (self.cursor_col as isize + col_delta).max(0) as usize;
117 self.move_cursor(new_row, new_col);
118 }
119
120 pub fn set_cursor_visible(&mut self, visible: bool) {
121 self.cursor_visible = visible;
122 }
123
124 pub fn clear_screen(&mut self) {
125 for row in &mut self.cells {
126 for cell in row {
127 *cell = Cell::default();
128 }
129 }
130 }
131
132 pub fn clear_line(&mut self, row: usize) {
133 if row < self.rows {
134 for col in 0..self.cols {
135 self.cells[row][col] = Cell::default();
136 }
137 }
138 }
139
140 pub fn clear_line_from_cursor(&mut self) {
141 let row = self.cursor_row;
142 if row < self.rows {
143 for col in self.cursor_col..self.cols {
144 self.cells[row][col] = Cell::default();
145 }
146 }
147 }
148
149 pub fn clear_line_to_cursor(&mut self) {
150 let row = self.cursor_row;
151 if row < self.rows {
152 for col in 0..=self.cursor_col.min(self.cols - 1) {
153 self.cells[row][col] = Cell::default();
154 }
155 }
156 }
157
158 pub fn scroll_up(&mut self) {
159 self.cells.remove(0);
160 self.cells.push(vec![Cell::default(); self.cols]);
161 }
162
163 pub fn carriage_return(&mut self) {
164 self.cursor_col = 0;
165 }
166
167 pub fn newline(&mut self) {
168 self.cursor_row += 1;
169 if self.cursor_row >= self.rows {
170 self.scroll_up();
171 self.cursor_row = self.rows - 1;
172 }
173 }
174
175 pub fn tab(&mut self) {
176 let next_stop = ((self.cursor_col / 8) + 1) * 8;
178 self.cursor_col = next_stop.min(self.cols - 1);
179 }
180
181 pub fn backspace(&mut self) {
182 if self.cursor_col > 0 {
183 self.cursor_col -= 1;
184 }
185 }
186
187 pub fn set_sgr(&mut self, fg: Color, bg: Color, bold: bool, italic: bool, underline: bool, dim: bool) {
188 self.current_fg = fg;
189 self.current_bg = bg;
190 self.current_bold = bold;
191 self.current_italic = italic;
192 self.current_underline = underline;
193 self.current_dim = dim;
194 }
195
196 pub fn reset_sgr(&mut self) {
197 self.current_fg = Color::Reset;
198 self.current_bg = Color::Reset;
199 self.current_bold = false;
200 self.current_italic = false;
201 self.current_underline = false;
202 self.current_dim = false;
203 }
204}
205
206enum PtyMessage {
208 Output(Vec<u8>),
209 Exited,
210}
211
212#[derive(Clone)]
214pub struct TerminalHandle {
215 inner: Rc<RefCell<TerminalHandleInner>>,
216}
217
218struct TerminalHandleInner {
219 buffer: TerminalBuffer,
220 parser: vte::Parser,
221 pty_pair: Option<PtyPair>,
222 output_rx: Option<Receiver<PtyMessage>>,
223 is_started: bool,
224 is_exited: bool,
225}
226
227struct PtyPair {
228 writer: Box<dyn Write + Send>,
229 _reader_thread: thread::JoinHandle<()>,
230}
231
232impl TerminalHandle {
233 pub fn new(rows: usize, cols: usize) -> Self {
234 Self {
235 inner: Rc::new(RefCell::new(TerminalHandleInner {
236 buffer: TerminalBuffer::new(rows, cols),
237 parser: vte::Parser::new(),
238 pty_pair: None,
239 output_rx: None,
240 is_started: false,
241 is_exited: false,
242 })),
243 }
244 }
245
246 pub fn is_started(&self) -> bool {
247 self.inner.borrow().is_started
248 }
249
250 pub fn is_exited(&self) -> bool {
251 self.inner.borrow().is_exited
252 }
253
254 pub fn spawn(&self, command: &str, args: &[&str], cols: usize, rows: usize) -> Result<(), String> {
255 let mut inner = self.inner.borrow_mut();
256
257 if inner.is_started {
258 return Err("Terminal already started".to_string());
259 }
260
261 let pty_system = native_pty_system();
262
263 let pty_pair = pty_system
264 .openpty(PtySize {
265 rows: rows as u16,
266 cols: cols as u16,
267 pixel_width: 0,
268 pixel_height: 0,
269 })
270 .map_err(|e| format!("Failed to open PTY: {}", e))?;
271
272 let mut cmd = CommandBuilder::new(command);
273 for arg in args {
274 cmd.arg(arg);
275 }
276
277 let mut child = pty_pair
278 .slave
279 .spawn_command(cmd)
280 .map_err(|e| format!("Failed to spawn command: {}", e))?;
281
282 let (output_tx, output_rx) = mpsc::channel();
284 let mut reader = pty_pair
285 .master
286 .try_clone_reader()
287 .map_err(|e| format!("Failed to clone reader: {}", e))?;
288
289 let reader_thread = thread::spawn(move || {
290 let mut buf = [0u8; 4096];
291 loop {
292 match reader.read(&mut buf) {
293 Ok(0) => {
294 let _ = output_tx.send(PtyMessage::Exited);
296 break;
297 }
298 Ok(n) => {
299 if output_tx.send(PtyMessage::Output(buf[..n].to_vec())).is_err() {
300 break;
302 }
303 }
304 Err(_) => {
305 let _ = output_tx.send(PtyMessage::Exited);
306 break;
307 }
308 }
309 }
310 let _ = child.wait();
312 });
313
314 let writer = pty_pair
315 .master
316 .take_writer()
317 .map_err(|e| format!("Failed to take writer: {}", e))?;
318
319 inner.pty_pair = Some(PtyPair {
320 writer,
321 _reader_thread: reader_thread,
322 });
323 inner.output_rx = Some(output_rx);
324 inner.is_started = true;
325 inner.buffer = TerminalBuffer::new(rows, cols);
326
327 Ok(())
328 }
329
330 pub fn poll(&self) {
331 let messages: Vec<PtyMessage> = {
333 let inner = self.inner.borrow();
334
335 if !inner.is_started || inner.is_exited {
336 return;
337 }
338
339 let Some(ref rx) = inner.output_rx else {
340 return;
341 };
342
343 let mut msgs = Vec::new();
345 while let Ok(msg) = rx.try_recv() {
346 msgs.push(msg);
347 }
348 msgs
349 };
350
351 for msg in messages {
353 match msg {
354 PtyMessage::Output(data) => {
355 let mut inner = self.inner.borrow_mut();
356 let buffer_ptr = &mut inner.buffer as *mut TerminalBuffer;
360 let parser_ptr = &mut inner.parser as *mut vte::Parser;
361
362 unsafe {
363 let mut performer = TerminalPerformer {
364 buffer: &mut *buffer_ptr,
365 };
366 (*parser_ptr).advance(&mut performer, &data);
367 }
368 }
369 PtyMessage::Exited => {
370 let mut inner = self.inner.borrow_mut();
371 inner.is_exited = true;
372 return;
373 }
374 }
375 }
376 }
377
378 pub fn send_input(&self, data: &[u8]) -> Result<(), String> {
379 let mut inner = self.inner.borrow_mut();
380
381 if !inner.is_started || inner.is_exited {
382 return Err("Terminal not running".to_string());
383 }
384
385 if let Some(ref mut pty_pair) = inner.pty_pair {
386 pty_pair.writer.write_all(data)
387 .map_err(|e| format!("Failed to write to PTY: {}", e))?;
388 pty_pair.writer.flush()
389 .map_err(|e| format!("Failed to flush PTY: {}", e))?;
390 }
391
392 Ok(())
393 }
394
395 pub fn get_buffer(&self) -> TerminalBuffer {
396 self.inner.borrow().buffer.clone()
397 }
398
399 pub fn resize(&self, rows: usize, cols: usize) -> Result<(), String> {
400 let mut inner = self.inner.borrow_mut();
401
402 if !inner.is_started {
403 return Ok(());
404 }
405
406 if let Some(ref mut _pty_pair) = inner.pty_pair {
407 }
410
411 inner.buffer = TerminalBuffer::new(rows, cols);
412 Ok(())
413 }
414}
415
416struct TerminalPerformer<'a> {
418 buffer: &'a mut TerminalBuffer,
419}
420
421impl<'a> Perform for TerminalPerformer<'a> {
422 fn print(&mut self, c: char) {
423 self.buffer.write_char(c);
424 }
425
426 fn execute(&mut self, byte: u8) {
427 match byte {
428 b'\n' => {
429 self.buffer.newline();
431 }
432 b'\r' => {
433 self.buffer.carriage_return();
435 }
436 b'\t' => {
437 self.buffer.tab();
439 }
440 0x08 => {
441 self.buffer.backspace();
443 }
444 0x07 => {
445 }
447 _ => {
448 }
450 }
451 }
452
453 fn hook(&mut self, _params: &Params, _intermediates: &[u8], _ignore: bool, _c: char) {
454 }
456
457 fn put(&mut self, _byte: u8) {
458 }
460
461 fn unhook(&mut self) {
462 }
464
465 fn osc_dispatch(&mut self, _params: &[&[u8]], _bell_terminated: bool) {
466 }
468
469 fn csi_dispatch(&mut self, params: &Params, _intermediates: &[u8], _ignore: bool, c: char) {
470 match c {
471 'H' | 'f' => {
472 let row = params.iter().next().and_then(|p| p.first().copied()).unwrap_or(1);
474 let col = params.iter().nth(1).and_then(|p| p.first().copied()).unwrap_or(1);
475 self.buffer.move_cursor(
476 (row as usize).saturating_sub(1),
477 (col as usize).saturating_sub(1),
478 );
479 }
480 'A' => {
481 let n = params.iter().next().and_then(|p| p.first().copied()).unwrap_or(1);
483 self.buffer.move_cursor_relative(-(n as isize), 0);
484 }
485 'B' => {
486 let n = params.iter().next().and_then(|p| p.first().copied()).unwrap_or(1);
488 self.buffer.move_cursor_relative(n as isize, 0);
489 }
490 'C' => {
491 let n = params.iter().next().and_then(|p| p.first().copied()).unwrap_or(1);
493 self.buffer.move_cursor_relative(0, n as isize);
494 }
495 'D' => {
496 let n = params.iter().next().and_then(|p| p.first().copied()).unwrap_or(1);
498 self.buffer.move_cursor_relative(0, -(n as isize));
499 }
500 'J' => {
501 let n = params.iter().next().and_then(|p| p.first().copied()).unwrap_or(0);
503 match n {
504 0 => {
505 self.buffer.clear_line_from_cursor();
507 let row = self.buffer.cursor_row();
508 for r in (row + 1)..self.buffer.rows() {
509 self.buffer.clear_line(r);
510 }
511 }
512 1 => {
513 self.buffer.clear_line_to_cursor();
515 let row = self.buffer.cursor_row();
516 for r in 0..row {
517 self.buffer.clear_line(r);
518 }
519 }
520 2 => {
521 self.buffer.clear_screen();
523 }
524 _ => {}
525 }
526 }
527 'K' => {
528 let n = params.iter().next().and_then(|p| p.first().copied()).unwrap_or(0);
530 match n {
531 0 => self.buffer.clear_line_from_cursor(),
532 1 => self.buffer.clear_line_to_cursor(),
533 2 => self.buffer.clear_line(self.buffer.cursor_row()),
534 _ => {}
535 }
536 }
537 'm' => {
538 let mut fg = self.buffer.current_fg;
540 let mut bg = self.buffer.current_bg;
541 let mut bold = self.buffer.current_bold;
542 let mut italic = self.buffer.current_italic;
543 let mut underline = self.buffer.current_underline;
544 let mut dim = self.buffer.current_dim;
545
546 let mut iter = params.iter();
547 while let Some(param) = iter.next() {
548 let n = param.first().copied().unwrap_or(0);
549 match n {
550 0 => {
551 fg = Color::Reset;
553 bg = Color::Reset;
554 bold = false;
555 italic = false;
556 underline = false;
557 dim = false;
558 }
559 1 => bold = true,
560 2 => dim = true,
561 3 => italic = true,
562 4 => underline = true,
563 22 => {
564 bold = false;
565 dim = false;
566 }
567 23 => italic = false,
568 24 => underline = false,
569 30 => fg = Color::Black,
571 31 => fg = Color::DarkRed,
572 32 => fg = Color::DarkGreen,
573 33 => fg = Color::DarkYellow,
574 34 => fg = Color::DarkBlue,
575 35 => fg = Color::DarkMagenta,
576 36 => fg = Color::DarkCyan,
577 37 => fg = Color::Grey,
578 38 => {
579 if let Some(next_param) = iter.next() {
581 let mode = next_param.first().copied().unwrap_or(0);
582 if mode == 5 {
583 if let Some(color_param) = iter.next() {
585 let color_idx = color_param.first().copied().unwrap_or(0);
586 fg = Color::AnsiValue(color_idx as u8);
587 }
588 } else if mode == 2 {
589 let r = iter.next().and_then(|p| p.first().copied()).unwrap_or(0) as u8;
591 let g = iter.next().and_then(|p| p.first().copied()).unwrap_or(0) as u8;
592 let b = iter.next().and_then(|p| p.first().copied()).unwrap_or(0) as u8;
593 fg = Color::Rgb { r, g, b };
594 }
595 }
596 }
597 39 => fg = Color::Reset,
598 40 => bg = Color::Black,
600 41 => bg = Color::DarkRed,
601 42 => bg = Color::DarkGreen,
602 43 => bg = Color::DarkYellow,
603 44 => bg = Color::DarkBlue,
604 45 => bg = Color::DarkMagenta,
605 46 => bg = Color::DarkCyan,
606 47 => bg = Color::Grey,
607 48 => {
608 if let Some(next_param) = iter.next() {
610 let mode = next_param.first().copied().unwrap_or(0);
611 if mode == 5 {
612 if let Some(color_param) = iter.next() {
614 let color_idx = color_param.first().copied().unwrap_or(0);
615 bg = Color::AnsiValue(color_idx as u8);
616 }
617 } else if mode == 2 {
618 let r = iter.next().and_then(|p| p.first().copied()).unwrap_or(0) as u8;
620 let g = iter.next().and_then(|p| p.first().copied()).unwrap_or(0) as u8;
621 let b = iter.next().and_then(|p| p.first().copied()).unwrap_or(0) as u8;
622 bg = Color::Rgb { r, g, b };
623 }
624 }
625 }
626 49 => bg = Color::Reset,
627 90 => fg = Color::DarkGrey,
629 91 => fg = Color::Red,
630 92 => fg = Color::Green,
631 93 => fg = Color::Yellow,
632 94 => fg = Color::Blue,
633 95 => fg = Color::Magenta,
634 96 => fg = Color::Cyan,
635 97 => fg = Color::White,
636 100 => bg = Color::DarkGrey,
638 101 => bg = Color::Red,
639 102 => bg = Color::Green,
640 103 => bg = Color::Yellow,
641 104 => bg = Color::Blue,
642 105 => bg = Color::Magenta,
643 106 => bg = Color::Cyan,
644 107 => bg = Color::White,
645 _ => {}
646 }
647 }
648
649 self.buffer.set_sgr(fg, bg, bold, italic, underline, dim);
650 }
651 'h' => {
652 if let Some(param) = params.iter().next() {
654 let n = param.first().copied().unwrap_or(0);
655 if n == 25 {
656 self.buffer.set_cursor_visible(true);
658 }
659 }
660 }
661 'l' => {
662 if let Some(param) = params.iter().next() {
664 let n = param.first().copied().unwrap_or(0);
665 if n == 25 {
666 self.buffer.set_cursor_visible(false);
668 }
669 }
670 }
671 _ => {
672 }
674 }
675 }
676
677 fn esc_dispatch(&mut self, _intermediates: &[u8], _ignore: bool, _byte: u8) {
678 }
680}