Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

40
vendor/gilrs/src/constants.rs vendored Normal file
View File

@@ -0,0 +1,40 @@
// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
pub const BTN_UNKNOWN: u16 = 0;
pub const BTN_SOUTH: u16 = 1;
pub const BTN_EAST: u16 = 2;
pub const BTN_C: u16 = 3;
pub const BTN_NORTH: u16 = 4;
pub const BTN_WEST: u16 = 5;
pub const BTN_Z: u16 = 6;
pub const BTN_LT: u16 = 7;
pub const BTN_RT: u16 = 8;
pub const BTN_LT2: u16 = 9;
pub const BTN_RT2: u16 = 10;
pub const BTN_SELECT: u16 = 11;
pub const BTN_START: u16 = 12;
pub const BTN_MODE: u16 = 13;
pub const BTN_LTHUMB: u16 = 14;
pub const BTN_RTHUMB: u16 = 15;
pub const BTN_DPAD_UP: u16 = 16;
pub const BTN_DPAD_DOWN: u16 = 17;
pub const BTN_DPAD_LEFT: u16 = 18;
pub const BTN_DPAD_RIGHT: u16 = 19;
pub const AXIS_UNKNOWN: u16 = 0;
pub const AXIS_LSTICKX: u16 = 1;
pub const AXIS_LSTICKY: u16 = 2;
pub const AXIS_LEFTZ: u16 = 3;
pub const AXIS_RSTICKX: u16 = 4;
pub const AXIS_RSTICKY: u16 = 5;
pub const AXIS_RIGHTZ: u16 = 6;
pub const AXIS_DPADX: u16 = 7;
pub const AXIS_DPADY: u16 = 8;

547
vendor/gilrs/src/ev/filter.rs vendored Normal file
View File

@@ -0,0 +1,547 @@
// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
//! Alter events in various ways.
//!
//! This modules contains "event filters" that can change, drop or create new events. To use them,
//! import `Filter` trait and call `filter()` function on `Option<Event>`. Because `filter` also
//! returns `Option<Event>` you can combine multiple filters by using `filter()` function on
//! returned event.
//!
//! Filters in this modules have public fields that can be used to configure their behaviour. You
//! can also create them with default values using `new()` method. If filter is not configurable,
//! it is implemented as function (for example `deadzone()`).
//!
//! # Example
//!
//! ```
//! use gilrs::{GilrsBuilder, Filter};
//! use gilrs::ev::filter::{Jitter, Repeat, deadzone};
//!
//! let mut gilrs = GilrsBuilder::new().with_default_filters(false).build().unwrap();
//! let jitter = Jitter { threshold: 0.02 };
//! let repeat = Repeat::new();
//!
//! // Event loop
//! loop {
//! while let Some(event) = gilrs
//! .next_event()
//! .filter_ev(&jitter, &mut gilrs)
//! .filter_ev(&deadzone, &mut gilrs)
//! .filter_ev(&repeat, &mut gilrs)
//! {
//! gilrs.update(&event);
//! println!("{:?}", event);
//! }
//! # break;
//! }
//! ```
//! # Implementing custom filters
//!
//! If you want to implement your own filters, you will have to implement `FilterFn` trait.
//! **Do not return `None` if you got `Some(event)`**. If you want to discard an event, uses
//! `EventType::Dropped`. Returning `None` means that there are no more events to process and
//! will end `while let` loop.
//!
//! ## Example
//!
//! Example implementations of filter that will drop all events with `Unknown` axis or button.
//!
//! ```
//! use gilrs::ev::filter::FilterFn;
//! use gilrs::{Gilrs, Event, EventType, Button, Axis, Filter};
//!
//! struct UnknownSlayer;
//!
//! impl FilterFn for UnknownSlayer {
//! fn filter(&self, ev: Option<Event>, _gilrs: &mut Gilrs) -> Option<Event> {
//! match ev {
//! Some(Event { event: EventType::ButtonPressed(Button::Unknown, ..), id, .. })
//! | Some(Event { event: EventType::ButtonReleased(Button::Unknown, ..), id, .. })
//! | Some(Event { event: EventType::AxisChanged(Axis::Unknown, ..), id, .. })
//! => Some(Event::new(id, EventType::Dropped)),
//! _ => ev,
//! }
//! }
//! }
//! ```
//!
//! `FilterFn` is also implemented for all `Fn(Option<Event>, &Gilrs) -> Option<Event>`, so above
//! example could be simplified to passing closure to `filter()` function.
use crate::ev::{Axis, AxisOrBtn, Button, Code, Event, EventType};
use crate::gamepad::{Gamepad, Gilrs};
use crate::utils;
use std::time::Duration;
/// Discard axis events that changed less than `threshold`.
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct Jitter {
pub threshold: f32,
}
impl Jitter {
/// Creates new `Repeat` filter with threshold set to 0.01.
pub fn new() -> Self {
Jitter { threshold: 0.01 }
}
}
impl Default for Jitter {
fn default() -> Self {
Self::new()
}
}
impl FilterFn for Jitter {
fn filter(&self, ev: Option<Event>, gilrs: &mut Gilrs) -> Option<Event> {
match ev {
Some(Event {
event: EventType::AxisChanged(_, val, axis),
id,
..
}) => match gilrs.gamepad(id).state().axis_data(axis) {
Some(data) if val != 0.0 && (val - data.value()).abs() < self.threshold => {
Some(Event::new(id, EventType::Dropped))
}
_ => ev,
},
_ => ev,
}
}
}
fn apply_deadzone(x: f32, y: f32, threshold: f32) -> (f32, f32) {
let magnitude = utils::clamp((x * x + y * y).sqrt(), 0.0, 1.0);
if magnitude <= threshold {
(0.0, 0.0)
} else {
let norm = ((magnitude - threshold) / (1.0 - threshold)) / magnitude;
(x * norm, y * norm)
}
}
fn deadzone_nonzero_axis_idx(axis: Axis) -> Option<usize> {
Some(match axis {
Axis::DPadX => 0,
Axis::DPadY => 1,
Axis::LeftStickX => 2,
Axis::LeftStickY => 3,
Axis::RightStickX => 4,
Axis::RightStickY => 5,
_ => {
return None;
}
})
}
/// Drops events in dead zone and remaps value to keep it in standard range.
pub fn deadzone(ev: Option<Event>, gilrs: &mut Gilrs) -> Option<Event> {
match ev {
Some(Event {
event: EventType::AxisChanged(axis, val, nec),
id,
time,
}) => {
let threshold = match gilrs.gamepad(id).deadzone(nec) {
Some(t) => t,
None => return ev,
};
if let Some((other_axis, other_code)) = axis
.second_axis()
.and_then(|axis| gilrs.gamepad(id).axis_code(axis).map(|code| (axis, code)))
{
let other_val = gilrs.gamepad(id).state().value(other_code);
let val = apply_deadzone(val, other_val, threshold);
// Since this is the second axis, deadzone_nonzero_axis_idx() will always returns something.
let other_axis_idx = deadzone_nonzero_axis_idx(other_axis).unwrap();
if val.0 == 0.
&& val.1 == 0.
&& gilrs.gamepads_data[id.0].have_sent_nonzero_for_axis[other_axis_idx]
&& gilrs.gamepad(id).state().value(other_code) != 0.
{
// Clear other axis that is now within the dead zone threshold.
gilrs.insert_event(Event {
id,
time,
event: EventType::AxisChanged(other_axis, 0., other_code),
});
gilrs.gamepads_data[id.0].have_sent_nonzero_for_axis[other_axis_idx] = false;
}
Some(if gilrs.gamepad(id).state().value(nec) == val.0 {
Event::new(id, EventType::Dropped)
} else {
if let Some(axis_idx) = deadzone_nonzero_axis_idx(axis) {
gilrs.gamepads_data[id.0].have_sent_nonzero_for_axis[axis_idx] =
val.0 != 0.;
}
Event {
id,
time,
event: EventType::AxisChanged(axis, val.0, nec),
}
})
} else {
let val = apply_deadzone(val, 0.0, threshold).0;
Some(if gilrs.gamepad(id).state().value(nec) == val {
Event::new(id, EventType::Dropped)
} else {
if let Some(axis_idx) = deadzone_nonzero_axis_idx(axis) {
gilrs.gamepads_data[id.0].have_sent_nonzero_for_axis[axis_idx] = val != 0.;
}
Event {
id,
time,
event: EventType::AxisChanged(axis, val, nec),
}
})
}
}
Some(Event {
event: EventType::ButtonChanged(btn, val, nec),
id,
time,
}) => {
let gp = &gilrs.gamepad(id);
let threshold = match gp.deadzone(nec) {
Some(t) => t,
None => return ev,
};
let val = apply_deadzone(val, 0.0, threshold).0;
Some(if gp.state().value(nec) == val {
Event::new(id, EventType::Dropped)
} else {
Event {
id,
time,
event: EventType::ButtonChanged(btn, val, nec),
}
})
}
_ => ev,
}
}
/// Maps axis dpad events to button dpad events.
///
/// This filter will do nothing if gamepad has dpad buttons (to prevent double events for same
/// element) and if standard `NativeEvCode` for dpads is used by some other buttons. It will always
/// try to map if SDL mappings contains mappings for all four hats.
pub fn axis_dpad_to_button(ev: Option<Event>, gilrs: &mut Gilrs) -> Option<Event> {
use gilrs_core::native_ev_codes as necs;
fn can_map(gp: &Gamepad<'_>) -> bool {
let hats_mapped = gp.mapping().hats_mapped();
if hats_mapped == 0b0000_1111 {
true
} else if hats_mapped == 0 {
gp.axis_or_btn_name(Code(necs::BTN_DPAD_RIGHT)).is_none()
&& gp.axis_or_btn_name(Code(necs::BTN_DPAD_LEFT)).is_none()
&& gp.axis_or_btn_name(Code(necs::BTN_DPAD_DOWN)).is_none()
&& gp.axis_or_btn_name(Code(necs::BTN_DPAD_UP)).is_none()
&& gp.button_code(Button::DPadRight).is_none()
} else {
// Not all hats are mapped so let's ignore it for now.
false
}
}
let ev = ev?;
let gamepad = gilrs.gamepad(ev.id);
if !can_map(&gamepad) {
return Some(ev);
}
let mut out_event = ev.drop();
match ev.event {
EventType::AxisChanged(Axis::DPadX, val, _) => {
let mut release_left = false;
let mut release_right = false;
if val == 1.0 {
// The axis value might change from left (-1.0) to right (1.0) immediately without
// us getting an additional event for the release at the center position (0.0).
release_left = gamepad.state().is_pressed(Code(necs::BTN_DPAD_LEFT));
gilrs.insert_event(Event {
event: EventType::ButtonChanged(
Button::DPadRight,
1.0,
Code(necs::BTN_DPAD_RIGHT),
),
..ev
});
out_event = Event {
event: EventType::ButtonPressed(Button::DPadRight, Code(necs::BTN_DPAD_RIGHT)),
..ev
};
} else if val == -1.0 {
// The axis value might change from right (1.0) to left (-1.0) immediately without
// us getting an additional event for the release at the center position (0.0).
release_right = gamepad.state().is_pressed(Code(necs::BTN_DPAD_RIGHT));
gilrs.insert_event(Event {
event: EventType::ButtonChanged(
Button::DPadLeft,
1.0,
Code(necs::BTN_DPAD_LEFT),
),
..ev
});
out_event = Event {
event: EventType::ButtonPressed(Button::DPadLeft, Code(necs::BTN_DPAD_LEFT)),
..ev
};
} else {
release_left = gamepad.state().is_pressed(Code(necs::BTN_DPAD_LEFT));
release_right = gamepad.state().is_pressed(Code(necs::BTN_DPAD_RIGHT));
}
if release_right {
if !out_event.is_dropped() {
gilrs.insert_event(out_event);
}
gilrs.insert_event(Event {
event: EventType::ButtonChanged(
Button::DPadRight,
0.0,
Code(necs::BTN_DPAD_RIGHT),
),
..ev
});
out_event = Event {
event: EventType::ButtonReleased(Button::DPadRight, Code(necs::BTN_DPAD_RIGHT)),
..ev
};
}
if release_left {
if !out_event.is_dropped() {
gilrs.insert_event(out_event);
}
gilrs.insert_event(Event {
event: EventType::ButtonChanged(
Button::DPadLeft,
0.0,
Code(necs::BTN_DPAD_LEFT),
),
..ev
});
out_event = Event {
event: EventType::ButtonReleased(Button::DPadLeft, Code(necs::BTN_DPAD_LEFT)),
..ev
};
}
Some(out_event)
}
EventType::AxisChanged(Axis::DPadY, val, _) => {
let mut release_up = false;
let mut release_down = false;
if val == 1.0 {
// The axis value might change from down (-1.0) to up (1.0) immediately without us
// getting an additional event for the release at the center position (0.0).
release_down = gamepad.state().is_pressed(Code(necs::BTN_DPAD_DOWN));
gilrs.insert_event(Event {
event: EventType::ButtonChanged(Button::DPadUp, 1.0, Code(necs::BTN_DPAD_UP)),
..ev
});
out_event = Event {
event: EventType::ButtonPressed(Button::DPadUp, Code(necs::BTN_DPAD_UP)),
..ev
};
} else if val == -1.0 {
// The axis value might change from up (1.0) to down (-1.0) immediately without us
// getting an additional event for the release at the center position (0.0).
release_up = gamepad.state().is_pressed(Code(necs::BTN_DPAD_UP));
gilrs.insert_event(Event {
event: EventType::ButtonChanged(
Button::DPadDown,
1.0,
Code(necs::BTN_DPAD_DOWN),
),
..ev
});
out_event = Event {
event: EventType::ButtonPressed(Button::DPadDown, Code(necs::BTN_DPAD_DOWN)),
..ev
};
} else {
release_up = gamepad.state().is_pressed(Code(necs::BTN_DPAD_UP));
release_down = gamepad.state().is_pressed(Code(necs::BTN_DPAD_DOWN));
}
if release_up {
if !out_event.is_dropped() {
gilrs.insert_event(out_event);
}
gilrs.insert_event(Event {
event: EventType::ButtonChanged(Button::DPadUp, 0.0, Code(necs::BTN_DPAD_UP)),
..ev
});
out_event = Event {
event: EventType::ButtonReleased(Button::DPadUp, Code(necs::BTN_DPAD_UP)),
..ev
};
}
if release_down {
if !out_event.is_dropped() {
gilrs.insert_event(out_event);
}
gilrs.insert_event(Event {
event: EventType::ButtonChanged(
Button::DPadDown,
0.0,
Code(necs::BTN_DPAD_DOWN),
),
..ev
});
out_event = Event {
event: EventType::ButtonReleased(Button::DPadDown, Code(necs::BTN_DPAD_DOWN)),
..ev
};
}
Some(out_event)
}
_ => Some(ev),
}
}
/// Repeats pressed keys.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct Repeat {
pub after: Duration,
pub every: Duration,
}
impl Repeat {
/// Creates new `Repeat` filter with `after` set to 500ms and `every` set to 30ms.
pub fn new() -> Self {
Repeat {
after: Duration::from_millis(500),
every: Duration::from_millis(30),
}
}
}
impl Default for Repeat {
fn default() -> Self {
Self::new()
}
}
impl FilterFn for Repeat {
fn filter(&self, ev: Option<Event>, gilrs: &mut Gilrs) -> Option<Event> {
match ev {
Some(ev) => Some(ev),
None => {
let now = utils::time_now();
for (id, gamepad) in gilrs.gamepads() {
for (nec, btn_data) in gamepad.state().buttons() {
match (
btn_data.is_pressed(),
btn_data.is_repeating(),
now.duration_since(btn_data.timestamp()),
) {
(true, false, Ok(dur)) if dur >= self.after => {
let btn_name = match gamepad.axis_or_btn_name(nec) {
Some(AxisOrBtn::Btn(b)) => b,
_ => Button::Unknown,
};
return Some(Event {
id,
event: EventType::ButtonRepeated(btn_name, nec),
time: btn_data.timestamp() + self.after,
});
}
(true, true, Ok(dur)) if dur >= self.every => {
let btn_name = match gamepad.axis_or_btn_name(nec) {
Some(AxisOrBtn::Btn(b)) => b,
_ => Button::Unknown,
};
return Some(Event {
id,
event: EventType::ButtonRepeated(btn_name, nec),
time: btn_data.timestamp() + self.every,
});
}
_ => (),
}
}
}
None
}
}
}
}
/// Allow filtering events.
///
/// See module level documentation for more info.
pub trait Filter {
fn filter_ev<F: FilterFn>(&self, filter: &F, gilrs: &mut Gilrs) -> Option<Event>;
}
/// Actual filter implementation.
///
/// See module level documentation for more info.
pub trait FilterFn {
fn filter(&self, ev: Option<Event>, gilrs: &mut Gilrs) -> Option<Event>;
}
impl<F> FilterFn for F
where
F: Fn(Option<Event>, &mut Gilrs) -> Option<Event>,
{
fn filter(&self, ev: Option<Event>, gilrs: &mut Gilrs) -> Option<Event> {
self(ev, gilrs)
}
}
impl Filter for Option<Event> {
fn filter_ev<F: FilterFn>(&self, filter: &F, gilrs: &mut Gilrs) -> Option<Event> {
let e = filter.filter(*self, gilrs);
debug_assert!(
!(self.is_some() && e.is_none()),
"Filter changed Some(event) into None. See ev::filter documentation for more info."
);
e
}
}
impl Filter for Event {
fn filter_ev<F: FilterFn>(&self, filter: &F, gilrs: &mut Gilrs) -> Option<Event> {
let e = filter.filter(Some(*self), gilrs);
debug_assert!(
e.is_some(),
"Filter changed Some(event) into None. See ev::filter documentation for more info."
);
e
}
}

