use std::ffi::CString;
use std::fs;
use std::mem;
extern "C" {
fn open(pathname: *const u8, flags: i32) -> i32;
fn close(fd: i32) -> i32;
fn fcntl(fd: i32, cmd: i32, v: i32) -> i32;
}
struct Device {
name: Option<String>,
fd: i32,
}
impl PartialEq for Device {
fn eq(&self, other: &Device) -> bool {
if let Some(ref name) = self.name {
if let Some(ref name2) = other.name {
name == name2
} else {
false
}
} else {
false
}
}
}
pub struct NativeManager {
devices: Vec<Device>,
}
impl NativeManager {
pub fn new() -> NativeManager {
NativeManager {
devices: Vec::new(),
}
}
pub fn search(&mut self) -> (usize, usize) {
let devices = find_devices();
for mut i in devices {
if self.devices.contains(&i) {
continue;
}
open_joystick(&mut i);
if i.fd != -1 {
joystick_async(i.fd);
let index = self.add(i);
return (self.devices.len(), index);
}
}
(self.num_plugged_in(), ::std::usize::MAX)
}
pub fn get_id(&self, id: usize) -> (u32, bool) {
if id >= self.devices.len() {
(0, true)
} else {
let (a, b) = joystick_id(self.devices[id].fd);
(a, b)
}
}
pub fn get_abs(&self, id: usize) -> (i32, i32, bool) {
if id >= self.devices.len() {
(0, 0, true)
} else {
joystick_abs(self.devices[id].fd)
}
}
pub fn get_fd(&self, id: usize) -> (i32, bool, bool) {
let (_, unplug) = self.get_id(id);
(self.devices[id].fd, unplug, self.devices[id].name == None)
}
pub fn num_plugged_in(&self) -> usize {
self.devices.len()
}
pub fn disconnect(&mut self, fd: i32) {
for i in 0..self.devices.len() {
if self.devices[i].fd == fd {
joystick_drop(fd);
self.devices[i].name = None;
return;
}
}
panic!("There was no fd of {}", fd);
}
fn add(&mut self, device: Device) -> usize {
let mut r = 0;
for i in &mut self.devices {
if i.name == None {
*i = device;
return r;
}
r += 1;
}
self.devices.push(device);
r
}
}
impl Drop for NativeManager {
fn drop(&mut self) {
while let Some(device) = self.devices.pop() {
self.disconnect(device.fd);
}
}
}
fn find_devices() -> Vec<Device> {
let mut rtn = Vec::new();
let paths = fs::read_dir("/dev/input/by-id/");
let paths = if let Ok(paths) = paths {
paths
} else {
return vec![];
};
for path in paths {
let path_str = path.unwrap().path();
let path_str = path_str.to_str().unwrap();
if path_str.ends_with("-event-joystick") {
rtn.push(Device {
name: Some(path_str.to_string()),
fd: -1,
});
}
}
rtn
}
fn open_joystick(device: &mut Device) {
let file_name = CString::new(device.name.clone().unwrap()).unwrap();
device.fd = unsafe { open(file_name.as_ptr() as *const _, 0) };
}
fn joystick_async(fd: i32) {
let error = unsafe { fcntl(fd, 0x4, 0x800) } == -1;
if error {
panic!("Joystick unplugged 2!");
}
}
fn joystick_id(fd: i32) -> (u32, bool) {
let mut a = [0u16; 4];
extern "C" {
fn ioctl(fd: i32, request: usize, v: *mut u16) -> i32;
}
if unsafe { ioctl(fd, 0x_8008_4502, &mut a[0]) } == -1 {
return (0, true);
}
(((a[1] as u32) << 16) | (a[2] as u32), false)
}
fn joystick_abs(fd: i32) -> (i32, i32, bool) {
#[derive(Debug)]
#[repr(C)]
struct AbsInfo {
value: i32,
minimum: i32,
maximum: i32,
fuzz: i32,
flat: i32,
resolution: i32,
}
let mut a = unsafe { mem::uninitialized() };
extern "C" {
fn ioctl(fd: i32, request: usize, v: *mut AbsInfo) -> i32;
}
if unsafe { ioctl(fd, 0x_8018_4540, &mut a) } == -1 {
return (0, 0, true);
}
(a.minimum, a.maximum, false)
}
fn joystick_drop(fd: i32) {
if unsafe { close(fd) == -1 } {
panic!("Failed to disconnect joystick.");
}
}