Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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.