xremap 0.14.15

Dynamic key remapp for X and Wayland
Documentation
use crate::config::key::parse_key;
use evdev::KeyCode as Key;
use serde::{Deserialize, Deserializer};
use std::error::{self, Error};

#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct KeyPress {
    pub key: Key,
    pub modifiers: Vec<Modifier>,
}

#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum Modifier {
    // Matches left, right, or both
    Shift,
    Control,
    Alt,
    Windows,
    // Matches exactly this key
    Key(Key),
}

impl<'de> Deserialize<'de> for KeyPress {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let key_press = String::deserialize(deserializer)?;
        parse_key_press(&key_press).map_err(serde::de::Error::custom)
    }
}

fn parse_key_press(input: &str) -> Result<KeyPress, Box<dyn error::Error>> {
    let keys: Vec<&str> = input.split('-').collect();
    if let Some((key, modifier_keys)) = keys.split_last() {
        let mut modifiers = vec![];
        for modifier_key in modifier_keys.iter() {
            modifiers.push(parse_modifier(modifier_key)?);
        }

        Ok(KeyPress {
            key: parse_key(key)?,
            modifiers,
        })
    } else {
        Err(format!("empty key_press: {input}").into())
    }
}

fn parse_modifier(modifier: &str) -> Result<Modifier, Box<dyn Error>> {
    // Everything is case-insensitive
    match &modifier.to_uppercase()[..] {
        // Shift
        "SHIFT" => Ok(Modifier::Shift),
        // Control
        "C" => Ok(Modifier::Control),
        "CTRL" => Ok(Modifier::Control),
        "CONTROL" => Ok(Modifier::Control),
        // Alt
        "M" => Ok(Modifier::Alt),
        "ALT" => Ok(Modifier::Alt),
        // Windows
        "SUPER" => Ok(Modifier::Windows),
        "WIN" => Ok(Modifier::Windows),
        "WINDOWS" => Ok(Modifier::Windows),
        // else
        key => parse_key(key).map(Modifier::Key),
    }
}

#[test]
fn test_parse_key_press() {
    // Can have modifiers with unspecified sidedness
    assert_eq!(
        parse_key_press("Shift-2").unwrap(),
        KeyPress {
            key: Key::KEY_2,
            modifiers: vec![Modifier::Shift]
        }
    );

    // Can use custom key names. Defined in `parse_key`.
    assert_eq!(
        parse_key_press("Shift_L-2").unwrap(),
        KeyPress {
            key: Key::KEY_2,
            modifiers: vec![Modifier::Key(Key::KEY_LEFTSHIFT)]
        }
    );

    // All keys are accepted as modifiers, because it's not possible to know
    // if the key is listed in virtual_modifiers at this point.
    assert_eq!(
        parse_key_press("Enter-2").unwrap(),
        KeyPress {
            key: Key::KEY_2,
            modifiers: vec![Modifier::Key(Key::KEY_ENTER)]
        }
    );
}