1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
|
use std::io::{self, Read, Write, IsTerminal};
use std::os::unix::io::AsRawFd;
use termios::*;
struct ScopedRawMode {
fd: i32,
settings: Termios,
}
impl Drop for ScopedRawMode {
fn drop(&mut self) {
self.disable();
}
}
impl ScopedRawMode {
fn on_fd(fd: i32) -> Self {
let mut termios = Termios::from_fd(fd).unwrap();
let settings = termios.clone();
cfmakeraw(&mut termios);
tcsetattr(fd, TCSANOW, &termios).unwrap();
Self { fd, settings }
}
fn disable(&self) {
tcsetattr(self.fd, TCSANOW, &self.settings).unwrap();
}
}
macro_rules! print {
($($x:tt)*) => {{
write!(io::stdout(), $($x)*).unwrap();
io::stdout().flush().unwrap();
}}
}
fn main() {
let stdin = io::stdin();
let stdout = io::stdout();
if !stdin.is_terminal() {
println!("need to run in a tty");
return;
}
let fd = stdin.as_raw_fd();
let _scoped_raw = ScopedRawMode::on_fd(fd); // needs to be a var, gets dropped on scope exit,
// even if something panics
let mut stdin = stdin.lock();
let mut stdout = stdout.lock();
let mut buffer = [0u8; 1];
loop {
let Ok(_) = stdin.read_exact(&mut buffer) else {
break;
};
match buffer[0] {
// EOF
4 => {
break;
}
// Enter
b'\r' => {
println!("\r");
}
// Backspace (127 on most systems)
127 => {
write!(stdout, "\x08 \x08").unwrap();
stdout.flush().unwrap();
}
// Escape sequence
27 => {
let mut seq = [0u8; 2];
stdin.read_exact(&mut seq).unwrap();
if seq[0] == b'[' {
match seq[1] {
b'A' => print!("Up"),
b'B' => print!("Down"),
b'C' => print!("Right"),
b'D' => print!("Left"),
_ => {}
}
}
}
// Normal character
x => {
stdout.write(&[x]).unwrap();
stdout.flush().unwrap();
}
}
}
}
|