266
vendor/gilrs/src/ev/mod.rs vendored Normal file
View File

@@ -0,0 +1,266 @@
// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
//! Gamepad state and other event related functionality.
pub mod filter;
pub mod state;
use std::{
fmt::{Display, Formatter, Result as FmtResult},
time::SystemTime,
};
use crate::{constants::*, gamepad::GamepadId, utils};
#[cfg(feature = "serde-serialize")]
use serde::{Deserialize, Serialize};
/// Platform specific event code.
///
/// This type represents single gamepads's element like specific axis or button.
/// It can't be directly created, but you can get it from events or using
/// `Gamepad`'s methods [`button_code`](crate::Gamepad::button_code) and
/// [`axis_code`](crate::Gamepad::axis_code). If `serde-serialize` feature is
/// enabled, `Code` can be serialized and deserialized, but keep in mind that
/// layout **is** platform-specific. So it's not possible to serialize `Code` on
/// Linux and deserialize it on Windows. This also apply to `Display` implementation.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
pub struct Code(pub(crate) gilrs_core::EvCode);
impl Display for Code {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
self.0.fmt(f)
}
}
impl Code {
pub fn into_u32(&self) -> u32 {
self.0.into_u32()
}
}
/// Holds information about gamepad event.
#[derive(Copy, Clone, PartialEq, Debug)]
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
#[non_exhaustive]
pub struct Event {
/// Id of gamepad.
pub id: GamepadId,
/// Event's data.
pub event: EventType,
/// Time when event was emitted.
pub time: SystemTime,
}
impl Event {
/// Creates new event with current time.
pub fn new(id: GamepadId, event: EventType) -> Self {
Event {
id,
event,
time: utils::time_now(),
}
}
/// Returns `Event` with `EventType::Dropped`.
pub fn drop(mut self) -> Event {
self.event = EventType::Dropped;
self
}
/// Returns true if event is `Dropped` and should be ignored.
pub fn is_dropped(&self) -> bool {
self.event == EventType::Dropped
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
#[non_exhaustive]
/// Gamepad event.
pub enum EventType {
/// Some button on gamepad has been pressed.
ButtonPressed(Button, Code),
/// This event can be generated by [`ev::Repeat`](filter/struct.Repeat.html) event filter.
ButtonRepeated(Button, Code),
/// Previously pressed button has been released.
ButtonReleased(Button, Code),
/// Value of button has changed. Value can be in range [0.0, 1.0].
ButtonChanged(Button, f32, Code),
/// Value of axis has changed. Value can be in range [-1.0, 1.0].
AxisChanged(Axis, f32, Code),
/// Gamepad has been connected. If gamepad's UUID doesn't match one of disconnected gamepads,
/// newly connected gamepad will get new ID.
Connected,
/// Gamepad has been disconnected. Disconnected gamepad will not generate any new events.
Disconnected,
/// There was an `Event`, but it was dropped by one of filters. You should ignore it.
Dropped,
/// A force feedback effect has ran for its duration and stopped.
ForceFeedbackEffectCompleted,
}
#[repr(u16)]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
/// Gamepad's elements which state can be represented by value from 0.0 to 1.0.
///
/// ![Controller layout](https://gilrs-project.gitlab.io/gilrs/img/controller.svg)
pub enum Button {
// Action Pad
South = BTN_SOUTH,
East = BTN_EAST,
North = BTN_NORTH,
West = BTN_WEST,
C = BTN_C,
Z = BTN_Z,
// Triggers
LeftTrigger = BTN_LT,
LeftTrigger2 = BTN_LT2,
RightTrigger = BTN_RT,
RightTrigger2 = BTN_RT2,
// Menu Pad
Select = BTN_SELECT,
Start = BTN_START,
Mode = BTN_MODE,
// Sticks
LeftThumb = BTN_LTHUMB,
RightThumb = BTN_RTHUMB,
// D-Pad
DPadUp = BTN_DPAD_UP,
DPadDown = BTN_DPAD_DOWN,
DPadLeft = BTN_DPAD_LEFT,
DPadRight = BTN_DPAD_RIGHT,
#[default]
Unknown = BTN_UNKNOWN,
}
impl Button {
pub fn is_action(self) -> bool {
use crate::Button::*;
matches!(self, South | East | North | West | C | Z)
}
pub fn is_trigger(self) -> bool {
use crate::Button::*;
matches!(
self,
LeftTrigger | LeftTrigger2 | RightTrigger | RightTrigger2
)
}
pub fn is_menu(self) -> bool {
use crate::Button::*;
matches!(self, Select | Start | Mode)
}
pub fn is_stick(self) -> bool {
use crate::Button::*;
matches!(self, LeftThumb | RightThumb)
}
pub fn is_dpad(self) -> bool {
use crate::Button::*;
matches!(self, DPadUp | DPadDown | DPadLeft | DPadRight)
}
pub fn to_nec(self) -> Option<Code> {
use gilrs_core::native_ev_codes as necs;
match self {
Button::South => Some(necs::BTN_SOUTH),
Button::East => Some(necs::BTN_EAST),
Button::North => Some(necs::BTN_NORTH),
Button::West => Some(necs::BTN_WEST),
Button::C => Some(necs::BTN_C),
Button::Z => Some(necs::BTN_Z),
Button::LeftTrigger => Some(necs::BTN_LT),
Button::LeftTrigger2 => Some(necs::BTN_LT2),
Button::RightTrigger => Some(necs::BTN_RT),
Button::RightTrigger2 => Some(necs::BTN_RT2),
Button::Select => Some(necs::BTN_SELECT),
Button::Start => Some(necs::BTN_START),
Button::Mode => Some(necs::BTN_MODE),
Button::LeftThumb => Some(necs::BTN_LTHUMB),
Button::RightThumb => Some(necs::BTN_RTHUMB),
Button::DPadUp => Some(necs::BTN_DPAD_UP),
Button::DPadDown => Some(necs::BTN_DPAD_DOWN),
Button::DPadLeft => Some(necs::BTN_DPAD_LEFT),
Button::DPadRight => Some(necs::BTN_DPAD_RIGHT),
_ => None,
}
.map(Code)
}
}
#[repr(u16)]
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
/// Gamepad's elements which state can be represented by value from -1.0 to 1.0.
///
/// ![Controller layout](https://gilrs-project.gitlab.io/gilrs/img/controller.svg)
pub enum Axis {
LeftStickX = AXIS_LSTICKX,
LeftStickY = AXIS_LSTICKY,
LeftZ = AXIS_LEFTZ,
RightStickX = AXIS_RSTICKX,
RightStickY = AXIS_RSTICKY,
RightZ = AXIS_RIGHTZ,
DPadX = AXIS_DPADX,
DPadY = AXIS_DPADY,
Unknown = AXIS_UNKNOWN,
}
impl Axis {
/// Returns true if axis is `LeftStickX`, `LeftStickY`, `RightStickX` or `RightStickY`.
pub fn is_stick(self) -> bool {
use crate::Axis::*;
matches!(self, LeftStickX | LeftStickY | RightStickX | RightStickY)
}
/// Returns the other axis from same element of gamepad, if any.
///
/// | input | output |
/// |-------------|-------------------|
/// |`LeftStickX` |`Some(LeftStickY)` |
/// |`LeftStickY` |`Some(LeftStickX)` |
/// |`RightStickX`|`Some(RightStickY)`|
/// |`RightStickY`|`Some(RightStickX)`|
/// |`DpadX` |`Some(DpadY)` |
/// |`DpadY` |`Some(DpadX)` |
/// | … |`None` |
pub fn second_axis(self) -> Option<Self> {
use crate::Axis::*;
match self {
LeftStickX => Some(LeftStickY),
LeftStickY => Some(LeftStickX),
RightStickX => Some(RightStickY),
RightStickY => Some(RightStickX),
DPadX => Some(DPadY),
DPadY => Some(DPadX),
_ => None,
}
}
}
/// Represents `Axis` or `Button`.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
pub enum AxisOrBtn {
Axis(Axis),
Btn(Button),
}
impl AxisOrBtn {
pub(crate) fn is_button(&self) -> bool {
matches!(self, AxisOrBtn::Btn(_))
}
}

230
vendor/gilrs/src/ev/state.rs vendored Normal file
View File

@@ -0,0 +1,230 @@
// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
use crate::ev::Code;
use fnv::FnvHashMap;
use std::collections::hash_map;
use std::iter::Iterator;
use std::time::SystemTime;
/// Cached gamepad state.
#[derive(Clone, Debug)]
pub struct GamepadState {
// Indexed by EvCode (nec)
buttons: FnvHashMap<Code, ButtonData>,
// Indexed by EvCode (nec)
axes: FnvHashMap<Code, AxisData>,
}
impl GamepadState {
pub(crate) fn new() -> Self {
GamepadState {
buttons: FnvHashMap::default(),
axes: FnvHashMap::default(),
}
}
/// Returns `true` if given button is pressed. Returns `false` if there is no information about
/// `btn` or it is not pressed.
pub fn is_pressed(&self, btn: Code) -> bool {
self.buttons
.get(&btn)
.map(|s| s.is_pressed())
.unwrap_or(false)
}
/// Returns value of `el` or 0.0 when there is no information about it. `el` can be either axis
/// or button.
pub fn value(&self, el: Code) -> f32 {
self.axes
.get(&el)
.map(|s| s.value())
.or_else(|| self.buttons.get(&el).map(|s| s.value()))
.unwrap_or(0.0)
}
/// Iterate over buttons data.
pub fn buttons(&self) -> ButtonDataIter<'_> {
ButtonDataIter(self.buttons.iter())
}
/// Iterate over axes data.
pub fn axes(&self) -> AxisDataIter<'_> {
AxisDataIter(self.axes.iter())
}
/// Returns button state and when it changed.
pub fn button_data(&self, btn: Code) -> Option<&ButtonData> {
self.buttons.get(&btn)
}
/// Returns axis state and when it changed.
pub fn axis_data(&self, axis: Code) -> Option<&AxisData> {
self.axes.get(&axis)
}
pub(crate) fn set_btn_pressed(
&mut self,
btn: Code,
pressed: bool,
counter: u64,
timestamp: SystemTime,
) {
let data = self.buttons.entry(btn).or_insert_with(|| {
ButtonData::new(
if pressed { 1.0 } else { 0.0 },
pressed,
false,
counter,
timestamp,
)
});
data.is_pressed = pressed;
data.is_repeating = false;
data.counter = counter;
data.last_event_ts = timestamp;
}
pub(crate) fn set_btn_repeating(&mut self, btn: Code, counter: u64, timestamp: SystemTime) {
let data = self
.buttons
.entry(btn)
.or_insert_with(|| ButtonData::new(1.0, true, true, counter, timestamp));
data.is_repeating = true;
data.counter = counter;
data.last_event_ts = timestamp;
}
pub(crate) fn set_btn_value(
&mut self,
btn: Code,
value: f32,
counter: u64,
timestamp: SystemTime,
) {
let data = self
.buttons
.entry(btn)
.or_insert_with(|| ButtonData::new(value, false, false, counter, timestamp));
data.value = value;
data.counter = counter;
data.last_event_ts = timestamp;
}
pub(crate) fn update_axis(&mut self, axis: Code, data: AxisData) {
self.axes.insert(axis, data);
}
}
/// Iterator over `ButtonData`.
pub struct ButtonDataIter<'a>(hash_map::Iter<'a, Code, ButtonData>);
/// Iterator over `AxisData`.
pub struct AxisDataIter<'a>(hash_map::Iter<'a, Code, AxisData>);
impl<'a> Iterator for ButtonDataIter<'a> {
type Item = (Code, &'a ButtonData);
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|(k, v)| (*k, v))
}
}
impl<'a> Iterator for AxisDataIter<'a> {
type Item = (Code, &'a AxisData);
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|(k, v)| (*k, v))
}
}
/// Information about button stored in `State`.
#[derive(Clone, Copy, Debug)]
pub struct ButtonData {
last_event_ts: SystemTime,
counter: u64,
value: f32,
is_pressed: bool,
is_repeating: bool,
}
impl ButtonData {
pub(crate) fn new(
value: f32,
pressed: bool,
repeating: bool,
counter: u64,
time: SystemTime,
) -> Self {
ButtonData {
last_event_ts: time,
counter,
value,
is_pressed: pressed,
is_repeating: repeating,
}
}
/// Returns `true` if button is pressed.
pub fn is_pressed(&self) -> bool {
self.is_pressed
}
/// Returns value of button.
pub fn value(&self) -> f32 {
self.value
}
/// Returns `true` if button is repeating.
pub fn is_repeating(&self) -> bool {
self.is_repeating
}
/// Returns value of counter when button state last changed.
pub fn counter(&self) -> u64 {
self.counter
}
/// Returns when button state last changed.
pub fn timestamp(&self) -> SystemTime {
self.last_event_ts
}
}
/// Information about axis stored in `State`.
#[derive(Clone, Copy, Debug)]
pub struct AxisData {
last_event_ts: SystemTime,
last_event_c: u64,
value: f32,
}
impl AxisData {
pub(crate) fn new(value: f32, counter: u64, time: SystemTime) -> Self {
AxisData {
last_event_ts: time,
last_event_c: counter,
value,
}
}
/// Returns value of axis.
pub fn value(&self) -> f32 {
self.value
}
/// Returns value of counter when axis value last changed.
pub fn counter(&self) -> u64 {
self.last_event_c
}
/// Returns when axis value last changed.
pub fn timestamp(&self) -> SystemTime {
self.last_event_ts
}
}

