Skip to content

Commit 3a1b9b9

Browse files
authored
Make Gilrs a normal resource on non-Wasm targets (#12092)
# Objective Partially address #888. Gilrs is initialized on a separate thread, and thus conditionally implements `Send`, and all platforms other than Wasm. This means the `NonSend` resource constraint is likely too conservative. ## Solution Relax the requirement, and conditionally derive Resource on a wrapper around it, using `SyncCell` to satisfy the `Sync` requirement on it.
1 parent ad5d790 commit 3a1b9b9

File tree

3 files changed

+43
-26
lines changed

3 files changed

+43
-26
lines changed

crates/bevy_gilrs/src/gilrs_system.rs

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,26 @@
1-
use crate::converter::{convert_axis, convert_button, convert_gamepad_id};
1+
use crate::{
2+
converter::{convert_axis, convert_button, convert_gamepad_id},
3+
Gilrs,
4+
};
25
use bevy_ecs::event::EventWriter;
3-
use bevy_ecs::system::{NonSend, NonSendMut, Res, ResMut};
6+
#[cfg(target_arch = "wasm32")]
7+
use bevy_ecs::system::NonSendMut;
8+
use bevy_ecs::system::{Res, ResMut};
49
use bevy_input::gamepad::{
510
GamepadAxisChangedEvent, GamepadButtonChangedEvent, GamepadConnection, GamepadConnectionEvent,
611
GamepadSettings,
712
};
813
use bevy_input::gamepad::{GamepadEvent, GamepadInfo};
914
use bevy_input::prelude::{GamepadAxis, GamepadButton};
1015
use bevy_input::Axis;
11-
use gilrs::{ev::filter::axis_dpad_to_button, EventType, Filter, Gilrs};
16+
use gilrs::{ev::filter::axis_dpad_to_button, EventType, Filter};
1217

1318
pub fn gilrs_event_startup_system(
14-
gilrs: NonSend<Gilrs>,
19+
#[cfg(target_arch = "wasm32")] mut gilrs: NonSendMut<Gilrs>,
20+
#[cfg(not(target_arch = "wasm32"))] mut gilrs: ResMut<Gilrs>,
1521
mut connection_events: EventWriter<GamepadConnectionEvent>,
1622
) {
17-
for (id, gamepad) in gilrs.gamepads() {
23+
for (id, gamepad) in gilrs.0.get().gamepads() {
1824
let info = GamepadInfo {
1925
name: gamepad.name().into(),
2026
};
@@ -27,16 +33,15 @@ pub fn gilrs_event_startup_system(
2733
}
2834

2935
pub fn gilrs_event_system(
30-
mut gilrs: NonSendMut<Gilrs>,
36+
#[cfg(target_arch = "wasm32")] mut gilrs: NonSendMut<Gilrs>,
37+
#[cfg(not(target_arch = "wasm32"))] mut gilrs: ResMut<Gilrs>,
3138
mut events: EventWriter<GamepadEvent>,
3239
mut gamepad_buttons: ResMut<Axis<GamepadButton>>,
3340
gamepad_axis: Res<Axis<GamepadAxis>>,
3441
gamepad_settings: Res<GamepadSettings>,
3542
) {
36-
while let Some(gilrs_event) = gilrs
37-
.next_event()
38-
.filter_ev(&axis_dpad_to_button, &mut gilrs)
39-
{
43+
let gilrs = gilrs.0.get();
44+
while let Some(gilrs_event) = gilrs.next_event().filter_ev(&axis_dpad_to_button, gilrs) {
4045
gilrs.update(&gilrs_event);
4146

4247
let gamepad = convert_gamepad_id(gilrs_event.id);

crates/bevy_gilrs/src/lib.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,14 @@ mod rumble;
1010
use bevy_app::{App, Plugin, PostUpdate, PreStartup, PreUpdate};
1111
use bevy_ecs::prelude::*;
1212
use bevy_input::InputSystem;
13-
use bevy_utils::tracing::error;
13+
use bevy_utils::{synccell::SyncCell, tracing::error};
1414
use gilrs::GilrsBuilder;
1515
use gilrs_system::{gilrs_event_startup_system, gilrs_event_system};
1616
use rumble::{play_gilrs_rumble, RunningRumbleEffects};
1717

18+
#[cfg_attr(not(target_arch = "wasm32"), derive(Resource))]
19+
pub(crate) struct Gilrs(pub SyncCell<gilrs::Gilrs>);
20+
1821
/// Plugin that provides gamepad handling to an [`App`].
1922
#[derive(Default)]
2023
pub struct GilrsPlugin;
@@ -31,8 +34,12 @@ impl Plugin for GilrsPlugin {
3134
.build()
3235
{
3336
Ok(gilrs) => {
34-
app.insert_non_send_resource(gilrs)
35-
.init_non_send_resource::<RunningRumbleEffects>()
37+
#[cfg(target_arch = "wasm32")]
38+
app.insert_non_send_resource(Gilrs(SyncCell::new(gilrs)));
39+
#[cfg(not(target_arch = "wasm32"))]
40+
app.insert_resource(Gilrs(SyncCell::new(gilrs)));
41+
42+
app.init_resource::<RunningRumbleEffects>()
3643
.add_systems(PreStartup, gilrs_event_startup_system)
3744
.add_systems(PreUpdate, gilrs_event_system.before(InputSystem))
3845
.add_systems(PostUpdate, play_gilrs_rumble.in_set(RumbleSystem));

crates/bevy_gilrs/src/rumble.rs

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
//! Handle user specified rumble request events.
2-
use bevy_ecs::{
3-
prelude::{EventReader, Res},
4-
system::NonSendMut,
5-
};
2+
use crate::Gilrs;
3+
use bevy_ecs::prelude::{EventReader, Res, ResMut, Resource};
4+
#[cfg(target_arch = "wasm32")]
5+
use bevy_ecs::system::NonSendMut;
66
use bevy_input::gamepad::{GamepadRumbleIntensity, GamepadRumbleRequest};
77
use bevy_log::{debug, warn};
88
use bevy_time::{Real, Time};
9-
use bevy_utils::{Duration, HashMap};
9+
use bevy_utils::{synccell::SyncCell, Duration, HashMap};
1010
use gilrs::{
1111
ff::{self, BaseEffect, BaseEffectType, Repeat, Replay},
12-
GamepadId, Gilrs,
12+
GamepadId,
1313
};
1414
use thiserror::Error;
1515

@@ -23,7 +23,7 @@ struct RunningRumble {
2323
///
2424
/// Dropping it will cause the effect to stop
2525
#[allow(dead_code)]
26-
effect: ff::Effect,
26+
effect: SyncCell<ff::Effect>,
2727
}
2828

2929
#[derive(Error, Debug)]
@@ -35,7 +35,7 @@ enum RumbleError {
3535
}
3636

3737
/// Contains the gilrs rumble effects that are currently running for each gamepad
38-
#[derive(Default)]
38+
#[derive(Default, Resource)]
3939
pub(crate) struct RunningRumbleEffects {
4040
/// If multiple rumbles are running at the same time, their resulting rumble
4141
/// will be the saturated sum of their strengths up until [`u16::MAX`]
@@ -80,7 +80,7 @@ fn get_base_effects(
8080

8181
fn handle_rumble_request(
8282
running_rumbles: &mut RunningRumbleEffects,
83-
gilrs: &mut Gilrs,
83+
gilrs: &mut gilrs::Gilrs,
8484
rumble: GamepadRumbleRequest,
8585
current_time: Duration,
8686
) -> Result<(), RumbleError> {
@@ -113,18 +113,23 @@ fn handle_rumble_request(
113113

114114
let gamepad_rumbles = running_rumbles.rumbles.entry(gamepad_id).or_default();
115115
let deadline = current_time + duration;
116-
gamepad_rumbles.push(RunningRumble { deadline, effect });
116+
gamepad_rumbles.push(RunningRumble {
117+
deadline,
118+
effect: SyncCell::new(effect),
119+
});
117120
}
118121
}
119122

120123
Ok(())
121124
}
122125
pub(crate) fn play_gilrs_rumble(
123126
time: Res<Time<Real>>,
124-
mut gilrs: NonSendMut<Gilrs>,
127+
#[cfg(target_arch = "wasm32")] mut gilrs: NonSendMut<Gilrs>,
128+
#[cfg(not(target_arch = "wasm32"))] mut gilrs: ResMut<Gilrs>,
125129
mut requests: EventReader<GamepadRumbleRequest>,
126-
mut running_rumbles: NonSendMut<RunningRumbleEffects>,
130+
mut running_rumbles: ResMut<RunningRumbleEffects>,
127131
) {
132+
let gilrs = gilrs.0.get();
128133
let current_time = time.elapsed();
129134
// Remove outdated rumble effects.
130135
for rumbles in running_rumbles.rumbles.values_mut() {
@@ -138,7 +143,7 @@ pub(crate) fn play_gilrs_rumble(
138143
// Add new effects.
139144
for rumble in requests.read().cloned() {
140145
let gamepad = rumble.gamepad();
141-
match handle_rumble_request(&mut running_rumbles, &mut gilrs, rumble, current_time) {
146+
match handle_rumble_request(&mut running_rumbles, gilrs, rumble, current_time) {
142147
Ok(()) => {}
143148
Err(RumbleError::GilrsError(err)) => {
144149
if let ff::Error::FfNotSupported(_) = err {

0 commit comments

Comments
 (0)