1#![cfg_attr(target_arch = "wasm32", allow(dead_code))]
10
11mod 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
78pub 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 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 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 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 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 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 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 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#[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 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 pub fn add_effect(&mut self, effect: BaseEffect) -> &mut Self {
257 self.base_effects.push(effect);
258 self
259 }
260
261 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 pub fn add_gamepad(&mut self, gamepad: &Gamepad<'_>) -> &mut Self {
272 self.devices.insert(gamepad.id().0, ());
273
274 self
275 }
276
277 pub fn repeat(&mut self, repeat: Repeat) -> &mut Self {
279 self.repeat = repeat;
280 self
281 }
282
283 pub fn distance_model(&mut self, model: DistanceModel) -> &mut Self {
285 self.dist_model = model;
286 self
287 }
288
289 pub fn position<Vec3f: Into<[f32; 3]>>(&mut self, position: Vec3f) -> &mut Self {
291 self.position = position.into();
292 self
293 }
294
295 pub fn gain(&mut self, gain: f32) -> &mut Self {
297 self.gain = utils::clamp(gain, 0.0, f32::MAX);
298 self
299 }
300
301 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#[derive(Copy, Clone, Debug, PartialEq, Eq)]
350#[non_exhaustive]
351pub enum Error {
352 FfNotSupported(GamepadId),
354 Disconnected(GamepadId),
356 InvalidDistanceModel(DistanceModelError),
358 SendFailed,
360 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}