| use crate::attribute_set::EvdevEnum; |
| use crate::compat::{ff_condition_effect, ff_envelope, ff_replay, ff_trigger}; |
| use crate::constants::FFEffectCode; |
| use crate::sys; |
| |
| /// Describes a generic force feedback effect envelope. |
| #[derive(Clone, Copy, Debug, Eq, PartialEq)] |
| pub struct FFEnvelope { |
| /// How long the attack should last in milliseconds. |
| pub attack_length: u16, |
| /// The level of the attack at the beginning of the attack. |
| pub attack_level: u16, |
| /// How long the fade should last in milliseconds. |
| pub fade_length: u16, |
| /// The level of the fade at the end of the fade. |
| pub fade_level: u16, |
| } |
| |
| impl From<ff_envelope> for FFEnvelope { |
| fn from(value: ff_envelope) -> Self { |
| Self { |
| attack_length: value.attack_length, |
| attack_level: value.attack_level, |
| fade_length: value.fade_length, |
| fade_level: value.fade_level, |
| } |
| } |
| } |
| |
| impl From<FFEnvelope> for ff_envelope { |
| fn from(other: FFEnvelope) -> Self { |
| ff_envelope { |
| attack_length: other.attack_length, |
| attack_level: other.attack_level, |
| fade_length: other.fade_length, |
| fade_level: other.fade_level, |
| } |
| } |
| } |
| |
| /// Describes the waveform for periodic force feedback effects. |
| #[derive(Clone, Copy, Debug, Eq, PartialEq)] |
| pub enum FFWaveform { |
| /// Square waveform. |
| Square, |
| /// Triangle waveform. |
| Triangle, |
| /// Sine waveform. |
| Sine, |
| /// Sawtooth up waveform. |
| SawUp, |
| /// Sawtooth down waveform. |
| SawDown, |
| } |
| |
| impl From<FFWaveform> for FFEffectCode { |
| fn from(other: FFWaveform) -> Self { |
| match other { |
| FFWaveform::Square => FFEffectCode::FF_SQUARE, |
| FFWaveform::Triangle => FFEffectCode::FF_TRIANGLE, |
| FFWaveform::Sine => FFEffectCode::FF_SINE, |
| FFWaveform::SawUp => FFEffectCode::FF_SAW_UP, |
| FFWaveform::SawDown => FFEffectCode::FF_SAW_DOWN, |
| } |
| } |
| } |
| |
| /// Describes a spring or friction force feedback effect. |
| #[derive(Clone, Copy, Debug, Eq, PartialEq)] |
| pub struct FFCondition { |
| /// The maximum level when the joystick is moved all the way to the right. |
| pub right_saturation: u16, |
| /// The maximum level when the joystick is moved all the way to the left. |
| pub left_saturation: u16, |
| /// The coefficient that controls how fast the force grows when the joystick moves to the |
| /// right. |
| pub right_coefficient: i16, |
| /// The coefficient that controls how fast the force grows when the joystick moves to the left. |
| pub left_coefficient: i16, |
| /// The size of the dead zone, which is the zone where no force is produced. |
| pub deadband: u16, |
| /// The position of the dead zone. |
| pub center: i16, |
| } |
| |
| impl From<ff_condition_effect> for FFCondition { |
| fn from(value: ff_condition_effect) -> Self { |
| Self { |
| right_saturation: value.right_saturation, |
| left_saturation: value.left_saturation, |
| right_coefficient: value.right_coeff, |
| left_coefficient: value.left_coeff, |
| deadband: value.deadband, |
| center: value.center, |
| } |
| } |
| } |
| |
| impl From<FFCondition> for ff_condition_effect { |
| fn from(other: FFCondition) -> Self { |
| ff_condition_effect { |
| right_saturation: other.right_saturation, |
| left_saturation: other.left_saturation, |
| right_coeff: other.right_coefficient, |
| left_coeff: other.left_coefficient, |
| deadband: other.deadband, |
| center: other.center, |
| } |
| } |
| } |
| |
| #[derive(Clone, Copy, Debug, Eq, PartialEq)] |
| pub enum FFEffectKind { |
| Damper, |
| Inertia, |
| Constant { |
| /// The strength of the effect. |
| level: i16, |
| /// Envelope data. |
| envelope: FFEnvelope, |
| }, |
| Ramp { |
| /// The strength at the beginning of the effect. |
| start_level: i16, |
| /// The strength at the end of the effect. |
| end_level: i16, |
| /// Envelope data. |
| envelope: FFEnvelope, |
| }, |
| Periodic { |
| /// The kind of waveform to use for the force feedback effect. |
| waveform: FFWaveform, |
| /// The period of the wave in milliseconds. |
| period: u16, |
| /// The peak value or amplitude of the wave. |
| magnitude: i16, |
| /// The mean value of the wave (roughly). |
| offset: i16, |
| /// The horizontal shift. |
| phase: u16, |
| /// Envelope data. |
| envelope: FFEnvelope, |
| }, |
| Spring { |
| /// Condition data for each axis. |
| condition: [FFCondition; 2], |
| }, |
| Friction { |
| /// Condition data for each axis. |
| condition: [FFCondition; 2], |
| }, |
| Rumble { |
| /// The magnitude of the heavy motor. |
| strong_magnitude: u16, |
| /// The magnitude of the light motor. |
| weak_magnitude: u16, |
| }, |
| } |
| |
| impl From<FFEffectKind> for FFEffectCode { |
| fn from(other: FFEffectKind) -> Self { |
| match other { |
| FFEffectKind::Damper => FFEffectCode::FF_DAMPER, |
| FFEffectKind::Inertia => FFEffectCode::FF_INERTIA, |
| FFEffectKind::Constant { .. } => FFEffectCode::FF_CONSTANT, |
| FFEffectKind::Ramp { .. } => FFEffectCode::FF_RAMP, |
| FFEffectKind::Periodic { .. } => FFEffectCode::FF_PERIODIC, |
| FFEffectKind::Spring { .. } => FFEffectCode::FF_SPRING, |
| FFEffectKind::Friction { .. } => FFEffectCode::FF_FRICTION, |
| FFEffectKind::Rumble { .. } => FFEffectCode::FF_RUMBLE, |
| } |
| } |
| } |
| |
| /// Trigger information for the force feedback effect. |
| #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] |
| pub struct FFTrigger { |
| /// The button number that triggers the force feedback effect. |
| pub button: u16, |
| /// How long to wait before the force feedback effect can be triggered again in milliseconds. |
| pub interval: u16, |
| } |
| |
| impl From<ff_trigger> for FFTrigger { |
| fn from(value: ff_trigger) -> Self { |
| Self { |
| button: value.button, |
| interval: value.interval, |
| } |
| } |
| } |
| |
| impl From<FFTrigger> for ff_trigger { |
| fn from(other: FFTrigger) -> Self { |
| ff_trigger { |
| button: other.button, |
| interval: other.interval, |
| } |
| } |
| } |
| |
| /// Scheduling information for the force feedback effect. |
| #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] |
| pub struct FFReplay { |
| /// How long the force feedback effect should last in milliseconds. |
| pub length: u16, |
| /// How long to wait before the force feedback effect should play in milliseconds. |
| pub delay: u16, |
| } |
| |
| impl From<ff_replay> for FFReplay { |
| fn from(value: ff_replay) -> Self { |
| Self { |
| length: value.length, |
| delay: value.delay, |
| } |
| } |
| } |
| |
| impl From<FFReplay> for ff_replay { |
| fn from(other: FFReplay) -> Self { |
| ff_replay { |
| length: other.length, |
| delay: other.delay, |
| } |
| } |
| } |
| |
| #[derive(Clone, Copy, Debug, Eq, PartialEq)] |
| pub struct FFEffectData { |
| /// The direction of the force feedback effect. |
| pub direction: u16, |
| /// Trigger conditions. |
| pub trigger: FFTrigger, |
| /// Scheduling of the effect. |
| pub replay: FFReplay, |
| /// The type of force feedback effect and any associated parameters. |
| pub kind: FFEffectKind, |
| } |
| |
| impl From<sys::ff_effect> for FFEffectData { |
| fn from(value: sys::ff_effect) -> Self { |
| let kind = match FFEffectCode::from_index(value.type_ as usize) { |
| FFEffectCode::FF_DAMPER => FFEffectKind::Damper, |
| FFEffectCode::FF_INERTIA => FFEffectKind::Inertia, |
| FFEffectCode::FF_CONSTANT => { |
| let constant = unsafe { value.u.constant }; |
| |
| FFEffectKind::Constant { |
| level: constant.level, |
| envelope: constant.envelope.into(), |
| } |
| } |
| FFEffectCode::FF_RAMP => { |
| let ramp = unsafe { value.u.ramp }; |
| |
| FFEffectKind::Ramp { |
| start_level: ramp.start_level, |
| end_level: ramp.end_level, |
| envelope: ramp.envelope.into(), |
| } |
| } |
| FFEffectCode::FF_PERIODIC => { |
| let periodic = unsafe { value.u.periodic }; |
| |
| FFEffectKind::Periodic { |
| waveform: match FFEffectCode::from_index(periodic.waveform as usize) { |
| FFEffectCode::FF_SQUARE => FFWaveform::Square, |
| FFEffectCode::FF_TRIANGLE => FFWaveform::Triangle, |
| FFEffectCode::FF_SINE => FFWaveform::Sine, |
| FFEffectCode::FF_SAW_UP => FFWaveform::SawUp, |
| FFEffectCode::FF_SAW_DOWN => FFWaveform::SawDown, |
| _ => unreachable!(), |
| }, |
| period: periodic.period, |
| magnitude: periodic.magnitude, |
| offset: periodic.offset, |
| phase: periodic.phase, |
| envelope: periodic.envelope.into(), |
| } |
| } |
| FFEffectCode::FF_SPRING => { |
| let condition = unsafe { value.u.condition }; |
| |
| FFEffectKind::Spring { |
| condition: [condition[0].into(), condition[1].into()], |
| } |
| } |
| FFEffectCode::FF_FRICTION => { |
| let condition = unsafe { value.u.condition }; |
| |
| FFEffectKind::Friction { |
| condition: [condition[0].into(), condition[1].into()], |
| } |
| } |
| FFEffectCode::FF_RUMBLE => { |
| let rumble = unsafe { value.u.rumble }; |
| |
| FFEffectKind::Rumble { |
| strong_magnitude: rumble.strong_magnitude, |
| weak_magnitude: rumble.weak_magnitude, |
| } |
| } |
| _ => unreachable!(), |
| }; |
| |
| Self { |
| direction: value.direction, |
| trigger: value.trigger.into(), |
| replay: value.replay.into(), |
| kind, |
| } |
| } |
| } |
| |
| impl From<FFEffectData> for sys::ff_effect { |
| fn from(other: FFEffectData) -> Self { |
| let mut effect: sys::ff_effect = unsafe { std::mem::zeroed() }; |
| |
| let type_: FFEffectCode = other.kind.into(); |
| effect.type_ = type_.0; |
| effect.direction = other.direction; |
| effect.trigger = other.trigger.into(); |
| effect.replay = other.replay.into(); |
| |
| match other.kind { |
| FFEffectKind::Constant { level, envelope } => { |
| effect.u.constant.level = level; |
| effect.u.constant.envelope = envelope.into(); |
| } |
| FFEffectKind::Ramp { |
| start_level, |
| end_level, |
| envelope, |
| } => { |
| effect.u.ramp.start_level = start_level; |
| effect.u.ramp.end_level = end_level; |
| effect.u.ramp.envelope = envelope.into(); |
| } |
| FFEffectKind::Periodic { |
| waveform, |
| period, |
| magnitude, |
| offset, |
| phase, |
| envelope, |
| } => { |
| let waveform: FFEffectCode = waveform.into(); |
| effect.u.periodic.waveform = waveform.0; |
| effect.u.periodic.period = period; |
| effect.u.periodic.magnitude = magnitude; |
| effect.u.periodic.offset = offset; |
| effect.u.periodic.phase = phase; |
| effect.u.periodic.envelope = envelope.into(); |
| } |
| FFEffectKind::Spring { condition } | FFEffectKind::Friction { condition } => { |
| effect.u.condition = [condition[0].into(), condition[1].into()]; |
| } |
| FFEffectKind::Rumble { |
| strong_magnitude, |
| weak_magnitude, |
| } => { |
| effect.u.rumble.strong_magnitude = strong_magnitude; |
| effect.u.rumble.weak_magnitude = weak_magnitude; |
| } |
| _ => (), |
| } |
| |
| effect |
| } |
| } |