mirror of
https://github.com/joleuger/vuinputd.git
synced 2026-07-17 16:36:03 +00:00
Test for absolute mouse device added
This commit is contained in:
parent
0fda223d4e
commit
65802bfa63
9 changed files with 287 additions and 34 deletions
|
|
@ -5,10 +5,7 @@
|
|||
use clap::{Parser, Subcommand};
|
||||
use vuinputd_tests::scenarios::{
|
||||
basic_keyboard::BasicKeyboard, basic_mouse::BasicMouse, basic_ps4_gamepad::BasicPs4Gamepad,
|
||||
basic_xbox_gamepad::BasicXboxGamepad,
|
||||
ff_xbox_gamepad::FfXboxGamepad, /*
|
||||
reuse_keyboard::ReuseKeyboard, reuse_xbox_gamepad::ReuseXboxGamepad,
|
||||
ScenarioArgs, stress_keyboard::StressKeyboard, stress_xbox_gamepad::StressXboxGamepad, */
|
||||
basic_xbox_gamepad::BasicXboxGamepad, ff_xbox_gamepad::FfXboxGamepad, BasicMouseAbsolute,
|
||||
ScenarioArgs,
|
||||
};
|
||||
|
||||
|
|
@ -36,6 +33,9 @@ enum Commands {
|
|||
/// Basic mouse test
|
||||
BasicMouse,
|
||||
|
||||
/// Basic mouse (absolute) test
|
||||
BasicMouseAbsolute,
|
||||
|
||||
/// Basic PS4 gamepad test
|
||||
BasicPs4Gamepad,
|
||||
|
||||
|
|
@ -70,6 +70,7 @@ fn main() -> Result<(), std::io::Error> {
|
|||
match cli.command {
|
||||
Commands::BasicKeyboard => BasicKeyboard::run(&args),
|
||||
Commands::BasicMouse => BasicMouse::run(&args),
|
||||
Commands::BasicMouseAbsolute => BasicMouseAbsolute::run(&args),
|
||||
Commands::BasicPs4Gamepad => BasicPs4Gamepad::run(&args),
|
||||
Commands::BasicXboxGamepad => BasicXboxGamepad::run(&args),
|
||||
Commands::FfXboxGamepad => FfXboxGamepad::run(&args),
|
||||
|
|
|
|||
|
|
@ -23,6 +23,16 @@ pub const SYN_REPORT: u16 = 0;
|
|||
pub const BUS_USB: u16 = 0x03;
|
||||
pub const SYS_INPUT_DIR: &str = "/sys/devices/virtual/input/";
|
||||
|
||||
// Absolute Axes
|
||||
pub const ABS_X: u16 = 0x00;
|
||||
pub const ABS_Y: u16 = 0x01;
|
||||
pub const ABS_Z: u16 = 0x02;
|
||||
pub const ABS_RX: u16 = 0x03;
|
||||
pub const ABS_RY: u16 = 0x04;
|
||||
pub const ABS_RZ: u16 = 0x05;
|
||||
pub const ABS_HAT0X: u16 = 0x10;
|
||||
pub const ABS_HAT0Y: u16 = 0x11;
|
||||
|
||||
/// Struct holding device state
|
||||
pub struct DeviceState {
|
||||
pub uinput_fd: i32,
|
||||
|
|
@ -99,7 +109,8 @@ pub trait Device: Sized {
|
|||
val: i32,
|
||||
) -> io::Result<LoggedInputEvent> {
|
||||
let event_device_fd = self.get_event_device()?;
|
||||
let event = emit_read_and_log(event_device_fd, self.uinput_fd(), ev_type, code, val, false)?;
|
||||
let event =
|
||||
emit_read_and_log(event_device_fd, self.uinput_fd(), ev_type, code, val, false)?;
|
||||
self.state_mut().events.push(event.clone());
|
||||
Ok(event)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
pub mod device_base;
|
||||
pub mod keyboard;
|
||||
pub mod mouse;
|
||||
pub mod mouse_absolute;
|
||||
pub mod ps4_gamepad;
|
||||
pub mod utils;
|
||||
pub mod xbox_gamepad;
|
||||
|
|
@ -14,6 +15,7 @@ pub use device_base::Device;
|
|||
pub use device_base::DeviceState;
|
||||
pub use keyboard::KeyboardDevice;
|
||||
pub use mouse::MouseDevice;
|
||||
pub use mouse_absolute::MouseAbsoluteDevice;
|
||||
pub use ps4_gamepad::Ps4GamepadDevice;
|
||||
pub use xbox_gamepad::XboxGamepadDevice;
|
||||
|
||||
|
|
|
|||
161
vuinputd-tests/src/devices/mouse_absolute.rs
Normal file
161
vuinputd-tests/src/devices/mouse_absolute.rs
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Author: Johannes Leupolz <dev@leupolz.eu>
|
||||
|
||||
use crate::devices::{
|
||||
device_base::{fetch_device_node, open_uinput, Device, DeviceState, BUS_USB},
|
||||
utils::{ABS_X, ABS_Y},
|
||||
};
|
||||
use libc::{c_int, close, input_absinfo, open, uinput_abs_setup, INPUT_PROP_DIRECT};
|
||||
use std::io;
|
||||
use uinput_ioctls::*;
|
||||
|
||||
// Mouse codes
|
||||
pub const BTN_LEFT: u16 = 272;
|
||||
pub const BTN_RIGHT: u16 = 273;
|
||||
pub const BTN_MIDDLE: u16 = 274;
|
||||
pub const REL_X: u16 = 0;
|
||||
pub const REL_Y: u16 = 1;
|
||||
|
||||
// non linux constants used this way in inputtino
|
||||
pub const ABS_MAX_WIDTH: i32 = 19200;
|
||||
pub const ABS_MAX_HEIGHT: i32 = 12000;
|
||||
|
||||
/// Setup absolute mouse device
|
||||
unsafe fn setup_mouse_absolute(fd: c_int) -> io::Result<()> {
|
||||
// EV_SYN (implicitly handled by libevdev, but required manually for uinput)
|
||||
ui_set_evbit(fd, super::EV_SYN.try_into().unwrap())?;
|
||||
|
||||
// INPUT_PROP_DIRECT
|
||||
ui_set_propbit(fd, INPUT_PROP_DIRECT.try_into().unwrap())?;
|
||||
|
||||
// EV_KEY
|
||||
ui_set_evbit(fd, super::EV_KEY.try_into().unwrap())?;
|
||||
ui_set_keybit(fd, BTN_LEFT.try_into().unwrap())?;
|
||||
|
||||
// EV_ABS
|
||||
ui_set_evbit(fd, super::EV_ABS.try_into().unwrap())?;
|
||||
ui_set_absbit(fd, ABS_X.try_into().unwrap())?;
|
||||
ui_set_absbit(fd, ABS_Y.try_into().unwrap())?;
|
||||
|
||||
// Setup absolute axis parameters (min, max, fuzz, flat, resolution)
|
||||
let abs_x_setup = uinput_abs_setup {
|
||||
code: ABS_X,
|
||||
absinfo: input_absinfo {
|
||||
value: 0,
|
||||
minimum: 0,
|
||||
maximum: ABS_MAX_WIDTH,
|
||||
fuzz: 1,
|
||||
flat: 0,
|
||||
resolution: 28,
|
||||
},
|
||||
};
|
||||
ui_abs_setup(fd, &abs_x_setup).map_err(|e| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("ui_abs_setup X failed: {:?}", e),
|
||||
)
|
||||
})?;
|
||||
|
||||
let abs_y_setup = uinput_abs_setup {
|
||||
code: ABS_Y,
|
||||
absinfo: input_absinfo {
|
||||
value: 0,
|
||||
minimum: 0,
|
||||
maximum: ABS_MAX_HEIGHT,
|
||||
fuzz: 1,
|
||||
flat: 0,
|
||||
resolution: 28,
|
||||
},
|
||||
};
|
||||
ui_abs_setup(fd, &abs_y_setup).map_err(|e| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("ui_abs_setup Y failed: {:?}", e),
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub struct MouseAbsoluteDevice {
|
||||
state: DeviceState,
|
||||
}
|
||||
|
||||
impl Device for MouseAbsoluteDevice {
|
||||
fn name() -> &'static str {
|
||||
"Mouse Absolute"
|
||||
}
|
||||
|
||||
fn state(&self) -> &DeviceState {
|
||||
&self.state
|
||||
}
|
||||
|
||||
fn state_mut(&mut self) -> &mut DeviceState {
|
||||
&mut self.state
|
||||
}
|
||||
|
||||
fn get_event_device(&self) -> Result<c_int, io::Error> {
|
||||
Ok(self.state.event_device_fd)
|
||||
}
|
||||
|
||||
fn create(device: Option<&str>, name: &str) -> Result<Self, io::Error> {
|
||||
let fd = open_uinput(device)?;
|
||||
|
||||
unsafe { setup_mouse_absolute(fd)? };
|
||||
|
||||
let temp_device = MouseAbsoluteDevice {
|
||||
state: DeviceState {
|
||||
uinput_fd: fd,
|
||||
sysname: String::new(),
|
||||
device_name: name.to_string(),
|
||||
event_device_node: String::new(),
|
||||
event_device_fd: -1,
|
||||
events: Vec::new(),
|
||||
},
|
||||
};
|
||||
temp_device.setup_device(name, 0xbeef, 0xdead, BUS_USB, 0)?;
|
||||
|
||||
unsafe {
|
||||
ui_dev_create(fd).map_err(|e| {
|
||||
eprintln!("ui_dev_create failed: {:?}", e);
|
||||
e
|
||||
})?;
|
||||
}
|
||||
|
||||
let sysname = temp_device.get_sysname()?;
|
||||
|
||||
let event_device_node = fetch_device_node(&sysname)?;
|
||||
let event_device_fd = unsafe {
|
||||
open(
|
||||
event_device_node.as_ptr() as *const i8,
|
||||
libc::O_RDONLY | libc::O_NONBLOCK,
|
||||
)
|
||||
};
|
||||
if event_device_fd < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
Ok(MouseAbsoluteDevice {
|
||||
state: DeviceState {
|
||||
uinput_fd: fd,
|
||||
sysname,
|
||||
device_name: name.to_string(),
|
||||
event_device_node,
|
||||
event_device_fd,
|
||||
events: Vec::new(),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
fn destroy(self) {
|
||||
unsafe {
|
||||
ui_dev_destroy(self.state.uinput_fd).unwrap_or_else(|e| {
|
||||
eprintln!("ui_dev_destroy failed: {:?}", e);
|
||||
std::process::exit(1);
|
||||
});
|
||||
close(self.state.uinput_fd);
|
||||
close(self.state.event_device_fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,13 +2,24 @@
|
|||
//
|
||||
// Author: Johannes Leupolz <dev@leupolz.eu>
|
||||
|
||||
use crate::devices::device_base::{fetch_device_node, open_uinput, Device, DeviceState, BUS_USB};
|
||||
use crate::devices::{
|
||||
device_base::{fetch_device_node, open_uinput, Device, DeviceState, BUS_USB},
|
||||
utils::{ABS_HAT0X, ABS_HAT0Y, ABS_RX, ABS_RY, ABS_RZ, ABS_X, ABS_Y, ABS_Z},
|
||||
};
|
||||
use libc::{c_int, close, ff_effect, input_event, open, uinput_ff_upload, EAGAIN};
|
||||
use nix::{ioctl_write_int, ioctl_write_ptr, poll::{PollFd, PollFlags, PollTimeout, poll}};
|
||||
use nix::{
|
||||
ioctl_write_int, ioctl_write_ptr,
|
||||
poll::{poll, PollFd, PollFlags, PollTimeout},
|
||||
};
|
||||
use std::{
|
||||
io, os::fd::BorrowedFd, sync::{
|
||||
Arc, atomic::{AtomicBool, Ordering}
|
||||
}, thread, time::Duration
|
||||
io,
|
||||
os::fd::BorrowedFd,
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
},
|
||||
thread,
|
||||
time::Duration,
|
||||
};
|
||||
use uinput_ioctls::*;
|
||||
|
||||
|
|
@ -28,16 +39,6 @@ pub const BTN_MODE: u16 = 0x13c;
|
|||
pub const BTN_THUMBL: u16 = 0x13d;
|
||||
pub const BTN_THUMBR: u16 = 0x13e;
|
||||
|
||||
// Absolute Axes
|
||||
pub const ABS_X: u16 = 0x00;
|
||||
pub const ABS_Y: u16 = 0x01;
|
||||
pub const ABS_Z: u16 = 0x02;
|
||||
pub const ABS_RX: u16 = 0x03;
|
||||
pub const ABS_RY: u16 = 0x04;
|
||||
pub const ABS_RZ: u16 = 0x05;
|
||||
pub const ABS_HAT0X: u16 = 0x10;
|
||||
pub const ABS_HAT0Y: u16 = 0x11;
|
||||
|
||||
// Force Feedback
|
||||
// https://github.com/torvalds/linux/blob/master/include/uapi/linux/input.h
|
||||
pub const FF_RUMBLE: u16 = 0x50;
|
||||
|
|
@ -288,7 +289,7 @@ impl Device for XboxGamepadDevice {
|
|||
}
|
||||
|
||||
impl XboxGamepadDevice {
|
||||
pub fn read_process_ff_event_from_uinput(&self, shutdown: Arc<AtomicBool>,use_poll:bool) {
|
||||
pub fn read_process_ff_event_from_uinput(&self, shutdown: Arc<AtomicBool>, use_poll: bool) {
|
||||
// Copy the i32 file descriptor so we can move it into the thread safely
|
||||
let fd = self.state().uinput_fd;
|
||||
|
||||
|
|
@ -296,10 +297,10 @@ impl XboxGamepadDevice {
|
|||
// Buffer for the raw bytes
|
||||
let mut buffer = [0u8; 256];
|
||||
|
||||
let mut pollfds = [
|
||||
PollFd::new(unsafe { BorrowedFd::borrow_raw(fd) }, PollFlags::POLLIN),
|
||||
];
|
||||
|
||||
let mut pollfds = [PollFd::new(
|
||||
unsafe { BorrowedFd::borrow_raw(fd) },
|
||||
PollFlags::POLLIN,
|
||||
)];
|
||||
|
||||
loop {
|
||||
if shutdown.load(Ordering::SeqCst) {
|
||||
|
|
@ -348,11 +349,12 @@ impl XboxGamepadDevice {
|
|||
println!("effect type: {}", upload.effect.type_);
|
||||
ui_end_ff_upload(fd, ptr).unwrap();
|
||||
};
|
||||
}
|
||||
else {
|
||||
println!("event: {} {} {}",input_event.type_,input_event.code,input_event.value);
|
||||
crate::devices::utils::emit(fd, input_event.type_,input_event.code,input_event.value).unwrap();
|
||||
|
||||
} else {
|
||||
println!(
|
||||
"event: {} {} {}",
|
||||
input_event.type_, input_event.code, input_event.value
|
||||
);
|
||||
//crate::devices::utils::emit(fd, input_event.type_,input_event.code,input_event.value).unwrap();
|
||||
}
|
||||
} else {
|
||||
println!("Read {} bytes", result);
|
||||
|
|
|
|||
39
vuinputd-tests/src/scenarios/basic_mouse_absolute.rs
Normal file
39
vuinputd-tests/src/scenarios/basic_mouse_absolute.rs
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Author: Johannes Leupolz <dev@leupolz.eu>
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::devices::{Device, MouseAbsoluteDevice, EV_KEY};
|
||||
use crate::scenarios::ScenarioArgs;
|
||||
use crate::test_log::{LoggedInputEvent, TestLog};
|
||||
|
||||
const BTN_LEFT: u16 = 272;
|
||||
|
||||
pub struct BasicMouseAbsolute;
|
||||
|
||||
impl BasicMouseAbsolute {
|
||||
pub fn run(args: &ScenarioArgs) -> Result<(), std::io::Error> {
|
||||
let device = args
|
||||
.dev_path
|
||||
.clone()
|
||||
.unwrap_or_else(|| "/dev/uinput".to_string());
|
||||
|
||||
let mut mouse = MouseAbsoluteDevice::create(Some(&device), "Example Mouse (absolute)")?;
|
||||
eprintln!("sysname: {}", mouse.sysname());
|
||||
|
||||
thread::sleep(Duration::from_secs(1));
|
||||
|
||||
let _ev1 = mouse.emit_read_and_log(EV_KEY, BTN_LEFT, 1)?;
|
||||
let _ev2 = mouse.emit_read_and_log(EV_KEY, BTN_LEFT, 0)?;
|
||||
|
||||
let eventlog = TestLog {
|
||||
events: mouse.event_log().to_vec(),
|
||||
};
|
||||
let serialized = serde_json::to_string(&eventlog).unwrap();
|
||||
println!("Event log: {}", serialized);
|
||||
|
||||
MouseAbsoluteDevice::destroy(mouse);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
@ -41,7 +41,7 @@ impl FfXboxGamepad {
|
|||
|
||||
// ensure uploaded effect gets processed
|
||||
let shutdown = Arc::new(AtomicBool::new(false));
|
||||
gamepad.read_process_ff_event_from_uinput(shutdown.clone(),false);
|
||||
gamepad.read_process_ff_event_from_uinput(shutdown.clone(), false);
|
||||
|
||||
// Upload effect via ioctl
|
||||
let effect_id = upload_effect(gamepad.state().event_device_fd, &mut effect)?;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
pub mod basic_keyboard;
|
||||
pub mod basic_mouse;
|
||||
pub mod basic_mouse_absolute;
|
||||
pub mod basic_ps4_gamepad;
|
||||
pub mod basic_xbox_gamepad;
|
||||
pub mod ff_xbox_gamepad;
|
||||
|
|
@ -17,6 +18,7 @@ pub mod stress_xbox_gamepad;
|
|||
// Re-exports for type checking
|
||||
pub use basic_keyboard::BasicKeyboard;
|
||||
pub use basic_mouse::BasicMouse;
|
||||
pub use basic_mouse_absolute::BasicMouseAbsolute;
|
||||
pub use basic_ps4_gamepad::BasicPs4Gamepad;
|
||||
pub use basic_xbox_gamepad::BasicXboxGamepad;
|
||||
pub use ff_xbox_gamepad::FfXboxGamepad;
|
||||
|
|
|
|||
|
|
@ -194,7 +194,6 @@ fn test_keyboard_in_container_with_vuinput_placement_on_host() {
|
|||
assert!(out.status.success());
|
||||
}
|
||||
|
||||
|
||||
#[ignore = "not implemented yet"]
|
||||
#[cfg(all(
|
||||
feature = "requires-privileges",
|
||||
|
|
@ -221,7 +220,7 @@ fn test_gamepad_with_ff_in_container() {
|
|||
.expect("failed to create IPC");
|
||||
|
||||
let out = builder
|
||||
.command(test_scenarios, &["--ipc","ff-xbox-gamepad"])
|
||||
.command(test_scenarios, &["--ipc", "ff-xbox-gamepad"])
|
||||
.run()
|
||||
.unwrap_or_else(|e| panic!("failed to run bwrap!: {e}"));
|
||||
|
||||
|
|
@ -230,4 +229,40 @@ fn test_gamepad_with_ff_in_container() {
|
|||
println!("stderr: {}", str::from_utf8(&out.stderr).unwrap());
|
||||
|
||||
assert!(out.status.success());
|
||||
}
|
||||
}
|
||||
#[ignore = "not implemented yet"]
|
||||
#[cfg(all(
|
||||
feature = "requires-privileges",
|
||||
feature = "requires-uinput",
|
||||
feature = "requires-bwrap"
|
||||
))]
|
||||
#[test]
|
||||
fn test_mouse_absolute_in_container() {
|
||||
let _guard: run_vuinputd::VuinputdGuard = run_vuinputd::ensure_vuinputd_running(&[]);
|
||||
|
||||
let test_scenarios = env!("CARGO_BIN_EXE_test-scenarios");
|
||||
|
||||
let (builder, _ipc) = bwrap::BwrapBuilder::new()
|
||||
.unshare_net()
|
||||
.ro_bind("/", "/")
|
||||
.tmpfs("/tmp")
|
||||
// dev needs to be writable for the new devices
|
||||
.dev()
|
||||
// run needs to be writable for the udev devices
|
||||
.tmpfs("/run")
|
||||
.dev_bind("/dev/vuinput-test", "/dev/uinput")
|
||||
.die_with_parent()
|
||||
.with_ipc()
|
||||
.expect("failed to create IPC");
|
||||
|
||||
let out = builder
|
||||
.command(test_scenarios, &["--ipc", "basic-mouse-absolute"])
|
||||
.run()
|
||||
.unwrap_or_else(|e| panic!("failed to run bwrap!: {e}"));
|
||||
|
||||
println!("Output");
|
||||
println!("stdout: {}", str::from_utf8(&out.stdout).unwrap());
|
||||
println!("stderr: {}", str::from_utf8(&out.stderr).unwrap());
|
||||
|
||||
assert!(out.status.success());
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue