use std::path::PathBuf; use clap::Parser; use evdev::raw_stream::RawDevice; #[derive(Parser, Debug)] #[command(version, about, long_about = None)] struct Args { /// Print additional information about each device. (-vv for even more verbosity) #[arg(short, long, action = clap::ArgAction::Count)] verbose: u8, } fn main() { let args = Args::parse(); let devices = evdev::raw_stream::enumerate(); for (path, dev) in devices { if is_joystick_like(&dev) { print_device(path, dev, args.verbose) } } } const JOYSTICK_BUTTONS: &[evdev::KeyCode] = &[ evdev::KeyCode::BTN_TRIGGER_HAPPY1, evdev::KeyCode::BTN_TRIGGER_HAPPY2, evdev::KeyCode::BTN_TRIGGER_HAPPY3, evdev::KeyCode::BTN_TRIGGER_HAPPY4, evdev::KeyCode::BTN_TRIGGER_HAPPY5, evdev::KeyCode::BTN_TRIGGER_HAPPY6, evdev::KeyCode::BTN_TRIGGER_HAPPY7, evdev::KeyCode::BTN_TRIGGER_HAPPY8, evdev::KeyCode::BTN_TRIGGER_HAPPY9, evdev::KeyCode::BTN_TRIGGER_HAPPY10, evdev::KeyCode::BTN_TRIGGER_HAPPY11, ]; fn is_joystick_like(device: &RawDevice) -> bool { if let Some(_) = device.supported_absolute_axes() { return true; } if let Some(keys) = device.supported_keys() { for key in keys.iter() { if JOYSTICK_BUTTONS.contains(&key) { return true; } } } return false; } fn print_device(path: PathBuf, device: RawDevice, verbose: u8) { println!( "{}: \"{}\"", path.to_str().unwrap_or("unknown_device_path"), device.name().unwrap_or("unknown_device_name") ); if verbose > 0 { let input_id = device.input_id(); println!("\tUUID:\t\t'{}'", device.unique_name().unwrap_or("n/a")); println!("\tVendor:\t\t'0x{:x}'", input_id.vendor()); println!("\tProduct:\t'0x{:x}'", input_id.product()); println!("\tVersion:\t'{}'", input_id.version()); } if verbose > 1 { if let Ok(abs_info) = device.get_absinfo() { if abs_info.count() > 0 { println!("\tAxis Data:"); abs_info.for_each(|info| println!("\t\t{} {}")); } } } if verbose > 0 { println!(); } }