391 lines
10 KiB
Rust
391 lines
10 KiB
Rust
|
|
/// Decode a raw 16-bit keycode into a human-readable string.
|
||
|
|
///
|
||
|
|
/// Covers all KaSe firmware keycode ranges: HID basic keys, layer switches,
|
||
|
|
/// macros, Bluetooth, one-shot, mod-tap, layer-tap, tap-dance, and more.
|
||
|
|
pub fn decode_keycode(raw: u16) -> String {
|
||
|
|
// --- HID basic keycodes 0x00..=0xE7 ---
|
||
|
|
if raw <= 0x00E7 {
|
||
|
|
return hid_key_name(raw as u8);
|
||
|
|
}
|
||
|
|
|
||
|
|
// --- MO (Momentary Layer): 0x0100..=0x0A00, low byte == 0 ---
|
||
|
|
if raw >= 0x0100 && raw <= 0x0A00 && (raw & 0xFF) == 0 {
|
||
|
|
let layer = (raw >> 8) - 1;
|
||
|
|
return format!("MO {layer}");
|
||
|
|
}
|
||
|
|
|
||
|
|
// --- TO (Toggle Layer): 0x0B00..=0x1400, low byte == 0 ---
|
||
|
|
if raw >= 0x0B00 && raw <= 0x1400 && (raw & 0xFF) == 0 {
|
||
|
|
let layer = (raw >> 8) - 0x0B;
|
||
|
|
return format!("TO {layer}");
|
||
|
|
}
|
||
|
|
|
||
|
|
// --- MACRO: 0x1500..=0x2800, low byte == 0 ---
|
||
|
|
if raw >= 0x1500 && raw <= 0x2800 && (raw & 0xFF) == 0 {
|
||
|
|
let idx = (raw >> 8) - 0x14;
|
||
|
|
return format!("M{idx}");
|
||
|
|
}
|
||
|
|
|
||
|
|
// --- BT keycodes ---
|
||
|
|
match raw {
|
||
|
|
0x2900 => return "BT Next".into(),
|
||
|
|
0x2A00 => return "BT Prev".into(),
|
||
|
|
0x2B00 => return "BT Pair".into(),
|
||
|
|
0x2C00 => return "BT Disc".into(),
|
||
|
|
0x2E00 => return "USB/BT".into(),
|
||
|
|
0x2F00 => return "BT On/Off".into(),
|
||
|
|
_ => {}
|
||
|
|
}
|
||
|
|
|
||
|
|
// --- OSM (One-Shot Mod): 0x3000..=0x30FF ---
|
||
|
|
if raw >= 0x3000 && raw <= 0x30FF {
|
||
|
|
let mods = (raw & 0xFF) as u8;
|
||
|
|
return format!("OSM {}", mod_name(mods));
|
||
|
|
}
|
||
|
|
|
||
|
|
// --- OSL (One-Shot Layer): 0x3100..=0x310F ---
|
||
|
|
if raw >= 0x3100 && raw <= 0x310F {
|
||
|
|
let layer = raw & 0x0F;
|
||
|
|
return format!("OSL {layer}");
|
||
|
|
}
|
||
|
|
|
||
|
|
// --- Fixed special codes ---
|
||
|
|
match raw {
|
||
|
|
0x3200 => return "Caps Word".into(),
|
||
|
|
0x3300 => return "Repeat".into(),
|
||
|
|
0x3400 => return "Leader".into(),
|
||
|
|
0x3500 => return "Feed".into(),
|
||
|
|
0x3600 => return "Play".into(),
|
||
|
|
0x3700 => return "Sleep".into(),
|
||
|
|
0x3800 => return "Meds".into(),
|
||
|
|
0x3900 => return "GEsc".into(),
|
||
|
|
0x3A00 => return "Layer Lock".into(),
|
||
|
|
0x3C00 => return "AS Toggle".into(),
|
||
|
|
_ => {}
|
||
|
|
}
|
||
|
|
|
||
|
|
// --- KO (Key Override) slots: 0x3D00..=0x3DFF ---
|
||
|
|
if raw >= 0x3D00 && raw <= 0x3DFF {
|
||
|
|
let slot = raw & 0xFF;
|
||
|
|
return format!("KO {slot}");
|
||
|
|
}
|
||
|
|
|
||
|
|
// --- LT (Layer-Tap): 0x4000..=0x4FFF ---
|
||
|
|
// layout: 0x4LKK where L = layer (0..F), KK = HID keycode
|
||
|
|
if raw >= 0x4000 && raw <= 0x4FFF {
|
||
|
|
let layer = (raw >> 8) & 0x0F;
|
||
|
|
let kc = (raw & 0xFF) as u8;
|
||
|
|
return format!("LT {} {}", layer, hid_key_name(kc));
|
||
|
|
}
|
||
|
|
|
||
|
|
// --- MT (Mod-Tap): 0x5000..=0x5FFF ---
|
||
|
|
// layout: 0x5MKK where M = mod nibble (4 bits), KK = HID keycode
|
||
|
|
if raw >= 0x5000 && raw <= 0x5FFF {
|
||
|
|
let mods = ((raw >> 8) & 0x0F) as u8;
|
||
|
|
let kc = (raw & 0xFF) as u8;
|
||
|
|
return format!("MT {} {}", mod_name(mods), hid_key_name(kc));
|
||
|
|
}
|
||
|
|
|
||
|
|
// --- TD (Tap Dance): 0x6000..=0x6FFF ---
|
||
|
|
if raw >= 0x6000 && raw <= 0x6FFF {
|
||
|
|
let index = (raw >> 8) & 0x0F;
|
||
|
|
return format!("TD {index}");
|
||
|
|
}
|
||
|
|
|
||
|
|
// --- Unknown ---
|
||
|
|
format!("0x{raw:04X}")
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Decode a modifier bitmask into a human-readable string.
|
||
|
|
///
|
||
|
|
/// Bits: 0x01=Ctrl, 0x02=Shift, 0x04=Alt, 0x08=GUI,
|
||
|
|
/// 0x10=RCtrl, 0x20=RShift, 0x40=RAlt, 0x80=RGUI.
|
||
|
|
/// Multiple modifiers are joined with "+".
|
||
|
|
pub fn mod_name(mod_mask: u8) -> String {
|
||
|
|
let mut parts = Vec::new();
|
||
|
|
if mod_mask & 0x01 != 0 { parts.push("Ctrl"); }
|
||
|
|
if mod_mask & 0x02 != 0 { parts.push("Shift"); }
|
||
|
|
if mod_mask & 0x04 != 0 { parts.push("Alt"); }
|
||
|
|
if mod_mask & 0x08 != 0 { parts.push("GUI"); }
|
||
|
|
if mod_mask & 0x10 != 0 { parts.push("RCtrl"); }
|
||
|
|
if mod_mask & 0x20 != 0 { parts.push("RShift"); }
|
||
|
|
if mod_mask & 0x40 != 0 { parts.push("RAlt"); }
|
||
|
|
if mod_mask & 0x80 != 0 { parts.push("RGUI"); }
|
||
|
|
if parts.is_empty() {
|
||
|
|
format!("0x{mod_mask:02X}")
|
||
|
|
} else {
|
||
|
|
parts.join("+")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Map a single HID usage code (0x00..=0xE7) to a short readable name.
|
||
|
|
pub fn hid_key_name(code: u8) -> String {
|
||
|
|
match code {
|
||
|
|
// No key / transparent
|
||
|
|
0x00 => "None",
|
||
|
|
// 0x01 = ErrorRollOver, 0x02 = POSTFail, 0x03 = ErrorUndefined (not user-facing)
|
||
|
|
0x01 => "ErrRollOver",
|
||
|
|
0x02 => "POSTFail",
|
||
|
|
0x03 => "ErrUndef",
|
||
|
|
|
||
|
|
// Letters
|
||
|
|
0x04 => "A",
|
||
|
|
0x05 => "B",
|
||
|
|
0x06 => "C",
|
||
|
|
0x07 => "D",
|
||
|
|
0x08 => "E",
|
||
|
|
0x09 => "F",
|
||
|
|
0x0A => "G",
|
||
|
|
0x0B => "H",
|
||
|
|
0x0C => "I",
|
||
|
|
0x0D => "J",
|
||
|
|
0x0E => "K",
|
||
|
|
0x0F => "L",
|
||
|
|
0x10 => "M",
|
||
|
|
0x11 => "N",
|
||
|
|
0x12 => "O",
|
||
|
|
0x13 => "P",
|
||
|
|
0x14 => "Q",
|
||
|
|
0x15 => "R",
|
||
|
|
0x16 => "S",
|
||
|
|
0x17 => "T",
|
||
|
|
0x18 => "U",
|
||
|
|
0x19 => "V",
|
||
|
|
0x1A => "W",
|
||
|
|
0x1B => "X",
|
||
|
|
0x1C => "Y",
|
||
|
|
0x1D => "Z",
|
||
|
|
|
||
|
|
// Number row
|
||
|
|
0x1E => "1",
|
||
|
|
0x1F => "2",
|
||
|
|
0x20 => "3",
|
||
|
|
0x21 => "4",
|
||
|
|
0x22 => "5",
|
||
|
|
0x23 => "6",
|
||
|
|
0x24 => "7",
|
||
|
|
0x25 => "8",
|
||
|
|
0x26 => "9",
|
||
|
|
0x27 => "0",
|
||
|
|
|
||
|
|
// Common control keys
|
||
|
|
0x28 => "Enter",
|
||
|
|
0x29 => "Esc",
|
||
|
|
0x2A => "Backspace",
|
||
|
|
0x2B => "Tab",
|
||
|
|
0x2C => "Space",
|
||
|
|
|
||
|
|
// Punctuation / symbols
|
||
|
|
0x2D => "-",
|
||
|
|
0x2E => "=",
|
||
|
|
0x2F => "[",
|
||
|
|
0x30 => "]",
|
||
|
|
0x31 => "\\",
|
||
|
|
0x32 => "Europe1",
|
||
|
|
0x33 => ";",
|
||
|
|
0x34 => "'",
|
||
|
|
0x35 => "`",
|
||
|
|
0x36 => ",",
|
||
|
|
0x37 => ".",
|
||
|
|
0x38 => "/",
|
||
|
|
|
||
|
|
// Caps Lock
|
||
|
|
0x39 => "Caps Lock",
|
||
|
|
|
||
|
|
// Function keys
|
||
|
|
0x3A => "F1",
|
||
|
|
0x3B => "F2",
|
||
|
|
0x3C => "F3",
|
||
|
|
0x3D => "F4",
|
||
|
|
0x3E => "F5",
|
||
|
|
0x3F => "F6",
|
||
|
|
0x40 => "F7",
|
||
|
|
0x41 => "F8",
|
||
|
|
0x42 => "F9",
|
||
|
|
0x43 => "F10",
|
||
|
|
0x44 => "F11",
|
||
|
|
0x45 => "F12",
|
||
|
|
|
||
|
|
// Navigation / editing cluster
|
||
|
|
0x46 => "PrtSc",
|
||
|
|
0x47 => "ScrLk",
|
||
|
|
0x48 => "Pause",
|
||
|
|
0x49 => "Ins",
|
||
|
|
0x4A => "Home",
|
||
|
|
0x4B => "PgUp",
|
||
|
|
0x4C => "Del",
|
||
|
|
0x4D => "End",
|
||
|
|
0x4E => "PgDn",
|
||
|
|
|
||
|
|
// Arrow keys
|
||
|
|
0x4F => "Right",
|
||
|
|
0x50 => "Left",
|
||
|
|
0x51 => "Down",
|
||
|
|
0x52 => "Up",
|
||
|
|
|
||
|
|
// Keypad
|
||
|
|
0x53 => "NumLk",
|
||
|
|
0x54 => "Num /",
|
||
|
|
0x55 => "Num *",
|
||
|
|
0x56 => "Num -",
|
||
|
|
0x57 => "Num +",
|
||
|
|
0x58 => "Num Enter",
|
||
|
|
0x59 => "Num 1",
|
||
|
|
0x5A => "Num 2",
|
||
|
|
0x5B => "Num 3",
|
||
|
|
0x5C => "Num 4",
|
||
|
|
0x5D => "Num 5",
|
||
|
|
0x5E => "Num 6",
|
||
|
|
0x5F => "Num 7",
|
||
|
|
0x60 => "Num 8",
|
||
|
|
0x61 => "Num 9",
|
||
|
|
0x62 => "Num 0",
|
||
|
|
0x63 => "Num .",
|
||
|
|
0x64 => "Europe2",
|
||
|
|
0x65 => "Menu",
|
||
|
|
0x66 => "Power",
|
||
|
|
0x67 => "Num =",
|
||
|
|
|
||
|
|
// F13-F24
|
||
|
|
0x68 => "F13",
|
||
|
|
0x69 => "F14",
|
||
|
|
0x6A => "F15",
|
||
|
|
0x6B => "F16",
|
||
|
|
0x6C => "F17",
|
||
|
|
0x6D => "F18",
|
||
|
|
0x6E => "F19",
|
||
|
|
0x6F => "F20",
|
||
|
|
0x70 => "F21",
|
||
|
|
0x71 => "F22",
|
||
|
|
0x72 => "F23",
|
||
|
|
0x73 => "F24",
|
||
|
|
|
||
|
|
// Misc system keys
|
||
|
|
0x74 => "Execute",
|
||
|
|
0x75 => "Help",
|
||
|
|
0x76 => "Menu2",
|
||
|
|
0x77 => "Select",
|
||
|
|
0x78 => "Stop",
|
||
|
|
0x79 => "Again",
|
||
|
|
0x7A => "Undo",
|
||
|
|
0x7B => "Cut",
|
||
|
|
0x7C => "Copy",
|
||
|
|
0x7D => "Paste",
|
||
|
|
0x7E => "Find",
|
||
|
|
0x7F => "Mute",
|
||
|
|
0x80 => "Vol Up",
|
||
|
|
0x81 => "Vol Down",
|
||
|
|
|
||
|
|
// Locking keys
|
||
|
|
0x82 => "Lock Caps",
|
||
|
|
0x83 => "Lock Num",
|
||
|
|
0x84 => "Lock Scroll",
|
||
|
|
|
||
|
|
// Keypad extras
|
||
|
|
0x85 => "Num ,",
|
||
|
|
0x86 => "Num =2",
|
||
|
|
|
||
|
|
// International / Kanji
|
||
|
|
0x87 => "Kanji1",
|
||
|
|
0x88 => "Kanji2",
|
||
|
|
0x89 => "Kanji3",
|
||
|
|
0x8A => "Kanji4",
|
||
|
|
0x8B => "Kanji5",
|
||
|
|
0x8C => "Kanji6",
|
||
|
|
0x8D => "Kanji7",
|
||
|
|
0x8E => "Kanji8",
|
||
|
|
0x8F => "Kanji9",
|
||
|
|
|
||
|
|
// Language keys
|
||
|
|
0x90 => "Lang1",
|
||
|
|
0x91 => "Lang2",
|
||
|
|
0x92 => "Lang3",
|
||
|
|
0x93 => "Lang4",
|
||
|
|
0x94 => "Lang5",
|
||
|
|
0x95 => "Lang6",
|
||
|
|
0x96 => "Lang7",
|
||
|
|
0x97 => "Lang8",
|
||
|
|
0x98 => "Lang9",
|
||
|
|
|
||
|
|
// Rare system keys
|
||
|
|
0x99 => "Alt Erase",
|
||
|
|
0x9A => "SysReq",
|
||
|
|
0x9B => "Cancel",
|
||
|
|
0x9C => "Clear",
|
||
|
|
0x9D => "Prior",
|
||
|
|
0x9E => "Return",
|
||
|
|
0x9F => "Separator",
|
||
|
|
0xA0 => "Out",
|
||
|
|
0xA1 => "Oper",
|
||
|
|
0xA2 => "Clear Again",
|
||
|
|
0xA3 => "CrSel",
|
||
|
|
0xA4 => "ExSel",
|
||
|
|
|
||
|
|
// 0xA5..=0xAF reserved / not defined in standard HID tables
|
||
|
|
|
||
|
|
// Extended keypad
|
||
|
|
0xB0 => "Num 00",
|
||
|
|
0xB1 => "Num 000",
|
||
|
|
0xB2 => "Thousands Sep",
|
||
|
|
0xB3 => "Decimal Sep",
|
||
|
|
0xB4 => "Currency",
|
||
|
|
0xB5 => "Currency Sub",
|
||
|
|
0xB6 => "Num (",
|
||
|
|
0xB7 => "Num )",
|
||
|
|
0xB8 => "Num {",
|
||
|
|
0xB9 => "Num }",
|
||
|
|
0xBA => "Num Tab",
|
||
|
|
0xBB => "Num Bksp",
|
||
|
|
0xBC => "Num A",
|
||
|
|
0xBD => "Num B",
|
||
|
|
0xBE => "Num C",
|
||
|
|
0xBF => "Num D",
|
||
|
|
0xC0 => "Num E",
|
||
|
|
0xC1 => "Num F",
|
||
|
|
0xC2 => "Num XOR",
|
||
|
|
0xC3 => "Num ^",
|
||
|
|
0xC4 => "Num %",
|
||
|
|
0xC5 => "Num <",
|
||
|
|
0xC6 => "Num >",
|
||
|
|
0xC7 => "Num &",
|
||
|
|
0xC8 => "Num &&",
|
||
|
|
0xC9 => "Num |",
|
||
|
|
0xCA => "Num ||",
|
||
|
|
0xCB => "Num :",
|
||
|
|
0xCC => "Num #",
|
||
|
|
0xCD => "Num Space",
|
||
|
|
0xCE => "Num @",
|
||
|
|
0xCF => "Num !",
|
||
|
|
0xD0 => "Num M Store",
|
||
|
|
0xD1 => "Num M Recall",
|
||
|
|
0xD2 => "Num M Clear",
|
||
|
|
0xD3 => "Num M+",
|
||
|
|
0xD4 => "Num M-",
|
||
|
|
0xD5 => "Num M*",
|
||
|
|
0xD6 => "Num M/",
|
||
|
|
0xD7 => "Num +/-",
|
||
|
|
0xD8 => "Num Clear",
|
||
|
|
0xD9 => "Num ClrEntry",
|
||
|
|
0xDA => "Num Binary",
|
||
|
|
0xDB => "Num Octal",
|
||
|
|
0xDC => "Num Decimal",
|
||
|
|
0xDD => "Num Hex",
|
||
|
|
|
||
|
|
// 0xDE..=0xDF reserved
|
||
|
|
|
||
|
|
// Modifier keys
|
||
|
|
0xE0 => "LCtrl",
|
||
|
|
0xE1 => "LShift",
|
||
|
|
0xE2 => "LAlt",
|
||
|
|
0xE3 => "LGUI",
|
||
|
|
0xE4 => "RCtrl",
|
||
|
|
0xE5 => "RShift",
|
||
|
|
0xE6 => "RAlt",
|
||
|
|
0xE7 => "RGUI",
|
||
|
|
|
||
|
|
// Anything else in 0x00..=0xFF not covered above
|
||
|
|
_ => return format!("0x{code:02X}"),
|
||
|
|
}
|
||
|
|
.into()
|
||
|
|
}
|