commit
						99759854e2
					
				@ -0,0 +1,4 @@ | 
				
			|||||||
 | 
					/target | 
				
			||||||
 | 
					.idea/ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -0,0 +1,67 @@ | 
				
			|||||||
 | 
					# This file is automatically @generated by Cargo. | 
				
			||||||
 | 
					# It is not intended for manual editing. | 
				
			||||||
 | 
					version = 3 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]] | 
				
			||||||
 | 
					name = "anes" | 
				
			||||||
 | 
					version = "0.1.6" | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index" | 
				
			||||||
 | 
					checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" | 
				
			||||||
 | 
					dependencies = [ | 
				
			||||||
 | 
					 "bitflags", | 
				
			||||||
 | 
					] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]] | 
				
			||||||
 | 
					name = "bitflags" | 
				
			||||||
 | 
					version = "1.3.2" | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index" | 
				
			||||||
 | 
					checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]] | 
				
			||||||
 | 
					name = "bless" | 
				
			||||||
 | 
					version = "0.1.0" | 
				
			||||||
 | 
					dependencies = [ | 
				
			||||||
 | 
					 "anes", | 
				
			||||||
 | 
					 "crossbeam-channel", | 
				
			||||||
 | 
					 "termios", | 
				
			||||||
 | 
					] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]] | 
				
			||||||
 | 
					name = "cfg-if" | 
				
			||||||
 | 
					version = "1.0.0" | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index" | 
				
			||||||
 | 
					checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]] | 
				
			||||||
 | 
					name = "crossbeam-channel" | 
				
			||||||
 | 
					version = "0.5.6" | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index" | 
				
			||||||
 | 
					checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" | 
				
			||||||
 | 
					dependencies = [ | 
				
			||||||
 | 
					 "cfg-if", | 
				
			||||||
 | 
					 "crossbeam-utils", | 
				
			||||||
 | 
					] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]] | 
				
			||||||
 | 
					name = "crossbeam-utils" | 
				
			||||||
 | 
					version = "0.8.14" | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index" | 
				
			||||||
 | 
					checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" | 
				
			||||||
 | 
					dependencies = [ | 
				
			||||||
 | 
					 "cfg-if", | 
				
			||||||
 | 
					] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]] | 
				
			||||||
 | 
					name = "libc" | 
				
			||||||
 | 
					version = "0.2.138" | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index" | 
				
			||||||
 | 
					checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]] | 
				
			||||||
 | 
					name = "termios" | 
				
			||||||
 | 
					version = "0.3.3" | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index" | 
				
			||||||
 | 
					checksum = "411c5bf740737c7918b8b1fe232dca4dc9f8e754b8ad5e20966814001ed0ac6b" | 
				
			||||||
 | 
					dependencies = [ | 
				
			||||||
 | 
					 "libc", | 
				
			||||||
 | 
					] | 
				
			||||||
@ -0,0 +1,13 @@ | 
				
			|||||||
 | 
					[package] | 
				
			||||||
 | 
					name = "bless" | 
				
			||||||
 | 
					version = "0.1.0" | 
				
			||||||
 | 
					edition = "2021" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[dependencies] | 
				
			||||||
 | 
					anes = { version = "0.1.6", features = ["parser"] } | 
				
			||||||
 | 
					crossbeam-channel = "0.5.6" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# For TTY interface | 
				
			||||||
 | 
					termios = "0.3.3" | 
				
			||||||