151
vendor/gilrs/src/ff/base_effect.rs vendored Normal file
View File

@@ -0,0 +1,151 @@
// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
use std::ops::Mul;
use super::time::Ticks;
/// Kind of [`BaseEffect`](struct.BaseEffect.html).
///
/// Currently base effect support only xinput model of force feedback, which means that gamepad
/// have weak and strong motor.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[non_exhaustive]
pub enum BaseEffectType {
Weak { magnitude: u16 },
Strong { magnitude: u16 },
}
impl BaseEffectType {
fn magnitude(&self) -> u16 {
match *self {
BaseEffectType::Weak { magnitude } => magnitude,
BaseEffectType::Strong { magnitude } => magnitude,
}
}
}
impl Mul<f32> for BaseEffectType {
type Output = BaseEffectType;
fn mul(self, rhs: f32) -> Self::Output {
let mg = (self.magnitude() as f32 * rhs) as u16;
match self {
BaseEffectType::Weak { .. } => BaseEffectType::Weak { magnitude: mg },
BaseEffectType::Strong { .. } => BaseEffectType::Strong { magnitude: mg },
}
}
}
impl Default for BaseEffectType {
fn default() -> Self {
BaseEffectType::Weak { magnitude: 0 }
}
}
/// Basic building block used to create more complex force feedback effects.
///
/// For each base effect you can specify it's type, for how long should it be played and it's
/// strength during playback.
#[derive(Copy, Clone, PartialEq, Debug, Default)]
pub struct BaseEffect {
/// Type of base effect.
pub kind: BaseEffectType,
/// Defines playback duration and delays between each repetition.
pub scheduling: Replay,
// TODO: maybe allow other f(t)?
/// Basic attenuation function.
pub envelope: Envelope,
}
impl BaseEffect {
/// Returns `Weak` or `Strong` after applying envelope.
pub(super) fn magnitude_at(&self, ticks: Ticks) -> BaseEffectType {
if let Some(wrapped) = self.scheduling.wrap(ticks) {
let att =
self.scheduling.at(wrapped) * self.envelope.at(wrapped, self.scheduling.play_for);
self.kind * att
} else {
self.kind * 0.0
}
}
}
// TODO: Image with "envelope"
#[derive(Copy, Clone, PartialEq, Debug, Default)]
/// Envelope shaped attenuation(time) function.
pub struct Envelope {
pub attack_length: Ticks,
pub attack_level: f32,
pub fade_length: Ticks,
pub fade_level: f32,
}
impl Envelope {
pub(super) fn at(&self, ticks: Ticks, dur: Ticks) -> f32 {
debug_assert!(self.fade_length < dur);
debug_assert!(self.attack_length + self.fade_length < dur);
if ticks < self.attack_length {
self.attack_level
+ ticks.0 as f32 * (1.0 - self.attack_level) / self.attack_length.0 as f32
} else if ticks + self.fade_length > dur {
1.0 + (ticks + self.fade_length - dur).0 as f32 * (self.fade_level - 1.0)
/ self.fade_length.0 as f32
} else {
1.0
}
}
}
/// Defines scheduling of the basic force feedback effect.
///
/// ```text
/// ____________ ____________ ____________
/// | | | | |
/// _______| |____________| |____________|
/// after play_for with_delay play_for with_delay play_for
/// ```
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct Replay {
/// Start playback `after` ticks after `Effect::play()` is called.
pub after: Ticks,
/// Playback duration.
pub play_for: Ticks,
/// If playback should be repeated delay it for `with_delay` ticks.
pub with_delay: Ticks,
}
impl Replay {
pub(super) fn at(&self, ticks: Ticks) -> f32 {
if ticks >= self.play_for {
0.0
} else {
1.0
}
}
/// Returns duration of effect calculated as `play_for + with_delay`.
pub fn dur(&self) -> Ticks {
self.play_for + self.with_delay
}
/// Returns `None` if effect hasn't started; or wrapped value
fn wrap(&self, ticks: Ticks) -> Option<Ticks> {
ticks.checked_sub(self.after).map(|t| t % self.dur())
}
}
impl Default for Replay {
fn default() -> Self {
Replay {
after: Ticks(0),
play_for: Ticks(1),
with_delay: Ticks(0),
}
}
}

378
vendor/gilrs/src/ff/effect_source.rs vendored Normal file
View File

@@ -0,0 +1,378 @@
// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
use std::error::Error;
use std::ops::{AddAssign, Mul};
use std::{fmt, mem, u16};
use crate::{Event, EventType, GamepadId};
use super::base_effect::{BaseEffect, BaseEffectType};
use super::time::{Repeat, Ticks};
use vec_map::VecMap;
/// Specifies how distance between effect source and listener attenuates effect.
///
/// They are based on
/// [OpenAL Specification](http://openal.org/documentation/openal-1.1-specification.pdf) (chapter
/// 3.4), but the best way to see how they differ is to run `ff_pos` example.
///
/// Make sure that all parameters are ≥ 0. Additionally `Linear` and `LinearClamped` models don't
/// like if `ref_distance == max_distance` while others would prefer `ref_distance > 0`.
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub enum DistanceModel {
/// Effect is not attenuated by distance.
#[default]
None,
/// Linear distance model.
Linear {
ref_distance: f32,
rolloff_factor: f32,
max_distance: f32,
},
/// Linear distance clamped model.
LinearClamped {
ref_distance: f32,
rolloff_factor: f32,
max_distance: f32,
},
/// Inverse distance model.
Inverse {
ref_distance: f32,
rolloff_factor: f32,
},
/// Inverse distance clamped model.
InverseClamped {
ref_distance: f32,
rolloff_factor: f32,
max_distance: f32,
},
/// Exponential distance model.
Exponential {
ref_distance: f32,
rolloff_factor: f32,
},
/// Exponential distance clamped model.
ExponentialClamped {
ref_distance: f32,
rolloff_factor: f32,
max_distance: f32,
},
}
impl DistanceModel {
fn attenuation(self, mut distance: f32) -> f32 {
// For now we will follow OpenAL[1] specification for distance models. See chapter 3.4 for
// more details.
//
// [1]: http://openal.org/documentation/openal-1.1-specification.pdf
match self {
DistanceModel::Linear {
ref_distance,
max_distance,
rolloff_factor,
} => {
distance = distance.min(max_distance);
1.0 - rolloff_factor * (distance - ref_distance) / (max_distance - ref_distance)
}
DistanceModel::LinearClamped {
ref_distance,
max_distance,
rolloff_factor,
} => {
distance = distance.max(ref_distance);
distance = distance.min(max_distance);
1.0 - rolloff_factor * (distance - ref_distance) / (max_distance - ref_distance)
}
DistanceModel::Inverse {
ref_distance,
rolloff_factor,
} => ref_distance / (ref_distance + rolloff_factor * (distance - ref_distance)),
DistanceModel::InverseClamped {
ref_distance,
max_distance,
rolloff_factor,
} => {
distance = distance.max(ref_distance);
distance = distance.min(max_distance);
ref_distance / (ref_distance + rolloff_factor * (distance - ref_distance))
}
DistanceModel::Exponential {
ref_distance,
rolloff_factor,
} => (distance / ref_distance).powf(-rolloff_factor),
DistanceModel::ExponentialClamped {
ref_distance,
max_distance,
rolloff_factor,
} => {
distance = distance.max(ref_distance);
distance = distance.min(max_distance);
(distance / ref_distance).powf(-rolloff_factor)
}
DistanceModel::None => 1.0,
}
}
pub(crate) fn validate(self) -> Result<(), DistanceModelError> {
let (ref_distance, rolloff_factor, max_distance) = match self {
DistanceModel::Inverse {
ref_distance,
rolloff_factor,
} => {
if ref_distance <= 0.0 {
return Err(DistanceModelError::InvalidModelParameter);
}
(ref_distance, rolloff_factor, 0.0)
}
DistanceModel::InverseClamped {
ref_distance,
max_distance,
rolloff_factor,
} => {
if ref_distance <= 0.0 {
return Err(DistanceModelError::InvalidModelParameter);
}
(ref_distance, rolloff_factor, max_distance)
}
DistanceModel::Linear {
ref_distance,
max_distance,
rolloff_factor,
} => {
if ref_distance == max_distance {
return Err(DistanceModelError::InvalidModelParameter);
}
(ref_distance, rolloff_factor, max_distance)
}
DistanceModel::LinearClamped {
ref_distance,
max_distance,
rolloff_factor,
} => {
if ref_distance == max_distance {
return Err(DistanceModelError::InvalidModelParameter);
}
(ref_distance, rolloff_factor, max_distance)
}
DistanceModel::Exponential {
ref_distance,
rolloff_factor,
} => {
if ref_distance <= 0.0 {
return Err(DistanceModelError::InvalidModelParameter);
}
(ref_distance, rolloff_factor, 0.0)
}
DistanceModel::ExponentialClamped {
ref_distance,
max_distance,
rolloff_factor,
} => {
if ref_distance <= 0.0 {
return Err(DistanceModelError::InvalidModelParameter);
}
(ref_distance, rolloff_factor, max_distance)
}
DistanceModel::None => (0.0, 0.0, 0.0),
};
if ref_distance < 0.0 {
Err(DistanceModelError::InvalidReferenceDistance)
} else if rolloff_factor < 0.0 {
Err(DistanceModelError::InvalidRolloffFactor)
} else if max_distance < 0.0 {
Err(DistanceModelError::InvalidMaxDistance)
} else {
Ok(())
}
}
}
/// Error that can be returned when passing [`DistanceModel`](struct.DistanceModel.html) with
/// invalid value.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum DistanceModelError {
/// Reference distance is < 0.
InvalidReferenceDistance,
/// Rolloff factor is < 0.
InvalidRolloffFactor,
/// Max distance is < 0.
InvalidMaxDistance,
/// Possible divide by zero
InvalidModelParameter,
}
impl Error for DistanceModelError {}
impl fmt::Display for DistanceModelError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
DistanceModelError::InvalidReferenceDistance => "reference distance is < 0",
DistanceModelError::InvalidRolloffFactor => "rolloff factor is < 0",
DistanceModelError::InvalidMaxDistance => "max distance is < 0",
DistanceModelError::InvalidModelParameter => "possible divide by zero",
};
f.write_str(s)
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub(super) enum EffectState {
Playing { since: Ticks },
Stopped,
}
#[derive(Clone, PartialEq, Debug)]
pub(crate) struct EffectSource {
base_effects: Vec<BaseEffect>,
// TODO: Use bitset
pub(super) devices: VecMap<()>,
pub(super) repeat: Repeat,
pub(super) distance_model: DistanceModel,
pub(super) position: [f32; 3],
pub(super) gain: f32,
pub(super) state: EffectState,
pub(super) completion_events: Vec<Event>,
}
impl EffectSource {
pub(super) fn new(
base_effects: Vec<BaseEffect>,
devices: VecMap<()>,
repeat: Repeat,
dist_model: DistanceModel,
position: [f32; 3],
gain: f32,
) -> Self {
EffectSource {
base_effects,
devices,
repeat,
distance_model: dist_model,
position,
gain,
state: EffectState::Stopped,
completion_events: vec![],
}
}
pub(super) fn combine_base_effects(&mut self, ticks: Ticks, actor_pos: [f32; 3]) -> Magnitude {
let ticks = match self.state {
EffectState::Playing { since } => {
debug_assert!(ticks >= since);
ticks - since
}
EffectState::Stopped => return Magnitude::zero(),
};
match self.repeat {
Repeat::For(max_dur) if ticks > max_dur => {
self.state = EffectState::Stopped;
self.devices.keys().for_each(|id| {
let event = Event::new(GamepadId(id), EventType::ForceFeedbackEffectCompleted);
self.completion_events.push(event);
});
}
_ => (),
}
let attenuation = self
.distance_model
.attenuation(self.position.distance(actor_pos))
* self.gain;
if attenuation < 0.05 {
return Magnitude::zero();
}
let mut final_magnitude = Magnitude::zero();
for effect in &self.base_effects {
match effect.magnitude_at(ticks) {
BaseEffectType::Strong { magnitude } => {
final_magnitude.strong = final_magnitude.strong.saturating_add(magnitude)
}
BaseEffectType::Weak { magnitude } => {
final_magnitude.weak = final_magnitude.weak.saturating_add(magnitude)
}
};
}
final_magnitude * attenuation
}
pub(super) fn flush_completion_events(&mut self) -> Vec<Event> {
mem::take(&mut self.completion_events)
}
}
/// (strong, weak) pair.
#[derive(Copy, Clone, Debug)]
pub(super) struct Magnitude {
pub strong: u16,
pub weak: u16,
}
impl Magnitude {
pub fn zero() -> Self {
Magnitude { strong: 0, weak: 0 }
}
}
impl Mul<f32> for Magnitude {
type Output = Magnitude;
fn mul(self, rhs: f32) -> Self::Output {
debug_assert!(rhs >= 0.0);
let strong = self.strong as f32 * rhs;
let strong = if strong > u16::MAX as f32 {
u16::MAX
} else {
strong as u16
};
let weak = self.weak as f32 * rhs;
let weak = if weak > u16::MAX as f32 {
u16::MAX
} else {
weak as u16
};
Magnitude { strong, weak }
}
}
impl AddAssign for Magnitude {
fn add_assign(&mut self, rhs: Magnitude) {
self.strong = self.strong.saturating_add(rhs.strong);
self.weak = self.weak.saturating_add(rhs.weak);
}
}
trait SliceVecExt {
type Base;
fn distance(self, from: Self) -> Self::Base;
}
impl SliceVecExt for [f32; 3] {
type Base = f32;
fn distance(self, from: Self) -> f32 {
((from[0] - self[0]).powi(2) + (from[1] - self[1]).powi(2) + (from[2] - self[2]).powi(2))
.sqrt()
}
}

