diff --git a/Cargo.toml b/Cargo.toml index 287359e6..4f7012be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,10 @@ opt-level = 3 members = ["./", "tools/ci", "macros"] [features] -default = ['ui', 'block_ui_interactions'] +default = ['asset', 'ui', 'block_ui_interactions'] + +# Allow you to use the `InputMap` as `bevy::asset::Asset`. +asset = ['bevy/bevy_asset'] # Provide functionality refer to `bevy::ui`: # - `MockUIInteraction` for `bevy::ui` input mocking. @@ -35,11 +38,12 @@ egui = ['dep:bevy_egui'] [dependencies] leafwing_input_manager_macros = { path = "macros", version = "0.12" } -bevy = { version = "0.12", default-features = false, features = [ +bevy = { version = "0.13", default-features = false, features = [ "serialize", "bevy_gilrs", ] } -bevy_egui = { version = "0.24", optional = true } +# TODO(clean): Existing PR repo for update `bevy_egui` to bevy 0.13 +bevy_egui = { git = "https://github.com/Shute052/bevy_egui.git", branch = "Bevy-0.13-and-Egui-0.26", optional = true } derive_more = { version = "0.99", default-features = false, features = [ "display", @@ -51,7 +55,7 @@ fixedbitset = "0.4.2" once_cell = "1.17.1" [dev-dependencies] -bevy = { version = "0.12", default-features = false, features = [ +bevy = { version = "0.13", default-features = false, features = [ "bevy_asset", "bevy_sprite", "bevy_text", @@ -60,7 +64,8 @@ bevy = { version = "0.12", default-features = false, features = [ "bevy_core_pipeline", "x11", ] } -bevy_egui = { version = "0.24" } +# TODO(clean): Existing PR repo for update `bevy_egui` to bevy 0.13 +bevy_egui = { git = "https://github.com/Shute052/bevy_egui.git", branch = "Bevy-0.13-and-Egui-0.26" } serde_test = "1.0" criterion = "0.5" diff --git a/README.md b/README.md index 7afd9b8b..52193f7c 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,16 @@ The mapping between inputs and actions is many-to-many, and easily configured an A single action can be triggered by multiple inputs (or set directly by UI elements or gameplay logic), and a single input can result in multiple actions being triggered, which can be handled contextually. +## Supported Bevy Versions + +| Bevy | leafwing-input-manager | +|------|------------------------| +| 0.13 | 0.13 | +| 0.12 | 0.11..0.12 | +| 0.11 | 0.10 | +| 0.10 | 0.9 | +| 0.9 | 0.7..0.8 | + ## Features - Full keyboard, mouse and joystick support for button-like and axis inputs @@ -25,13 +35,13 @@ and a single input can result in multiple actions being triggered, which can be - Ergonomic insertion API that seamlessly blends multiple input types for you - Can't decide between `input_map.insert(Action::Jump, KeyCode::Space)` and `input_map.insert(Action::Jump, GamepadButtonType::South)`? Have both! - Full support for arbitrary button combinations: chord your heart out. - - `input_map.insert_chord(Action::Console, [KeyCode::ControlLeft, KeyCode::Shift, KeyCode::C])` + - `input_map.insert_chord(Action::Console, [KeyCode::ControlLeft, KeyCode::Shift, KeyCode::KeyC])` - Sophisticated input disambiguation with the `ClashStrategy` enum: stop triggering individual buttons when you meant to press a chord! - Create an arbitrary number of strongly typed disjoint action sets by adding multiple copies of this plugin: decouple your camera and player state - Local multiplayer support: freely bind keys to distinct entities, rather than worrying about singular global state - Networked multiplayer support: serializable structs, and a space-conscious `ActionDiff` representation to send on the wire - Powerful and easy-to-use input mocking API for integration testing your Bevy applications - - `app.send_input(KeyCode::B)` or `world.send_input(UserInput::chord([KeyCode::B, KeyCode::E, KeyCode::V, KeyCode::Y])` + - `app.send_input(KeyCode::KeyB)` or `world.send_input(UserInput::chord([KeyCode::KeyB, KeyCode::KeyE, KeyCode::KeyV, KeyCode::KeyY])` - Control which state this plugin is active in: stop wandering around while in a menu! - Leafwing Studio's trademark `#![forbid(missing_docs)]` @@ -100,5 +110,5 @@ This snippet is the `minimal.rs` example from the [`examples`](./examples) folde ## Crate Feature Flags -This crate has three feature flags: `ui`, `block_ui_interactions`, and `egui`. +This crate has four feature flags: `asset`, `ui`, `block_ui_interactions`, and `egui`. Please refer to the `[features]` section in the [`Cargo.toml`](./Cargo.toml) for detailed information about their configurations. diff --git a/RELEASES.md b/RELEASES.md index e885be7c..c8fa3187 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,5 +1,23 @@ # Release Notes +## Unreleased + +### Breaking Changes + +- both `KeyCode`-based logical keybindings and `ScanCode`-based physical keybindings are no longer supported; +- please migrate to: + - `KeyCode` is now representing physical keybindings. + - `InputKind::KeyLocation` have been removed; please use `InputKind::PhysicalKey` instead. + - `ScanCode` and `QwertyScanCode` have been removed; please use `KeyCode` instead: + - all letter keys now follow the format `KeyCode::Key`, e.g., `ScanCode::K` is now `KeyCode::KeyK`. + - all number keys over letters now follow the format `KeyCode::Digit`, e.g., `ScanCode::Key1` is now `KeyCode::Digit1`. + - all arrow keys now follow the format `KeyCode::Arrow`, e.g., `ScanCode::Up` is now `KeyCode::ArrowUp`. + +### Usability + +- `bevy` dependency has been bumped from 0.12 to 0.13. +- `bevy_egui` dependency has been bumped from 0.24 to 0.25. + ## Version 0.12 ### Enhancements diff --git a/benches/input_map.rs b/benches/input_map.rs index 3d4ad05a..4a4bf470 100644 --- a/benches/input_map.rs +++ b/benches/input_map.rs @@ -28,32 +28,32 @@ enum TestAction { fn construct_input_map_from_iter() -> InputMap { black_box(InputMap::new([ - (TestAction::A, KeyCode::A), - (TestAction::B, KeyCode::B), - (TestAction::C, KeyCode::C), - (TestAction::D, KeyCode::D), - (TestAction::E, KeyCode::E), - (TestAction::F, KeyCode::F), - (TestAction::G, KeyCode::G), - (TestAction::H, KeyCode::H), - (TestAction::I, KeyCode::I), - (TestAction::J, KeyCode::J), + (TestAction::A, KeyCode::KeyA), + (TestAction::B, KeyCode::KeyB), + (TestAction::C, KeyCode::KeyC), + (TestAction::D, KeyCode::KeyD), + (TestAction::E, KeyCode::KeyE), + (TestAction::F, KeyCode::KeyF), + (TestAction::G, KeyCode::KeyG), + (TestAction::H, KeyCode::KeyH), + (TestAction::I, KeyCode::KeyI), + (TestAction::J, KeyCode::KeyJ), ])) } fn construct_input_map_from_chained_calls() -> InputMap { black_box( InputMap::default() - .insert(TestAction::A, KeyCode::A) - .insert(TestAction::B, KeyCode::B) - .insert(TestAction::C, KeyCode::C) - .insert(TestAction::D, KeyCode::D) - .insert(TestAction::E, KeyCode::E) - .insert(TestAction::F, KeyCode::F) - .insert(TestAction::G, KeyCode::G) - .insert(TestAction::H, KeyCode::H) - .insert(TestAction::I, KeyCode::I) - .insert(TestAction::J, KeyCode::J) + .insert(TestAction::A, KeyCode::KeyA) + .insert(TestAction::B, KeyCode::KeyB) + .insert(TestAction::C, KeyCode::KeyC) + .insert(TestAction::D, KeyCode::KeyD) + .insert(TestAction::E, KeyCode::KeyE) + .insert(TestAction::F, KeyCode::KeyF) + .insert(TestAction::G, KeyCode::KeyG) + .insert(TestAction::H, KeyCode::KeyH) + .insert(TestAction::I, KeyCode::KeyI) + .insert(TestAction::J, KeyCode::KeyJ) .build(), ) } @@ -78,8 +78,8 @@ pub fn criterion_benchmark(c: &mut Criterion) { // Constructing our test app / input stream outside of the timed benchmark let mut app = App::new(); app.add_plugins(InputPlugin); - app.send_input(KeyCode::A); - app.send_input(KeyCode::B); + app.send_input(KeyCode::KeyA); + app.send_input(KeyCode::KeyB); app.update(); let input_streams = InputStreams::from_world(&app.world, None); diff --git a/examples/action_state_resource.rs b/examples/action_state_resource.rs index 3d980b30..e8711c36 100644 --- a/examples/action_state_resource.rs +++ b/examples/action_state_resource.rs @@ -30,7 +30,7 @@ impl PlayerAction { fn mkb_input_map() -> InputMap { use KeyCode::*; InputMap::new([ - (Self::Jump, UserInput::Single(InputKind::Keyboard(Space))), + (Self::Jump, UserInput::Single(InputKind::PhysicalKey(Space))), (Self::Move, UserInput::VirtualDPad(VirtualDPad::wasd())), ]) } diff --git a/examples/arpg_indirection.rs b/examples/arpg_indirection.rs index 9af9a61b..7dbf28b7 100644 --- a/examples/arpg_indirection.rs +++ b/examples/arpg_indirection.rs @@ -96,10 +96,10 @@ fn spawn_player(mut commands: Commands) { commands.spawn(PlayerBundle { player: Player, slot_input_map: InputMap::new([ - (Slot::Ability1, Q), - (Slot::Ability2, W), - (Slot::Ability3, E), - (Slot::Ability4, R), + (Slot::Ability1, KeyQ), + (Slot::Ability2, KeyW), + (Slot::Ability3, KeyE), + (Slot::Ability4, KeyR), ]) .insert(Slot::Primary, MouseButton::Left) .insert(Slot::Secondary, MouseButton::Right) diff --git a/examples/clash_handling.rs b/examples/clash_handling.rs index 5f6588ea..da30e63f 100644 --- a/examples/clash_handling.rs +++ b/examples/clash_handling.rs @@ -36,13 +36,13 @@ fn spawn_input_map(mut commands: Commands) { let mut input_map = InputMap::default(); // Setting up input mappings in the obvious way - input_map.insert_multiple([(One, Key1), (Two, Key2), (Three, Key3)]); + input_map.insert_multiple([(One, Digit1), (Two, Digit2), (Three, Digit3)]); - input_map.insert_chord(OneAndTwo, [Key1, Key2]); - input_map.insert_chord(OneAndThree, [Key1, Key3]); - input_map.insert_chord(TwoAndThree, [Key2, Key3]); + input_map.insert_chord(OneAndTwo, [Digit1, Digit2]); + input_map.insert_chord(OneAndThree, [Digit1, Digit3]); + input_map.insert_chord(TwoAndThree, [Digit2, Digit3]); - input_map.insert_chord(OneAndTwoAndThree, [Key1, Key2, Key3]); + input_map.insert_chord(OneAndTwoAndThree, [Digit1, Digit2, Digit3]); commands.spawn(InputManagerBundle { input_map, diff --git a/examples/consuming_actions.rs b/examples/consuming_actions.rs index fd92bde3..c3d5d188 100644 --- a/examples/consuming_actions.rs +++ b/examples/consuming_actions.rs @@ -13,8 +13,8 @@ fn main() { .init_resource::>() .insert_resource(InputMap::::new([ (MenuAction::CloseWindow, KeyCode::Escape), - (MenuAction::OpenMainMenu, KeyCode::M), - (MenuAction::OpenSubMenu, KeyCode::S), + (MenuAction::OpenMainMenu, KeyCode::KeyM), + (MenuAction::OpenSubMenu, KeyCode::KeyS), ])) .init_resource::() .init_resource::() diff --git a/examples/default_controls.rs b/examples/default_controls.rs index 77fccd56..829c3000 100644 --- a/examples/default_controls.rs +++ b/examples/default_controls.rs @@ -32,7 +32,7 @@ impl PlayerAction { // Match against the provided action to get the correct default keyboard-mouse input match self { Self::Run => UserInput::VirtualDPad(VirtualDPad::wasd()), - Self::Jump => UserInput::Single(InputKind::Keyboard(KeyCode::Space)), + Self::Jump => UserInput::Single(InputKind::PhysicalKey(KeyCode::Space)), Self::UseItem => UserInput::Single(InputKind::Mouse(MouseButton::Left)), } } diff --git a/examples/multiplayer.rs b/examples/multiplayer.rs index b853e3e6..0805812e 100644 --- a/examples/multiplayer.rs +++ b/examples/multiplayer.rs @@ -32,9 +32,9 @@ impl PlayerBundle { fn input_map(player: Player) -> InputMap { let mut input_map = match player { Player::One => InputMap::new([ - (Action::Left, KeyCode::A), - (Action::Right, KeyCode::D), - (Action::Jump, KeyCode::W), + (Action::Left, KeyCode::KeyA), + (Action::Right, KeyCode::KeyD), + (Action::Jump, KeyCode::KeyW), ]) // This is a quick and hacky solution: // you should coordinate with the `Gamepads` resource to determine the correct gamepad for each player @@ -44,9 +44,9 @@ impl PlayerBundle { .set_gamepad(Gamepad { id: 0 }) .build(), Player::Two => InputMap::new([ - (Action::Left, KeyCode::Left), - (Action::Right, KeyCode::Right), - (Action::Jump, KeyCode::Up), + (Action::Left, KeyCode::ArrowLeft), + (Action::Right, KeyCode::ArrowRight), + (Action::Jump, KeyCode::ArrowUp), ]) .set_gamepad(Gamepad { id: 1 }) .build(), diff --git a/examples/physical_key_bindings.rs b/examples/physical_key_bindings.rs deleted file mode 100644 index 1e5055b9..00000000 --- a/examples/physical_key_bindings.rs +++ /dev/null @@ -1,73 +0,0 @@ -//! For some controls, such as the classical WASD movement controls, -//! we don't care about the actual output of the keys, but rather where they are positioned. -//! -//! For example, on the French AZERTY keyboard layout, these keys are not in the standard triangle pattern, -//! so using them for player movement would feel very awkward. -//! -//! Instead, we can base our bindings on the physical position of the keys. -//! This functionality is provided by _scan codes_. -//! -//! In order to not deal with arbitrary numbers to define the key positions, -//! we can use [`QwertyScanCode`] to define the name of the keys on the US QWERTY layout. -//! The mapping to the other keyboard layouts is done automatically. - -use bevy::prelude::*; -use leafwing_input_manager::prelude::*; - -fn main() { - App::new() - .add_plugins(DefaultPlugins) - .add_plugins(InputManagerPlugin::::default()) - // The InputMap and ActionState components will be added to any entity with the Player component - .add_systems(Startup, spawn_player) - // Read the ActionState in your systems using queries! - .add_systems(Update, jump) - .run(); -} - -// This is the list of "things in the game I want to be able to do based on input" -#[derive(Actionlike, PartialEq, Eq, Clone, Copy, Hash, Debug, Reflect)] -enum Action { - Forward, - Left, - Backward, - Right, -} - -#[derive(Component)] -struct Player; - -fn spawn_player(mut commands: Commands) { - commands - .spawn(InputManagerBundle:: { - // Stores "which actions are currently pressed" - action_state: ActionState::default(), - // We define the name of the keys based on the US QWERTY layout. - // The keys the user will actually have to press depends on their selected keyboard layout. - // However, the _position_ of the keys will be the same, regardless of layout. - // This way, every player can use the classic triangle shaped key arrangement. - input_map: InputMap::new([ - (Action::Forward, QwertyScanCode::W), - (Action::Left, QwertyScanCode::A), - (Action::Backward, QwertyScanCode::S), - (Action::Right, QwertyScanCode::D), - ]), - }) - .insert(Player); -} - -// Query for the `ActionState` component in your game logic systems! -fn jump(query: Query<&ActionState, With>) { - let action_state = query.single(); - - // Each action has a button-like state of its own that you can check - if action_state.just_pressed(&Action::Forward) { - println!("Going forward!"); - } else if action_state.just_pressed(&Action::Left) { - println!("Going left!"); - } else if action_state.just_pressed(&Action::Backward) { - println!("Going backward!"); - } else if action_state.just_pressed(&Action::Right) { - println!("Going right!"); - } -} diff --git a/examples/press_duration.rs b/examples/press_duration.rs index 060772c4..4c9f1c0c 100644 --- a/examples/press_duration.rs +++ b/examples/press_duration.rs @@ -47,10 +47,10 @@ impl PlayerBundle { use Action::*; InputMap::new([ - (Left, KeyCode::A), - (Left, KeyCode::Left), - (Right, KeyCode::D), - (Right, KeyCode::Right), + (Left, KeyCode::KeyA), + (Left, KeyCode::ArrowLeft), + (Right, KeyCode::KeyD), + (Right, KeyCode::ArrowRight), ]) } } diff --git a/examples/register_gamepads.rs b/examples/register_gamepads.rs index 257bf554..5b47c41a 100644 --- a/examples/register_gamepads.rs +++ b/examples/register_gamepads.rs @@ -32,7 +32,7 @@ fn join( mut commands: Commands, mut joined_players: ResMut, gamepads: Res, - button_inputs: Res>, + button_inputs: Res>, ) { for gamepad in gamepads.iter() { // Join the game when both bumpers (L+R) on the controller are pressed diff --git a/examples/send_actions_over_network.rs b/examples/send_actions_over_network.rs index 525ea12d..6975570d 100644 --- a/examples/send_actions_over_network.rs +++ b/examples/send_actions_over_network.rs @@ -127,7 +127,7 @@ fn spawn_player(mut commands: Commands) { commands .spawn(InputManagerBundle { - input_map: InputMap::new([(MoveLeft, W), (MoveRight, D), (Jump, Space)]) + input_map: InputMap::new([(MoveLeft, KeyW), (MoveRight, KeyD), (Jump, Space)]) .insert(Shoot, MouseButton::Left) .build(), ..default() diff --git a/examples/single_player.rs b/examples/single_player.rs index fab76380..f7310d74 100644 --- a/examples/single_player.rs +++ b/examples/single_player.rs @@ -74,34 +74,34 @@ impl PlayerBundle { let mut input_map = InputMap::default(); // Movement - input_map.insert(Up, KeyCode::Up); + input_map.insert(Up, KeyCode::ArrowUp); input_map.insert(Up, GamepadButtonType::DPadUp); - input_map.insert(Down, KeyCode::Down); + input_map.insert(Down, KeyCode::ArrowDown); input_map.insert(Down, GamepadButtonType::DPadDown); - input_map.insert(Left, KeyCode::Left); + input_map.insert(Left, KeyCode::ArrowLeft); input_map.insert(Left, GamepadButtonType::DPadLeft); - input_map.insert(Right, KeyCode::Right); + input_map.insert(Right, KeyCode::ArrowRight); input_map.insert(Right, GamepadButtonType::DPadRight); // Abilities - input_map.insert(Ability1, KeyCode::Q); + input_map.insert(Ability1, KeyCode::KeyQ); input_map.insert(Ability1, GamepadButtonType::West); input_map.insert(Ability1, MouseButton::Left); - input_map.insert(Ability2, KeyCode::W); + input_map.insert(Ability2, KeyCode::KeyW); input_map.insert(Ability2, GamepadButtonType::North); input_map.insert(Ability2, MouseButton::Right); - input_map.insert(Ability3, KeyCode::E); + input_map.insert(Ability3, KeyCode::KeyE); input_map.insert(Ability3, GamepadButtonType::East); input_map.insert(Ability4, KeyCode::Space); input_map.insert(Ability4, GamepadButtonType::South); - input_map.insert(Ultimate, KeyCode::R); + input_map.insert(Ultimate, KeyCode::KeyR); input_map.insert(Ultimate, GamepadButtonType::LeftTrigger2); input_map diff --git a/examples/twin_stick_controller.rs b/examples/twin_stick_controller.rs index 18c1c6bd..c14e4805 100644 --- a/examples/twin_stick_controller.rs +++ b/examples/twin_stick_controller.rs @@ -83,8 +83,8 @@ pub struct InputModeManagerPlugin; impl Plugin for InputModeManagerPlugin { fn build(&self, app: &mut App) { - // Add a state to record the current active input - app.add_state::() + // Init a state to record the current active input + app.init_state::() // System to switch to gamepad as active input .add_systems( Update, @@ -156,7 +156,9 @@ fn player_mouse_look( if let Some(p) = window .cursor_position() .and_then(|cursor| camera.viewport_to_world(camera_transform, cursor)) - .and_then(|ray| Some(ray).zip(ray.intersect_plane(player_transform.translation, Vec3::Y))) + .and_then(|ray| { + Some(ray).zip(ray.intersect_plane(player_transform.translation, Plane3d::new(Vec3::Y))) + }) .map(|(ray, p)| ray.get_point(p)) { let diff = (p - player_transform.translation).xz(); diff --git a/examples/ui_driven_actions.rs b/examples/ui_driven_actions.rs index 44698576..9e285f5e 100644 --- a/examples/ui_driven_actions.rs +++ b/examples/ui_driven_actions.rs @@ -25,8 +25,8 @@ struct Player; fn spawn_player(mut commands: Commands) { let mut input_map = InputMap::default(); - input_map.insert(Action::Left, KeyCode::Left); - input_map.insert(Action::Right, KeyCode::Right); + input_map.insert(Action::Left, KeyCode::ArrowLeft); + input_map.insert(Action::Right, KeyCode::ArrowRight); commands .spawn(SpriteBundle { diff --git a/examples/virtual_dpad.rs b/examples/virtual_dpad.rs index 8fd780bf..28a8ca04 100644 --- a/examples/virtual_dpad.rs +++ b/examples/virtual_dpad.rs @@ -30,10 +30,10 @@ fn spawn_player(mut commands: Commands) { input_map: InputMap::new([( Action::Move, VirtualDPad { - up: KeyCode::W.into(), - down: KeyCode::S.into(), - left: KeyCode::A.into(), - right: KeyCode::D.into(), + up: KeyCode::KeyW.into(), + down: KeyCode::KeyS.into(), + left: KeyCode::KeyA.into(), + right: KeyCode::KeyD.into(), }, )]) .build(), diff --git a/src/action_state.rs b/src/action_state.rs index 2003abb4..1d141c92 100644 --- a/src/action_state.rs +++ b/src/action_state.rs @@ -101,7 +101,7 @@ impl ActionState { /// Updates the [`ActionState`] based on a vector of [`ActionData`], ordered by [`Actionlike::id`](Actionlike). /// /// The `action_data` is typically constructed from [`InputMap::which_pressed`](crate::input_map::InputMap), - /// which reads from the assorted [`Input`](bevy::input::Input) resources. + /// which reads from the assorted [`ButtonInput`](bevy::input::ButtonInput) resources. pub fn update(&mut self, action_data: HashMap) { for (action, action_datum) in action_data { match self.action_data.entry(action) { @@ -263,8 +263,7 @@ impl ActionState { /// Consider clamping this to account for multiple triggering inputs, /// typically using the [`clamped_axis_pair`](Self::clamped_axis_pair) method instead. pub fn axis_pair(&self, action: &A) -> Option { - let action_data = self.action_data(action)?; - action_data.axis_pair + self.action_data(action)?.axis_pair } /// Get the [`DualAxisData`] associated with the corresponding `action`, clamped to `[-1.0, 1.0]`. @@ -320,13 +319,7 @@ impl ActionState { /// Instead, this is set through [`ActionState::tick()`] #[inline] pub fn press(&mut self, action: &A) { - let action_data = match self.action_data_mut(action) { - Some(action_data) => action_data, - None => { - self.set_action_data(action.clone(), ActionData::default()); - self.action_data_mut(action).unwrap() - } - }; + let action_data = self.action_data.entry(action.clone()).or_default(); // Consumed actions cannot be pressed until they are released if action_data.consumed { @@ -346,13 +339,7 @@ impl ActionState { /// Instead, this is set through [`ActionState::tick()`] #[inline] pub fn release(&mut self, action: &A) { - let action_data = match self.action_data_mut(action) { - Some(action_data) => action_data, - None => { - self.set_action_data(action.clone(), ActionData::default()); - self.action_data_mut(action).unwrap() - } - }; + let action_data = self.action_data.entry(action.clone()).or_default(); // Once released, consumed actions can be pressed again action_data.consumed = false; @@ -405,13 +392,7 @@ impl ActionState { /// ``` #[inline] pub fn consume(&mut self, action: &A) { - let action_data = match self.action_data_mut(action) { - Some(action_data) => action_data, - None => { - self.set_action_data(action.clone(), ActionData::default()); - self.action_data_mut(action).unwrap() - } - }; + let action_data = self.action_data.entry(action.clone()).or_default(); // This is the only difference from action_state.release(&action) action_data.consumed = true; @@ -438,30 +419,21 @@ impl ActionState { #[inline] #[must_use] pub fn consumed(&self, action: &A) -> bool { - match self.action_data(action) { - Some(action_data) => action_data.consumed, - None => false, - } + matches!(self.action_data(action), Some(action_data) if action_data.consumed) } /// Is this `action` currently pressed? #[inline] #[must_use] pub fn pressed(&self, action: &A) -> bool { - match self.action_data(action) { - Some(action_data) => action_data.state.pressed(), - None => false, - } + matches!(self.action_data(action), Some(action_data) if action_data.state.pressed()) } /// Was this `action` pressed since the last time [tick](ActionState::tick) was called? #[inline] #[must_use] pub fn just_pressed(&self, action: &A) -> bool { - match self.action_data(action) { - Some(action_data) => action_data.state.just_pressed(), - None => false, - } + matches!(self.action_data(action), Some(action_data) if action_data.state.just_pressed()) } /// Is this `action` currently released? @@ -480,10 +452,7 @@ impl ActionState { #[inline] #[must_use] pub fn just_released(&self, action: &A) -> bool { - match self.action_data(action) { - Some(action_data) => action_data.state.just_released(), - None => false, - } + matches!(self.action_data(action), Some(action_data) if action_data.state.just_released()) } #[must_use] @@ -537,8 +506,7 @@ impl ActionState { /// /// This will also be [`None`] if the action was never pressed or released. pub fn instant_started(&self, action: &A) -> Option { - let action_data = self.action_data(action)?; - action_data.timing.instant_started + self.action_data(action)?.timing.instant_started } /// The [`Duration`] for which the action has been held or released @@ -636,7 +604,7 @@ mod tests { // Input map let mut input_map = InputMap::default(); - input_map.insert(Action::Run, KeyCode::R); + input_map.insert(Action::Run, KeyCode::KeyR); // Starting state let input_streams = InputStreams::from_world(&app.world, None); @@ -648,7 +616,7 @@ mod tests { assert!(!action_state.just_released(&Action::Run)); // Pressing - app.send_input(KeyCode::R); + app.send_input(KeyCode::KeyR); // Process the input events into Input data app.update(); let input_streams = InputStreams::from_world(&app.world, None); @@ -670,7 +638,7 @@ mod tests { assert!(!action_state.just_released(&Action::Run)); // Releasing - app.release_input(KeyCode::R); + app.release_input(KeyCode::KeyR); app.update(); let input_streams = InputStreams::from_world(&app.world, None); diff --git a/src/axislike.rs b/src/axislike.rs index 1cef8c0c..f546f9a5 100644 --- a/src/axislike.rs +++ b/src/axislike.rs @@ -2,7 +2,6 @@ use crate::buttonlike::{MouseMotionDirection, MouseWheelDirection}; use crate::orientation::{Direction, Rotation}; -use crate::prelude::QwertyScanCode; use crate::user_input::InputKind; use bevy::input::{ gamepad::{GamepadAxisType, GamepadButtonType}, @@ -366,10 +365,10 @@ impl VirtualDPad { /// Generates a [`VirtualDPad`] corresponding to the arrow keyboard keycodes pub fn arrow_keys() -> VirtualDPad { VirtualDPad { - up: InputKind::Keyboard(KeyCode::Up), - down: InputKind::Keyboard(KeyCode::Down), - left: InputKind::Keyboard(KeyCode::Left), - right: InputKind::Keyboard(KeyCode::Right), + up: InputKind::PhysicalKey(KeyCode::ArrowUp), + down: InputKind::PhysicalKey(KeyCode::ArrowDown), + left: InputKind::PhysicalKey(KeyCode::ArrowLeft), + right: InputKind::PhysicalKey(KeyCode::ArrowRight), } } @@ -381,10 +380,10 @@ impl VirtualDPad { /// which enables comfortable movement controls. pub fn wasd() -> VirtualDPad { VirtualDPad { - up: InputKind::KeyLocation(QwertyScanCode::W.into()), - down: InputKind::KeyLocation(QwertyScanCode::S.into()), - left: InputKind::KeyLocation(QwertyScanCode::A.into()), - right: InputKind::KeyLocation(QwertyScanCode::D.into()), + up: InputKind::PhysicalKey(KeyCode::KeyW), + down: InputKind::PhysicalKey(KeyCode::KeyS), + left: InputKind::PhysicalKey(KeyCode::KeyA), + right: InputKind::PhysicalKey(KeyCode::KeyD), } } @@ -467,32 +466,32 @@ pub struct VirtualAxis { impl VirtualAxis { /// Helper function for generating a [`VirtualAxis`] from arbitrary keycodes, shorthand for - /// wrapping each key in [`InputKind::Keyboard`] + /// wrapping each key in [`InputKind::PhysicalKey`] pub fn from_keys(negative: KeyCode, positive: KeyCode) -> VirtualAxis { VirtualAxis { - negative: InputKind::Keyboard(negative), - positive: InputKind::Keyboard(positive), + negative: InputKind::PhysicalKey(negative), + positive: InputKind::PhysicalKey(positive), } } /// Generates a [`VirtualAxis`] corresponding to the horizontal arrow keyboard keycodes pub fn horizontal_arrow_keys() -> VirtualAxis { - VirtualAxis::from_keys(KeyCode::Left, KeyCode::Right) + VirtualAxis::from_keys(KeyCode::ArrowLeft, KeyCode::ArrowRight) } /// Generates a [`VirtualAxis`] corresponding to the horizontal arrow keyboard keycodes pub fn vertical_arrow_keys() -> VirtualAxis { - VirtualAxis::from_keys(KeyCode::Down, KeyCode::Up) + VirtualAxis::from_keys(KeyCode::ArrowDown, KeyCode::ArrowUp) } /// Generates a [`VirtualAxis`] corresponding to the `AD` keyboard keycodes. pub fn ad() -> VirtualAxis { - VirtualAxis::from_keys(KeyCode::A, KeyCode::D) + VirtualAxis::from_keys(KeyCode::KeyA, KeyCode::KeyD) } /// Generates a [`VirtualAxis`] corresponding to the `WS` keyboard keycodes. pub fn ws() -> VirtualAxis { - VirtualAxis::from_keys(KeyCode::S, KeyCode::W) + VirtualAxis::from_keys(KeyCode::KeyS, KeyCode::KeyW) } #[allow(clippy::doc_markdown)] @@ -686,10 +685,7 @@ impl DualAxisData { #[inline] pub fn direction(&self) -> Option { // TODO: replace this quick-n-dirty hack once Direction::new no longer panics - if self.xy.length() > 0.00001 { - return Some(Direction::new(self.xy)); - } - None + (self.xy.length() > 0.00001).then(|| Direction::new(self.xy)) } /// The [`Rotation`] (measured clockwise from midnight) that this axis is pointing towards, if any @@ -698,10 +694,7 @@ impl DualAxisData { #[must_use] #[inline] pub fn rotation(&self) -> Option { - match Rotation::from_xy(self.xy) { - Ok(rotation) => Some(rotation), - Err(_) => None, - } + Rotation::from_xy(self.xy).ok() } /// How far from the origin is this axis's position? @@ -741,20 +734,20 @@ impl From for Vec2 { /// The shape of the deadzone for a [`DualAxis`] input. /// /// Input values that are on the boundary of the shape are counted as inside. -/// If a size of a shape is 0.0, then all input values are read, except for 0.0. +/// If the size of a shape is 0.0, then all input values are read, except for 0.0. /// /// All inputs are scaled to be continuous. -/// So with a ellipse deadzone of a radius of 0.1, the input range `0.1..=1.0` will be scaled to `0.0..=1.0`. +/// So with an ellipse deadzone of a radius of 0.1, the input range `0.1..=1.0` will be scaled to `0.0..=1.0`. /// /// Deadzone values should be in the range `0.0..=1.0`. #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Reflect)] pub enum DeadZoneShape { /// Deadzone with the shape of a cross. /// - /// The cross is represented by horizonal and vertical rectangles. - /// Each axis is handled seperately which creates a per-axis "snapping" effect. + /// The cross is represented by horizontal and vertical rectangles. + /// Each axis is handled separately which creates a per-axis "snapping" effect. Cross { - /// The width of the horizonal axis. + /// The width of the horizontal axis. /// /// Affects the snapping of the y-axis. horizontal_width: f32, @@ -794,15 +787,13 @@ impl std::hash::Hash for DeadZoneShape { impl DeadZoneShape { /// Computes the input value based on the deadzone. pub fn deadzone_input_value(&self, x: f32, y: f32) -> Option { - let value = Vec2::new(x, y); - match self { DeadZoneShape::Cross { horizontal_width, vertical_width, - } => self.cross_deadzone_value(value, *horizontal_width, *vertical_width), + } => self.cross_deadzone_value(x, y, *horizontal_width, *vertical_width), DeadZoneShape::Ellipse { radius_x, radius_y } => { - self.ellipse_deadzone_value(value, *radius_x, *radius_y) + self.ellipse_deadzone_value(x, y, *radius_x, *radius_y) } } } @@ -810,41 +801,46 @@ impl DeadZoneShape { /// Computes the input value based on the cross deadzone. fn cross_deadzone_value( &self, - value: Vec2, + x: f32, + y: f32, horizontal_width: f32, vertical_width: f32, ) -> Option { - let new_x = f32::from(value.x.abs() > vertical_width) * value.x; - let new_y = f32::from(value.y.abs() > horizontal_width) * value.y; - let new_value = Vec2::new(new_x, new_y); - - if new_value == Vec2::ZERO { - None - } else { - let scaled_value = - Self::scale_value(new_value, Vec2::new(vertical_width, horizontal_width)); - Some(DualAxisData::from_xy(scaled_value)) - } + let new_x = deadzone_axis_value(x, vertical_width); + let new_y = deadzone_axis_value(y, horizontal_width); + let is_outside_deadzone = new_x != 0.0 || new_y != 0.0; + is_outside_deadzone.then(|| DualAxisData::new(new_x, new_y)) } /// Computes the input value based on the ellipse deadzone. fn ellipse_deadzone_value( &self, - value: Vec2, + x: f32, + y: f32, radius_x: f32, radius_y: f32, ) -> Option { - let clamped_radius_x = radius_x.max(f32::EPSILON); - let clamped_radius_y = radius_y.max(f32::EPSILON); - if (value.x / clamped_radius_x).powi(2) + (value.y / clamped_radius_y).powi(2) < 1.0 { - return None; - } - - let scaled_value = Self::scale_value(value, Vec2::new(radius_x, radius_y)); - Some(DualAxisData::from_xy(scaled_value)) + let x_ratio = x / radius_x.max(f32::EPSILON); + let y_ratio = y / radius_y.max(f32::EPSILON); + let is_outside_deadzone = x_ratio.powi(2) + y_ratio.powi(2) >= 1.0; + is_outside_deadzone.then(|| { + let new_x = deadzone_axis_value(x, radius_x); + let new_y = deadzone_axis_value(y, radius_y); + DualAxisData::new(new_x, new_y) + }) } +} - fn scale_value(value: Vec2, deadzone_size: Vec2) -> Vec2 { - value.signum() * (value.abs() - deadzone_size).max(Vec2::ZERO) / (1.0 - deadzone_size) +/// Applies the given deadzone to the axis value. +/// +/// Returns 0.0 if the axis value is within the deadzone. +/// Otherwise, returns the normalized axis value between -1.0 and 1.0. +#[inline(always)] +pub(crate) fn deadzone_axis_value(axis_value: f32, deadzone: f32) -> f32 { + let abs_axis_value = axis_value.abs(); + if abs_axis_value <= deadzone { + 0.0 + } else { + axis_value.signum() * (abs_axis_value - deadzone) / (1.0 - deadzone) } } diff --git a/src/clashing_inputs.rs b/src/clashing_inputs.rs index c0b18782..7e5b4615 100644 --- a/src/clashing_inputs.rs +++ b/src/clashing_inputs.rs @@ -103,8 +103,8 @@ impl InputMap { pub(crate) fn possible_clashes(&self) -> Vec> { let mut clashes = Vec::default(); - for (action_a, _) in self.iter() { - for (action_b, _) in self.iter() { + for action_a in self.actions() { + for action_b in self.actions() { if let Some(clash) = self.possible_clash(action_a, action_b) { clashes.push(clash); } @@ -162,11 +162,8 @@ impl InputMap { } } - if !clash.inputs_a.is_empty() { - Some(clash) - } else { - None - } + let not_empty = !clash.inputs_a.is_empty(); + not_empty.then_some(clash) } } @@ -196,49 +193,25 @@ impl Clash { // Does the `button` clash with the `chord`? #[must_use] fn button_chord_clash(button: &InputKind, chord: &[InputKind]) -> bool { - if chord.len() <= 1 { - return false; - } - - chord.contains(button) + chord.len() > 1 && chord.contains(button) } // Does the `dpad` clash with the `chord`? #[must_use] fn dpad_chord_clash(dpad: &VirtualDPad, chord: &[InputKind]) -> bool { - if chord.len() <= 1 { - return false; - } - - for button in &[dpad.up, dpad.down, dpad.left, dpad.right] { - if chord.contains(button) { - return true; - } - } - - false + chord.len() > 1 && chord.iter().any(|button| dpad_button_clash(dpad, button)) } fn dpad_button_clash(dpad: &VirtualDPad, button: &InputKind) -> bool { - for dpad_button in &[dpad.up, dpad.down, dpad.left, dpad.right] { - if button == dpad_button { - return true; - } - } - - false + [dpad.up, dpad.down, dpad.left, dpad.right] + .iter() + .any(|dpad_button| button == dpad_button) } fn dpad_dpad_clash(dpad1: &VirtualDPad, dpad2: &VirtualDPad) -> bool { - for button1 in &[dpad1.up, dpad1.down, dpad1.left, dpad1.right] { - for button2 in &[dpad2.up, dpad2.down, dpad2.left, dpad2.right] { - if button1 == button2 { - return true; - } - } - } - - false + let iter1 = [&dpad1.up, &dpad1.down, &dpad1.left, &dpad1.right].into_iter(); + let iter2 = [&dpad2.up, &dpad2.down, &dpad2.left, &dpad2.right].into_iter(); + iter1.zip(iter2).any(|(left, right)| left == right) } #[must_use] @@ -248,30 +221,23 @@ fn virtual_axis_button_clash(axis: &VirtualAxis, button: &InputKind) -> bool { #[must_use] fn virtual_axis_dpad_clash(axis: &VirtualAxis, dpad: &VirtualDPad) -> bool { - for dpad_button in &[dpad.up, dpad.down, dpad.left, dpad.right] { - if dpad_button == &axis.negative || dpad_button == &axis.positive { - return true; - } - } - - false + [&dpad.up, &dpad.down, &dpad.left, &dpad.right] + .iter() + .any(|button| virtual_axis_button_clash(axis, button)) } #[must_use] fn virtual_axis_chord_clash(axis: &VirtualAxis, chord: &[InputKind]) -> bool { - if chord.len() <= 1 { - return false; - } - - chord.contains(&axis.negative) || chord.contains(&axis.positive) + chord.len() > 1 + && chord + .iter() + .any(|button| virtual_axis_button_clash(axis, button)) } #[must_use] fn virtual_axis_virtual_axis_clash(axis1: &VirtualAxis, axis2: &VirtualAxis) -> bool { - axis1.negative == axis2.negative - || axis1.negative == axis2.positive - || axis1.positive == axis2.negative - || axis1.positive == axis2.positive + virtual_axis_button_clash(axis1, &axis2.negative) + || virtual_axis_button_clash(axis1, &axis2.positive) } /// Does the `chord_a` clash with `chord_b`? @@ -285,17 +251,11 @@ fn chord_chord_clash(chord_a: &Vec, chord_b: &Vec) -> bool return false; } - is_subset(chord_a, chord_b) || is_subset(chord_b, chord_a) -} - -fn is_subset(slice_a: &[InputKind], slice_b: &[InputKind]) -> bool { - for a in slice_a { - if !slice_b.contains(a) { - return false; - } + fn is_subset(slice_a: &[InputKind], slice_b: &[InputKind]) -> bool { + slice_a.iter().all(|a| slice_b.contains(a)) } - true + is_subset(chord_a, chord_b) || is_subset(chord_b, chord_a) } /// Given the `input_streams`, does the provided clash actually occur? @@ -325,11 +285,8 @@ fn check_clash(clash: &Clash, input_streams: &InputStreams) -> } } - if !clash.inputs_a.is_empty() { - Some(actual_clash) - } else { - None - } + let not_empty = !clash.inputs_a.is_empty(); + not_empty.then_some(actual_clash) } /// Which (if any) of the actions in the [`Clash`] should be discarded? @@ -418,24 +375,24 @@ mod tests { let mut input_map = InputMap::default(); - input_map.insert(One, Key1); - input_map.insert(Two, Key2); - input_map.insert_chord(OneAndTwo, [Key1, Key2]); - input_map.insert_chord(TwoAndThree, [Key2, Key3]); - input_map.insert_chord(OneAndTwoAndThree, [Key1, Key2, Key3]); - input_map.insert_chord(CtrlOne, [ControlLeft, Key1]); - input_map.insert_chord(AltOne, [AltLeft, Key1]); - input_map.insert_chord(CtrlAltOne, [ControlLeft, AltLeft, Key1]); + input_map.insert(One, Digit1); + input_map.insert(Two, Digit2); + input_map.insert_chord(OneAndTwo, [Digit1, Digit2]); + input_map.insert_chord(TwoAndThree, [Digit2, Digit3]); + input_map.insert_chord(OneAndTwoAndThree, [Digit1, Digit2, Digit3]); + input_map.insert_chord(CtrlOne, [ControlLeft, Digit1]); + input_map.insert_chord(AltOne, [AltLeft, Digit1]); + input_map.insert_chord(CtrlAltOne, [ControlLeft, AltLeft, Digit1]); input_map.insert( MoveDPad, VirtualDPad { - up: Up.into(), - down: Down.into(), - left: Left.into(), - right: Right.into(), + up: ArrowUp.into(), + down: ArrowDown.into(), + left: ArrowLeft.into(), + right: ArrowRight.into(), }, ); - input_map.insert_chord(CtrlUp, [ControlLeft, Up]); + input_map.insert_chord(CtrlUp, [ControlLeft, ArrowUp]); input_map } @@ -450,33 +407,33 @@ mod tests { #[test] fn clash_detection() { - let a: UserInput = A.into(); - let b: UserInput = B.into(); - let c: UserInput = C.into(); - let ab = UserInput::chord([A, B]); - let bc = UserInput::chord([B, C]); - let abc = UserInput::chord([A, B, C]); + let a: UserInput = KeyA.into(); + let b: UserInput = KeyB.into(); + let c: UserInput = KeyC.into(); + let ab = UserInput::chord([KeyA, KeyB]); + let bc = UserInput::chord([KeyB, KeyC]); + let abc = UserInput::chord([KeyA, KeyB, KeyC]); let axyz_dpad: UserInput = VirtualDPad { - up: A.into(), - down: X.into(), - left: Y.into(), - right: Z.into(), + up: KeyA.into(), + down: KeyX.into(), + left: KeyY.into(), + right: KeyZ.into(), } .into(); let abcd_dpad: UserInput = VirtualDPad { - up: A.into(), - down: B.into(), - left: C.into(), - right: D.into(), + up: KeyA.into(), + down: KeyB.into(), + left: KeyC.into(), + right: KeyD.into(), } .into(); - let ctrl_up: UserInput = UserInput::chord([Up, ControlLeft]); + let ctrl_up: UserInput = UserInput::chord([ArrowUp, ControlLeft]); let directions_dpad: UserInput = VirtualDPad { - up: Up.into(), - down: Down.into(), - left: Left.into(), - right: Right.into(), + up: ArrowUp.into(), + down: ArrowDown.into(), + left: ArrowLeft.into(), + right: ArrowRight.into(), } .into(); @@ -500,8 +457,8 @@ mod tests { let correct_clash = Clash { action_a: One, action_b: OneAndTwo, - inputs_a: vec![Key1.into()], - inputs_b: vec![UserInput::chord([Key1, Key2])], + inputs_a: vec![Digit1.into()], + inputs_b: vec![UserInput::chord([Digit1, Digit2])], }; assert_eq!(observed_clash, correct_clash); @@ -517,8 +474,8 @@ mod tests { let correct_clash = Clash { action_a: OneAndTwoAndThree, action_b: OneAndTwo, - inputs_a: vec![UserInput::chord([Key1, Key2, Key3])], - inputs_b: vec![UserInput::chord([Key1, Key2])], + inputs_a: vec![UserInput::chord([Digit1, Digit2, Digit3])], + inputs_b: vec![UserInput::chord([Digit1, Digit2])], }; assert_eq!(observed_clash, correct_clash); @@ -544,8 +501,8 @@ mod tests { let input_map = test_input_map(); let simple_clash = input_map.possible_clash(&One, &OneAndTwo).unwrap(); - app.send_input(Key1); - app.send_input(Key2); + app.send_input(Digit1); + app.send_input(Digit2); app.update(); let input_streams = InputStreams::from_world(&app.world, None); @@ -572,7 +529,7 @@ mod tests { let chord_clash = input_map .possible_clash(&OneAndTwo, &OneAndTwoAndThree) .unwrap(); - app.send_input(Key3); + app.send_input(Digit3); app.update(); let input_streams = InputStreams::from_world(&app.world, None); @@ -593,8 +550,8 @@ mod tests { app.add_plugins(InputPlugin); let input_map = test_input_map(); - app.send_input(Key1); - app.send_input(Key2); + app.send_input(Digit1); + app.send_input(Digit2); app.update(); let mut action_data = HashMap::new(); @@ -625,7 +582,7 @@ mod tests { let input_map = test_input_map(); app.send_input(ControlLeft); - app.send_input(Up); + app.send_input(ArrowUp); app.update(); let mut action_data = HashMap::new(); @@ -652,8 +609,8 @@ mod tests { app.add_plugins(InputPlugin); let input_map = test_input_map(); - app.send_input(Key1); - app.send_input(Key2); + app.send_input(Digit1); + app.send_input(Digit2); app.send_input(ControlLeft); app.update(); diff --git a/src/display_impl.rs b/src/display_impl.rs index abc8ee17..1783bbf8 100644 --- a/src/display_impl.rs +++ b/src/display_impl.rs @@ -2,6 +2,7 @@ use crate::axislike::{VirtualAxis, VirtualDPad}; use crate::user_input::{InputKind, UserInput}; +use itertools::Itertools; use std::fmt::Display; impl Display for UserInput { @@ -10,14 +11,7 @@ impl Display for UserInput { // The representation of the button UserInput::Single(button) => write!(f, "{button}"), // The representation of each button, separated by "+" - UserInput::Chord(button_set) => { - let mut string = String::default(); - for button in button_set.iter() { - string.push('+'); - string.push_str(&button.to_string()); - } - write!(f, "{string}") - } + UserInput::Chord(button_set) => f.write_str(&button_set.iter().join("+")), UserInput::VirtualDPad(VirtualDPad { up, down, @@ -45,9 +39,8 @@ impl Display for InputKind { InputKind::Mouse(button) => write!(f, "{button:?}"), InputKind::MouseWheel(button) => write!(f, "{button:?}"), InputKind::MouseMotion(button) => write!(f, "{button:?}"), - InputKind::Keyboard(button) => write!(f, "{button:?}"), // TODO: We probably want to display the key on the currently active layout - InputKind::KeyLocation(scan_code) => write!(f, "{scan_code:?}"), + InputKind::PhysicalKey(key_code) => write!(f, "{key_code:?}"), InputKind::Modifier(button) => write!(f, "{button:?}"), } } diff --git a/src/input_map.rs b/src/input_map.rs index afcef5fe..c19ce45a 100644 --- a/src/input_map.rs +++ b/src/input_map.rs @@ -7,6 +7,7 @@ use crate::input_streams::InputStreams; use crate::user_input::{InputKind, Modifier, UserInput}; use crate::Actionlike; +#[cfg(feature = "asset")] use bevy::asset::Asset; use bevy::ecs::component::Component; use bevy::ecs::system::Resource; @@ -60,9 +61,9 @@ let mut input_map = InputMap::new([ input_map.insert(Action::Run, MouseButton::Left) .insert(Action::Run, KeyCode::ShiftLeft) // Chords -.insert_modified(Action::Run, Modifier::Control, KeyCode::R) +.insert_modified(Action::Run, Modifier::Control, KeyCode::KeyR) .insert_chord(Action::Run, - [InputKind::Keyboard(KeyCode::H), + [InputKind::PhysicalKey(KeyCode::KeyH), InputKind::GamepadButton(GamepadButtonType::South), InputKind::Mouse(MouseButton::Middle)], ); @@ -71,9 +72,8 @@ input_map.insert(Action::Run, MouseButton::Left) input_map.clear_action(&Action::Hide); ``` **/ -#[derive( - Resource, Component, Debug, Clone, PartialEq, Eq, Asset, Reflect, Serialize, Deserialize, -)] +#[derive(Resource, Component, Debug, Clone, PartialEq, Eq, Reflect, Serialize, Deserialize)] +#[cfg_attr(feature = "asset", derive(Asset))] pub struct InputMap { /// The usize stored here is the index of the input in the Actionlike iterator map: HashMap>, @@ -357,6 +357,10 @@ impl InputMap { pub fn iter(&self) -> impl Iterator)> { self.map.iter() } + /// Returns an iterator over actions + pub(crate) fn actions(&self) -> impl Iterator { + self.map.keys() + } /// Returns a reference to the inputs mapped to `action` #[must_use] pub fn get(&self, action: &A) -> Option<&Vec> { @@ -372,11 +376,7 @@ impl InputMap { /// How many input bindings are registered total? #[must_use] pub fn len(&self) -> usize { - let mut i = 0; - for inputs in self.map.values() { - i += inputs.len(); - } - i + self.map.values().map(|inputs| inputs.len()).sum() } /// Are any input bindings registered at all? @@ -406,11 +406,7 @@ impl InputMap { /// Returns `Some(input)` if found. pub fn remove_at(&mut self, action: &A, index: usize) -> Option { let input_vec = self.map.get_mut(action)?; - if input_vec.len() <= index { - None - } else { - Some(input_vec.remove(index)) - } + (input_vec.len() > index).then(|| input_vec.remove(index)) } /// Removes the input for the `action`, if it exists @@ -517,17 +513,15 @@ mod tests { let mut input_map_1 = InputMap::::default(); input_map_1.insert(Action::Run, KeyCode::Space); - input_map_1.insert(Action::Run, KeyCode::Return); + input_map_1.insert(Action::Run, KeyCode::Enter); assert_eq!( input_map_1.get(&Action::Run), - Some(&vec![KeyCode::Space.into(), KeyCode::Return.into()]) + Some(&vec![KeyCode::Space.into(), KeyCode::Enter.into()]) ); - let input_map_2 = InputMap::::new([ - (Action::Run, KeyCode::Space), - (Action::Run, KeyCode::Return), - ]); + let input_map_2 = + InputMap::::new([(Action::Run, KeyCode::Space), (Action::Run, KeyCode::Enter)]); assert_eq!(input_map_1, input_map_2); } @@ -580,7 +574,7 @@ mod tests { let mut input_map = InputMap::default(); let mut default_keyboard_map = InputMap::default(); default_keyboard_map.insert(Action::Run, KeyCode::ShiftLeft); - default_keyboard_map.insert_chord(Action::Hide, [KeyCode::ControlLeft, KeyCode::H]); + default_keyboard_map.insert_chord(Action::Hide, [KeyCode::ControlLeft, KeyCode::KeyH]); let mut default_gamepad_map = InputMap::default(); default_gamepad_map.insert(Action::Run, GamepadButtonType::South); default_gamepad_map.insert(Action::Hide, GamepadButtonType::East); diff --git a/src/input_mocking.rs b/src/input_mocking.rs index fc397ca2..84e0d483 100644 --- a/src/input_mocking.rs +++ b/src/input_mocking.rs @@ -10,7 +10,7 @@ use crate::axislike::{AxisType, MouseMotionAxisType, MouseWheelAxisType}; use crate::buttonlike::{MouseMotionDirection, MouseWheelDirection}; use crate::input_streams::{InputStreams, MutableInputStreams}; -use crate::user_input::UserInput; +use crate::user_input::{RawInputs, UserInput}; use bevy::app::App; use bevy::ecs::event::Events; @@ -19,6 +19,7 @@ use bevy::ecs::world::World; #[cfg(feature = "ui")] use bevy::ecs::{component::Component, query::With, system::Query}; use bevy::input::gamepad::{GamepadAxisChangedEvent, GamepadButtonChangedEvent}; +use bevy::input::keyboard::{Key, NativeKey}; use bevy::input::mouse::MouseScrollUnit; use bevy::input::ButtonState; use bevy::input::{ @@ -26,7 +27,7 @@ use bevy::input::{ keyboard::{KeyCode, KeyboardInput}, mouse::{MouseButton, MouseButtonInput, MouseMotion, MouseWheel}, touch::{TouchInput, Touches}, - Input, + ButtonInput, }; use bevy::math::Vec2; use bevy::prelude::Entity; @@ -50,7 +51,7 @@ use bevy::window::CursorMoved; /// app.add_plugins(InputPlugin); /// /// // Pay respects! -/// app.send_input(KeyCode::F); +/// app.send_input(KeyCode::KeyF); /// app.update(); /// ``` /// @@ -63,7 +64,7 @@ use bevy::window::CursorMoved; /// app.add_plugins(InputPlugin); /// /// // Send inputs one at a time -/// let B_E_V_Y = [KeyCode::B, KeyCode::E, KeyCode::V, KeyCode::Y]; +/// let B_E_V_Y = [KeyCode::KeyB, KeyCode::KeyE, KeyCode::KeyV, KeyCode::KeyY]; /// /// for letter in B_E_V_Y { /// app.send_input(letter); @@ -76,7 +77,7 @@ use bevy::window::CursorMoved; pub trait MockInput { /// Send the specified `user_input` directly /// - /// These are sent as the raw input events, and do not set the value of [`Input`] or [`Axis`](bevy::input::Axis) directly. + /// These are sent as the raw input events, and do not set the value of [`ButtonInput`] or [`Axis`](bevy::input::Axis) directly. /// Note that inputs will continue to be pressed until explicitly released or [`MockInput::reset_inputs`] is called. /// /// To send specific values for axislike inputs, set their `value` field. @@ -88,7 +89,7 @@ pub trait MockInput { /// /// You *must* call `app.update()` at least once after sending input /// with `InputPlugin` included in your plugin set - /// for the raw input events to be processed into [`Input`] and [`Axis`](bevy::input::Axis) data. + /// for the raw input events to be processed into [`ButtonInput`] and [`Axis`](bevy::input::Axis) data. fn send_input(&mut self, input: impl Into); /// Send the specified `user_input` directly, using the specified gamepad @@ -111,7 +112,7 @@ pub trait MockInput { /// Clears all user input streams, resetting them to their default state /// - /// All buttons are released, and `just_pressed` and `just_released` information on the [`Input`] type are lost. + /// All buttons are released, and `just_pressed` and `just_released` information on the [`ButtonInput`] type are lost. /// `just_pressed` and `just_released` on the [`ActionState`](crate::action_state::ActionState) will be kept. /// /// This will clear all [`KeyCode`], [`GamepadButton`] and [`MouseButton`] input streams, @@ -119,20 +120,20 @@ pub trait MockInput { fn reset_inputs(&mut self); } -/// Query [`Input`] state directly for testing purposes. +/// Query [`ButtonInput`] state directly for testing purposes. /// /// In game code, you should (almost) always be using [`ActionState`](crate::action_state::ActionState) /// methods instead. pub trait QueryInput { /// Is the provided `user_input` pressed? /// - /// This method is intended as a convenience for testing; check the [`Input`] resource directly, + /// This method is intended as a convenience for testing; check the [`ButtonInput`] resource directly, /// or use an [`InputMap`](crate::input_map::InputMap) in real code. fn pressed(&self, input: impl Into) -> bool; /// Is the provided `user_input` pressed for the provided [`Gamepad`]? /// - /// This method is intended as a convenience for testing; check the [`Input`] resource directly, + /// This method is intended as a convenience for testing; check the [`ButtonInput`] resource directly, /// or use an [`InputMap`](crate::input_map::InputMap) in real code. fn pressed_for_gamepad(&self, input: impl Into, gamepad: Option) -> bool; } @@ -161,129 +162,60 @@ impl MockInput for MutableInputStreams<'_> { // Extract the raw inputs let raw_inputs = input_to_send.raw_inputs(); - // Keyboard buttons - for button in raw_inputs.keycodes { - self.keyboard_events.send(KeyboardInput { - scan_code: u32::MAX, - key_code: Some(button), - state: ButtonState::Pressed, - window: Entity::PLACEHOLDER, - }); - } + self.send_keyboard_input(ButtonState::Pressed, &raw_inputs); // Mouse buttons - for button in raw_inputs.mouse_buttons { + for button in raw_inputs.mouse_buttons.iter() { self.mouse_button_events.send(MouseButtonInput { - button, + button: *button, state: ButtonState::Pressed, window: Entity::PLACEHOLDER, }); } // Discrete mouse wheel events - for mouse_wheel_direction in raw_inputs.mouse_wheel { - match mouse_wheel_direction { - MouseWheelDirection::Left => self.mouse_wheel.send(MouseWheel { - unit: MouseScrollUnit::Pixel, - x: -1.0, - y: 0.0, - window: Entity::PLACEHOLDER, - }), - MouseWheelDirection::Right => self.mouse_wheel.send(MouseWheel { - unit: MouseScrollUnit::Pixel, - x: 1.0, - y: 0.0, - window: Entity::PLACEHOLDER, - }), - MouseWheelDirection::Up => self.mouse_wheel.send(MouseWheel { - unit: MouseScrollUnit::Pixel, - x: 0.0, - y: 1.0, - window: Entity::PLACEHOLDER, - }), - MouseWheelDirection::Down => self.mouse_wheel.send(MouseWheel { - unit: MouseScrollUnit::Pixel, - x: 0.0, - y: -1.0, - window: Entity::PLACEHOLDER, - }), - } + for mouse_wheel_direction in raw_inputs.mouse_wheel.iter() { + match *mouse_wheel_direction { + MouseWheelDirection::Left => self.send_mouse_wheel(-1.0, 0.0), + MouseWheelDirection::Right => self.send_mouse_wheel(1.0, 0.0), + MouseWheelDirection::Up => self.send_mouse_wheel(0.0, 1.0), + MouseWheelDirection::Down => self.send_mouse_wheel(0.0, -1.0), + }; } // Discrete mouse motion event - for mouse_motion_direction in raw_inputs.mouse_motion { - match mouse_motion_direction { - MouseMotionDirection::Up => self.mouse_motion.send(MouseMotion { - delta: Vec2 { x: 0.0, y: 1.0 }, - }), - MouseMotionDirection::Down => self.mouse_motion.send(MouseMotion { - delta: Vec2 { x: 0.0, y: -1.0 }, - }), - MouseMotionDirection::Right => self.mouse_motion.send(MouseMotion { - delta: Vec2 { x: 1.0, y: 0.0 }, - }), - MouseMotionDirection::Left => self.mouse_motion.send(MouseMotion { - delta: Vec2 { x: -1.0, y: 0.0 }, - }), - } + for mouse_motion_direction in raw_inputs.mouse_motion.iter() { + match *mouse_motion_direction { + MouseMotionDirection::Up => self.send_mouse_motion(0.0, 1.0), + MouseMotionDirection::Down => self.send_mouse_motion(0.0, -1.0), + MouseMotionDirection::Right => self.send_mouse_motion(1.0, 0.0), + MouseMotionDirection::Left => self.send_mouse_motion(-1.0, 0.0), + }; } - // Gamepad buttons - for button_type in raw_inputs.gamepad_buttons { - if let Some(gamepad) = gamepad { - self.gamepad_events - .send(GamepadEvent::Button(GamepadButtonChangedEvent { - gamepad, - button_type, - value: 1.0, - })); - } - } + self.send_gamepad_button_changed(gamepad, &raw_inputs); // Axis data - for (outer_axis_type, maybe_position_data) in raw_inputs.axis_data { - if let Some(position_data) = maybe_position_data { + for (outer_axis_type, maybe_position_data) in raw_inputs.axis_data.iter() { + if let Some(position_data) = *maybe_position_data { match outer_axis_type { AxisType::Gamepad(axis_type) => { if let Some(gamepad) = gamepad { self.gamepad_events .send(GamepadEvent::Axis(GamepadAxisChangedEvent { gamepad, - axis_type, + axis_type: *axis_type, value: position_data, })); } } - AxisType::MouseWheel(axis_type) => { - match axis_type { - // FIXME: MouseScrollUnit is not recorded and is always assumed to be Pixel - MouseWheelAxisType::X => self.mouse_wheel.send(MouseWheel { - unit: MouseScrollUnit::Pixel, - x: position_data, - y: 0.0, - window: Entity::PLACEHOLDER, - }), - MouseWheelAxisType::Y => self.mouse_wheel.send(MouseWheel { - unit: MouseScrollUnit::Pixel, - x: 0.0, - y: position_data, - window: Entity::PLACEHOLDER, - }), - } - } - AxisType::MouseMotion(axis_type) => match axis_type { - MouseMotionAxisType::X => self.mouse_motion.send(MouseMotion { - delta: Vec2 { - x: position_data, - y: 0.0, - }, - }), - MouseMotionAxisType::Y => self.mouse_motion.send(MouseMotion { - delta: Vec2 { - x: 0.0, - y: position_data, - }, - }), + AxisType::MouseWheel(axis_type) => match *axis_type { + MouseWheelAxisType::X => self.send_mouse_wheel(position_data, 0.0), + MouseWheelAxisType::Y => self.send_mouse_wheel(0.0, position_data), + }, + AxisType::MouseMotion(axis_type) => match *axis_type { + MouseMotionAxisType::X => self.send_mouse_motion(position_data, 0.0), + MouseMotionAxisType::Y => self.send_mouse_motion(0.0, position_data), }, } } @@ -300,25 +232,9 @@ impl MockInput for MutableInputStreams<'_> { let input_to_release: UserInput = input.into(); let raw_inputs = input_to_release.raw_inputs(); - for button_type in raw_inputs.gamepad_buttons { - if let Some(gamepad) = gamepad { - self.gamepad_events - .send(GamepadEvent::Button(GamepadButtonChangedEvent { - gamepad, - button_type, - value: 1.0, - })); - } - } + self.send_gamepad_button_changed(gamepad, &raw_inputs); - for button in raw_inputs.keycodes { - self.keyboard_events.send(KeyboardInput { - scan_code: u32::MAX, - key_code: Some(button), - state: ButtonState::Released, - window: Entity::PLACEHOLDER, - }); - } + self.send_keyboard_input(ButtonState::Released, &raw_inputs); for button in raw_inputs.mouse_buttons { self.mouse_button_events.send(MouseButtonInput { @@ -342,6 +258,44 @@ impl MockInput for MutableInputStreams<'_> { } } +impl MutableInputStreams<'_> { + fn send_keyboard_input(&mut self, button_state: ButtonState, raw_inputs: &RawInputs) { + for key_code in raw_inputs.keycodes.iter() { + self.keyboard_events.send(KeyboardInput { + logical_key: Key::Unidentified(NativeKey::Unidentified), + key_code: *key_code, + state: button_state, + window: Entity::PLACEHOLDER, + }); + } + } + + fn send_mouse_wheel(&mut self, x: f32, y: f32) { + // FIXME: MouseScrollUnit is not recorded and is always assumed to be Pixel + let unit = MouseScrollUnit::Pixel; + let window = Entity::PLACEHOLDER; + self.mouse_wheel.send(MouseWheel { unit, x, y, window }); + } + + fn send_mouse_motion(&mut self, x: f32, y: f32) { + let delta = Vec2::new(x, y); + self.mouse_motion.send(MouseMotion { delta }); + } + + fn send_gamepad_button_changed(&mut self, gamepad: Option, raw_inputs: &RawInputs) { + for button_type in raw_inputs.gamepad_buttons.iter() { + if let Some(gamepad) = gamepad { + self.gamepad_events + .send(GamepadEvent::Button(GamepadButtonChangedEvent { + gamepad, + button_type: *button_type, + value: 1.0, + })); + } + } + } +} + impl QueryInput for InputStreams<'_> { fn pressed(&self, input: impl Into) -> bool { self.input_pressed(&input.into()) @@ -393,9 +347,9 @@ impl MockInput for World { } let mut input_system_state: SystemState<( - Option>>, - Option>>, - Option>>, + Option>>, + Option>>, + Option>>, )> = SystemState::new(self); let (maybe_gamepad, maybe_keyboard, maybe_mouse) = input_system_state.get_mut(self); @@ -501,7 +455,7 @@ impl MockUIInteraction for App { #[cfg(test)] mod test { - use crate::input_mocking::{MockInput, MockUIInteraction, QueryInput}; + use crate::input_mocking::{MockInput, QueryInput}; use bevy::{ input::{ gamepad::{GamepadConnection, GamepadConnectionEvent, GamepadEvent, GamepadInfo}, @@ -525,7 +479,7 @@ mod test { app.update(); // Verify that checking the resource value directly works - let keyboard_input: &Input = app.world.resource(); + let keyboard_input: &ButtonInput = app.world.resource(); assert!(keyboard_input.pressed(KeyCode::Space)); // Test the convenient .pressed API @@ -564,7 +518,7 @@ mod test { // Checking the old-fashioned way // FIXME: put this in a gamepad_button.rs integration test. - let gamepad_input = app.world.resource::>(); + let gamepad_input = app.world.resource::>(); assert!(gamepad_input.pressed(GamepadButton { gamepad, button_type: GamepadButtonType::North, @@ -615,6 +569,7 @@ mod test { #[test] #[cfg(feature = "ui")] fn ui_inputs() { + use crate::input_mocking::MockUIInteraction; use bevy::ecs::prelude::*; use bevy::ui::Interaction; diff --git a/src/input_streams.rs b/src/input_streams.rs index 2503074a..3f999abf 100644 --- a/src/input_streams.rs +++ b/src/input_streams.rs @@ -1,42 +1,41 @@ //! Unified input streams for working with [`bevy::input`] data. -use bevy::ecs::prelude::{Events, ResMut, World}; +use bevy::ecs::prelude::{Event, Events, ResMut, World}; use bevy::ecs::system::SystemState; use bevy::input::{ gamepad::{Gamepad, GamepadAxis, GamepadButton, GamepadEvent, Gamepads}, - keyboard::{KeyCode, KeyboardInput, ScanCode}, + keyboard::{KeyCode, KeyboardInput}, mouse::{MouseButton, MouseButtonInput, MouseMotion, MouseWheel}, - Axis, Input, + Axis, ButtonInput, }; +use bevy::math::Vec2; use bevy::utils::HashSet; use crate::axislike::{ - AxisType, DualAxisData, MouseMotionAxisType, MouseWheelAxisType, SingleAxis, VirtualAxis, - VirtualDPad, + deadzone_axis_value, AxisType, DualAxisData, MouseMotionAxisType, MouseWheelAxisType, + SingleAxis, VirtualAxis, }; use crate::buttonlike::{MouseMotionDirection, MouseWheelDirection}; use crate::prelude::DualAxis; use crate::user_input::{InputKind, UserInput}; -/// A collection of [`Input`] structs, which can be used to update an [`InputMap`](crate::input_map::InputMap). +/// A collection of [`ButtonInput`] structs, which can be used to update an [`InputMap`](crate::input_map::InputMap). /// /// These are typically collected via a system from the [`World`] as resources. #[derive(Debug, Clone)] pub struct InputStreams<'a> { - /// A [`GamepadButton`] [`Input`] stream - pub gamepad_buttons: &'a Input, + /// A [`GamepadButton`] [`Input`](ButtonInput) stream + pub gamepad_buttons: &'a ButtonInput, /// A [`GamepadButton`] [`Axis`] stream pub gamepad_button_axes: &'a Axis, /// A [`GamepadAxis`] [`Axis`] stream pub gamepad_axes: &'a Axis, /// A list of registered gamepads pub gamepads: &'a Gamepads, - /// A [`KeyCode`] [`Input`] stream - pub keycodes: Option<&'a Input>, - /// A [`ScanCode`] [`Input`] stream - pub scan_codes: Option<&'a Input>, - /// A [`MouseButton`] [`Input`] stream - pub mouse_buttons: Option<&'a Input>, + /// A [`KeyCode`] [`ButtonInput`] stream + pub keycodes: Option<&'a ButtonInput>, + /// A [`MouseButton`] [`Input`](ButtonInput) stream + pub mouse_buttons: Option<&'a ButtonInput>, /// A [`MouseWheel`] event stream pub mouse_wheel: Option>, /// A [`MouseMotion`] event stream @@ -49,26 +48,17 @@ pub struct InputStreams<'a> { impl<'a> InputStreams<'a> { /// Construct an [`InputStreams`] from a [`World`] pub fn from_world(world: &'a World, gamepad: Option) -> Self { - let gamepad_buttons = world.resource::>(); + let gamepad_buttons = world.resource::>(); let gamepad_button_axes = world.resource::>(); let gamepad_axes = world.resource::>(); let gamepads = world.resource::(); - let keycodes = world.get_resource::>(); - let scan_codes = world.get_resource::>(); - let mouse_buttons = world.get_resource::>(); + let keycodes = world.get_resource::>(); + let mouse_buttons = world.get_resource::>(); let mouse_wheel = world.resource::>(); let mouse_motion = world.resource::>(); - let mouse_wheel: Vec = mouse_wheel - .get_reader() - .read(mouse_wheel) - .cloned() - .collect(); - let mouse_motion: Vec = mouse_motion - .get_reader() - .read(mouse_motion) - .cloned() - .collect(); + let mouse_wheel: Vec = collect_events_cloned(mouse_wheel); + let mouse_motion: Vec = collect_events_cloned(mouse_motion); InputStreams { gamepad_buttons, @@ -76,7 +66,6 @@ impl<'a> InputStreams<'a> { gamepad_axes, gamepads, keycodes, - scan_codes, mouse_buttons, mouse_wheel: Some(mouse_wheel), mouse_motion, @@ -92,19 +81,9 @@ impl<'a> InputStreams<'a> { match input { UserInput::Single(button) => self.button_pressed(*button), UserInput::Chord(buttons) => self.all_buttons_pressed(buttons), - UserInput::VirtualDPad(VirtualDPad { - up, - down, - left, - right, - }) => { - for button in [up, down, left, right] { - if self.button_pressed(*button) { - return true; - } - } - false - } + UserInput::VirtualDPad(dpad) => [&dpad.up, &dpad.down, &dpad.left, &dpad.right] + .into_iter() + .any(|button| self.button_pressed(*button)), UserInput::VirtualAxis(VirtualAxis { negative, positive }) => { self.button_pressed(*negative) || self.button_pressed(*positive) } @@ -114,61 +93,32 @@ impl<'a> InputStreams<'a> { /// Is at least one of the `inputs` pressed? #[must_use] pub fn any_pressed(&self, inputs: &HashSet) -> bool { - for input in inputs.iter() { - if self.input_pressed(input) { - return true; - } - } - // If none of the inputs matched, return false - false + inputs.iter().any(|input| self.input_pressed(input)) } /// Is the `button` pressed? #[must_use] pub fn button_pressed(&self, button: InputKind) -> bool { match button { - InputKind::DualAxis(axis) => { - let x_value = - self.input_value(&UserInput::Single(InputKind::SingleAxis(axis.x)), false); - let y_value = - self.input_value(&UserInput::Single(InputKind::SingleAxis(axis.y)), false); - - axis.deadzone - .deadzone_input_value(x_value, y_value) - .is_some() - } + InputKind::DualAxis(axis) => self.extract_dual_axis_data(&axis).is_some(), InputKind::SingleAxis(axis) => { - let value = self.input_value(&UserInput::Single(button), false); + let value = self.input_value(&button.into(), false); value < axis.negative_low || value > axis.positive_low } - InputKind::GamepadButton(gamepad_button) => { - if let Some(gamepad) = self.associated_gamepad { + InputKind::GamepadButton(button_type) => self + .associated_gamepad + .into_iter() + .chain(self.gamepads.iter()) + .any(|gamepad| { self.gamepad_buttons.pressed(GamepadButton { gamepad, - button_type: gamepad_button, + button_type, }) - } else { - for gamepad in self.gamepads.iter() { - if self.gamepad_buttons.pressed(GamepadButton { - gamepad, - button_type: gamepad_button, - }) { - // Return early if *any* gamepad is pressing this button - return true; - } - } - - // If we don't have the required data, fall back to false - false - } - } - InputKind::Keyboard(keycode) => { + }), + InputKind::PhysicalKey(keycode) => { matches!(self.keycodes, Some(keycodes) if keycodes.pressed(keycode)) } - InputKind::KeyLocation(scan_code) => { - matches!(self.scan_codes, Some(scan_codes) if scan_codes.pressed(scan_code)) - } InputKind::Modifier(modifier) => { let key_codes = modifier.key_codes(); // Short circuiting is probably not worth the branch here @@ -182,51 +132,30 @@ impl<'a> InputStreams<'a> { return false; }; - let mut total_mouse_wheel_movement = 0.0; - + // The compiler will compile this into a direct f64 accumulation when opt-level >= 1. + // // PERF: this summing is computed for every individual input // This should probably be computed once, and then cached / read // Fix upstream! - for mouse_wheel_event in mouse_wheel { - total_mouse_wheel_movement += match mouse_wheel_direction { - MouseWheelDirection::Up | MouseWheelDirection::Down => mouse_wheel_event.y, - MouseWheelDirection::Left | MouseWheelDirection::Right => { - mouse_wheel_event.x - } - } - } - + let Vec2 { x, y } = mouse_wheel + .iter() + .map(|wheel| Vec2::new(wheel.x, wheel.y)) + .sum(); match mouse_wheel_direction { - MouseWheelDirection::Up | MouseWheelDirection::Right => { - total_mouse_wheel_movement > 0.0 - } - MouseWheelDirection::Down | MouseWheelDirection::Left => { - total_mouse_wheel_movement < 0.0 - } + MouseWheelDirection::Up => y > 0.0, + MouseWheelDirection::Down => y < 0.0, + MouseWheelDirection::Left => x < 0.0, + MouseWheelDirection::Right => x > 0.0, } } - // CLEANUP: refactor to share code with MouseWheel InputKind::MouseMotion(mouse_motion_direction) => { - let mut total_mouse_movement = 0.0; - - for mouse_motion_event in &self.mouse_motion { - total_mouse_movement += match mouse_motion_direction { - MouseMotionDirection::Up | MouseMotionDirection::Down => { - mouse_motion_event.delta.y - } - MouseMotionDirection::Left | MouseMotionDirection::Right => { - mouse_motion_event.delta.x - } - } - } - + // The compiler will compile this into a direct f64 accumulation when opt-level >= 1. + let Vec2 { x, y } = self.mouse_motion.iter().map(|motion| motion.delta).sum(); match mouse_motion_direction { - MouseMotionDirection::Up | MouseMotionDirection::Right => { - total_mouse_movement > 0.0 - } - MouseMotionDirection::Down | MouseMotionDirection::Left => { - total_mouse_movement < 0.0 - } + MouseMotionDirection::Up => y > 0.0, + MouseMotionDirection::Down => y < 0.0, + MouseMotionDirection::Left => x < 0.0, + MouseMotionDirection::Right => x > 0.0, } } } @@ -235,14 +164,7 @@ impl<'a> InputStreams<'a> { /// Are all of the `buttons` pressed? #[must_use] pub fn all_buttons_pressed(&self, buttons: &[InputKind]) -> bool { - for &button in buttons.iter() { - // If any of the appropriate inputs failed to match, the action is considered pressed - if !self.button_pressed(button) { - return false; - } - } - // If none of the inputs failed to match, return true - true + buttons.iter().all(|button| self.button_pressed(*button)) } /// Get the "value" of the input. @@ -258,13 +180,7 @@ impl<'a> InputStreams<'a> { /// If you need to ensure that this value is always in the range `[-1., 1.]`, /// be sure to clamp the returned data. pub fn input_value(&self, input: &UserInput, include_deadzone: bool) -> f32 { - let use_button_value = || -> f32 { - if self.input_pressed(input) { - 1.0 - } else { - 0.0 - } - }; + let use_button_value = || -> f32 { f32::from(self.input_pressed(input)) }; // Helper that takes the value returned by an axis and returns 0.0 if it is not within the // triggering range. @@ -274,12 +190,12 @@ impl<'a> InputStreams<'a> { return 0.0; } - let width = if value.is_sign_positive() { + let deadzone = if value.is_sign_positive() { axis.positive_low.abs() } else { axis.negative_low.abs() }; - value = value.signum() * (value.abs() - width).max(0.0) / (1.0 - width); + value = deadzone_axis_value(value, deadzone); } if axis.inverted { value *= -1.0; @@ -292,28 +208,20 @@ impl<'a> InputStreams<'a> { UserInput::Single(InputKind::SingleAxis(single_axis)) => { match single_axis.axis_type { AxisType::Gamepad(axis_type) => { - if let Some(gamepad) = self.associated_gamepad { - let value = self - .gamepad_axes + let get_gamepad_value = |gamepad: Gamepad| -> f32 { + self.gamepad_axes .get(GamepadAxis { gamepad, axis_type }) - .unwrap_or_default(); - + .unwrap_or_default() + }; + if let Some(gamepad) = self.associated_gamepad { + let value = get_gamepad_value(gamepad); value_in_axis_range(single_axis, value) } else { - for gamepad in self.gamepads.iter() { - let value = self - .gamepad_axes - .get(GamepadAxis { gamepad, axis_type }) - .unwrap_or_default(); - - // Return early if *any* gamepad is pressing this axis - if value != 0.0 { - return value_in_axis_range(single_axis, value); - } - } - - // If we don't have the required data, fall back to 0.0 - 0.0 + self.gamepads + .iter() + .map(get_gamepad_value) + .find(|value| *value != 0.0) + .map_or(0.0, |value| value_in_axis_range(single_axis, value)) } } AxisType::MouseWheel(axis_type) => { @@ -321,33 +229,30 @@ impl<'a> InputStreams<'a> { return 0.0; }; - let mut total_mouse_wheel_movement = 0.0; - - for mouse_wheel_event in mouse_wheel { - total_mouse_wheel_movement += match axis_type { - MouseWheelAxisType::X => mouse_wheel_event.x, - MouseWheelAxisType::Y => mouse_wheel_event.y, - } - } - value_in_axis_range(single_axis, total_mouse_wheel_movement) + // The compiler will compile this into a direct f64 accumulation when opt-level >= 1. + let Vec2 { x, y } = mouse_wheel + .iter() + .map(|wheel| Vec2::new(wheel.x, wheel.y)) + .sum(); + let movement = match axis_type { + MouseWheelAxisType::X => x, + MouseWheelAxisType::Y => y, + }; + value_in_axis_range(single_axis, movement) } - // CLEANUP: deduplicate code with MouseWheel AxisType::MouseMotion(axis_type) => { - let mut total_mouse_motion_movement = 0.0; - - for mouse_wheel_event in &self.mouse_motion { - total_mouse_motion_movement += match axis_type { - MouseMotionAxisType::X => mouse_wheel_event.delta.x, - MouseMotionAxisType::Y => mouse_wheel_event.delta.y, - } - } - value_in_axis_range(single_axis, total_mouse_motion_movement) + // The compiler will compile this into a direct f64 accumulation when opt-level >= 1. + let Vec2 { x, y } = self.mouse_motion.iter().map(|e| e.delta).sum(); + let movement = match axis_type { + MouseMotionAxisType::X => x, + MouseMotionAxisType::Y => y, + }; + value_in_axis_range(single_axis, movement) } } } - UserInput::VirtualAxis(VirtualAxis { negative, positive }) => { - self.input_value(&UserInput::Single(*positive), true).abs() - - self.input_value(&UserInput::Single(*negative), true).abs() + UserInput::VirtualAxis(axis) => { + self.extract_single_axis_data(&axis.positive, &axis.negative) } UserInput::Single(InputKind::DualAxis(_)) => { self.input_axis_pair(input).unwrap_or_default().length() @@ -364,18 +269,15 @@ impl<'a> InputStreams<'a> { value += match input { InputKind::SingleAxis(axis) => { has_axis = true; - self.input_value(&UserInput::Single(InputKind::SingleAxis(*axis)), true) + self.input_value(&InputKind::SingleAxis(*axis).into(), true) } InputKind::MouseWheel(axis) => { has_axis = true; - self.input_value(&UserInput::Single(InputKind::MouseWheel(*axis)), true) + self.input_value(&InputKind::MouseWheel(*axis).into(), true) } InputKind::MouseMotion(axis) => { has_axis = true; - self.input_value( - &UserInput::Single(InputKind::MouseMotion(*axis)), - true, - ) + self.input_value(&InputKind::MouseMotion(*axis).into(), true) } _ => 0.0, } @@ -389,32 +291,22 @@ impl<'a> InputStreams<'a> { } // This is required because upstream bevy::input still waffles about whether triggers are buttons or axes UserInput::Single(InputKind::GamepadButton(button_type)) => { - if let Some(gamepad) = self.associated_gamepad { - // Get the value from the registered gamepad + let get_gamepad_value = |gamepad: Gamepad| -> f32 { self.gamepad_button_axes .get(GamepadButton { gamepad, button_type: *button_type, }) .unwrap_or_else(use_button_value) + }; + if let Some(gamepad) = self.associated_gamepad { + get_gamepad_value(gamepad) } else { - for gamepad in self.gamepads.iter() { - let value = self - .gamepad_button_axes - .get(GamepadButton { - gamepad, - button_type: *button_type, - }) - .unwrap_or_else(use_button_value); - - // Return early if *any* gamepad is pressing this button - if value != 0.0 { - return value; - } - } - - // If we don't have the required data, fall back to 0.0 - 0.0 + self.gamepads + .iter() + .map(get_gamepad_value) + .find(|value| *value != 0.0) + .unwrap_or_default() } } _ => use_button_value(), @@ -424,8 +316,7 @@ impl<'a> InputStreams<'a> { /// Get the axis pair associated to the user input. /// /// If `input` is a chord, returns result of the first dual axis in the chord. - - /// If `input` is not a [`DualAxis`] or [`VirtualDPad`], returns [`None`]. + /// If `input` is not a [`DualAxis`] or [`VirtualDPad`](crate::axislike::VirtualDPad), returns [`None`]. /// /// # Warning /// @@ -450,44 +341,44 @@ impl<'a> InputStreams<'a> { UserInput::Single(InputKind::DualAxis(dual_axis)) => { Some(self.extract_dual_axis_data(dual_axis).unwrap_or_default()) } - UserInput::VirtualDPad(VirtualDPad { - up, - down, - left, - right, - }) => { - let x = self.input_value(&UserInput::Single(*right), true).abs() - - self.input_value(&UserInput::Single(*left), true).abs(); - let y = self.input_value(&UserInput::Single(*up), true).abs() - - self.input_value(&UserInput::Single(*down), true).abs(); + UserInput::VirtualDPad(dpad) => { + let x = self.extract_single_axis_data(&dpad.right, &dpad.left); + let y = self.extract_single_axis_data(&dpad.up, &dpad.down); Some(DualAxisData::new(x, y)) } _ => None, } } + fn extract_single_axis_data(&self, positive: &InputKind, negative: &InputKind) -> f32 { + let positive = self.input_value(&UserInput::Single(*positive), true); + let negative = self.input_value(&UserInput::Single(*negative), true); + + positive.abs() - negative.abs() + } + fn extract_dual_axis_data(&self, dual_axis: &DualAxis) -> Option { - let x = self.input_value( - &UserInput::Single(InputKind::SingleAxis(dual_axis.x)), - false, - ); - let y = self.input_value( - &UserInput::Single(InputKind::SingleAxis(dual_axis.y)), - false, - ); + let x = self.input_value(&dual_axis.x.into(), false); + let y = self.input_value(&dual_axis.y.into(), false); dual_axis.deadzone.deadzone_input_value(x, y) } } -/// A mutable collection of [`Input`] structs, which can be used for mocking user inputs. +// Clones and collects the received events into a `Vec`. +#[inline] +fn collect_events_cloned(events: &Events) -> Vec { + events.get_reader().read(events).cloned().collect() +} + +/// A mutable collection of [`ButtonInput`] structs, which can be used for mocking user inputs. /// /// These are typically collected via a system from the [`World`] as resources. // WARNING: If you update the fields of this type, you must also remember to update `InputMocking::reset_inputs`. #[derive(Debug)] pub struct MutableInputStreams<'a> { - /// A [`GamepadButton`] [`Input`] stream - pub gamepad_buttons: &'a mut Input, + /// A [`GamepadButton`] [`Input`](ButtonInput) stream + pub gamepad_buttons: &'a mut ButtonInput, /// A [`GamepadButton`] [`Axis`] stream pub gamepad_button_axes: &'a mut Axis, /// A [`GamepadAxis`] [`Axis`] stream @@ -497,15 +388,13 @@ pub struct MutableInputStreams<'a> { /// Events used for mocking gamepad-related inputs pub gamepad_events: &'a mut Events, - /// A [`KeyCode`] [`Input`] stream - pub keycodes: &'a mut Input, - /// A [`ScanCode`] [`Input`] stream - pub scan_codes: &'a mut Input, + /// A [`KeyCode`] [`ButtonInput`] stream + pub keycodes: &'a mut ButtonInput, /// Events used for mocking keyboard-related inputs pub keyboard_events: &'a mut Events, - /// A [`MouseButton`] [`Input`] stream - pub mouse_buttons: &'a mut Input, + /// A [`MouseButton`] [`Input`](ButtonInput) stream + pub mouse_buttons: &'a mut ButtonInput, /// Events used for mocking [`MouseButton`] inputs pub mouse_button_events: &'a mut Events, /// A [`MouseWheel`] event stream @@ -521,15 +410,14 @@ impl<'a> MutableInputStreams<'a> { /// Construct a [`MutableInputStreams`] from the [`World`] pub fn from_world(world: &'a mut World, gamepad: Option) -> Self { let mut input_system_state: SystemState<( - ResMut>, + ResMut>, ResMut>, ResMut>, ResMut, ResMut>, - ResMut>, - ResMut>, + ResMut>, ResMut>, - ResMut>, + ResMut>, ResMut>, ResMut>, ResMut>, @@ -542,7 +430,6 @@ impl<'a> MutableInputStreams<'a> { gamepads, gamepad_events, keycodes, - scan_codes, keyboard_events, mouse_buttons, mouse_button_events, @@ -557,7 +444,6 @@ impl<'a> MutableInputStreams<'a> { gamepads: gamepads.into_inner(), gamepad_events: gamepad_events.into_inner(), keycodes: keycodes.into_inner(), - scan_codes: scan_codes.into_inner(), keyboard_events: keyboard_events.into_inner(), mouse_buttons: mouse_buttons.into_inner(), mouse_button_events: mouse_button_events.into_inner(), @@ -587,22 +473,9 @@ impl<'a> From> for InputStreams<'a> { gamepad_axes: mutable_streams.gamepad_axes, gamepads: mutable_streams.gamepads, keycodes: Some(mutable_streams.keycodes), - scan_codes: Some(mutable_streams.scan_codes), mouse_buttons: Some(mutable_streams.mouse_buttons), - mouse_wheel: Some( - mutable_streams - .mouse_wheel - .get_reader() - .read(mutable_streams.mouse_wheel) - .cloned() - .collect(), - ), - mouse_motion: mutable_streams - .mouse_motion - .get_reader() - .read(mutable_streams.mouse_motion) - .cloned() - .collect(), + mouse_wheel: Some(collect_events_cloned(mutable_streams.mouse_wheel)), + mouse_motion: collect_events_cloned(mutable_streams.mouse_motion), associated_gamepad: mutable_streams.associated_gamepad, } } @@ -616,22 +489,9 @@ impl<'a> From<&'a MutableInputStreams<'a>> for InputStreams<'a> { gamepad_axes: mutable_streams.gamepad_axes, gamepads: mutable_streams.gamepads, keycodes: Some(mutable_streams.keycodes), - scan_codes: Some(mutable_streams.scan_codes), mouse_buttons: Some(mutable_streams.mouse_buttons), - mouse_wheel: Some( - mutable_streams - .mouse_wheel - .get_reader() - .read(mutable_streams.mouse_wheel) - .cloned() - .collect(), - ), - mouse_motion: mutable_streams - .mouse_motion - .get_reader() - .read(mutable_streams.mouse_motion) - .cloned() - .collect(), + mouse_wheel: Some(collect_events_cloned(mutable_streams.mouse_wheel)), + mouse_motion: collect_events_cloned(mutable_streams.mouse_motion), associated_gamepad: mutable_streams.associated_gamepad, } } diff --git a/src/lib.rs b/src/lib.rs index c78d4348..c9113b50 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,7 +23,6 @@ pub mod input_mocking; pub mod input_streams; pub mod orientation; pub mod plugin; -pub mod scan_codes; pub mod systems; pub mod timing; pub mod user_input; @@ -44,7 +43,6 @@ pub mod prelude { #[cfg(feature = "ui")] pub use crate::input_mocking::MockUIInteraction; pub use crate::input_mocking::{MockInput, QueryInput}; - pub use crate::scan_codes::QwertyScanCode; pub use crate::user_input::{Modifier, UserInput}; pub use crate::plugin::InputManagerPlugin; diff --git a/src/orientation.rs b/src/orientation.rs index 592f5c53..8c2aaf39 100644 --- a/src/orientation.rs +++ b/src/orientation.rs @@ -651,8 +651,9 @@ mod conversions { // for 1.0 and -1.0 can't be represented exactly, so our unit vectors start with an // approximate value and both `atan2` above and `from_radians` below magnify the // imprecision. So, we cheat. - const APPROX_SOUTH: f32 = -1.5707964; - const APPROX_NORTHWEST: f32 = 2.3561945; + use std::f32::consts::FRAC_PI_2; + const APPROX_SOUTH: f32 = -FRAC_PI_2; + const APPROX_NORTHWEST: f32 = 1.5 * FRAC_PI_2; if radians == APPROX_NORTHWEST { Rotation::from_degrees_int(135) } else if radians == APPROX_SOUTH { diff --git a/src/plugin.rs b/src/plugin.rs index 858ee47e..5e8ff2aa 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -23,7 +23,7 @@ use bevy::reflect::TypePath; #[cfg(feature = "ui")] use bevy::ui::UiSystem; -/// A [`Plugin`] that collects [`Input`](bevy::input::Input) from disparate sources, producing an [`ActionState`] that can be conveniently checked +/// A [`Plugin`] that collects [`ButtonInput`](bevy::input::ButtonInput) from disparate sources, producing an [`ActionState`] that can be conveniently checked /// /// This plugin needs to be passed in an [`Actionlike`] enum type that you've created for your game. /// Each variant represents a "virtual button" whose state is stored in an [`ActionState`] struct. @@ -48,7 +48,7 @@ use bevy::ui::UiSystem; /// Complete list: /// /// - [`tick_action_state`](crate::systems::tick_action_state), which resets the `pressed` and `just_pressed` fields of the [`ActionState`] each frame -/// - [`update_action_state`](crate::systems::update_action_state), which collects [`Input`](bevy::input::Input) resources to update the [`ActionState`] +/// - [`update_action_state`](crate::systems::update_action_state), which collects [`ButtonInput`](bevy::input::ButtonInput) resources to update the [`ActionState`] /// - [`update_action_state_from_interaction`](crate::systems::update_action_state_from_interaction), for triggering actions from buttons /// - powers the [`ActionStateDriver`](crate::action_driver::ActionStateDriver) component based on an [`Interaction`](bevy::ui::Interaction) component /// - [`release_on_disable`](crate::systems::release_on_disable), which resets action states when [`ToggleActions`] is flipped, to avoid persistent presses. diff --git a/src/scan_codes/linux.rs b/src/scan_codes/linux.rs deleted file mode 100644 index 39abc88b..00000000 --- a/src/scan_codes/linux.rs +++ /dev/null @@ -1,278 +0,0 @@ -//! Helper enum to define scan codes on the QWERTY keyboard layout. - -/// The key locations as defined by the keys on the QWERTY keyboard layout. -/// -/// The [`u32`] representation of this enum are scan codes of the corresponding keys on X-like Linux systems. -/// See . -#[repr(u32)] -pub enum QwertyScanCode { - /// The location of the Escape/Esc key on the QWERTY keyboard layout. - Escape = 1, - /// The location of the `1` key on the QWERTY keyboard layout. - Key1 = 2, - /// The location of the `2` key on the QWERTY keyboard layout. - Key2 = 3, - /// The location of the `3` key on the QWERTY keyboard layout. - Key3 = 4, - /// The location of the `4` key on the QWERTY keyboard layout. - Key4 = 5, - /// The location of the `5` key on the QWERTY keyboard layout. - Key5 = 6, - /// The location of the `6` key on the QWERTY keyboard layout. - Key6 = 7, - /// The location of the `7` key on the QWERTY keyboard layout. - Key7 = 8, - /// The location of the `8` key on the QWERTY keyboard layout. - Key8 = 9, - /// The location of the `9` key on the QWERTY keyboard layout. - Key9 = 10, - /// The location of the `0` key on the QWERTY keyboard layout. - Key0 = 11, - /// The location of the `-` key on the QWERTY keyboard layout. - Minus = 12, - /// The location of the `=` key on the QWERTY keyboard layout. - Equals = 13, - /// The location of the back(space) key on the QWERTY keyboard layout. - Backspace = 14, - /// The location of the tabulator key on the QWERTY keyboard layout. - Tab = 15, - /// The location of the `Q` key on the QWERTY keyboard layout. - Q = 16, - /// The location of the `W` key on the QWERTY keyboard layout. - W = 17, - /// The location of the `E` key on the QWERTY keyboard layout. - E = 18, - /// The location of the `R` key on the QWERTY keyboard layout. - R = 19, - /// The location of the `T` key on the QWERTY keyboard layout. - T = 20, - /// The location of the `Y` key on the QWERTY keyboard layout. - Y = 21, - /// The location of the `U` key on the QWERTY keyboard layout. - U = 22, - /// The location of the `I` key on the QWERTY keyboard layout. - I = 23, - /// The location of the `O` key on the QWERTY keyboard layout. - O = 24, - /// The location of the `P` key on the QWERTY keyboard layout. - P = 25, - /// The location of the `[` key on the QWERTY keyboard layout. - BracketLeft = 26, - /// The location of the `]` key on the QWERTY keyboard layout. - BracketRight = 27, - /// The location of the Enter/Return key on the QWERTY keyboard layout. - Enter = 28, - /// The location of the left Control key on the QWERTY keyboard layout. - ControlLeft = 29, - /// The location of the `A` key on the QWERTY keyboard layout. - A = 30, - /// The location of the `S` key on the QWERTY keyboard layout. - S = 31, - /// The location of the `D` key on the QWERTY keyboard layout. - D = 32, - /// The location of the `F` key on the QWERTY keyboard layout. - F = 33, - /// The location of the `G` key on the QWERTY keyboard layout. - G = 34, - /// The location of the `H` key on the QWERTY keyboard layout. - H = 35, - /// The location of the `J` key on the QWERTY keyboard layout. - J = 36, - /// The location of the `K` key on the QWERTY keyboard layout. - K = 37, - /// The location of the `L` key on the QWERTY keyboard layout. - L = 38, - /// The location of the `;` key on the QWERTY keyboard layout. - SemiColon = 39, - /// The location of the `'` key on the QWERTY keyboard layout. - Apostrophe = 40, - /// The location of the `` ` `` key on the QWERTY keyboard layout. - Backtick = 41, - /// The location of the left Shift key on the QWERTY keyboard layout. - ShiftLeft = 42, - /// The location of the `\` on the QWERTY keyboard layout. - Backslash = 43, - /// The location of the `Z` key on the QWERTY keyboard layout. - Z = 44, - /// The location of the `X` key on the QWERTY keyboard layout. - X = 45, - /// The location of the `C` key on the QWERTY keyboard layout. - C = 46, - /// The location of the `V key on the QWERTY keyboard layout. - V = 47, - /// The location of the `B` key on the QWERTY keyboard layout. - B = 48, - /// The location of the `N` key on the QWERTY keyboard layout. - N = 49, - /// The location of the `M` key on the QWERTY keyboard layout. - M = 50, - /// The location of the `,` key on the QWERTY keyboard layout. - Comma = 51, - /// The location of the `.` key on the QWERTY keyboard layout. - Period = 52, - /// The location of the `/` key on the QWERTY keyboard layout. - Slash = 53, - /// The location of the right Shift key on the QWERTY keyboard layout. - ShiftRight = 54, - /// The location of the `*` key on the numpad of the QWERTY keyboard layout. - /// Maps to `NumpadDivide` on Apple keyboards. - NumpadMultiply = 55, - /// The location of the left Alt key on the QWERTY keyboard layout. - /// Maps to left Option key on Apple keyboards. - AltLeft = 56, - /// The location of the Space key on the QWERTY keyboard layout. - Space = 57, - /// The location of the caps lock key on the QWERTY keyboard layout. - CapsLock = 58, - /// The location of the `F1` key on the QWERTY keyboard layout. - F1 = 59, - /// The location of the `F2` key on the QWERTY keyboard layout. - F2 = 60, - /// The location of the `F3` key on the QWERTY keyboard layout. - F3 = 61, - /// The location of the `F4` key on the QWERTY keyboard layout. - F4 = 62, - /// The location of the `F5` key on the QWERTY keyboard layout. - F5 = 63, - /// The location of the `F6` key on the QWERTY keyboard layout. - F6 = 64, - /// The location of the `F7` key on the QWERTY keyboard layout. - F7 = 65, - /// The location of the `F8` key on the QWERTY keyboard layout. - F8 = 66, - /// The location of the `F9` key on the QWERTY keyboard layout. - F9 = 67, - /// The location of the `F10` key on the QWERTY keyboard layout. - F10 = 68, - /// The location of the Numlock key on the QWERTY keyboard layout. - /// Maps to `NumpadClear` on Apple keyboards. - Numlock = 69, - /// The location of the Scroll / Scroll Lock key on the QWERTY keyboard layout. - /// Maps to the `F14` key on Apple keyboards. - Scroll = 70, - /// The location of the `7` key on the numpad of the QWERTY keyboard layout. - Numpad7 = 71, - /// The location of the `8` key on the numpad of the QWERTY keyboard layout. - Numpad8 = 72, - /// The location of the `9` key on the numpad of the QWERTY keyboard layout. - Numpad9 = 73, - /// The location of the `*` key on the numpad of the QWERTY keyboard layout. - /// Maps to `NumpadMultiply` on Apple keyboards. - NumpadSubtract = 74, - /// The location of the `4` key on the numpad of the QWERTY keyboard layout. - Numpad4 = 75, - /// The location of the `5` key on the numpad of the QWERTY keyboard layout. - Numpad5 = 76, - /// The location of the `6` key on the numpad of the QWERTY keyboard layout. - Numpad6 = 77, - /// The location of the `+` key on the numpad of the QWERTY keyboard layout. - NumpadAdd = 78, - /// The location of the `1` key on the numpad of the QWERTY keyboard layout. - Numpad1 = 79, - /// The location of the `2` key on the numpad of the QWERTY keyboard layout. - Numpad2 = 80, - /// The location of the `3` key on the numpad of the QWERTY keyboard layout. - Numpad3 = 81, - /// The location of the `0` key on the numpad of the QWERTY keyboard layout. - Numpad0 = 82, - /// The location of the `.` key on the numpad of the QWERTY keyboard layout. - NumpadDecimal = 83, - // KEY_ZENKAKUHANKAKU = 85, - // KEY_102ND = 86, - /// The location of the `F11` key on the QWERTY keyboard layout. - F11 = 87, - /// The location of the `F12` key on the QWERTY keyboard layout. - F12 = 88, - // KEY_R0 = 89, - // KEY_KATAKANA = 90, - // KEY_HIRAGANA = 91, - // KEY_HENKAN = 92, - // KEY_KATAKANAHIRAGANA = 93, - // KEY_MUHENKAN = 94, - // KEY_KPJPCOMMA = 95, - /// The location of the Enter key on the numpad of the QWERTY keyboard layout. - NumpadEnter = 96, - /// The location of the right Control key on the QWERTY keyboard layout. - ControlRight = 97, - /// The location of the `/` key on the numpad of the QWERTY keyboard layout. - /// Maps to `NumpadEquals` on Apple keyboards. - NumpadDivide = 98, - /// The location of the Alt+Sysrq key on the QWERTY keyboard layout. - AltSysrq = 99, - /// The location of the right Alt key on the QWERTY keyboard layout. - /// Maps to right Option key on Apple keyboards. - AltRight = 100, - // KEY_LINEFEED = 101, - /// The location of the Home key on the QWERTY keyboard layout. - Home = 102, - /// The location of the Arrow Up key on the QWERTY keyboard layout. - Up = 103, - /// The location of the Page Up key on the QWERTY keyboard layout. - PageUp = 104, - /// The location of the Arrow Left key on the QWERTY keyboard layout. - Left = 105, - /// The location of the Arrow Right key on the QWERTY keyboard layout. - Right = 106, - /// The location of the End key on the QWERTY keyboard layout. - End = 107, - /// The location of the Arrow Down key on the QWERTY keyboard layout. - Down = 108, - /// The location of the Page Down key on the QWERTY keyboard layout. - PageDown = 109, - /// The location of the Insert key on the QWERTY keyboard layout. - /// Maps to the Help key on Apple keyboards. - Insert = 110, - /// The location of the Delete key on the QWERTY keyboard layout. - Delete = 111, - // KEY_MACRO = 112, - // KEY_MUTE = 113, - // KEY_VOLUMEDOWN = 114, - // KEY_VOLUMEDOWN = 115, - /// The location of the Power key on the QWERTY keyboard layout. - Power = 116, - // KEY_KPEQUAL = 117, - // KEY_KPPLUSMINUS = 118, - /// The location of the Pause key on the QWERTY keyboard layout. - /// Maps to the `F15` key on Apple keyboards. - Pause = 119, - // KEY_SCALE = 120, - // KEY_KPCOMMA = 121, - // KEY_HANGEUL = 122, - // KEY_HANJA = 123, - // KEY_YEN = 124, - /// The location of the left Windows key on the QWERTY keyboard layout. - /// Maps to the Command key on Apple keyboards. - SuperLeft = 125, - /// The location of the right Windows key on the QWERTY keyboard layout. - SuperRight = 126, - // KEY_COMPOSE = 127, - // KEY_STOP = 128, - // KEY_AGAIN = 129, - // KEY_PROPS = 130, - // KEY_UNDO = 131, - // KEY_FRONT = 132, - // KEY_COPY = 133, - // KEY_OPEN = 134, - // KEY_PASTE = 135, - // KEY_FIND = 136, - // KEY_CUT = 137, - // KEY_HELP = 138, - /// The location of the Menu key on the QWERTY keyboard layout. - Menu = 139, - // KEY_CALC = 140, - // KEY_SETUP = 141, - /// The location of the Sleep key on the QWERTY keyboard layout. - Sleep = 142, - /// The location of the Wake key on the QWERTY keyboard layout. - Wake = 143, - // Keys to figure out later: - // /// A key not available on the US QWERTY layout. - // /// - // /// This is for example the `#` key on other layouts. - // NonUs1 = 0x00, - // /// The location of the Snapshot / Print Screen key on the QWERTY keyboard layout. - // /// Maps to the `F13` key on Apple keyboards. - // Snapshot = 0xe0_37, - // /// The location of the Ctrl+Break key on the QWERTY keyboard layout. - // CtrlBreak = 0xe0_46, -} diff --git a/src/scan_codes/mac_os.rs b/src/scan_codes/mac_os.rs deleted file mode 100644 index d57b467d..00000000 --- a/src/scan_codes/mac_os.rs +++ /dev/null @@ -1,234 +0,0 @@ -//! Helper enum to define scan codes on the QWERTY keyboard layout. - -/// The key locations as defined by the keys on the QWERTY keyboard layout. -/// -/// The [`u32`] representation of this enum are the Mac OS scan codes of the corresponding keys. -/// See . -#[repr(u32)] -pub enum QwertyScanCode { - /// The location of the `A` key on the QWERTY keyboard layout. - A = 0x00, - /// The location of the `S` key on the QWERTY keyboard layout. - S = 0x01, - /// The location of the `D` key on the QWERTY keyboard layout. - D = 0x02, - /// The location of the `F` key on the QWERTY keyboard layout. - F = 0x03, - /// The location of the `H` key on the QWERTY keyboard layout. - H = 0x04, - /// The location of the `G` key on the QWERTY keyboard layout. - G = 0x05, - /// The location of the `Z` key on the QWERTY keyboard layout. - Z = 0x06, - /// The location of the `X` key on the QWERTY keyboard layout. - X = 0x07, - /// The location of the `C` key on the QWERTY keyboard layout. - C = 0x08, - /// The location of the `V` key on the QWERTY keyboard layout. - V = 0x09, - /// The location of the `B` key on the QWERTY keyboard layout. - B = 0x0b, - /// The location of the `Q` key on the QWERTY keyboard layout. - Q = 0x0c, - /// The location of the `W` key on the QWERTY keyboard layout. - W = 0x0d, - /// The location of the `E` key on the QWERTY keyboard layout. - E = 0x0e, - /// The location of the `R` key on the QWERTY keyboard layout. - R = 0x0f, - /// The location of the `Y` key on the QWERTY keyboard layout. - Y = 0x10, - /// The location of the `T` key on the QWERTY keyboard layout. - T = 0x11, - /// The location of the `1` key on the QWERTY keyboard layout. - Key1 = 0x12, - /// The location of the `2` key on the QWERTY keyboard layout. - Key2 = 0x13, - /// The location of the `3` key on the QWERTY keyboard layout. - Key3 = 0x14, - /// The location of the `4` key on the QWERTY keyboard layout. - Key4 = 0x15, - /// The location of the `6` key on the QWERTY keyboard layout. - Key6 = 0x16, - /// The location of the `5` key on the QWERTY keyboard layout. - Key5 = 0x17, - /// The location of the `=` key on the QWERTY keyboard layout. - Equals = 0x18, - /// The location of the `9` key on the QWERTY keyboard layout. - Key9 = 0x19, - /// The location of the `7` key on the QWERTY keyboard layout. - Key7 = 0x1a, - /// The location of the `-` key on the QWERTY keyboard layout. - Minus = 0x1b, - /// The location of the `8` key on the QWERTY keyboard layout. - Key8 = 0x1c, - /// The location of the `0` key on the QWERTY keyboard layout. - Key0 = 0x1d, - /// The location of the `]` key on the QWERTY keyboard layout. - BracketRight = 0x1e, - /// The location of the `O` key on the QWERTY keyboard layout. - O = 0x1f, - /// The location of the `U` key on the QWERTY keyboard layout. - U = 0x20, - /// The location of the `[` key on the QWERTY keyboard layout. - BracketLeft = 0x21, - /// The location of the `I` key on the QWERTY keyboard layout. - I = 0x22, - /// The location of the `P` key on the QWERTY keyboard layout. - P = 0x23, - /// The location of the `L` key on the QWERTY keyboard layout. - L = 0x25, - /// The location of the `J` key on the QWERTY keyboard layout. - J = 0x26, - /// The location of the `'` key on the QWERTY keyboard layout. - Apostrophe = 0x27, - /// The location of the `K` key on the QWERTY keyboard layout. - K = 0x28, - /// The location of the `;` key on the QWERTY keyboard layout. - SemiColon = 0x29, - /// The location of the `\` on the QWERTY keyboard layout. - Backslash = 0x2a, - /// The location of the `,` key on the QWERTY keyboard layout. - Comma = 0x2b, - /// The location of the `/` key on the QWERTY keyboard layout. - Slash = 0x2c, - /// The location of the `N` key on the QWERTY keyboard layout. - N = 0x2d, - /// The location of the `M` key on the QWERTY keyboard layout. - M = 0x2e, - /// The location of the `.` key on the QWERTY keyboard layout. - Period = 0x2f, - /// The location of the `` ` `` key on the QWERTY keyboard layout. - Backtick = 0x32, - /// The location of the `.` key on the numpad of the QWERTY keyboard layout. - NumpadDecimal = 0x41, - /// The location of the `*` key on the numpad of the QWERTY keyboard layout. - /// Maps to `NumpadMultiply` on Apple keyboards. - NumpadSubtract = 0x43, - /// The location of the `+` key on the numpad of the QWERTY keyboard layout. - NumpadAdd = 0x45, - /// The location of the Numlock key on the QWERTY keyboard layout. - /// Maps to `NumpadClear` on Apple keyboards. - Numlock = 0x47, - /// The location of the `*` key on the numpad of the QWERTY keyboard layout. - /// Maps to `NumpadDivide` on Apple keyboards. - NumpadMultiply = 0x4b, - /// The location of the Enter key on the numpad of the QWERTY keyboard layout. - NumpadEnter = 0x4c, - // This is an extra key on mac keyboards: NumpadSubtract = 0x4e, - /// The location of the `/` key on the numpad of the QWERTY keyboard layout. - /// Maps to `NumpadEquals` on Apple keyboards. - NumpadDivide = 0x51, - /// The location of the `0` key on the numpad of the QWERTY keyboard layout. - Numpad0 = 0x52, - /// The location of the `1` key on the numpad of the QWERTY keyboard layout. - Numpad1 = 0x53, - /// The location of the `2` key on the numpad of the QWERTY keyboard layout. - Numpad2 = 0x54, - /// The location of the `3` key on the numpad of the QWERTY keyboard layout. - Numpad3 = 0x55, - /// The location of the `4` key on the numpad of the QWERTY keyboard layout. - Numpad4 = 0x56, - /// The location of the `5` key on the numpad of the QWERTY keyboard layout. - Numpad5 = 0x57, - /// The location of the `6` key on the numpad of the QWERTY keyboard layout. - Numpad6 = 0x58, - /// The location of the `7` key on the numpad of the QWERTY keyboard layout. - Numpad7 = 0x59, - /// The location of the `8` key on the numpad of the QWERTY keyboard layout. - Numpad8 = 0x5b, - /// The location of the `9` key on the numpad of the QWERTY keyboard layout. - Numpad9 = 0x5c, - /// The location of the Enter/Return key on the QWERTY keyboard layout. - Enter = 0x24, - /// The location of the Tabulator key on the QWERTY keyboard layout. - Tab = 0x30, - /// The location of the space key on the QWERTY keyboard layout. - Space = 0x31, - /// The location of the back(space) key on the QWERTY keyboard layout. - Backspace = 0x33, - /// The location of the Escape/Esc key on the QWERTY keyboard layout. - Escape = 0x35, - /// The location of the left Windows key on the QWERTY keyboard layout. - /// Maps to the left Command key on Apple keyboards. - SuperLeft = 0x37, - /// The location of the left Shift key on the QWERTY keyboard layout. - ShiftLeft = 0x38, - /// The location of the Caps Lock key on the QWERTY keyboard layout. - CapsLock = 0x39, - /// The location of the left Alt key on the QWERTY keyboard layout. - /// Maps to left Option key on Apple keyboards. - AltLeft = 0x3a, - /// The location of the left Control key on the QWERTY keyboard layout. - ControlLeft = 0x3b, - /// The location of the right Shift key on the QWERTY keyboard layout. - ShiftRight = 0x3c, - /// The location of the right Alt key on the QWERTY keyboard layout. - /// Maps to right Option key on Apple keyboards. - AltRight = 0x3d, - /// The location of the right Control key on the QWERTY keyboard layout. - ControlRight = 0x3e, - // This is an extra key on mac keyboards: Function = 0x3f, - // This is an extra key on mac keyboards: F18 = 0x4f, - // This is an extra key on mac keyboards: F19 = 0x50, - // This is an extra key on mac keyboards: F20 = 0x5a, - /// The location of the `F5` key on the QWERTY keyboard layout. - F5 = 0x60, - /// The location of the `F6` key on the QWERTY keyboard layout. - F6 = 0x61, - /// The location of the `F7` key on the QWERTY keyboard layout. - F7 = 0x62, - /// The location of the `F3` key on the QWERTY keyboard layout. - F3 = 0x63, - /// The location of the `F8` key on the QWERTY keyboard layout. - F8 = 0x64, - /// The location of the `F9` key on the QWERTY keyboard layout. - F9 = 0x65, - /// The location of the `F11` key on the QWERTY keyboard layout. - F11 = 0x67, - /// The location of the Snapshot / Print Screen key on the QWERTY keyboard layout. - /// Maps to the `F13` key on Apple keyboards. - Snapshot = 0x69, - // This is an extra key on mac keyboards: F16 = 0x6a, - /// The location of the Scroll / Scroll Lock key on the QWERTY keyboard layout. - /// Maps to the `F14` key on Apple keyboards. - Scroll = 0x6b, - /// The location of the `F10` key on the QWERTY keyboard layout. - F10 = 0x6d, - /// The location of the `F12` key on the QWERTY keyboard layout. - F12 = 0x6f, - /// The location of the Pause key on the QWERTY keyboard layout. - /// Maps to the `F15` key on Apple keyboards. - Pause = 0x71, - /// The location of the Insert key on the QWERTY keyboard layout. - /// Maps to the Help key on Apple keyboards. - Insert = 0xe0_52, - /// The location of the Home key on the QWERTY keyboard layout. - Home = 0x73, - /// The location of the Page Up key on the QWERTY keyboard layout. - PageUp = 0x74, - /// The location of the Delete key on the QWERTY keyboard layout. - Delete = 0x75, - /// The location of the `F4` key on the QWERTY keyboard layout. - F4 = 0x76, - /// The location of the end key on the QWERTY keyboard layout. - End = 0x77, - /// The location of the `F2` key on the QWERTY keyboard layout. - F2 = 0x78, - /// The location of the page down key on the QWERTY keyboard layout. - PageDown = 0x79, - /// The location of the `F1` key on the QWERTY keyboard layout. - F1 = 0x7a, - /// The location of the left arrow key on the QWERTY keyboard layout. - Left = 0x7b, - /// The location of the right arrow key on the QWERTY keyboard layout. - Right = 0x7c, - /// The location of the arrow down key on the QWERTY keyboard layout. - Down = 0x7d, - /// The location of the arrow up key on the QWERTY keyboard layout. - Up = 0x7e, - // Not listed in the provided link, found by manual testing. - /// The location of the right Windows key on the QWERTY keyboard layout. - /// Maps to the right Command key on Apple keyboards. - SuperRight = 0x36, -} diff --git a/src/scan_codes/mod.rs b/src/scan_codes/mod.rs deleted file mode 100644 index cea021c5..00000000 --- a/src/scan_codes/mod.rs +++ /dev/null @@ -1,40 +0,0 @@ -//! Helper enums to easily obtain the scan code of a key. -use bevy::prelude::ScanCode; - -// Wasm -#[cfg(target_family = "wasm")] -mod wasm; -#[cfg(target_family = "wasm")] -pub use wasm::QwertyScanCode; - -// MacOs -#[cfg(all(target_os = "macos", not(target_family = "wasm")))] -mod mac_os; -#[cfg(all(target_os = "macos", not(target_family = "wasm")))] -pub use mac_os::QwertyScanCode; - -// Linux -#[cfg(all(target_os = "linux", not(target_os = "macos")))] -mod linux; -#[cfg(all(target_os = "linux", not(target_os = "macos")))] -pub use linux::QwertyScanCode; - -// Windows and other stuff using Set 1 -#[cfg(all( - not(target_family = "wasm"), - not(target_os = "macos"), - not(target_os = "linux") -))] -mod windows; -#[cfg(all( - not(target_family = "wasm"), - not(target_os = "macos"), - not(target_os = "linux") -))] -pub use windows::QwertyScanCode; - -impl From for ScanCode { - fn from(value: QwertyScanCode) -> Self { - ScanCode(value as u32) - } -} diff --git a/src/scan_codes/wasm.rs b/src/scan_codes/wasm.rs deleted file mode 100644 index 29626e12..00000000 --- a/src/scan_codes/wasm.rs +++ /dev/null @@ -1,262 +0,0 @@ -//! Helper enum to define scan codes on the QWERTY keyboard layout. - -/// The key locations as defined by the keys on the QWERTY keyboard layout. -/// -/// The [`u32`] representation of this enum are the `KeyboardEvent.code` values on Wasm. -/// See . -#[repr(u32)] -pub enum QwertyScanCode { - /// The location of the `1` key on the QWERTY keyboard layout. - Key1 = 0x31, - /// The location of the `2` key on the QWERTY keyboard layout. - Key2 = 0x32, - /// The location of the `3` key on the QWERTY keyboard layout. - Key3 = 0x33, - /// The location of the `4` key on the QWERTY keyboard layout. - Key4 = 0x34, - /// The location of the `5` key on the QWERTY keyboard layout. - Key5 = 0x35, - /// The location of the `6` key on the QWERTY keyboard layout. - Key6 = 0x36, - /// The location of the `7` key on the QWERTY keyboard layout. - Key7 = 0x37, - /// The location of the `8` key on the QWERTY keyboard layout. - Key8 = 0x38, - /// The location of the `9` key on the QWERTY keyboard layout. - Key9 = 0x39, - /// The location of the `0` key on the QWERTY keyboard layout. - Key0 = 0x30, - /// The location of the `A` key on the QWERTY keyboard layout. - A = 0x41, - /// The location of the `B` key on the QWERTY keyboard layout. - B = 0x42, - /// The location of the `C` key on the QWERTY keyboard layout. - C = 0x43, - /// The location of the `D` key on the QWERTY keyboard layout. - D = 0x44, - /// The location of the `E` key on the QWERTY keyboard layout. - E = 0x45, - /// The location of the `F` key on the QWERTY keyboard layout. - F = 0x46, - /// The location of the `G` key on the QWERTY keyboard layout. - G = 0x47, - /// The location of the `H` key on the QWERTY keyboard layout. - H = 0x48, - /// The location of the `I` key on the QWERTY keyboard layout. - I = 0x49, - /// The location of the `J` key on the QWERTY keyboard layout. - J = 0x4a, - /// The location of the `K` key on the QWERTY keyboard layout. - K = 0x4b, - /// The location of the `L` key on the QWERTY keyboard layout. - L = 0x4c, - /// The location of the `M` key on the QWERTY keyboard layout. - M = 0x4d, - /// The location of the `N` key on the QWERTY keyboard layout. - N = 0x4e, - /// The location of the `O` key on the QWERTY keyboard layout. - O = 0x4f, - /// The location of the `P` key on the QWERTY keyboard layout. - P = 0x50, - /// The location of the `Q` key on the QWERTY keyboard layout. - Q = 0x51, - /// The location of the `R` key on the QWERTY keyboard layout. - R = 0x52, - /// The location of the `S` key on the QWERTY keyboard layout. - S = 0x53, - /// The location of the `T` key on the QWERTY keyboard layout. - T = 0x54, - /// The location of the `U` key on the QWERTY keyboard layout. - U = 0x55, - /// The location of the `V key on the QWERTY keyboard layout. - V = 0x56, - /// The location of the `W` key on the QWERTY keyboard layout. - W = 0x57, - /// The location of the `X` key on the QWERTY keyboard layout. - X = 0x58, - /// The location of the `Y` key on the QWERTY keyboard layout. - Y = 0x59, - /// The location of the `Z` key on the QWERTY keyboard layout. - Z = 0x5a, - /// The location of the `,` key on the QWERTY keyboard layout. - Comma = 0xbc, - /// The location of the `.` key on the QWERTY keyboard layout. - Period = 0xbe, - /// The location of the `;` key on the QWERTY keyboard layout. - SemiColon = 0xba, - /// The location of the `'` key on the QWERTY keyboard layout. - Apostrophe = 0xde, - /// The location of the `[` key on the QWERTY keyboard layout. - BracketLeft = 0xdb, - /// The location of the `]` key on the QWERTY keyboard layout. - BracketRight = 0xdd, - /// The location of the `` ` `` key on the QWERTY keyboard layout. - Backtick = 0xc0, - /// The location of the `\` on the QWERTY keyboard layout. - Backslash = 0xdc, - /// The location of the `-` key on the QWERTY keyboard layout. - Minus = 0xbd, - /// The location of the `=` key on the QWERTY keyboard layout. - Equals = 0xbb, - /// The location of the left Alt key on the QWERTY keyboard layout. - /// Maps to left Option key on Apple keyboards. - AltLeft = 0x12, - /// The location of the right Alt key on the QWERTY keyboard layout. - /// Maps to right Option key on Apple keyboards. - /// - /// On Wasm, this scan code value maps to the AltGraph key on Linux, if available. - /// Note the platform specific details here: - /// - AltRight = 0xe1, - /// The location of the caps lock key on the QWERTY keyboard layout. - /// - /// On Wasm, note the platform specific details here: - /// - CapsLock = 0x14, - /// The location of the left Control key on the QWERTY keyboard layout. - /// - /// On Wasm, the right Control key has the same scan code, see: - /// - ControlLeft = 0x11, - /// The location of the left Windows key on the QWERTY keyboard layout. - /// Maps to the Command key on Apple keyboards. - /// - /// On Wasm, note the platform specific details here: - /// - SuperLeft = 0x5b, - /// The location of the right Windows key on the QWERTY keyboard layout. - /// - /// On Wasm, note the platform specific details here: - /// - SuperRight = 0x5c, - /// The location of the left Shift key on the QWERTY keyboard layout. - /// - /// On Wasm, the right Shift key has the same scan code, see: - /// - ShiftLeft = 0x10, - - /// The location of the Menu key on the QWERTY keyboard layout. - /// - /// On Wasm, note the platform specific details here: - /// - Menu = 0x5d, - /// The location of the Enter/Return key on the QWERTY keyboard layout. - Enter = 0x0d, - /// The location of the Space key on the QWERTY keyboard layout. - Space = 0x20, - /// The location of the tabulator key on the QWERTY keyboard layout. - Tab = 0x09, - /// The location of the Delete key on the QWERTY keyboard layout. - Delete = 0x2e, - /// The location of the End key on the QWERTY keyboard layout. - End = 0x23, - // Help = 0x2d, Not available on a lot of platforms and the scan code varies a lot - /// The location of the Home key on the QWERTY keyboard layout. - Home = 0x24, - /// The location of the Insert key on the QWERTY keyboard layout. - /// Maps to the Help key on Apple keyboards. - Insert = 0x2d, - /// The location of the Page Down key on the QWERTY keyboard layout. - PageDown = 0x22, - /// The location of the Page Up key on the QWERTY keyboard layout. - PageUp = 0x21, - /// The location of the Arrow Down key on the QWERTY keyboard layout. - Down = 0x28, - /// The location of the Arrow Left key on the QWERTY keyboard layout. - Left = 0x25, - /// The location of the Arrow Right key on the QWERTY keyboard layout. - Right = 0x27, - /// The location of the Arrow Up key on the QWERTY keyboard layout. - Up = 0x26, - /// The location of the Escape/Esc key on the QWERTY keyboard layout. - Escape = 0x1b, - /// The location of the Snapshot / Print Screen key on the QWERTY keyboard layout. - /// Maps to the `F13` key on Apple keyboards. - /// - /// On Wasm, note the platform specific details here: - /// - Snapshot = 0x2c, - /// The location of the Scroll / Scroll Lock key on the QWERTY keyboard layout. - /// Maps to the `F14` key on Apple keyboards. - /// - /// On Wasm, note the platform specific details here: - /// - Scroll = 0x91, - /// The location of the Pause key on the QWERTY keyboard layout. - /// Maps to the `F15` key on Apple keyboards. - /// - /// On Wasm, note the platform specific details here: - /// - Pause = 0x13, - - /// The location of the `F1` key on the QWERTY keyboard layout. - F1 = 0x70, - /// The location of the `F2` key on the QWERTY keyboard layout. - F2 = 0x71, - /// The location of the `F3` key on the QWERTY keyboard layout. - F3 = 0x72, - /// The location of the `F4` key on the QWERTY keyboard layout. - F4 = 0x73, - /// The location of the `F5` key on the QWERTY keyboard layout. - F5 = 0x74, - /// The location of the `F6` key on the QWERTY keyboard layout. - F6 = 0x75, - /// The location of the `F7` key on the QWERTY keyboard layout. - F7 = 0x76, - /// The location of the `F8` key on the QWERTY keyboard layout. - F8 = 0x77, - /// The location of the `F9` key on the QWERTY keyboard layout. - F9 = 0x78, - /// The location of the `F10` key on the QWERTY keyboard layout. - F10 = 0x79, - /// The location of the `F11` key on the QWERTY keyboard layout. - F11 = 0x7a, - /// The location of the `F12` key on the QWERTY keyboard layout. - F12 = 0x7b, - // F13-F24: Ignoring for now as I'm not sure what they are equivalent to on Windows - /// The location of the Numlock key on the QWERTY keyboard layout. - /// Maps to `NumpadClear` on Apple keyboards. - /// - /// On Wasm, note the platform specific details here: - /// - Numlock = 0x90, - /// The location of the `0` key on the numpad of the QWERTY keyboard layout. - Numpad0 = 0x06, - /// The location of the `1` key on the numpad of the QWERTY keyboard layout. - Numpad1 = 0x61, - /// The location of the `2` key on the numpad of the QWERTY keyboard layout. - Numpad2 = 0x62, - /// The location of the `3` key on the numpad of the QWERTY keyboard layout. - Numpad3 = 0x63, - /// The location of the `4` key on the numpad of the QWERTY keyboard layout. - Numpad4 = 0x64, - /// The location of the `5` key on the numpad of the QWERTY keyboard layout. - Numpad5 = 0x65, - /// The location of the `6` key on the numpad of the QWERTY keyboard layout. - Numpad6 = 0x66, - /// The location of the `7` key on the numpad of the QWERTY keyboard layout. - Numpad7 = 0x67, - /// The location of the `8` key on the numpad of the QWERTY keyboard layout. - Numpad8 = 0x68, - /// The location of the `9` key on the numpad of the QWERTY keyboard layout. - Numpad9 = 0x69, - /// The location of the `+` key on the numpad of the QWERTY keyboard layout. - NumpadAdd = 0x6b, - // NumpadComma = 0xc2: No idea what the difference to NumpadDecimal is. - /// The location of the `.` key on the numpad of the QWERTY keyboard layout. - /// - /// On Wasm, note the platform specific details here: - /// - NumpadDecimal = 0x6e, - /// The location of the `/` key on the numpad of the QWERTY keyboard layout. - /// Maps to `NumpadEquals` on Apple keyboards. - NumpadDivide = 0x6f, - // NumpadEnter = 0x0d, This is the same as `Enter` on Wasm - // NumpadEqual = 0x0c: No idea what the difference to NumpadEnter is. - /// The location of the `*` key on the numpad of the QWERTY keyboard layout. - /// Maps to `NumpadDivide` on Apple keyboards. - NumpadMultiply = 0x6a, - /// The location of the `*` key on the numpad of the QWERTY keyboard layout. - /// Maps to `NumpadMultiply` on Apple keyboards. - NumpadSubtract = 0x6d, -} diff --git a/src/scan_codes/windows.rs b/src/scan_codes/windows.rs deleted file mode 100644 index 5758cb31..00000000 --- a/src/scan_codes/windows.rs +++ /dev/null @@ -1,242 +0,0 @@ -//! Helper enum to define scan codes on the QWERTY keyboard layout. - -/// The key locations as defined by the keys on the QWERTY keyboard layout. -/// -/// The [`u32`] representation of this enum are the Set 1 scan codes of the corresponding keys. -/// See section 10.6 at . -#[repr(u32)] -pub enum QwertyScanCode { - /// The location of the `` ` `` key on the QWERTY keyboard layout. - Backtick = 0x29, - /// The location of the `1` key on the QWERTY keyboard layout. - Key1 = 0x02, - /// The location of the `2` key on the QWERTY keyboard layout. - Key2 = 0x03, - /// The location of the `3` key on the QWERTY keyboard layout. - Key3 = 0x04, - /// The location of the `4` key on the QWERTY keyboard layout. - Key4 = 0x05, - /// The location of the `5` key on the QWERTY keyboard layout. - Key5 = 0x06, - /// The location of the `6` key on the QWERTY keyboard layout. - Key6 = 0x07, - /// The location of the `7` key on the QWERTY keyboard layout. - Key7 = 0x08, - /// The location of the `8` key on the QWERTY keyboard layout. - Key8 = 0x09, - /// The location of the `9` key on the QWERTY keyboard layout. - Key9 = 0x0a, - /// The location of the `0` key on the QWERTY keyboard layout. - Key0 = 0x0b, - /// The location of the `-` key on the QWERTY keyboard layout. - Minus = 0x0c, - /// The location of the `=` key on the QWERTY keyboard layout. - Equals = 0x0d, - /// The location of the back(space) key on the QWERTY keyboard layout. - Backspace = 0x0e, - /// The location of the tabulator key on the QWERTY keyboard layout. - Tab = 0x0f, - /// The location of the `Q` key on the QWERTY keyboard layout. - Q = 0x10, - /// The location of the `W` key on the QWERTY keyboard layout. - W = 0x11, - /// The location of the `E` key on the QWERTY keyboard layout. - E = 0x12, - /// The location of the `R` key on the QWERTY keyboard layout. - R = 0x13, - /// The location of the `T` key on the QWERTY keyboard layout. - T = 0x14, - /// The location of the `Y` key on the QWERTY keyboard layout. - Y = 0x15, - /// The location of the `U` key on the QWERTY keyboard layout. - U = 0x16, - /// The location of the `I` key on the QWERTY keyboard layout. - I = 0x17, - /// The location of the `O` key on the QWERTY keyboard layout. - O = 0x18, - /// The location of the `P` key on the QWERTY keyboard layout. - P = 0x19, - /// The location of the `[` key on the QWERTY keyboard layout. - BracketLeft = 0x1a, - /// The location of the `]` key on the QWERTY keyboard layout. - BracketRight = 0x1b, - /// The location of the `\` on the QWERTY keyboard layout. - Backslash = 0x2b, - /// The location of the caps lock key on the QWERTY keyboard layout. - CapsLock = 0x3a, - /// The location of the `A` key on the QWERTY keyboard layout. - A = 0x1e, - /// The location of the `S` key on the QWERTY keyboard layout. - S = 0x1f, - /// The location of the `D` key on the QWERTY keyboard layout. - D = 0x20, - /// The location of the `F` key on the QWERTY keyboard layout. - F = 0x21, - /// The location of the `G` key on the QWERTY keyboard layout. - G = 0x22, - /// The location of the `H` key on the QWERTY keyboard layout. - H = 0x23, - /// The location of the `J` key on the QWERTY keyboard layout. - J = 0x24, - /// The location of the `K` key on the QWERTY keyboard layout. - K = 0x25, - /// The location of the `L` key on the QWERTY keyboard layout. - L = 0x26, - /// The location of the `;` key on the QWERTY keyboard layout. - SemiColon = 0x27, - /// The location of the `'` key on the QWERTY keyboard layout. - Apostrophe = 0x28, - /// A key not available on the US QWERTY layout. - /// - /// This is for example the `#` key on other layouts. - NonUs1 = 0x00, - /// The location of the Enter/Return key on the QWERTY keyboard layout. - Enter = 0x1c, - /// The location of the left Shift key on the QWERTY keyboard layout. - ShiftLeft = 0x2a, - /// The location of the `Z` key on the QWERTY keyboard layout. - Z = 0x2c, - /// The location of the `X` key on the QWERTY keyboard layout. - X = 0x2d, - /// The location of the `C` key on the QWERTY keyboard layout. - C = 0x2e, - /// The location of the `V key on the QWERTY keyboard layout. - V = 0x2f, - /// The location of the `B` key on the QWERTY keyboard layout. - B = 0x30, - /// The location of the `N` key on the QWERTY keyboard layout. - N = 0x31, - /// The location of the `M` key on the QWERTY keyboard layout. - M = 0x32, - /// The location of the `,` key on the QWERTY keyboard layout. - Comma = 0x33, - /// The location of the `.` key on the QWERTY keyboard layout. - Period = 0x34, - /// The location of the `/` key on the QWERTY keyboard layout. - Slash = 0x35, - /// The location of the right Shift key on the QWERTY keyboard layout. - ShiftRight = 0x36, - /// The location of the left Control key on the QWERTY keyboard layout. - ControlLeft = 0x1d, - /// The location of the left Alt key on the QWERTY keyboard layout. - /// Maps to left Option key on Apple keyboards. - AltLeft = 0x38, - /// The location of the Space key on the QWERTY keyboard layout. - Space = 0x39, - /// The location of the right Alt key on the QWERTY keyboard layout. - /// Maps to right Option key on Apple keyboards. - AltRight = 0xe0_e8, - /// The location of the right Control key on the QWERTY keyboard layout. - ControlRight = 0xe0_1d, - /// The location of the Insert key on the QWERTY keyboard layout. - /// Maps to the Help key on Apple keyboards. - Insert = 0xe0_52, - /// The location of the Delete key on the QWERTY keyboard layout. - Delete = 0xe0_53, - /// The location of the Home key on the QWERTY keyboard layout. - Home = 0xe0_47, - /// The location of the End key on the QWERTY keyboard layout. - End = 0xe0_4f, - /// The location of the Page Up key on the QWERTY keyboard layout. - PageUp = 0xe0_49, - /// The location of the Page Down key on the QWERTY keyboard layout. - PageDown = 0xe0_51, - /// The location of the Arrow Left key on the QWERTY keyboard layout. - Left = 0xe0_4b, - /// The location of the Arrow Up key on the QWERTY keyboard layout. - Up = 0xe0_48, - /// The location of the Arrow Down key on the QWERTY keyboard layout. - Down = 0xe0_50, - /// The location of the Arrow Right key on the QWERTY keyboard layout. - Right = 0xe0_4d, - /// The location of the Numlock key on the QWERTY keyboard layout. - /// Maps to `NumpadClear` on Apple keyboards. - Numlock = 0x45, - /// The location of the `7` key on the numpad of the QWERTY keyboard layout. - Numpad7 = 0x47, - /// The location of the `4` key on the numpad of the QWERTY keyboard layout. - Numpad4 = 0x4b, - /// The location of the `1` key on the numpad of the QWERTY keyboard layout. - Numpad1 = 0x4f, - /// The location of the `/` key on the numpad of the QWERTY keyboard layout. - /// Maps to `NumpadEquals` on Apple keyboards. - NumpadDivide = 0xe0_35, - /// The location of the `8` key on the numpad of the QWERTY keyboard layout. - Numpad8 = 0x48, - /// The location of the `5` key on the numpad of the QWERTY keyboard layout. - Numpad5 = 0x4c, - /// The location of the `2` key on the numpad of the QWERTY keyboard layout. - Numpad2 = 0x50, - /// The location of the `0` key on the numpad of the QWERTY keyboard layout. - Numpad0 = 0x52, - /// The location of the `*` key on the numpad of the QWERTY keyboard layout. - /// Maps to `NumpadDivide` on Apple keyboards. - NumpadMultiply = 0x37, - /// The location of the `9` key on the numpad of the QWERTY keyboard layout. - Numpad9 = 0x49, - /// The location of the `6` key on the numpad of the QWERTY keyboard layout. - Numpad6 = 0x4d, - /// The location of the `3` key on the numpad of the QWERTY keyboard layout. - Numpad3 = 0x51, - /// The location of the `.` key on the numpad of the QWERTY keyboard layout. - NumpadDecimal = 0x53, - /// The location of the `*` key on the numpad of the QWERTY keyboard layout. - /// Maps to `NumpadMultiply` on Apple keyboards. - NumpadSubtract = 0x4a, - /// The location of the `+` key on the numpad of the QWERTY keyboard layout. - NumpadAdd = 0x4e, - /// The location of the Enter key on the numpad of the QWERTY keyboard layout. - NumpadEnter = 0xe0_1c, - /// The location of the Escape/Esc key on the QWERTY keyboard layout. - Escape = 0x01, - /// The location of the `F1` key on the QWERTY keyboard layout. - F1 = 0x3b, - /// The location of the `F2` key on the QWERTY keyboard layout. - F2 = 0x3c, - /// The location of the `F3` key on the QWERTY keyboard layout. - F3 = 0x3d, - /// The location of the `F4` key on the QWERTY keyboard layout. - F4 = 0x3e, - /// The location of the `F5` key on the QWERTY keyboard layout. - F5 = 0x3f, - /// The location of the `F6` key on the QWERTY keyboard layout. - F6 = 0x40, - /// The location of the `F7` key on the QWERTY keyboard layout. - F7 = 0x41, - /// The location of the `F8` key on the QWERTY keyboard layout. - F8 = 0x42, - /// The location of the `F9` key on the QWERTY keyboard layout. - F9 = 0x43, - /// The location of the `F10` key on the QWERTY keyboard layout. - F10 = 0x44, - /// The location of the `F11` key on the QWERTY keyboard layout. - F11 = 0x57, - /// The location of the `F12` key on the QWERTY keyboard layout. - F12 = 0x58, - /// The location of the Snapshot / Print Screen key on the QWERTY keyboard layout. - /// Maps to the `F13` key on Apple keyboards. - Snapshot = 0xe0_37, - /// The location of the Alt+Sysrq key on the QWERTY keyboard layout. - AltSysrq = 0x54, - /// The location of the Scroll / Scroll Lock key on the QWERTY keyboard layout. - /// Maps to the `F14` key on Apple keyboards. - Scroll = 0x46, - /// The location of the Pause key on the QWERTY keyboard layout. - /// Maps to the `F15` key on Apple keyboards. - Pause = 0xe1_1d_45, - /// The location of the Ctrl+Break key on the QWERTY keyboard layout. - CtrlBreak = 0xe0_46, - /// The location of the left Windows key on the QWERTY keyboard layout. - /// Maps to the Command key on Apple keyboards. - SuperLeft = 0xe0_5b, - /// The location of the right Windows key on the QWERTY keyboard layout. - SuperRight = 0xe0_5c, - /// The location of the Menu key on the QWERTY keyboard layout. - Menu = 0xe0_5d, - /// The location of the Sleep key on the QWERTY keyboard layout. - Sleep = 0xe0_5f, - /// The location of the Power key on the QWERTY keyboard layout. - Power = 0xe0_5e, - /// The location of the Wake key on the QWERTY keyboard layout. - Wake = 0xe0_63, -} diff --git a/src/systems.rs b/src/systems.rs index bad94b99..900c7e64 100644 --- a/src/systems.rs +++ b/src/systems.rs @@ -7,13 +7,13 @@ use crate::{ input_streams::InputStreams, plugin::ToggleActions, Actionlike, }; -use bevy::{ecs::prelude::*, prelude::ScanCode}; +use bevy::ecs::prelude::*; use bevy::{ input::{ gamepad::{GamepadAxis, GamepadButton, Gamepads}, keyboard::KeyCode, mouse::{MouseButton, MouseMotion, MouseWheel}, - Axis, Input, + Axis, ButtonInput, }, log::warn, math::Vec2, @@ -58,18 +58,17 @@ pub fn tick_action_state( *stored_previous_instant = time.last_update(); } -/// Fetches all of the relevant [`Input`] resources to update [`ActionState`] according to the [`InputMap`]. +/// Fetches all of the relevant [`ButtonInput`] resources to update [`ActionState`] according to the [`InputMap`]. /// /// Missing resources will be ignored, and treated as if none of the corresponding inputs were pressed. #[allow(clippy::too_many_arguments)] pub fn update_action_state( - gamepad_buttons: Res>, + gamepad_buttons: Res>, gamepad_button_axes: Res>, gamepad_axes: Res>, gamepads: Res, - keycodes: Option>>, - scan_codes: Option>>, - mouse_buttons: Option>>, + keycodes: Option>>, + mouse_buttons: Option>>, mut mouse_wheel: EventReader, mut mouse_motion: EventReader, clash_strategy: Res, @@ -86,7 +85,6 @@ pub fn update_action_state( let gamepad_axes = gamepad_axes.into_inner(); let gamepads = gamepads.into_inner(); let keycodes = keycodes.map(|keycodes| keycodes.into_inner()); - let scan_codes = scan_codes.map(|scan_codes| scan_codes.into_inner()); let mouse_buttons = mouse_buttons.map(|mouse_buttons| mouse_buttons.into_inner()); let mouse_wheel: Option> = Some(mouse_wheel.read().cloned().collect()); @@ -105,14 +103,11 @@ pub fn update_action_state( // If egui wants to own inputs, don't also apply them to the game state #[cfg(feature = "egui")] - let (keycodes, scan_codes) = if maybe_egui + let keycodes = maybe_egui .iter_mut() - .any(|(_, mut ctx)| ctx.get_mut().wants_keyboard_input()) - { - (None, None) - } else { - (keycodes, scan_codes) - }; + .all(|(_, mut ctx)| !ctx.get_mut().wants_keyboard_input()) + .then_some(keycodes) + .flatten(); // `wants_pointer_input` sometimes returns `false` after clicking or holding a button over a widget, // so `is_pointer_over_area` is also needed. @@ -136,7 +131,6 @@ pub fn update_action_state( gamepad_axes, gamepads, keycodes, - scan_codes, mouse_buttons, mouse_wheel: mouse_wheel.clone(), mouse_motion: mouse_motion.clone(), @@ -201,10 +195,8 @@ pub fn generate_action_diffs( axis_pair: axis_pair.into(), }); previous_axis_pairs - .raw_entry_mut() - .from_key(&action) - .or_insert_with(|| (action.clone(), HashMap::default())) - .1 + .entry(action) + .or_insert_with(HashMap::default) .insert(maybe_entity, axis_pair.xy()); } None => { @@ -221,10 +213,8 @@ pub fn generate_action_diffs( } }); previous_values - .raw_entry_mut() - .from_key(&action) - .or_insert_with(|| (action.clone(), HashMap::default())) - .1 + .entry(action) + .or_insert_with(HashMap::default) .insert(maybe_entity, value); } } diff --git a/src/user_input.rs b/src/user_input.rs index e6a19c90..d1582ee2 100644 --- a/src/user_input.rs +++ b/src/user_input.rs @@ -1,13 +1,11 @@ //! Helpful abstractions over user inputs of all sorts -use bevy::input::keyboard::ScanCode; use bevy::input::{gamepad::GamepadButtonType, keyboard::KeyCode, mouse::MouseButton}; use bevy::reflect::Reflect; use bevy::utils::HashSet; use serde::{Deserialize, Serialize}; use crate::axislike::VirtualAxis; -use crate::scan_codes::QwertyScanCode; use crate::{ axislike::{AxisType, DualAxis, SingleAxis, VirtualDPad}, buttonlike::{MouseMotionDirection, MouseWheelDirection}, @@ -50,17 +48,10 @@ impl UserInput { /// /// If `inputs` has a length of 1, a [`UserInput::Single`] variant will be returned instead. pub fn chord(inputs: impl IntoIterator>) -> Self { - // We can't just check the length unless we add an ExactSizeIterator bound :( - let mut length: u8 = 0; + let vec: Vec = inputs.into_iter().map(|input| input.into()).collect(); - let mut vec: Vec = Vec::default(); - for button in inputs { - length += 1; - vec.push(button.into()); - } - - match length { - 1 => UserInput::Single(*vec.first().unwrap()), + match vec.len() { + 1 => UserInput::Single(vec[0]), _ => UserInput::Chord(vec), } } @@ -70,12 +61,11 @@ impl UserInput { /// - A [`Single`][UserInput::Single] input returns 1 /// - A [`Chord`][UserInput::Chord] returns the number of buttons in the chord /// - A [`VirtualDPad`][UserInput::VirtualDPad] returns 1 + /// - A [`VirtualAxis`][UserInput::VirtualAxis] returns 1 pub fn len(&self) -> usize { match self { - UserInput::Single(_) => 1, UserInput::Chord(button_set) => button_set.len(), - UserInput::VirtualDPad { .. } => 1, - UserInput::VirtualAxis { .. } => 1, + _ => 1, } } @@ -93,9 +83,9 @@ impl UserInput { /// use leafwing_input_manager::user_input::UserInput; /// /// let buttons = HashSet::from_iter([ControlLeft.into(), AltLeft.into()]); - /// let a: UserInput = A.into(); - /// let ctrl_a = UserInput::chord([ControlLeft, A]); - /// let ctrl_alt_a = UserInput::chord([ControlLeft, AltLeft, A]); + /// let a: UserInput = KeyA.into(); + /// let ctrl_a = UserInput::chord([ControlLeft, KeyA]); + /// let ctrl_alt_a = UserInput::chord([ControlLeft, AltLeft, KeyA]); /// /// assert_eq!(a.n_matching(&buttons), 0); /// assert_eq!(ctrl_a.n_matching(&buttons), 1); @@ -104,44 +94,23 @@ impl UserInput { pub fn n_matching(&self, buttons: &HashSet) -> usize { match self { UserInput::Single(button) => usize::from(buttons.contains(button)), - UserInput::Chord(chord_buttons) => { - let mut n_matching = 0; - for button in buttons.iter() { - if chord_buttons.contains(button) { - n_matching += 1; - } - } - - n_matching - } - UserInput::VirtualDPad(VirtualDPad { - up, - down, - left, - right, - }) => { - let mut n_matching = 0; - for button in buttons.iter() { - for dpad_button in [up, down, left, right] { - if button == dpad_button { - n_matching += 1; - } - } - } - - n_matching + UserInput::Chord(chord_buttons) => buttons + .iter() + .filter(|button| chord_buttons.contains(button)) + .count(), + UserInput::VirtualDPad(dpad) => { + let dpad_buttons = [dpad.up, dpad.down, dpad.left, dpad.right]; + buttons + .iter() + .filter(|button| dpad_buttons.contains(button)) + .count() } UserInput::VirtualAxis(VirtualAxis { negative, positive }) => { - let mut n_matching = 0; - for button in buttons.iter() { - for dpad_button in [negative, positive] { - if button == dpad_button { - n_matching += 1; - } - } - } - - n_matching + let axis_buttons = [negative, positive]; + buttons + .iter() + .filter(|button| axis_buttons.contains(button)) + .count() } } } @@ -151,119 +120,22 @@ impl UserInput { let mut raw_inputs = RawInputs::default(); match self { - UserInput::Single(button) => match *button { - InputKind::DualAxis(dual_axis) => { - raw_inputs - .axis_data - .push((dual_axis.x.axis_type, dual_axis.x.value)); - raw_inputs - .axis_data - .push((dual_axis.y.axis_type, dual_axis.y.value)); - } - InputKind::SingleAxis(single_axis) => raw_inputs - .axis_data - .push((single_axis.axis_type, single_axis.value)), - InputKind::GamepadButton(button) => raw_inputs.gamepad_buttons.push(button), - InputKind::Keyboard(button) => raw_inputs.keycodes.push(button), - InputKind::KeyLocation(scan_code) => raw_inputs.scan_codes.push(scan_code), - InputKind::Modifier(modifier) => { - let key_codes = modifier.key_codes(); - raw_inputs.keycodes.push(key_codes[0]); - raw_inputs.keycodes.push(key_codes[1]); - } - InputKind::Mouse(button) => raw_inputs.mouse_buttons.push(button), - InputKind::MouseWheel(button) => raw_inputs.mouse_wheel.push(button), - InputKind::MouseMotion(button) => raw_inputs.mouse_motion.push(button), - }, + UserInput::Single(button) => { + raw_inputs.merge_input_data(button); + } UserInput::Chord(button_set) => { for button in button_set.iter() { - match *button { - InputKind::DualAxis(dual_axis) => { - raw_inputs - .axis_data - .push((dual_axis.x.axis_type, dual_axis.x.value)); - raw_inputs - .axis_data - .push((dual_axis.y.axis_type, dual_axis.y.value)); - } - InputKind::SingleAxis(single_axis) => raw_inputs - .axis_data - .push((single_axis.axis_type, single_axis.value)), - InputKind::GamepadButton(button) => raw_inputs.gamepad_buttons.push(button), - InputKind::Keyboard(button) => raw_inputs.keycodes.push(button), - InputKind::KeyLocation(scan_code) => raw_inputs.scan_codes.push(scan_code), - InputKind::Modifier(modifier) => { - let key_codes = modifier.key_codes(); - raw_inputs.keycodes.push(key_codes[0]); - raw_inputs.keycodes.push(key_codes[1]); - } - InputKind::Mouse(button) => raw_inputs.mouse_buttons.push(button), - InputKind::MouseWheel(button) => raw_inputs.mouse_wheel.push(button), - InputKind::MouseMotion(button) => raw_inputs.mouse_motion.push(button), - } + raw_inputs.merge_input_data(button); } } - UserInput::VirtualDPad(VirtualDPad { - up, - down, - left, - right, - }) => { - for button in [up, down, left, right] { - match *button { - InputKind::DualAxis(dual_axis) => { - raw_inputs - .axis_data - .push((dual_axis.x.axis_type, dual_axis.x.value)); - raw_inputs - .axis_data - .push((dual_axis.y.axis_type, dual_axis.y.value)); - } - InputKind::SingleAxis(single_axis) => raw_inputs - .axis_data - .push((single_axis.axis_type, single_axis.value)), - InputKind::GamepadButton(button) => raw_inputs.gamepad_buttons.push(button), - InputKind::Keyboard(button) => raw_inputs.keycodes.push(button), - InputKind::KeyLocation(scan_code) => raw_inputs.scan_codes.push(scan_code), - InputKind::Modifier(modifier) => { - let key_codes = modifier.key_codes(); - raw_inputs.keycodes.push(key_codes[0]); - raw_inputs.keycodes.push(key_codes[1]); - } - InputKind::Mouse(button) => raw_inputs.mouse_buttons.push(button), - InputKind::MouseWheel(button) => raw_inputs.mouse_wheel.push(button), - InputKind::MouseMotion(button) => raw_inputs.mouse_motion.push(button), - } + UserInput::VirtualDPad(dpad) => { + for button in [dpad.up, dpad.down, dpad.left, dpad.right] { + raw_inputs.merge_input_data(&button); } } UserInput::VirtualAxis(VirtualAxis { negative, positive }) => { - for button in [negative, positive] { - // todo: dedup with VirtualDPad? - match *button { - InputKind::DualAxis(dual_axis) => { - raw_inputs - .axis_data - .push((dual_axis.x.axis_type, dual_axis.x.value)); - raw_inputs - .axis_data - .push((dual_axis.y.axis_type, dual_axis.y.value)); - } - InputKind::SingleAxis(single_axis) => raw_inputs - .axis_data - .push((single_axis.axis_type, single_axis.value)), - InputKind::GamepadButton(button) => raw_inputs.gamepad_buttons.push(button), - InputKind::Keyboard(button) => raw_inputs.keycodes.push(button), - InputKind::KeyLocation(scan_code) => raw_inputs.scan_codes.push(scan_code), - InputKind::Modifier(modifier) => { - let key_codes = modifier.key_codes(); - raw_inputs.keycodes.push(key_codes[0]); - raw_inputs.keycodes.push(key_codes[1]); - } - InputKind::Mouse(button) => raw_inputs.mouse_buttons.push(button), - InputKind::MouseWheel(button) => raw_inputs.mouse_wheel.push(button), - InputKind::MouseMotion(button) => raw_inputs.mouse_motion.push(button), - } - } + raw_inputs.merge_input_data(negative); + raw_inputs.merge_input_data(positive); } }; @@ -309,19 +181,7 @@ impl From for UserInput { impl From for UserInput { fn from(input: KeyCode) -> Self { - UserInput::Single(InputKind::Keyboard(input)) - } -} - -impl From for UserInput { - fn from(input: ScanCode) -> Self { - UserInput::Single(InputKind::KeyLocation(input)) - } -} - -impl From for UserInput { - fn from(input: QwertyScanCode) -> Self { - UserInput::Single(InputKind::KeyLocation(input.into())) + UserInput::Single(InputKind::PhysicalKey(input)) } } @@ -353,7 +213,7 @@ impl From for UserInput { /// /// Commonly stored in the [`UserInput`] enum. /// -/// Unfortunately we cannot use a trait object here, as the types used by `Input` +/// Unfortunately we cannot use a trait object here, as the types used by `ButtonInput` /// require traits that are not object-safe. /// /// Please contact the maintainers if you need support for another type! @@ -366,18 +226,8 @@ pub enum InputKind { SingleAxis(SingleAxis), /// Two paired axes of continuous motion DualAxis(DualAxis), - /// A logical key on the keyboard. - /// - /// The actual (physical) key that has to be pressed depends on the keyboard layout. - /// If you care about the position of the key rather than what it stands for, - /// use [`InputKind::KeyLocation`] instead. - Keyboard(KeyCode), /// The physical location of a key on the keyboard. - /// - /// The logical key which is emitted by this key depends on the keyboard layout. - /// If you care about the output of the key rather than where it is positioned, - /// use [`InputKind::Keyboard`] instead. - KeyLocation(ScanCode), + PhysicalKey(KeyCode), /// A keyboard modifier, like `Ctrl` or `Alt`, which doesn't care about which side it's on. Modifier(Modifier), /// A button on a mouse @@ -408,19 +258,7 @@ impl From for InputKind { impl From for InputKind { fn from(input: KeyCode) -> Self { - InputKind::Keyboard(input) - } -} - -impl From for InputKind { - fn from(input: ScanCode) -> Self { - InputKind::KeyLocation(input) - } -} - -impl From for InputKind { - fn from(input: QwertyScanCode) -> Self { - InputKind::KeyLocation(input.into()) + InputKind::PhysicalKey(input) } } @@ -484,10 +322,8 @@ impl Modifier { /// Obtained by calling [`UserInput::raw_inputs()`]. #[derive(Default, Debug, Clone, PartialEq)] pub struct RawInputs { - /// Logical keyboard keys. - pub keycodes: Vec, /// Physical key locations. - pub scan_codes: Vec, + pub keycodes: Vec, /// Mouse buttons pub mouse_buttons: Vec, /// Discretized mouse wheel inputs @@ -502,6 +338,31 @@ pub struct RawInputs { pub axis_data: Vec<(AxisType, Option)>, } +impl RawInputs { + /// Merges the data from the given `input_kind` into `self`. + fn merge_input_data(&mut self, input_kind: &InputKind) { + match *input_kind { + InputKind::DualAxis(dual_axis) => { + self.axis_data + .push((dual_axis.x.axis_type, dual_axis.x.value)); + self.axis_data + .push((dual_axis.y.axis_type, dual_axis.y.value)); + } + InputKind::SingleAxis(single_axis) => self + .axis_data + .push((single_axis.axis_type, single_axis.value)), + InputKind::GamepadButton(button) => self.gamepad_buttons.push(button), + InputKind::PhysicalKey(key_code) => self.keycodes.push(key_code), + InputKind::Modifier(modifier) => { + self.keycodes.extend_from_slice(&modifier.key_codes()); + } + InputKind::Mouse(button) => self.mouse_buttons.push(button), + InputKind::MouseWheel(button) => self.mouse_wheel.push(button), + InputKind::MouseMotion(button) => self.mouse_motion.push(button), + } + } +} + #[cfg(test)] impl RawInputs { fn from_keycode(keycode: KeyCode) -> RawInputs { @@ -647,7 +508,7 @@ mod raw_input_tests { fn keyboard_button() { use bevy::input::keyboard::KeyCode; - let button = KeyCode::A; + let button = KeyCode::KeyA; let expected = RawInputs::from_keycode(button); let raw = UserInput::from(button).raw_inputs(); assert_eq!(expected, raw); @@ -658,9 +519,9 @@ mod raw_input_tests { use crate::user_input::Modifier; use bevy::input::keyboard::KeyCode; - let input = UserInput::modified(Modifier::Control, KeyCode::S); + let input = UserInput::modified(Modifier::Control, KeyCode::KeyS); let expected = RawInputs { - keycodes: vec![KeyCode::ControlLeft, KeyCode::ControlRight, KeyCode::S], + keycodes: vec![KeyCode::ControlLeft, KeyCode::ControlRight, KeyCode::KeyS], ..Default::default() }; let raw = input.raw_inputs(); diff --git a/tests/action_diffs.rs b/tests/action_diffs.rs index 7e448b55..c8a434c0 100644 --- a/tests/action_diffs.rs +++ b/tests/action_diffs.rs @@ -73,12 +73,11 @@ fn send_action_diff(app: &mut App, action_diff: ActionDiffEvent) { fn assert_has_no_action_diffs(app: &mut App) { let action_diff_events = get_events::>(app); let action_diff_event_reader = &mut action_diff_events.get_reader(); - match action_diff_event_reader.read(action_diff_events).next() { - Some(action_diff) => panic!( + if let Some(action_diff) = action_diff_event_reader.read(action_diff_events).next() { + panic!( "Expected no `ActionDiff` variants. Received: {:?}", action_diff - ), - None => {} + ) } } @@ -103,23 +102,23 @@ fn assert_action_diff_received(app: &mut App, action_diff_event: ActionDiffEvent match action_diff_event.action_diffs.first().unwrap().clone() { ActionDiff::Pressed { action } => { assert!(action_state.pressed(&action)); - assert!(action_state.value(&action) == 1.); + assert_eq!(action_state.value(&action), 1.); } ActionDiff::Released { action } => { assert!(action_state.released(&action)); - assert!(action_state.value(&action) == 0.); + assert_eq!(action_state.value(&action), 0.); assert!(action_state.axis_pair(&action).is_none()); } ActionDiff::ValueChanged { action, value } => { assert!(action_state.pressed(&action)); - assert!(action_state.value(&action) == value); + assert_eq!(action_state.value(&action), value); } ActionDiff::AxisPairChanged { action, axis_pair } => { assert!(action_state.pressed(&action)); match action_state.axis_pair(&action) { Some(axis_pair_data) => { - assert!(axis_pair_data.xy() == axis_pair); - assert!(action_state.value(&action) == axis_pair_data.xy().length()); + assert_eq!(axis_pair_data.xy(), axis_pair); + assert_eq!(action_state.value(&action), axis_pair_data.xy().length()); } None => panic!("Expected an `AxisPair` variant. Received none."), } diff --git a/tests/clashes.rs b/tests/clashes.rs index 45410d4b..f98db53b 100644 --- a/tests/clashes.rs +++ b/tests/clashes.rs @@ -48,14 +48,14 @@ fn spawn_input_map(mut commands: Commands) { let mut input_map = InputMap::default(); - input_map.insert(One, Key1); - input_map.insert(Two, Key2); - input_map.insert_chord(OneAndTwo, [Key1, Key2]); - input_map.insert_chord(TwoAndThree, [Key2, Key3]); - input_map.insert_chord(OneAndTwoAndThree, [Key1, Key2, Key3]); - input_map.insert_chord(CtrlOne, [ControlLeft, Key1]); - input_map.insert_chord(AltOne, [AltLeft, Key1]); - input_map.insert_chord(CtrlAltOne, [ControlLeft, AltLeft, Key1]); + input_map.insert(One, Digit1); + input_map.insert(Two, Digit2); + input_map.insert_chord(OneAndTwo, [Digit1, Digit2]); + input_map.insert_chord(TwoAndThree, [Digit2, Digit3]); + input_map.insert_chord(OneAndTwoAndThree, [Digit1, Digit2, Digit3]); + input_map.insert_chord(CtrlOne, [ControlLeft, Digit1]); + input_map.insert_chord(AltOne, [AltLeft, Digit1]); + input_map.insert_chord(CtrlAltOne, [ControlLeft, AltLeft, Digit1]); commands.spawn(input_map); } @@ -85,7 +85,7 @@ impl ClashTestExt for App { let input_map_query = input_system_state.get(&self.world); let input_map = input_map_query.single(); - let keyboard_input = self.world.resource::>(); + let keyboard_input = self.world.resource::>(); for action in Action::variants() { if pressed_actions.contains(action) { @@ -111,8 +111,8 @@ fn two_inputs_clash_handling() { let mut app = test_app(); // Two inputs - app.send_input(Key1); - app.send_input(Key2); + app.send_input(Digit1); + app.send_input(Digit2); app.update(); app.assert_input_map_actions_eq(ClashStrategy::PressAll, [One, Two, OneAndTwo]); @@ -128,9 +128,9 @@ fn three_inputs_clash_handling() { // Three inputs app.reset_inputs(); - app.send_input(Key1); - app.send_input(Key2); - app.send_input(Key3); + app.send_input(Digit1); + app.send_input(Digit2); + app.send_input(Digit3); app.update(); app.assert_input_map_actions_eq( @@ -149,9 +149,9 @@ fn modifier_clash_handling() { // Modifier app.reset_inputs(); - app.send_input(Key1); - app.send_input(Key2); - app.send_input(Key3); + app.send_input(Digit1); + app.send_input(Digit2); + app.send_input(Digit3); app.send_input(ControlLeft); app.update(); @@ -174,7 +174,7 @@ fn multiple_modifiers_clash_handling() { // Multiple modifiers app.reset_inputs(); - app.send_input(Key1); + app.send_input(Digit1); app.send_input(ControlLeft); app.send_input(AltLeft); app.update(); @@ -192,8 +192,8 @@ fn action_order_clash_handling() { // Action order app.reset_inputs(); - app.send_input(Key3); - app.send_input(Key2); + app.send_input(Digit3); + app.send_input(Digit2); app.update(); app.assert_input_map_actions_eq(ClashStrategy::PressAll, [Two, TwoAndThree]); diff --git a/tests/gamepad_axis.rs b/tests/gamepad_axis.rs index 97599cab..bb717fa8 100644 --- a/tests/gamepad_axis.rs +++ b/tests/gamepad_axis.rs @@ -233,7 +233,7 @@ fn game_pad_single_axis() { app.update(); let action_state = app.world.resource::>(); assert!(action_state.pressed(&AxislikeTestAction::X)); - assert!(action_state.value(&AxislikeTestAction::X) == 0.11111112); + assert_eq!(action_state.value(&AxislikeTestAction::X), 0.11111112); } #[test] @@ -264,7 +264,7 @@ fn game_pad_single_axis_inverted() { app.update(); let action_state = app.world.resource::>(); assert!(action_state.pressed(&AxislikeTestAction::X)); - assert!(action_state.value(&AxislikeTestAction::X) == -1.0); + assert_eq!(action_state.value(&AxislikeTestAction::X), -1.0); // -X let input = SingleAxis { @@ -279,7 +279,7 @@ fn game_pad_single_axis_inverted() { app.update(); let action_state = app.world.resource::>(); assert!(action_state.pressed(&AxislikeTestAction::X)); - assert!(action_state.value(&AxislikeTestAction::X) == 1.0); + assert_eq!(action_state.value(&AxislikeTestAction::X), 1.0); // +Y let input = SingleAxis { @@ -294,7 +294,7 @@ fn game_pad_single_axis_inverted() { app.update(); let action_state = app.world.resource::>(); assert!(action_state.pressed(&AxislikeTestAction::Y)); - assert!(action_state.value(&AxislikeTestAction::Y) == -1.0); + assert_eq!(action_state.value(&AxislikeTestAction::Y), -1.0); // -Y let input = SingleAxis { @@ -309,7 +309,7 @@ fn game_pad_single_axis_inverted() { app.update(); let action_state = app.world.resource::>(); assert!(action_state.pressed(&AxislikeTestAction::Y)); - assert!(action_state.value(&AxislikeTestAction::Y) == 1.0); + assert_eq!(action_state.value(&AxislikeTestAction::Y), 1.0); } #[test] diff --git a/tests/integration.rs b/tests/integration.rs index 02ce4a5b..03b1196a 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -43,7 +43,7 @@ struct Player; fn spawn_player(mut commands: Commands) { commands .spawn(InputManagerBundle:: { - input_map: InputMap::::new([(Action::PayRespects, KeyCode::F)]), + input_map: InputMap::::new([(Action::PayRespects, KeyCode::KeyF)]), ..Default::default() }) .insert(Player); @@ -62,13 +62,16 @@ fn disable_input() { .add_plugins(InputManagerPlugin::::default()) .add_systems(Startup, spawn_player) .init_resource::>() - .insert_resource(InputMap::::new([(Action::PayRespects, KeyCode::F)])) + .insert_resource(InputMap::::new([( + Action::PayRespects, + KeyCode::KeyF, + )])) .init_resource::() .add_systems(Update, pay_respects) .add_systems(PreUpdate, respect_fades); // Press F to pay respects - app.send_input(KeyCode::F); + app.send_input(KeyCode::KeyF); app.update(); let respect = app.world.resource::(); assert_eq!(*respect, Respect(true)); @@ -83,7 +86,7 @@ fn disable_input() { assert_eq!(*respect, Respect(false)); // And even pressing F cannot bring it back - app.send_input(KeyCode::F); + app.send_input(KeyCode::KeyF); app.update(); let respect = app.world.resource::(); assert_eq!(*respect, Respect(false)); @@ -101,13 +104,16 @@ fn release_when_input_map_removed() { .add_plugins(InputManagerPlugin::::default()) .add_systems(Startup, spawn_player) .init_resource::>() - .insert_resource(InputMap::::new([(Action::PayRespects, KeyCode::F)])) + .insert_resource(InputMap::::new([( + Action::PayRespects, + KeyCode::KeyF, + )])) .init_resource::() .add_systems(Update, (pay_respects, remove_input_map)) .add_systems(PreUpdate, respect_fades); // Press F to pay respects - app.send_input(KeyCode::F); + app.send_input(KeyCode::KeyF); app.update(); let respect = app.world.resource::(); assert_eq!(*respect, Respect(true)); @@ -123,7 +129,7 @@ fn release_when_input_map_removed() { assert_eq!(*respect, Respect(false)); // And even pressing F cannot bring it back - app.send_input(KeyCode::F); + app.send_input(KeyCode::KeyF); app.update(); let respect = app.world.resource::(); assert_eq!(*respect, Respect(false)); @@ -143,7 +149,7 @@ fn action_state_driver() { fn setup(mut commands: Commands) { let player_entity = commands .spawn(InputManagerBundle:: { - input_map: InputMap::::new([(Action::PayRespects, KeyCode::F)]), + input_map: InputMap::::new([(Action::PayRespects, KeyCode::KeyF)]), ..Default::default() }) .insert(Player) @@ -226,7 +232,10 @@ fn duration() { .add_plugins(InputManagerPlugin::::default()) .add_systems(Startup, spawn_player) .init_resource::>() - .insert_resource(InputMap::::new([(Action::PayRespects, KeyCode::F)])) + .insert_resource(InputMap::::new([( + Action::PayRespects, + KeyCode::KeyF, + )])) .init_resource::() .add_systems(Update, hold_f_to_pay_respects); @@ -234,7 +243,7 @@ fn duration() { app.update(); // Press - app.send_input(KeyCode::F); + app.send_input(KeyCode::KeyF); // Hold std::thread::sleep(2 * RESPECTFUL_DURATION);