From 96719c0a9c6226c46684a5eacba9b1370cfc40a8 Mon Sep 17 00:00:00 2001 From: Johannes Leupolz Date: Fri, 3 Apr 2026 22:34:24 +0000 Subject: [PATCH] Fixes for polling and ff example. Not working, yet. --- vuinputd-tests/src/devices/xbox_gamepad.rs | 103 ++++++++++++------ .../src/scenarios/ff_xbox_gamepad.rs | 21 +++- vuinputd/src/cuse_device/vuinput_open.rs | 3 +- vuinputd/src/cuse_device/vuinput_poll.rs | 2 + 4 files changed, 86 insertions(+), 43 deletions(-) diff --git a/vuinputd-tests/src/devices/xbox_gamepad.rs b/vuinputd-tests/src/devices/xbox_gamepad.rs index 69d3a23..b14c3a3 100644 --- a/vuinputd-tests/src/devices/xbox_gamepad.rs +++ b/vuinputd-tests/src/devices/xbox_gamepad.rs @@ -3,9 +3,13 @@ // Author: Johannes Leupolz 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,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); } }); } diff --git a/vuinputd-tests/src/scenarios/ff_xbox_gamepad.rs b/vuinputd-tests/src/scenarios/ff_xbox_gamepad.rs index 0ab0a2e..b2e26ab 100644 --- a/vuinputd-tests/src/scenarios/ff_xbox_gamepad.rs +++ b/vuinputd-tests/src/scenarios/ff_xbox_gamepad.rs @@ -2,6 +2,8 @@ // // Author: Johannes Leupolz +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(), diff --git a/vuinputd/src/cuse_device/vuinput_open.rs b/vuinputd/src/cuse_device/vuinput_open.rs index 532351a..223d9ac 100644 --- a/vuinputd/src/cuse_device/vuinput_open.rs +++ b/vuinputd/src/cuse_device/vuinput_open.rs @@ -42,7 +42,7 @@ pub unsafe extern "C" fn vuinput_open( debug!("fh {}: namespaces {}", fh, requesting_process); // namespaces net:4026531840, uts:4026531838, ipc:4026531839, pid:4026531836, pid_for_children:4026531836, user:4026531837, mnt:4026531841, cgroup:4026531835, time:4026531834, time_for_children:4026531834 (*_fi).fh = fh; - // Open the path in read-only mode, returns `io::Result` + // Open the path, returns `io::Result` let open_vuinput_result = OpenOptions::new() .read(true) .write(true) @@ -52,7 +52,6 @@ pub unsafe extern "C" fn vuinput_open( match open_vuinput_result { Ok(v) => { let vu_fh: VuFileHandle = VuFileHandle::Fh(fh); - let uinput_fd: std::os::unix::prelude::BorrowedFd<'_> = v.as_fd(); insert_vuinput_state( &vu_fh, VuInputState { diff --git a/vuinputd/src/cuse_device/vuinput_poll.rs b/vuinputd/src/cuse_device/vuinput_poll.rs index 507415d..1d24e50 100644 --- a/vuinputd/src/cuse_device/vuinput_poll.rs +++ b/vuinputd/src/cuse_device/vuinput_poll.rs @@ -25,6 +25,8 @@ pub unsafe extern "C" fn vuinput_poll( get_vuinput_state(&VuFileHandle::from_fuse_file_info(fi.as_ref().unwrap())).unwrap(); let mut vuinput_state = vuinput_state_mutex.lock().unwrap(); + debug!("poll"); + match vuinput_state.poll.pollphase { PollPhase::Empty => { let ph = NonNull::::new(ph);