1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
// Copyright 2016 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 ff::{EffectData, Waveform, Trigger, Error};
use super::gamepad::Gamepad;
use ioctl::{ff_effect, input_event};
use ioctl;
use libc as c;
use std::mem;
use constants;
use super::ioctl_def;

#[derive(Debug)]
pub struct Effect {
    id: i16,
    fd: i32,
}

impl Effect {
    pub fn new(gamepad: &Gamepad, data: EffectData) -> Result<Self, Error> {
        let mut data: ff_effect = data.into();
        let res = unsafe { ioctl::eviocsff(gamepad.fd(), &mut data) };
        if res == -1 {
            Err(Error::EffectNotSupported)
        } else {
            Ok(Effect {
                id: data.id,
                fd: gamepad.fd(),
            })
        }
    }

    pub fn upload(&mut self, data: EffectData) -> Result<(), Error> {
        let mut data: ff_effect = data.into();
        data.id = self.id;
        let res = unsafe { ioctl::eviocsff(self.fd, &mut data) };
        if res == -1 { Err(Error::EffectNotSupported) } else { Ok(()) }
    }

    pub fn play(&mut self, n: u16) -> Result<(), Error> {
        let ev = input_event {
            _type: EV_FF,
            code: self.id as u16,
            value: n as i32,
            time: unsafe { mem::uninitialized() },
        };

        if unsafe { c::write(self.fd, mem::transmute(&ev), 24) } == -1 {
            Err(Error::FailedToPlay)
        } else {
            Ok(())
        }
    }

    pub fn stop(&mut self) {
        let _ = self.play(0);
    }
}

impl Drop for Effect {
    fn drop(&mut self) {
        unsafe {
            // bug in ioctl crate, second argument is i32 not pointer to i32
            ioctl_def::eviocrmff(self.fd, mem::transmute(self.id as isize));
        }
    }
}

impl Into<ff_effect> for EffectData {
    fn into(self) -> ff_effect {
        let mut effect = ff_effect {
            _type: FF_PERIODIC,
            id: -1,
            direction: self.direction.angle,
            trigger: self.trigger.into(),
            replay: unsafe { mem::transmute(self.replay) },
            u: unsafe { mem::uninitialized() },
        };
        unsafe {
            let mut periodic = effect.u.periodic();
            (*periodic).waveform = self.wave.into();
            (*periodic).period = self.period;
            (*periodic).magnitude = self.magnitude;
            (*periodic).offset = self.offset;
            (*periodic).phase = self.phase;
            (*periodic).envelope = mem::transmute(self.envelope);
        }
        effect
    }
}

impl Into<u16> for Waveform {
    fn into(self) -> u16 {
        match self {
            Waveform::Square => FF_SQUARE,
            Waveform::Triangle => FF_TRIANGLE,
            Waveform::Sine => FF_SINE,
        }
    }
}

impl Into<ioctl::ff_trigger> for Trigger {
    fn into(self) -> ioctl::ff_trigger {
        let mut val = self.button as u16;
        if val >= constants::BTN_SOUTH && val <= constants::BTN_RTHUMB {
            val += BTN_GAMEPAD;
        } else if val >= constants::BTN_DPAD_UP && val <= constants::BTN_DPAD_RIGHT {
            val += BTN_DPAD_UP - constants::BTN_DPAD_UP;
        } else {
            val = 0;
        };
        ioctl::ff_trigger {
            button: val,
            interval: self.interval,
        }
    }
}

const EV_FF: u16 = 0x15;

const FF_PERIODIC: u16 = 0x51;
const FF_SQUARE: u16 = 0x58;
const FF_TRIANGLE: u16 = 0x59;
const FF_SINE: u16 = 0x5a;
const BTN_GAMEPAD: u16 = 0x130;
const BTN_DPAD_UP: u16 = 0x220;