d6a0538009
This brings this naming into consistency with both fellow consts in the same file (ex. LeaderMode is singular) as well as the variables in which the consts are usually used (usually a `Firmware.unicode_mode` attribute in a keymap).
693 lines
22 KiB
Python
693 lines
22 KiB
Python
try:
|
|
from collections import namedtuple
|
|
except ImportError:
|
|
# This is handled by micropython-lib/collections, but on local runs of
|
|
# MicroPython, it doesn't exist
|
|
from ucollections import namedtuple
|
|
|
|
from kmk.consts import UnicodeMode
|
|
from kmk.types import AttrDict
|
|
|
|
FIRST_KMK_INTERNAL_KEYCODE = 1000
|
|
|
|
kc_lookup_cache = {}
|
|
|
|
|
|
def lookup_kc_with_cache(char):
|
|
found_code = kc_lookup_cache.get(
|
|
char,
|
|
getattr(Common, 'KC_{}'.format(char.upper())),
|
|
)
|
|
|
|
kc_lookup_cache[char] = found_code
|
|
kc_lookup_cache[char.upper()] = found_code
|
|
kc_lookup_cache[char.lower()] = found_code
|
|
|
|
return found_code
|
|
|
|
|
|
def generate_codepoint_keysym_seq(codepoint, expected_length=4):
|
|
# To make MacOS and Windows happy, always try to send
|
|
# sequences that are of length 4 at a minimum
|
|
# On Linux systems, we can happily send longer strings.
|
|
# They will almost certainly break on MacOS and Windows,
|
|
# but this is a documentation problem more than anything.
|
|
# Not sure how to send emojis on Mac/Windows like that,
|
|
# though, since (for example) the Canadian flag is assembled
|
|
# from two five-character codepoints, 1f1e8 and 1f1e6
|
|
#
|
|
# As a bonus, this function can be pretty useful for
|
|
# leader dictionary keys as strings.
|
|
seq = [Common.KC_0 for _ in range(max(len(codepoint), expected_length))]
|
|
|
|
for idx, codepoint_fragment in enumerate(reversed(codepoint)):
|
|
seq[-(idx + 1)] = lookup_kc_with_cache(codepoint_fragment)
|
|
|
|
return seq
|
|
|
|
|
|
def generate_leader_dictionary_seq(string):
|
|
return tuple(generate_codepoint_keysym_seq(string, 1))
|
|
|
|
|
|
class RawKeycodes:
|
|
'''
|
|
These are raw keycode numbers for keys we'll use in generated "keys".
|
|
For example, we want to be able to check against these numbers in
|
|
the internal_keycodes reducer fragments, but due to a limitation in
|
|
MicroPython, we can't simply assign the `.code` attribute to
|
|
a function (which is what most internal KMK keys (including layer stuff)
|
|
are implemented as). Thus, we have to keep an external lookup table.
|
|
'''
|
|
LCTRL = 0x01
|
|
LSHIFT = 0x02
|
|
LALT = 0x04
|
|
LGUI = 0x08
|
|
RCTRL = 0x10
|
|
RSHIFT = 0x20
|
|
RALT = 0x40
|
|
RGUI = 0x80
|
|
|
|
KC_DF = 1050
|
|
KC_MO = 1051
|
|
KC_LM = 1052
|
|
KC_LT = 1053
|
|
KC_TG = 1054
|
|
KC_TO = 1055
|
|
KC_TT = 1056
|
|
|
|
KC_UC_MODE = 1109
|
|
|
|
KC_MACRO = 1110
|
|
KC_MACRO_SLEEP_MS = 1111
|
|
KC_TAP_DANCE = 1113
|
|
|
|
|
|
# These shouldn't have all the fancy shenanigans Keycode allows
|
|
# such as no_press, because they modify KMK internal state in
|
|
# ways we need to tightly control. Thus, we can get away with
|
|
# a lighter-weight namedtuple implementation here
|
|
LayerKeycode = namedtuple('LayerKeycode', ('code', 'layer', 'kc'))
|
|
MacroSleepKeycode = namedtuple('MacroSleepKeycode', ('code', 'ms'))
|
|
|
|
|
|
class UnicodeModeKeycode(namedtuple('UnicodeModeKeycode', ('code', 'mode'))):
|
|
@staticmethod
|
|
def from_mode_const(mode):
|
|
return UnicodeModeKeycode(RawKeycodes.KC_UC_MODE, mode)
|
|
|
|
|
|
class Keycode:
|
|
def __init__(self, code, has_modifiers=None, no_press=False, no_release=False):
|
|
self.code = code
|
|
self.has_modifiers = has_modifiers
|
|
# cast to bool() in case we get a None value
|
|
self.no_press = bool(no_press)
|
|
self.no_release = bool(no_press)
|
|
|
|
def __call__(self, no_press=None, no_release=None):
|
|
if no_press is None and no_release is None:
|
|
return self
|
|
|
|
return Keycode(
|
|
code=self.code,
|
|
has_modifiers=self.has_modifiers,
|
|
no_press=no_press,
|
|
no_release=no_release,
|
|
)
|
|
|
|
def __repr__(self):
|
|
return 'Keycode(code={}, has_modifiers={})'.format(self.code, self.has_modifiers)
|
|
|
|
|
|
class ModifierKeycode(Keycode):
|
|
FAKE_CODE = -1
|
|
|
|
def __call__(self, modified_code=None, no_press=None, no_release=None):
|
|
if modified_code is None and no_press is None and no_release is None:
|
|
return self
|
|
|
|
if modified_code is not None:
|
|
if isinstance(modified_code, ModifierKeycode):
|
|
new_keycode = ModifierKeycode(
|
|
ModifierKeycode.FAKE_CODE,
|
|
set() if self.has_modifiers is None else self.has_modifiers,
|
|
no_press=no_press,
|
|
no_release=no_release,
|
|
)
|
|
|
|
if self.code != ModifierKeycode.FAKE_CODE:
|
|
new_keycode.has_modifiers.add(self.code)
|
|
|
|
if modified_code.code != ModifierKeycode.FAKE_CODE:
|
|
new_keycode.has_modifiers.add(modified_code.code)
|
|
else:
|
|
new_keycode = Keycode(
|
|
modified_code.code,
|
|
{self.code},
|
|
no_press=no_press,
|
|
no_release=no_release,
|
|
)
|
|
|
|
if modified_code.has_modifiers:
|
|
new_keycode.has_modifiers |= modified_code.has_modifiers
|
|
else:
|
|
new_keycode = Keycode(
|
|
self.code,
|
|
no_press=no_press,
|
|
no_release=no_release,
|
|
)
|
|
|
|
return new_keycode
|
|
|
|
def __repr__(self):
|
|
return 'ModifierKeycode(code={}, has_modifiers={})'.format(self.code, self.has_modifiers)
|
|
|
|
|
|
class ConsumerKeycode(Keycode):
|
|
pass
|
|
|
|
|
|
class Macro:
|
|
'''
|
|
A special "key" which triggers a macro.
|
|
'''
|
|
code = RawKeycodes.KC_MACRO
|
|
|
|
def __init__(self, keydown=None, keyup=None):
|
|
self.keydown = keydown
|
|
self.keyup = keyup
|
|
|
|
def on_keydown(self):
|
|
return self.keydown() if self.keydown else None
|
|
|
|
def on_keyup(self):
|
|
return self.keyup() if self.keyup else None
|
|
|
|
|
|
class TapDanceKeycode:
|
|
code = RawKeycodes.KC_TAP_DANCE
|
|
|
|
def __init__(self, *codes):
|
|
self.codes = codes
|
|
|
|
|
|
class KeycodeCategory(type):
|
|
@classmethod
|
|
def to_dict(cls):
|
|
'''
|
|
MicroPython, for whatever reason (probably performance/memory) makes
|
|
__dict__ optional for ports. Unfortunately, at least the STM32
|
|
(Pyboard) port is one such port. This reimplements a subset of
|
|
__dict__, limited to just keys we're likely to care about (though this
|
|
could be opened up further later).
|
|
'''
|
|
|
|
hidden = ('to_dict', 'recursive_dict', 'contains')
|
|
return AttrDict({
|
|
key: getattr(cls, key)
|
|
for key in dir(cls)
|
|
if not key.startswith('_') and key not in hidden
|
|
})
|
|
|
|
@classmethod
|
|
def recursive_dict(cls):
|
|
'''
|
|
to_dict() executed recursively all the way down a tree
|
|
'''
|
|
ret = cls.to_dict()
|
|
|
|
for key, val in ret.items():
|
|
try:
|
|
nested_ret = val.recursive_dict()
|
|
except (AttributeError, NameError):
|
|
continue
|
|
|
|
ret[key] = nested_ret
|
|
|
|
return ret
|
|
|
|
@classmethod
|
|
def contains(cls, kc):
|
|
'''
|
|
Emulates the 'in' operator for keycode groupings, given MicroPython's
|
|
lack of support for metaclasses (meaning implementing 'in' for
|
|
uninstantiated classes, such as these, is largely not possible). Not
|
|
super useful in most cases, but does allow for sanity checks like
|
|
|
|
```python
|
|
assert Keycodes.Modifiers.contains(requested_key)
|
|
```
|
|
|
|
This is not bulletproof due to how HID codes are defined (there is
|
|
overlap). Keycodes.Common.KC_A, for example, is equal in value to
|
|
Keycodes.Modifiers.KC_LALT, but it can still prevent silly mistakes
|
|
like trying to use, say, Keycodes.Common.KC_Q as a modifier.
|
|
|
|
This is recursive across subgroups, enabling stuff like:
|
|
|
|
```python
|
|
assert Keycodes.contains(requested_key)
|
|
```
|
|
|
|
To ensure that a valid keycode has been requested to begin with. Again,
|
|
not bulletproof, but adds at least some cushion to stuff that would
|
|
otherwise cause AttributeErrors and crash the keyboard.
|
|
'''
|
|
subcategories = (
|
|
category for category in cls.to_dict().values()
|
|
# Disgusting, but since `cls.__bases__` isn't implemented in MicroPython,
|
|
# I resort to a less foolproof inheritance check that should still ignore
|
|
# strings and other stupid stuff (we don't want to iterate over __doc__,
|
|
# for example), but include nested classes.
|
|
#
|
|
# One huge lesson in this project is that uninstantiated classes are hard...
|
|
# and four times harder when the implementation of Python is half-baked.
|
|
if isinstance(category, type)
|
|
)
|
|
|
|
if any(
|
|
kc == _kc
|
|
for name, _kc in cls.to_dict().items()
|
|
if name.startswith('KC_')
|
|
):
|
|
return True
|
|
|
|
return any(sc.contains(kc) for sc in subcategories)
|
|
|
|
|
|
class Modifiers(KeycodeCategory):
|
|
KC_LCTRL = KC_LCTL = ModifierKeycode(RawKeycodes.LCTRL)
|
|
KC_LSHIFT = KC_LSFT = ModifierKeycode(RawKeycodes.LSHIFT)
|
|
KC_LALT = ModifierKeycode(RawKeycodes.LALT)
|
|
KC_LGUI = KC_LCMD = KC_LWIN = ModifierKeycode(RawKeycodes.LGUI)
|
|
KC_RCTRL = KC_RCTL = ModifierKeycode(RawKeycodes.RCTRL)
|
|
KC_RSHIFT = KC_RSFT = ModifierKeycode(RawKeycodes.RSHIFT)
|
|
KC_RALT = ModifierKeycode(RawKeycodes.RALT)
|
|
KC_RGUI = KC_RCMD = KC_RWIN = ModifierKeycode(RawKeycodes.RGUI)
|
|
|
|
KC_MEH = KC_LSHIFT(KC_LALT(KC_LCTRL))
|
|
KC_HYPR = KC_HYPER = KC_MEH(KC_LGUI)
|
|
|
|
|
|
class Common(KeycodeCategory):
|
|
KC_A = Keycode(4)
|
|
KC_B = Keycode(5)
|
|
KC_C = Keycode(6)
|
|
KC_D = Keycode(7)
|
|
KC_E = Keycode(8)
|
|
KC_F = Keycode(9)
|
|
KC_G = Keycode(10)
|
|
KC_H = Keycode(11)
|
|
KC_I = Keycode(12)
|
|
KC_J = Keycode(13)
|
|
KC_K = Keycode(14)
|
|
KC_L = Keycode(15)
|
|
KC_M = Keycode(16)
|
|
KC_N = Keycode(17)
|
|
KC_O = Keycode(18)
|
|
KC_P = Keycode(19)
|
|
KC_Q = Keycode(20)
|
|
KC_R = Keycode(21)
|
|
KC_S = Keycode(22)
|
|
KC_T = Keycode(23)
|
|
KC_U = Keycode(24)
|
|
KC_V = Keycode(25)
|
|
KC_W = Keycode(26)
|
|
KC_X = Keycode(27)
|
|
KC_Y = Keycode(28)
|
|
KC_Z = Keycode(29)
|
|
|
|
# Aliases to play nicely with AttrDict, since KC.1 isn't a valid
|
|
# attribute key in Python, but KC.N1 is
|
|
KC_1 = KC_N1 = Keycode(30)
|
|
KC_2 = KC_N2 = Keycode(31)
|
|
KC_3 = KC_N3 = Keycode(32)
|
|
KC_4 = KC_N4 = Keycode(33)
|
|
KC_5 = KC_N5 = Keycode(34)
|
|
KC_6 = KC_N6 = Keycode(35)
|
|
KC_7 = KC_N7 = Keycode(36)
|
|
KC_8 = KC_N8 = Keycode(37)
|
|
KC_9 = KC_N9 = Keycode(38)
|
|
KC_0 = KC_N0 = Keycode(39)
|
|
|
|
KC_ENTER = KC_ENT = Keycode(40)
|
|
KC_ESCAPE = KC_ESC = Keycode(41)
|
|
KC_BACKSPACE = KC_BSPC = KC_BKSP = Keycode(42)
|
|
KC_TAB = Keycode(43)
|
|
KC_SPACE = KC_SPC = Keycode(44)
|
|
KC_MINUS = KC_MINS = Keycode(45)
|
|
KC_EQUAL = KC_EQL = Keycode(46)
|
|
KC_LBRACKET = KC_LBRC = Keycode(47)
|
|
KC_RBRACKET = KC_RBRC = Keycode(48)
|
|
KC_BACKSLASH = KC_BSLASH = KC_BSLS = Keycode(49)
|
|
KC_NONUS_HASH = KC_NUHS = Keycode(50)
|
|
KC_NONUS_BSLASH = KC_NUBS = Keycode(100)
|
|
KC_SEMICOLON = KC_SCOLON = KC_SCLN = Keycode(51)
|
|
KC_QUOTE = KC_QUOT = Keycode(52)
|
|
KC_GRAVE = KC_GRV = KC_ZKHK = Keycode(53)
|
|
KC_COMMA = KC_COMM = Keycode(54)
|
|
KC_DOT = Keycode(55)
|
|
KC_SLASH = KC_SLSH = Keycode(56)
|
|
|
|
|
|
class ShiftedKeycodes(KeycodeCategory):
|
|
KC_TILDE = KC_TILD = Modifiers.KC_LSHIFT(Common.KC_GRAVE)
|
|
KC_EXCLAIM = KC_EXLM = Modifiers.KC_LSHIFT(Common.KC_1)
|
|
KC_AT = Modifiers.KC_LSHIFT(Common.KC_2)
|
|
KC_HASH = Modifiers.KC_LSHIFT(Common.KC_3)
|
|
KC_DOLLAR = KC_DLR = Modifiers.KC_LSHIFT(Common.KC_4)
|
|
KC_PERCENT = KC_PERC = Modifiers.KC_LSHIFT(Common.KC_5)
|
|
KC_CIRCUMFLEX = KC_CIRC = Modifiers.KC_LSHIFT(Common.KC_6) # The ^ Symbol
|
|
KC_AMPERSAND = KC_AMPR = Modifiers.KC_LSHIFT(Common.KC_7)
|
|
KC_ASTERISK = KC_ASTR = Modifiers.KC_LSHIFT(Common.KC_8)
|
|
KC_LEFT_PAREN = KC_LPRN = Modifiers.KC_LSHIFT(Common.KC_9)
|
|
KC_RIGHT_PAREN = KC_RPRN = Modifiers.KC_LSHIFT(Common.KC_0)
|
|
KC_UNDERSCORE = KC_UNDS = Modifiers.KC_LSHIFT(Common.KC_MINUS)
|
|
KC_PLUS = Modifiers.KC_LSHIFT(Common.KC_EQUAL)
|
|
KC_LEFT_CURLY_BRACE = KC_LCBR = Modifiers.KC_LSHIFT(Common.KC_LBRACKET)
|
|
KC_RIGHT_CURLY_BRACE = KC_RCBR = Modifiers.KC_LSHIFT(Common.KC_RBRACKET)
|
|
KC_PIPE = Modifiers.KC_LSHIFT(Common.KC_BACKSLASH)
|
|
KC_COLON = KC_COLN = Modifiers.KC_LSHIFT(Common.KC_SEMICOLON)
|
|
KC_DOUBLE_QUOTE = KC_DQUO = KC_DQT = Modifiers.KC_LSHIFT(Common.KC_QUOTE)
|
|
KC_LEFT_ANGLE_BRACKET = KC_LABK = Modifiers.KC_LSHIFT(Common.KC_COMMA)
|
|
KC_RIGHT_ANGLE_BRACKET = KC_RABK = Modifiers.KC_LSHIFT(Common.KC_DOT)
|
|
KC_QUESTION = KC_QUES = Modifiers.KC_LSHIFT(Common.KC_SLSH)
|
|
|
|
|
|
class FunctionKeys(KeycodeCategory):
|
|
KC_F1 = Keycode(58)
|
|
KC_F2 = Keycode(59)
|
|
KC_F3 = Keycode(60)
|
|
KC_F4 = Keycode(61)
|
|
KC_F5 = Keycode(62)
|
|
KC_F6 = Keycode(63)
|
|
KC_F7 = Keycode(64)
|
|
KC_F8 = Keycode(65)
|
|
KC_F9 = Keycode(66)
|
|
KC_F10 = Keycode(67)
|
|
KC_F11 = Keycode(68)
|
|
KC_F12 = Keycode(69)
|
|
KC_F13 = Keycode(104)
|
|
KC_F14 = Keycode(105)
|
|
KC_F15 = Keycode(106)
|
|
KC_F16 = Keycode(107)
|
|
KC_F17 = Keycode(108)
|
|
KC_F18 = Keycode(109)
|
|
KC_F19 = Keycode(110)
|
|
KC_F20 = Keycode(111)
|
|
KC_F21 = Keycode(112)
|
|
KC_F22 = Keycode(113)
|
|
KC_F23 = Keycode(114)
|
|
KC_F24 = Keycode(115)
|
|
|
|
|
|
class NavAndLocks(KeycodeCategory):
|
|
KC_CAPS_LOCK = KC_CLCK = KC_CAPS = Keycode(57)
|
|
KC_LOCKING_CAPS = KC_LCAP = Keycode(130)
|
|
KC_PSCREEN = KC_PSCR = Keycode(70)
|
|
KC_SCROLLLOCK = KC_SLCK = Keycode(71)
|
|
KC_LOCKING_SCROLL = KC_LSCRL = Keycode(132)
|
|
KC_PAUSE = KC_PAUS = KC_BRK = Keycode(72)
|
|
KC_INSERT = KC_INS = Keycode(73)
|
|
KC_HOME = Keycode(74)
|
|
KC_PGUP = Keycode(75)
|
|
KC_DELETE = KC_DEL = Keycode(76)
|
|
KC_END = Keycode(77)
|
|
KC_PGDOWN = KC_PGDN = Keycode(78)
|
|
KC_RIGHT = KC_RGHT = Keycode(79)
|
|
KC_LEFT = Keycode(80)
|
|
KC_DOWN = Keycode(81)
|
|
KC_UP = Keycode(82)
|
|
|
|
|
|
class Numpad(KeycodeCategory):
|
|
KC_NUMLOCK = KC_NLCK = Keycode(83)
|
|
KC_LOCKING_NUM = KC_LNUM = Keycode(131)
|
|
KC_KP_SLASH = KC_PSLS = Keycode(84)
|
|
KC_KP_ASTERIK = KC_PAST = Keycode(85)
|
|
KC_KP_MINUS = KC_PMNS = Keycode(86)
|
|
KC_KP_PLUS = KC_PPLS = Keycode(87)
|
|
KC_KP_ENTER = KC_PENT = Keycode(88)
|
|
KC_KP_1 = KC_P1 = Keycode(89)
|
|
KC_KP_2 = KC_P2 = Keycode(90)
|
|
KC_KP_3 = KC_P3 = Keycode(91)
|
|
KC_KP_4 = KC_P4 = Keycode(92)
|
|
KC_KP_5 = KC_P5 = Keycode(93)
|
|
KC_KP_6 = KC_P6 = Keycode(94)
|
|
KC_KP_7 = KC_P7 = Keycode(95)
|
|
KC_KP_8 = KC_P8 = Keycode(96)
|
|
KC_KP_9 = KC_P9 = Keycode(97)
|
|
KC_KP_0 = KC_P0 = Keycode(98)
|
|
KC_KP_DOT = KC_PDOT = Keycode(99)
|
|
KC_KP_EQUAL = KC_PEQL = Keycode(103)
|
|
KC_KP_COMMA = KC_PCMM = Keycode(133)
|
|
KC_KP_EQUAL_AS400 = Keycode(134)
|
|
|
|
|
|
class International(KeycodeCategory):
|
|
KC_INT1 = KC_RO = Keycode(135)
|
|
KC_INT2 = KC_KANA = Keycode(136)
|
|
KC_INT3 = KC_JYEN = Keycode(137)
|
|
KC_INT4 = KC_HENK = Keycode(138)
|
|
KC_INT5 = KC_MHEN = Keycode(139)
|
|
KC_INT6 = Keycode(140)
|
|
KC_INT7 = Keycode(141)
|
|
KC_INT8 = Keycode(142)
|
|
KC_INT9 = Keycode(143)
|
|
KC_LANG1 = KC_HAEN = Keycode(144)
|
|
KC_LANG2 = KC_HAEJ = Keycode(145)
|
|
KC_LANG3 = Keycode(146)
|
|
KC_LANG4 = Keycode(147)
|
|
KC_LANG5 = Keycode(148)
|
|
KC_LANG6 = Keycode(149)
|
|
KC_LANG7 = Keycode(150)
|
|
KC_LANG8 = Keycode(151)
|
|
KC_LANG9 = Keycode(152)
|
|
|
|
|
|
class Misc(KeycodeCategory):
|
|
KC_APPLICATION = KC_APP = ConsumerKeycode(101)
|
|
KC_POWER = ConsumerKeycode(102)
|
|
KC_EXECUTE = KC_EXEC = ConsumerKeycode(116)
|
|
KC_SYSTEM_POWER = KC_PWR = ConsumerKeycode(165)
|
|
KC_SYSTEM_SLEEP = KC_SLEP = ConsumerKeycode(166)
|
|
KC_SYSTEM_WAKE = KC_WAKE = ConsumerKeycode(167)
|
|
KC_HELP = ConsumerKeycode(117)
|
|
KC_MENU = ConsumerKeycode(118)
|
|
KC_SELECT = KC_SLCT = ConsumerKeycode(119)
|
|
KC_STOP = ConsumerKeycode(120)
|
|
KC_AGAIN = KC_AGIN = ConsumerKeycode(121)
|
|
KC_UNDO = ConsumerKeycode(122)
|
|
KC_CUT = ConsumerKeycode(123)
|
|
KC_COPY = ConsumerKeycode(124)
|
|
KC_PASTE = KC_PSTE = ConsumerKeycode(125)
|
|
KC_FIND = ConsumerKeycode(126)
|
|
KC_ALT_ERASE = KC_ERAS = ConsumerKeycode(153)
|
|
KC_SYSREQ = ConsumerKeycode(154)
|
|
KC_CANCEL = ConsumerKeycode(155)
|
|
KC_CLEAR = KC_CLR = ConsumerKeycode(156)
|
|
KC_PRIOR = ConsumerKeycode(157)
|
|
KC_RETURN = ConsumerKeycode(158)
|
|
KC_SEPERATOR = ConsumerKeycode(159)
|
|
KC_OUT = ConsumerKeycode(160)
|
|
KC_OPER = ConsumerKeycode(161)
|
|
KC_CLEAR_AGAIN = ConsumerKeycode(162)
|
|
KC_CRSEL = ConsumerKeycode(163)
|
|
KC_EXSEL = ConsumerKeycode(164)
|
|
KC_MAIL = ConsumerKeycode(177)
|
|
KC_CALCULATOR = KC_CALC = ConsumerKeycode(178)
|
|
KC_MY_COMPUTER = KC_MYCM = ConsumerKeycode(179)
|
|
KC_WWW_SEARCH = KC_WSCH = ConsumerKeycode(180)
|
|
KC_WWW_HOME = KC_WHOM = ConsumerKeycode(181)
|
|
KC_WWW_BACK = KC_WBAK = ConsumerKeycode(182)
|
|
KC_WWW_FORWARD = KC_WFWD = ConsumerKeycode(183)
|
|
KC_WWW_STOP = KC_WSTP = ConsumerKeycode(184)
|
|
KC_WWW_REFRESH = KC_WREF = ConsumerKeycode(185)
|
|
KC_WWW_FAVORITES = KC_WFAV = ConsumerKeycode(186)
|
|
|
|
|
|
class Media(KeycodeCategory):
|
|
# I believe QMK used these double-underscore codes for MacOS
|
|
# support or something. I have no idea, but modern MacOS supports
|
|
# PC volume keys so I really don't care that these codes are the
|
|
# same as below. If bugs arise, these codes may need to change.
|
|
KC__MUTE = ConsumerKeycode(226)
|
|
KC__VOLUP = ConsumerKeycode(233)
|
|
KC__VOLDOWN = ConsumerKeycode(234)
|
|
|
|
KC_AUDIO_MUTE = KC_MUTE = ConsumerKeycode(226) # 0xE2
|
|
KC_AUDIO_VOL_UP = KC_VOLU = ConsumerKeycode(233) # 0xE9
|
|
KC_AUDIO_VOL_DOWN = KC_VOLD = ConsumerKeycode(234) # 0xEA
|
|
KC_MEDIA_NEXT_TRACK = KC_MNXT = ConsumerKeycode(181) # 0xB5
|
|
KC_MEDIA_PREV_TRACK = KC_MPRV = ConsumerKeycode(182) # 0xB6
|
|
KC_MEDIA_STOP = KC_MSTP = ConsumerKeycode(183) # 0xB7
|
|
KC_MEDIA_PLAY_PAUSE = KC_MPLY = ConsumerKeycode(205) # 0xCD (this may not be right)
|
|
KC_MEDIA_EJECT = KC_EJCT = ConsumerKeycode(184) # 0xB8
|
|
KC_MEDIA_FAST_FORWARD = KC_MFFD = ConsumerKeycode(179) # 0xB3
|
|
KC_MEDIA_REWIND = KC_MRWD = ConsumerKeycode(180) # 0xB4
|
|
|
|
|
|
class KMK(KeycodeCategory):
|
|
KC_RESET = Keycode(1000)
|
|
KC_DEBUG = Keycode(1001)
|
|
KC_GESC = Keycode(1002)
|
|
KC_LSPO = Keycode(1003)
|
|
KC_RSPC = Keycode(1004)
|
|
KC_LEAD = Keycode(1005)
|
|
KC_LOCK = Keycode(1006)
|
|
KC_NO = Keycode(1107)
|
|
KC_TRANSPARENT = KC_TRNS = Keycode(1108)
|
|
KC_DEBUG = KC_DBG = Keycode(1112)
|
|
|
|
@staticmethod
|
|
def KC_UC_MODE(mode):
|
|
'''
|
|
Set any Unicode Mode at runtime (allows the same keymap's unicode
|
|
sequences to work across all supported platforms)
|
|
'''
|
|
return UnicodeModeKeycode.from_mode_const(mode)
|
|
|
|
KC_UC_MODE_NOOP = KC_UC_DISABLE = UnicodeModeKeycode.from_mode_const(UnicodeMode.NOOP)
|
|
KC_UC_MODE_LINUX = KC_UC_MODE_IBUS = UnicodeModeKeycode.from_mode_const(UnicodeMode.IBUS)
|
|
KC_UC_MODE_MACOS = KC_UC_MODE_OSX = KC_UC_MODE_RALT = UnicodeModeKeycode.from_mode_const(
|
|
UnicodeMode.RALT,
|
|
)
|
|
KC_UC_MODE_WINC = UnicodeModeKeycode.from_mode_const(UnicodeMode.WINC)
|
|
|
|
@staticmethod
|
|
def KC_MACRO_SLEEP_MS(ms):
|
|
return MacroSleepKeycode(RawKeycodes.KC_MACRO_SLEEP_MS, ms)
|
|
|
|
@staticmethod
|
|
def KC_TAP_DANCE(*args):
|
|
return TapDanceKeycode(*args)
|
|
|
|
|
|
KMK.KC_TD = KMK.KC_TAP_DANCE
|
|
|
|
|
|
class Layers(KeycodeCategory):
|
|
@staticmethod
|
|
def KC_DF(layer):
|
|
return LayerKeycode(RawKeycodes.KC_DF, layer, KC.NO)
|
|
|
|
@staticmethod
|
|
def KC_MO(layer):
|
|
return LayerKeycode(RawKeycodes.KC_MO, layer, KC.NO)
|
|
|
|
@staticmethod
|
|
def KC_LM(layer, kc):
|
|
return LayerKeycode(RawKeycodes.KC_LM, layer, kc)
|
|
|
|
@staticmethod
|
|
def KC_LT(layer, kc):
|
|
return LayerKeycode(RawKeycodes.KC_LT, layer, kc)
|
|
|
|
@staticmethod
|
|
def KC_TG(layer):
|
|
return LayerKeycode(RawKeycodes.KC_TG, layer, KC.NO)
|
|
|
|
@staticmethod
|
|
def KC_TO(layer):
|
|
return LayerKeycode(RawKeycodes.KC_TO, layer, KC.NO)
|
|
|
|
@staticmethod
|
|
def KC_TT(layer):
|
|
return LayerKeycode(RawKeycodes.KC_TT, layer, KC.NO)
|
|
|
|
|
|
class Keycodes(KeycodeCategory):
|
|
'''
|
|
A massive grouping of keycodes
|
|
|
|
Some of these are from http://www.freebsddiary.org/APC/usb_hid_usages.php,
|
|
one of the most useful pages on the interwebs for HID stuff, apparently.
|
|
'''
|
|
_groupings = [
|
|
'Modifiers',
|
|
'Common',
|
|
'ShiftedKeycodes',
|
|
'FunctionKeys',
|
|
'NavAndLocks',
|
|
'Numpad',
|
|
'International',
|
|
'Misc',
|
|
'Media',
|
|
'KMK',
|
|
'Layers',
|
|
]
|
|
|
|
Modifiers = Modifiers
|
|
Common = Common
|
|
ShiftedKeycodes = ShiftedKeycodes
|
|
FunctionKeys = FunctionKeys
|
|
NavAndLocks = NavAndLocks
|
|
Numpad = Numpad
|
|
International = International
|
|
Misc = Misc
|
|
Media = Media
|
|
KMK = KMK
|
|
Layers = Layers
|
|
|
|
|
|
class LazyKC:
|
|
def __init__(self):
|
|
self.cache = {}
|
|
|
|
def __getattr__(self, attr):
|
|
if attr in self.cache:
|
|
return self.cache[attr]
|
|
|
|
for grouping in Keycodes._groupings:
|
|
grouping_cls = getattr(Keycodes, grouping)
|
|
|
|
try:
|
|
found = getattr(grouping_cls, 'KC_{}'.format(attr))
|
|
self.cache[attr] = found
|
|
return found
|
|
except AttributeError:
|
|
continue
|
|
|
|
raise AttributeError(attr)
|
|
|
|
|
|
KC = LazyKC()
|
|
|
|
char_lookup = {
|
|
"\n": Common.KC_ENTER,
|
|
"\t": Common.KC_TAB,
|
|
' ': Common.KC_SPACE,
|
|
'-': Common.KC_MINUS,
|
|
'=': Common.KC_EQUAL,
|
|
'[': Common.KC_LBRACKET,
|
|
']': Common.KC_RBRACKET,
|
|
"\\": Common.KC_BACKSLASH,
|
|
';': Common.KC_SEMICOLON,
|
|
"'": Common.KC_QUOTE,
|
|
'`': Common.KC_GRAVE,
|
|
',': Common.KC_COMMA,
|
|
'.': Common.KC_DOT,
|
|
'~': ShiftedKeycodes.KC_TILDE,
|
|
'!': ShiftedKeycodes.KC_EXCLAIM,
|
|
'@': ShiftedKeycodes.KC_AT,
|
|
'#': ShiftedKeycodes.KC_HASH,
|
|
'$': ShiftedKeycodes.KC_DOLLAR,
|
|
'%': ShiftedKeycodes.KC_PERCENT,
|
|
'^': ShiftedKeycodes.KC_CIRCUMFLEX,
|
|
'&': ShiftedKeycodes.KC_AMPERSAND,
|
|
'*': ShiftedKeycodes.KC_ASTERISK,
|
|
'(': ShiftedKeycodes.KC_LEFT_PAREN,
|
|
')': ShiftedKeycodes.KC_RIGHT_PAREN,
|
|
'_': ShiftedKeycodes.KC_UNDERSCORE,
|
|
'+': ShiftedKeycodes.KC_PLUS,
|
|
'{': ShiftedKeycodes.KC_LEFT_CURLY_BRACE,
|
|
'}': ShiftedKeycodes.KC_RIGHT_CURLY_BRACE,
|
|
'|': ShiftedKeycodes.KC_PIPE,
|
|
':': ShiftedKeycodes.KC_COLON,
|
|
'"': ShiftedKeycodes.KC_DOUBLE_QUOTE,
|
|
'<': ShiftedKeycodes.KC_LEFT_ANGLE_BRACKET,
|
|
'>': ShiftedKeycodes.KC_RIGHT_ANGLE_BRACKET,
|
|
'?': ShiftedKeycodes.KC_QUESTION,
|
|
}
|