I would like to detect a keydown event in Rust and then check if a combination of keys is pressed, in order to do further actions based on that. So basically support keyboard shortcuts in my Rust application.
I've looked at some crates for example ncurses but they did not match my requirements...
In plain JavaScript, you can use the EventTarget. addEventListener() method to listen for keyup event. When it occurs, check the keyCode 's value to see if an Enter key is pressed.
The keydown event is fired when a key is pressed. Unlike the keypress event, the keydown event is fired for all keys, regardless of whether they produce a character value. The keydown and keyup events provide a code indicating which key is pressed, while keypress indicates which character was entered.
If you don't need support for Windows then the best is termion.
It's a library for manipulating the terminal. In which you can detect key events and even keyboard shortcuts. And it's also really lightweight! Only 22.78 kB (as of version 1.5.5).
Here's a quick program I put together to showcase few shortcuts.
Add this code to main.rs
, add termion = "1.5.5"
to Cargo.toml
and start it with cargo run
!
use std::io::{stdin, stdout, Write};
use termion::event::Key;
use termion::input::TermRead;
use termion::raw::IntoRawMode;
fn main() {
let stdin = stdin();
//setting up stdout and going into raw mode
let mut stdout = stdout().into_raw_mode().unwrap();
//printing welcoming message, clearing the screen and going to left top corner with the cursor
write!(stdout, r#"{}{}ctrl + q to exit, ctrl + h to print "Hello world!", alt + t to print "termion is cool""#, termion::cursor::Goto(1, 1), termion::clear::All)
.unwrap();
stdout.flush().unwrap();
//detecting keydown events
for c in stdin.keys() {
//clearing the screen and going to top left corner
write!(
stdout,
"{}{}",
termion::cursor::Goto(1, 1),
termion::clear::All
)
.unwrap();
//i reckon this speaks for itself
match c.unwrap() {
Key::Ctrl('h') => println!("Hello world!"),
Key::Ctrl('q') => break,
Key::Alt('t') => println!("termion is cool"),
_ => (),
}
stdout.flush().unwrap();
}
}
If you need to support Windows and all other platforms, then you can use crossterm. It's a pretty decent library and quite heavier than termion. It's 98.06 kB (as of version 0.16.0).
Here's the same program as above but written using crossterm.
Add this code to main.rs
, add crossterm = "0.16.0"
to Cargo.toml
and try it with cargo run
!
//importing in execute! macro
#[macro_use]
extern crate crossterm;
use crossterm::cursor;
use crossterm::event::{read, Event, KeyCode, KeyEvent, KeyModifiers};
use crossterm::style::Print;
use crossterm::terminal::{disable_raw_mode, enable_raw_mode, Clear, ClearType};
use std::io::{stdout, Write};
fn main() {
let mut stdout = stdout();
//going into raw mode
enable_raw_mode().unwrap();
//clearing the screen, going to top left corner and printing welcoming message
execute!(stdout, Clear(ClearType::All), cursor::MoveTo(0, 0), Print(r#"ctrl + q to exit, ctrl + h to print "Hello world", alt + t to print "crossterm is cool""#))
.unwrap();
//key detection
loop {
//going to top left corner
execute!(stdout, cursor::MoveTo(0, 0)).unwrap();
//matching the key
match read().unwrap() {
//i think this speaks for itself
Event::Key(KeyEvent {
code: KeyCode::Char('h'),
modifiers: KeyModifiers::CONTROL,
//clearing the screen and printing our message
}) => execute!(stdout, Clear(ClearType::All), Print("Hello world!")).unwrap(),
Event::Key(KeyEvent {
code: KeyCode::Char('t'),
modifiers: KeyModifiers::ALT,
}) => execute!(stdout, Clear(ClearType::All), Print("crossterm is cool")).unwrap(),
Event::Key(KeyEvent {
code: KeyCode::Char('q'),
modifiers: KeyModifiers::CONTROL,
}) => break,
_ => (),
}
}
//disabling raw mode
disable_raw_mode().unwrap();
}
I'm not going to lie, this is a bit harder to read than the termion
solution, but it does the same job. I have no prior experience with crossterm
so this code may actually not be the best but it's decent.
Looking for a way to detect only key press without any modifier (Shift, Control, Alt)? Check this simplified code:
//-- code --
loop {
//--code--
match read().unwrap() {
Event::Key(KeyEvent {
code: KeyCode::Char('a'),
modifiers: KeyModifiers::NONE,
}) => //--code--
}
//--code--
}
//--code--
The important part here is the use of KeyModifiers::NONE
.
You could use console as a simple cross-platform solution.
use console::Term;
fn main() {
let stdout = Term::buffered_stdout();
'game_loop: loop {
if let Ok(character) = stdout.read_char() {
match character {
'w' => todo!("Up"),
'a' => todo!("Left"),
's' => todo!("Down"),
'd' => todo!("Right"),
_ => break 'game_loop,
}
}
}
}
The snippet above shows a basic example for matching common movement characters for a platform.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With