1extern crate nix;
26extern crate termios;
27
28use std::io::{self, Read, Write};
29use std::os::unix::io::RawFd;
30
31const STDIN_FILENO: RawFd = 0;
32
33#[derive(std::fmt::Debug, std::cmp::PartialEq)]
37pub enum InputEvent {
38 Key(String),
39 Ctrl(u8),
40 Enter,
41 CarriageReturn,
42 Backspace,
43 ArrowUp,
44 ArrowLeft,
45 ArrowRight,
46 ArrowDown
47}
48
49
50pub fn backspace() {
54 print(String::from("\x08 \x08"));
56}
57
58pub fn move_cursor_right() {
59 print(String::from("\x1b[1C"));
60}
61
62pub fn move_cursor_left() {
63 print(String::from("\x1b[1D"));
64}
65
66pub fn carriage_return() {
70 print(String::from("\r"));
71}
72
73pub fn clear() {
77 print(String::from("\x1b[H\x1b[2J"));
78}
79
80pub fn read() -> Option<InputEvent> {
84 let stdin_read = |buff: &mut [u8]| -> io::Result<()> {
85 io::stdin().read_exact(buff)
86 };
87 prepare_termios();
88 let ev: Option<InputEvent> = to_input_event(&input_ready, &stdin_read);
89 reset_termios();
90 ev
91}
92
93fn to_input_event(ready_fn: &dyn Fn() -> bool, read_fn: &dyn Fn(&mut [u8]) -> io::Result<()>) -> Option<InputEvent> {
97 match ready_fn() {
99 false => None,
100 true => {
101 let mut buf: Vec<u8> = vec![0u8; 1];
103 let _ = read_fn(&mut buf);
104 let key: u8 = *buf.get(0).unwrap_or(&0);
106 let ev: InputEvent = match key {
107 8 | 127 => InputEvent::Backspace,
108 10 => InputEvent::Enter,
109 13 => InputEvent::CarriageReturn,
110 0..=26 => InputEvent::Ctrl(key), 27 => { let _ = read_fn(&mut buf);
114 let _ = read_fn(&mut buf);
115 let direction: char = *buf.get(0).unwrap_or(&0) as char;
116 match direction {
117 'A' => InputEvent::ArrowUp,
118 'B' => InputEvent::ArrowDown,
119 'C' => InputEvent::ArrowRight,
120 'D' => InputEvent::ArrowLeft,
121 _ => return None }
123 },
124 _ => { let mut utfbuffer: [u8; 4] = [0; 4];
128 let mut buff_index: usize = 0;
129 let mut keystr: Option<String> = None;
130 loop {
131 if buff_index >= 4 { break
134 }
135 utfbuffer[buff_index] = *buf.get(0).unwrap_or(&0);
136 buff_index += 1;
137 match std::str::from_utf8(&utfbuffer[0..buff_index]) { Ok(key) => {
140 keystr = Some(String::from(key));
141 break
142 },
143 Err(_) => { if let Err(_) = read_fn(&mut buf) {
145 break
146 }
147 continue
148 }
149 };
150 }
151 match keystr {
152 Some(s) => InputEvent::Key(s),
153 None => return None }
155 }
156 };
157 Some(ev)
158 }
159 }
160}
161
162pub fn rewrite(row: String, len: usize) {
166 for _ in 0..len {
167 backspace();
168 }
169 print(row);
170}
171
172pub fn print(row: String) {
176 print!("{}", row);
177 let _ = io::stdout().flush();
178}
179
180pub fn println(row: String) {
184 println!("{}", row);
185}
186
187fn input_ready() -> bool {
191 prepare_termios();
192 let mut poll_fds: [nix::poll::PollFd; 1] = [nix::poll::PollFd::new(STDIN_FILENO, nix::poll::PollFlags::POLLIN | nix::poll::PollFlags::POLLRDBAND | nix::poll::PollFlags::POLLHUP)];
193 let ready: bool = match nix::poll::poll(&mut poll_fds, 100) {
194 Ok(ret) => {
195 if ret > 0 && poll_fds[0].revents().is_some() { let event: nix::poll::PollFlags = poll_fds[0].revents().unwrap();
197 if event.intersects(nix::poll::PollFlags::POLLIN) || event.intersects(nix::poll::PollFlags::POLLRDBAND) {
198 true
199 } else {
200 false
201 }
202 } else {
203 false
204 }
205 },
206 Err(_) => false
207 };
208 reset_termios();
209 ready
210}
211
212fn prepare_termios() {
216 let mut term = termios::Termios::from_fd(STDIN_FILENO).unwrap();
217 let _ = termios::tcgetattr(STDIN_FILENO, &mut term);
218 term.c_lflag &= !termios::ICANON;
219 term.c_lflag &= !termios::ECHO;
220 let _ = termios::tcsetattr(STDIN_FILENO, termios::TCSANOW, &term);
221}
222
223fn reset_termios() {
227 let mut term = termios::Termios::from_fd(STDIN_FILENO).unwrap();
228 let _ = termios::tcgetattr(STDIN_FILENO, &mut term);
229 term.c_lflag |= termios::ICANON;
230 term.c_lflag &= termios::ECHO;
231 let _ = termios::tcsetattr(STDIN_FILENO, termios::TCSADRAIN, &term);
232}
233
234pub fn input_event_to_string(ev: InputEvent) -> String {
238 match ev {
239 InputEvent::ArrowDown => String::from("\x1b[B"),
240 InputEvent::ArrowLeft => String::from("\x1b[D"),
241 InputEvent::ArrowRight => String::from("\x1b[C"),
242 InputEvent::ArrowUp => String::from("\x1b[A"),
243 InputEvent::Backspace => String::from("\x7F"),
244 InputEvent::CarriageReturn => String::from("\x0D"),
245 InputEvent::Ctrl(sig) => {
246 let ch = sig as char;
247 let mut s = String::new();
248 s.push(ch);
249 s
250 },
251 InputEvent::Enter => String::from("\x0A"),
252 InputEvent::Key(k) => String::from(k)
253 }
254}
255
256#[cfg(test)]
257mod tests {
258
259 use super::*;
260
261 #[test]
262 fn test_utils_console_backspace() {
263 backspace();
264 }
265
266 #[test]
267 fn test_utils_console_move_cursor() {
268 move_cursor_left();
269 move_cursor_right();
270 carriage_return();
271 }
272
273 #[test]
274 fn test_utils_console_clear() {
275 clear();
276 }
277
278 #[test]
279 fn test_utils_console_print() {
280 print(String::from("foo"));
281 rewrite(String::from("Foobar"), 3);
282 println(String::from("bar"));
283 }
284
285 #[test]
286 fn test_utils_console_input_ready() {
287 assert_eq!(input_ready(), false);
288 }
289
290 #[test]
291 fn test_utils_console_termios() {
292 prepare_termios();
293 reset_termios();
294 }
295
296 #[test]
297 fn test_utils_console_read() {
298 assert!(read().is_none());
299 let ready_fn = || -> bool {
301 false
302 };
303 let read_fn = |_buff: &mut [u8]| -> io::Result<()> {
304 Ok(())
305 };
306 assert!(to_input_event(&ready_fn, &read_fn).is_none());
307 let ready_fn = || -> bool {
309 true
310 };
311 let read_fn = |buff: &mut [u8]| -> io::Result<()> {
312 buff[0] = 127;
313 Ok(())
314 };
315 assert_eq!(to_input_event(&ready_fn, &read_fn).unwrap(), InputEvent::Backspace);
316 let read_fn = |buff: &mut [u8]| -> io::Result<()> {
317 buff[0] = 8;
318 Ok(())
319 };
320 assert_eq!(to_input_event(&ready_fn, &read_fn).unwrap(), InputEvent::Backspace);
321 let read_fn = |buff: &mut [u8]| -> io::Result<()> {
323 buff[0] = 10;
324 Ok(())
325 };
326 assert_eq!(to_input_event(&ready_fn, &read_fn).unwrap(), InputEvent::Enter);
327 let read_fn = |buff: &mut [u8]| -> io::Result<()> {
329 buff[0] = 13;
330 Ok(())
331 };
332 assert_eq!(to_input_event(&ready_fn, &read_fn).unwrap(), InputEvent::CarriageReturn);
333 let read_fn = |buff: &mut [u8]| -> io::Result<()> {
335 buff[0] = 3;
336 Ok(())
337 };
338 assert_eq!(to_input_event(&ready_fn, &read_fn).unwrap(), InputEvent::Ctrl(3));
339 let read_fn = |buff: &mut [u8]| -> io::Result<()> {
341 let curr_value: u8 = buff[0];
342 match curr_value {
343 91 => buff[0] = 'A' as u8,
344 27 => buff[0] = 91,
345 _ => buff[0] = 27
346 }
347 Ok(())
348 };
349 assert_eq!(to_input_event(&ready_fn, &read_fn).unwrap(), InputEvent::ArrowUp);
350 let read_fn = |buff: &mut [u8]| -> io::Result<()> {
351 let curr_value: u8 = buff[0];
352 match curr_value {
353 91 => buff[0] = 'B' as u8,
354 27 => buff[0] = 91,
355 _ => buff[0] = 27
356 }
357 Ok(())
358 };
359 assert_eq!(to_input_event(&ready_fn, &read_fn).unwrap(), InputEvent::ArrowDown);
360 let read_fn = |buff: &mut [u8]| -> io::Result<()> {
361 let curr_value: u8 = buff[0];
362 match curr_value {
363 91 => buff[0] = 'C' as u8,
364 27 => buff[0] = 91,
365 _ => buff[0] = 27
366 }
367 Ok(())
368 };
369 assert_eq!(to_input_event(&ready_fn, &read_fn).unwrap(), InputEvent::ArrowRight);
370 let read_fn = |buff: &mut [u8]| -> io::Result<()> {
371 let curr_value: u8 = buff[0];
372 match curr_value {
373 91 => buff[0] = 'D' as u8,
374 27 => buff[0] = 91,
375 _ => buff[0] = 27
376 }
377 Ok(())
378 };
379 assert_eq!(to_input_event(&ready_fn, &read_fn).unwrap(), InputEvent::ArrowLeft);
380 let read_fn = |buff: &mut [u8]| -> io::Result<()> {
382 let curr_value: u8 = buff[0];
383 match curr_value {
384 91 => buff[0] = 'E' as u8,
385 27 => buff[0] = 91,
386 _ => buff[0] = 27
387 }
388 Ok(())
389 };
390 assert!(to_input_event(&ready_fn, &read_fn).is_none());
391 let read_fn = |buff: &mut [u8]| -> io::Result<()> {
393 buff[0] = 'A' as u8;
394 Ok(())
395 };
396 assert_eq!(to_input_event(&ready_fn, &read_fn).unwrap(), InputEvent::Key(String::from("A")));
397 let read_fn = |buff: &mut [u8]| -> io::Result<()> {
399 let curr_value: u8 = buff[0];
400 match curr_value {
401 0xd0 => buff[0] = 0xbf,
402 _ => buff[0] = 0xd0
403 }
404 Ok(())
405 };
406 assert_eq!(to_input_event(&ready_fn, &read_fn).unwrap(), InputEvent::Key(String::from("п")));
407 let read_fn = |buff: &mut [u8]| -> io::Result<()> {
409 let curr_value: u8 = buff[0];
410 match curr_value {
411 0x98 => buff[0] = 0x82,
412 0x9f => buff[0] = 0x98,
413 0xf0 => buff[0] = 0x9f,
414 _ => buff[0] = 0xf0
415 }
416 Ok(())
417 };
418 assert_eq!(to_input_event(&ready_fn, &read_fn).unwrap(), InputEvent::Key(String::from("😂")));
419 let read_fn = |buff: &mut [u8]| -> io::Result<()> {
421 let curr_value: u8 = buff[0];
422 match curr_value {
423 0x98 => buff[0] = 0xff,
424 0x9f => buff[0] = 0x98,
425 0xf0 => buff[0] = 0x9f,
426 _ => buff[0] = 0xf0
427 }
428 Ok(())
429 };
430 assert!(to_input_event(&ready_fn, &read_fn).is_none());
431 }
432
433 #[test]
434 fn test_utils_console_input_event_to_str() {
435 assert_eq!(input_event_to_string(InputEvent::ArrowDown), String::from("\x1b[B"));
436 assert_eq!(input_event_to_string(InputEvent::ArrowLeft), String::from("\x1b[D"));
437 assert_eq!(input_event_to_string(InputEvent::ArrowRight), String::from("\x1b[C"));
438 assert_eq!(input_event_to_string(InputEvent::ArrowUp), String::from("\x1b[A"));
439 assert_eq!(input_event_to_string(InputEvent::Backspace), String::from("\x7F"));
440 assert_eq!(input_event_to_string(InputEvent::CarriageReturn), String::from("\x0D"));
441 assert_eq!(input_event_to_string(InputEvent::Ctrl(3)), String::from("\x03"));
442 assert_eq!(input_event_to_string(InputEvent::Enter), String::from("\x0A"));
443 assert_eq!(input_event_to_string(InputEvent::Key(String::from("A"))), String::from("A"));
444 }
445
446}