use bevy::input::{
gamepad::{Gamepad, GamepadAxis, GamepadButton, GamepadEventRaw, Gamepads},
keyboard::{KeyCode, KeyboardInput},
mouse::{MouseButton, MouseButtonInput, MouseMotion, MouseWheel},
Axis, Input,
};
use petitset::PetitSet;
use bevy::ecs::prelude::{Events, ResMut, World};
use bevy::ecs::system::SystemState;
use crate::axislike::{
AxisType, DualAxisData, MouseMotionAxisType, MouseWheelAxisType, SingleAxis, VirtualAxis,
VirtualDPad,
};
use crate::buttonlike::{MouseMotionDirection, MouseWheelDirection};
use crate::user_input::{InputKind, UserInput};
#[derive(Debug, Clone)]
pub struct InputStreams<'a> {
pub gamepad_buttons: &'a Input<GamepadButton>,
pub gamepad_button_axes: &'a Axis<GamepadButton>,
pub gamepad_axes: &'a Axis<GamepadAxis>,
pub gamepads: &'a Gamepads,
pub keycodes: Option<&'a Input<KeyCode>>,
pub mouse_buttons: Option<&'a Input<MouseButton>>,
pub mouse_wheel: Option<&'a Events<MouseWheel>>,
pub mouse_motion: &'a Events<MouseMotion>,
pub associated_gamepad: Option<Gamepad>,
}
impl<'a> InputStreams<'a> {
pub fn from_world(world: &'a World, gamepad: Option<Gamepad>) -> Self {
let gamepad_buttons = world.resource::<Input<GamepadButton>>();
let gamepad_button_axes = world.resource::<Axis<GamepadButton>>();
let gamepad_axes = world.resource::<Axis<GamepadAxis>>();
let gamepads = world.resource::<Gamepads>();
let keycodes = world.get_resource::<Input<KeyCode>>();
let mouse_buttons = world.get_resource::<Input<MouseButton>>();
let mouse_wheel = world.get_resource::<Events<MouseWheel>>();
let mouse_motion = world.resource::<Events<MouseMotion>>();
InputStreams {
gamepad_buttons,
gamepad_button_axes,
gamepad_axes,
gamepads,
keycodes,
mouse_buttons,
mouse_wheel,
mouse_motion,
associated_gamepad: gamepad,
}
}
}
impl<'a> InputStreams<'a> {
pub fn guess_gamepad(&self) -> Option<Gamepad> {
match self.associated_gamepad {
Some(gamepad) => Some(gamepad),
None => self.gamepads.iter().next(),
}
}
pub fn input_pressed(&self, input: &UserInput) -> bool {
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::VirtualAxis(VirtualAxis { negative, positive }) => {
self.button_pressed(*negative) || self.button_pressed(*positive)
}
}
}
#[must_use]
pub fn any_pressed(&self, inputs: &PetitSet<UserInput, 16>) -> bool {
for input in inputs.iter() {
if self.input_pressed(input) {
return true;
}
}
false
}
#[must_use]
pub fn button_pressed(&self, button: InputKind) -> bool {
match button {
InputKind::DualAxis(_) => {
let axis_pair = self.input_axis_pair(&UserInput::Single(button)).unwrap();
axis_pair.length() != 0.0
}
InputKind::SingleAxis(_) => {
let value = self.input_value(&UserInput::Single(button));
value != 0.0
}
InputKind::GamepadButton(gamepad_button) => {
if let Some(gamepad) = self.guess_gamepad() {
self.gamepad_buttons.pressed(GamepadButton {
gamepad,
button_type: gamepad_button,
})
} else {
false
}
}
InputKind::Keyboard(keycode) => {
matches!(self.keycodes, Some(keycodes) if keycodes.pressed(keycode))
}
InputKind::Modifier(modifier) => {
let key_codes = modifier.key_codes();
matches!(self.keycodes, Some(keycodes) if keycodes.pressed(key_codes[0]) | keycodes.pressed(key_codes[1]))
}
InputKind::Mouse(mouse_button) => {
matches!(self.mouse_buttons, Some(mouse_buttons) if mouse_buttons.pressed(mouse_button))
}
InputKind::MouseWheel(mouse_wheel_direction) => {
let Some(mouse_wheel) = self.mouse_wheel else {
return false;
};
let mut total_mouse_wheel_movement = 0.0;
let mut event_reader = mouse_wheel.get_reader();
for mouse_wheel_event in event_reader.iter(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
}
}
}
match mouse_wheel_direction {
MouseWheelDirection::Up | MouseWheelDirection::Right => {
total_mouse_wheel_movement > 0.0
}
MouseWheelDirection::Down | MouseWheelDirection::Left => {
total_mouse_wheel_movement < 0.0
}
}
}
InputKind::MouseMotion(mouse_motion_direction) => {
let mut total_mouse_movement = 0.0;
let mut event_reader = self.mouse_motion.get_reader();
for mouse_motion_event in event_reader.iter(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
}
}
}
match mouse_motion_direction {
MouseMotionDirection::Up | MouseMotionDirection::Right => {
total_mouse_movement > 0.0
}
MouseMotionDirection::Down | MouseMotionDirection::Left => {
total_mouse_movement < 0.0
}
}
}
}
}
#[must_use]
pub fn all_buttons_pressed(&self, buttons: &PetitSet<InputKind, 8>) -> bool {
for &button in buttons.iter() {
if !self.button_pressed(button) {
return false;
}
}
true
}
pub fn input_value(&self, input: &UserInput) -> f32 {
let use_button_value = || -> f32 {
if self.input_pressed(input) {
1.0
} else {
0.0
}
};
let value_in_axis_range = |axis: &SingleAxis, value: f32| -> f32 {
if value >= axis.negative_low && value <= axis.positive_low {
0.0
} else {
value
}
};
match input {
UserInput::Single(InputKind::SingleAxis(single_axis)) => {
match single_axis.axis_type {
AxisType::Gamepad(axis_type) => {
if let Some(gamepad) = self.guess_gamepad() {
let value = self
.gamepad_axes
.get(GamepadAxis { gamepad, axis_type })
.unwrap_or_default();
value_in_axis_range(single_axis, value)
} else {
0.0
}
}
AxisType::MouseWheel(axis_type) => {
let Some(mouse_wheel) = self.mouse_wheel else {
return 0.0;
};
let mut total_mouse_wheel_movement = 0.0;
let mut event_reader = mouse_wheel.get_reader();
for mouse_wheel_event in event_reader.iter(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)
}
AxisType::MouseMotion(axis_type) => {
let mut total_mouse_motion_movement = 0.0;
let mut event_reader = self.mouse_motion.get_reader();
for mouse_wheel_event in event_reader.iter(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)
}
}
}
UserInput::VirtualAxis(VirtualAxis { negative, positive }) => {
self.input_value(&UserInput::Single(*positive)).abs()
- self.input_value(&UserInput::Single(*negative)).abs()
}
UserInput::Single(InputKind::DualAxis(_)) => {
self.input_axis_pair(input).unwrap_or_default().length()
}
UserInput::VirtualDPad { .. } => {
self.input_axis_pair(input).unwrap_or_default().length()
}
UserInput::Single(InputKind::GamepadButton(button_type)) => {
if let Some(gamepad) = self.guess_gamepad() {
self.gamepad_button_axes
.get(GamepadButton {
gamepad,
button_type: *button_type,
})
.unwrap_or_else(use_button_value)
} else {
0.0
}
}
_ => use_button_value(),
}
}
pub fn input_axis_pair(&self, input: &UserInput) -> Option<DualAxisData> {
match input {
UserInput::Single(InputKind::DualAxis(dual_axis)) => {
let x = self.input_value(&UserInput::Single(InputKind::SingleAxis(dual_axis.x)));
let y = self.input_value(&UserInput::Single(InputKind::SingleAxis(dual_axis.y)));
if x > dual_axis.x.positive_low
|| x < dual_axis.x.negative_low
|| y > dual_axis.y.positive_low
|| y < dual_axis.y.negative_low
{
Some(DualAxisData::new(x, y))
} else {
Some(DualAxisData::new(0.0, 0.0))
}
}
UserInput::VirtualDPad(VirtualDPad {
up,
down,
left,
right,
}) => {
let x = self.input_value(&UserInput::Single(*right)).abs()
- self.input_value(&UserInput::Single(*left)).abs();
let y = self.input_value(&UserInput::Single(*up)).abs()
- self.input_value(&UserInput::Single(*down)).abs();
Some(DualAxisData::new(x, y))
}
_ => None,
}
}
}
#[derive(Debug)]
pub struct MutableInputStreams<'a> {
pub gamepad_buttons: &'a mut Input<GamepadButton>,
pub gamepad_button_axes: &'a mut Axis<GamepadButton>,
pub gamepad_axes: &'a mut Axis<GamepadAxis>,
pub gamepads: &'a mut Gamepads,
pub gamepad_events: &'a mut Events<GamepadEventRaw>,
pub keycodes: &'a mut Input<KeyCode>,
pub keyboard_events: &'a mut Events<KeyboardInput>,
pub mouse_buttons: &'a mut Input<MouseButton>,
pub mouse_button_events: &'a mut Events<MouseButtonInput>,
pub mouse_wheel: &'a mut Events<MouseWheel>,
pub mouse_motion: &'a mut Events<MouseMotion>,
pub associated_gamepad: Option<Gamepad>,
}
impl<'a> MutableInputStreams<'a> {
pub fn from_world(world: &'a mut World, gamepad: Option<Gamepad>) -> Self {
let mut input_system_state: SystemState<(
ResMut<Input<GamepadButton>>,
ResMut<Axis<GamepadButton>>,
ResMut<Axis<GamepadAxis>>,
ResMut<Gamepads>,
ResMut<Events<GamepadEventRaw>>,
ResMut<Input<KeyCode>>,
ResMut<Events<KeyboardInput>>,
ResMut<Input<MouseButton>>,
ResMut<Events<MouseButtonInput>>,
ResMut<Events<MouseWheel>>,
ResMut<Events<MouseMotion>>,
)> = SystemState::new(world);
let (
gamepad_buttons,
gamepad_button_axes,
gamepad_axes,
gamepads,
gamepad_events,
keycodes,
keyboard_events,
mouse_buttons,
mouse_button_events,
mouse_wheel,
mouse_motion,
) = input_system_state.get_mut(world);
MutableInputStreams {
gamepad_buttons: gamepad_buttons.into_inner(),
gamepad_button_axes: gamepad_button_axes.into_inner(),
gamepad_axes: gamepad_axes.into_inner(),
gamepads: gamepads.into_inner(),
gamepad_events: gamepad_events.into_inner(),
keycodes: keycodes.into_inner(),
keyboard_events: keyboard_events.into_inner(),
mouse_buttons: mouse_buttons.into_inner(),
mouse_button_events: mouse_button_events.into_inner(),
mouse_wheel: mouse_wheel.into_inner(),
mouse_motion: mouse_motion.into_inner(),
associated_gamepad: gamepad,
}
}
pub fn guess_gamepad(&self) -> Option<Gamepad> {
match self.associated_gamepad {
Some(gamepad) => Some(gamepad),
None => self.gamepads.iter().next(),
}
}
}
impl<'a> From<MutableInputStreams<'a>> for InputStreams<'a> {
fn from(mutable_streams: MutableInputStreams<'a>) -> Self {
InputStreams {
gamepad_buttons: mutable_streams.gamepad_buttons,
gamepad_button_axes: mutable_streams.gamepad_button_axes,
gamepad_axes: mutable_streams.gamepad_axes,
gamepads: mutable_streams.gamepads,
keycodes: Some(mutable_streams.keycodes),
mouse_buttons: Some(mutable_streams.mouse_buttons),
mouse_wheel: Some(mutable_streams.mouse_wheel),
mouse_motion: mutable_streams.mouse_motion,
associated_gamepad: mutable_streams.associated_gamepad,
}
}
}
impl<'a> From<&'a MutableInputStreams<'a>> for InputStreams<'a> {
fn from(mutable_streams: &'a MutableInputStreams<'a>) -> Self {
InputStreams {
gamepad_buttons: mutable_streams.gamepad_buttons,
gamepad_button_axes: mutable_streams.gamepad_button_axes,
gamepad_axes: mutable_streams.gamepad_axes,
gamepads: mutable_streams.gamepads,
keycodes: Some(mutable_streams.keycodes),
mouse_buttons: Some(mutable_streams.mouse_buttons),
mouse_wheel: Some(mutable_streams.mouse_wheel),
mouse_motion: mutable_streams.mouse_motion,
associated_gamepad: mutable_streams.associated_gamepad,
}
}
}
#[cfg(test)]
mod tests {
use super::MutableInputStreams;
use crate::prelude::MockInput;
use bevy::input::InputPlugin;
use bevy::prelude::*;
#[test]
fn modifier_key_triggered_by_either_input() {
use crate::user_input::Modifier;
let mut app = App::new();
app.add_plugin(InputPlugin);
let mut input_streams = MutableInputStreams::from_world(&mut app.world, None);
assert!(!input_streams.pressed(Modifier::Control));
input_streams.send_input(KeyCode::LControl);
app.update();
let mut input_streams = MutableInputStreams::from_world(&mut app.world, None);
assert!(input_streams.pressed(Modifier::Control));
input_streams.reset_inputs();
app.update();
let mut input_streams = MutableInputStreams::from_world(&mut app.world, None);
assert!(!input_streams.pressed(Modifier::Control));
input_streams.send_input(KeyCode::RControl);
app.update();
let input_streams = MutableInputStreams::from_world(&mut app.world, None);
assert!(input_streams.pressed(Modifier::Control));
}
}