okey-cli 0.1.2

An advanced, easy-to-use key remapper for Linux
Documentation
use std::{cell::RefCell, collections::HashMap, hash::Hash, str::FromStr};

use serde::{
    de::{value::StringDeserializer, IntoDeserializer},
    Deserialize, Deserializer,
};

use super::defaults;

#[derive(Debug, Deserialize)]
pub struct Config {
    #[serde(default)]
    pub defaults: DefaultConfig,
    pub keyboards: Vec<KeyboardConfig>,
}

#[derive(Debug, Default, Clone, Deserialize)]
pub struct DefaultConfig {
    #[serde(default)]
    pub tap_dance: DefaultTapDanceConfig,
    #[serde(default)]
    pub combo: DefaultComboConfig,
    #[serde(default)]
    pub general: GeneralConfig,
}

#[derive(Debug, Clone, Deserialize)]
pub struct DefaultTapDanceConfig {
    #[serde(default = "defaults::tap_dance_timeout")]
    pub default_timeout: u16,
}

impl Default for DefaultTapDanceConfig {
    fn default() -> Self {
        Self {
            default_timeout: defaults::tap_dance_timeout(),
        }
    }
}

#[derive(Debug, Clone, Deserialize)]
pub struct DefaultComboConfig {
    #[serde(default = "defaults::combo_threshold")]
    pub default_threshold: u16,
}

impl Default for DefaultComboConfig {
    fn default() -> Self {
        Self {
            default_threshold: defaults::combo_threshold(),
        }
    }
}

#[derive(Debug, Clone, Deserialize)]
pub struct GeneralConfig {
    #[serde(default = "defaults::event_poll_timeout")]
    pub event_poll_timeout: u16,
    #[serde(default = "defaults::deferred_key_delay")]
    pub deferred_key_delay: u16,
    #[serde(default = "defaults::unicode_input_delay")]
    pub unicode_input_delay: u16,
    #[serde(default = "defaults::maximum_lookup_depth")]
    pub maximum_lookup_depth: u8,
}

impl Default for GeneralConfig {
    fn default() -> Self {
        Self {
            event_poll_timeout: defaults::event_poll_timeout(),
            deferred_key_delay: defaults::deferred_key_delay(),
            unicode_input_delay: defaults::unicode_input_delay(),
            maximum_lookup_depth: defaults::maximum_lookup_depth(),
        }
    }
}

#[derive(Debug, Deserialize)]
pub struct KeyboardConfig {
    pub name: String,
    #[serde(default)]
    pub keys: HashMap<KeyCode, KeyAction>,
    #[serde(default)]
    pub combos: ComboConfig,
    #[serde(default)]
    pub tap_dances: HashMap<KeyCode, TapDanceConfig>,
    #[serde(default)]
    pub layers: HashMap<String, LayerDefinition>,
}

#[derive(Debug, Default, Deserialize)]
pub struct ComboConfig(pub Vec<ComboDefinition>);

#[derive(Debug, Deserialize)]
pub struct ComboDefinition {
    pub keys: Vec<KeyCode>,
    pub action: KeyAction,
}

#[derive(Debug, Deserialize)]
pub struct TapDanceConfig {
    pub timeout: Option<u16>,
    pub tap: KeyAction,
    pub hold: KeyAction,
}

#[derive(Debug, Deserialize)]
pub struct LayerDefinition {
    pub modifier: LayerModiferConfig,
    pub keys: HashMap<KeyCode, KeyAction>,
}

#[derive(Debug, Deserialize)]
#[serde(untagged)]
pub enum LayerModiferConfig {
    Simple(KeyCode),
    Custom {
        key: KeyCode,
        #[serde(default)]
        r#type: LayerModifierKind,
    },
}

