Add no_std support to bevy_input (#16995)

# Objective

- Contributes to #15460

## Solution

- Added the following features:
  - `std` (default)
  - `smol_str` (default)
  - `portable-atomic`
  - `critical-section`
  - `libm`
- Fixed an existing issue where `bevy_reflect` wasn't properly feature
gated.

## Testing

- CI

## Notes

- There were some minor issues with `bevy_math` and `bevy_ecs` noticed
in this PR which I have also resolved here. I can split these out if
desired, but I've left them here for now as they're very small changes
and I don't consider this PR itself to be very controversial.
- `libm`, `portable-atomic`, and `critical-section` are shortcuts to
enable the relevant features in dependencies, making the usage of this
crate on atomically challenged platforms possible and simpler.
- `smol_str` is gated as it doesn't support atomically challenged
platforms (e.g., Raspberry Pi Pico). I have an issue and a
[PR](https://github.com/rust-analyzer/smol_str/pull/91) to discuss this
upstream.
This commit is contained in:
Zachary Harrold 2024-12-30 09:46:30 +11:00 committed by GitHub
parent 6114347bc4
commit 46af46695b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 111 additions and 54 deletions

View File

@ -13,9 +13,12 @@ use core::{
}; };
#[cfg(feature = "serialize")] #[cfg(feature = "serialize")]
use serde::{ use {
alloc::string::ToString,
serde::{
de::{Error, Visitor}, de::{Error, Visitor},
Deserialize, Deserializer, Serialize, Serializer, Deserialize, Deserializer, Serialize, Serializer,
},
}; };
#[cfg(feature = "bevy_reflect")] #[cfg(feature = "bevy_reflect")]

View File

@ -9,36 +9,72 @@ license = "MIT OR Apache-2.0"
keywords = ["bevy"] keywords = ["bevy"]
[features] [features]
default = ["bevy_reflect"] default = ["std", "bevy_reflect", "bevy_ecs/async_executor", "smol_str"]
# Functionality
## Adds runtime reflection support using `bevy_reflect`.
bevy_reflect = [ bevy_reflect = [
"dep:bevy_reflect", "dep:bevy_reflect",
"bevy_app/bevy_reflect", "bevy_app/bevy_reflect",
"bevy_ecs/bevy_reflect", "bevy_ecs/bevy_reflect",
"bevy_math/bevy_reflect", "bevy_math/bevy_reflect",
] ]
serialize = ["serde", "smol_str/serde"]
## Adds serialization support through `serde`.
serialize = [
"serde",
"smol_str/serde",
"bevy_ecs/serialize",
"bevy_math/serialize",
]
## Uses the small-string optimization provided by `smol_str`.
smol_str = ["dep:smol_str", "bevy_reflect/smol_str"]
# Platform Compatibility
## Allows access to the `std` crate. Enabling this feature will prevent compilation
## on `no_std` targets, but provides access to certain additional features on
## supported platforms.
std = [
"bevy_app/std",
"bevy_ecs/std",
"bevy_math/std",
"bevy_utils/std",
"bevy_reflect/std",
]
## `critical-section` provides the building blocks for synchronization primitives
## on all platforms, including `no_std`.
critical-section = ["bevy_app/critical-section", "bevy_ecs/critical-section"]
## `portable-atomic` provides additional platform support for atomic types and
## operations, even on targets without native support.
portable-atomic = ["bevy_app/portable-atomic", "bevy_ecs/portable-atomic"]
## Uses the `libm` maths library instead of the one provided in `std` and `core`.
libm = ["bevy_math/libm"]
[dependencies] [dependencies]
# bevy # bevy
bevy_app = { path = "../bevy_app", version = "0.15.0-dev", default-features = false } bevy_app = { path = "../bevy_app", version = "0.15.0-dev", default-features = false }
bevy_ecs = { path = "../bevy_ecs", version = "0.15.0-dev", default-features = false, features = [ bevy_ecs = { path = "../bevy_ecs", version = "0.15.0-dev", default-features = false }
"serialize", bevy_math = { path = "../bevy_math", version = "0.15.0-dev", default-features = false }
] } bevy_utils = { path = "../bevy_utils", version = "0.15.0-dev", default-features = false }
bevy_math = { path = "../bevy_math", version = "0.15.0-dev", default-features = false, features = [
"rand",
"serialize",
] }
bevy_utils = { path = "../bevy_utils", version = "0.15.0-dev" }
bevy_reflect = { path = "../bevy_reflect", version = "0.15.0-dev", features = [ bevy_reflect = { path = "../bevy_reflect", version = "0.15.0-dev", features = [
"glam", "glam",
"smol_str", ], default-features = false, optional = true }
], optional = true }
# other # other
serde = { version = "1", features = ["derive"], optional = true } serde = { version = "1", features = [
"alloc",
"derive",
], default-features = false, optional = true }
thiserror = { version = "2", default-features = false } thiserror = { version = "2", default-features = false }
derive_more = { version = "1", default-features = false, features = ["from"] } derive_more = { version = "1", default-features = false, features = ["from"] }
smol_str = "0.2" smol_str = { version = "0.2", default-features = false, optional = true }
log = { version = "0.4", default-features = false }
[lints] [lints]
workspace = true workspace = true

View File

@ -1,6 +1,7 @@
//! The gamepad input functionality. //! The gamepad input functionality.
use crate::{Axis, ButtonInput, ButtonState}; use crate::{Axis, ButtonInput, ButtonState};
use alloc::string::String;
#[cfg(feature = "bevy_reflect")] #[cfg(feature = "bevy_reflect")]
use bevy_ecs::prelude::ReflectComponent; use bevy_ecs::prelude::ReflectComponent;
use bevy_ecs::{ use bevy_ecs::{
@ -12,16 +13,15 @@ use bevy_ecs::{
prelude::require, prelude::require,
system::{Commands, Query}, system::{Commands, Query},
}; };
use bevy_math::ops;
use bevy_math::Vec2; use bevy_math::Vec2;
#[cfg(feature = "bevy_reflect")] #[cfg(feature = "bevy_reflect")]
use bevy_reflect::{std_traits::ReflectDefault, Reflect}; use bevy_reflect::{std_traits::ReflectDefault, Reflect};
#[cfg(all(feature = "serialize", feature = "bevy_reflect"))] #[cfg(all(feature = "serialize", feature = "bevy_reflect"))]
use bevy_reflect::{ReflectDeserialize, ReflectSerialize}; use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
use bevy_utils::{ use bevy_utils::{Duration, HashMap};
tracing::{info, warn},
Duration, HashMap,
};
use derive_more::derive::From; use derive_more::derive::From;
use log::{info, warn};
use thiserror::Error; use thiserror::Error;
/// A gamepad event. /// A gamepad event.
@ -54,11 +54,11 @@ pub enum GamepadEvent {
/// the in-frame relative ordering of events is important. /// the in-frame relative ordering of events is important.
/// ///
/// This event type is used by `bevy_input` to feed its components. /// This event type is used by `bevy_input` to feed its components.
#[derive(Event, Debug, Clone, PartialEq, Reflect, From)] #[derive(Event, Debug, Clone, PartialEq, From)]
#[reflect(Debug, PartialEq)] #[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, PartialEq))]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr( #[cfg_attr(
feature = "serialize", all(feature = "serialize", feature = "bevy_reflect"),
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize) reflect(Serialize, Deserialize)
)] )]
pub enum RawGamepadEvent { pub enum RawGamepadEvent {
@ -71,11 +71,11 @@ pub enum RawGamepadEvent {
} }
/// [`GamepadButton`] changed event unfiltered by [`GamepadSettings`] /// [`GamepadButton`] changed event unfiltered by [`GamepadSettings`]
#[derive(Event, Debug, Copy, Clone, PartialEq, Reflect)] #[derive(Event, Debug, Copy, Clone, PartialEq)]
#[reflect(Debug, PartialEq)] #[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, PartialEq))]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr( #[cfg_attr(
feature = "serialize", all(feature = "serialize", feature = "bevy_reflect"),
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize) reflect(Serialize, Deserialize)
)] )]
pub struct RawGamepadButtonChangedEvent { pub struct RawGamepadButtonChangedEvent {
@ -99,11 +99,11 @@ impl RawGamepadButtonChangedEvent {
} }
/// [`GamepadAxis`] changed event unfiltered by [`GamepadSettings`] /// [`GamepadAxis`] changed event unfiltered by [`GamepadSettings`]
#[derive(Event, Debug, Copy, Clone, PartialEq, Reflect)] #[derive(Event, Debug, Copy, Clone, PartialEq)]
#[reflect(Debug, PartialEq)] #[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, PartialEq))]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr( #[cfg_attr(
feature = "serialize", all(feature = "serialize", feature = "bevy_reflect"),
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize) reflect(Serialize, Deserialize)
)] )]
pub struct RawGamepadAxisChangedEvent { pub struct RawGamepadAxisChangedEvent {
@ -128,11 +128,11 @@ impl RawGamepadAxisChangedEvent {
/// A Gamepad connection event. Created when a connection to a gamepad /// A Gamepad connection event. Created when a connection to a gamepad
/// is established and when a gamepad is disconnected. /// is established and when a gamepad is disconnected.
#[derive(Event, Debug, Clone, PartialEq, Reflect)] #[derive(Event, Debug, Clone, PartialEq)]
#[reflect(Debug, PartialEq)] #[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, PartialEq))]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr( #[cfg_attr(
feature = "serialize", all(feature = "serialize", feature = "bevy_reflect"),
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize) reflect(Serialize, Deserialize)
)] )]
pub struct GamepadConnectionEvent { pub struct GamepadConnectionEvent {
@ -163,11 +163,11 @@ impl GamepadConnectionEvent {
} }
/// [`GamepadButton`] event triggered by a digital state change /// [`GamepadButton`] event triggered by a digital state change
#[derive(Event, Debug, Clone, Copy, PartialEq, Eq, Reflect)] #[derive(Event, Debug, Clone, Copy, PartialEq, Eq)]
#[reflect(Debug, PartialEq)] #[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, PartialEq))]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr( #[cfg_attr(
feature = "serialize", all(feature = "serialize", feature = "bevy_reflect"),
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize) reflect(Serialize, Deserialize)
)] )]
pub struct GamepadButtonStateChangedEvent { pub struct GamepadButtonStateChangedEvent {
@ -191,11 +191,11 @@ impl GamepadButtonStateChangedEvent {
} }
/// [`GamepadButton`] event triggered by an analog state change /// [`GamepadButton`] event triggered by an analog state change
#[derive(Event, Debug, Clone, Copy, PartialEq, Reflect)] #[derive(Event, Debug, Clone, Copy, PartialEq)]
#[reflect(Debug, PartialEq)] #[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, PartialEq))]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr( #[cfg_attr(
feature = "serialize", all(feature = "serialize", feature = "bevy_reflect"),
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize) reflect(Serialize, Deserialize)
)] )]
pub struct GamepadButtonChangedEvent { pub struct GamepadButtonChangedEvent {
@ -222,11 +222,11 @@ impl GamepadButtonChangedEvent {
} }
/// [`GamepadAxis`] event triggered by an analog state change /// [`GamepadAxis`] event triggered by an analog state change
#[derive(Event, Debug, Clone, Copy, PartialEq, Reflect)] #[derive(Event, Debug, Clone, Copy, PartialEq)]
#[reflect(Debug, PartialEq)] #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, PartialEq))]
#[cfg_attr( #[cfg_attr(
feature = "serialize", all(feature = "bevy_reflect", feature = "serialize"),
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize) reflect(Serialize, Deserialize)
)] )]
pub struct GamepadAxisChangedEvent { pub struct GamepadAxisChangedEvent {
@ -1232,7 +1232,7 @@ impl AxisSettings {
return true; return true;
} }
f32::abs(new_value - old_value.unwrap()) > self.threshold ops::abs(new_value - old_value.unwrap()) > self.threshold
} }
/// Filters the `new_value` based on the `old_value`, according to the [`AxisSettings`]. /// Filters the `new_value` based on the `old_value`, according to the [`AxisSettings`].
@ -1307,7 +1307,7 @@ impl ButtonAxisSettings {
return true; return true;
} }
f32::abs(new_value - old_value.unwrap()) > self.threshold ops::abs(new_value - old_value.unwrap()) > self.threshold
} }
/// Filters the `new_value` based on the `old_value`, according to the [`ButtonAxisSettings`]. /// Filters the `new_value` based on the `old_value`, according to the [`ButtonAxisSettings`].

