Skip to content

Commit bcc33f6

Browse files
committed
feat: add GamepadInfo, expose gamepad names (#6342)
# Objective Fixes #6339. ## Solution This PR adds a new type, `GamepadInfo`, which holds metadata associated with a particular `Gamepad`. The `Gamepads` resource now holds a `HashMap<Gamepad, GamepadInfo>`. The `GamepadInfo` is created when the gamepad backend (by default `bevy_gilrs`) emits a "gamepad connected" event. The `gamepad_viewer` example has been updated to showcase the new functionality. Before: ![bevy-gamepad-old](https://user-images.githubusercontent.com/86984145/197359427-2130a3c0-bd8a-4683-ae24-2a9eaa98b586.png) After: ![bevy-gamepad-new](https://user-images.githubusercontent.com/86984145/197359429-f7963163-df26-4906-af7f-6186fe3bd338.png) --- ## Changelog ### Added - Added `GamepadInfo`. - Added `Gamepads::name()`, which returns the name of the specified gamepad if it exists. ### Changed - `GamepadEventType::Connected` is now a tuple variant with a single field of type `GamepadInfo`. - Since `GamepadInfo` is not `Copy`, `GamepadEventType` is no longer `Copy`. The same is true of `GamepadEvent` and `GamepadEventRaw`. ## Migration Guide - Pattern matches on `GamepadEventType::Connected` will need to be updated, as the form of the variant has changed. - Code that requires `GamepadEvent`, `GamepadEventRaw` or `GamepadEventType` to be `Copy` will need to be updated.
1 parent c9ec5c7 commit bcc33f6

File tree

4 files changed

+56
-33
lines changed

4 files changed

+56
-33
lines changed

crates/bevy_gilrs/src/gilrs_system.rs

+13-3
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
use crate::converter::{convert_axis, convert_button, convert_gamepad_id};
22
use bevy_ecs::event::EventWriter;
33
use bevy_ecs::system::{NonSend, NonSendMut};
4+
use bevy_input::gamepad::GamepadInfo;
45
use bevy_input::{gamepad::GamepadEventRaw, prelude::*};
56
use gilrs::{ev::filter::axis_dpad_to_button, EventType, Filter, Gilrs};
67

78
pub fn gilrs_event_startup_system(gilrs: NonSend<Gilrs>, mut events: EventWriter<GamepadEventRaw>) {
8-
for (id, _) in gilrs.gamepads() {
9+
for (id, gamepad) in gilrs.gamepads() {
10+
let info = GamepadInfo {
11+
name: gamepad.name().into(),
12+
};
13+
914
events.send(GamepadEventRaw::new(
1015
convert_gamepad_id(id),
11-
GamepadEventType::Connected,
16+
GamepadEventType::Connected(info),
1217
));
1318
}
1419
}
@@ -22,9 +27,14 @@ pub fn gilrs_event_system(mut gilrs: NonSendMut<Gilrs>, mut events: EventWriter<
2227

2328
match gilrs_event.event {
2429
EventType::Connected => {
30+
let pad = gilrs.gamepad(gilrs_event.id);
31+
let info = GamepadInfo {
32+
name: pad.name().into(),
33+
};
34+
2535
events.send(GamepadEventRaw::new(
2636
convert_gamepad_id(gilrs_event.id),
27-
GamepadEventType::Connected,
37+
GamepadEventType::Connected(info),
2838
));
2939
}
3040
EventType::Disconnected => {

crates/bevy_input/src/gamepad.rs

+40-27
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::{Axis, Input};
22
use bevy_ecs::event::{EventReader, EventWriter};
33
use bevy_ecs::system::{Res, ResMut, Resource};
4-
use bevy_utils::{tracing::info, HashMap, HashSet};
4+
use bevy_utils::{tracing::info, HashMap};
55
use thiserror::Error;
66

77
/// Errors that occur when setting axis settings for gamepad input.
@@ -78,6 +78,13 @@ impl Gamepad {
7878
}
7979
}
8080

81+
/// Metadata associated with a `Gamepad`.
82+
#[derive(Debug, Clone, PartialEq, Eq)]
83+
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
84+
pub struct GamepadInfo {
85+
pub name: String,
86+
}
87+
8188
/// A collection of connected [`Gamepad`]s.
8289
///
8390
/// ## Usage
@@ -92,23 +99,27 @@ impl Gamepad {
9299
#[derive(Resource, Default, Debug)]
93100
pub struct Gamepads {
94101
/// The collection of the connected [`Gamepad`]s.
95-
gamepads: HashSet<Gamepad>,
102+
gamepads: HashMap<Gamepad, GamepadInfo>,
96103
}
97104

98105
impl Gamepads {
99106
/// Returns `true` if the `gamepad` is connected.
100107
pub fn contains(&self, gamepad: Gamepad) -> bool {
101-
self.gamepads.contains(&gamepad)
108+
self.gamepads.contains_key(&gamepad)
102109
}
103110

104111
/// Returns an iterator over registered [`Gamepad`]s in an arbitrary order.
105112
pub fn iter(&self) -> impl Iterator<Item = Gamepad> + '_ {
106-
self.gamepads.iter().copied()
113+
self.gamepads.keys().copied()
114+
}
115+
116+
pub fn name(&self, gamepad: Gamepad) -> Option<&str> {
117+
self.gamepads.get(&gamepad).map(|g| g.name.as_str())
107118
}
108119

109120
/// Registers the `gamepad`, marking it as connected.
110-
fn register(&mut self, gamepad: Gamepad) {
111-
self.gamepads.insert(gamepad);
121+
fn register(&mut self, gamepad: Gamepad, info: GamepadInfo) {
122+
self.gamepads.insert(gamepad, info);
112123
}
113124

114125
/// Deregisters the `gamepad`, marking it as disconnected.
@@ -118,11 +129,11 @@ impl Gamepads {
118129
}
119130

120131
/// The data contained in a [`GamepadEvent`] or [`GamepadEventRaw`].
121-
#[derive(Debug, Clone, Copy, PartialEq)]
132+
#[derive(Debug, Clone, PartialEq)]
122133
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
123134
pub enum GamepadEventType {
124135
/// A [`Gamepad`] has been connected.
125-
Connected,
136+
Connected(GamepadInfo),
126137
/// A [`Gamepad`] has been disconnected.
127138
Disconnected,
128139

@@ -151,7 +162,7 @@ pub enum GamepadEventType {
151162
/// [`Axis<GamepadAxis>`], and [`Axis<GamepadButton>`] resources won't be updated correctly.
152163
///
153164
/// An example for gamepad input mocking can be seen in the documentation of the [`GamepadEventRaw`].
154-
#[derive(Debug, Clone, Copy, PartialEq)]
165+
#[derive(Debug, Clone, PartialEq)]
155166
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
156167
pub struct GamepadEvent {
157168
/// The gamepad this event corresponds to.
@@ -191,7 +202,7 @@ impl GamepadEvent {
191202
/// ```
192203
/// # use bevy_input::prelude::*;
193204
/// # use bevy_input::InputPlugin;
194-
/// # use bevy_input::gamepad::GamepadEventRaw;
205+
/// # use bevy_input::gamepad::{GamepadEventRaw, GamepadInfo};
195206
/// # use bevy_app::prelude::*;
196207
/// # use bevy_ecs::prelude::*;
197208
/// #[derive(Resource)]
@@ -223,7 +234,8 @@ impl GamepadEvent {
223234
///
224235
/// // Send the gamepad connected event to mark our gamepad as connected.
225236
/// // This updates the `Gamepads` resource accordingly.
226-
/// app.world.send_event(GamepadEventRaw::new(gamepad, GamepadEventType::Connected));
237+
/// let info = GamepadInfo { name: "Mock Gamepad".into() };
238+
/// app.world.send_event(GamepadEventRaw::new(gamepad, GamepadEventType::Connected(info)));
227239
///
228240
/// // Send the gamepad input event to mark the `South` gamepad button as pressed.
229241
/// // This updates the `Input<GamepadButton>` resource accordingly.
@@ -254,7 +266,7 @@ impl GamepadEvent {
254266
/// #
255267
/// # bevy_ecs::system::assert_is_system(change_resource_on_gamepad_button_press);
256268
/// ```
257-
#[derive(Debug, Clone, Copy, PartialEq)]
269+
#[derive(Debug, Clone, PartialEq)]
258270
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
259271
pub struct GamepadEventRaw {
260272
/// The gamepad this event corresponds to.
@@ -1062,11 +1074,12 @@ pub fn gamepad_connection_system(
10621074
mut gamepad_event: EventReader<GamepadEvent>,
10631075
) {
10641076
for event in gamepad_event.iter() {
1065-
match event.event_type {
1066-
GamepadEventType::Connected => {
1067-
gamepads.register(event.gamepad);
1077+
match &event.event_type {
1078+
GamepadEventType::Connected(info) => {
1079+
gamepads.register(event.gamepad, info.clone());
10681080
info!("{:?} Connected", event.gamepad);
10691081
}
1082+
10701083
GamepadEventType::Disconnected => {
10711084
gamepads.deregister(event.gamepad);
10721085
info!("{:?} Disconnected", event.gamepad);
@@ -1096,9 +1109,9 @@ pub fn gamepad_event_system(
10961109
) {
10971110
button_input.clear();
10981111
for event in raw_events.iter() {
1099-
match event.event_type {
1100-
GamepadEventType::Connected => {
1101-
events.send(GamepadEvent::new(event.gamepad, event.event_type));
1112+
match &event.event_type {
1113+
GamepadEventType::Connected(_) => {
1114+
events.send(GamepadEvent::new(event.gamepad, event.event_type.clone()));
11021115
for button_type in &ALL_BUTTON_TYPES {
11031116
let gamepad_button = GamepadButton::new(event.gamepad, *button_type);
11041117
button_input.reset(gamepad_button);
@@ -1109,7 +1122,7 @@ pub fn gamepad_event_system(
11091122
}
11101123
}
11111124
GamepadEventType::Disconnected => {
1112-
events.send(GamepadEvent::new(event.gamepad, event.event_type));
1125+
events.send(GamepadEvent::new(event.gamepad, event.event_type.clone()));
11131126
for button_type in &ALL_BUTTON_TYPES {
11141127
let gamepad_button = GamepadButton::new(event.gamepad, *button_type);
11151128
button_input.reset(gamepad_button);
@@ -1120,37 +1133,37 @@ pub fn gamepad_event_system(
11201133
}
11211134
}
11221135
GamepadEventType::AxisChanged(axis_type, value) => {
1123-
let gamepad_axis = GamepadAxis::new(event.gamepad, axis_type);
1136+
let gamepad_axis = GamepadAxis::new(event.gamepad, *axis_type);
11241137
if let Some(filtered_value) = settings
11251138
.get_axis_settings(gamepad_axis)
1126-
.filter(value, axis.get(gamepad_axis))
1139+
.filter(*value, axis.get(gamepad_axis))
11271140
{
11281141
axis.set(gamepad_axis, filtered_value);
11291142
events.send(GamepadEvent::new(
11301143
event.gamepad,
1131-
GamepadEventType::AxisChanged(axis_type, filtered_value),
1144+
GamepadEventType::AxisChanged(*axis_type, filtered_value),
11321145
));
11331146
}
11341147
}
11351148
GamepadEventType::ButtonChanged(button_type, value) => {
1136-
let gamepad_button = GamepadButton::new(event.gamepad, button_type);
1149+
let gamepad_button = GamepadButton::new(event.gamepad, *button_type);
11371150
if let Some(filtered_value) = settings
11381151
.get_button_axis_settings(gamepad_button)
1139-
.filter(value, button_axis.get(gamepad_button))
1152+
.filter(*value, button_axis.get(gamepad_button))
11401153
{
11411154
button_axis.set(gamepad_button, filtered_value);
11421155
events.send(GamepadEvent::new(
11431156
event.gamepad,
1144-
GamepadEventType::ButtonChanged(button_type, filtered_value),
1157+
GamepadEventType::ButtonChanged(*button_type, filtered_value),
11451158
));
11461159
}
11471160

11481161
let button_property = settings.get_button_settings(gamepad_button);
11491162
if button_input.pressed(gamepad_button) {
1150-
if button_property.is_released(value) {
1163+
if button_property.is_released(*value) {
11511164
button_input.release(gamepad_button);
11521165
}
1153-
} else if button_property.is_pressed(value) {
1166+
} else if button_property.is_pressed(*value) {
11541167
button_input.press(gamepad_button);
11551168
}
11561169
}

examples/input/gamepad_input_events.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ fn main() {
1515
fn gamepad_events(mut gamepad_event: EventReader<GamepadEvent>) {
1616
for event in gamepad_event.iter() {
1717
match event.event_type {
18-
GamepadEventType::Connected => {
18+
GamepadEventType::Connected(_) => {
1919
info!("{:?} Connected", event.gamepad);
2020
}
2121
GamepadEventType::Disconnected => {

examples/tools/gamepad_viewer.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,7 @@ fn setup_connected(mut commands: Commands, font: Res<FontHandle>) {
437437
commands.spawn((
438438
TextBundle::from_sections([
439439
TextSection {
440-
value: "Connected Gamepads\n".to_string(),
440+
value: "Connected Gamepads:\n".to_string(),
441441
style: style.clone(),
442442
},
443443
TextSection {
@@ -521,7 +521,7 @@ fn update_connected(
521521

522522
let formatted = gamepads
523523
.iter()
524-
.map(|g| format!("{:?}", g))
524+
.map(|g| format!("- {}", gamepads.name(g).unwrap()))
525525
.collect::<Vec<_>>()
526526
.join("\n");
527527

0 commit comments

Comments
 (0)