capture keyboard event (#832)

* capture keyboard event

* try a different strategy - still not working right

* fixed up
This commit is contained in:
Darren Schroeder 2022-01-23 16:09:39 -06:00 committed by GitHub
parent 4e171203cc
commit e11a030780
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 154 additions and 0 deletions

View File

@ -174,6 +174,7 @@ pub fn create_default_context(cwd: impl AsRef<Path>) -> EngineState {
AnsiStrip,
Clear,
Input,
InputKeys,
Kill,
Sleep,
TermSize,

View File

@ -0,0 +1,151 @@
use crossterm::QueueableCommand;
use crossterm::{event::Event, event::KeyCode, event::KeyEvent, terminal};
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Value,
};
use std::io::{stdout, Write};
#[derive(Clone)]
pub struct InputKeys;
impl Command for InputKeys {
fn name(&self) -> &str {
"input-keys"
}
fn usage(&self) -> &str {
"Get input from the user."
}
fn signature(&self) -> Signature {
Signature::build("input-keys").category(Category::Platform)
}
fn run(
&self,
_engine_state: &EngineState,
stack: &mut Stack,
_call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
println!("Type any key combination to see key details. Press ESC to abort.");
match print_events(stack) {
Ok(v) => Ok(v.into_pipeline_data()),
Err(e) => {
terminal::disable_raw_mode()?;
Err(ShellError::LabeledError(
"Error with input".to_string(),
e.to_string(),
))
}
}
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Type and see key event codes",
example: "input-keys",
result: None,
}]
}
}
pub fn print_events(stack: &mut Stack) -> Result<Value, ShellError> {
let config = stack.get_config()?;
stdout().flush()?;
terminal::enable_raw_mode()?;
let mut stdout = std::io::BufWriter::new(std::io::stderr());
loop {
let event = crossterm::event::read()?;
if event == Event::Key(KeyCode::Esc.into()) {
break;
}
// stdout.queue(crossterm::style::Print(format!("event: {:?}", &event)))?;
// stdout.queue(crossterm::style::Print("\r\n"))?;
// Get a record
let v = print_events_helper(event)?;
// Print out the record
let o = match v {
Value::Record { cols, vals, .. } => cols
.iter()
.zip(vals.iter())
.map(|(x, y)| format!("{}: {}", x, y.into_string("", &config)))
.collect::<Vec<String>>()
.join(", "),
_ => "".to_string(),
};
stdout.queue(crossterm::style::Print(o))?;
stdout.queue(crossterm::style::Print("\r\n"))?;
stdout.flush()?;
}
terminal::disable_raw_mode()?;
Ok(Value::nothing(Span::test_data()))
}
// this fn is totally ripped off from crossterm's examples
// it's really a diagnostic routine to see if crossterm is
// even seeing the events. if you press a key and no events
// are printed, it's a good chance your terminal is eating
// those events.
fn print_events_helper(event: Event) -> Result<Value, ShellError> {
if let Event::Key(KeyEvent { code, modifiers }) = event {
match code {
KeyCode::Char(c) => {
let record = Value::Record {
cols: vec![
"char".into(),
"code".into(),
"modifier".into(),
"flags".into(),
],
vals: vec![
Value::string(format!("{}", c), Span::test_data()),
Value::string(format!("{:#08x}", u32::from(c)), Span::test_data()),
Value::string(format!("{:?}", modifiers), Span::test_data()),
Value::string(format!("{:#08b}", modifiers), Span::test_data()),
],
span: Span::test_data(),
};
Ok(record)
}
_ => {
let record = Value::Record {
cols: vec!["code".into(), "modifier".into(), "flags".into()],
vals: vec![
Value::string(format!("{:?}", code), Span::test_data()),
Value::string(format!("{:?}", modifiers), Span::test_data()),
Value::string(format!("{:#08b}", modifiers), Span::test_data()),
],
span: Span::test_data(),
};
Ok(record)
}
}
} else {
let record = Value::Record {
cols: vec!["event".into()],
vals: vec![Value::string(format!("{:?}", event), Span::test_data())],
span: Span::test_data(),
};
Ok(record)
}
}
#[cfg(test)]
mod tests {
use super::InputKeys;
#[test]
fn examples_work_as_expected() {
use crate::test_examples;
test_examples(InputKeys {})
}
}

View File

@ -1,6 +1,7 @@
mod ansi;
mod clear;
mod input;
mod input_keys;
mod kill;
mod sleep;
mod term_size;
@ -8,6 +9,7 @@ mod term_size;
pub use ansi::{Ansi, AnsiGradient, AnsiStrip};
pub use clear::Clear;
pub use input::Input;
pub use input_keys::InputKeys;
pub use kill::Kill;
pub use sleep::Sleep;
pub use term_size::TermSize;