From 2a348ce32d131ebf6b9f45e9f4e9cb960b8397b0 Mon Sep 17 00:00:00 2001 From: ABeltramo Date: Wed, 31 Jul 2024 20:48:58 +0100 Subject: [PATCH] feat: better Rust bindings, properly wrapped mouse --- bindings/rust/src/common.rs | 27 ++++++++ bindings/rust/src/lib.rs | 61 ++++++----------- bindings/rust/src/mouse.rs | 129 ++++++++++++++++++++++++++++++++++++ 3 files changed, 178 insertions(+), 39 deletions(-) create mode 100644 bindings/rust/src/common.rs create mode 100644 bindings/rust/src/mouse.rs diff --git a/bindings/rust/src/common.rs b/bindings/rust/src/common.rs new file mode 100644 index 0000000..0328565 --- /dev/null +++ b/bindings/rust/src/common.rs @@ -0,0 +1,27 @@ +pub struct InputtinoDeviceDefinition { + pub def: super::InputtinoDeviceDefinition, +} + +impl InputtinoDeviceDefinition { + pub fn new(name: &str, vendor_id: u16, product_id: u16, version: u16, phys: &str, uniq: &str) -> Self { + let name = std::ffi::CString::new(name).unwrap(); + let phys = std::ffi::CString::new(phys).unwrap(); + let uniq = std::ffi::CString::new(uniq).unwrap(); + let def = super::InputtinoDeviceDefinition { + name: name.as_ptr(), + vendor_id: vendor_id, + product_id: product_id, + version: version, + device_phys: phys.as_ptr(), // TODO: optional, if not present random MAC address + device_uniq: uniq.as_ptr(), + }; + InputtinoDeviceDefinition { def } + } +} + +pub unsafe extern "C" fn error_handler_fn(error_message: *const ::core::ffi::c_char, + user_data: *mut ::core::ffi::c_void) { + let error_str = std::ffi::CStr::from_ptr(error_message); + let user_data = user_data as *mut std::ffi::CString; + *user_data = std::ffi::CString::from(error_str); +} diff --git a/bindings/rust/src/lib.rs b/bindings/rust/src/lib.rs index 03ef284..7acee45 100644 --- a/bindings/rust/src/lib.rs +++ b/bindings/rust/src/lib.rs @@ -4,47 +4,30 @@ include!(concat!(env!("OUT_DIR"), "/bindings.rs")); -#[cfg(test)] -mod tests{ +pub mod common; +pub mod mouse; - use std::ffi::{CStr, CString}; - use super::*; +#[cfg(test)] +mod tests { + use crate::*; #[test] - fn test_inputtino_mouse(){ - 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 = InputtinoDeviceDefinition { - name: device_name.as_ptr(), - vendor_id: 0, - product_id: 0, - version: 0, - device_phys: device_phys.as_ptr(), - device_uniq: device_uniq.as_ptr(), - }; - let _error_handler_fn = | error_message: *const ::core::ffi::c_char, user_data: *mut ::core::ffi::c_void | { - unsafe{ println!("Error: {:?}", CStr::from_ptr(error_message).to_str().unwrap()); } - }; - let error_handler = InputtinoErrorHandler { - eh: None, // TODO: InputtinoErrorHandlerFn::new(error_handler_fn) ??? - user_data: std::ptr::null_mut(), - }; - - unsafe{ - let mouse = inputtino_mouse_create(&def, &error_handler); - assert!(!mouse.is_null()); - - let mut nodes_count: core::ffi::c_int = 0; - let nodes = inputtino_mouse_get_nodes(mouse, & mut nodes_count); - assert!(nodes_count == 2); - assert!(!nodes.is_null()); - // Check that the nodes start with /dev/input/event - assert!(CString::from_raw(*nodes.offset(0)).to_str().unwrap().starts_with("/dev/input/event")); - assert!(CString::from_raw(*nodes.offset(1)).to_str().unwrap().starts_with("/dev/input/event")); - - inputtino_mouse_destroy(mouse); - } + fn test_inputtino_mouse() { + let device = crate::common::InputtinoDeviceDefinition::new("Rusty Mouse", 0, 0, 0, "Rusty Mouse Phys", "Rusty Mouse Uniq"); + let mouse = crate::mouse::InputtinoMouse::new(&device).unwrap(); + let nodes = mouse.get_nodes().unwrap(); + assert_eq!(nodes.len(), 2); + + // Check that the nodes start with /dev/input/event + assert!(nodes[0].starts_with("/dev/input/event")); + assert!(nodes[1].starts_with("/dev/input/event")); + + // TODO: test the followings with libinput + mouse.move_rel(10, 10); + mouse.move_abs(100, 100, 1920, 1080); + mouse.press_button(INPUTTINO_MOUSE_BUTTON::LEFT); + mouse.release_button(INPUTTINO_MOUSE_BUTTON::LEFT); + mouse.scroll_vertical(100); + mouse.scroll_horizontal(100); } - } diff --git a/bindings/rust/src/mouse.rs b/bindings/rust/src/mouse.rs new file mode 100644 index 0000000..4dec33b --- /dev/null +++ b/bindings/rust/src/mouse.rs @@ -0,0 +1,129 @@ +use std::ffi::{CString}; +use crate::{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}; +use crate::common::{InputtinoDeviceDefinition, error_handler_fn}; + +pub struct InputtinoMouse { + mouse: *mut super::InputtinoMouse, +} + +impl InputtinoMouse { + pub fn new(device: &InputtinoDeviceDefinition) -> Result { + let error_str = std::ptr::null_mut(); + let error_handler = super::InputtinoErrorHandler { + eh: Some(error_handler_fn), + user_data: error_str, + }; + unsafe { + let mouse = inputtino_mouse_create(&device.def, &error_handler); + if mouse.is_null() { // TODO: test this + let error_msg = (error_str as *mut std::ffi::CString).as_ref().unwrap().to_str().unwrap(); + Err("Failed to create Mouse: ".to_string() + error_msg) + } else { + Ok(InputtinoMouse { mouse }) + } + } + } + + pub fn get_nodes(&self) -> Result, String> { + unsafe { + let mut nodes_count: core::ffi::c_int = 0; + let nodes = inputtino_mouse_get_nodes(self.mouse, &mut nodes_count); + if nodes.is_null() { + return Err("Failed to get nodes".to_string()); + } + + let mut result = Vec::new(); + for i in 0..nodes_count { + let node = CString::from_raw(*nodes.offset(i as isize)); + result.push(node.to_str().unwrap().to_string()); + } + Ok(result) + } + } + + pub fn move_rel(&self, x: i32, y: i32) { + unsafe { + inputtino_mouse_move(self.mouse, x, y); + } + } + + pub fn move_abs(&self, x: i32, y: i32, screen_width: i32, screen_height: i32) { + unsafe { + inputtino_mouse_move_absolute(self.mouse, x, y, screen_width, screen_height); + } + } + + pub fn press_button(&self, button: super::INPUTTINO_MOUSE_BUTTON) { + unsafe { + inputtino_mouse_press_button(self.mouse, button); + } + } + + pub fn release_button(&self, button: super::INPUTTINO_MOUSE_BUTTON) { + unsafe { + inputtino_mouse_release_button(self.mouse, button); + } + } + + pub fn scroll_vertical(&self, amount: i32) { + unsafe { + inputtino_mouse_scroll_vertical(self.mouse, amount); + } + } + + pub fn scroll_horizontal(&self, amount: i32) { + unsafe { + inputtino_mouse_scroll_horizontal(self.mouse, amount); + } + } +} + +impl Drop for InputtinoMouse { + fn drop(&mut self) { + unsafe { + inputtino_mouse_destroy(self.mouse); + } + } +} + +#[cfg(test)] +mod tests { + use std::ffi::{CString}; + use super::*; + + #[test] + fn test_inputtino_c_mouse() { + 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 { + name: device_name.as_ptr(), + vendor_id: 0, + product_id: 0, + version: 0, + device_phys: device_phys.as_ptr(), + device_uniq: device_uniq.as_ptr(), + }; + // TODO: test this somehow + let error_str = std::ptr::null_mut(); + let error_handler = crate::InputtinoErrorHandler { + eh: Some(error_handler_fn), + user_data: error_str, + }; + + unsafe { + let mouse = inputtino_mouse_create(&def, &error_handler); + assert!(!mouse.is_null()); + + let mut nodes_count: core::ffi::c_int = 0; + let nodes = inputtino_mouse_get_nodes(mouse, &mut nodes_count); + assert_eq!(nodes_count, 2); + assert!(!nodes.is_null()); + // Check that the nodes start with /dev/input/event + assert!(CString::from_raw(*nodes.offset(0)).to_str().unwrap().starts_with("/dev/input/event")); + assert!(CString::from_raw(*nodes.offset(1)).to_str().unwrap().starts_with("/dev/input/event")); + + inputtino_mouse_destroy(mouse); + } + } +}