diff --git a/bindings/rust/Cargo.toml b/bindings/rust/Cargo.toml index ce50bed..3a60998 100644 --- a/bindings/rust/Cargo.toml +++ b/bindings/rust/Cargo.toml @@ -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" diff --git a/bindings/rust/src/c_bindings.rs b/bindings/rust/src/c_bindings.rs new file mode 100644 index 0000000..37c7abd --- /dev/null +++ b/bindings/rust/src/c_bindings.rs @@ -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")); diff --git a/bindings/rust/src/common.rs b/bindings/rust/src/common.rs index 0a562f0..798ec27 100644 --- a/bindings/rust/src/common.rs +++ b/bindings/rust/src/common.rs @@ -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, @@ -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, @@ -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, }; diff --git a/bindings/rust/src/joypad_xbox.rs b/bindings/rust/src/joypad_xbox.rs new file mode 100644 index 0000000..5e88352 --- /dev/null +++ b/bindings/rust/src/joypad_xbox.rs @@ -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 ()>, +} + +impl InputtinoXOneJoypad { + pub fn new(device: &InputtinoDeviceDefinition) -> Result { + 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, 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); +} diff --git a/bindings/rust/src/keyboard.rs b/bindings/rust/src/keyboard.rs index 7e3d0ed..d24902a 100644 --- a/bindings/rust/src/keyboard.rs +++ b/bindings/rust/src/keyboard.rs @@ -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 { @@ -24,13 +25,13 @@ 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); } } } @@ -38,7 +39,7 @@ impl InputtinoKeyboard { impl Drop for InputtinoKeyboard { fn drop(&mut self) { unsafe { - super::inputtino_keyboard_destroy(self.kb); + inputtino_keyboard_destroy(self.kb); } } } diff --git a/bindings/rust/src/lib.rs b/bindings/rust/src/lib.rs index 3ed6f65..8024e0b 100644 --- a/bindings/rust/src/lib.rs +++ b/bindings/rust/src/lib.rs @@ -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; diff --git a/bindings/rust/src/mouse.rs b/bindings/rust/src/mouse.rs index 0f49db3..9ddc9b4 100644 --- a/bindings/rust/src/mouse.rs +++ b/bindings/rust/src/mouse.rs @@ -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 { @@ -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); } @@ -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, @@ -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, }; diff --git a/bindings/rust/tests/joypad_xbox.rs b/bindings/rust/tests/joypad_xbox.rs new file mode 100644 index 0000000..523853e --- /dev/null +++ b/bindings/rust/tests/joypad_xbox.rs @@ -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)); + } +} diff --git a/bindings/rust/tests/mouse.rs b/bindings/rust/tests/mouse.rs index a5484f0..dfff4e2 100644 --- a/bindings/rust/tests/mouse.rs +++ b/bindings/rust/tests/mouse.rs @@ -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};