evdev/
ff.rs

1use crate::attribute_set::EvdevEnum;
2use crate::compat::{ff_condition_effect, ff_envelope, ff_replay, ff_trigger};
3use crate::constants::FFEffectCode;
4use crate::sys;
5
6/// Describes a generic force feedback effect envelope.
7#[derive(Clone, Copy, Debug, Eq, PartialEq)]
8pub struct FFEnvelope {
9    /// How long the attack should last in milliseconds.
10    pub attack_length: u16,
11    /// The level of the attack at the beginning of the attack.
12    pub attack_level: u16,
13    /// How long the fade should last in milliseconds.
14    pub fade_length: u16,
15    /// The level of the fade at the end of the fade.
16    pub fade_level: u16,
17}
18
19impl From<ff_envelope> for FFEnvelope {
20    fn from(value: ff_envelope) -> Self {
21        Self {
22            attack_length: value.attack_length,
23            attack_level: value.attack_level,
24            fade_length: value.fade_length,
25            fade_level: value.fade_level,
26        }
27    }
28}
29
30impl From<FFEnvelope> for ff_envelope {
31    fn from(other: FFEnvelope) -> Self {
32        ff_envelope {
33            attack_length: other.attack_length,
34            attack_level: other.attack_level,
35            fade_length: other.fade_length,
36            fade_level: other.fade_level,
37        }
38    }
39}
40
41/// Describes the waveform for periodic force feedback effects.
42#[derive(Clone, Copy, Debug, Eq, PartialEq)]
43pub enum FFWaveform {
44    /// Square waveform.
45    Square,
46    /// Triangle waveform.
47    Triangle,
48    /// Sine waveform.
49    Sine,
50    /// Sawtooth up waveform.
51    SawUp,
52    /// Sawtooth down waveform.
53    SawDown,
54}
55
56impl From<FFWaveform> for FFEffectCode {
57    fn from(other: FFWaveform) -> Self {
58        match other {
59            FFWaveform::Square => FFEffectCode::FF_SQUARE,
60            FFWaveform::Triangle => FFEffectCode::FF_TRIANGLE,
61            FFWaveform::Sine => FFEffectCode::FF_SINE,
62            FFWaveform::SawUp => FFEffectCode::FF_SAW_UP,
63            FFWaveform::SawDown => FFEffectCode::FF_SAW_DOWN,
64        }
65    }
66}
67
68/// Describes a spring or friction force feedback effect.
69#[derive(Clone, Copy, Debug, Eq, PartialEq)]
70pub struct FFCondition {
71    /// The maximum level when the joystick is moved all the way to the right.
72    pub right_saturation: u16,
73    /// The maximum level when the joystick is moved all the way to the left.
74    pub left_saturation: u16,
75    /// The coefficient that controls how fast the force grows when the joystick moves to the
76    /// right.
77    pub right_coefficient: i16,
78    /// The coefficient that controls how fast the force grows when the joystick moves to the left.
79    pub left_coefficient: i16,
80    /// The size of the dead zone, which is the zone where no force is produced.
81    pub deadband: u16,
82    /// The position of the dead zone.
83    pub center: i16,
84}
85
86impl From<ff_condition_effect> for FFCondition {
87    fn from(value: ff_condition_effect) -> Self {
88        Self {
89            right_saturation: value.right_saturation,
90            left_saturation: value.left_saturation,
91            right_coefficient: value.right_coeff,
92            left_coefficient: value.left_coeff,
93            deadband: value.deadband,
94            center: value.center,
95        }
96    }
97}
98
99impl From<FFCondition> for ff_condition_effect {
100    fn from(other: FFCondition) -> Self {
101        ff_condition_effect {
102            right_saturation: other.right_saturation,
103            left_saturation: other.left_saturation,
104            right_coeff: other.right_coefficient,
105            left_coeff: other.left_coefficient,
106            deadband: other.deadband,
107            center: other.center,
108        }
109    }
110}
111
112#[derive(Clone, Copy, Debug, Eq, PartialEq)]
113pub enum FFEffectKind {
114    Damper,
115    Inertia,
116    Constant {
117        /// The strength of the effect.
118        level: i16,
119        /// Envelope data.
120        envelope: FFEnvelope,
121    },
122    Ramp {
123        /// The strength at the beginning of the effect.
124        start_level: i16,
125        /// The strength at the end of the effect.
126        end_level: i16,
127        /// Envelope data.
128        envelope: FFEnvelope,
129    },
130    Periodic {
131        /// The kind of waveform to use for the force feedback effect.
132        waveform: FFWaveform,
133        /// The period of the wave in milliseconds.
134        period: u16,
135        /// The peak value or amplitude of the wave.
136        magnitude: i16,
137        /// The mean value of the wave (roughly).
138        offset: i16,
139        /// The horizontal shift.
140        phase: u16,
141        /// Envelope data.
142        envelope: FFEnvelope,
143    },
144    Spring {
145        /// Condition data for each axis.
146        condition: [FFCondition; 2],
147    },
148    Friction {
149        /// Condition data for each axis.
150        condition: [FFCondition; 2],
151    },
152    Rumble {
153        /// The magnitude of the heavy motor.
154        strong_magnitude: u16,
155        /// The magnitude of the light motor.
156        weak_magnitude: u16,
157    },
158}
159
160impl From<FFEffectKind> for FFEffectCode {
161    fn from(other: FFEffectKind) -> Self {
162        match other {
163            FFEffectKind::Damper => FFEffectCode::FF_DAMPER,
164            FFEffectKind::Inertia => FFEffectCode::FF_INERTIA,
165            FFEffectKind::Constant { .. } => FFEffectCode::FF_CONSTANT,
166            FFEffectKind::Ramp { .. } => FFEffectCode::FF_RAMP,
167            FFEffectKind::Periodic { .. } => FFEffectCode::FF_PERIODIC,
168            FFEffectKind::Spring { .. } => FFEffectCode::FF_SPRING,
169            FFEffectKind::Friction { .. } => FFEffectCode::FF_FRICTION,
170            FFEffectKind::Rumble { .. } => FFEffectCode::FF_RUMBLE,
171        }
172    }
173}
174
175/// Trigger information for the force feedback effect.
176#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
177pub struct FFTrigger {
178    /// The button number that triggers the force feedback effect.
179    pub button: u16,
180    /// How long to wait before the force feedback effect can be triggered again in milliseconds.
181    pub interval: u16,
182}
183
184impl From<ff_trigger> for FFTrigger {
185    fn from(value: ff_trigger) -> Self {
186        Self {
187            button: value.button,
188            interval: value.interval,
189        }
190    }
191}
192
193impl From<FFTrigger> for ff_trigger {
194    fn from(other: FFTrigger) -> Self {
195        ff_trigger {
196            button: other.button,
197            interval: other.interval,
198        }
199    }
200}
201
202/// Scheduling information for the force feedback effect.
203#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
204pub struct FFReplay {
205    /// How long the force feedback effect should last in milliseconds.
206    pub length: u16,
207    /// How long to wait before the force feedback effect should play in milliseconds.
208    pub delay: u16,
209}
210
211impl From<ff_replay> for FFReplay {
212    fn from(value: ff_replay) -> Self {
213        Self {
214            length: value.length,
215            delay: value.delay,
216        }
217    }
218}
219
220impl From<FFReplay> for ff_replay {
221    fn from(other: FFReplay) -> Self {
222        ff_replay {
223            length: other.length,
224            delay: other.delay,
225        }
226    }
227}
228
229#[derive(Clone, Copy, Debug, Eq, PartialEq)]
230pub struct FFEffectData {
231    /// The direction of the force feedback effect.
232    pub direction: u16,
233    /// Trigger conditions.
234    pub trigger: FFTrigger,
235    /// Scheduling of the effect.
236    pub replay: FFReplay,
237    /// The type of force feedback effect and any associated parameters.
238    pub kind: FFEffectKind,
239}
240
241impl From<sys::ff_effect> for FFEffectData {
242    fn from(value: sys::ff_effect) -> Self {
243        let kind = match FFEffectCode::from_index(value.type_ as usize) {
244            FFEffectCode::FF_DAMPER => FFEffectKind::Damper,
245            FFEffectCode::FF_INERTIA => FFEffectKind::Inertia,
246            FFEffectCode::FF_CONSTANT => {
247                let constant = unsafe { value.u.constant };
248
249                FFEffectKind::Constant {
250                    level: constant.level,
251                    envelope: constant.envelope.into(),
252                }
253            }
254            FFEffectCode::FF_RAMP => {
255                let ramp = unsafe { value.u.ramp };
256
257                FFEffectKind::Ramp {
258                    start_level: ramp.start_level,
259                    end_level: ramp.end_level,
260                    envelope: ramp.envelope.into(),
261                }
262            }
263            FFEffectCode::FF_PERIODIC => {
264                let periodic = unsafe { value.u.periodic };
265
266                FFEffectKind::Periodic {
267                    waveform: match FFEffectCode::from_index(periodic.waveform as usize) {
268                        FFEffectCode::FF_SQUARE => FFWaveform::Square,
269                        FFEffectCode::FF_TRIANGLE => FFWaveform::Triangle,
270                        FFEffectCode::FF_SINE => FFWaveform::Sine,
271                        FFEffectCode::FF_SAW_UP => FFWaveform::SawUp,
272                        FFEffectCode::FF_SAW_DOWN => FFWaveform::SawDown,
273                        _ => unreachable!(),
274                    },
275                    period: periodic.period,
276                    magnitude: periodic.magnitude,
277                    offset: periodic.offset,
278                    phase: periodic.phase,
279                    envelope: periodic.envelope.into(),
280                }
281            }
282            FFEffectCode::FF_SPRING => {
283                let condition = unsafe { value.u.condition };
284
285                FFEffectKind::Spring {
286                    condition: [condition[0].into(), condition[1].into()],
287                }
288            }
289            FFEffectCode::FF_FRICTION => {
290                let condition = unsafe { value.u.condition };
291
292                FFEffectKind::Friction {
293                    condition: [condition[0].into(), condition[1].into()],
294                }
295            }
296            FFEffectCode::FF_RUMBLE => {
297                let rumble = unsafe { value.u.rumble };
298
299                FFEffectKind::Rumble {
300                    strong_magnitude: rumble.strong_magnitude,
301                    weak_magnitude: rumble.weak_magnitude,
302                }
303            }
304            _ => unreachable!(),
305        };
306
307        Self {
308            direction: value.direction,
309            trigger: value.trigger.into(),
310            replay: value.replay.into(),
311            kind,
312        }
313    }
314}
315
316impl From<FFEffectData> for sys::ff_effect {
317    fn from(other: FFEffectData) -> Self {
318        let mut effect: sys::ff_effect = unsafe { std::mem::zeroed() };
319
320        let type_: FFEffectCode = other.kind.into();
321        effect.type_ = type_.0;
322        effect.direction = other.direction;
323        effect.trigger = other.trigger.into();
324        effect.replay = other.replay.into();
325
326        match other.kind {
327            FFEffectKind::Constant { level, envelope } => {
328                effect.u.constant.level = level;
329                effect.u.constant.envelope = envelope.into();
330            }
331            FFEffectKind::Ramp {
332                start_level,
333                end_level,
334                envelope,
335            } => {
336                effect.u.ramp.start_level = start_level;
337                effect.u.ramp.end_level = end_level;
338                effect.u.ramp.envelope = envelope.into();
339            }
340            FFEffectKind::Periodic {
341                waveform,
342                period,
343                magnitude,
344                offset,
345                phase,
346                envelope,
347            } => {
348                let waveform: FFEffectCode = waveform.into();
349                effect.u.periodic.waveform = waveform.0;
350                effect.u.periodic.period = period;
351                effect.u.periodic.magnitude = magnitude;
352                effect.u.periodic.offset = offset;
353                effect.u.periodic.phase = phase;
354                effect.u.periodic.envelope = envelope.into();
355            }
356            FFEffectKind::Spring { condition } | FFEffectKind::Friction { condition } => {
357                effect.u.condition = [condition[0].into(), condition[1].into()];
358            }
359            FFEffectKind::Rumble {
360                strong_magnitude,
361                weak_magnitude,
362            } => {
363                effect.u.rumble.strong_magnitude = strong_magnitude;
364                effect.u.rumble.weak_magnitude = weak_magnitude;
365            }
366            _ => (),
367        }
368
369        effect
370    }
371}