simple_term_renderer/
input.rs

1/*
2
3    MIT License
4    
5    Copyright (c) 2022 Siandfrance
6    
7    Permission is hereby granted, free of charge, to any person obtaining a copy
8    of this software and associated documentation files (the "Software"), to deal
9    in the Software without restriction, including without limitation the rights
10    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11    copies of the Software, and to permit persons to whom the Software is
12    furnished to do so, subject to the following conditions:
13    
14    The above copyright notice and this permission notice shall be included in all
15    copies or substantial portions of the Software.
16    
17    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23    SOFTWARE.
24
25*/
26
27// HEAVELY inspired by the termion library
28
29
30use std::io::{Error, ErrorKind};
31use std::str;
32
33// use std::{fs, io};
34use std::io::{Read, Write, stdin, stdout};
35
36use std::thread;
37use std::sync::mpsc;
38
39use crate::math::Vec2;
40
41
42#[derive(Debug, Clone, PartialEq, Eq, Hash)]
43pub enum InputEvent {
44    Key(KeyEvent),
45    Mouse(MouseEvent),
46    Unsupported(Vec<u8>)
47}
48
49
50#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
51pub enum KeyEvent {
52    Backspace,
53    Left,
54    Right,
55    Up,
56    Down,
57    Home,
58    End,
59    PageUp,
60    PageDown,
61    BackTab, // Shift + Tab
62    Delete,
63    Insert,
64    F(u8),   // Only some function keys are reachable
65    Char(char),
66    Alt(char),
67    Ctrl(char),
68    Null,
69    Esc
70}
71
72
73// TODO: add modifiers (shift, ctrl, meta) to MouseEvent
74#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
75pub enum MouseEvent {
76    ButtonPressed(MouseButton, Vec2),
77    ButtonReleased(MouseButton, Vec2),
78    Hold(MouseButton, Vec2)
79}
80
81
82#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
83pub enum MouseButton {
84    Left,
85    Right,
86    Middle,
87    WheelUp,
88    WheelDown
89}
90
91
92
93fn get_real_mouse_pos(cx: u16, cy: u16) -> Vec2 {
94    vec2!(cx as i32 - 1, 2 * (cy as i32) - 2)
95}
96
97
98/// Parse an Event from `item` and possibly subsequent bytes through `iter`.
99fn parse_event<I>(item: u8, iter: &mut I) -> Result<InputEvent, Error>
100    where I: Iterator<Item = Result<u8, Error>>
101{
102    let error = Error::new(ErrorKind::Other, "Could not parse an event");
103    match item {
104        b'\x1B' => {
105            // This is an escape character, leading a control sequence.
106            Ok(match iter.next() {
107                Some(Ok(b'O')) => {
108                    match iter.next() {
109                        // F1-F4
110                        Some(Ok(val @ b'P'..=b'S')) => InputEvent::Key(KeyEvent::F(1 + val - b'P')),
111                        _ => return Err(error),
112                    }
113                }
114                Some(Ok(b'[')) => {
115                    // This is a CSI sequence.
116                    parse_csi(iter).ok_or(error)?
117                }
118                Some(Ok(c)) => {
119                    let ch = parse_utf8_char(c, iter)?;
120                    InputEvent::Key(KeyEvent::Alt(ch))
121                }
122                Some(Err(_)) | None => return Err(error),
123            })
124        }
125        b'\n' | b'\r' => Ok(InputEvent::Key(KeyEvent::Char('\n'))),
126        b'\t' => Ok(InputEvent::Key(KeyEvent::Char('\t'))),
127        b'\x7F' => Ok(InputEvent::Key(KeyEvent::Backspace)),
128        c @ b'\x01'..=b'\x1A' => Ok(InputEvent::Key(KeyEvent::Ctrl((c as u8 - 0x1 + b'a') as char))),
129        c @ b'\x1C'..=b'\x1F' => Ok(InputEvent::Key(KeyEvent::Ctrl((c as u8 - 0x1C + b'4') as char))),
130        b'\0' => Ok(InputEvent::Key(KeyEvent::Null)),
131        c => {
132            Ok({
133                let ch = parse_utf8_char(c, iter)?;
134                InputEvent::Key(KeyEvent::Char(ch))
135            })
136        }
137    }
138}
139
140
141/// Parses a CSI sequence, just after reading ^[
142///
143/// Returns None if an unrecognized sequence is found.
144fn parse_csi<I>(iter: &mut I) -> Option<InputEvent>
145    where I: Iterator<Item = Result<u8, Error>>
146{
147    Some(match iter.next() {
148        Some(Ok(b'[')) => match iter.next() {
149            Some(Ok(val @ b'A'..=b'E')) => InputEvent::Key(KeyEvent::F(1 + val - b'A')),
150            _ => return None,
151        },
152        Some(Ok(b'D')) => InputEvent::Key(KeyEvent::Left),
153        Some(Ok(b'C')) => InputEvent::Key(KeyEvent::Right),
154        Some(Ok(b'A')) => InputEvent::Key(KeyEvent::Up),
155        Some(Ok(b'B')) => InputEvent::Key(KeyEvent::Down),
156        Some(Ok(b'H')) => InputEvent::Key(KeyEvent::Home),
157        Some(Ok(b'F')) => InputEvent::Key(KeyEvent::End),
158        Some(Ok(b'Z')) => InputEvent::Key(KeyEvent::BackTab),
159        Some(Ok(b'M')) => {
160            // X10 emulation mouse encoding: ESC [ CB Cx Cy (6 characters only).
161            let mut next = || iter.next().unwrap().unwrap();
162
163            let cb = next() as i8 - 32;
164            // (0, 0) are the coords for upper left.
165            let cx = next().saturating_sub(32) as u16;
166            let cy = next().saturating_sub(32) as u16;
167            InputEvent::Mouse(match cb & 0b11 {
168                0 => {
169                    if cb & 0x40 != 0 {
170                        MouseEvent::ButtonPressed(MouseButton::WheelUp, get_real_mouse_pos(cx, cy))
171                    } else {
172                        MouseEvent::ButtonPressed(MouseButton::Left, get_real_mouse_pos(cx, cy))
173                    }
174                }
175                1 => {
176                    if cb & 0x40 != 0 {
177                        MouseEvent::ButtonPressed(MouseButton::WheelDown, get_real_mouse_pos(cx, cy))
178                    } else {
179                        MouseEvent::ButtonPressed(MouseButton::Middle, get_real_mouse_pos(cx, cy))
180                    }
181                }
182                2 => MouseEvent::ButtonPressed(MouseButton::Right, get_real_mouse_pos(cx, cy)),
183                // default to Left button, will be modified down the line
184                3 => MouseEvent::ButtonReleased(MouseButton::Left, get_real_mouse_pos(cx, cy)),
185                _ => return None,
186            })
187        }
188        Some(Ok(b'<')) => {
189            // xterm mouse encoding:
190            // ESC [ < Cb ; Cx ; Cy (;) (M or m)
191            let mut buf = Vec::new();
192            let mut c = iter.next().unwrap().unwrap();
193            while match c {
194                      b'm' | b'M' => false,
195                      _ => true,
196                  } {
197                buf.push(c);
198                c = iter.next().unwrap().unwrap();
199            }
200            let str_buf = String::from_utf8(buf).unwrap();
201            let nums = &mut str_buf.split(';');
202
203            let cb = nums.next()
204                .unwrap()
205                .parse::<u16>()
206                .unwrap();
207            let cx = nums.next()
208                .unwrap()
209                .parse::<u16>()
210                .unwrap();
211            let cy = nums.next()
212                .unwrap()
213                .parse::<u16>()
214                .unwrap();
215
216            let event = match cb {
217                0..=2 | 64..=65 => {
218                    let button = match cb {
219                        0 => MouseButton::Left,
220                        1 => MouseButton::Middle,
221                        2 => MouseButton::Right,
222                        64 => MouseButton::WheelUp,
223                        65 => MouseButton::WheelDown,
224                        _ => unreachable!(),
225                    };
226                    match c {
227                        b'M' => MouseEvent::ButtonPressed(button, get_real_mouse_pos(cx, cy)),
228                        // default to Left button, will be modified down the line
229                        b'm' => MouseEvent::ButtonReleased(MouseButton::Left, get_real_mouse_pos(cx, cy)),
230                        _ => return None,
231                    }
232                }
233                // default to Left button, will be modified down the line
234                32 => MouseEvent::Hold(MouseButton::Left, get_real_mouse_pos(cx, cy)),
235                // default to Left button, will be modified down the line
236                3 => MouseEvent::ButtonReleased(MouseButton::Left, get_real_mouse_pos(cx, cy)),
237                _ => return None,
238            };
239
240            InputEvent::Mouse(event)
241        }
242        Some(Ok(c @ b'0'..=b'9')) => {
243            // Numbered escape code.
244            let mut buf = Vec::new();
245            buf.push(c);
246            let mut c = iter.next().unwrap().unwrap();
247            // The final byte of a CSI sequence can be in the range 64-126, so
248            // let's keep reading anything else.
249            while c < 64 || c > 126 {
250                buf.push(c);
251                c = iter.next().unwrap().unwrap();
252            }
253
254            match c {
255                // rxvt mouse encoding:
256                // ESC [ Cb ; Cx ; Cy ; M
257                b'M' => {
258                    let str_buf = String::from_utf8(buf).unwrap();
259
260                    let nums: Vec<u16> = str_buf.split(';').map(|n| n.parse().unwrap()).collect();
261
262                    let cb = nums[0];
263                    let cx = nums[1];
264                    let cy = nums[2];
265
266                    let event = match cb {
267                        32 => MouseEvent::ButtonPressed(MouseButton::Left, get_real_mouse_pos(cx, cy)),
268                        33 => MouseEvent::ButtonPressed(MouseButton::Middle, get_real_mouse_pos(cx, cy)),
269                        34 => MouseEvent::ButtonPressed(MouseButton::Right, get_real_mouse_pos(cx, cy)),
270                        // default to Left button, will be modified down the line
271                        35 => MouseEvent::ButtonReleased(MouseButton::Left, get_real_mouse_pos(cx, cy)),
272                        // default to Left button, will be modified down the line
273                        64 => MouseEvent::Hold(MouseButton::Left, get_real_mouse_pos(cx, cy)),
274                        96 | 97 => MouseEvent::ButtonPressed(MouseButton::WheelUp, get_real_mouse_pos(cx, cy)),
275                        _ => return None,
276                    };
277
278                    InputEvent::Mouse(event)
279                }
280                // Special key code.
281                b'~' => {
282                    let str_buf = String::from_utf8(buf).unwrap();
283
284                    // This CSI sequence can be a list of semicolon-separated
285                    // numbers.
286                    let nums: Vec<u8> = str_buf.split(';').map(|n| n.parse().unwrap()).collect();
287
288                    if nums.is_empty() {
289                        return None;
290                    }
291
292                    // TODO: handle multiple values for key modififiers (ex: values
293                    // [3, 2] means Shift+Delete)
294                    if nums.len() > 1 {
295                        return None;
296                    }
297
298                    match nums[0] {
299                        1 | 7 => InputEvent::Key(KeyEvent::Home),
300                        2 => InputEvent::Key(KeyEvent::Insert),
301                        3 => InputEvent::Key(KeyEvent::Delete),
302                        4 | 8 => InputEvent::Key(KeyEvent::End),
303                        5 => InputEvent::Key(KeyEvent::PageUp),
304                        6 => InputEvent::Key(KeyEvent::PageDown),
305                        v @ 11..=15 => InputEvent::Key(KeyEvent::F(v - 10)),
306                        v @ 17..=21 => InputEvent::Key(KeyEvent::F(v - 11)),
307                        v @ 23..=24 => InputEvent::Key(KeyEvent::F(v - 12)),
308                        _ => return None,
309                    }
310                }
311                _ => return None,
312            }
313        }
314        _ => return None,
315    })
316}
317
318
319/// Parse `c` as either a single byte ASCII char or a variable size UTF-8 char.
320fn parse_utf8_char<I>(c: u8, iter: &mut I) -> Result<char, Error>
321    where I: Iterator<Item = Result<u8, Error>>
322{
323    let error = Err(Error::new(ErrorKind::Other, "Input character is not valid UTF-8"));
324    if c.is_ascii() {
325        Ok(c as char)
326    } else {
327        let bytes = &mut Vec::new();
328        bytes.push(c);
329
330        loop {
331            match iter.next() {
332                Some(Ok(next)) => {
333                    bytes.push(next);
334                    if let Ok(st) = str::from_utf8(bytes) {
335                        return Ok(st.chars().next().unwrap());
336                    }
337                    if bytes.len() >= 4 {
338                        return error;
339                    }
340                }
341                _ => return error,
342            }
343        }
344    }
345}
346
347
348/// Input Server Singleton instance
349static mut INPUT_SERVER: Option<Input> = None;
350
351
352/// The Input is a singleton that handles async io operations
353/// 
354/// # Usage
355/// 
356/// To get events from keyboard and mouse, there are two functions:
357/// 
358/// get_event - returns Some(InputEvent) or None depending on weather there was an input
359/// 
360/// get_event_blocking - waits for an event and returns it
361/// 
362/// # Mouse
363/// 
364/// To have mouse input or not use enable_mouse or disable_mouse
365/// by default, there is no mouse input
366pub struct Input {
367    _server_handle: Option<thread::JoinHandle<()>>,
368    input_recv: mpsc::Receiver<InputEvent>
369}
370
371
372impl Input {
373
374    /// Creates the Input singleton, will only be called once
375    fn init() -> Self {
376        let (input_send, input_recv) = mpsc::channel();
377
378        let handle = thread::spawn(move || {
379            let mut mb = MouseButton::Left;
380            loop {
381                let mut stdin = stdin().bytes();
382
383                if let Some(Ok(item)) = stdin.next() {
384                    match parse_event(item, &mut stdin) {
385                        Ok(evt) => {
386                            let event = match evt {
387                                InputEvent::Mouse(MouseEvent::ButtonPressed(button, _)) => {
388                                    mb = button;
389                                    evt
390                                }
391                                InputEvent::Mouse(MouseEvent::ButtonReleased(_, pos)) =>
392                                    InputEvent::Mouse(MouseEvent::ButtonReleased(mb, pos)),
393                                InputEvent::Mouse(MouseEvent::Hold(_, pos)) =>
394                                    InputEvent::Mouse(MouseEvent::Hold(mb, pos)),
395                                _ => evt
396                            };
397                            input_send.send(event).expect("input recv dropped")
398                        }
399                        Err(_) => {}
400                    }
401                };
402            }
403        });
404
405        Self {
406            _server_handle: Some(handle),
407            input_recv: input_recv
408        }
409    }
410
411
412    /// Returns the Input singleton.
413    /// If no call to Input::get() is made, the server never starts;
414    /// this can be usefull when custom input handling is needed.
415    pub fn get() -> &'static mut Input {
416        unsafe {
417            match &mut INPUT_SERVER {
418                None => {
419                    INPUT_SERVER = Some(Input::init());
420                    Input::get()
421                },
422                Some(i) => i
423            }
424        }
425    }
426
427
428    /// If there was an event, return it.
429    /// Never blocks the current thread.
430    pub fn get_event(&mut self) -> Option<InputEvent> {
431        self.input_recv.try_recv().ok()
432    }
433
434
435    /// Wait for an InputEvent to occur and return it.
436    pub fn get_event_blocking(&mut self) -> InputEvent {
437        self.input_recv.recv().ok().expect("Input thread was killed")
438    }
439
440
441    /// Enable MouseEvent.
442    pub fn enable_mouse() {
443        print!("\x1b[?1000h\x1b[?1002h\x1b[?1015h\x1b[?1006h");
444        stdout().flush().expect("Could not write to stdout");
445    }
446
447
448    /// Disable MouseEvent.
449    pub fn disable_mouse() {
450        print!("\x1b[?1006l\x1b[?1015l\x1b[?1002l\x1b[?1000l");
451        stdout().flush().expect("Could not write to stdout");
452    }
453}