Skip to content

Commit

Permalink
feat: better Rust bindings, properly wrapped mouse
Browse files Browse the repository at this point in the history
  • Loading branch information
ABeltramo committed Jul 31, 2024
1 parent 56bc064 commit 2a348ce
Show file tree
Hide file tree
Showing 3 changed files with 178 additions and 39 deletions.
27 changes: 27 additions & 0 deletions bindings/rust/src/common.rs
Original file line number Diff line number Diff line change
@@ -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);
}
61 changes: 22 additions & 39 deletions bindings/rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

}
129 changes: 129 additions & 0 deletions bindings/rust/src/mouse.rs
Original file line number Diff line number Diff line change
@@ -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<Self, String> {
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<Vec<String>, 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);
}
}
}

0 comments on commit 2a348ce

Please sign in to comment.