180 lines
5.2 KiB
Python
180 lines
5.2 KiB
Python
|
import string
|
||
|
import win32con
|
||
|
import win32api
|
||
|
import win32ui
|
||
|
|
||
|
MAPVK_VK_TO_CHAR = 2
|
||
|
|
||
|
key_name_to_vk = {}
|
||
|
key_code_to_name = {}
|
||
|
|
||
|
_better_names = {
|
||
|
"escape": "esc",
|
||
|
"return": "enter",
|
||
|
"back": "pgup",
|
||
|
"next": "pgdn",
|
||
|
}
|
||
|
|
||
|
def _fillvkmap():
|
||
|
# Pull the VK_names from win32con
|
||
|
names = [entry for entry in win32con.__dict__ if entry.startswith("VK_")]
|
||
|
for name in names:
|
||
|
code = getattr(win32con, name)
|
||
|
n = name[3:].lower()
|
||
|
key_name_to_vk[n] = code
|
||
|
if n in _better_names:
|
||
|
n = _better_names[n]
|
||
|
key_name_to_vk[n] = code
|
||
|
key_code_to_name[code] = n
|
||
|
|
||
|
|
||
|
_fillvkmap()
|
||
|
|
||
|
def get_vk(chardesc):
|
||
|
if len(chardesc)==1:
|
||
|
# it is a character.
|
||
|
info = win32api.VkKeyScan(chardesc)
|
||
|
if info==-1:
|
||
|
# Note: returning None, None causes an error when keyboard layout is non-English, see the report below
|
||
|
# https://stackoverflow.com/questions/45138084/pythonwin-occasionally-gives-an-error-on-opening
|
||
|
return 0, 0
|
||
|
vk = win32api.LOBYTE(info)
|
||
|
state = win32api.HIBYTE(info)
|
||
|
modifiers = 0
|
||
|
if state & 0x1:
|
||
|
modifiers |= win32con.SHIFT_PRESSED
|
||
|
if state & 0x2:
|
||
|
modifiers |= win32con.LEFT_CTRL_PRESSED | win32con.RIGHT_CTRL_PRESSED
|
||
|
if state & 0x4:
|
||
|
modifiers |= win32con.LEFT_ALT_PRESSED | win32con.RIGHT_ALT_PRESSED
|
||
|
return vk, modifiers
|
||
|
# must be a 'key name'
|
||
|
return key_name_to_vk.get(chardesc.lower()), 0
|
||
|
|
||
|
modifiers = {
|
||
|
"alt" : win32con.LEFT_ALT_PRESSED | win32con.RIGHT_ALT_PRESSED,
|
||
|
"lalt" : win32con.LEFT_ALT_PRESSED,
|
||
|
"ralt" : win32con.RIGHT_ALT_PRESSED,
|
||
|
"ctrl" : win32con.LEFT_CTRL_PRESSED | win32con.RIGHT_CTRL_PRESSED,
|
||
|
"ctl" : win32con.LEFT_CTRL_PRESSED | win32con.RIGHT_CTRL_PRESSED,
|
||
|
"control" : win32con.LEFT_CTRL_PRESSED | win32con.RIGHT_CTRL_PRESSED,
|
||
|
"lctrl" : win32con.LEFT_CTRL_PRESSED,
|
||
|
"lctl" : win32con.LEFT_CTRL_PRESSED,
|
||
|
"rctrl" : win32con.RIGHT_CTRL_PRESSED,
|
||
|
"rctl" : win32con.RIGHT_CTRL_PRESSED,
|
||
|
"shift" : win32con.SHIFT_PRESSED,
|
||
|
"key" : 0, # ignore key tag.
|
||
|
}
|
||
|
|
||
|
def parse_key_name(name):
|
||
|
name = name + "-" # Add a sentinal
|
||
|
start = pos = 0
|
||
|
max = len(name)
|
||
|
toks = []
|
||
|
while pos<max:
|
||
|
if name[pos] in "+-":
|
||
|
tok = name[start:pos]
|
||
|
# use the ascii lower() version of tok, so ascii chars require
|
||
|
# an explicit shift modifier - ie 'Ctrl+G' should be treated as
|
||
|
# 'ctrl+g' - 'ctrl+shift+g' would be needed if desired.
|
||
|
# This is mainly to avoid changing all the old keystroke defs
|
||
|
toks.append(tok.lower())
|
||
|
pos += 1 # skip the sep
|
||
|
start = pos
|
||
|
pos += 1
|
||
|
flags = 0
|
||
|
# do the modifiers
|
||
|
for tok in toks[:-1]:
|
||
|
mod = modifiers.get(tok.lower())
|
||
|
if mod is not None:
|
||
|
flags |= mod
|
||
|
# the key name
|
||
|
vk, this_flags = get_vk(toks[-1])
|
||
|
return vk, flags | this_flags
|
||
|
|
||
|
_checks = [
|
||
|
[ # Shift
|
||
|
("Shift", win32con.SHIFT_PRESSED),
|
||
|
],
|
||
|
[ # Ctrl key
|
||
|
("Ctrl", win32con.LEFT_CTRL_PRESSED | win32con.RIGHT_CTRL_PRESSED),
|
||
|
("LCtrl", win32con.LEFT_CTRL_PRESSED),
|
||
|
("RCtrl", win32con.RIGHT_CTRL_PRESSED),
|
||
|
],
|
||
|
[ # Alt key
|
||
|
("Alt", win32con.LEFT_ALT_PRESSED | win32con.RIGHT_ALT_PRESSED),
|
||
|
("LAlt", win32con.LEFT_ALT_PRESSED),
|
||
|
("RAlt", win32con.RIGHT_ALT_PRESSED),
|
||
|
],
|
||
|
]
|
||
|
|
||
|
def make_key_name(vk, flags):
|
||
|
# Check alt keys.
|
||
|
flags_done = 0
|
||
|
parts = []
|
||
|
for moddata in _checks:
|
||
|
for name, checkflag in moddata:
|
||
|
if flags & checkflag:
|
||
|
parts.append(name)
|
||
|
flags_done = flags_done & checkflag
|
||
|
break
|
||
|
if flags_done & flags:
|
||
|
parts.append(hex( flags & ~flags_done ) )
|
||
|
# Now the key name.
|
||
|
if vk is None:
|
||
|
parts.append("<Unknown scan code>")
|
||
|
else:
|
||
|
try:
|
||
|
parts.append(key_code_to_name[vk])
|
||
|
except KeyError:
|
||
|
# Not in our virtual key map - ask Windows what character this
|
||
|
# key corresponds to.
|
||
|
scancode = win32api.MapVirtualKey(vk, MAPVK_VK_TO_CHAR)
|
||
|
parts.append(chr(scancode))
|
||
|
sep = "+"
|
||
|
if sep in parts: sep = "-"
|
||
|
return sep.join([p.capitalize() for p in parts])
|
||
|
|
||
|
def _psc(char):
|
||
|
sc, mods = get_vk(char)
|
||
|
print("Char %s -> %d -> %s" % (repr(char), sc, key_code_to_name.get(sc)))
|
||
|
|
||
|
def test1():
|
||
|
for ch in """aA0/?[{}];:'"`~_-+=\\|,<.>/?""":
|
||
|
_psc(ch)
|
||
|
for code in ["Home", "End", "Left", "Right", "Up", "Down", "Menu", "Next"]:
|
||
|
_psc(code)
|
||
|
|
||
|
def _pkn(n):
|
||
|
vk, flags = parse_key_name(n)
|
||
|
print("%s -> %s,%s -> %s" % (n, vk, flags, make_key_name(vk, flags)))
|
||
|
|
||
|
def test2():
|
||
|
_pkn("ctrl+alt-shift+x")
|
||
|
_pkn("ctrl-home")
|
||
|
_pkn("Shift-+")
|
||
|
_pkn("Shift--")
|
||
|
_pkn("Shift+-")
|
||
|
_pkn("Shift++")
|
||
|
_pkn("LShift-+")
|
||
|
_pkn("ctl+home")
|
||
|
_pkn("ctl+enter")
|
||
|
_pkn("alt+return")
|
||
|
_pkn("Alt+/")
|
||
|
_pkn("Alt+BadKeyName")
|
||
|
_pkn("A") # an ascii char - should be seen as 'a'
|
||
|
_pkn("a")
|
||
|
_pkn("Shift-A")
|
||
|
_pkn("Shift-a")
|
||
|
_pkn("a")
|
||
|
_pkn("(")
|
||
|
_pkn("Ctrl+(")
|
||
|
_pkn("Ctrl+Shift-8")
|
||
|
_pkn("Ctrl+*")
|
||
|
_pkn("{")
|
||
|
_pkn("!")
|
||
|
_pkn(".")
|
||
|
|
||
|
if __name__=='__main__':
|
||
|
test2()
|