simple_ls_rw/
simple_ls_rw.rs1use std::{
2 io::{self, BufWriter},
3 sync::{Arc, RwLock},
4};
5
6use crossterm::{
7 event::{self, Event, KeyCode, KeyEventKind},
8 execute,
9 terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
10};
11use portable_pty::{CommandBuilder, NativePtySystem, PtySize, PtySystem};
12use ratatui::{
13 backend::{Backend, CrosstermBackend},
14 layout::Alignment,
15 style::{Modifier, Style},
16 text::Line,
17 widgets::{Block, Borders, Paragraph},
18 Frame, Terminal,
19};
20use tui_term::widget::PseudoTerminal;
21use vt100::Screen;
22
23fn main() -> std::io::Result<()> {
24 let (mut terminal, size) = setup_terminal().unwrap();
25
26 let pty_system = NativePtySystem::default();
27 let cwd = std::env::current_dir().unwrap();
28 let mut cmd = CommandBuilder::new("ls");
29 cmd.cwd(cwd);
30
31 let pair = pty_system
32 .openpty(PtySize {
33 rows: size.rows,
34 cols: size.cols,
35 pixel_width: 0,
36 pixel_height: 0,
37 })
38 .unwrap();
39 let mut child = pair.slave.spawn_command(cmd).unwrap();
40 drop(pair.slave);
41
42 let mut reader = pair.master.try_clone_reader().unwrap();
43 let parser = Arc::new(RwLock::new(vt100::Parser::new(
44 size.rows - 1,
45 size.cols - 1,
46 0,
47 )));
48
49 {
50 let parser = parser.clone();
51 std::thread::spawn(move || {
52 let mut s = String::new();
54 reader.read_to_string(&mut s).unwrap();
55 if !s.is_empty() {
56 let mut parser = parser.write().unwrap();
57 parser.process(s.as_bytes());
58 }
59 });
60 }
61
62 {
63 let _writer = pair.master.take_writer().unwrap();
65 }
66
67 let _child_exit_status = child.wait().unwrap();
69
70 drop(pair.master);
71
72 run(&mut terminal, parser)?;
73
74 cleanup_terminal(&mut terminal).unwrap();
75 Ok(())
76}
77
78fn run<B: Backend>(
79 terminal: &mut Terminal<B>,
80 parser: Arc<RwLock<vt100::Parser>>,
81) -> 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
126fn setup_terminal() -> io::Result<(Terminal<CrosstermBackend<BufWriter<io::Stdout>>>, Size)> {
127 enable_raw_mode()?;
128 let stdout = io::stdout();
129 let backend = CrosstermBackend::new(BufWriter::new(stdout));
130 let mut terminal = Terminal::new(backend)?;
131 let initial_size = terminal.size()?;
132 let size = Size {
133 rows: initial_size.height,
134 cols: initial_size.width,
135 };
136 execute!(terminal.backend_mut(), EnterAlternateScreen)?;
137 Ok((terminal, size))
138}
139
140fn cleanup_terminal(
141 terminal: &mut Terminal<CrosstermBackend<BufWriter<io::Stdout>>>,
142) -> io::Result<()> {
143 execute!(terminal.backend_mut(), LeaveAlternateScreen)?;
144 disable_raw_mode()?;
145 terminal.show_cursor()?;
146 terminal.clear()?;
147 Ok(())
148}
149
150#[derive(Debug, Clone)]
151struct Size {
152 cols: u16,
153 rows: u16,
154}