458
vendor/gilrs/src/ff/mod.rs vendored Normal file
View File

@@ -0,0 +1,458 @@
// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
// This code is not used on wasm
#![cfg_attr(target_arch = "wasm32", allow(dead_code))]
//! Force feedback module.
//!
//! To use force feedback, you have to create one or more [`Effect`s](struct.Effect.html). Each
//! `Effect` contains one or more [`BasicEffect`s](struct.BasicEffect.html) and parameters that
//! describe effect's source, like it's position, gain or used
//! [`DistanceModel`](enum.DistanceModel.html). Final strength of effect is based on saturating sum
//! (to `u16::MAX`) of all base effects and time from the start of playback, attenuation from
//! distance between effect source and listener (represented by gamepad) and effect's gain.
//!
//! See also [`Gilrs::set_listener_position()`](../struct.Gilrs.html#method.set_listener_position)
//! and [`Gamepad::is_ff_supported()`](../struct.Gamepad.html#method.is_ff_supported).
//!
//! # Example
//!
//! ```rust
//! use gilrs::Gilrs;
//! use gilrs::ff::{EffectBuilder, Replay, BaseEffect, BaseEffectType, Ticks};
//!
//! let mut gilrs = Gilrs::new().unwrap();
//! let support_ff = gilrs
//! .gamepads()
//! .filter_map(|(id, gp)| if gp.is_ff_supported() { Some(id) } else { None })
//! .collect::<Vec<_>>();
//!
//! let duration = Ticks::from_ms(150);
//! let effect = EffectBuilder::new()
//! .add_effect(BaseEffect {
//! kind: BaseEffectType::Strong { magnitude: 60_000 },
//! scheduling: Replay { play_for: duration, with_delay: duration * 3, ..Default::default() },
//! envelope: Default::default(),
//! })
//! .add_effect(BaseEffect {
//! kind: BaseEffectType::Weak { magnitude: 60_000 },
//! scheduling: Replay { after: duration * 2, play_for: duration, with_delay: duration * 3 },
//! ..Default::default()
//! })
//! .gamepads(&support_ff)
//! .finish(&mut gilrs).unwrap();
//!
//! effect.play().unwrap();
//! ```
//!
//! See [`examples/ff_pos.rs`](https://gitlab.com/gilrs-project/gilrs/blob/v0.11.0/examples/ff_pos.rs) for
//! more advanced example.
mod base_effect;
mod effect_source;
pub(crate) mod server;
mod time;
pub use self::base_effect::{BaseEffect, BaseEffectType, Envelope, Replay};
pub use self::effect_source::{DistanceModel, DistanceModelError};
#[allow(unused_imports)]
pub(crate) use self::time::TICK_DURATION;
pub use self::time::{Repeat, Ticks};
use std::error::Error as StdError;
use std::hash::{Hash, Hasher};
use std::sync::mpsc::{SendError, Sender};
use std::{f32, fmt};
use self::effect_source::EffectSource;
use crate::ff::server::Message;
use crate::gamepad::{Gamepad, GamepadId, Gilrs};
use crate::utils;
use vec_map::VecMap;
/// Handle to force feedback effect.
///
/// `Effect` represents force feedback effect that can be played on one or more gamepads. It uses a
/// form of reference counting, so it can be cheaply cloned. To create new `Effect` use
/// [`EffectBuilder`](struct.EffectBuilder.html).
///
/// All methods on can return `Error::SendFailed` although it shouldn't normally happen.
pub struct Effect {
id: usize,
tx: Sender<Message>,
}
impl PartialEq for Effect {
fn eq(&self, other: &Effect) -> bool {
self.id == other.id
}
}
impl Eq for Effect {}
impl Hash for Effect {
fn hash<H: Hasher>(&self, state: &mut H) {
self.id.hash(state);
}
}
impl Clone for Effect {
fn clone(&self) -> Self {
let _ = self.tx.send(Message::HandleCloned { id: self.id });
Effect {
id: self.id,
tx: self.tx.clone(),
}
}
}
impl Drop for Effect {
fn drop(&mut self) {
let _ = self.tx.send(Message::HandleDropped { id: self.id });
}
}
impl Effect {
/// Plays effect on all associated gamepads.
pub fn play(&self) -> Result<(), Error> {
self.tx.send(Message::Play { id: self.id })?;
Ok(())
}
pub fn stop(&self) -> Result<(), Error> {
self.tx.send(Message::Stop { id: self.id })?;
Ok(())
}
/// Changes gamepads that are associated with effect. Effect will be only played on gamepads
/// from last call to this function.
///
/// # Errors
///
/// Returns `Error::Disconnected(id)` or `Error::FfNotSupported(id)` on first gamepad in `ids`
/// that is disconnected or doesn't support force feedback.
pub fn set_gamepads(&self, ids: &[GamepadId], gilrs: &Gilrs) -> Result<(), Error> {
let mut gamepads = VecMap::new();
for dev in ids.iter().cloned() {
if !gilrs
.connected_gamepad(dev)
.ok_or(Error::Disconnected(dev))?
.is_ff_supported()
{
return Err(Error::FfNotSupported(dev));
} else {
gamepads.insert(dev.0, ());
}
}
self.tx.send(Message::SetGamepads {
id: self.id,
gamepads,
})?;
Ok(())
}
/// Adds gamepad to the list of gamepads associated with effect.
///
/// # Errors
///
/// Returns `Error::Disconnected(id)` or `Error::FfNotSupported(id)` if gamepad is not connected
/// or does not support force feedback.
pub fn add_gamepad(&self, gamepad: &Gamepad<'_>) -> Result<(), Error> {
if !gamepad.is_connected() {
Err(Error::Disconnected(gamepad.id()))
} else if !gamepad.is_ff_supported() {
Err(Error::FfNotSupported(gamepad.id()))
} else {
self.tx.send(Message::AddGamepad {
id: self.id,
gamepad_id: gamepad.id(),
})?;
Ok(())
}
}
/// Changes what should happen to effect when it ends.
pub fn set_repeat(&self, repeat: Repeat) -> Result<(), Error> {
self.tx.send(Message::SetRepeat {
id: self.id,
repeat,
})?;
Ok(())
}
/// Changes distance model associated with effect.
///
/// # Errors
///
/// Returns `Error::InvalidDistanceModel` if `model` is not valid. See
/// [`DistanceModel`](enum.DistanceModelError.html) for details.
pub fn set_distance_model(&self, model: DistanceModel) -> Result<(), Error> {
model.validate()?;
self.tx
.send(Message::SetDistanceModel { id: self.id, model })?;
Ok(())
}
/// Changes position of the source of effect.
pub fn set_position<Vec3f: Into<[f32; 3]>>(&self, position: Vec3f) -> Result<(), Error> {
let position = position.into();
self.tx.send(Message::SetPosition {
id: self.id,
position,
})?;
Ok(())
}
/// Changes gain of the effect. `gain` will be clamped to \[0.0, f32::MAX\].
pub fn set_gain(&self, gain: f32) -> Result<(), Error> {
let gain = utils::clamp(gain, 0.0, f32::MAX);
self.tx.send(Message::SetGain { id: self.id, gain })?;
Ok(())
}
}
/// Creates new [`Effect`](struct.Effect.html).
#[derive(Clone, PartialEq, Debug)]
pub struct EffectBuilder {
base_effects: Vec<BaseEffect>,
devices: VecMap<()>,
repeat: Repeat,
dist_model: DistanceModel,
position: [f32; 3],
gain: f32,
}
impl EffectBuilder {
/// Creates new builder with following defaults: no gamepads, no base effects, repeat set to
/// infinitely, no distance model, position in (0.0, 0.0, 0.0) and gain 1.0. Use `finish()` to
/// create new effect.
pub fn new() -> Self {
EffectBuilder {
base_effects: Vec::new(),
devices: VecMap::new(),
repeat: Repeat::Infinitely,
dist_model: DistanceModel::None,
position: [0.0, 0.0, 0.0],
gain: 1.0,
}
}
/// Adds new [`BaseEffect`](struct.BaseEffect.html).
pub fn add_effect(&mut self, effect: BaseEffect) -> &mut Self {
self.base_effects.push(effect);
self
}
/// Changes gamepads that are associated with effect. Effect will be only played on gamepads
/// from last call to this function.
pub fn gamepads(&mut self, ids: &[GamepadId]) -> &mut Self {
for dev in ids {
self.devices.insert(dev.0, ());
}
self
}
/// Adds gamepad to the list of gamepads associated with effect.
pub fn add_gamepad(&mut self, gamepad: &Gamepad<'_>) -> &mut Self {
self.devices.insert(gamepad.id().0, ());
self
}
/// Changes what should happen to effect when it ends.
pub fn repeat(&mut self, repeat: Repeat) -> &mut Self {
self.repeat = repeat;
self
}
/// Changes distance model associated with effect.
pub fn distance_model(&mut self, model: DistanceModel) -> &mut Self {
self.dist_model = model;
self
}
/// Changes position of the source of effect.
pub fn position<Vec3f: Into<[f32; 3]>>(&mut self, position: Vec3f) -> &mut Self {
self.position = position.into();
self
}
/// Changes gain of the effect. `gain` will be clamped to \[0.0, f32::MAX\].
pub fn gain(&mut self, gain: f32) -> &mut Self {
self.gain = utils::clamp(gain, 0.0, f32::MAX);
self
}
/// Validates all parameters and creates new effect.
///
/// # Errors
///
/// Returns `Error::Disconnected(id)` or `Error::FfNotSupported(id)` on first gamepad in `ids`
/// that is disconnected or doesn't support force feedback.
///
/// Returns `Error::InvalidDistanceModel` if `model` is not valid. See
/// [`DistanceModel`](enum.DistanceModelError.html) for details.
pub fn finish(&mut self, gilrs: &mut Gilrs) -> Result<Effect, Error> {
for (dev, _) in &self.devices {
let dev = GamepadId(dev);
if !gilrs
.connected_gamepad(dev)
.ok_or(Error::Disconnected(dev))?
.is_ff_supported()
{
return Err(Error::FfNotSupported(dev));
}
}
self.dist_model.validate()?;
let effect = EffectSource::new(
self.base_effects.clone(),
self.devices.clone(),
self.repeat,
self.dist_model,
self.position,
self.gain,
);
let id = gilrs.next_ff_id();
let tx = gilrs.ff_sender();
tx.send(Message::Create {
id,
effect: Box::new(effect),
})?;
Ok(Effect { id, tx: tx.clone() })
}
}
impl Default for EffectBuilder {
fn default() -> Self {
Self::new()
}
}
/// Basic error type in force feedback module.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum Error {
/// Force feedback is not supported by device with this ID
FfNotSupported(GamepadId),
/// Device is not connected
Disconnected(GamepadId),
/// Distance model is invalid.
InvalidDistanceModel(DistanceModelError),
/// The other end of channel was dropped.
SendFailed,
/// Unexpected error has occurred
Other,
}
impl StdError for Error {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
match self {
Error::InvalidDistanceModel(m) => Some(m),
_ => None,
}
}
}
impl fmt::Display for Error {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
let sbuf;
let s = match self {
Error::FfNotSupported(id) => {
sbuf = format!(
"force feedback is not supported by device with id {}.",
id.0
);
sbuf.as_ref()
}
Error::Disconnected(id) => {
sbuf = format!("device with id {} is not connected.", id.0);
sbuf.as_ref()
}
Error::InvalidDistanceModel(_) => "distance model is invalid",
Error::SendFailed => "receiving end of a channel is disconnected.",
Error::Other => "unespected error has occurred.",
};
fmt.write_str(s)
}
}
impl<T> From<SendError<T>> for Error {
fn from(_: SendError<T>) -> Self {
Error::SendFailed
}
}
impl From<DistanceModelError> for Error {
fn from(f: DistanceModelError) -> Self {
Error::InvalidDistanceModel(f)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn envelope() {
let env = Envelope {
attack_length: Ticks(10),
attack_level: 0.2,
fade_length: Ticks(10),
fade_level: 0.2,
};
let dur = Ticks(40);
assert_eq!(env.at(Ticks(0), dur), 0.2);
assert_eq!(env.at(Ticks(5), dur), 0.6);
assert_eq!(env.at(Ticks(10), dur), 1.0);
assert_eq!(env.at(Ticks(20), dur), 1.0);
assert_eq!(env.at(Ticks(30), dur), 1.0);
assert_eq!(env.at(Ticks(35), dur), 0.6);
assert_eq!(env.at(Ticks(40), dur), 0.19999999);
}
#[test]
fn envelope_default() {
let env = Envelope::default();
let dur = Ticks(40);
assert_eq!(env.at(Ticks(0), dur), 1.0);
assert_eq!(env.at(Ticks(20), dur), 1.0);
assert_eq!(env.at(Ticks(40), dur), 1.0);
}
#[test]
fn replay() {
let replay = Replay {
after: Ticks(10),
play_for: Ticks(50),
with_delay: Ticks(20),
};
assert_eq!(replay.at(Ticks(0)), 1.0);
assert_eq!(replay.at(Ticks(9)), 1.0);
assert_eq!(replay.at(Ticks(10)), 1.0);
assert_eq!(replay.at(Ticks(30)), 1.0);
assert_eq!(replay.at(Ticks(59)), 0.0);
assert_eq!(replay.at(Ticks(60)), 0.0);
assert_eq!(replay.at(Ticks(70)), 0.0);
}
}

319
vendor/gilrs/src/ff/server.rs vendored Normal file
View File

@@ -0,0 +1,319 @@
// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
use super::effect_source::{DistanceModel, EffectSource, EffectState, Magnitude};
use super::time::{Repeat, Ticks, TICK_DURATION};
use std::ops::{Deref, DerefMut};
use std::sync::mpsc::{self, Receiver, Sender};
use std::thread;
use std::time::{Duration, Instant};
use crate::gamepad::GamepadId;
use crate::Event;
use gilrs_core::FfDevice;
use vec_map::VecMap;
#[derive(Debug)]
pub(crate) enum Message {
Create {
id: usize,
effect: Box<EffectSource>,
},
HandleCloned {
id: usize,
},
HandleDropped {
id: usize,
},
Play {
id: usize,
},
Stop {
id: usize,
},
Open {
id: usize,
device: FfDevice,
},
Close {
id: usize,
},
SetListenerPosition {
id: usize,
position: [f32; 3],
},
SetGamepads {
id: usize,
gamepads: VecMap<()>,
},
AddGamepad {
id: usize,
gamepad_id: GamepadId,
},
SetRepeat {
id: usize,
repeat: Repeat,
},
SetDistanceModel {
id: usize,
model: DistanceModel,
},
SetPosition {
id: usize,
position: [f32; 3],
},
SetGain {
id: usize,
gain: f32,
},
}
pub(crate) enum FfMessage {
EffectCompleted { event: Event },
}
impl Message {
// Whether to use trace level logging or debug
fn use_trace_level(&self) -> bool {
use self::Message::*;
matches!(
self,
&SetListenerPosition { .. } | &HandleCloned { .. } | &HandleDropped { .. }
)
}
}
#[derive(Debug)]
struct Device {
inner: FfDevice,
position: [f32; 3],
}
struct Effect {
source: EffectSource,
/// Number of created effect's handles.
count: usize,
}
impl Effect {
fn inc(&mut self) -> usize {
self.count += 1;
self.count
}
fn dec(&mut self) -> usize {
self.count -= 1;
self.count
}
}
impl From<EffectSource> for Effect {
fn from(source: EffectSource) -> Self {
Effect { source, count: 1 }
}
}
impl Deref for Effect {
type Target = EffectSource;
fn deref(&self) -> &Self::Target {
&self.source
}
}
impl DerefMut for Effect {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.source
}
}
impl From<FfDevice> for Device {
fn from(inner: FfDevice) -> Self {
Device {
inner,
position: [0.0, 0.0, 0.0],
}
}
}
pub(crate) fn run(tx: Sender<FfMessage>, rx: Receiver<Message>) {
let mut effects = VecMap::<Effect>::new();
let mut devices = VecMap::<Device>::new();
let sleep_dur = Duration::from_millis(TICK_DURATION.into());
let mut tick = Ticks(0);
let mut completion_events = Vec::<Event>::new();
loop {
let t1 = Instant::now();
while let Ok(ev) = rx.try_recv() {
if ev.use_trace_level() {
trace!("New ff event: {:?}", ev);
} else {
debug!("New ff event: {:?}", ev);
}
match ev {
Message::Create { id, effect } => {
effects.insert(id, (*effect).into());
}
Message::Play { id } => {
if let Some(effect) = effects.get_mut(id) {
effect.source.state = EffectState::Playing { since: tick }
} else {
error!("{:?} with wrong ID", ev);
}
}
Message::Stop { id } => {
if let Some(effect) = effects.get_mut(id) {
effect.source.state = EffectState::Stopped
} else {
error!("{:?} with wrong ID", ev);
}
}
Message::Open { id, device } => {
devices.insert(id, device.into());
}
Message::Close { id } => {
devices.remove(id);
}
Message::SetListenerPosition { id, position } => {
if let Some(device) = devices.get_mut(id) {
device.position = position;
} else {
error!("{:?} with wrong ID", ev);
}
}
Message::HandleCloned { id } => {
if let Some(effect) = effects.get_mut(id) {
effect.inc();
} else {
error!("{:?} with wrong ID", ev);
}
}
Message::HandleDropped { id } => {
let mut drop = false;
if let Some(effect) = effects.get_mut(id) {
if effect.dec() == 0 {
drop = true;
}
} else {
error!("{:?} with wrong ID", ev);
}
if drop {
effects.remove(id);
}
}
Message::SetGamepads { id, gamepads } => {
if let Some(eff) = effects.get_mut(id) {
eff.source.devices = gamepads;
} else {
error!("Invalid effect id {} when changing gamepads.", id);
}
}
Message::AddGamepad { id, gamepad_id } => {
if let Some(eff) = effects.get_mut(id) {
eff.source.devices.insert(gamepad_id.0, ());
} else {
error!("Invalid effect id {} when changing gamepads.", id);
}
}
Message::SetRepeat { id, repeat } => {
if let Some(eff) = effects.get_mut(id) {
eff.source.repeat = repeat;
} else {
error!("Invalid effect id {} when changing repeat mode.", id);
}
}
Message::SetDistanceModel { id, model } => {
if let Some(eff) = effects.get_mut(id) {
eff.source.distance_model = model;
} else {
error!("Invalid effect id {} when changing distance model.", id);
}
}
Message::SetPosition { id, position } => {
if let Some(eff) = effects.get_mut(id) {
eff.source.position = position;
} else {
error!("Invalid effect id {}.", id);
}
}
Message::SetGain { id, gain } => {
if let Some(eff) = effects.get_mut(id) {
eff.source.gain = gain;
} else {
error!("Invalid effect id {} when changing effect gain.", id);
}
}
}
}
combine_and_play(&mut effects, &mut devices, tick, &mut completion_events);
completion_events.iter().for_each(|ev| {
let _ = tx.send(FfMessage::EffectCompleted { event: *ev });
});
completion_events.clear();
let dur = Instant::now().duration_since(t1);
if dur > sleep_dur {
// TODO: Should we add dur - sleep_dur to next iteration's dur?
warn!(
"One iteration of a force feedback loop took more than {}ms!",
TICK_DURATION
);
} else {
thread::sleep(sleep_dur - dur);
}
tick.inc();
}
}
pub(crate) fn init() -> (Sender<Message>, Receiver<FfMessage>) {
let (tx, _rx) = mpsc::channel();
let (_tx2, rx2) = mpsc::channel();
// Wasm doesn't support threads and force feedback
#[cfg(not(target_arch = "wasm32"))]
std::thread::Builder::new()
.name("gilrs".to_owned())
.spawn(move || run(_tx2, _rx))
.expect("failed to spawn thread");
(tx, rx2)
}
fn combine_and_play(
effects: &mut VecMap<Effect>,
devices: &mut VecMap<Device>,
tick: Ticks,
completion_events: &mut Vec<Event>,
) {
for (dev_id, dev) in devices {
let mut magnitude = Magnitude::zero();
for (_, ref mut effect) in effects.iter_mut() {
if effect.devices.contains_key(dev_id) {
magnitude += effect.combine_base_effects(tick, dev.position);
completion_events.extend(effect.flush_completion_events());
}
}
trace!(
"({:?}) Setting ff state of {:?} to {:?}",
tick,
dev,
magnitude
);
dev.inner.set_ff_state(
magnitude.strong,
magnitude.weak,
Duration::from_millis(u64::from(TICK_DURATION) * 2),
);
}
}

