// Create a virtual force feedback device, just while this is running. use evdev::{ uinput::{UInputEvent, VirtualDeviceBuilder}, AttributeSet, Error, EvdevEnum, FFEffectType, FFStatus, InputEventKind, UInputEventType, }; use std::collections::BTreeSet; fn main() -> Result<(), Error> { let mut device = VirtualDeviceBuilder::new()? .name("Fake Force Feedback") .with_ff(&AttributeSet::from_iter([FFEffectType::FF_RUMBLE]))? .with_ff_effects_max(16) .build()?; for path in device.enumerate_dev_nodes_blocking()? { let path = path?; println!("Available as {}", path.display()); } let mut ids: BTreeSet = (0..16).into_iter().collect(); println!("Waiting for Ctrl-C..."); loop { let events: Vec = device.fetch_events()?.collect(); for event in events { let code = match event.kind() { InputEventKind::UInput(code) => UInputEventType::from_index(code as usize), InputEventKind::ForceFeedback(effect_id) => { let value = FFStatus::from_index(event.value() as usize); match value { FFStatus::FF_STATUS_STOPPED => { println!("stopped effect ID = {}", effect_id); } FFStatus::FF_STATUS_PLAYING => { println!("playing effect ID = {}", effect_id); } _ => (), } continue; } kind => { println!("event kind = {:?}", kind); continue; } }; match code { UInputEventType::UI_FF_UPLOAD => { let mut event = device.process_ff_upload(event)?; let id = ids.iter().next().copied(); match id { Some(id) => { ids.remove(&id); event.set_effect_id(id as i16); event.set_retval(0); } None => { event.set_retval(-1); } } println!("upload effect {:?}", event.effect()); } UInputEventType::UI_FF_ERASE => { let event = device.process_ff_erase(event)?; ids.insert(event.effect_id() as u16); println!("erase effect ID = {}", event.effect_id()); } _ => { println!("event code {}", event.code()); } } } } }