1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
//! Gilrs integration.
use crate::prelude::*;
use gilrs::{ev::filter::axis_dpad_to_button, EventType, Filter, Gilrs as GilrsContext};
use once_cell::sync::Lazy;
use std::sync::{Arc, Mutex};

#[cfg(target_arch = "wasm32")]
use send_wrapper::SendWrapper;

/// Lazy-initialized GilrsContext
#[cfg(not(target_arch = "wasm32"))]
static GILRS_CONTEXT: Lazy<Arc<Mutex<GilrsContext>>> = Lazy::new(|| {
    Arc::new(Mutex::new(
        GilrsContext::new().expect("Failed to initialize GilrsContext"),
    ))
});

// Use SendWrapper when on wasm - GilrsContext is not Sync/Send on wasm,
// this is ok because bevy is single threaded on wasm, it will only be
// accessed from one thread.
/// Lazy-initialized GilrsContext
#[cfg(target_arch = "wasm32")]
static GILRS_CONTEXT: Lazy<Arc<Mutex<SendWrapper<GilrsContext>>>> = Lazy::new(|| {
    Arc::new(Mutex::new(SendWrapper::new(
        GilrsContext::new().expect("Failed to initialize GilrsContext"),
    )))
});

/// Processes gilrs gamepad events into Bones-native GamepadInputs
pub fn process_gamepad_events() -> GamepadInputs {
    let mut gamepad_inputs = GamepadInputs::default();
    let mut gilrs = GILRS_CONTEXT.lock().unwrap();
    while let Some(gilrs_event) = gilrs
        .next_event()
        .filter_ev(&axis_dpad_to_button, &mut gilrs)
    {
        gilrs.update(&gilrs_event);

        let gamepad = usize::from(gilrs_event.id) as u32;
        match gilrs_event.event {
            EventType::Connected => {
                let _pad = gilrs.gamepad(gilrs_event.id);
                gamepad_inputs.gamepad_events.push(GamepadEvent::Connection(
                    GamepadConnectionEvent {
                        gamepad,
                        event: GamepadConnectionEventKind::Connected,
                    },
                ));
            }
            EventType::Disconnected => {
                gamepad_inputs.gamepad_events.push(GamepadEvent::Connection(
                    GamepadConnectionEvent {
                        gamepad,
                        event: GamepadConnectionEventKind::Disconnected,
                    },
                ));
            }
            EventType::ButtonChanged(gilrs_button, value, _) => {
                if let Some(button) = convert_button(gilrs_button) {
                    gamepad_inputs
                        .gamepad_events
                        .push(GamepadEvent::Button(GamepadButtonEvent {
                            gamepad,
                            button,
                            value,
                        }));
                }
            }
            EventType::AxisChanged(gilrs_axis, value, _) => {
                if let Some(axis) = convert_axis(gilrs_axis) {
                    gamepad_inputs
                        .gamepad_events
                        .push(GamepadEvent::Axis(GamepadAxisEvent {
                            gamepad,
                            axis,
                            value,
                        }));
                }
            }
            _ => (),
        };
    }
    gamepad_inputs
}

/// Converts a gilrs button to a bones-native button
fn convert_button(button: gilrs::Button) -> Option<GamepadButton> {
    match button {
        gilrs::Button::South => Some(GamepadButton::South),
        gilrs::Button::East => Some(GamepadButton::East),
        gilrs::Button::North => Some(GamepadButton::North),
        gilrs::Button::West => Some(GamepadButton::West),
        gilrs::Button::C => Some(GamepadButton::C),
        gilrs::Button::Z => Some(GamepadButton::Z),
        gilrs::Button::LeftTrigger => Some(GamepadButton::LeftTrigger),
        gilrs::Button::LeftTrigger2 => Some(GamepadButton::LeftTrigger2),
        gilrs::Button::RightTrigger => Some(GamepadButton::RightTrigger),
        gilrs::Button::RightTrigger2 => Some(GamepadButton::RightTrigger2),
        gilrs::Button::Select => Some(GamepadButton::Select),
        gilrs::Button::Start => Some(GamepadButton::Start),
        gilrs::Button::Mode => Some(GamepadButton::Mode),
        gilrs::Button::LeftThumb => Some(GamepadButton::LeftThumb),
        gilrs::Button::RightThumb => Some(GamepadButton::RightThumb),
        gilrs::Button::DPadUp => Some(GamepadButton::DPadUp),
        gilrs::Button::DPadDown => Some(GamepadButton::DPadDown),
        gilrs::Button::DPadLeft => Some(GamepadButton::DPadLeft),
        gilrs::Button::DPadRight => Some(GamepadButton::DPadRight),
        gilrs::Button::Unknown => None,
    }
}

/// Converts a gilrs axis to a bones-native axis
fn convert_axis(axis: gilrs::Axis) -> Option<GamepadAxis> {
    match axis {
        gilrs::Axis::LeftStickX => Some(GamepadAxis::LeftStickX),
        gilrs::Axis::LeftStickY => Some(GamepadAxis::LeftStickY),
        gilrs::Axis::LeftZ => Some(GamepadAxis::LeftZ),
        gilrs::Axis::RightStickX => Some(GamepadAxis::RightStickX),
        gilrs::Axis::RightStickY => Some(GamepadAxis::RightStickY),
        gilrs::Axis::RightZ => Some(GamepadAxis::RightZ),
        gilrs::Axis::Unknown | gilrs::Axis::DPadX | gilrs::Axis::DPadY => None,
    }
}