114
vendor/gilrs/src/ff/time.rs vendored Normal file
View File

@@ -0,0 +1,114 @@
// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
use std::ops::{Add, AddAssign, Mul, MulAssign, Rem, Sub, SubAssign};
use std::time::Duration;
use crate::utils;
pub(crate) const TICK_DURATION: u32 = 50;
/// Represents duration.
///
/// This type is only useful as input parameter for other functions in force feedback module. To
/// create it, use `from_ms()` method. Keep in mind that `Ticks` **is not precise** representation
/// of time.
///
/// # Example
///
/// ```rust
/// use gilrs::ff::Ticks;
/// use std::time::Duration;
///
/// let t1 = Ticks::from_ms(110);
/// let t2 = Ticks::from(Duration::from_millis(130));
///
/// /// `Ticks` is not precise.
/// assert_eq!(t1, t2);
/// ```
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Default)]
pub struct Ticks(pub(super) u32);
impl Ticks {
pub fn from_ms(dur: u32) -> Self {
Ticks(utils::ceil_div(dur, TICK_DURATION))
}
pub(super) fn inc(&mut self) {
self.0 += 1
}
pub(super) fn checked_sub(self, rhs: Ticks) -> Option<Ticks> {
self.0.checked_sub(rhs.0).map(Ticks)
}
}
impl From<Duration> for Ticks {
fn from(dur: Duration) -> Self {
Ticks::from_ms(dur.as_secs() as u32 * 1000 + dur.subsec_millis())
}
}
impl Add for Ticks {
type Output = Ticks;
fn add(self, rhs: Ticks) -> Self::Output {
Ticks(self.0 + rhs.0)
}
}
impl AddAssign for Ticks {
fn add_assign(&mut self, rhs: Ticks) {
self.0 += rhs.0
}
}
impl Sub for Ticks {
type Output = Ticks;
fn sub(self, rhs: Ticks) -> Self::Output {
Ticks(self.0 - rhs.0)
}
}
impl SubAssign for Ticks {
fn sub_assign(&mut self, rhs: Ticks) {
self.0 -= rhs.0
}
}
impl Mul<u32> for Ticks {
type Output = Ticks;
fn mul(self, rhs: u32) -> Self::Output {
Ticks(self.0 * rhs)
}
}
impl MulAssign<u32> for Ticks {
fn mul_assign(&mut self, rhs: u32) {
self.0 *= rhs;
}
}
impl Rem for Ticks {
type Output = Ticks;
fn rem(self, rhs: Ticks) -> Self::Output {
Ticks(self.0 % rhs.0)
}
}
/// Describes how long effect should be played.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum Repeat {
/// Play effect until stop() is called.
#[default]
Infinitely,
/// Play effect for specified time.
For(Ticks),
}

