Skip to content

Commit

Permalink
feat: added xbox joypad, separated c_bindings
Browse files Browse the repository at this point in the history
Added test using SDL
  • Loading branch information
ABeltramo committed Aug 4, 2024
1 parent 0b57533 commit 7f197ff
Show file tree
Hide file tree
Showing 9 changed files with 221 additions and 22 deletions.
1 change: 1 addition & 0 deletions bindings/rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ pkg-config = "0.3.30"
input = "0.9.0"
rustix = { version = "0.38.18", features = ["fs"] }
approx = "0.5.1"
sdl2 = "0.37.0"
5 changes: 5 additions & 0 deletions bindings/rust/src/c_bindings.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(dead_code)]
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
7 changes: 4 additions & 3 deletions bindings/rust/src/common.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use std::ffi::{CString};
use crate::c_bindings;

#[allow(dead_code)]
pub struct InputtinoDeviceDefinition {
pub def: super::InputtinoDeviceDefinition,
pub def: c_bindings::InputtinoDeviceDefinition,
// Keep those around since we are passing them as pointers
name: CString,
phys: CString,
Expand All @@ -14,7 +15,7 @@ impl InputtinoDeviceDefinition {
let name = CString::new(name).unwrap();
let phys = CString::new(phys).unwrap();
let uniq = CString::new(uniq).unwrap();
let def = super::InputtinoDeviceDefinition {
let def = c_bindings::InputtinoDeviceDefinition {
name: name.as_ptr(),
vendor_id: vendor_id,
product_id: product_id,
Expand Down Expand Up @@ -59,7 +60,7 @@ macro_rules! make_device {
($fn_call:expr, $device:expr) => {
{
let error_str = std::ptr::null_mut();
let error_handler = super::InputtinoErrorHandler {
let error_handler = crate::c_bindings::InputtinoErrorHandler {
eh: Some(error_handler_fn),
user_data: error_str,
};
Expand Down
73 changes: 73 additions & 0 deletions bindings/rust/src/joypad_xbox.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
use std::ffi::{c_int, c_void};
use crate::{get_nodes, make_device};
use crate::common::{InputtinoDeviceDefinition, error_handler_fn};
use crate::c_bindings::{inputtino_joypad_xone_create, inputtino_joypad_xone_destroy, inputtino_joypad_xone_get_nodes, inputtino_joypad_xone_set_on_rumble, inputtino_joypad_xone_set_pressed_buttons, inputtino_joypad_xone_set_stick, inputtino_joypad_xone_set_triggers};

// re-export INPUTTINO_JOYPAD_BTN and INPUTTINO_JOYPAD_STICK_POSITION
pub use crate::c_bindings::{INPUTTINO_JOYPAD_BTN, INPUTTINO_JOYPAD_STICK_POSITION};

pub struct InputtinoXOneJoypad {
joypad: *mut crate::c_bindings::InputtinoXOneJoypad,
on_rumble_fn: Box<dyn FnMut(i32, i32) -> ()>,
}

impl InputtinoXOneJoypad {
pub fn new(device: &InputtinoDeviceDefinition) -> Result<Self, String> {
unsafe {
let dev = make_device!(inputtino_joypad_xone_create, device);
match dev {
Ok(joypad) => {
Ok(InputtinoXOneJoypad { joypad, on_rumble_fn: Box::new(|_, _| {}) })
}
Err(e) => Err(e),
}
}
}

pub fn get_nodes(&self) -> Result<Vec<String>, String> {
unsafe {
get_nodes!(inputtino_joypad_xone_get_nodes, self.joypad)
}
}

pub fn set_pressed(&self, buttons: i32) {
unsafe {
inputtino_joypad_xone_set_pressed_buttons(self.joypad, buttons);
}
}

pub fn set_triggers(&self, left_trigger: i16, right_trigger: i16) {
unsafe {
inputtino_joypad_xone_set_triggers(self.joypad, left_trigger, right_trigger);
}
}

pub fn set_stick(&self, stick_type: INPUTTINO_JOYPAD_STICK_POSITION, x: i16, y: i16) {
unsafe {
inputtino_joypad_xone_set_stick(self.joypad, stick_type, x, y);
}
}

pub fn set_on_rumble(&mut self, on_rumble_fn: impl FnMut(i32, i32) -> () + 'static) {
self.on_rumble_fn = Box::new(on_rumble_fn);
unsafe {
let state_ptr = self as *const _ as *mut c_void;
inputtino_joypad_xone_set_on_rumble(self.joypad, Some(on_rumble_c_fn), state_ptr);
}
}
}

impl Drop for InputtinoXOneJoypad {
fn drop(&mut self) {
unsafe {
inputtino_joypad_xone_destroy(self.joypad);
}
}
}

#[allow(dead_code)]
pub unsafe extern "C" fn on_rumble_c_fn(left_motor: c_int, right_motor: c_int, user_data: *mut ::core::ffi::c_void) {
println!("on_rumble_c_fn {:?}-{:?}", left_motor, right_motor);
let joypad: &mut InputtinoXOneJoypad = &mut *(user_data as *mut InputtinoXOneJoypad);
((*joypad).on_rumble_fn)(left_motor, right_motor);
}
11 changes: 6 additions & 5 deletions bindings/rust/src/keyboard.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use crate::common::{error_handler_fn, InputtinoDeviceDefinition};
use crate::{get_nodes, inputtino_keyboard_create, inputtino_keyboard_get_nodes, make_device};
use crate::{get_nodes, make_device};
use crate::c_bindings::{inputtino_keyboard_create, inputtino_keyboard_get_nodes, inputtino_keyboard_press, inputtino_keyboard_release, inputtino_keyboard_destroy};

pub struct InputtinoKeyboard {
kb: *mut super::InputtinoKeyboard,
kb: *mut crate::c_bindings::InputtinoKeyboard,
}

impl InputtinoKeyboard {
Expand All @@ -24,21 +25,21 @@ impl InputtinoKeyboard {

pub fn press_key(&self, key: i16) {
unsafe {
super::inputtino_keyboard_press(self.kb, key);
inputtino_keyboard_press(self.kb, key);
}
}

pub fn release_key(&self, key: i16) {
unsafe {
super::inputtino_keyboard_release(self.kb, key);
inputtino_keyboard_release(self.kb, key);
}
}
}

impl Drop for InputtinoKeyboard {
fn drop(&mut self) {
unsafe {
super::inputtino_keyboard_destroy(self.kb);
inputtino_keyboard_destroy(self.kb);
}
}
}
8 changes: 2 additions & 6 deletions bindings/rust/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]

include!(concat!(env!("OUT_DIR"), "/bindings.rs"));

pub mod common;
pub mod mouse;
pub mod keyboard;
pub mod joypad_xbox;
mod c_bindings;
15 changes: 9 additions & 6 deletions bindings/rust/src/mouse.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use crate::{get_nodes, inputtino_mouse_create, inputtino_mouse_destroy, inputtino_mouse_get_nodes, inputtino_mouse_move, inputtino_mouse_move_absolute, inputtino_mouse_press_button, inputtino_mouse_release_button, inputtino_mouse_scroll_horizontal, inputtino_mouse_scroll_vertical, make_device};
use crate::{get_nodes, make_device};
use crate::common::{InputtinoDeviceDefinition, error_handler_fn};
use crate::c_bindings::{inputtino_mouse_create, inputtino_mouse_destroy, inputtino_mouse_get_nodes, inputtino_mouse_move, inputtino_mouse_move_absolute, inputtino_mouse_press_button, inputtino_mouse_release_button, inputtino_mouse_scroll_horizontal, inputtino_mouse_scroll_vertical};

pub use crate::c_bindings::{INPUTTINO_MOUSE_BUTTON};

pub struct InputtinoMouse {
mouse: *mut super::InputtinoMouse,
mouse: *mut crate::c_bindings::InputtinoMouse,
}

impl InputtinoMouse {
Expand Down Expand Up @@ -34,13 +37,13 @@ impl InputtinoMouse {
}
}

pub fn press_button(&self, button: super::INPUTTINO_MOUSE_BUTTON) {
pub fn press_button(&self, button: INPUTTINO_MOUSE_BUTTON) {
unsafe {
inputtino_mouse_press_button(self.mouse, button);
}
}

pub fn release_button(&self, button: super::INPUTTINO_MOUSE_BUTTON) {
pub fn release_button(&self, button: INPUTTINO_MOUSE_BUTTON) {
unsafe {
inputtino_mouse_release_button(self.mouse, button);
}
Expand Down Expand Up @@ -77,7 +80,7 @@ mod tests {
let device_name = CString::new("Rusty Mouse").unwrap();
let device_phys = CString::new("Rusty Mouse Phys").unwrap();
let device_uniq = CString::new("Rusty Mouse Uniq").unwrap();
let def = crate::InputtinoDeviceDefinition {
let def = crate::c_bindings::InputtinoDeviceDefinition {
name: device_name.as_ptr(),
vendor_id: 0,
product_id: 0,
Expand All @@ -87,7 +90,7 @@ mod tests {
};
// TODO: test this somehow
let error_str = std::ptr::null_mut();
let error_handler = crate::InputtinoErrorHandler {
let error_handler = crate::c_bindings::InputtinoErrorHandler {
eh: Some(error_handler_fn),
user_data: error_str,
};
Expand Down
120 changes: 120 additions & 0 deletions bindings/rust/tests/joypad_xbox.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
use inputtino_rs::common::InputtinoDeviceDefinition;
use inputtino_rs::joypad_xbox::{InputtinoXOneJoypad, INPUTTINO_JOYPAD_BTN, INPUTTINO_JOYPAD_STICK_POSITION};

#[test]
fn test_xbox_joypad() {
let device = InputtinoDeviceDefinition::new("Rusty XOne controller", 0x045e, 0x02dd, 0x0100, "00:11:22:33:44", "00:11:22:33:44");
let mut joypad = InputtinoXOneJoypad::new(&device).unwrap();

let nodes = joypad.get_nodes().unwrap();
{
assert_eq!(nodes.len(), 2);
assert!(nodes[0].starts_with("/dev/input/event"));
assert!(nodes[1].starts_with("/dev/input/js"));
}

let sdl = sdl2::init().unwrap();
let joystick_subsystem = sdl.game_controller().unwrap();
let mut sdl_js = joystick_subsystem.open(0).unwrap();
let mut event_pump = sdl.event_pump().unwrap();

for event in event_pump.poll_iter() {
match event {
sdl2::event::Event::JoyDeviceAdded { which, .. } => {
assert_eq!(which, 0);
}
sdl2::event::Event::ControllerDeviceAdded { which, .. } => {
assert_eq!(which, 0);
}
_ => panic!("Unexpected event : {:?}", event),
}
}

assert_eq!(sdl_js.name(), "Xbox One Controller");
assert!(sdl_js.has_rumble());

{
joypad.set_pressed(INPUTTINO_JOYPAD_BTN::A as i32);
for event in event_pump.wait_timeout_iter(50) {
match event {
sdl2::event::Event::ControllerButtonDown { button, .. } => {
assert_eq!(button, sdl2::controller::Button::A);
}
sdl2::event::Event::JoyButtonDown { button_idx, .. } => {
assert_eq!(button_idx, sdl2::controller::Button::A as u8);
break;
}
_ => panic!("Unexpected event : {:?}", event),
}
}
}

{
joypad.set_triggers(0, 0);
for event in event_pump.wait_timeout_iter(50) {
match event {
sdl2::event::Event::ControllerAxisMotion { axis, value, .. } => {
assert_eq!(axis, sdl2::controller::Axis::TriggerLeft);
assert_eq!(value, 0);
}
sdl2::event::Event::JoyAxisMotion { axis_idx, value, .. } => {
assert_eq!(axis_idx, sdl2::controller::Axis::TriggerLeft as u8);
assert_eq!(value, 0);
break;
}
_ => panic!("Unexpected event : {:?}", event),
}
}
}

{
joypad.set_stick(INPUTTINO_JOYPAD_STICK_POSITION::LS, 0, 0);
for event in event_pump.wait_timeout_iter(50) {
match event {
sdl2::event::Event::ControllerAxisMotion { axis, value, .. } => {
assert_eq!(axis, sdl2::controller::Axis::LeftX);
assert_eq!(value, 0);
}
sdl2::event::Event::JoyAxisMotion { axis_idx, value, .. } => {
assert_eq!(axis_idx, sdl2::controller::Axis::LeftX as u8);
assert_eq!(value, 0);
break;
}
_ => panic!("Unexpected event : {:?}", event),
}
}
}

{
joypad.set_stick(INPUTTINO_JOYPAD_STICK_POSITION::RS, 0, 0);
for event in event_pump.wait_timeout_iter(50) {
match event {
sdl2::event::Event::ControllerAxisMotion { axis, value, .. } => {
assert_eq!(axis, sdl2::controller::Axis::RightX);
assert_eq!(value, 0);
}
sdl2::event::Event::JoyAxisMotion { axis_idx, value, .. } => {
assert_eq!(axis_idx, sdl2::controller::Axis::RightX as u8);
assert_eq!(value, 0);
break;
}
_ => panic!("Unexpected event : {:?}", event),
}
}
}

{
joypad.set_on_rumble(move |left, right| {
assert_eq!(left, 100);
assert_eq!(right, 200);
});
let res = sdl_js.set_rumble(100, 200, 150);
assert!(res.is_ok());
std::thread::sleep(std::time::Duration::from_millis(25));
joypad.set_on_rumble(move |left, right| {
assert_eq!(left, 0);
assert_eq!(right, 0);
});
std::thread::sleep(std::time::Duration::from_millis(125));
}
}
3 changes: 1 addition & 2 deletions bindings/rust/tests/mouse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
extern crate approx;

use inputtino_rs::{
INPUTTINO_MOUSE_BUTTON,
mouse::InputtinoMouse,
mouse::{InputtinoMouse, INPUTTINO_MOUSE_BUTTON},
common::InputtinoDeviceDefinition,
};
use input::{Event, Libinput};
Expand Down

0 comments on commit 7f197ff

Please sign in to comment.