use libc::{termios, winsize}; use std::error::Error; use std::io::{stdin, stdout, Read, Write}; pub fn size() -> Result> { unsafe { let mut window: winsize = std::mem::zeroed(); let status = libc::ioctl(1, libc::TIOCGWINSZ, &mut window); if status < 0 { return Err(Box::from(format!( "terminal size: ioctl failed with code {}", status ))); } return Ok(window); } } fn get_attr() -> Result> { unsafe { let mut attr: termios = std::mem::zeroed(); let status = libc::tcgetattr(1, &mut attr); if status < 0 { return Err(Box::from(format!("tcgetattr failed with code {}", status))); } return Ok(attr); } } fn set_attr(attrs: &termios) -> Result<(), Box> { unsafe { let status = libc::tcsetattr(1, libc::TCSANOW, attrs); if status < 0 { return Err(Box::from(format!("set attributes: code {}", status))); } return Ok(()); } } 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) { let _ = write!(stdout(), "\x1b[?7h"); let _ = stdout().flush(); let _ = set_attr(&self.old); } } pub fn raw() -> Result> { write!(stdout(), "\x1b[?7l")?; stdout().flush()?; let old = get_attr()?; set_attr(&make_raw())?; return Ok(RawHandle { old: old }); } pub enum Key { CtrlQ, CtrlS, CtrlZ, CtrlY, Up, Down, Left, Right, Backspace, Char(u8), } pub fn read_key() -> Result> { const CTRL_Q: u8 = 17; const CTRL_S: u8 = 19; const CTRL_Y: u8 = 25; const CTRL_Z: u8 = 26; const BACKSPACE: u8 = 127; const ESC: u8 = 27; loop { let mut buf = vec![0; 1]; stdin().read_exact(&mut buf)?; if buf[0] == CTRL_Q { return Ok(Key::CtrlQ); } if buf[0] == CTRL_S { return Ok(Key::CtrlS); } if buf[0] == CTRL_Y { return Ok(Key::CtrlY); } if buf[0] == CTRL_Z { return Ok(Key::CtrlZ); } if buf[0] == BACKSPACE { return Ok(Key::Backspace); } if buf[0] != ESC { return Ok(Key::Char(buf[0])); } // Try to handle an escape sequence. let mut buf = vec![0; 2]; stdin().read_exact(&mut buf)?; 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; } } } }