gilrs_core/platform/linux/
ff.rs
1use std::fs::File;
9use std::io::{Error as IoError, ErrorKind, Result as IoResult, Write};
10use std::os::unix::io::AsRawFd;
11use std::{mem, slice};
12
13use super::ioctl::{self, ff_effect, ff_replay, ff_rumble_effect, input_event};
14use nix::errno::Errno;
15use std::time::Duration;
16
17#[derive(Debug)]
18pub struct Device {
19 effect: i16,
20 file: File,
21}
22
23impl Device {
24 pub(crate) fn new(path: &str) -> IoResult<Self> {
25 let file = File::create(path)?;
26 let mut effect = ff_effect {
27 type_: FF_RUMBLE,
28 id: -1,
29 direction: 0,
30 trigger: Default::default(),
31 replay: Default::default(),
32 u: Default::default(),
33 };
34
35 #[allow(clippy::unnecessary_mut_passed)]
36 let res = unsafe { ioctl::eviocsff(file.as_raw_fd(), &mut effect) };
37
38 if res.is_err() {
39 Err(IoError::new(ErrorKind::Other, "Failed to create effect"))
40 } else {
41 Ok(Device {
42 effect: effect.id,
43 file,
44 })
45 }
46 }
47
48 pub fn set_ff_state(&mut self, strong: u16, weak: u16, min_duration: Duration) {
49 let duration = min_duration.as_secs() * 1000 + u64::from(min_duration.subsec_millis());
50 let duration = if duration > u64::from(u16::MAX) {
51 u16::MAX
52 } else {
53 duration as u16
54 };
55
56 let mut effect = ff_effect {
57 type_: FF_RUMBLE,
58 id: self.effect,
59 direction: 0,
60 trigger: Default::default(),
61 replay: ff_replay {
62 delay: 0,
63 length: duration,
64 },
65 u: Default::default(),
66 };
67
68 unsafe {
69 let rumble = &mut effect.u as *mut _ as *mut ff_rumble_effect;
70 (*rumble).strong_magnitude = strong;
71 (*rumble).weak_magnitude = weak;
72
73 if let Err(err) = ioctl::eviocsff(self.file.as_raw_fd(), &effect) {
74 error!(
75 "Failed to modify effect of gamepad {:?}, error: {}",
76 self.file, err
77 );
78
79 return;
80 }
81 };
82
83 let time = libc::timeval {
84 tv_sec: 0,
85 tv_usec: 0,
86 };
87 let ev = input_event {
88 type_: EV_FF,
89 code: self.effect as u16,
90 value: 1,
91 time,
92 };
93
94 let size = mem::size_of::<input_event>();
95 let s = unsafe { slice::from_raw_parts(&ev as *const _ as *const u8, size) };
96
97 match self.file.write(s) {
98 Ok(s) if s == size => (),
99 Ok(_) => unreachable!(),
100 Err(e) => error!("Failed to set ff state: {}", e),
101 }
102 }
103}
104
105impl Drop for Device {
106 fn drop(&mut self) {
107 #[cfg(target_os = "linux")]
108 let effect = self.effect as ::libc::c_ulong;
109 #[cfg(not(target_os = "linux"))]
110 let effect = self.effect as ::libc::c_int;
111
112 if let Err(err) = unsafe { ioctl::eviocrmff(self.file.as_raw_fd(), effect) } {
113 if err != Errno::ENODEV {
114 error!(
115 "Failed to remove effect of gamepad {:?}: {}",
116 self.file, err
117 )
118 }
119 };
120 }
121}
122
123const EV_FF: u16 = 0x15;
124const FF_RUMBLE: u16 = 0x50;