summaryrefslogtreecommitdiffstats
path: root/sndplay/src/command.rs
blob: 3f5ecdeb3cdebbb48161c268da4eb2b1df4740a2 (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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
use libc::{termios, tcgetattr, tcsetattr};
use std::sync::mpsc;
use std::io::{Read, BufRead};
use std::thread;
use nihav_core::frame::NATimePoint;

#[derive(Clone,Copy,Debug,PartialEq)]
pub enum Command {
    Debug,
    Mute,
    VolumeUp,
    VolumeDown,
    Pause,
    Back(u8),
    Forward(u8),
    Seek(u64),
    Repeat,
    Next,
    PauseDisplay,
    ResumeDisplay,
    Quit,
}

pub struct CmdLineState {
    orig_state: termios
}
impl CmdLineState {
    pub fn new() -> Self {
        let mut orig_state: termios = unsafe { std::mem::uninitialized() };
        unsafe { tcgetattr(0, &mut orig_state); }
        let mut new_state = orig_state;
        new_state.c_lflag &= !(libc::ECHO | libc::ICANON);
        unsafe { tcsetattr(0, 0, &new_state); }
        Self { orig_state }
    }
    pub fn new_normal() -> Self {
        let mut orig_state: termios = unsafe { std::mem::uninitialized() };
        unsafe { tcgetattr(0, &mut orig_state); }
        let mut new_state = orig_state;
        new_state.c_lflag |= libc::ECHO | libc::ICANON;
        unsafe { tcsetattr(0, 0, &new_state); }
        Self { orig_state }
    }
    pub fn restore(&self) {
        unsafe { tcsetattr(0, 0, &self.orig_state); }
    }
}

pub fn start_reader() -> (thread::JoinHandle<()>, mpsc::Receiver<Command>) {
    let (sender, cmd_receiver) = mpsc::sync_channel(100);
    (thread::spawn(move || {
        let stdin = std::io::stdin();
        let mut file = stdin.lock();
        let mut ch = [0u8; 8];
        loop {
/*
\e char -> alt-char (including alt-[)
\e [ char -> A - up B - down C - right D - left F - end H - home (or \e[OF/OH)
\e [ O P-S -> F1-F4
\e [ num ~ -> 5 - PgUp, 6 - PgDn, 15, 17-24 - F5,F6-F12
*/
            match file.read(&mut ch) {
                Ok(1) => {
                    match ch[0] {
                        b'\n' => { sender.send(Command::Next).unwrap(); },
                        b' ' => { sender.send(Command::Pause).unwrap(); },
                        b'q' | b'Q' | 0o33 => {
                            sender.send(Command::Quit).unwrap();
                            break;
                        },
                        b'+' => { sender.send(Command::VolumeUp).unwrap(); },
                        b'-' => { sender.send(Command::VolumeDown).unwrap(); },
                        b'd' | b'D' => { sender.send(Command::Debug).unwrap(); },
                        b'm' | b'M' => { sender.send(Command::Mute).unwrap(); },
                        b'j' | b'J' => {
                            sender.send(Command::PauseDisplay).unwrap();
                            let cstate = CmdLineState::new_normal();
                            // wait so that the main thread stops displaying
                            thread::sleep(std::time::Duration::from_millis(500));
                            print!("\nJump to: ");
                            let mut str = String::new();
                            let ret = file.read_line(&mut str);
                            cstate.restore();
                            sender.send(Command::ResumeDisplay).unwrap();

                            if ret.is_ok() && str.len() > 1 {
                                str.pop(); // newline
                                if let Ok(NATimePoint::Milliseconds(time)) = str.parse::<NATimePoint>() {
                                    sender.send(Command::Seek(time)).unwrap();
                                } else {
                                    println!("wrong time");
                                }
                            }
                        },
                        _ => {},
                    };
                },
                Ok(3) => {
                    if ch[0] == 0o33 {
                        if ch[1] == b'[' {
                            match ch[2] {
                                b'D' => { sender.send(Command::Back(1)).unwrap(); },
                                b'B' => { sender.send(Command::Back(2)).unwrap(); },
                                b'C' => { sender.send(Command::Forward(1)).unwrap(); },
                                b'A' => { sender.send(Command::Forward(2)).unwrap(); },
                                b'F' => { sender.send(Command::Repeat).unwrap(); },
                                b'H' => { sender.send(Command::Next).unwrap(); },
                                _ => {},
                            };
                        } else if ch[1] == b'O' {
                            match ch[2] {
                                b'F' => { sender.send(Command::Repeat).unwrap(); },
                                b'H' => { sender.send(Command::Next).unwrap(); },
                                _ => {},
                            };
                        }
                    }
                },
                Ok(4) => {
                    if ch[0] == 0o33 && ch[1] == b'[' && ch[3] == b'~' {
                        match ch[2] {
                            b'5' => { sender.send(Command::Forward(3)).unwrap(); },
                            b'6' => { sender.send(Command::Back(3)).unwrap(); },
                            _ => {},
                        };
                    }
                },
                Ok(_) => {},
                Err(_) => break,
            }
        }
    }), cmd_receiver)
}