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
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 {
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;