use crate::config::key_press::parse_modifier_alias;
use crate::event_handler::{DISGUISED_EVENT_OFFSETTER, KEY_MATCH_ANY};
use anyhow::Context;
use evdev::KeyCode as Key;
use serde::{Deserialize, Deserializer};
use std::error::Error;
use std::str::FromStr;
pub fn deserialize_key<'de, D>(deserializer: D) -> Result<Key, D::Error>
where
D: Deserializer<'de>,
{
let key = String::deserialize(deserializer)?;
parse_key(&key).map_err(serde::de::Error::custom)
}
pub fn parse_key(input: &str) -> Result<Key, Box<dyn Error>> {
let name = input.to_uppercase();
if let Ok(key) = Key::from_str(&name) {
return Ok(key);
}
if let Ok(key) = Key::from_str(&format!("KEY_{name}")) {
return Ok(key);
}
if name.starts_with("CODE_") {
let key_code = name.replacen("CODE_", "", 1);
let key_code = u16::from_str_radix(&key_code, 10).context(format!("Invalid key_code in: {name}"))?;
return Ok(Key(key_code));
}
match &name[..] {
"SHIFT_R" => Ok(Key::KEY_RIGHTSHIFT),
"SHIFT_L" => Ok(Key::KEY_LEFTSHIFT),
"S_R" => Ok(Key::KEY_RIGHTSHIFT),
"S_L" => Ok(Key::KEY_LEFTSHIFT),
"CONTROL_R" => Ok(Key::KEY_RIGHTCTRL),
"CONTROL_L" => Ok(Key::KEY_LEFTCTRL),
"CTRL_R" => Ok(Key::KEY_RIGHTCTRL),
"CTRL_L" => Ok(Key::KEY_LEFTCTRL),
"C_R" => Ok(Key::KEY_RIGHTCTRL),
"C_L" => Ok(Key::KEY_LEFTCTRL),
"ALT_R" => Ok(Key::KEY_RIGHTALT),
"ALT_L" => Ok(Key::KEY_LEFTALT),
"A_R" => Ok(Key::KEY_RIGHTALT),
"A_L" => Ok(Key::KEY_LEFTALT),
"M_R" => Ok(Key::KEY_RIGHTALT),
"M_L" => Ok(Key::KEY_LEFTALT),
"SUPER_R" => Ok(Key::KEY_RIGHTMETA),
"SUPER_L" => Ok(Key::KEY_LEFTMETA),
"WINDOWS_R" => Ok(Key::KEY_RIGHTMETA),
"WINDOWS_L" => Ok(Key::KEY_LEFTMETA),
"WIN_R" => Ok(Key::KEY_RIGHTMETA),
"WIN_L" => Ok(Key::KEY_LEFTMETA),
"W_R" => Ok(Key::KEY_RIGHTMETA),
"W_L" => Ok(Key::KEY_LEFTMETA),
"XRIGHTCURSOR" => Ok(Key(DISGUISED_EVENT_OFFSETTER)),
"XLEFTCURSOR" => Ok(Key(DISGUISED_EVENT_OFFSETTER + 1)),
"XDOWNCURSOR" => Ok(Key(DISGUISED_EVENT_OFFSETTER + 2)),
"XUPCURSOR" => Ok(Key(DISGUISED_EVENT_OFFSETTER + 3)),
"XREL_Z_AXIS_1" => Ok(Key(DISGUISED_EVENT_OFFSETTER + 4)),
"XREL_Z_AXIS_2" => Ok(Key(DISGUISED_EVENT_OFFSETTER + 5)),
"XREL_RX_AXIS_1" => Ok(Key(DISGUISED_EVENT_OFFSETTER + 6)),
"XREL_RX_AXIS_2" => Ok(Key(DISGUISED_EVENT_OFFSETTER + 7)),
"XREL_RY_AXIS_1" => Ok(Key(DISGUISED_EVENT_OFFSETTER + 8)),
"XREL_RY_AXIS_2" => Ok(Key(DISGUISED_EVENT_OFFSETTER + 9)),
"XREL_RZ_AXIS_1" => Ok(Key(DISGUISED_EVENT_OFFSETTER + 10)),
"XREL_RZ_AXIS_2" => Ok(Key(DISGUISED_EVENT_OFFSETTER + 11)),
"XRIGHTSCROLL" => Ok(Key(DISGUISED_EVENT_OFFSETTER + 12)),
"XLEFTSCROLL" => Ok(Key(DISGUISED_EVENT_OFFSETTER + 13)),
"XREL_DIAL_1" => Ok(Key(DISGUISED_EVENT_OFFSETTER + 14)),
"XREL_DIAL_2" => Ok(Key(DISGUISED_EVENT_OFFSETTER + 15)),
"XUPSCROLL" => Ok(Key(DISGUISED_EVENT_OFFSETTER + 16)),
"XDOWNSCROLL" => Ok(Key(DISGUISED_EVENT_OFFSETTER + 17)),
"XREL_MISC_1" => Ok(Key(DISGUISED_EVENT_OFFSETTER + 18)),
"XREL_MISC_2" => Ok(Key(DISGUISED_EVENT_OFFSETTER + 19)),
"XREL_RESERVED_1" => Ok(Key(DISGUISED_EVENT_OFFSETTER + 20)),
"XREL_RESERVED_2" => Ok(Key(DISGUISED_EVENT_OFFSETTER + 21)),
"XHIRES_UPSCROLL" => Ok(Key(DISGUISED_EVENT_OFFSETTER + 22)),
"XHIRES_DOWNSCROLL" => Ok(Key(DISGUISED_EVENT_OFFSETTER + 23)),
"XHIRES_RIGHTSCROLL" => Ok(Key(DISGUISED_EVENT_OFFSETTER + 24)),
"XHIRES_LEFTSCROLL" => Ok(Key(DISGUISED_EVENT_OFFSETTER + 25)),
"ANY" => Ok(KEY_MATCH_ANY),
_ => {
if parse_modifier_alias(input).is_some() {
Err(format!("Modifiers must have left/right specified when used as key: '{input}'").into())
} else {
Err(format!("Unknown key '{input}'").into())
}
}
}
}
#[test]
fn test_parse_key() {
assert_eq!(parse_key("Enter").unwrap(), Key::KEY_ENTER);
assert_eq!(parse_key("key_enter").unwrap(), Key::KEY_ENTER);
assert_eq!(parse_key("KEY_ENTER").unwrap(), Key::KEY_ENTER);
assert_eq!(parse_key("S").unwrap(), Key::KEY_S);
assert_eq!(
parse_key("Shift").unwrap_err().to_string(),
"Modifiers must have left/right specified when used as key: 'Shift'"
);
}
#[test]
fn test_parse_literal_key_code() {
assert_eq!(parse_key("Code_123").unwrap(), Key(123));
assert_eq!(parse_key("Code_0012").unwrap(), Key(12));
assert_eq!(parse_key("Code_abc").unwrap_err().to_string(), "Invalid key_code in: CODE_ABC");
assert_eq!(parse_key("Code_70000").unwrap_err().to_string(), "Invalid key_code in: CODE_70000");
}