Skip to content

Commit df85779

Browse files
committed
Use thread_local! to replace !Send gilrs resource
1 parent ca9829c commit df85779

File tree

3 files changed

+131
-124
lines changed

3 files changed

+131
-124
lines changed
Lines changed: 90 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
use crate::{
22
converter::{convert_axis, convert_button},
3-
Gilrs, GilrsGamepads,
3+
GilrsGamepads, GILRS,
44
};
55
use bevy_ecs::event::EventWriter;
66
use bevy_ecs::prelude::Commands;
7-
#[cfg(target_arch = "wasm32")]
8-
use bevy_ecs::system::NonSendMut;
97
use bevy_ecs::system::ResMut;
108
use bevy_input::gamepad::{
119
GamepadConnection, GamepadConnectionEvent, RawGamepadAxisChangedEvent,
@@ -15,101 +13,109 @@ use gilrs::{ev::filter::axis_dpad_to_button, EventType, Filter};
1513

1614
pub fn gilrs_event_startup_system(
1715
mut commands: Commands,
18-
#[cfg(target_arch = "wasm32")] mut gilrs: NonSendMut<Gilrs>,
19-
#[cfg(not(target_arch = "wasm32"))] mut gilrs: ResMut<Gilrs>,
2016
mut gamepads: ResMut<GilrsGamepads>,
2117
mut events: EventWriter<GamepadConnectionEvent>,
2218
) {
23-
for (id, gamepad) in gilrs.0.get().gamepads() {
24-
// Create entity and add to mapping
25-
let entity = commands.spawn_empty().id();
26-
gamepads.id_to_entity.insert(id, entity);
27-
gamepads.entity_to_id.insert(entity, id);
19+
GILRS.with(|gilrs_ref_cell| {
20+
for (id, gamepad) in gilrs_ref_cell
21+
.borrow()
22+
.as_ref()
23+
.expect("gilrs should have been initialized")
24+
.gamepads()
25+
{
26+
// Create entity and add to mapping
27+
let entity = commands.spawn_empty().id();
28+
gamepads.id_to_entity.insert(id, entity);
29+
gamepads.entity_to_id.insert(entity, id);
2830

29-
events.send(GamepadConnectionEvent {
30-
gamepad: entity,
31-
connection: GamepadConnection::Connected {
32-
name: gamepad.name().to_string(),
33-
vendor_id: gamepad.vendor_id(),
34-
product_id: gamepad.product_id(),
35-
},
36-
});
37-
}
31+
events.send(GamepadConnectionEvent {
32+
gamepad: entity,
33+
connection: GamepadConnection::Connected {
34+
name: gamepad.name().to_string(),
35+
vendor_id: gamepad.vendor_id(),
36+
product_id: gamepad.product_id(),
37+
},
38+
});
39+
}
40+
});
3841
}
3942

4043
pub fn gilrs_event_system(
4144
mut commands: Commands,
42-
#[cfg(target_arch = "wasm32")] mut gilrs: NonSendMut<Gilrs>,
43-
#[cfg(not(target_arch = "wasm32"))] mut gilrs: ResMut<Gilrs>,
4445
mut gamepads: ResMut<GilrsGamepads>,
4546
mut events: EventWriter<RawGamepadEvent>,
4647
mut connection_events: EventWriter<GamepadConnectionEvent>,
4748
mut button_events: EventWriter<RawGamepadButtonChangedEvent>,
4849
mut axis_event: EventWriter<RawGamepadAxisChangedEvent>,
4950
) {
50-
let gilrs = gilrs.0.get();
51-
while let Some(gilrs_event) = gilrs.next_event().filter_ev(&axis_dpad_to_button, gilrs) {
52-
gilrs.update(&gilrs_event);
53-
match gilrs_event.event {
54-
EventType::Connected => {
55-
let pad = gilrs.gamepad(gilrs_event.id);
56-
let entity = gamepads.get_entity(gilrs_event.id).unwrap_or_else(|| {
57-
let entity = commands.spawn_empty().id();
58-
gamepads.id_to_entity.insert(gilrs_event.id, entity);
59-
gamepads.entity_to_id.insert(entity, gilrs_event.id);
60-
entity
61-
});
51+
GILRS.with(|gilrs_ref_cell| {
52+
let mut gilrs_ref = gilrs_ref_cell.borrow_mut();
53+
let gilrs = gilrs_ref.as_mut().expect("");
54+
while let Some(gilrs_event) = gilrs.next_event().filter_ev(&axis_dpad_to_button, gilrs) {
55+
gilrs.update(&gilrs_event);
56+
match gilrs_event.event {
57+
EventType::Connected => {
58+
let pad = gilrs.gamepad(gilrs_event.id);
59+
let entity = gamepads.get_entity(gilrs_event.id).unwrap_or_else(|| {
60+
let entity = commands.spawn_empty().id();
61+
gamepads.id_to_entity.insert(gilrs_event.id, entity);
62+
gamepads.entity_to_id.insert(entity, gilrs_event.id);
63+
entity
64+
});
6265

63-
let event = GamepadConnectionEvent::new(
64-
entity,
65-
GamepadConnection::Connected {
66-
name: pad.name().to_string(),
67-
vendor_id: pad.vendor_id(),
68-
product_id: pad.product_id(),
69-
},
70-
);
66+
let event = GamepadConnectionEvent::new(
67+
entity,
68+
GamepadConnection::Connected {
69+
name: pad.name().to_string(),
70+
vendor_id: pad.vendor_id(),
71+
product_id: pad.product_id(),
72+
},
73+
);
7174

72-
events.send(event.clone().into());
73-
connection_events.send(event);
74-
}
75-
EventType::Disconnected => {
76-
let gamepad = gamepads
77-
.id_to_entity
78-
.get(&gilrs_event.id)
79-
.copied()
80-
.expect("mapping should exist from connection");
81-
let event = GamepadConnectionEvent::new(gamepad, GamepadConnection::Disconnected);
82-
events.send(event.clone().into());
83-
connection_events.send(event);
84-
}
85-
EventType::ButtonChanged(gilrs_button, raw_value, _) => {
86-
let Some(button) = convert_button(gilrs_button) else {
87-
continue;
88-
};
89-
let gamepad = gamepads
90-
.id_to_entity
91-
.get(&gilrs_event.id)
92-
.copied()
93-
.expect("mapping should exist from connection");
94-
events.send(RawGamepadButtonChangedEvent::new(gamepad, button, raw_value).into());
95-
button_events.send(RawGamepadButtonChangedEvent::new(
96-
gamepad, button, raw_value,
97-
));
98-
}
99-
EventType::AxisChanged(gilrs_axis, raw_value, _) => {
100-
let Some(axis) = convert_axis(gilrs_axis) else {
101-
continue;
102-
};
103-
let gamepad = gamepads
104-
.id_to_entity
105-
.get(&gilrs_event.id)
106-
.copied()
107-
.expect("mapping should exist from connection");
108-
events.send(RawGamepadAxisChangedEvent::new(gamepad, axis, raw_value).into());
109-
axis_event.send(RawGamepadAxisChangedEvent::new(gamepad, axis, raw_value));
110-
}
111-
_ => (),
112-
};
113-
}
114-
gilrs.inc();
75+
events.send(event.clone().into());
76+
connection_events.send(event);
77+
}
78+
EventType::Disconnected => {
79+
let gamepad = gamepads
80+
.id_to_entity
81+
.get(&gilrs_event.id)
82+
.copied()
83+
.expect("mapping should exist from connection");
84+
let event =
85+
GamepadConnectionEvent::new(gamepad, GamepadConnection::Disconnected);
86+
events.send(event.clone().into());
87+
connection_events.send(event);
88+
}
89+
EventType::ButtonChanged(gilrs_button, raw_value, _) => {
90+
let Some(button) = convert_button(gilrs_button) else {
91+
continue;
92+
};
93+
let gamepad = gamepads
94+
.id_to_entity
95+
.get(&gilrs_event.id)
96+
.copied()
97+
.expect("mapping should exist from connection");
98+
events
99+
.send(RawGamepadButtonChangedEvent::new(gamepad, button, raw_value).into());
100+
button_events.send(RawGamepadButtonChangedEvent::new(
101+
gamepad, button, raw_value,
102+
));
103+
}
104+
EventType::AxisChanged(gilrs_axis, raw_value, _) => {
105+
let Some(axis) = convert_axis(gilrs_axis) else {
106+
continue;
107+
};
108+
let gamepad = gamepads
109+
.id_to_entity
110+
.get(&gilrs_event.id)
111+
.copied()
112+
.expect("mapping should exist from connection");
113+
events.send(RawGamepadAxisChangedEvent::new(gamepad, axis, raw_value).into());
114+
axis_event.send(RawGamepadAxisChangedEvent::new(gamepad, axis, raw_value));
115+
}
116+
_ => (),
117+
};
118+
}
119+
gilrs.inc();
120+
});
115121
}

crates/bevy_gilrs/src/lib.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,24 @@ mod converter;
1414
mod gilrs_system;
1515
mod rumble;
1616

17+
use std::cell::RefCell;
18+
1719
use bevy_app::{App, Plugin, PostUpdate, PreStartup, PreUpdate};
1820
use bevy_ecs::entity::hash_map::EntityHashMap;
1921
use bevy_ecs::prelude::*;
2022
use bevy_input::InputSystem;
2123
use bevy_platform_support::collections::HashMap;
22-
use bevy_utils::synccell::SyncCell;
2324
use gilrs::GilrsBuilder;
2425
use gilrs_system::{gilrs_event_startup_system, gilrs_event_system};
2526
use rumble::{play_gilrs_rumble, RunningRumbleEffects};
2627
use tracing::error;
2728

28-
#[cfg_attr(not(target_arch = "wasm32"), derive(Resource))]
29-
pub(crate) struct Gilrs(pub SyncCell<gilrs::Gilrs>);
29+
thread_local! {
30+
/// Stores main object responsible of managing gamepads.
31+
///
32+
/// Inner `Option` defaults to `None` and becomes `Some<Gilrs>` upon initialization in `GilrsPlugin::build()`.
33+
pub static GILRS: RefCell<Option<gilrs::Gilrs>> = RefCell::new(None);
34+
}
3035

3136
/// A [`resource`](Resource) with the mapping of connected [`gilrs::GamepadId`] and their [`Entity`].
3237
#[derive(Debug, Default, Resource)]
@@ -65,10 +70,7 @@ impl Plugin for GilrsPlugin {
6570
.build()
6671
{
6772
Ok(gilrs) => {
68-
#[cfg(target_arch = "wasm32")]
69-
app.insert_non_send_resource(Gilrs(SyncCell::new(gilrs)));
70-
#[cfg(not(target_arch = "wasm32"))]
71-
app.insert_resource(Gilrs(SyncCell::new(gilrs)));
73+
GILRS.set(Some(gilrs));
7274
app.init_resource::<GilrsGamepads>();
7375
app.init_resource::<RunningRumbleEffects>()
7476
.add_systems(PreStartup, gilrs_event_startup_system)

crates/bevy_gilrs/src/rumble.rs

Lines changed: 32 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
//! Handle user specified rumble request events.
2-
use crate::{Gilrs, GilrsGamepads};
2+
use crate::{GilrsGamepads, GILRS};
33
use bevy_ecs::prelude::{EventReader, Res, ResMut, Resource};
4-
#[cfg(target_arch = "wasm32")]
5-
use bevy_ecs::system::NonSendMut;
64
use bevy_input::gamepad::{GamepadRumbleIntensity, GamepadRumbleRequest};
75
use bevy_platform_support::collections::HashMap;
86
use bevy_time::{Real, Time};
@@ -128,42 +126,43 @@ fn handle_rumble_request(
128126
}
129127
pub(crate) fn play_gilrs_rumble(
130128
time: Res<Time<Real>>,
131-
#[cfg(target_arch = "wasm32")] mut gilrs: NonSendMut<Gilrs>,
132-
#[cfg(not(target_arch = "wasm32"))] mut gilrs: ResMut<Gilrs>,
133129
gamepads: Res<GilrsGamepads>,
134130
mut requests: EventReader<GamepadRumbleRequest>,
135131
mut running_rumbles: ResMut<RunningRumbleEffects>,
136132
) {
137-
let gilrs = gilrs.0.get();
138-
let current_time = time.elapsed();
139-
// Remove outdated rumble effects.
140-
for rumbles in running_rumbles.rumbles.values_mut() {
141-
// `ff::Effect` uses RAII, dropping = deactivating
142-
rumbles.retain(|RunningRumble { deadline, .. }| *deadline >= current_time);
143-
}
144-
running_rumbles
145-
.rumbles
146-
.retain(|_gamepad, rumbles| !rumbles.is_empty());
147-
148-
// Add new effects.
149-
for rumble in requests.read().cloned() {
150-
let gamepad = rumble.gamepad();
151-
match handle_rumble_request(&mut running_rumbles, gilrs, &gamepads, rumble, current_time) {
152-
Ok(()) => {}
153-
Err(RumbleError::GilrsError(err)) => {
154-
if let ff::Error::FfNotSupported(_) = err {
155-
debug!("Tried to rumble {gamepad:?}, but it doesn't support force feedback");
156-
} else {
157-
warn!(
158-
"Tried to handle rumble request for {gamepad:?} but an error occurred: {err}"
133+
GILRS.with(|gilrs_ref_cell| {
134+
let mut gilrs_ref = gilrs_ref_cell.borrow_mut();
135+
let gilrs = gilrs_ref.as_mut().expect("");
136+
let current_time = time.elapsed();
137+
// Remove outdated rumble effects.
138+
for rumbles in running_rumbles.rumbles.values_mut() {
139+
// `ff::Effect` uses RAII, dropping = deactivating
140+
rumbles.retain(|RunningRumble { deadline, .. }| *deadline >= current_time);
141+
}
142+
running_rumbles
143+
.rumbles
144+
.retain(|_gamepad, rumbles| !rumbles.is_empty());
145+
146+
// Add new effects.
147+
for rumble in requests.read().cloned() {
148+
let gamepad = rumble.gamepad();
149+
match handle_rumble_request(&mut running_rumbles, gilrs, &gamepads, rumble, current_time) {
150+
Ok(()) => {}
151+
Err(RumbleError::GilrsError(err)) => {
152+
if let ff::Error::FfNotSupported(_) = err {
153+
debug!("Tried to rumble {gamepad:?}, but it doesn't support force feedback");
154+
} else {
155+
warn!(
156+
"Tried to handle rumble request for {gamepad:?} but an error occurred: {err}"
159157
);
158+
}
160159
}
161-
}
162-
Err(RumbleError::GamepadNotFound) => {
163-
warn!("Tried to handle rumble request {gamepad:?} but it doesn't exist!");
164-
}
165-
};
166-
}
160+
Err(RumbleError::GamepadNotFound) => {
161+
warn!("Tried to handle rumble request {gamepad:?} but it doesn't exist!");
162+
}
163+
};
164+
}
165+
});
167166
}
168167

169168
#[cfg(test)]

0 commit comments

Comments
 (0)