1251
vendor/gilrs/src/gamepad.rs vendored Normal file

File diff suppressed because it is too large Load Diff

128
vendor/gilrs/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,128 @@
// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
//! GilRs - Game Input Library for Rust
//! ===================================
//!
//! GilRs abstract platform specific APIs to provide unified interfaces for working with gamepads.
//!
//! Main features:
//!
//! - Unified gamepad layout—buttons and axes are represented by familiar names
//! - Support for SDL2 mappings including `SDL_GAMECONTROLLERCONFIG` environment
//! variable which Steam uses
//! - Hotplugging—GilRs will try to assign new IDs for new gamepads and reuse same
//! ID for gamepads which reconnected
//! - Force feedback (rumble)
//! - Power information (is gamepad wired, current battery status)
//!
//! Example
//! -------
//!
//! ```
//! use gilrs::{Gilrs, Button, Event};
//!
//! let mut gilrs = Gilrs::new().unwrap();
//!
//! // Iterate over all connected gamepads
//! for (_id, gamepad) in gilrs.gamepads() {
//! println!("{} is {:?}", gamepad.name(), gamepad.power_info());
//! }
//!
//! let mut active_gamepad = None;
//!
//! loop {
//! // Examine new events
//! while let Some(Event { id, event, time, .. }) = gilrs.next_event() {
//! println!("{:?} New event from {}: {:?}", time, id, event);
//! active_gamepad = Some(id);
//! }
//!
//! // You can also use cached gamepad state
//! if let Some(gamepad) = active_gamepad.map(|id| gilrs.gamepad(id)) {
//! if gamepad.is_pressed(Button::South) {
//! println!("Button South is pressed (XBox - A, PS - X)");
//! }
//! }
//! # break;
//! }
//! ```
//!
//! Supported features
//! ------------------
//!
//! | | Input | Hotplugging | Force feedback |
//! |------------------|:-----:|:-----------:|:--------------:|
//! | Linux/BSD (evdev)| ✓ | ✓ | ✓ |
//! | Windows (XInput) | ✓ | ✓ | ✓ |
//! | OS X | ✓ | ✓ | ✕ |
//! | Wasm | ✓ | ✓ | n/a |
//! | Android | ✕ | ✕ | ✕ |
//!
//! Controller layout
//! -----------------
//!
//! ![Controller layout](https://gilrs-project.gitlab.io/gilrs/img/controller.svg)
//! [original image by nicefrog](http://opengameart.org/content/generic-gamepad-template)
//!
//! Mappings
//! --------
//!
//! GilRs use SDL-compatible controller mappings to fix on Linux legacy drivers that doesn't follow
//! [Linux Gamepad API](https://www.kernel.org/doc/Documentation/input/gamepad.txt) and to provide
//! unified button layout for platforms that doesn't make any guarantees about it. The main source
//! is [SDL_GameControllerDB](https://github.com/gabomdq/SDL_GameControllerDB), but library also
//! support loading mappings from environment variable `SDL_GAMECONTROLLERCONFIG` (which Steam
//! use).
//!
//! Cargo features
//! --------------
//!
//! - `serde-serialize` - enable deriving of serde's `Serialize` and `Deserialize` for
//! various types.
//!
//! Platform specific notes
//! ======================
//!
//! Linux/BSD (evdev)
//! -----
//!
//! With evdev, GilRs read (and write, in case of force feedback) directly from appropriate
//! `/dev/input/event*` file. This mean that user have to have read and write access to this file.
//! On most distros it shouldn't be a problem, but if it is, you will have to create udev rule.
//! On FreeBSD generic HID gamepads use hgame(4) and special use Linux driver via `webcamd`.
//!
//! To build GilRs, you will need pkg-config and libudev .pc file. On some distributions this file
//! is packaged in separate archive (e.g., `libudev-dev` in Debian, `libudev-devd` in FreeBSD).
//!
//! Wasm
//! ----
//!
//! Wasm implementation uses stdweb, or wasm-bindgen with the wasm-bindgen feature.
//! For stdweb, you will need [cargo-web](https://github.com/koute/cargo-web) to build gilrs for
//! wasm32-unknown-unknown. For wasm-bindgen, you will need the wasm-bindgen cli or a tool like
//! [wasm-pack](https://rustwasm.github.io/wasm-pack/installer/).
//! Unlike other platforms, events are only generated when you call `Gilrs::next_event()`.
#[macro_use]
extern crate log;
mod constants;
mod gamepad;
mod mapping;
mod utils;
pub mod ev;
pub mod ff;
pub use crate::ev::filter::Filter;
pub use crate::ev::{Axis, Button, Event, EventType};
pub use crate::gamepad::{
ConnectedGamepadsIterator, Error, Gamepad, GamepadId, Gilrs, GilrsBuilder, MappingSource,
PowerInfo,
};
pub use crate::mapping::{MappingData as Mapping, MappingError};

731
vendor/gilrs/src/mapping/mod.rs vendored Normal file
View File

