Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

View File

@@ -0,0 +1 @@
{"files":{"Cargo.lock":"343f1c828275a2b103d2cdadafe6dccb8e4810402800afa6d282a03e0cdad02a","Cargo.toml":"9280579965895ecac3b126f764c751d8e9d6712cf8cb71522f3520e49580e0e3","LICENSE-APACHE":"a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9","LICENSE-MIT":"508a77d2e7b51d98adeed32648ad124b7b30241a8e70b2e72c99f92d8e5874d1","README.md":"495b39189027cf8a965a32f8c2d8974acd0799f6536d15702b616635d4cd279d","src/axis.rs":"f5df81ebc1d1894c9f50305131014de6b0bd910ae36c23da505bafa54b315e40","src/button_input.rs":"b312caf6f0d8a9b964f42455dc0d9d8c88e20427f3667bc8978ed3eae46e6f0b","src/common_conditions.rs":"47d55e324d131445528c5e023cdf7aa1096d9265d951416d5e9f59770e532d26","src/gamepad.rs":"297efb7e92da906096b8fd4ece68a0fe3ae4181771bfc5e602f062f8fe97a27d","src/gestures.rs":"52933832b4cffb03ccf10ee9fa334c9dc3bd1bfd4b4997dc7833e121d4515c8e","src/keyboard.rs":"1f3151cd29c7ec059c39150177988e84d63e89ced0fe20693f1eb03c94c3ec60","src/lib.rs":"b419dfe5628a026059998a1292209d4947ec63815bb8a1b5ef2f4d37d6015d44","src/mouse.rs":"f8279fc6c319b2578f1304bc305ef144bb8fe8b779bc1d30c50c10683ee52301","src/touch.rs":"70d443395c74cfc090404dd1d871207060e0ebc70ae831ad99a7264c1abfd576"},"package":"18d6b6516433f6f7d680f648d04eb1866bb3927a1782d52f74831b62042f3cd1"}

1128
vendor/bevy_input/Cargo.lock generated vendored Normal file

File diff suppressed because it is too large Load Diff

168
vendor/bevy_input/Cargo.toml vendored Normal file
View File

