aboutsummaryrefslogtreecommitdiffstats
path: root/src/main.rs
blob: 7f372c1bd4abe1d56a4e0c6ec2371ce41fd81dfe (plain) (blame)
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();
            }
        }
    }
}