
# Objective Resolves #3824. `unsafe` code should be the exception, not the norm in Rust. It's obviously needed for various use cases as it's interfacing with platforms and essentially running the borrow checker at runtime in the ECS, but the touted benefits of Bevy is that we are able to heavily leverage Rust's safety, and we should be holding ourselves accountable to that by minimizing our unsafe footprint. ## Solution Deny `unsafe_code` workspace wide. Add explicit exceptions for the following crates, and forbid it in almost all of the others. * bevy_ecs - Obvious given how much unsafe is needed to achieve performant results * bevy_ptr - Works with raw pointers, even more low level than bevy_ecs. * bevy_render - due to needing to integrate with wgpu * bevy_window - due to needing to integrate with raw_window_handle * bevy_utils - Several unsafe utilities used by bevy_ecs. Ideally moved into bevy_ecs instead of made publicly usable. * bevy_reflect - Required for the unsafe type casting it's doing. * bevy_transform - for the parallel transform propagation * bevy_gizmos - For the SystemParam impls it has. * bevy_assets - To support reflection. Might not be required, not 100% sure yet. * bevy_mikktspace - due to being a conversion from a C library. Pending safe rewrite. * bevy_dynamic_plugin - Inherently unsafe due to the dynamic loading nature. Several uses of unsafe were rewritten, as they did not need to be using them: * bevy_text - a case of `Option::unchecked` could be rewritten as a normal for loop and match instead of an iterator. * bevy_color - the Pod/Zeroable implementations were replaceable with bytemuck's derive macros.
147 lines
4.8 KiB
Rust
147 lines
4.8 KiB
Rust
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
|
#![forbid(unsafe_code)]
|
|
#![doc(
|
|
html_logo_url = "https://bevyengine.org/assets/icon.png",
|
|
html_favicon_url = "https://bevyengine.org/assets/icon.png"
|
|
)]
|
|
|
|
//! Input functionality for the [Bevy game engine](https://bevyengine.org/).
|
|
//!
|
|
//! # Supported input devices
|
|
//!
|
|
//! `bevy` currently supports keyboard, mouse, gamepad, and touch inputs.
|
|
|
|
mod axis;
|
|
mod button_input;
|
|
/// Common run conditions
|
|
pub mod common_conditions;
|
|
pub mod gamepad;
|
|
pub mod keyboard;
|
|
pub mod mouse;
|
|
pub mod touch;
|
|
pub mod touchpad;
|
|
|
|
pub use axis::*;
|
|
pub use button_input::*;
|
|
|
|
/// Most commonly used re-exported types.
|
|
pub mod prelude {
|
|
#[doc(hidden)]
|
|
pub use crate::{
|
|
gamepad::{
|
|
Gamepad, GamepadAxis, GamepadAxisType, GamepadButton, GamepadButtonType, Gamepads,
|
|
},
|
|
keyboard::KeyCode,
|
|
mouse::MouseButton,
|
|
touch::{TouchInput, Touches},
|
|
Axis, ButtonInput,
|
|
};
|
|
}
|
|
|
|
use bevy_app::prelude::*;
|
|
use bevy_ecs::prelude::*;
|
|
use bevy_reflect::Reflect;
|
|
use keyboard::{keyboard_input_system, KeyCode, KeyboardInput};
|
|
use mouse::{mouse_button_input_system, MouseButton, MouseButtonInput, MouseMotion, MouseWheel};
|
|
use touch::{touch_screen_input_system, TouchInput, Touches};
|
|
use touchpad::{TouchpadMagnify, TouchpadRotate};
|
|
|
|
use gamepad::{
|
|
gamepad_axis_event_system, gamepad_button_event_system, gamepad_connection_system,
|
|
gamepad_event_system, GamepadAxis, GamepadAxisChangedEvent, GamepadButton,
|
|
GamepadButtonChangedEvent, GamepadButtonInput, GamepadConnectionEvent, GamepadEvent,
|
|
GamepadRumbleRequest, GamepadSettings, Gamepads,
|
|
};
|
|
|
|
#[cfg(feature = "serialize")]
|
|
use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
|
|
|
|
/// Adds keyboard and mouse input to an App
|
|
#[derive(Default)]
|
|
pub struct InputPlugin;
|
|
|
|
/// Label for systems that update the input data.
|
|
#[derive(Debug, PartialEq, Eq, Clone, Hash, SystemSet)]
|
|
pub struct InputSystem;
|
|
|
|
impl Plugin for InputPlugin {
|
|
fn build(&self, app: &mut App) {
|
|
app
|
|
// keyboard
|
|
.add_event::<KeyboardInput>()
|
|
.init_resource::<ButtonInput<KeyCode>>()
|
|
.add_systems(PreUpdate, keyboard_input_system.in_set(InputSystem))
|
|
// mouse
|
|
.add_event::<MouseButtonInput>()
|
|
.add_event::<MouseMotion>()
|
|
.add_event::<MouseWheel>()
|
|
.init_resource::<ButtonInput<MouseButton>>()
|
|
.add_systems(PreUpdate, mouse_button_input_system.in_set(InputSystem))
|
|
.add_event::<TouchpadMagnify>()
|
|
.add_event::<TouchpadRotate>()
|
|
// gamepad
|
|
.add_event::<GamepadConnectionEvent>()
|
|
.add_event::<GamepadButtonChangedEvent>()
|
|
.add_event::<GamepadButtonInput>()
|
|
.add_event::<GamepadAxisChangedEvent>()
|
|
.add_event::<GamepadEvent>()
|
|
.add_event::<GamepadRumbleRequest>()
|
|
.init_resource::<GamepadSettings>()
|
|
.init_resource::<Gamepads>()
|
|
.init_resource::<ButtonInput<GamepadButton>>()
|
|
.init_resource::<Axis<GamepadAxis>>()
|
|
.init_resource::<Axis<GamepadButton>>()
|
|
.add_systems(
|
|
PreUpdate,
|
|
(
|
|
gamepad_event_system,
|
|
gamepad_connection_system.after(gamepad_event_system),
|
|
gamepad_button_event_system
|
|
.after(gamepad_event_system)
|
|
.after(gamepad_connection_system),
|
|
gamepad_axis_event_system
|
|
.after(gamepad_event_system)
|
|
.after(gamepad_connection_system),
|
|
)
|
|
.in_set(InputSystem),
|
|
)
|
|
// touch
|
|
.add_event::<TouchInput>()
|
|
.init_resource::<Touches>()
|
|
.add_systems(PreUpdate, touch_screen_input_system.in_set(InputSystem));
|
|
|
|
// Register common types
|
|
app.register_type::<ButtonState>()
|
|
.register_type::<KeyboardInput>()
|
|
.register_type::<MouseButtonInput>()
|
|
.register_type::<TouchpadMagnify>()
|
|
.register_type::<TouchpadRotate>()
|
|
.register_type::<TouchInput>()
|
|
.register_type::<GamepadEvent>()
|
|
.register_type::<GamepadButtonInput>()
|
|
.register_type::<GamepadSettings>();
|
|
}
|
|
}
|
|
|
|
/// The current "press" state of an element
|
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Reflect)]
|
|
#[reflect(Debug, Hash, PartialEq)]
|
|
#[cfg_attr(
|
|
feature = "serialize",
|
|
derive(serde::Serialize, serde::Deserialize),
|
|
reflect(Serialize, Deserialize)
|
|
)]
|
|
pub enum ButtonState {
|
|
/// The button is pressed.
|
|
Pressed,
|
|
/// The button is not pressed.
|
|
Released,
|
|
}
|
|
|
|
impl ButtonState {
|
|
/// Is this button pressed?
|
|
pub fn is_pressed(&self) -> bool {
|
|
matches!(self, ButtonState::Pressed)
|
|
}
|
|
}
|