evdevil 0.4.1

Bindings to Linux' input device APIs: evdev and uinput
Documentation
use std::{env, io, process, str::FromStr};

use evdevil::{Evdev, Scancode, event::Key};

fn main() -> io::Result<()> {
    match &*env::args().skip(1).collect::<Vec<_>>() {
        [path] => dump_keymap(&Evdev::open(path)?),
        [path, arg] if arg.contains('=') => {
            let Some((scancode, key)) = arg.split_once('=') else {
                unreachable!()
            };

            let key = Key::from_str(key)?;
            let scancode = u32::from_str_radix(scancode, 16)
                .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
            let scancode = Scancode::from(scancode);

            println!("setting scancode {scancode} -> {key:?}");
            Evdev::open(path)?.set_keymap_entry(scancode, key)?;

            Ok(())
        }
        [path, scancode] => {
            let scancode = u32::from_str_radix(scancode, 16)
                .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
            let scancode = Scancode::from(scancode);
            query_code(&Evdev::open(path)?, scancode)
        }
        _ => {
            eprintln!("usage: keymap <path> [scancode[=key]]");
            eprintln!();
            eprintln!("If [scancode=key] is absent, prints the whole keymap.");
            eprintln!("If only a scancode is provided, prints the key associated with it.");
            eprintln!(
                "`scancode` must be a hexadecimal code, `key` must be the \
                name of one of the evdev keycodes (eg. `KEY_BACKSPACE`)"
            );
            process::exit(1);
        }
    }
}

fn dump_keymap(device: &Evdev) -> io::Result<()> {
    let keys = device.supported_keys()?;
    if !keys.is_empty() && device.keymap_entry_by_index(0).is_ok() {
        println!("  keymap:");
        for i in 0.. {
            let Some(ent) = device.keymap_entry_by_index(i)? else {
                break;
            };

            println!("  - {:?}", ent);
        }
    } else {
        println!("this device does not have a keymap");
    }
    Ok(())
}

fn query_code(device: &Evdev, code: Scancode) -> io::Result<()> {
    let ent = device.keymap_entry(code)?;
    match ent {
        Some(ent) => println!("scancode {code} is mapped to {:?}", ent.keycode()),
        None => println!("scancode {code} not found in keymap"),
    }
    Ok(())
}