xmodmap-pke 0.1.0

xmodmap -pke in mostly pure rust
Documentation
use std::collections::HashMap;
use std::ffi::CStr;
use x11::xlib::{
    Display, KeyCode, KeySym, XDisplayKeycodes, XFree, XGetKeyboardMapping, XKeysymToString,
};

pub type XmodmapPke = HashMap<u8, Vec<String>>;

pub unsafe fn xmodmap_pke(dpy: *mut Display) -> Result<XmodmapPke, String> {
    let mut xmodmap_pke_table: XmodmapPke = HashMap::new();
    let mut min_keycode = 0;
    let mut max_keycode = 0;
    let mut keysyms_per_keycode = 0;
    let mut keymap: *mut KeySym;
    let origkeymap: *mut KeySym;
    XDisplayKeycodes(dpy, &mut min_keycode, &mut max_keycode);
    origkeymap = XGetKeyboardMapping(
        dpy,
        min_keycode as KeyCode,
        max_keycode - min_keycode + 1_i32,
        &mut keysyms_per_keycode,
    );
    if origkeymap.is_null() {
        return Err("unable to get keyboard mapping table.".to_string());
    }
    keymap = origkeymap;
    for i in min_keycode..=max_keycode {
        let mut max;
        max = keysyms_per_keycode - 1;
        while max >= 0 && *keymap.offset(max as isize) == 0_i64 as libc::c_ulong {
            max -= 1
        }
        let mut ksyms: Vec<String> = Vec::new();
        for j in 0..=max {
            let ks: KeySym = *keymap.offset(j as isize);
            let s = if ks != 0_i64 as libc::c_ulong {
                CStr::from_ptr(XKeysymToString(ks)).to_str().unwrap()
            } else {
                "NoSymbol"
            };
            if !s.is_empty() {
                ksyms.push(s.to_string());
            } else {
                ksyms.push(format!("{:0>4x}", ks));
            }
        }
        keymap = keymap.offset(keysyms_per_keycode as isize);
        xmodmap_pke_table.insert(i as u8, ksyms);
    }
    XFree(origkeymap as *mut libc::c_char as *mut libc::c_void);
    Ok(xmodmap_pke_table)
}