#!/usr/bin/env python3 """Карта «LED-индекс → физическая клавиша» для GANSS ARDOR_Katana (0c45:8006). Построена экспериментально: группы индексов красились контрастными цветами, пользователь сообщал, какие клавиши загорелись (методика и открытые вопросы — в docs/protocol.md). Имена клавиш — позиции US-раскладки. """ __all__ = ["KEYMAP", "BLIND", "UNKNOWN", "PROBE_COLORS", "KEY_SYMBOL_ALIASES", "name"] # Подтверждённая карта (индекс → имя клавиши) KEYMAP = { 0x01: "Esc", 0x02: "F1", 0x03: "F2", 0x04: "F3", 0x05: "F4", 0x06: "F5", 0x07: "F6", 0x08: "F7", 0x09: "F8", 0x0A: "F9", 0x0B: "F10", 0x0C: "F11", 0x0D: "F12", 0x13: "`(~)", 0x14: "1", 0x15: "2", 0x16: "3", 0x17: "4", 0x18: "5", 0x19: "6", 0x1A: "7", 0x1B: "8", 0x1C: "9", 0x1D: "0", 0x1E: "-(_)", 0x1F: "=(+)", 0x20: "NumLock", 0x21: "Num/", 0x22: "Num*", 0x25: "Tab", 0x26: "Q", 0x27: "W", 0x28: "E", 0x29: "R", 0x2A: "T", 0x2B: "Y", 0x2C: "U", 0x2D: "I", 0x2E: "O", 0x2F: "P", 0x30: "[({)", 0x31: "](})", 0x32: "Num7", 0x33: "Num8", 0x34: "Num9", 0x37: "Caps", 0x38: "A", 0x39: "S", 0x3A: "D", 0x3B: "F", 0x3C: "G", 0x3D: "H", 0x3E: "J", 0x3F: "K", 0x40: "L", 0x41: ";", 0x42: "'", 0x43: "\\", 0x44: "Num4", 0x45: "Num5", 0x46: "Num6", 0x49: "LShift", 0x4A: "Z", 0x4B: "X", 0x4C: "C", 0x4D: "V", 0x4E: "B", 0x4F: "N", 0x50: "M", 0x51: ",", 0x52: ".", 0x53: "/", 0x54: "RShift", 0x55: "Enter", 0x56: "Num1", 0x57: "Num2", 0x58: "Num3", 0x5B: "LCtrl", 0x5C: "LWin", 0x5D: "LAlt", 0x5E: "Space", 0x5F: "RAlt", 0x60: "Fn", 0x61: "Menu", 0x62: "RCtrl", 0x63: "ArrowLeft", 0x64: "ArrowDown", 0x65: "ArrowUp", 0x66: "ArrowRight", 0x67: "Backspace", 0x68: "Num0", 0x69: "Num.(Del)", 0x6A: "NumEnter", 0x70: "PrtSc", 0x71: "ScrLk", 0x73: "Pause", 0x74: "Insert", 0x75: "Home", 0x76: "PgUp", 0x77: "Delete", 0x78: "End", 0x79: "PgDown", 0x7A: "Num-", 0x7B: "Num+", } # Слоты, которые красились, но не светятся: ни к какому LED не подключены BLIND = {0x00, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x23, 0x24, 0x35, 0x36, 0x47, 0x48, 0x59, 0x5A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x72, 0x7C, 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E} # Индексы, не идентифицированные на момент снятия карты UNKNOWN = sorted(set(range(0x00, 0x8F)) - set(KEYMAP) - BLIND) # Контрастная палитра для картирования (максимально различимые цвета, # за раз — немного точек) PROBE_COLORS = [ (0xFF, 0x00, 0x00), # красный (0x00, 0xFF, 0x00), # зелёный (0x00, 0x00, 0xFF), # синий (0xFF, 0xFF, 0x00), # жёлтый (0xFF, 0x00, 0xFF), # маджента (0x00, 0xFF, 0xFF), # циан (0xFF, 0x80, 0x00), # оранжевый ] # Дополнительные алиасы клавиш: синоним → каноническое имя из KEYMAP # (в нижнем регистре). Позволяют писать --key tilde=red, --key lbracket=blue. # Символы-псевдонимы ('[', '~', ';') работают напрямую из KEYMAP. KEY_SYMBOL_ALIASES = { # цифровой ряд "tilde": "`", "grave": "`", "backquote": "`", "backtick": "`", "minus": "-", "dash": "-", "hyphen": "-", "equal": "=", "equals": "=", # основной блок "lbracket": "[", "leftbracket": "[", "openbracket": "[", "rbracket": "]", "rightbracket": "]", "closebracket": "]", "semicolon": ";", "colon": ";", "quote": "'", "apostrophe": "'", "singlequote": "'", "backslash": chr(92), "bslash": chr(92), "comma": ",", "period": ".", "dot": ".", "slash": "/", "question": "/", "lessthan": ",", "greaterthan": ".", # пробел и служебные "spacebar": "space", "capslock": "caps", "leftshift": "lshift", "shift": "lshift", "rightshift": "rshift", "leftctrl": "lctrl", "ctrl": "lctrl", "rightctrl": "rctrl", "leftalt": "lalt", "alt": "lalt", "rightalt": "ralt", "altgr": "ralt", "windows": "lwin", "super": "lwin", "meta": "lwin", "menu": "menu", "context": "menu", "apps": "menu", "enter": "enter", "return": "enter", "cr": "enter", "tab": "tab", "escape": "esc", "backspace": "backspace", "bksp": "backspace", "delete": "delete", "del": "delete", "insert": "insert", "ins": "insert", "home": "home", "end": "end", "pageup": "pgup", "pgdn": "pgdown", "pagedown": "pgdown", "printscreen": "prtsc", "prntscr": "prtsc", "pscr": "prtsc", "scrolllock": "scrlk", "slck": "scrlk", "pause": "pause", "break": "pause", "leftarrow": "arrowleft", "rightarrow": "arrowright", "uparrow": "arrowup", "downarrow": "arrowdown", "left": "arrowleft", "right": "arrowright", "up": "arrowup", "down": "arrowdown", # нумпад "numlock": "numlock", "numasterisk": "num*", "numtimes": "num*", "numminus": "num-", "numplus": "num+", "numpoint": "num.", "numdot": "num.", "numdecimal": "num.", "numdel": "num.", "numcomma": "num.", "numperiod": "num.", "numslash": "num/", "numdivide": "num/", "numenter": "numenter", "num0": "num0", "num1": "num1", "num2": "num2", "num3": "num3", "num4": "num4", "num5": "num5", "num6": "num6", "num7": "num7", "num8": "num8", "num9": "num9", } def name(idx: int) -> str: """Имя слота: клавиша, «(blind)» для слепых или 0xNN? для неизвестных.""" if idx in KEYMAP: return KEYMAP[idx] if idx in BLIND: return "(blind)" return f"0x{idx:02x}?" if __name__ == "__main__": print("Confirmed keymap:") for i in sorted(KEYMAP): print(f" 0x{i:02x}: {KEYMAP[i]}") print(f"\nBlind slots: {' '.join(f'{i:02x}' for i in sorted(BLIND))}") print(f"Unidentified ({len(UNKNOWN)}): " + " ".join(f"{i:02x}" for i in UNKNOWN)) # алиасы, сгруппированные по клавишам в порядке раскладки from collections import defaultdict groups = defaultdict(list) for alias, canonical in KEY_SYMBOL_ALIASES.items(): groups[canonical].append(alias) def _idx(canonical: str) -> int: """LED-индекс канонического имени (0xFF — если не найдено).""" for i, lbl in KEYMAP.items(): if lbl.lower() == canonical or lbl.split("(")[0].lower() == canonical: return i return 0xFF print(f"\nKey aliases ({len(KEY_SYMBOL_ALIASES)}):") for canonical in sorted(groups, key=_idx): aliases = ", ".join(sorted(groups[canonical])) print(f" {canonical:12s} <- {aliases}")