use hashbrown::{HashMap, HashSet};
use crate::math::Vec2;
use crate::Context;
pub(crate) struct GamepadState {
pub platform_id: u32,
pub buttons_down: HashSet<GamepadButton>,
pub buttons_pressed: HashSet<GamepadButton>,
pub buttons_released: HashSet<GamepadButton>,
pub current_axis_state: HashMap<GamepadAxis, f32>,
}
impl GamepadState {
pub(crate) fn new(platform_id: u32) -> GamepadState {
GamepadState {
platform_id,
buttons_down: HashSet::new(),
buttons_pressed: HashSet::new(),
buttons_released: HashSet::new(),
current_axis_state: HashMap::new(),
}
}
pub(crate) fn set_button_down(&mut self, btn: GamepadButton) -> bool {
let was_up = self.buttons_down.insert(btn);
if was_up {
self.buttons_pressed.insert(btn);
}
was_up
}
pub(crate) fn set_button_up(&mut self, btn: GamepadButton) -> bool {
let was_down = self.buttons_down.remove(&btn);
if was_down {
self.buttons_released.insert(btn);
}
was_down
}
pub(crate) fn set_axis_position(&mut self, axis: GamepadAxis, value: f32) {
self.current_axis_state.insert(axis, value);
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(
feature = "serde_support",
derive(serde::Serialize, serde::Deserialize)
)]
#[allow(missing_docs)]
pub enum GamepadButton {
A,
B,
X,
Y,
Up,
Down,
Left,
Right,
LeftShoulder,
LeftTrigger,
LeftStick,
RightShoulder,
RightTrigger,
RightStick,
Start,
Back,
Guide,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(
feature = "serde_support",
derive(serde::Serialize, serde::Deserialize)
)]
#[allow(missing_docs)]
pub enum GamepadAxis {
LeftStickX,
LeftStickY,
LeftTrigger,
RightStickX,
RightStickY,
RightTrigger,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(
feature = "serde_support",
derive(serde::Serialize, serde::Deserialize)
)]
#[allow(missing_docs)]
pub enum GamepadStick {
LeftStick,
RightStick,
}
pub fn is_gamepad_connected(ctx: &Context, gamepad_id: usize) -> bool {
get_gamepad(ctx, gamepad_id).is_some()
}
pub fn get_gamepad_name(ctx: &Context, gamepad_id: usize) -> Option<String> {
get_gamepad(ctx, gamepad_id)
.map(|g| g.platform_id)
.map(|id| ctx.window.get_gamepad_name(id))
}
pub fn is_gamepad_button_down(ctx: &Context, gamepad_id: usize, button: GamepadButton) -> bool {
if let Some(pad) = get_gamepad(ctx, gamepad_id) {
pad.buttons_down.contains(&button)
} else {
false
}
}
pub fn is_gamepad_button_up(ctx: &Context, gamepad_id: usize, button: GamepadButton) -> bool {
if let Some(pad) = get_gamepad(ctx, gamepad_id) {
!pad.buttons_down.contains(&button)
} else {
true
}
}
pub fn is_gamepad_button_pressed(ctx: &Context, gamepad_id: usize, button: GamepadButton) -> bool {
if let Some(pad) = get_gamepad(ctx, gamepad_id) {
pad.buttons_pressed.contains(&button)
} else {
false
}
}
pub fn is_gamepad_button_released(ctx: &Context, gamepad_id: usize, button: GamepadButton) -> bool {
if let Some(pad) = get_gamepad(ctx, gamepad_id) {
pad.buttons_released.contains(&button)
} else {
false
}
}
enum GamepadIterator<T> {
Disconnected,
Connected(T),
}
impl<T> Iterator for GamepadIterator<T>
where
T: Iterator,
{
type Item = T::Item;
fn next(&mut self) -> Option<T::Item> {
match self {
GamepadIterator::Disconnected => None,
GamepadIterator::Connected(i) => i.next(),
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
match self {
GamepadIterator::Disconnected => (0, Some(0)),
GamepadIterator::Connected(i) => i.size_hint(),
}
}
}
pub fn get_gamepad_buttons_down(
ctx: &Context,
gamepad_id: usize,
) -> impl Iterator<Item = &GamepadButton> {
if let Some(pad) = get_gamepad(ctx, gamepad_id) {
GamepadIterator::Connected(pad.buttons_down.iter())
} else {
GamepadIterator::Disconnected
}
}
pub fn get_gamepad_buttons_pressed(
ctx: &Context,
gamepad_id: usize,
) -> impl Iterator<Item = &GamepadButton> {
if let Some(pad) = get_gamepad(ctx, gamepad_id) {
GamepadIterator::Connected(pad.buttons_pressed.iter())
} else {
GamepadIterator::Disconnected
}
}
pub fn get_gamepad_buttons_released(
ctx: &Context,
gamepad_id: usize,
) -> impl Iterator<Item = &GamepadButton> {
if let Some(pad) = get_gamepad(ctx, gamepad_id) {
GamepadIterator::Connected(pad.buttons_released.iter())
} else {
GamepadIterator::Disconnected
}
}
pub fn get_gamepad_axis_position(ctx: &Context, gamepad_id: usize, axis: GamepadAxis) -> f32 {
if let Some(pad) = get_gamepad(ctx, gamepad_id) {
if let Some(value) = pad.current_axis_state.get(&axis) {
*value
} else {
0.0
}
} else {
0.0
}
}
pub fn get_gamepad_stick_position(
ctx: &Context,
gamepad_id: usize,
stick: GamepadStick,
) -> Vec2<f32> {
let (x_axis, y_axis) = match stick {
GamepadStick::LeftStick => (GamepadAxis::LeftStickX, GamepadAxis::LeftStickY),
GamepadStick::RightStick => (GamepadAxis::RightStickX, GamepadAxis::RightStickY),
};
Vec2::new(
get_gamepad_axis_position(ctx, gamepad_id, x_axis),
get_gamepad_axis_position(ctx, gamepad_id, y_axis),
)
}
pub fn is_gamepad_vibration_supported(ctx: &Context, gamepad_id: usize) -> bool {
if let Some(pad) = get_gamepad(ctx, gamepad_id) {
ctx.window.is_gamepad_vibration_supported(pad.platform_id)
} else {
false
}
}
pub fn set_gamepad_vibration(ctx: &mut Context, gamepad_id: usize, strength: f32) {
if let Some(platform_id) = get_gamepad(ctx, gamepad_id).map(|g| g.platform_id) {
ctx.window.set_gamepad_vibration(platform_id, strength);
}
}
pub fn start_gamepad_vibration(ctx: &mut Context, gamepad_id: usize, strength: f32, duration: u32) {
if let Some(platform_id) = get_gamepad(ctx, gamepad_id).map(|g| g.platform_id) {
ctx.window
.start_gamepad_vibration(platform_id, strength, duration);
}
}
pub fn stop_gamepad_vibration(ctx: &mut Context, gamepad_id: usize) {
if let Some(platform_id) = get_gamepad(ctx, gamepad_id).map(|g| g.platform_id) {
ctx.window.stop_gamepad_vibration(platform_id);
}
}
pub(crate) fn add_gamepad(ctx: &mut Context, platform_id: u32) -> usize {
for (i, slot) in ctx.input.pads.iter_mut().enumerate() {
if slot.is_none() {
*slot = Some(GamepadState::new(platform_id));
return i;
}
}
let i = ctx.input.pads.len();
ctx.input.pads.push(Some(GamepadState::new(platform_id)));
i
}
pub(crate) fn remove_gamepad(ctx: &mut Context, gamepad_id: usize) {
ctx.input.pads[gamepad_id] = None;
}
pub(crate) fn get_gamepad(ctx: &Context, gamepad_id: usize) -> Option<&GamepadState> {
if let Some(Some(pad)) = ctx.input.pads.get(gamepad_id) {
Some(pad)
} else {
None
}
}
pub(crate) fn get_gamepad_mut(ctx: &mut Context, gamepad_id: usize) -> Option<&mut GamepadState> {
if let Some(Some(pad)) = ctx.input.pads.get_mut(gamepad_id) {
Some(pad)
} else {
None
}
}