#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![forbid(unsafe_code)]
#![doc(
html_logo_url = "https://bevy.org/assets/icon.png",
html_favicon_url = "https://bevy.org/assets/icon.png"
)]
mod converter;
mod gilrs_system;
mod rumble;
#[cfg(not(target_arch = "wasm32"))]
use bevy_platform::cell::SyncCell;
#[cfg(target_arch = "wasm32")]
use core::cell::RefCell;
use bevy_app::{App, Plugin, PostUpdate, PreStartup, PreUpdate};
use bevy_ecs::entity::EntityHashMap;
use bevy_ecs::prelude::*;
use bevy_input::InputSystems;
use bevy_platform::collections::HashMap;
use gilrs::GilrsBuilder;
use gilrs_system::{gilrs_event_startup_system, gilrs_event_system};
use rumble::{play_gilrs_rumble, RunningRumbleEffects};
use tracing::error;
#[cfg(target_arch = "wasm32")]
thread_local! {
pub static GILRS: RefCell<Option<gilrs::Gilrs>> = const { RefCell::new(None) };
}
#[derive(Resource)]
pub(crate) struct Gilrs {
#[cfg(not(target_arch = "wasm32"))]
cell: SyncCell<gilrs::Gilrs>,
}
impl Gilrs {
#[inline]
pub fn with(&mut self, f: impl FnOnce(&mut gilrs::Gilrs)) {
#[cfg(target_arch = "wasm32")]
GILRS.with(|g| f(g.borrow_mut().as_mut().expect("GILRS was not initialized")));
#[cfg(not(target_arch = "wasm32"))]
f(self.cell.get());
}
}
#[derive(Debug, Default, Resource)]
pub(crate) struct GilrsGamepads {
pub(crate) entity_to_id: EntityHashMap<gilrs::GamepadId>,
pub(crate) id_to_entity: HashMap<gilrs::GamepadId, Entity>,
}
impl GilrsGamepads {
pub fn get_entity(&self, gamepad_id: gilrs::GamepadId) -> Option<Entity> {
self.id_to_entity.get(&gamepad_id).copied()
}
pub fn get_gamepad_id(&self, entity: Entity) -> Option<gilrs::GamepadId> {
self.entity_to_id.get(&entity).copied()
}
}
#[derive(Default)]
pub struct GilrsPlugin;
#[derive(Debug, PartialEq, Eq, Clone, Hash, SystemSet)]
pub struct RumbleSystems;
#[deprecated(since = "0.17.0", note = "Renamed to `RumbleSystems`.")]
pub type RumbleSystem = RumbleSystems;
impl Plugin for GilrsPlugin {
fn build(&self, app: &mut App) {
match GilrsBuilder::new()
.with_default_filters(false)
.set_update_state(false)
.build()
{
Ok(gilrs) => {
let g = Gilrs {
#[cfg(not(target_arch = "wasm32"))]
cell: SyncCell::new(gilrs),
};
#[cfg(target_arch = "wasm32")]
GILRS.with(|g| {
g.replace(Some(gilrs));
});
app.insert_resource(g);
app.init_resource::<GilrsGamepads>();
app.init_resource::<RunningRumbleEffects>()
.add_systems(PreStartup, gilrs_event_startup_system)
.add_systems(PreUpdate, gilrs_event_system.before(InputSystems))
.add_systems(PostUpdate, play_gilrs_rumble.in_set(RumbleSystems));
}
Err(err) => error!("Failed to start Gilrs. {}", err),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn world_is_truly_send() {
let mut app = App::new();
app.add_plugins(GilrsPlugin);
let world = core::mem::take(app.world_mut());
let handler = std::thread::spawn(move || {
drop(world);
});
handler.join().unwrap();
}
}