Ratatui detect arrow key press and release
⚓ Rust 📅 2026-04-22 👤 surdeus 👁️ 2I am new to ratatui and want to implement a console-based controller for a USB nerf missile launcher. I can capture the key press events of the arrow key, but releasing the key is not being detected as an event. I.e. only ever the key press event fires.
//! TUI controller for the USB missile launcher.
use std::io;
use crossterm::event;
use crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind};
use log::{debug, error, info};
use ratatui::prelude::*;
use ratatui::symbols::border;
use ratatui::widgets::{Block, Paragraph};
use ratatui::{DefaultTerminal, Frame};
fn main() -> io::Result<()> {
env_logger::init();
ratatui::run(|terminal| App::new().run(terminal))
}
#[derive(Debug)]
struct App {
exit: bool,
}
impl App {
/// Crate a new application.
#[must_use]
pub const fn new() -> Self {
Self { exit: false }
}
/// Run the application's main loop until the user quits.
///
/// # Errors
///
/// Returns an [`io::Error`] if any I/O error occurs.
pub fn run(&mut self, terminal: &mut DefaultTerminal) -> io::Result<()> {
while !self.exit {
terminal.draw(|frame| self.draw(frame))?;
self.handle_events()?;
}
Ok(())
}
fn draw(&self, frame: &mut Frame<'_>) {
frame.render_widget(self, frame.area());
}
fn handle_events(&mut self) -> io::Result<()> {
if let Event::Key(key_event) = event::read()? {
self.handle_key_event(key_event);
};
Ok(())
}
const fn exit(&mut self) {
self.exit = true;
}
fn handle_key_event(&mut self, key_event: KeyEvent) {
info!("Key event: {:?}", key_event);
match key_event.kind {
KeyEventKind::Press => match key_event.code {
KeyCode::Esc => self.exit(),
KeyCode::Left | KeyCode::Right | KeyCode::Up | KeyCode::Down | KeyCode::Enter => {
println!("KEY PRESSED: {key_event:?}")
}
other => debug!("Unsupported key pressed: {other:?}"),
},
KeyEventKind::Release => match key_event.code {
KeyCode::Left | KeyCode::Right | KeyCode::Up | KeyCode::Down | KeyCode::Enter => {
println!("KEY RELEASED: {key_event:?}");
}
other => debug!("Unsupported key released: {other:?}"),
},
KeyEventKind::Repeat => debug!("Unsupported key repeat: {key_event:?}"),
}
}
}
impl Widget for &App {
fn render(self, area: Rect, buf: &mut Buffer) {
let title = Line::from(" Counter App Tutorial ".bold());
let instructions = Line::from(vec![
" Decrement ".into(),
"<Left>".blue().bold(),
" Increment ".into(),
"<Right>".blue().bold(),
" Quit ".into(),
"<Q> ".blue().bold(),
]);
let block = Block::bordered()
.title(title.centered())
.title_bottom(instructions.centered())
.border_set(border::THICK);
Paragraph::new("Foobar")
.centered()
.block(block)
.render(area, buf);
}
}
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Decrement <Left> Increment <Right> Quit <Q> ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛KEY PRESSED: KeyEvent { code: Up, modifiers: KeyModifiers(0x0), kind: Press, state: KeyEventState(0x0) }
KEY PRESSED: KeyEvent { code: Left, modifiers: KeyModifiers(0x0), kind: Press, state: KeyEventState(0x0) }
KEY PRESSED: KeyEvent { code: Down, modifiers: KeyModifiers(0x0), kind: Press, state: KeyEventState(0x0) }
KEY PRESSED: KeyEvent { code: Right, modifiers: KeyModifiers(0x0), kind: Press, state: KeyEventState(0x0) }
KEY PRESSED: KeyEvent { code: Up, modifiers: KeyModifiers(0x0), kind: Press, state: KeyEventState(0x0) }
KEY PRESSED: KeyEvent { code: Left, modifiers: KeyModifiers(0x0), kind: Press, state: KeyEventState(0x0) }
KEY PRESSED: KeyEvent { code: Down, modifiers: KeyModifiers(0x0), kind: Press, state: KeyEventState(0x0) }
KEY PRESSED: KeyEvent { code: Right, modifiers: KeyModifiers(0x0), kind: Press, state: KeyEventState(0x0) }
KEY PRESSED: KeyEvent { code: Up, modifiers: KeyModifiers(0x0), kind: Press, state: KeyEventState(0x0) }
KEY PRESSED: KeyEvent { code: Left, modifiers: KeyModifiers(0x0), kind: Press, state: KeyEventState(0x0) }
How can I differentiate between key down and key release events?
3 posts - 2 participants
🏷️ Rust_feed