Fixes for polling and ff example. Not working, yet.

This commit is contained in:
Johannes Leupolz 2026-04-03 22:34:24 +00:00
parent 5cffaec3da
commit 96719c0a9c
4 changed files with 86 additions and 43 deletions

View file

@ -3,9 +3,13 @@
// Author: Johannes Leupolz <dev@leupolz.eu>
use crate::devices::device_base::{fetch_device_node, open_uinput, Device, DeviceState, BUS_USB};
use libc::{c_int, close, ff_effect, input_event, open, uinput_ff_upload};
use nix::{ioctl_write_int, ioctl_write_ptr};
use std::io;
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 std::{
io, os::fd::BorrowedFd, sync::{
Arc, atomic::{AtomicBool, Ordering}
}, thread, time::Duration
};
use uinput_ioctls::*;
// Xbox Gamepad codes
@ -252,7 +256,7 @@ impl Device for XboxGamepadDevice {
let event_device_fd = unsafe {
open(
event_device_node.as_ptr() as *const i8,
libc::O_RDONLY | libc::O_NONBLOCK,
libc::O_RDWR | libc::O_NONBLOCK,
)
};
if event_device_fd < 0 {
@ -284,7 +288,7 @@ impl Device for XboxGamepadDevice {
}
impl XboxGamepadDevice {
pub fn read_process_ff_event_from_uinput(&self) {
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;
@ -292,39 +296,66 @@ impl XboxGamepadDevice {
// Buffer for the raw bytes
let mut buffer = [0u8; 256];
// Calling C functions always requires an unsafe block
let result =
unsafe { libc::read(fd, buffer.as_mut_ptr() as *mut libc::c_void, buffer.len()) };
let mut pollfds = [
PollFd::new(unsafe { BorrowedFd::borrow_raw(fd) }, PollFlags::POLLIN),
];
// libc::read returns an isize (ssize_t in C)
if result < 0 {
// result < 0 means an error occurred. We use std::io::Error::last_os_error()
// to get the correct OS error message based on the C `errno`.
eprintln!(
"Error reading in thread: {}",
std::io::Error::last_os_error()
);
return;
} else if result == 0 {
// 0 bytes usually means End-Of-File (EOF) or that the device was closed
println!("0 bytes (EOF) - Terminating thread");
return;
} else if result == 24 {
println!("read_process_ff_event_from_uinput: processing input event (read)");
let input_event = buffer.as_ptr() as *const libc::input_event;
let input_event = unsafe { *input_event };
if input_event.type_ == EV_UINPUT && input_event.code == UI_FF_UPLOAD {
let mut upload: uinput_ff_upload = unsafe { std::mem::zeroed() };
upload.request_id = input_event.value.try_into().unwrap();
unsafe {
let ptr = &mut upload as *mut uinput_ff_upload;
ui_begin_ff_upload(fd, ptr).unwrap();
println!("effect type: {}", upload.effect.type_);
ui_end_ff_upload(fd, ptr).unwrap();
};
loop {
if shutdown.load(Ordering::SeqCst) {
break;
}
println!("Loop in read_process_ff_event_from_uinput");
if use_poll {
let _ = poll(&mut pollfds, 500u16);
} else {
thread::sleep(Duration::from_millis(200));
}
// Calling C functions always requires an unsafe block
let result = unsafe {
libc::read(fd, buffer.as_mut_ptr() as *mut libc::c_void, buffer.len())
};
if result < 0 {
// result < 0 means an error occurred. We use std::io::Error::last_os_error()
// to get the correct OS error message based on the C `errno`.
let error = std::io::Error::last_os_error();
match error.kind() {
io::ErrorKind::WouldBlock => {
eprintln!("a read would block. waiting for the next real event");
continue;
}
_ => {
eprintln!("Error reading in thread: {}", error);
return;
}
}
} else if result == 0 {
// 0 bytes usually means End-Of-File (EOF) or that the device was closed
println!("0 bytes (EOF) - Terminating thread");
return;
} else if result == 24 {
println!("read_process_ff_event_from_uinput: processing input event (read)");
let input_event = buffer.as_ptr() as *const libc::input_event;
let input_event = unsafe { *input_event };
if input_event.type_ == EV_UINPUT && input_event.code == UI_FF_UPLOAD {
let mut upload: uinput_ff_upload = unsafe { std::mem::zeroed() };
upload.request_id = input_event.value.try_into().unwrap();
unsafe {
let ptr = &mut upload as *mut uinput_ff_upload;
ui_begin_ff_upload(fd, ptr).unwrap();
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);
}
} else {
println!("Read {} bytes", result);
}
} else {
println!("Read {} bytes", result);
}
});
}

View file

@ -2,6 +2,8 @@
//
// Author: Johannes Leupolz <dev@leupolz.eu>
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
use std::thread;
use std::time::Duration;
@ -38,7 +40,9 @@ impl FfXboxGamepad {
effect.u = xbox_gamepad::create_rumble_array(0x8000, 0x0);
// ensure uploaded effect gets processed
gamepad.read_process_ff_event_from_uinput();
let shutdown = Arc::new(AtomicBool::new(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)?;
@ -46,12 +50,19 @@ impl FfXboxGamepad {
thread::sleep(Duration::from_secs(1));
// Play effect (value=1)
let _play_effect_event =
gamepad.emit_read_and_log(EV_FF, effect_id.try_into().unwrap(), 1)?;
let _play_effect_event = gamepad.emit_to_evdev_read_from_uinput_and_log(
EV_FF,
effect_id.try_into().unwrap(),
1,
)?;
thread::sleep(Duration::from_secs(1));
let _stop_effect_event =
gamepad.emit_read_and_log(EV_FF, effect_id.try_into().unwrap(), 0)?;
let _stop_effect_event = gamepad.emit_to_evdev_read_from_uinput_and_log(
EV_FF,
effect_id.try_into().unwrap(),
0,
)?;
thread::sleep(Duration::from_secs(1));
shutdown.store(true, std::sync::atomic::Ordering::SeqCst);
let eventlog = TestLog {
events: gamepad.event_log().to_vec(),