1use std::{
16 io::Result as IoResult,
17 sync::{Arc, RwLock},
18};
19
20use portable_pty::{CommandBuilder, ExitStatus, PtySystem};
21use vt100::{Parser, Screen};
22
23pub struct Controller {
25 cmd: CommandBuilder,
27 size: Size,
28 parser: Option<Arc<RwLock<Parser>>>,
29 exit_status: Option<IoResult<ExitStatus>>,
30}
31
32impl Controller {
33 pub fn new(cmd: CommandBuilder, size: Option<Size>) -> Self {
34 Self {
35 cmd,
36 size: size.unwrap_or_default(),
37 parser: None,
38 exit_status: None,
39 }
40 }
41
42 pub fn run(&mut self) {
44 let pair = self.init_pty();
45 let mut child = pair.slave.spawn_command(self.cmd.clone()).unwrap();
46 drop(pair.slave);
47 let mut reader = pair.master.try_clone_reader().unwrap();
48 let parser = Arc::new(RwLock::new(vt100::Parser::new(
49 self.size.rows,
50 self.size.cols,
51 0,
52 )));
53 {
54 let parser = parser.clone();
55 std::thread::spawn(move || {
56 let mut s = String::new();
58 reader.read_to_string(&mut s).unwrap();
59 if !s.is_empty() {
60 let mut parser = parser.write().unwrap();
61 parser.process(s.as_bytes());
62 }
63 });
64 }
65 self.exit_status = Some(child.wait());
67 let _writer = pair.master.take_writer().unwrap();
69
70 drop(pair.master);
71 self.parser = Some(parser);
72 }
73
74 fn init_pty(&self) -> portable_pty::PtyPair {
75 use portable_pty::{NativePtySystem, PtySize};
76 let pty_system = NativePtySystem::default();
77
78 pty_system
79 .openpty(PtySize {
80 rows: self.size.rows,
81 cols: self.size.cols,
82 pixel_width: self.size.pixel_width,
83 pixel_height: self.size.pixel_height,
84 })
85 .unwrap()
86 }
87
88 pub fn screen(&self) -> Option<Screen> {
89 if let Some(parser) = &self.parser {
90 let binding = parser.read().ok()?;
93 Some(binding.screen().clone())
94 } else {
95 None
96 }
97 }
98
99 pub fn finished(&self) -> bool {
101 self.exit_status.is_some()
102 }
103
104 pub fn status(&self) -> Option<&IoResult<ExitStatus>> {
106 self.exit_status.as_ref()
107 }
108}
109
110#[derive(Default, Clone)]
111pub struct Size {
112 pub cols: u16,
113 pub rows: u16,
114 pixel_width: u16,
115 pixel_height: u16,
116}
117
118impl Size {
119 pub fn new(cols: u16, rows: u16, pixel_width: u16, pixel_height: u16) -> Self {
120 Self {
121 cols,
122 rows,
123 pixel_width,
124 pixel_height,
125 }
126 }
127}