ctf_pwn/io/stdio/
ncurses_bridge.rs1use crate::io::stdio::{is_stop_terminal, is_terminate_process, TerminalBridge, TerminalResult};
2use crate::io::{ansi, TerminalError};
3use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
4use crossterm::*;
5use std::io::ErrorKind::TimedOut;
6use std::io::stdout;
7use std::sync::atomic::{AtomicBool, Ordering};
8use std::sync::Arc;
9use std::time::Duration;
10use crossterm::terminal::*;
11use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
12use tokio::join;
13use tokio::task::JoinError;
14
15pub struct NcursesTerminalBridge {}
16
17async fn read_task<R>(reader: &mut R, stop_signal: Arc<AtomicBool>) -> TerminalResult<()>
18where
19 R: AsyncRead + Send + Unpin,
20{
21 let mut buffer = [0; 1024];
22 loop {
23 if stop_signal.load(Ordering::SeqCst) {
24 break;
25 }
26
27 let n = match reader.read(&mut buffer).await {
28 Ok(n) if n == 0 => break, Ok(n) => n,
30 Err(e) if e.kind() == TimedOut => continue,
31 Err(e) => return Err(e.into()),
32 };
33
34 let mut stdout = tokio::io::stdout();
35 terminal::disable_raw_mode().unwrap();
36 stdout.write_all(&buffer[..n]).await?;
37 stdout.flush().await?;
38 terminal::enable_raw_mode().unwrap();
39 }
40 Ok(())
41}
42
43async fn write_ansi_command<W: AsyncWrite + Send + Unpin, T: Command>(
44 writer: &mut W,
45 command: T,
46) -> Result<usize, TerminalError> {
47 let mut ansi_command = String::new();
48 command.write_ansi(&mut ansi_command)?;
49 let size = writer.write(ansi_command.as_bytes()).await?;
50 Ok(size)
51}
52
53async fn send_key<W>(
54 writer: &mut W,
55 stop_signal: Arc<AtomicBool>,
56 key_event: KeyEvent,
57) -> TerminalResult<()>
58where
59 W: AsyncWrite + Send + Unpin,
60{
61 if is_terminate_process(key_event) {
62 return Err(TerminalError::Terminate);
63 }
64
65 if is_stop_terminal(key_event) {
66 stop_signal.store(true, Ordering::SeqCst);
67 return Ok(());
68 }
69
70 if key_event.kind != KeyEventKind::Press && key_event.kind != KeyEventKind::Repeat {
71 return Ok(());
72 }
73
74 match key_event.code {
75 KeyCode::Char(c) => {
76 let mut buf = [0; 4]; let bytes = c.encode_utf8(&mut buf);
78 writer.write_all(bytes.as_bytes()).await?;
79 }
80 KeyCode::Left => {
81 write_ansi_command(writer, ansi::Left).await?;
82 }
83 KeyCode::Right => {
84 write_ansi_command(writer, ansi::Right).await?;
85 }
86 KeyCode::Up => {
87 write_ansi_command(writer, ansi::Up).await?;
88 }
89 KeyCode::Down => {
90 write_ansi_command(writer, ansi::Down).await?;
91 }
92 KeyCode::Delete => {
93 write_ansi_command(writer, ansi::Del).await?;
94 }
95 KeyCode::Backspace => {
96 write_ansi_command(writer, ansi::Backspace).await?;
97 }
98 KeyCode::Esc => {
99 write_ansi_command(writer, ansi::Esc).await?;
100 }
101 KeyCode::Enter => {
102 write_ansi_command(writer, ansi::Enter).await?;
103 }
104 KeyCode::Home => {
105 write_ansi_command(writer, ansi::Home).await?;
106 }
107 KeyCode::End => {
108 write_ansi_command(writer, ansi::End).await?;
109 }
110 KeyCode::PageUp => {
111 write_ansi_command(writer, ansi::PgUp).await?;
112 }
113 KeyCode::PageDown => {
114 write_ansi_command(writer, ansi::PgDn).await?;
115 }
116 KeyCode::F(no) => {
117 write_ansi_command(writer, ansi::F(no)).await?;
118 }
119 _ => {}
120 };
121
122 writer.flush().await?;
123
124 Ok(())
125}
126
127async fn write_task<W>(writer: &mut W, stop_signal: Arc<AtomicBool>) -> TerminalResult<()>
128where
129 W: AsyncWrite + Send + Unpin,
130{
131 loop {
132 if stop_signal.load(Ordering::SeqCst) {
133 return Ok(());
134 }
135
136 if let Ok(true) = event::poll(Duration::from_millis(0)) {
137 match event::read() {
138 Ok(event::Event::Key(key_event)) => {
139 send_key(writer, stop_signal.clone(), key_event).await?;
140 }
141 _ => {}
142 }
143 }
144 }
145}
146
147impl TerminalBridge for NcursesTerminalBridge {
148 async fn bridge<R: AsyncRead + Send + Unpin, W: AsyncWrite + Send + Unpin>(
149 reader: &mut R,
150 writer: &mut W,
151 ) {
152 let reader_ptr = reader as *mut R as usize;
153 let writer_ptr = writer as *mut W as usize;
154
155 execute!(stdout(), EnterAlternateScreen).unwrap();
156 terminal::enable_raw_mode().unwrap();
157
158 let stop_signal = Arc::new(AtomicBool::new(false));
159 let read_stop_signal = stop_signal.clone();
160
161 let reader_task = tokio::spawn(async move {
162 let reader_ptr = reader_ptr as *mut R;
163 let reader = unsafe { &mut *reader_ptr };
164 let res = read_task(reader, read_stop_signal.clone()).await;
165 read_stop_signal.store(true, Ordering::SeqCst);
166 res
167 });
168
169 let writer_task = tokio::spawn(async move {
170 let writer_ptr = writer_ptr as *mut W;
171 let writer = unsafe { &mut *writer_ptr };
172 let res = write_task(writer, stop_signal.clone()).await;
173 stop_signal.store(true, Ordering::SeqCst);
174 res
175 });
176
177 let (read_res, write_res) = join!(reader_task, writer_task);
178
179 terminal::disable_raw_mode().unwrap();
180
181 execute!(stdout(), LeaveAlternateScreen).unwrap();
182
183 if let Ok(Err(TerminalError::Terminate)) = read_res
184 {
185 std::process::exit(130); }
187 else if let Ok(Err(TerminalError::Terminate)) = write_res
188 {
189 std::process::exit(130); }
191 }
192}