master
Ondřej Hruška 1 year ago
commit 99759854e2
  1. 4
      .gitignore
  2. 67
      Cargo.lock
  3. 13
      Cargo.toml
  4. 117
      src/main.rs

4
.gitignore vendored

@ -0,0 +1,4 @@
/target
.idea/

67
Cargo.lock generated

@ -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…
Cancel
Save