@@ -0,0 +1,168 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2024"
name = "bevy_input"
version = "0.16.1"
build = false
autolib = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "Provides input functionality for Bevy Engine"
homepage = "https://bevyengine.org"
readme = "README.md"
keywords = ["bevy"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/bevyengine/bevy"
resolver = "2"
[package.metadata.docs.rs]
all-features = true
rustdoc-args = [
"-Zunstable-options",
"--generate-link-to-definition",
]
[features]
bevy_reflect = [
"dep:bevy_reflect",
"bevy_app/bevy_reflect",
"bevy_ecs/bevy_reflect",
"bevy_math/bevy_reflect",
]
critical-section = [
"bevy_app/critical-section",
"bevy_ecs/critical-section",
"bevy_reflect?/critical-section",
"bevy_platform/critical-section",
]
default = [
"std",
"bevy_reflect",
"bevy_ecs/async_executor",
"smol_str",
]
libm = ["bevy_math/libm"]
serialize = [
"serde",
"smol_str?/serde",
"bevy_ecs/serialize",
"bevy_math/serialize",
"bevy_platform/serialize",
]
smol_str = [
"dep:smol_str",
"bevy_reflect/smol_str",
]
std = [
"bevy_app/std",
"bevy_ecs/std",
"bevy_math/std",
"bevy_utils/std",
"bevy_reflect/std",
"bevy_platform/std",
]
[lib]
name = "bevy_input"
path = "src/lib.rs"
[dependencies.bevy_app]
version = "0.16.1"
default-features = false
[dependencies.bevy_ecs]
version = "0.16.1"
default-features = false
[dependencies.bevy_math]
version = "0.16.1"
default-features = false
[dependencies.bevy_platform]
version = "0.16.1"
default-features = false
[dependencies.bevy_reflect]
version = "0.16.1"
features = ["glam"]
optional = true
default-features = false
[dependencies.bevy_utils]
version = "0.16.1"
default-features = false
[dependencies.derive_more]
version = "1"
features = ["from"]
default-features = false
[dependencies.log]
version = "0.4"
default-features = false
[dependencies.serde]
version = "1"
features = [
"alloc",
"derive",
]
optional = true
default-features = false
[dependencies.smol_str]
version = "0.2"
optional = true
default-features = false
[dependencies.thiserror]
version = "2"
default-features = false
[lints.clippy]
alloc_instead_of_core = "warn"
allow_attributes = "warn"
allow_attributes_without_reason = "warn"
doc_markdown = "warn"
manual_let_else = "warn"
match_same_arms = "warn"
needless_lifetimes = "allow"
nonstandard_macro_braces = "warn"
print_stderr = "warn"
print_stdout = "warn"
ptr_as_ptr = "warn"
ptr_cast_constness = "warn"
redundant_closure_for_method_calls = "warn"
redundant_else = "warn"
ref_as_ptr = "warn"
semicolon_if_nothing_returned = "warn"
std_instead_of_alloc = "warn"
std_instead_of_core = "warn"
too_long_first_doc_paragraph = "allow"
too_many_arguments = "allow"
type_complexity = "allow"
undocumented_unsafe_blocks = "warn"
unwrap_or_default = "warn"
[lints.rust]
missing_docs = "warn"
unsafe_code = "deny"
unsafe_op_in_unsafe_fn = "warn"
unused_qualifications = "warn"
[lints.rust.unexpected_cfgs]
level = "warn"
priority = 0
check-cfg = ["cfg(docsrs_dep)"]

176
vendor/bevy_input/LICENSE-APACHE vendored Normal file
View File

@@ -0,0 +1,176 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

19
vendor/bevy_input/LICENSE-MIT vendored Normal file
View File

@@ -0,0 +1,19 @@
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

7
vendor/bevy_input/README.md vendored Normal file
View File

@@ -0,0 +1,7 @@
# Bevy Input
[![License](https://img.shields.io/badge/license-MIT%2FApache-blue.svg)](https://github.com/bevyengine/bevy#license)
[![Crates.io](https://img.shields.io/crates/v/bevy_input.svg)](https://crates.io/crates/bevy_input)
[![Downloads](https://img.shields.io/crates/d/bevy_input.svg)](https://crates.io/crates/bevy_input)
[![Docs](https://docs.rs/bevy_input/badge.svg)](https://docs.rs/bevy_input/latest/bevy_input/)
[![Discord](https://img.shields.io/discord/691052431525675048.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/bevy)

136
vendor/bevy_input/src/axis.rs vendored Normal file
View File

@@ -0,0 +1,136 @@
//! The generic axis type.
use bevy_ecs::resource::Resource;
use bevy_platform::collections::HashMap;
use core::hash::Hash;
#[cfg(feature = "bevy_reflect")]
use bevy_reflect::Reflect;
/// Stores the position data of the input devices of type `T`.
///
/// The values are stored as `f32`s, using [`Axis::set`].
/// Use [`Axis::get`] to retrieve the value clamped between [`Axis::MIN`] and [`Axis::MAX`]
/// inclusive, or unclamped using [`Axis::get_unclamped`].
#[derive(Debug, Resource)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
pub struct Axis<T> {
/// The position data of the input devices.
axis_data: HashMap<T, f32>,
}
impl<T> Default for Axis<T>
where
T: Copy + Eq + Hash,
{
fn default() -> Self {
Axis {
axis_data: HashMap::default(),
}
}
}
impl<T> Axis<T>
where
T: Copy + Eq + Hash,
{
/// The smallest possible axis value.
pub const MIN: f32 = -1.0;
/// The largest possible axis value.
pub const MAX: f32 = 1.0;
/// Sets the position data of the `input_device` to `position_data`.
///
/// If the `input_device`:
/// - was present before, the position data is updated, and the old value is returned.
/// - wasn't present before, `None` is returned.
pub fn set(&mut self, input_device: impl Into<T>, position_data: f32) -> Option<f32> {
self.axis_data.insert(input_device.into(), position_data)
}
/// Returns the position data of the provided `input_device`.
///
/// This will be clamped between [`Axis::MIN`] and [`Axis::MAX`] inclusive.
pub fn get(&self, input_device: impl Into<T>) -> Option<f32> {
self.axis_data
.get(&input_device.into())
.copied()
.map(|value| value.clamp(Self::MIN, Self::MAX))
}
/// Returns the unclamped position data of the provided `input_device`.
///
/// This value may be outside the [`Axis::MIN`] and [`Axis::MAX`] range.
///
/// Use for things like camera zoom, where you want devices like mouse wheels to be able to
/// exceed the normal range. If being able to move faster on one input device
/// than another would give an unfair advantage, you should likely use [`Axis::get`] instead.
pub fn get_unclamped(&self, input_device: impl Into<T>) -> Option<f32> {
self.axis_data.get(&input_device.into()).copied()
}
/// Removes the position data of the `input_device`, returning the position data if the input device was previously set.
pub fn remove(&mut self, input_device: impl Into<T>) -> Option<f32> {
self.axis_data.remove(&input_device.into())
}
/// Returns an iterator over all axes.
pub fn all_axes(&self) -> impl Iterator<Item = &T> {
self.axis_data.keys()
}
/// Returns an iterator over all axes and their values.
pub fn all_axes_and_values(&self) -> impl Iterator<Item = (&T, f32)> {
self.axis_data.iter().map(|(axis, value)| (axis, *value))
}
}
#[cfg(test)]
mod tests {
use crate::{gamepad::GamepadButton, Axis};
#[test]
fn test_axis_set() {
let cases = [
(-1.5, Some(-1.0)),
(-1.1, Some(-1.0)),
(-1.0, Some(-1.0)),
(-0.9, Some(-0.9)),
(-0.1, Some(-0.1)),
(0.0, Some(0.0)),
(0.1, Some(0.1)),
(0.9, Some(0.9)),
(1.0, Some(1.0)),
(1.1, Some(1.0)),
(1.6, Some(1.0)),
];
for (value, expected) in cases {
let mut axis = Axis::<GamepadButton>::default();
axis.set(GamepadButton::RightTrigger, value);
let actual = axis.get(GamepadButton::RightTrigger);
assert_eq!(expected, actual);
}
}
#[test]
fn test_axis_remove() {
let cases = [-1.0, -0.9, -0.1, 0.0, 0.1, 0.9, 1.0];
for value in cases {
let mut axis = Axis::<GamepadButton>::default();
axis.set(GamepadButton::RightTrigger, value);
assert!(axis.get(GamepadButton::RightTrigger).is_some());
axis.remove(GamepadButton::RightTrigger);
let actual = axis.get(GamepadButton::RightTrigger);
let expected = None;
assert_eq!(expected, actual);
}
}
}

580
vendor/bevy_input/src/button_input.rs vendored Normal file
View File

@@ -0,0 +1,580 @@
//! The generic input type.
use bevy_ecs::resource::Resource;
use bevy_platform::collections::HashSet;
use core::hash::Hash;
#[cfg(feature = "bevy_reflect")]
use {
bevy_ecs::reflect::ReflectResource,
bevy_reflect::{std_traits::ReflectDefault, Reflect},
};
/// A "press-able" input of type `T`.
///
/// ## Usage
///
/// This type can be used as a resource to keep the current state of an input, by reacting to
/// events from the input. For a given input value:
///
/// * [`ButtonInput::pressed`] will return `true` between a press and a release event.
/// * [`ButtonInput::just_pressed`] will return `true` for one frame after a press event.
/// * [`ButtonInput::just_released`] will return `true` for one frame after a release event.
///
/// ## Multiple systems
///
/// In case multiple systems are checking for [`ButtonInput::just_pressed`] or [`ButtonInput::just_released`]
/// but only one should react, for example when modifying a
/// [`Resource`], you should consider clearing the input state, either by:
///
/// * Using [`ButtonInput::clear_just_pressed`] or [`ButtonInput::clear_just_released`] instead.
/// * Calling [`ButtonInput::clear`] or [`ButtonInput::reset`] immediately after the state change.
///
/// ## Performance
///
/// For all operations, the following conventions are used:
/// - **n** is the number of stored inputs.
/// - **m** is the number of input arguments passed to the method.
/// - **\***-suffix denotes an amortized cost.
/// - **~**-suffix denotes an expected cost.
///
/// See Rust's [std::collections doc on performance](https://doc.rust-lang.org/std/collections/index.html#performance) for more details on the conventions used here.
///
/// | **[`ButtonInput`] operations** | **Computational complexity** |
/// |-----------------------------------|------------------------------------|
/// | [`ButtonInput::any_just_pressed`] | *O*(m)~ |
/// | [`ButtonInput::any_just_released`] | *O*(m)~ |
/// | [`ButtonInput::any_pressed`] | *O*(m)~ |
/// | [`ButtonInput::get_just_pressed`] | *O*(n) |
/// | [`ButtonInput::get_just_released`] | *O*(n) |
/// | [`ButtonInput::get_pressed`] | *O*(n) |
/// | [`ButtonInput::just_pressed`] | *O*(1)~ |
/// | [`ButtonInput::just_released`] | *O*(1)~ |
/// | [`ButtonInput::pressed`] | *O*(1)~ |
/// | [`ButtonInput::press`] | *O*(1)~* |
/// | [`ButtonInput::release`] | *O*(1)~* |
/// | [`ButtonInput::release_all`] | *O*(n)~* |
/// | [`ButtonInput::clear_just_pressed`] | *O*(1)~ |
/// | [`ButtonInput::clear_just_released`] | *O*(1)~ |
/// | [`ButtonInput::reset_all`] | *O*(n) |
/// | [`ButtonInput::clear`] | *O*(n) |
///
/// ## Window focus
///
/// `ButtonInput<KeyCode>` is tied to window focus. For example, if the user holds a button
/// while the window loses focus, [`ButtonInput::just_released`] will be triggered. Similarly if the window
/// regains focus, [`ButtonInput::just_pressed`] will be triggered.
///
/// `ButtonInput<GamepadButton>` is independent of window focus.
///
/// ## Examples
///
/// Reading and checking against the current set of pressed buttons:
/// ```no_run
/// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins, Update};
/// # use bevy_ecs::{prelude::{IntoScheduleConfigs, Res, Resource, resource_changed}, schedule::Condition};
/// # use bevy_input::{ButtonInput, prelude::{KeyCode, MouseButton}};
///
/// fn main() {
/// App::new()
/// .add_plugins(DefaultPlugins)
/// .add_systems(
/// Update,
/// print_mouse.run_if(resource_changed::<ButtonInput<MouseButton>>),
/// )
/// .add_systems(
/// Update,
/// print_keyboard.run_if(resource_changed::<ButtonInput<KeyCode>>),
/// )
/// .run();
/// }
///
/// fn print_mouse(mouse: Res<ButtonInput<MouseButton>>) {
/// println!("Mouse: {:?}", mouse.get_pressed().collect::<Vec<_>>());
/// }
///
/// fn print_keyboard(keyboard: Res<ButtonInput<KeyCode>>) {
/// if keyboard.any_pressed([KeyCode::ControlLeft, KeyCode::ControlRight])
/// && keyboard.any_pressed([KeyCode::AltLeft, KeyCode::AltRight])
/// && keyboard.any_pressed([KeyCode::ShiftLeft, KeyCode::ShiftRight])
/// && keyboard.any_pressed([KeyCode::SuperLeft, KeyCode::SuperRight])
/// && keyboard.pressed(KeyCode::KeyL)
/// {
/// println!("On Windows this opens LinkedIn.");
/// } else {
/// println!("keyboard: {:?}", keyboard.get_pressed().collect::<Vec<_>>());
/// }
/// }
/// ```
///
/// ## Note
///
/// When adding this resource for a new input type, you should:
///
/// * Call the [`ButtonInput::press`] method for each press event.
/// * Call the [`ButtonInput::release`] method for each release event.
/// * Call the [`ButtonInput::clear`] method at each frame start, before processing events.
///
/// Note: Calling `clear` from a [`ResMut`] will trigger change detection.
/// It may be preferable to use [`DetectChangesMut::bypass_change_detection`]
/// to avoid causing the resource to always be marked as changed.
///
/// [`ResMut`]: bevy_ecs::system::ResMut
/// [`DetectChangesMut::bypass_change_detection`]: bevy_ecs::change_detection::DetectChangesMut::bypass_change_detection
#[derive(Debug, Clone, Resource)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Default, Resource))]
pub struct ButtonInput<T: Copy + Eq + Hash + Send + Sync + 'static> {
/// A collection of every button that is currently being pressed.
pressed: HashSet<T>,
/// A collection of every button that has just been pressed.
just_pressed: HashSet<T>,
/// A collection of every button that has just been released.
just_released: HashSet<T>,
}
impl<T: Copy + Eq + Hash + Send + Sync + 'static> Default for ButtonInput<T> {
fn default() -> Self {
Self {
pressed: Default::default(),
just_pressed: Default::default(),
just_released: Default::default(),
}
}
}
impl<T> ButtonInput<T>
where
T: Copy + Eq + Hash + Send + Sync + 'static,
{
/// Registers a press for the given `input`.
pub fn press(&mut self, input: T) {
// Returns `true` if the `input` wasn't pressed.
if self.pressed.insert(input) {
self.just_pressed.insert(input);
}
}
/// Returns `true` if the `input` has been pressed.
pub fn pressed(&self, input: T) -> bool {
self.pressed.contains(&input)
}
/// Returns `true` if any item in `inputs` has been pressed.
pub fn any_pressed(&self, inputs: impl IntoIterator<Item = T>) -> bool {
inputs.into_iter().any(|it| self.pressed(it))
}
/// Returns `true` if all items in `inputs` have been pressed.
pub fn all_pressed(&self, inputs: impl IntoIterator<Item = T>) -> bool {
inputs.into_iter().all(|it| self.pressed(it))
}
/// Registers a release for the given `input`.
pub fn release(&mut self, input: T) {
// Returns `true` if the `input` was pressed.
if self.pressed.remove(&input) {
self.just_released.insert(input);
}
}
/// Registers a release for all currently pressed inputs.
pub fn release_all(&mut self) {
// Move all items from pressed into just_released
self.just_released.extend(self.pressed.drain());
}
/// Returns `true` if the `input` has been pressed during the current frame.
///
/// Note: This function does not imply information regarding the current state of [`ButtonInput::pressed`] or [`ButtonInput::just_released`].
pub fn just_pressed(&self, input: T) -> bool {
self.just_pressed.contains(&input)
}
/// Returns `true` if any item in `inputs` has been pressed during the current frame.
pub fn any_just_pressed(&self, inputs: impl IntoIterator<Item = T>) -> bool {
inputs.into_iter().any(|it| self.just_pressed(it))
}
/// Clears the `just_pressed` state of the `input` and returns `true` if the `input` has just been pressed.
///
/// Future calls to [`ButtonInput::just_pressed`] for the given input will return false until a new press event occurs.
pub fn clear_just_pressed(&mut self, input: T) -> bool {
self.just_pressed.remove(&input)
}
/// Returns `true` if the `input` has been released during the current frame.
///
/// Note: This function does not imply information regarding the current state of [`ButtonInput::pressed`] or [`ButtonInput::just_pressed`].
pub fn just_released(&self, input: T) -> bool {
self.just_released.contains(&input)
}
/// Returns `true` if any item in `inputs` has just been released.
pub fn any_just_released(&self, inputs: impl IntoIterator<Item = T>) -> bool {
inputs.into_iter().any(|input| self.just_released(input))
}
/// Returns `true` if all items in `inputs` have just been released.
pub fn all_just_released(&self, inputs: impl IntoIterator<Item = T>) -> bool {
inputs.into_iter().all(|input| self.just_released(input))
}
/// Returns `true` if all items in `inputs` have been just pressed.
pub fn all_just_pressed(&self, inputs: impl IntoIterator<Item = T>) -> bool {
inputs.into_iter().all(|input| self.just_pressed(input))
}
/// Clears the `just_released` state of the `input` and returns `true` if the `input` has just been released.
///
/// Future calls to [`ButtonInput::just_released`] for the given input will return false until a new release event occurs.
pub fn clear_just_released(&mut self, input: T) -> bool {
self.just_released.remove(&input)
}
/// Clears the `pressed`, `just_pressed` and `just_released` data of the `input`.
pub fn reset(&mut self, input: T) {
self.pressed.remove(&input);
self.just_pressed.remove(&input);
self.just_released.remove(&input);
}
/// Clears the `pressed`, `just_pressed`, and `just_released` data for every input.
///
/// See also [`ButtonInput::clear`] for simulating elapsed time steps.
pub fn reset_all(&mut self) {
self.pressed.clear();
self.just_pressed.clear();
self.just_released.clear();
}
/// Clears the `just pressed` and `just released` data for every input.
///
/// See also [`ButtonInput::reset_all`] for a full reset.
pub fn clear(&mut self) {
self.just_pressed.clear();
self.just_released.clear();
}
/// An iterator visiting every pressed input in arbitrary order.
pub fn get_pressed(&self) -> impl ExactSizeIterator<Item = &T> {
self.pressed.iter()
}
/// An iterator visiting every just pressed input in arbitrary order.
///
/// Note: Returned elements do not imply information regarding the current state of [`ButtonInput::pressed`] or [`ButtonInput::just_released`].
pub fn get_just_pressed(&self) -> impl ExactSizeIterator<Item = &T> {
self.just_pressed.iter()
}
/// An iterator visiting every just released input in arbitrary order.
///
/// Note: Returned elements do not imply information regarding the current state of [`ButtonInput::pressed`] or [`ButtonInput::just_pressed`].
pub fn get_just_released(&self) -> impl ExactSizeIterator<Item = &T> {
self.just_released.iter()
}
}
#[cfg(test)]
mod test {
use crate::ButtonInput;
/// Used for testing the functionality of [`ButtonInput`].
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
enum DummyInput {
Input1,
Input2,
}
#[test]
fn test_press() {
let mut input = ButtonInput::default();
assert!(!input.pressed.contains(&DummyInput::Input1));
assert!(!input.just_pressed.contains(&DummyInput::Input1));
input.press(DummyInput::Input1);
assert!(input.just_pressed.contains(&DummyInput::Input1));
assert!(input.pressed.contains(&DummyInput::Input1));
}
#[test]
fn test_pressed() {
let mut input = ButtonInput::default();
assert!(!input.pressed(DummyInput::Input1));
input.press(DummyInput::Input1);
assert!(input.pressed(DummyInput::Input1));
}
#[test]
fn test_any_pressed() {
let mut input = ButtonInput::default();
assert!(!input.any_pressed([DummyInput::Input1]));
assert!(!input.any_pressed([DummyInput::Input2]));
assert!(!input.any_pressed([DummyInput::Input1, DummyInput::Input2]));
input.press(DummyInput::Input1);
assert!(input.any_pressed([DummyInput::Input1]));
assert!(!input.any_pressed([DummyInput::Input2]));
assert!(input.any_pressed([DummyInput::Input1, DummyInput::Input2]));
}
#[test]
fn test_all_pressed() {
let mut input = ButtonInput::default();
assert!(!input.all_pressed([DummyInput::Input1]));
assert!(!input.all_pressed([DummyInput::Input2]));
assert!(!input.all_pressed([DummyInput::Input1, DummyInput::Input2]));
input.press(DummyInput::Input1);
assert!(input.all_pressed([DummyInput::Input1]));
assert!(!input.all_pressed([DummyInput::Input1, DummyInput::Input2]));
input.press(DummyInput::Input2);
assert!(input.all_pressed([DummyInput::Input1, DummyInput::Input2]));
}
#[test]
fn test_release() {
let mut input = ButtonInput::default();
input.press(DummyInput::Input1);
assert!(input.pressed.contains(&DummyInput::Input1));
assert!(!input.just_released.contains(&DummyInput::Input1));
input.release(DummyInput::Input1);
assert!(!input.pressed.contains(&DummyInput::Input1));
assert!(input.just_released.contains(&DummyInput::Input1));
}
#[test]
fn test_release_all() {
let mut input = ButtonInput::default();
input.press(DummyInput::Input1);
input.press(DummyInput::Input2);
input.release_all();
assert!(input.pressed.is_empty());
assert!(input.just_released.contains(&DummyInput::Input1));
assert!(input.just_released.contains(&DummyInput::Input2));
}
#[test]
fn test_just_pressed() {
let mut input = ButtonInput::default();
assert!(!input.just_pressed(DummyInput::Input1));
input.press(DummyInput::Input1);
assert!(input.just_pressed(DummyInput::Input1));
}
#[test]
fn test_any_just_pressed() {
let mut input = ButtonInput::default();
assert!(!input.any_just_pressed([DummyInput::Input1]));
assert!(!input.any_just_pressed([DummyInput::Input2]));
assert!(!input.any_just_pressed([DummyInput::Input1, DummyInput::Input2]));
input.press(DummyInput::Input1);
assert!(input.any_just_pressed([DummyInput::Input1]));
assert!(!input.any_just_pressed([DummyInput::Input2]));
assert!(input.any_just_pressed([DummyInput::Input1, DummyInput::Input2]));
}
#[test]
fn test_clear_just_pressed() {
let mut input = ButtonInput::default();
input.press(DummyInput::Input1);
assert!(input.just_pressed(DummyInput::Input1));
input.clear_just_pressed(DummyInput::Input1);
assert!(!input.just_pressed(DummyInput::Input1));
}
#[test]
fn test_just_released() {
let mut input = ButtonInput::default();
input.press(DummyInput::Input1);
assert!(!input.just_released(DummyInput::Input1));
input.release(DummyInput::Input1);
assert!(input.just_released(DummyInput::Input1));
}
#[test]
fn test_any_just_released() {
let mut input = ButtonInput::default();
input.press(DummyInput::Input1);
assert!(!input.any_just_released([DummyInput::Input1]));
assert!(!input.any_just_released([DummyInput::Input2]));
assert!(!input.any_just_released([DummyInput::Input1, DummyInput::Input2]));
input.release(DummyInput::Input1);
assert!(input.any_just_released([DummyInput::Input1]));
assert!(!input.any_just_released([DummyInput::Input2]));
assert!(input.any_just_released([DummyInput::Input1, DummyInput::Input2]));
}
#[test]
fn test_clear_just_released() {
let mut input = ButtonInput::default();
input.press(DummyInput::Input1);
input.release(DummyInput::Input1);
assert!(input.just_released(DummyInput::Input1));
input.clear_just_released(DummyInput::Input1);
assert!(!input.just_released(DummyInput::Input1));
}
#[test]
fn test_reset() {
let mut input = ButtonInput::default();
// Pressed
input.press(DummyInput::Input1);
assert!(input.pressed(DummyInput::Input1));
assert!(input.just_pressed(DummyInput::Input1));
assert!(!input.just_released(DummyInput::Input1));
input.reset(DummyInput::Input1);
assert!(!input.pressed(DummyInput::Input1));
assert!(!input.just_pressed(DummyInput::Input1));
assert!(!input.just_released(DummyInput::Input1));
// Released
input.press(DummyInput::Input1);
input.release(DummyInput::Input1);
assert!(!input.pressed(DummyInput::Input1));
assert!(input.just_pressed(DummyInput::Input1));
assert!(input.just_released(DummyInput::Input1));
input.reset(DummyInput::Input1);
assert!(!input.pressed(DummyInput::Input1));
assert!(!input.just_pressed(DummyInput::Input1));
assert!(!input.just_released(DummyInput::Input1));
}
#[test]
fn test_reset_all() {
let mut input = ButtonInput::default();
input.press(DummyInput::Input1);
input.press(DummyInput::Input2);
input.release(DummyInput::Input2);
assert!(input.pressed.contains(&DummyInput::Input1));
assert!(input.just_pressed.contains(&DummyInput::Input1));
assert!(input.just_released.contains(&DummyInput::Input2));
input.reset_all();
assert!(input.pressed.is_empty());
assert!(input.just_pressed.is_empty());
assert!(input.just_released.is_empty());
}
#[test]
fn test_clear() {
let mut input = ButtonInput::default();
// Pressed
input.press(DummyInput::Input1);
assert!(input.pressed(DummyInput::Input1));
assert!(input.just_pressed(DummyInput::Input1));
assert!(!input.just_released(DummyInput::Input1));
input.clear();
assert!(input.pressed(DummyInput::Input1));
assert!(!input.just_pressed(DummyInput::Input1));
assert!(!input.just_released(DummyInput::Input1));
// Released
input.press(DummyInput::Input1);
input.release(DummyInput::Input1);
assert!(!input.pressed(DummyInput::Input1));
assert!(!input.just_pressed(DummyInput::Input1));
assert!(input.just_released(DummyInput::Input1));
input.clear();
assert!(!input.pressed(DummyInput::Input1));
assert!(!input.just_pressed(DummyInput::Input1));
assert!(!input.just_released(DummyInput::Input1));
}
#[test]
fn test_get_pressed() {
let mut input = ButtonInput::default();
input.press(DummyInput::Input1);
input.press(DummyInput::Input2);
let pressed = input.get_pressed();
assert_eq!(pressed.len(), 2);
for pressed_input in pressed {
assert!(input.pressed.contains(pressed_input));
}
}
#[test]
fn test_get_just_pressed() {
let mut input = ButtonInput::default();
input.press(DummyInput::Input1);
input.press(DummyInput::Input2);
let just_pressed = input.get_just_pressed();
assert_eq!(just_pressed.len(), 2);
for just_pressed_input in just_pressed {
assert!(input.just_pressed.contains(just_pressed_input));
}
}
#[test]
fn test_get_just_released() {
let mut input = ButtonInput::default();
input.press(DummyInput::Input1);
input.press(DummyInput::Input2);
input.release(DummyInput::Input1);
input.release(DummyInput::Input2);
let just_released = input.get_just_released();
assert_eq!(just_released.len(), 2);
for just_released_input in just_released {
assert!(input.just_released.contains(just_released_input));
}
}
#[test]
fn test_general_input_handling() {
let mut input = ButtonInput::default();
// Test pressing
input.press(DummyInput::Input1);
input.press(DummyInput::Input2);
// Check if they were `just_pressed` (pressed on this update)
assert!(input.just_pressed(DummyInput::Input1));
assert!(input.just_pressed(DummyInput::Input2));
// Check if they are also marked as pressed
assert!(input.pressed(DummyInput::Input1));
assert!(input.pressed(DummyInput::Input2));
// Clear the `input`, removing `just_pressed` and `just_released`
input.clear();
// Check if they're marked `just_pressed`
assert!(!input.just_pressed(DummyInput::Input1));
assert!(!input.just_pressed(DummyInput::Input2));
// Check if they're marked as pressed
assert!(input.pressed(DummyInput::Input1));
assert!(input.pressed(DummyInput::Input2));
// Release the inputs and check state
input.release(DummyInput::Input1);
input.release(DummyInput::Input2);
// Check if they're marked as `just_released` (released on this update)
assert!(input.just_released(DummyInput::Input1));
assert!(input.just_released(DummyInput::Input2));
// Check that they're not incorrectly marked as pressed
assert!(!input.pressed(DummyInput::Input1));
assert!(!input.pressed(DummyInput::Input2));
// Clear the `Input` and check for removal from `just_released`
input.clear();
// Check that they're not incorrectly marked as just released
assert!(!input.just_released(DummyInput::Input1));
assert!(!input.just_released(DummyInput::Input2));
// Set up an `Input` to test resetting
let mut input = ButtonInput::default();
input.press(DummyInput::Input1);
input.release(DummyInput::Input2);
// Reset the `Input` and test if it was reset correctly
input.reset(DummyInput::Input1);
input.reset(DummyInput::Input2);
assert!(!input.just_pressed(DummyInput::Input1));
assert!(!input.pressed(DummyInput::Input1));
assert!(!input.just_released(DummyInput::Input2));
}
}

View File

@@ -0,0 +1,122 @@
use crate::ButtonInput;
use bevy_ecs::system::Res;
use core::hash::Hash;
/// Stateful run condition that can be toggled via an input press using [`ButtonInput::just_pressed`].
///
/// ```no_run
/// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins, Update};
/// # use bevy_ecs::prelude::IntoScheduleConfigs;
/// # use bevy_input::{common_conditions::input_toggle_active, prelude::KeyCode};
///
/// fn main() {
/// App::new()
/// .add_plugins(DefaultPlugins)
/// .add_systems(Update, pause_menu.run_if(input_toggle_active(false, KeyCode::Escape)))
/// .run();
/// }
///
/// fn pause_menu() {
/// println!("in pause menu");
/// }
/// ```
///
/// If you want other systems to be able to access whether the toggled state is active,
/// you should use a custom resource or a state for that:
/// ```no_run
/// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins, Update};
/// # use bevy_ecs::prelude::{IntoScheduleConfigs, Res, ResMut, Resource};
/// # use bevy_input::{common_conditions::input_just_pressed, prelude::KeyCode};
///
/// #[derive(Resource, Default)]
/// struct Paused(bool);
///
/// fn main() {
/// App::new()
/// .add_plugins(DefaultPlugins)
/// .init_resource::<Paused>()
/// .add_systems(Update, toggle_pause_state.run_if(input_just_pressed(KeyCode::Escape)))
/// .add_systems(Update, pause_menu.run_if(|paused: Res<Paused>| paused.0))
/// .run();
/// }
///
/// fn toggle_pause_state(mut paused: ResMut<Paused>) {
/// paused.0 = !paused.0;
/// }
///
/// fn pause_menu() {
/// println!("in pause menu");
/// }
/// ```
pub fn input_toggle_active<T>(
default: bool,
input: T,
) -> impl FnMut(Res<ButtonInput<T>>) -> bool + Clone
where
T: Copy + Eq + Hash + Send + Sync + 'static,
{
let mut active = default;
move |inputs: Res<ButtonInput<T>>| {
active ^= inputs.just_pressed(input);
active
}
}
/// Run condition that is active if [`ButtonInput::pressed`] is true for the given input.
pub fn input_pressed<T>(input: T) -> impl FnMut(Res<ButtonInput<T>>) -> bool + Clone
where
T: Copy + Eq + Hash + Send + Sync + 'static,
{
move |inputs: Res<ButtonInput<T>>| inputs.pressed(input)
}
/// Run condition that is active if [`ButtonInput::just_pressed`] is true for the given input.
///
/// ```no_run
/// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins, Update};
/// # use bevy_ecs::prelude::IntoScheduleConfigs;
/// # use bevy_input::{common_conditions::input_just_pressed, prelude::KeyCode};
/// fn main() {
/// App::new()
/// .add_plugins(DefaultPlugins)
/// .add_systems(Update, jump.run_if(input_just_pressed(KeyCode::Space)))
/// .run();
/// }
///
/// # fn jump() {}
/// ```
pub fn input_just_pressed<T>(input: T) -> impl FnMut(Res<ButtonInput<T>>) -> bool + Clone
where
T: Copy + Eq + Hash + Send + Sync + 'static,
{
move |inputs: Res<ButtonInput<T>>| inputs.just_pressed(input)
}
/// Run condition that is active if [`ButtonInput::just_released`] is true for the given input.
pub fn input_just_released<T>(input: T) -> impl FnMut(Res<ButtonInput<T>>) -> bool + Clone
where
T: Copy + Eq + Hash + Send + Sync + 'static,
{
move |inputs: Res<ButtonInput<T>>| inputs.just_released(input)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::prelude::KeyCode;
use bevy_ecs::schedule::{IntoScheduleConfigs, Schedule};
fn test_system() {}
// Ensure distributive_run_if compiles with the common conditions.
#[test]
fn distributive_run_if_compiles() {
Schedule::default().add_systems(
(test_system, test_system)
.distributive_run_if(input_toggle_active(false, KeyCode::Escape))
.distributive_run_if(input_pressed(KeyCode::Escape))
.distributive_run_if(input_just_pressed(KeyCode::Escape))
.distributive_run_if(input_just_released(KeyCode::Escape)),
);
}
}

2978
vendor/bevy_input/src/gamepad.rs vendored Normal file

File diff suppressed because it is too large Load Diff

90
vendor/bevy_input/src/gestures.rs vendored Normal file
View File

@@ -0,0 +1,90 @@
//! Gestures functionality, from touchscreens and touchpads.
use bevy_ecs::event::Event;
use bevy_math::Vec2;
#[cfg(feature = "bevy_reflect")]
use bevy_reflect::Reflect;
#[cfg(all(feature = "serialize", feature = "bevy_reflect"))]
use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
/// Two-finger pinch gesture, often used for magnifications.
///
/// Positive delta values indicate magnification (zooming in) and
/// negative delta values indicate shrinking (zooming out).
///
/// ## Platform-specific
///
/// - Only available on **`macOS`** and **`iOS`**.
/// - On **`iOS`**, must be enabled first
#[derive(Event, Debug, Clone, Copy, PartialEq)]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Debug, PartialEq, Clone)
)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
all(feature = "serialize", feature = "bevy_reflect"),
reflect(Serialize, Deserialize)
)]
pub struct PinchGesture(pub f32);
/// Two-finger rotation gesture.
///
/// Positive delta values indicate rotation counterclockwise and
/// negative delta values indicate rotation clockwise.
///
/// ## Platform-specific
///
/// - Only available on **`macOS`** and **`iOS`**.
/// - On **`iOS`**, must be enabled first
#[derive(Event, Debug, Clone, Copy, PartialEq)]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Debug, PartialEq, Clone)
)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
all(feature = "serialize", feature = "bevy_reflect"),
reflect(Serialize, Deserialize)
)]
pub struct RotationGesture(pub f32);
/// Double tap gesture.
///
/// ## Platform-specific
///
/// - Only available on **`macOS`** and **`iOS`**.
/// - On **`iOS`**, must be enabled first
#[derive(Event, Debug, Clone, Copy, PartialEq)]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Debug, PartialEq, Clone)
)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
all(feature = "serialize", feature = "bevy_reflect"),
reflect(Serialize, Deserialize)
)]
pub struct DoubleTapGesture;
/// Pan gesture.
///
/// ## Platform-specific
///
/// - On **`iOS`**, must be enabled first
#[derive(Event, Debug, Clone, Copy, PartialEq)]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Debug, PartialEq, Clone)
)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
all(feature = "serialize", feature = "bevy_reflect"),
reflect(Serialize, Deserialize)
)]
pub struct PanGesture(pub Vec2);