@@ -0,0 +1,731 @@
// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
#![cfg_attr(target_os = "windows", allow(dead_code))]
mod parser;
use crate::ev::{self, Axis, AxisOrBtn, Button};
use crate::utils::PATH_SEPARATOR;
use gilrs_core::native_ev_codes as nec;
use gilrs_core::EvCode;
use std::collections::HashMap;
use std::env;
use std::error::Error;
use std::fmt::{Display, Formatter, Result as FmtResult, Write as _};
use fnv::FnvHashMap;
use uuid::Uuid;
use vec_map::VecMap;
use self::parser::{Error as ParserError, ErrorKind as ParserErrorKind, Parser, Token};
/// Platform name used by SDL mappings
#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd"))]
const SDL_PLATFORM_NAME: &str = "Linux";
#[cfg(target_os = "macos")]
const SDL_PLATFORM_NAME: &str = "Mac OS X";
#[cfg(target_os = "windows")]
const SDL_PLATFORM_NAME: &str = "Windows";
#[cfg(all(
not(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd")),
not(target_os = "macos"),
not(target_os = "windows")
))]
const SDL_PLATFORM_NAME: &str = "Unknown";
#[derive(Debug)]
#[cfg_attr(test, derive(PartialEq))]
/// Store mappings from one `EvCode` (`u16`) to another.
///
/// This struct is internal, `MappingData` is exported in public interface as `Mapping`.
pub struct Mapping {
mappings: FnvHashMap<EvCode, AxisOrBtn>,
name: String,
default: bool,
hats_mapped: u8,
}
impl Mapping {
pub fn new() -> Self {
Mapping {
mappings: FnvHashMap::default(),
name: String::new(),
default: false,
hats_mapped: 0,
}
}
pub fn default(gamepad: &gilrs_core::Gamepad) -> Self {
use self::Axis as Ax;
use self::AxisOrBtn::*;
macro_rules! fnv_map {
( $( $key:expr => $elem:expr ),* ) => {
{
let mut map = FnvHashMap::default();
$(
map.insert($key, $elem);
)*
map
}
};
}
let mut mappings = fnv_map![
nec::BTN_SOUTH => Btn(Button::South),
nec::BTN_EAST => Btn(Button::East),
nec::BTN_C => Btn(Button::C),
nec::BTN_NORTH => Btn(Button::North),
nec::BTN_WEST => Btn(Button::West),
nec::BTN_Z => Btn(Button::Z),
nec::BTN_LT => Btn(Button::LeftTrigger),
nec::BTN_RT => Btn(Button::RightTrigger),
nec::BTN_LT2 => Btn(Button::LeftTrigger2),
nec::BTN_RT2 => Btn(Button::RightTrigger2),
nec::BTN_SELECT => Btn(Button::Select),
nec::BTN_START => Btn(Button::Start),
nec::BTN_MODE => Btn(Button::Mode),
nec::BTN_LTHUMB => Btn(Button::LeftThumb),
nec::BTN_RTHUMB => Btn(Button::RightThumb),
nec::BTN_DPAD_UP => Btn(Button::DPadUp),
nec::BTN_DPAD_DOWN => Btn(Button::DPadDown),
nec::BTN_DPAD_LEFT => Btn(Button::DPadLeft),
nec::BTN_DPAD_RIGHT => Btn(Button::DPadRight),
nec::AXIS_LT => Btn(Button::LeftTrigger),
nec::AXIS_RT => Btn(Button::RightTrigger),
nec::AXIS_LT2 => Btn(Button::LeftTrigger2),
nec::AXIS_RT2 => Btn(Button::RightTrigger2),
nec::AXIS_LSTICKX => Axis(Ax::LeftStickX),
nec::AXIS_LSTICKY => Axis(Ax::LeftStickY),
nec::AXIS_LEFTZ => Axis(Ax::LeftZ),
nec::AXIS_RSTICKX => Axis(Ax::RightStickX),
nec::AXIS_RSTICKY => Axis(Ax::RightStickY),
nec::AXIS_RIGHTZ => Axis(Ax::RightZ),
nec::AXIS_DPADX => Axis(Ax::DPadX),
nec::AXIS_DPADY => Axis(Ax::DPadY)
];
// Remove all mappings that don't have corresponding element in gamepad. Partial fix to #83
let axes = [
nec::AXIS_DPADX,
nec::AXIS_DPADY,
nec::AXIS_LEFTZ,
nec::AXIS_LSTICKX,
nec::AXIS_LSTICKY,
nec::AXIS_RSTICKX,
nec::AXIS_RSTICKY,
nec::AXIS_LT,
nec::AXIS_LT2,
nec::AXIS_RT,
nec::AXIS_RT2,
nec::AXIS_RIGHTZ,
];
let btns = [
nec::BTN_SOUTH,
nec::BTN_NORTH,
nec::BTN_WEST,
nec::BTN_WEST,
nec::BTN_C,
nec::BTN_Z,
nec::BTN_LT,
nec::BTN_LT2,
nec::BTN_RT,
nec::BTN_RT2,
nec::BTN_SELECT,
nec::BTN_START,
nec::BTN_MODE,
nec::BTN_LTHUMB,
nec::BTN_RTHUMB,
nec::BTN_DPAD_DOWN,
nec::BTN_DPAD_LEFT,
nec::BTN_DPAD_RIGHT,
nec::BTN_DPAD_UP,
];
for axis in &axes {
if !gamepad.axes().contains(axis) {
mappings.remove(axis);
}
}
for btn in &btns {
if !gamepad.buttons().contains(btn) {
mappings.remove(btn);
}
}
Mapping {
mappings,
name: String::new(),
default: true,
hats_mapped: 0,
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn from_data(
data: &MappingData,
buttons: &[EvCode],
axes: &[EvCode],
name: &str,
uuid: Uuid,
) -> Result<(Self, String), MappingError> {
use crate::constants::*;
if !Self::is_name_valid(name) {
return Err(MappingError::InvalidName);
}
let mut mappings = FnvHashMap::default();
let mut sdl_mappings = format!("{},{},", uuid.as_simple(), name);
{
let mut add_button = |ident, ev_code, mapped_btn| {
Self::add_button(
ident,
ev_code,
mapped_btn,
buttons,
&mut sdl_mappings,
&mut mappings,
)
};
for (button, &ev_code) in &data.buttons {
match button as u16 {
BTN_SOUTH => add_button("a", ev_code, Button::South)?,
BTN_EAST => add_button("b", ev_code, Button::East)?,
BTN_WEST => add_button("x", ev_code, Button::West)?,
BTN_NORTH => add_button("y", ev_code, Button::North)?,
BTN_LT => add_button("leftshoulder", ev_code, Button::LeftTrigger)?,
BTN_RT => add_button("rightshoulder", ev_code, Button::RightTrigger)?,
BTN_LT2 => add_button("lefttrigger", ev_code, Button::LeftTrigger2)?,
BTN_RT2 => add_button("righttrigger", ev_code, Button::RightTrigger2)?,
BTN_SELECT => add_button("back", ev_code, Button::Select)?,
BTN_START => add_button("start", ev_code, Button::Start)?,
BTN_MODE => add_button("guide", ev_code, Button::Mode)?,
BTN_LTHUMB => add_button("leftstick", ev_code, Button::LeftThumb)?,
BTN_RTHUMB => add_button("rightstick", ev_code, Button::RightThumb)?,
BTN_DPAD_UP => add_button("dpup", ev_code, Button::DPadUp)?,
BTN_DPAD_DOWN => add_button("dpdown", ev_code, Button::DPadDown)?,
BTN_DPAD_LEFT => add_button("dpleft", ev_code, Button::DPadLeft)?,
BTN_DPAD_RIGHT => add_button("dpright", ev_code, Button::DPadRight)?,
BTN_C => add_button("c", ev_code, Button::C)?,
BTN_Z => add_button("z", ev_code, Button::Z)?,
BTN_UNKNOWN => return Err(MappingError::UnknownElement),
_ => unreachable!(),
}
}
}
{
let mut add_axis = |ident, ev_code, mapped_axis| {
Self::add_axis(
ident,
ev_code,
mapped_axis,
axes,
&mut sdl_mappings,
&mut mappings,
)
};
for (axis, &ev_code) in &data.axes {
match axis as u16 {
AXIS_LSTICKX => add_axis("leftx", ev_code, Axis::LeftStickX)?,
AXIS_LSTICKY => add_axis("lefty", ev_code, Axis::LeftStickY)?,
AXIS_RSTICKX => add_axis("rightx", ev_code, Axis::RightStickX)?,
AXIS_RSTICKY => add_axis("righty", ev_code, Axis::RightStickY)?,
AXIS_LEFTZ => add_axis("leftz", ev_code, Axis::LeftZ)?,
AXIS_RIGHTZ => add_axis("rightz", ev_code, Axis::RightZ)?,
AXIS_UNKNOWN => return Err(MappingError::UnknownElement),
_ => unreachable!(),
}
}
}
let mapping = Mapping {
mappings,
name: name.to_owned(),
default: false,
hats_mapped: 0,
};
Ok((mapping, sdl_mappings))
}
pub fn parse_sdl_mapping(
line: &str,
buttons: &[EvCode],
axes: &[EvCode],
) -> Result<Self, ParseSdlMappingError> {
let mut mapping = Mapping::new();
let mut parser = Parser::new(line);
let mut uuid: Option<Uuid> = None;
while let Some(token) = parser.next_token() {
if let Err(ref e) = token {
if e.kind() == &ParserErrorKind::EmptyValue {
continue;
}
}
let token = token?;
match token {
Token::Platform(platform) => {
if platform != SDL_PLATFORM_NAME {
warn!("Mappings for different platform {}", platform);
}
}
Token::Uuid(v) => uuid = Some(v),
Token::Name(name) => mapping.name = name.to_owned(),
Token::AxisMapping { from, to, .. } => {
let axis = axes.get(from as usize).cloned();
if let Some(axis) = axis {
mapping.mappings.insert(axis, to);
} else {
warn!(
"SDL-mapping {} {}: Unknown axis a{}",
uuid.unwrap(),
mapping.name,
from
)
}
}
Token::ButtonMapping { from, to, .. } => {
let btn = buttons.get(from as usize).cloned();
if let Some(btn) = btn {
mapping.mappings.insert(btn, to);
} else {
warn!(
"SDL-mapping {} {}: Unknown button b{}",
uuid.unwrap(),
mapping.name,
from
)
}
}
Token::HatMapping {
hat, direction, to, ..
} => {
if hat != 0 {
warn!(
"Hat mappings are only supported for dpads (requested to map hat \
{}.{} to {:?}",
hat, direction, to
);
} else {
// We don't have anything like "hat" in gilrs, so let's jus assume that
// user want to map dpad axes.
//
// We have to add mappings for axes AND buttons, because axis_dpad_to_button
// filter may transform event to button event.
let (from_axis, from_btn) = match direction {
1 => (nec::AXIS_DPADY, nec::BTN_DPAD_UP),
4 => (nec::AXIS_DPADY, nec::BTN_DPAD_DOWN),
2 => (nec::AXIS_DPADX, nec::BTN_DPAD_RIGHT),
8 => (nec::AXIS_DPADX, nec::BTN_DPAD_LEFT),
0 => continue, // FIXME: I have no idea what 0 means here
_ => return Err(ParseSdlMappingError::UnknownHatDirection),
};
if to.is_button() {
match to {
AxisOrBtn::Btn(Button::DPadLeft | Button::DPadRight) => {
mapping
.mappings
.insert(from_axis, AxisOrBtn::Axis(Axis::DPadX));
}
AxisOrBtn::Btn(Button::DPadUp | Button::DPadDown) => {
mapping
.mappings
.insert(from_axis, AxisOrBtn::Axis(Axis::DPadY));
}
_ => (),
}
mapping.mappings.insert(from_btn, to);
} else {
mapping.mappings.insert(from_axis, to);
}
mapping.hats_mapped |= direction as u8;
}
}
}
}
Ok(mapping)
}
fn add_button(
ident: &str,
ev_code: EvCode,
mapped_btn: Button,
buttons: &[EvCode],
sdl_mappings: &mut String,
mappings: &mut FnvHashMap<EvCode, AxisOrBtn>,
) -> Result<(), MappingError> {
let n_btn = buttons
.iter()
.position(|&x| x == ev_code)
.ok_or(MappingError::InvalidCode(ev::Code(ev_code)))?;
let _ = write!(sdl_mappings, "{}:b{},", ident, n_btn);
mappings.insert(ev_code, AxisOrBtn::Btn(mapped_btn));
Ok(())
}
fn add_axis(
ident: &str,
ev_code: EvCode,
mapped_axis: Axis,
axes: &[EvCode],
sdl_mappings: &mut String,
mappings: &mut FnvHashMap<EvCode, AxisOrBtn>,
) -> Result<(), MappingError> {
let n_axis = axes
.iter()
.position(|&x| x == ev_code)
.ok_or(MappingError::InvalidCode(ev::Code(ev_code)))?;
let _ = write!(sdl_mappings, "{}:a{},", ident, n_axis);
mappings.insert(ev_code, AxisOrBtn::Axis(mapped_axis));
Ok(())
}
fn is_name_valid(name: &str) -> bool {
!name.chars().any(|x| x == ',')
}
pub fn map(&self, code: &EvCode) -> Option<AxisOrBtn> {
self.mappings.get(code).cloned()
}
pub fn map_rev(&self, el: &AxisOrBtn) -> Option<EvCode> {
self.mappings.iter().find(|x| x.1 == el).map(|x| *x.0)
}
pub fn is_default(&self) -> bool {
self.default
}
/// Return bit field with mapped hats. Only for mappings created from SDL format this function
/// can return non-zero value.
pub fn hats_mapped(&self) -> u8 {
self.hats_mapped
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum ParseSdlMappingError {
UnknownHatDirection,
ParseError(ParserError),
}
impl From<ParserError> for ParseSdlMappingError {
fn from(f: ParserError) -> Self {
ParseSdlMappingError::ParseError(f)
}
}
impl Error for ParseSdlMappingError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
if let ParseSdlMappingError::ParseError(ref err) = self {
Some(err)
} else {
None
}
}
}
impl Display for ParseSdlMappingError {
fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
match self {
ParseSdlMappingError::UnknownHatDirection => {
fmt.write_str("hat direction wasn't 1, 2, 4 or 8")
}
ParseSdlMappingError::ParseError(_) => fmt.write_str("parsing error"),
}
}
}
#[derive(Debug)]
pub struct MappingDb {
mappings: HashMap<Uuid, String>,
}
impl MappingDb {
pub fn new() -> Self {
MappingDb {
mappings: HashMap::new(),
}
}
pub fn add_included_mappings(&mut self) {
self.insert(include_str!(concat!(
env!("OUT_DIR"),
PATH_SEPARATOR!(),
"gamecontrollerdb.txt"
)));
}
pub fn add_env_mappings(&mut self) {
if let Ok(mapping) = env::var("SDL_GAMECONTROLLERCONFIG") {
self.insert(&mapping);
}
}
pub fn insert(&mut self, s: &str) {
for mapping in s.lines() {
let pat = "platform:";
if let Some(offset) = mapping.find(pat).map(|o| o + pat.len()) {
let s = &mapping[offset..];
let end = s.find(',').unwrap_or(s.len());
if &s[..end] != SDL_PLATFORM_NAME {
continue;
}
}
mapping
.split(',')
.next()
.and_then(|s| Uuid::parse_str(s).ok())
.and_then(|uuid| self.mappings.insert(uuid, mapping.to_owned()));
}
}
pub fn get(&self, uuid: Uuid) -> Option<&str> {
self.mappings.get(&uuid).map(String::as_ref)
}
pub fn len(&self) -> usize {
self.mappings.len()
}
}
/// Stores data used to map gamepad buttons and axes.
///
/// After you add all mappings, use
/// [`Gamepad::set_mapping(…)`](struct.Gamepad.html#method.set_mapping) to change mapping of
/// existing gamepad.
///
/// See `examples/mapping.rs` for more detailed example.
#[derive(Debug, Clone, Default)]
// Re-exported as Mapping
pub struct MappingData {
buttons: VecMap<EvCode>,
axes: VecMap<EvCode>,
}
impl MappingData {
/// Creates new `Mapping`.
pub fn new() -> Self {
MappingData {
buttons: VecMap::with_capacity(18),
axes: VecMap::with_capacity(11),
}
}
/// Returns `EvCode` associated with button index.
pub fn button(&self, idx: Button) -> Option<ev::Code> {
self.buttons.get(idx as usize).cloned().map(ev::Code)
}
/// Returns `EvCode` associated with axis index.
pub fn axis(&self, idx: Axis) -> Option<ev::Code> {
self.axes.get(idx as usize).cloned().map(ev::Code)
}
/// Inserts new button mapping.
pub fn insert_btn(&mut self, from: ev::Code, to: Button) -> Option<ev::Code> {
self.buttons.insert(to as usize, from.0).map(ev::Code)
}
/// Inserts new axis mapping.
pub fn insert_axis(&mut self, from: ev::Code, to: Axis) -> Option<ev::Code> {
self.axes.insert(to as usize, from.0).map(ev::Code)
}
/// Removes button and returns associated `NativEvCode`.
pub fn remove_button(&mut self, idx: Button) -> Option<ev::Code> {
self.buttons.remove(idx as usize).map(ev::Code)
}
/// Removes axis and returns associated `NativEvCode`.
pub fn remove_axis(&mut self, idx: Axis) -> Option<ev::Code> {
self.axes.remove(idx as usize).map(ev::Code)
}
}
/// The error type for functions related to gamepad mapping.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum MappingError {
/// Gamepad does not have element referenced by `EvCode`.
InvalidCode(ev::Code),
/// Name contains comma (',').
InvalidName,
/// This function is not implemented for current platform.
NotImplemented,
/// Gamepad is not connected.
NotConnected,
/// Same gamepad element is referenced by axis and button.
DuplicatedEntry,
/// `Mapping` with `Button::Unknown` or `Axis::Unknown`.
UnknownElement,
/// `Mapping` have button or axis that are not present in SDL2.
NotSdl2Compatible,
}
impl Error for MappingError {}
impl Display for MappingError {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
let sbuf;
let s = match self {
MappingError::InvalidCode(code) => {
sbuf = format!("gamepad does not have element with {}", code);
sbuf.as_ref()
}
MappingError::InvalidName => "name can not contain comma",
MappingError::NotImplemented => {
"current platform does not implement setting custom mappings"
}
MappingError::NotConnected => "gamepad is not connected",
MappingError::DuplicatedEntry => {
"same gamepad element is referenced by axis and button"
}
MappingError::UnknownElement => "Button::Unknown and Axis::Unknown are not allowed",
MappingError::NotSdl2Compatible => "one of buttons or axes is not compatible with SDL2",
};
f.write_str(s)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ev::{Axis, Button};
use gilrs_core::native_ev_codes as nec;
use gilrs_core::EvCode;
use uuid::Uuid;
// Do not include platform, mapping from (with UUID modified)
// https://github.com/gabomdq/SDL_GameControllerDB/blob/master/gamecontrollerdb.txt
const TEST_STR: &str = "03000000260900008888000000010001,GameCube {WiseGroup USB \
box},a:b0,b:b2,y:b3,x:b1,start:b7,rightshoulder:b6,dpup:h0.1,dpleft:\
h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,\
lefttrigger:a4,righttrigger:a5,";
const BUTTONS: [EvCode; 15] = [
nec::BTN_SOUTH,
nec::BTN_EAST,
nec::BTN_C,
nec::BTN_NORTH,
nec::BTN_WEST,
nec::BTN_Z,
nec::BTN_LT,
nec::BTN_RT,
nec::BTN_LT2,
nec::BTN_RT2,
nec::BTN_SELECT,
nec::BTN_START,
nec::BTN_MODE,
nec::BTN_LTHUMB,
nec::BTN_RTHUMB,
];
const AXES: [EvCode; 12] = [
nec::AXIS_LSTICKX,
nec::AXIS_LSTICKY,
nec::AXIS_LEFTZ,
nec::AXIS_RSTICKX,
nec::AXIS_RSTICKY,
nec::AXIS_RIGHTZ,
nec::AXIS_DPADX,
nec::AXIS_DPADY,
nec::AXIS_RT,
nec::AXIS_LT,
nec::AXIS_RT2,
nec::AXIS_LT2,
];
#[test]
fn mapping() {
Mapping::parse_sdl_mapping(TEST_STR, &BUTTONS, &AXES).unwrap();
}
#[test]
fn from_data() {
let uuid = Uuid::nil();
let name = "Best Gamepad";
let buttons = BUTTONS.iter().cloned().map(ev::Code).collect::<Vec<_>>();
let axes = AXES.iter().cloned().map(ev::Code).collect::<Vec<_>>();
let mut data = MappingData::new();
data.insert_axis(axes[0], Axis::LeftStickX);
data.insert_axis(axes[1], Axis::LeftStickY);
data.insert_axis(axes[2], Axis::LeftZ);
data.insert_axis(axes[3], Axis::RightStickX);
data.insert_axis(axes[4], Axis::RightStickY);
data.insert_axis(axes[5], Axis::RightZ);
data.insert_btn(buttons[0], Button::South);
data.insert_btn(buttons[1], Button::East);
data.insert_btn(buttons[3], Button::North);
data.insert_btn(buttons[4], Button::West);
data.insert_btn(buttons[5], Button::Select);
data.insert_btn(buttons[6], Button::Start);
data.insert_btn(buttons[7], Button::DPadDown);
data.insert_btn(buttons[8], Button::DPadLeft);
data.insert_btn(buttons[9], Button::RightThumb);
let (mappings, sdl_mappings) =
Mapping::from_data(&data, &BUTTONS, &AXES, name, uuid).unwrap();
let sdl_mappings = Mapping::parse_sdl_mapping(&sdl_mappings, &BUTTONS, &AXES).unwrap();
assert_eq!(mappings, sdl_mappings);
let incorrect_mappings = Mapping::from_data(&data, &BUTTONS, &AXES, "Inval,id name", uuid);
assert_eq!(Err(MappingError::InvalidName), incorrect_mappings);
data.insert_btn(ev::Code(nec::BTN_DPAD_RIGHT), Button::DPadRight);
let incorrect_mappings = Mapping::from_data(&data, &BUTTONS, &AXES, name, uuid);
assert_eq!(
Err(MappingError::InvalidCode(ev::Code(nec::BTN_DPAD_RIGHT))),
incorrect_mappings
);
data.insert_btn(ev::Code(BUTTONS[3]), Button::Unknown);
let incorrect_mappings = Mapping::from_data(&data, &BUTTONS, &AXES, name, uuid);
assert_eq!(Err(MappingError::UnknownElement), incorrect_mappings);
}
#[test]
fn with_mappings() {
let mappings = format!(
"\nShould be ignored\nThis also should,be ignored\n\n{}",
TEST_STR
);
let mut db = MappingDb::new();
db.add_included_mappings();
db.insert(&mappings);
assert_eq!(
Some(TEST_STR),
db.get(Uuid::parse_str("03000000260900008888000000010001").unwrap())
);
}
}

