gilrs/ff/
mod.rs

1// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8// This code is not used on wasm
9#![cfg_attr(target_arch = "wasm32", allow(dead_code))]
10
11//! Force feedback module.
12//!
13//! To use force feedback, you have to create one or more [`Effect`s](struct.Effect.html). Each
14//! `Effect` contains one or more [`BasicEffect`s](struct.BasicEffect.html) and parameters that
15//! describe effect's source, like it's position, gain or used
16//! [`DistanceModel`](enum.DistanceModel.html). Final strength of effect is based on saturating sum
17//! (to `u16::MAX`) of all base effects and time from the start of playback, attenuation from
18//! distance between effect source and listener (represented by gamepad) and effect's gain.
19//!
20//! See also [`Gilrs::set_listener_position()`](../struct.Gilrs.html#method.set_listener_position)
21//! and [`Gamepad::is_ff_supported()`](../struct.Gamepad.html#method.is_ff_supported).
22//!
23//! # Example
24//!
25//! ```rust
26//! use gilrs::Gilrs;
27//! use gilrs::ff::{EffectBuilder, Replay, BaseEffect, BaseEffectType, Ticks};
28//!
29//! let mut gilrs = Gilrs::new().unwrap();
30//! let support_ff = gilrs
31//!     .gamepads()
32//!     .filter_map(|(id, gp)| if gp.is_ff_supported() { Some(id) } else { None })
33//!     .collect::<Vec<_>>();
34//!
35//! let duration = Ticks::from_ms(150);
36//! let effect = EffectBuilder::new()
37//!     .add_effect(BaseEffect {
38//!         kind: BaseEffectType::Strong { magnitude: 60_000 },
39//!         scheduling: Replay { play_for: duration, with_delay: duration * 3, ..Default::default() },
40//!         envelope: Default::default(),
41//!     })
42//!     .add_effect(BaseEffect {
43//!         kind: BaseEffectType::Weak { magnitude: 60_000 },
44//!         scheduling: Replay { after: duration * 2, play_for: duration, with_delay: duration * 3 },
45//!         ..Default::default()
46//!     })
47//!     .gamepads(&support_ff)
48//!     .finish(&mut gilrs).unwrap();
49//!
50//! effect.play().unwrap();
51//! ```
52//!
53//! See [`examples/ff_pos.rs`](https://gitlab.com/gilrs-project/gilrs/blob/v0.11.0/examples/ff_pos.rs) for
54//! more advanced example.
55mod base_effect;
56mod effect_source;
57pub(crate) mod server;
58mod time;
59
60pub use self::base_effect::{BaseEffect, BaseEffectType, Envelope, Replay};
61pub use self::effect_source::{DistanceModel, DistanceModelError};
62#[allow(unused_imports)]
63pub(crate) use self::time::TICK_DURATION;
64pub use self::time::{Repeat, Ticks};
65
66use std::error::Error as StdError;
67use std::hash::{Hash, Hasher};
68use std::sync::mpsc::{SendError, Sender};
69use std::{f32, fmt};
70
71use self::effect_source::EffectSource;
72use crate::ff::server::Message;
73use crate::gamepad::{Gamepad, GamepadId, Gilrs};
74use crate::utils;
75
76use vec_map::VecMap;
77
78/// Handle to force feedback effect.
79///
80/// `Effect` represents force feedback effect that can be played on one or more gamepads. It uses a
81/// form of reference counting, so it can be cheaply cloned. To create new `Effect` use
82/// [`EffectBuilder`](struct.EffectBuilder.html).
83///
84/// All methods on can return `Error::SendFailed` although it shouldn't normally happen.
85pub struct Effect {
86    id: usize,
87    tx: Sender<Message>,
88}
89
90impl PartialEq for Effect {
91    fn eq(&self, other: &Effect) -> bool {
92        self.id == other.id
93    }
94}
95
96impl Eq for Effect {}
97
98impl Hash for Effect {
99    fn hash<H: Hasher>(&self, state: &mut H) {
100        self.id.hash(state);
101    }
102}
103
104impl Clone for Effect {
105    fn clone(&self) -> Self {
106        let _ = self.tx.send(Message::HandleCloned { id: self.id });
107        Effect {
108            id: self.id,
109            tx: self.tx.clone(),
110        }
111    }
112}
113
114impl Drop for Effect {
115    fn drop(&mut self) {
116        let _ = self.tx.send(Message::HandleDropped { id: self.id });
117    }
118}
119
120impl Effect {
121    /// Plays effect on all associated gamepads.
122    pub fn play(&self) -> Result<(), Error> {
123        self.tx.send(Message::Play { id: self.id })?;
124
125        Ok(())
126    }
127
128    pub fn stop(&self) -> Result<(), Error> {
129        self.tx.send(Message::Stop { id: self.id })?;
130
131        Ok(())
132    }
133
134    /// Changes gamepads that are associated with effect. Effect will be only played on gamepads
135    /// from last call to this function.
136    ///
137    /// # Errors
138    ///
139    /// Returns `Error::Disconnected(id)` or `Error::FfNotSupported(id)` on first gamepad in `ids`
140    /// that is disconnected or doesn't support force feedback.
141    pub fn set_gamepads(&self, ids: &[GamepadId], gilrs: &Gilrs) -> Result<(), Error> {
142        let mut gamepads = VecMap::new();
143
144        for dev in ids.iter().cloned() {
145            if !gilrs
146                .connected_gamepad(dev)
147                .ok_or(Error::Disconnected(dev))?
148                .is_ff_supported()
149            {
150                return Err(Error::FfNotSupported(dev));
151            } else {
152                gamepads.insert(dev.0, ());
153            }
154        }
155
156        self.tx.send(Message::SetGamepads {
157            id: self.id,
158            gamepads,
159        })?;
160
161        Ok(())
162    }
163
164    /// Adds gamepad to the list of gamepads associated with effect.
165    ///
166    /// # Errors
167    ///
168    /// Returns `Error::Disconnected(id)` or `Error::FfNotSupported(id)` if gamepad is not connected
169    /// or does not support force feedback.
170    pub fn add_gamepad(&self, gamepad: &Gamepad<'_>) -> Result<(), Error> {
171        if !gamepad.is_connected() {
172            Err(Error::Disconnected(gamepad.id()))
173        } else if !gamepad.is_ff_supported() {
174            Err(Error::FfNotSupported(gamepad.id()))
175        } else {
176            self.tx.send(Message::AddGamepad {
177                id: self.id,
178                gamepad_id: gamepad.id(),
179            })?;
180
181            Ok(())
182        }
183    }
184
185    /// Changes what should happen to effect when it ends.
186    pub fn set_repeat(&self, repeat: Repeat) -> Result<(), Error> {
187        self.tx.send(Message::SetRepeat {
188            id: self.id,
189            repeat,
190        })?;
191
192        Ok(())
193    }
194
195    /// Changes distance model associated with effect.
196    ///
197    /// # Errors
198    ///
199    /// Returns `Error::InvalidDistanceModel` if `model` is not valid. See
200    /// [`DistanceModel`](enum.DistanceModelError.html) for details.
201    pub fn set_distance_model(&self, model: DistanceModel) -> Result<(), Error> {
202        model.validate()?;
203        self.tx
204            .send(Message::SetDistanceModel { id: self.id, model })?;
205
206        Ok(())
207    }
208
209    /// Changes position of the source of effect.
210    pub fn set_position<Vec3f: Into<[f32; 3]>>(&self, position: Vec3f) -> Result<(), Error> {
211        let position = position.into();
212        self.tx.send(Message::SetPosition {
213            id: self.id,
214            position,
215        })?;
216
217        Ok(())
218    }
219
220    /// Changes gain of the effect. `gain` will be clamped to \[0.0, f32::MAX\].
221    pub fn set_gain(&self, gain: f32) -> Result<(), Error> {
222        let gain = utils::clamp(gain, 0.0, f32::MAX);
223        self.tx.send(Message::SetGain { id: self.id, gain })?;
224
225        Ok(())
226    }
227}
228
229/// Creates new [`Effect`](struct.Effect.html).
230#[derive(Clone, PartialEq, Debug)]
231pub struct EffectBuilder {
232    base_effects: Vec<BaseEffect>,
233    devices: VecMap<()>,
234    repeat: Repeat,
235    dist_model: DistanceModel,
236    position: [f32; 3],
237    gain: f32,
238}
239
240impl EffectBuilder {
241    /// Creates new builder with following defaults: no gamepads, no base effects, repeat set to
242    /// infinitely, no distance model, position in (0.0, 0.0, 0.0) and gain 1.0. Use `finish()` to
243    /// create new effect.
244    pub fn new() -> Self {
245        EffectBuilder {
246            base_effects: Vec::new(),
247            devices: VecMap::new(),
248            repeat: Repeat::Infinitely,
249            dist_model: DistanceModel::None,
250            position: [0.0, 0.0, 0.0],
251            gain: 1.0,
252        }
253    }
254
255    /// Adds new [`BaseEffect`](struct.BaseEffect.html).
256    pub fn add_effect(&mut self, effect: BaseEffect) -> &mut Self {
257        self.base_effects.push(effect);
258        self
259    }
260
261    /// Changes gamepads that are associated with effect. Effect will be only played on gamepads
262    /// from last call to this function.
263    pub fn gamepads(&mut self, ids: &[GamepadId]) -> &mut Self {
264        for dev in ids {
265            self.devices.insert(dev.0, ());
266        }
267        self
268    }
269
270    /// Adds gamepad to the list of gamepads associated with effect.
271    pub fn add_gamepad(&mut self, gamepad: &Gamepad<'_>) -> &mut Self {
272        self.devices.insert(gamepad.id().0, ());
273
274        self
275    }
276
277    /// Changes what should happen to effect when it ends.
278    pub fn repeat(&mut self, repeat: Repeat) -> &mut Self {
279        self.repeat = repeat;
280        self
281    }
282
283    /// Changes distance model associated with effect.
284    pub fn distance_model(&mut self, model: DistanceModel) -> &mut Self {
285        self.dist_model = model;
286        self
287    }
288
289    /// Changes position of the source of effect.
290    pub fn position<Vec3f: Into<[f32; 3]>>(&mut self, position: Vec3f) -> &mut Self {
291        self.position = position.into();
292        self
293    }
294
295    /// Changes gain of the effect. `gain` will be clamped to \[0.0, f32::MAX\].
296    pub fn gain(&mut self, gain: f32) -> &mut Self {
297        self.gain = utils::clamp(gain, 0.0, f32::MAX);
298        self
299    }
300
301    /// Validates all parameters and creates new effect.
302    ///
303    /// # Errors
304    ///
305    /// Returns `Error::Disconnected(id)` or `Error::FfNotSupported(id)` on first gamepad in `ids`
306    /// that is disconnected or doesn't support force feedback.
307    ///
308    /// Returns `Error::InvalidDistanceModel` if `model` is not valid. See
309    /// [`DistanceModel`](enum.DistanceModelError.html) for details.
310    pub fn finish(&mut self, gilrs: &mut Gilrs) -> Result<Effect, Error> {
311        for (dev, _) in &self.devices {
312            let dev = GamepadId(dev);
313            if !gilrs
314                .connected_gamepad(dev)
315                .ok_or(Error::Disconnected(dev))?
316                .is_ff_supported()
317            {
318                return Err(Error::FfNotSupported(dev));
319            }
320        }
321
322        self.dist_model.validate()?;
323
324        let effect = EffectSource::new(
325            self.base_effects.clone(),
326            self.devices.clone(),
327            self.repeat,
328            self.dist_model,
329            self.position,
330            self.gain,
331        );
332        let id = gilrs.next_ff_id();
333        let tx = gilrs.ff_sender();
334        tx.send(Message::Create {
335            id,
336            effect: Box::new(effect),
337        })?;
338        Ok(Effect { id, tx: tx.clone() })
339    }
340}
341
342impl Default for EffectBuilder {
343    fn default() -> Self {
344        Self::new()
345    }
346}
347
348/// Basic error type in force feedback module.
349#[derive(Copy, Clone, Debug, PartialEq, Eq)]
350#[non_exhaustive]
351pub enum Error {
352    /// Force feedback is not supported by device with this ID
353    FfNotSupported(GamepadId),
354    /// Device is not connected
355    Disconnected(GamepadId),
356    /// Distance model is invalid.
357    InvalidDistanceModel(DistanceModelError),
358    /// The other end of channel was dropped.
359    SendFailed,
360    /// Unexpected error has occurred
361    Other,
362}
363
364impl StdError for Error {
365    fn source(&self) -> Option<&(dyn StdError + 'static)> {
366        match self {
367            Error::InvalidDistanceModel(m) => Some(m),
368            _ => None,
369        }
370    }
371}
372
373impl fmt::Display for Error {
374    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
375        let sbuf;
376        let s = match self {
377            Error::FfNotSupported(id) => {
378                sbuf = format!(
379                    "force feedback is not supported by device with id {}.",
380                    id.0
381                );
382                sbuf.as_ref()
383            }
384            Error::Disconnected(id) => {
385                sbuf = format!("device with id {} is not connected.", id.0);
386                sbuf.as_ref()
387            }
388            Error::InvalidDistanceModel(_) => "distance model is invalid",
389            Error::SendFailed => "receiving end of a channel is disconnected.",
390            Error::Other => "unespected error has occurred.",
391        };
392
393        fmt.write_str(s)
394    }
395}
396
397impl<T> From<SendError<T>> for Error {
398    fn from(_: SendError<T>) -> Self {
399        Error::SendFailed
400    }
401}
402
403impl From<DistanceModelError> for Error {
404    fn from(f: DistanceModelError) -> Self {
405        Error::InvalidDistanceModel(f)
406    }
407}
408
409#[cfg(test)]
410mod tests {
411    use super::*;
412
413    #[test]
414    fn envelope() {
415        let env = Envelope {
416            attack_length: Ticks(10),
417            attack_level: 0.2,
418            fade_length: Ticks(10),
419            fade_level: 0.2,
420        };
421        let dur = Ticks(40);
422
423        assert_eq!(env.at(Ticks(0), dur), 0.2);
424        assert_eq!(env.at(Ticks(5), dur), 0.6);
425        assert_eq!(env.at(Ticks(10), dur), 1.0);
426        assert_eq!(env.at(Ticks(20), dur), 1.0);
427        assert_eq!(env.at(Ticks(30), dur), 1.0);
428        assert_eq!(env.at(Ticks(35), dur), 0.6);
429        assert_eq!(env.at(Ticks(40), dur), 0.19999999);
430    }
431
432    #[test]
433    fn envelope_default() {
434        let env = Envelope::default();
435        let dur = Ticks(40);
436
437        assert_eq!(env.at(Ticks(0), dur), 1.0);
438        assert_eq!(env.at(Ticks(20), dur), 1.0);
439        assert_eq!(env.at(Ticks(40), dur), 1.0);
440    }
441
442    #[test]
443    fn replay() {
444        let replay = Replay {
445            after: Ticks(10),
446            play_for: Ticks(50),
447            with_delay: Ticks(20),
448        };
449
450        assert_eq!(replay.at(Ticks(0)), 1.0);
451        assert_eq!(replay.at(Ticks(9)), 1.0);
452        assert_eq!(replay.at(Ticks(10)), 1.0);
453        assert_eq!(replay.at(Ticks(30)), 1.0);
454        assert_eq!(replay.at(Ticks(59)), 0.0);
455        assert_eq!(replay.at(Ticks(60)), 0.0);
456        assert_eq!(replay.at(Ticks(70)), 0.0);
457    }
458}