@ -0,0 +1,117 @@ | 
				
			|||||||
 | 
					use std::io::{Read, stdin, stdout, Write}; | 
				
			||||||
 | 
					use std::os::unix::io::RawFd; | 
				
			||||||
 | 
					use anes::parser::{KeyCode, Sequence}; | 
				
			||||||
 | 
					use termios::{tcgetattr, tcsetattr, Termios}; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const STDIN_FILENO : RawFd = 0; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct StdinNonblockingGuard { | 
				
			||||||
 | 
					    original_termios : Termios, | 
				
			||||||
 | 
					} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl StdinNonblockingGuard { | 
				
			||||||
 | 
					    pub fn new() -> std::io::Result<Self> { | 
				
			||||||
 | 
					        let mut old_tio = unsafe { std::mem::zeroed::<Termios>() }; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* get the terminal settings for stdin */ | 
				
			||||||
 | 
					        tcgetattr(STDIN_FILENO, &mut old_tio)?; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* we want to keep the old setting to restore them a the end */ | 
				
			||||||
 | 
					        let mut new_tio = old_tio; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* disable canonical mode (buffered i/o) and local echo */ | 
				
			||||||
 | 
					        new_tio.c_lflag &= !termios::ICANON & !termios::ECHO; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* set the new settings immediately */ | 
				
			||||||
 | 
					        tcsetattr(STDIN_FILENO, termios::TCSANOW, &new_tio)?; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(Self { | 
				
			||||||
 | 
					            original_termios: old_tio, | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Drop for StdinNonblockingGuard { | 
				
			||||||
 | 
					    fn drop(&mut self) { | 
				
			||||||
 | 
					        /* restore the former settings */ | 
				
			||||||
 | 
					        let _ = tcsetattr(STDIN_FILENO, termios::TCSANOW, &self.original_termios); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum InputEvent { | 
				
			||||||
 | 
					    Key(anes::parser::Sequence), | 
				
			||||||
 | 
					    EndOfStream, | 
				
			||||||
 | 
					} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct TerminalInterface { | 
				
			||||||
 | 
					} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl TerminalInterface { | 
				
			||||||
 | 
					    pub fn new() -> (Self, crossbeam_channel::Receiver<InputEvent>) { | 
				
			||||||
 | 
					        let (sender, receiver) = crossbeam_channel::bounded(0); | 
				
			||||||
 | 
					        std::thread::spawn(move || { | 
				
			||||||
 | 
					            let stdin_guard = StdinNonblockingGuard::new().unwrap(); | 
				
			||||||
 | 
					            let mut sin = stdin().lock(); | 
				
			||||||
 | 
					            let mut parser = anes::parser::Parser::default(); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            'input: loop { | 
				
			||||||
 | 
					                let mut buf = [0u8; 64]; | 
				
			||||||
 | 
					                // TODO add a way to kill the thread from outside
 | 
				
			||||||
 | 
					                let numch = match sin.read(&mut buf[..]) { | 
				
			||||||
 | 
					                    Err(_) | Ok(0) => { | 
				
			||||||
 | 
					                        let _ = sender.send(InputEvent::EndOfStream); | 
				
			||||||
 | 
					                        break 'input; | 
				
			||||||
 | 
					                    }, | 
				
			||||||
 | 
					                    Ok(numch) => numch | 
				
			||||||
 | 
					                }; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                let received_slice = &buf[0..numch]; | 
				
			||||||
 | 
					                parser.advance(received_slice, numch == buf.len()); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                while let Some(item) = parser.next() { | 
				
			||||||
 | 
					                    if sender.send(InputEvent::Key(item)).is_err() { | 
				
			||||||
 | 
					                        break 'input; | 
				
			||||||
 | 
					                    } | 
				
			||||||
 | 
					                } | 
				
			||||||
 | 
					            } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            drop(stdin_guard); | 
				
			||||||
 | 
					        }); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        (Self {}, receiver) | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Write for TerminalInterface { | 
				
			||||||
 | 
					    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> { | 
				
			||||||
 | 
					        stdout().write(buf) | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn flush(&mut self) -> std::io::Result<()> { | 
				
			||||||
 | 
					        stdout().flush() | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn main() -> std::io::Result<()> { | 
				
			||||||
 | 
					    let (mut term, receiver) = TerminalInterface::new(); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    'lp: loop { | 
				
			||||||
 | 
					        match receiver.recv() { | 
				
			||||||
 | 
					            Ok(InputEvent::EndOfStream) | Err(_) => { | 
				
			||||||
 | 
					                break 'lp; | 
				
			||||||
 | 
					            } | 
				
			||||||
 | 
					            Ok(InputEvent::Key(k)) => { | 
				
			||||||
 | 
					                write!(term, "{:?}\n", k)?; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if let Sequence::Key(KeyCode::Esc, _) = k { | 
				
			||||||
 | 
					                    write!(term, "ESC, exit\n")?; | 
				
			||||||
 | 
					                    break 'lp; | 
				
			||||||
 | 
					                } | 
				
			||||||
 | 
					            } | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Ok(()) | 
				
			||||||
 | 
					} | 
				
			||||||
					Loading…
					
					
				
		Reference in new issue