1521
vendor/bevy_input/src/keyboard.rs vendored Normal file

File diff suppressed because it is too large Load Diff

186
vendor/bevy_input/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,186 @@
#![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"
)]
#![no_std]
//! Input functionality for the [Bevy game engine](https://bevyengine.org/).
//!
//! # Supported input devices
//!
//! `bevy` currently supports keyboard, mouse, gamepad, and touch inputs.
#[cfg(feature = "std")]
extern crate std;
extern crate alloc;
mod axis;
mod button_input;
/// Common run conditions
pub mod common_conditions;
pub mod gamepad;
pub mod gestures;
pub mod keyboard;
pub mod mouse;
pub mod touch;
pub use axis::*;
pub use button_input::*;
/// The input prelude.
///
/// This includes the most common types in this crate, re-exported for your convenience.
pub mod prelude {
#[doc(hidden)]
pub use crate::{
gamepad::{Gamepad, GamepadAxis, GamepadButton, GamepadSettings},
keyboard::KeyCode,
mouse::MouseButton,
touch::{TouchInput, Touches},
Axis, ButtonInput,
};
}
use bevy_app::prelude::*;
use bevy_ecs::prelude::*;
#[cfg(feature = "bevy_reflect")]
use bevy_reflect::Reflect;
use gestures::*;
use keyboard::{keyboard_input_system, KeyCode, KeyboardFocusLost, KeyboardInput};
use mouse::{
accumulate_mouse_motion_system, accumulate_mouse_scroll_system, mouse_button_input_system,
AccumulatedMouseMotion, AccumulatedMouseScroll, MouseButton, MouseButtonInput, MouseMotion,
MouseWheel,
};
use touch::{touch_screen_input_system, TouchInput, Touches};
#[cfg(feature = "bevy_reflect")]
use gamepad::Gamepad;
use gamepad::{
gamepad_connection_system, gamepad_event_processing_system, GamepadAxis,
GamepadAxisChangedEvent, GamepadButton, GamepadButtonChangedEvent,
GamepadButtonStateChangedEvent, GamepadConnection, GamepadConnectionEvent, GamepadEvent,
GamepadInput, GamepadRumbleRequest, GamepadSettings, RawGamepadAxisChangedEvent,
RawGamepadButtonChangedEvent, RawGamepadEvent,
};
#[cfg(all(feature = "serialize", feature = "bevy_reflect"))]
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>()
.add_event::<KeyboardFocusLost>()
.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,
accumulate_mouse_motion_system,
accumulate_mouse_scroll_system,
)
.in_set(InputSystem),
)
.add_event::<PinchGesture>()
.add_event::<RotationGesture>()
.add_event::<DoubleTapGesture>()
.add_event::<PanGesture>()
// gamepad
.add_event::<GamepadEvent>()
.add_event::<GamepadConnectionEvent>()
.add_event::<GamepadButtonChangedEvent>()
.add_event::<GamepadButtonStateChangedEvent>()
.add_event::<GamepadAxisChangedEvent>()
.add_event::<RawGamepadEvent>()
.add_event::<RawGamepadAxisChangedEvent>()
.add_event::<RawGamepadButtonChangedEvent>()
.add_event::<GamepadRumbleRequest>()
.init_resource::<AccumulatedMouseMotion>()
.init_resource::<AccumulatedMouseScroll>()
.add_systems(
PreUpdate,
(
gamepad_connection_system,
gamepad_event_processing_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));
#[cfg(feature = "bevy_reflect")]
{
// Register common types
app.register_type::<ButtonState>()
.register_type::<KeyboardInput>()
.register_type::<MouseButtonInput>()
.register_type::<PinchGesture>()
.register_type::<RotationGesture>()
.register_type::<DoubleTapGesture>()
.register_type::<PanGesture>()
.register_type::<TouchInput>()
.register_type::<RawGamepadEvent>()
.register_type::<RawGamepadAxisChangedEvent>()
.register_type::<RawGamepadButtonChangedEvent>()
.register_type::<Gamepad>()
.register_type::<GamepadConnectionEvent>()
.register_type::<GamepadButtonChangedEvent>()
.register_type::<GamepadAxisChangedEvent>()
.register_type::<GamepadButtonStateChangedEvent>()
.register_type::<GamepadConnection>()
.register_type::<GamepadSettings>()
.register_type::<GamepadAxis>()
.register_type::<GamepadButton>()
.register_type::<GamepadInput>()
.register_type::<AccumulatedMouseMotion>()
.register_type::<AccumulatedMouseScroll>();
}
}
}
/// The current "press" state of an element
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Debug, Hash, PartialEq, Clone)
)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
all(feature = "serialize", feature = "bevy_reflect"),
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)
}
}

