use super::NativeManager;
#[repr(C)]
struct TimeVal {
tv_sec: isize,
tv_usec: isize,
}
#[repr(C)]
struct Event {
ev_time: TimeVal,
ev_type: i16,
ev_code: i16,
ev_value: i32,
}
#[derive(Copy, Clone)]
#[repr(u8)]
pub enum Btn {
Left = 0u8,
Right = 1,
Up = 2,
Down = 3,
X = 4,
A = 5,
Y = 6,
B = 7,
L = 8,
R = 9,
W = 10,
Z = 11,
F = 12,
E = 13,
D = 14,
C = 15,
}
impl From<Btn> for u8 {
fn from(b: Btn) -> Self {
b as u8
}
}
#[derive(Debug, Copy, Clone, Default)]
#[repr(C)]
pub struct Device {
joy: (i8, i8),
lrt: (u8, u8),
cam: (i8, i8),
pan: i16,
btn: u64,
native_handle: u32,
hardware_id: u32,
abs_min: i32,
abs_max: i32,
}
impl std::fmt::Display for Device {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
let joy: (f32, f32) = (
(self.joy.0 as f32) / (std::i8::MAX as f32),
(self.joy.1 as f32) / (std::i8::MAX as f32),
);
let cam: (f32, f32) = (
(self.cam.0 as f32) / (std::i8::MAX as f32),
(self.cam.1 as f32) / (std::i8::MAX as f32),
);
let lrt: (f32, f32) = (
(self.lrt.0 as f32) / (std::u8::MAX as f32),
(self.lrt.1 as f32) / (std::u8::MAX as f32),
);
let pan: f32 = (self.pan as f32) / (std::i16::MAX as f32);
let b_btn: char = if self.btn(Btn::B) == Some(true) {
'▣'
} else {
'□'
};
let a_btn: char = if self.btn(Btn::A) == Some(true) {
'▣'
} else {
'□'
};
let y_btn: char = if self.btn(Btn::Y) == Some(true) {
'▣'
} else {
'□'
};
let x_btn: char = if self.btn(Btn::X) == Some(true) {
'▣'
} else {
'□'
};
let dl: char = if self.btn(Btn::Left) == Some(true) {
'▣'
} else {
'□'
};
let dr: char = if self.btn(Btn::Right) == Some(true) {
'▣'
} else {
'□'
};
let du: char = if self.btn(Btn::Up) == Some(true) {
'▣'
} else {
'□'
};
let dd: char = if self.btn(Btn::Down) == Some(true) {
'▣'
} else {
'□'
};
let w_btn: char = if self.btn(Btn::W) == Some(true) {
'▣'
} else {
'□'
};
let z_btn: char = if self.btn(Btn::Z) == Some(true) {
'▣'
} else {
'□'
};
let l_btn: char = if self.btn(Btn::L) == Some(true) {
'▣'
} else {
'□'
};
let r_btn: char = if self.btn(Btn::R) == Some(true) {
'▣'
} else {
'□'
};
let d_btn: char = if self.btn(Btn::D) == Some(true) {
'▣'
} else {
'□'
};
let c_btn: char = if self.btn(Btn::C) == Some(true) {
'▣'
} else {
'□'
};
let f_btn: char = if self.btn(Btn::F) == Some(true) {
'▣'
} else {
'□'
};
let e_btn: char = if self.btn(Btn::E) == Some(true) {
'▣'
} else {
'□'
};
write!(
f,
"j({:.2},{:.2}) p({:.2}) c({:.2},{:.2}) T({:.2},{:.2}) b{} a{} y{} x{} ←{} →{} \
↑{} ↓{} l{} r{} w{} z{} f{} e{} d{} c{}",
joy.0,
joy.1,
pan,
cam.0,
cam.1,
lrt.0,
lrt.1,
b_btn,
a_btn,
y_btn,
x_btn,
dl,
dr,
du,
dd,
l_btn,
r_btn,
w_btn,
z_btn,
f_btn,
e_btn,
d_btn,
c_btn,
)
}
}
impl Device {
pub fn joy(&self) -> Option<(f32, f32)> {
Some((
(self.joy.0 as f32) / (std::i8::MAX as f32),
(self.joy.1 as f32) / (std::i8::MAX as f32),
))
}
pub fn cam(&mut self) -> Option<(f32, f32)> {
#[allow(clippy::single_match)]
match self.hardware_id {
0x_07B5_0316 => return None,
_ => {}
}
if self.cam.0 == -128 || self.cam.1 == -128 || self.pan == -128 {
return None;
}
let rtn = (
(self.cam.0 as f32) / (std::i8::MAX as f32),
(self.cam.1 as f32) / (std::i8::MAX as f32),
);
self.cam = (-128, -128);
self.pan = std::i16::MIN;
Some(rtn)
}
pub fn dir(&mut self) -> Option<(f32, f32)> {
#[allow(clippy::single_match)]
match self.hardware_id {
0x_07B5_0316 => return None,
_ => {}
}
if self.cam.0 == -128 || self.cam.1 == -128 || self.pan == std::i16::MIN {
return None;
}
let rtn = (
(self.cam.0 as f32) / (std::i8::MAX as f32),
(self.pan as f32) / (std::i16::MAX as f32),
);
self.cam = (-128, -128);
self.pan = std::i16::MIN;
Some(rtn)
}
pub fn pitch(&mut self) -> Option<f32> {
if self.cam.0 == -128 || self.cam.1 == -128 || self.pan == std::i16::MIN {
return None;
}
let rtn = (self.pan as f32) / (std::i16::MAX as f32);
self.pan = std::i16::MIN;
Some(rtn)
}
pub fn btn<B: Into<u8>>(&self, b: B) -> Option<bool> {
Some(self.btn & (1 << (b.into())) != 0)
}
pub fn mod_swap_btn<B: Into<u8> + Copy + Clone>(&mut self, a: B, b: B) {
let new_b = self.btn(a).unwrap();
let new_a = self.btn(b).unwrap();
if new_a {
self.btn |= 1 << a.into();
} else {
self.btn &= !(1 << a.into());
}
if new_b {
self.btn |= 1 << b.into();
} else {
self.btn &= !(1 << b.into());
}
}
pub fn mod_swap_joy(&mut self) {
std::mem::swap(&mut self.joy.0, &mut self.joy.1)
}
pub fn mod_l2pitch(&mut self) {
let l = self.lrt.0 as i8;
self.pan = ((l as i32 * std::i16::MAX as i32) / 127) as i16;
}
pub fn mod_t2lr(&mut self) {
if self.lrt.0 == 255 {
self.btn |= 1 << Btn::L as u8;
} else {
self.btn &= !(1 << Btn::L as u8);
}
if self.lrt.1 == 255 {
self.btn |= 1 << Btn::R as u8;
} else {
self.btn &= !(1 << Btn::R as u8);
}
}
pub fn mod_expand(&mut self) {
self.joy.0 = self
.joy
.0
.saturating_add((3 * self.joy.0 as i16 / 4) as i8)
.max(-127);
self.joy.1 = self
.joy
.1
.saturating_add((3 * self.joy.1 as i16 / 4) as i8)
.max(-127);
self.cam.0 = self
.cam
.0
.saturating_add((3 * self.cam.0 as i16 / 4) as i8)
.max(-127);
self.cam.1 = self
.cam
.1
.saturating_add((3 * self.cam.1 as i16 / 4) as i8)
.max(-127);
let lrt0 = (self.lrt.0 as i8).overflowing_add(-127).0;
self.lrt.0 = ((lrt0.saturating_add((3 * lrt0 as i16 / 4) as i8).max(-127)) as u8)
.overflowing_add(127)
.0;
let lrt1 = (self.lrt.1 as i8).overflowing_add(-127).0;
self.lrt.1 = ((lrt1.saturating_add((3 * lrt1 as i16 / 4) as i8).max(-127)) as u8)
.overflowing_add(127)
.0;
}
}
pub struct Port {
manager: NativeManager,
controllers: Vec<Device>,
}
impl Port {
pub fn new() -> Port {
let manager = NativeManager::new();
let controllers = vec![];
Port {
manager,
controllers,
}
}
pub fn update(&mut self) -> u16 {
for mut controller in &mut self.controllers {
let (fd, is_out, ne) = self.manager.get_fd(controller.native_handle as usize);
if ne {
continue;
}
if is_out {
self.manager.disconnect(fd);
continue;
}
while joystick_poll_event(fd, &mut controller) {}
controller.pan = controller.pan.saturating_add(controller.cam.1 as i16 * 8);
}
let (device_count, added) = self.manager.search();
if added != ::std::usize::MAX {
let (min, max, _) = self.manager.get_abs(added);
self.controllers.resize_with(device_count, Default::default);
self.controllers[added] = Device {
native_handle: added as u32,
hardware_id: self.manager.get_id(added).0,
abs_min: min,
abs_max: max,
joy: (0, 0),
cam: (0, 0),
lrt: (0, 0),
pan: 0,
btn: 0,
};
}
self.controllers.len() as u16
}
pub fn get(&self, stick: u16) -> Device {
let mut rtn = self.controllers[stick as usize];
match rtn.hardware_id {
0x_0E6F_0501 => {
rtn.mod_swap_btn(Btn::A, Btn::B);
rtn.mod_t2lr();
}
0x_054C_0268 => {
rtn.mod_swap_btn(Btn::X, Btn::Y);
rtn.mod_t2lr();
}
0x_07B5_0316 => rtn.mod_l2pitch(),
0x_0079_1844 => rtn.mod_expand(),
_ => {}
}
rtn
}
pub fn swap(&mut self, a: u16, b: u16) {
self.controllers.swap(a as usize, b as usize);
}
#[allow(unused)]
pub fn name(&self, a: u16) -> String {
"Unknown".to_string()
}
}
fn joystick_poll_event(fd: i32, device: &mut Device) -> bool {
extern "C" {
fn read(fd: i32, buf: *mut Event, count: usize) -> isize;
}
let mut js = unsafe { std::mem::uninitialized() };
let bytes = unsafe { read(fd, &mut js, std::mem::size_of::<Event>()) };
if bytes != (std::mem::size_of::<Event>() as isize) {
return false;
}
fn edit<B: Into<u8>>(is: bool, device: &mut Device, b: B) {
if is {
device.btn |= 1 << b.into()
} else {
device.btn &= !(1 << b.into())
}
}
match js.ev_type {
0x01 => {
let is = js.ev_value == 1;
match js.ev_code - 0x120 {
0 | 19 => edit(is, device, Btn::X),
1 | 17 => edit(is, device, Btn::A),
2 | 16 => edit(is, device, Btn::B),
3 | 20 => edit(is, device, Btn::Y),
4 | 24 => edit(is, device, Btn::L),
5 | 25 => edit(is, device, Btn::R),
6 | 22 => edit(is, device, Btn::W),
7 | 23 => edit(is, device, Btn::Z),
8 | 26 => edit(is, device, Btn::F),
9 | 27 => edit(is, device, Btn::E),
10 => println!("Button 10 is Unknown"),
12 | 256 => edit(is, device, Btn::Up),
13 | 259 => edit(is, device, Btn::Right),
14 | 257 => edit(is, device, Btn::Down),
15 | 258 => edit(is, device, Btn::Left),
18 => println!("Button 18 is Unknown"),
21 => println!("Button 21 is Unknown"),
28 => println!("Button 28 is Unknown"),
29 => edit(is, device, Btn::D),
30 => edit(is, device, Btn::C),
a => println!("Button {} is Unknown", a),
}
}
0x03 => {
let value = transform(device.abs_min, device.abs_max, js.ev_value);
let (cam_x, cam_y, lrt_l, lrt_r) = match device.hardware_id {
0x_0079_1844 => (5, 2, 3, 4),
_ => (3, 4, 2, 5),
};
match js.ev_code {
0 => device.joy.0 = value,
1 => device.joy.1 = value,
16 => {
if js.ev_value < 0 {
edit(true, device, Btn::Left);
edit(false, device, Btn::Right);
} else if js.ev_value > 0 {
edit(false, device, Btn::Left);
edit(true, device, Btn::Right);
} else {
edit(false, device, Btn::Left);
edit(false, device, Btn::Right);
}
}
17 => {
if js.ev_value < 0 {
edit(true, device, Btn::Up);
edit(false, device, Btn::Down);
} else if js.ev_value > 0 {
edit(false, device, Btn::Up);
edit(true, device, Btn::Down);
} else {
edit(false, device, Btn::Up);
edit(false, device, Btn::Down);
}
}
40 => {}
a => {
if a == cam_x {
device.cam.0 = value;
} else if a == cam_y {
device.cam.1 = value;
} else if a == lrt_l {
js.ev_value = js.ev_value.max(-127);
device.lrt.0 = js.ev_value as u8;
} else if a == lrt_r {
js.ev_value = js.ev_value.max(-127);
device.lrt.1 = js.ev_value as u8;
}
}
}
}
_ => {}
}
true
}
fn deadzone(min: i32, max: i32, val: i32) -> (i32, i32) {
let range = max - min;
let halfr = range >> 1;
let deadz = halfr >> 2;
let midpt = min + halfr;
let value = val - midpt;
let value = if value < deadz {
if value > -deadz {
0
} else {
value + deadz
}
} else {
value - deadz
};
(value, (range >> 1) - deadz)
}
fn transform(min: i32, max: i32, val: i32) -> i8 {
let (value, full) = deadzone(min, max, val);
((value * 127) / full).max(-127).min(127) as i8
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn transform_test() {
let a = deadzone(-100, 100, 100);
assert_eq!(a.0, a.1);
assert_eq!(75, a.1);
let b = deadzone(-100, 100, -100);
assert_eq!(b.0, -b.1);
assert_eq!(75, b.1);
let c = deadzone(-100, 100, 0);
assert_eq!(c.0, 0);
assert_eq!(75, b.1);
assert_eq!(transform(-100, 100, 100), 127);
assert_eq!(transform(-100, 100, -100), -127);
assert_eq!(transform(-100, 100, 0), 0);
assert_eq!(transform(-128, 127, 127), 127);
assert_eq!(transform(-128, 127, 0), 0);
assert_eq!(transform(-128, 127, -128), -127);
}
}