simple_ls_rw/
simple_ls_rw.rs1use std::{
2 io,
3 sync::{Arc, RwLock},
4};
5
6use crossterm::event::{self, Event, KeyCode, KeyEventKind};
7use portable_pty::{CommandBuilder, NativePtySystem, PtySize, PtySystem};
8use ratatui::{
9 DefaultTerminal, Frame,
10 layout::Alignment,
11 style::{Modifier, Style},
12 text::Line,
13 widgets::{Block, Borders, Paragraph},
14};
15use tui_term::widget::PseudoTerminal;
16use vt100::Screen;
17
18fn main() -> std::io::Result<()> {
19 let mut terminal = ratatui::init();
20 let result = run_app(&mut terminal);
21 ratatui::restore();
22 result
23}
24
25fn run_app(terminal: &mut DefaultTerminal) -> std::io::Result<()> {
26 let terminal_size = terminal.size()?;
27 let size = Size {
28 rows: terminal_size.height,
29 cols: terminal_size.width,
30 };
31
32 let pty_system = NativePtySystem::default();
33 let cwd = std::env::current_dir().unwrap();
34 let mut cmd = CommandBuilder::new("ls");
35 cmd.cwd(cwd);
36
37 let pair = pty_system
38 .openpty(PtySize {
39 rows: size.rows,
40 cols: size.cols,
41 pixel_width: 0,
42 pixel_height: 0,
43 })
44 .unwrap();
45 let mut child = pair.slave.spawn_command(cmd).unwrap();
46 drop(pair.slave);
47
48 let mut reader = pair.master.try_clone_reader().unwrap();
49 let parser = Arc::new(RwLock::new(vt100::Parser::new(
50 size.rows - 1,
51 size.cols - 1,
52 0,
53 )));
54
55 {
56 let parser = parser.clone();
57 std::thread::spawn(move || {
58 let mut s = String::new();
60 reader.read_to_string(&mut s).unwrap();
61 if !s.is_empty() {
62 let mut parser = parser.write().unwrap();
63 parser.process(s.as_bytes());
64 }
65 });
66 }
67
68 {
69 let _writer = pair.master.take_writer().unwrap();
71 }
72
73 let _child_exit_status = child.wait().unwrap();
75
76 drop(pair.master);
77
78 run(terminal, parser)
79}
80
81fn run(terminal: &mut DefaultTerminal, parser: Arc<RwLock<vt100::Parser>>) -> io::Result<()> {
82 loop {
83 terminal.draw(|f| ui(f, parser.read().unwrap().screen()))?;
84
85 if let Event::Key(key) = event::read()? {
86 if key.kind == KeyEventKind::Press {
87 if let KeyCode::Char('q') = key.code {
88 return Ok(());
89 }
90 }
91 }
92 }
93}
94
95fn ui(f: &mut Frame, screen: &Screen) {
96 let chunks = ratatui::layout::Layout::default()
97 .direction(ratatui::layout::Direction::Vertical)
98 .margin(1)
99 .constraints(
100 [
101 ratatui::layout::Constraint::Percentage(50),
102 ratatui::layout::Constraint::Percentage(50),
103 ratatui::layout::Constraint::Min(1),
104 ]
105 .as_ref(),
106 )
107 .split(f.area());
108 let title = Line::from("[ Running: ls ]");
109 let block = Block::default()
110 .borders(Borders::ALL)
111 .title(title)
112 .style(Style::default().add_modifier(Modifier::BOLD));
113 let pseudo_term = PseudoTerminal::new(screen).block(block.clone());
114 f.render_widget(pseudo_term, chunks[0]);
115 let pseudo_term = PseudoTerminal::new(screen).block(block);
116 f.render_widget(pseudo_term, chunks[1]);
117 let block = Block::default().borders(Borders::ALL);
118 f.render_widget(block, f.area());
119 let explanation = "Press q to exit";
120 let explanation = Paragraph::new(explanation)
121 .style(Style::default().add_modifier(Modifier::BOLD | Modifier::REVERSED))
122 .alignment(Alignment::Center);
123 f.render_widget(explanation, chunks[2]);
124}
125
126#[derive(Debug, Clone)]
127struct Size {
128 cols: u16,
129 rows: u16,
130}