268
vendor/bevy_input/src/mouse.rs vendored Normal file
View File

@@ -0,0 +1,268 @@
//! The mouse input functionality.
use crate::{ButtonInput, ButtonState};
use bevy_ecs::{
change_detection::DetectChangesMut,
entity::Entity,
event::{Event, EventReader},
resource::Resource,
system::ResMut,
};
use bevy_math::Vec2;
#[cfg(feature = "bevy_reflect")]
use {
bevy_ecs::reflect::ReflectResource,
bevy_reflect::{std_traits::ReflectDefault, Reflect},
};
#[cfg(all(feature = "serialize", feature = "bevy_reflect"))]
use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
/// A mouse button input event.
///
/// This event is the translated version of the `WindowEvent::MouseInput` from the `winit` crate.
///
/// ## Usage
///
/// The event is read inside of the [`mouse_button_input_system`]
/// to update the [`ButtonInput<MouseButton>`] resource.
#[derive(Event, Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Debug, PartialEq, Clone)
)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
all(feature = "serialize", feature = "bevy_reflect"),
reflect(Serialize, Deserialize)
)]
pub struct MouseButtonInput {
/// The mouse button assigned to the event.
pub button: MouseButton,
/// The pressed state of the button.
pub state: ButtonState,
/// Window that received the input.
pub window: Entity,
}
/// A button on a mouse device.
///
/// ## Usage
///
/// It is used as the generic `T` value of an [`ButtonInput`] to create a `bevy`
/// resource.
///
/// ## Updating
///
/// The resource is updated inside of the [`mouse_button_input_system`].
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Debug, Hash, PartialEq, Clone)
)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
all(feature = "serialize", feature = "bevy_reflect"),
reflect(Serialize, Deserialize)
)]
pub enum MouseButton {
/// The left mouse button.
Left,
/// The right mouse button.
Right,
/// The middle mouse button.
Middle,
/// The back mouse button.
Back,
/// The forward mouse button.
Forward,
/// Another mouse button with the associated number.
Other(u16),
}
/// An event reporting the change in physical position of a pointing device.
///
/// This represents raw, unfiltered physical motion.
/// It is the translated version of [`DeviceEvent::MouseMotion`] from the `winit` crate.
///
/// All pointing devices connected to a single machine at the same time can emit the event independently.
/// However, the event data does not make it possible to distinguish which device it is referring to.
///
/// [`DeviceEvent::MouseMotion`]: https://docs.rs/winit/latest/winit/event/enum.DeviceEvent.html#variant.MouseMotion
#[derive(Event, Debug, Clone, Copy, PartialEq)]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Debug, PartialEq, Clone)
)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
all(feature = "serialize", feature = "bevy_reflect"),
reflect(Serialize, Deserialize)
)]
pub struct MouseMotion {
/// The change in the position of the pointing device since the last event was sent.
pub delta: Vec2,
}
/// The scroll unit.
///
/// Describes how a value of a [`MouseWheel`] event has to be interpreted.
///
/// The value of the event can either be interpreted as the amount of lines or the amount of pixels
/// to scroll.
#[derive(Debug, Hash, Clone, Copy, Eq, PartialEq)]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Debug, PartialEq, Clone)
)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
all(feature = "serialize", feature = "bevy_reflect"),
reflect(Serialize, Deserialize)
)]
pub enum MouseScrollUnit {
/// The line scroll unit.
///
/// The delta of the associated [`MouseWheel`] event corresponds
/// to the amount of lines or rows to scroll.
Line,
/// The pixel scroll unit.
///
/// The delta of the associated [`MouseWheel`] event corresponds
/// to the amount of pixels to scroll.
Pixel,
}
/// A mouse wheel event.
///
/// This event is the translated version of the `WindowEvent::MouseWheel` from the `winit` crate.
#[derive(Event, Debug, Clone, Copy, PartialEq)]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Debug, PartialEq, Clone)
)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
all(feature = "serialize", feature = "bevy_reflect"),
reflect(Serialize, Deserialize)
)]
pub struct MouseWheel {
/// The mouse scroll unit.
pub unit: MouseScrollUnit,
/// The horizontal scroll value.
pub x: f32,
/// The vertical scroll value.
pub y: f32,
/// Window that received the input.
pub window: Entity,
}
/// Updates the [`ButtonInput<MouseButton>`] resource with the latest [`MouseButtonInput`] events.
///
/// ## Differences
///
/// The main difference between the [`MouseButtonInput`] event and the [`ButtonInput<MouseButton>`] resource is that
/// the latter has convenient functions like [`ButtonInput::pressed`], [`ButtonInput::just_pressed`] and [`ButtonInput::just_released`].
pub fn mouse_button_input_system(
mut mouse_button_input: ResMut<ButtonInput<MouseButton>>,
mut mouse_button_input_events: EventReader<MouseButtonInput>,
) {
mouse_button_input.bypass_change_detection().clear();
for event in mouse_button_input_events.read() {
match event.state {
ButtonState::Pressed => mouse_button_input.press(event.button),
ButtonState::Released => mouse_button_input.release(event.button),
}
}
}
/// Tracks how much the mouse has moved every frame.
///
/// This resource is reset to zero every frame.
///
/// This resource sums the total [`MouseMotion`] events received this frame.
#[derive(Resource, Debug, Clone, Copy, PartialEq, Default)]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Debug, Default, Resource, PartialEq, Clone)
)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
all(feature = "serialize", feature = "bevy_reflect"),
reflect(Serialize, Deserialize)
)]
pub struct AccumulatedMouseMotion {
/// The change in mouse position.
pub delta: Vec2,
}
/// Tracks how much the mouse has scrolled every frame.
///
/// This resource is reset to zero every frame.
///
/// This resource sums the total [`MouseWheel`] events received this frame.
#[derive(Resource, Debug, Clone, Copy, PartialEq)]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Debug, Default, Resource, PartialEq, Clone)
)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
all(feature = "serialize", feature = "bevy_reflect"),
reflect(Serialize, Deserialize)
)]
pub struct AccumulatedMouseScroll {
/// The mouse scroll unit.
/// If this value changes while scrolling, then the
/// result of the accumulation could be incorrect
pub unit: MouseScrollUnit,
/// The change in scroll position.
pub delta: Vec2,
}
impl Default for AccumulatedMouseScroll {
fn default() -> Self {
Self {
unit: MouseScrollUnit::Line,
delta: Vec2::ZERO,
}
}
}
/// Updates the [`AccumulatedMouseMotion`] resource using the [`MouseMotion`] event.
/// The value of [`AccumulatedMouseMotion`] is reset to zero every frame
pub fn accumulate_mouse_motion_system(
mut mouse_motion_event: EventReader<MouseMotion>,
mut accumulated_mouse_motion: ResMut<AccumulatedMouseMotion>,
) {
let mut delta = Vec2::ZERO;
for event in mouse_motion_event.read() {
delta += event.delta;
}
accumulated_mouse_motion.delta = delta;
}
/// Updates the [`AccumulatedMouseScroll`] resource using the [`MouseWheel`] event.
/// The value of [`AccumulatedMouseScroll`] is reset to zero every frame
pub fn accumulate_mouse_scroll_system(
mut mouse_scroll_event: EventReader<MouseWheel>,
mut accumulated_mouse_scroll: ResMut<AccumulatedMouseScroll>,
) {
let mut delta = Vec2::ZERO;
let mut unit = MouseScrollUnit::Line;
for event in mouse_scroll_event.read() {
if event.unit != unit {
unit = event.unit;
}
delta += Vec2::new(event.x, event.y);
}
accumulated_mouse_scroll.delta = delta;
accumulated_mouse_scroll.unit = unit;
}

