2. Input
Chapter 1’s @ sat still. This chapter gives it a position and moves it with the arrow keys –
still no map, just the four edges of the grid to bump into.
State
Hello’s empty struct becomes a position:
/// State for the input example: the player's position, in cells.
pub struct Input {
x: u16,
y: u16,
}
impl Default for Input {
fn default() -> Self {
// Centered on the 50x25 grid every backend in this crate uses by default; chapter 3's
// `init` override centers on the real backend size instead (see its own doc comment).
Self { x: 25, y: 12 }
}
}
Reading input and moving
Every backend’s Terminal::drain_events yields the same Event enum, so the input loop looks
identical whether it’s driven by real key presses, a synthetic test event, or (in chapter 6) a
browser’s keyboard events forwarded over WASM. try_move clamps the new position to the grid so @
can’t walk off the edge of the screen:
/// Moves the player by one cell in `(dx, dy)`, clamped to `width`x`height` so it never
/// leaves the grid.
fn try_move(&mut self, width: u16, height: u16, dx: i32, dy: i32) {
let nx = i32::from(self.x) + dx;
let ny = i32::from(self.y) + dy;
if let Ok(nx) = u16::try_from(nx)
&& nx < width
{
self.x = nx;
}
if let Ok(ny) = u16::try_from(ny)
&& ny < height
{
self.y = ny;
}
}
/// Drains pending input: moves on an arrow key, returns `false` if the user asked to quit.
fn handle_events<B: Backend>(&mut self, term: &mut Terminal<B>) -> bool {
let size = term.size();
let (width, height) = (size.width, size.height);
for event in term.drain_events() {
match event {
Event::Key(key) => match key.code {
KeyCode::Char('q') | KeyCode::Escape => return false,
KeyCode::Left => self.try_move(width, height, -1, 0),
KeyCode::Right => self.try_move(width, height, 1, 0),
KeyCode::Up => self.try_move(width, height, 0, -1),
KeyCode::Down => self.try_move(width, height, 0, 1),
_ => {}
},
Event::Close => return false,
_ => {}
}
}
true
}
Running it
cargo run --example 02_input --features crossterm
cargo run --example 02_input --features software
cargo run --example 02_input # headless fallback, prints a few frames to stdout
Arrow keys move @. q or Escape quits. Chapter 3 replaces the screen-edge clamp with a real map
and wall collision.