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

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),
}