From a0cb3e6d577cd9a93b97eef9ca92b1c983c4a93b Mon Sep 17 00:00:00 2001 From: ABeltramo Date: Sun, 24 Mar 2024 20:31:43 +0000 Subject: [PATCH] feat: added basic Rust bindings --- {python => bindings/python}/.gitignore | 0 {python => bindings/python}/CMakeLists.txt | 3 +- {python => bindings/python}/inputtino.i | 0 bindings/rust/Cargo.toml | 15 +++++ bindings/rust/build.rs | 65 ++++++++++++++++++++++ bindings/rust/src/lib.rs | 50 +++++++++++++++++ bindings/rust/wrapper.hpp | 1 + 7 files changed, 132 insertions(+), 2 deletions(-) rename {python => bindings/python}/.gitignore (100%) rename {python => bindings/python}/CMakeLists.txt (89%) rename {python => bindings/python}/inputtino.i (100%) create mode 100644 bindings/rust/Cargo.toml create mode 100644 bindings/rust/build.rs create mode 100644 bindings/rust/src/lib.rs create mode 100644 bindings/rust/wrapper.hpp diff --git a/python/.gitignore b/bindings/python/.gitignore similarity index 100% rename from python/.gitignore rename to bindings/python/.gitignore diff --git a/python/CMakeLists.txt b/bindings/python/CMakeLists.txt similarity index 89% rename from python/CMakeLists.txt rename to bindings/python/CMakeLists.txt index 2d9f92e..f5f2f66 100644 --- a/python/CMakeLists.txt +++ b/bindings/python/CMakeLists.txt @@ -7,7 +7,7 @@ set_property(SOURCE inputtino.i PROPERTY SWIG_MODULE_NAME py_inputtino) swig_add_library(py_inputtino TYPE MODULE LANGUAGE python - OUTPUT_DIR ${CMAKE_SOURCE_DIR}/python/out/py_inputtino + OUTPUT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/out/py_inputtino SOURCES inputtino.i) add_library(inputtino::py_inputtino ALIAS py_inputtino) @@ -15,7 +15,6 @@ find_package(Python3 REQUIRED COMPONENTS Interpreter Development.Module) target_include_directories(py_inputtino PRIVATE - ../include ${Python3_INCLUDE_DIRS} ) set_property(TARGET py_inputtino PROPERTY SWIG_USE_TARGET_INCLUDE_DIRECTORIES ON) diff --git a/python/inputtino.i b/bindings/python/inputtino.i similarity index 100% rename from python/inputtino.i rename to bindings/python/inputtino.i diff --git a/bindings/rust/Cargo.toml b/bindings/rust/Cargo.toml new file mode 100644 index 0000000..e1aa98f --- /dev/null +++ b/bindings/rust/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "inputtino" +version = "0.1.0" +edition = "2021" +license = "MIT" +rust-version = "1.72" +links = "libinputtino" + +[lib] +name = "inputtino_rs" +path = "src/lib.rs" + +[build-dependencies] +bindgen = "0.69.4" +cmake = "0.1" diff --git a/bindings/rust/build.rs b/bindings/rust/build.rs new file mode 100644 index 0000000..4e9b99e --- /dev/null +++ b/bindings/rust/build.rs @@ -0,0 +1,65 @@ +extern crate bindgen; + +use std::env; +use std::path::PathBuf; + +use cmake::Config; + +fn main() { + let build_static = false; + + // This is the directory where the `c` library is located. + let libdir_path = PathBuf::from("../../") + // Canonicalize the path as `rustc-link-search` requires an absolute + // path. + .canonicalize() + .expect("cannot canonicalize path"); + + // Compile the library using CMake + let dst = Config::new(libdir_path) + .target("libinputtino") + .define("BUILD_SHARED_LIBS", if build_static { "OFF" } else { "ON" }) + .define("LIBINPUTTINO_INSTALL", "ON") + .define("BUILD_TESTING", "OFF") + .define("BUILD_SERVER", "OFF") + .define("BUILD_C_BINDINGS", "ON") + .profile("Release") + .define("CMAKE_CONFIGURATION_TYPES", "Release") + .build(); + + // Dependencies + if !build_static { + println!("cargo:rustc-link-lib=evdev"); + println!("cargo:rustc-link-lib=udev"); + println!("cargo:rustc-link-lib=stdc++"); + } + + //libinputtino + println!("cargo:rustc-link-search=native={}/lib", dst.display()); + println!("cargo:rustc-link-lib={}libinputtino", if build_static { "static=" } else { "" }); + + // The bindgen::Builder is the main entry point + // to bindgen, and lets you build up options for + // the resulting bindings. + let bindings = bindgen::Builder::default() + .use_core() + .default_enum_style(bindgen::EnumVariation::Rust { + non_exhaustive: false, + }) + // Add the include directory + .clang_arg(format!("-I{}/include/", dst.display())) + // Set the INPUTTINO_STATIC_DEFINE macro + .clang_arg(if build_static {"-D INPUTTINO_STATIC_DEFINE=1"} else {""}) + // The input header we would like to generate bindings for. + .header("wrapper.hpp") + // Finish the builder and generate the bindings. + .generate() + // Unwrap the Result and panic on failure. + .expect("Unable to generate bindings"); + + // Write the bindings to the $OUT_DIR/bindings.rs file. + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("bindings.rs"); + bindings + .write_to_file(out_path) + .expect("Couldn't write bindings!"); +} diff --git a/bindings/rust/src/lib.rs b/bindings/rust/src/lib.rs new file mode 100644 index 0000000..03ef284 --- /dev/null +++ b/bindings/rust/src/lib.rs @@ -0,0 +1,50 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] + +include!(concat!(env!("OUT_DIR"), "/bindings.rs")); + +#[cfg(test)] +mod tests{ + + use std::ffi::{CStr, CString}; + use super::*; + + #[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); + } + } + +} diff --git a/bindings/rust/wrapper.hpp b/bindings/rust/wrapper.hpp new file mode 100644 index 0000000..69b470d --- /dev/null +++ b/bindings/rust/wrapper.hpp @@ -0,0 +1 @@ +#include