View File

@ -72,8 +72,14 @@ use bevy_ecs::{
event::{Event, EventReader}, event::{Event, EventReader},
system::ResMut, system::ResMut,
}; };
#[cfg(feature = "bevy_reflect")] #[cfg(feature = "bevy_reflect")]
use bevy_reflect::Reflect; use bevy_reflect::Reflect;
#[cfg(not(feature = "smol_str"))]
use alloc::string::String as SmolStr;
#[cfg(feature = "smol_str")]
use smol_str::SmolStr; use smol_str::SmolStr;
#[cfg(all(feature = "serialize", feature = "bevy_reflect"))] #[cfg(all(feature = "serialize", feature = "bevy_reflect"))]

View File

@ -4,6 +4,7 @@
html_logo_url = "https://bevyengine.org/assets/icon.png", html_logo_url = "https://bevyengine.org/assets/icon.png",
html_favicon_url = "https://bevyengine.org/assets/icon.png" html_favicon_url = "https://bevyengine.org/assets/icon.png"
)] )]
#![cfg_attr(not(feature = "std"), no_std)]
//! Input functionality for the [Bevy game engine](https://bevyengine.org/). //! Input functionality for the [Bevy game engine](https://bevyengine.org/).
//! //!
@ -11,6 +12,8 @@
//! //!
//! `bevy` currently supports keyboard, mouse, gamepad, and touch inputs. //! `bevy` currently supports keyboard, mouse, gamepad, and touch inputs.
extern crate alloc;
mod axis; mod axis;
mod button_input; mod button_input;
/// Common run conditions /// Common run conditions

