use alloc::vec::Vec;
use azul_core::dom::DomNodeId;
use azul_core::events::{
EventData, EventProvider, EventSource as CoreEventSource, EventType, SyntheticEvent,
};
use azul_core::task::Instant;
pub use azul_core::gamepad::{GamepadAxis, GamepadButton, GamepadId, GamepadState};
#[derive(Debug, Clone, PartialEq, Default)]
pub struct GamepadManager {
pads: Vec<GamepadState>,
pending_event: bool,
}
impl GamepadManager {
pub fn new() -> Self {
Self::default()
}
pub fn state(&self, id: GamepadId) -> Option<GamepadState> {
self.pads.iter().find(|p| p.id == id).copied()
}
pub fn primary(&self) -> Option<GamepadState> {
self.pads.iter().find(|p| p.connected).copied()
}
pub fn gamepads(&self) -> &[GamepadState] {
&self.pads
}
pub fn set_state(&mut self, state: GamepadState) -> bool {
let changed = if let Some(slot) = self.pads.iter_mut().find(|p| p.id == state.id) {
let changed = !state_bitwise_eq(slot, &state);
*slot = state;
changed
} else {
self.pads.push(state);
true
};
if changed {
self.pending_event = true;
}
changed
}
pub fn clear_pending_event(&mut self) {
self.pending_event = false;
}
}
impl EventProvider for GamepadManager {
fn get_pending_events(&self, timestamp: Instant) -> Vec<SyntheticEvent> {
if self.pending_event {
alloc::vec![SyntheticEvent::new(
EventType::GamepadInput,
CoreEventSource::User,
DomNodeId::ROOT,
timestamp,
EventData::None,
)]
} else {
Vec::new()
}
}
}
fn state_bitwise_eq(a: &GamepadState, b: &GamepadState) -> bool {
a.id == b.id
&& a.connected == b.connected
&& a.buttons == b.buttons
&& a.left_stick_x.to_bits() == b.left_stick_x.to_bits()
&& a.left_stick_y.to_bits() == b.left_stick_y.to_bits()
&& a.right_stick_x.to_bits() == b.right_stick_x.to_bits()
&& a.right_stick_y.to_bits() == b.right_stick_y.to_bits()
&& a.left_z.to_bits() == b.left_z.to_bits()
&& a.right_z.to_bits() == b.right_z.to_bits()
}
static PENDING_STATES: std::sync::Mutex<Vec<GamepadState>> = std::sync::Mutex::new(Vec::new());
pub fn push_gamepad_state(state: GamepadState) {
let mut q = PENDING_STATES.lock().unwrap_or_else(|e| e.into_inner());
q.push(state);
}
pub fn drain_gamepad_states() -> Vec<GamepadState> {
let mut q = PENDING_STATES.lock().unwrap_or_else(|e| e.into_inner());
core::mem::take(&mut *q)
}
#[cfg(test)]
mod tests {
use super::*;
fn st(id: u32, connected: bool, buttons: u32) -> GamepadState {
let mut s = GamepadState::empty(GamepadId { id });
s.connected = connected;
s.buttons = buttons;
s
}
#[test]
fn manager_upserts_by_id_and_flags_change() {
let mut mgr = GamepadManager::new();
assert_eq!(mgr.state(GamepadId { id: 0 }), None);
assert!(mgr.set_state(st(0, true, 0b1)));
assert!(mgr.state(GamepadId { id: 0 }).is_some());
assert!(!mgr.set_state(st(0, true, 0b1)));
assert!(mgr.set_state(st(0, true, 0b11)));
assert_eq!(mgr.gamepads().len(), 1);
assert!(mgr.set_state(st(1, true, 0)));
assert_eq!(mgr.gamepads().len(), 2);
}
#[test]
fn primary_is_first_connected() {
let mut mgr = GamepadManager::new();
mgr.set_state(st(0, false, 0)); mgr.set_state(st(1, true, 0));
assert_eq!(mgr.primary().map(|p| p.id.id), Some(1));
}
#[test]
fn is_pressed_decodes_the_bitset() {
let s = st(0, true, GamepadButton::South.bit() | GamepadButton::Start.bit());
assert!(s.is_pressed(GamepadButton::South));
assert!(s.is_pressed(GamepadButton::Start));
assert!(!s.is_pressed(GamepadButton::East));
}
#[test]
fn states_round_trip_through_the_channel() {
let _ = drain_gamepad_states();
push_gamepad_state(st(0, true, 0b1));
push_gamepad_state(st(0, true, 0b10)); push_gamepad_state(st(1, true, 0));
let drained = drain_gamepad_states();
assert_eq!(drained.len(), 3);
let mut mgr = GamepadManager::new();
for s in &drained {
mgr.set_state(*s);
}
assert_eq!(mgr.state(GamepadId { id: 0 }).map(|p| p.buttons), Some(0b10));
assert_eq!(mgr.gamepads().len(), 2);
assert!(drain_gamepad_states().is_empty());
}
}