fix(keys): fix shifted keys bug, make lazy key defs more readable

This commit is contained in:
Josh Klar 2021-07-07 14:44:44 -07:00 committed by Kyle Brown
parent ab6aa1838c
commit 083e4a701a

View File

@ -10,6 +10,8 @@ from kmk.key_validators import (
)
from kmk.types import AttrDict, UnicodeModeKeyMeta
DEBUG_OUTPUT = False
FIRST_KMK_INTERNAL_KEY = const(1000)
NEXT_AVAILABLE_KEY = 1000
@ -17,407 +19,76 @@ KEY_SIMPLE = const(0)
KEY_MODIFIER = const(1)
KEY_CONSUMER = const(2)
ALL_ALPHAS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
ALL_NUMBERS = '1234567890'
# since KC.1 isn't valid Python, alias to KC.N1
ALL_NUMBER_ALIASES = tuple(f'N{x}' for x in ALL_NUMBERS)
class InfiniteLoopDetected(Exception):
pass
# this is a bit of an FP style thing - combining a pipe operator a-la F# with
# a bootleg Maybe monad to clean up these make_key sequences
def left_pipe_until_some(candidate, functor, *args_iter):
for args in args_iter:
result = functor(candidate, *args)
if result is not None:
return result
def first_truthy(candidate, *funcs):
for func in funcs:
result = func(candidate)
if result is not None:
return result
def maybe_make_mod_key(candidate, code, names):
if candidate in names:
return make_mod_key(code=code, names=names)
def maybe_make_key(candidate, code, names):
if candidate in names:
return make_key(code=code, names=names)
def maybe_make_shifted_key(candidate, target_name, names):
if candidate in names:
return make_shifted_key(target_name=target_name, names=names)
def maybe_make_consumer_key(candidate, code, names):
if candidate in names:
return make_consumer_key(code=code, names=names)
class KeyAttrDict(AttrDict):
def __getattr__(self, key):
def __getattr__(self, key, depth=0):
if depth > 1:
raise InfiniteLoopDetected()
try:
return super(KeyAttrDict, self).__getattr__(key)
except Exception:
pass
# Modifiers
if key in ('LEFT_CONTROL', 'LCTRL', 'LCTL'):
make_mod_key(code=0x01, names=('LEFT_CONTROL', 'LCTRL', 'LCTL'))
elif key in ('LEFT_SHIFT', 'LSHIFT', 'LSFT'):
make_mod_key(code=0x02, names=('LEFT_SHIFT', 'LSHIFT', 'LSFT'))
elif key in ('LEFT_ALT', 'LALT'):
make_mod_key(code=0x04, names=('LEFT_ALT', 'LALT'))
elif key in ('LEFT_SUPER', 'LGUI', 'LCMD', 'LWIN'):
make_mod_key(code=0x08, names=('LEFT_SUPER', 'LGUI', 'LCMD', 'LWIN'))
elif key in ('RIGHT_CONTROL', 'RCTRL', 'RCTL'):
make_mod_key(code=0x10, names=('RIGHT_CONTROL', 'RCTRL', 'RCTL'))
elif key in ('RIGHT_SHIFT', 'RSHIFT', 'RSFT'):
make_mod_key(code=0x20, names=('RIGHT_SHIFT', 'RSHIFT', 'RSFT'))
elif key in ('RIGHT_ALT', 'RALT'):
make_mod_key(code=0x40, names=('RIGHT_ALT', 'RALT'))
elif key in ('RIGHT_SUPER', 'RGUI', 'RCMD', 'RWIN'):
make_mod_key(code=0x80, names=('RIGHT_SUPER', 'RGUI', 'RCMD', 'RWIN'))
# MEH = LCTL | LALT | LSFT# MEH = LCTL |
elif key in ('MEH',):
make_mod_key(code=0x07, names=('MEH',))
# HYPR = LCTL | LALT | LSFT | LGUI
elif key in ('HYPER', 'HYPR'):
make_mod_key(code=0x0F, names=('HYPER', 'HYPR'))
# Basic ASCII letters
elif key in ('A',):
make_key(code=4, names=('A',))
elif key in ('B',):
make_key(code=5, names=('B',))
elif key in ('C',):
make_key(code=6, names=('C',))
elif key in ('D',):
make_key(code=7, names=('D',))
elif key in ('E',):
make_key(code=8, names=('E',))
elif key in ('F',):
make_key(code=9, names=('F',))
elif key in ('G',):
make_key(code=10, names=('G',))
elif key in ('H',):
make_key(code=11, names=('H',))
elif key in ('I',):
make_key(code=12, names=('I',))
elif key in ('J',):
make_key(code=13, names=('J',))
elif key in ('K',):
make_key(code=14, names=('K',))
elif key in ('L',):
make_key(code=15, names=('L',))
elif key in ('M',):
make_key(code=16, names=('M',))
elif key in ('N',):
make_key(code=17, names=('N',))
elif key in ('O',):
make_key(code=18, names=('O',))
elif key in ('P',):
make_key(code=19, names=('P',))
elif key in ('Q',):
make_key(code=20, names=('Q',))
elif key in ('R',):
make_key(code=21, names=('R',))
elif key in ('S',):
make_key(code=22, names=('S',))
elif key in ('T',):
make_key(code=23, names=('T',))
elif key in ('U',):
make_key(code=24, names=('U',))
elif key in ('V',):
make_key(code=25, names=('V',))
elif key in ('W',):
make_key(code=26, names=('W',))
elif key in ('X',):
make_key(code=27, names=('X',))
elif key in ('Y',):
make_key(code=28, names=('Y',))
elif key in ('Z',):
make_key(code=29, names=('Z',))
# Basic ASCII letters/numbers don't need anything fancy, so check those
# in the laziest way
if key in ALL_ALPHAS:
make_key(code=4 + ALL_ALPHAS.index(key), names=(key,))
elif key in ALL_NUMBERS or key in ALL_NUMBER_ALIASES:
try:
offset = ALL_NUMBERS.index(key)
except ValueError:
offset = ALL_NUMBER_ALIASES.index(key)
# Numbers
# Aliases to play nicely with AttrDict, since KC.1 isn't a valid
# attribute key in Python, but KC.N1 is
elif key in ('1', 'N1'):
make_key(code=30, names=('1', 'N1', 'EXCLAIM', 'EXLM', '!'))
elif key in ('2', 'N2'):
make_key(code=31, names=('2', 'N2', 'AT', '@'))
elif key in ('3', 'N3', 'HASH', 'POUND', '#'):
make_key(code=32, names=('3', 'N3'))
elif key in ('4', 'N4'):
make_key(code=33, names=('4', 'N4', 'DOLLAR', 'DLR', '$'))
elif key in ('5', 'N5'):
make_key(code=34, names=('5', 'N5', 'PERCENT', 'PERC', '%'))
elif key in ('6', 'N6'):
make_key(code=35, names=('6', 'N6', 'CIRCUMFLEX', 'CIRC', '^'))
elif key in ('7', 'N7'):
make_key(code=36, names=('7', 'N7', 'AMPERSAND', 'AMPR', '&'))
elif key in ('8', 'N8'):
make_key(code=37, names=('8', 'N8', 'ASTERISK', 'ASTR', '*'))
elif key in ('9', 'N9'):
make_key(code=38, names=('9', 'N9', 'LEFT_PAREN', 'LPRN', '['))
elif key in ('0', 'N0', 'RIGHT_PAREN', 'LPRN', ']'):
make_key(code=39, names=('0', 'N0'))
names = (ALL_NUMBERS[offset], ALL_NUMBER_ALIASES[offset])
make_key(code=30 + offset, names=names)
# More ASCII standard keys
elif key in ('ENTER', 'ENT', '\n'):
make_key(code=40, names=('ENTER', 'ENT', '\n'))
elif key in ('ESCAPE', 'ESC'):
make_key(code=41, names=('ESCAPE', 'ESC'))
elif key in ('BACKSPACE', 'BSPC', 'BKSP'):
make_key(code=42, names=('BACKSPACE', 'BSPC', 'BKSP'))
elif key in ('TAB', '\t'):
make_key(code=43, names=('TAB', '\t'))
elif key in ('SPACE', 'SPC', ' '):
make_key(code=44, names=('SPACE', 'SPC', ' '))
elif key in ('MINUS', 'MINS', '-', 'UNDERSCORE', 'UNDS', '_'):
make_key(code=45, names=('MINUS', 'MINS', '-'))
elif key in ('EQUAL', 'EQL', '=', 'PLUS', '+'):
make_key(code=46, names=('EQUAL', 'EQL', '='))
elif key in ('LBRACKET', 'LBRC', '[', 'LEFT_CURLY_BRACE', 'LCRB', '{'):
make_key(code=47, names=('LBRACKET', 'LBRC', '['))
elif key in ('RBRACKET', 'RBRC', ']', 'RIGHT_CURLY_BRACE', 'RCRB', '}'):
make_key(code=48, names=('RBRACKET', 'RBRC', ']'))
elif key in ('BACKSLASH', 'BSLASH', 'BSLS', '\\', 'PIPE', '|'):
make_key(code=49, names=('BACKSLASH', 'BSLASH', 'BSLS', '\\'))
elif key in ('SEMICOLON', 'SCOLON', 'SCLN', ';', 'COLON', 'COLN', ':'):
make_key(code=51, names=('SEMICOLON', 'SCOLON', 'SCLN', ';'))
elif key in ('QUOTE', 'QUOT', "'", 'DOUBLE_QUOTE', 'DQUO', 'DQT', '"'):
make_key(code=52, names=('QUOTE', 'QUOT', "'"))
elif key in ('GRAVE', 'GRV', 'ZKHK', '`', 'TILDE', 'TILD', '~'):
make_key(code=53, names=('GRAVE', 'GRV', 'ZKHK', '`'))
elif key in ('COMMA', 'COMM', ',', 'LEFT_ANGLE_BRACKET', 'LABK', '<'):
make_key(code=54, names=('COMMA', 'COMM', ','))
elif key in ('DOT', '.', 'RIGHT_ANGLE_BRACKET', 'RABK', '>'):
make_key(code=55, names=('DOT', '.'))
elif key in ('SLASH', 'SLSH', 'QUESTION', 'QUES', '?'):
make_key(code=56, names=('SLASH', 'SLSH'))
# Function Keys
elif key in ('F1',):
make_key(code=58, names=('F1',))
elif key in ('F2',):
make_key(code=59, names=('F2',))
elif key in ('F3',):
make_key(code=60, names=('F3',))
elif key in ('F4',):
make_key(code=61, names=('F4',))
elif key in ('F5',):
make_key(code=62, names=('F5',))
elif key in ('F6',):
make_key(code=63, names=('F6',))
elif key in ('F7',):
make_key(code=64, names=('F7',))
elif key in ('F8',):
make_key(code=65, names=('F8',))
elif key in ('F9',):
make_key(code=66, names=('F9',))
elif key in ('F10',):
make_key(code=67, names=('F10',))
elif key in ('F11',):
make_key(code=68, names=('F11',))
elif key in ('F12',):
make_key(code=69, names=('F12',))
elif key in ('F13',):
make_key(code=104, names=('F13',))
elif key in ('F14',):
make_key(code=105, names=('F14',))
elif key in ('F15',):
make_key(code=106, names=('F15',))
elif key in ('F16',):
make_key(code=107, names=('F16',))
elif key in ('F17',):
make_key(code=108, names=('F17',))
elif key in ('F18',):
make_key(code=109, names=('F18',))
elif key in ('F19',):
make_key(code=110, names=('F19',))
elif key in ('F20',):
make_key(code=111, names=('F20',))
elif key in ('F21',):
make_key(code=112, names=('F21',))
elif key in ('F22',):
make_key(code=113, names=('F22',))
elif key in ('F23',):
make_key(code=114, names=('F23',))
elif key in ('F24',):
make_key(code=115, names=('F24',))
# Lock Keys, Navigation, etc.
elif key in ('CAPS_LOCK', 'CAPSLOCK', 'CLCK', 'CAPS'):
make_key(code=57, names=('CAPS_LOCK', 'CAPSLOCK', 'CLCK', 'CAPS'))
# FIXME: Investigate whether this key actually works, and
# uncomment when/if it does.
# elif key in ('LOCKING_CAPS', 'LCAP'):
# # make_key(code=130, names=('LOCKING_CAPS', 'LCAP'))
elif key in ('PRINT_SCREEN', 'PSCREEN', 'PSCR'):
make_key(code=70, names=('PRINT_SCREEN', 'PSCREEN', 'PSCR'))
elif key in ('SCROLL_LOCK', 'SCROLLLOCK', 'SLCK'):
make_key(code=71, names=('SCROLL_LOCK', 'SCROLLLOCK', 'SLCK'))
# FIXME: Investigate whether this key actually works, and
# uncomment when/if it does.
# elif key in ('LOCKING_SCROLL', 'LSCRL'):
# make_key(code=132, names=('LOCKING_SCROLL', 'LSCRL'))
elif key in ('PAUSE', 'PAUS', 'BRK'):
make_key(code=72, names=('PAUSE', 'PAUS', 'BRK'))
elif key in ('INSERT', 'INS'):
make_key(code=73, names=('INSERT', 'INS'))
elif key in ('HOME',):
make_key(code=74, names=('HOME',))
elif key in ('PGUP',):
make_key(code=75, names=('PGUP',))
elif key in ('DELETE', 'DEL'):
make_key(code=76, names=('DELETE', 'DEL'))
elif key in ('END',):
make_key(code=77, names=('END',))
elif key in ('PGDOWN', 'PGDN'):
make_key(code=78, names=('PGDOWN', 'PGDN'))
elif key in ('RIGHT', 'RGHT'):
make_key(code=79, names=('RIGHT', 'RGHT'))
elif key in ('LEFT',):
make_key(code=80, names=('LEFT',))
elif key in ('DOWN',):
make_key(code=81, names=('DOWN',))
elif key in ('UP',):
make_key(code=82, names=('UP',))
# Numpad
elif key in ('NUM_LOCK', 'NUMLOCK', 'NLCK'):
make_key(code=83, names=('NUM_LOCK', 'NUMLOCK', 'NLCK'))
# FIXME: Investigate whether this key actually works, and
# uncomment when/if it does.
# elif key in ('LOCKING_NUM', 'LNUM'):
# make_key(code=131, names=('LOCKING_NUM', 'LNUM'))
elif key in ('KP_SLASH', 'NUMPAD_SLASH', 'PSLS'):
make_key(code=84, names=('KP_SLASH', 'NUMPAD_SLASH', 'PSLS'))
elif key in ('KP_ASTERISK', 'NUMPAD_ASTERISK', 'PAST'):
make_key(code=85, names=('KP_ASTERISK', 'NUMPAD_ASTERISK', 'PAST'))
elif key in ('KP_MINUS', 'NUMPAD_MINUS', 'PMNS'):
make_key(code=86, names=('KP_MINUS', 'NUMPAD_MINUS', 'PMNS'))
elif key in ('KP_PLUS', 'NUMPAD_PLUS', 'PPLS'):
make_key(code=87, names=('KP_PLUS', 'NUMPAD_PLUS', 'PPLS'))
elif key in ('KP_ENTER', 'NUMPAD_ENTER', 'PENT'):
make_key(code=88, names=('KP_ENTER', 'NUMPAD_ENTER', 'PENT'))
elif key in ('KP_1', 'P1', 'NUMPAD_1'):
make_key(code=89, names=('KP_1', 'P1', 'NUMPAD_1'))
elif key in ('KP_2', 'P2', 'NUMPAD_2'):
make_key(code=90, names=('KP_2', 'P2', 'NUMPAD_2'))
elif key in ('KP_3', 'P3', 'NUMPAD_3'):
make_key(code=91, names=('KP_3', 'P3', 'NUMPAD_3'))
elif key in ('KP_4', 'P4', 'NUMPAD_4'):
make_key(code=92, names=('KP_4', 'P4', 'NUMPAD_4'))
elif key in ('KP_5', 'P5', 'NUMPAD_5'):
make_key(code=93, names=('KP_5', 'P5', 'NUMPAD_5'))
elif key in ('KP_6', 'P6', 'NUMPAD_6'):
make_key(code=94, names=('KP_6', 'P6', 'NUMPAD_6'))
elif key in ('KP_7', 'P7', 'NUMPAD_7'):
make_key(code=95, names=('KP_7', 'P7', 'NUMPAD_7'))
elif key in ('KP_8', 'P8', 'NUMPAD_8'):
make_key(code=96, names=('KP_8', 'P8', 'NUMPAD_8'))
elif key in ('KP_9', 'P9', 'NUMPAD_9'):
make_key(code=97, names=('KP_9', 'P9', 'NUMPAD_9'))
elif key in ('KP_0', 'P0', 'NUMPAD_0'):
make_key(code=98, names=('KP_0', 'P0', 'NUMPAD_0'))
elif key in ('KP_DOT', 'PDOT', 'NUMPAD_DOT'):
make_key(code=99, names=('KP_DOT', 'PDOT', 'NUMPAD_DOT'))
elif key in ('KP_EQUAL', 'PEQL', 'NUMPAD_EQUAL'):
make_key(code=103, names=('KP_EQUAL', 'PEQL', 'NUMPAD_EQUAL'))
elif key in ('KP_COMMA', 'PCMM', 'NUMPAD_COMMA'):
make_key(code=133, names=('KP_COMMA', 'PCMM', 'NUMPAD_COMMA'))
elif key in ('KP_EQUAL_AS400', 'NUMPAD_EQUAL_AS400'):
make_key(code=134, names=('KP_EQUAL_AS400', 'NUMPAD_EQUAL_AS400'))
# Making life better for folks on tiny keyboards especially: exposes
# the 'shifted' keys as raw keys. Under the hood we're still
# sending Shift+(whatever key is normally pressed) to get these, so
# for example `KC_AT` will hold shift and press 2.
elif key in ('TILDE', 'TILD', '~'):
make_shifted_key('GRAVE', names=('TILDE', 'TILD', '~'))
elif key in ('EXCLAIM', 'EXLM', '!'):
make_shifted_key('1', names=('EXCLAIM', 'EXLM', '!'))
elif key in ('AT', '@'):
make_shifted_key('2', names=('AT', '@'))
elif key in ('HASH', 'POUND', '#'):
make_shifted_key('3', names=('HASH', 'POUND', '#'))
elif key in ('DOLLAR', 'DLR', '$'):
make_shifted_key('4', names=('DOLLAR', 'DLR', '$'))
elif key in ('PERCENT', 'PERC', '%'):
make_shifted_key('5', names=('PERCENT', 'PERC', '%'))
elif key in ('CIRCUMFLEX', 'CIRC', '^'):
make_shifted_key('6', names=('CIRCUMFLEX', 'CIRC', '^'))
elif key in ('AMPERSAND', 'AMPR', '&'):
make_shifted_key('7', names=('AMPERSAND', 'AMPR', '&'))
elif key in ('ASTERISK', 'ASTR', '*'):
make_shifted_key('8', names=('ASTERISK', 'ASTR', '*'))
elif key in ('LEFT_PAREN', 'LPRN', '('):
make_shifted_key('9', names=('LEFT_PAREN', 'LPRN', '('))
elif key in ('RIGHT_PAREN', 'RPRN', ')'):
make_shifted_key('0', names=('RIGHT_PAREN', 'RPRN', ')'))
elif key in ('UNDERSCORE', 'UNDS', '_'):
make_shifted_key('MINUS', names=('UNDERSCORE', 'UNDS', '_'))
elif key in ('PLUS', '+'):
make_shifted_key('EQUAL', names=('PLUS', '+'))
elif key in ('LEFT_CURLY_BRACE', 'LCBR', '{'):
make_shifted_key('LBRACKET', names=('LEFT_CURLY_BRACE', 'LCBR', '{'))
elif key in ('RIGHT_CURLY_BRACE', 'RCBR', '}'):
make_shifted_key('RBRACKET', names=('RIGHT_CURLY_BRACE', 'RCBR', '}'))
elif key in ('PIPE', '|'):
make_shifted_key('BACKSLASH', names=('PIPE', '|'))
elif key in ('COLON', 'COLN', ':'):
make_shifted_key('SEMICOLON', names=('COLON', 'COLN', ':'))
elif key in ('DOUBLE_QUOTE', 'DQUO', 'DQT', '"'):
make_shifted_key('QUOTE', names=('DOUBLE_QUOTE', 'DQUO', 'DQT', '"'))
elif key in ('LEFT_ANGLE_BRACKET', 'LABK', '<'):
make_shifted_key('COMMA', names=('LEFT_ANGLE_BRACKET', 'LABK', '<'))
elif key in ('RIGHT_ANGLE_BRACKET', 'RABK', '>'):
make_shifted_key('DOT', names=('RIGHT_ANGLE_BRACKET', 'RABK', '>'))
elif key in ('QUESTION', 'QUES', '?'):
make_shifted_key('SLSH', names=('QUESTION', 'QUES', '?'))
# International
elif key in ('NONUS_HASH', 'NUHS'):
make_key(code=50, names=('NONUS_HASH', 'NUHS'))
elif key in ('NONUS_BSLASH', 'NUBS'):
make_key(code=100, names=('NONUS_BSLASH', 'NUBS'))
elif key in ('APP', 'APPLICATION', 'SEL', 'WINMENU'):
make_key(code=101, names=('APP', 'APPLICATION', 'SEL', 'WINMENU'))
elif key in ('INT1', 'RO'):
make_key(code=135, names=('INT1', 'RO'))
elif key in ('INT2', 'KANA'):
make_key(code=136, names=('INT2', 'KANA'))
elif key in ('INT3', 'JYEN'):
make_key(code=137, names=('INT3', 'JYEN'))
elif key in ('INT4', 'HENK'):
make_key(code=138, names=('INT4', 'HENK'))
elif key in ('INT5', 'MHEN'):
make_key(code=139, names=('INT5', 'MHEN'))
elif key in ('INT6',):
make_key(code=140, names=('INT6',))
elif key in ('INT7',):
make_key(code=141, names=('INT7',))
elif key in ('INT8',):
make_key(code=142, names=('INT8',))
elif key in ('INT9',):
make_key(code=143, names=('INT9',))
elif key in ('LANG1', 'HAEN'):
make_key(code=144, names=('LANG1', 'HAEN'))
elif key in ('LANG2', 'HAEJ'):
make_key(code=145, names=('LANG2', 'HAEJ'))
elif key in ('LANG3',):
make_key(code=146, names=('LANG3',))
elif key in ('LANG4',):
make_key(code=147, names=('LANG4',))
elif key in ('LANG5',):
make_key(code=148, names=('LANG5',))
elif key in ('LANG6',):
make_key(code=149, names=('LANG6',))
elif key in ('LANG7',):
make_key(code=150, names=('LANG7',))
elif key in ('LANG8',):
make_key(code=151, names=('LANG8',))
elif key in ('LANG9',):
make_key(code=152, names=('LANG9',))
# Consumer ("media") keys. Most known keys aren't supported here. A much
# longer list used to exist in this file, but the codes were almost certainly
# incorrect, conflicting with each other, or otherwise 'weird'. We'll add them
# back in piecemeal as needed. PRs welcome.
#
# A super useful reference for these is http://www.freebsddiary.org/APC/usb_hid_usages.php
# Note that currently we only have the PC codes. Recent MacOS versions seem to
# support PC media keys, so I don't know how much value we would get out of
# adding the old Apple-specific consumer codes, but again, PRs welcome if the
# lack of them impacts you.
elif key in ('AUDIO_MUTE', 'MUTE'):
make_consumer_key(code=226, names=('AUDIO_MUTE', 'MUTE')) # 0xE2
elif key in ('AUDIO_VOL_UP', 'VOLU'):
make_consumer_key(code=233, names=('AUDIO_VOL_UP', 'VOLU')) # 0xE9
elif key in ('AUDIO_VOL_DOWN', 'VOLD'):
make_consumer_key(code=234, names=('AUDIO_VOL_DOWN', 'VOLD')) # 0xEA
elif key in ('MEDIA_NEXT_TRACK', 'MNXT'):
make_consumer_key(code=181, names=('MEDIA_NEXT_TRACK', 'MNXT')) # 0xB5
elif key in ('MEDIA_PREV_TRACK', 'MPRV'):
make_consumer_key(code=182, names=('MEDIA_PREV_TRACK', 'MPRV')) # 0xB6
elif key in ('MEDIA_STOP', 'MSTP'):
make_consumer_key(code=183, names=('MEDIA_STOP', 'MSTP')) # 0xB7
elif key in ('MEDIA_PLAY_PAUSE', 'MPLY'):
make_consumer_key(
code=205, names=('MEDIA_PLAY_PAUSE', 'MPLY')
) # 0xCD (this may not be right)
elif key in ('MEDIA_EJECT', 'EJCT'):
make_consumer_key(code=184, names=('MEDIA_EJECT', 'EJCT')) # 0xB8
elif key in ('MEDIA_FAST_FORWARD', 'MFFD'):
make_consumer_key(code=179, names=('MEDIA_FAST_FORWARD', 'MFFD')) # 0xB3
elif key in ('MEDIA_REWIND', 'MRWD'):
make_consumer_key(code=180, names=('MEDIA_REWIND', 'MRWD')) # 0xB4
# Now try all the other weird special cases to get them out of our way:
# Internal, diagnostic, or auxiliary/enhanced keys
@ -447,12 +118,6 @@ class KeyAttrDict(AttrDict):
on_press=handlers.debug_pressed,
on_release=handlers.passthrough,
)
elif key in ('GESC',):
make_key(
names=('GESC',),
on_press=handlers.gesc_pressed,
on_release=handlers.gesc_released,
)
elif key in ('BKDL',):
make_key(
names=('BKDL',),
@ -514,8 +179,204 @@ class KeyAttrDict(AttrDict):
elif key in ('HID_SWITCH', 'HID'):
make_key(names=('HID_SWITCH', 'HID'), on_press=handlers.hid_switch)
else:
raise ValueError('Invalid key')
return self.__getattr__(key)
maybe_key = first_truthy(
key,
# Modifiers
lambda key: left_pipe_until_some(
key,
maybe_make_mod_key,
(0x01, ('LEFT_CONTROL', 'LCTRL', 'LCTL')),
(0x02, ('LEFT_SHIFT', 'LSHIFT', 'LSFT')),
(0x04, ('LEFT_ALT', 'LALT')),
(0x08, ('LEFT_SUPER', 'LGUI', 'LCMD', 'LWIN')),
(0x10, ('RIGHT_CONTROL', 'RCTRL', 'RCTL')),
(0x20, ('RIGHT_SHIFT', 'RSHIFT', 'RSFT')),
(0x40, ('RIGHT_ALT', 'RALT')),
(0x80, ('RIGHT_SUPER', 'RGUI', 'RCMD', 'RWIN')),
# MEH = LCTL | LALT | LSFT# MEH = LCTL |
(0x07, ('MEH',)),
# HYPR = LCTL | LALT | LSFT | LGUI
(0x0F, ('HYPER', 'HYPR')),
),
lambda key: left_pipe_until_some(
key,
maybe_make_key,
# More ASCII standard keys
(40, ('ENTER', 'ENT', '\n')),
(41, ('ESCAPE', 'ESC')),
(42, ('BACKSPACE', 'BSPC', 'BKSP')),
(43, ('TAB', '\t')),
(44, ('SPACE', 'SPC', ' ')),
(45, ('MINUS', 'MINS', '-')),
(46, ('EQUAL', 'EQL', '=')),
(47, ('LBRACKET', 'LBRC', '[')),
(48, ('RBRACKET', 'RBRC', ']')),
(49, ('BACKSLASH', 'BSLASH', 'BSLS', '\\')),
(51, ('SEMICOLON', 'SCOLON', 'SCLN', ';')),
(52, ('QUOTE', 'QUOT', "'")),
(53, ('GRAVE', 'GRV', 'ZKHK', '`')),
(54, ('COMMA', 'COMM', ',')),
(55, ('DOT', '.')),
(56, ('SLASH', 'SLSH')),
# Function Keys
(58, ('F1',)),
(59, ('F2',)),
(60, ('F3',)),
(61, ('F4',)),
(62, ('F5',)),
(63, ('F6',)),
(64, ('F7',)),
(65, ('F8',)),
(66, ('F9',)),
(67, ('F10',)),
(68, ('F11',)),
(69, ('F12',)),
(104, ('F13',)),
(105, ('F14',)),
(106, ('F15',)),
(107, ('F16',)),
(108, ('F17',)),
(109, ('F18',)),
(110, ('F19',)),
(111, ('F20',)),
(112, ('F21',)),
(113, ('F22',)),
(114, ('F23',)),
(115, ('F24',)),
# Lock Keys, Navigation, etc.
(57, ('CAPS_LOCK', 'CAPSLOCK', 'CLCK', 'CAPS')),
# FIXME: Investigate whether this key actually works, and
# uncomment when/if it does.
# (130, ('LOCKING_CAPS', 'LCAP')),
(70, ('PRINT_SCREEN', 'PSCREEN', 'PSCR')),
(71, ('SCROLL_LOCK', 'SCROLLLOCK', 'SLCK')),
# FIXME: Investigate whether this key actually works, and
# uncomment when/if it does.
# (132, ('LOCKING_SCROLL', 'LSCRL')),
(72, ('PAUSE', 'PAUS', 'BRK')),
(73, ('INSERT', 'INS')),
(74, ('HOME',)),
(75, ('PGUP',)),
(76, ('DELETE', 'DEL')),
(77, ('END',)),
(78, ('PGDOWN', 'PGDN')),
(79, ('RIGHT', 'RGHT')),
(80, ('LEFT',)),
(81, ('DOWN',)),
(82, ('UP',)),
# Numpad
(83, ('NUM_LOCK', 'NUMLOCK', 'NLCK')),
# FIXME: Investigate whether this key actually works, and
# uncomment when/if it does.
# (131, names=('LOCKING_NUM', 'LNUM')),
(84, ('KP_SLASH', 'NUMPAD_SLASH', 'PSLS')),
(85, ('KP_ASTERISK', 'NUMPAD_ASTERISK', 'PAST')),
(86, ('KP_MINUS', 'NUMPAD_MINUS', 'PMNS')),
(87, ('KP_PLUS', 'NUMPAD_PLUS', 'PPLS')),
(88, ('KP_ENTER', 'NUMPAD_ENTER', 'PENT')),
(89, ('KP_1', 'P1', 'NUMPAD_1')),
(90, ('KP_2', 'P2', 'NUMPAD_2')),
(91, ('KP_3', 'P3', 'NUMPAD_3')),
(92, ('KP_4', 'P4', 'NUMPAD_4')),
(93, ('KP_5', 'P5', 'NUMPAD_5')),
(94, ('KP_6', 'P6', 'NUMPAD_6')),
(95, ('KP_7', 'P7', 'NUMPAD_7')),
(96, ('KP_8', 'P8', 'NUMPAD_8')),
(97, ('KP_9', 'P9', 'NUMPAD_9')),
(98, ('KP_0', 'P0', 'NUMPAD_0')),
(99, ('KP_DOT', 'PDOT', 'NUMPAD_DOT')),
(103, ('KP_EQUAL', 'PEQL', 'NUMPAD_EQUAL')),
(133, ('KP_COMMA', 'PCMM', 'NUMPAD_COMMA')),
(134, ('KP_EQUAL_AS400', 'NUMPAD_EQUAL_AS400')),
),
# Making life better for folks on tiny keyboards especially: exposes
# the 'shifted' keys as raw keys. Under the hood we're still
# sending Shift+(whatever key is normally pressed) to get these, so
# for example `KC_AT` will hold shift and press 2.
lambda key: left_pipe_until_some(
key,
maybe_make_shifted_key,
('GRAVE', ('TILDE', 'TILD', '~')),
('1', ('EXCLAIM', 'EXLM', '!')),
('2', ('AT', '@')),
('3', ('HASH', 'POUND', '#')),
('4', ('DOLLAR', 'DLR', '$')),
('5', ('PERCENT', 'PERC', '%')),
('6', ('CIRCUMFLEX', 'CIRC', '^')),
('7', ('AMPERSAND', 'AMPR', '&')),
('8', ('ASTERISK', 'ASTR', '*')),
('9', ('LEFT_PAREN', 'LPRN', '(')),
('0', ('RIGHT_PAREN', 'RPRN', ')')),
('MINUS', ('UNDERSCORE', 'UNDS', '_')),
('EQUAL', ('PLUS', '+')),
('LBRACKET', ('LEFT_CURLY_BRACE', 'LCBR', '{')),
('RBRACKET', ('RIGHT_CURLY_BRACE', 'RCBR', '}')),
('BACKSLASH', ('PIPE', '|')),
('SEMICOLON', ('COLON', 'COLN', ':')),
('QUOTE', ('DOUBLE_QUOTE', 'DQUO', 'DQT', '"')),
('COMMA', ('LEFT_ANGLE_BRACKET', 'LABK', '<')),
('DOT', ('RIGHT_ANGLE_BRACKET', 'RABK', '>')),
('SLSH', ('QUESTION', 'QUES', '?')),
),
# International
lambda key: left_pipe_until_some(
key,
maybe_make_key,
(50, ('NONUS_HASH', 'NUHS')),
(100, ('NONUS_BSLASH', 'NUBS')),
(101, ('APP', 'APPLICATION', 'SEL', 'WINMENU')),
(135, ('INT1', 'RO')),
(136, ('INT2', 'KANA')),
(137, ('INT3', 'JYEN')),
(138, ('INT4', 'HENK')),
(139, ('INT5', 'MHEN')),
(140, ('INT6',)),
(141, ('INT7',)),
(142, ('INT8',)),
(143, ('INT9',)),
(144, ('LANG1', 'HAEN')),
(145, ('LANG2', 'HAEJ')),
(146, ('LANG3',)),
(147, ('LANG4',)),
(148, ('LANG5',)),
(149, ('LANG6',)),
(150, ('LANG7',)),
(151, ('LANG8',)),
(152, ('LANG9',)),
),
# Consumer ("media") keys. Most known keys aren't supported here. A much
# longer list used to exist in this file, but the codes were almost certainly
# incorrect, conflicting with each other, or otherwise 'weird'. We'll add them
# back in piecemeal as needed. PRs welcome.
#
# A super useful reference for these is http://www.freebsddiary.org/APC/usb_hid_usages.php
# Note that currently we only have the PC codes. Recent MacOS versions seem to
# support PC media keys, so I don't know how much value we would get out of
# adding the old Apple-specific consumer codes, but again, PRs welcome if the
# lack of them impacts you.
lambda key: left_pipe_until_some(
key,
maybe_make_consumer_key,
(226, ('AUDIO_MUTE', 'MUTE')), # 0xE2
(233, ('AUDIO_VOL_UP', 'VOLU')), # 0xE9
(234, ('AUDIO_VOL_DOWN', 'VOLD')), # 0xEA
(181, ('MEDIA_NEXT_TRACK', 'MNXT')), # 0xB5
(182, ('MEDIA_PREV_TRACK', 'MPRV')), # 0xB6
(183, ('MEDIA_STOP', 'MSTP')), # 0xB7
(205, ('MEDIA_PLAY_PAUSE', 'MPLY')), # 0xCD (this may not be right)
(184, ('MEDIA_EJECT', 'EJCT')), # 0xB8
(179, ('MEDIA_FAST_FORWARD', 'MFFD')), # 0xB3
(180, ('MEDIA_REWIND', 'MRWD')), # 0xB4
),
)
if DEBUG_OUTPUT:
print(f'{key}: {maybe_key}')
if not maybe_key:
raise ValueError('Invalid key')
return self.__getattr__(key, depth=depth + 1)
# Global state, will be filled in througout this file, and
@ -810,12 +671,19 @@ def make_key(code=None, names=tuple(), type=KEY_SIMPLE, **kwargs): # NOQA
return key
def make_mod_key(*args, **kwargs):
return make_key(*args, **kwargs, type=KEY_MODIFIER)
def make_mod_key(code, names, *args, **kwargs):
return make_key(code, names, *args, **kwargs, type=KEY_MODIFIER)
def make_shifted_key(target_name, names=tuple()): # NOQA
key = KC.LSFT(KC[target_name])
# For... probably a few years, a bug existed here where keys were looked
# up by `KC[...]`, but that's incorrect: an AttrDit exposes a dictionary
# with attributes, but key-based dictionary access with brackets does
# *not* implicitly call __getattr__. We got away with this when key defs
# were not lazily created, but now that they are, shifted keys would
# sometimes break, complaining that they couldn't find their underlying
# key to shift. Fixed!
key = KC.LSFT(getattr(KC, target_name))
register_key_names(key, names)