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

Input

pebble::wgpu::window::Input (re-exported from pebble::wgpu::prelude) is the frame’s keyboard/mouse/window input state. WGPUPlugin inserts it automatically, so any system can fetch it directly:

use pebble::prelude::*;
use pebble::wgpu::prelude::*;

fn movement(input: Res<Input>, mut query: Query<&mut Transform>) {
    for transform in query.iter() {
        if input.key_held(KeyCode::KeyW) {
            transform.position.z += 1.0;
        }
        if input.key_pressed(KeyCode::Space) {
            // fires once, the step the key goes down
        }
        if input.mouse_held(MouseButton::Left) {
            let (dx, dy) = input.mouse_diff();
            // e.g. rotate a camera by (dx, dy)
        }
    }
}

Input is a plain resource — Res<Input>, no different from any other Res<T>. There’s no concrete backend type to name and no separate lookup step: unlike WindowResource<W> (which needs W, e.g. WindowResource<WinitWindow>, because it also carries the raw surface handle), Input doesn’t depend on which backend produced it, so WindowPlugin inserts it as its own top-level resource in addition to nesting it inside WindowResource<W>::exposed.

KeyCode and MouseButton are Pebble’s own types (mirroring winit’s exactly) — like every other value type in pebble::wgpu, no raw winit type crosses Input’s API, so nothing here requires depending on winit yourself.

Cheap to clone, no guard to hold

Every accessor (key_pressed, key_held, mouse_diff, …) locks internally and returns a plain value — there’s no MutexGuard or similar to keep alive across a match arm or loop body. Clone Input freely; it’s an Arc internally, so every clone reads the same underlying state.

What’s tracked

State refreshes once per step, before systems run, so every accessor below reflects that step:

  • Keyboard: key_pressed/key_released (edge-triggered — true only the step the state changes) and key_held (true for as long as it’s down), plus held_shift/held_control/held_alt for the common modifier check. All physical-key based (KeyCode), so bindings stay put across keyboard layouts — the right choice for game controls.
  • Mouse: mouse_pressed/mouse_released/mouse_held (same edge/level distinction, MouseButton), cursor() (position in pixels, None when unfocused), cursor_diff() (frame-to-frame position delta), mouse_diff() (raw device motion — the one to use for a captured-mouse camera, since it isn’t clamped to the window like cursor_diff), and scroll_diff().
  • Window: close_requested(), resolution(), dropped_file(), delta_time().
  • Touch: touches() — every finger currently on the screen, as a Vec<TouchPoint> (id, position, phase) — and touch_count(). Scoped like raylib’s own touch API: current points only, not edge-triggered the way keys/mouse buttons are (no separate “just started” tracking). id is stable for one finger’s whole contact, from Started through Ended/Cancelled.

Text entry (IME-aware logical keys, typed characters) isn’t covered — Input is scoped to physical input suitable for game controls and simple UI toggles, not a text field implementation.

See Gamepad Input for controllers — a separate Gamepads resource, not part of Input.

Input::delta_time() is a side effect of how winit_input_helper times its own step cycle, not a general-purpose clock — it’s Option<Duration> (None on the first step), and only exists once a window backend has inserted Input at all. For frame delta/elapsed time/fps in any app, graphical or not, reach for Time instead.