//! Per-frame input — raw state, edges, and remappable named actions. //! //! Stage 7 builds the engine's input abstraction in three layers: //! //! 1. [`InputState`] (piece 1) — the per-frame snapshot of keyboard, mouse, //! cursor, and scroll, with `pressed` / `released` edge detection and a //! persistent `held` state. The windowing runner pumps raw `WindowEvent`s //! into it and clears edges between frames; game/editor code reads it via //! [`AppCtx::input`](crate::window::AppCtx::input). //! 2. [`Binding`] + [`ActionMap`] (piece 2) — named actions like `"Jump"` //! bound to one or more physical inputs, each carrying a **default** //! binding and a (possibly remapped) **current** binding. Game code //! queries actions by name, so a user-facing remap never touches game //! code. Current bindings round-trip through RON for persistence //! (typically via the [`Settings`](crate::settings::Settings) framework). //! 3. [`AxisBinding`] + [`Axis2DBinding`] (piece 3) — directional inputs //! composed from `Binding` direction sets (e.g. `WASD` → `Vec2 "Move"`), //! stored alongside button actions in the same [`ActionMap`] and //! persisted through the same [`ActionOverrides`] payload. //! //! # Why edges and state are tracked separately //! //! Game logic typically wants three distinct things from a physical input: //! the moment it became pressed (a jump fires once on key-down, never on //! subsequent frames while held), the moment it was released (a charged //! shot fires on key-up), and whether it is currently down (a sprint key //! accelerates while held). Tracking all three explicitly makes the //! semantics robust against OS key auto-repeat — a held key produces a //! single `pressed` edge no matter how many times the OS re-sends the //! event — and avoids the per-callsite bookkeeping every action would //! otherwise need. //! //! # Quick reference //! //! ``` //! use oxide_engine::input::{ActionMap, Binding, InputState}; //! use oxide_engine::winit::keyboard::KeyCode; //! //! let mut input = InputState::new(); //! input.press_key(KeyCode::Space); //! assert!(input.pressed(KeyCode::Space)); // edge — true only this frame //! assert!(input.held(KeyCode::Space)); // state — true while held //! //! // Layer named actions on top — game code never names the physical key. //! let mut actions = ActionMap::new(); //! actions.register("Jump", [Binding::Key(KeyCode::Space)]); //! assert!(actions.action_pressed("Jump", &input)); //! //! input.end_frame(); //! assert!(!input.pressed(KeyCode::Space)); // edge cleared //! assert!(input.held(KeyCode::Space)); // held persists //! ``` //! //! # Synthesized-event API //! //! The mutators on [`InputState`] (`press_key`, `release_mouse`, //! `set_cursor`, `add_mouse_delta`, `add_scroll`, `forget_cursor`, //! `release_all_held`) are the same path `handle_event` uses, and are //! intentionally public so tests can drive input directly without //! constructing `winit` events (winit 0.30's `DeviceId` cannot be //! fabricated outside an event loop, so most `WindowEvent` variants are //! unreachable from synthesized events). mod action; mod axis; mod binding; mod state; pub use action::{ActionMap, ActionOverrides}; pub use axis::{Axis2DBinding, AxisBinding}; pub use binding::Binding; pub use state::InputState;