'up': 126, 'down': 125, 'left': 123, 'right': 124, 'page_up': 116, 'page_down': 121, 'home': 115, 'end': 119, 'f1': 122, 'f2': 120, 'f3': 99, 'f4': 118, 'f5': 96, 'f6': 97, 'f7': 98, 'f8': 100, 'f9': 101, 'f10': 109, 'f11': 103, 'f12': 111, 'f13': 105, 'f14': 107, 'f15': 113, 'f16': 106, 'f17': 64, 'f18': 79, 'f19': 80, 'f20': 90, 'kp_0': 82, 'kp_1': 83, 'kp_2': 84, 'kp_3': 85, 'kp_4': 86, 'kp_5': 87, 'kp_6': 88, 'kp_7': 89, 'kp_8': 91, 'kp_9': 92, 'kp_add': 69, 'kp_decimal': 65, 'kp_delete': 71, 'kp_divide': 75, 'kp_enter': 76, 'kp_equal': 81, 'kp_multiply': 67, 'kp_subtract': 78, } for name, code in NX_KEYS.items(): KEYNAME_TO_KEYCODE[name.lower()] = code + NX_KEY_OFFSET add_modifiers_aliases(KEYNAME_TO_KEYCODE) DEADKEY_SYMBOLS = { 'dead_acute': '´', 'dead_circumflex': '^', 'dead_diaeresis': '¨', 'dead_grave': '`', 'dead_tilde': '~', } def down(seq): return [(x, True) for x in seq] def up(seq): return [(x, False) for x in reversed(seq)]
"kp_6": 88, "kp_7": 89, "kp_8": 91, "kp_9": 92, "kp_add": 69, "kp_decimal": 65, "kp_delete": 71, "kp_divide": 75, "kp_enter": 76, "kp_equal": 81, "kp_multiply": 67, "kp_subtract": 78, } for name, code in NX_KEYS.items(): KEYNAME_TO_KEYCODE[name.lower()] = code + NX_KEY_OFFSET add_modifiers_aliases(KEYNAME_TO_KEYCODE) DEADKEY_SYMBOLS = { "dead_acute": "´", "dead_circumflex": "^", "dead_diaeresis": "¨", "dead_grave": "`", "dead_tilde": "~", } def down(seq): return [(x, True) for x in seq] def up(seq):
def __init__(self, layout_id=None, debug=False): if layout_id is None: layout_id = KeyboardLayout.current_layout_id() self.layout_id = layout_id # Find virtual key code for each scan code (if any). sc_to_vk = {} vk_to_sc = {} for sc in range(0x01, 0x7f + 1): vk = MapVirtualKeyEx(sc, 3, layout_id) if vk != 0: sc_to_vk[sc] = vk vk_to_sc[vk] = sc state = (wintypes.BYTE * 256)() strbuf = ctypes.create_unicode_buffer(8) def fill_state(ss): for mod_state, mod_vk in ( (SHIFT_STATE.SHIFT, VK.SHIFT), (SHIFT_STATE.CTRL, VK.CONTROL), (SHIFT_STATE.MENU, VK.MENU), ): state[mod_vk] = 0x80 if (ss & mod_state) != 0 else 0 def to_unichr(vk, sc, ss): fill_state(ss) rc = ToUnicodeEx(vk, sc, state, strbuf, len(strbuf), 0, layout_id) if rc > 0: dead_key = False char = ctypes.wstring_at(strbuf.value, rc) elif rc < 0: # Dead key, flush state. dead_key = True fill_state(0) rc = ToUnicodeEx(VK.SPACE, vk_to_sc[VK.SPACE], state, strbuf, len(strbuf), 0, layout_id) char = ctypes.wstring_at(strbuf.value, rc) if rc > 0 else '' else: dead_key = False char = '' return char, dead_key def sort_vk_ss_list(vk_ss_list): # Prefer lower modifiers combo. return sorted(vk_ss_list, key=lambda vk_ss: popcount_8(vk_ss[1])) char_to_vk_ss = defaultdict(list) keyname_to_vk = defaultdict(list) for sc, vk in sorted(sc_to_vk.items()): for ss in SHIFT_STATE: if ss in (SHIFT_STATE.MENU, SHIFT_STATE.SHIFT_MENU): # Alt and Shift+Alt don't work, so skip them. continue char, dead_key = to_unichr(vk, sc, ss) if debug and char: print('%s%s -> %s [%r] %s' % ( shift_state_str(ss), vk_to_str(vk), char, char, '[dead key]' if dead_key else '', )) kn = None if char: kn = CHAR_TO_KEYNAME.get(char) if dead_key: kn = kn and DEAD_KEYNAME.get(kn, 'dead_' + kn) else: char_to_vk_ss[char].append((vk, ss)) if kn: keyname_to_vk[kn].append((vk, ss)) self.char_to_vk_ss = { char: sort_vk_ss_list(vk_ss_list)[0] for char, vk_ss_list in char_to_vk_ss.items() } self.char_to_vk_ss['\n'] = self.char_to_vk_ss['\r'] self.keyname_to_vk = { kn: vk for vk, kn in VK_TO_KEYNAME.items() } self.keyname_to_vk.update({ 'next' : VK.NEXT, 'prior' : VK.PRIOR, 'kp_delete' : VK.DELETE, 'kp_enter' : VK.RETURN, 'break' : VK.CANCEL, }) self.keyname_to_vk.update({ kn: sort_vk_ss_list(vk_ss_list)[0][0] for kn, vk_ss_list in keyname_to_vk.items() }) add_modifiers_aliases(self.keyname_to_vk) self.ss_to_vks = {} for ss in SHIFT_STATE: vk_list = [] for mod_state, mod_vk in ( (SHIFT_STATE.SHIFT, VK.SHIFT), (SHIFT_STATE.CTRL, VK.CONTROL), (SHIFT_STATE.MENU, VK.MENU), ): if (ss & mod_state) != 0: vk_list.append(mod_vk) self.ss_to_vks[ss] = tuple(vk_list)