impl LayerModiferConfig {
    pub fn get_modifer(&self) -> &KeyCode {
        match self {
            LayerModiferConfig::Simple(keycode) => keycode,
            LayerModiferConfig::Custom { key, r#type: _ } => key,
        }
    }

    pub fn get_modifer_kind(&self) -> LayerModifierKind {
        match self {
            LayerModiferConfig::Simple(_) => LayerModifierKind::Momentary,
            LayerModiferConfig::Custom { key: _, r#type } => *r#type,
        }
    }
}

#[derive(Debug, Default, Clone, Copy, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum LayerModifierKind {
    #[default]
    Momentary,
    Toggle,
    Oneshoot,
}

#[derive(Debug, Clone, Deserialize)]
#[serde(untagged)]
pub enum KeyAction {
    KeyCode(KeyCode),
    Macro(Macro),
}

#[derive(Debug, Clone, Deserialize)]
#[serde(untagged)]
pub enum Macro {
    Single(EventMacro),
    Sequence(Vec<EventMacro>),
}

#[derive(Debug, Clone, Deserialize)]
#[serde(untagged)]
pub enum EventMacro {
    Tap(KeyCode),
    Press { press: KeyCode },
    Hold { hold: KeyCode },
    Release { release: KeyCode },
    Delay { delay: u32 },
    String { string: String },
    Env { env: String },
    Unicode { unicode: String },
    Shell { shell: String, trim: Option<bool> },
}

const SAFE_KEYCODE_START: u16 = 999;
const SHIFTED_KEYCODE_START: u16 = 800;

#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub struct KeyCode(evdev::KeyCode);

impl From<evdev::KeyCode> for KeyCode {
    fn from(value: evdev::KeyCode) -> Self {
        Self(value)
    }
}

const fn to_shifted_code(code: evdev::KeyCode) -> isize {
    (SHIFTED_KEYCODE_START + code.code()) as isize
}

#[derive(Debug, Clone, Copy, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
#[allow(clippy::enum_variant_names)]
pub enum ShiftedKeycodes {
    KeyExclamation = to_shifted_code(evdev::KeyCode::KEY_1),
    KeyAt = to_shifted_code(evdev::KeyCode::KEY_2),
    KeyHash = to_shifted_code(evdev::KeyCode::KEY_3),
    KeyDollarsign = to_shifted_code(evdev::KeyCode::KEY_4),
    KeyPercent = to_shifted_code(evdev::KeyCode::KEY_5),
    KeyCaret = to_shifted_code(evdev::KeyCode::KEY_6),
    KeyAmpersand = to_shifted_code(evdev::KeyCode::KEY_7),
    KeyStar = to_shifted_code(evdev::KeyCode::KEY_8),
    KeyLeftparen = to_shifted_code(evdev::KeyCode::KEY_9),
    KeyRightparen = to_shifted_code(evdev::KeyCode::KEY_0),
    KeyUnderscore = to_shifted_code(evdev::KeyCode::KEY_MINUS),
    KeyPlus = to_shifted_code(evdev::KeyCode::KEY_EQUAL),
    KeyLeftcurly = to_shifted_code(evdev::KeyCode::KEY_LEFTBRACE),
    KeyRightcurly = to_shifted_code(evdev::KeyCode::KEY_RIGHTBRACE),
    KeyColon = to_shifted_code(evdev::KeyCode::KEY_SEMICOLON),
    KeyDoublequote = to_shifted_code(evdev::KeyCode::KEY_APOSTROPHE),
    KeyLess = to_shifted_code(evdev::KeyCode::KEY_COMMA),
    KeyGreater = to_shifted_code(evdev::KeyCode::KEY_DOT),
    KeyQuestion = to_shifted_code(evdev::KeyCode::KEY_SLASH),
    KeyTilde = to_shifted_code(evdev::KeyCode::KEY_GRAVE),
    KeyPipe = to_shifted_code(evdev::KeyCode::KEY_BACKSLASH),
}

impl KeyCode {
    pub fn new(code: u16) -> Self {
        Self(evdev::KeyCode::new(code))
    }

    pub fn value(self) -> u16 {
        self.0.code()
    }

    pub fn is_custom(self) -> bool {
        self.0.code() >= SAFE_KEYCODE_START
    }

    pub fn is_shifted(self) -> bool {
        self.0.code() > SHIFTED_KEYCODE_START && self.0.code() < SAFE_KEYCODE_START
    }

    pub fn unshift(self) -> Self {
        KeyCode::new(self.0.code() - SHIFTED_KEYCODE_START)
    }
}

thread_local! {
    static CUSTOM_KEYCODES: RefCell<HashMap<String, u16>> = HashMap::default().into();
}

impl<'de> Deserialize<'de> for KeyCode {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let key = String::deserialize(deserializer)?;
        let key_deserializer: StringDeserializer<D::Error> = key.clone().into_deserializer();

        if let Ok(shifted) = ShiftedKeycodes::deserialize(key_deserializer) {
            return Ok(KeyCode(evdev::KeyCode(shifted as u16)));
        }

        if let Ok(value) = evdev::KeyCode::from_str(&key) {
            return Ok(KeyCode(value));
        }

        CUSTOM_KEYCODES.with_borrow_mut(|keycodes| {
            let count = keycodes.len() as u16;
            let entry = keycodes.entry(key).or_insert(count + SAFE_KEYCODE_START);
            Ok(KeyCode(evdev::KeyCode::new(*entry)))
        })
    }
}