View File

@ -25,7 +25,7 @@ approx = { version = "0.5", default-features = false, optional = true }
rand = { version = "0.8", default-features = false, optional = true } rand = { version = "0.8", default-features = false, optional = true }
rand_distr = { version = "0.4.3", optional = true } rand_distr = { version = "0.4.3", optional = true }
smallvec = { version = "1.11" } smallvec = { version = "1.11" }
bevy_reflect = { path = "../bevy_reflect", version = "0.15.0-dev", features = [ bevy_reflect = { path = "../bevy_reflect", version = "0.15.0-dev", default-features = false, features = [
"glam", "glam",
], optional = true } ], optional = true }
variadics_please = "1.1" variadics_please = "1.1"
@ -52,6 +52,7 @@ std = [
"approx?/std", "approx?/std",
"rand?/std", "rand?/std",
"rand_distr?/std", "rand_distr?/std",
"bevy_reflect?/std",
] ]
alloc = [ alloc = [
"itertools/use_alloc", "itertools/use_alloc",
@ -76,8 +77,8 @@ debug_glam_assert = ["glam/debug-glam-assert"]
rand = ["dep:rand", "dep:rand_distr", "glam/rand"] rand = ["dep:rand", "dep:rand_distr", "glam/rand"]
# Include code related to the Curve trait # Include code related to the Curve trait
curve = [] curve = []
# Enable bevy_reflect (requires std) # Enable bevy_reflect (requires alloc)
bevy_reflect = ["dep:bevy_reflect", "std"] bevy_reflect = ["dep:bevy_reflect", "alloc"]
[lints] [lints]
workspace = true workspace = true

View File

@ -118,6 +118,14 @@ impl Prepare for CompileCheckNoStdCommand {
"Please fix compiler errors in output above for bevy_hierarchy no_std compatibility.", "Please fix compiler errors in output above for bevy_hierarchy no_std compatibility.",
)); ));
commands.push(PreparedCommand::new::<Self>(
cmd!(
sh,
"cargo check -p bevy_input --no-default-features --features libm,serialize,bevy_reflect --target {target}"
),
"Please fix compiler errors in output above for bevy_input no_std compatibility.",
));
commands commands
} }
} }