use libc::{termios, winsize}; use std::io::{Read, Stdin}; pub fn size() -> Option { unsafe { let mut window: winsize = std::mem::zeroed(); if libc::ioctl(1, libc::TIOCGWINSZ, &mut window) < 0 { return None; } return Some(window); } } fn get_attr() -> Option { unsafe { let mut attr: termios = std::mem::zeroed(); if libc::tcgetattr(1, &mut attr) < 0 { return None; } return Some(attr); } } fn set_attr(attrs: &termios) -> Option<()> { unsafe { if libc::tcsetattr(1, libc::TCSANOW, attrs) < 0 { return None; } return Some(()); } } fn make_raw() -> termios { unsafe { let mut attr: termios = std::mem::zeroed(); libc::cfmakeraw(&mut attr); return attr; } } pub struct RawHandle { old: termios, } impl Drop for RawHandle { fn drop(&mut self) { set_attr(&self.old); } } pub fn raw() -> Option { let old = match get_attr() { Some(old) => old, None => { return None; } }; if let None = set_attr(&make_raw()) { return None; } return Some(RawHandle { old: old }); } pub enum Key { CtrlQ, Up, Down, Left, Right, Char(char), } pub fn read_key(stdin: &mut Stdin) -> Result { const CTRL_Q: u8 = 17; const ESC: u8 = 27; loop { let mut buf = vec![0; 1]; if let Err(err) = stdin.read_exact(&mut buf) { return Err(format!("read input: {}", err)); } if buf[0] == CTRL_Q { return Ok(Key::CtrlQ); } if buf[0] != ESC { return Ok(Key::Char(char::from(buf[0]))); } // Try to handle an escape sequence. let mut buf = vec![0; 2]; if let Err(err) = stdin.read_exact(&mut buf) { return Err(format!("read input: {}", err)); } if buf[0] != b'[' { // Unknown escape sequence, just read another key. continue; } match buf[1] { b'A' => { return Ok(Key::Up); } b'B' => { return Ok(Key::Down); } b'C' => { return Ok(Key::Right); } b'D' => { return Ok(Key::Left); } _ => { // Unknown key, just ignore it. continue; } } } }