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