codekingpro/portable-devtools
114k
1import win32api2import win32con3import win32ui4 5MAPVK_VK_TO_CHAR = 26 7key_name_to_vk = {}8key_code_to_name = {}9 10_better_names = {11 "escape": "esc",12 "return": "enter",13 "back": "pgup",14 "next": "pgdn",15}16 17 18def _fillvkmap():19 # Pull the VK_names from win32con20 names = [entry for entry in win32con.__dict__ if entry.startswith("VK_")]21 for name in names:22 code = getattr(win32con, name)23 n = name[3:].lower()24 key_name_to_vk[n] = code25 if n in _better_names:26 n = _better_names[n]27 key_name_to_vk[n] = code28 key_code_to_name[code] = n29 30 31_fillvkmap()32 33 34def get_vk(chardesc):35 if len(chardesc) == 1:36 # it is a character.37 info = win32api.VkKeyScan(chardesc)38 if info == -1:39 # Note: returning None, None causes an error when keyboard layout is non-English, see the report below40 # https://stackoverflow.com/questions/45138084/pythonwin-occasionally-gives-an-error-on-opening41 return 0, 042 vk = win32api.LOBYTE(info)43 state = win32api.HIBYTE(info)44 modifiers = 045 if state & 0x1:46 modifiers |= win32con.SHIFT_PRESSED47 if state & 0x2:48 modifiers |= win32con.LEFT_CTRL_PRESSED | win32con.RIGHT_CTRL_PRESSED49 if state & 0x4:50 modifiers |= win32con.LEFT_ALT_PRESSED | win32con.RIGHT_ALT_PRESSED51 return vk, modifiers52 # must be a 'key name'53 return key_name_to_vk.get(chardesc.lower()), 054 55 56modifiers = {57 "alt": win32con.LEFT_ALT_PRESSED | win32con.RIGHT_ALT_PRESSED,58 "lalt": win32con.LEFT_ALT_PRESSED,59 "ralt": win32con.RIGHT_ALT_PRESSED,60 "ctrl": win32con.LEFT_CTRL_PRESSED | win32con.RIGHT_CTRL_PRESSED,61 "ctl": win32con.LEFT_CTRL_PRESSED | win32con.RIGHT_CTRL_PRESSED,62 "control": win32con.LEFT_CTRL_PRESSED | win32con.RIGHT_CTRL_PRESSED,63 "lctrl": win32con.LEFT_CTRL_PRESSED,64 "lctl": win32con.LEFT_CTRL_PRESSED,65 "rctrl": win32con.RIGHT_CTRL_PRESSED,66 "rctl": win32con.RIGHT_CTRL_PRESSED,67 "shift": win32con.SHIFT_PRESSED,68 "key": 0, # ignore key tag.69}70 71 72def parse_key_name(name):73 name = name + "-" # Add a sentinal74 start = pos = 075 max = len(name)76 toks = []77 while pos < max:78 if name[pos] in "+-":79 tok = name[start:pos]80 # use the ascii lower() version of tok, so ascii chars require81 # an explicit shift modifier - ie 'Ctrl+G' should be treated as82 # 'ctrl+g' - 'ctrl+shift+g' would be needed if desired.83 # This is mainly to avoid changing all the old keystroke defs84 toks.append(tok.lower())85 pos += 1 # skip the sep86 start = pos87 pos += 188 flags = 089 # do the modifiers90 for tok in toks[:-1]:91 mod = modifiers.get(tok.lower())92 if mod is not None:93 flags |= mod94 # the key name95 vk, this_flags = get_vk(toks[-1])96 return vk, flags | this_flags97 98 99_checks = [100 [ # Shift101 ("Shift", win32con.SHIFT_PRESSED),102 ],103 [ # Ctrl key104 ("Ctrl", win32con.LEFT_CTRL_PRESSED | win32con.RIGHT_CTRL_PRESSED),105 ("LCtrl", win32con.LEFT_CTRL_PRESSED),106 ("RCtrl", win32con.RIGHT_CTRL_PRESSED),107 ],108 [ # Alt key109 ("Alt", win32con.LEFT_ALT_PRESSED | win32con.RIGHT_ALT_PRESSED),110 ("LAlt", win32con.LEFT_ALT_PRESSED),111 ("RAlt", win32con.RIGHT_ALT_PRESSED),112 ],113]114 115 116def make_key_name(vk, flags):117 # Check alt keys.118 flags_done = 0119 parts = []120 for moddata in _checks:121 for name, checkflag in moddata:122 if flags & checkflag:123 parts.append(name)124 flags_done = flags_done & checkflag125 break126 if flags_done & flags:127 parts.append(hex(flags & ~flags_done))128 # Now the key name.129 if vk is None:130 parts.append("<Unknown scan code>")131 else:132 try:133 parts.append(key_code_to_name[vk])134 except KeyError:135 # Not in our virtual key map - ask Windows what character this136 # key corresponds to.137 scancode = win32api.MapVirtualKey(vk, MAPVK_VK_TO_CHAR)138 parts.append(chr(scancode))139 sep = "+"140 if sep in parts:141 sep = "-"142 return sep.join([p.capitalize() for p in parts])143 144 145def _psc(char):146 sc, mods = get_vk(char)147 print("Char %s -> %d -> %s" % (repr(char), sc, key_code_to_name.get(sc)))148 149 150def test1():151 for ch in """aA0/?[{}];:'"`~_-+=\\|,<.>/?""":152 _psc(ch)153 for code in ["Home", "End", "Left", "Right", "Up", "Down", "Menu", "Next"]:154 _psc(code)155 156 157def _pkn(n):158 vk, flags = parse_key_name(n)159 print("%s -> %s,%s -> %s" % (n, vk, flags, make_key_name(vk, flags)))160 161 162def test2():163 _pkn("ctrl+alt-shift+x")164 _pkn("ctrl-home")165 _pkn("Shift-+")166 _pkn("Shift--")167 _pkn("Shift+-")168 _pkn("Shift++")169 _pkn("LShift-+")170 _pkn("ctl+home")171 _pkn("ctl+enter")172 _pkn("alt+return")173 _pkn("Alt+/")174 _pkn("Alt+BadKeyName")175 _pkn("A") # an ascii char - should be seen as 'a'176 _pkn("a")177 _pkn("Shift-A")178 _pkn("Shift-a")179 _pkn("a")180 _pkn("(")181 _pkn("Ctrl+(")182 _pkn("Ctrl+Shift-8")183 _pkn("Ctrl+*")184 _pkn("{")185 _pkn("!")186 _pkn(".")187 188 189if __name__ == "__main__":190 test2()191 