You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

494 lines
11 KiB

# Which key(s) were pressed
# What chars to print (if any)
# Keycode(s) generating event
import string
from .unikeys import UnicodeAsciiKeys
# These are used for the names of ctrl keys, etc.
ASCII_NAMES = {
'\t': 'tab',
' ': 'space', # 0x20
'!': 'exclamation', # 0x21
'"': 'double quote', # 0x22
'#': 'hash', # 0x23
'$': 'dollar', # 0x24
'%': 'percent', # 0x25
'&': 'ampersand', # 0x26
'\'': 'single quote', # 0x27
'(': 'open paren', # 0x28
')': 'close paren', # 0x29
'*': 'asterisk', # 0x2a
'+': 'plus', # 0x2b
',': 'comma', # 0x2c
'-': 'minus', # 0x2d
'.': 'period', # 0x2e
'/': 'slash', # 0x2f
':': 'colon', # 0x3a
';': 'semicolon', # 0x3b
'<': 'less than', # 0x3c
'=': 'equals', # 0x3d
'>': 'greater than', # 0x3e
'?': 'question', # 0x3f
'@': 'at', # 0x40
'[': 'left bracket', # 0x5b
'\\': 'backslash', # 0x5c
']': 'right bracket', # 0x5d
'^': 'caret', # 0x5e
'_': 'underscore', # 0x5f
'`': 'backtick', # 0x60
'{': 'left brace', # 0x7b
'|': 'pipe', # 0x7c
'}': 'right brace', # 0x7d
'~': 'tilde', # 0x7e
}
class UnicodeKeys(object):
# Names from ftp://ftp.unicode.org/Public/UNIDATA/NamesList.txt
NULL = chr(0x00)
START_OF_HEADING = chr(0x01)
class JargonKeys(object):
BANG = '!'
SHRIEK = '!'
DOUBLE_QUOTE = '"'
QUOTE = '"'
NUMBER_SIGN = '#'
SHARP = '#'
OCTOTHORPE = '#'
BUCK = '$'
CASH = '$'
STRING = '$'
MOD = '%'
GRAPES = '%'
AMPERSAND = '&'
AMP = '&'
AND_SIGN = '&'
APOSTROPHE = '\''
PRIME = '\''
TICK = '\''
STAR = '*'
SPLAT = '*'
GLOB = '*'
ADD = '+'
class IntercalKeys(object):
SPOT = '.'
TWO_SPOT = ':'
TAIL = ','
HYBRID = ';'
MESH = '#'
HALF_MESH = '='
SPARK = '\''
BACKSPARK = '`'
WOW = '!'
WHAT = '?'
RABBIT_EARS = '"'
# RABBIT is `"` over `.`
SPIKE = '|'
DOUBLE_OH_SEVEN = '%'
WORM = '-'
ANGLE = '<'
RIGHT_ANGLE = '>'
WAX = '('
WANE = ')'
U_TURN = '['
U_TURN_BACK = ']'
EMBRACE = '{'
BRACELET = '}'
SPLAT = '*'
AMPERSAND = '&'
V = 'V'
BOOK = 'V'
# BOOKWORM is `-` over `V`
BIG_MONEY = '$'
# CHANGE is cent sign
SQUIGGLE = '~'
FLAT_WORM = '_'
# OVERLINE is line on top
INTERSECTION = '+'
SLAT = '/'
BACKSLAT = '\\'
WHIRLPOOL = '@'
# HOOKWORK is logical NOT symbol
SHARK = '^'
SHARKFIN = '^'
# BLOTCH is several characters smashed on top of each other
class VT100StandardModeKeys(object):
# http://www.braun-home.net/michael/mbedit/info/misc/VT100_commands.htm
# http://www.ccs.neu.edu/research/gpc/MSim/vona/terminal/VT100_Escape_Codes.html
F1 = '\x1bOP'
F2 = '\x1bOQ'
F3 = '\x1bOR'
F4 = '\x1bOS'
UP = '\x1b[A'
DOWN = '\x1b[B'
RIGHT = '\x1b[C'
LEFT = '\x1b[D'
class VT100ApplicationsModeKeys(object):
F1 = '\x1bOP'
F2 = '\x1bOQ'
F3 = '\x1bOR'
F4 = '\x1bOS'
UP = '\x1bOA'
DOWN = '\x1bOB'
RIGHT = '\x1bOC'
LEFT = '\x1bOD'
KEYPAD_0 = '\x1bOp'
KEYPAD_1 = '\x1bOq'
KEYPAD_2 = '\x1bOr'
KEYPAD_3 = '\x1bOs'
KEYPAD_4 = '\x1bOt'
KEYPAD_5 = '\x1bOu'
KEYPAD_6 = '\x1bOv'
KEYPAD_7 = '\x1bOw'
KEYPAD_8 = '\x1bOx'
KEYPAD_9 = '\x1bOy'
KEYPAD_MINUS = '\x1bOm'
KEYPAD_COMMA = '\x1bOl'
KEYPAD_PERIOD = '\x1bOn'
KEYPAD_ENTER = '\x1bOM'
class VT220Keys(object):
# F1-F5 didn't exist historically, but were added by later emulators
F1 = '\x1b[11~'
F2 = '\x1b[12~'
F3 = '\x1b[13~'
F4 = '\x1b[14~'
F5 = '\x1b[15~'
# Historical keys
F6 = '\x1b[17~'
F7 = '\x1b[18~'
F8 = '\x1b[19~'
F9 = '\x1b[20~'
F10 = '\x1b[21~'
F11 = '\x1b[23~'
F12 = '\x1b[24~'
# F13+ and key combinations to enter them are of limited usefulness today
class UnixKeys(object):
# Keys found experimentally, of unknown provenance
ESC = '\x1b'
HOME = '\x1b[H'
END = '\x1b[F'
PAGE_UP = '\x1b[5~'
PAGE_DOWN = '\x1b[6~'
ENTER = '\n'
CR = '\r'
BACKSPACE = '\x7f'
SPACE = ' '
INSERT = '\x1b[2~'
DELETE = '\x1b[3~'
class AlternativeUnixFunctionKeys(object):
# Unsure origin: alternate V220 mode?
F1 = '\x1bO11~'
F2 = '\x1bO12~'
F3 = '\x1bO13~'
F4 = '\x1bO14~'
F5 = '\x1bO15~'
F6 = '\x1bO17~'
F7 = '\x1bO18~'
F8 = '\x1bO19~'
F9 = '\x1bO20~'
F10 = '\x1bO21~'
F11 = '\x1bO23~'
F12 = '\x1bO24~'
class WindowsKeys(object):
ESC = '\x1b'
LEFT = '\xe0K'
RIGHT = '\xe0M'
UP = '\xe0H'
DOWN = '\xe0P'
ENTER = '\r'
BACKSPACE = '\x08'
SPACE = ' '
F1 = '\x00;'
F2 = '\x00<'
F3 = '\x00='
F4 = '\x00>'
F5 = '\x00?'
F6 = '\x00@'
F7 = '\x00A'
F8 = '\x00B'
F9 = '\x00C'
F10 = '\x00D'
F11 = '\xe0\x85'
F12 = '\xe0\x86'
INSERT = '\xe0R'
DELETE = '\xe0S'
PAGE_UP = '\xe0I'
PAGE_DOWN = '\xe0Q'
HOME = '\xe0G'
END = '\xe0O'
SHIFT_F1 = '\x00T'
SHIFT_F2 = '\x00U'
SHIFT_F3 = '\x00V'
SHIFT_F4 = '\x00W'
SHIFT_F5 = '\x00X'
SHIFT_F6 = '\x00Y'
SHIFT_F7 = '\x00Z'
SHIFT_F8 = '\x00['
SHIFT_F9 = '\x00\\'
SHIFT_F10 = '\x00]'
SHIFT_F11 = '\xe0\x87'
SHIFT_F12 = '\xe0\x88'
CTRL_F1 = '\x00^'
CTRL_F2 = '\x00_'
CTRL_F3 = '\x00`'
CTRL_F4 = '\x00a'
CTRL_F5 = '\x00b'
CTRL_F6 = '\x00c'
CTRL_F7 = '\x00d' # Captured by something?
CTRL_F8 = '\x00e'
CTRL_F9 = '\x00f'
CTRL_F10 = '\x00g'
CTRL_F11 = '\xe0\x89'
CTRL_F12 = '\xe0\x8a'
CTRL_HOME = '\xe0w'
CTRL_END = '\xe0u'
CTRL_INSERT = '\xe0\x92'
CTRL_DELETE = '\xe0\x93'
CTRL_PAGE_DOWN = '\xe0v'
CTRL_2 = '\x00\x03'
CTRL_UP = '\xe0\x8d'
CTRL_DOWN = '\xe0\x91'
CTRL_LEFT = '\xe0s'
CTRL_RIGHT = '\xe0t'
CTRL_ALT_A = '\x00\x1e'
CTRL_ALT_B = '\x000'
CTRL_ALT_C = '\x00.'
CTRL_ALT_D = '\x00 '
CTRL_ALT_E = '\x00\x12'
CTRL_ALT_F = '\x00!'
CTRL_ALT_G = '\x00"'
CTRL_ALT_H = '\x00#'
CTRL_ALT_I = '\x00\x17'
CTRL_ALT_J = '\x00$'
CTRL_ALT_K = '\x00%'
CTRL_ALT_L = '\x00&'
CTRL_ALT_M = '\x002'
CTRL_ALT_N = '\x001'
CTRL_ALT_O = '\x00\x18'
CTRL_ALT_P = '\x00\x19'
CTRL_ALT_Q = '\x00\x10'
CTRL_ALT_R = '\x00\x13'
CTRL_ALT_S = '\x00\x1f'
CTRL_ALT_T = '\x00\x14'
CTRL_ALT_U = '\x00\x16'
CTRL_ALT_V = '\x00/'
CTRL_ALT_W = '\x00\x11'
CTRL_ALT_X = '\x00-'
CTRL_ALT_Y = '\x00\x15'
CTRL_ALT_Z = '\x00,'
CTRL_ALT_1 = '\x00x'
CTRL_ALT_2 = '\x00y'
CTRL_ALT_3 = '\x00z'
CTRL_ALT_4 = '\x00{'
CTRL_ALT_5 = '\x00|'
CTRL_ALT_6 = '\x00}'
CTRL_ALT_7 = '\x00~'
CTRL_ALT_8 = '\x00\x7f'
CTRL_ALT_9 = '\x00\x80'
CTRL_ALT_0 = '\x00\x81'
CTRL_ALT_MINUS = '\x00\x82'
CTRL_ALT_EQUALS = '\x00\x83'
CTRL_ALT_BACKSPACE = '\x00\x0e'
ALT_F1 = '\x00h'
ALT_F2 = '\x00i'
ALT_F3 = '\x00j'
ALT_F4 = '\x00k'
ALT_F5 = '\x00l'
ALT_F6 = '\x00m'
ALT_F7 = '\x00n'
ALT_F8 = '\x00o'
ALT_F9 = '\x00p'
ALT_F10 = '\x00q'
ALT_F11 = '\xe0\x8b'
ALT_F12 = '\xe0\x8c'
ALT_HOME = '\x00\x97'
ALT_END = '\x00\x9f'
ALT_INSERT = '\x00\xa2'
ALT_DELETE = '\x00\xa3'
ALT_PAGE_UP = '\x00\x99'
ALT_PAGE_DOWN = '\x00\xa1'
ALT_LEFT = '\x00\x9b'
ALT_RIGHT = '\x00\x9d'
ALT_UP = '\x00\x98'
ALT_DOWN = '\x00\xa0'
CTRL_ALT_LEFT_BRACKET = '\x00\x1a'
CTRL_ALT_RIGHT_BRACKET = '\x00\x1b'
CTRL_ALT_SEMICOLON = '\x00\''
CTRL_ALT_SINGLE_QUOTE = '\x00('
CTRL_ALT_ENTER = '\x00\x1c'
CTRL_ALT_SLASH = '\x005'
CTRL_ALT_PERIOD = '\x004'
CTRL_ALT_COMMA = '\x003'
class ControlKeys(object):
def __init__(self, format='CTRL_{}'):
for i in range(0x20):
low_char = chr(i)
high_char = chr(i + 0x40)
name = ASCII_NAMES.get(high_char, high_char).upper()
ctrl_name = format.format(name)
setattr(self, ctrl_name, low_char)
class AsciiKeys(object):
def __init__(
self,
lower_format='{}', upper_format='SHIFT_{}', digit_format='N{}',
ascii_names=ASCII_NAMES,
):
for letter in string.ascii_lowercase:
name = lower_format.format(letter.upper())
setattr(self, name, letter)
for letter in string.ascii_uppercase:
name = upper_format.format(letter.upper())
setattr(self, name, letter)
for digit in string.digits:
name = digit_format.format(digit)
setattr(self, name, digit)
for char, name in ascii_names.items():
name = name.upper().replace(' ', '_')
setattr(self, name, char)
class Keys(object):
def __init__(self, keyclasses):
self.__names = dict() # Map of codes -> names
self.__codes = dict() # Map of names -> codes
self.__escapes = set()
for keyclass in keyclasses:
for name in dir(keyclass):
if self._is_key_name(name):
code = getattr(keyclass, name)
self.register(name, code)
def register(self, name, code):
if name not in self.__codes:
self.__codes[name] = code
if code not in self.__names:
self.__names[code] = name
for i in range(len(code)):
self.__escapes.add(code[:i])
# Update towards canonicity
while True:
canon_code = self.canon(code)
canon_canon_code = self.canon(canon_code)
if canon_code != canon_canon_code:
self.__codes[self.name(code)] = canon_canon_code
else:
break
while True:
canon_name = self.name(self.code(name))
canon_canon_name = self.name(self.code(canon_name))
if canon_name != canon_canon_name:
self.__names[self.code(name)] = canon_canon_name
else:
break
@property
def escapes(self):
return self.__escapes
@property
def names(self):
return self.__codes.keys()
def name(self, code):
return self.__names.get(code)
def code(self, name):
return self.__codes.get(name)
def canon(self, code):
name = self.name(code)
return self.code(name) if name else code
def __getattr__(self, name):
code = self.code(name)
if code is not None:
return code
else:
return self.__getattribute__(name)
def _is_key_name(self, name):
return name == name.upper() and not name.startswith('_')
def _make_escapes(codes):
escapes = set()
for code in codes:
for i in range(len(code)):
escapes.add(code[:i])
return escapes
unix_keys = Keys([
VT100StandardModeKeys(),
VT100ApplicationsModeKeys(),
VT220Keys(),
UnixKeys(),
AlternativeUnixFunctionKeys(),
AsciiKeys(),
ControlKeys(),
UnicodeAsciiKeys(),
JargonKeys(),
IntercalKeys()
])
windows_keys = Keys([
WindowsKeys(),
AsciiKeys(),
ControlKeys(),
UnicodeAsciiKeys(),
JargonKeys(),
IntercalKeys()
])
PLATFORM_KEYS = {
'unix': unix_keys,
'windows': windows_keys,
}