421
vendor/gilrs/src/mapping/parser.rs vendored Normal file
View File

@@ -0,0 +1,421 @@
// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
use std::error::Error as StdError;
use std::fmt::{self, Display};
use uuid::Uuid;
use crate::ev::{Axis, AxisOrBtn, Button};
// Must be sorted!
static AXES_SDL: [&str; 31] = [
"a",
"b",
"back",
"c",
"dpdown",
"dpleft",
"dpright",
"dpup",
"guide",
"leftshoulder",
"leftstick",
"lefttrigger",
"leftx",
"lefty",
"leftz",
"misc1",
"paddle1",
"paddle2",
"paddle3",
"paddle4",
"rightshoulder",
"rightstick",
"righttrigger",
"rightx",
"righty",
"rightz",
"start",
"touchpad",
"x",
"y",
"z",
];
static AXES: [AxisOrBtn; 31] = [
AxisOrBtn::Btn(Button::South),
AxisOrBtn::Btn(Button::East),
AxisOrBtn::Btn(Button::Select),
AxisOrBtn::Btn(Button::C),
AxisOrBtn::Btn(Button::DPadDown),
AxisOrBtn::Btn(Button::DPadLeft),
AxisOrBtn::Btn(Button::DPadRight),
AxisOrBtn::Btn(Button::DPadUp),
AxisOrBtn::Btn(Button::Mode),
AxisOrBtn::Btn(Button::LeftTrigger),
AxisOrBtn::Btn(Button::LeftThumb),
AxisOrBtn::Btn(Button::LeftTrigger2),
AxisOrBtn::Axis(Axis::LeftStickX),
AxisOrBtn::Axis(Axis::LeftStickY),
AxisOrBtn::Axis(Axis::LeftZ),
AxisOrBtn::Btn(Button::Unknown),
AxisOrBtn::Btn(Button::Unknown),
AxisOrBtn::Btn(Button::Unknown),
AxisOrBtn::Btn(Button::Unknown),
AxisOrBtn::Btn(Button::Unknown),
AxisOrBtn::Btn(Button::RightTrigger),
AxisOrBtn::Btn(Button::RightThumb),
AxisOrBtn::Btn(Button::RightTrigger2),
AxisOrBtn::Axis(Axis::RightStickX),
AxisOrBtn::Axis(Axis::RightStickY),
AxisOrBtn::Axis(Axis::RightZ),
AxisOrBtn::Btn(Button::Start),
AxisOrBtn::Btn(Button::Unknown),
AxisOrBtn::Btn(Button::West),
AxisOrBtn::Btn(Button::North),
AxisOrBtn::Btn(Button::Z),
];
pub struct Parser<'a> {
data: &'a str,
pos: usize,
state: State,
}
impl<'a> Parser<'a> {
pub fn new(mapping: &'a str) -> Self {
Parser {
data: mapping,
pos: 0,
state: State::Uuid,
}
}
pub fn next_token(&mut self) -> Option<Result<Token<'_>, Error>> {
if self.pos >= self.data.len() {
None
} else {
Some(match self.state {
State::Uuid => self.parse_uuid(),
State::Name => self.parse_name(),
State::KeyVal => self.parse_key_val(),
State::Invalid => Err(Error::new(ErrorKind::InvalidParserState, self.pos)),
})
}
}
fn parse_uuid(&mut self) -> Result<Token<'_>, Error> {
let next_comma = self.next_comma_or_end();
let uuid_field = &self.data[self.pos..next_comma];
let uuid = if uuid_field == "xinput" {
Ok(Token::Uuid(Uuid::nil()))
} else {
Uuid::parse_str(uuid_field)
.map(Token::Uuid)
.map_err(|_| Error::new(ErrorKind::InvalidGuid, self.pos))
};
if uuid.is_err() {
self.state = State::Invalid;
} else if next_comma == self.data.len() {
self.state = State::Invalid;
return Err(Error::new(ErrorKind::UnexpectedEnd, self.pos));
} else {
self.state = State::Name;
self.pos = next_comma + 1;
}
uuid
}
fn parse_name(&mut self) -> Result<Token<'_>, Error> {
let next_comma = self.next_comma_or_end();
let name = &self.data[self.pos..next_comma];
self.state = State::KeyVal;
self.pos = next_comma + 1;
Ok(Token::Name(name))
}
fn parse_key_val(&mut self) -> Result<Token<'_>, Error> {
let next_comma = self.next_comma_or_end();
let pair = &self.data[self.pos..next_comma];
let pos = self.pos;
self.pos = next_comma + 1;
let mut split = pair.split(':');
let key = split
.next()
.ok_or_else(|| Error::new(ErrorKind::InvalidKeyValPair, pos))?;
let value = split
.next()
.ok_or_else(|| Error::new(ErrorKind::InvalidKeyValPair, pos))?;
if split.next().is_some() {
return Err(Error::new(ErrorKind::InvalidKeyValPair, pos));
}
if value.is_empty() {
return Err(Error::new(ErrorKind::EmptyValue, pos));
}
if key == "platform" {
return Ok(Token::Platform(value));
}
let mut input = AxisRange::Full;
let mut output = AxisRange::Full;
let mut inverted = false;
let mut is_axis = false;
let key = match key.get(0..1) {
Some("+") => {
output = AxisRange::UpperHalf;
&key[1..]
}
Some("-") => {
output = AxisRange::LowerHalf;
&key[1..]
}
_ => key,
};
let from = match value.get(0..1) {
Some("+") if value.get(1..2) == Some("a") => {
is_axis = true;
input = AxisRange::UpperHalf;
if value.get((value.len() - 1)..) == Some("~") {
inverted = true;
&value[2..(value.len() - 1)]
} else {
&value[2..]
}
}
Some("-") if value.get(1..2) == Some("a") => {
is_axis = true;
input = AxisRange::LowerHalf;
if value.get((value.len() - 1)..) == Some("~") {
inverted = true;
&value[2..(value.len() - 1)]
} else {
&value[2..]
}
}
Some("a") => {
is_axis = true;
if value.get((value.len() - 1)..) == Some("~") {
inverted = true;
&value[1..(value.len() - 1)]
} else {
&value[1..]
}
}
Some("b") => &value[1..],
Some("h") => {
let dot_idx = value
.find('.')
.ok_or_else(|| Error::new(ErrorKind::InvalidValue, pos))?;
let hat = value[1..dot_idx]
.parse()
.map_err(|_| Error::new(ErrorKind::InvalidValue, pos + 1))?;
let direction = value
.get((dot_idx + 1)..)
.and_then(|s| s.parse().ok())
.ok_or_else(|| Error::new(ErrorKind::InvalidValue, pos + dot_idx + 1))?;
let idx = AXES_SDL
.binary_search(&key)
.map_err(|_| Error::new(ErrorKind::UnknownButton, pos))?;
return Ok(Token::HatMapping {
hat,
direction,
to: AXES[idx],
output,
});
}
_ => return Err(Error::new(ErrorKind::InvalidValue, pos)),
}
.parse::<u16>()
.map_err(|_| Error::new(ErrorKind::InvalidValue, pos))?;
if is_axis {
let idx = AXES_SDL
.binary_search(&key)
.map_err(|_| Error::new(ErrorKind::UnknownAxis, pos))?;
Ok(Token::AxisMapping {
from,
to: AXES[idx],
input,
output,
inverted,
})
} else {
let idx = AXES_SDL
.binary_search(&key)
.map_err(|_| Error::new(ErrorKind::UnknownButton, pos))?;
Ok(Token::ButtonMapping {
from,
to: AXES[idx],
output,
})
}
}
fn next_comma_or_end(&self) -> usize {
self.data[self.pos..]
.find(',')
.map(|x| x + self.pos)
.unwrap_or_else(|| self.data.len())
}
}
#[derive(Debug)]
pub enum Token<'a> {
Uuid(Uuid),
Platform(&'a str),
Name(&'a str),
#[allow(dead_code)]
AxisMapping {
from: u16,
to: AxisOrBtn,
input: AxisRange,
output: AxisRange,
inverted: bool,
},
ButtonMapping {
from: u16,
to: AxisOrBtn,
#[allow(dead_code)]
output: AxisRange,
},
// This is just SDL representation, we will convert this to axis mapping later
HatMapping {
hat: u16,
// ?
direction: u16,
to: AxisOrBtn,
#[allow(dead_code)]
output: AxisRange,
},
}
#[repr(u8)]
#[derive(Debug)]
pub enum AxisRange {
LowerHalf,
UpperHalf,
Full,
}
#[derive(Copy, Clone, Eq, PartialEq)]
enum State {
Uuid,
Name,
KeyVal,
Invalid,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Error {
pub(crate) position: usize,
kind: ErrorKind,
}
impl Error {
pub fn new(kind: ErrorKind, position: usize) -> Self {
Error { position, kind }
}
pub fn kind(&self) -> &ErrorKind {
&self.kind
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum ErrorKind {
InvalidGuid,
InvalidKeyValPair,
InvalidValue,
EmptyValue,
UnknownAxis,
UnknownButton,
InvalidParserState,
UnexpectedEnd,
}
impl StdError for Error {}
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self.kind {
ErrorKind::InvalidGuid => "GUID is invalid",
ErrorKind::InvalidKeyValPair => "expected key value pair",
ErrorKind::InvalidValue => "value is not valid",
ErrorKind::EmptyValue => "value is empty",
ErrorKind::UnknownAxis => "invalid axis name",
ErrorKind::UnknownButton => "invalid button name",
ErrorKind::InvalidParserState => "attempt to parse after unrecoverable error",
ErrorKind::UnexpectedEnd => "mapping does not have all required fields",
};
f.write_fmt(format_args!("{} at {}", s, self.position))
}
}
#[cfg(test)]
mod tests {
use crate::mapping::parser::{ErrorKind, Parser};
use crate::utils::PATH_SEPARATOR;
#[test]
fn test_all_sdl_mappings_for_parse_errors() {
let included_mappings = include_str!(concat!(
env!("OUT_DIR"),
PATH_SEPARATOR!(),
"gamecontrollerdb.txt"
))
.lines();
let mut errors = 0;
let mut index = 0;
for line in included_mappings {
let mut parser = Parser::new(line);
while let Some(token) = parser.next_token() {
if let Err(ref e) = token {
if e.kind() != &ErrorKind::EmptyValue {
errors += 1;
println!("{:?}", e);
println!(
"{}: {} (...) {}\n",
index,
line.chars().take(50).collect::<String>(),
line.chars().skip(e.position).take(15).collect::<String>()
);
if e.kind() == &ErrorKind::InvalidParserState {
break;
}
}
}
index += 1;
}
}
assert_eq!(errors, 0);
}
}

49
vendor/gilrs/src/utils.rs vendored Normal file
View File

@@ -0,0 +1,49 @@
// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
pub use gilrs_core::utils::*;
/// Like `(a: f32 / b).ceil()` but for integers.
pub fn ceil_div(a: u32, b: u32) -> u32 {
if a == 0 {
0
} else {
1 + ((a - 1) / b)
}
}
pub fn clamp(x: f32, min: f32, max: f32) -> f32 {
x.clamp(min, max)
}
#[cfg(path_separator = "backslash")]
macro_rules! PATH_SEPARATOR {
() => {
r"\"
};
}
#[cfg(path_separator = "slash")]
macro_rules! PATH_SEPARATOR {
() => {
r"/"
};
}
pub(crate) use PATH_SEPARATOR;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn t_clamp() {
assert_eq!(clamp(-1.0, 0.0, 1.0), 0.0);
assert_eq!(clamp(0.5, 0.0, 1.0), 0.5);
assert_eq!(clamp(2.0, 0.0, 1.0), 1.0);
}
}