890
vendor/bevy_input/src/touch.rs vendored Normal file
View File

@@ -0,0 +1,890 @@
//! The touch input functionality.
use bevy_ecs::{
entity::Entity,
event::{Event, EventReader},
resource::Resource,
system::ResMut,
};
use bevy_math::Vec2;
use bevy_platform::collections::HashMap;
#[cfg(feature = "bevy_reflect")]
use bevy_reflect::Reflect;
#[cfg(all(feature = "serialize", feature = "bevy_reflect"))]
use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
/// A touch input event.
///
/// ## Logic
///
/// Every time the user touches the screen, a new [`TouchPhase::Started`] event with an unique
/// identifier for the finger is generated. When the finger is lifted, the [`TouchPhase::Ended`]
/// event is generated with the same finger id.
///
/// After a [`TouchPhase::Started`] event has been emitted, there may be zero or more [`TouchPhase::Moved`]
/// events when the finger is moved or the touch pressure changes.
///
/// The finger id may be reused by the system after an [`TouchPhase::Ended`] event. The user
/// should assume that a new [`TouchPhase::Started`] event received with the same id has nothing
/// to do with the old finger and is a new finger.
///
/// A [`TouchPhase::Canceled`] event is emitted when the system has canceled tracking this
/// touch, such as when the window loses focus, or on iOS if the user moves the
/// device against their face.
///
/// ## Note
///
/// This event is the translated version of the `WindowEvent::Touch` from the `winit` crate.
/// It is available to the end user and can be used for game logic.
#[derive(Event, Debug, Clone, Copy, PartialEq)]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Debug, PartialEq, Clone)
)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
all(feature = "serialize", feature = "bevy_reflect"),
reflect(Serialize, Deserialize)
)]
pub struct TouchInput {
/// The phase of the touch input.
pub phase: TouchPhase,
/// The position of the finger on the touchscreen.
pub position: Vec2,
/// The window entity registering the touch.
pub window: Entity,
/// Describes how hard the screen was pressed.
///
/// May be [`None`] if the platform does not support pressure sensitivity.
/// This feature is only available on **iOS** 9.0+ and **Windows** 8+.
pub force: Option<ForceTouch>,
/// The unique identifier of the finger.
pub id: u64,
}
/// A force description of a [`Touch`] input.
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Debug, PartialEq, Clone)
)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
all(feature = "serialize", feature = "bevy_reflect"),
reflect(Serialize, Deserialize)
)]
pub enum ForceTouch {
/// On iOS, the force is calibrated so that the same number corresponds to
/// roughly the same amount of pressure on the screen regardless of the
/// device.
Calibrated {
/// The force of the touch, where a value of 1.0 represents the force of
/// an average touch (predetermined by the system, not user-specific).
///
/// The force reported by Apple Pencil is measured along the axis of the
/// pencil. If you want a force perpendicular to the device, you need to
/// calculate this value using the `altitude_angle` value.
force: f64,
/// The maximum possible force for a touch.
///
/// The value of this field is sufficiently high to provide a wide
/// dynamic range for values of the `force` field.
max_possible_force: f64,
/// The altitude (in radians) of the stylus.
///
/// A value of 0 radians indicates that the stylus is parallel to the
/// surface. The value of this property is Pi/2 when the stylus is
/// perpendicular to the surface.
altitude_angle: Option<f64>,
},
/// If the platform reports the force as normalized, we have no way of
/// knowing how much pressure 1.0 corresponds to we know it's the maximum
/// amount of force, but as to how much force, you might either have to
/// press really hard, or not hard at all, depending on the device.
Normalized(f64),
}
/// A phase of a [`TouchInput`].
///
/// ## Usage
///
/// It is used to describe the phase of the touch input that is currently active.
/// This includes a phase that indicates that a touch input has started or ended,
/// or that a finger has moved. There is also a canceled phase that indicates that
/// the system canceled the tracking of the finger.
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Debug, Hash, PartialEq, Clone)
)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
all(feature = "serialize", feature = "bevy_reflect"),
reflect(Serialize, Deserialize)
)]
pub enum TouchPhase {
/// A finger started to touch the touchscreen.
Started,
/// A finger moved over the touchscreen.
Moved,
/// A finger stopped touching the touchscreen.
Ended,
/// The system canceled the tracking of the finger.
///
/// This occurs when the window loses focus, or on iOS if the user moves the
/// device against their face.
Canceled,
}
/// A touch input.
///
/// ## Usage
///
/// It is used to store the position and force of a touch input and also the `id` of the finger.
/// The data of the touch input comes from the [`TouchInput`] event and is being stored
/// inside of the [`Touches`] `bevy` resource.
#[derive(Debug, Clone, Copy)]
pub struct Touch {
/// The id of the touch input.
id: u64,
/// The starting position of the touch input.
start_position: Vec2,
/// The starting force of the touch input.
start_force: Option<ForceTouch>,
/// The previous position of the touch input.
previous_position: Vec2,
/// The previous force of the touch input.
previous_force: Option<ForceTouch>,
/// The current position of the touch input.
position: Vec2,
/// The current force of the touch input.
force: Option<ForceTouch>,
}
impl Touch {
/// The delta of the current `position` and the `previous_position`.
pub fn delta(&self) -> Vec2 {
self.position - self.previous_position
}
/// The distance of the `start_position` and the current `position`.
pub fn distance(&self) -> Vec2 {
self.position - self.start_position
}
/// Returns the `id` of the touch.
#[inline]
pub fn id(&self) -> u64 {
self.id
}
/// Returns the `start_position` of the touch.
#[inline]
pub fn start_position(&self) -> Vec2 {
self.start_position
}
/// Returns the `start_force` of the touch.
#[inline]
pub fn start_force(&self) -> Option<ForceTouch> {
self.start_force
}
/// Returns the `previous_position` of the touch.
#[inline]
pub fn previous_position(&self) -> Vec2 {
self.previous_position
}
/// Returns the `previous_force` of the touch.
#[inline]
pub fn previous_force(&self) -> Option<ForceTouch> {
self.previous_force
}
/// Returns the current `position` of the touch.
#[inline]
pub fn position(&self) -> Vec2 {
self.position
}
/// Returns the current `force` of the touch.
#[inline]
pub fn force(&self) -> Option<ForceTouch> {
self.force
}
}
impl From<&TouchInput> for Touch {
fn from(input: &TouchInput) -> Touch {
Touch {
id: input.id,
start_position: input.position,
start_force: input.force,
previous_position: input.position,
previous_force: input.force,
position: input.position,
force: input.force,
}
}
}
/// A collection of [`Touch`]es.
///
/// ## Usage
///
/// It is used to create a `bevy` resource that stores the data of the touches on a touchscreen
/// and can be accessed inside of a system.
///
/// ## Updating
///
/// The resource is updated inside of the [`touch_screen_input_system`].
#[derive(Debug, Clone, Default, Resource)]
pub struct Touches {
/// A collection of every [`Touch`] that is currently being pressed.
pressed: HashMap<u64, Touch>,
/// A collection of every [`Touch`] that just got pressed.
just_pressed: HashMap<u64, Touch>,
/// A collection of every [`Touch`] that just got released.
just_released: HashMap<u64, Touch>,
/// A collection of every [`Touch`] that just got canceled.
just_canceled: HashMap<u64, Touch>,
}
impl Touches {
/// An iterator visiting every pressed [`Touch`] input in arbitrary order.
pub fn iter(&self) -> impl Iterator<Item = &Touch> + '_ {
self.pressed.values()
}
/// Returns the [`Touch`] input corresponding to the `id` if it is being pressed.
pub fn get_pressed(&self, id: u64) -> Option<&Touch> {
self.pressed.get(&id)
}
/// Checks if any touch input was just pressed.
pub fn any_just_pressed(&self) -> bool {
!self.just_pressed.is_empty()
}
/// Register a release for a given touch input.
pub fn release(&mut self, id: u64) {
if let Some(touch) = self.pressed.remove(&id) {
self.just_released.insert(id, touch);
}
}
/// Registers a release for all currently pressed touch inputs.
pub fn release_all(&mut self) {
self.just_released.extend(self.pressed.drain());
}
/// Returns `true` if the input corresponding to the `id` has just been pressed.
pub fn just_pressed(&self, id: u64) -> bool {
self.just_pressed.contains_key(&id)
}
/// Clears the `just_pressed` state of the touch input and returns `true` if the touch input has just been pressed.
///
/// Future calls to [`Touches::just_pressed`] for the given touch input will return false until a new press event occurs.
pub fn clear_just_pressed(&mut self, id: u64) -> bool {
self.just_pressed.remove(&id).is_some()
}
/// An iterator visiting every just pressed [`Touch`] input in arbitrary order.
pub fn iter_just_pressed(&self) -> impl Iterator<Item = &Touch> {
self.just_pressed.values()
}
/// Returns the [`Touch`] input corresponding to the `id` if it has just been released.
pub fn get_released(&self, id: u64) -> Option<&Touch> {
self.just_released.get(&id)
}
/// Checks if any touch input was just released.
pub fn any_just_released(&self) -> bool {
!self.just_released.is_empty()
}
/// Returns `true` if the input corresponding to the `id` has just been released.
pub fn just_released(&self, id: u64) -> bool {
self.just_released.contains_key(&id)
}
/// Clears the `just_released` state of the touch input and returns `true` if the touch input has just been released.
///
/// Future calls to [`Touches::just_released`] for the given touch input will return false until a new release event occurs.
pub fn clear_just_released(&mut self, id: u64) -> bool {
self.just_released.remove(&id).is_some()
}
/// An iterator visiting every just released [`Touch`] input in arbitrary order.
pub fn iter_just_released(&self) -> impl Iterator<Item = &Touch> {
self.just_released.values()
}
/// Checks if any touch input was just canceled.
pub fn any_just_canceled(&self) -> bool {
!self.just_canceled.is_empty()
}
/// Returns `true` if the input corresponding to the `id` has just been canceled.
pub fn just_canceled(&self, id: u64) -> bool {
self.just_canceled.contains_key(&id)
}
/// Clears the `just_canceled` state of the touch input and returns `true` if the touch input has just been canceled.
///
/// Future calls to [`Touches::just_canceled`] for the given touch input will return false until a new cancel event occurs.
pub fn clear_just_canceled(&mut self, id: u64) -> bool {
self.just_canceled.remove(&id).is_some()
}
/// An iterator visiting every just canceled [`Touch`] input in arbitrary order.
pub fn iter_just_canceled(&self) -> impl Iterator<Item = &Touch> {
self.just_canceled.values()
}
/// Retrieves the position of the first currently pressed touch, if any
pub fn first_pressed_position(&self) -> Option<Vec2> {
// Looking for the position in `pressed`. If nothing is found, also look into `just_pressed`
// A touch can be in `just_pressed` but not in `pressed` if it ended in the same frame it started
self.pressed
.values()
.next()
.or_else(|| self.just_pressed.values().next())
.map(|t| t.position)
}
/// Clears `just_pressed`, `just_released`, and `just_canceled` data for every touch input.
///
/// See also [`Touches::reset_all`] for a full reset.
pub fn clear(&mut self) {
self.just_pressed.clear();
self.just_released.clear();
self.just_canceled.clear();
}
/// Clears `pressed`, `just_pressed`, `just_released`, and `just_canceled` data for every touch input.
///
/// See also [`Touches::clear`] for clearing only touches that have just been pressed, released or canceled.
pub fn reset_all(&mut self) {
self.pressed.clear();
self.just_pressed.clear();
self.just_released.clear();
self.just_canceled.clear();
}
/// Processes a [`TouchInput`] event by updating the `pressed`, `just_pressed`,
/// `just_released`, and `just_canceled` collections.
fn process_touch_event(&mut self, event: &TouchInput) {
match event.phase {
TouchPhase::Started => {
self.pressed.insert(event.id, event.into());
self.just_pressed.insert(event.id, event.into());
}
TouchPhase::Moved => {
if let Some(mut new_touch) = self.pressed.get(&event.id).cloned() {
// NOTE: This does not update the previous_force / previous_position field;
// they should be updated once per frame, not once per event
// See https://github.com/bevyengine/bevy/issues/12442
new_touch.position = event.position;
new_touch.force = event.force;
self.pressed.insert(event.id, new_touch);
}
}
TouchPhase::Ended => {
// if touch `just_released`, add related event to it
// the event position info is inside `pressed`, so use it unless not found
if let Some((_, v)) = self.pressed.remove_entry(&event.id) {
self.just_released.insert(event.id, v);
} else {
self.just_released.insert(event.id, event.into());
}
}
TouchPhase::Canceled => {
// if touch `just_canceled`, add related event to it
// the event position info is inside `pressed`, so use it unless not found
if let Some((_, v)) = self.pressed.remove_entry(&event.id) {
self.just_canceled.insert(event.id, v);
} else {
self.just_canceled.insert(event.id, event.into());
}
}
};
}
}
/// Updates the [`Touches`] resource with the latest [`TouchInput`] events.
///
/// This is not clearing the `pressed` collection, because it could incorrectly mark a touch input
/// as not pressed even though it is pressed. This could happen if the touch input is not moving
/// for a single frame and would therefore be marked as not pressed, because this function is
/// called on every single frame no matter if there was an event or not.
///
/// ## Differences
///
/// The main difference between the [`TouchInput`] event and the [`Touches`] resource is that
/// the latter has convenient functions like [`Touches::just_pressed`] and [`Touches::just_released`].
pub fn touch_screen_input_system(
mut touch_state: ResMut<Touches>,
mut touch_input_events: EventReader<TouchInput>,
) {
if !touch_state.just_pressed.is_empty() {
touch_state.just_pressed.clear();
}
if !touch_state.just_released.is_empty() {
touch_state.just_released.clear();
}
if !touch_state.just_canceled.is_empty() {
touch_state.just_canceled.clear();
}
if !touch_input_events.is_empty() {
for touch in touch_state.pressed.values_mut() {
touch.previous_position = touch.position;
touch.previous_force = touch.force;
}
for event in touch_input_events.read() {
touch_state.process_touch_event(event);
}
}
}
#[cfg(test)]
mod test {
use super::Touches;
#[test]
fn touch_update() {
use crate::{touch::Touch, Touches};
use bevy_math::Vec2;
let mut touches = Touches::default();
let touch_event = Touch {
id: 4,
start_position: Vec2::ZERO,
start_force: None,
previous_position: Vec2::ZERO,
previous_force: None,
position: Vec2::ZERO,
force: None,
};
// Add a touch to `just_pressed`, 'just_released', and 'just canceled'
touches.just_pressed.insert(4, touch_event);
touches.just_released.insert(4, touch_event);
touches.just_canceled.insert(4, touch_event);
clear_all(&mut touches);
// Verify that all the `just_x` maps are cleared
assert!(touches.just_pressed.is_empty());
assert!(touches.just_released.is_empty());
assert!(touches.just_canceled.is_empty());
}
#[test]
fn touch_process() {
use crate::{touch::TouchPhase, TouchInput, Touches};
use bevy_ecs::entity::Entity;
use bevy_math::Vec2;
let mut touches = Touches::default();
// Test adding a `TouchPhase::Started`
let touch_event = TouchInput {
phase: TouchPhase::Started,
position: Vec2::splat(4.0),
window: Entity::PLACEHOLDER,
force: None,
id: 4,
};
clear_all(&mut touches);
touches.process_touch_event(&touch_event);
assert!(touches.pressed.get(&touch_event.id).is_some());
assert!(touches.just_pressed.get(&touch_event.id).is_some());
// Test adding a `TouchPhase::Moved`
let moved_touch_event = TouchInput {
phase: TouchPhase::Moved,
position: Vec2::splat(5.0),
window: Entity::PLACEHOLDER,
force: None,
id: touch_event.id,
};
clear_all(&mut touches);
touches.process_touch_event(&moved_touch_event);
assert_eq!(
touches
.pressed
.get(&moved_touch_event.id)
.expect("Missing from pressed after move.")
.previous_position,
touch_event.position
);
// Test cancelling an event
let cancel_touch_event = TouchInput {
phase: TouchPhase::Canceled,
position: Vec2::ONE,
window: Entity::PLACEHOLDER,
force: None,
id: touch_event.id,
};
clear_all(&mut touches);
touches.process_touch_event(&cancel_touch_event);
assert!(touches.just_canceled.get(&touch_event.id).is_some());
assert!(touches.pressed.get(&touch_event.id).is_none());
// Test ending an event
let end_touch_event = TouchInput {
phase: TouchPhase::Ended,
position: Vec2::splat(4.0),
window: Entity::PLACEHOLDER,
force: None,
id: touch_event.id,
};
clear_all(&mut touches);
touches.process_touch_event(&touch_event);
touches.process_touch_event(&moved_touch_event);
touches.process_touch_event(&end_touch_event);
assert!(touches.just_released.get(&touch_event.id).is_some());
assert!(touches.pressed.get(&touch_event.id).is_none());
let touch = touches.just_released.get(&touch_event.id).unwrap();
// Make sure the position is updated from TouchPhase::Moved and TouchPhase::Ended
assert_ne!(touch.previous_position, touch.position);
}
// See https://github.com/bevyengine/bevy/issues/12442
#[test]
fn touch_process_multi_event() {
use crate::{touch::TouchPhase, TouchInput, Touches};
use bevy_ecs::entity::Entity;
use bevy_math::Vec2;
let mut touches = Touches::default();
let started_touch_event = TouchInput {
phase: TouchPhase::Started,
position: Vec2::splat(4.0),
window: Entity::PLACEHOLDER,
force: None,
id: 4,
};
let moved_touch_event1 = TouchInput {
phase: TouchPhase::Moved,
position: Vec2::splat(5.0),
window: Entity::PLACEHOLDER,
force: None,
id: started_touch_event.id,
};
let moved_touch_event2 = TouchInput {
phase: TouchPhase::Moved,
position: Vec2::splat(6.0),
window: Entity::PLACEHOLDER,
force: None,
id: started_touch_event.id,
};
// tick 1: touch is started during frame
for touch in touches.pressed.values_mut() {
// update ONCE, at start of frame
touch.previous_position = touch.position;
}
touches.process_touch_event(&started_touch_event);
touches.process_touch_event(&moved_touch_event1);
touches.process_touch_event(&moved_touch_event2);
{
let touch = touches.get_pressed(started_touch_event.id).unwrap();
assert_eq!(touch.previous_position, started_touch_event.position);
assert_eq!(touch.position, moved_touch_event2.position);
}
// tick 2: touch was started before frame
for touch in touches.pressed.values_mut() {
touch.previous_position = touch.position;
}
touches.process_touch_event(&moved_touch_event1);
touches.process_touch_event(&moved_touch_event2);
touches.process_touch_event(&moved_touch_event1);
{
let touch = touches.get_pressed(started_touch_event.id).unwrap();
assert_eq!(touch.previous_position, moved_touch_event2.position);
assert_eq!(touch.position, moved_touch_event1.position);
}
}
#[test]
fn touch_pressed() {
use crate::{touch::TouchPhase, TouchInput, Touches};
use bevy_ecs::entity::Entity;
use bevy_math::Vec2;
let mut touches = Touches::default();
let touch_event = TouchInput {
phase: TouchPhase::Started,
position: Vec2::splat(4.0),
window: Entity::PLACEHOLDER,
force: None,
id: 4,
};
// Register the touch and test that it was registered correctly
touches.process_touch_event(&touch_event);
assert!(touches.get_pressed(touch_event.id).is_some());
assert!(touches.just_pressed(touch_event.id));
assert_eq!(touches.iter().count(), 1);
touches.clear_just_pressed(touch_event.id);
assert!(!touches.just_pressed(touch_event.id));
}
#[test]
fn touch_released() {
use crate::{touch::TouchPhase, TouchInput, Touches};
use bevy_ecs::entity::Entity;
use bevy_math::Vec2;
let mut touches = Touches::default();
let touch_event = TouchInput {
phase: TouchPhase::Ended,
position: Vec2::splat(4.0),
window: Entity::PLACEHOLDER,
force: None,
id: 4,
};
// Register the touch and test that it was registered correctly
touches.process_touch_event(&touch_event);
assert!(touches.get_released(touch_event.id).is_some());
assert!(touches.just_released(touch_event.id));
assert_eq!(touches.iter_just_released().count(), 1);
touches.clear_just_released(touch_event.id);
assert!(!touches.just_released(touch_event.id));
}
#[test]
fn touch_canceled() {
use crate::{touch::TouchPhase, TouchInput, Touches};
use bevy_ecs::entity::Entity;
use bevy_math::Vec2;
let mut touches = Touches::default();
let touch_event = TouchInput {
phase: TouchPhase::Canceled,
position: Vec2::splat(4.0),
window: Entity::PLACEHOLDER,
force: None,
id: 4,
};
// Register the touch and test that it was registered correctly
touches.process_touch_event(&touch_event);
assert!(touches.just_canceled(touch_event.id));
assert_eq!(touches.iter_just_canceled().count(), 1);
touches.clear_just_canceled(touch_event.id);
assert!(!touches.just_canceled(touch_event.id));
}
#[test]
fn release_touch() {
use crate::{touch::TouchPhase, TouchInput, Touches};
use bevy_ecs::entity::Entity;
use bevy_math::Vec2;
let mut touches = Touches::default();
let touch_event = TouchInput {
phase: TouchPhase::Started,
position: Vec2::splat(4.0),
window: Entity::PLACEHOLDER,
force: None,
id: 4,
};
// Register the touch and test that it was registered correctly
touches.process_touch_event(&touch_event);
assert!(touches.get_pressed(touch_event.id).is_some());
touches.release(touch_event.id);
assert!(touches.get_pressed(touch_event.id).is_none());
assert!(touches.just_released(touch_event.id));
}
#[test]
fn release_all_touches() {
use crate::{touch::TouchPhase, TouchInput, Touches};
use bevy_ecs::entity::Entity;
use bevy_math::Vec2;
let mut touches = Touches::default();
let touch_pressed_event = TouchInput {
phase: TouchPhase::Started,
position: Vec2::splat(4.0),
window: Entity::PLACEHOLDER,
force: None,
id: 4,
};
let touch_moved_event = TouchInput {
phase: TouchPhase::Moved,
position: Vec2::splat(4.0),
window: Entity::PLACEHOLDER,
force: None,
id: 4,
};
touches.process_touch_event(&touch_pressed_event);
touches.process_touch_event(&touch_moved_event);
assert!(touches.get_pressed(touch_pressed_event.id).is_some());
assert!(touches.get_pressed(touch_moved_event.id).is_some());
touches.release_all();
assert!(touches.get_pressed(touch_pressed_event.id).is_none());
assert!(touches.just_released(touch_pressed_event.id));
assert!(touches.get_pressed(touch_moved_event.id).is_none());
assert!(touches.just_released(touch_moved_event.id));
}
#[test]
fn clear_touches() {
use crate::{touch::TouchPhase, TouchInput, Touches};
use bevy_ecs::entity::Entity;
use bevy_math::Vec2;
let mut touches = Touches::default();
let touch_press_event = TouchInput {
phase: TouchPhase::Started,
position: Vec2::splat(4.0),
window: Entity::PLACEHOLDER,
force: None,
id: 4,
};
let touch_canceled_event = TouchInput {
phase: TouchPhase::Canceled,
position: Vec2::splat(4.0),
window: Entity::PLACEHOLDER,
force: None,
id: 5,
};
let touch_released_event = TouchInput {
phase: TouchPhase::Ended,
position: Vec2::splat(4.0),
window: Entity::PLACEHOLDER,
force: None,
id: 6,
};
// Register the touches and test that it was registered correctly
touches.process_touch_event(&touch_press_event);
touches.process_touch_event(&touch_canceled_event);
touches.process_touch_event(&touch_released_event);
assert!(touches.get_pressed(touch_press_event.id).is_some());
assert!(touches.just_pressed(touch_press_event.id));
assert!(touches.just_canceled(touch_canceled_event.id));
assert!(touches.just_released(touch_released_event.id));
touches.clear();
assert!(touches.get_pressed(touch_press_event.id).is_some());
assert!(!touches.just_pressed(touch_press_event.id));
assert!(!touches.just_canceled(touch_canceled_event.id));
assert!(!touches.just_released(touch_released_event.id));
}
#[test]
fn reset_all_touches() {
use crate::{touch::TouchPhase, TouchInput, Touches};
use bevy_ecs::entity::Entity;
use bevy_math::Vec2;
let mut touches = Touches::default();
let touch_press_event = TouchInput {
phase: TouchPhase::Started,
position: Vec2::splat(4.0),
window: Entity::PLACEHOLDER,
force: None,
id: 4,
};
let touch_canceled_event = TouchInput {
phase: TouchPhase::Canceled,
position: Vec2::splat(4.0),
window: Entity::PLACEHOLDER,
force: None,
id: 5,
};
let touch_released_event = TouchInput {
phase: TouchPhase::Ended,
position: Vec2::splat(4.0),
window: Entity::PLACEHOLDER,
force: None,
id: 6,
};
// Register the touches and test that it was registered correctly
touches.process_touch_event(&touch_press_event);
touches.process_touch_event(&touch_canceled_event);
touches.process_touch_event(&touch_released_event);
assert!(touches.get_pressed(touch_press_event.id).is_some());
assert!(touches.just_pressed(touch_press_event.id));
assert!(touches.just_canceled(touch_canceled_event.id));
assert!(touches.just_released(touch_released_event.id));
touches.reset_all();
assert!(touches.get_pressed(touch_press_event.id).is_none());
assert!(!touches.just_pressed(touch_press_event.id));
assert!(!touches.just_canceled(touch_canceled_event.id));
assert!(!touches.just_released(touch_released_event.id));
}
fn clear_all(touch_state: &mut Touches) {
touch_state.just_pressed.clear();
touch_state.just_released.clear();
touch_state.just_canceled.clear();
}
}